@rahmantech/litedesign 0.1.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -10
- package/dist/extension/content.js +448 -28
- package/dist/extension/core.iife.js +214 -8
- package/dist/extension/manifest.json +1 -1
- package/dist/extension/sw.js +1 -0
- package/dist/index.js +120 -15
- package/package.json +3 -2
- package/dist/extension/content.js.map +0 -7
- package/dist/extension/popup.js.map +0 -7
- package/dist/extension/sw.js.map +0 -7
- package/dist/index.js.map +0 -7
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../src/observer.ts", "../src/types.ts", "../src/store.ts", "../src/toolbar.ts", "../src/boot.ts", "../src/bridge.ts", "../src/content.ts"],
|
|
4
|
-
"sourcesContent": ["export type ObserverOptions = {\n root: Element\n debounceMs?: number\n onChange(): void\n}\n\nexport type Observer = {\n stop(): void\n}\n\nconst DEFAULT_DEBOUNCE = 150\n\n/**\n * Rebuilds after the page changes. MutationObserver catches DOM edits,\n * ResizeObserver catches layout-only changes, and window resize catches\n * viewport reflow. All paths share one debounce so a burst of mutations\n * produces one measurement pass.\n */\nexport function observeChanges(options: ObserverOptions): Observer {\n const debounceMs = options.debounceMs ?? DEFAULT_DEBOUNCE\n let timer: ReturnType<typeof setTimeout> | undefined\n\n const schedule = (): void => {\n if (timer !== undefined) clearTimeout(timer)\n\n timer = setTimeout(() => {\n timer = undefined\n options.onChange()\n }, debounceMs)\n }\n\n const mutation = new MutationObserver(schedule)\n mutation.observe(options.root, {\n childList: true,\n subtree: true,\n attributes: true,\n attributeFilter: ['class', 'style', 'hidden'],\n })\n\n const resize = new ResizeObserver(schedule)\n resize.observe(options.root)\n\n const onResize = (): void => schedule()\n const onNavigate = (): void => schedule()\n\n window.addEventListener('resize', onResize)\n window.addEventListener('popstate', onNavigate)\n window.addEventListener('hashchange', onNavigate)\n\n return {\n stop(): void {\n if (timer !== undefined) clearTimeout(timer)\n mutation.disconnect()\n resize.disconnect()\n window.removeEventListener('resize', onResize)\n window.removeEventListener('popstate', onNavigate)\n window.removeEventListener('hashchange', onNavigate)\n },\n }\n}\n", "import type { Graph, GraphNode, LayerName, OverlayHandle, OverlayOptions } from '@litedesign/core'\n\nexport type { Graph, GraphNode, LayerName, OverlayHandle }\n\n/**\n * The injected core is a global, not an import. This interface is the only\n * thing the extension knows about it; everything else comes from `core/` types\n * so there is no second source of truth.\n */\nexport type Core = {\n buildGraph(root?: Element | null): Graph\n resolveNode(graph: Graph, selector: string): GraphNode | null\n render(graph: Graph, options?: OverlayOptions): OverlayHandle\n VERSION: string\n}\n\n/** Exhaustive by construction: adding a layer to core breaks this object. */\nconst LAYER_NAMES: Record<LayerName, true> = {\n grid: true,\n guide: true,\n gap: true,\n issue: true,\n hover: true,\n}\n\nexport const LAYERS = Object.keys(LAYER_NAMES) as LayerName[]\n\n/**\n * What the popup shows: whether the worker holds a socket to the bridge, on\n * which port, and which tab it currently measures.\n */\nexport type BridgeStatus = {\n bridge: boolean\n port: number\n page: { tabId: number; url: string; title: string } | null\n}\n\n/** Layers the toolbar can switch. Hover inspection stays on. */\nexport const TOGGLE_LAYERS = [\n 'grid',\n 'guide',\n 'gap',\n 'issue',\n] as const satisfies readonly LayerName[]\nexport type ToggleLayer = (typeof TOGGLE_LAYERS)[number]\n\ndeclare global {\n interface Window {\n __litedesign?: Core\n __litedesign_state__?: 'starting' | 'active'\n __litedesign_teardown__?: () => void\n /** Replaces the visible layers without remounting. */\n __litedesign_layers__?: (layers: LayerName[]) => void\n /** True once the content-script bridge is listening. */\n __litedesign_bridge__?: boolean\n /** Toggle the user overlay. The service worker sends `toggle` here. */\n __litedesign_toggle__?: () => void\n }\n}\n", "import { LAYERS, type LayerName } from './types.js'\n\nconst STORAGE_KEY = 'litedesign'\n\nconst DEFAULT_LAYERS: LayerName[] = [...LAYERS]\n\n/**\n * What survives a reload: whether the overlay was on, and which layers it\n * showed. The fixture server reloads the page on every save, so without this\n * the grid disappears each time a file changes.\n */\nexport type OverlayState = {\n enabled: boolean\n layers: LayerName[]\n}\n\nexport function defaultLayers(): LayerName[] {\n return [...DEFAULT_LAYERS]\n}\n\nexport async function loadState(): Promise<OverlayState> {\n if (!hasStorage()) return { enabled: false, layers: defaultLayers() }\n\n try {\n const stored = await chrome.storage.local.get(STORAGE_KEY)\n return parseState(stored[STORAGE_KEY])\n } catch (error) {\n // Preferences are a convenience; a storage failure must not hide the\n // overlay. The user sees defaults instead.\n console.warn('[litedesign] could not read preferences', error)\n return { enabled: false, layers: defaultLayers() }\n }\n}\n\nexport async function saveState(state: OverlayState): Promise<void> {\n if (!hasStorage()) return\n\n try {\n await chrome.storage.local.set({ [STORAGE_KEY]: state })\n } catch (error) {\n // The current session keeps working with in-memory state.\n console.warn('[litedesign] could not save preferences', error)\n }\n}\n\nfunction hasStorage(): boolean {\n return typeof chrome !== 'undefined' && chrome.storage?.local !== undefined\n}\n\nfunction parseState(value: unknown): OverlayState {\n if (typeof value !== 'object' || value === null) {\n return { enabled: false, layers: defaultLayers() }\n }\n\n const record = value as { enabled?: unknown; layers?: unknown }\n\n return {\n // A value written before this flag existed means \"not known to be on\".\n enabled: record.enabled === true,\n layers: parseLayers(record.layers),\n }\n}\n\nfunction parseLayers(value: unknown): LayerName[] {\n if (!Array.isArray(value)) return defaultLayers()\n\n const valid = [...new Set(value.filter(isLayerName))]\n return valid.includes('hover') ? valid : [...valid, 'hover']\n}\n\nfunction isLayerName(value: unknown): value is LayerName {\n return typeof value === 'string' && (LAYERS as readonly string[]).includes(value)\n}\n", "import { type LayerName, TOGGLE_LAYERS, type ToggleLayer } from './types.js'\n\nexport type ToolbarOptions = {\n layers: LayerName[]\n onToggle(layer: ToggleLayer, enabled: boolean): void\n onClose(): void\n}\n\nexport type Toolbar = {\n element: HTMLElement\n setStats(nodes: number, issues: number, truncated: boolean): void\n setLayers(layers: LayerName[]): void\n}\n\nconst STYLE = `\n .bar {\n position: absolute;\n top: 12px;\n right: 12px;\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 6px 8px;\n border-radius: 10px;\n background: rgba(17, 17, 17, 0.94);\n box-shadow: 0 6px 20px rgba(0, 0, 0, 0.28);\n color: #e5e5e5;\n font: 12px/1 ui-monospace, SFMono-Regular, Menlo, monospace;\n pointer-events: auto;\n user-select: none;\n }\n .brand {\n padding: 0 2px;\n color: #ffffff;\n font-weight: 600;\n letter-spacing: 0.02em;\n }\n .stats {\n padding: 0 4px;\n color: #a3a3a3;\n white-space: nowrap;\n }\n button {\n padding: 5px 8px;\n border: 0;\n border-radius: 6px;\n background: #262626;\n color: #d4d4d4;\n font: inherit;\n cursor: pointer;\n }\n button:hover {\n background: #404040;\n }\n button.on {\n background: #3b82f6;\n color: #ffffff;\n }\n button[data-action='close'] {\n padding: 5px 7px;\n background: transparent;\n color: #a3a3a3;\n font-size: 14px;\n }\n button[data-action='close']:hover {\n background: #404040;\n color: #ffffff;\n }\n`\n\nexport function createToolbar(options: ToolbarOptions): Toolbar {\n const root = document.createElement('div')\n root.dataset.litedesignUi = ''\n\n const style = document.createElement('style')\n style.textContent = STYLE\n\n const bar = document.createElement('div')\n bar.className = 'bar'\n\n const brand = document.createElement('span')\n brand.className = 'brand'\n brand.textContent = 'litedesign'\n\n const stats = document.createElement('span')\n stats.className = 'stats'\n stats.textContent = 'measuring\u2026'\n\n let enabled = new Set<LayerName>(options.layers)\n const isEnabled = (layer: ToggleLayer): boolean => enabled.has(layer)\n\n bar.append(brand, stats)\n\n const buttons = new Map<ToggleLayer, HTMLButtonElement>()\n\n for (const layer of TOGGLE_LAYERS) {\n const button = document.createElement('button')\n button.type = 'button'\n button.dataset.layer = layer\n button.textContent = layer\n button.title = `Toggle ${layer} layer`\n button.addEventListener('click', () => options.onToggle(layer, !isEnabled(layer)))\n\n buttons.set(layer, button)\n bar.append(button)\n }\n\n const close = document.createElement('button')\n close.type = 'button'\n close.dataset.action = 'close'\n close.textContent = '\u00D7'\n close.title = 'Close litedesign'\n close.setAttribute('aria-label', 'Close litedesign')\n close.addEventListener('click', () => options.onClose())\n\n bar.append(close)\n root.append(style, bar)\n\n const setLayers = (layers: LayerName[]): void => {\n enabled = new Set(layers)\n\n for (const [layer, button] of buttons) {\n const on = enabled.has(layer)\n button.setAttribute('aria-pressed', String(on))\n button.classList.toggle('on', on)\n }\n }\n\n setLayers(options.layers)\n\n return {\n element: root,\n setStats(nodes: number, issues: number, truncated: boolean): void {\n stats.textContent = `${nodes} nodes \u00B7 ${issues} issues${truncated ? ' \u00B7 truncated' : ''}`\n },\n setLayers,\n }\n}\n", "import { type Observer, observeChanges } from './observer.js'\nimport { loadState, saveState } from './store.js'\nimport { createToolbar, type Toolbar } from './toolbar.js'\nimport type { Core, Graph, LayerName, OverlayHandle } from './types.js'\n\n/**\n * The host of the overlay that is on screen. The bridge hides it during a\n * capture instead of tearing it down, so the user's layer selection survives\n * the screenshot.\n */\nlet mountedHost: HTMLElement | undefined\n\nexport function overlayHost(): HTMLElement | undefined {\n return mountedHost\n}\n\n/**\n * Idempotent entry point. Chrome may inject the bundle again into the same tab,\n * and a fresh module scope forgets everything, so the state lives on `window`.\n *\n * - idle: mount\n * - starting: ignore the click, storage is still reading\n * - active: tear down\n */\nexport async function boot(core: Core): Promise<void> {\n if (window.__litedesign_state__ === 'active') {\n window.__litedesign_teardown__?.()\n return\n }\n\n if (window.__litedesign_state__ === 'starting') return\n\n window.__litedesign_state__ = 'starting'\n\n const state = await loadState()\n\n // The user may have clicked again while storage was reading.\n if (window.__litedesign_state__ !== 'starting') return\n\n try {\n mount(core, state.layers)\n } catch (error) {\n window.__litedesign_state__ = undefined\n throw error\n }\n}\n\n/**\n * MCP control. `boot` toggles because a click means \"flip it\"; this sets the\n * state, because the agent says exactly what it wants to see.\n *\n * Returns what the page is actually showing afterwards, so the tool can report\n * a reading instead of echoing the request.\n */\nexport async function applyOverlay(\n core: Core,\n options: { enabled: boolean; layers?: LayerName[] },\n): Promise<{ enabled: boolean; layers: LayerName[] }> {\n if (!options.enabled) {\n window.__litedesign_teardown__?.()\n return { enabled: false, layers: [] }\n }\n\n const layers = options.layers ?? (await loadState()).layers\n\n if (window.__litedesign_state__ === 'active') {\n window.__litedesign_layers__?.(layers)\n return { enabled: true, layers }\n }\n\n window.__litedesign_state__ = 'starting'\n\n try {\n mount(core, layers)\n } catch (error) {\n window.__litedesign_state__ = undefined\n throw error\n }\n\n return { enabled: overlayHost() !== undefined, layers }\n}\n\nfunction mount(core: Core, initial: LayerName[]): void {\n // `document.body` can be missing if the user clicks before parsing finishes;\n // documentElement is always there and catches the body once it appears.\n const root = document.body ?? document.documentElement\n\n let layers = initial\n let graph: Graph = core.buildGraph(root)\n let handle: OverlayHandle | undefined\n let observer: Observer | undefined\n let toolbar: Toolbar\n\n const teardown = (): void => {\n observer?.stop()\n handle?.destroy()\n handle = undefined\n observer = undefined\n mountedHost = undefined\n window.__litedesign_teardown__ = undefined\n window.__litedesign_layers__ = undefined\n window.__litedesign_state__ = undefined\n void saveState({ enabled: false, layers })\n }\n\n const refresh = (): void => {\n graph = core.buildGraph(root)\n handle?.update(graph)\n toolbar.setStats(graph.nodes.length, graph.issues.length, graph.truncated)\n }\n\n const setLayers = (next: LayerName[]): void => {\n layers = next\n handle?.setLayers(layers)\n toolbar.setLayers(layers)\n void saveState({ enabled: true, layers })\n }\n\n toolbar = createToolbar({\n layers,\n onToggle(layer, enabled): void {\n setLayers(\n enabled ? [...new Set([...layers, layer])] : layers.filter((name) => name !== layer),\n )\n },\n onClose: teardown,\n })\n\n toolbar.setStats(graph.nodes.length, graph.issues.length, graph.truncated)\n\n handle = core.render(graph, { layers, toolbar: toolbar.element })\n observer = observeChanges({ root, onChange: refresh })\n\n mountedHost = handle.host\n window.__litedesign_layers__ = setLayers\n window.__litedesign_teardown__ = teardown\n window.__litedesign_state__ = 'active'\n\n // A reload has to know the overlay was on, otherwise the next page load\n // starts clean and the grid vanishes on every save.\n void saveState({ enabled: true, layers })\n}\n", "import { applyOverlay, boot, overlayHost } from './boot.js'\nimport type { Core, Graph, LayerName, OverlayHandle } from './types.js'\n\ntype Payload = {\n selector?: string\n rules?: string[]\n layers?: LayerName[]\n fullPage?: boolean\n enabled?: boolean\n}\n\ntype Rect = { x: number; y: number; w: number; h: number }\n\ntype Prepared = { rect?: Rect; dpr: number }\n\ntype Response = { ok: true; result?: unknown } | { ok: false; error: string }\n\nconst TEMP_LAYERS: LayerName[] = ['grid', 'guide', 'gap', 'issue']\n\nconst FULL_PAGE =\n 'full-page screenshots need the Playwright driver. The extension can only capture the visible viewport.'\n\ntype Hidden = { element: HTMLElement; display: string }\n\n/**\n * While a capture is running the user's overlay is hidden. `handle` is the\n * temporary overlay for an annotated capture; `hidden` is the user's overlay\n * with the display value it had before the capture.\n */\nlet suspended: { handle?: OverlayHandle; hidden?: Hidden } | undefined\n\n/**\n * Installs the content-script side of the bridge. Safe to call on every\n * injection: Chrome may inject the bundle more than once into the same tab.\n */\nexport function installBridge(core: Core): void {\n window.__litedesign_toggle__ = () => void boot(core)\n\n if (window.__litedesign_bridge__ === true) return\n window.__litedesign_bridge__ = true\n\n // Tests inject this bundle into a normal page, where `chrome` does not exist.\n if (typeof chrome === 'undefined' || chrome.runtime?.onMessage === undefined) return\n\n chrome.runtime.onMessage.addListener((message: unknown, _sender, sendResponse) => {\n void respond(core, message).then(sendResponse, (error: unknown) => {\n sendResponse({ ok: false, error: messageOf(error) })\n })\n\n // Keep the channel open for the async response.\n return true\n })\n}\n\nasync function respond(core: Core, raw: unknown): Promise<Response> {\n if (typeof raw !== 'object' || raw === null) throw new Error('invalid message')\n\n const message = raw as { action?: unknown; payload?: unknown }\n const action = typeof message.action === 'string' ? message.action : ''\n const payload = (message.payload ?? {}) as Payload\n\n switch (action) {\n case 'ping':\n return { ok: true, result: true }\n case 'toggle':\n await boot(core)\n return { ok: true }\n case 'overlay':\n return {\n ok: true,\n result: await applyOverlay(core, {\n enabled: payload.enabled !== false,\n layers: payload.layers,\n }),\n }\n case 'graph':\n return { ok: true, result: core.buildGraph(root()) }\n case 'measure':\n return { ok: true, result: measure(core, payload.selector) }\n case 'screenshot_prepare':\n return { ok: true, result: await prepareScreenshot(core, payload) }\n case 'annotate_prepare':\n return { ok: true, result: await prepareAnnotation(core, payload) }\n case 'capture_cleanup':\n await cleanup()\n return { ok: true }\n default:\n throw new Error(`unknown action: ${action || '(none)'}`)\n }\n}\n\nfunction measure(core: Core, selector: string | undefined): unknown {\n if (selector === undefined || selector === '') throw new Error('measure needs a selector')\n\n const graph = core.buildGraph(root())\n const node = core.resolveNode(graph, selector)\n if (node === null) throw new Error(`selector \"${selector}\" did not match any element on the page`)\n\n const gaps = graph.gaps\n .filter((gap) => gap.a === node.id || gap.b === node.id)\n .map((gap) => ({\n to: gap.a === node.id ? gap.b : gap.a,\n axis: gap.axis,\n distance: gap.distance,\n kind: gap.kind,\n }))\n\n return {\n id: node.id,\n selector: node.selector,\n tag: node.tag,\n rect: node.rect,\n styles: node.styles,\n gaps,\n }\n}\n\nasync function prepareScreenshot(core: Core, payload: Payload): Promise<Prepared> {\n if (payload.fullPage === true) throw new Error(FULL_PAGE)\n\n const dpr = window.devicePixelRatio || 1\n const rect = await prepareElement(core, payload.selector)\n suspend()\n\n return { rect, dpr }\n}\n\nasync function prepareAnnotation(core: Core, payload: Payload): Promise<Prepared> {\n if (payload.fullPage === true) throw new Error(FULL_PAGE)\n\n const dpr = window.devicePixelRatio || 1\n const rect = await prepareElement(core, payload.selector)\n\n let graph: Graph = core.buildGraph(root())\n if (payload.rules !== undefined && payload.rules.length > 0) {\n const wanted = new Set(payload.rules)\n graph = { ...graph, issues: graph.issues.filter((issue) => wanted.has(issue.rule)) }\n }\n\n const handle = core.render(graph, { layers: payload.layers ?? TEMP_LAYERS })\n suspend(handle)\n await frames(2)\n\n return { rect, dpr }\n}\n\nasync function cleanup(): Promise<void> {\n const current = suspended\n if (current === undefined) return\n\n suspended = undefined\n current.handle?.destroy()\n showUserOverlay(current.hidden)\n}\n\n/**\n * Hides the user's overlay for the duration of a capture. It used to tear the\n * overlay down and remount it afterwards from storage, which meant a capture\n * could bring back a different layer selection than the one on screen. A\n * previous capture's overlay is replaced, not stacked.\n */\nfunction suspend(handle?: OverlayHandle): void {\n const previous = suspended\n previous?.handle?.destroy()\n showUserOverlay(previous?.hidden)\n\n suspended =\n handle === undefined ? { hidden: hideUserOverlay() } : { handle, hidden: hideUserOverlay() }\n}\n\nfunction hideUserOverlay(): Hidden | undefined {\n const element = overlayHost()\n if (element === undefined) return undefined\n\n const display = element.style.display\n element.style.display = 'none'\n\n return { element, display }\n}\n\nfunction showUserOverlay(hidden: Hidden | undefined): void {\n if (hidden !== undefined) hidden.element.style.display = hidden.display\n}\n\nasync function prepareElement(core: Core, selector: string | undefined): Promise<Rect | undefined> {\n if (selector === undefined || selector === '') return undefined\n\n const graph = core.buildGraph(root())\n const node = core.resolveNode(graph, selector)\n if (node === null) throw new Error(`selector \"${selector}\" did not match any element on the page`)\n\n let element: Element | null = null\n\n try {\n element = document.querySelector(node.selector)\n } catch {\n element = null\n }\n\n if (element === null) {\n throw new Error(\n `selector \"${selector}\" is inside a shadow root. Measure it, but capture the whole viewport instead.`,\n )\n }\n\n element.scrollIntoView({ block: 'center', inline: 'center' })\n await frames(1)\n\n const rect = element.getBoundingClientRect()\n return { x: rect.left, y: rect.top, w: rect.width, h: rect.height }\n}\n\nfunction root(): Element {\n return document.body ?? document.documentElement\n}\n\nfunction frames(count: number): Promise<void> {\n return new Promise((resolve) => {\n let left = count\n\n const tick = (): void => {\n left -= 1\n if (left <= 0) resolve()\n else requestAnimationFrame(tick)\n }\n\n requestAnimationFrame(tick)\n })\n}\n\nfunction messageOf(error: unknown): string {\n if (error instanceof Error && error.message !== '') return error.message\n if (typeof error === 'string' && error !== '') return error\n return 'unexpected error'\n}\n", "import { applyOverlay } from './boot.js'\nimport { installBridge } from './bridge.js'\nimport { loadState } from './store.js'\nimport type { Core } from './types.js'\n\nconst core: Core | undefined = window.__litedesign\n\nif (core === undefined) {\n console.warn('[litedesign] core not found; core.iife.js must be injected first')\n} else {\n installBridge(core)\n void restore(core)\n}\n\n/**\n * A reload drops the overlay with the rest of the DOM. If it was on when the\n * page went away, put it back, so a hot reload during an edit does not\n * silently clear the grid.\n */\nasync function restore(core: Core): Promise<void> {\n const state = await loadState()\n if (!state.enabled) return\n\n await applyOverlay(core, { enabled: true, layers: state.layers })\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
6
|
-
"names": ["root", "core", "root", "core", "core"]
|
|
7
|
-
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../src/popup.ts"],
|
|
4
|
-
"sourcesContent": ["import type { BridgeStatus } from './types.js'\n\n/**\n * The popup next to the icon. It shows what the worker holds and lets the\n * human bind the tab on purpose: \"watch this tab\" is the only way the agent\n * learns which page to measure without asking.\n */\n\ntype Reply<T> = { ok: true; result: T } | { ok: false; error: string }\n\nconst pageLine = element<HTMLParagraphElement>('page')\nconst statusLine = element<HTMLParagraphElement>('status')\nconst watchButton = element<HTMLButtonElement>('watch')\nconst overlayButton = element<HTMLButtonElement>('overlay')\n\nlet tab: chrome.tabs.Tab | undefined\n\nwatchButton.addEventListener('click', () => void watchThisTab())\noverlayButton.addEventListener('click', () => void toggleOverlay())\nvoid init()\n\nasync function init(): Promise<void> {\n const [active] = await chrome.tabs.query({ active: true, currentWindow: true })\n tab = active\n\n pageLine.textContent = active?.url ?? 'no active tab'\n watchButton.disabled = active?.id === undefined\n overlayButton.disabled = active?.id === undefined\n\n await refresh()\n}\n\nasync function watchThisTab(): Promise<void> {\n if (tab?.id === undefined) return\n\n const reply = await ask<{ url: string }>({ type: 'watch', tabId: tab.id })\n\n if (!reply.ok) {\n setStatus(reply.error, true)\n return\n }\n\n pageLine.textContent = reply.result.url\n await refresh()\n}\n\nasync function toggleOverlay(): Promise<void> {\n if (tab?.id === undefined) return\n\n const reply = await ask<unknown>({ type: 'overlay', tabId: tab.id })\n\n if (!reply.ok) {\n setStatus(reply.error, true)\n return\n }\n\n // The toolbar is on the page now; get out of its way.\n window.close()\n}\n\nasync function refresh(): Promise<void> {\n const reply = await ask<BridgeStatus>({ type: 'status' })\n\n if (!reply.ok) {\n setStatus(reply.error, true)\n return\n }\n\n const status = reply.result\n const watching = tab?.id !== undefined && status.page?.tabId === tab.id\n\n watchButton.textContent = watching ? 'Watching this tab' : 'Watch this tab'\n watchButton.disabled = tab?.id === undefined || watching\n\n setStatus(\n status.bridge\n ? `bridge online \u00B7 port ${status.port}`\n : 'bridge offline \u00B7 run: litedesign ws start',\n !status.bridge,\n )\n}\n\nfunction setStatus(text: string, bad = false): void {\n statusLine.textContent = text\n statusLine.classList.toggle('bad', bad)\n}\n\nasync function ask<T>(message: unknown): Promise<Reply<T>> {\n try {\n return (await chrome.runtime.sendMessage(message)) as Reply<T>\n } catch (error) {\n return { ok: false, error: error instanceof Error ? error.message : String(error) }\n }\n}\n\nfunction element<T extends HTMLElement>(id: string): T {\n const found = document.getElementById(id)\n if (found === null) throw new Error(`missing element: ${id}`)\n return found as T\n}\n"],
|
|
5
|
-
"mappings": ";;;AAUA,MAAM,WAAW,QAA8B,MAAM;AACrD,MAAM,aAAa,QAA8B,QAAQ;AACzD,MAAM,cAAc,QAA2B,OAAO;AACtD,MAAM,gBAAgB,QAA2B,SAAS;AAE1D,MAAI;AAEJ,cAAY,iBAAiB,SAAS,MAAM,KAAK,aAAa,CAAC;AAC/D,gBAAc,iBAAiB,SAAS,MAAM,KAAK,cAAc,CAAC;AAClE,OAAK,KAAK;AAEV,iBAAe,OAAsB;AACnC,UAAM,CAAC,MAAM,IAAI,MAAM,OAAO,KAAK,MAAM,EAAE,QAAQ,MAAM,eAAe,KAAK,CAAC;AAC9E,UAAM;AAEN,aAAS,cAAc,QAAQ,OAAO;AACtC,gBAAY,WAAW,QAAQ,OAAO;AACtC,kBAAc,WAAW,QAAQ,OAAO;AAExC,UAAM,QAAQ;AAAA,EAChB;AAEA,iBAAe,eAA8B;AAC3C,QAAI,KAAK,OAAO,OAAW;AAE3B,UAAM,QAAQ,MAAM,IAAqB,EAAE,MAAM,SAAS,OAAO,IAAI,GAAG,CAAC;AAEzE,QAAI,CAAC,MAAM,IAAI;AACb,gBAAU,MAAM,OAAO,IAAI;AAC3B;AAAA,IACF;AAEA,aAAS,cAAc,MAAM,OAAO;AACpC,UAAM,QAAQ;AAAA,EAChB;AAEA,iBAAe,gBAA+B;AAC5C,QAAI,KAAK,OAAO,OAAW;AAE3B,UAAM,QAAQ,MAAM,IAAa,EAAE,MAAM,WAAW,OAAO,IAAI,GAAG,CAAC;AAEnE,QAAI,CAAC,MAAM,IAAI;AACb,gBAAU,MAAM,OAAO,IAAI;AAC3B;AAAA,IACF;AAGA,WAAO,MAAM;AAAA,EACf;AAEA,iBAAe,UAAyB;AACtC,UAAM,QAAQ,MAAM,IAAkB,EAAE,MAAM,SAAS,CAAC;AAExD,QAAI,CAAC,MAAM,IAAI;AACb,gBAAU,MAAM,OAAO,IAAI;AAC3B;AAAA,IACF;AAEA,UAAM,SAAS,MAAM;AACrB,UAAM,WAAW,KAAK,OAAO,UAAa,OAAO,MAAM,UAAU,IAAI;AAErE,gBAAY,cAAc,WAAW,sBAAsB;AAC3D,gBAAY,WAAW,KAAK,OAAO,UAAa;AAEhD;AAAA,MACE,OAAO,SACH,2BAAwB,OAAO,IAAI,KACnC;AAAA,MACJ,CAAC,OAAO;AAAA,IACV;AAAA,EACF;AAEA,WAAS,UAAU,MAAc,MAAM,OAAa;AAClD,eAAW,cAAc;AACzB,eAAW,UAAU,OAAO,OAAO,GAAG;AAAA,EACxC;AAEA,iBAAe,IAAO,SAAqC;AACzD,QAAI;AACF,aAAQ,MAAM,OAAO,QAAQ,YAAY,OAAO;AAAA,IAClD,SAAS,OAAO;AACd,aAAO,EAAE,IAAI,OAAO,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,EAAE;AAAA,IACpF;AAAA,EACF;AAEA,WAAS,QAA+B,IAAe;AACrD,UAAM,QAAQ,SAAS,eAAe,EAAE;AACxC,QAAI,UAAU,KAAM,OAAM,IAAI,MAAM,oBAAoB,EAAE,EAAE;AAC5D,WAAO;AAAA,EACT;",
|
|
6
|
-
"names": []
|
|
7
|
-
}
|
package/dist/extension/sw.js.map
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../src/backoff.ts", "../src/sw.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Reconnect policy for the service-worker socket. Pure on purpose: the worker\n * itself cannot be unit tested, but the numbers behind it can.\n *\n * The socket is also the control channel: the MCP server asks the worker to\n * attach to the active tab over it. So the worker never stops trying. It backs\n * off instead, and switching tabs retries immediately.\n */\n\nexport const RECONNECT_MIN = 1000\nexport const RECONNECT_MAX = 30000\n\n/** Failures before the icon admits the hub is not there. */\nexport const OFFLINE_AFTER = 3\n\n/** Delay before the next attempt. `failures` counts the attempts already made. */\nexport function backoffDelay(failures: number): number {\n const step = Math.max(failures - 1, 0)\n return Math.min(RECONNECT_MIN * 2 ** step, RECONNECT_MAX)\n}\n", "import { backoffDelay, OFFLINE_AFTER } from './backoff.js'\nimport type { BridgeStatus } from './types.js'\n\n/**\n * The service worker owns the WebSocket to the MCP hub, forwards measurement\n * requests to the content script, and captures the visible tab.\n *\n * MV3 kills the worker when it goes idle. A WebSocket with traffic every 20s\n * keeps it alive; if Chrome still stops it, module init reconnects and restores\n * the last page from `chrome.storage.session`.\n *\n * The socket is also the control channel. The MCP server asks the worker to\n * attach to whatever tab the human is looking at, so nobody has to click the\n * icon before an agent can measure. That is why the worker keeps retrying: a\n * stopped socket would make the extension unreachable until a click.\n */\n\nconst DEFAULT_PORT = 8787\nconst PING_INTERVAL = 20000\nconst SESSION_KEY = 'page'\nconst ONLINE_TITLE = 'litedesign: watch this tab'\n\ntype Page = { tabId: number; url: string; title: string }\n\ntype BridgeResult = { ok: true; result?: unknown } | { ok: false; error: string }\n\ntype Prepared = { rect?: Rect; dpr?: number }\n\ntype Rect = { x: number; y: number; w: number; h: number }\n\nlet socket: WebSocket | undefined\nlet current: Page | undefined\nlet port = DEFAULT_PORT\nlet failures = 0\nlet offline = false\nlet reconnectTimer: ReturnType<typeof setTimeout> | undefined\nlet pingTimer: ReturnType<typeof setInterval> | undefined\n\nconst ready = start()\n\nasync function start(): Promise<void> {\n port = await readPort()\n current = await readPage()\n\n connect()\n}\n\n/**\n * The human is looking at a tab, which is the moment the hub may want to\n * attach. Retrying here means the first `inspect` after starting the server\n * does not wait for the backoff timer.\n */\nfunction wake(): void {\n retry()\n}\n\nchrome.tabs.onActivated.addListener(wake)\nchrome.windows.onFocusChanged.addListener(wake)\n\n/**\n * The popup asks the worker what it holds and for explicit actions. The icon\n * opens that menu instead of toggling the overlay, so binding a tab is the\n * human's decision rather than a side effect of a click.\n */\nchrome.runtime.onMessage.addListener((message, _sender, respond) => {\n void answer(message).then(respond, (error: unknown) => {\n respond({ ok: false, error: messageOf(error) })\n })\n\n // Keeps the channel open for the async answer.\n return true\n})\n\nasync function answer(message: unknown): Promise<BridgeResult> {\n await ready\n\n if (typeof message !== 'object' || message === null) throw new Error('invalid message')\n\n const request = message as { type?: unknown; tabId?: unknown }\n\n switch (request.type) {\n case 'status':\n return { ok: true, result: status() }\n case 'watch':\n if (typeof request.tabId !== 'number') throw new Error('watch needs a tab id')\n return { ok: true, result: await watch(request.tabId) }\n case 'overlay':\n if (typeof request.tabId !== 'number') throw new Error('overlay needs a tab id')\n return { ok: true, result: await toggleOverlay(request.tabId) }\n default:\n throw new Error(`unknown message: ${String(request.type)}`)\n }\n}\n\nfunction status(): BridgeStatus {\n return {\n bridge: socket?.readyState === WebSocket.OPEN,\n port,\n page: current ?? null,\n }\n}\n\n/**\n * Binds the worker to one tab on purpose. This is the same state `attach`\n * produces for the agent, but started by the human from the popup.\n */\nasync function watch(tabId: number): Promise<Page> {\n const tab = await chrome.tabs.get(tabId)\n\n if (!isSupported(tab.url)) {\n throw new Error('this tab is not an http(s) page. Open the page you want to measure.')\n }\n\n const page: Page = { tabId, url: tab.url ?? '', title: tab.title ?? '' }\n\n await ensureContent(tabId)\n\n current = page\n await savePage(page)\n send({ type: 'hello', ...page, version: chrome.runtime.getManifest().version })\n retry()\n await applyStatus(tabId)\n\n return page\n}\n\n/** The overlay belongs to the watched tab, so watch it first when needed. */\nasync function toggleOverlay(tabId: number): Promise<unknown> {\n if (current?.tabId !== tabId) await watch(tabId)\n return toTab('toggle')\n}\n\nchrome.tabs.onRemoved.addListener((tabId) => {\n if (current?.tabId !== tabId) return\n\n current = undefined\n void savePage(undefined)\n send({ type: 'bye' })\n\n // Keep the socket: the hub may still ask this worker to attach to another tab.\n})\n\n/**\n * A reload drops the content script, and with it the overlay. Re-inject once\n * the watched tab has finished loading; the content script restores the\n * overlay from storage, so saving a file during an edit keeps the grid up.\n */\nchrome.tabs.onUpdated.addListener((tabId, info) => {\n if (info.status !== 'complete' || current?.tabId !== tabId) return\n\n void ensureContent(tabId).catch(() => undefined)\n})\n\nfunction connect(): void {\n if (socket !== undefined && socket.readyState <= WebSocket.OPEN) return\n\n const next = new WebSocket(`ws://127.0.0.1:${port}`)\n socket = next\n\n next.addEventListener('open', () => {\n failures = 0\n void setOnline()\n\n if (current !== undefined) {\n send({ type: 'hello', ...current, version: chrome.runtime.getManifest().version })\n }\n\n startPing()\n })\n\n next.addEventListener('message', (event) => {\n void onMessage(event.data)\n })\n\n next.addEventListener('close', () => {\n if (socket === next) socket = undefined\n\n stopPing()\n failures += 1\n\n // Only complain when there is a page waiting on the bridge.\n if (current !== undefined && failures >= OFFLINE_AFTER) void setOffline()\n\n scheduleReconnect()\n })\n\n next.addEventListener('error', () => {\n // `close` follows; reconnection is handled there.\n })\n}\n\n/** The icon click means the user expects a server now, so start a fresh run. */\nfunction retry(): void {\n failures = 0\n\n if (reconnectTimer !== undefined) {\n clearTimeout(reconnectTimer)\n reconnectTimer = undefined\n }\n\n connect()\n}\n\nfunction scheduleReconnect(): void {\n if (reconnectTimer !== undefined) return\n\n const delay = backoffDelay(failures) + Math.round(Math.random() * 250)\n reconnectTimer = setTimeout(() => {\n reconnectTimer = undefined\n connect()\n }, delay)\n}\n\n/**\n * Says the bridge is down once, then keeps retrying quietly. The browser logs\n * each failed attempt itself, so one clear line beats one per attempt.\n */\nasync function setOffline(): Promise<void> {\n const first = !offline\n offline = true\n\n if (first) {\n console.info(\n `[litedesign] no MCP server on ws://127.0.0.1:${port} after ${OFFLINE_AFTER} attempts. ` +\n 'Start \"litedesign mcp\"; the bridge reconnects by itself.',\n )\n }\n\n await applyStatus()\n}\n\nasync function setOnline(): Promise<void> {\n if (!offline) return\n\n offline = false\n await applyStatus()\n}\n\nfunction offlineTitle(): string {\n return `litedesign: no MCP server on port ${port} \u2014 start \"litedesign mcp\"`\n}\n\n/** Global for new tabs, per-tab for the tab we care about. */\nasync function applyStatus(tabId: number | undefined = current?.tabId): Promise<void> {\n const badge = offline ? '!' : ''\n const title = offline ? offlineTitle() : ONLINE_TITLE\n\n try {\n await chrome.action.setBadgeText({ text: badge })\n await chrome.action.setBadgeBackgroundColor({ color: '#dc2626' })\n await chrome.action.setTitle({ title })\n\n if (tabId !== undefined) {\n await chrome.action.setBadgeText({ tabId, text: badge })\n await chrome.action.setTitle({ tabId, title })\n }\n } catch {\n // The action API is unavailable while Chrome shuts down.\n }\n}\n\nfunction startPing(): void {\n stopPing()\n pingTimer = setInterval(() => send({ type: 'ping' }), PING_INTERVAL)\n}\n\nfunction stopPing(): void {\n if (pingTimer !== undefined) clearInterval(pingTimer)\n pingTimer = undefined\n}\n\nfunction send(message: unknown): void {\n if (socket?.readyState !== WebSocket.OPEN) return\n socket.send(JSON.stringify(message))\n}\n\nasync function onMessage(raw: unknown): Promise<void> {\n let message: Record<string, unknown>\n\n try {\n const parsed: unknown = JSON.parse(String(raw))\n if (typeof parsed !== 'object' || parsed === null) return\n message = parsed as Record<string, unknown>\n } catch {\n return\n }\n\n if (message.type === 'ping') {\n send({ type: 'pong' })\n return\n }\n\n if (\n message.type === 'request' &&\n typeof message.id === 'string' &&\n typeof message.action === 'string'\n ) {\n await handleRequest(message.id, message.action, message.payload)\n }\n}\n\nasync function handleRequest(id: string, action: string, payload: unknown): Promise<void> {\n try {\n const result = await route(action, payload)\n send({ type: 'response', id, ok: true, result })\n } catch (error) {\n send({ type: 'response', id, ok: false, error: messageOf(error) })\n }\n}\n\nasync function route(action: string, payload: unknown): Promise<unknown> {\n switch (action) {\n case 'attach':\n return attach()\n case 'graph':\n case 'measure':\n case 'overlay':\n return toTab(action, payload)\n case 'screenshot':\n return capture('screenshot_prepare', payload)\n case 'annotate':\n return capture('annotate_prepare', payload)\n default:\n throw new Error(`unknown action: ${action}`)\n }\n}\n\n/**\n * Binds to the tab the human is looking at. This is what removes the click:\n * the server asks for a page, the worker picks the active one and announces it.\n */\nasync function attach(): Promise<Page> {\n const [tab] = await chrome.tabs.query({ active: true, lastFocusedWindow: true })\n\n if (tab?.id === undefined || !isSupported(tab.url)) {\n throw new Error(\n 'the active tab is not an http(s) page. Switch to the page you want to measure.',\n )\n }\n\n const page: Page = { tabId: tab.id, url: tab.url ?? '', title: tab.title ?? '' }\n\n await ensureContent(page.tabId)\n\n current = page\n await savePage(page)\n send({ type: 'hello', ...page, version: chrome.runtime.getManifest().version })\n\n return page\n}\n\nasync function capture(\n action: 'screenshot_prepare' | 'annotate_prepare',\n payload: unknown,\n): Promise<{ data: string }> {\n const page = requirePage()\n const tab = await chrome.tabs.get(page.tabId)\n\n if (tab.active !== true) {\n throw new Error('the page is not the active tab. Switch to it and try again.')\n }\n\n if (tab.windowId === undefined) throw new Error('the page has no window.')\n\n const prepared = await toTabRaw(action, payload)\n if (!prepared.ok) throw new Error(prepared.error)\n\n const prep = (prepared.result ?? {}) as Prepared\n\n try {\n const dataUrl = await chrome.tabs.captureVisibleTab(tab.windowId, { format: 'png' })\n const base64 = dataUrl.slice(dataUrl.indexOf(',') + 1)\n\n return {\n data: prep.rect === undefined ? base64 : await crop(base64, prep.rect, prep.dpr ?? 1),\n }\n } finally {\n await toTabRaw('capture_cleanup').catch(() => undefined)\n }\n}\n\nasync function toTab(action: string, payload?: unknown): Promise<unknown> {\n const response = await toTabRaw(action, payload)\n if (!response.ok) throw new Error(response.error)\n return response.result\n}\n\nasync function toTabRaw(action: string, payload?: unknown): Promise<BridgeResult> {\n const page = requirePage()\n await ensureContent(page.tabId)\n\n const response = (await chrome.tabs.sendMessage(page.tabId, { action, payload })) as\n | BridgeResult\n | undefined\n\n if (response === undefined) throw new Error('the content script did not respond')\n return response\n}\n\n/**\n * Injects on demand. The icon click is the usual path, but a reconnecting\n * service worker may ask for a measurement after the content script is gone.\n */\nasync function ensureContent(tabId: number): Promise<void> {\n try {\n const response = (await chrome.tabs.sendMessage(tabId, { action: 'ping' })) as\n | BridgeResult\n | undefined\n\n if (response?.ok === true) return\n } catch {\n // No receiver yet; fall through to injection.\n }\n\n await chrome.scripting.executeScript({\n target: { tabId },\n files: ['core.iife.js', 'content.js'],\n })\n}\n\nasync function crop(base64: string, rect: Rect, dpr: number): Promise<string> {\n const bitmap = await createImageBitmap(toBlob(base64))\n const x = clamp(Math.round(rect.x * dpr), 0, bitmap.width)\n const y = clamp(Math.round(rect.y * dpr), 0, bitmap.height)\n const w = clamp(Math.round(rect.w * dpr), 1, bitmap.width - x)\n const h = clamp(Math.round(rect.h * dpr), 1, bitmap.height - y)\n\n if (w <= 0 || h <= 0) throw new Error('the element is outside the visible viewport.')\n\n const canvas = new OffscreenCanvas(w, h)\n const context = canvas.getContext('2d')\n if (context === null) throw new Error('could not create an offscreen canvas.')\n\n context.drawImage(bitmap, x, y, w, h, 0, 0, w, h)\n bitmap.close()\n\n const blob = await canvas.convertToBlob({ type: 'png' })\n return toBase64(await blob.arrayBuffer())\n}\n\nfunction toBlob(base64: string): Blob {\n const binary = atob(base64)\n const bytes = new Uint8Array(binary.length)\n\n for (let i = 0; i < binary.length; i += 1) bytes[i] = binary.charCodeAt(i)\n\n return new Blob([bytes], { type: 'image/png' })\n}\n\nfunction toBase64(buffer: ArrayBuffer): string {\n const bytes = new Uint8Array(buffer)\n const chunk = 0x8000\n let binary = ''\n\n for (let i = 0; i < bytes.length; i += chunk) {\n binary += String.fromCharCode(...bytes.subarray(i, i + chunk))\n }\n\n return btoa(binary)\n}\n\nfunction requirePage(): Page {\n if (current === undefined) {\n throw new Error('no page is attached. Call inspect first so the extension can attach to it.')\n }\n\n return current\n}\n\nfunction isSupported(url: string | undefined): boolean {\n return url !== undefined && /^(https?|file):/.test(url)\n}\n\nfunction clamp(value: number, min: number, max: number): number {\n return Math.min(Math.max(value, min), max)\n}\n\nfunction messageOf(error: unknown): string {\n if (error instanceof Error && error.message !== '') return error.message\n if (typeof error === 'string' && error !== '') return error\n return 'unexpected error'\n}\n\nasync function readPort(): Promise<number> {\n try {\n const response = await fetch(chrome.runtime.getURL('config.json'), { cache: 'no-store' })\n if (!response.ok) return DEFAULT_PORT\n\n const config = (await response.json()) as { port?: unknown }\n const value = config.port\n\n return typeof value === 'number' && Number.isInteger(value) && value > 0 && value < 65536\n ? value\n : DEFAULT_PORT\n } catch {\n return DEFAULT_PORT\n }\n}\n\nasync function readPage(): Promise<Page | undefined> {\n try {\n const stored = await chrome.storage.session.get(SESSION_KEY)\n const value = stored[SESSION_KEY] as Page | undefined\n\n return value !== undefined && typeof value.tabId === 'number' && typeof value.url === 'string'\n ? value\n : undefined\n } catch {\n return undefined\n }\n}\n\nasync function savePage(page: Page | undefined): Promise<void> {\n try {\n if (page === undefined) await chrome.storage.session.remove(SESSION_KEY)\n else await chrome.storage.session.set({ [SESSION_KEY]: page })\n } catch {\n // The connection still works without the restore hint.\n }\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
6
|
-
"names": ["failures"]
|
|
7
|
-
}
|