@atom.io/spa-devtools 0.0.2 → 0.0.4
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 +2 -2
- package/README.mdx +1 -1
- package/dist/index.js +71 -71
- package/dist/index.js.map +1 -1
- package/package.json +6 -6
- package/src/AtomIODevtools.tsx +23 -20
- package/src/Indexes.tsx +44 -42
- package/src/JsonEditor.tsx +4 -4
- package/src/store.ts +10 -10
package/README.md
CHANGED
|
@@ -33,7 +33,7 @@ Tsdown builds the package JavaScript and declarations. SPA's compiler transforms
|
|
|
33
33
|
|
|
34
34
|
Breakage exercises the packed package in isolated, locked peer environments, including the coordinated atom.io workspace candidate. Until the first published release, Breakage reports a first-release baseline; these checks establish compatibility with the declared peers rather than a previous spa release. Coverage gates require 100% of statements, branches, functions, and lines across the package source.
|
|
35
35
|
|
|
36
|
-
The serving recipe measures an interactive SPA app with the debugger: atom creation, render,
|
|
36
|
+
The serving recipe measures an interactive SPA app with the debugger: atom creation, render, useGetter, useSetter, JSX, and AtomIODevtools. The combined browser bundle includes atom.io, SPA, and the debugger, with shared modules counted once and unused exports removed. SPA and atom.io remain peer dependencies of the debugger package; these consumer-app measurements include both. The required debugger stylesheet is imported but its bytes are excluded from the JavaScript total, as are application components, declarations, and source maps.
|
|
37
37
|
|
|
38
38
|
## Browser serving size including SPA and atom.io
|
|
39
39
|
|
|
@@ -43,7 +43,7 @@ Sizes are exact minified and level-9 gzip JavaScript byte counts. Declarations,
|
|
|
43
43
|
|
|
44
44
|
| Recipe | Entry | Minified JS | Gzip JS |
|
|
45
45
|
| ----------------------------- | ----------------------------- | ----------: | -------: |
|
|
46
|
-
| Interactive app with debugger | `tonnage-recipes/debugger.ts` |
|
|
46
|
+
| Interactive app with debugger | `tonnage-recipes/debugger.ts` | 87,866 B | 25,583 B |
|
|
47
47
|
|
|
48
48
|
Report maintained with [tonnage](https://github.com/jeremybanka/tonnage).
|
|
49
49
|
|
package/README.mdx
CHANGED
|
@@ -26,7 +26,7 @@ Tsdown builds the package JavaScript and declarations. SPA's compiler transforms
|
|
|
26
26
|
|
|
27
27
|
Breakage exercises the packed package in isolated, locked peer environments, including the coordinated atom.io workspace candidate. Until the first published release, Breakage reports a first-release baseline; these checks establish compatibility with the declared peers rather than a previous spa release. Coverage gates require 100% of statements, branches, functions, and lines across the package source.
|
|
28
28
|
|
|
29
|
-
The serving recipe measures an interactive SPA app with the debugger: atom creation, render,
|
|
29
|
+
The serving recipe measures an interactive SPA app with the debugger: atom creation, render, useGetter, useSetter, JSX, and AtomIODevtools. The combined browser bundle includes atom.io, SPA, and the debugger, with shared modules counted once and unused exports removed. SPA and atom.io remain peer dependencies of the debugger package; these consumer-app measurements include both. The required debugger stylesheet is imported but its bytes are excluded from the JavaScript total, as are application components, declarations, and source maps.
|
|
30
30
|
|
|
31
31
|
<Tonnage config={config} />
|
|
32
32
|
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx } from "@atom.io/spa/jsx-runtime";
|
|
2
|
-
import { onCleanup, onMount, render, useId, useSetter, useTimeline
|
|
2
|
+
import { onCleanup, onMount, render, useGetter, useId, useSetter, useTimeline } from "@atom.io/spa";
|
|
3
3
|
import { IMPLICIT, createRegularAtomFamily, disposeFromStore, findInStore, isReservedIntrospectionKey, setIntoStore } from "atom.io/internal";
|
|
4
4
|
import { attachIntrospectionStates } from "atom.io/introspection";
|
|
5
5
|
// src/store.ts
|
|
@@ -23,7 +23,7 @@ function ownedAtom(store, initial) {
|
|
|
23
23
|
}
|
|
24
24
|
function useLocal(store, initial) {
|
|
25
25
|
const token = ownedAtom(store, initial);
|
|
26
|
-
return [
|
|
26
|
+
return [useGetter(token), useSetter(token)];
|
|
27
27
|
}
|
|
28
28
|
function useSaved(store, key, fallback) {
|
|
29
29
|
let initial = fallback;
|
|
@@ -31,11 +31,11 @@ function useSaved(store, key, fallback) {
|
|
|
31
31
|
const saved = JSON.parse(localStorage.getItem(key) ?? `null`);
|
|
32
32
|
if (saved !== null && typeof saved === typeof fallback) initial = saved;
|
|
33
33
|
} catch {}
|
|
34
|
-
const [
|
|
35
|
-
return [
|
|
36
|
-
|
|
34
|
+
const [getValue, setValue] = useLocal(store, initial);
|
|
35
|
+
return [getValue, (next) => {
|
|
36
|
+
setValue(next);
|
|
37
37
|
try {
|
|
38
|
-
localStorage.setItem(key, JSON.stringify(
|
|
38
|
+
localStorage.setItem(key, JSON.stringify(getValue()));
|
|
39
39
|
} catch {}
|
|
40
40
|
}];
|
|
41
41
|
}
|
|
@@ -68,14 +68,14 @@ function useTools(store) {
|
|
|
68
68
|
};
|
|
69
69
|
}
|
|
70
70
|
function useExpanded(tools, path, initial = false) {
|
|
71
|
-
const
|
|
72
|
-
const
|
|
71
|
+
const getExpanded = useGetter(tools.expanded);
|
|
72
|
+
const setExpanded = useSetter(tools.expanded);
|
|
73
73
|
const key = JSON.stringify(path);
|
|
74
74
|
const group = `all:${JSON.stringify(path.slice(0, -1))}`;
|
|
75
|
-
const read = () =>
|
|
75
|
+
const read = () => getExpanded()[key] ?? getExpanded()[group] ?? initial;
|
|
76
76
|
return [read, (event) => {
|
|
77
77
|
const next = !read();
|
|
78
|
-
|
|
78
|
+
setExpanded((previous) => {
|
|
79
79
|
const result = { ...previous };
|
|
80
80
|
if (event.shiftKey) {
|
|
81
81
|
const parent = JSON.stringify(path.slice(0, -1));
|
|
@@ -141,7 +141,7 @@ const TYPES = [
|
|
|
141
141
|
`object`
|
|
142
142
|
];
|
|
143
143
|
function JsonEditor(props) {
|
|
144
|
-
const [
|
|
144
|
+
const [getExpanded, toggle] = useExpanded(props.tools, props.path);
|
|
145
145
|
const circular = () => (props.ancestors?.includes(props.value) ?? false) || props.path.length > 50;
|
|
146
146
|
const container = () => !circular() && (kind(props.value) === `object` || kind(props.value) === `array`);
|
|
147
147
|
const entries = () => Object.entries(props.value);
|
|
@@ -157,12 +157,12 @@ function JsonEditor(props) {
|
|
|
157
157
|
return `Expand ${props.path.at(-1)}`;
|
|
158
158
|
},
|
|
159
159
|
get "aria-expanded"() {
|
|
160
|
-
return
|
|
160
|
+
return getExpanded();
|
|
161
161
|
},
|
|
162
162
|
get "onClick"() {
|
|
163
163
|
return toggle;
|
|
164
164
|
},
|
|
165
|
-
children: () =>
|
|
165
|
+
children: () => getExpanded() ? `▾` : `▸`
|
|
166
166
|
}) : jsx("span", { "class": "carat-spacer" }),
|
|
167
167
|
jsx("span", {
|
|
168
168
|
"class": "field-name",
|
|
@@ -212,7 +212,7 @@ function JsonEditor(props) {
|
|
|
212
212
|
}
|
|
213
213
|
}),
|
|
214
214
|
() => props.actions
|
|
215
|
-
] }), () => container() &&
|
|
215
|
+
] }), () => container() && getExpanded() ? jsx("section", {
|
|
216
216
|
"class": "properties",
|
|
217
217
|
children: [() => entries().map(([key]) => jsx(Property, {
|
|
218
218
|
"key": key,
|
|
@@ -376,15 +376,15 @@ Primitive.spa = true;
|
|
|
376
376
|
Property.spa = true;
|
|
377
377
|
// src/Indexes.tsx
|
|
378
378
|
function StateIndex(props) {
|
|
379
|
-
const
|
|
379
|
+
const getStateIndex = useGetter(props.type === `atoms` ? props.tools.atomIndex : props.tools.selectorIndex);
|
|
380
380
|
return jsx("article", {
|
|
381
381
|
"class": "index",
|
|
382
382
|
"data-testid": "state-index",
|
|
383
|
-
children: () =>
|
|
383
|
+
children: () => getStateIndex().size === 0 ? jsx("p", { children: [
|
|
384
384
|
"(no ",
|
|
385
385
|
() => props.type,
|
|
386
386
|
")"
|
|
387
|
-
] }) : [...
|
|
387
|
+
] }) : [...getStateIndex().values()].map((node) => jsx(StateNode, {
|
|
388
388
|
"key": node.key,
|
|
389
389
|
get "tools"() {
|
|
390
390
|
return props.tools;
|
|
@@ -396,7 +396,7 @@ function StateIndex(props) {
|
|
|
396
396
|
});
|
|
397
397
|
}
|
|
398
398
|
function StateNode(props) {
|
|
399
|
-
const [
|
|
399
|
+
const [getOpen, toggle] = useExpanded(props.tools, [props.node.key]);
|
|
400
400
|
return jsx("section", {
|
|
401
401
|
"class": "node",
|
|
402
402
|
get "data-testid"() {
|
|
@@ -406,7 +406,7 @@ function StateNode(props) {
|
|
|
406
406
|
jsx("button", {
|
|
407
407
|
"type": "button",
|
|
408
408
|
get "aria-expanded"() {
|
|
409
|
-
return
|
|
409
|
+
return getOpen();
|
|
410
410
|
},
|
|
411
411
|
get "data-testid"() {
|
|
412
412
|
return `open-close-state-family-${props.node.key}`;
|
|
@@ -414,11 +414,11 @@ function StateNode(props) {
|
|
|
414
414
|
get "onClick"() {
|
|
415
415
|
return toggle;
|
|
416
416
|
},
|
|
417
|
-
children: () =>
|
|
417
|
+
children: () => getOpen() ? `▾` : `▸`
|
|
418
418
|
}),
|
|
419
419
|
jsx("h2", { children: () => props.node.key }),
|
|
420
420
|
jsx("small", { children: "family" })
|
|
421
|
-
] }), () =>
|
|
421
|
+
] }), () => getOpen() ? [...props.node.familyMembers.values()].map((token) => jsx(StateLeaf, {
|
|
422
422
|
"key": token.key,
|
|
423
423
|
get "tools"() {
|
|
424
424
|
return props.tools;
|
|
@@ -437,11 +437,11 @@ function StateNode(props) {
|
|
|
437
437
|
});
|
|
438
438
|
}
|
|
439
439
|
function StateLeaf(props) {
|
|
440
|
-
const
|
|
441
|
-
const
|
|
440
|
+
const getValue = useGetter(props.token);
|
|
441
|
+
const setValue = !props.token.type.startsWith(`readonly`) ? useSetter(props.token) : void 0;
|
|
442
442
|
const value = () => {
|
|
443
443
|
try {
|
|
444
|
-
return
|
|
444
|
+
return getValue();
|
|
445
445
|
} catch (error) {
|
|
446
446
|
return error;
|
|
447
447
|
}
|
|
@@ -476,9 +476,9 @@ function StateLeaf(props) {
|
|
|
476
476
|
return value();
|
|
477
477
|
},
|
|
478
478
|
get "change"() {
|
|
479
|
-
return
|
|
480
|
-
const next = update(
|
|
481
|
-
|
|
479
|
+
return setValue ? (update) => {
|
|
480
|
+
const next = update(getValue());
|
|
481
|
+
setValue(() => next);
|
|
482
482
|
} : void 0;
|
|
483
483
|
},
|
|
484
484
|
get "path"() {
|
|
@@ -488,8 +488,8 @@ function StateLeaf(props) {
|
|
|
488
488
|
});
|
|
489
489
|
}
|
|
490
490
|
function TransactionIndex(props) {
|
|
491
|
-
const
|
|
492
|
-
const visible = () =>
|
|
491
|
+
const getTransactionIndex = useGetter(props.tools.transactionIndex);
|
|
492
|
+
const visible = () => getTransactionIndex().filter((token) => !isReservedIntrospectionKey(token.key));
|
|
493
493
|
return jsx("article", {
|
|
494
494
|
"class": "index",
|
|
495
495
|
"data-testid": "transaction-index",
|
|
@@ -505,8 +505,8 @@ function TransactionIndex(props) {
|
|
|
505
505
|
});
|
|
506
506
|
}
|
|
507
507
|
function TransactionLog(props) {
|
|
508
|
-
const
|
|
509
|
-
const [
|
|
508
|
+
const getTransactionLog = useGetter(findInStore(props.tools.store, props.tools.transactionLogSelectors, props.token.key));
|
|
509
|
+
const [getOpen, toggle] = useExpanded(props.tools, [props.token.key]);
|
|
510
510
|
return jsx("section", {
|
|
511
511
|
"class": "node",
|
|
512
512
|
get "data-testid"() {
|
|
@@ -516,7 +516,7 @@ function TransactionLog(props) {
|
|
|
516
516
|
jsx("button", {
|
|
517
517
|
"type": "button",
|
|
518
518
|
get "aria-expanded"() {
|
|
519
|
-
return
|
|
519
|
+
return getOpen();
|
|
520
520
|
},
|
|
521
521
|
get "data-testid"() {
|
|
522
522
|
return `open-close-transaction-${props.token.key}`;
|
|
@@ -524,15 +524,15 @@ function TransactionLog(props) {
|
|
|
524
524
|
get "onClick"() {
|
|
525
525
|
return toggle;
|
|
526
526
|
},
|
|
527
|
-
children: () =>
|
|
527
|
+
children: () => getOpen() ? `▾` : `▸`
|
|
528
528
|
}),
|
|
529
529
|
jsx("h2", { children: () => props.token.key }),
|
|
530
530
|
jsx("small", { children: [
|
|
531
531
|
"(",
|
|
532
|
-
() =>
|
|
532
|
+
() => getTransactionLog().length,
|
|
533
533
|
")"
|
|
534
534
|
] })
|
|
535
|
-
] }), () =>
|
|
535
|
+
] }), () => getOpen() ? getTransactionLog().map((event, index) => jsx(EventView, {
|
|
536
536
|
"key": index,
|
|
537
537
|
get "tools"() {
|
|
538
538
|
return props.tools;
|
|
@@ -547,7 +547,7 @@ function TransactionLog(props) {
|
|
|
547
547
|
});
|
|
548
548
|
}
|
|
549
549
|
function EventView(props) {
|
|
550
|
-
const [
|
|
550
|
+
const [getOpen, toggle] = useExpanded(props.tools, [
|
|
551
551
|
`event`,
|
|
552
552
|
props.event.token?.key ?? ``,
|
|
553
553
|
String(props.index)
|
|
@@ -561,16 +561,16 @@ function EventView(props) {
|
|
|
561
561
|
jsx("button", {
|
|
562
562
|
"type": "button",
|
|
563
563
|
get "aria-expanded"() {
|
|
564
|
-
return
|
|
564
|
+
return getOpen();
|
|
565
565
|
},
|
|
566
566
|
get "onClick"() {
|
|
567
567
|
return toggle;
|
|
568
568
|
},
|
|
569
|
-
children: () =>
|
|
569
|
+
children: () => getOpen() ? `▾` : `▸`
|
|
570
570
|
}),
|
|
571
571
|
jsx("strong", { children: () => props.event.token?.key }),
|
|
572
572
|
jsx("small", { children: () => props.event.type })
|
|
573
|
-
] }), () =>
|
|
573
|
+
] }), () => getOpen() ? props.event.type === `transaction_outcome` ? jsx(null, { children: [
|
|
574
574
|
jsx("p", { children: ["params: ", jsx("code", { children: () => describe(props.event.params) })] }),
|
|
575
575
|
jsx("p", { children: ["output: ", jsx("code", { children: () => describe(props.event.output) })] }),
|
|
576
576
|
jsx("section", {
|
|
@@ -597,10 +597,10 @@ function EventView(props) {
|
|
|
597
597
|
});
|
|
598
598
|
}
|
|
599
599
|
function TimelineIndex(props) {
|
|
600
|
-
const
|
|
600
|
+
const getTimelineIndex = useGetter(props.tools.timelineIndex);
|
|
601
601
|
const groups = () => {
|
|
602
602
|
const result = /* @__PURE__ */ new Map();
|
|
603
|
-
for (const token of
|
|
603
|
+
for (const token of getTimelineIndex()) {
|
|
604
604
|
if (isReservedIntrospectionKey(token.key) || token.key.startsWith(`👁🗨`)) continue;
|
|
605
605
|
const key = token.family?.key ?? ``;
|
|
606
606
|
const members = result.get(key) ?? [];
|
|
@@ -631,8 +631,8 @@ function TimelineIndex(props) {
|
|
|
631
631
|
}
|
|
632
632
|
function TimelineLog(props) {
|
|
633
633
|
const controls = useTimeline(props.token);
|
|
634
|
-
const
|
|
635
|
-
const [
|
|
634
|
+
const getTimeline = useGetter(findInStore(props.tools.store, props.tools.timelineSelectors, props.token.key));
|
|
635
|
+
const [getOpen, toggle] = useExpanded(props.tools, [props.token.key]);
|
|
636
636
|
return jsx("section", {
|
|
637
637
|
"class": "node",
|
|
638
638
|
get "data-testid"() {
|
|
@@ -642,7 +642,7 @@ function TimelineLog(props) {
|
|
|
642
642
|
jsx("button", {
|
|
643
643
|
"type": "button",
|
|
644
644
|
get "aria-expanded"() {
|
|
645
|
-
return
|
|
645
|
+
return getOpen();
|
|
646
646
|
},
|
|
647
647
|
get "data-testid"() {
|
|
648
648
|
return `open-close-timeline-${props.token.key}`;
|
|
@@ -650,39 +650,39 @@ function TimelineLog(props) {
|
|
|
650
650
|
get "onClick"() {
|
|
651
651
|
return toggle;
|
|
652
652
|
},
|
|
653
|
-
children: () =>
|
|
653
|
+
children: () => getOpen() ? `▾` : `▸`
|
|
654
654
|
}),
|
|
655
655
|
jsx("h2", { children: () => props.token.family?.subKey ?? props.token.key }),
|
|
656
656
|
jsx("small", { children: [
|
|
657
657
|
"(",
|
|
658
|
-
() => controls()
|
|
658
|
+
() => controls.getCursor(),
|
|
659
659
|
"/",
|
|
660
|
-
() => controls()
|
|
660
|
+
() => controls.getLength(),
|
|
661
661
|
")"
|
|
662
662
|
] }),
|
|
663
663
|
jsx("button", {
|
|
664
664
|
"type": "button",
|
|
665
665
|
get "disabled"() {
|
|
666
|
-
return controls()
|
|
666
|
+
return controls.getCursor() === 0;
|
|
667
667
|
},
|
|
668
668
|
"onClick": (() => {
|
|
669
|
-
props.tools.attempt(controls
|
|
669
|
+
props.tools.attempt(controls.undo);
|
|
670
670
|
}),
|
|
671
671
|
children: "undo"
|
|
672
672
|
}),
|
|
673
673
|
jsx("button", {
|
|
674
674
|
"type": "button",
|
|
675
675
|
get "disabled"() {
|
|
676
|
-
return controls()
|
|
676
|
+
return controls.getCursor() === controls.getLength();
|
|
677
677
|
},
|
|
678
678
|
"onClick": (() => {
|
|
679
|
-
props.tools.attempt(controls
|
|
679
|
+
props.tools.attempt(controls.redo);
|
|
680
680
|
}),
|
|
681
681
|
children: "redo"
|
|
682
682
|
})
|
|
683
|
-
] }), () =>
|
|
683
|
+
] }), () => getOpen() ? jsx("section", { children: [() => getTimeline().history.map((event, index) => jsx("section", {
|
|
684
684
|
"key": index,
|
|
685
|
-
children: [() => index ===
|
|
685
|
+
children: [() => index === getTimeline().at ? jsx("p", {
|
|
686
686
|
"class": "you-are-here",
|
|
687
687
|
children: "you are here"
|
|
688
688
|
}) : null, jsx(EventView, {
|
|
@@ -696,7 +696,7 @@ function TimelineLog(props) {
|
|
|
696
696
|
return index;
|
|
697
697
|
}
|
|
698
698
|
})]
|
|
699
|
-
})), () =>
|
|
699
|
+
})), () => getTimeline().at === getTimeline().history.length ? jsx("p", {
|
|
700
700
|
"class": "you-are-here",
|
|
701
701
|
children: "you are here"
|
|
702
702
|
}) : null] }) : null]
|
|
@@ -741,15 +741,15 @@ const VIEWS = [
|
|
|
741
741
|
];
|
|
742
742
|
function Panel(props) {
|
|
743
743
|
const tools = useTools(props.store);
|
|
744
|
-
const [
|
|
745
|
-
const [
|
|
746
|
-
const [
|
|
747
|
-
const view = () => VIEWS.includes(
|
|
748
|
-
const [
|
|
744
|
+
const [getHidden, setHidden] = useSaved(props.store, `🔍 Devtools Are Hidden`, props.hideByDefault);
|
|
745
|
+
const [getOpen, setOpen] = useSaved(props.store, `🔍 Devtools Are Open`, true);
|
|
746
|
+
const [getSelection, setSelection] = useSaved(props.store, `🔍 Devtools View`, `atoms`);
|
|
747
|
+
const view = () => VIEWS.includes(getSelection()) ? getSelection() : `atoms`;
|
|
748
|
+
const [getPosition, setPosition] = useLocal(props.store, {
|
|
749
749
|
x: 0,
|
|
750
750
|
y: 0
|
|
751
751
|
});
|
|
752
|
-
const
|
|
752
|
+
const getError = useGetter(tools.error);
|
|
753
753
|
let panel = null;
|
|
754
754
|
const attach = (element) => {
|
|
755
755
|
panel = element;
|
|
@@ -782,13 +782,13 @@ function Panel(props) {
|
|
|
782
782
|
"data-css": "spa-devtools",
|
|
783
783
|
"data-testid": "atom-io-devtools",
|
|
784
784
|
get "hidden"() {
|
|
785
|
-
return
|
|
785
|
+
return getHidden();
|
|
786
786
|
},
|
|
787
787
|
get "data-open"() {
|
|
788
|
-
return
|
|
788
|
+
return getOpen();
|
|
789
789
|
},
|
|
790
790
|
get "style"() {
|
|
791
|
-
return `transform:translate(${
|
|
791
|
+
return `transform:translate(${getPosition().x}px,${getPosition().y}px)`;
|
|
792
792
|
},
|
|
793
793
|
children: [jsx("header", {
|
|
794
794
|
"class": "toolbar",
|
|
@@ -804,7 +804,7 @@ function Panel(props) {
|
|
|
804
804
|
id: event.pointerId,
|
|
805
805
|
startX: event.clientX,
|
|
806
806
|
startY: event.clientY,
|
|
807
|
-
...
|
|
807
|
+
...getPosition()
|
|
808
808
|
};
|
|
809
809
|
event.currentTarget.setPointerCapture(event.pointerId);
|
|
810
810
|
}),
|
|
@@ -815,8 +815,8 @@ function Panel(props) {
|
|
|
815
815
|
x: drag.x + event.clientX - drag.startX,
|
|
816
816
|
y: drag.y + event.clientY - drag.startY
|
|
817
817
|
};
|
|
818
|
-
next.x = Math.max(
|
|
819
|
-
next.y = Math.max(
|
|
818
|
+
next.x = Math.max(getPosition().x - rect.left, Math.min(next.x, getPosition().x + window.innerWidth - rect.right));
|
|
819
|
+
next.y = Math.max(getPosition().y - rect.top, Math.min(next.y, getPosition().y + window.innerHeight - rect.bottom));
|
|
820
820
|
setPosition(next);
|
|
821
821
|
}),
|
|
822
822
|
"onPointerup": (() => {
|
|
@@ -843,7 +843,7 @@ function Panel(props) {
|
|
|
843
843
|
children: "⠿"
|
|
844
844
|
}),
|
|
845
845
|
jsx("h1", { children: "atom.io" }),
|
|
846
|
-
() =>
|
|
846
|
+
() => getOpen() ? jsx("nav", {
|
|
847
847
|
"aria-label": "Debugger views",
|
|
848
848
|
children: () => VIEWS.map((name) => jsx("button", {
|
|
849
849
|
"type": "button",
|
|
@@ -862,13 +862,13 @@ function Panel(props) {
|
|
|
862
862
|
jsx("button", {
|
|
863
863
|
"type": "button",
|
|
864
864
|
get "aria-label"() {
|
|
865
|
-
return
|
|
865
|
+
return getOpen() ? `Minimize debugger` : `Open debugger`;
|
|
866
866
|
},
|
|
867
867
|
"data-testid": "open-close-devtools",
|
|
868
868
|
"onClick": (() => {
|
|
869
869
|
setOpen((value) => !value);
|
|
870
870
|
}),
|
|
871
|
-
children: () =>
|
|
871
|
+
children: () => getOpen() ? `−` : `🔍`
|
|
872
872
|
}),
|
|
873
873
|
jsx("button", {
|
|
874
874
|
"type": "button",
|
|
@@ -879,9 +879,9 @@ function Panel(props) {
|
|
|
879
879
|
children: "×"
|
|
880
880
|
})
|
|
881
881
|
]
|
|
882
|
-
}), () =>
|
|
882
|
+
}), () => getOpen() ? jsx(null, { children: [() => getError() ? jsx("p", {
|
|
883
883
|
"role": "alert",
|
|
884
|
-
children: () =>
|
|
884
|
+
children: () => getError()
|
|
885
885
|
}) : null, () => view() === `atoms` || view() === `selectors` ? jsx(StateIndex, {
|
|
886
886
|
"key": view(),
|
|
887
887
|
get "tools"() {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["_spa","_spa","_spa"],"sources":["../src/store.ts","../src/value.ts","../src/JsonEditor.tsx","../src/Indexes.tsx","../src/AtomIODevtools.tsx"],"sourcesContent":["import type { Accessor } from \"@atom.io/spa\"\nimport { onCleanup, useId, useSetter, useValue } from \"@atom.io/spa\"\nimport type { RegularAtomFamilyToken, RegularAtomToken, Setter } from \"atom.io\"\nimport type { RootStore } from \"atom.io/internal\"\nimport {\n\tcreateRegularAtomFamily,\n\tdisposeFromStore,\n\tfindInStore,\n\tsetIntoStore,\n} from \"atom.io/internal\"\nimport type { IntrospectionStates } from \"atom.io/introspection\"\nimport { attachIntrospectionStates } from \"atom.io/introspection\"\n\nconst locals = new WeakMap<RootStore, RegularAtomFamilyToken<unknown, string>>()\nconst introspection = new WeakMap<RootStore, IntrospectionStates>()\nexport type Path = readonly string[]\nexport interface Tools extends IntrospectionStates {\n\tstore: RootStore\n\texpanded: RegularAtomToken<Record<string, boolean>>\n\terror: RegularAtomToken<string>\n\tattempt(run: () => void): void\n}\n\nexport function ownedAtom<T>(store: RootStore, initial: T): RegularAtomToken<T> {\n\tlet family = locals.get(store)\n\tif (!family) {\n\t\tfamily = createRegularAtomFamily<unknown, string, never>(store, {\n\t\t\tkey: `🔍 spa devtools`,\n\t\t\tdefault: undefined,\n\t\t})\n\t\tlocals.set(store, family)\n\t}\n\tconst token = findInStore(store, family, useId()) as RegularAtomToken<T>\n\tsetIntoStore(store, token, initial)\n\tonCleanup(() => {\n\t\tdisposeFromStore(store, token)\n\t})\n\treturn token\n}\nexport function useLocal<T>(\n\tstore: RootStore,\n\tinitial: T,\n): [Accessor<T>, (next: T | Setter<T>) => void] {\n\tconst token = ownedAtom(store, initial)\n\treturn [useValue(token) as Accessor<T>, useSetter(token)]\n}\nexport function useSaved<T>(\n\tstore: RootStore,\n\tkey: string,\n\tfallback: T,\n): [Accessor<T>, (next: T | Setter<T>) => void] {\n\tlet initial = fallback\n\ttry {\n\t\tconst saved: unknown = JSON.parse(localStorage.getItem(key) ?? `null`)\n\t\tif (saved !== null && typeof saved === typeof fallback) initial = saved as T\n\t} catch {\n\t\t/* Storage may be unavailable. */\n\t}\n\tconst [value, set] = useLocal(store, initial)\n\treturn [\n\t\tvalue,\n\t\t(next) => {\n\t\t\tset(next)\n\t\t\ttry {\n\t\t\t\tlocalStorage.setItem(key, JSON.stringify(value()))\n\t\t\t} catch {\n\t\t\t\t/* Keep in-memory state when storage is unavailable. */\n\t\t\t}\n\t\t},\n\t]\n}\nexport function useTools(store: RootStore): Tools {\n\tlet states = introspection.get(store)\n\tif (!states) {\n\t\tstates = attachIntrospectionStates(store)\n\t\tintrospection.set(store, states)\n\t}\n\tlet expanded: Record<string, boolean> = {}\n\ttry {\n\t\tconst saved: unknown = JSON.parse(\n\t\t\tlocalStorage.getItem(`🔍 spa expanded`) ?? `{}`,\n\t\t)\n\t\tif (\n\t\t\tsaved &&\n\t\t\ttypeof saved === `object` &&\n\t\t\t!Array.isArray(saved) &&\n\t\t\tObject.values(saved).every((value) => typeof value === `boolean`)\n\t\t)\n\t\t\texpanded = saved as Record<string, boolean>\n\t} catch {\n\t\t/* Ignore corrupt or unavailable storage. */\n\t}\n\tconst error = ownedAtom(store, ``)\n\tconst setError = useSetter(error)\n\treturn {\n\t\t...states,\n\t\tstore,\n\t\terror,\n\t\texpanded: ownedAtom(store, expanded),\n\t\tattempt(run) {\n\t\t\ttry {\n\t\t\t\trun()\n\t\t\t\tsetError(``)\n\t\t\t} catch (caught) {\n\t\t\t\tsetError(caught instanceof Error ? caught.message : String(caught))\n\t\t\t}\n\t\t},\n\t}\n}\nexport function useExpanded(\n\ttools: Tools,\n\tpath: Path,\n\tinitial = false,\n): [Accessor<boolean>, (event: MouseEvent) => void] {\n\tconst value = useValue(tools.expanded)\n\tconst set = useSetter(tools.expanded)\n\tconst key = JSON.stringify(path)\n\tconst group = `all:${JSON.stringify(path.slice(0, -1))}`\n\tconst read = () => value()[key] ?? value()[group] ?? initial\n\treturn [\n\t\tread,\n\t\t(event) => {\n\t\t\tconst next = !read()\n\t\t\tset((previous) => {\n\t\t\t\tconst result = { ...previous }\n\t\t\t\tif (event.shiftKey) {\n\t\t\t\t\tconst parent = JSON.stringify(path.slice(0, -1))\n\t\t\t\t\tfor (const item of Object.keys(result)) {\n\t\t\t\t\t\tif (item.startsWith(`[`)) {\n\t\t\t\t\t\t\tconst candidate = JSON.parse(item) as string[]\n\t\t\t\t\t\t\tif (JSON.stringify(candidate.slice(0, -1)) === parent)\n\t\t\t\t\t\t\t\tReflect.deleteProperty(result, item)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tresult[group] = next\n\t\t\t\t} else {\n\t\t\t\t\tresult[key] = next\n\t\t\t\t}\n\t\t\t\ttry {\n\t\t\t\t\tlocalStorage.setItem(`🔍 spa expanded`, JSON.stringify(result))\n\t\t\t\t} catch {\n\t\t\t\t\t/* Keep in-memory expansion state. */\n\t\t\t\t}\n\t\t\t\treturn result\n\t\t\t})\n\t\t},\n\t]\n}\n","export type Change = (update: (previous: unknown) => unknown) => void\nexport type JsonKind =\n\t| `string`\n\t| `number`\n\t| `boolean`\n\t| `null`\n\t| `array`\n\t| `object`\nexport function kind(value: unknown): JsonKind | `other` {\n\tif (value === null) return `null`\n\tif (Array.isArray(value)) return `array`\n\tif (\n\t\ttypeof value === `object` &&\n\t\t(Object.getPrototypeOf(value) === Object.prototype ||\n\t\t\tObject.getPrototypeOf(value) === null)\n\t)\n\t\treturn `object`\n\tif (\n\t\ttypeof value === `string` ||\n\t\ttypeof value === `number` ||\n\t\ttypeof value === `boolean`\n\t)\n\t\treturn typeof value as JsonKind\n\treturn `other`\n}\nexport function describe(value: unknown): string {\n\ttry {\n\t\tconst seen = new WeakSet<object>()\n\t\treturn (\n\t\t\tJSON.stringify(value, (_, item: unknown) => {\n\t\t\t\tif (typeof item === `bigint`) return `${item}n`\n\t\t\t\tif (item instanceof Error) return `${item.name}: ${item.message}`\n\t\t\t\tif (item instanceof Map) return { Map: [...item] }\n\t\t\t\tif (item instanceof Set) return { Set: [...item] }\n\t\t\t\tif (typeof item === `object` && item !== null) {\n\t\t\t\t\tif (seen.has(item)) return `[Circular]`\n\t\t\t\t\tseen.add(item)\n\t\t\t\t}\n\t\t\t\treturn item\n\t\t\t}) ?? String(value)\n\t\t)\n\t} catch {\n\t\treturn Object.prototype.toString.call(value)\n\t}\n}\nexport function convert(value: unknown, type: JsonKind): unknown {\n\tswitch (type) {\n\t\tcase `null`:\n\t\t\treturn null\n\t\tcase `string`:\n\t\t\treturn value == null\n\t\t\t\t? ``\n\t\t\t\t: typeof value === `string`\n\t\t\t\t\t? value\n\t\t\t\t\t: describe(value)\n\t\tcase `number`: {\n\t\t\tconst next = Array.isArray(value) ? value.length : Number(value)\n\t\t\treturn Number.isFinite(next) ? next : 0\n\t\t}\n\t\tcase `boolean`:\n\t\t\treturn typeof value === `string` ? value === `true` : Boolean(value)\n\t\tcase `array`:\n\t\t\treturn Array.isArray(value)\n\t\t\t\t? value\n\t\t\t\t: value == null\n\t\t\t\t\t? []\n\t\t\t\t\t: typeof value === `object`\n\t\t\t\t\t\t? Object.entries(value)\n\t\t\t\t\t\t: [value]\n\t\tcase `object`:\n\t\t\treturn kind(value) === `object`\n\t\t\t\t? value\n\t\t\t\t: Array.isArray(value)\n\t\t\t\t\t? Object.fromEntries(value.map((item, index) => [index, item]))\n\t\t\t\t\t: value == null\n\t\t\t\t\t\t? {}\n\t\t\t\t\t\t: { value }\n\t}\n}\n","import type { Child, VNode } from \"@atom.io/spa\"\n\nimport type { Path, Tools } from \"./store.ts\"\nimport { useExpanded } from \"./store.ts\"\nimport type { Change, JsonKind } from \"./value.ts\"\nimport { convert, describe, kind } from \"./value.ts\"\n\ninterface EditorProps {\n\ttools: Tools\n\tvalue: unknown\n\tchange?: Change | undefined\n\tpath: Path\n\tancestors?: readonly unknown[]\n\tlabel?: Child\n\tactions?: Child\n}\nconst TYPES: JsonKind[] = [\n\t`string`,\n\t`number`,\n\t`boolean`,\n\t`null`,\n\t`array`,\n\t`object`,\n]\nexport function JsonEditor(props: EditorProps): VNode {\n\tconst [expanded, toggle] = useExpanded(props.tools, props.path)\n\tconst circular = () =>\n\t\t(props.ancestors?.includes(props.value) ?? false) || props.path.length > 50\n\tconst container = () =>\n\t\t!circular() &&\n\t\t(kind(props.value) === `object` || kind(props.value) === `array`)\n\tconst entries = () => Object.entries(props.value as Record<string, unknown>)\n\treturn (\n\t\t<section\n\t\t\tclass=\"json-editor\"\n\t\t\tdata-testid={`${props.path.join(`.`)}-state-editor`}\n\t\t>\n\t\t\t<header>\n\t\t\t\t{container() ? (\n\t\t\t\t\t<button\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\taria-label={`Expand ${props.path.at(-1)}`}\n\t\t\t\t\t\taria-expanded={expanded()}\n\t\t\t\t\t\tonClick={toggle}\n\t\t\t\t\t>\n\t\t\t\t\t\t{expanded() ? `▾` : `▸`}\n\t\t\t\t\t</button>\n\t\t\t\t) : (\n\t\t\t\t\t<span class=\"carat-spacer\" />\n\t\t\t\t)}\n\t\t\t\t<span class=\"field-name\">{props.label}</span>\n\t\t\t\t{props.change ? (\n\t\t\t\t\t<select\n\t\t\t\t\t\taria-label={`Type of ${props.path.at(-1)}`}\n\t\t\t\t\t\tvalue={kind(props.value)}\n\t\t\t\t\t\tonChange={(event) => {\n\t\t\t\t\t\t\tconst type = event.currentTarget.value as JsonKind\n\t\t\t\t\t\t\tprops.tools.attempt(() => {\n\t\t\t\t\t\t\t\tprops.change?.((previous) => convert(previous, type))\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t}}\n\t\t\t\t\t>\n\t\t\t\t\t\t<option value=\"other\" disabled>\n\t\t\t\t\t\t\tother\n\t\t\t\t\t\t</option>\n\t\t\t\t\t\t{TYPES.map((type) => (\n\t\t\t\t\t\t\t<option value={type}>{type}</option>\n\t\t\t\t\t\t))}\n\t\t\t\t\t</select>\n\t\t\t\t) : (\n\t\t\t\t\t<small>{kind(props.value)}</small>\n\t\t\t\t)}\n\t\t\t\t{container() ? (\n\t\t\t\t\t<code title={describe(props.value)}>{describe(props.value)}</code>\n\t\t\t\t) : circular() ? (\n\t\t\t\t\t<code>[Circular]</code>\n\t\t\t\t) : (\n\t\t\t\t\t<Primitive\n\t\t\t\t\t\tvalue={props.value}\n\t\t\t\t\t\tchange={props.change}\n\t\t\t\t\t\ttools={props.tools}\n\t\t\t\t\t\tlabel={props.path.at(-1) ?? `value`}\n\t\t\t\t\t/>\n\t\t\t\t)}\n\t\t\t\t{props.actions}\n\t\t\t</header>\n\t\t\t{container() && expanded() ? (\n\t\t\t\t<section class=\"properties\">\n\t\t\t\t\t{entries().map(([key]) => (\n\t\t\t\t\t\t<Property key={key} name={key} editor={props} />\n\t\t\t\t\t))}\n\t\t\t\t\t{props.change ? (\n\t\t\t\t\t\t<footer>\n\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\tonClick={() => {\n\t\t\t\t\t\t\t\t\tprops.tools.attempt(() => {\n\t\t\t\t\t\t\t\t\t\tprops.change?.((previous) => {\n\t\t\t\t\t\t\t\t\t\t\tif (Array.isArray(previous)) return [...previous, null]\n\t\t\t\t\t\t\t\t\t\t\tconst object = previous as Record<string, unknown>\n\t\t\t\t\t\t\t\t\t\t\tlet name = `new`\n\t\t\t\t\t\t\t\t\t\t\twhile (Object.hasOwn(object, name)) name += `_`\n\t\t\t\t\t\t\t\t\t\t\treturn { ...object, [name]: null }\n\t\t\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\tadd {Array.isArray(props.value) ? `item` : `property`}\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t{Array.isArray(props.value) ? (\n\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\tonClick={() => {\n\t\t\t\t\t\t\t\t\t\tprops.tools.attempt(() => {\n\t\t\t\t\t\t\t\t\t\t\tprops.change?.((previous) =>\n\t\t\t\t\t\t\t\t\t\t\t\t[...(previous as unknown[])].sort((a, b) =>\n\t\t\t\t\t\t\t\t\t\t\t\t\ttypeof a === `number` && typeof b === `number`\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t? a - b\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t: describe(a).localeCompare(describe(b)),\n\t\t\t\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\tsort\n\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t</footer>\n\t\t\t\t\t) : null}\n\t\t\t\t</section>\n\t\t\t) : null}\n\t\t</section>\n\t)\n}\nfunction Primitive(props: {\n\tvalue: unknown\n\tchange?: Change | undefined\n\ttools: Tools\n\tlabel: string\n}): VNode {\n\tconst set = (value: unknown) => {\n\t\tprops.tools.attempt(() => {\n\t\t\tprops.change?.(() => value)\n\t\t})\n\t}\n\treturn (\n\t\t<span class=\"primitive\">\n\t\t\t{kind(props.value) === `boolean` ? (\n\t\t\t\t<input\n\t\t\t\t\taria-label={props.label}\n\t\t\t\t\ttype=\"checkbox\"\n\t\t\t\t\tchecked={Boolean(props.value)}\n\t\t\t\t\tdisabled={!props.change}\n\t\t\t\t\tonChange={(event) => {\n\t\t\t\t\t\tset(event.currentTarget.checked)\n\t\t\t\t\t}}\n\t\t\t\t/>\n\t\t\t) : kind(props.value) === `string` || kind(props.value) === `number` ? (\n\t\t\t\t<input\n\t\t\t\t\taria-label={props.label}\n\t\t\t\t\ttype=\"text\"\n\t\t\t\t\tinputmode={kind(props.value) === `number` ? `decimal` : `text`}\n\t\t\t\t\tvalue={String(props.value)}\n\t\t\t\t\treadOnly={!props.change}\n\t\t\t\t\tonInput={(event) => {\n\t\t\t\t\t\tconst text = event.currentTarget.value\n\t\t\t\t\t\tif (kind(props.value) === `string`) set(text)\n\t\t\t\t\t\telse if (text.trim() && Number.isFinite(Number(text)))\n\t\t\t\t\t\t\tset(Number(text))\n\t\t\t\t\t}}\n\t\t\t\t\tonBlur={(event) => {\n\t\t\t\t\t\tevent.currentTarget.value = String(props.value)\n\t\t\t\t\t}}\n\t\t\t\t/>\n\t\t\t) : (\n\t\t\t\t<code>{describe(props.value)}</code>\n\t\t\t)}\n\t\t</span>\n\t)\n}\nfunction Property(props: { name: string; editor: EditorProps }): VNode {\n\tconst parent = props.editor\n\tconst read = () => (parent.value as Record<string, unknown>)[props.name]\n\tconst change: Change = (update) => {\n\t\tparent.change?.((previous) => {\n\t\t\tif (Array.isArray(previous))\n\t\t\t\treturn previous.map((value, index) =>\n\t\t\t\t\tindex === Number(props.name) ? update(value) : value,\n\t\t\t\t)\n\t\t\tconst object = previous as Record<string, unknown>\n\t\t\treturn { ...object, [props.name]: update(object[props.name]) }\n\t\t})\n\t}\n\treturn (\n\t\t<section class=\"property\">\n\t\t\t<JsonEditor\n\t\t\t\tlabel={\n\t\t\t\t\tparent.change && !Array.isArray(parent.value) ? (\n\t\t\t\t\t\t<input\n\t\t\t\t\t\t\taria-label={`Rename ${props.name}`}\n\t\t\t\t\t\t\tstyle={`width:calc(${Math.max(1, props.name.length)}ch + 6px)`}\n\t\t\t\t\t\t\tvalue={props.name}\n\t\t\t\t\t\t\tonChange={(event) => {\n\t\t\t\t\t\t\t\tconst input = event.currentTarget\n\t\t\t\t\t\t\t\tconst name = input.value\n\t\t\t\t\t\t\t\tparent.tools.attempt(() => {\n\t\t\t\t\t\t\t\t\tparent.change?.((previous) => {\n\t\t\t\t\t\t\t\t\t\tconst object = previous as Record<string, unknown>\n\t\t\t\t\t\t\t\t\t\tif (name !== props.name && Object.hasOwn(object, name))\n\t\t\t\t\t\t\t\t\t\t\tthrow new Error(`Property \"${name}\" already exists`)\n\t\t\t\t\t\t\t\t\t\treturn Object.fromEntries(\n\t\t\t\t\t\t\t\t\t\t\tObject.entries(object).map(([key, value]) => [\n\t\t\t\t\t\t\t\t\t\t\t\tkey === props.name ? name : key,\n\t\t\t\t\t\t\t\t\t\t\t\tvalue,\n\t\t\t\t\t\t\t\t\t\t\t]),\n\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\tinput.value = props.name\n\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t/>\n\t\t\t\t\t) : (\n\t\t\t\t\t\t<strong>{props.name}</strong>\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t\tactions={\n\t\t\t\t\tparent.change ? (\n\t\t\t\t\t\t<button\n\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\taria-label={`Delete ${props.name}`}\n\t\t\t\t\t\t\tonClick={() => {\n\t\t\t\t\t\t\t\tparent.tools.attempt(() => {\n\t\t\t\t\t\t\t\t\tparent.change?.((previous) =>\n\t\t\t\t\t\t\t\t\t\tArray.isArray(previous)\n\t\t\t\t\t\t\t\t\t\t\t? previous.filter(\n\t\t\t\t\t\t\t\t\t\t\t\t\t(_, index) => index !== Number(props.name),\n\t\t\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t\t\t: Object.fromEntries(\n\t\t\t\t\t\t\t\t\t\t\t\t\tObject.entries(previous as object).filter(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t([key]) => key !== props.name,\n\t\t\t\t\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t×\n\t\t\t\t\t\t</button>\n\t\t\t\t\t) : null\n\t\t\t\t}\n\t\t\t\ttools={parent.tools}\n\t\t\t\tpath={[...parent.path, props.name]}\n\t\t\t\tvalue={read()}\n\t\t\t\tchange={parent.change ? change : undefined}\n\t\t\t\tancestors={[...(parent.ancestors ?? []), parent.value]}\n\t\t\t/>\n\t\t</section>\n\t)\n}\n","import type { VNode } from \"@atom.io/spa\"\nimport { useSetter, useTimeline, useValue } from \"@atom.io/spa\"\nimport type {\n\tReadableToken,\n\tTimelineToken,\n\tTransactionToken,\n\tWritableToken,\n} from \"atom.io\"\nimport {\n\tdisposeFromStore,\n\tfindInStore,\n\tisReservedIntrospectionKey,\n} from \"atom.io/internal\"\nimport type { FamilyNode } from \"atom.io/introspection\"\n\nimport { JsonEditor } from \"./JsonEditor.tsx\"\nimport type { Tools } from \"./store.ts\"\nimport { useExpanded } from \"./store.ts\"\nimport { describe } from \"./value.ts\"\n\ntype Token = ReadableToken<unknown, any, any>\nexport function StateIndex(props: {\n\ttools: Tools\n\ttype: `atoms` | `selectors`\n}): VNode {\n\tconst index = useValue<Map<string, Token | FamilyNode<Token>>>(\n\t\tprops.type === `atoms` ? props.tools.atomIndex : props.tools.selectorIndex,\n\t)\n\treturn (\n\t\t<article class=\"index\" data-testid=\"state-index\">\n\t\t\t{index().size === 0 ? (\n\t\t\t\t<p>(no {props.type})</p>\n\t\t\t) : (\n\t\t\t\t[...index().values()].map((node) => (\n\t\t\t\t\t<StateNode key={node.key} tools={props.tools} node={node} />\n\t\t\t\t))\n\t\t\t)}\n\t\t</article>\n\t)\n}\nfunction StateNode(props: {\n\ttools: Tools\n\tnode: Token | FamilyNode<Token>\n}): VNode {\n\tconst [open, toggle] = useExpanded(props.tools, [props.node.key])\n\treturn (\n\t\t<section class=\"node\" data-testid={`state-${props.node.key}`}>\n\t\t\t{`familyMembers` in props.node ? (\n\t\t\t\t<>\n\t\t\t\t\t<header>\n\t\t\t\t\t\t<button\n\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\taria-expanded={open()}\n\t\t\t\t\t\t\tdata-testid={`open-close-state-family-${props.node.key}`}\n\t\t\t\t\t\t\tonClick={toggle}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{open() ? `▾` : `▸`}\n\t\t\t\t\t\t</button>\n\t\t\t\t\t\t<h2>{props.node.key}</h2>\n\t\t\t\t\t\t<small>family</small>\n\t\t\t\t\t</header>\n\t\t\t\t\t{open()\n\t\t\t\t\t\t? [...props.node.familyMembers.values()].map((token) => (\n\t\t\t\t\t\t\t\t<StateLeaf key={token.key} tools={props.tools} token={token} />\n\t\t\t\t\t\t\t))\n\t\t\t\t\t\t: null}\n\t\t\t\t</>\n\t\t\t) : (\n\t\t\t\t<StateLeaf tools={props.tools} token={props.node} />\n\t\t\t)}\n\t\t</section>\n\t)\n}\nfunction StateLeaf(props: { tools: Tools; token: Token }): VNode {\n\tconst read = useValue(props.token)\n\tconst writable = !props.token.type.startsWith(`readonly`)\n\tconst set = writable\n\t\t? useSetter(props.token as WritableToken<unknown, any, any>)\n\t\t: undefined\n\tconst value = () => {\n\t\ttry {\n\t\t\treturn read()\n\t\t} catch (error) {\n\t\t\treturn error\n\t\t}\n\t}\n\treturn (\n\t\t<section class=\"state-leaf\" data-testid={`state-value-${props.token.key}`}>\n\t\t\t<JsonEditor\n\t\t\t\tlabel={<h3>{props.token.family?.subKey ?? props.token.key}</h3>}\n\t\t\t\tactions={\n\t\t\t\t\tprops.token.family ? (\n\t\t\t\t\t\t<button\n\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\taria-label={`Dispose ${props.token.key}`}\n\t\t\t\t\t\t\tonClick={() => {\n\t\t\t\t\t\t\t\tprops.tools.attempt(() => {\n\t\t\t\t\t\t\t\t\tdisposeFromStore(props.tools.store, props.token)\n\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t×\n\t\t\t\t\t\t</button>\n\t\t\t\t\t) : null\n\t\t\t\t}\n\t\t\t\ttools={props.tools}\n\t\t\t\tvalue={value()}\n\t\t\t\tchange={\n\t\t\t\t\tset\n\t\t\t\t\t\t? (update) => {\n\t\t\t\t\t\t\t\t// Validate edits before opening an atom.io write operation.\n\t\t\t\t\t\t\t\tconst next = update(read())\n\t\t\t\t\t\t\t\tset(() => next)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t: undefined\n\t\t\t\t}\n\t\t\t\tpath={\n\t\t\t\t\tprops.token.family\n\t\t\t\t\t\t? [props.token.family.key, props.token.family.subKey]\n\t\t\t\t\t\t: [props.token.key]\n\t\t\t\t}\n\t\t\t/>\n\t\t</section>\n\t)\n}\nexport function TransactionIndex(props: { tools: Tools }): VNode {\n\tconst index = useValue(props.tools.transactionIndex)\n\tconst visible = () =>\n\t\tindex().filter((token) => !isReservedIntrospectionKey(token.key))\n\treturn (\n\t\t<article class=\"index\" data-testid=\"transaction-index\">\n\t\t\t{visible().length === 0 ? (\n\t\t\t\t<p>(no transactions)</p>\n\t\t\t) : (\n\t\t\t\tvisible().map((token) => (\n\t\t\t\t\t<TransactionLog key={token.key} tools={props.tools} token={token} />\n\t\t\t\t))\n\t\t\t)}\n\t\t</article>\n\t)\n}\nfunction TransactionLog(props: {\n\ttools: Tools\n\ttoken: TransactionToken<any>\n}): VNode {\n\tconst log = useValue(\n\t\tfindInStore(\n\t\t\tprops.tools.store,\n\t\t\tprops.tools.transactionLogSelectors,\n\t\t\tprops.token.key,\n\t\t),\n\t)\n\tconst [open, toggle] = useExpanded(props.tools, [props.token.key])\n\treturn (\n\t\t<section class=\"node\" data-testid={`transaction-${props.token.key}`}>\n\t\t\t<header>\n\t\t\t\t<button\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\taria-expanded={open()}\n\t\t\t\t\tdata-testid={`open-close-transaction-${props.token.key}`}\n\t\t\t\t\tonClick={toggle}\n\t\t\t\t>\n\t\t\t\t\t{open() ? `▾` : `▸`}\n\t\t\t\t</button>\n\t\t\t\t<h2>{props.token.key}</h2>\n\t\t\t\t<small>({log().length})</small>\n\t\t\t</header>\n\t\t\t{open()\n\t\t\t\t? log().map((event, index) => (\n\t\t\t\t\t\t<EventView\n\t\t\t\t\t\t\tkey={index}\n\t\t\t\t\t\t\ttools={props.tools}\n\t\t\t\t\t\t\tevent={event}\n\t\t\t\t\t\t\tindex={index}\n\t\t\t\t\t\t/>\n\t\t\t\t\t))\n\t\t\t\t: null}\n\t\t</section>\n\t)\n}\n// Events are recursive discriminated records, including user-defined transaction outputs.\nexport function EventView(props: {\n\ttools: Tools\n\tevent: any\n\tindex: number\n}): VNode {\n\tconst [open, toggle] = useExpanded(\n\t\tprops.tools,\n\t\t[`event`, props.event.token?.key ?? ``, String(props.index)],\n\t\ttrue,\n\t)\n\treturn (\n\t\t<section\n\t\t\tclass=\"event\"\n\t\t\tdata-testid={`${props.event.type === `transaction_outcome` ? `transaction` : `timeline`}-update-${props.event.token?.key}-${props.index}`}\n\t\t>\n\t\t\t<header>\n\t\t\t\t<button type=\"button\" aria-expanded={open()} onClick={toggle}>\n\t\t\t\t\t{open() ? `▾` : `▸`}\n\t\t\t\t</button>\n\t\t\t\t<strong>{props.event.token?.key}</strong>\n\t\t\t\t<small>{props.event.type}</small>\n\t\t\t</header>\n\t\t\t{open() ? (\n\t\t\t\tprops.event.type === `transaction_outcome` ? (\n\t\t\t\t\t<>\n\t\t\t\t\t\t<p>\n\t\t\t\t\t\t\tparams: <code>{describe(props.event.params)}</code>\n\t\t\t\t\t\t</p>\n\t\t\t\t\t\t<p>\n\t\t\t\t\t\t\toutput: <code>{describe(props.event.output)}</code>\n\t\t\t\t\t\t</p>\n\t\t\t\t\t\t<section class=\"impact\">\n\t\t\t\t\t\t\t{props.event.subEvents.map((event: any, index: number) => (\n\t\t\t\t\t\t\t\t<EventView\n\t\t\t\t\t\t\t\t\tkey={index}\n\t\t\t\t\t\t\t\t\ttools={props.tools}\n\t\t\t\t\t\t\t\t\tevent={event}\n\t\t\t\t\t\t\t\t\tindex={index}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t</section>\n\t\t\t\t\t</>\n\t\t\t\t) : props.event.update ? (\n\t\t\t\t\t<p>\n\t\t\t\t\t\t<del>{describe(props.event.update.oldValue)}</del> →{` `}\n\t\t\t\t\t\t<ins>{describe(props.event.update.newValue)}</ins>\n\t\t\t\t\t</p>\n\t\t\t\t) : (\n\t\t\t\t\t<code>{describe(props.event)}</code>\n\t\t\t\t)\n\t\t\t) : null}\n\t\t</section>\n\t)\n}\nexport function TimelineIndex(props: { tools: Tools }): VNode {\n\tconst index = useValue(props.tools.timelineIndex)\n\tconst groups = () => {\n\t\tconst result = new Map<string, TimelineToken<any>[]>()\n\t\tfor (const token of index()) {\n\t\t\tif (isReservedIntrospectionKey(token.key) || token.key.startsWith(`👁🗨`))\n\t\t\t\tcontinue\n\t\t\tconst key = token.family?.key ?? ``\n\t\t\tconst members = result.get(key) ?? []\n\t\t\tmembers.push(token)\n\t\t\tresult.set(key, members)\n\t\t}\n\t\treturn [...result]\n\t}\n\treturn (\n\t\t<article class=\"index\" data-testid=\"timeline-index\">\n\t\t\t{groups().length === 0 ? (\n\t\t\t\t<p>(no timelines)</p>\n\t\t\t) : (\n\t\t\t\tgroups().map(([family, members]) => (\n\t\t\t\t\t<section\n\t\t\t\t\t\tkey={family}\n\t\t\t\t\t\tdata-testid={family ? `timeline-family-${family}` : undefined}\n\t\t\t\t\t>\n\t\t\t\t\t\t{family ? <h2>{family}</h2> : null}\n\t\t\t\t\t\t{members.map((token) => (\n\t\t\t\t\t\t\t<TimelineLog key={token.key} tools={props.tools} token={token} />\n\t\t\t\t\t\t))}\n\t\t\t\t\t</section>\n\t\t\t\t))\n\t\t\t)}\n\t\t</article>\n\t)\n}\nfunction TimelineLog(props: { tools: Tools; token: TimelineToken<any> }): VNode {\n\tconst controls = useTimeline(props.token)\n\tconst log = useValue(\n\t\tfindInStore(\n\t\t\tprops.tools.store,\n\t\t\tprops.tools.timelineSelectors,\n\t\t\tprops.token.key,\n\t\t),\n\t)\n\tconst [open, toggle] = useExpanded(props.tools, [props.token.key])\n\treturn (\n\t\t<section class=\"node\" data-testid={`timeline-${props.token.key}`}>\n\t\t\t<header>\n\t\t\t\t<button\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\taria-expanded={open()}\n\t\t\t\t\tdata-testid={`open-close-timeline-${props.token.key}`}\n\t\t\t\t\tonClick={toggle}\n\t\t\t\t>\n\t\t\t\t\t{open() ? `▾` : `▸`}\n\t\t\t\t</button>\n\t\t\t\t<h2>{props.token.family?.subKey ?? props.token.key}</h2>\n\t\t\t\t<small>\n\t\t\t\t\t({controls().at}/{controls().length})\n\t\t\t\t</small>\n\t\t\t\t<button\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\tdisabled={controls().at === 0}\n\t\t\t\t\tonClick={() => {\n\t\t\t\t\t\tprops.tools.attempt(controls().undo)\n\t\t\t\t\t}}\n\t\t\t\t>\n\t\t\t\t\tundo\n\t\t\t\t</button>\n\t\t\t\t<button\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\tdisabled={controls().at === controls().length}\n\t\t\t\t\tonClick={() => {\n\t\t\t\t\t\tprops.tools.attempt(controls().redo)\n\t\t\t\t\t}}\n\t\t\t\t>\n\t\t\t\t\tredo\n\t\t\t\t</button>\n\t\t\t</header>\n\t\t\t{open() ? (\n\t\t\t\t<section>\n\t\t\t\t\t{log().history.map((event, index) => (\n\t\t\t\t\t\t<section key={index}>\n\t\t\t\t\t\t\t{index === log().at ? (\n\t\t\t\t\t\t\t\t<p class=\"you-are-here\">you are here</p>\n\t\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t\t<EventView tools={props.tools} event={event} index={index} />\n\t\t\t\t\t\t</section>\n\t\t\t\t\t))}\n\t\t\t\t\t{log().at === log().history.length ? (\n\t\t\t\t\t\t<p class=\"you-are-here\">you are here</p>\n\t\t\t\t\t) : null}\n\t\t\t\t</section>\n\t\t\t) : null}\n\t\t</section>\n\t)\n}\n","import type { VNode } from \"@atom.io/spa\"\nimport { onCleanup, onMount, render, useValue } from \"@atom.io/spa\"\nimport type { RootStore } from \"atom.io/internal\"\nimport { IMPLICIT } from \"atom.io/internal\"\n\nimport { StateIndex, TimelineIndex, TransactionIndex } from \"./Indexes.tsx\"\nimport { useLocal, useSaved, useTools } from \"./store.ts\"\n\nexport interface AtomIODevtoolsProps {\n\t/** Inspected store. Fixed for this component instance; change its key to replace it. */\n\tstore?: RootStore\n\thideByDefault?: boolean\n}\n/** Owns a rendering root in the inspected store, independently of its parent island. */\nexport function AtomIODevtools(props: AtomIODevtoolsProps): VNode {\n\tlet host: HTMLElement | null = null\n\tconst attach = (element: HTMLElement | null) => {\n\t\thost = element\n\t}\n\tonMount(() => {\n\t\tconst store = props.store ?? IMPLICIT.STORE\n\t\tconst root = render(\n\t\t\t() => <Panel store={store} hideByDefault={props.hideByDefault ?? false} />,\n\t\t\thost!,\n\t\t\t{ store },\n\t\t)\n\t\tonCleanup(root.dispose)\n\t})\n\treturn <spa-devtools ref={attach} />\n}\nconst VIEWS = [`atoms`, `selectors`, `transactions`, `timelines`] as const\ntype View = (typeof VIEWS)[number]\nfunction Panel(props: { store: RootStore; hideByDefault: boolean }): VNode {\n\tconst tools = useTools(props.store)\n\tconst [hidden, setHidden] = useSaved(\n\t\tprops.store,\n\t\t`🔍 Devtools Are Hidden`,\n\t\tprops.hideByDefault,\n\t)\n\tconst [open, setOpen] = useSaved(props.store, `🔍 Devtools Are Open`, true)\n\tconst [selection, setSelection] = useSaved<string>(\n\t\tprops.store,\n\t\t`🔍 Devtools View`,\n\t\t`atoms`,\n\t)\n\tconst view = () =>\n\t\tVIEWS.includes(selection() as View) ? (selection() as View) : `atoms`\n\tconst [position, setPosition] = useLocal(props.store, { x: 0, y: 0 })\n\tconst error = useValue(tools.error)\n\tlet panel: HTMLElement | null = null\n\tconst attach = (element: HTMLElement | null) => {\n\t\tpanel = element\n\t}\n\tlet drag:\n\t\t| { id: number; startX: number; startY: number; x: number; y: number }\n\t\t| undefined\n\tonMount(() => {\n\t\tconst shortcut = (event: KeyboardEvent) => {\n\t\t\tif (event.ctrlKey && event.shiftKey && event.code === `KeyA`) {\n\t\t\t\tevent.preventDefault()\n\t\t\t\tsetHidden((value) => !value)\n\t\t\t}\n\t\t}\n\t\tconst reset = () => {\n\t\t\tsetPosition({ x: 0, y: 0 })\n\t\t}\n\t\tdocument.addEventListener(`keydown`, shortcut)\n\t\twindow.addEventListener(`resize`, reset)\n\t\tonCleanup(() => {\n\t\t\tdocument.removeEventListener(`keydown`, shortcut)\n\t\t\twindow.removeEventListener(`resize`, reset)\n\t\t})\n\t})\n\treturn (\n\t\t<main\n\t\t\tref={attach}\n\t\t\tdata-css=\"spa-devtools\"\n\t\t\tdata-testid=\"atom-io-devtools\"\n\t\t\thidden={hidden()}\n\t\t\tdata-open={open()}\n\t\t\tstyle={`transform:translate(${position().x}px,${position().y}px)`}\n\t\t>\n\t\t\t<header class=\"toolbar\">\n\t\t\t\t<button\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\tclass=\"drag-handle\"\n\t\t\t\t\taria-label=\"Move debugger\"\n\t\t\t\t\ttitle=\"Drag to move; arrow keys to nudge\"\n\t\t\t\t\tonPointerdown={(event) => {\n\t\t\t\t\t\tif (event.button !== 0) return\n\t\t\t\t\t\tdrag = {\n\t\t\t\t\t\t\tid: event.pointerId,\n\t\t\t\t\t\t\tstartX: event.clientX,\n\t\t\t\t\t\t\tstartY: event.clientY,\n\t\t\t\t\t\t\t...position(),\n\t\t\t\t\t\t}\n\t\t\t\t\t\t;(event.currentTarget as HTMLElement).setPointerCapture(\n\t\t\t\t\t\t\tevent.pointerId,\n\t\t\t\t\t\t)\n\t\t\t\t\t}}\n\t\t\t\t\tonPointermove={(event) => {\n\t\t\t\t\t\tif (!drag || drag.id !== event.pointerId || !panel) return\n\t\t\t\t\t\tconst rect = panel.getBoundingClientRect()\n\t\t\t\t\t\tconst next = {\n\t\t\t\t\t\t\tx: drag.x + event.clientX - drag.startX,\n\t\t\t\t\t\t\ty: drag.y + event.clientY - drag.startY,\n\t\t\t\t\t\t}\n\t\t\t\t\t\tnext.x = Math.max(\n\t\t\t\t\t\t\tposition().x - rect.left,\n\t\t\t\t\t\t\tMath.min(next.x, position().x + window.innerWidth - rect.right),\n\t\t\t\t\t\t)\n\t\t\t\t\t\tnext.y = Math.max(\n\t\t\t\t\t\t\tposition().y - rect.top,\n\t\t\t\t\t\t\tMath.min(next.y, position().y + window.innerHeight - rect.bottom),\n\t\t\t\t\t\t)\n\t\t\t\t\t\tsetPosition(next)\n\t\t\t\t\t}}\n\t\t\t\t\tonPointerup={() => {\n\t\t\t\t\t\tdrag = undefined\n\t\t\t\t\t}}\n\t\t\t\t\tonPointercancel={() => {\n\t\t\t\t\t\tdrag = undefined\n\t\t\t\t\t}}\n\t\t\t\t\tonKeydown={(event) => {\n\t\t\t\t\t\tconst step = {\n\t\t\t\t\t\t\tArrowLeft: [-10, 0],\n\t\t\t\t\t\t\tArrowRight: [10, 0],\n\t\t\t\t\t\t\tArrowUp: [0, -10],\n\t\t\t\t\t\t\tArrowDown: [0, 10],\n\t\t\t\t\t\t}[event.key]\n\t\t\t\t\t\tif (step) {\n\t\t\t\t\t\t\tevent.preventDefault()\n\t\t\t\t\t\t\tsetPosition(({ x, y }) => ({ x: x + step[0], y: y + step[1] }))\n\t\t\t\t\t\t}\n\t\t\t\t\t}}\n\t\t\t\t>\n\t\t\t\t\t⠿\n\t\t\t\t</button>\n\t\t\t\t<h1>atom.io</h1>\n\t\t\t\t{open() ? (\n\t\t\t\t\t<nav aria-label=\"Debugger views\">\n\t\t\t\t\t\t{VIEWS.map((name) => (\n\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\taria-pressed={view() === name}\n\t\t\t\t\t\t\t\tdata-testid={`view-${name}`}\n\t\t\t\t\t\t\t\tonClick={() => {\n\t\t\t\t\t\t\t\t\tsetSelection(name)\n\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{name}\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t))}\n\t\t\t\t\t</nav>\n\t\t\t\t) : null}\n\t\t\t\t<button\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\taria-label={open() ? `Minimize debugger` : `Open debugger`}\n\t\t\t\t\tdata-testid=\"open-close-devtools\"\n\t\t\t\t\tonClick={() => {\n\t\t\t\t\t\tsetOpen((value) => !value)\n\t\t\t\t\t}}\n\t\t\t\t>\n\t\t\t\t\t{open() ? `−` : `🔍`}\n\t\t\t\t</button>\n\t\t\t\t<button\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\taria-label=\"Hide debugger (Ctrl+Shift+A)\"\n\t\t\t\t\tonClick={() => {\n\t\t\t\t\t\tsetHidden(true)\n\t\t\t\t\t}}\n\t\t\t\t>\n\t\t\t\t\t×\n\t\t\t\t</button>\n\t\t\t</header>\n\t\t\t{open() ? (\n\t\t\t\t<>\n\t\t\t\t\t{error() ? <p role=\"alert\">{error()}</p> : null}\n\t\t\t\t\t{view() === `atoms` || view() === `selectors` ? (\n\t\t\t\t\t\t<StateIndex\n\t\t\t\t\t\t\tkey={view()}\n\t\t\t\t\t\t\ttools={tools}\n\t\t\t\t\t\t\ttype={view() as `atoms` | `selectors`}\n\t\t\t\t\t\t/>\n\t\t\t\t\t) : view() === `transactions` ? (\n\t\t\t\t\t\t<TransactionIndex tools={tools} />\n\t\t\t\t\t) : (\n\t\t\t\t\t\t<TimelineIndex tools={tools} />\n\t\t\t\t\t)}\n\t\t\t\t</>\n\t\t\t) : null}\n\t\t</main>\n\t)\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["_spa","_spa","_spa"],"sources":["../src/store.ts","../src/value.ts","../src/JsonEditor.tsx","../src/Indexes.tsx","../src/AtomIODevtools.tsx"],"sourcesContent":["import type { Accessor } from \"@atom.io/spa\"\nimport { onCleanup, useGetter, useId, useSetter } from \"@atom.io/spa\"\nimport type { RegularAtomFamilyToken, RegularAtomToken, Setter } from \"atom.io\"\nimport type { RootStore } from \"atom.io/internal\"\nimport {\n\tcreateRegularAtomFamily,\n\tdisposeFromStore,\n\tfindInStore,\n\tsetIntoStore,\n} from \"atom.io/internal\"\nimport type { IntrospectionStates } from \"atom.io/introspection\"\nimport { attachIntrospectionStates } from \"atom.io/introspection\"\n\nconst locals = new WeakMap<RootStore, RegularAtomFamilyToken<unknown, string>>()\nconst introspection = new WeakMap<RootStore, IntrospectionStates>()\nexport type Path = readonly string[]\nexport interface Tools extends IntrospectionStates {\n\tstore: RootStore\n\texpanded: RegularAtomToken<Record<string, boolean>>\n\terror: RegularAtomToken<string>\n\tattempt(run: () => void): void\n}\n\nexport function ownedAtom<T>(store: RootStore, initial: T): RegularAtomToken<T> {\n\tlet family = locals.get(store)\n\tif (!family) {\n\t\tfamily = createRegularAtomFamily<unknown, string, never>(store, {\n\t\t\tkey: `🔍 spa devtools`,\n\t\t\tdefault: undefined,\n\t\t})\n\t\tlocals.set(store, family)\n\t}\n\tconst token = findInStore(store, family, useId()) as RegularAtomToken<T>\n\tsetIntoStore(store, token, initial)\n\tonCleanup(() => {\n\t\tdisposeFromStore(store, token)\n\t})\n\treturn token\n}\nexport function useLocal<T>(\n\tstore: RootStore,\n\tinitial: T,\n): [Accessor<T>, (next: T | Setter<T>) => void] {\n\tconst token = ownedAtom(store, initial)\n\treturn [useGetter(token) as Accessor<T>, useSetter(token)]\n}\nexport function useSaved<T>(\n\tstore: RootStore,\n\tkey: string,\n\tfallback: T,\n): [Accessor<T>, (next: T | Setter<T>) => void] {\n\tlet initial = fallback\n\ttry {\n\t\tconst saved: unknown = JSON.parse(localStorage.getItem(key) ?? `null`)\n\t\tif (saved !== null && typeof saved === typeof fallback) initial = saved as T\n\t} catch {\n\t\t/* Storage may be unavailable. */\n\t}\n\tconst [getValue, setValue] = useLocal(store, initial)\n\treturn [\n\t\tgetValue,\n\t\t(next) => {\n\t\t\tsetValue(next)\n\t\t\ttry {\n\t\t\t\tlocalStorage.setItem(key, JSON.stringify(getValue()))\n\t\t\t} catch {\n\t\t\t\t/* Keep in-memory state when storage is unavailable. */\n\t\t\t}\n\t\t},\n\t]\n}\nexport function useTools(store: RootStore): Tools {\n\tlet states = introspection.get(store)\n\tif (!states) {\n\t\tstates = attachIntrospectionStates(store)\n\t\tintrospection.set(store, states)\n\t}\n\tlet expanded: Record<string, boolean> = {}\n\ttry {\n\t\tconst saved: unknown = JSON.parse(\n\t\t\tlocalStorage.getItem(`🔍 spa expanded`) ?? `{}`,\n\t\t)\n\t\tif (\n\t\t\tsaved &&\n\t\t\ttypeof saved === `object` &&\n\t\t\t!Array.isArray(saved) &&\n\t\t\tObject.values(saved).every((value) => typeof value === `boolean`)\n\t\t)\n\t\t\texpanded = saved as Record<string, boolean>\n\t} catch {\n\t\t/* Ignore corrupt or unavailable storage. */\n\t}\n\tconst error = ownedAtom(store, ``)\n\tconst setError = useSetter(error)\n\treturn {\n\t\t...states,\n\t\tstore,\n\t\terror,\n\t\texpanded: ownedAtom(store, expanded),\n\t\tattempt(run) {\n\t\t\ttry {\n\t\t\t\trun()\n\t\t\t\tsetError(``)\n\t\t\t} catch (caught) {\n\t\t\t\tsetError(caught instanceof Error ? caught.message : String(caught))\n\t\t\t}\n\t\t},\n\t}\n}\nexport function useExpanded(\n\ttools: Tools,\n\tpath: Path,\n\tinitial = false,\n): [Accessor<boolean>, (event: MouseEvent) => void] {\n\tconst getExpanded = useGetter(tools.expanded)\n\tconst setExpanded = useSetter(tools.expanded)\n\tconst key = JSON.stringify(path)\n\tconst group = `all:${JSON.stringify(path.slice(0, -1))}`\n\tconst read = () => getExpanded()[key] ?? getExpanded()[group] ?? initial\n\treturn [\n\t\tread,\n\t\t(event) => {\n\t\t\tconst next = !read()\n\t\t\tsetExpanded((previous) => {\n\t\t\t\tconst result = { ...previous }\n\t\t\t\tif (event.shiftKey) {\n\t\t\t\t\tconst parent = JSON.stringify(path.slice(0, -1))\n\t\t\t\t\tfor (const item of Object.keys(result)) {\n\t\t\t\t\t\tif (item.startsWith(`[`)) {\n\t\t\t\t\t\t\tconst candidate = JSON.parse(item) as string[]\n\t\t\t\t\t\t\tif (JSON.stringify(candidate.slice(0, -1)) === parent)\n\t\t\t\t\t\t\t\tReflect.deleteProperty(result, item)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tresult[group] = next\n\t\t\t\t} else {\n\t\t\t\t\tresult[key] = next\n\t\t\t\t}\n\t\t\t\ttry {\n\t\t\t\t\tlocalStorage.setItem(`🔍 spa expanded`, JSON.stringify(result))\n\t\t\t\t} catch {\n\t\t\t\t\t/* Keep in-memory expansion state. */\n\t\t\t\t}\n\t\t\t\treturn result\n\t\t\t})\n\t\t},\n\t]\n}\n","export type Change = (update: (previous: unknown) => unknown) => void\nexport type JsonKind =\n\t| `string`\n\t| `number`\n\t| `boolean`\n\t| `null`\n\t| `array`\n\t| `object`\nexport function kind(value: unknown): JsonKind | `other` {\n\tif (value === null) return `null`\n\tif (Array.isArray(value)) return `array`\n\tif (\n\t\ttypeof value === `object` &&\n\t\t(Object.getPrototypeOf(value) === Object.prototype ||\n\t\t\tObject.getPrototypeOf(value) === null)\n\t)\n\t\treturn `object`\n\tif (\n\t\ttypeof value === `string` ||\n\t\ttypeof value === `number` ||\n\t\ttypeof value === `boolean`\n\t)\n\t\treturn typeof value as JsonKind\n\treturn `other`\n}\nexport function describe(value: unknown): string {\n\ttry {\n\t\tconst seen = new WeakSet<object>()\n\t\treturn (\n\t\t\tJSON.stringify(value, (_, item: unknown) => {\n\t\t\t\tif (typeof item === `bigint`) return `${item}n`\n\t\t\t\tif (item instanceof Error) return `${item.name}: ${item.message}`\n\t\t\t\tif (item instanceof Map) return { Map: [...item] }\n\t\t\t\tif (item instanceof Set) return { Set: [...item] }\n\t\t\t\tif (typeof item === `object` && item !== null) {\n\t\t\t\t\tif (seen.has(item)) return `[Circular]`\n\t\t\t\t\tseen.add(item)\n\t\t\t\t}\n\t\t\t\treturn item\n\t\t\t}) ?? String(value)\n\t\t)\n\t} catch {\n\t\treturn Object.prototype.toString.call(value)\n\t}\n}\nexport function convert(value: unknown, type: JsonKind): unknown {\n\tswitch (type) {\n\t\tcase `null`:\n\t\t\treturn null\n\t\tcase `string`:\n\t\t\treturn value == null\n\t\t\t\t? ``\n\t\t\t\t: typeof value === `string`\n\t\t\t\t\t? value\n\t\t\t\t\t: describe(value)\n\t\tcase `number`: {\n\t\t\tconst next = Array.isArray(value) ? value.length : Number(value)\n\t\t\treturn Number.isFinite(next) ? next : 0\n\t\t}\n\t\tcase `boolean`:\n\t\t\treturn typeof value === `string` ? value === `true` : Boolean(value)\n\t\tcase `array`:\n\t\t\treturn Array.isArray(value)\n\t\t\t\t? value\n\t\t\t\t: value == null\n\t\t\t\t\t? []\n\t\t\t\t\t: typeof value === `object`\n\t\t\t\t\t\t? Object.entries(value)\n\t\t\t\t\t\t: [value]\n\t\tcase `object`:\n\t\t\treturn kind(value) === `object`\n\t\t\t\t? value\n\t\t\t\t: Array.isArray(value)\n\t\t\t\t\t? Object.fromEntries(value.map((item, index) => [index, item]))\n\t\t\t\t\t: value == null\n\t\t\t\t\t\t? {}\n\t\t\t\t\t\t: { value }\n\t}\n}\n","import type { Child, VNode } from \"@atom.io/spa\"\n\nimport type { Path, Tools } from \"./store.ts\"\nimport { useExpanded } from \"./store.ts\"\nimport type { Change, JsonKind } from \"./value.ts\"\nimport { convert, describe, kind } from \"./value.ts\"\n\ninterface EditorProps {\n\ttools: Tools\n\tvalue: unknown\n\tchange?: Change | undefined\n\tpath: Path\n\tancestors?: readonly unknown[]\n\tlabel?: Child\n\tactions?: Child\n}\nconst TYPES: JsonKind[] = [\n\t`string`,\n\t`number`,\n\t`boolean`,\n\t`null`,\n\t`array`,\n\t`object`,\n]\nexport function JsonEditor(props: EditorProps): VNode {\n\tconst [getExpanded, toggle] = useExpanded(props.tools, props.path)\n\tconst circular = () =>\n\t\t(props.ancestors?.includes(props.value) ?? false) || props.path.length > 50\n\tconst container = () =>\n\t\t!circular() &&\n\t\t(kind(props.value) === `object` || kind(props.value) === `array`)\n\tconst entries = () => Object.entries(props.value as Record<string, unknown>)\n\treturn (\n\t\t<section\n\t\t\tclass=\"json-editor\"\n\t\t\tdata-testid={`${props.path.join(`.`)}-state-editor`}\n\t\t>\n\t\t\t<header>\n\t\t\t\t{container() ? (\n\t\t\t\t\t<button\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\taria-label={`Expand ${props.path.at(-1)}`}\n\t\t\t\t\t\taria-expanded={getExpanded()}\n\t\t\t\t\t\tonClick={toggle}\n\t\t\t\t\t>\n\t\t\t\t\t\t{getExpanded() ? `▾` : `▸`}\n\t\t\t\t\t</button>\n\t\t\t\t) : (\n\t\t\t\t\t<span class=\"carat-spacer\" />\n\t\t\t\t)}\n\t\t\t\t<span class=\"field-name\">{props.label}</span>\n\t\t\t\t{props.change ? (\n\t\t\t\t\t<select\n\t\t\t\t\t\taria-label={`Type of ${props.path.at(-1)}`}\n\t\t\t\t\t\tvalue={kind(props.value)}\n\t\t\t\t\t\tonChange={(event) => {\n\t\t\t\t\t\t\tconst type = event.currentTarget.value as JsonKind\n\t\t\t\t\t\t\tprops.tools.attempt(() => {\n\t\t\t\t\t\t\t\tprops.change?.((previous) => convert(previous, type))\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t}}\n\t\t\t\t\t>\n\t\t\t\t\t\t<option value=\"other\" disabled>\n\t\t\t\t\t\t\tother\n\t\t\t\t\t\t</option>\n\t\t\t\t\t\t{TYPES.map((type) => (\n\t\t\t\t\t\t\t<option value={type}>{type}</option>\n\t\t\t\t\t\t))}\n\t\t\t\t\t</select>\n\t\t\t\t) : (\n\t\t\t\t\t<small>{kind(props.value)}</small>\n\t\t\t\t)}\n\t\t\t\t{container() ? (\n\t\t\t\t\t<code title={describe(props.value)}>{describe(props.value)}</code>\n\t\t\t\t) : circular() ? (\n\t\t\t\t\t<code>[Circular]</code>\n\t\t\t\t) : (\n\t\t\t\t\t<Primitive\n\t\t\t\t\t\tvalue={props.value}\n\t\t\t\t\t\tchange={props.change}\n\t\t\t\t\t\ttools={props.tools}\n\t\t\t\t\t\tlabel={props.path.at(-1) ?? `value`}\n\t\t\t\t\t/>\n\t\t\t\t)}\n\t\t\t\t{props.actions}\n\t\t\t</header>\n\t\t\t{container() && getExpanded() ? (\n\t\t\t\t<section class=\"properties\">\n\t\t\t\t\t{entries().map(([key]) => (\n\t\t\t\t\t\t<Property key={key} name={key} editor={props} />\n\t\t\t\t\t))}\n\t\t\t\t\t{props.change ? (\n\t\t\t\t\t\t<footer>\n\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\tonClick={() => {\n\t\t\t\t\t\t\t\t\tprops.tools.attempt(() => {\n\t\t\t\t\t\t\t\t\t\tprops.change?.((previous) => {\n\t\t\t\t\t\t\t\t\t\t\tif (Array.isArray(previous)) return [...previous, null]\n\t\t\t\t\t\t\t\t\t\t\tconst object = previous as Record<string, unknown>\n\t\t\t\t\t\t\t\t\t\t\tlet name = `new`\n\t\t\t\t\t\t\t\t\t\t\twhile (Object.hasOwn(object, name)) name += `_`\n\t\t\t\t\t\t\t\t\t\t\treturn { ...object, [name]: null }\n\t\t\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\tadd {Array.isArray(props.value) ? `item` : `property`}\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t{Array.isArray(props.value) ? (\n\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\tonClick={() => {\n\t\t\t\t\t\t\t\t\t\tprops.tools.attempt(() => {\n\t\t\t\t\t\t\t\t\t\t\tprops.change?.((previous) =>\n\t\t\t\t\t\t\t\t\t\t\t\t[...(previous as unknown[])].sort((a, b) =>\n\t\t\t\t\t\t\t\t\t\t\t\t\ttypeof a === `number` && typeof b === `number`\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t? a - b\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t: describe(a).localeCompare(describe(b)),\n\t\t\t\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\tsort\n\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t</footer>\n\t\t\t\t\t) : null}\n\t\t\t\t</section>\n\t\t\t) : null}\n\t\t</section>\n\t)\n}\nfunction Primitive(props: {\n\tvalue: unknown\n\tchange?: Change | undefined\n\ttools: Tools\n\tlabel: string\n}): VNode {\n\tconst set = (value: unknown) => {\n\t\tprops.tools.attempt(() => {\n\t\t\tprops.change?.(() => value)\n\t\t})\n\t}\n\treturn (\n\t\t<span class=\"primitive\">\n\t\t\t{kind(props.value) === `boolean` ? (\n\t\t\t\t<input\n\t\t\t\t\taria-label={props.label}\n\t\t\t\t\ttype=\"checkbox\"\n\t\t\t\t\tchecked={Boolean(props.value)}\n\t\t\t\t\tdisabled={!props.change}\n\t\t\t\t\tonChange={(event) => {\n\t\t\t\t\t\tset(event.currentTarget.checked)\n\t\t\t\t\t}}\n\t\t\t\t/>\n\t\t\t) : kind(props.value) === `string` || kind(props.value) === `number` ? (\n\t\t\t\t<input\n\t\t\t\t\taria-label={props.label}\n\t\t\t\t\ttype=\"text\"\n\t\t\t\t\tinputmode={kind(props.value) === `number` ? `decimal` : `text`}\n\t\t\t\t\tvalue={String(props.value)}\n\t\t\t\t\treadOnly={!props.change}\n\t\t\t\t\tonInput={(event) => {\n\t\t\t\t\t\tconst text = event.currentTarget.value\n\t\t\t\t\t\tif (kind(props.value) === `string`) set(text)\n\t\t\t\t\t\telse if (text.trim() && Number.isFinite(Number(text)))\n\t\t\t\t\t\t\tset(Number(text))\n\t\t\t\t\t}}\n\t\t\t\t\tonBlur={(event) => {\n\t\t\t\t\t\tevent.currentTarget.value = String(props.value)\n\t\t\t\t\t}}\n\t\t\t\t/>\n\t\t\t) : (\n\t\t\t\t<code>{describe(props.value)}</code>\n\t\t\t)}\n\t\t</span>\n\t)\n}\nfunction Property(props: { name: string; editor: EditorProps }): VNode {\n\tconst parent = props.editor\n\tconst read = () => (parent.value as Record<string, unknown>)[props.name]\n\tconst change: Change = (update) => {\n\t\tparent.change?.((previous) => {\n\t\t\tif (Array.isArray(previous))\n\t\t\t\treturn previous.map((value, index) =>\n\t\t\t\t\tindex === Number(props.name) ? update(value) : value,\n\t\t\t\t)\n\t\t\tconst object = previous as Record<string, unknown>\n\t\t\treturn { ...object, [props.name]: update(object[props.name]) }\n\t\t})\n\t}\n\treturn (\n\t\t<section class=\"property\">\n\t\t\t<JsonEditor\n\t\t\t\tlabel={\n\t\t\t\t\tparent.change && !Array.isArray(parent.value) ? (\n\t\t\t\t\t\t<input\n\t\t\t\t\t\t\taria-label={`Rename ${props.name}`}\n\t\t\t\t\t\t\tstyle={`width:calc(${Math.max(1, props.name.length)}ch + 6px)`}\n\t\t\t\t\t\t\tvalue={props.name}\n\t\t\t\t\t\t\tonChange={(event) => {\n\t\t\t\t\t\t\t\tconst input = event.currentTarget\n\t\t\t\t\t\t\t\tconst name = input.value\n\t\t\t\t\t\t\t\tparent.tools.attempt(() => {\n\t\t\t\t\t\t\t\t\tparent.change?.((previous) => {\n\t\t\t\t\t\t\t\t\t\tconst object = previous as Record<string, unknown>\n\t\t\t\t\t\t\t\t\t\tif (name !== props.name && Object.hasOwn(object, name))\n\t\t\t\t\t\t\t\t\t\t\tthrow new Error(`Property \"${name}\" already exists`)\n\t\t\t\t\t\t\t\t\t\treturn Object.fromEntries(\n\t\t\t\t\t\t\t\t\t\t\tObject.entries(object).map(([key, value]) => [\n\t\t\t\t\t\t\t\t\t\t\t\tkey === props.name ? name : key,\n\t\t\t\t\t\t\t\t\t\t\t\tvalue,\n\t\t\t\t\t\t\t\t\t\t\t]),\n\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\tinput.value = props.name\n\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t/>\n\t\t\t\t\t) : (\n\t\t\t\t\t\t<strong>{props.name}</strong>\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t\tactions={\n\t\t\t\t\tparent.change ? (\n\t\t\t\t\t\t<button\n\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\taria-label={`Delete ${props.name}`}\n\t\t\t\t\t\t\tonClick={() => {\n\t\t\t\t\t\t\t\tparent.tools.attempt(() => {\n\t\t\t\t\t\t\t\t\tparent.change?.((previous) =>\n\t\t\t\t\t\t\t\t\t\tArray.isArray(previous)\n\t\t\t\t\t\t\t\t\t\t\t? previous.filter(\n\t\t\t\t\t\t\t\t\t\t\t\t\t(_, index) => index !== Number(props.name),\n\t\t\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t\t\t: Object.fromEntries(\n\t\t\t\t\t\t\t\t\t\t\t\t\tObject.entries(previous as object).filter(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t([key]) => key !== props.name,\n\t\t\t\t\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t×\n\t\t\t\t\t\t</button>\n\t\t\t\t\t) : null\n\t\t\t\t}\n\t\t\t\ttools={parent.tools}\n\t\t\t\tpath={[...parent.path, props.name]}\n\t\t\t\tvalue={read()}\n\t\t\t\tchange={parent.change ? change : undefined}\n\t\t\t\tancestors={[...(parent.ancestors ?? []), parent.value]}\n\t\t\t/>\n\t\t</section>\n\t)\n}\n","import type { VNode } from \"@atom.io/spa\"\nimport { useGetter, useSetter, useTimeline } from \"@atom.io/spa\"\nimport type {\n\tReadableToken,\n\tTimelineToken,\n\tTransactionToken,\n\tWritableToken,\n} from \"atom.io\"\nimport {\n\tdisposeFromStore,\n\tfindInStore,\n\tisReservedIntrospectionKey,\n} from \"atom.io/internal\"\nimport type { FamilyNode } from \"atom.io/introspection\"\n\nimport { JsonEditor } from \"./JsonEditor.tsx\"\nimport type { Tools } from \"./store.ts\"\nimport { useExpanded } from \"./store.ts\"\nimport { describe } from \"./value.ts\"\n\ntype Token = ReadableToken<unknown, any, any>\nexport function StateIndex(props: {\n\ttools: Tools\n\ttype: `atoms` | `selectors`\n}): VNode {\n\tconst getStateIndex = useGetter<Map<string, Token | FamilyNode<Token>>>(\n\t\tprops.type === `atoms` ? props.tools.atomIndex : props.tools.selectorIndex,\n\t)\n\treturn (\n\t\t<article class=\"index\" data-testid=\"state-index\">\n\t\t\t{getStateIndex().size === 0 ? (\n\t\t\t\t<p>(no {props.type})</p>\n\t\t\t) : (\n\t\t\t\t[...getStateIndex().values()].map((node) => (\n\t\t\t\t\t<StateNode key={node.key} tools={props.tools} node={node} />\n\t\t\t\t))\n\t\t\t)}\n\t\t</article>\n\t)\n}\nfunction StateNode(props: {\n\ttools: Tools\n\tnode: Token | FamilyNode<Token>\n}): VNode {\n\tconst [getOpen, toggle] = useExpanded(props.tools, [props.node.key])\n\treturn (\n\t\t<section class=\"node\" data-testid={`state-${props.node.key}`}>\n\t\t\t{`familyMembers` in props.node ? (\n\t\t\t\t<>\n\t\t\t\t\t<header>\n\t\t\t\t\t\t<button\n\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\taria-expanded={getOpen()}\n\t\t\t\t\t\t\tdata-testid={`open-close-state-family-${props.node.key}`}\n\t\t\t\t\t\t\tonClick={toggle}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{getOpen() ? `▾` : `▸`}\n\t\t\t\t\t\t</button>\n\t\t\t\t\t\t<h2>{props.node.key}</h2>\n\t\t\t\t\t\t<small>family</small>\n\t\t\t\t\t</header>\n\t\t\t\t\t{getOpen()\n\t\t\t\t\t\t? [...props.node.familyMembers.values()].map((token) => (\n\t\t\t\t\t\t\t\t<StateLeaf key={token.key} tools={props.tools} token={token} />\n\t\t\t\t\t\t\t))\n\t\t\t\t\t\t: null}\n\t\t\t\t</>\n\t\t\t) : (\n\t\t\t\t<StateLeaf tools={props.tools} token={props.node} />\n\t\t\t)}\n\t\t</section>\n\t)\n}\nfunction StateLeaf(props: { tools: Tools; token: Token }): VNode {\n\tconst getValue = useGetter(props.token)\n\tconst writable = !props.token.type.startsWith(`readonly`)\n\tconst setValue = writable\n\t\t? useSetter(props.token as WritableToken<unknown, any, any>)\n\t\t: undefined\n\tconst value = () => {\n\t\ttry {\n\t\t\treturn getValue()\n\t\t} catch (error) {\n\t\t\treturn error\n\t\t}\n\t}\n\treturn (\n\t\t<section class=\"state-leaf\" data-testid={`state-value-${props.token.key}`}>\n\t\t\t<JsonEditor\n\t\t\t\tlabel={<h3>{props.token.family?.subKey ?? props.token.key}</h3>}\n\t\t\t\tactions={\n\t\t\t\t\tprops.token.family ? (\n\t\t\t\t\t\t<button\n\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\taria-label={`Dispose ${props.token.key}`}\n\t\t\t\t\t\t\tonClick={() => {\n\t\t\t\t\t\t\t\tprops.tools.attempt(() => {\n\t\t\t\t\t\t\t\t\tdisposeFromStore(props.tools.store, props.token)\n\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t×\n\t\t\t\t\t\t</button>\n\t\t\t\t\t) : null\n\t\t\t\t}\n\t\t\t\ttools={props.tools}\n\t\t\t\tvalue={value()}\n\t\t\t\tchange={\n\t\t\t\t\tsetValue\n\t\t\t\t\t\t? (update) => {\n\t\t\t\t\t\t\t\t// Validate edits before opening an atom.io write operation.\n\t\t\t\t\t\t\t\tconst next = update(getValue())\n\t\t\t\t\t\t\t\tsetValue(() => next)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t: undefined\n\t\t\t\t}\n\t\t\t\tpath={\n\t\t\t\t\tprops.token.family\n\t\t\t\t\t\t? [props.token.family.key, props.token.family.subKey]\n\t\t\t\t\t\t: [props.token.key]\n\t\t\t\t}\n\t\t\t/>\n\t\t</section>\n\t)\n}\nexport function TransactionIndex(props: { tools: Tools }): VNode {\n\tconst getTransactionIndex = useGetter(props.tools.transactionIndex)\n\tconst visible = () =>\n\t\tgetTransactionIndex().filter(\n\t\t\t(token) => !isReservedIntrospectionKey(token.key),\n\t\t)\n\treturn (\n\t\t<article class=\"index\" data-testid=\"transaction-index\">\n\t\t\t{visible().length === 0 ? (\n\t\t\t\t<p>(no transactions)</p>\n\t\t\t) : (\n\t\t\t\tvisible().map((token) => (\n\t\t\t\t\t<TransactionLog key={token.key} tools={props.tools} token={token} />\n\t\t\t\t))\n\t\t\t)}\n\t\t</article>\n\t)\n}\nfunction TransactionLog(props: {\n\ttools: Tools\n\ttoken: TransactionToken<any>\n}): VNode {\n\tconst getTransactionLog = useGetter(\n\t\tfindInStore(\n\t\t\tprops.tools.store,\n\t\t\tprops.tools.transactionLogSelectors,\n\t\t\tprops.token.key,\n\t\t),\n\t)\n\tconst [getOpen, toggle] = useExpanded(props.tools, [props.token.key])\n\treturn (\n\t\t<section class=\"node\" data-testid={`transaction-${props.token.key}`}>\n\t\t\t<header>\n\t\t\t\t<button\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\taria-expanded={getOpen()}\n\t\t\t\t\tdata-testid={`open-close-transaction-${props.token.key}`}\n\t\t\t\t\tonClick={toggle}\n\t\t\t\t>\n\t\t\t\t\t{getOpen() ? `▾` : `▸`}\n\t\t\t\t</button>\n\t\t\t\t<h2>{props.token.key}</h2>\n\t\t\t\t<small>({getTransactionLog().length})</small>\n\t\t\t</header>\n\t\t\t{getOpen()\n\t\t\t\t? getTransactionLog().map((event, index) => (\n\t\t\t\t\t\t<EventView\n\t\t\t\t\t\t\tkey={index}\n\t\t\t\t\t\t\ttools={props.tools}\n\t\t\t\t\t\t\tevent={event}\n\t\t\t\t\t\t\tindex={index}\n\t\t\t\t\t\t/>\n\t\t\t\t\t))\n\t\t\t\t: null}\n\t\t</section>\n\t)\n}\n// Events are recursive discriminated records, including user-defined transaction outputs.\nexport function EventView(props: {\n\ttools: Tools\n\tevent: any\n\tindex: number\n}): VNode {\n\tconst [getOpen, toggle] = useExpanded(\n\t\tprops.tools,\n\t\t[`event`, props.event.token?.key ?? ``, String(props.index)],\n\t\ttrue,\n\t)\n\treturn (\n\t\t<section\n\t\t\tclass=\"event\"\n\t\t\tdata-testid={`${props.event.type === `transaction_outcome` ? `transaction` : `timeline`}-update-${props.event.token?.key}-${props.index}`}\n\t\t>\n\t\t\t<header>\n\t\t\t\t<button type=\"button\" aria-expanded={getOpen()} onClick={toggle}>\n\t\t\t\t\t{getOpen() ? `▾` : `▸`}\n\t\t\t\t</button>\n\t\t\t\t<strong>{props.event.token?.key}</strong>\n\t\t\t\t<small>{props.event.type}</small>\n\t\t\t</header>\n\t\t\t{getOpen() ? (\n\t\t\t\tprops.event.type === `transaction_outcome` ? (\n\t\t\t\t\t<>\n\t\t\t\t\t\t<p>\n\t\t\t\t\t\t\tparams: <code>{describe(props.event.params)}</code>\n\t\t\t\t\t\t</p>\n\t\t\t\t\t\t<p>\n\t\t\t\t\t\t\toutput: <code>{describe(props.event.output)}</code>\n\t\t\t\t\t\t</p>\n\t\t\t\t\t\t<section class=\"impact\">\n\t\t\t\t\t\t\t{props.event.subEvents.map((event: any, index: number) => (\n\t\t\t\t\t\t\t\t<EventView\n\t\t\t\t\t\t\t\t\tkey={index}\n\t\t\t\t\t\t\t\t\ttools={props.tools}\n\t\t\t\t\t\t\t\t\tevent={event}\n\t\t\t\t\t\t\t\t\tindex={index}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t</section>\n\t\t\t\t\t</>\n\t\t\t\t) : props.event.update ? (\n\t\t\t\t\t<p>\n\t\t\t\t\t\t<del>{describe(props.event.update.oldValue)}</del> →{` `}\n\t\t\t\t\t\t<ins>{describe(props.event.update.newValue)}</ins>\n\t\t\t\t\t</p>\n\t\t\t\t) : (\n\t\t\t\t\t<code>{describe(props.event)}</code>\n\t\t\t\t)\n\t\t\t) : null}\n\t\t</section>\n\t)\n}\nexport function TimelineIndex(props: { tools: Tools }): VNode {\n\tconst getTimelineIndex = useGetter(props.tools.timelineIndex)\n\tconst groups = () => {\n\t\tconst result = new Map<string, TimelineToken<any>[]>()\n\t\tfor (const token of getTimelineIndex()) {\n\t\t\tif (isReservedIntrospectionKey(token.key) || token.key.startsWith(`👁🗨`))\n\t\t\t\tcontinue\n\t\t\tconst key = token.family?.key ?? ``\n\t\t\tconst members = result.get(key) ?? []\n\t\t\tmembers.push(token)\n\t\t\tresult.set(key, members)\n\t\t}\n\t\treturn [...result]\n\t}\n\treturn (\n\t\t<article class=\"index\" data-testid=\"timeline-index\">\n\t\t\t{groups().length === 0 ? (\n\t\t\t\t<p>(no timelines)</p>\n\t\t\t) : (\n\t\t\t\tgroups().map(([family, members]) => (\n\t\t\t\t\t<section\n\t\t\t\t\t\tkey={family}\n\t\t\t\t\t\tdata-testid={family ? `timeline-family-${family}` : undefined}\n\t\t\t\t\t>\n\t\t\t\t\t\t{family ? <h2>{family}</h2> : null}\n\t\t\t\t\t\t{members.map((token) => (\n\t\t\t\t\t\t\t<TimelineLog key={token.key} tools={props.tools} token={token} />\n\t\t\t\t\t\t))}\n\t\t\t\t\t</section>\n\t\t\t\t))\n\t\t\t)}\n\t\t</article>\n\t)\n}\nfunction TimelineLog(props: { tools: Tools; token: TimelineToken<any> }): VNode {\n\tconst controls = useTimeline(props.token)\n\tconst getTimeline = useGetter(\n\t\tfindInStore(\n\t\t\tprops.tools.store,\n\t\t\tprops.tools.timelineSelectors,\n\t\t\tprops.token.key,\n\t\t),\n\t)\n\tconst [getOpen, toggle] = useExpanded(props.tools, [props.token.key])\n\treturn (\n\t\t<section class=\"node\" data-testid={`timeline-${props.token.key}`}>\n\t\t\t<header>\n\t\t\t\t<button\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\taria-expanded={getOpen()}\n\t\t\t\t\tdata-testid={`open-close-timeline-${props.token.key}`}\n\t\t\t\t\tonClick={toggle}\n\t\t\t\t>\n\t\t\t\t\t{getOpen() ? `▾` : `▸`}\n\t\t\t\t</button>\n\t\t\t\t<h2>{props.token.family?.subKey ?? props.token.key}</h2>\n\t\t\t\t<small>\n\t\t\t\t\t({controls.getCursor()}/{controls.getLength()})\n\t\t\t\t</small>\n\t\t\t\t<button\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\tdisabled={controls.getCursor() === 0}\n\t\t\t\t\tonClick={() => {\n\t\t\t\t\t\tprops.tools.attempt(controls.undo)\n\t\t\t\t\t}}\n\t\t\t\t>\n\t\t\t\t\tundo\n\t\t\t\t</button>\n\t\t\t\t<button\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\tdisabled={controls.getCursor() === controls.getLength()}\n\t\t\t\t\tonClick={() => {\n\t\t\t\t\t\tprops.tools.attempt(controls.redo)\n\t\t\t\t\t}}\n\t\t\t\t>\n\t\t\t\t\tredo\n\t\t\t\t</button>\n\t\t\t</header>\n\t\t\t{getOpen() ? (\n\t\t\t\t<section>\n\t\t\t\t\t{getTimeline().history.map((event, index) => (\n\t\t\t\t\t\t<section key={index}>\n\t\t\t\t\t\t\t{index === getTimeline().at ? (\n\t\t\t\t\t\t\t\t<p class=\"you-are-here\">you are here</p>\n\t\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t\t<EventView tools={props.tools} event={event} index={index} />\n\t\t\t\t\t\t</section>\n\t\t\t\t\t))}\n\t\t\t\t\t{getTimeline().at === getTimeline().history.length ? (\n\t\t\t\t\t\t<p class=\"you-are-here\">you are here</p>\n\t\t\t\t\t) : null}\n\t\t\t\t</section>\n\t\t\t) : null}\n\t\t</section>\n\t)\n}\n","import type { VNode } from \"@atom.io/spa\"\nimport { onCleanup, onMount, render, useGetter } from \"@atom.io/spa\"\nimport type { RootStore } from \"atom.io/internal\"\nimport { IMPLICIT } from \"atom.io/internal\"\n\nimport { StateIndex, TimelineIndex, TransactionIndex } from \"./Indexes.tsx\"\nimport { useLocal, useSaved, useTools } from \"./store.ts\"\n\nexport interface AtomIODevtoolsProps {\n\t/** Inspected store. Fixed for this component instance; change its key to replace it. */\n\tstore?: RootStore\n\thideByDefault?: boolean\n}\n/** Owns a rendering root in the inspected store, independently of its parent island. */\nexport function AtomIODevtools(props: AtomIODevtoolsProps): VNode {\n\tlet host: HTMLElement | null = null\n\tconst attach = (element: HTMLElement | null) => {\n\t\thost = element\n\t}\n\tonMount(() => {\n\t\tconst store = props.store ?? IMPLICIT.STORE\n\t\tconst root = render(\n\t\t\t() => <Panel store={store} hideByDefault={props.hideByDefault ?? false} />,\n\t\t\thost!,\n\t\t\t{ store },\n\t\t)\n\t\tonCleanup(root.dispose)\n\t})\n\treturn <spa-devtools ref={attach} />\n}\nconst VIEWS = [`atoms`, `selectors`, `transactions`, `timelines`] as const\ntype View = (typeof VIEWS)[number]\nfunction Panel(props: { store: RootStore; hideByDefault: boolean }): VNode {\n\tconst tools = useTools(props.store)\n\tconst [getHidden, setHidden] = useSaved(\n\t\tprops.store,\n\t\t`🔍 Devtools Are Hidden`,\n\t\tprops.hideByDefault,\n\t)\n\tconst [getOpen, setOpen] = useSaved(props.store, `🔍 Devtools Are Open`, true)\n\tconst [getSelection, setSelection] = useSaved<string>(\n\t\tprops.store,\n\t\t`🔍 Devtools View`,\n\t\t`atoms`,\n\t)\n\tconst view = () =>\n\t\tVIEWS.includes(getSelection() as View) ? (getSelection() as View) : `atoms`\n\tconst [getPosition, setPosition] = useLocal(props.store, { x: 0, y: 0 })\n\tconst getError = useGetter(tools.error)\n\tlet panel: HTMLElement | null = null\n\tconst attach = (element: HTMLElement | null) => {\n\t\tpanel = element\n\t}\n\tlet drag:\n\t\t| { id: number; startX: number; startY: number; x: number; y: number }\n\t\t| undefined\n\tonMount(() => {\n\t\tconst shortcut = (event: KeyboardEvent) => {\n\t\t\tif (event.ctrlKey && event.shiftKey && event.code === `KeyA`) {\n\t\t\t\tevent.preventDefault()\n\t\t\t\tsetHidden((value) => !value)\n\t\t\t}\n\t\t}\n\t\tconst reset = () => {\n\t\t\tsetPosition({ x: 0, y: 0 })\n\t\t}\n\t\tdocument.addEventListener(`keydown`, shortcut)\n\t\twindow.addEventListener(`resize`, reset)\n\t\tonCleanup(() => {\n\t\t\tdocument.removeEventListener(`keydown`, shortcut)\n\t\t\twindow.removeEventListener(`resize`, reset)\n\t\t})\n\t})\n\treturn (\n\t\t<main\n\t\t\tref={attach}\n\t\t\tdata-css=\"spa-devtools\"\n\t\t\tdata-testid=\"atom-io-devtools\"\n\t\t\thidden={getHidden()}\n\t\t\tdata-open={getOpen()}\n\t\t\tstyle={`transform:translate(${getPosition().x}px,${getPosition().y}px)`}\n\t\t>\n\t\t\t<header class=\"toolbar\">\n\t\t\t\t<button\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\tclass=\"drag-handle\"\n\t\t\t\t\taria-label=\"Move debugger\"\n\t\t\t\t\ttitle=\"Drag to move; arrow keys to nudge\"\n\t\t\t\t\tonPointerdown={(event) => {\n\t\t\t\t\t\tif (event.button !== 0) return\n\t\t\t\t\t\tdrag = {\n\t\t\t\t\t\t\tid: event.pointerId,\n\t\t\t\t\t\t\tstartX: event.clientX,\n\t\t\t\t\t\t\tstartY: event.clientY,\n\t\t\t\t\t\t\t...getPosition(),\n\t\t\t\t\t\t}\n\t\t\t\t\t\t;(event.currentTarget as HTMLElement).setPointerCapture(\n\t\t\t\t\t\t\tevent.pointerId,\n\t\t\t\t\t\t)\n\t\t\t\t\t}}\n\t\t\t\t\tonPointermove={(event) => {\n\t\t\t\t\t\tif (!drag || drag.id !== event.pointerId || !panel) return\n\t\t\t\t\t\tconst rect = panel.getBoundingClientRect()\n\t\t\t\t\t\tconst next = {\n\t\t\t\t\t\t\tx: drag.x + event.clientX - drag.startX,\n\t\t\t\t\t\t\ty: drag.y + event.clientY - drag.startY,\n\t\t\t\t\t\t}\n\t\t\t\t\t\tnext.x = Math.max(\n\t\t\t\t\t\t\tgetPosition().x - rect.left,\n\t\t\t\t\t\t\tMath.min(next.x, getPosition().x + window.innerWidth - rect.right),\n\t\t\t\t\t\t)\n\t\t\t\t\t\tnext.y = Math.max(\n\t\t\t\t\t\t\tgetPosition().y - rect.top,\n\t\t\t\t\t\t\tMath.min(\n\t\t\t\t\t\t\t\tnext.y,\n\t\t\t\t\t\t\t\tgetPosition().y + window.innerHeight - rect.bottom,\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t)\n\t\t\t\t\t\tsetPosition(next)\n\t\t\t\t\t}}\n\t\t\t\t\tonPointerup={() => {\n\t\t\t\t\t\tdrag = undefined\n\t\t\t\t\t}}\n\t\t\t\t\tonPointercancel={() => {\n\t\t\t\t\t\tdrag = undefined\n\t\t\t\t\t}}\n\t\t\t\t\tonKeydown={(event) => {\n\t\t\t\t\t\tconst step = {\n\t\t\t\t\t\t\tArrowLeft: [-10, 0],\n\t\t\t\t\t\t\tArrowRight: [10, 0],\n\t\t\t\t\t\t\tArrowUp: [0, -10],\n\t\t\t\t\t\t\tArrowDown: [0, 10],\n\t\t\t\t\t\t}[event.key]\n\t\t\t\t\t\tif (step) {\n\t\t\t\t\t\t\tevent.preventDefault()\n\t\t\t\t\t\t\tsetPosition(({ x, y }) => ({ x: x + step[0], y: y + step[1] }))\n\t\t\t\t\t\t}\n\t\t\t\t\t}}\n\t\t\t\t>\n\t\t\t\t\t⠿\n\t\t\t\t</button>\n\t\t\t\t<h1>atom.io</h1>\n\t\t\t\t{getOpen() ? (\n\t\t\t\t\t<nav aria-label=\"Debugger views\">\n\t\t\t\t\t\t{VIEWS.map((name) => (\n\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\taria-pressed={view() === name}\n\t\t\t\t\t\t\t\tdata-testid={`view-${name}`}\n\t\t\t\t\t\t\t\tonClick={() => {\n\t\t\t\t\t\t\t\t\tsetSelection(name)\n\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{name}\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t))}\n\t\t\t\t\t</nav>\n\t\t\t\t) : null}\n\t\t\t\t<button\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\taria-label={getOpen() ? `Minimize debugger` : `Open debugger`}\n\t\t\t\t\tdata-testid=\"open-close-devtools\"\n\t\t\t\t\tonClick={() => {\n\t\t\t\t\t\tsetOpen((value) => !value)\n\t\t\t\t\t}}\n\t\t\t\t>\n\t\t\t\t\t{getOpen() ? `−` : `🔍`}\n\t\t\t\t</button>\n\t\t\t\t<button\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\taria-label=\"Hide debugger (Ctrl+Shift+A)\"\n\t\t\t\t\tonClick={() => {\n\t\t\t\t\t\tsetHidden(true)\n\t\t\t\t\t}}\n\t\t\t\t>\n\t\t\t\t\t×\n\t\t\t\t</button>\n\t\t\t</header>\n\t\t\t{getOpen() ? (\n\t\t\t\t<>\n\t\t\t\t\t{getError() ? <p role=\"alert\">{getError()}</p> : null}\n\t\t\t\t\t{view() === `atoms` || view() === `selectors` ? (\n\t\t\t\t\t\t<StateIndex\n\t\t\t\t\t\t\tkey={view()}\n\t\t\t\t\t\t\ttools={tools}\n\t\t\t\t\t\t\ttype={view() as `atoms` | `selectors`}\n\t\t\t\t\t\t/>\n\t\t\t\t\t) : view() === `transactions` ? (\n\t\t\t\t\t\t<TransactionIndex tools={tools} />\n\t\t\t\t\t) : (\n\t\t\t\t\t\t<TimelineIndex tools={tools} />\n\t\t\t\t\t)}\n\t\t\t\t</>\n\t\t\t) : null}\n\t\t</main>\n\t)\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atom.io/spa-devtools",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.4",
|
|
4
4
|
"description": "Atom.io's debugger, rendered with spa.",
|
|
5
5
|
"homepage": "https://github.com/jeremybanka/atom.io/tree/main/packages/spa-devtools#readme",
|
|
6
6
|
"repository": {
|
|
@@ -32,20 +32,20 @@
|
|
|
32
32
|
"src"
|
|
33
33
|
],
|
|
34
34
|
"devDependencies": {
|
|
35
|
-
"@atom.io/spa": "0.0.
|
|
35
|
+
"@atom.io/spa": "0.0.4",
|
|
36
36
|
"@breakage/changesets": "0.1.0",
|
|
37
37
|
"@types/node": "26.6.6",
|
|
38
38
|
"@vitest/coverage-v8": "5.1.0",
|
|
39
|
-
"atom.io": "0.54.
|
|
40
|
-
"breakage": "0.10.
|
|
39
|
+
"atom.io": "0.54.5",
|
|
40
|
+
"breakage": "0.10.2",
|
|
41
41
|
"eslint": "10.14.0",
|
|
42
42
|
"happy-dom": "21.0.4",
|
|
43
|
-
"tsdown": "0.23.
|
|
43
|
+
"tsdown": "0.23.5",
|
|
44
44
|
"typescript": "7.0.2",
|
|
45
45
|
"vitest": "5.1.0"
|
|
46
46
|
},
|
|
47
47
|
"peerDependencies": {
|
|
48
|
-
"@atom.io/spa": ">=0.0.
|
|
48
|
+
"@atom.io/spa": ">=0.0.4 <0.2.0",
|
|
49
49
|
"atom.io": "^0.54.1"
|
|
50
50
|
},
|
|
51
51
|
"publishConfig": {
|
package/src/AtomIODevtools.tsx
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { VNode } from "@atom.io/spa"
|
|
2
|
-
import { onCleanup, onMount, render,
|
|
2
|
+
import { onCleanup, onMount, render, useGetter } from "@atom.io/spa"
|
|
3
3
|
import type { RootStore } from "atom.io/internal"
|
|
4
4
|
import { IMPLICIT } from "atom.io/internal"
|
|
5
5
|
|
|
@@ -32,21 +32,21 @@ const VIEWS = [`atoms`, `selectors`, `transactions`, `timelines`] as const
|
|
|
32
32
|
type View = (typeof VIEWS)[number]
|
|
33
33
|
function Panel(props: { store: RootStore; hideByDefault: boolean }): VNode {
|
|
34
34
|
const tools = useTools(props.store)
|
|
35
|
-
const [
|
|
35
|
+
const [getHidden, setHidden] = useSaved(
|
|
36
36
|
props.store,
|
|
37
37
|
`🔍 Devtools Are Hidden`,
|
|
38
38
|
props.hideByDefault,
|
|
39
39
|
)
|
|
40
|
-
const [
|
|
41
|
-
const [
|
|
40
|
+
const [getOpen, setOpen] = useSaved(props.store, `🔍 Devtools Are Open`, true)
|
|
41
|
+
const [getSelection, setSelection] = useSaved<string>(
|
|
42
42
|
props.store,
|
|
43
43
|
`🔍 Devtools View`,
|
|
44
44
|
`atoms`,
|
|
45
45
|
)
|
|
46
46
|
const view = () =>
|
|
47
|
-
VIEWS.includes(
|
|
48
|
-
const [
|
|
49
|
-
const
|
|
47
|
+
VIEWS.includes(getSelection() as View) ? (getSelection() as View) : `atoms`
|
|
48
|
+
const [getPosition, setPosition] = useLocal(props.store, { x: 0, y: 0 })
|
|
49
|
+
const getError = useGetter(tools.error)
|
|
50
50
|
let panel: HTMLElement | null = null
|
|
51
51
|
const attach = (element: HTMLElement | null) => {
|
|
52
52
|
panel = element
|
|
@@ -76,9 +76,9 @@ function Panel(props: { store: RootStore; hideByDefault: boolean }): VNode {
|
|
|
76
76
|
ref={attach}
|
|
77
77
|
data-css="spa-devtools"
|
|
78
78
|
data-testid="atom-io-devtools"
|
|
79
|
-
hidden={
|
|
80
|
-
data-open={
|
|
81
|
-
style={`transform:translate(${
|
|
79
|
+
hidden={getHidden()}
|
|
80
|
+
data-open={getOpen()}
|
|
81
|
+
style={`transform:translate(${getPosition().x}px,${getPosition().y}px)`}
|
|
82
82
|
>
|
|
83
83
|
<header class="toolbar">
|
|
84
84
|
<button
|
|
@@ -92,7 +92,7 @@ function Panel(props: { store: RootStore; hideByDefault: boolean }): VNode {
|
|
|
92
92
|
id: event.pointerId,
|
|
93
93
|
startX: event.clientX,
|
|
94
94
|
startY: event.clientY,
|
|
95
|
-
...
|
|
95
|
+
...getPosition(),
|
|
96
96
|
}
|
|
97
97
|
;(event.currentTarget as HTMLElement).setPointerCapture(
|
|
98
98
|
event.pointerId,
|
|
@@ -106,12 +106,15 @@ function Panel(props: { store: RootStore; hideByDefault: boolean }): VNode {
|
|
|
106
106
|
y: drag.y + event.clientY - drag.startY,
|
|
107
107
|
}
|
|
108
108
|
next.x = Math.max(
|
|
109
|
-
|
|
110
|
-
Math.min(next.x,
|
|
109
|
+
getPosition().x - rect.left,
|
|
110
|
+
Math.min(next.x, getPosition().x + window.innerWidth - rect.right),
|
|
111
111
|
)
|
|
112
112
|
next.y = Math.max(
|
|
113
|
-
|
|
114
|
-
Math.min(
|
|
113
|
+
getPosition().y - rect.top,
|
|
114
|
+
Math.min(
|
|
115
|
+
next.y,
|
|
116
|
+
getPosition().y + window.innerHeight - rect.bottom,
|
|
117
|
+
),
|
|
115
118
|
)
|
|
116
119
|
setPosition(next)
|
|
117
120
|
}}
|
|
@@ -137,7 +140,7 @@ function Panel(props: { store: RootStore; hideByDefault: boolean }): VNode {
|
|
|
137
140
|
⠿
|
|
138
141
|
</button>
|
|
139
142
|
<h1>atom.io</h1>
|
|
140
|
-
{
|
|
143
|
+
{getOpen() ? (
|
|
141
144
|
<nav aria-label="Debugger views">
|
|
142
145
|
{VIEWS.map((name) => (
|
|
143
146
|
<button
|
|
@@ -155,13 +158,13 @@ function Panel(props: { store: RootStore; hideByDefault: boolean }): VNode {
|
|
|
155
158
|
) : null}
|
|
156
159
|
<button
|
|
157
160
|
type="button"
|
|
158
|
-
aria-label={
|
|
161
|
+
aria-label={getOpen() ? `Minimize debugger` : `Open debugger`}
|
|
159
162
|
data-testid="open-close-devtools"
|
|
160
163
|
onClick={() => {
|
|
161
164
|
setOpen((value) => !value)
|
|
162
165
|
}}
|
|
163
166
|
>
|
|
164
|
-
{
|
|
167
|
+
{getOpen() ? `−` : `🔍`}
|
|
165
168
|
</button>
|
|
166
169
|
<button
|
|
167
170
|
type="button"
|
|
@@ -173,9 +176,9 @@ function Panel(props: { store: RootStore; hideByDefault: boolean }): VNode {
|
|
|
173
176
|
×
|
|
174
177
|
</button>
|
|
175
178
|
</header>
|
|
176
|
-
{
|
|
179
|
+
{getOpen() ? (
|
|
177
180
|
<>
|
|
178
|
-
{
|
|
181
|
+
{getError() ? <p role="alert">{getError()}</p> : null}
|
|
179
182
|
{view() === `atoms` || view() === `selectors` ? (
|
|
180
183
|
<StateIndex
|
|
181
184
|
key={view()}
|
package/src/Indexes.tsx
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { VNode } from "@atom.io/spa"
|
|
2
|
-
import { useSetter, useTimeline
|
|
2
|
+
import { useGetter, useSetter, useTimeline } from "@atom.io/spa"
|
|
3
3
|
import type {
|
|
4
4
|
ReadableToken,
|
|
5
5
|
TimelineToken,
|
|
@@ -23,15 +23,15 @@ export function StateIndex(props: {
|
|
|
23
23
|
tools: Tools
|
|
24
24
|
type: `atoms` | `selectors`
|
|
25
25
|
}): VNode {
|
|
26
|
-
const
|
|
26
|
+
const getStateIndex = useGetter<Map<string, Token | FamilyNode<Token>>>(
|
|
27
27
|
props.type === `atoms` ? props.tools.atomIndex : props.tools.selectorIndex,
|
|
28
28
|
)
|
|
29
29
|
return (
|
|
30
30
|
<article class="index" data-testid="state-index">
|
|
31
|
-
{
|
|
31
|
+
{getStateIndex().size === 0 ? (
|
|
32
32
|
<p>(no {props.type})</p>
|
|
33
33
|
) : (
|
|
34
|
-
[...
|
|
34
|
+
[...getStateIndex().values()].map((node) => (
|
|
35
35
|
<StateNode key={node.key} tools={props.tools} node={node} />
|
|
36
36
|
))
|
|
37
37
|
)}
|
|
@@ -42,7 +42,7 @@ function StateNode(props: {
|
|
|
42
42
|
tools: Tools
|
|
43
43
|
node: Token | FamilyNode<Token>
|
|
44
44
|
}): VNode {
|
|
45
|
-
const [
|
|
45
|
+
const [getOpen, toggle] = useExpanded(props.tools, [props.node.key])
|
|
46
46
|
return (
|
|
47
47
|
<section class="node" data-testid={`state-${props.node.key}`}>
|
|
48
48
|
{`familyMembers` in props.node ? (
|
|
@@ -50,16 +50,16 @@ function StateNode(props: {
|
|
|
50
50
|
<header>
|
|
51
51
|
<button
|
|
52
52
|
type="button"
|
|
53
|
-
aria-expanded={
|
|
53
|
+
aria-expanded={getOpen()}
|
|
54
54
|
data-testid={`open-close-state-family-${props.node.key}`}
|
|
55
55
|
onClick={toggle}
|
|
56
56
|
>
|
|
57
|
-
{
|
|
57
|
+
{getOpen() ? `▾` : `▸`}
|
|
58
58
|
</button>
|
|
59
59
|
<h2>{props.node.key}</h2>
|
|
60
60
|
<small>family</small>
|
|
61
61
|
</header>
|
|
62
|
-
{
|
|
62
|
+
{getOpen()
|
|
63
63
|
? [...props.node.familyMembers.values()].map((token) => (
|
|
64
64
|
<StateLeaf key={token.key} tools={props.tools} token={token} />
|
|
65
65
|
))
|
|
@@ -72,14 +72,14 @@ function StateNode(props: {
|
|
|
72
72
|
)
|
|
73
73
|
}
|
|
74
74
|
function StateLeaf(props: { tools: Tools; token: Token }): VNode {
|
|
75
|
-
const
|
|
75
|
+
const getValue = useGetter(props.token)
|
|
76
76
|
const writable = !props.token.type.startsWith(`readonly`)
|
|
77
|
-
const
|
|
77
|
+
const setValue = writable
|
|
78
78
|
? useSetter(props.token as WritableToken<unknown, any, any>)
|
|
79
79
|
: undefined
|
|
80
80
|
const value = () => {
|
|
81
81
|
try {
|
|
82
|
-
return
|
|
82
|
+
return getValue()
|
|
83
83
|
} catch (error) {
|
|
84
84
|
return error
|
|
85
85
|
}
|
|
@@ -106,11 +106,11 @@ function StateLeaf(props: { tools: Tools; token: Token }): VNode {
|
|
|
106
106
|
tools={props.tools}
|
|
107
107
|
value={value()}
|
|
108
108
|
change={
|
|
109
|
-
|
|
109
|
+
setValue
|
|
110
110
|
? (update) => {
|
|
111
111
|
// Validate edits before opening an atom.io write operation.
|
|
112
|
-
const next = update(
|
|
113
|
-
|
|
112
|
+
const next = update(getValue())
|
|
113
|
+
setValue(() => next)
|
|
114
114
|
}
|
|
115
115
|
: undefined
|
|
116
116
|
}
|
|
@@ -124,9 +124,11 @@ function StateLeaf(props: { tools: Tools; token: Token }): VNode {
|
|
|
124
124
|
)
|
|
125
125
|
}
|
|
126
126
|
export function TransactionIndex(props: { tools: Tools }): VNode {
|
|
127
|
-
const
|
|
127
|
+
const getTransactionIndex = useGetter(props.tools.transactionIndex)
|
|
128
128
|
const visible = () =>
|
|
129
|
-
|
|
129
|
+
getTransactionIndex().filter(
|
|
130
|
+
(token) => !isReservedIntrospectionKey(token.key),
|
|
131
|
+
)
|
|
130
132
|
return (
|
|
131
133
|
<article class="index" data-testid="transaction-index">
|
|
132
134
|
{visible().length === 0 ? (
|
|
@@ -143,30 +145,30 @@ function TransactionLog(props: {
|
|
|
143
145
|
tools: Tools
|
|
144
146
|
token: TransactionToken<any>
|
|
145
147
|
}): VNode {
|
|
146
|
-
const
|
|
148
|
+
const getTransactionLog = useGetter(
|
|
147
149
|
findInStore(
|
|
148
150
|
props.tools.store,
|
|
149
151
|
props.tools.transactionLogSelectors,
|
|
150
152
|
props.token.key,
|
|
151
153
|
),
|
|
152
154
|
)
|
|
153
|
-
const [
|
|
155
|
+
const [getOpen, toggle] = useExpanded(props.tools, [props.token.key])
|
|
154
156
|
return (
|
|
155
157
|
<section class="node" data-testid={`transaction-${props.token.key}`}>
|
|
156
158
|
<header>
|
|
157
159
|
<button
|
|
158
160
|
type="button"
|
|
159
|
-
aria-expanded={
|
|
161
|
+
aria-expanded={getOpen()}
|
|
160
162
|
data-testid={`open-close-transaction-${props.token.key}`}
|
|
161
163
|
onClick={toggle}
|
|
162
164
|
>
|
|
163
|
-
{
|
|
165
|
+
{getOpen() ? `▾` : `▸`}
|
|
164
166
|
</button>
|
|
165
167
|
<h2>{props.token.key}</h2>
|
|
166
|
-
<small>({
|
|
168
|
+
<small>({getTransactionLog().length})</small>
|
|
167
169
|
</header>
|
|
168
|
-
{
|
|
169
|
-
?
|
|
170
|
+
{getOpen()
|
|
171
|
+
? getTransactionLog().map((event, index) => (
|
|
170
172
|
<EventView
|
|
171
173
|
key={index}
|
|
172
174
|
tools={props.tools}
|
|
@@ -184,7 +186,7 @@ export function EventView(props: {
|
|
|
184
186
|
event: any
|
|
185
187
|
index: number
|
|
186
188
|
}): VNode {
|
|
187
|
-
const [
|
|
189
|
+
const [getOpen, toggle] = useExpanded(
|
|
188
190
|
props.tools,
|
|
189
191
|
[`event`, props.event.token?.key ?? ``, String(props.index)],
|
|
190
192
|
true,
|
|
@@ -195,13 +197,13 @@ export function EventView(props: {
|
|
|
195
197
|
data-testid={`${props.event.type === `transaction_outcome` ? `transaction` : `timeline`}-update-${props.event.token?.key}-${props.index}`}
|
|
196
198
|
>
|
|
197
199
|
<header>
|
|
198
|
-
<button type="button" aria-expanded={
|
|
199
|
-
{
|
|
200
|
+
<button type="button" aria-expanded={getOpen()} onClick={toggle}>
|
|
201
|
+
{getOpen() ? `▾` : `▸`}
|
|
200
202
|
</button>
|
|
201
203
|
<strong>{props.event.token?.key}</strong>
|
|
202
204
|
<small>{props.event.type}</small>
|
|
203
205
|
</header>
|
|
204
|
-
{
|
|
206
|
+
{getOpen() ? (
|
|
205
207
|
props.event.type === `transaction_outcome` ? (
|
|
206
208
|
<>
|
|
207
209
|
<p>
|
|
@@ -234,10 +236,10 @@ export function EventView(props: {
|
|
|
234
236
|
)
|
|
235
237
|
}
|
|
236
238
|
export function TimelineIndex(props: { tools: Tools }): VNode {
|
|
237
|
-
const
|
|
239
|
+
const getTimelineIndex = useGetter(props.tools.timelineIndex)
|
|
238
240
|
const groups = () => {
|
|
239
241
|
const result = new Map<string, TimelineToken<any>[]>()
|
|
240
|
-
for (const token of
|
|
242
|
+
for (const token of getTimelineIndex()) {
|
|
241
243
|
if (isReservedIntrospectionKey(token.key) || token.key.startsWith(`👁🗨`))
|
|
242
244
|
continue
|
|
243
245
|
const key = token.family?.key ?? ``
|
|
@@ -269,59 +271,59 @@ export function TimelineIndex(props: { tools: Tools }): VNode {
|
|
|
269
271
|
}
|
|
270
272
|
function TimelineLog(props: { tools: Tools; token: TimelineToken<any> }): VNode {
|
|
271
273
|
const controls = useTimeline(props.token)
|
|
272
|
-
const
|
|
274
|
+
const getTimeline = useGetter(
|
|
273
275
|
findInStore(
|
|
274
276
|
props.tools.store,
|
|
275
277
|
props.tools.timelineSelectors,
|
|
276
278
|
props.token.key,
|
|
277
279
|
),
|
|
278
280
|
)
|
|
279
|
-
const [
|
|
281
|
+
const [getOpen, toggle] = useExpanded(props.tools, [props.token.key])
|
|
280
282
|
return (
|
|
281
283
|
<section class="node" data-testid={`timeline-${props.token.key}`}>
|
|
282
284
|
<header>
|
|
283
285
|
<button
|
|
284
286
|
type="button"
|
|
285
|
-
aria-expanded={
|
|
287
|
+
aria-expanded={getOpen()}
|
|
286
288
|
data-testid={`open-close-timeline-${props.token.key}`}
|
|
287
289
|
onClick={toggle}
|
|
288
290
|
>
|
|
289
|
-
{
|
|
291
|
+
{getOpen() ? `▾` : `▸`}
|
|
290
292
|
</button>
|
|
291
293
|
<h2>{props.token.family?.subKey ?? props.token.key}</h2>
|
|
292
294
|
<small>
|
|
293
|
-
({controls()
|
|
295
|
+
({controls.getCursor()}/{controls.getLength()})
|
|
294
296
|
</small>
|
|
295
297
|
<button
|
|
296
298
|
type="button"
|
|
297
|
-
disabled={controls()
|
|
299
|
+
disabled={controls.getCursor() === 0}
|
|
298
300
|
onClick={() => {
|
|
299
|
-
props.tools.attempt(controls
|
|
301
|
+
props.tools.attempt(controls.undo)
|
|
300
302
|
}}
|
|
301
303
|
>
|
|
302
304
|
undo
|
|
303
305
|
</button>
|
|
304
306
|
<button
|
|
305
307
|
type="button"
|
|
306
|
-
disabled={controls()
|
|
308
|
+
disabled={controls.getCursor() === controls.getLength()}
|
|
307
309
|
onClick={() => {
|
|
308
|
-
props.tools.attempt(controls
|
|
310
|
+
props.tools.attempt(controls.redo)
|
|
309
311
|
}}
|
|
310
312
|
>
|
|
311
313
|
redo
|
|
312
314
|
</button>
|
|
313
315
|
</header>
|
|
314
|
-
{
|
|
316
|
+
{getOpen() ? (
|
|
315
317
|
<section>
|
|
316
|
-
{
|
|
318
|
+
{getTimeline().history.map((event, index) => (
|
|
317
319
|
<section key={index}>
|
|
318
|
-
{index ===
|
|
320
|
+
{index === getTimeline().at ? (
|
|
319
321
|
<p class="you-are-here">you are here</p>
|
|
320
322
|
) : null}
|
|
321
323
|
<EventView tools={props.tools} event={event} index={index} />
|
|
322
324
|
</section>
|
|
323
325
|
))}
|
|
324
|
-
{
|
|
326
|
+
{getTimeline().at === getTimeline().history.length ? (
|
|
325
327
|
<p class="you-are-here">you are here</p>
|
|
326
328
|
) : null}
|
|
327
329
|
</section>
|
package/src/JsonEditor.tsx
CHANGED
|
@@ -23,7 +23,7 @@ const TYPES: JsonKind[] = [
|
|
|
23
23
|
`object`,
|
|
24
24
|
]
|
|
25
25
|
export function JsonEditor(props: EditorProps): VNode {
|
|
26
|
-
const [
|
|
26
|
+
const [getExpanded, toggle] = useExpanded(props.tools, props.path)
|
|
27
27
|
const circular = () =>
|
|
28
28
|
(props.ancestors?.includes(props.value) ?? false) || props.path.length > 50
|
|
29
29
|
const container = () =>
|
|
@@ -40,10 +40,10 @@ export function JsonEditor(props: EditorProps): VNode {
|
|
|
40
40
|
<button
|
|
41
41
|
type="button"
|
|
42
42
|
aria-label={`Expand ${props.path.at(-1)}`}
|
|
43
|
-
aria-expanded={
|
|
43
|
+
aria-expanded={getExpanded()}
|
|
44
44
|
onClick={toggle}
|
|
45
45
|
>
|
|
46
|
-
{
|
|
46
|
+
{getExpanded() ? `▾` : `▸`}
|
|
47
47
|
</button>
|
|
48
48
|
) : (
|
|
49
49
|
<span class="carat-spacer" />
|
|
@@ -84,7 +84,7 @@ export function JsonEditor(props: EditorProps): VNode {
|
|
|
84
84
|
)}
|
|
85
85
|
{props.actions}
|
|
86
86
|
</header>
|
|
87
|
-
{container() &&
|
|
87
|
+
{container() && getExpanded() ? (
|
|
88
88
|
<section class="properties">
|
|
89
89
|
{entries().map(([key]) => (
|
|
90
90
|
<Property key={key} name={key} editor={props} />
|
package/src/store.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Accessor } from "@atom.io/spa"
|
|
2
|
-
import { onCleanup, useId, useSetter
|
|
2
|
+
import { onCleanup, useGetter, useId, useSetter } from "@atom.io/spa"
|
|
3
3
|
import type { RegularAtomFamilyToken, RegularAtomToken, Setter } from "atom.io"
|
|
4
4
|
import type { RootStore } from "atom.io/internal"
|
|
5
5
|
import {
|
|
@@ -42,7 +42,7 @@ export function useLocal<T>(
|
|
|
42
42
|
initial: T,
|
|
43
43
|
): [Accessor<T>, (next: T | Setter<T>) => void] {
|
|
44
44
|
const token = ownedAtom(store, initial)
|
|
45
|
-
return [
|
|
45
|
+
return [useGetter(token) as Accessor<T>, useSetter(token)]
|
|
46
46
|
}
|
|
47
47
|
export function useSaved<T>(
|
|
48
48
|
store: RootStore,
|
|
@@ -56,13 +56,13 @@ export function useSaved<T>(
|
|
|
56
56
|
} catch {
|
|
57
57
|
/* Storage may be unavailable. */
|
|
58
58
|
}
|
|
59
|
-
const [
|
|
59
|
+
const [getValue, setValue] = useLocal(store, initial)
|
|
60
60
|
return [
|
|
61
|
-
|
|
61
|
+
getValue,
|
|
62
62
|
(next) => {
|
|
63
|
-
|
|
63
|
+
setValue(next)
|
|
64
64
|
try {
|
|
65
|
-
localStorage.setItem(key, JSON.stringify(
|
|
65
|
+
localStorage.setItem(key, JSON.stringify(getValue()))
|
|
66
66
|
} catch {
|
|
67
67
|
/* Keep in-memory state when storage is unavailable. */
|
|
68
68
|
}
|
|
@@ -112,16 +112,16 @@ export function useExpanded(
|
|
|
112
112
|
path: Path,
|
|
113
113
|
initial = false,
|
|
114
114
|
): [Accessor<boolean>, (event: MouseEvent) => void] {
|
|
115
|
-
const
|
|
116
|
-
const
|
|
115
|
+
const getExpanded = useGetter(tools.expanded)
|
|
116
|
+
const setExpanded = useSetter(tools.expanded)
|
|
117
117
|
const key = JSON.stringify(path)
|
|
118
118
|
const group = `all:${JSON.stringify(path.slice(0, -1))}`
|
|
119
|
-
const read = () =>
|
|
119
|
+
const read = () => getExpanded()[key] ?? getExpanded()[group] ?? initial
|
|
120
120
|
return [
|
|
121
121
|
read,
|
|
122
122
|
(event) => {
|
|
123
123
|
const next = !read()
|
|
124
|
-
|
|
124
|
+
setExpanded((previous) => {
|
|
125
125
|
const result = { ...previous }
|
|
126
126
|
if (event.shiftKey) {
|
|
127
127
|
const parent = JSON.stringify(path.slice(0, -1))
|