@shadow-js/devtools 0.0.0-stage → 1.0.0
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/CHANGELOG.md +15 -0
- package/LICENSE +21 -0
- package/README.md +576 -2
- package/dist/dev/agent.d.ts +239 -0
- package/dist/dev/agent.js +626 -0
- package/dist/dev/agent.js.map +1 -0
- package/dist/dev/backend.d.ts +53 -0
- package/dist/dev/backend.js +53 -0
- package/dist/dev/backend.js.map +1 -0
- package/dist/dev/hook.d.ts +83 -0
- package/dist/dev/hook.js +136 -0
- package/dist/dev/hook.js.map +1 -0
- package/dist/dev/index.d.ts +17 -0
- package/dist/dev/index.js +17 -0
- package/dist/dev/inspection.d.ts +226 -0
- package/dist/dev/inspection.js +263 -0
- package/dist/dev/inspection.js.map +1 -0
- package/dist/dev/inspector.d.ts +331 -0
- package/dist/dev/inspector.js +429 -0
- package/dist/dev/inspector.js.map +1 -0
- package/dist/dev/model.d.ts +418 -0
- package/dist/dev/model.js +559 -0
- package/dist/dev/model.js.map +1 -0
- package/dist/dev/mount.d.ts +207 -0
- package/dist/dev/mount.js +256 -0
- package/dist/dev/mount.js.map +1 -0
- package/dist/dev/overlay.d.ts +102 -0
- package/dist/dev/overlay.js +176 -0
- package/dist/dev/overlay.js.map +1 -0
- package/dist/dev/preview.d.ts +74 -0
- package/dist/dev/preview.js +122 -0
- package/dist/dev/preview.js.map +1 -0
- package/dist/dev/protocol.d.ts +288 -0
- package/dist/dev/protocol.js +109 -0
- package/dist/dev/protocol.js.map +1 -0
- package/dist/dev/ring.d.ts +50 -0
- package/dist/dev/ring.js +94 -0
- package/dist/dev/ring.js.map +1 -0
- package/dist/dev/transport.d.ts +347 -0
- package/dist/dev/transport.js +394 -0
- package/dist/dev/transport.js.map +1 -0
- package/dist/dev/ui/details.js +294 -0
- package/dist/dev/ui/details.js.map +1 -0
- package/dist/dev/ui/inspection-state.d.ts +43 -0
- package/dist/dev/ui/inspection-state.js +62 -0
- package/dist/dev/ui/inspection-state.js.map +1 -0
- package/dist/dev/ui/issues.js +85 -0
- package/dist/dev/ui/issues.js.map +1 -0
- package/dist/dev/ui/panel.d.ts +55 -0
- package/dist/dev/ui/panel.js +160 -0
- package/dist/dev/ui/panel.js.map +1 -0
- package/dist/dev/ui/rows.d.ts +44 -0
- package/dist/dev/ui/rows.js +132 -0
- package/dist/dev/ui/rows.js.map +1 -0
- package/dist/dev/ui/state-tab.js +88 -0
- package/dist/dev/ui/state-tab.js.map +1 -0
- package/dist/dev/ui/state.d.ts +192 -0
- package/dist/dev/ui/state.js +147 -0
- package/dist/dev/ui/state.js.map +1 -0
- package/dist/dev/ui/styles.d.ts +31 -0
- package/dist/dev/ui/styles.js +113 -0
- package/dist/dev/ui/styles.js.map +1 -0
- package/dist/dev/ui/timeline.js +135 -0
- package/dist/dev/ui/timeline.js.map +1 -0
- package/dist/dev/ui/tree.js +186 -0
- package/dist/dev/ui/tree.js.map +1 -0
- package/dist/prod/agent.js +2 -0
- package/dist/prod/agent.js.map +1 -0
- package/dist/prod/backend.js +2 -0
- package/dist/prod/backend.js.map +1 -0
- package/dist/prod/hook.js +2 -0
- package/dist/prod/hook.js.map +1 -0
- package/dist/prod/index.js +1 -0
- package/dist/prod/inspection.js +2 -0
- package/dist/prod/inspection.js.map +1 -0
- package/dist/prod/inspector.js +2 -0
- package/dist/prod/inspector.js.map +1 -0
- package/dist/prod/model.js +2 -0
- package/dist/prod/model.js.map +1 -0
- package/dist/prod/mount.js +2 -0
- package/dist/prod/mount.js.map +1 -0
- package/dist/prod/overlay.js +11 -0
- package/dist/prod/overlay.js.map +1 -0
- package/dist/prod/preview.js +2 -0
- package/dist/prod/preview.js.map +1 -0
- package/dist/prod/protocol.js +2 -0
- package/dist/prod/protocol.js.map +1 -0
- package/dist/prod/ring.js +2 -0
- package/dist/prod/ring.js.map +1 -0
- package/dist/prod/transport.js +2 -0
- package/dist/prod/transport.js.map +1 -0
- package/dist/prod/ui/details.js +2 -0
- package/dist/prod/ui/details.js.map +1 -0
- package/dist/prod/ui/inspection-state.js +2 -0
- package/dist/prod/ui/inspection-state.js.map +1 -0
- package/dist/prod/ui/issues.js +2 -0
- package/dist/prod/ui/issues.js.map +1 -0
- package/dist/prod/ui/panel.js +2 -0
- package/dist/prod/ui/panel.js.map +1 -0
- package/dist/prod/ui/rows.js +2 -0
- package/dist/prod/ui/rows.js.map +1 -0
- package/dist/prod/ui/state-tab.js +2 -0
- package/dist/prod/ui/state-tab.js.map +1 -0
- package/dist/prod/ui/state.js +2 -0
- package/dist/prod/ui/state.js.map +1 -0
- package/dist/prod/ui/styles.js +82 -0
- package/dist/prod/ui/styles.js.map +1 -0
- package/dist/prod/ui/timeline.js +2 -0
- package/dist/prod/ui/timeline.js.map +1 -0
- package/dist/prod/ui/tree.js +2 -0
- package/dist/prod/ui/tree.js.map +1 -0
- package/package.json +69 -4
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state.js","names":["TABS","createPanelState","tab","setTab","useSignal","options","selected","setSelected","filter","setFilter","componentsOnly","setComponentsOnly","collapsed","setCollapsed","rowLimit","setRowLimit","flash","setFlashSignal","paused","setPausedSignal","version","setVersion","snapshot","useMemo","snapshotOf","backend","nodes","node","rows","buildRows","selectedNode","next","current","id","limit","on","explainRun","flameTimeline","componentStats","stat","nodeLabel"],"sources":["../../../src/ui/state.ts"],"sourcesContent":["import { type Accessor, useMemo, useSignal } from \"@shadow-js/core\";\nimport type { Backend } from \"../backend\";\nimport {\n type ComponentStat,\n componentStats,\n type Explanation,\n explainRun,\n type FlameFlush,\n flameTimeline,\n type GraphSnapshot,\n nodeLabel,\n type SnapshotNode,\n snapshotOf,\n} from \"../inspector\";\nimport type { ErrorRecord, HydrationRecord } from \"../model\";\nimport { buildRows, type TreeRow } from \"./rows\";\n\n/** The panel's tabs. @since 1.0.0 */\nexport type TabId = \"tree\" | \"timeline\" | \"state\" | \"issues\";\n\n/**\n * The panel's tabs in display order. Each entry has the tab's `id` and the label shown on it.\n *\n * @example\n * import { TABS } from \"@shadow-js/devtools\";\n * for (const tab of TABS) console.log(tab.id, tab.label);\n * @since 1.0.0\n */\nexport const TABS: ReadonlyArray<{ id: TabId; label: string }> = [\n { id: \"tree\", label: \"Components\" },\n { id: \"timeline\", label: \"Timeline\" },\n { id: \"state\", label: \"State\" },\n { id: \"issues\", label: \"Issues\" },\n];\n\n/** Everything the panel's components read and write. All state is signals; the heavy views are memos. @since 1.0.0 */\nexport interface PanelState {\n /**\n * The backend the panel reads from. The panel does not know whether it is local or remote.\n */\n readonly backend: Backend;\n /**\n * The active tab.\n */\n readonly tab: Accessor<TabId>;\n /**\n * Makes `tab` the active tab.\n */\n setTab(tab: TabId): void;\n /**\n * Id of the selected node. `0` when no node is selected, because `NO_ID` never names a node.\n */\n readonly selected: Accessor<number>;\n /**\n * Selects the node with this id.\n */\n select(id: number): void;\n /**\n * The tree's text filter.\n */\n readonly filter: Accessor<string>;\n /**\n * Sets the tree's text filter. Matching rows are shown with their ancestors.\n */\n setFilter(text: string): void;\n /**\n * Whether the tree shows components only.\n */\n readonly componentsOnly: Accessor<boolean>;\n /**\n * Shows components only when `on` is true, and every node otherwise.\n */\n setComponentsOnly(on: boolean): void;\n /**\n * Ids of the nodes the user collapsed.\n */\n readonly collapsed: Accessor<ReadonlySet<number>>;\n /**\n * Collapses the node when it is expanded, and expands it when it is collapsed.\n */\n toggleCollapsed(id: number): void;\n /**\n * The most tree rows shown at once.\n */\n readonly rowLimit: Accessor<number>;\n /**\n * Doubles the row limit. The tree's Show more button calls this.\n */\n showMoreRows(): void;\n /**\n * Whether components flash when they update.\n */\n readonly flash: Accessor<boolean>;\n /**\n * Switches flashing on or off, then calls `onFlash`.\n */\n setFlash(on: boolean): void;\n /**\n * Whether the user paused recording.\n */\n readonly paused: Accessor<boolean>;\n /**\n * Pauses or resumes recording. Sends `pause` or `resume` to the backend, then calls `onPause`.\n */\n setPaused(on: boolean): void;\n /** Counter bumped each time the model changed; views depend on it. */\n readonly version: Accessor<number>;\n /** Re-reads the model now. */\n refresh(): void;\n /**\n * The graph snapshot of the backend's model. Recomputed when `version` changes.\n */\n readonly snapshot: Accessor<GraphSnapshot>;\n /**\n * The nodes of {@link PanelState.snapshot}, by id.\n */\n readonly nodes: Accessor<ReadonlyMap<number, SnapshotNode>>;\n /**\n * The tree rows for the current filter, switches, collapse state and row limit, and how many matching\n * rows the limit cut.\n */\n readonly rows: Accessor<{ rows: TreeRow[]; hidden: number }>;\n /**\n * The selected node, or `undefined` when it is not in the snapshot.\n */\n readonly selectedNode: Accessor<SnapshotNode | undefined>;\n /**\n * Why the selected memo or effect last ran, or `null` when the selected node is not a memo or effect.\n */\n readonly explanation: Accessor<Explanation | null>;\n /**\n * The newest 12 flushes, laid out as flame lanes.\n */\n readonly flames: Accessor<FlameFlush[]>;\n /**\n * Up to 10 components that re-ran, most re-runs first.\n */\n readonly hot: Accessor<ComponentStat[]>;\n /**\n * The errors the runtime reported, newest first.\n */\n readonly errors: Accessor<ErrorRecord[]>;\n /**\n * The hydration mismatches, newest first.\n */\n readonly hydration: Accessor<HydrationRecord[]>;\n /** Highlights a component on the page. */\n highlight(id: number | null): void;\n /** The display name of a node, `#id` when it is gone. */\n nameOf(id: number): string;\n}\n\n/**\n * Options of {@link createPanelState}. Every field is optional.\n *\n * @since 1.0.0\n */\nexport interface PanelStateOptions {\n /**\n * Called when the user switches flashing on or off.\n */\n onFlash?(on: boolean): void;\n /**\n * Called with the pause state the user chose. The state also sends `pause` or `resume` to the backend,\n * so the agent stops or resumes recording either way.\n */\n onPause?(paused: boolean): void;\n /**\n * Starting tab.\n *\n * @default \"tree\"\n */\n tab?: TabId;\n /**\n * Starting row limit of the tree.\n *\n * @default 300\n */\n rowLimit?: number;\n /**\n * Starting value of the flash switch.\n *\n * @default true\n */\n flash?: boolean;\n}\n\n/**\n * Creates the panel's state. Call it inside the panel's reactive root, because the memos belong to the\n * current owner. The heavy views (snapshot, rows, flame lanes) are memos of the `version` counter, so a\n * burst of model changes costs one recomputation per refresh.\n *\n * @param backend The backend to read from.\n * @param options Initial values and callbacks.\n * @returns The state.\n * @example\n * import { createAgent, createLocalBackend, createPanelState } from \"@shadow-js/devtools\";\n * const backend = createLocalBackend(createAgent());\n * // Inside the panel's reactive root (the one render() creates):\n * const state = createPanelState(backend, { tab: \"timeline\", flash: false });\n * state.setTab(\"issues\");\n * @since 1.0.0\n */\nexport function createPanelState(backend: Backend, options: PanelStateOptions = {}): PanelState {\n const [tab, setTab] = useSignal<TabId>(options.tab ?? \"tree\");\n const [selected, setSelected] = useSignal(0);\n const [filter, setFilter] = useSignal(\"\");\n const [componentsOnly, setComponentsOnly] = useSignal(false);\n const [collapsed, setCollapsed] = useSignal<ReadonlySet<number>>(new Set());\n const [rowLimit, setRowLimit] = useSignal(options.rowLimit ?? 300);\n const [flash, setFlashSignal] = useSignal(options.flash !== false);\n const [paused, setPausedSignal] = useSignal(false);\n const [version, setVersion] = useSignal(0);\n\n const snapshot = useMemo(() => {\n version();\n return snapshotOf(backend.model);\n });\n const nodes = useMemo(() => new Map(snapshot().nodes.map((node) => [node.id, node] as const)));\n const rows = useMemo(() =>\n buildRows(snapshot(), {\n filter: filter(),\n componentsOnly: componentsOnly(),\n collapsed: collapsed(),\n limit: rowLimit(),\n }),\n );\n const selectedNode = useMemo(() => nodes().get(selected()));\n const explanation = useMemo(() => {\n version();\n const node = selectedNode();\n if (node === undefined || (node.type !== \"memo\" && node.type !== \"effect\")) return null;\n return explainRun(backend.model, node.id);\n });\n const flames = useMemo(() => {\n version();\n return flameTimeline(backend.model, { last: 12 });\n });\n const hot = useMemo(() => {\n version();\n return componentStats(backend.model)\n .filter((stat) => stat.updates > 0)\n .slice(0, 10);\n });\n const errors = useMemo(() => {\n version();\n return backend.model.errors.toArray().reverse();\n });\n const hydration = useMemo(() => {\n version();\n return backend.model.hydration.toArray().reverse();\n });\n\n return {\n backend,\n tab,\n setTab,\n selected,\n select: setSelected,\n filter,\n setFilter,\n componentsOnly,\n setComponentsOnly,\n collapsed,\n toggleCollapsed(id): void {\n setCollapsed((current) => {\n const next = new Set(current);\n if (!next.delete(id)) next.add(id);\n return next;\n });\n },\n rowLimit,\n showMoreRows(): void {\n setRowLimit((limit) => limit * 2);\n },\n flash,\n setFlash(on): void {\n setFlashSignal(on);\n options.onFlash?.(on);\n },\n paused,\n setPaused(on): void {\n setPausedSignal(on);\n // Through the backend, so a remote agent pauses too; the callback is for page-side hooks.\n void backend.request({ type: on ? \"pause\" : \"resume\" });\n options.onPause?.(on);\n },\n version,\n refresh(): void {\n setVersion((current) => current + 1);\n },\n snapshot,\n nodes,\n rows,\n selectedNode,\n explanation,\n flames,\n hot,\n errors,\n hydration,\n highlight(id): void {\n void backend.request({ type: \"highlight\", id });\n },\n nameOf(id): string {\n const node = nodes().get(id) ?? backend.model.node(id);\n return node === undefined ? `#${id}` : nodeLabel(node);\n },\n };\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
const e=`
|
|
2
|
+
:host { all: initial; }
|
|
3
|
+
*, *::before, *::after { box-sizing: border-box; }
|
|
4
|
+
.panel {
|
|
5
|
+
--bg: #1e1f22; --bg2: #26282c; --bg3: #2f3237; --fg: #d7dae0; --muted: #8d939c; --line: #3a3d42;
|
|
6
|
+
--accent: #4da3ff; --good: #6bcb77; --warn: #ffc107; --bad: #ff6b6b; --select: #254a73;
|
|
7
|
+
position: fixed; z-index: 2147483647; width: 460px; height: 380px; min-width: 300px; min-height: 200px;
|
|
8
|
+
display: flex; flex-direction: column; overflow: hidden; resize: both;
|
|
9
|
+
background: var(--bg); color: var(--fg); border: 1px solid var(--line); border-radius: 8px;
|
|
10
|
+
box-shadow: 0 8px 30px rgba(0,0,0,.45); font: 12px/1.45 ui-sans-serif, system-ui, sans-serif;
|
|
11
|
+
}
|
|
12
|
+
@media (prefers-color-scheme: light) {
|
|
13
|
+
.panel { --bg: #fbfbfc; --bg2: #f0f1f3; --bg3: #e4e6ea; --fg: #22252a; --muted: #6b717b; --line: #cfd3d9;
|
|
14
|
+
--accent: #0b6bcb; --good: #1f8a3b; --warn: #a66b00; --bad: #c62828; --select: #cfe3fb; }
|
|
15
|
+
}
|
|
16
|
+
.panel[hidden] { display: none; }
|
|
17
|
+
.header { display: flex; align-items: center; gap: 8px; padding: 6px 8px; background: var(--bg2); border-bottom: 1px solid var(--line); cursor: grab; user-select: none; touch-action: none; }
|
|
18
|
+
.header.dragging { cursor: grabbing; }
|
|
19
|
+
.title { font-weight: 600; letter-spacing: .02em; }
|
|
20
|
+
.spacer { flex: 1; }
|
|
21
|
+
.tabs { display: flex; gap: 2px; padding: 4px 6px 0; background: var(--bg2); border-bottom: 1px solid var(--line); }
|
|
22
|
+
.tab { border: 0; background: transparent; color: var(--muted); padding: 5px 10px; border-radius: 6px 6px 0 0; cursor: pointer; font: inherit; }
|
|
23
|
+
.tab[aria-selected="true"] { color: var(--fg); background: var(--bg); box-shadow: inset 0 -2px 0 var(--accent); }
|
|
24
|
+
.badge { display: inline-block; min-width: 16px; padding: 0 5px; margin-left: 4px; border-radius: 8px; background: var(--bg3); color: var(--fg); text-align: center; font-size: 10px; }
|
|
25
|
+
.badge.bad { background: var(--bad); color: #fff; }
|
|
26
|
+
.btn { border: 1px solid var(--line); background: var(--bg3); color: var(--fg); border-radius: 5px; padding: 2px 8px; cursor: pointer; font: inherit; }
|
|
27
|
+
.btn:hover { border-color: var(--accent); }
|
|
28
|
+
.btn[aria-pressed="true"] { background: var(--select); border-color: var(--accent); }
|
|
29
|
+
.body { flex: 1; min-height: 0; display: flex; flex-direction: column; }
|
|
30
|
+
.toolbar { display: flex; align-items: center; gap: 8px; padding: 6px 8px; border-bottom: 1px solid var(--line); }
|
|
31
|
+
.input, .textarea { background: var(--bg); color: var(--fg); border: 1px solid var(--line); border-radius: 5px; padding: 3px 6px; font: inherit; }
|
|
32
|
+
.input { flex: 1; min-width: 0; }
|
|
33
|
+
.textarea { width: 100%; min-height: 70px; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; resize: vertical; }
|
|
34
|
+
.split { flex: 1; min-height: 0; display: flex; flex-direction: column; }
|
|
35
|
+
.list { flex: 1; min-height: 60px; overflow: auto; }
|
|
36
|
+
.detail { max-height: 45%; overflow: auto; border-top: 1px solid var(--line); padding: 8px; background: var(--bg2); }
|
|
37
|
+
.row { display: flex; align-items: center; gap: 4px; padding: 1px 8px; cursor: default; white-space: nowrap; }
|
|
38
|
+
.row:hover { background: var(--bg3); }
|
|
39
|
+
.row[aria-selected="true"] { background: var(--select); }
|
|
40
|
+
.twisty { width: 14px; padding: 0; border: 0; background: none; text-align: center; color: var(--muted); cursor: pointer; flex: none; font: inherit; }
|
|
41
|
+
.twisty[hidden] { display: none; }
|
|
42
|
+
.row:focus-visible { outline: 1px solid var(--accent); outline-offset: -1px; }
|
|
43
|
+
.kind { font-size: 10px; padding: 0 4px; border-radius: 3px; background: var(--bg3); color: var(--muted); flex: none; }
|
|
44
|
+
.kind.component { color: var(--accent); }
|
|
45
|
+
.kind.signal { color: var(--good); }
|
|
46
|
+
.kind.memo { color: var(--warn); }
|
|
47
|
+
.name { overflow: hidden; text-overflow: ellipsis; }
|
|
48
|
+
.name.component { color: var(--accent); font-weight: 600; }
|
|
49
|
+
.value { color: var(--muted); overflow: hidden; text-overflow: ellipsis; }
|
|
50
|
+
.updates { margin-left: auto; color: var(--warn); font-variant-numeric: tabular-nums; flex: none; }
|
|
51
|
+
.more { padding: 6px 8px; }
|
|
52
|
+
.section { margin-bottom: 8px; }
|
|
53
|
+
.section h4 { margin: 0 0 4px; font-size: 11px; text-transform: uppercase; letter-spacing: .06em; color: var(--muted); }
|
|
54
|
+
.chain { list-style: none; margin: 0; padding: 0; }
|
|
55
|
+
.chain li { padding: 1px 0; }
|
|
56
|
+
.chain li::before { content: "→ "; color: var(--muted); }
|
|
57
|
+
.chain li:first-child::before { content: ""; }
|
|
58
|
+
.link { color: var(--accent); cursor: pointer; background: none; border: 0; padding: 0; font: inherit; text-decoration: underline; }
|
|
59
|
+
.flush { padding: 6px 8px; border-bottom: 1px solid var(--line); }
|
|
60
|
+
.flush-head { display: flex; gap: 8px; color: var(--muted); margin-bottom: 4px; }
|
|
61
|
+
.lanes { position: relative; background: var(--bg2); border-radius: 4px; overflow: hidden; }
|
|
62
|
+
.bar { position: absolute; height: 14px; overflow: hidden; white-space: nowrap; font-size: 10px; line-height: 14px; padding: 0 2px; border-radius: 2px; background: var(--accent); color: #fff; cursor: pointer; }
|
|
63
|
+
.bar.memo { background: var(--warn); color: #000; }
|
|
64
|
+
.bar.unchanged { opacity: .55; }
|
|
65
|
+
.table { width: 100%; border-collapse: collapse; }
|
|
66
|
+
.table th, .table td { text-align: left; padding: 2px 8px; border-bottom: 1px solid var(--line); }
|
|
67
|
+
.table td.num, .table th.num { text-align: right; font-variant-numeric: tabular-nums; }
|
|
68
|
+
.empty { padding: 16px; color: var(--muted); text-align: center; }
|
|
69
|
+
.status { color: var(--muted); }
|
|
70
|
+
.status.bad { color: var(--bad); }
|
|
71
|
+
.status.good { color: var(--good); }
|
|
72
|
+
.issue { padding: 6px 8px; border-bottom: 1px solid var(--line); }
|
|
73
|
+
.issue code { color: var(--bad); }
|
|
74
|
+
.footer { display: flex; gap: 12px; padding: 4px 8px; border-top: 1px solid var(--line); background: var(--bg2); color: var(--muted); }
|
|
75
|
+
`,t=`
|
|
76
|
+
.launcher { position: fixed; right: 12px; bottom: 12px; z-index: 2147483647; width: 36px; height: 36px; border-radius: 50%;
|
|
77
|
+
border: 1px solid #3a3d42; background: #1e1f22; color: #4da3ff; font: 600 14px/1 ui-sans-serif, system-ui, sans-serif; cursor: pointer;
|
|
78
|
+
box-shadow: 0 4px 14px rgba(0,0,0,.4); }
|
|
79
|
+
.launcher:hover { border-color: #4da3ff; }
|
|
80
|
+
.launcher[hidden] { display: none; }
|
|
81
|
+
`;export{t as LAUNCHER_CSS,e as PANEL_CSS};
|
|
82
|
+
//# sourceMappingURL=styles.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"styles.js","names":["PANEL_CSS","LAUNCHER_CSS"],"sources":["../../../src/ui/styles.ts"],"sourcesContent":["/**\n * The panel's stylesheet. It lives inside the shadow root, so page styles cannot reach the panel and the\n * panel's styles cannot leak out. Colors follow `prefers-color-scheme`. Development tool: `mountDevtools()`\n * inserts it in development builds only.\n *\n * @example\n * import { LAUNCHER_CSS, PANEL_CSS } from \"@shadow-js/devtools\";\n * const host = document.createElement(\"div\");\n * const root = host.attachShadow({ mode: \"open\" });\n * const style = document.createElement(\"style\");\n * style.textContent = `${PANEL_CSS}${LAUNCHER_CSS}`;\n * root.append(style);\n * document.body.append(host);\n * @since 1.0.0\n */\nexport const PANEL_CSS: string = `\n:host { all: initial; }\n*, *::before, *::after { box-sizing: border-box; }\n.panel {\n --bg: #1e1f22; --bg2: #26282c; --bg3: #2f3237; --fg: #d7dae0; --muted: #8d939c; --line: #3a3d42;\n --accent: #4da3ff; --good: #6bcb77; --warn: #ffc107; --bad: #ff6b6b; --select: #254a73;\n position: fixed; z-index: 2147483647; width: 460px; height: 380px; min-width: 300px; min-height: 200px;\n display: flex; flex-direction: column; overflow: hidden; resize: both;\n background: var(--bg); color: var(--fg); border: 1px solid var(--line); border-radius: 8px;\n box-shadow: 0 8px 30px rgba(0,0,0,.45); font: 12px/1.45 ui-sans-serif, system-ui, sans-serif;\n}\n@media (prefers-color-scheme: light) {\n .panel { --bg: #fbfbfc; --bg2: #f0f1f3; --bg3: #e4e6ea; --fg: #22252a; --muted: #6b717b; --line: #cfd3d9;\n --accent: #0b6bcb; --good: #1f8a3b; --warn: #a66b00; --bad: #c62828; --select: #cfe3fb; }\n}\n.panel[hidden] { display: none; }\n.header { display: flex; align-items: center; gap: 8px; padding: 6px 8px; background: var(--bg2); border-bottom: 1px solid var(--line); cursor: grab; user-select: none; touch-action: none; }\n.header.dragging { cursor: grabbing; }\n.title { font-weight: 600; letter-spacing: .02em; }\n.spacer { flex: 1; }\n.tabs { display: flex; gap: 2px; padding: 4px 6px 0; background: var(--bg2); border-bottom: 1px solid var(--line); }\n.tab { border: 0; background: transparent; color: var(--muted); padding: 5px 10px; border-radius: 6px 6px 0 0; cursor: pointer; font: inherit; }\n.tab[aria-selected=\"true\"] { color: var(--fg); background: var(--bg); box-shadow: inset 0 -2px 0 var(--accent); }\n.badge { display: inline-block; min-width: 16px; padding: 0 5px; margin-left: 4px; border-radius: 8px; background: var(--bg3); color: var(--fg); text-align: center; font-size: 10px; }\n.badge.bad { background: var(--bad); color: #fff; }\n.btn { border: 1px solid var(--line); background: var(--bg3); color: var(--fg); border-radius: 5px; padding: 2px 8px; cursor: pointer; font: inherit; }\n.btn:hover { border-color: var(--accent); }\n.btn[aria-pressed=\"true\"] { background: var(--select); border-color: var(--accent); }\n.body { flex: 1; min-height: 0; display: flex; flex-direction: column; }\n.toolbar { display: flex; align-items: center; gap: 8px; padding: 6px 8px; border-bottom: 1px solid var(--line); }\n.input, .textarea { background: var(--bg); color: var(--fg); border: 1px solid var(--line); border-radius: 5px; padding: 3px 6px; font: inherit; }\n.input { flex: 1; min-width: 0; }\n.textarea { width: 100%; min-height: 70px; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; resize: vertical; }\n.split { flex: 1; min-height: 0; display: flex; flex-direction: column; }\n.list { flex: 1; min-height: 60px; overflow: auto; }\n.detail { max-height: 45%; overflow: auto; border-top: 1px solid var(--line); padding: 8px; background: var(--bg2); }\n.row { display: flex; align-items: center; gap: 4px; padding: 1px 8px; cursor: default; white-space: nowrap; }\n.row:hover { background: var(--bg3); }\n.row[aria-selected=\"true\"] { background: var(--select); }\n.twisty { width: 14px; padding: 0; border: 0; background: none; text-align: center; color: var(--muted); cursor: pointer; flex: none; font: inherit; }\n.twisty[hidden] { display: none; }\n.row:focus-visible { outline: 1px solid var(--accent); outline-offset: -1px; }\n.kind { font-size: 10px; padding: 0 4px; border-radius: 3px; background: var(--bg3); color: var(--muted); flex: none; }\n.kind.component { color: var(--accent); }\n.kind.signal { color: var(--good); }\n.kind.memo { color: var(--warn); }\n.name { overflow: hidden; text-overflow: ellipsis; }\n.name.component { color: var(--accent); font-weight: 600; }\n.value { color: var(--muted); overflow: hidden; text-overflow: ellipsis; }\n.updates { margin-left: auto; color: var(--warn); font-variant-numeric: tabular-nums; flex: none; }\n.more { padding: 6px 8px; }\n.section { margin-bottom: 8px; }\n.section h4 { margin: 0 0 4px; font-size: 11px; text-transform: uppercase; letter-spacing: .06em; color: var(--muted); }\n.chain { list-style: none; margin: 0; padding: 0; }\n.chain li { padding: 1px 0; }\n.chain li::before { content: \"→ \"; color: var(--muted); }\n.chain li:first-child::before { content: \"\"; }\n.link { color: var(--accent); cursor: pointer; background: none; border: 0; padding: 0; font: inherit; text-decoration: underline; }\n.flush { padding: 6px 8px; border-bottom: 1px solid var(--line); }\n.flush-head { display: flex; gap: 8px; color: var(--muted); margin-bottom: 4px; }\n.lanes { position: relative; background: var(--bg2); border-radius: 4px; overflow: hidden; }\n.bar { position: absolute; height: 14px; overflow: hidden; white-space: nowrap; font-size: 10px; line-height: 14px; padding: 0 2px; border-radius: 2px; background: var(--accent); color: #fff; cursor: pointer; }\n.bar.memo { background: var(--warn); color: #000; }\n.bar.unchanged { opacity: .55; }\n.table { width: 100%; border-collapse: collapse; }\n.table th, .table td { text-align: left; padding: 2px 8px; border-bottom: 1px solid var(--line); }\n.table td.num, .table th.num { text-align: right; font-variant-numeric: tabular-nums; }\n.empty { padding: 16px; color: var(--muted); text-align: center; }\n.status { color: var(--muted); }\n.status.bad { color: var(--bad); }\n.status.good { color: var(--good); }\n.issue { padding: 6px 8px; border-bottom: 1px solid var(--line); }\n.issue code { color: var(--bad); }\n.footer { display: flex; gap: 12px; padding: 4px 8px; border-top: 1px solid var(--line); background: var(--bg2); color: var(--muted); }\n`;\n\n/**\n * Styles of the small button that opens the panel. Like {@link PANEL_CSS}, it is inserted into the shadow\n * root by `mountDevtools()` in development builds only.\n *\n * @example\n * import { LAUNCHER_CSS } from \"@shadow-js/devtools\";\n * const style = document.createElement(\"style\");\n * style.textContent = LAUNCHER_CSS;\n * document.head.append(style);\n * @since 1.0.0\n */\nexport const LAUNCHER_CSS: string = `\n.launcher { position: fixed; right: 12px; bottom: 12px; z-index: 2147483647; width: 36px; height: 36px; border-radius: 50%;\n border: 1px solid #3a3d42; background: #1e1f22; color: #4da3ff; font: 600 14px/1 ui-sans-serif, system-ui, sans-serif; cursor: pointer;\n box-shadow: 0 4px 14px rgba(0,0,0,.4); }\n.launcher:hover { border-color: #4da3ff; }\n.launcher[hidden] { display: none; }\n`;\n"],"mappings":"AAeA,MAAaA,EAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAuFpBC,EAAuB"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{For as e,Show as t,addEventListener as n,anchor as r,className as i,createComponent as a,effect as o,insert as s,setAttribute as c,style as l,template as u}from"@shadow-js/web";const d=/*#__PURE__*/ u(`<div></div>`),f=/*#__PURE__*/ u(`<div class="empty">No flushes recorded yet.</div>`),p=/*#__PURE__*/ u(`<span>queue: render / user</span>`),m=/*#__PURE__*/ u(`<div class="flush"><div class="flush-head"><strong></strong><span> runs</span><span> ms</span></div><div class="lanes"></div></div>`),h=/*#__PURE__*/ u(`<tr><td><button type="button" class="link"></button></td><td class="num"></td><td class="num"></td></tr>`),g=/*#__PURE__*/ u(`<div class="flush"><div class="flush-head"><strong>Components that re-ran most</strong></div><table class="table" aria-label="Component update counts"><thead><tr><th>Component</th><th class="num">Updates</th><th class="num">Total ms</th></tr></thead><tbody></tbody></table></div>`),_=/*#__PURE__*/ u(`<section class="list" aria-label="Timeline"></section>`),v=e=>{let t=d();return o(()=>{i(t,`bar ${e.span.kind}${e.span.changed?``:` unchanged`}`)}),o(()=>{c(t,`data-span`,e.span.seq)}),o(()=>{c(t,`title`,`${e.span.label}: ${e.span.duration.toFixed(2)} ms${e.span.changed?``:` (value unchanged)`}`)}),o(()=>({left:`${e.flush.duration<=0?0:(e.span.start-e.flush.start)/e.flush.duration*100}%`,width:`${e.flush.duration<=0?100:Math.max(e.span.duration/e.flush.duration*100,1)}%`,top:`${e.span.depth*16}px`}),(e,n)=>{l(t,e,n)}),n(t,`click`,()=>e.state.select(e.span.id)),s(t,()=>e.span.label),t},y=i=>{let u=_(),d=r(u,null);return s(u,a(t,{get when(){return i.state.flames().length>0},get fallback(){return f()},get children(){return a(e,{get each(){return i.state.flames().slice().reverse()},key:e=>e.flush,children:n=>{let r=m(),u=r.firstChild,d=u.firstChild,f=d.nextSibling,h=f.firstChild,g=f.nextSibling,_=g.firstChild,y=u.nextSibling;return o(()=>{c(r,`data-flush`,n().flush)}),s(d,()=>n().flush===0?`outside a flush`:`flush #${n().flush}`),s(f,()=>n().spans.length,h),s(g,()=>n().duration.toFixed(2),_),s(u,a(t,{get when(){return n().flush!==0},get children(){let e=p(),t=e.firstChild.splitText(7),r=t.splitText(10);return s(e,()=>n().render,t),s(e,()=>n().user,r),e}})),o(()=>({height:`${Math.max(n().lanes,1)*16}px`}),(e,t)=>{l(y,e,t)}),s(y,a(e,{get each(){return n().spans},key:e=>e.seq,children:e=>a(v,{get state(){return i.state},get flush(){return n()},get span(){return e()}})})),r}})}}),d),s(u,a(t,{get when(){return i.state.hot().length>0},get children(){let t=g(),r=t.firstChild.nextSibling.firstChild.nextSibling;return s(r,a(e,{get each(){return i.state.hot()},key:e=>e.id,children:e=>{let t=h(),r=t.firstChild,a=r.firstChild,l=r.nextSibling,u=l.nextSibling;return o(()=>{c(t,`data-component`,e().id)}),n(a,`click`,()=>i.state.select(e().id)),s(a,()=>e().name),s(l,()=>e().updates),s(u,()=>e().totalDuration.toFixed(2)),t}})),t}})),u};export{y as TimelineTab};
|
|
2
|
+
//# sourceMappingURL=timeline.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"timeline.js","names":["_$anchor","_$createComponent","_tmpl$","_tmpl$2","_tmpl$3","_tmpl$4","_tmpl$5","_tmpl$6","_tmpl$7","Bar","_el$","effect","className","props","setAttribute","style","_v$","_p$","addEventListener","insert","TimelineTab","_el$2","anchor","createComponent","Show","For","flush","_el$4","_el$5","_el$7","_el$6","_el$8","_el$3","span","stat"],"sources":["../../../src/ui/timeline.tsx"],"sourcesContent":["import { For, Show } from \"@shadow-js/web\";\nimport type { Component } from \"@shadow-js/web/jsx-runtime\";\nimport type { FlameFlush, FlameSpan } from \"../inspector\";\nimport type { TabProps } from \"./tree\";\n\ninterface SpanProps extends TabProps {\n flush: FlameFlush;\n span: FlameSpan;\n}\n\nconst LANE_HEIGHT = 16;\n\n/** One bar of the flame graph: positioned by time inside its flush, one lane per nesting level. */\nconst Bar: Component<SpanProps> = (props) => (\n <div\n class={`bar ${props.span.kind}${props.span.changed ? \"\" : \" unchanged\"}`}\n data-span={props.span.seq}\n title={`${props.span.label}: ${props.span.duration.toFixed(2)} ms${props.span.changed ? \"\" : \" (value unchanged)\"}`}\n style={{\n left: `${props.flush.duration <= 0 ? 0 : ((props.span.start - props.flush.start) / props.flush.duration) * 100}%`,\n width: `${props.flush.duration <= 0 ? 100 : Math.max((props.span.duration / props.flush.duration) * 100, 1)}%`,\n top: `${props.span.depth * LANE_HEIGHT}px`,\n }}\n on:click={() => props.state.select(props.span.id)}\n >\n {props.span.label}\n </div>\n);\n\n/**\n * The Timeline tab. Props: `state` is the panel's {@link PanelState}. It shows the newest 12 flushes, each\n * with its run count and duration (flushes also show their queue sizes), and its runs laid out as flame lanes (a\n * memo pulled by an effect sits one lane below the effect). Clicking a bar selects its node. Below the\n * flushes it lists up to 10 components that re-ran most, with their update counts and total time.\n *\n * Internal to the panel: the package index does not export this component. Used in development builds\n * only.\n *\n * @internal\n */\nexport const TimelineTab: Component<TabProps> = (props) => (\n <section class=\"list\" aria-label=\"Timeline\">\n <Show\n when={props.state.flames().length > 0}\n fallback={<div class=\"empty\">No flushes recorded yet.</div>}\n >\n <For each={props.state.flames().slice().reverse()} key={(flush) => flush.flush}>\n {(flush) => (\n <div class=\"flush\" data-flush={flush().flush}>\n <div class=\"flush-head\">\n <strong>{flush().flush === 0 ? \"outside a flush\" : `flush #${flush().flush}`}</strong>\n <span>{flush().spans.length} runs</span>\n <span>{flush().duration.toFixed(2)} ms</span>\n <Show when={flush().flush !== 0}>\n <span>\n queue: {flush().render} render / {flush().user} user\n </span>\n </Show>\n </div>\n <div class=\"lanes\" style={{ height: `${Math.max(flush().lanes, 1) * LANE_HEIGHT}px` }}>\n <For each={flush().spans} key={(span) => span.seq}>\n {(span) => <Bar state={props.state} flush={flush()} span={span()} />}\n </For>\n </div>\n </div>\n )}\n </For>\n </Show>\n <Show when={props.state.hot().length > 0}>\n <div class=\"flush\">\n <div class=\"flush-head\">\n <strong>Components that re-ran most</strong>\n </div>\n <table class=\"table\" aria-label=\"Component update counts\">\n <thead>\n <tr>\n <th>Component</th>\n <th class=\"num\">Updates</th>\n <th class=\"num\">Total ms</th>\n </tr>\n </thead>\n <tbody>\n <For each={props.state.hot()} key={(stat) => stat.id}>\n {(stat) => (\n <tr data-component={stat().id}>\n <td>\n <button\n type=\"button\"\n class=\"link\"\n on:click={() => props.state.select(stat().id)}\n >\n {stat().name}\n </button>\n </td>\n <td class=\"num\">{stat().updates}</td>\n <td class=\"num\">{stat().totalDuration.toFixed(2)}</td>\n </tr>\n )}\n </For>\n </tbody>\n </table>\n </div>\n </Show>\n </section>\n);\n"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{kindLetter as e}from"./rows.js";import{Details as t}from"./details.js";import{For as n,Show as r,addEventListener as i,anchor as a,className as o,createComponent as s,effect as c,insert as l,setAttribute as u,style as d,template as f}from"@shadow-js/web";const p=/*#__PURE__*/ f(`<span class="twisty"></span>`),m=/*#__PURE__*/ f(`<span class="updates" title="Re-runs of effects and memos below this node">×</span>`),h=/*#__PURE__*/ f(`<div class="row" role="treeitem"><button type="button" class="twisty" tabindex="-1" aria-label="Toggle children"></button><span></span><span></span><span class="value"></span></div>`),g=/*#__PURE__*/ f(`<div class="empty">Nothing recorded yet. Interact with the app.</div>`),_=/*#__PURE__*/ f(`<div class="more"><button type="button" class="btn">Show more ( hidden)</button></div>`),v=/*#__PURE__*/ f(`<div class="split"><div class="toolbar"><input class="input" type="search" placeholder="Filter by name, kind or value" aria-label="Filter"><label><input type="checkbox"> components only</label></div><div class="list" role="tree" aria-label="Reactive nodes"></div></div>`),y=t=>{let n=h(),a=n.firstChild,f=a.nextSibling,g=f.nextSibling,_=g.nextSibling;return c(()=>{u(n,`aria-level`,t.row.depth+1)}),c(()=>{u(n,`aria-selected`,t.state.selected()===t.row.id)}),c(()=>{u(n,`aria-expanded`,t.row.hasChildren?!t.row.collapsed:void 0)}),c(()=>{u(n,`data-id`,t.row.id)}),c(()=>{u(n,`tabindex`,t.state.selected()===t.row.id?0:-1)}),c(()=>({paddingLeft:`${8+t.row.depth*14}px`}),(e,t)=>{d(n,e,t)}),i(n,`click`,()=>t.state.select(t.row.id)),i(n,`pointerenter`,()=>t.state.highlight(t.row.id)),i(n,`pointerleave`,()=>t.state.highlight(null)),c(()=>{a.hidden=!t.row.hasChildren}),i(a,`click`,e=>{e.stopPropagation(),t.state.toggleCollapsed(t.row.id)}),l(a,()=>t.row.collapsed?`▸`:`▾`),l(n,s(r,{get when(){return!t.row.hasChildren},get children(){return p()}}),f),c(()=>{o(f,`kind ${t.row.isComponent?`component`:t.row.type}`)}),l(f,()=>e(t.row)),c(()=>{o(g,t.row.isComponent?`name component`:`name`)}),l(g,()=>t.row.label),l(_,()=>t.row.detail),l(n,s(r,{get when(){return t.row.updates>0},get children(){let e=m();return l(e,()=>t.row.updates),e}})),n};function b(e,t){let n=e.rows().rows,r=n.findIndex(t=>t.id===e.selected()),i=r=>{let i=n[r];if(i===void 0)return;t.preventDefault(),e.select(i.id),e.highlight(i.id);let a=t.currentTarget;a instanceof HTMLElement&&queueMicrotask(()=>a.querySelector(`[data-id="${i.id}"]`)?.focus())},a=n[r];switch(t.key){case`ArrowDown`:i(Math.min(r+1,n.length-1));break;case`ArrowUp`:i(Math.max(r-1,0));break;case`Home`:i(0);break;case`End`:i(n.length-1);break;case`ArrowRight`:a?.hasChildren===!0&&a.collapsed&&(t.preventDefault(),e.toggleCollapsed(a.id));break;case`ArrowLeft`:a?.hasChildren===!0&&!a.collapsed&&(t.preventDefault(),e.toggleCollapsed(a.id))}}const x=e=>{let o=v(),u=o.firstChild,d=u.firstChild,f=d.nextSibling.firstChild,p=u.nextSibling,m=a(p,null);return i(d,`input`,t=>e.state.setFilter(t.currentTarget.value)),c(()=>{d.value=e.state.filter()??``}),i(f,`change`,t=>e.state.setComponentsOnly(t.currentTarget.checked)),c(()=>{f.checked=e.state.componentsOnly()}),i(p,`keydown`,t=>b(e.state,t)),l(p,s(r,{get when(){return e.state.rows().rows.length>0},get fallback(){return g()},get children(){return s(n,{get each(){return e.state.rows().rows},key:e=>e.id,children:t=>s(y,{get state(){return e.state},get row(){return t()}})})}}),m),l(p,s(r,{get when(){return e.state.rows().hidden>0},get children(){let t=_(),n=t.firstChild,r=n.firstChild.splitText(11);return i(n,`click`,()=>e.state.showMoreRows()),l(n,()=>e.state.rows().hidden,r),t}})),l(o,s(r,{get when(){return e.state.selectedNode()!==void 0},get children(){return s(t,{get state(){return e.state}})}})),o};export{x as TreeTab};
|
|
2
|
+
//# sourceMappingURL=tree.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tree.js","names":["_$createComponent","_$anchor","_tmpl$","_tmpl$2","_tmpl$3","_tmpl$4","_tmpl$5","_tmpl$6","TreeRowView","_el$","_el$2","_el$3","_el$4","_el$5","effect","setAttribute","props","style","_v$","_p$","addEventListener","event","insert","createComponent","Show","className","kindLetter","navigate","rows","state","index","row","select","next","list","current","TreeTab","_el$6","_el$7","anchor","For","Details"],"sources":["../../../src/ui/tree.tsx"],"sourcesContent":["import { For, Show } from \"@shadow-js/web\";\nimport type { Component } from \"@shadow-js/web/jsx-runtime\";\nimport { Details } from \"./details\";\nimport { kindLetter, type TreeRow } from \"./rows\";\nimport type { PanelState } from \"./state\";\n\ninterface RowProps {\n state: PanelState;\n row: TreeRow;\n}\n\n/** One line of the tree: twisty, kind, name, value and the re-run count. */\nconst TreeRowView: Component<RowProps> = (props) => (\n <div\n class=\"row\"\n role=\"treeitem\"\n aria-level={props.row.depth + 1}\n aria-selected={props.state.selected() === props.row.id}\n aria-expanded={props.row.hasChildren ? !props.row.collapsed : undefined}\n data-id={props.row.id}\n tabIndex={props.state.selected() === props.row.id ? 0 : -1}\n style={{ paddingLeft: `${8 + props.row.depth * 14}px` }}\n on:click={() => props.state.select(props.row.id)}\n on:pointerenter={() => props.state.highlight(props.row.id)}\n on:pointerleave={() => props.state.highlight(null)}\n >\n <button\n type=\"button\"\n class=\"twisty\"\n tabIndex={-1}\n aria-label=\"Toggle children\"\n hidden={!props.row.hasChildren}\n on:click={(event) => {\n event.stopPropagation();\n props.state.toggleCollapsed(props.row.id);\n }}\n >\n {props.row.collapsed ? \"▸\" : \"▾\"}\n </button>\n <Show when={!props.row.hasChildren}>\n <span class=\"twisty\" />\n </Show>\n <span class={`kind ${props.row.isComponent ? \"component\" : props.row.type}`}>\n {kindLetter(props.row)}\n </span>\n <span class={props.row.isComponent ? \"name component\" : \"name\"}>{props.row.label}</span>\n <span class=\"value\">{props.row.detail}</span>\n <Show when={props.row.updates > 0}>\n <span class=\"updates\" title=\"Re-runs of effects and memos below this node\">\n ×{props.row.updates}\n </span>\n </Show>\n </div>\n);\n\n/**\n * Keyboard navigation of the tree: arrows move the selection, left and right collapse and expand,\n * Home and End jump to the ends. Focus follows the selected row (roving tabindex).\n */\nfunction navigate(state: PanelState, event: KeyboardEvent): void {\n const rows = state.rows().rows;\n const index = rows.findIndex((row) => row.id === state.selected());\n const select = (next: number): void => {\n const row = rows[next];\n if (row === undefined) return;\n event.preventDefault();\n state.select(row.id);\n state.highlight(row.id);\n const list = event.currentTarget;\n if (list instanceof HTMLElement) {\n queueMicrotask(() => list.querySelector<HTMLElement>(`[data-id=\"${row.id}\"]`)?.focus());\n }\n };\n const current = rows[index];\n switch (event.key) {\n case \"ArrowDown\":\n select(Math.min(index + 1, rows.length - 1));\n break;\n case \"ArrowUp\":\n select(Math.max(index - 1, 0));\n break;\n case \"Home\":\n select(0);\n break;\n case \"End\":\n select(rows.length - 1);\n break;\n case \"ArrowRight\":\n if (current?.hasChildren === true && current.collapsed) {\n event.preventDefault();\n state.toggleCollapsed(current.id);\n }\n break;\n case \"ArrowLeft\":\n if (current?.hasChildren === true && !current.collapsed) {\n event.preventDefault();\n state.toggleCollapsed(current.id);\n }\n break;\n default:\n break;\n }\n}\n\n/**\n * Props of the tab views: the panel's {@link PanelState}.\n *\n * @since 1.0.0\n */\nexport interface TabProps {\n /**\n * The panel's state.\n */\n state: PanelState;\n}\n\n/**\n * The Components tab, rendered by the panel from a {@link TabProps} `state`. It shows a filter box, a\n * components-only switch, the owners tree (every reactive node: components, owners, memos, effects,\n * signals, resources and stores) with each row's kind letter, value and re-run count, and the details of\n * the selected node below it. Hovering a row highlights its component on the page.\n *\n * Internal to the panel: the package index does not export this component. Used in development builds\n * only.\n *\n * @internal\n */\nexport const TreeTab: Component<TabProps> = (props) => (\n <div class=\"split\">\n <div class=\"toolbar\">\n <input\n class=\"input\"\n type=\"search\"\n placeholder=\"Filter by name, kind or value\"\n aria-label=\"Filter\"\n value={props.state.filter()}\n on:input={(event) => props.state.setFilter(event.currentTarget.value)}\n />\n <label>\n <input\n type=\"checkbox\"\n checked={props.state.componentsOnly()}\n on:change={(event) => props.state.setComponentsOnly(event.currentTarget.checked)}\n />{\" \"}\n components only\n </label>\n </div>\n <div\n class=\"list\"\n role=\"tree\"\n aria-label=\"Reactive nodes\"\n on:keydown={(event) => navigate(props.state, event)}\n >\n <Show\n when={props.state.rows().rows.length > 0}\n fallback={<div class=\"empty\">Nothing recorded yet. Interact with the app.</div>}\n >\n <For each={props.state.rows().rows} key={(row) => row.id}>\n {(row) => <TreeRowView state={props.state} row={row()} />}\n </For>\n </Show>\n <Show when={props.state.rows().hidden > 0}>\n <div class=\"more\">\n <button type=\"button\" class=\"btn\" on:click={() => props.state.showMoreRows()}>\n Show more ({props.state.rows().hidden} hidden)\n </button>\n </div>\n </Show>\n </div>\n <Show when={props.state.selectedNode() !== undefined}>\n <Details state={props.state} />\n </Show>\n </div>\n);\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,71 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shadow-js/devtools",
|
|
3
|
-
"version": "
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "A development panel that shows the components, signals, effects and updates of a running app",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"sideEffects": false,
|
|
8
|
+
"engines": {
|
|
9
|
+
"node": ">=22.12.0"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"dist",
|
|
13
|
+
"!dist/extension",
|
|
14
|
+
"README.md",
|
|
15
|
+
"CHANGELOG.md"
|
|
16
|
+
],
|
|
17
|
+
"publishConfig": {
|
|
18
|
+
"access": "public",
|
|
19
|
+
"provenance": true
|
|
20
|
+
},
|
|
21
|
+
"exports": {
|
|
22
|
+
".": {
|
|
23
|
+
"types": "./dist/dev/index.d.ts",
|
|
24
|
+
"development": "./dist/dev/index.js",
|
|
25
|
+
"default": "./dist/prod/index.js"
|
|
26
|
+
},
|
|
27
|
+
"./package.json": "./package.json"
|
|
28
|
+
},
|
|
29
|
+
"scripts": {
|
|
30
|
+
"build": "tsdown --config-loader native && node extension/build.mjs",
|
|
31
|
+
"typecheck": "tsc -p tsconfig.json && tsc -p extension/tsconfig.json && npm run typecheck:types",
|
|
32
|
+
"typecheck:types": "tsc -p tsconfig.test-types.json",
|
|
33
|
+
"build:extension": "node extension/build.mjs",
|
|
34
|
+
"zip:extension": "node extension/zip.mjs",
|
|
35
|
+
"lint": "biome check .",
|
|
36
|
+
"test": "vitest run --project unit --project dom --project server",
|
|
37
|
+
"test:gc": "vitest run --project gc --passWithNoTests",
|
|
38
|
+
"test:perf": "vitest run --project perf --passWithNoTests",
|
|
39
|
+
"check": "npm run lint && npm run typecheck && npm run build && npm test && npm run test:perf && npm run test:gc"
|
|
40
|
+
},
|
|
41
|
+
"peerDependencies": {
|
|
42
|
+
"@shadow-js/core": "^1.0.0",
|
|
43
|
+
"@shadow-js/web": "^1.0.0"
|
|
44
|
+
},
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"@biomejs/biome": "2.5.15",
|
|
47
|
+
"@shadow-js/compiler": "^1.0.0",
|
|
48
|
+
"@shadow-js/core": "^1.0.0",
|
|
49
|
+
"@shadow-js/web": "^1.0.0",
|
|
50
|
+
"@types/node": "^24.0.0",
|
|
51
|
+
"happy-dom": "^21.0.3",
|
|
52
|
+
"rolldown": "1.2.13",
|
|
53
|
+
"tsdown": "0.23.0",
|
|
54
|
+
"typescript": "7.0.2",
|
|
55
|
+
"vitest": "5.0.3"
|
|
56
|
+
},
|
|
57
|
+
"keywords": [
|
|
58
|
+
"shadowjs",
|
|
59
|
+
"devtools",
|
|
60
|
+
"signals",
|
|
61
|
+
"reactivity"
|
|
62
|
+
],
|
|
63
|
+
"homepage": "https://devtools.shadowjs.net",
|
|
64
|
+
"repository": {
|
|
65
|
+
"type": "git",
|
|
66
|
+
"url": "git+https://github.com/shadowjs-dev/devtools.git"
|
|
67
|
+
},
|
|
68
|
+
"bugs": {
|
|
69
|
+
"url": "https://github.com/shadowjs-dev/devtools/issues"
|
|
70
|
+
}
|
|
71
|
+
}
|