@zaly/tui 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (228) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +59 -0
  3. package/assets/schemas/theme.schema.json +899 -0
  4. package/assets/themes/LICENSE-tokyonight.txt +201 -0
  5. package/assets/themes/NOTICES.md +69 -0
  6. package/assets/themes/ayu-dark.json +119 -0
  7. package/assets/themes/catppuccin-frappe.json +124 -0
  8. package/assets/themes/catppuccin-latte.json +124 -0
  9. package/assets/themes/catppuccin-macchiato.json +124 -0
  10. package/assets/themes/catppuccin-mocha.json +124 -0
  11. package/assets/themes/dracula-soft.json +119 -0
  12. package/assets/themes/dracula.json +119 -0
  13. package/assets/themes/everforest-dark.json +118 -0
  14. package/assets/themes/everforest-light.json +118 -0
  15. package/assets/themes/github-dark.json +123 -0
  16. package/assets/themes/github-light.json +123 -0
  17. package/assets/themes/gruvbox-dark-medium.json +112 -0
  18. package/assets/themes/kanagawa-dragon.json +115 -0
  19. package/assets/themes/kanagawa-wave.json +115 -0
  20. package/assets/themes/material-theme-palenight.json +114 -0
  21. package/assets/themes/min-dark.json +55 -0
  22. package/assets/themes/min-light.json +59 -0
  23. package/assets/themes/monokai.json +116 -0
  24. package/assets/themes/night-owl.json +116 -0
  25. package/assets/themes/nord.json +101 -0
  26. package/assets/themes/one-dark-pro.json +110 -0
  27. package/assets/themes/one-light.json +108 -0
  28. package/assets/themes/poimandres.json +123 -0
  29. package/assets/themes/rose-pine.json +98 -0
  30. package/assets/themes/solarized-dark.json +116 -0
  31. package/assets/themes/solarized-light.json +116 -0
  32. package/assets/themes/synthwave-84.json +100 -0
  33. package/assets/themes/tokyonight-day.json +157 -0
  34. package/assets/themes/tokyonight-moon.json +157 -0
  35. package/assets/themes/tokyonight-night.json +157 -0
  36. package/assets/themes/tokyonight-storm.json +157 -0
  37. package/assets/themes/vesper.json +103 -0
  38. package/assets/themes/vitesse-dark.json +126 -0
  39. package/assets/themes/vitesse-light.json +126 -0
  40. package/dist/ansi-Clq0ozyA.mjs +206 -0
  41. package/dist/ansi-Clq0ozyA.mjs.map +1 -0
  42. package/dist/ansi.d.mts +48 -0
  43. package/dist/ansi.d.mts.map +1 -0
  44. package/dist/ansi.mjs +2 -0
  45. package/dist/autocomplete-Dfw-yQDa.d.mts +100 -0
  46. package/dist/autocomplete-Dfw-yQDa.d.mts.map +1 -0
  47. package/dist/box-DyaByEiG.mjs +386 -0
  48. package/dist/box-DyaByEiG.mjs.map +1 -0
  49. package/dist/builder-D49ECBEf.mjs +385 -0
  50. package/dist/builder-D49ECBEf.mjs.map +1 -0
  51. package/dist/callbacks-CNjGNxVw.mjs +279 -0
  52. package/dist/callbacks-CNjGNxVw.mjs.map +1 -0
  53. package/dist/client-D6C_uczF.mjs +142 -0
  54. package/dist/client-D6C_uczF.mjs.map +1 -0
  55. package/dist/clipboard.d.mts +104 -0
  56. package/dist/clipboard.d.mts.map +1 -0
  57. package/dist/clipboard.mjs +404 -0
  58. package/dist/clipboard.mjs.map +1 -0
  59. package/dist/flex-Bguo-fUe.mjs +202 -0
  60. package/dist/flex-Bguo-fUe.mjs.map +1 -0
  61. package/dist/image-Cm5T-uw-.mjs +108 -0
  62. package/dist/image-Cm5T-uw-.mjs.map +1 -0
  63. package/dist/image.d.mts +123 -0
  64. package/dist/image.d.mts.map +1 -0
  65. package/dist/image.mjs +2 -0
  66. package/dist/index.d.mts +64 -0
  67. package/dist/index.d.mts.map +1 -0
  68. package/dist/index.mjs +222 -0
  69. package/dist/index.mjs.map +1 -0
  70. package/dist/input-B5Vy-iaa.d.mts +141 -0
  71. package/dist/input-B5Vy-iaa.d.mts.map +1 -0
  72. package/dist/inspect-D1d9t-ni.mjs +101 -0
  73. package/dist/inspect-D1d9t-ni.mjs.map +1 -0
  74. package/dist/inspect-D6FW_QUo.d.mts +67 -0
  75. package/dist/inspect-D6FW_QUo.d.mts.map +1 -0
  76. package/dist/kitty-JbCJC0iQ.mjs +673 -0
  77. package/dist/kitty-JbCJC0iQ.mjs.map +1 -0
  78. package/dist/markdown.d.mts +24 -0
  79. package/dist/markdown.d.mts.map +1 -0
  80. package/dist/markdown.mjs +2 -0
  81. package/dist/md.bun.d.mts +14 -0
  82. package/dist/md.bun.d.mts.map +1 -0
  83. package/dist/md.bun.mjs +28 -0
  84. package/dist/md.bun.mjs.map +1 -0
  85. package/dist/md.node.d.mts +14 -0
  86. package/dist/md.node.d.mts.map +1 -0
  87. package/dist/md.node.mjs +126 -0
  88. package/dist/md.node.mjs.map +1 -0
  89. package/dist/node-BZ7f6HzU.mjs +316 -0
  90. package/dist/node-BZ7f6HzU.mjs.map +1 -0
  91. package/dist/picker-D8OLb6kZ.d.mts +36 -0
  92. package/dist/picker-D8OLb6kZ.d.mts.map +1 -0
  93. package/dist/picker-ndVVt6qF.mjs +621 -0
  94. package/dist/picker-ndVVt6qF.mjs.map +1 -0
  95. package/dist/queries-DH3bj74K.d.mts +1963 -0
  96. package/dist/queries-DH3bj74K.d.mts.map +1 -0
  97. package/dist/reactive-f7NpAi69.mjs +623 -0
  98. package/dist/reactive-f7NpAi69.mjs.map +1 -0
  99. package/dist/registry-U654j61Y.mjs +356 -0
  100. package/dist/registry-U654j61Y.mjs.map +1 -0
  101. package/dist/render-DxVLqqB9.mjs +74 -0
  102. package/dist/render-DxVLqqB9.mjs.map +1 -0
  103. package/dist/renderer-CdLCkJB-.mjs +42 -0
  104. package/dist/renderer-CdLCkJB-.mjs.map +1 -0
  105. package/dist/renderer-CxL4xbkl.mjs +2644 -0
  106. package/dist/renderer-CxL4xbkl.mjs.map +1 -0
  107. package/dist/rolldown-runtime-D7D4PA-g.mjs +13 -0
  108. package/dist/search-FvGnswRl.d.mts +49 -0
  109. package/dist/search-FvGnswRl.d.mts.map +1 -0
  110. package/dist/select-CbmAZdpt.d.mts +116 -0
  111. package/dist/select-CbmAZdpt.d.mts.map +1 -0
  112. package/dist/services/logger.d.mts +2 -0
  113. package/dist/services/logger.mjs +71 -0
  114. package/dist/services/logger.mjs.map +1 -0
  115. package/dist/services/notifier.d.mts +17 -0
  116. package/dist/services/notifier.d.mts.map +1 -0
  117. package/dist/services/notifier.mjs +71 -0
  118. package/dist/services/notifier.mjs.map +1 -0
  119. package/dist/services/picker.d.mts +36 -0
  120. package/dist/services/picker.d.mts.map +1 -0
  121. package/dist/services/picker.mjs +137 -0
  122. package/dist/services/picker.mjs.map +1 -0
  123. package/dist/services/prompt.d.mts +30 -0
  124. package/dist/services/prompt.d.mts.map +1 -0
  125. package/dist/services/prompt.mjs +83 -0
  126. package/dist/services/prompt.mjs.map +1 -0
  127. package/dist/shiki-BrjVUkeh.mjs +423 -0
  128. package/dist/shiki-BrjVUkeh.mjs.map +1 -0
  129. package/dist/size-D8JogYLy.mjs +34 -0
  130. package/dist/size-D8JogYLy.mjs.map +1 -0
  131. package/dist/style.d.mts +10 -0
  132. package/dist/style.d.mts.map +1 -0
  133. package/dist/style.mjs +4 -0
  134. package/dist/text.d.mts +33 -0
  135. package/dist/text.d.mts.map +1 -0
  136. package/dist/text.mjs +111 -0
  137. package/dist/text.mjs.map +1 -0
  138. package/dist/theme.config-CU3B50HN.mjs +832 -0
  139. package/dist/theme.config-CU3B50HN.mjs.map +1 -0
  140. package/dist/themes.d.mts +113 -0
  141. package/dist/themes.d.mts.map +1 -0
  142. package/dist/themes.mjs +2 -0
  143. package/dist/tree-C28QOVIK.d.mts +23 -0
  144. package/dist/tree-C28QOVIK.d.mts.map +1 -0
  145. package/dist/types-B9Eh7k2v.d.mts +212 -0
  146. package/dist/types-B9Eh7k2v.d.mts.map +1 -0
  147. package/dist/types-D3jk1rtk.d.mts +140 -0
  148. package/dist/types-D3jk1rtk.d.mts.map +1 -0
  149. package/dist/utils-3M4D_AXZ.mjs +40 -0
  150. package/dist/utils-3M4D_AXZ.mjs.map +1 -0
  151. package/dist/widgets/autocomplete.d.mts +2 -0
  152. package/dist/widgets/autocomplete.mjs +165 -0
  153. package/dist/widgets/autocomplete.mjs.map +1 -0
  154. package/dist/widgets/box.d.mts +2 -0
  155. package/dist/widgets/box.mjs +2 -0
  156. package/dist/widgets/code.d.mts +48 -0
  157. package/dist/widgets/code.d.mts.map +1 -0
  158. package/dist/widgets/code.mjs +59 -0
  159. package/dist/widgets/code.mjs.map +1 -0
  160. package/dist/widgets/completions/actions.d.mts +41 -0
  161. package/dist/widgets/completions/actions.d.mts.map +1 -0
  162. package/dist/widgets/completions/actions.mjs +48 -0
  163. package/dist/widgets/completions/actions.mjs.map +1 -0
  164. package/dist/widgets/completions/files.d.mts +29 -0
  165. package/dist/widgets/completions/files.d.mts.map +1 -0
  166. package/dist/widgets/completions/files.mjs +42 -0
  167. package/dist/widgets/completions/files.mjs.map +1 -0
  168. package/dist/widgets/completions/github.d.mts +64 -0
  169. package/dist/widgets/completions/github.d.mts.map +1 -0
  170. package/dist/widgets/completions/github.mjs +137 -0
  171. package/dist/widgets/completions/github.mjs.map +1 -0
  172. package/dist/widgets/completions/index.d.mts +4 -0
  173. package/dist/widgets/completions/index.mjs +4 -0
  174. package/dist/widgets/diff.d.mts +57 -0
  175. package/dist/widgets/diff.d.mts.map +1 -0
  176. package/dist/widgets/diff.mjs +199 -0
  177. package/dist/widgets/diff.mjs.map +1 -0
  178. package/dist/widgets/divider.d.mts +11 -0
  179. package/dist/widgets/divider.d.mts.map +1 -0
  180. package/dist/widgets/divider.mjs +9 -0
  181. package/dist/widgets/divider.mjs.map +1 -0
  182. package/dist/widgets/image.d.mts +37 -0
  183. package/dist/widgets/image.d.mts.map +1 -0
  184. package/dist/widgets/image.mjs +77 -0
  185. package/dist/widgets/image.mjs.map +1 -0
  186. package/dist/widgets/input.d.mts +2 -0
  187. package/dist/widgets/input.mjs +497 -0
  188. package/dist/widgets/input.mjs.map +1 -0
  189. package/dist/widgets/log.d.mts +2 -0
  190. package/dist/widgets/log.mjs +130 -0
  191. package/dist/widgets/log.mjs.map +1 -0
  192. package/dist/widgets/markdown.d.mts +45 -0
  193. package/dist/widgets/markdown.d.mts.map +1 -0
  194. package/dist/widgets/markdown.mjs +124 -0
  195. package/dist/widgets/markdown.mjs.map +1 -0
  196. package/dist/widgets/overlay.d.mts +2 -0
  197. package/dist/widgets/overlay.mjs +18 -0
  198. package/dist/widgets/overlay.mjs.map +1 -0
  199. package/dist/widgets/picker.d.mts +2 -0
  200. package/dist/widgets/picker.mjs +2 -0
  201. package/dist/widgets/progress.d.mts +48 -0
  202. package/dist/widgets/progress.d.mts.map +1 -0
  203. package/dist/widgets/progress.mjs +48 -0
  204. package/dist/widgets/progress.mjs.map +1 -0
  205. package/dist/widgets/select.d.mts +2 -0
  206. package/dist/widgets/select.mjs +195 -0
  207. package/dist/widgets/select.mjs.map +1 -0
  208. package/dist/widgets/show.d.mts +55 -0
  209. package/dist/widgets/show.d.mts.map +1 -0
  210. package/dist/widgets/show.mjs +85 -0
  211. package/dist/widgets/show.mjs.map +1 -0
  212. package/dist/widgets/spinner.d.mts +65 -0
  213. package/dist/widgets/spinner.d.mts.map +1 -0
  214. package/dist/widgets/spinner.mjs +147 -0
  215. package/dist/widgets/spinner.mjs.map +1 -0
  216. package/dist/widgets/text.d.mts +2 -0
  217. package/dist/widgets/text.mjs +32 -0
  218. package/dist/widgets/text.mjs.map +1 -0
  219. package/dist/widgets/tree.d.mts +2 -0
  220. package/dist/widgets/tree.mjs +73 -0
  221. package/dist/widgets/tree.mjs.map +1 -0
  222. package/dist/widgets/widget.d.mts +49 -0
  223. package/dist/widgets/widget.d.mts.map +1 -0
  224. package/dist/widgets/widget.mjs +40 -0
  225. package/dist/widgets/widget.mjs.map +1 -0
  226. package/dist/worker-DjHuQSbY.mjs +27 -0
  227. package/dist/worker-DjHuQSbY.mjs.map +1 -0
  228. package/package.json +117 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"node-BZ7f6HzU.mjs","names":["#children","#state","#mountAc","#ctx","#id","#parent","#actionTargets","#cache","#invalidations","#rendering","#render","#registerActionMeta"],"sources":["../src/core/node.ts"],"sourcesContent":["import type { MaybePromise } from \"@zaly/shared\"\nimport type { TryResult } from \"@zaly/shared/logger\"\nimport type { ActionCtx, ActionDef, NodeActionMap } from \"../input/actions.ts\"\nimport type { RoutedKey, RoutedPaste } from \"../input/router.ts\"\nimport type { SurfaceType } from \"../renderer/renderer.ts\"\nimport type { MountCtx, RenderCtx } from \"./ctx.ts\"\nimport type { Ref, SignalStore } from \"./reactive.ts\"\nimport type { Layout, State } from \"./state.ts\"\n\nimport { Emitter } from \"@zaly/shared\"\nimport { createStore, inRenderContextOf, unwrap, withActiveNode } from \"./reactive.ts\"\n\n/** Minimum event map every node carries. Custom event maps intersect\n * this with their own events via `&`. */\nexport type BaseEvents = {\n invalidate: {}\n mount: {}\n unmount: {}\n focus: {}\n blur: {}\n show: {}\n hide: {}\n key: { key: RoutedKey }\n paste: { paste: RoutedPaste }\n childadded: { child: Node }\n childremoved: { child: Node }\n}\n\n/**\n * Public Node handle. Calling `render(ctx)` returns the node's rows from the\n * per-node cache, recomputing only if state read inside `_render` has changed\n * since the last call.\n *\n * Concrete subclasses implement the protected `_render(ctx)` hook; the public\n * `render` is the caching wrapper around it.\n *\n * State is a `createStore` proxy: reads inside `_render` track per-field;\n * mutating a read field invalidates this node. Three write forms:\n *\n * - `n.state.field = value` (direct assignment)\n * - `n.state.set({ field: value, ... })` (patch — partial merge)\n * - `n.state.set(current => ({ ... }))` (updater — receives untracked current)\n *\n * Writes through nested objects/arrays (`n.state.padding[0] = 1`) do NOT —\n * reassign the whole field instead.\n */\nexport abstract class Node<T extends object = object, E extends {} = {}> extends Emitter<\n BaseEvents,\n E\n> {\n #cache?: { rows: string[]; version: number; width: number }\n #parent?: Node\n #rendering: Promise<string[]> | undefined\n /** Bumped on every `invalidate()`. Captured at the start of a render\n * and re-checked after `_render` resolves: if the count moved, an\n * external mutation landed during the render and the rows we just\n * produced reflect pre-mutation state. We skip writing the cache in\n * that case so the surface's already-scheduled re-paint sees a\n * cache miss and re-renders against the latest state. */\n #invalidations = 0\n readonly #children: Node[] = []\n #state: SignalStore<T>\n #ctx?: MountCtx\n #id?: string\n #actionTargets = new Set<Node>()\n #mountAc?: AbortController\n actions?: NodeActionMap\n type?: string\n protected layout?(ctx: RenderCtx): MaybePromise<Layout | undefined>\n\n constructor(state: State<T>, ...children: Node[]) {\n super()\n this.onEmitError = (err) => {\n const logger = this.logger\n if (logger) logger.error(err)\n // oxlint-disable-next-line no-console\n else console.error(\"Uncaught node event error (node is unmounted, no ctx):\", err)\n }\n // `createStore` gives us per-field reactivity for free: reads\n // inside `_render` subscribe via the rendering Node's tracking\n // ctx, so mutating a read field invalidates exactly this node.\n // Fields nobody reads never allocate a signal — mutation is just\n // a state update with no cascade. Finer-grained than the old\n // \"any write invalidates everything\" Proxy.\n this.#state = createStore(state)\n children.forEach((c) => this.add(c))\n }\n\n withEvents<Events extends {}>(): this & Emitter<Events> {\n return this as unknown as this & Emitter<Events>\n }\n\n withActions<A extends NodeActionMap>(actions: A): this & { actions: A } {\n this.actions = { ...this.actions, ...actions }\n return this as this & { actions: A }\n }\n\n action(id: keyof NonNullable<this[\"actions\"]> & string, opts?: Partial<ActionCtx>): boolean {\n const entry = this.actions?.[id]\n const fn = typeof entry === \"function\" ? entry : entry?.fn\n if (!fn) return false\n fn({ source: \"programmatic\", target: this, ...opts, id, node: this })\n return true\n }\n\n get mountSignal(): AbortSignal {\n this.#mountAc ??= new AbortController()\n return this.#mountAc.signal\n }\n\n get state(): SignalStore<T> & State<T> {\n return this.#state\n }\n\n get children(): readonly Node[] {\n return this.#children\n }\n\n protected get logger() {\n return this.#ctx?.logger.child({\n id: this.#id,\n name: \"node\",\n node: this.type ?? \"unknown\",\n })\n }\n\n protected try<R>(fn: () => R): TryResult<R> {\n const logger = this.logger\n if (!logger) return fn() as TryResult<R>\n return logger.try(fn) as TryResult<R>\n }\n\n /** Fragment protocol — opt-in. When defined, layout containers\n * (Box's row/column allocator) treat this node as a transparent\n * fragment and substitute the returned children for `this` during\n * flex allocation and rendering. The fragment Node still exists in\n * the tree (it's mounted; it bubbles invalidates from its\n * descendants), but layout sees through it.\n *\n * Used by `show`, `errorBoundary`, `suspense`: their children carry\n * their own flex props through to the parent box, can share its\n * `gap`, and a row containing a `show` with multiple flex children\n * allocates per-leaf-slot rather than collapsing into one. */\n protected layoutChildren?(): readonly Node[]\n\n // Make parent readonly\n get parent(): Node | undefined {\n return this.#parent\n }\n\n /** Mount context when this node is attached to a surface — surface\n * identifier plus scoped handles to router / overlay / tree lookups.\n * `undefined` before mount and after unmount. Exposed so widget\n * authors can reach renderer services without closing over a ref. */\n get ctx(): MountCtx | undefined {\n return this.#ctx\n }\n\n get surface(): SurfaceType | undefined {\n return this.#ctx?.surface\n }\n\n get mounted(): boolean {\n return this.#ctx !== undefined\n }\n\n /**\n * Read or set the node's `id`. Called without args, returns the\n * current id. Called with a string, sets it and returns `this` for\n * chaining — useful when building trees inline:\n *\n * ```ts\n * input({...}).id(\"chat-input\").focus().on(\"submit\", ...)\n * ```\n *\n * The id is used by `ctx.getNode(id)` / `Renderer.getNode(id)` for\n * tree lookups, and by the input router as a per-instance scope for\n * keymap bindings (alongside the class-level `type`).\n */\n id(): string | undefined\n id(value: string): this\n id(value?: string): string | undefined | this {\n if (value === undefined) return this.#id\n this.#id = value\n return this\n }\n\n async getLayout(ctx: RenderCtx): Promise<Layout | undefined> {\n if (!this.layout) return\n return this.layout(ctx)\n }\n\n get layoutNodes(): readonly Node[] {\n if (this.layoutChildren === undefined) return [this]\n const ret: Node[] = []\n for (const c of this.with(() => this.layoutChildren?.()) ?? []) ret.push(...c.layoutNodes)\n return ret\n }\n\n addActionTarget(node: Node): this {\n this.#actionTargets.add(node)\n return this\n }\n\n removeActionTarget(node: Node): this {\n this.#actionTargets.delete(node)\n return this\n }\n\n get actionTargets(): Iterable<Node> {\n return this.#actionTargets\n }\n\n invalidate(): this {\n this.#cache = undefined\n // Suppress only when this invalidate originates from *inside this\n // node's own render call stack* — e.g. Markdown mutating its child\n // Text inside its own `_render`. The active render observes the\n // mutation as part of its own logic and produces rows that already\n // reflect it, so we skip the cascade *and* the generation bump:\n // the cache writeback at the end of the render is valid.\n //\n // External mutations (network callbacks, event handlers) and\n // mutations cascading up from a *deeper* render's own call stack\n // (e.g. Text._render mutating something that bubbles to Markdown\n // mid-Markdown-render) run outside *this* node's ALS scope, so\n // `inRenderContextOf(this)` returns `false` and they emit + bump\n // normally — the in-flight render's rows are stale, the cache\n // writeback gets skipped, and the surface schedules a fresh paint.\n if (inRenderContextOf(this)) return this\n this.#invalidations++\n // Always emit and always cascade — surfaces (stream, UI) dedupe\n // via their own `scheduled` flag, and the parent's cache always\n // includes whatever rows we returned last render, so a child\n // mutation always implies a stale parent cache. (We previously\n // skipped the cascade when `hadCache` was false, which was buggy:\n // a node that intentionally doesn't cache — e.g. one whose\n // `_render` mutates a child mid-render and skips the writeback —\n // would silently fail to dirty its parent on the next mutation,\n // pinning the surface to the first render's output.)\n void this.emit(\"invalidate\")\n this.parent?.invalidate()\n return this\n }\n\n with<R>(fn: () => R): R {\n return withActiveNode(this, () => fn())\n }\n\n async render(ctx: RenderCtx): Promise<string[]> {\n // `visible: false` on the state suppresses the render entirely —\n // no `_render` call, no cached rows, zero layout footprint. Opt-in\n // via `state.visible`; absence or `true` is the default shown path.\n // Resolve inside the tracking ctx so a signal accessor read\n // subscribes this node. Async work registered via `createAsync` is\n // fire-and-forget here; surfaces that need a \"wait until settled\"\n // commit (Stream) install a `SuspenseContext` boundary at their\n // append boundary and await it before painting.\n this.#rendering ??= this.with(() => this.#render(ctx))\n\n try {\n return await this.#rendering\n } finally {\n this.#rendering = undefined\n }\n }\n\n async #render(ctx: RenderCtx): Promise<string[]> {\n // Cache the hidden result too, so a later `invalidate()` sees\n // `hadCache === true` and cascades up — otherwise a toggleable\n // panel (autocomplete, modal) whose first paint happened while\n // hidden would swallow the flip-to-visible invalidate.\n if (!(unwrap(this.state.visible) ?? true)) {\n this.#cache = { rows: [], version: ctx.version, width: ctx.width }\n return this.#cache.rows\n }\n // Cache key includes `ctx.width` because rendered output is\n // width-dependent. `version` alone catches state/theme mutations\n // (top-level bumps version on theme change), but a parent passing\n // a different `width` to the same node — common in flex\n // measure-then-allocate passes — needs a fresh render.\n if (this.#cache?.version === ctx.version && this.#cache.width === ctx.width) {\n return this.#cache.rows\n }\n // Capture the invalidation count *before* awaiting `_render`. If\n // it bumps mid-render, the rows we get back are based on stale\n // state — the external mutation that bumped it has already emitted\n // and re-scheduled the surface, so skip caching. The re-paint will\n // run a fresh `_render` against the latest state.\n const stamp = this.#invalidations\n try {\n const rows = await this._render(ctx)\n if (this.#invalidations === stamp) {\n this.#cache = { rows, version: ctx.version, width: ctx.width }\n }\n return rows\n } catch (error) {\n this.logger?.error(error)\n // Don't cache the error result — a later invalidate should\n // re-render and give the user a chance to recover.\n this.#cache = undefined\n return [\n ctx.style.error(\n `[Node render error: ${error instanceof Error ? error.message : String(error)}]`\n ),\n ]\n }\n }\n\n protected abstract _render(ctx: RenderCtx): Promise<string[]> | string[]\n\n add(child: Node): this {\n this.splice(this.#children.length, 0, child)\n return this\n }\n\n remove(child: Node): this {\n const i = this.#children.indexOf(child)\n if (i === -1) return this\n this.splice(i, 1)\n return this\n }\n\n splice(start: number, deleteCount: number, ...items: Node[]): this {\n // Reject self-insertion — a node in its own children list would\n // create a cycle and stack-overflow on traversal.\n const filtered: Node[] = []\n for (const c of items) {\n if (c === this) continue\n filtered.push(c)\n }\n\n // If any item is already a child of this container, pull it out\n // first so we don't end up with duplicates. Adjust `start` when a\n // removal falls before it so the caller-relative index is preserved.\n let adjustedStart = Math.max(0, Math.min(start, this.#children.length))\n for (const c of filtered) {\n if (c.parent !== this) continue\n const i = this.#children.indexOf(c)\n if (i === -1) continue\n this.#children.splice(i, 1)\n if (i < adjustedStart) adjustedStart--\n }\n\n const removed = this.#children.splice(adjustedStart, deleteCount, ...filtered)\n for (const c of removed) {\n void this.emit(\"childremoved\", { child: c })\n if (c.mounted) c.unmount()\n if (c.parent === this) c.#parent = undefined\n }\n for (const c of filtered) {\n // Detach from old parent before rewriting `#parent`, so the old\n // parent's `remove()` path observes a consistent `c.parent === old`\n // and cleans up its own bookkeeping.\n if (c.parent && c.parent !== this) {\n c.unmount()\n c.parent.remove(c)\n }\n c.#parent = this\n void this.emit(\"childadded\", { child: c })\n if (this.#ctx !== undefined) c.mount(this.#ctx)\n }\n this.invalidate()\n return this\n }\n\n clear(): this {\n this.splice(0, this.#children.length)\n return this\n }\n\n mount(ctx: MountCtx): this {\n if (this.#ctx?.surface === ctx.surface) return this\n if (this.#ctx) {\n throw new Error(\n `Node is already mounted on \"${this.#ctx.surface}\" (requested \"${ctx.surface}\"). Unmount first if you meant to move it.`\n )\n }\n // Make sure state.visible is tracked and will trigger invalidate\n withActiveNode(this, () => unwrap(this.state.visible))\n this.#ctx = ctx\n this.#registerActionMeta(ctx)\n void this.emit(\"mount\")\n for (const c of this.#children) c.mount(ctx)\n return this\n }\n\n /** Contribute any `ActionInfo`-shaped entries in `this.actions` to\n * the catalog. The instance `fn` stays on the node (dispatch finds\n * it via the focus-chain walk); only metadata lands in the registry.\n * Uses `extend: false` so defaults don't clobber user overrides\n * that were registered before the widget mounted. */\n #registerActionMeta(ctx: MountCtx): void {\n if (!this.actions) return\n const metas: Record<string, ActionDef> = {}\n let any = false\n for (const [id, entry] of Object.entries(this.actions)) {\n if (typeof entry === \"function\") continue\n const { fn: _fn, ...info } = entry\n metas[id] = info\n any = true\n }\n if (any) ctx.actions.register(metas, { default: true })\n }\n\n unmount(): this {\n if (!this.#ctx) return this\n this.#mountAc?.abort()\n this.#mountAc = undefined\n for (const c of this.#children) c.unmount()\n void this.emit(\"unmount\")\n this.#ctx.input.blur(this)\n this.#ctx = undefined\n return this\n }\n\n /** Become the focused node. Routes through the MountCtx, which emits\n * `blur` on the previous focus and `focus` on this node.\n *\n * When called before the node is mounted (the common case for\n * `input({ focus: true }).on(...)` style setup), the focus is\n * deferred until the next `mount` event. That keeps the API\n * synchronous and side-effect-free at build time — callers don't\n * have to think about ordering. */\n focus(): this {\n if (!this.#ctx) {\n this.once(\"mount\", () => this.focus())\n return this\n }\n this.#ctx.input.focus(this)\n return this\n }\n\n /** Release focus. No-op when not mounted. */\n blur(): this {\n this.#ctx?.input.blur(this)\n return this\n }\n\n get visible() {\n return unwrap(this.state.visible) ?? true\n }\n\n show(): this {\n if (this.visible) return this\n this.state.visible = true\n void this.emit(\"show\")\n return this\n }\n\n hide(): this {\n if (!this.visible) return this\n this.state.visible = false\n void this.emit(\"hide\")\n return this\n }\n\n toggle(): this {\n this.state.visible = !this.visible\n return this\n }\n\n ref<N extends Node>(ref?: Ref<this extends N ? N : never>): this {\n if (ref) ref.value = this as unknown as Exclude<typeof ref.value, undefined>\n return this\n }\n\n isVisible(): boolean {\n return this.visible && (this.parent ? this.parent.isVisible() : true)\n }\n}\n\n/** Runtime type guard for Node.\n *\n * @internal*/\nexport function isNode(x: unknown): x is Node {\n return x instanceof Node\n}\n"],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ import { Ut as NodeActionMap, X as State, nt as Reactive, rt as Ref } from "./queries-DH3bj74K.mjs";
2
+ import { t as Input } from "./input-B5Vy-iaa.mjs";
3
+ import { n as SearchOptions, r as SearchItem, t as SearchItems } from "./search-FvGnswRl.mjs";
4
+ import { i as Select, o as SelectState, t as Option } from "./select-CbmAZdpt.mjs";
5
+ import { r as TreeProps, t as TreeItem } from "./tree-C28QOVIK.mjs";
6
+ //#region src/widgets/picker.d.ts
7
+ type PickerItem = Option & SearchItem;
8
+ type PickerActions = keyof ReturnType<typeof picker>["actions"];
9
+ type PickerBaseProps<T extends PickerItem = PickerItem> = SearchOptions<T> & {
10
+ /** The `Input` to watch. Pass the node directly, or a `Ref<Input>`
11
+ * populated by `node.ref(ref)` elsewhere in the tree — the latter
12
+ * enables fully inline composition where the input doesn't need a
13
+ * local binding. The ref is dereferenced on mount, so wiring is
14
+ * type-safe and ordering-flexible (autocomplete can be constructed
15
+ * before the Input is). */
16
+ input?: Input | Ref<Input>;
17
+ pattern?: Reactive<string>;
18
+ reverse?: boolean;
19
+ actions?: NodeActionMap<Select<T>>;
20
+ };
21
+ type PickerSelectProps<T extends PickerItem = PickerItem> = PickerBaseProps<T> & Omit<SelectState<T>, "items"> & {
22
+ items: Reactive<SearchItems<T>>;
23
+ tree?: never;
24
+ };
25
+ type PickerTreeProps<T extends TreeItem = TreeItem> = PickerBaseProps<T> & TreeProps<T> & {
26
+ items?: never;
27
+ };
28
+ declare const picker: <T extends PickerItem = PickerItem>(props: State<PickerSelectProps<T> | PickerTreeProps<T>>) => Select<T> & {
29
+ actions: {
30
+ "picker.next": () => boolean | undefined;
31
+ "picker.prev": () => boolean | undefined;
32
+ };
33
+ };
34
+ //#endregion
35
+ export { picker as a, PickerTreeProps as i, PickerItem as n, PickerSelectProps as r, PickerActions as t };
36
+ //# sourceMappingURL=picker-D8OLb6kZ.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"picker-D8OLb6kZ.d.mts","names":[],"sources":["../src/widgets/picker.ts"],"mappings":";;;;;;KAgBY,aAAa,SAAS;KACtB,sBAAsB,kBAAkB;KAE/C,gBAAgB,UAAU,aAAa,cAAc,cAAc;;;;;;;EAOtE,QAAQ,QAAQ,IAAI;EACpB,UAAU;EACV;EACA,UAAU,cAAc,OAAO;;KAGrB,kBAAkB,UAAU,aAAa,cAAc,gBAAgB,KACjF,KAAK,YAAY;EACf,OAAO,SAAS,YAAY;EAC5B;;KAGQ,gBAAgB,UAAU,WAAW,YAAY,gBAAgB,KAC3E,UAAU;EACR;;cASS,SACV,UAAU,aAAU,YAAA,OAAsB,MAAM,kBAAkB,KAAK,gBAAgB,QAAG,OAAA"}