@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":"flex-Bguo-fUe.mjs","names":[],"sources":["../src/layout/flex.ts"],"sourcesContent":["import type { FlexState } from \"../core/state.ts\"\n\nimport { sliceAnsi, stringWidth } from \"@zaly/shared/ansi\"\nimport { clamp, resolveSize } from \"./size.ts\"\n\n/**\n * Items participating in a row-direction allocation. Extends the\n * `Flexible` mixin with the node's intrinsic content sizes (from\n * `Node.layout()` or fallback measurement). Box constructs these\n * before calling `allocateRow`.\n *\n * @internal\n */\nexport interface RowItem extends FlexState {\n /** Measured natural (max-content) width — used as the flex-basis\n * when no fixed `width` is set. Falls back to 0 when omitted. */\n natural?: number\n /** Intrinsic min-content width — the smallest the node can render\n * without breaking content. Acts as a shrink floor below which the\n * allocator won't go (combined with `Flexible.minWidth`). */\n intrinsicMin?: number\n}\n\nexport interface AllocateOpts {\n contentWidth: number\n gap: number\n}\n\n/**\n * Allocate widths to row-direction children with CSS-style flex\n * semantics. Each item starts at its **basis**:\n *\n * - `width: <number | \"N%\">` → resolved fixed width.\n * - `width: \"fill\"` → 0 (claim slack, ignore intrinsic).\n * - any other non-fixed spec → `natural` (content size, 0 when not\n * provided).\n *\n * Three branches:\n *\n * - `slack > 0`: distribute by `flexGrow`. Items without grow weight\n * stay at basis; leftover slack is *not* smeared onto siblings.\n * - `slack < 0` (deficit): distribute the shortfall by\n * `basis * flexShrink` (CSS shrink default = 1). Each item is\n * clamped at its `minWidth` floor so content doesn't break.\n * - `slack == 0`: items take their basis as-is.\n *\n * `min`/`max` (combined with `intrinsicMin`) clamp per-item; rounding\n * remainder lands on the last grower / shrinker.\n *\n * @internal\n */\nexport function allocateRow(items: readonly RowItem[], opts: AllocateOpts): number[] {\n const { contentWidth } = opts\n const gapTotal = opts.gap * Math.max(0, items.length - 1)\n const available = contentWidth - gapTotal\n\n const bases: number[] = Array.from({ length: items.length })\n const grows: number[] = Array.from({ length: items.length })\n const shrinks: number[] = Array.from({ length: items.length })\n // Effective minimum per item — the larger of the user-set `minWidth`\n // and the node's intrinsic `min-content`. Shrink can't go below this.\n const mins: number[] = Array.from({ length: items.length })\n let baseSum = 0\n let growSum = 0\n\n for (let i = 0; i < items.length; i++) {\n const item = items[i]\n if (isFixedWidth(item.width)) {\n bases[i] = resolveSize(item.width, contentWidth) ?? 0\n grows[i] = 0\n shrinks[i] = 0 // Fixed-width items never shrink — author asked for an exact size.\n } else if (item.width === \"fill\") {\n // `fill` claims slack and ignores intrinsic basis (CSS `flex: 1 1 0`).\n bases[i] = 0\n grows[i] = item.flexGrow ?? 1\n shrinks[i] = item.flexShrink ?? 1\n } else {\n bases[i] = item.natural ?? 0\n grows[i] = item.flexGrow ?? 0\n shrinks[i] = item.flexShrink ?? 1\n }\n const intrinsicMin = item.intrinsicMin ?? 0\n const userMin = resolveSize(item.minWidth, contentWidth) ?? 0\n mins[i] = Math.max(intrinsicMin, userMin)\n baseSum += bases[i]\n growSum += grows[i]\n }\n\n const slack = available - baseSum\n const widths: number[] = Array.from({ length: items.length })\n let allocated = 0\n let lastGrowIndex = -1\n let lastShrinkIndex = -1\n\n if (slack >= 0) {\n // Grow phase — distribute slack among items that asked for it.\n for (let i = 0; i < items.length; i++) {\n const item = items[i]\n let w = bases[i]\n if (growSum > 0 && grows[i] > 0) {\n w += Math.floor((slack * grows[i]) / growSum)\n lastGrowIndex = i\n }\n w = clamp(w, { available: contentWidth, max: item.maxWidth, min: mins[i] })\n widths[i] = w\n allocated += w\n }\n // Tail grower absorbs rounding remainder and any over/underflow\n // from min/max clamps. We aim at `baseSum + slack` (i.e. exactly\n // `available`) only when growers exist; otherwise leftover is real\n // slack the parent will pad as end-of-row space.\n if (lastGrowIndex !== -1) {\n const target = baseSum + slack\n if (allocated !== target) {\n widths[lastGrowIndex] = Math.max(0, widths[lastGrowIndex] + (target - allocated))\n }\n }\n } else {\n // Shrink phase — distribute the deficit weighted by `basis * shrink`.\n // Items at their `mins[i]` floor stop shrinking; the remaining\n // deficit re-routes to the others. Iterate until stable or no\n // shrinkable items remain.\n for (let i = 0; i < items.length; i++) widths[i] = bases[i]\n let deficit = -slack // positive\n const frozen: boolean[] = Array.from({ length: items.length }, () => false)\n // Cap loop at items.length+1 — each pass freezes at least one item\n // or fully resolves the deficit. Bounded, no risk of looping.\n for (let pass = 0; pass <= items.length && deficit > 0; pass++) {\n let weightSum = 0\n for (let i = 0; i < items.length; i++) {\n if (!frozen[i]) weightSum += widths[i] * shrinks[i]\n }\n if (weightSum === 0) break // nothing left can shrink\n let froze = false\n for (let i = 0; i < items.length; i++) {\n if (frozen[i]) continue\n const share = Math.floor((deficit * widths[i] * shrinks[i]) / weightSum)\n const next = widths[i] - share\n if (next <= mins[i]) {\n deficit -= widths[i] - mins[i]\n widths[i] = mins[i]\n frozen[i] = true\n froze = true\n } else {\n widths[i] = next\n lastShrinkIndex = i\n }\n }\n if (!froze) {\n // Apply remaining deficit proportionally one more time without\n // freezing — the round above didn't hit any floor.\n deficit = 0\n }\n }\n for (let i = 0; i < items.length; i++) {\n widths[i] = clamp(widths[i], {\n available: contentWidth,\n max: items[i].maxWidth,\n min: mins[i],\n })\n allocated += widths[i]\n }\n // Soak any rounding-shaped over/underflow into the last shrinker so\n // the row sums exactly to `available` (or to the minSum floor when\n // mins force overflow).\n if (lastShrinkIndex !== -1) {\n const target = Math.max(\n available,\n mins.reduce((a, b) => a + b, 0)\n )\n if (allocated !== target) {\n const adj = target - allocated\n widths[lastShrinkIndex] = Math.max(mins[lastShrinkIndex], widths[lastShrinkIndex] + adj)\n }\n }\n }\n\n return widths\n}\n\n/**\n * Whether a `Flexible.width` spec is a fixed numeric size (number or\n * `Pct`). `\"fill\"`/`\"fit\"`/undefined are non-fixed and resolve from\n * surrounding context (slack / measurement).\n *\n * @internal\n */\nexport function isFixedWidth(w: FlexState[\"width\"]): boolean {\n return typeof w === \"number\" || (typeof w === \"string\" && w.endsWith(\"%\"))\n}\n\nexport interface ZipOpts {\n widths: readonly number[]\n gap: number\n}\n\n/**\n * Zip pre-rendered child rows horizontally. Shorter children are padded with\n * blank rows of their allocated width. Rows of each child must already be\n * padded to the corresponding width in `widths`.\n *\n * @internal\n */\nexport function zipRow(children: readonly (readonly string[])[], opts: ZipOpts): string[] {\n if (children.length === 0) return []\n const gapStr = \" \".repeat(opts.gap)\n const height = children.reduce((h, rows) => Math.max(h, rows.length), 0)\n const out: string[] = []\n for (let r = 0; r < height; r++) {\n let line = \"\"\n for (let c = 0; c < children.length; c++) {\n if (c > 0) line += gapStr\n const rows = children[c]\n line += r < rows.length ? rows[r] : \" \".repeat(opts.widths[c])\n }\n out.push(line)\n }\n return out\n}\n\nexport interface StackOpts {\n gap: number\n width: number\n}\n\n/**\n * Stack pre-rendered child rows vertically. `gap` blank rows of `width`\n * spaces separate each sibling pair that *contributes rows*. Children\n * that emit no rows (e.g. `visible: false`, falsy `show()` branch) are\n * skipped along with the gap that would have surrounded them — no\n * stray blank bands. Caller must ensure each child's rows are already\n * padded to `width`.\n *\n * @internal\n */\nexport function stackColumn(children: readonly (readonly string[])[], opts: StackOpts): string[] {\n const out: string[] = []\n const blank = \" \".repeat(opts.width)\n let any = false\n for (const child of children) {\n if (child.length === 0) continue\n if (any) for (let g = 0; g < opts.gap; g++) out.push(blank)\n out.push(...child)\n any = true\n }\n return out\n}\n\n/**\n * Pad a row to `width` cells without injecting a `RESET`. Used by the\n * row/column zip-and-pad steps — the parent `Box`'s outer\n * `reapplyStyle` chain handles re-emitting any ambient SGR after inner\n * `[0m`s, so the pad cells stay inside the active style. Over-wide\n * rows are clipped via `sliceAnsi`, preserving SGR state.\n *\n * Distinct from `padOrClip` in `style/ansi.ts`, which is for\n * standalone (non-Box-wrapped) paths and *does* inject a `RESET`\n * before the pad.\n *\n * @internal\n */\nexport function padRow(row: string, width: number): string {\n const w = stringWidth(row)\n if (w === width) return row\n if (w < width) return row + \" \".repeat(width - w)\n return sliceAnsi(row, 0, width)\n}\n"],"mappings":"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"}
@@ -0,0 +1,108 @@
1
+ import { stripAnsi } from "@zaly/shared/ansi";
2
+ import { basename } from "pathe";
3
+ //#region src/markdown/image.ts
4
+ /**
5
+ * Image callback + post-processor for the markdown pipeline.
6
+ *
7
+ * The markdown renderer is synchronous (callbacks return strings), but
8
+ * image preparation is async (sharp metadata, KGP transmit, format
9
+ * conversion). We reconcile with a two-pass approach:
10
+ *
11
+ * 1. During rendering, the `image` callback pushes `{ src, alt }` into
12
+ * the entries array and emits a compact `<img id=N>` marker where
13
+ * `N` is the entry's index. The marker is plain text so any
14
+ * renderer (marked or `Bun.markdown.render`) emits it verbatim.
15
+ * 2. After rendering, `resolve(ctx, rendered)` renders every unique
16
+ * src concurrently via cached `Image` nodes, then replaces each
17
+ * marker with either the rendered rows (for *block* images — ones
18
+ * that end up on their own line) or the markdown alt text (for
19
+ * inline images, which would otherwise break text flow).
20
+ *
21
+ * The caller passes in a persistent `cache: Map<src, Image>` owned by
22
+ * the Markdown instance so re-renders reuse the same Image node (and
23
+ * therefore the same KGP placement id, which the spec guarantees is a
24
+ * flicker-free move/resize).
25
+ */
26
+ function createImageCallback(host) {
27
+ const entries = [];
28
+ const cache = host.images;
29
+ return {
30
+ cb(alt, meta) {
31
+ const id = entries.length;
32
+ entries.push({
33
+ alt,
34
+ src: meta.src
35
+ });
36
+ return `<img id=${id}>`;
37
+ },
38
+ async resolve(ctx, rendered) {
39
+ if (entries.length === 0) return rendered;
40
+ const { Image } = await import("./widgets/image.mjs");
41
+ const ordinals = /* @__PURE__ */ new Map();
42
+ const rowsByEntry = [];
43
+ const used = /* @__PURE__ */ new Set();
44
+ await Promise.all(entries.map(async (entry, idx) => {
45
+ const ord = ordinals.get(entry.src) ?? 0;
46
+ ordinals.set(entry.src, ord + 1);
47
+ const key = `${entry.src}\u0000${ord}`;
48
+ used.add(key);
49
+ let img = cache.get(key);
50
+ if (img === void 0) {
51
+ img = new Image({
52
+ alt: entry.alt,
53
+ src: entry.src
54
+ });
55
+ host.add(img);
56
+ cache.set(key, img);
57
+ }
58
+ rowsByEntry[idx] = await img.render(ctx);
59
+ }));
60
+ for (const [key] of cache) if (!used.has(key)) {
61
+ const img = cache.get(key);
62
+ if (img) {
63
+ host.remove(img);
64
+ cache.delete(key);
65
+ }
66
+ }
67
+ const out = [];
68
+ for (const line of rendered.split("\n")) {
69
+ const matched = [];
70
+ for (const m of line.matchAll(MARKER_RE)) {
71
+ const idx = Number(m[1]);
72
+ matched.push({
73
+ entry: entries[idx],
74
+ rows: rowsByEntry[idx] ?? []
75
+ });
76
+ }
77
+ if (matched.length === 0) {
78
+ out.push(line);
79
+ continue;
80
+ }
81
+ if (matched.length === 1 && stripAnsi(line.replace(MARKER_RE, "")).trim() === "" && matched[0].rows.length > 0) {
82
+ out.push(...matched[0].rows);
83
+ continue;
84
+ }
85
+ out.push(line.replaceAll(MARKER_RE, (_, idxStr) => labelOf(entries[Number(idxStr)])));
86
+ for (const { rows } of matched) if (rows.some(isRealGraphics)) out.push(...rows);
87
+ }
88
+ return out.join("\n");
89
+ }
90
+ };
91
+ }
92
+ /** Friendly inline placeholder for an image that can't render at this
93
+ * position. Prefers the markdown alt text; falls back to the basename
94
+ * of the source path (so `[clip.png]` instead of `[/tmp/long/path/clip.png]`),
95
+ * and "image" for anything weirder. */
96
+ function labelOf(entry) {
97
+ return `[${entry.alt !== "" ? entry.alt : basename(entry.src) || "image"}]`;
98
+ }
99
+ const MARKER_RE = /<img id=(\d+)>/g;
100
+ /** True when a row contains a Kitty Graphics Protocol escape (transmit
101
+ * or place) — i.e. real image data, not the text alt fallback. */
102
+ function isRealGraphics(row) {
103
+ return row.includes("\x1B_G");
104
+ }
105
+ //#endregion
106
+ export { createImageCallback };
107
+
108
+ //# sourceMappingURL=image-Cm5T-uw-.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"image-Cm5T-uw-.mjs","names":[],"sources":["../src/markdown/image.ts"],"sourcesContent":["import type { RenderCtx } from \"../core/ctx.ts\"\nimport type { Node } from \"../core/node.ts\"\nimport type { Image } from \"../widgets/image.ts\"\nimport type { MdCallbacks, MdImageMeta } from \"./types.ts\"\n\nimport { stripAnsi } from \"@zaly/shared/ansi\"\nimport { basename } from \"pathe\"\n\n/** Minimal host shape the image callback needs. Any Node-like parent\n * with a per-src `Image` cache satisfies it — the `Markdown` widget is\n * the primary caller but the interface keeps this file decoupled from\n * widget internals (and breaks the widgets↔markdown import loop). */\nexport interface ImageHost {\n /** Per-src cache so re-renders reuse the same `Image` — same KGP\n * placement id, which the spec guarantees is a flicker-free\n * move/resize. */\n images: Map<string, Image>\n /** Attach a freshly-created `Image` as a child so mount/unmount\n * propagate. */\n add: (child: Node) => unknown\n remove: (child: Node) => unknown\n}\n\n/** Per-occurrence image metadata collected during markdown rendering. */\ninterface ImageEntry {\n src: string\n alt: string\n}\n\n/**\n * Image callback + post-processor for the markdown pipeline.\n *\n * The markdown renderer is synchronous (callbacks return strings), but\n * image preparation is async (sharp metadata, KGP transmit, format\n * conversion). We reconcile with a two-pass approach:\n *\n * 1. During rendering, the `image` callback pushes `{ src, alt }` into\n * the entries array and emits a compact `<img id=N>` marker where\n * `N` is the entry's index. The marker is plain text so any\n * renderer (marked or `Bun.markdown.render`) emits it verbatim.\n * 2. After rendering, `resolve(ctx, rendered)` renders every unique\n * src concurrently via cached `Image` nodes, then replaces each\n * marker with either the rendered rows (for *block* images — ones\n * that end up on their own line) or the markdown alt text (for\n * inline images, which would otherwise break text flow).\n *\n * The caller passes in a persistent `cache: Map<src, Image>` owned by\n * the Markdown instance so re-renders reuse the same Image node (and\n * therefore the same KGP placement id, which the spec guarantees is a\n * flicker-free move/resize).\n */\nexport function createImageCallback(host: ImageHost): {\n cb: MdCallbacks[\"image\"]\n resolve: (ctx: RenderCtx, rendered: string) => Promise<string>\n} {\n const entries: ImageEntry[] = []\n const cache = host.images\n\n return {\n cb(alt: string, meta: MdImageMeta): string {\n const id = entries.length\n entries.push({ alt, src: meta.src })\n return `<img id=${id}>`\n },\n\n async resolve(ctx: RenderCtx, rendered: string): Promise<string> {\n if (entries.length === 0) return rendered\n\n // `Image` is heavy (pulls image-meta, sharp lazy loaders, KGP\n // encoder) and 99% of markdown has no images. Load it only when\n // we have something to place.\n const { Image } = await import(\"../widgets/image.ts\")\n\n // One Image (and therefore one KGP `placementId`) per occurrence\n // so multiple references to the same `src` show up as distinct\n // placements on screen. Cache key is `${src}\\0${ordinal-within-src}`\n // — the ordinal makes each repeated reference stable across\n // re-renders (slot 0 is always slot 0, so the placementId stays\n // pinned and KGP gives a flicker-free move/resize). Image bytes\n // are de-duped at a different layer (`transmitOnce` in kitty.ts),\n // so multiple occurrences cost N placements but only one transmit.\n const ordinals = new Map<string, number>()\n const rowsByEntry: string[][] = []\n const used = new Set<string>()\n await Promise.all(\n entries.map(async (entry, idx) => {\n const ord = ordinals.get(entry.src) ?? 0\n ordinals.set(entry.src, ord + 1)\n const key = `${entry.src}\\u0000${ord}`\n used.add(key)\n let img = cache.get(key)\n if (img === undefined) {\n img = new Image({ alt: entry.alt, src: entry.src })\n host.add(img)\n cache.set(key, img)\n }\n rowsByEntry[idx] = await img.render(ctx)\n })\n )\n\n for (const [key] of cache) {\n if (!used.has(key)) {\n const img = cache.get(key)\n if (img) {\n host.remove(img)\n cache.delete(key)\n }\n }\n }\n\n // Walk the rendered output line by line:\n // - Marker alone on its line (whitespace + ANSI tolerated) →\n // replace the line with the full image rows.\n // - Marker mid-line → inline ref placeholder, then splice the\n // image rows on the next line(s) so the picture still\n // appears below the surrounding text.\n // Multiple markers on one mid-line all get inline refs; their\n // image rows stack underneath in document order.\n const out: string[] = []\n for (const line of rendered.split(\"\\n\")) {\n const matched: { entry: ImageEntry; rows: string[] }[] = []\n for (const m of line.matchAll(MARKER_RE)) {\n const idx = Number(m[1])\n matched.push({ entry: entries[idx], rows: rowsByEntry[idx] ?? [] })\n }\n\n if (matched.length === 0) {\n out.push(line)\n continue\n }\n\n const lineHasOnlyMarker =\n matched.length === 1 && stripAnsi(line.replace(MARKER_RE, \"\")).trim() === \"\"\n\n if (lineHasOnlyMarker && matched[0].rows.length > 0) {\n out.push(...matched[0].rows)\n continue\n }\n\n // Mid-line: replace each marker with a nice inline ref, then\n // append every image's rows below the line in order. Skip rows\n // that don't contain real graphics (KGP escapes) — when the\n // terminal can't render images, `Image.render` falls back to a\n // single-row alt-text representation, which would just\n // duplicate the inline label we already emitted.\n out.push(\n line.replaceAll(MARKER_RE, (_, idxStr: string) => labelOf(entries[Number(idxStr)]))\n )\n for (const { rows } of matched) {\n if (rows.some(isRealGraphics)) out.push(...rows)\n }\n }\n return out.join(\"\\n\")\n },\n }\n}\n\n/** Friendly inline placeholder for an image that can't render at this\n * position. Prefers the markdown alt text; falls back to the basename\n * of the source path (so `[clip.png]` instead of `[/tmp/long/path/clip.png]`),\n * and \"image\" for anything weirder. */\nfunction labelOf(entry: ImageEntry): string {\n const label = entry.alt !== \"\" ? entry.alt : basename(entry.src) || \"image\"\n return `[${label}]`\n}\n\nconst MARKER_RE = /<img id=(\\d+)>/g\n\n/** True when a row contains a Kitty Graphics Protocol escape (transmit\n * or place) — i.e. real image data, not the text alt fallback. */\nfunction isRealGraphics(row: string): boolean {\n return row.includes(\"\\x1b_G\")\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAmDA,SAAgB,oBAAoB,MAGlC;CACA,MAAM,UAAwB,CAAC;CAC/B,MAAM,QAAQ,KAAK;CAEnB,OAAO;EACL,GAAG,KAAa,MAA2B;GACzC,MAAM,KAAK,QAAQ;GACnB,QAAQ,KAAK;IAAE;IAAK,KAAK,KAAK;GAAI,CAAC;GACnC,OAAO,WAAW,GAAG;EACvB;EAEA,MAAM,QAAQ,KAAgB,UAAmC;GAC/D,IAAI,QAAQ,WAAW,GAAG,OAAO;GAKjC,MAAM,EAAE,UAAU,MAAM,OAAO;GAU/B,MAAM,2BAAW,IAAI,IAAoB;GACzC,MAAM,cAA0B,CAAC;GACjC,MAAM,uBAAO,IAAI,IAAY;GAC7B,MAAM,QAAQ,IACZ,QAAQ,IAAI,OAAO,OAAO,QAAQ;IAChC,MAAM,MAAM,SAAS,IAAI,MAAM,GAAG,KAAK;IACvC,SAAS,IAAI,MAAM,KAAK,MAAM,CAAC;IAC/B,MAAM,MAAM,GAAG,MAAM,IAAI,QAAQ;IACjC,KAAK,IAAI,GAAG;IACZ,IAAI,MAAM,MAAM,IAAI,GAAG;IACvB,IAAI,QAAQ,KAAA,GAAW;KACrB,MAAM,IAAI,MAAM;MAAE,KAAK,MAAM;MAAK,KAAK,MAAM;KAAI,CAAC;KAClD,KAAK,IAAI,GAAG;KACZ,MAAM,IAAI,KAAK,GAAG;IACpB;IACA,YAAY,OAAO,MAAM,IAAI,OAAO,GAAG;GACzC,CAAC,CACH;GAEA,KAAK,MAAM,CAAC,QAAQ,OAClB,IAAI,CAAC,KAAK,IAAI,GAAG,GAAG;IAClB,MAAM,MAAM,MAAM,IAAI,GAAG;IACzB,IAAI,KAAK;KACP,KAAK,OAAO,GAAG;KACf,MAAM,OAAO,GAAG;IAClB;GACF;GAWF,MAAM,MAAgB,CAAC;GACvB,KAAK,MAAM,QAAQ,SAAS,MAAM,IAAI,GAAG;IACvC,MAAM,UAAmD,CAAC;IAC1D,KAAK,MAAM,KAAK,KAAK,SAAS,SAAS,GAAG;KACxC,MAAM,MAAM,OAAO,EAAE,EAAE;KACvB,QAAQ,KAAK;MAAE,OAAO,QAAQ;MAAM,MAAM,YAAY,QAAQ,CAAC;KAAE,CAAC;IACpE;IAEA,IAAI,QAAQ,WAAW,GAAG;KACxB,IAAI,KAAK,IAAI;KACb;IACF;IAKA,IAFE,QAAQ,WAAW,KAAK,UAAU,KAAK,QAAQ,WAAW,EAAE,CAAC,CAAC,CAAC,KAAK,MAAM,MAEnD,QAAQ,EAAE,CAAC,KAAK,SAAS,GAAG;KACnD,IAAI,KAAK,GAAG,QAAQ,EAAE,CAAC,IAAI;KAC3B;IACF;IAQA,IAAI,KACF,KAAK,WAAW,YAAY,GAAG,WAAmB,QAAQ,QAAQ,OAAO,MAAM,EAAE,CAAC,CACpF;IACA,KAAK,MAAM,EAAE,UAAU,SACrB,IAAI,KAAK,KAAK,cAAc,GAAG,IAAI,KAAK,GAAG,IAAI;GAEnD;GACA,OAAO,IAAI,KAAK,IAAI;EACtB;CACF;AACF;;;;;AAMA,SAAS,QAAQ,OAA2B;CAE1C,OAAO,IADO,MAAM,QAAQ,KAAK,MAAM,MAAM,SAAS,MAAM,GAAG,KAAK,QACnD;AACnB;AAEA,MAAM,YAAY;;;AAIlB,SAAS,eAAe,KAAsB;CAC5C,OAAO,IAAI,SAAS,QAAQ;AAC9B"}
@@ -0,0 +1,123 @@
1
+ import { n as TerminalQuery, t as TerminalQueries } from "./queries-DH3bj74K.mjs";
2
+ import { MaybePromise } from "@zaly/shared";
3
+ import { ImageInfo } from "@zaly/shared/image";
4
+ //#region src/image/kitty.d.ts
5
+ type KittyPlacement = {
6
+ imageId: number;
7
+ /** Placement ID. Omitted when this is an inline placement (U=1) */
8
+ placementId?: number;
9
+ /** inline: placement using unicode placeholder (U=1) */
10
+ inline?: boolean;
11
+ /** The KGP escape sequence to place the image */
12
+ seq: string;
13
+ /** Rows to render (excluding the placement escape sequence) */
14
+ data: string[];
15
+ };
16
+ type ImageSupport = {
17
+ terminal?: {
18
+ name: string;
19
+ version?: string;
20
+ };
21
+ remote?: boolean;
22
+ wrap?: (seq: string) => string;
23
+ tmux?: boolean;
24
+ } & ({
25
+ ok: false;
26
+ error: string;
27
+ terminal?: {
28
+ name: string;
29
+ version?: string;
30
+ };
31
+ } | {
32
+ ok: true;
33
+ error?: never;
34
+ terminal: {
35
+ name: string;
36
+ version?: string;
37
+ };
38
+ inline?: boolean;
39
+ });
40
+ type KittyResponse = {
41
+ attrs: Record<string, string | number>;
42
+ ok: true;
43
+ } | {
44
+ attrs: Record<string, string | number>;
45
+ ok: false;
46
+ error: {
47
+ code: string;
48
+ message: string;
49
+ };
50
+ };
51
+ type PlacementDims = {
52
+ /** Display width in terminal cells. */
53
+ cols: number;
54
+ /** Display height in terminal cells. */
55
+ rows: number;
56
+ };
57
+ declare const DIACRITICS: string[];
58
+ declare const PLACEHOLDER: string;
59
+ /** Allocate a fresh 24-bit image id (1..0xFFFFFFFE). */
60
+ declare function allocateImageId(): number;
61
+ /** Allocate a fresh 24-bit placement id. */
62
+ declare function allocatePlacementId(): number;
63
+ declare class Kitty {
64
+ #private;
65
+ constructor(support: ImageSupport);
66
+ get supported(): boolean;
67
+ get inline(): boolean;
68
+ /** Create a KGP request escape sequence */
69
+ request(attrs: Record<string, string | number>, data?: string | Uint8Array, opts?: {
70
+ base64?: boolean;
71
+ }): string;
72
+ /** Create a TerminalQuery that requests a KGP operation and parses the response. */
73
+ query(attrs: Record<string, string | number>, data?: string | Uint8Array, opts?: {
74
+ base64?: boolean;
75
+ }): TerminalQuery;
76
+ /** Probe the terminal for KGP support. Should return an error response. */
77
+ probe(): string;
78
+ /** Parse a KGP response escape sequence into a structured object. */
79
+ parse(res: string): KittyResponse | undefined;
80
+ placement(imageId: number, dims: PlacementDims): KittyPlacement | undefined;
81
+ /** Delete an image and all its placements, or ALL images if `imageId` is omitted. */
82
+ deleteImage(imageId?: number): string;
83
+ /** Delete a placement (keeps image data so it can be re-placed cheaply). */
84
+ deletePlacement(imageId: number, placementId: number): string;
85
+ /**
86
+ * Ensure an image has been transmitted to the terminal at least once.
87
+ * On the first call per `src`, returns the full `a=t,t=f` escape to be
88
+ * prepended to the caller's first row — the APC payload has zero display
89
+ * width, so layout sees through it. Subsequent calls return `transmit:
90
+ * ""` since the terminal already has the bytes under `imageId`.
91
+ *
92
+ * The caller is expected to combine this with `placement(...)` on every
93
+ * render to actually paint the image at the right cell rectangle.
94
+ */
95
+ transmitOnce(info: ImageInfo): Promise<{
96
+ imageId: number;
97
+ transmit?: string;
98
+ } | undefined>;
99
+ /**
100
+ * Transmit an image to the terminal by file path (`t=f`). Near-zero cost
101
+ * on the wire — the terminal opens and reads the file itself. `path`
102
+ * must be absolute and must point to a regular file; symlinks are
103
+ * followed by the terminal.
104
+ */
105
+ transmitFile(id: number, path: string): string;
106
+ /**
107
+ * Remote/bytes transmission fallback (`t=d`) for when the terminal can't
108
+ * read local files — typical of SSH sessions. Base64-encodes and chunks
109
+ * the PNG payload at 4KB boundaries per the protocol.
110
+ */
111
+ transmitBytes(id: number, png: Uint8Array): string;
112
+ }
113
+ /** Drop the per-src transmit cache. Mostly for tests. */
114
+ declare function resetTransmitCache(): void;
115
+ declare function resetKittyGraphics(): void;
116
+ declare function bumpPlacements(rows: string[]): undefined | {
117
+ delete: () => string;
118
+ rows: string[];
119
+ };
120
+ declare function loadKittyGraphics(tq: TerminalQueries): MaybePromise<Kitty>;
121
+ //#endregion
122
+ export { DIACRITICS, ImageSupport, Kitty, KittyPlacement, KittyResponse, PLACEHOLDER, PlacementDims, allocateImageId, allocatePlacementId, bumpPlacements, loadKittyGraphics, resetKittyGraphics, resetTransmitCache };
123
+ //# sourceMappingURL=image.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"image.d.mts","names":[],"sources":["../src/image/kitty.ts"],"mappings":";;;;KAqBY;EACV;;EAEA;;EAEA;;EAEA;;EAEA;;KAGU;EACV;IAAa;IAAc;;EAC3B;EACA,QAAQ;EACR;;EAGI;EACA;EACA;IAAa;IAAc;;;EAG3B;EACA;EACA;IAAY;IAAc;;EAC1B;;KAIM;EAEN,OAAO;EACP;;EAGA,OAAO;EACP;EACA;IAAS;IAAc;;;KAGjB;;EAEV;;EAEA;;cAaW;cACA;;iBAgBG;;iBAKA;cAIH;;EAIX,YAAY,SAAS;MAKjB;MAIA;;EAKJ,QACE,OAAO,iCACP,gBAAgB,YAChB;IAAQ;;;EAcV,MACE,OAAO,iCACP,gBAAgB,YAChB;IAAQ;MACP;;EAaH;;EAKA,MAAM,cAAc;EAsBpB,UAAU,iBAAiB,MAAM,gBAAgB;;EA+BjD,YAAY;;EAOZ,gBAAgB,iBAAiB;;;;;;;;;;;EAc3B,aAAa,MAAM,YAAY;IAAU;IAAiB;;;;;;;;EAqChE,aAAa,YAAY;;;;;;EASzB,cAAc,YAAY,KAAK;;;iBAoBjB;iBAIA;iBAwCA,eACd;EACe;EAAsB;;iBAiFvB,kBAAkB,IAAI,kBAAkB,aAAa"}
package/dist/image.mjs ADDED
@@ -0,0 +1,2 @@
1
+ import { a as allocatePlacementId, c as resetKittyGraphics, i as allocateImageId, l as resetTransmitCache, n as Kitty, o as bumpPlacements, r as PLACEHOLDER, s as loadKittyGraphics, t as DIACRITICS } from "./kitty-JbCJC0iQ.mjs";
2
+ export { DIACRITICS, Kitty, PLACEHOLDER, allocateImageId, allocatePlacementId, bumpPlacements, loadKittyGraphics, resetKittyGraphics, resetTransmitCache };
@@ -0,0 +1,64 @@
1
+ import { $ as Context, At as useContext, Bt as Actions, C as OverlaySurface, Ct as peek, Dt as untrack, Et as toAccessor, Ft as ActionCtx, Gt as KeyModifier, Ht as NodeAction, It as ActionDef, J as Layout, Jt as SpecialKeyName, Kt as KeyPattern, Lt as ActionFilter, Mt as withOwner, N as MountCtx, Ot as unwrap, P as RenderCtx, Pt as Action, Q as Accessor, Rt as ActionFn, S as Stream, St as onCleanup, Tt as signal, Ut as NodeActionMap, Vt as KeyBinding, Wt as KeyEvent, X as State, Xt as isKeyPattern, Y as LayoutState, Yt as canonical, Z as StyleState, Zt as keyMatches, _ as RenderStats, _t as createSuspenseBoundary, a as InputRouter, at as Signal, b as SurfaceType, bt as isAccessor, c as KeyPatterns, ct as SuspenseContext, d as RoutedPaste, dt as createIterable, et as Owner, f as BaseEvents, ft as createNode, g as RenderEvents, gt as createStore, h as NodeVisitor, ht as createRoot, i as parseAttrs, it as Setter, jt as withActiveNode, kt as useActiveOwner, l as KeymapEntry, lt as createAsync, m as isNode, mt as createRef, n as TerminalQuery, nt as Reactive, o as InputRouterEvents, ot as SignalStore, p as Node, pt as createProgressive, q as FlexState, qt as KeyPatternError, r as TerminalQueryOpts, rt as Ref, s as KeyHandler, st as SuspenseBoundary, t as TerminalQueries, tt as Progressive, u as RoutedKey, ut as createContext, v as Renderer, vt as effect, w as Terminal, wt as provideContext, x as UI, xt as memo, y as RendererOptions, yt as inRenderContextOf, zt as ActionMap } from "./queries-DH3bj74K.mjs";
2
+ import { _ as ShikiRequest, a as AnsiStyle, b as ShikiWorkerRequest, c as Color, d as RGB, f as Style, g as ShikiOpts, h as ShikiLanguage, i as AnsiColorName, l as ColorLightness, m as ShikiJob, n as ThemeValue, o as AnyStyle, p as ThemeKey, r as AnsiColor, s as BrightAnsiColorName, t as Theme, u as HexColor, v as ShikiResult, y as ShikiTheme } from "./types-B9Eh7k2v.mjs";
3
+ import { a as StyleBuilder, i as isMarkdown, n as inspect, r as inspectFormat, t as InspectOpts } from "./inspect-D6FW_QUo.mjs";
4
+ import { ArgsOpts } from "@zaly/shared/args";
5
+ //#region src/input/index.d.ts
6
+ declare function defineAction<T extends ArgsOpts = ArgsOpts>(action: ActionDef<T>): ActionDef<T>;
7
+ declare function defineAction<T extends ArgsOpts = ArgsOpts>(action: Action<T>): Action<T>;
8
+ //#endregion
9
+ //#region src/core/render.d.ts
10
+ /** Components-facing slice of the render environment — reactive
11
+ * accessors a widget body reads via `useContext(RenderContext)`. Use
12
+ * this when a widget needs ambient theme/style outside an `_render`
13
+ * call (the place where `RenderCtx` is threaded in). Width is
14
+ * deliberately omitted — that's a per-render allotted-size concept,
15
+ * not an ambient one. */
16
+ type RenderContextValue = {
17
+ /** Active theme. Tracked — widgets that read it re-fire on swap. */
18
+ theme: Accessor<Theme>;
19
+ /** Theme-bound chainable style builder. Derived memo over `theme`. */
20
+ style: Accessor<StyleBuilder>;
21
+ /** Whether to render images (when supported) */
22
+ images: Accessor<boolean>;
23
+ queries: TerminalQueries;
24
+ };
25
+ declare const RenderContext: Context<RenderContextValue | undefined>;
26
+ /**
27
+ * Render a Node, draining `createAsync` work in its subtree before
28
+ * returning final rows. Two shapes:
29
+ *
30
+ * - `createRender(() => node, ctx?)` — factory form. Creates a fresh
31
+ * `SuspenseBoundary` inside a new `createRoot` and runs the
32
+ * factory in that scope so descendants find the boundary via
33
+ * `useContext(SuspenseContext)`. For tests, benches, REPL.
34
+ *
35
+ * - `createRender(node, { ...ctx, boundary })` — pre-built Node
36
+ * with a caller-provided boundary. Used by `Stream.render`: the
37
+ * boundary was installed at `append` time, so we just render +
38
+ * drain without a new root scope.
39
+ *
40
+ * `ctx` defaults to `createCtx()` in the factory form.
41
+ *
42
+ * ```ts
43
+ * const rows = await createRender(() => markdown("**hi**"))
44
+ * ```
45
+ */
46
+ declare function createRender(node: Node, ctx: RenderCtx & {
47
+ boundary: SuspenseBoundary;
48
+ }): Promise<string[]>;
49
+ declare function createRender(node: () => Node, ctx?: RenderCtx): Promise<string[]>;
50
+ //#endregion
51
+ //#region src/core/index.d.ts
52
+ declare function createCtx(opts?: Partial<RenderCtx> & {
53
+ theme?: Theme;
54
+ }): Promise<RenderCtx>;
55
+ //#endregion
56
+ //#region src/renderer/index.d.ts
57
+ /** Factory shorthand for `new Renderer(opts)`. */
58
+ declare function createRenderer(opts?: RendererOptions): Promise<Renderer>;
59
+ //#endregion
60
+ //#region src/shiki/shiki.d.ts
61
+ declare function codeToAnsi(code: string, lang: string, opts?: ShikiOpts): Promise<string>;
62
+ //#endregion
63
+ export { Accessor, type Action, type ActionCtx, type ActionDef, type ActionFilter, type ActionFn, type ActionMap, type Actions, type AnsiColor, type AnsiColorName, type AnsiStyle, type AnyStyle, type BaseEvents, type BrightAnsiColorName, type Color, type ColorLightness, Context, type FlexState, type HexColor, type InputRouter, type InputRouterEvents, InspectOpts, type KeyBinding, KeyEvent, type KeyHandler, KeyModifier, KeyPattern, KeyPatternError, type KeyPatterns, type KeymapEntry, type Layout, type LayoutState, type MountCtx, type Node, type NodeAction, type NodeActionMap, type NodeVisitor, type OverlaySurface, Owner, Progressive, type RGB, Reactive, Ref, RenderContext, RenderContextValue, type RenderCtx, type RenderEvents, type RenderStats, type Renderer, type RendererOptions, type RoutedKey, type RoutedPaste, Setter, type ShikiJob, type ShikiLanguage, type ShikiOpts, type ShikiRequest, type ShikiResult, type ShikiTheme, type ShikiWorkerRequest, Signal, SignalStore, SpecialKeyName, type State, type Stream, type Style, type StyleState, type SurfaceType, SuspenseBoundary, SuspenseContext, type Terminal, TerminalQueries, TerminalQuery, TerminalQueryOpts, type Theme, type ThemeKey, type ThemeValue, type UI, canonical, codeToAnsi, createAsync, createContext, createCtx, createIterable, createNode, createProgressive, createRef, createRender, createRenderer, createRoot, createStore, createSuspenseBoundary, defineAction, effect, inRenderContextOf, inspect, inspectFormat, isAccessor, isKeyPattern, isMarkdown, type isNode, keyMatches, memo, onCleanup, parseAttrs, peek, provideContext, signal, toAccessor, untrack, unwrap, useActiveOwner, useContext, withActiveNode, withOwner };
64
+ //# sourceMappingURL=index.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/input/index.ts","../src/core/render.ts","../src/core/index.ts","../src/renderer/index.ts","../src/shiki/shiki.ts"],"mappings":";;;;;iBAGgB,aAAa,UAAU,WAAW,UAAU,QAAQ,UAAU,KAAK,UAAU;iBAC7E,aAAa,UAAU,WAAW,UAAU,QAAQ,OAAO,KAAK,OAAO;;;;;;;;;KCmB3E;;EAEV,OAAO,SAAS;;EAEhB,OAAO,SAAS;;EAEhB,QAAQ;EACR,SAAS;;cAEE,eAAa,QAAA;;;;;;;;;;;;;;;;;;;;;iBAsBJ,aACpB,MAAM,MACN,KAAK;EAAc,UAAU;IAC5B;iBACmB,aAAa,YAAY,MAAM,MAAM,YAAY;;;iBC/CjD,UACpB,OAAM,QAAQ;EAAe,QAAQ;IACpC,QAAQ;;;;iBCJW,eAAe,OAAM,kBAAuB,QAAQ;;;iBCJpD,WAAW,cAAc,cAAc,OAAO,YAAY"}
package/dist/index.mjs ADDED
@@ -0,0 +1,222 @@
1
+ import { C as useActiveOwner, E as withOwner, S as unwrap, T as withActiveNode, _ as peek, a as createIterable, b as toAccessor, c as createRef, d as createSuspenseBoundary, f as effect, g as onCleanup, h as memo, i as createContext, l as createRoot, m as isAccessor, n as SuspenseContext, o as createNode, p as inRenderContextOf, r as createAsync, s as createProgressive, t as Owner, u as createStore, v as provideContext, w as useContext, x as untrack, y as signal } from "./reactive-f7NpAi69.mjs";
2
+ import { n as inspectFormat, r as isMarkdown, t as inspect } from "./inspect-D1d9t-ni.mjs";
3
+ import { n as createRender, t as RenderContext } from "./render-DxVLqqB9.mjs";
4
+ import { t as codeToAnsi } from "./shiki-BrjVUkeh.mjs";
5
+ //#region src/input/index.ts
6
+ function defineAction(action) {
7
+ return action;
8
+ }
9
+ //#endregion
10
+ //#region src/core/index.ts
11
+ async function createCtx(opts = {}) {
12
+ const { createCtx: create } = await import("./render-DxVLqqB9.mjs").then((n) => n.i);
13
+ return create(opts);
14
+ }
15
+ //#endregion
16
+ //#region src/renderer/index.ts
17
+ /** Factory shorthand for `new Renderer(opts)`. */
18
+ async function createRenderer(opts = {}) {
19
+ const { Renderer } = await import("./renderer-CxL4xbkl.mjs");
20
+ return new Renderer(opts);
21
+ }
22
+ //#endregion
23
+ //#region src/input/keys.ts
24
+ const MOD_NAMES = /* @__PURE__ */ new Set([
25
+ "ctrl",
26
+ "alt",
27
+ "shift",
28
+ "meta"
29
+ ]);
30
+ const SPECIAL_KEY_NAMES = /* @__PURE__ */ new Set([
31
+ "enter",
32
+ "tab",
33
+ "backspace",
34
+ "delete",
35
+ "space",
36
+ "esc",
37
+ "up",
38
+ "down",
39
+ "left",
40
+ "right",
41
+ "home",
42
+ "end",
43
+ "pageup",
44
+ "pagedown",
45
+ "insert",
46
+ "f1",
47
+ "f2",
48
+ "f3",
49
+ "f4",
50
+ "f5",
51
+ "f6",
52
+ "f7",
53
+ "f8",
54
+ "f9",
55
+ "f10",
56
+ "f11",
57
+ "f12"
58
+ ]);
59
+ var KeyPatternError = class extends Error {
60
+ constructor(pattern, reason) {
61
+ super(`invalid key pattern ${JSON.stringify(pattern)}: ${reason}`);
62
+ this.name = "KeyPatternError";
63
+ }
64
+ };
65
+ function parsePattern(pattern) {
66
+ const mods = {
67
+ alt: false,
68
+ ctrl: false,
69
+ meta: false,
70
+ shift: false
71
+ };
72
+ let name = pattern;
73
+ while (name.length > 0) {
74
+ const dash = name.indexOf("-");
75
+ if (dash <= 0) break;
76
+ const mod = name.slice(0, dash);
77
+ if (!MOD_NAMES.has(mod)) throw new KeyPatternError(pattern, `unknown modifier: ${mod}`);
78
+ if (mods[mod]) throw new KeyPatternError(pattern, `duplicate modifier: ${mod}`);
79
+ mods[mod] = true;
80
+ name = name.slice(dash + 1);
81
+ }
82
+ const ret = normalizeKey({
83
+ ...mods,
84
+ name
85
+ });
86
+ name = ret.name;
87
+ if (name === "") throw new KeyPatternError(pattern, "missing key name");
88
+ if (name.includes("-") && name !== "-") throw new KeyPatternError(pattern, "too many keys");
89
+ if (!isPrintableKeyName(name)) throw new KeyPatternError(pattern, `unknown key name: ${name}`);
90
+ if (mods.shift && !canUseShiftModifier(name)) throw new KeyPatternError(pattern, `shift-${name} will not trigger; bind the shifted character instead`);
91
+ return ret;
92
+ }
93
+ function normalizeKey(p) {
94
+ if (/^[A-Z]$/.test(p.name)) return {
95
+ ...p,
96
+ name: p.name.toLowerCase(),
97
+ shift: true
98
+ };
99
+ if (p.name === " ") return {
100
+ ...p,
101
+ name: "space"
102
+ };
103
+ return p;
104
+ }
105
+ function isPrintableKeyName(s) {
106
+ return SPECIAL_KEY_NAMES.has(s) || [...s].length === 1 && !/[\p{C}]/u.test(s);
107
+ }
108
+ function canUseShiftModifier(name) {
109
+ return /^[a-zA-Z]$/.test(name) || SPECIAL_KEY_NAMES.has(name);
110
+ }
111
+ function isKeyPattern(s) {
112
+ try {
113
+ canonical(s);
114
+ return true;
115
+ } catch {
116
+ return false;
117
+ }
118
+ }
119
+ /**
120
+ * Canonical string form of a pattern or event — modifiers in a fixed
121
+ * alphabetical order, then the key name, joined by `-`. Used by the
122
+ * router to build a pattern index whose keys match regardless of how
123
+ * the user spelled the modifier order (`"ctrl-shift-a"` and
124
+ * `"shift-ctrl-a"` both canonicalize to `"ctrl-shift-a"`).
125
+ */
126
+ function canonical(patternOrEvent) {
127
+ const p = typeof patternOrEvent === "string" ? parsePattern(patternOrEvent) : normalizeKey(patternOrEvent);
128
+ const parts = [];
129
+ if (p.alt) parts.push("alt");
130
+ if (p.ctrl) parts.push("ctrl");
131
+ if (p.meta) parts.push("meta");
132
+ if (p.shift) parts.push("shift");
133
+ parts.push(p.name);
134
+ return parts.join("-");
135
+ }
136
+ /**
137
+ * Test whether an event matches a pattern (or any of a list of patterns).
138
+ * Unspecified modifiers in the pattern must be *absent* on the event —
139
+ * matching is strict, not inclusive, so `"a"` only matches a bare `a`
140
+ * (no ctrl/alt/etc.).
141
+ */
142
+ function keyMatches(ev, pattern) {
143
+ if (Array.isArray(pattern)) {
144
+ for (const p of pattern) if (keyMatches(ev, p)) return true;
145
+ return false;
146
+ }
147
+ const p = parsePattern(pattern);
148
+ const e = normalizeKey(ev);
149
+ return e.name === p.name && e.ctrl === p.ctrl && e.alt === p.alt && e.shift === p.shift && e.meta === p.meta;
150
+ }
151
+ //#endregion
152
+ //#region src/input/queries.ts
153
+ var TerminalQueries = class {
154
+ #router;
155
+ #terminal;
156
+ constructor(router, terminal) {
157
+ this.#router = router;
158
+ this.#terminal = terminal;
159
+ }
160
+ query(query) {
161
+ const timeout = query.timeout ?? 200;
162
+ const ac = new AbortController();
163
+ return new Promise((resolve) => {
164
+ const finish = (value) => {
165
+ if (ac.signal.aborted) return;
166
+ ac.abort();
167
+ clearTimeout(timer);
168
+ resolve(value);
169
+ };
170
+ const timer = setTimeout(() => finish(), timeout);
171
+ timer.unref();
172
+ this.#router.on("term-response", ({ event }) => {
173
+ const value = query.match(event);
174
+ if (value !== void 0) finish(value);
175
+ }, { signal: ac.signal });
176
+ this.#terminal.write(query.wrap?.(query.request) ?? query.request);
177
+ });
178
+ }
179
+ primaryDeviceAttributes(opts = {}) {
180
+ return this.query({
181
+ match: (ev) => ev.kind === "csi" && ev.final === "c" && ev.params.startsWith("?") ? {
182
+ ...ev,
183
+ attrs: parseAttrs(ev.params)
184
+ } : void 0,
185
+ request: "\x1B[c",
186
+ ...opts
187
+ });
188
+ }
189
+ secondaryDeviceAttributes(opts = {}) {
190
+ return this.query({
191
+ match: (ev) => ev.kind === "csi" && ev.final === "c" && ev.params.startsWith(">") ? {
192
+ ...ev,
193
+ attrs: parseAttrs(ev.params)
194
+ } : void 0,
195
+ request: "\x1B[>c",
196
+ ...opts
197
+ });
198
+ }
199
+ xtVersion(opts = {}) {
200
+ return this.query({
201
+ match: (ev) => {
202
+ if (ev.kind !== "dcs" || !ev.payload.startsWith(">|")) return;
203
+ const payload = ev.payload.slice(2);
204
+ const m = payload.match(/^([^\s]+)\(([^\s]+)\)$/) ?? payload.match(/^([^\s]+)\s+([^\s]+)$/);
205
+ return {
206
+ ...ev,
207
+ name: m?.[1] ?? payload,
208
+ version: m?.[2]
209
+ };
210
+ },
211
+ request: "\x1B[>q",
212
+ ...opts
213
+ });
214
+ }
215
+ };
216
+ function parseAttrs(params) {
217
+ return params.slice(1).split(";").map((n) => Number.parseInt(n, 10)).filter(Number.isFinite);
218
+ }
219
+ //#endregion
220
+ export { KeyPatternError, Owner, RenderContext, SuspenseContext, TerminalQueries, canonical, codeToAnsi, createAsync, createContext, createCtx, createIterable, createNode, createProgressive, createRef, createRender, createRenderer, createRoot, createStore, createSuspenseBoundary, defineAction, effect, inRenderContextOf, inspect, inspectFormat, isAccessor, isKeyPattern, isMarkdown, keyMatches, memo, onCleanup, parseAttrs, peek, provideContext, signal, toAccessor, untrack, unwrap, useActiveOwner, useContext, withActiveNode, withOwner };
221
+
222
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":["#router","#terminal"],"sources":["../src/input/index.ts","../src/core/index.ts","../src/renderer/index.ts","../src/input/keys.ts","../src/input/queries.ts"],"sourcesContent":["import type { ArgsOpts } from \"@zaly/shared/args\"\nimport type { Action, ActionDef } from \"./actions.ts\"\n\nexport function defineAction<T extends ArgsOpts = ArgsOpts>(action: ActionDef<T>): ActionDef<T>\nexport function defineAction<T extends ArgsOpts = ArgsOpts>(action: Action<T>): Action<T>\nexport function defineAction<T extends ArgsOpts = ArgsOpts>(\n action: ActionDef<T> | Action<T>\n): ActionDef<T> | Action<T> {\n return action\n}\n","import type { Theme } from \"../themes/types.ts\"\nimport type { RenderCtx } from \"./ctx.ts\"\n\nexport type { RenderCtx, MountCtx } from \"./ctx.ts\"\n\nexport type * from \"./node.ts\"\nexport type * from \"./state.ts\"\n\nexport * from \"./reactive.ts\"\nexport * from \"./render.ts\"\n\nexport async function createCtx(\n opts: Partial<RenderCtx> & { theme?: Theme } = {}\n): Promise<RenderCtx> {\n const { createCtx: create } = await import(\"./ctx.ts\")\n return create(opts)\n}\n","import type { Renderer, RendererOptions } from \"./renderer.ts\"\n\nexport type * from \"./renderer.ts\"\nexport type { OverlaySurface } from \"./overlay.ts\"\nexport type { Stream } from \"./stream.ts\"\nexport type { Terminal } from \"./terminal.ts\"\nexport type { UI } from \"./ui.ts\"\n\n/** Factory shorthand for `new Renderer(opts)`. */\nexport async function createRenderer(opts: RendererOptions = {}): Promise<Renderer> {\n const { Renderer } = await import(\"./renderer.ts\")\n return new Renderer(opts)\n}\n","/**\n * Keyboard event shape + pattern-based matcher.\n *\n * `KeyEvent.name` is a canonical name:\n * - Single characters for printable keys: `\"a\"`, `\"A\"`, `\" \"`, `\"!\"`. The\n * `name` reflects the raw character the decoder produced — so `Shift+A`\n * arrives as `{ name: \"A\", shift: true }` (terminals already fold\n * shift into the uppercase byte for letters).\n * - Named constants for non-printing keys: `\"enter\"`, `\"tab\"`, `\"esc\"`,\n * `\"backspace\"`, `\"delete\"`, `\"space\"`, arrows `\"up\"/\"down\"/\"left\"/\n * \"right\"`, navigation `\"home\"/\"end\"/\"pageup\"/\"pagedown\"/\"insert\"`,\n * function keys `\"f1\"` .. `\"f12\"`.\n *\n * Patterns for `keyMatches` are dash-separated modifier prefixes plus the\n * name: `\"a\"`, `\"ctrl-c\"`, `\"shift-tab\"`, `\"ctrl-shift-x\"`. Unknown\n * prefixes are treated as literal characters in the name, so\n * `\"something-foo\"` is interpreted as the name `\"something-foo\"` rather\n * than a missing-modifier error.\n *\n * Modifier prefixes (order-independent):\n * - `ctrl-`\n * - `alt-`\n * - `shift-`\n * - `meta-` (a.k.a. super / cmd)\n */\n\nexport interface KeyEvent {\n /** Canonical key name — raw character or one of the named constants above. */\n name: string\n /**\n * The literal text the keystroke would insert, when that makes sense.\n * For printable chars this is the char itself; for Enter, Tab, etc. it's\n * undefined. Widgets use this to decide whether to insert content vs.\n * treat the key as a command.\n */\n text?: string\n ctrl: boolean\n alt: boolean\n shift: boolean\n meta: boolean\n}\n\n/** Canonical modifier names used in `KeyPattern`. */\nexport type KeyModifier = \"ctrl\" | \"alt\" | \"shift\" | \"meta\"\n\n/**\n * Named keys the decoder emits for non-printable keystrokes. Bindings\n * reference these directly (`\"enter\"`, `\"f5\"`, `\"pageup\"`, …).\n */\nexport type SpecialKeyName = (typeof specialKeys)[number]\n\ndeclare const KeyPatternBrand: unique symbol\n\n/** A validated, canonical key binding string. */\nexport type KeyPattern = string & { readonly [KeyPatternBrand]: true }\n\nconst MOD_NAMES = new Set<string>([\"ctrl\", \"alt\", \"shift\", \"meta\"])\nconst specialKeys = [\n \"enter\",\n \"tab\",\n \"backspace\",\n \"delete\",\n \"space\",\n \"esc\",\n \"up\",\n \"down\",\n \"left\",\n \"right\",\n \"home\",\n \"end\",\n \"pageup\",\n \"pagedown\",\n \"insert\",\n \"f1\",\n \"f2\",\n \"f3\",\n \"f4\",\n \"f5\",\n \"f6\",\n \"f7\",\n \"f8\",\n \"f9\",\n \"f10\",\n \"f11\",\n \"f12\",\n] as const\nconst SPECIAL_KEY_NAMES = new Set<string>(specialKeys)\n\ninterface ParsedPattern {\n name: string\n ctrl: boolean\n alt: boolean\n shift: boolean\n meta: boolean\n}\n\nexport class KeyPatternError extends Error {\n constructor(pattern: string, reason: string) {\n super(`invalid key pattern ${JSON.stringify(pattern)}: ${reason}`)\n this.name = \"KeyPatternError\"\n }\n}\n\nfunction parsePattern(pattern: string): ParsedPattern {\n const mods = { alt: false, ctrl: false, meta: false, shift: false }\n let name = pattern\n while (name.length > 0) {\n const dash = name.indexOf(\"-\")\n if (dash <= 0) break\n const mod = name.slice(0, dash)\n if (!MOD_NAMES.has(mod)) throw new KeyPatternError(pattern, `unknown modifier: ${mod}`)\n if (mods[mod as KeyModifier]) throw new KeyPatternError(pattern, `duplicate modifier: ${mod}`)\n mods[mod as KeyModifier] = true\n name = name.slice(dash + 1)\n }\n\n const ret = normalizeKey({ ...mods, name })\n name = ret.name\n if (name === \"\") throw new KeyPatternError(pattern, \"missing key name\")\n if (name.includes(\"-\") && name !== \"-\") throw new KeyPatternError(pattern, \"too many keys\")\n if (!isPrintableKeyName(name)) throw new KeyPatternError(pattern, `unknown key name: ${name}`)\n if (mods.shift && !canUseShiftModifier(name)) {\n throw new KeyPatternError(\n pattern,\n `shift-${name} will not trigger; bind the shifted character instead`\n )\n }\n return ret\n}\n\nfunction normalizeKey(p: ParsedPattern): ParsedPattern {\n if (/^[A-Z]$/.test(p.name)) return { ...p, name: p.name.toLowerCase(), shift: true }\n if (p.name === \" \") return { ...p, name: \"space\" }\n return p\n}\n\nfunction isPrintableKeyName(s: string): boolean {\n // oxlint-disable-next-line typescript/no-misused-spread\n return SPECIAL_KEY_NAMES.has(s) || ([...s].length === 1 && !/[\\p{C}]/u.test(s))\n}\n\nfunction canUseShiftModifier(name: string): boolean {\n return /^[a-zA-Z]$/.test(name) || SPECIAL_KEY_NAMES.has(name)\n}\n\nexport function isKeyPattern(s: string): s is KeyPattern {\n try {\n canonical(s)\n return true\n } catch {\n return false\n }\n}\n\n/**\n * Canonical string form of a pattern or event — modifiers in a fixed\n * alphabetical order, then the key name, joined by `-`. Used by the\n * router to build a pattern index whose keys match regardless of how\n * the user spelled the modifier order (`\"ctrl-shift-a\"` and\n * `\"shift-ctrl-a\"` both canonicalize to `\"ctrl-shift-a\"`).\n */\nexport function canonical(patternOrEvent: string | KeyEvent): KeyPattern {\n const p =\n typeof patternOrEvent === \"string\" ? parsePattern(patternOrEvent) : normalizeKey(patternOrEvent)\n const parts: string[] = []\n if (p.alt) parts.push(\"alt\")\n if (p.ctrl) parts.push(\"ctrl\")\n if (p.meta) parts.push(\"meta\")\n if (p.shift) parts.push(\"shift\")\n parts.push(p.name)\n return parts.join(\"-\") as KeyPattern\n}\n\n/**\n * Test whether an event matches a pattern (or any of a list of patterns).\n * Unspecified modifiers in the pattern must be *absent* on the event —\n * matching is strict, not inclusive, so `\"a\"` only matches a bare `a`\n * (no ctrl/alt/etc.).\n */\nexport function keyMatches(ev: KeyEvent, pattern: string | readonly string[]): boolean {\n if (Array.isArray(pattern)) {\n for (const p of pattern) if (keyMatches(ev, p)) return true\n return false\n }\n const p = parsePattern(pattern as string)\n const e = normalizeKey(ev)\n return (\n e.name === p.name &&\n e.ctrl === p.ctrl &&\n e.alt === p.alt &&\n e.shift === p.shift &&\n e.meta === p.meta\n )\n}\n","import type { TerminalResponseEvent } from \"./decoder.ts\"\nimport type { InputRouter } from \"./router.ts\"\n\nexport type TerminalQuery<T = TerminalResponseEvent> = TerminalQueryOpts & {\n request: string\n match: (event: TerminalResponseEvent) => T | undefined\n}\n\nexport type TerminalQueryOpts = {\n timeout?: number\n wrap?: (seq: string) => string\n}\n\ntype TerminalQueryWriter = {\n write: (seq: string) => void\n}\n\nexport class TerminalQueries {\n readonly #router: InputRouter\n readonly #terminal: TerminalQueryWriter\n\n constructor(router: InputRouter, terminal: TerminalQueryWriter) {\n this.#router = router\n this.#terminal = terminal\n }\n\n query<T>(query: TerminalQuery<T>): Promise<T | undefined> {\n const timeout = query.timeout ?? 200\n const ac = new AbortController()\n return new Promise((resolve) => {\n const finish = (value?: T) => {\n if (ac.signal.aborted) return\n ac.abort()\n clearTimeout(timer)\n resolve(value)\n }\n const timer = setTimeout(() => finish(), timeout)\n timer.unref()\n this.#router.on(\n \"term-response\",\n ({ event }) => {\n const value = query.match(event)\n if (value !== undefined) finish(value)\n },\n { signal: ac.signal }\n )\n this.#terminal.write(query.wrap?.(query.request) ?? query.request)\n })\n }\n\n primaryDeviceAttributes(opts: TerminalQueryOpts = {}) {\n return this.query<TerminalResponseEvent & { attrs: number[] }>({\n match: (ev) =>\n ev.kind === \"csi\" && ev.final === \"c\" && ev.params.startsWith(\"?\")\n ? { ...ev, attrs: parseAttrs(ev.params) }\n : undefined,\n request: \"\\x1b[c\",\n ...opts,\n })\n }\n\n secondaryDeviceAttributes(opts: TerminalQueryOpts = {}) {\n return this.query<TerminalResponseEvent & { attrs: number[] }>({\n match: (ev) =>\n ev.kind === \"csi\" && ev.final === \"c\" && ev.params.startsWith(\">\")\n ? { ...ev, attrs: parseAttrs(ev.params) }\n : undefined,\n request: \"\\x1b[>c\",\n ...opts,\n })\n }\n\n xtVersion(opts: TerminalQueryOpts = {}) {\n return this.query<TerminalResponseEvent & { name: string; version?: string }>({\n match: (ev) => {\n if (ev.kind !== \"dcs\" || !ev.payload.startsWith(\">|\")) return\n const payload = ev.payload.slice(2)\n const m = payload.match(/^([^\\s]+)\\(([^\\s]+)\\)$/) ?? payload.match(/^([^\\s]+)\\s+([^\\s]+)$/)\n return { ...ev, name: m?.[1] ?? payload, version: m?.[2] }\n },\n request: \"\\x1b[>q\",\n ...opts,\n })\n }\n}\n\nexport function parseAttrs(params: string): number[] {\n return params\n .slice(1)\n .split(\";\")\n .map((n) => Number.parseInt(n, 10))\n .filter(Number.isFinite)\n}\n"],"mappings":"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"}