{
+ setDragging(false);
+ }}
+ />
+ )}
+ >
+ );
+}
+
+export default App;
diff --git a/src/pages/inject/LevitateBall/index.tsx b/src/pages/inject/LevitateBall/index.tsx
new file mode 100644
index 00000000..65c6267a
--- /dev/null
+++ b/src/pages/inject/LevitateBall/index.tsx
@@ -0,0 +1,29 @@
+import React from 'react';
+import { createRoot } from 'react-dom/client';
+import AntdLayout from '@/components/AntdLayout';
+import LevitateBallApp from './app';
+
+
+interface ICreateWordMarkOption {
+ dom: HTMLElement;
+}
+
+function App() {
+ return (
+
+
+
+ );
+}
+
+export function createLevitateBall(option: ICreateWordMarkOption) {
+ const div = document.createElement('div');
+ const root = createRoot(div);
+ root.render(
);
+ option.dom.appendChild(div);
+
+ return () => {
+ root.unmount();
+ option.dom.removeChild(div);
+ };
+}
diff --git a/src/pages/inject/WordMark/Inner/DisableMenu.tsx b/src/pages/inject/WordMark/Inner/DisableMenu.tsx
index 47ffbed1..87a45be0 100644
--- a/src/pages/inject/WordMark/Inner/DisableMenu.tsx
+++ b/src/pages/inject/WordMark/Inner/DisableMenu.tsx
@@ -7,13 +7,19 @@ import styles from './DisableMenu.module.less';
function DisableMenu() {
const disableForever = () => {
- backgroundBridge.wordMarkConfig.update(WordMarkConfigKey.enable, false, {
- notice: true,
- });
+ backgroundBridge.configManager.update(
+ 'wordMark',
+ WordMarkConfigKey.enable,
+ false,
+ {
+ notice: true,
+ },
+ );
};
const disableForPage = () => {
- backgroundBridge.wordMarkConfig.update(
+ backgroundBridge.configManager.update(
+ 'wordMark',
WordMarkConfigKey.disableUrl,
`${window.location.origin}${window.location.pathname}`,
{
diff --git a/src/pages/inject/WordMark/Inner/OperateMenu.tsx b/src/pages/inject/WordMark/Inner/OperateMenu.tsx
index fd5270ef..a7491fcc 100644
--- a/src/pages/inject/WordMark/Inner/OperateMenu.tsx
+++ b/src/pages/inject/WordMark/Inner/OperateMenu.tsx
@@ -26,7 +26,11 @@ function OperateMenu(props: IOperateMenuProps) {
const updateToolbar = (list: ToolbarItem[]) => {
const result = list.map(item => item.id) as WordMarkOptionTypeEnum[];
setToolbarKeys(result);
- backgroundBridge.wordMarkConfig.update(WordMarkConfigKey.toolbars, result);
+ backgroundBridge.configManager.update(
+ 'wordMark',
+ WordMarkConfigKey.toolbars,
+ result,
+ );
};
const toolbars = useMemo(() => {
diff --git a/src/pages/inject/WordMark/Inner/index.tsx b/src/pages/inject/WordMark/Inner/index.tsx
index ce62a0e8..65cc45b1 100644
--- a/src/pages/inject/WordMark/Inner/index.tsx
+++ b/src/pages/inject/WordMark/Inner/index.tsx
@@ -28,7 +28,8 @@ function InnerWordMark(props: InnerWordMarkProps) {
const result = tools.includes(type)
? tools.filter(t => t !== type)
: [type, ...tools];
- backgroundBridge.wordMarkConfig.update(
+ backgroundBridge.configManager.update(
+ 'wordMark',
WordMarkConfigKey.innerPinList,
result,
);
diff --git a/src/pages/inject/action-listener.tsx b/src/pages/inject/action-listener.tsx
index 1719c610..bdc8c1dc 100644
--- a/src/pages/inject/action-listener.tsx
+++ b/src/pages/inject/action-listener.tsx
@@ -13,6 +13,7 @@ import { AccountLayoutMessageActions } from '@/isomorphic/event/accountLayout';
import { App } from './content-scripts';
import { showScreenShot } from './ScreenShot';
import { showSelectArea } from './AreaSelector';
+import { LevitateBallMessageActions } from '@/isomorphic/event/levitateBall';
type MessageSender = chrome.runtime.MessageSender;
@@ -115,6 +116,14 @@ export const initContentScriptActionListener = (context: App) => {
sendResponse(true);
break;
}
+ case ContentScriptEvents.LevitateConfigChange: {
+ context.sendMessageToLevitateBall(
+ LevitateBallMessageActions.levitateBallConfigUpdate,
+ request.data,
+ );
+ sendResponse(true);
+ break;
+ }
case ContentScriptEvents.AddContentToClipAssistant: {
context.sendMessageToClipAssistant(
ClipAssistantMessageActions.addContent,
diff --git a/src/pages/inject/content-scripts.ts b/src/pages/inject/content-scripts.ts
index e46a9102..7cd5b86a 100644
--- a/src/pages/inject/content-scripts.ts
+++ b/src/pages/inject/content-scripts.ts
@@ -16,11 +16,16 @@ import {
AccountLayoutMessageActions,
AccountLayoutMessageKey,
} from '@/isomorphic/event/accountLayout';
+import {
+ LevitateBallMessageActions,
+ LevitateBallMessageKey,
+} from '@/isomorphic/event/levitateBall';
import {
initContentScriptActionListener,
initContentScriptMessageListener,
} from './action-listener';
import { createWordMark } from './WordMark';
+import { createLevitateBall } from './LevitateBall';
import '@/styles/inject.less';
enum SidePanelStatus {
@@ -66,6 +71,10 @@ export class App {
//
};
+ public removeLevitateBall: VoidCallback = () => {
+ //
+ };
+
constructor() {
this.initRoot();
}
@@ -104,6 +113,9 @@ export class App {
initContentScriptActionListener(this);
initContentScriptMessageListener();
this.initSidePanel();
+ this.removeLevitateBall = createLevitateBall({
+ dom: root,
+ });
});
}
@@ -125,7 +137,7 @@ export class App {
height: 100vh;
right: 0;
top: 0;
- z-index: 999999;
+ z-index: 2147483645;
color-scheme: none;
user-select: none;
}
@@ -272,6 +284,20 @@ export class App {
'*',
);
}
+
+ async sendMessageToLevitateBall(
+ action: LevitateBallMessageActions,
+ data?: any,
+ ) {
+ window.postMessage(
+ {
+ key: LevitateBallMessageKey,
+ action,
+ data,
+ },
+ '*',
+ );
+ }
}
function initSandbox() {
diff --git a/src/pages/setting/app.tsx b/src/pages/setting/app.tsx
index 5b0c6aba..b37652cb 100644
--- a/src/pages/setting/app.tsx
+++ b/src/pages/setting/app.tsx
@@ -9,6 +9,7 @@ import About from './about';
import Help from './help';
import Shortcut from './shortcut';
import styles from './app.module.less';
+import SidePanel from './sidePanel';
import '@/styles/global.less';
initI18N();
@@ -18,6 +19,8 @@ enum Page {
general = 'general',
// 快捷键设置
shortcut = 'shortcut',
+ // 侧边栏
+ sidePanel = 'sidePanel',
// 划词工具栏
wordMark = 'wordMark',
// 帮助页面
@@ -37,6 +40,11 @@ const menus = [
key: Page.shortcut,
page:
,
},
+ {
+ name: __i18n('侧边栏'),
+ key: Page.sidePanel,
+ page:
,
+ },
{
name: __i18n('划词工具栏'),
key: Page.wordMark,
diff --git a/src/pages/setting/sidePanel/index.module.less b/src/pages/setting/sidePanel/index.module.less
new file mode 100644
index 00000000..767d85a5
--- /dev/null
+++ b/src/pages/setting/sidePanel/index.module.less
@@ -0,0 +1,46 @@
+@import '~@/styles/parameters.less';
+
+.configWrapper {
+ max-width: 668px;
+
+ .card {
+ padding: 24px 0;
+ border-bottom: 1px solid @border-light-color;
+ color: @text-primary;
+ .body {
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+
+ .configItem {
+ display: flex;
+ justify-content: space-between;
+
+ :global {
+ .yuque-chrome-extension-select-arrow {
+ color: @text-color-secondary;
+ }
+ }
+ }
+
+ .desc {
+ font-size: @font-size;
+ display: flex;
+ align-items: center;
+ }
+
+ .disableUrlCard {
+ margin-top: 12px;
+ }
+ }
+ }
+
+ .card:first-child {
+ padding-top: 0;
+ }
+
+ .card:last-child {
+ border-bottom: 1px solid transparent;
+ }
+}
+
diff --git a/src/pages/setting/sidePanel/index.tsx b/src/pages/setting/sidePanel/index.tsx
new file mode 100644
index 00000000..4e185511
--- /dev/null
+++ b/src/pages/setting/sidePanel/index.tsx
@@ -0,0 +1,73 @@
+import React, { useCallback, useEffect, useState } from 'react';
+import { Switch } from 'antd';
+import { backgroundBridge } from '@/core/bridge/background';
+import {
+ ILevitateConfig,
+ LevitateConfigKey,
+} from '@/isomorphic/constant/levitate';
+import DisableUrlCard, { IDisableUrlItem } from '@/components/DisableUrlCard';
+import styles from './index.module.less';
+
+function Shortcut() {
+ const [config, setConfig] = useState({} as ILevitateConfig);
+
+ const onConfigChange = useCallback(
+ async (key: LevitateConfigKey, value: any) => {
+ await backgroundBridge.configManager.update('levitate', key, value, {
+ notice: true,
+ });
+ setConfig(pre => ({
+ ...pre,
+ [key]: value,
+ }));
+ },
+ [],
+ );
+
+ const onDelete = useCallback(
+ (item: IDisableUrlItem, index: number) => {
+ const filterArray = config.disableUrl?.filter(
+ d => d.origin !== item.origin,
+ );
+ onConfigChange('disableUrl', filterArray);
+ },
+ [config],
+ );
+
+ useEffect(() => {
+ backgroundBridge.configManager.get('levitate').then(res => {
+ setConfig(res);
+ });
+ }, []);
+
+ return (
+
+
+
+
+
{__i18n('展示侧边栏悬浮气泡')}
+
onConfigChange('enable', !config.enable)}
+ />
+
+ {!!config.disableUrl?.length && (
+
+
+ {__i18n('管理不展示侧边栏气泡的页面')}
+
+
+
+
+
+ )}
+
+
+
+ );
+}
+
+export default React.memo(Shortcut);
diff --git a/src/pages/setting/wordMark/index.tsx b/src/pages/setting/wordMark/index.tsx
index 33ae08c0..ba46118c 100644
--- a/src/pages/setting/wordMark/index.tsx
+++ b/src/pages/setting/wordMark/index.tsx
@@ -57,7 +57,9 @@ function WordMark() {
const [config, setConfig] = useState
(null);
const onConfigChange = async (key: WordMarkConfigKey, value: any) => {
- await backgroundBridge.wordMarkConfig.update(key, value, { notice: true });
+ await backgroundBridge.configManager.update('wordMark', key, value, {
+ notice: true,
+ });
setConfig({
...(config as IWordMarkConfig),
[key]: value,
@@ -65,7 +67,7 @@ function WordMark() {
};
useEffect(() => {
- backgroundBridge.wordMarkConfig.get().then(res => {
+ backgroundBridge.configManager.get('wordMark').then(res => {
setConfig(res);
});
}, []);
@@ -113,7 +115,8 @@ function WordMark() {
{__i18n('默认保存位置')}
{
- backgroundBridge.wordMarkConfig.update(
+ backgroundBridge.configManager.update(
+ 'wordMark',
WordMarkConfigKey.defaultSavePosition,
item,
);