foldkit 0.33.3 → 0.33.5
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
CHANGED
|
@@ -64,7 +64,7 @@ const ClickedIncrement = m('ClickedIncrement')
|
|
|
64
64
|
const ClickedReset = m('ClickedReset')
|
|
65
65
|
|
|
66
66
|
const Message = S.Union(ClickedDecrement, ClickedIncrement, ClickedReset)
|
|
67
|
-
|
|
67
|
+
type Message = typeof Message.Type
|
|
68
68
|
|
|
69
69
|
// UPDATE
|
|
70
70
|
|
|
@@ -142,6 +142,7 @@ Foldkit is a complete system, not a collection of libraries you stitch together.
|
|
|
142
142
|
- **UI Components** — Dialog, menu, tabs, listbox, disclosure — fully accessible primitives that are easy to style and customize.
|
|
143
143
|
- **Field Validation** — Per-field validation state modeled as a discriminated union. Define rules as data, apply them in update, and the Model tracks the result.
|
|
144
144
|
- **Virtual DOM** — Declarative Views powered by [Snabbdom](https://github.com/snabbdom/snabbdom). Fast, keyed diffing. Views are plain functions of your Model.
|
|
145
|
+
- **DevTools** — Built-in overlay for inspecting Messages and Model state. Time-travel mode lets you jump to any point in your app's history.
|
|
145
146
|
- **HMR** — Vite plugin with state-preserving hot module replacement. Change your view, keep your state.
|
|
146
147
|
|
|
147
148
|
## Correctness You (And Your LLM) Can See
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
declare const overlayStyles = ":host {\n --dt-bg: #1e1e2e;\n --dt-surface-selected: #282839;\n --dt-border: #
|
|
1
|
+
declare const overlayStyles = ":host {\n --dt-bg: #1e1e2e;\n --dt-surface-selected: #282839;\n --dt-border: #45475a;\n --dt-text: #cdd6f4;\n --dt-text-muted: #9399b2;\n --dt-accent: #cba6f7;\n --dt-live: #a6e3a1;\n --dt-paused: #fab387;\n --dt-json-string: #a6e3a1;\n --dt-json-number: #89b4fa;\n --dt-json-boolean: #fab387;\n --dt-json-null: #9399b2;\n --dt-json-key: #89dceb;\n --dt-json-tag: #cba6f7;\n --dt-json-preview: #9399b2;\n --dt-json-arrow: #9399b2;\n --dt-tree-hover: #313244;\n --dt-diff-changed: #74c7ec;\n}\n\n*,\n*::before,\n*::after {\n box-sizing: border-box;\n margin: 0;\n padding: 0;\n}\nbutton {\n font: inherit;\n color: inherit;\n}\nul {\n list-style: none;\n}\n\n.fixed {\n position: fixed;\n}\n.flex {\n display: flex;\n}\n.flex-col {\n flex-direction: column;\n}\n.flex-1 {\n flex: 1 1 0%;\n}\n.items-center {\n align-items: center;\n}\n.justify-center {\n justify-content: center;\n}\n.justify-between {\n justify-content: space-between;\n}\n.shrink-0 {\n flex-shrink: 0;\n}\n.inline-block {\n display: inline-block;\n}\n.gap-0\\.5 {\n gap: 2px;\n}\n.gap-1\\.5 {\n gap: 6px;\n}\n.gap-2 {\n gap: 8px;\n}\n.gap-px {\n gap: 1px;\n}\n.px-1 {\n padding-left: 4px;\n padding-right: 4px;\n}\n.px-2 {\n padding-left: 8px;\n padding-right: 8px;\n}\n.px-2\\.5 {\n padding-left: 10px;\n padding-right: 10px;\n}\n.p-3 {\n padding: 12px;\n}\n.px-3 {\n padding-left: 12px;\n padding-right: 12px;\n}\n.py-0\\.5 {\n padding-top: 2px;\n padding-bottom: 2px;\n}\n.pt-1 {\n padding-top: 4px;\n}\n.pl-1 {\n padding-left: 4px;\n}\n.py-1 {\n padding-top: 4px;\n padding-bottom: 4px;\n}\n.py-1\\.5 {\n padding-top: 6px;\n padding-bottom: 6px;\n}\n.py-2 {\n padding-top: 8px;\n padding-bottom: 8px;\n}\n.py-px {\n padding-top: 1px;\n padding-bottom: 1px;\n}\n.w-1\\.5 {\n width: 6px;\n}\n.h-1\\.5 {\n height: 6px;\n}\n.w-3 {\n width: 12px;\n}\n.w-5 {\n width: 20px;\n}\n.h-5 {\n height: 20px;\n}\n.w-14 {\n width: 56px;\n}\n.h-14 {\n height: 56px;\n}\n.min-w-0 {\n min-width: 0;\n}\n.min-w-5 {\n min-width: 20px;\n}\n.min-h-0 {\n min-height: 0;\n}\n/* Badge positions \u2014 flush against side edge */\n.dt-pos-br {\n bottom: 16px;\n right: 0;\n border-radius: 6px 0 0 6px;\n}\n.dt-pos-bl {\n bottom: 16px;\n left: 0;\n border-radius: 0 6px 6px 0;\n}\n.dt-pos-tr {\n top: 16px;\n right: 0;\n border-radius: 6px 0 0 6px;\n}\n.dt-pos-tl {\n top: 16px;\n left: 0;\n border-radius: 0 6px 6px 0;\n}\n.overflow-hidden {\n overflow: hidden;\n}\n.overflow-auto {\n overflow: auto;\n}\n.overflow-y-auto {\n overflow-y: auto;\n}\n\n.truncate {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.rounded {\n border-radius: 4px;\n}\n.rounded-lg {\n border-radius: 8px;\n}\n.rounded-full {\n border-radius: 9999px;\n}\n.border {\n border-width: 1px;\n border-style: solid;\n border-color: var(--dt-border);\n}\n.border-b {\n border-bottom: 1px solid var(--dt-border);\n}\n.border-t {\n border-top: 1px solid var(--dt-border);\n}\n.border-r {\n border-right: 1px solid var(--dt-border);\n}\n.border-l {\n border-left: 1px solid var(--dt-border);\n}\n.border-none {\n border: none;\n}\n.border-l-3 {\n border-left: 3px solid transparent;\n}\n.border-dt-live {\n border-color: var(--dt-live);\n}\n.selected {\n background-color: var(--dt-surface-selected);\n}\n.dt-row:hover:not(.selected) {\n background-color: var(--dt-tree-hover);\n}\n.dt-header-button:hover {\n color: var(--dt-text);\n}\n.dt-resume-button:hover {\n opacity: 0.7;\n}\n.dt-tab-button {\n position: relative;\n background: transparent;\n border: none;\n border-right: 1px solid var(--dt-border);\n outline: none;\n flex: 1;\n}\n.dt-tab-button:last-child {\n border-right: none;\n}\n.dt-tab-active {\n background-color: var(--dt-surface-selected);\n}\n.dt-tab-button:not(.dt-tab-active):hover {\n color: var(--dt-text);\n background-color: rgba(49, 50, 68, 0.3);\n}\n.font-sans {\n font-family:\n system-ui,\n -apple-system,\n BlinkMacSystemFont,\n 'Segoe UI',\n Roboto,\n sans-serif;\n}\n.font-mono {\n font-family:\n ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, monospace;\n}\n.font-medium {\n font-weight: 500;\n}\n.font-semibold {\n font-weight: 600;\n}\n.text-xs {\n font-size: 12px;\n}\n.text-2xs {\n font-size: 10px;\n}\n.text-sm {\n font-size: 11px;\n}\n.text-base {\n font-size: 13px;\n}\n.text-md {\n font-size: 15px;\n}\n.text-lg {\n font-size: 20px;\n}\n.text-xl {\n font-size: 26px;\n}\n.italic {\n font-style: italic;\n}\n.text-right {\n text-align: right;\n}\n.tracking-wide {\n letter-spacing: 0.025em;\n}\n.tracking-wider {\n letter-spacing: 0.05em;\n}\n.leading-none {\n line-height: 1;\n}\n.leading-snug {\n line-height: 1.35;\n}\n.bg-dt-bg {\n background-color: var(--dt-bg);\n}\n.bg-dt-live {\n background-color: var(--dt-live);\n}\n.bg-transparent {\n background-color: transparent;\n}\n.text-dt {\n color: var(--dt-text);\n}\n.text-dt-bg {\n color: var(--dt-bg);\n}\n.text-dt-muted {\n color: var(--dt-text-muted);\n}\n.text-dt-accent {\n color: var(--dt-accent);\n}\n.text-dt-live {\n color: var(--dt-live);\n}\n.text-dt-paused {\n color: var(--dt-paused);\n}\n.cursor-pointer {\n cursor: pointer;\n}\n.outline-none {\n outline: none;\n}\n.transition-colors {\n transition-property: color, background-color, border-color;\n transition-duration: 100ms;\n transition-timing-function: ease;\n}\n\n/* Panel */\n.dt-panel {\n width: 360px;\n height: 480px;\n box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5);\n z-index: 99998;\n}\n/* Panel positions */\n.dt-panel-br {\n bottom: 16px;\n right: 28px;\n}\n.dt-panel-bl {\n bottom: 16px;\n left: 28px;\n}\n.dt-panel-tr {\n top: 16px;\n right: 28px;\n}\n.dt-panel-tl {\n top: 16px;\n left: 28px;\n}\n.dt-panel-wide {\n width: 720px;\n}\n.dt-message-pane {\n width: 320px;\n flex-shrink: 0;\n}\n.dt-badge {\n z-index: 99999;\n box-shadow: -2px 0 8px rgba(0, 0, 0, 0.3);\n transition: background-color 150ms ease;\n border: 1px solid var(--dt-border);\n}\n.dt-badge-accent:hover {\n background-color: #252538;\n}\n.dt-badge-paused {\n background-color: var(--dt-paused);\n color: var(--dt-bg);\n border: none;\n}\n.dt-badge-paused:hover {\n background-color: #e0a070;\n}\n.dt-badge.dt-pos-br,\n.dt-badge.dt-pos-tr {\n border-right: none;\n}\n.dt-badge.dt-pos-bl,\n.dt-badge.dt-pos-tl {\n border-left: none;\n}\n\n/* JSON tree */\n.tree-row {\n position: relative;\n white-space: nowrap;\n line-height: 18px;\n padding-right: 8px;\n}\n.tree-row-expandable:hover {\n background-color: var(--dt-tree-hover);\n}\n.inspector-tree {\n font-family:\n ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, monospace;\n}\n.json-key {\n color: var(--dt-json-key);\n}\n.json-string {\n color: var(--dt-json-string);\n}\n.json-number {\n color: var(--dt-json-number);\n}\n.json-boolean {\n color: var(--dt-json-boolean);\n}\n.json-null {\n color: var(--dt-json-null);\n}\n.json-tag {\n color: var(--dt-json-tag);\n margin-right: 4px;\n}\n.json-preview {\n color: var(--dt-json-preview);\n}\n.json-arrow {\n color: var(--dt-json-arrow);\n width: 10px;\n height: 10px;\n user-select: none;\n}\n\n/* Diff */\n.diff-changed {\n background-color: rgba(116, 199, 236, 0.06);\n}\n.diff-dot {\n position: absolute;\n left: 3px;\n width: 5px;\n height: 5px;\n border-radius: 9999px;\n background-color: var(--dt-diff-changed);\n}\n.diff-dot-inline {\n width: 5px;\n height: 5px;\n border-radius: 9999px;\n background-color: var(--dt-diff-changed);\n flex-shrink: 0;\n}\n.dot-column {\n width: 5px;\n flex-shrink: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n.pause-column {\n width: 8px;\n flex-shrink: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n.dt-pause-icon {\n width: 8px;\n height: 8px;\n color: var(--dt-paused);\n}\n\n/* Interaction blocker \u2014 covers the app while time-travelling */\n.dt-interaction-blocker {\n position: fixed;\n inset: 0;\n z-index: 99997;\n cursor: not-allowed;\n}\n\n/* Mobile */\n@media (max-width: 767px) {\n .dt-panel {\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n width: 100%;\n height: 100%;\n border-radius: 0;\n border: none;\n }\n .dt-panel-wide {\n width: 100%;\n }\n .dt-content {\n flex-direction: column;\n }\n .dt-message-pane {\n width: 100%;\n max-height: 40%;\n border-bottom: 1px solid var(--dt-border);\n }\n .message-list > :last-child {\n border-bottom: none;\n }\n .dt-inspector-pane {\n border-left: none;\n }\n}\n";
|
|
2
2
|
export { overlayStyles };
|
|
3
3
|
//# sourceMappingURL=overlay-styles.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"overlay-styles.d.ts","sourceRoot":"","sources":["../../src/devtools/overlay-styles.ts"],"names":[],"mappings":"AAAA,QAAA,MAAM,aAAa,
|
|
1
|
+
{"version":3,"file":"overlay-styles.d.ts","sourceRoot":"","sources":["../../src/devtools/overlay-styles.ts"],"names":[],"mappings":"AAAA,QAAA,MAAM,aAAa,67QA6gBlB,CAAA;AAED,OAAO,EAAE,aAAa,EAAE,CAAA"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const overlayStyles = `:host {
|
|
2
2
|
--dt-bg: #1e1e2e;
|
|
3
3
|
--dt-surface-selected: #282839;
|
|
4
|
-
--dt-border: #
|
|
4
|
+
--dt-border: #45475a;
|
|
5
5
|
--dt-text: #cdd6f4;
|
|
6
6
|
--dt-text-muted: #9399b2;
|
|
7
7
|
--dt-accent: #cba6f7;
|
|
@@ -239,6 +239,9 @@ ul {
|
|
|
239
239
|
outline: none;
|
|
240
240
|
flex: 1;
|
|
241
241
|
}
|
|
242
|
+
.dt-tab-button:last-child {
|
|
243
|
+
border-right: none;
|
|
244
|
+
}
|
|
242
245
|
.dt-tab-active {
|
|
243
246
|
background-color: var(--dt-surface-selected);
|
|
244
247
|
}
|
|
@@ -316,6 +319,9 @@ ul {
|
|
|
316
319
|
.text-dt {
|
|
317
320
|
color: var(--dt-text);
|
|
318
321
|
}
|
|
322
|
+
.text-dt-bg {
|
|
323
|
+
color: var(--dt-bg);
|
|
324
|
+
}
|
|
319
325
|
.text-dt-muted {
|
|
320
326
|
color: var(--dt-text-muted);
|
|
321
327
|
}
|
|
@@ -375,9 +381,7 @@ ul {
|
|
|
375
381
|
z-index: 99999;
|
|
376
382
|
box-shadow: -2px 0 8px rgba(0, 0, 0, 0.3);
|
|
377
383
|
transition: background-color 150ms ease;
|
|
378
|
-
|
|
379
|
-
.dt-badge-accent {
|
|
380
|
-
border: none;
|
|
384
|
+
border: 1px solid var(--dt-border);
|
|
381
385
|
}
|
|
382
386
|
.dt-badge-accent:hover {
|
|
383
387
|
background-color: #252538;
|
|
@@ -390,8 +394,13 @@ ul {
|
|
|
390
394
|
.dt-badge-paused:hover {
|
|
391
395
|
background-color: #e0a070;
|
|
392
396
|
}
|
|
393
|
-
.
|
|
394
|
-
|
|
397
|
+
.dt-badge.dt-pos-br,
|
|
398
|
+
.dt-badge.dt-pos-tr {
|
|
399
|
+
border-right: none;
|
|
400
|
+
}
|
|
401
|
+
.dt-badge.dt-pos-bl,
|
|
402
|
+
.dt-badge.dt-pos-tl {
|
|
403
|
+
border-left: none;
|
|
395
404
|
}
|
|
396
405
|
|
|
397
406
|
/* JSON tree */
|
|
@@ -507,6 +516,9 @@ ul {
|
|
|
507
516
|
max-height: 40%;
|
|
508
517
|
border-bottom: 1px solid var(--dt-border);
|
|
509
518
|
}
|
|
519
|
+
.message-list > :last-child {
|
|
520
|
+
border-bottom: none;
|
|
521
|
+
}
|
|
510
522
|
.dt-inspector-pane {
|
|
511
523
|
border-left: none;
|
|
512
524
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"overlay.d.ts","sourceRoot":"","sources":["../../src/devtools/overlay.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,MAAM,EAIN,MAAM,EAQP,MAAM,QAAQ,CAAA;AAOf,OAAO,KAAK,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAA;AAMxE,OAAO,EAAE,KAAK,aAAa,EAA+B,MAAM,SAAS,CAAA;
|
|
1
|
+
{"version":3,"file":"overlay.d.ts","sourceRoot":"","sources":["../../src/devtools/overlay.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,MAAM,EAIN,MAAM,EAQP,MAAM,QAAQ,CAAA;AAOf,OAAO,KAAK,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAA;AAMxE,OAAO,EAAE,KAAK,aAAa,EAA+B,MAAM,SAAS,CAAA;AAwwCzE,eAAO,MAAM,aAAa,GACxB,OAAO,aAAa,EACpB,UAAU,gBAAgB,EAC1B,MAAM,YAAY,EAClB,aAAa,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,sCA4ChC,CAAA"}
|
package/dist/devtools/overlay.js
CHANGED
|
@@ -467,7 +467,7 @@ const makeView = (position, mode, maybeBanner) => {
|
|
|
467
467
|
const INSPECTOR_TABS = ['Model', 'Message'];
|
|
468
468
|
const noMessageView = div([
|
|
469
469
|
Class('flex-1 flex items-center justify-center text-dt-muted text-2xs font-mono min-w-0'),
|
|
470
|
-
], ['
|
|
470
|
+
], ['init — no Message']);
|
|
471
471
|
const modelTabContent = (model, inspectedModel) => treeView(inspectedModel, 'root', model.expandedPaths, model.changedPaths, model.affectedPaths, Option.none(), true);
|
|
472
472
|
const messageTabContent = (model) => Option.match(model.maybeInspectedMessage, {
|
|
473
473
|
onNone: () => noMessageView,
|
|
@@ -481,7 +481,9 @@ const makeView = (position, mode, maybeBanner) => {
|
|
|
481
481
|
]),
|
|
482
482
|
});
|
|
483
483
|
const inspectorTabContent = (model, tab, inspectedModel) => M.value(tab).pipe(M.when('Model', () => modelTabContent(model, inspectedModel)), M.when('Message', () => messageTabContent(model)), M.exhaustive);
|
|
484
|
-
const inspectorPaneView = (model) => div([
|
|
484
|
+
const inspectorPaneView = (model) => div([
|
|
485
|
+
Class('flex flex-col border-l min-w-0 min-h-0 flex-1 dt-inspector-pane'),
|
|
486
|
+
], [
|
|
485
487
|
Tabs.view({
|
|
486
488
|
model: model.inspectorTabs,
|
|
487
489
|
toMessage: tabsMessage => GotInspectorTabsMessage({ message: tabsMessage }),
|
|
@@ -522,7 +524,7 @@ const makeView = (position, mode, maybeBanner) => {
|
|
|
522
524
|
], []),
|
|
523
525
|
])
|
|
524
526
|
: div([
|
|
525
|
-
Class('flex flex-col items-center gap-0.5
|
|
527
|
+
Class(clsx('flex flex-col items-center gap-0.5 font-semibold tracking-wider leading-none', model.isPaused ? 'text-dt-bg' : 'text-dt-muted')),
|
|
526
528
|
], [span([], ['D']), span([], ['E']), span([], ['V'])]),
|
|
527
529
|
]);
|
|
528
530
|
const headerClass = 'flex items-center justify-between px-3 py-1.5 border-b shrink-0';
|
|
@@ -561,7 +563,7 @@ const makeView = (position, mode, maybeBanner) => {
|
|
|
561
563
|
...OptionExt.when(mode === 'TimeTravel', span([Class('pause-column')], isPausedHere ? [pauseIconView] : [])).pipe(Option.toArray),
|
|
562
564
|
span([Class('dot-column')], []),
|
|
563
565
|
span([Class(indexClass)], []),
|
|
564
|
-
span([Class('text-base text-dt-muted font-mono')], ['
|
|
566
|
+
span([Class('text-base text-dt-muted font-mono')], ['init']),
|
|
565
567
|
]);
|
|
566
568
|
const pauseIconView = svg([
|
|
567
569
|
AriaHidden(true),
|