foldkit 0.33.5 → 0.34.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.
Files changed (82) hide show
  1. package/README.md +19 -19
  2. package/dist/devtools/overlay-styles.d.ts +1 -1
  3. package/dist/devtools/overlay-styles.d.ts.map +1 -1
  4. package/dist/devtools/overlay-styles.js +3 -0
  5. package/dist/devtools/overlay.d.ts.map +1 -1
  6. package/dist/devtools/overlay.js +79 -34
  7. package/dist/html/index.d.ts +431 -5
  8. package/dist/html/index.d.ts.map +1 -1
  9. package/dist/html/index.js +1 -0
  10. package/dist/html/public.d.ts +1 -2
  11. package/dist/html/public.d.ts.map +1 -1
  12. package/dist/html/public.js +1 -2
  13. package/dist/task/dom.d.ts +6 -6
  14. package/dist/task/dom.js +6 -6
  15. package/dist/task/inert.d.ts +2 -2
  16. package/dist/task/inert.js +2 -2
  17. package/dist/task/scrollLock.d.ts +2 -2
  18. package/dist/task/scrollLock.js +2 -2
  19. package/dist/ui/checkbox/index.d.ts +5 -9
  20. package/dist/ui/checkbox/index.d.ts.map +1 -1
  21. package/dist/ui/checkbox/index.js +7 -10
  22. package/dist/ui/checkbox/public.d.ts +1 -1
  23. package/dist/ui/checkbox/public.d.ts.map +1 -1
  24. package/dist/ui/combobox/multi.d.ts +31 -10
  25. package/dist/ui/combobox/multi.d.ts.map +1 -1
  26. package/dist/ui/combobox/multi.js +1 -1
  27. package/dist/ui/combobox/public.d.ts +1 -1
  28. package/dist/ui/combobox/public.d.ts.map +1 -1
  29. package/dist/ui/combobox/shared.d.ts +53 -14
  30. package/dist/ui/combobox/shared.d.ts.map +1 -1
  31. package/dist/ui/combobox/shared.js +72 -28
  32. package/dist/ui/combobox/single.d.ts +31 -10
  33. package/dist/ui/combobox/single.d.ts.map +1 -1
  34. package/dist/ui/combobox/single.js +1 -1
  35. package/dist/ui/dialog/index.d.ts +14 -8
  36. package/dist/ui/dialog/index.d.ts.map +1 -1
  37. package/dist/ui/dialog/index.js +21 -12
  38. package/dist/ui/dialog/public.d.ts +1 -1
  39. package/dist/ui/dialog/public.d.ts.map +1 -1
  40. package/dist/ui/dialog/public.js +1 -1
  41. package/dist/ui/disclosure/index.d.ts +11 -8
  42. package/dist/ui/disclosure/index.d.ts.map +1 -1
  43. package/dist/ui/disclosure/index.js +20 -18
  44. package/dist/ui/disclosure/public.d.ts +1 -1
  45. package/dist/ui/disclosure/public.d.ts.map +1 -1
  46. package/dist/ui/listbox/multi.d.ts +34 -9
  47. package/dist/ui/listbox/multi.d.ts.map +1 -1
  48. package/dist/ui/listbox/multi.js +1 -1
  49. package/dist/ui/listbox/public.d.ts +1 -1
  50. package/dist/ui/listbox/public.d.ts.map +1 -1
  51. package/dist/ui/listbox/shared.d.ts +57 -13
  52. package/dist/ui/listbox/shared.d.ts.map +1 -1
  53. package/dist/ui/listbox/shared.js +77 -27
  54. package/dist/ui/listbox/single.d.ts +34 -9
  55. package/dist/ui/listbox/single.d.ts.map +1 -1
  56. package/dist/ui/listbox/single.js +1 -1
  57. package/dist/ui/menu/index.d.ts +39 -11
  58. package/dist/ui/menu/index.d.ts.map +1 -1
  59. package/dist/ui/menu/index.js +81 -32
  60. package/dist/ui/menu/public.d.ts +1 -1
  61. package/dist/ui/menu/public.d.ts.map +1 -1
  62. package/dist/ui/popover/index.d.ts +28 -12
  63. package/dist/ui/popover/index.d.ts.map +1 -1
  64. package/dist/ui/popover/index.js +50 -24
  65. package/dist/ui/popover/public.d.ts +1 -1
  66. package/dist/ui/popover/public.d.ts.map +1 -1
  67. package/dist/ui/radioGroup/index.d.ts +8 -7
  68. package/dist/ui/radioGroup/index.d.ts.map +1 -1
  69. package/dist/ui/radioGroup/index.js +12 -9
  70. package/dist/ui/radioGroup/public.d.ts +1 -1
  71. package/dist/ui/radioGroup/public.d.ts.map +1 -1
  72. package/dist/ui/switch/index.d.ts +5 -9
  73. package/dist/ui/switch/index.d.ts.map +1 -1
  74. package/dist/ui/switch/index.js +7 -10
  75. package/dist/ui/switch/public.d.ts +1 -1
  76. package/dist/ui/switch/public.d.ts.map +1 -1
  77. package/dist/ui/tabs/index.d.ts +9 -7
  78. package/dist/ui/tabs/index.d.ts.map +1 -1
  79. package/dist/ui/tabs/index.js +27 -17
  80. package/dist/ui/tabs/public.d.ts +1 -1
  81. package/dist/ui/tabs/public.d.ts.map +1 -1
  82. package/package.json +3 -3
package/README.md CHANGED
@@ -1,8 +1,8 @@
1
1
  <p align="center">
2
2
  <picture>
3
- <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/devinjameson/foldkit/main/packages/website/public/logo-dark.svg">
4
- <source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/devinjameson/foldkit/main/packages/website/public/logo.svg">
5
- <img src="https://raw.githubusercontent.com/devinjameson/foldkit/main/packages/website/public/logo.svg" alt="Foldkit" width="350">
3
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/foldkit/foldkit/main/packages/website/public/logo-dark.svg">
4
+ <source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/foldkit/foldkit/main/packages/website/public/logo.svg">
5
+ <img src="https://raw.githubusercontent.com/foldkit/foldkit/main/packages/website/public/logo.svg" alt="Foldkit" width="350">
6
6
  </picture>
7
7
  </p>
8
8
 
@@ -13,12 +13,12 @@
13
13
  <h3 align="center">The frontend framework for correctness.</h3>
14
14
 
15
15
  <p align="center">
16
- <a href="https://foldkit.dev"><strong>Documentation</strong></a> · <a href="https://github.com/devinjameson/foldkit#examples"><strong>Examples</strong></a> · <a href="https://foldkit.dev/getting-started"><strong>Getting Started</strong></a>
16
+ <a href="https://foldkit.dev"><strong>Documentation</strong></a> · <a href="https://github.com/foldkit/foldkit#examples"><strong>Examples</strong></a> · <a href="https://foldkit.dev/getting-started"><strong>Getting Started</strong></a>
17
17
  </p>
18
18
 
19
19
  ---
20
20
 
21
- Foldkit is an [Elm Architecture](https://guide.elm-lang.org/architecture/) framework for TypeScript, powered by [Effect](https://effect.website/). One Model, one update function, one way to do things. No hooks, no local state, no hidden mutations.
21
+ Foldkit is a frontend framework for TypeScript, built on [Effect](https://effect.website/), using [The Elm Architecture](https://guide.elm-lang.org/architecture/). One Model, one update function, one way to do things. No hooks, no local state, no hidden mutations.
22
22
 
23
23
  > [!NOTE]
24
24
  > Foldkit is pre-1.0. The core API is stable, but breaking changes may occur in minor releases. See the [changelog](./CHANGELOG.md) for details.
@@ -129,7 +129,7 @@ const element = Runtime.makeElement({
129
129
  Runtime.run(element)
130
130
  ```
131
131
 
132
- Source: [examples/counter/src/main.ts](https://github.com/devinjameson/foldkit/blob/main/examples/counter/src/main.ts)
132
+ Source: [examples/counter/src/main.ts](https://github.com/foldkit/foldkit/blob/main/examples/counter/src/main.ts)
133
133
 
134
134
  ## What Ships With Foldkit
135
135
 
@@ -153,19 +153,19 @@ This is what makes Foldkit unusually AI-friendly. The same property that makes t
153
153
 
154
154
  ## Examples
155
155
 
156
- - **[Counter](https://github.com/devinjameson/foldkit/blob/main/examples/counter/src/main.ts)** — Increment/decrement with reset
157
- - **[Todo](https://github.com/devinjameson/foldkit/blob/main/examples/todo/src/main.ts)** — CRUD operations with localStorage persistence
158
- - **[Stopwatch](https://github.com/devinjameson/foldkit/blob/main/examples/stopwatch/src/main.ts)** — Timer with start/stop/reset
159
- - **[Error View](https://github.com/devinjameson/foldkit/blob/main/examples/error-view/src/main.ts)** — Custom error fallback UI
160
- - **[Form](https://github.com/devinjameson/foldkit/blob/main/examples/form/src/main.ts)** — Form validation with async email checking
161
- - **[Weather](https://github.com/devinjameson/foldkit/blob/main/examples/weather/src/main.ts)** — HTTP requests with async state handling
162
- - **[Routing](https://github.com/devinjameson/foldkit/blob/main/examples/routing/src/main.ts)** — URL routing with parser combinators
163
- - **[Query Sync](https://github.com/devinjameson/foldkit/blob/main/examples/query-sync/src/main.ts)** — URL query parameter sync with filtering and sorting
164
- - **[Snake](https://github.com/devinjameson/foldkit/blob/main/examples/snake/src/main.ts)** — Classic game built with Subscriptions
165
- - **[Auth](https://github.com/devinjameson/foldkit/blob/main/examples/auth/src/main.ts)** — Authentication flow with Submodels and OutMessage
166
- - **[Shopping Cart](https://github.com/devinjameson/foldkit/blob/main/examples/shopping-cart/src/main.ts)** — Nested models and complex state
167
- - **[WebSocket Chat](https://github.com/devinjameson/foldkit/blob/main/examples/websocket-chat/src/main.ts)** — Managed Resources with WebSocket integration
168
- - **[Typing Game](https://github.com/devinjameson/foldkit/tree/main/packages/typing-game)** — Multiplayer typing game with Effect RPC backend
156
+ - **[Counter](https://github.com/foldkit/foldkit/blob/main/examples/counter/src/main.ts)** — Increment/decrement with reset
157
+ - **[Todo](https://github.com/foldkit/foldkit/blob/main/examples/todo/src/main.ts)** — CRUD operations with localStorage persistence
158
+ - **[Stopwatch](https://github.com/foldkit/foldkit/blob/main/examples/stopwatch/src/main.ts)** — Timer with start/stop/reset
159
+ - **[Error View](https://github.com/foldkit/foldkit/blob/main/examples/error-view/src/main.ts)** — Custom error fallback UI
160
+ - **[Form](https://github.com/foldkit/foldkit/blob/main/examples/form/src/main.ts)** — Form validation with async email checking
161
+ - **[Weather](https://github.com/foldkit/foldkit/blob/main/examples/weather/src/main.ts)** — HTTP requests with async state handling
162
+ - **[Routing](https://github.com/foldkit/foldkit/blob/main/examples/routing/src/main.ts)** — URL routing with parser combinators
163
+ - **[Query Sync](https://github.com/foldkit/foldkit/blob/main/examples/query-sync/src/main.ts)** — URL query parameter sync with filtering and sorting
164
+ - **[Snake](https://github.com/foldkit/foldkit/blob/main/examples/snake/src/main.ts)** — Classic game built with Subscriptions
165
+ - **[Auth](https://github.com/foldkit/foldkit/blob/main/examples/auth/src/main.ts)** — Authentication flow with Submodels and OutMessage
166
+ - **[Shopping Cart](https://github.com/foldkit/foldkit/blob/main/examples/shopping-cart/src/main.ts)** — Nested models and complex state
167
+ - **[WebSocket Chat](https://github.com/foldkit/foldkit/blob/main/examples/websocket-chat/src/main.ts)** — Managed Resources with WebSocket integration
168
+ - **[Typing Game](https://github.com/foldkit/foldkit/tree/main/packages/typing-game)** — Multiplayer typing game with Effect RPC backend
169
169
 
170
170
  ## License
171
171
 
@@ -1,3 +1,3 @@
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";
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.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.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,67QA6gBlB,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,k/QAghBlB,CAAA;AAED,OAAO,EAAE,aAAa,EAAE,CAAA"}
@@ -178,6 +178,9 @@ ul {
178
178
  .overflow-y-auto {
179
179
  overflow-y: auto;
180
180
  }
181
+ .overscroll-none {
182
+ overscroll-behavior: none;
183
+ }
181
184
 
182
185
  .truncate {
183
186
  overflow: hidden;
@@ -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;AAwwCzE,eAAO,MAAM,aAAa,GACxB,OAAO,aAAa,EACpB,UAAU,gBAAgB,EAC1B,MAAM,YAAY,EAClB,aAAa,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,sCA4ChC,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;AA2yCzE,eAAO,MAAM,aAAa,GACxB,OAAO,aAAa,EACpB,UAAU,gBAAgB,EAC1B,MAAM,YAAY,EAClB,aAAa,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,sCA4ChC,CAAA"}
@@ -1,7 +1,7 @@
1
1
  import { clsx } from 'clsx';
2
2
  import { Array as Array_, Effect, HashSet, Match as M, Number as Number_, Option, Predicate, Record, Schema as S, Stream, String as String_, SubscriptionRef, pipe, } from 'effect';
3
3
  import { OptionExt } from '../effectExtensions';
4
- import { html } from '../html';
4
+ import { createKeyedLazy, html } from '../html';
5
5
  import { m } from '../message';
6
6
  import { makeElement } from '../runtime/runtime';
7
7
  import { makeSubscriptions } from '../runtime/subscription';
@@ -352,6 +352,8 @@ const PANEL_POSITION_CLASS = {
352
352
  };
353
353
  const makeView = (position, mode, maybeBanner) => {
354
354
  const { div, header, span, ul, button, svg, path, keyed, Class, Style, OnClick, AriaHidden, Xmlns, Fill, ViewBox, StrokeWidth, Stroke, StrokeLinecap, StrokeLinejoin, D, } = html();
355
+ const lazyTreeNode = createKeyedLazy();
356
+ const lazyMessageRow = createKeyedLazy();
355
357
  // JSON TREE
356
358
  const leafValueView = (value) => M.value(value).pipe(M.when(Predicate.isNull, () => span([Class('json-null italic')], ['null'])), M.when(Predicate.isUndefined, () => span([Class('json-null italic')], ['undefined'])), M.when(Predicate.isString, stringValue => span([Class('json-string')], [`"${stringValue}"`])), M.when(Predicate.isNumber, numberValue => span([Class('json-number')], [String(numberValue)])), M.when(Predicate.isBoolean, booleanValue => span([Class('json-boolean')], [String(booleanValue)])), M.orElse(unknownValue => span([Class('json-null')], [String(unknownValue)])));
357
359
  const keyView = (key) => span([Class('json-key')], [`${key}:\u00a0`]);
@@ -375,70 +377,103 @@ const makeView = (position, mode, maybeBanner) => {
375
377
  const tagLabelView = (tag) => span([Class('json-tag')], [tag]);
376
378
  const diffDotView = span([Class('diff-dot')], []);
377
379
  const inlineDiffDotView = span([Class('diff-dot-inline')], []);
378
- const flattenTree = (value, treePath, expandedPaths, changedPaths, affectedPaths, depth, maybeKey, accumulator, indentRootChildren) => {
380
+ const flattenTree = ({ value, treePath, depth, key, ...shared }) => {
381
+ const { expandedPaths, changedPaths, affectedPaths, accumulator, indentRootChildren, } = shared;
379
382
  const isRoot = treePath === 'root';
380
383
  const nodeIsExpandable = isExpandable(value);
381
384
  const isExpanded = nodeIsExpandable && (isRoot || HashSet.has(expandedPaths, treePath));
382
- const maybeTag = pipe(value, Option.liftPredicate(isTagged), Option.map(({ _tag }) => _tag));
385
+ const tag = isTagged(value) ? value._tag : '';
383
386
  accumulator.push({
384
387
  value,
385
388
  treePath,
386
389
  depth,
387
- maybeKey,
390
+ key,
388
391
  isExpandable: nodeIsExpandable,
389
392
  isExpanded,
390
393
  isChanged: HashSet.has(changedPaths, treePath),
391
394
  isAffected: HashSet.has(affectedPaths, treePath),
392
- maybeTag,
395
+ tag,
393
396
  });
394
397
  if (!isExpanded) {
395
398
  return;
396
399
  }
397
400
  const childDepth = isRoot && !indentRootChildren ? depth : depth + 1;
398
401
  if (Array.isArray(value)) {
399
- value.forEach((item, arrayIndex) => flattenTree(item, `${treePath}.${arrayIndex}`, expandedPaths, changedPaths, affectedPaths, childDepth, Option.some(String(arrayIndex)), accumulator, indentRootChildren));
402
+ value.forEach((item, arrayIndex) => flattenTree({
403
+ ...shared,
404
+ value: item,
405
+ treePath: `${treePath}.${arrayIndex}`,
406
+ depth: childDepth,
407
+ key: String(arrayIndex),
408
+ }));
400
409
  }
401
410
  else if (Predicate.isReadonlyRecord(value)) {
402
- pipe(value, Record.toEntries, Array_.filter(([key]) => key !== '_tag'), Array_.forEach(([key, childValue]) => flattenTree(childValue, `${treePath}.${key}`, expandedPaths, changedPaths, affectedPaths, childDepth, Option.some(key), accumulator, indentRootChildren)));
411
+ pipe(value, Record.toEntries, Array_.filter(([entryKey]) => entryKey !== '_tag'), Array_.forEach(([entryKey, childValue]) => flattenTree({
412
+ ...shared,
413
+ value: childValue,
414
+ treePath: `${treePath}.${entryKey}`,
415
+ depth: childDepth,
416
+ key: entryKey,
417
+ })));
403
418
  }
404
419
  };
405
- const flatNodeView = (node) => {
406
- const indent = Style({ paddingLeft: `${node.depth * TREE_INDENT_PX}px` });
407
- const hasDiffDot = node.isChanged || node.isAffected;
408
- if (!node.isExpandable) {
420
+ const flatNodeView = (value, treePath, depth, key, nodeIsExpandable, isExpanded, isChanged, isAffected, tag) => {
421
+ const indent = Style({ paddingLeft: `${depth * TREE_INDENT_PX}px` });
422
+ const hasDiffDot = isChanged || isAffected;
423
+ if (!nodeIsExpandable) {
409
424
  return div([
410
- Class(clsx('tree-row flex items-center gap-px font-mono text-2xs', node.isChanged && 'diff-changed')),
425
+ Class(clsx('tree-row flex items-center gap-px font-mono text-2xs', isChanged && 'diff-changed')),
411
426
  indent,
412
427
  ], [
413
428
  ...(hasDiffDot ? [diffDotView] : []),
414
- ...Array_.getSomes([Option.map(node.maybeKey, keyView)]),
415
- leafValueView(node.value),
429
+ ...(String_.isNonEmpty(key) ? [keyView(key)] : []),
430
+ leafValueView(value),
416
431
  ]);
417
432
  }
418
- const isRoot = node.treePath === 'root';
419
- const preview = node.isExpanded
420
- ? Array.isArray(node.value)
421
- ? `(${node.value.length})`
433
+ const isRoot = treePath === 'root';
434
+ const preview = isExpanded
435
+ ? Array.isArray(value)
436
+ ? `(${value.length})`
422
437
  : ''
423
- : collapsedPreview(node.value);
438
+ : collapsedPreview(value);
424
439
  return div([
425
- Class(clsx('tree-row flex items-center gap-px font-mono text-2xs', !isRoot && 'tree-row-expandable cursor-pointer', node.isChanged && 'diff-changed')),
440
+ Class(clsx('tree-row flex items-center gap-px font-mono text-2xs', !isRoot && 'tree-row-expandable cursor-pointer', isChanged && 'diff-changed')),
426
441
  indent,
427
- ...(isRoot ? [] : [OnClick(ToggledTreeNode({ path: node.treePath }))]),
442
+ ...(isRoot ? [] : [OnClick(ToggledTreeNode({ path: treePath }))]),
428
443
  ], [
429
- ...(isRoot ? [] : [arrowView(node.isExpanded)]),
444
+ ...(isRoot ? [] : [arrowView(isExpanded)]),
430
445
  ...(!isRoot && hasDiffDot ? [diffDotView] : []),
431
- ...Array_.getSomes([
432
- Option.map(node.maybeKey, keyView),
433
- Option.map(node.maybeTag, tagLabelView),
434
- ]),
446
+ ...(String_.isNonEmpty(key) ? [keyView(key)] : []),
447
+ ...(String_.isNonEmpty(tag) ? [tagLabelView(tag)] : []),
435
448
  span([Class('json-preview')], [preview]),
436
449
  ]);
437
450
  };
438
451
  const treeView = (value, rootPath, expandedPaths, changedPaths, affectedPaths, maybeRootLabel, indentRootChildren) => {
439
452
  const nodes = [];
440
- flattenTree(value, rootPath, expandedPaths, changedPaths, affectedPaths, 0, maybeRootLabel, nodes, indentRootChildren);
441
- return div([Class('inspector-tree flex-1 overflow-auto min-h-0 min-w-0')], nodes.map(flatNodeView));
453
+ flattenTree({
454
+ value,
455
+ treePath: rootPath,
456
+ expandedPaths,
457
+ changedPaths,
458
+ affectedPaths,
459
+ depth: 0,
460
+ key: Option.getOrElse(maybeRootLabel, () => ''),
461
+ accumulator: nodes,
462
+ indentRootChildren,
463
+ });
464
+ return div([
465
+ Class('inspector-tree flex-1 overflow-auto min-h-0 min-w-0 overscroll-none'),
466
+ ], nodes.map(node => lazyTreeNode(node.treePath, flatNodeView, [
467
+ node.value,
468
+ node.treePath,
469
+ node.depth,
470
+ node.key,
471
+ node.isExpandable,
472
+ node.isExpanded,
473
+ node.isChanged,
474
+ node.isAffected,
475
+ node.tag,
476
+ ])));
442
477
  };
443
478
  const inspectedTimestamp = (model) => {
444
479
  const lastIndex = Array_.isEmptyReadonlyArray(model.entries)
@@ -488,12 +523,15 @@ const makeView = (position, mode, maybeBanner) => {
488
523
  model: model.inspectorTabs,
489
524
  toMessage: tabsMessage => GotInspectorTabsMessage({ message: tabsMessage }),
490
525
  tabs: INSPECTOR_TABS,
491
- className: 'flex flex-col flex-1 min-h-0',
492
- tabListClassName: 'flex border-b shrink-0',
526
+ tabListAriaLabel: 'Inspector tabs',
527
+ attributes: [Class('flex flex-col flex-1 min-h-0')],
528
+ tabListAttributes: [Class('flex border-b shrink-0')],
493
529
  tabToConfig: (tab, { isActive }) => ({
494
- buttonClassName: clsx('dt-tab-button cursor-pointer text-base font-mono px-3 py-1', isActive ? 'text-dt dt-tab-active' : 'text-dt-muted'),
530
+ buttonAttributes: [
531
+ Class(clsx('dt-tab-button cursor-pointer text-base font-mono px-3 py-1', isActive ? 'text-dt dt-tab-active' : 'text-dt-muted')),
532
+ ],
495
533
  buttonContent: span([], [tab]),
496
- panelClassName: 'flex flex-col flex-1 min-h-0 min-w-0',
534
+ panelAttributes: [Class('flex flex-col flex-1 min-h-0 min-w-0')],
497
535
  panelContent: Option.match(model.maybeInspectedModel, {
498
536
  onNone: () => emptyInspectorView,
499
537
  onSome: inspectedModel => inspectorTabContent(model, tab, inspectedModel),
@@ -606,9 +644,16 @@ const makeView = (position, mode, maybeBanner) => {
606
644
  const absoluteIndex = model.startIndex + arrayIndex;
607
645
  const isSelected = selectedIndex === absoluteIndex;
608
646
  const isPausedHere = model.isPaused && model.pausedAtIndex === absoluteIndex;
609
- return messageRowView(entry.tag, absoluteIndex, isSelected, isPausedHere, entry.timestamp - baseTimestamp, entry.isModelChanged);
647
+ return lazyMessageRow(String(absoluteIndex), messageRowView, [
648
+ entry.tag,
649
+ absoluteIndex,
650
+ isSelected,
651
+ isPausedHere,
652
+ entry.timestamp - baseTimestamp,
653
+ entry.isModelChanged,
654
+ ]);
610
655
  }), Array_.reverse);
611
- return ul([Class('message-list flex-1 overflow-y-auto min-h-0')], [
656
+ return ul([Class('message-list flex-1 overflow-y-auto min-h-0 overscroll-none')], [
612
657
  ...messageRows,
613
658
  initRowView(isInitSelected, model.isPaused && model.pausedAtIndex === INIT_INDEX),
614
659
  ]);