foldkit 0.91.0 → 0.93.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,3 +1,3 @@
1
- declare const overlayStyles = ":host {\n position: relative;\n z-index: 2147483647;\n\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.overscroll-none {\n overscroll-behavior: none;\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.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-filter-wrapper {\n position: relative;\n flex-shrink: 0;\n border-bottom: 1px solid var(--dt-border);\n}\n.dt-filter-button {\n width: 100%;\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 6px 12px;\n background: transparent;\n border: none;\n color: var(--dt-text-muted);\n font-family:\n ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, monospace;\n font-size: 13px;\n cursor: pointer;\n text-align: left;\n}\n.dt-filter-button:hover {\n color: var(--dt-text);\n background-color: var(--dt-tree-hover);\n}\n.dt-filter-button:focus-visible {\n outline: 1px solid var(--dt-accent);\n outline-offset: -1px;\n}\n.dt-filter-button[data-open] {\n color: var(--dt-text);\n background-color: var(--dt-surface-selected);\n}\n.dt-filter-button[data-open]:hover {\n background-color: var(--dt-tree-hover);\n}\n.dt-filter-button[data-open] .json-arrow {\n transform: rotate(180deg);\n}\n.dt-filter-items {\n position: absolute;\n top: 100%;\n left: 0;\n right: 0;\n background-color: var(--dt-bg);\n border-top: 1px solid var(--dt-border);\n border-bottom: 1px solid var(--dt-border);\n z-index: 10;\n max-height: 200px;\n overflow-y: auto;\n outline: none;\n}\n.dt-filter-item {\n padding: 6px 12px;\n color: var(--dt-text-muted);\n cursor: pointer;\n font-family:\n ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, monospace;\n font-size: 13px;\n border-bottom: 1px solid var(--dt-border);\n}\n.dt-filter-item:last-child {\n border-bottom: none;\n}\n.dt-filter-item[data-active] {\n background-color: var(--dt-tree-hover);\n color: var(--dt-text);\n}\n.dt-filter-item[data-selected] {\n color: var(--dt-accent);\n}\n.dt-filter-check {\n width: 12px;\n height: 12px;\n visibility: hidden;\n}\n.dt-filter-item[data-selected] .dt-filter-check {\n visibility: visible;\n color: var(--dt-accent);\n}\n.dt-filter-backdrop {\n position: fixed;\n inset: 0;\n pointer-events: none;\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";
1
+ declare const overlayStyles = ":host {\n position: relative;\n z-index: 2147483647;\n\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-3 {\n gap: 12px;\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.overscroll-none {\n overscroll-behavior: none;\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.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-filter-wrapper {\n position: relative;\n flex-shrink: 0;\n border-bottom: 1px solid var(--dt-border);\n}\n.dt-filter-button {\n width: 100%;\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 6px 12px;\n background: transparent;\n border: none;\n color: var(--dt-text-muted);\n font-family:\n ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, monospace;\n font-size: 13px;\n cursor: pointer;\n text-align: left;\n}\n.dt-filter-button:hover {\n color: var(--dt-text);\n background-color: var(--dt-tree-hover);\n}\n.dt-filter-button:focus-visible {\n outline: 1px solid var(--dt-accent);\n outline-offset: -1px;\n}\n.dt-filter-button[data-open] {\n color: var(--dt-text);\n background-color: var(--dt-surface-selected);\n}\n.dt-filter-button[data-open]:hover {\n background-color: var(--dt-tree-hover);\n}\n.dt-filter-button[data-open] .json-arrow {\n transform: rotate(180deg);\n}\n.dt-filter-items {\n position: absolute;\n top: 100%;\n left: 0;\n right: 0;\n background-color: var(--dt-bg);\n border-top: 1px solid var(--dt-border);\n border-bottom: 1px solid var(--dt-border);\n z-index: 10;\n max-height: 200px;\n overflow-y: auto;\n outline: none;\n}\n.dt-filter-item {\n padding: 6px 12px;\n color: var(--dt-text-muted);\n cursor: pointer;\n font-family:\n ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, monospace;\n font-size: 13px;\n border-bottom: 1px solid var(--dt-border);\n}\n.dt-filter-item:last-child {\n border-bottom: none;\n}\n.dt-filter-item[data-active] {\n background-color: var(--dt-tree-hover);\n color: var(--dt-text);\n}\n.dt-filter-item[data-selected] {\n color: var(--dt-accent);\n}\n.dt-filter-check {\n width: 12px;\n height: 12px;\n visibility: hidden;\n}\n.dt-filter-item[data-selected] .dt-filter-check {\n visibility: visible;\n color: var(--dt-accent);\n}\n.dt-filter-backdrop {\n position: fixed;\n inset: 0;\n pointer-events: none;\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/* Scrubber */\n.dt-scrubber-row {\n background-color: var(--dt-bg);\n}\n.dt-scrubber-control {\n position: relative;\n height: 16px;\n width: 100%;\n padding: 0 7px;\n display: flex;\n align-items: center;\n}\n.dt-scrubber-track {\n width: 100%;\n height: 4px;\n background-color: var(--dt-border);\n border-radius: 9999px;\n cursor: pointer;\n}\n.dt-scrubber-track[data-disabled] {\n cursor: not-allowed;\n}\n.dt-scrubber-fill {\n height: 100%;\n background-color: var(--dt-accent);\n border-radius: 9999px;\n}\n.dt-scrubber-thumb {\n width: 14px;\n height: 14px;\n border-radius: 9999px;\n background-color: var(--dt-accent);\n border: 2px solid var(--dt-bg);\n cursor: grab;\n outline: none;\n top: 50%;\n margin-top: -7px;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.4);\n}\n.dt-scrubber-thumb:focus-visible {\n outline: 2px solid var(--dt-text);\n outline-offset: 2px;\n}\n.dt-scrubber-thumb[data-dragging] {\n cursor: grabbing;\n}\n.dt-scrubber-position {\n width: 72px;\n padding-left: 12px;\n border-left: 1px solid var(--dt-border);\n align-self: stretch;\n margin-top: -8px;\n margin-bottom: -8px;\n display: flex;\n align-items: center;\n justify-content: center;\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 .dt-badge.dt-pos-br,\n .dt-badge.dt-pos-bl {\n bottom: 44px;\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,q3UAimBlB,CAAA;AAED,OAAO,EAAE,aAAa,EAAE,CAAA"}
1
+ {"version":3,"file":"overlay-styles.d.ts","sourceRoot":"","sources":["../../src/devTools/overlay-styles.ts"],"names":[],"mappings":"AAAA,QAAA,MAAM,aAAa,ypXAkqBlB,CAAA;AAED,OAAO,EAAE,aAAa,EAAE,CAAA"}
@@ -73,6 +73,9 @@ ul {
73
73
  .gap-2 {
74
74
  gap: 8px;
75
75
  }
76
+ .gap-3 {
77
+ gap: 12px;
78
+ }
76
79
  .gap-px {
77
80
  gap: 1px;
78
81
  }
@@ -577,6 +580,64 @@ ul {
577
580
  cursor: not-allowed;
578
581
  }
579
582
 
583
+ /* Scrubber */
584
+ .dt-scrubber-row {
585
+ background-color: var(--dt-bg);
586
+ }
587
+ .dt-scrubber-control {
588
+ position: relative;
589
+ height: 16px;
590
+ width: 100%;
591
+ padding: 0 7px;
592
+ display: flex;
593
+ align-items: center;
594
+ }
595
+ .dt-scrubber-track {
596
+ width: 100%;
597
+ height: 4px;
598
+ background-color: var(--dt-border);
599
+ border-radius: 9999px;
600
+ cursor: pointer;
601
+ }
602
+ .dt-scrubber-track[data-disabled] {
603
+ cursor: not-allowed;
604
+ }
605
+ .dt-scrubber-fill {
606
+ height: 100%;
607
+ background-color: var(--dt-accent);
608
+ border-radius: 9999px;
609
+ }
610
+ .dt-scrubber-thumb {
611
+ width: 14px;
612
+ height: 14px;
613
+ border-radius: 9999px;
614
+ background-color: var(--dt-accent);
615
+ border: 2px solid var(--dt-bg);
616
+ cursor: grab;
617
+ outline: none;
618
+ top: 50%;
619
+ margin-top: -7px;
620
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.4);
621
+ }
622
+ .dt-scrubber-thumb:focus-visible {
623
+ outline: 2px solid var(--dt-text);
624
+ outline-offset: 2px;
625
+ }
626
+ .dt-scrubber-thumb[data-dragging] {
627
+ cursor: grabbing;
628
+ }
629
+ .dt-scrubber-position {
630
+ width: 72px;
631
+ padding-left: 12px;
632
+ border-left: 1px solid var(--dt-border);
633
+ align-self: stretch;
634
+ margin-top: -8px;
635
+ margin-bottom: -8px;
636
+ display: flex;
637
+ align-items: center;
638
+ justify-content: center;
639
+ }
640
+
580
641
  /* Mobile */
581
642
  @media (max-width: 767px) {
582
643
  .dt-panel {
@@ -606,6 +667,10 @@ ul {
606
667
  .dt-inspector-pane {
607
668
  border-left: none;
608
669
  }
670
+ .dt-badge.dt-pos-br,
671
+ .dt-badge.dt-pos-bl {
672
+ bottom: 44px;
673
+ }
609
674
  }
610
675
  `;
611
676
  export { overlayStyles };
@@ -7,6 +7,7 @@ declare const StoreService_base: Context.ServiceClass<StoreService, "foldkit/Dev
7
7
  recordMessage: (message: Readonly<{
8
8
  _tag: string;
9
9
  }>, modelBeforeUpdate: unknown, modelAfterUpdate: unknown, commands: ReadonlyArray<CommandRecord>, isModelChanged: boolean) => Effect.Effect<void>;
10
+ updateLatestModel: (model: unknown) => Effect.Effect<void>;
10
11
  attachRenderedMounts: (mountStarts: ReadonlyArray<MountRecord>, mountEnds: ReadonlyArray<MountRecord>) => Effect.Effect<void>;
11
12
  getModelAtIndex: (index: number) => Effect.Effect<unknown>;
12
13
  getMessageAtIndex: (index: number) => Effect.Effect<Option.Option<unknown>>;
@@ -1 +1 @@
1
- {"version":3,"file":"overlay.d.ts","sourceRoot":"","sources":["../../src/devTools/overlay.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,OAAO,EACP,MAAM,EAGN,OAAO,EAGP,MAAM,EAKN,MAAM,IAAI,CAAC,EAGX,eAAe,EAEhB,MAAM,QAAQ,CAAA;AAEf,OAAO,KAAK,OAAO,MAAM,qBAAqB,CAAA;AAa9C,OAAO,KAAK,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AAO3E,OAAO,EACL,KAAK,aAAa,EAClB,KAAK,aAAa,EAElB,KAAK,WAAW,EAChB,KAAK,UAAU,EAChB,MAAM,YAAY,CAAA;;oFA+XH,CAAC;;;;;;;;;;;;;AApIjB,cAAM,YAAa,SAAQ,iBAE1B;CAAG;;AAEJ,cAAM,iBAAkB,SAAQ,sBAGC;CAAG;AAEpC,eAAO,MAAM,UAAU;;iBAGsB,CAAA;AAE7C,eAAO,MAAM,YAAY;;iBAGwB,CAAA;AAWjD,eAAO,MAAM,MAAM;;;;wBAUlB,CAAA;AAED,eAAO,MAAM,YAAY;;;;;;;;wBAIqB,CAAA;AAE9C,eAAO,MAAM,aAAa;;;;;;wBAYzB,CAAA;AAED,eAAO,MAAM,MAAM;;wBASlB,CAAA;AAED,eAAO,MAAM,KAAK;;wBASjB,CAAA;AAED,eAAO,MAAM,WAAW;;6BAYvB,CAAA;AA2/CD,eAAO,MAAM,aAAa,GACxB,OAAO,aAAa,EACpB,UAAU,gBAAgB,EAC1B,MAAM,YAAY,EAClB,aAAa,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,sCAoDhC,CAAA"}
1
+ {"version":3,"file":"overlay.d.ts","sourceRoot":"","sources":["../../src/devTools/overlay.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,OAAO,EACP,MAAM,EAGN,OAAO,EAGP,MAAM,EAKN,MAAM,IAAI,CAAC,EAGX,eAAe,EAEhB,MAAM,QAAQ,CAAA;AAEf,OAAO,KAAK,OAAO,MAAM,qBAAqB,CAAA;AAa9C,OAAO,KAAK,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AAQ3E,OAAO,EACL,KAAK,aAAa,EAClB,KAAK,aAAa,EAElB,KAAK,WAAW,EAChB,KAAK,UAAU,EAChB,MAAM,YAAY,CAAA;;oFA8ZQ,CAAC;;;;;;;;;;;;;;AApI5B,cAAM,YAAa,SAAQ,iBAE1B;CAAG;;AAEJ,cAAM,iBAAkB,SAAQ,sBAGC;CAAG;AAEpC,eAAO,MAAM,UAAU;;iBAGsB,CAAA;AAE7C,eAAO,MAAM,YAAY;;iBAGwB,CAAA;AAWjD,eAAO,MAAM,MAAM;;;;wBAUlB,CAAA;AAED,eAAO,MAAM,YAAY;;;;;;;;wBAIqB,CAAA;AAE9C,eAAO,MAAM,aAAa;;;;;;wBAYzB,CAAA;AAED,eAAO,MAAM,MAAM;;wBASlB,CAAA;AAED,eAAO,MAAM,KAAK;;wBASjB,CAAA;AAED,eAAO,MAAM,WAAW;;6BAYvB,CAAA;AA0oDD,eAAO,MAAM,aAAa,GACxB,OAAO,aAAa,EACpB,UAAU,gBAAgB,EAC1B,MAAM,YAAY,EAClB,aAAa,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,sCAkEhC,CAAA"}
@@ -9,6 +9,7 @@ import { makeProgram } from '../runtime/runtime.js';
9
9
  import { makeSubscriptions } from '../runtime/subscription.js';
10
10
  import { evo } from '../struct/index.js';
11
11
  import * as Listbox from '../ui/listbox/public.js';
12
+ import * as Slider from '../ui/slider/public.js';
12
13
  import * as Tabs from '../ui/tabs/public.js';
13
14
  import { overlayStyles } from './overlay-styles.js';
14
15
  import { toInspectableValue } from './serialize.js';
@@ -35,6 +36,7 @@ const DisplayEntry = S.Struct({
35
36
  });
36
37
  const INSPECTOR_TABS_ID = 'dt-inspector';
37
38
  const SUBMODEL_FILTER_ID = 'dt-submodel-filter';
39
+ const SCRUBBER_SLIDER_ID = 'dt-scrubber';
38
40
  const InspectorTabsModel = S.Struct({
39
41
  id: S.String,
40
42
  activeIndex: S.Number,
@@ -73,6 +75,15 @@ const Model = S.Struct({
73
75
  changedPaths: S.HashSet(S.String),
74
76
  affectedPaths: S.HashSet(S.String),
75
77
  inspectorTabs: InspectorTabsModel,
78
+ // NOTE: empirically, inlining `Slider.Model` here throws
79
+ // "Cannot read properties of undefined (reading 'ast')" when running slider
80
+ // tests, because slider imports html → runtime → overlay, and overlay
81
+ // references Slider.Model mid-cycle. S.suspend defers the read until after
82
+ // the cycle resolves. Inlining Listbox.Model works in practice but goes
83
+ // through the same import chain; the exact cause of the asymmetry isn't
84
+ // pinned down. Suspend is the conservative fix until the runtime ↔ overlay
85
+ // cycle is broken at the source.
86
+ scrubberSlider: S.suspend(() => Slider.Model),
76
87
  });
77
88
  const Flags = S.Struct({
78
89
  isMobile: S.Boolean,
@@ -122,6 +133,10 @@ const GotSubmodelFilterMessage = m('GotSubmodelFilterMessage', {
122
133
  const SelectedSubmodelFilter = m('SelectedSubmodelFilter', {
123
134
  tag: S.String,
124
135
  });
136
+ // NOTE: suspend for the same init-order reason as scrubberSlider above.
137
+ const GotScrubberSliderMessage = m('GotScrubberSliderMessage', {
138
+ message: S.suspend(() => Slider.Message),
139
+ });
125
140
  const Message = S.Union([
126
141
  ClickedToggle,
127
142
  ClickedRow,
@@ -141,6 +156,7 @@ const Message = S.Union([
141
156
  ReceivedStoreUpdate,
142
157
  GotSubmodelFilterMessage,
143
158
  SelectedSubmodelFilter,
159
+ GotScrubberSliderMessage,
144
160
  ]);
145
161
  // HELPERS
146
162
  const MILLIS_PER_SECOND = 1000;
@@ -153,6 +169,14 @@ const NO_COMMANDS = [];
153
169
  const NO_MOUNTS = [];
154
170
  const formatTimeDelta = (deltaMs) => M.value(deltaMs).pipe(M.when(0, () => '0ms'), M.when(Number_.isLessThan(MILLIS_PER_SECOND), ms => `+${Math.round(ms)}ms`), M.orElse(ms => `+${(ms / MILLIS_PER_SECOND).toFixed(1)}s`));
155
171
  const MESSAGE_LIST_SELECTOR = '.message-list';
172
+ // NOTE: scrubber slider value space is independent of the store's host
173
+ // indices. Slider value 0 represents init; 1..entries.length represents
174
+ // positions after each buffered message. Passing pausedAtIndex (a host
175
+ // index) straight into setValue, or treating ChangedValue.value as a host
176
+ // index in jumpTo, will silently produce wrong navigation. Translate at
177
+ // the boundaries via the helpers below.
178
+ const hostIndexToSliderValue = (hostIndex, startIndex) => (hostIndex === INIT_INDEX ? 0 : hostIndex - startIndex + 1);
179
+ const sliderValueToHostIndex = (sliderValue, startIndex) => (sliderValue === 0 ? INIT_INDEX : startIndex + sliderValue - 1);
156
180
  const computeSubmodelTags = (entries) => pipe(entries, Array_.flatMap(({ submodelPath }) => submodelPath), Array_.dedupe, Array_.sort(Order.String));
157
181
  const toDisplayCommand = (command) => ({
158
182
  name: command.name,
@@ -358,6 +382,13 @@ const makeUpdate = (store, shadow, mode) => {
358
382
  })
359
383
  : current,
360
384
  selectedIndex: current => shouldFollowLatest ? latestIndex : current,
385
+ scrubberSlider: current => {
386
+ const sliderMax = entries.length;
387
+ const targetSliderValue = isPaused
388
+ ? hostIndexToSliderValue(pausedAtIndex, startIndex)
389
+ : sliderMax;
390
+ return Slider.setValue(Slider.setRange(current, { min: 0, max: sliderMax }), targetSliderValue);
391
+ },
361
392
  }),
362
393
  shouldFollowLatest ? [scrollToTop, inspectLatest] : [],
363
394
  ];
@@ -381,30 +412,68 @@ const makeUpdate = (store, shadow, mode) => {
381
412
  listboxCommands.map(Command.mapEffect(Effect.map(innerMessage => GotSubmodelFilterMessage({ message: innerMessage })))),
382
413
  ];
383
414
  },
415
+ GotScrubberSliderMessage: ({ message: sliderMessage }) => {
416
+ const [nextSlider, sliderCommands, maybeOutMessage] = Slider.update(model.scrubberSlider, sliderMessage);
417
+ const mappedSliderCommands = sliderCommands.map(Command.mapEffect(Effect.map(innerMessage => GotScrubberSliderMessage({ message: innerMessage }))));
418
+ const additionalCommands = Option.match(maybeOutMessage, {
419
+ onNone: () => [],
420
+ onSome: outMessage => M.value(outMessage).pipe(M.tagsExhaustive({
421
+ ChangedValue: ({ value }) => {
422
+ const hostIndex = sliderValueToHostIndex(value, model.startIndex);
423
+ return [jumpTo(hostIndex), inspectState(hostIndex)];
424
+ },
425
+ })),
426
+ });
427
+ return [
428
+ evo(model, { scrubberSlider: () => nextSlider }),
429
+ [...mappedSliderCommands, ...additionalCommands],
430
+ ];
431
+ },
384
432
  }), M.tag('CompletedJump', 'CompletedResume', 'CompletedClear', 'LockedScroll', 'UnlockedScroll', 'ScrolledToTop', () => [model, []]), M.exhaustive);
385
433
  };
386
434
  // SUBSCRIPTION
435
+ const ScrubberDragActivity = S.Literals(['Idle', 'Active']);
387
436
  const SubscriptionDeps = S.Struct({
388
437
  storeUpdates: S.Boolean,
389
438
  mobileBreakpoint: S.Null,
439
+ scrubberPointer: S.Struct({
440
+ dragActivity: ScrubberDragActivity,
441
+ id: S.String,
442
+ min: S.Number,
443
+ max: S.Number,
444
+ }),
445
+ scrubberEscape: S.Struct({
446
+ dragActivity: ScrubberDragActivity,
447
+ }),
390
448
  });
391
- const makeOverlaySubscriptions = (store) => makeSubscriptions(SubscriptionDeps)({
392
- storeUpdates: {
393
- modelToDependencies: () => true,
394
- dependenciesToStream: () => Stream.concat(Stream.fromEffect(SubscriptionRef.get(store.stateRef).pipe(Effect.map(state => ReceivedStoreUpdate(toDisplayState(state))))), Stream.map(SubscriptionRef.changes(store.stateRef), state => ReceivedStoreUpdate(toDisplayState(state)))),
395
- },
396
- mobileBreakpoint: {
397
- modelToDependencies: () => null,
398
- dependenciesToStream: () => Stream.callback(queue => Effect.acquireRelease(Effect.sync(() => {
399
- const mediaQuery = window.matchMedia(MOBILE_BREAKPOINT_QUERY);
400
- const handler = (event) => {
401
- Queue.offerUnsafe(queue, CrossedMobileBreakpoint({ isMobile: event.matches }));
402
- };
403
- mediaQuery.addEventListener('change', handler);
404
- return { mediaQuery, handler };
405
- }), ({ mediaQuery, handler }) => Effect.sync(() => mediaQuery.removeEventListener('change', handler))).pipe(Effect.flatMap(() => Effect.never))),
406
- },
407
- });
449
+ const makeOverlaySubscriptions = (store, shadow) => {
450
+ const sliderSubscriptions = Slider.subscriptionsForRoot(() => shadow);
451
+ return makeSubscriptions(SubscriptionDeps)({
452
+ storeUpdates: {
453
+ modelToDependencies: () => true,
454
+ dependenciesToStream: () => Stream.concat(Stream.fromEffect(SubscriptionRef.get(store.stateRef).pipe(Effect.map(state => ReceivedStoreUpdate(toDisplayState(state))))), Stream.map(SubscriptionRef.changes(store.stateRef), state => ReceivedStoreUpdate(toDisplayState(state)))),
455
+ },
456
+ mobileBreakpoint: {
457
+ modelToDependencies: () => null,
458
+ dependenciesToStream: () => Stream.callback(queue => Effect.acquireRelease(Effect.sync(() => {
459
+ const mediaQuery = window.matchMedia(MOBILE_BREAKPOINT_QUERY);
460
+ const handler = (event) => {
461
+ Queue.offerUnsafe(queue, CrossedMobileBreakpoint({ isMobile: event.matches }));
462
+ };
463
+ mediaQuery.addEventListener('change', handler);
464
+ return { mediaQuery, handler };
465
+ }), ({ mediaQuery, handler }) => Effect.sync(() => mediaQuery.removeEventListener('change', handler))).pipe(Effect.flatMap(() => Effect.never))),
466
+ },
467
+ scrubberPointer: {
468
+ modelToDependencies: model => sliderSubscriptions.dragPointer.modelToDependencies(model.scrubberSlider),
469
+ dependenciesToStream: (deps, readDeps) => Stream.map(sliderSubscriptions.dragPointer.dependenciesToStream(deps, readDeps), message => GotScrubberSliderMessage({ message })),
470
+ },
471
+ scrubberEscape: {
472
+ modelToDependencies: model => sliderSubscriptions.dragEscape.modelToDependencies(model.scrubberSlider),
473
+ dependenciesToStream: (deps, readDeps) => Stream.map(sliderSubscriptions.dragEscape.dependenciesToStream(deps, readDeps), message => GotScrubberSliderMessage({ message })),
474
+ },
475
+ });
476
+ };
408
477
  // VIEW
409
478
  const indexClass = 'text-2xs text-dt-muted font-mono min-w-5';
410
479
  const headerButtonClass = 'dt-header-button bg-transparent border-none text-dt-muted cursor-pointer text-base font-mono transition-colors';
@@ -421,7 +490,7 @@ const PANEL_POSITION_CLASS = {
421
490
  TopRight: 'dt-panel-tr',
422
491
  TopLeft: 'dt-panel-tl',
423
492
  };
424
- const makeView = (position, mode, maybeBanner) => {
493
+ const makeView = (position, mode, shadow, maybeBanner) => {
425
494
  const h = html();
426
495
  const lazyTreeNode = createKeyedLazy();
427
496
  const lazyMessageRow = createKeyedLazy();
@@ -971,7 +1040,37 @@ const makeView = (position, mode, maybeBanner) => {
971
1040
  model.maybeSubmodelFilter,
972
1041
  ]);
973
1042
  };
1043
+ // SCRUBBER
1044
+ const scrubberPositionLabel = (model) => {
1045
+ const total = String(model.entries.length).padStart(3, '0');
1046
+ const current = String(model.scrubberSlider.value).padStart(3, '0');
1047
+ return `${current} / ${total}`;
1048
+ };
1049
+ const scrubberView = (model) => Slider.view({
1050
+ model: model.scrubberSlider,
1051
+ toParentMessage: message => GotScrubberSliderMessage({ message }),
1052
+ ariaLabel: 'Session scrubber',
1053
+ getTrackRoot: () => shadow,
1054
+ formatValue: value => (value === 0 ? 'init' : `Message ${String(value)}`),
1055
+ toView: attributes => h.div([
1056
+ h.Class('dt-scrubber-row flex items-center gap-3 px-3 py-2 border-t shrink-0'),
1057
+ ], [
1058
+ h.div([
1059
+ ...attributes.root,
1060
+ h.Class('dt-scrubber-control flex-1 flex items-center'),
1061
+ ], [
1062
+ h.div([...attributes.track, h.Class('dt-scrubber-track')], [
1063
+ h.div([...attributes.filledTrack, h.Class('dt-scrubber-fill')], []),
1064
+ h.div([...attributes.thumb, h.Class('dt-scrubber-thumb')], []),
1065
+ ]),
1066
+ ]),
1067
+ h.span([
1068
+ h.Class('dt-scrubber-position text-2xs text-dt-muted font-mono shrink-0 tabular-nums'),
1069
+ ], [scrubberPositionLabel(model)]),
1070
+ ]),
1071
+ });
974
1072
  // PANEL
1073
+ const isScrubberVisible = mode === 'TimeTravel';
975
1074
  const panelView = (model) => h.keyed('div')('dt-panel', [
976
1075
  h.Class(clsx('fixed dt-panel dt-panel-wide bg-dt-bg border rounded-lg flex flex-col overflow-hidden font-mono text-dt', PANEL_POSITION_CLASS[position])),
977
1076
  ], [
@@ -989,6 +1088,7 @@ const makeView = (position, mode, maybeBanner) => {
989
1088
  ]),
990
1089
  inspectorPaneView(model),
991
1090
  ]),
1091
+ ...OptionExt.when(isScrubberVisible, scrubberView(model)).pipe(Option.toArray),
992
1092
  ]);
993
1093
  const interactionBlocker = h.div([h.Class('dt-interaction-blocker')], []);
994
1094
  return (model) => ({
@@ -1035,36 +1135,49 @@ export const createOverlay = (store, position, mode, maybeBanner) => Effect.gen(
1035
1135
  ...toDisplayState(storeState),
1036
1136
  };
1037
1137
  });
1038
- const init = (flags) => [
1039
- {
1040
- isOpen: false,
1041
- ...flags,
1042
- selectedIndex: INIT_INDEX,
1043
- isFollowingLatest: true,
1044
- submodelTags: computeSubmodelTags(flags.entries),
1045
- maybeSubmodelFilter: Option.none(),
1046
- submodelFilterListbox: Listbox.init({
1047
- id: SUBMODEL_FILTER_ID,
1048
- selectedItem: ALL_MESSAGES_VALUE,
1049
- }),
1050
- maybeInspectedModel: Option.none(),
1051
- maybeInspectedMessage: Option.none(),
1052
- expandedPaths: HashSet.empty(),
1053
- changedPaths: HashSet.empty(),
1054
- affectedPaths: HashSet.empty(),
1055
- inspectorTabs: Tabs.init({ id: INSPECTOR_TABS_ID }),
1056
- },
1057
- [],
1058
- ];
1138
+ const init = (flags) => {
1139
+ const sliderMax = flags.entries.length;
1140
+ const initialSliderValue = flags.isPaused
1141
+ ? hostIndexToSliderValue(flags.pausedAtIndex, flags.startIndex)
1142
+ : sliderMax;
1143
+ return [
1144
+ {
1145
+ isOpen: false,
1146
+ ...flags,
1147
+ selectedIndex: INIT_INDEX,
1148
+ isFollowingLatest: true,
1149
+ submodelTags: computeSubmodelTags(flags.entries),
1150
+ maybeSubmodelFilter: Option.none(),
1151
+ submodelFilterListbox: Listbox.init({
1152
+ id: SUBMODEL_FILTER_ID,
1153
+ selectedItem: ALL_MESSAGES_VALUE,
1154
+ }),
1155
+ maybeInspectedModel: Option.none(),
1156
+ maybeInspectedMessage: Option.none(),
1157
+ expandedPaths: HashSet.empty(),
1158
+ changedPaths: HashSet.empty(),
1159
+ affectedPaths: HashSet.empty(),
1160
+ inspectorTabs: Tabs.init({ id: INSPECTOR_TABS_ID }),
1161
+ scrubberSlider: Slider.init({
1162
+ id: SCRUBBER_SLIDER_ID,
1163
+ min: 0,
1164
+ max: sliderMax,
1165
+ step: 1,
1166
+ initialValue: initialSliderValue,
1167
+ }),
1168
+ },
1169
+ [],
1170
+ ];
1171
+ };
1059
1172
  const overlayRuntime = makeProgram({
1060
1173
  Model,
1061
1174
  Flags,
1062
1175
  flags,
1063
1176
  init,
1064
1177
  update: makeUpdate(store, shadow, mode),
1065
- view: makeView(position, mode, maybeBanner),
1178
+ view: makeView(position, mode, shadow, maybeBanner),
1066
1179
  container,
1067
- subscriptions: makeOverlaySubscriptions(store),
1180
+ subscriptions: makeOverlaySubscriptions(store, shadow),
1068
1181
  devTools: false,
1069
1182
  freezeModel: false,
1070
1183
  });
@@ -40,12 +40,23 @@ export type Bridge = Readonly<{
40
40
  render: (model: unknown) => Effect.Effect<void>;
41
41
  markRenderPending: Effect.Effect<void>;
42
42
  }>;
43
- export declare const createDevToolsStore: (bridge: Bridge, maxEntries?: number) => Effect.Effect<DevToolsStore>;
43
+ /**
44
+ * Options for `createDevToolsStore`.
45
+ *
46
+ * - `maxEntries`: Maximum number of history entries to retain before evicting the oldest segment. Defaults to 100.
47
+ * - `keyframeInterval`: Number of recorded entries between full model snapshots. Smaller values use more memory but make time-travel a constant-time lookup instead of a replay. Set to `1` to snapshot every entry, which keeps time-travel correct under exclusion-from-history (since excluded Messages are never replayed). Defaults to 31.
48
+ */
49
+ export type CreateDevToolsStoreOptions = Readonly<{
50
+ maxEntries?: number;
51
+ keyframeInterval?: number;
52
+ }>;
53
+ export declare const createDevToolsStore: (bridge: Bridge, options?: CreateDevToolsStoreOptions) => Effect.Effect<DevToolsStore>;
44
54
  export type DevToolsStore = Readonly<{
45
55
  recordInit: (model: unknown, commands: ReadonlyArray<CommandRecord>, mountStarts?: ReadonlyArray<MountRecord>) => Effect.Effect<void>;
46
56
  recordMessage: (message: Readonly<{
47
57
  _tag: string;
48
58
  }>, modelBeforeUpdate: unknown, modelAfterUpdate: unknown, commands: ReadonlyArray<CommandRecord>, isModelChanged: boolean) => Effect.Effect<void>;
59
+ updateLatestModel: (model: unknown) => Effect.Effect<void>;
49
60
  attachRenderedMounts: (mountStarts: ReadonlyArray<MountRecord>, mountEnds: ReadonlyArray<MountRecord>) => Effect.Effect<void>;
50
61
  getModelAtIndex: (index: number) => Effect.Effect<unknown>;
51
62
  getMessageAtIndex: (index: number) => Effect.Effect<Option.Option<unknown>>;
@@ -1 +1 @@
1
- {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/devTools/store.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/devTools/store.ts"],"names":[],"mappings":"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"}
@@ -1,7 +1,8 @@
1
1
  import { Array, Effect, HashMap, HashSet, Match, Option, Predicate, Record, String as String_, SubscriptionRef, pipe, } from 'effect';
2
+ import { evo } from '../struct/index.js';
2
3
  export const INIT_INDEX = -1;
3
- const KEYFRAME_INTERVAL = 31;
4
- const DEFAULT_MAX_ENTRIES = 500;
4
+ const DEFAULT_KEYFRAME_INTERVAL = 31;
5
+ const DEFAULT_MAX_ENTRIES = 100;
5
6
  export const emptyDiff = {
6
7
  changedPaths: HashSet.empty(),
7
8
  affectedPaths: HashSet.empty(),
@@ -74,25 +75,38 @@ const emptyState = {
74
75
  pausedAtIndex: 0,
75
76
  maybeLatestModel: Option.none(),
76
77
  };
77
- export const createDevToolsStore = (bridge, maxEntries = DEFAULT_MAX_ENTRIES) => Effect.gen(function* () {
78
+ export const createDevToolsStore = (bridge, options = {}) => Effect.gen(function* () {
79
+ const maxEntries = options.maxEntries ?? DEFAULT_MAX_ENTRIES;
80
+ const keyframeInterval = options.keyframeInterval ?? DEFAULT_KEYFRAME_INTERVAL;
78
81
  const stateRef = yield* SubscriptionRef.make(emptyState);
79
82
  const replayToIndex = (state, index) => {
80
- const segmentStart = Math.floor(index / KEYFRAME_INTERVAL) * KEYFRAME_INTERVAL;
83
+ // NOTE: the keyframe stored at `index + 1` represents the model
84
+ // state immediately after entry `index` was processed, per the
85
+ // convention `recordMessage` uses. Looking it up first short-circuits
86
+ // replay entirely; this hits on every index when keyframeInterval is
87
+ // 1 and at segment boundaries otherwise. Don't "simplify" to
88
+ // `keyframes[index]` — that's the model BEFORE entry `index`, which
89
+ // would require replaying the entry to land at the right state.
90
+ const directSnapshot = HashMap.get(state.keyframes, index + 1);
91
+ if (Option.isSome(directSnapshot)) {
92
+ return directSnapshot.value;
93
+ }
94
+ const segmentStart = Math.floor(index / keyframeInterval) * keyframeInterval;
81
95
  const keyframeIndex = HashMap.has(state.keyframes, segmentStart)
82
96
  ? segmentStart
83
97
  : state.startIndex;
84
98
  return pipe(state.keyframes, HashMap.get(keyframeIndex), Option.map(keyframeModel => pipe(state.entries, Array.drop(keyframeIndex - state.startIndex), Array.take(index - keyframeIndex + 1), Array.reduce(keyframeModel, (model, entry) => bridge.replay(model, entry.message)))), Option.getOrThrow);
85
99
  };
86
- const addKeyframeIfNeeded = (keyframes, nextAbsoluteIndex, modelAfterUpdate) => nextAbsoluteIndex % KEYFRAME_INTERVAL === 0
100
+ const addKeyframeIfNeeded = (keyframes, nextAbsoluteIndex, modelAfterUpdate) => nextAbsoluteIndex % keyframeInterval === 0
87
101
  ? HashMap.set(keyframes, nextAbsoluteIndex, modelAfterUpdate)
88
102
  : keyframes;
89
103
  const evictOldestSegment = (state) => {
90
- const nextStartIndex = state.startIndex + KEYFRAME_INTERVAL;
104
+ const nextStartIndex = state.startIndex + keyframeInterval;
91
105
  const isPausedAtRetainedIndex = state.pausedAtIndex >= nextStartIndex ||
92
106
  state.pausedAtIndex === INIT_INDEX;
93
107
  return {
94
108
  ...state,
95
- entries: Array.drop(state.entries, KEYFRAME_INTERVAL),
109
+ entries: Array.drop(state.entries, keyframeInterval),
96
110
  keyframes: HashMap.remove(state.keyframes, state.startIndex),
97
111
  startIndex: nextStartIndex,
98
112
  isPaused: state.isPaused && isPausedAtRetainedIndex,
@@ -214,9 +228,11 @@ export const createDevToolsStore = (bridge, maxEntries = DEFAULT_MAX_ENTRIES) =>
214
228
  onSome: ({ diff }) => diff,
215
229
  }));
216
230
  });
231
+ const updateLatestModel = (model) => SubscriptionRef.update(stateRef, evo({ maybeLatestModel: () => Option.some(model) }));
217
232
  return {
218
233
  recordInit,
219
234
  recordMessage,
235
+ updateLatestModel,
220
236
  attachRenderedMounts,
221
237
  getModelAtIndex,
222
238
  getMessageAtIndex,
@@ -121,7 +121,7 @@ export const oneOf = (...parsers) => ({
121
121
  onEmpty: () => Effect.fail(new ParseError({
122
122
  message: `No parsers provided for path: /${Array.join(segments, '/')}`,
123
123
  })),
124
- onNonEmpty: (head, tail) => Array.reduce(tail, head.parse(segments, search), (acc, parser) => acc.pipe(Effect.catch(() => parser.parse(segments, search)))),
124
+ onNonEmpty: () => Effect.firstSuccessOf(Array.map(parsers, parser => parser.parse(segments, search))),
125
125
  }),
126
126
  });
127
127
  /**
@@ -1,5 +1,5 @@
1
1
  export { makeProgram, run } from './runtime.js';
2
- export type { RoutingConfig, CrashConfig, CrashContext, RoutingProgramConfigWithFlags, RoutingProgramConfig, ProgramConfigWithFlags, ProgramConfig, ProgramInit, RoutingProgramInit, MakeRuntimeReturn, Visibility, SlowViewContext, SlowViewConfig, DevToolsConfig, } from './runtime.js';
2
+ export type { RoutingConfig, CrashConfig, CrashContext, RoutingProgramConfigWithFlags, RoutingProgramConfig, ProgramConfigWithFlags, ProgramConfig, ProgramInit, RoutingProgramInit, MakeRuntimeReturn, Visibility, SlowViewContext, SlowViewConfig, DevToolsConfig, DevToolsMode, DevToolsModeConfig, } from './runtime.js';
3
3
  export { UrlRequest } from './urlRequest.js';
4
4
  export type { Internal, External } from './urlRequest.js';
5
5
  //# sourceMappingURL=public.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"public.d.ts","sourceRoot":"","sources":["../../src/runtime/public.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,cAAc,CAAA;AAE/C,YAAY,EACV,aAAa,EACb,WAAW,EACX,YAAY,EACZ,6BAA6B,EAC7B,oBAAoB,EACpB,sBAAsB,EACtB,aAAa,EACb,WAAW,EACX,kBAAkB,EAClB,iBAAiB,EACjB,UAAU,EACV,eAAe,EACf,cAAc,EACd,cAAc,GACf,MAAM,cAAc,CAAA;AAErB,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAE5C,YAAY,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAA"}
1
+ {"version":3,"file":"public.d.ts","sourceRoot":"","sources":["../../src/runtime/public.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,cAAc,CAAA;AAE/C,YAAY,EACV,aAAa,EACb,WAAW,EACX,YAAY,EACZ,6BAA6B,EAC7B,oBAAoB,EACpB,sBAAsB,EACtB,aAAa,EACb,WAAW,EACX,kBAAkB,EAClB,iBAAiB,EACjB,UAAU,EACV,eAAe,EACf,cAAc,EACd,cAAc,EACd,YAAY,EACZ,kBAAkB,GACnB,MAAM,cAAc,CAAA;AAErB,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAE5C,YAAY,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAA"}
@@ -15,6 +15,15 @@ export type Visibility = 'Development' | 'Always';
15
15
  * - `'TimeTravel'`: Clicking a row pauses the app at that historical state. Resume to continue.
16
16
  */
17
17
  export type DevToolsMode = 'Inspect' | 'TimeTravel';
18
+ /** Mode value for the DevTools panel. Either a single mode used in every
19
+ * environment, or an object selecting different modes for development and
20
+ * production. Use the object form to keep `'TimeTravel'` for local debugging
21
+ * while shipping the safer `'Inspect'` mode to users. `'TimeTravel'` in
22
+ * production pauses the user's app when a history row is clicked. */
23
+ export type DevToolsModeConfig = DevToolsMode | Readonly<{
24
+ development: DevToolsMode;
25
+ production: DevToolsMode;
26
+ }>;
18
27
  /**
19
28
  * DevTools configuration.
20
29
  *
@@ -22,14 +31,18 @@ export type DevToolsMode = 'Inspect' | 'TimeTravel';
22
31
  *
23
32
  * - `show`: `'Development'` (default) enables in dev mode only, `'Always'` enables in all environments including production.
24
33
  * - `position`: Where the badge and panel appear. Defaults to `'BottomRight'`.
25
- * - `mode`: `'TimeTravel'` (default) enables full time-travel debugging. `'Inspect'` allows browsing state snapshots without pausing the app.
34
+ * - `mode`: `'TimeTravel'` (default) enables full time-travel debugging. `'Inspect'` allows browsing state snapshots without pausing the app. Pass `{ development, production }` to use different modes per environment. Useful when DevTools is shown in production (`show: 'Always'`) and you want `'TimeTravel'` only in local development.
26
35
  * - `banner`: Optional text shown as a banner at the top of the panel.
36
+ * - `excludeFromHistory`: Message `_tag` values whose dispatches should not be recorded in DevTools history. The Messages still drive `update` and the runtime as usual; they just don't appear in the history panel and don't pay the per-Message diff cost. Use for high-frequency Messages (animation frames, pointer moves, scroll events) that would flood history without adding insight.
37
+ * - `maxEntries`: Maximum number of recorded Messages retained in history before the oldest is evicted. Defaults to 100. Clamped to the range 20-500: smaller values keep the panel snappy under high message rates, larger values give you more scroll-back. Each retained entry stores a full Model snapshot, so memory cost scales linearly with both `maxEntries` and your Model size.
27
38
  */
28
39
  export type DevToolsConfig = false | Readonly<{
29
40
  show?: Visibility;
30
41
  position?: DevToolsPosition;
31
- mode?: DevToolsMode;
42
+ mode?: DevToolsModeConfig;
32
43
  banner?: string;
44
+ excludeFromHistory?: ReadonlyArray<string>;
45
+ maxEntries?: number;
33
46
  /**
34
47
  * The application's `Message` Schema. When provided and the running app
35
48
  * is connected to the Foldkit DevTools MCP server, AI agents can dispatch
@@ -1 +1 @@
1
- {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../src/runtime/runtime.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../src/runtime/runtime.ts"],"names":[],"mappings":"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"}
@@ -20,6 +20,16 @@ const toCommandRecord = (command) => command.args !== undefined
20
20
  const DEFAULT_DEV_TOOLS_SHOW = 'Development';
21
21
  const DEFAULT_DEV_TOOLS_POSITION = 'BottomRight';
22
22
  const DEFAULT_DEV_TOOLS_MODE = 'TimeTravel';
23
+ const resolveDevToolsMode = (config) => {
24
+ if (typeof config === 'string') {
25
+ return config;
26
+ }
27
+ else {
28
+ return import.meta.hot ? config.development : config.production;
29
+ }
30
+ };
31
+ const DEV_TOOLS_MAX_ENTRIES_MIN = 20;
32
+ const DEV_TOOLS_MAX_ENTRIES_MAX = 500;
23
33
  const DEFAULT_SLOW_VIEW_SHOW = 'Development';
24
34
  const DEFAULT_SLOW_VIEW_THRESHOLD_MS = 16;
25
35
  const defaultSlowViewCallback = (context) => {
@@ -43,6 +53,11 @@ const makeRuntime = ({ Model, flags: resolveFlags, init, update, view, subscript
43
53
  onSlowView: config.onSlowView ?? defaultSlowViewCallback,
44
54
  })));
45
55
  const isFreezeModelActive = freezeModel !== false && !!import.meta.hot;
56
+ const excludeFromHistoryTags = pipe(devTools ?? {}, Option.liftPredicate(config => config !== false), Option.flatMapNullishOr(config => config.excludeFromHistory), Option.match({
57
+ onNone: () => new Set(),
58
+ onSome: tags => new Set(tags),
59
+ }));
60
+ const devToolsMaxEntries = pipe(devTools ?? {}, Option.liftPredicate(config => config !== false), Option.flatMapNullishOr(config => config.maxEntries), Option.map(value => Math.max(DEV_TOOLS_MAX_ENTRIES_MIN, Math.min(DEV_TOOLS_MAX_ENTRIES_MAX, value))), Option.getOrUndefined);
46
61
  const maybeFreezeModel = (model) => isFreezeModelActive ? deepFreeze(model) : model;
47
62
  const runtimeId = container?.id ?? '';
48
63
  // NOTE: When the message queue drains a chain of dispatches (e.g. recursive
@@ -221,13 +236,25 @@ const makeRuntime = ({ Model, flags: resolveFlags, init, update, view, subscript
221
236
  attributes: command.args ?? {},
222
237
  }), provideAllResources, Effect.flatMap(enqueueNormal))));
223
238
  const maybeDevToolsStore = yield* Ref.get(maybeDevToolsStoreRef);
239
+ /* eslint-disable-next-line @typescript-eslint/consistent-type-assertions */
240
+ const messageTag = message._tag;
241
+ const isModelChanged = currentModel !== nextModel;
242
+ const isExcludedFromHistory = excludeFromHistoryTags.has(messageTag);
224
243
  yield* Option.match(maybeDevToolsStore, {
225
244
  onNone: () => Effect.void,
226
- onSome: store => store.recordMessage(
227
- /* eslint-disable-next-line @typescript-eslint/consistent-type-assertions */
228
- message, currentModel, nextModel, Array.map(
229
- /* eslint-disable-next-line @typescript-eslint/consistent-type-assertions */
230
- commands, toCommandRecord), currentModel !== nextModel),
245
+ onSome: store => {
246
+ if (!isExcludedFromHistory) {
247
+ return store.recordMessage(
248
+ /* eslint-disable-next-line @typescript-eslint/consistent-type-assertions */
249
+ message, currentModel, nextModel, Array.map(
250
+ /* eslint-disable-next-line @typescript-eslint/consistent-type-assertions */
251
+ commands, toCommandRecord), isModelChanged);
252
+ }
253
+ if (isModelChanged) {
254
+ return store.updateLatestModel(nextModel);
255
+ }
256
+ return Effect.void;
257
+ },
231
258
  });
232
259
  });
233
260
  // NOTE: `dispatchService` defaults to the live dispatch but is
@@ -264,17 +291,28 @@ const makeRuntime = ({ Model, flags: resolveFlags, init, update, view, subscript
264
291
  const isInIframe = window.self !== window.top;
265
292
  const resolvedDevTools = pipe(devTools ?? {}, Option.liftPredicate(config => config !== false), Option.filter(config => Match.value(config.show ?? DEFAULT_DEV_TOOLS_SHOW).pipe(Match.when('Always', () => true), Match.when('Development', () => !!import.meta.hot && !isInIframe), Match.exhaustive)), Option.map(config => ({
266
293
  position: config.position ?? DEFAULT_DEV_TOOLS_POSITION,
267
- mode: config.mode ?? DEFAULT_DEV_TOOLS_MODE,
294
+ mode: resolveDevToolsMode(config.mode ?? DEFAULT_DEV_TOOLS_MODE),
268
295
  maybeBanner: Option.fromNullishOr(config.banner),
269
296
  })));
270
297
  if (Option.isSome(resolvedDevTools)) {
271
298
  const { position, mode, maybeBanner } = resolvedDevTools.value;
299
+ // NOTE: when excludeFromHistory is active, the runtime drops
300
+ // excluded Messages from the recorded history. Replay walks the
301
+ // recorded entries forward from the nearest keyframe — but with
302
+ // exclusion, the dropped Messages aren't in that walk, so any
303
+ // cumulative state they would have produced is missing from the
304
+ // replayed model. Setting keyframeInterval to 1 stores a full
305
+ // snapshot on every recorded entry, so time-travel becomes a
306
+ // direct lookup that reflects the real live state at the moment
307
+ // the entry was recorded.
308
+ const isExcludingMessages = excludeFromHistoryTags.size > 0;
272
309
  const devToolsStore = yield* createDevToolsStore({
310
+ /* eslint-disable @typescript-eslint/consistent-type-assertions */
273
311
  replay: (model, message) => {
274
- /* eslint-disable-next-line @typescript-eslint/consistent-type-assertions */
275
312
  const [updatedModel] = update(model, message);
276
313
  return maybeFreezeModel(updatedModel);
277
314
  },
315
+ /* eslint-enable @typescript-eslint/consistent-type-assertions */
278
316
  // NOTE: clears the dirty bit on the jumpTo render so the
279
317
  // renderLoop's Stream.changes sees the next dispatch as a real
280
318
  // false-to-true transition rather than a deduped no-op. Passes
@@ -295,6 +333,11 @@ const makeRuntime = ({ Model, flags: resolveFlags, init, update, view, subscript
295
333
  // animation frame, which renders the live model with live
296
334
  // dispatch and rebinds listeners.
297
335
  markRenderPending: SubscriptionRef.set(isRenderPendingRef, true),
336
+ }, {
337
+ ...(isExcludingMessages && { keyframeInterval: 1 }),
338
+ ...(devToolsMaxEntries !== undefined && {
339
+ maxEntries: devToolsMaxEntries,
340
+ }),
298
341
  });
299
342
  yield* Ref.set(maybeDevToolsStoreRef, Option.some(devToolsStore));
300
343
  // The init render runs below; capture the events it produces. We
@@ -172,7 +172,6 @@ export declare const SubscriptionDeps: S.Struct<{
172
172
  export declare const subscriptions: import("../../runtime/subscription.js").Subscriptions<{
173
173
  readonly id: string;
174
174
  readonly orientation: "Vertical" | "Horizontal";
175
- readonly activationThreshold: number;
176
175
  readonly dragState: {
177
176
  readonly _tag: "Idle";
178
177
  } | {
@@ -209,7 +208,10 @@ export declare const subscriptions: import("../../runtime/subscription.js").Subs
209
208
  readonly targetContainerId: string;
210
209
  readonly targetIndex: number;
211
210
  };
211
+ readonly activationThreshold: number;
212
212
  }, {
213
+ readonly _tag: "CancelledDrag";
214
+ } | {
213
215
  readonly _tag: "PressedDraggable";
214
216
  readonly screenX: number;
215
217
  readonly screenY: number;
@@ -228,8 +230,6 @@ export declare const subscriptions: import("../../runtime/subscription.js").Subs
228
230
  }>;
229
231
  } | {
230
232
  readonly _tag: "ReleasedPointer";
231
- } | {
232
- readonly _tag: "CancelledDrag";
233
233
  } | {
234
234
  readonly _tag: "ActivatedKeyboardDrag";
235
235
  readonly index: number;
@@ -83,26 +83,41 @@ type UpdateReturn = readonly [
83
83
  /** Processes a slider message and returns the next model, commands, and an
84
84
  * optional out-message for the parent. */
85
85
  export declare const update: (model: Model, message: Message) => UpdateReturn;
86
- /** Schema describing the subscription dependencies for document-level drag
87
- * tracking. */
86
+ /** Updates the slider's range. Snaps and clamps the current value into the
87
+ * new range. Use this when min/max derive from external state (e.g. a
88
+ * bounded buffer whose first/last index shifts over time). Unlike `setValue`,
89
+ * this runs even while the user is Dragging: a structural range change
90
+ * cannot leave the value out of bounds. */
91
+ export declare const setRange: (model: Model, range: Readonly<{
92
+ min: number;
93
+ max: number;
94
+ }>) => Model;
95
+ /** Sets the slider's value, snapped and clamped into the current range.
96
+ * No-op while the user is actively dragging. drag state owns the value.
97
+ * Does not emit `ChangedValue`; use when the value is being driven by
98
+ * external state rather than user input. */
99
+ export declare const setValue: (model: Model, value: number) => Model;
100
+ /** Schema describing the subscription dependencies for slider drag. */
88
101
  export declare const SubscriptionDeps: S.Struct<{
89
- readonly documentPointer: S.Struct<{
102
+ readonly dragPointer: S.Struct<{
90
103
  readonly dragActivity: S.Literals<readonly ["Idle", "Active"]>;
91
104
  readonly id: S.String;
92
105
  readonly min: S.Number;
93
106
  readonly max: S.Number;
94
107
  }>;
95
- readonly documentEscape: S.Struct<{
108
+ readonly dragEscape: S.Struct<{
96
109
  readonly dragActivity: S.Literals<readonly ["Idle", "Active"]>;
97
110
  }>;
98
111
  }>;
99
- /** Document-level subscriptions for pointer and keyboard events during slider
100
- * drag. */
101
- export declare const subscriptions: import("../../runtime/subscription.js").Subscriptions<{
112
+ /** Builds slider drag subscriptions, looking up the track
113
+ * element through the supplied root resolver. Use this when the slider is
114
+ * rendered inside a Shadow DOM. The root is read lazily so consumers can
115
+ * resolve it at subscription time. */
116
+ export declare const subscriptionsForRoot: (getTrackRoot: () => Document | ShadowRoot) => import("../../runtime/subscription.js").Subscriptions<{
102
117
  readonly value: number;
103
118
  readonly id: string;
104
- readonly max: number;
105
119
  readonly min: number;
120
+ readonly max: number;
106
121
  readonly step: number;
107
122
  readonly dragState: {
108
123
  readonly _tag: "Idle";
@@ -111,8 +126,45 @@ export declare const subscriptions: import("../../runtime/subscription.js").Subs
111
126
  readonly originValue: number;
112
127
  };
113
128
  }, {
129
+ readonly _tag: "PressedThumb";
130
+ } | {
131
+ readonly _tag: "PressedPointer";
132
+ readonly value: number;
133
+ } | {
134
+ readonly _tag: "MovedDragPointer";
135
+ readonly value: number;
136
+ } | {
137
+ readonly _tag: "ReleasedDragPointer";
138
+ } | {
114
139
  readonly _tag: "CancelledDrag";
115
140
  } | {
141
+ readonly _tag: "PressedKeyboardNavigation";
142
+ readonly direction: "Max" | "Min" | "StepDecrement" | "StepIncrement" | "PageDecrement" | "PageIncrement";
143
+ }, S.Struct<{
144
+ readonly dragPointer: S.Struct<{
145
+ readonly dragActivity: S.Literals<readonly ["Idle", "Active"]>;
146
+ readonly id: S.String;
147
+ readonly min: S.Number;
148
+ readonly max: S.Number;
149
+ }>;
150
+ readonly dragEscape: S.Struct<{
151
+ readonly dragActivity: S.Literals<readonly ["Idle", "Active"]>;
152
+ }>;
153
+ }>, never>;
154
+ /** Default drag subscriptions, with the track looked up via `document`. */
155
+ export declare const subscriptions: import("../../runtime/subscription.js").Subscriptions<{
156
+ readonly value: number;
157
+ readonly id: string;
158
+ readonly min: number;
159
+ readonly max: number;
160
+ readonly step: number;
161
+ readonly dragState: {
162
+ readonly _tag: "Idle";
163
+ } | {
164
+ readonly _tag: "Dragging";
165
+ readonly originValue: number;
166
+ };
167
+ }, {
116
168
  readonly _tag: "PressedThumb";
117
169
  } | {
118
170
  readonly _tag: "PressedPointer";
@@ -122,17 +174,19 @@ export declare const subscriptions: import("../../runtime/subscription.js").Subs
122
174
  readonly value: number;
123
175
  } | {
124
176
  readonly _tag: "ReleasedDragPointer";
177
+ } | {
178
+ readonly _tag: "CancelledDrag";
125
179
  } | {
126
180
  readonly _tag: "PressedKeyboardNavigation";
127
181
  readonly direction: "Max" | "Min" | "StepDecrement" | "StepIncrement" | "PageDecrement" | "PageIncrement";
128
182
  }, S.Struct<{
129
- readonly documentPointer: S.Struct<{
183
+ readonly dragPointer: S.Struct<{
130
184
  readonly dragActivity: S.Literals<readonly ["Idle", "Active"]>;
131
185
  readonly id: S.String;
132
186
  readonly min: S.Number;
133
187
  readonly max: S.Number;
134
188
  }>;
135
- readonly documentEscape: S.Struct<{
189
+ readonly dragEscape: S.Struct<{
136
190
  readonly dragActivity: S.Literals<readonly ["Idle", "Active"]>;
137
191
  }>;
138
192
  }>, never>;
@@ -156,13 +210,17 @@ export type ViewConfig<ParentMessage> = Readonly<{
156
210
  formatValue?: (value: number) => string;
157
211
  isDisabled?: boolean;
158
212
  name?: string;
213
+ /** Resolves the root that holds the slider track when looking it up by its
214
+ * `data-slider-track-id` attribute. Defaults to `document`. Provide a
215
+ * ShadowRoot when rendering the slider inside a shadow tree so pointer
216
+ * events on the track can map clientX into a value. */
217
+ getTrackRoot?: () => Document | ShadowRoot;
159
218
  }>;
160
219
  /** Renders an accessible slider by building ARIA attribute groups and
161
220
  * delegating layout to the consumer's `toView` callback. Follows the
162
221
  * WAI-ARIA slider pattern — role="slider" on the thumb, aria-valuemin /
163
222
  * aria-valuemax / aria-valuenow, keyboard navigation by step / page / home /
164
- * end. Pointer drag is handled by the component's document-level
165
- * subscriptions. */
223
+ * end. Pointer drag is handled by the component's drag subscriptions. */
166
224
  export declare const view: <ParentMessage>(config: ViewConfig<ParentMessage>) => Html;
167
225
  /** Creates a memoized slider view. Static config is captured in a closure;
168
226
  * only `model` and `toParentMessage` are compared per render via `createLazy`. */
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/ui/slider/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/ui/slider/index.ts"],"names":[],"mappings":"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"}
@@ -152,10 +152,27 @@ export const update = (model, message) => M.value(message).pipe(withUpdateReturn
152
152
  }), M.orElse(() => [model, [], Option.none()])),
153
153
  PressedKeyboardNavigation: ({ direction }) => withValue(model, nextValueForDirection(model, direction), []),
154
154
  }));
155
+ /** Updates the slider's range. Snaps and clamps the current value into the
156
+ * new range. Use this when min/max derive from external state (e.g. a
157
+ * bounded buffer whose first/last index shifts over time). Unlike `setValue`,
158
+ * this runs even while the user is Dragging: a structural range change
159
+ * cannot leave the value out of bounds. */
160
+ export const setRange = (model, range) => evo(model, {
161
+ min: () => range.min,
162
+ max: () => range.max,
163
+ value: current => snapAndClamp(current, range.min, range.max, model.step),
164
+ });
165
+ /** Sets the slider's value, snapped and clamped into the current range.
166
+ * No-op while the user is actively dragging. drag state owns the value.
167
+ * Does not emit `ChangedValue`; use when the value is being driven by
168
+ * external state rather than user input. */
169
+ export const setValue = (model, value) => M.value(model.dragState).pipe(M.withReturnType(), M.tag('Dragging', () => model), M.orElse(() => evo(model, {
170
+ value: () => snapAndClamp(value, model.min, model.max, model.step),
171
+ })));
155
172
  // SUBSCRIPTION
156
173
  const DragActivity = S.Literals(['Idle', 'Active']);
157
174
  const dragActivityFromModel = (model) => M.value(model.dragState).pipe(M.withReturnType(), M.tag('Dragging', () => 'Active'), M.orElse(() => 'Idle'));
158
- const trackElement = (id) => Option.fromNullishOr(document.querySelector(`[data-slider-track-id="${id}"]`));
175
+ const trackElement = (id, root) => Option.fromNullishOr(root.querySelector(`[data-slider-track-id="${id}"]`));
159
176
  const valueFromClientX = (clientX, trackElement_, min, max) => {
160
177
  const rect = trackElement_.getBoundingClientRect();
161
178
  if (rect.width === 0) {
@@ -166,23 +183,24 @@ const valueFromClientX = (clientX, trackElement_, min, max) => {
166
183
  return min + fraction * (max - min);
167
184
  }
168
185
  };
169
- /** Schema describing the subscription dependencies for document-level drag
170
- * tracking. */
186
+ /** Schema describing the subscription dependencies for slider drag. */
171
187
  export const SubscriptionDeps = S.Struct({
172
- documentPointer: S.Struct({
188
+ dragPointer: S.Struct({
173
189
  dragActivity: DragActivity,
174
190
  id: S.String,
175
191
  min: S.Number,
176
192
  max: S.Number,
177
193
  }),
178
- documentEscape: S.Struct({
194
+ dragEscape: S.Struct({
179
195
  dragActivity: DragActivity,
180
196
  }),
181
197
  });
182
- /** Document-level subscriptions for pointer and keyboard events during slider
183
- * drag. */
184
- export const subscriptions = makeSubscriptions(SubscriptionDeps)({
185
- documentPointer: {
198
+ /** Builds slider drag subscriptions, looking up the track
199
+ * element through the supplied root resolver. Use this when the slider is
200
+ * rendered inside a Shadow DOM. The root is read lazily so consumers can
201
+ * resolve it at subscription time. */
202
+ export const subscriptionsForRoot = (getTrackRoot) => makeSubscriptions(SubscriptionDeps)({
203
+ dragPointer: {
186
204
  modelToDependencies: model => ({
187
205
  dragActivity: dragActivityFromModel(model),
188
206
  id: model.id,
@@ -190,7 +208,7 @@ export const subscriptions = makeSubscriptions(SubscriptionDeps)({
190
208
  max: model.max,
191
209
  }),
192
210
  dependenciesToStream: ({ dragActivity, id, min, max }) => {
193
- const pointerEvents = Stream.merge(Stream.fromEventListener(document, 'pointermove').pipe(Stream.mapEffect(event => Effect.sync(() => Option.map(trackElement(id), element => MovedDragPointer({
211
+ const pointerEvents = Stream.merge(Stream.fromEventListener(document, 'pointermove').pipe(Stream.mapEffect(event => Effect.sync(() => Option.map(trackElement(id, getTrackRoot()), element => MovedDragPointer({
194
212
  value: valueFromClientX(event.clientX, element, min, max),
195
213
  })))), Stream.filter(Option.isSome), Stream.map(option => option.value)), Stream.fromEventListener(document, 'pointerup').pipe(Stream.map(() => ReleasedDragPointer())));
196
214
  // NOTE: prevents text selection and locks cursor to grabbing while the
@@ -210,13 +228,15 @@ export const subscriptions = makeSubscriptions(SubscriptionDeps)({
210
228
  return Stream.when(Stream.merge(pointerEvents, documentDragStyles), Effect.sync(() => dragActivity === 'Active'));
211
229
  },
212
230
  },
213
- documentEscape: {
231
+ dragEscape: {
214
232
  modelToDependencies: model => ({
215
233
  dragActivity: dragActivityFromModel(model),
216
234
  }),
217
235
  dependenciesToStream: ({ dragActivity }) => Stream.when(Stream.fromEventListener(document, 'keydown').pipe(Stream.filter(({ key }) => key === 'Escape'), Stream.map(() => CancelledDrag())), Effect.sync(() => dragActivity === 'Active')),
218
236
  },
219
237
  });
238
+ /** Default drag subscriptions, with the track looked up via `document`. */
239
+ export const subscriptions = subscriptionsForRoot(() => document);
220
240
  // VIEW
221
241
  const LEFT_MOUSE_BUTTON = 0;
222
242
  const labelId = (id) => `${id}-label`;
@@ -226,16 +246,15 @@ const percentString = (fraction) => `${Math.round(fraction * 10000) / 100}%`;
226
246
  * delegating layout to the consumer's `toView` callback. Follows the
227
247
  * WAI-ARIA slider pattern — role="slider" on the thumb, aria-valuemin /
228
248
  * aria-valuemax / aria-valuenow, keyboard navigation by step / page / home /
229
- * end. Pointer drag is handled by the component's document-level
230
- * subscriptions. */
249
+ * end. Pointer drag is handled by the component's drag subscriptions. */
231
250
  export const view = (config) => {
232
251
  const h = html();
233
- const { model, toParentMessage, formatValue, isDisabled = false, name, } = config;
252
+ const { model, toParentMessage, formatValue, isDisabled = false, name, getTrackRoot = () => document, } = config;
234
253
  const { id, value, min, max } = model;
235
254
  const isDragging = model.dragState._tag === 'Dragging';
236
255
  const fraction = fractionOfValue(model);
237
256
  const handleKeyDown = (key) => Option.map(keyToDirection(key), direction => toParentMessage(PressedKeyboardNavigation({ direction })));
238
- const pointerAtClientX = (clientX) => Option.map(trackElement(id), element => toParentMessage(PressedPointer({
257
+ const pointerAtClientX = (clientX) => Option.map(trackElement(id, getTrackRoot()), element => toParentMessage(PressedPointer({
239
258
  value: valueFromClientX(clientX, element, min, max),
240
259
  })));
241
260
  const trackPointerHandler = (_pointerType, button, _screenX, _screenY, _timeStamp, clientX) => pipe(button, Option.liftPredicate(Equal.equals(LEFT_MOUSE_BUTTON)), Option.flatMap(() => pointerAtClientX(clientX)));
@@ -1,3 +1,3 @@
1
- export { init, update, view, lazy, subscriptions, fractionOfValue, Model, Message, OutMessage, SubscriptionDeps, } from './index.js';
1
+ export { init, update, setRange, setValue, view, lazy, subscriptions, subscriptionsForRoot, fractionOfValue, Model, Message, OutMessage, SubscriptionDeps, } from './index.js';
2
2
  export type { InitConfig, ViewConfig, SliderAttributes, PressedThumb, PressedPointer, MovedDragPointer, ReleasedDragPointer, CancelledDrag, PressedKeyboardNavigation, } from './index.js';
3
3
  //# sourceMappingURL=public.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"public.d.ts","sourceRoot":"","sources":["../../../src/ui/slider/public.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,IAAI,EACJ,MAAM,EACN,IAAI,EACJ,IAAI,EACJ,aAAa,EACb,eAAe,EACf,KAAK,EACL,OAAO,EACP,UAAU,EACV,gBAAgB,GACjB,MAAM,YAAY,CAAA;AAEnB,YAAY,EACV,UAAU,EACV,UAAU,EACV,gBAAgB,EAChB,YAAY,EACZ,cAAc,EACd,gBAAgB,EAChB,mBAAmB,EACnB,aAAa,EACb,yBAAyB,GAC1B,MAAM,YAAY,CAAA"}
1
+ {"version":3,"file":"public.d.ts","sourceRoot":"","sources":["../../../src/ui/slider/public.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,IAAI,EACJ,MAAM,EACN,QAAQ,EACR,QAAQ,EACR,IAAI,EACJ,IAAI,EACJ,aAAa,EACb,oBAAoB,EACpB,eAAe,EACf,KAAK,EACL,OAAO,EACP,UAAU,EACV,gBAAgB,GACjB,MAAM,YAAY,CAAA;AAEnB,YAAY,EACV,UAAU,EACV,UAAU,EACV,gBAAgB,EAChB,YAAY,EACZ,cAAc,EACd,gBAAgB,EAChB,mBAAmB,EACnB,aAAa,EACb,yBAAyB,GAC1B,MAAM,YAAY,CAAA"}
@@ -1 +1 @@
1
- export { init, update, view, lazy, subscriptions, fractionOfValue, Model, Message, OutMessage, SubscriptionDeps, } from './index.js';
1
+ export { init, update, setRange, setValue, view, lazy, subscriptions, subscriptionsForRoot, fractionOfValue, Model, Message, OutMessage, SubscriptionDeps, } from './index.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "foldkit",
3
- "version": "0.91.0",
3
+ "version": "0.93.0",
4
4
  "description": "A TypeScript frontend framework, built on Effect and architected like Elm",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -200,13 +200,13 @@
200
200
  "dist"
201
201
  ],
202
202
  "peerDependencies": {
203
- "@effect/platform-browser": "4.0.0-beta.59",
204
- "effect": "4.0.0-beta.59"
203
+ "@effect/platform-browser": "4.0.0-beta.64",
204
+ "effect": "4.0.0-beta.64"
205
205
  },
206
206
  "devDependencies": {
207
- "@effect/platform-browser": "4.0.0-beta.59",
208
- "@effect/vitest": "4.0.0-beta.59",
209
- "effect": "4.0.0-beta.59",
207
+ "@effect/platform-browser": "4.0.0-beta.64",
208
+ "@effect/vitest": "4.0.0-beta.64",
209
+ "effect": "4.0.0-beta.64",
210
210
  "happy-dom": "^20.9.0",
211
211
  "rimraf": "^6.1.3",
212
212
  "typedoc": "^0.28.19",