diff --git a/package-lock.json b/package-lock.json index 6d73af44de48..478824ac04cb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -90,6 +90,7 @@ } }, "eslint-plugin": { + "name": "eslint-plugin-lexical", "version": "1.0.0", "dev": true, "peerDependencies": { @@ -1178,6 +1179,18 @@ "node": ">= 0.10.4" } }, + "node_modules/@eduardoac-skimlinks/webext-redux": { + "version": "3.0.1-release-candidate", + "resolved": "https://registry.npmjs.org/@eduardoac-skimlinks/webext-redux/-/webext-redux-3.0.1-release-candidate.tgz", + "integrity": "sha512-Gv7LOjiqye+umkR4v0FqcWo8goqP333Lr2kqtMIWJMs5S12T5b3G87PoxuVqZByJk2Ebc6mJ/v7f3HMVzgFT+w==", + "dependencies": { + "lodash.assignin": "^4.2.0", + "lodash.clonedeep": "^4.5.0" + }, + "peerDependencies": { + "redux": ">= 3 <= 4" + } + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz", @@ -13605,12 +13618,22 @@ "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" }, + "node_modules/lodash.assignin": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.assignin/-/lodash.assignin-4.2.0.tgz", + "integrity": "sha512-yX/rx6d/UTVh7sSVWVSIMjfnz95evAgDFdb1ZozC35I9mSFCkmzptOzevxjgbQUsc78NR44LVHWjsoMQXy9FDg==" + }, "node_modules/lodash.camelcase": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", "dev": true }, + "node_modules/lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==" + }, "node_modules/lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", @@ -19267,6 +19290,14 @@ "requires-port": "^1.0.0" } }, + "node_modules/use-sync-external-store": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", + "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -19691,6 +19722,17 @@ "node": ">=10.13.0" } }, + "node_modules/webext-zustand": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/webext-zustand/-/webext-zustand-0.2.0.tgz", + "integrity": "sha512-CpEyOdWA5hzwvMazbzJycSGk6GGx+VBSZmibyzzUNlGWdF/RvQZpYpFe/H3/Aw1m+hKrl8NOwTJdt5iwxF/+fg==", + "dependencies": { + "@eduardoac-skimlinks/webext-redux": "3.0.1-release-candidate" + }, + "peerDependencies": { + "zustand": "^4" + } + }, "node_modules/webextension-polyfill": { "version": "0.10.0", "resolved": "https://registry.npmjs.org/webextension-polyfill/-/webextension-polyfill-0.10.0.tgz", @@ -20848,6 +20890,33 @@ "url": "https://github.com/sponsors/colinhacks" } }, + "node_modules/zustand": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.2.tgz", + "integrity": "sha512-2cN1tPkDVkwCy5ickKrI7vijSjPksFRfqS6237NzT0vqSsztTNnQdHw9mmN7uBdk3gceVXU0a+21jFzFzAc9+g==", + "dependencies": { + "use-sync-external-store": "1.2.0" + }, + "engines": { + "node": ">=12.7.0" + }, + "peerDependencies": { + "@types/react": ">=16.8", + "immer": ">=9.0.6", + "react": ">=16.8" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + } + } + }, "packages/lexical": { "version": "0.14.2", "license": "MIT" @@ -20887,7 +20956,9 @@ "hasInstallScript": true, "dependencies": { "react": "^18.2.0", - "react-dom": "^18.2.0" + "react-dom": "^18.2.0", + "webext-zustand": "0.2.0", + "zustand": "^4.5.1" }, "devDependencies": { "@types/react": "^18.2.46", diff --git a/packages/lexical-devtools/entrypoints/background.ts b/packages/lexical-devtools/entrypoints/background.ts index aad8c5e4d526..54d518a7dcdc 100644 --- a/packages/lexical-devtools/entrypoints/background.ts +++ b/packages/lexical-devtools/entrypoints/background.ts @@ -5,9 +5,16 @@ * LICENSE file in the root directory of this source tree. * */ +import store from '../store'; + export default defineBackground(() => { // eslint-disable-next-line no-console console.log('Hello from Lexical DevTools extension content script.', { id: browser.runtime.id, }); + + // listen state changes + store.subscribe((state) => { + // console.log(state); + }); }); diff --git a/packages/lexical-devtools/entrypoints/devtools-panel/App.tsx b/packages/lexical-devtools/entrypoints/devtools-panel/App.tsx new file mode 100644 index 000000000000..2cd42c878b6d --- /dev/null +++ b/packages/lexical-devtools/entrypoints/devtools-panel/App.tsx @@ -0,0 +1,36 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + */ + +import * as React from 'react'; + +import lexicalLogo from '/lexical.svg'; + +import useStore from '../../store'; + +function App() { + const counter = useStore((state) => state.counter); + const increase = useStore((state) => state.increase); + + return ( + <> +
+ + Lexical logo + +
+
+ +

+ Edit src/App.tsx and save to test HMR +

+
+ + ); +} + +export default App; diff --git a/packages/lexical-devtools/entrypoints/devtools-panel/index.html b/packages/lexical-devtools/entrypoints/devtools-panel/index.html new file mode 100644 index 000000000000..969aff824c3c --- /dev/null +++ b/packages/lexical-devtools/entrypoints/devtools-panel/index.html @@ -0,0 +1,11 @@ + + + + + + + +
+ + + diff --git a/packages/lexical-devtools/entrypoints/devtools-panel/main.tsx b/packages/lexical-devtools/entrypoints/devtools-panel/main.tsx new file mode 100644 index 000000000000..d51da95dd823 --- /dev/null +++ b/packages/lexical-devtools/entrypoints/devtools-panel/main.tsx @@ -0,0 +1,18 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + */ + +import React from 'react'; +import ReactDOM from 'react-dom/client'; + +import App from './App.tsx'; + +ReactDOM.createRoot(document.getElementById('root')!).render( + + + , +); diff --git a/packages/lexical-devtools/entrypoints/devtools/index.html b/packages/lexical-devtools/entrypoints/devtools/index.html new file mode 100644 index 000000000000..0efe111a4f91 --- /dev/null +++ b/packages/lexical-devtools/entrypoints/devtools/index.html @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/packages/lexical-devtools/entrypoints/devtools/main.ts b/packages/lexical-devtools/entrypoints/devtools/main.ts new file mode 100644 index 000000000000..f8b60cb0cf78 --- /dev/null +++ b/packages/lexical-devtools/entrypoints/devtools/main.ts @@ -0,0 +1,14 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + */ + +// Create the panel which appears within the browser devtools +browser.devtools.panels.create( + 'Lexical', + 'icon/128.png', + 'devtools-panel.html', +); diff --git a/packages/lexical-devtools/entrypoints/popup/App.tsx b/packages/lexical-devtools/entrypoints/popup/App.tsx index cea4bb6d82d8..92b014f8c6cf 100644 --- a/packages/lexical-devtools/entrypoints/popup/App.tsx +++ b/packages/lexical-devtools/entrypoints/popup/App.tsx @@ -7,13 +7,15 @@ */ import './App.css'; -import {useState} from 'react'; import * as React from 'react'; import lexicalLogo from '/lexical.svg'; +import useStore from '../../store'; + function App() { - const [count, setCount] = useState(0); + const counter = useStore((state) => state.counter); + const increase = useStore((state) => state.increase); return ( <> @@ -23,7 +25,7 @@ function App() {
- +

Edit src/App.tsx and save to test HMR

diff --git a/packages/lexical-devtools/entrypoints/popup/main.tsx b/packages/lexical-devtools/entrypoints/popup/main.tsx index 85457dfe2f21..b46c4fe34e9e 100644 --- a/packages/lexical-devtools/entrypoints/popup/main.tsx +++ b/packages/lexical-devtools/entrypoints/popup/main.tsx @@ -10,10 +10,15 @@ import './style.css'; import React from 'react'; import ReactDOM from 'react-dom/client'; +import {storeReadyPromise} from '../../store'; import App from './App.tsx'; -ReactDOM.createRoot(document.getElementById('root')!).render( - - - , -); +storeReadyPromise + .then(() => + ReactDOM.createRoot(document.getElementById('root')!).render( + + + , + ), + ) + .catch(console.error); diff --git a/packages/lexical-devtools/package.json b/packages/lexical-devtools/package.json index 7502ae8fb306..86f0f62e540f 100644 --- a/packages/lexical-devtools/package.json +++ b/packages/lexical-devtools/package.json @@ -16,7 +16,9 @@ }, "dependencies": { "react": "^18.2.0", - "react-dom": "^18.2.0" + "react-dom": "^18.2.0", + "webext-zustand": "0.2.0", + "zustand": "^4.5.1" }, "devDependencies": { "@types/react": "^18.2.46", diff --git a/packages/lexical-devtools/store.ts b/packages/lexical-devtools/store.ts new file mode 100644 index 000000000000..03c2f8d6d594 --- /dev/null +++ b/packages/lexical-devtools/store.ts @@ -0,0 +1,24 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + */ + +import {wrapStore} from 'webext-zustand'; +import {create} from 'zustand'; + +interface ExtensionState { + counter: number; + increase: (by: number) => void; +} + +export const useExtensionStore = create()((set) => ({ + counter: 0, + increase: (by) => set((state) => ({counter: state.counter + by})), +})); + +export const storeReadyPromise = wrapStore(useExtensionStore); + +export default useExtensionStore; diff --git a/packages/lexical-devtools/types.d.ts b/packages/lexical-devtools/types.d.ts new file mode 100644 index 000000000000..2a584c23714d --- /dev/null +++ b/packages/lexical-devtools/types.d.ts @@ -0,0 +1,6 @@ +// To be fixed in https://github.com/sinanbekar/webext-zustand/pull/3 +declare module 'webext-zustand' { + declare const wrapStore: ( + store: import('zustand').StoreApi, + ) => Promise; +}