feat: make icons and popups dynamic & change file structure (#3336)
* chore: components and popups folder * fix: run scripts on document_end * feat: make icons and popups dynamic * fix: restricted page handling Co-authored-by: Adithya Vardhan <im-adithya@Adithyas-MacBook-Air.local>
8
.hintrc
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"extends": [
|
||||
"development"
|
||||
],
|
||||
"hints": {
|
||||
"meta-viewport": "off"
|
||||
}
|
||||
}
|
@ -14,7 +14,7 @@ const {readFileSync, writeFileSync} = require('fs');
|
||||
const {copy, ensureDir, move, remove} = require('fs-extra');
|
||||
const {join} = require('path');
|
||||
|
||||
const STATIC_FILES = ['icons'];
|
||||
const STATIC_FILES = ['icons', 'popups'];
|
||||
|
||||
const preProcess = async (destinationPath, tempPath) => {
|
||||
await remove(destinationPath); // Clean up from previously completed builds
|
||||
|
@ -18,9 +18,9 @@
|
||||
"48": "icons/48-disabled.png",
|
||||
"128": "icons/128-disabled.png"
|
||||
},
|
||||
"default_popup": "src/popup/index.html"
|
||||
"default_popup": "popups/disabled.html"
|
||||
},
|
||||
"devtools_page": "src/devtools/index.html",
|
||||
"devtools_page": "src/main/index.html",
|
||||
"content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'",
|
||||
"web_accessible_resources": ["src/inject/index.js"],
|
||||
"background": {
|
||||
@ -31,7 +31,7 @@
|
||||
{
|
||||
"matches": ["<all_urls>"],
|
||||
"js": ["src/content/index.js"],
|
||||
"run_at": "document_start"
|
||||
"run_at": "document_end"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -18,9 +18,9 @@
|
||||
"48": "icons/48-disabled.png",
|
||||
"128": "icons/128-disabled.png"
|
||||
},
|
||||
"default_popup": "src/popup/index.html"
|
||||
"default_popup": "popups/disabled.html"
|
||||
},
|
||||
"devtools_page": "src/devtools/index.html",
|
||||
"devtools_page": "src/main/index.html",
|
||||
"content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'",
|
||||
"web_accessible_resources": ["src/inject/index.js"],
|
||||
"background": {
|
||||
@ -31,7 +31,7 @@
|
||||
{
|
||||
"matches": ["<all_urls>"],
|
||||
"js": ["src/content/index.js"],
|
||||
"run_at": "document_start"
|
||||
"run_at": "document_end"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -22,10 +22,10 @@
|
||||
"48": "icons/48-disabled.png",
|
||||
"128": "icons/128-disabled.png"
|
||||
},
|
||||
"default_popup": "src/popup/index.html",
|
||||
"default_popup": "popups/disabled.html",
|
||||
"browser_style": true
|
||||
},
|
||||
"devtools_page": "src/devtools/index.html",
|
||||
"devtools_page": "src/main/index.html",
|
||||
"content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'",
|
||||
"web_accessible_resources": ["src/inject/index.js"],
|
||||
"background": {
|
||||
@ -36,7 +36,7 @@
|
||||
{
|
||||
"matches": ["<all_urls>"],
|
||||
"js": ["src/content/index.js"],
|
||||
"run_at": "document_start"
|
||||
"run_at": "document_end"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 428 B After Width: | Height: | Size: 428 B |
Before Width: | Height: | Size: 813 B After Width: | Height: | Size: 813 B |
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
18
packages/lexical-devtools/popups/disabled.html
Normal file
@ -0,0 +1,18 @@
|
||||
<script src="shared.js"></script>
|
||||
<link rel="stylesheet" href="shared.css" />
|
||||
<style>
|
||||
html,
|
||||
body {
|
||||
min-width: 410px;
|
||||
min-height: 33px;
|
||||
}
|
||||
|
||||
hr {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
<p>
|
||||
<b>This page doesn’t appear to be using Lexical.</b>
|
||||
<br />
|
||||
If this seems wrong, follow the troubleshooting instructions.
|
||||
</p>
|
18
packages/lexical-devtools/popups/production.html
Normal file
@ -0,0 +1,18 @@
|
||||
<script src="shared.js"></script>
|
||||
<link rel="stylesheet" href="shared.css" />
|
||||
<style>
|
||||
html,
|
||||
body {
|
||||
min-width: 460px;
|
||||
min-height: 39px;
|
||||
}
|
||||
|
||||
hr {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
<p>
|
||||
<b>This page is using the production build of Lexical. ✅</b>
|
||||
<br />
|
||||
Open the developer tools, and "Lexical" tab will appear to the right.
|
||||
</p>
|
14
packages/lexical-devtools/popups/restricted.html
Normal file
@ -0,0 +1,14 @@
|
||||
<script src="shared.js"></script>
|
||||
<link rel="stylesheet" href="shared.css" />
|
||||
<style>
|
||||
html,
|
||||
body {
|
||||
min-width: 286px;
|
||||
min-height: 33px;
|
||||
}
|
||||
</style>
|
||||
<p>
|
||||
<b>This is a restricted browser page.</b>
|
||||
<br />
|
||||
Lexical devtools cannot access this page.
|
||||
</p>
|
8
packages/lexical-devtools/popups/shared.css
Normal file
@ -0,0 +1,8 @@
|
||||
html,
|
||||
body {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 8px;
|
||||
}
|
33
packages/lexical-devtools/popups/shared.js
Normal file
@ -0,0 +1,33 @@
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
/* globals chrome */
|
||||
|
||||
'use strict';
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
// Make links work
|
||||
const links = document.getElementsByTagName('a');
|
||||
for (let i = 0; i < links.length; i++) {
|
||||
(function () {
|
||||
const ln = links[i];
|
||||
const location = ln.href;
|
||||
ln.onclick = function () {
|
||||
chrome.tabs.create({active: true, url: location});
|
||||
return false;
|
||||
};
|
||||
})();
|
||||
}
|
||||
|
||||
// Work around https://bugs.chromium.org/p/chromium/issues/detail?id=428044
|
||||
document.body.style.opacity = 0;
|
||||
document.body.style.transition = 'opacity ease-out .4s';
|
||||
requestAnimationFrame(function () {
|
||||
document.body.style.opacity = 1;
|
||||
});
|
||||
});
|
@ -10,9 +10,15 @@
|
||||
// Each tab will have a separate messaging port for the devTools app & the inspectedWindow's content script, eg. { tabId: { reactPort, contentScriptPort } }
|
||||
const tabsToPorts: Record<
|
||||
number,
|
||||
{contentScriptPort?: chrome.runtime.Port; reactPort?: chrome.runtime.Port}
|
||||
{
|
||||
contentScriptPort?: chrome.runtime.Port;
|
||||
reactPort?: chrome.runtime.Port;
|
||||
devtoolsPort?: chrome.runtime.Port;
|
||||
}
|
||||
> = {};
|
||||
|
||||
const IS_FIREFOX: boolean = navigator.userAgent.indexOf('Firefox') >= 0;
|
||||
|
||||
// The Lexical DevTools React UI sends a message to initialize the port.
|
||||
chrome.runtime.onConnect.addListener((port: chrome.runtime.Port) => {
|
||||
port.onMessage.addListener((message) => {
|
||||
@ -28,20 +34,26 @@ chrome.runtime.onConnect.addListener((port: chrome.runtime.Port) => {
|
||||
return;
|
||||
}
|
||||
|
||||
if (message.name === 'init' && message.type === 'FROM_APP') {
|
||||
if (message.name === 'init' && port.name === 'react-app') {
|
||||
tabsToPorts[tabId] = tabsToPorts[tabId] ? tabsToPorts[tabId] : {};
|
||||
tabsToPorts[message.tabId].reactPort = port;
|
||||
return;
|
||||
}
|
||||
|
||||
if (message.name === 'init' && message.type === 'FROM_CONTENT') {
|
||||
if (message.name === 'init' && port.name === 'content-script') {
|
||||
tabsToPorts[tabId] = tabsToPorts[tabId] ? tabsToPorts[tabId] : {};
|
||||
tabsToPorts[tabId].contentScriptPort = port;
|
||||
port.postMessage({
|
||||
name: 'checkForLexical',
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// initial editorState requested from devtools panel
|
||||
if (message.name === 'init' && message.type === 'FROM_DEVTOOLS') {
|
||||
if (message.name === 'lexical-found' && port.name === 'content-script') {
|
||||
setIconAndPopup('production', tabId);
|
||||
}
|
||||
|
||||
if (message.name === 'init' && port.name === 'devtools') {
|
||||
const contentScriptPort = tabsToPorts[tabId].contentScriptPort;
|
||||
if (contentScriptPort) {
|
||||
contentScriptPort.postMessage({
|
||||
@ -70,3 +82,55 @@ chrome.runtime.onConnect.addListener((port: chrome.runtime.Port) => {
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
function isRestrictedBrowserPage(url: string | undefined) {
|
||||
return !url || new URL(url).protocol === 'chrome:';
|
||||
}
|
||||
|
||||
function checkAndHandleRestrictedPageIfSo(tab: chrome.tabs.Tab) {
|
||||
if (tab && tab.id && isRestrictedBrowserPage(tab.url)) {
|
||||
setIconAndPopup('restricted', tab.id);
|
||||
}
|
||||
}
|
||||
|
||||
if (!IS_FIREFOX) {
|
||||
chrome.tabs.query({}, (tabs) =>
|
||||
tabs.forEach(checkAndHandleRestrictedPageIfSo),
|
||||
);
|
||||
chrome.tabs.onCreated.addListener((tab: chrome.tabs.Tab) => {
|
||||
checkAndHandleRestrictedPageIfSo(tab);
|
||||
});
|
||||
}
|
||||
|
||||
// Listen to URL changes on the active tab and update the DevTools icon.
|
||||
chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
|
||||
if (IS_FIREFOX) {
|
||||
// We don't properly detect protected URLs in Firefox at the moment.
|
||||
// However we can reset the DevTools icon to its loading state when the URL changes.
|
||||
// It will be updated to the correct icon by the onMessage callback below.
|
||||
if (tab.active && changeInfo.status === 'loading') {
|
||||
setIconAndPopup('disabled', tabId);
|
||||
}
|
||||
} else {
|
||||
// Don't reset the icon to the loading state for Chrome or Edge.
|
||||
// The onUpdated callback fires more frequently for these browsers,
|
||||
// often after onMessage has been called.
|
||||
checkAndHandleRestrictedPageIfSo(tab);
|
||||
}
|
||||
});
|
||||
|
||||
function setIconAndPopup(lexicalBuildType: string, tabId: number) {
|
||||
chrome.browserAction.setIcon({
|
||||
path: {
|
||||
'128': 'icons/128-' + lexicalBuildType + '.png',
|
||||
'16': 'icons/16-' + lexicalBuildType + '.png',
|
||||
'32': 'icons/32-' + lexicalBuildType + '.png',
|
||||
'48': 'icons/48-' + lexicalBuildType + '.png',
|
||||
},
|
||||
tabId: tabId,
|
||||
});
|
||||
chrome.browserAction.setPopup({
|
||||
popup: 'popups/' + lexicalBuildType + '.html',
|
||||
tabId: tabId,
|
||||
});
|
||||
}
|
||||
|
@ -43,7 +43,6 @@ function App(): JSX.Element {
|
||||
lexicalKey,
|
||||
name: 'highlight',
|
||||
tabId: window.chrome.devtools.inspectedWindow.tabId,
|
||||
type: 'FROM_APP',
|
||||
});
|
||||
},
|
||||
[port],
|
||||
@ -54,7 +53,6 @@ function App(): JSX.Element {
|
||||
port.current?.postMessage({
|
||||
name: 'dehighlight',
|
||||
tabId: window.chrome.devtools.inspectedWindow.tabId,
|
||||
type: 'FROM_APP',
|
||||
});
|
||||
},
|
||||
[port],
|
||||
@ -62,13 +60,14 @@ function App(): JSX.Element {
|
||||
|
||||
useEffect(() => {
|
||||
// create and initialize the messaging port to receive editorState updates
|
||||
port.current = window.chrome.runtime.connect();
|
||||
port.current = window.chrome.runtime.connect({
|
||||
name: 'react-app',
|
||||
});
|
||||
|
||||
// post init message to background JS so tabId will be registered
|
||||
port.current.postMessage({
|
||||
name: 'init',
|
||||
tabId: window.chrome.devtools.inspectedWindow.tabId,
|
||||
type: 'FROM_APP',
|
||||
});
|
||||
|
||||
return () => {
|
@ -5,6 +5,7 @@
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
text-decoration: none;
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
@ -13,9 +13,13 @@ declare global {
|
||||
interface DocumentEventMap {
|
||||
editorStateUpdate: CustomEvent;
|
||||
highlight: CustomEvent;
|
||||
lexicalPresenceUpdate: CustomEvent;
|
||||
}
|
||||
}
|
||||
|
||||
let backendDisconnected = false;
|
||||
let backendInitialized = false;
|
||||
|
||||
// for security reasons, content scripts cannot read Lexical's changes to the DOM
|
||||
// in order to access the editorState, we inject this script directly into the page
|
||||
const script = document.createElement('script');
|
||||
@ -23,29 +27,20 @@ script.src = chrome.runtime.getURL('src/inject/index.js');
|
||||
document.documentElement.appendChild(script);
|
||||
if (script.parentNode) script.parentNode.removeChild(script);
|
||||
|
||||
const port = chrome.runtime.connect();
|
||||
|
||||
port.postMessage({
|
||||
name: 'init',
|
||||
type: 'FROM_CONTENT',
|
||||
const port = chrome.runtime.connect({
|
||||
name: 'content-script',
|
||||
});
|
||||
|
||||
// Listen to editorState updates from the inspected page, via the registerUpdateListener injected by devtools.js
|
||||
window.addEventListener('message', function (event) {
|
||||
if (event.source !== window) {
|
||||
// Security check: https://developer.chrome.com/docs/extensions/mv3/content_scripts/#host-page-communication
|
||||
return;
|
||||
}
|
||||
function sayHelloToBackend() {
|
||||
port.postMessage({
|
||||
name: 'init',
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
event.data.type &&
|
||||
event.data.type === 'FROM_PAGE' &&
|
||||
event.data.name === 'editor-update'
|
||||
) {
|
||||
document.addEventListener('lexicalPresenceUpdate', function (e) {
|
||||
if (e.detail.lexical) {
|
||||
port.postMessage({
|
||||
editorState: event.data.editorState,
|
||||
name: 'editor-update',
|
||||
type: 'FROM_CONTENT',
|
||||
name: 'lexical-found',
|
||||
});
|
||||
}
|
||||
});
|
||||
@ -54,7 +49,6 @@ document.addEventListener('editorStateUpdate', function (e) {
|
||||
port.postMessage({
|
||||
editorState: e.detail.editorState,
|
||||
name: 'editor-update',
|
||||
type: 'FROM_CONTENT',
|
||||
});
|
||||
});
|
||||
|
||||
@ -69,7 +63,21 @@ function getCloneInto(): CloneInto | null {
|
||||
|
||||
const cloneInto = getCloneInto();
|
||||
|
||||
function handleDisconnect() {
|
||||
backendDisconnected = true;
|
||||
// TODO: remove event listeners and post shutdown message
|
||||
}
|
||||
|
||||
port.onMessage.addListener((message) => {
|
||||
if (message.name === 'checkForLexical') {
|
||||
backendInitialized = true;
|
||||
// As we load scripts on document_end, we wait for the
|
||||
// page to load before dispatching checkForLexical event
|
||||
window.onload = function () {
|
||||
document.dispatchEvent(new CustomEvent('checkForLexical'));
|
||||
};
|
||||
}
|
||||
|
||||
if (message.name === 'highlight') {
|
||||
const data = {lexicalKey: message.lexicalKey as string};
|
||||
const detail =
|
||||
@ -91,3 +99,16 @@ port.onMessage.addListener((message) => {
|
||||
document.dispatchEvent(new CustomEvent('loadEditorState'));
|
||||
}
|
||||
});
|
||||
port.onDisconnect.addListener(handleDisconnect);
|
||||
|
||||
sayHelloToBackend();
|
||||
|
||||
if (!backendInitialized) {
|
||||
const intervalID = setInterval(() => {
|
||||
if (backendInitialized || backendDisconnected) {
|
||||
clearInterval(intervalID);
|
||||
} else {
|
||||
sayHelloToBackend();
|
||||
}
|
||||
}, 500);
|
||||
}
|
||||
|
@ -1,29 +0,0 @@
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
const port = chrome.runtime.connect();
|
||||
|
||||
// Create the panel which appears within the browser's DevTools, loading the Lexical DevTools App within index.html.
|
||||
chrome.devtools.panels.create(
|
||||
'Lexical',
|
||||
'',
|
||||
'/src/panel/index.html',
|
||||
function (panel) {
|
||||
panel.onShown.addListener(handleShown);
|
||||
// to do: add handleHidden() listener
|
||||
},
|
||||
);
|
||||
|
||||
function handleShown() {
|
||||
// init message goes → background script → content script
|
||||
// content script handles initial load of editorState
|
||||
port.postMessage({
|
||||
name: 'init',
|
||||
tabId: chrome.devtools.inspectedWindow.tabId,
|
||||
type: 'FROM_DEVTOOLS',
|
||||
});
|
||||
}
|
@ -15,6 +15,7 @@ import {
|
||||
RangeSelectionJSON,
|
||||
} from 'packages/lexical-devtools/types';
|
||||
|
||||
let lexical: boolean;
|
||||
let editorDOMNode: LexicalHTMLElement | null, editorKey: string | null;
|
||||
|
||||
const serializePoint = (point: PointType) => {
|
||||
@ -57,6 +58,14 @@ const postEditorState = (editorState: EditorState) => {
|
||||
document.dispatchEvent(new CustomEvent('editorStateUpdate', {detail: data}));
|
||||
};
|
||||
|
||||
document.addEventListener('checkForLexical', function (e) {
|
||||
lexical = document.querySelectorAll('div[data-lexical-editor]').length > 0;
|
||||
const data = {lexical: lexical};
|
||||
document.dispatchEvent(
|
||||
new CustomEvent('lexicalPresenceUpdate', {detail: data}),
|
||||
);
|
||||
});
|
||||
|
||||
document.addEventListener('loadEditorState', function (e) {
|
||||
// query document for Lexical editor instance.
|
||||
// TODO: add support multiple Lexical editors within the same page
|
||||
|
73
packages/lexical-devtools/src/main/index.ts
Normal file
@ -0,0 +1,73 @@
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
let panelCreated = false;
|
||||
|
||||
const port = chrome.runtime.connect({
|
||||
name: 'devtools',
|
||||
});
|
||||
|
||||
function syncSavedPreferences() {
|
||||
// TODO: Save devtools panel settings
|
||||
}
|
||||
|
||||
syncSavedPreferences();
|
||||
|
||||
function createPanelIfLexicalLoaded() {
|
||||
if (panelCreated) {
|
||||
return;
|
||||
}
|
||||
|
||||
chrome.devtools.inspectedWindow.eval(
|
||||
`window.document.querySelectorAll('div[data-lexical-editor]').length > 0`,
|
||||
function (pageHasLexical, error) {
|
||||
if (!pageHasLexical) {
|
||||
return;
|
||||
}
|
||||
|
||||
panelCreated = true;
|
||||
|
||||
clearInterval(loadCheckInterval);
|
||||
|
||||
// Create the panel which appears within the browser devtools
|
||||
chrome.devtools.panels.create(
|
||||
'Lexical',
|
||||
'',
|
||||
'src/panel/index.html',
|
||||
function (panel) {
|
||||
panel.onShown.addListener(handleShown);
|
||||
// TODO: add handleHidden() listener
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
function handleShown() {
|
||||
// init message goes → background script → content script
|
||||
// content script handles initial load of editorState
|
||||
port.postMessage({
|
||||
name: 'init',
|
||||
tabId: chrome.devtools.inspectedWindow.tabId,
|
||||
});
|
||||
}
|
||||
|
||||
// Load (or reload) the DevTools extension when the user navigates to a new page.
|
||||
function checkPageForLexical() {
|
||||
syncSavedPreferences();
|
||||
createPanelIfLexicalLoaded();
|
||||
}
|
||||
|
||||
// Check for Lexical before loading the DevTools extension when the user navigates to a new page.
|
||||
chrome.devtools.network.onNavigated.addListener(checkPageForLexical);
|
||||
|
||||
// In case Lexical is added after page load
|
||||
const loadCheckInterval = setInterval(function () {
|
||||
createPanelIfLexicalLoaded();
|
||||
}, 1000);
|
||||
|
||||
createPanelIfLexicalLoaded();
|
@ -2,11 +2,30 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Lexical Developer Tools</title>
|
||||
<style>
|
||||
html {
|
||||
display: flex;
|
||||
}
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
}
|
||||
#container {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="./components/main/index.tsx"></script>
|
||||
<div id="container">Unable to find Lexical on the page.</div>
|
||||
<script type="module" src="./index.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -11,9 +11,9 @@ import './index.css';
|
||||
import * as React from 'react';
|
||||
import * as ReactDOM from 'react-dom/client';
|
||||
|
||||
import App from '../App';
|
||||
import App from '../components/App';
|
||||
|
||||
ReactDOM.createRoot(document.getElementById('root') as Element).render(
|
||||
ReactDOM.createRoot(document.getElementById('container') as Element).render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
</React.StrictMode>,
|
@ -1,27 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Lexical Developer Tools</title>
|
||||
<style>
|
||||
html,
|
||||
body {
|
||||
font-size: 14px;
|
||||
font-family: sans-serif;
|
||||
width: 300px;
|
||||
}
|
||||
|
||||
body {
|
||||
padding: 8px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
<b>
|
||||
If this page uses Lexical, you can see its state tree by going to
|
||||
Developer Tools ➜ Lexical.
|
||||
</b>
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
@ -24,10 +24,9 @@ export default defineConfig({
|
||||
input: {
|
||||
background: resolve(root, 'background', 'index.ts'),
|
||||
content: resolve(root, 'content', 'index.ts'),
|
||||
devtools: resolve(root, 'devtools', 'index.html'),
|
||||
inject: resolve(root, 'inject', 'index.ts'),
|
||||
panel: resolve(root, 'panel', 'index.html'),
|
||||
popup: resolve(root, 'popup', 'index.html')
|
||||
main: resolve(root, 'main', 'index.html'),
|
||||
panel: resolve(root, 'panel', 'index.html')
|
||||
},
|
||||
output: {
|
||||
entryFileNames: (chunk) => `src/${chunk.name}/index.js`
|
||||
|