@zaly/tui 0.1.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/dist/{box-DToDoXlU.mjs → box-DbIjxh39.mjs} +2 -2
  2. package/dist/{box-DToDoXlU.mjs.map → box-DbIjxh39.mjs.map} +1 -1
  3. package/dist/{callbacks-DW8XKYKy.mjs → callbacks-UaBfDStQ.mjs} +2 -2
  4. package/dist/{callbacks-DW8XKYKy.mjs.map → callbacks-UaBfDStQ.mjs.map} +1 -1
  5. package/dist/{divider-qEj5prKE.mjs → divider-BPmnQ0Eu.mjs} +2 -2
  6. package/dist/{divider-qEj5prKE.mjs.map → divider-BPmnQ0Eu.mjs.map} +1 -1
  7. package/dist/index.mjs +1 -1
  8. package/dist/{input-BkE4EpBw.mjs → input-CtGmVrKa.mjs} +2 -2
  9. package/dist/{input-BkE4EpBw.mjs.map → input-CtGmVrKa.mjs.map} +1 -1
  10. package/dist/{log-96b1_J6G.mjs → log-D6gGicwQ.mjs} +4 -4
  11. package/dist/{log-96b1_J6G.mjs.map → log-D6gGicwQ.mjs.map} +1 -1
  12. package/dist/{logger-DwfXp8LF.mjs → logger-C0WuIlAh.mjs} +2 -2
  13. package/dist/{logger-DwfXp8LF.mjs.map → logger-C0WuIlAh.mjs.map} +1 -1
  14. package/dist/{markdown-CgRZBhIX.mjs → markdown-DvDIPT5L.mjs} +4 -4
  15. package/dist/{markdown-CgRZBhIX.mjs.map → markdown-DvDIPT5L.mjs.map} +1 -1
  16. package/dist/markdown.mjs +1 -1
  17. package/dist/{overlay-CevDWgSF.mjs → overlay-zDNpwKu0.mjs} +2 -2
  18. package/dist/{overlay-CevDWgSF.mjs.map → overlay-zDNpwKu0.mjs.map} +1 -1
  19. package/dist/{picker-D9WrE3zF.mjs → picker-DV_oxKEv.mjs} +2 -2
  20. package/dist/{picker-D9WrE3zF.mjs.map → picker-DV_oxKEv.mjs.map} +1 -1
  21. package/dist/renderer-Dv5fIuIk.mjs +1 -0
  22. package/dist/{renderer-BYl2pbdx.mjs → renderer-Ksfkfv5X.mjs} +2 -2
  23. package/dist/{renderer-BYl2pbdx.mjs.map → renderer-Ksfkfv5X.mjs.map} +1 -1
  24. package/dist/{renderer-B9sAMA10.mjs → renderer-OplFCBMq.mjs} +3 -3
  25. package/dist/{renderer-B9sAMA10.mjs.map → renderer-OplFCBMq.mjs.map} +1 -1
  26. package/dist/services/logger.mjs +1 -1
  27. package/dist/services/notifier.mjs +2 -2
  28. package/dist/services/picker.mjs +5 -5
  29. package/dist/services/prompt.mjs +4 -4
  30. package/dist/{text-4arC3odI.mjs → text-BEuhhxr7.mjs} +2 -2
  31. package/dist/text-BEuhhxr7.mjs.map +1 -0
  32. package/dist/{text-BArwVVkg.mjs → text-H_2kzwaz.mjs} +2 -2
  33. package/dist/{text-BArwVVkg.mjs.map → text-H_2kzwaz.mjs.map} +1 -1
  34. package/dist/text.mjs +1 -1
  35. package/dist/widgets/autocomplete.mjs +2 -2
  36. package/dist/widgets/box.mjs +1 -1
  37. package/dist/widgets/code.mjs +3 -3
  38. package/dist/widgets/diff.d.mts.map +1 -1
  39. package/dist/widgets/diff.mjs +3 -3
  40. package/dist/widgets/diff.mjs.map +1 -1
  41. package/dist/widgets/divider.mjs +1 -1
  42. package/dist/widgets/input.mjs +1 -1
  43. package/dist/widgets/log.mjs +1 -1
  44. package/dist/widgets/markdown.mjs +1 -1
  45. package/dist/widgets/overlay.mjs +1 -1
  46. package/dist/widgets/picker.mjs +1 -1
  47. package/dist/widgets/text.mjs +1 -1
  48. package/package.json +2 -2
  49. package/dist/renderer-Ctz0hv5o.mjs +0 -1
  50. package/dist/text-4arC3odI.mjs.map +0 -1
@@ -2,7 +2,7 @@ import { S as unwrap } from "./reactive-DkP75TPk.mjs";
2
2
  import { t as Node } from "./node-CXNobGaP.mjs";
3
3
  import { n as resolveSize, t as clamp } from "./size-BHRJuRk9.mjs";
4
4
  import { a as zipRow, i as stackColumn, n as isFixedWidth, r as padRow, t as allocateRow } from "./flex-CYp4etqn.mjs";
5
- import { r as textContent } from "./text-BArwVVkg.mjs";
5
+ import { r as textContent } from "./text-H_2kzwaz.mjs";
6
6
  import { sliceAnsi, stringWidth } from "@zaly/shared/ansi";
7
7
  // src/layout/border.ts
8
8
  /** @internal */
@@ -388,4 +388,4 @@ function resolvePadding(p) {
388
388
  }
389
389
  export { box as n, Box as t };
390
390
 
391
- //# sourceMappingURL=box-DToDoXlU.mjs.map
391
+ //# sourceMappingURL=box-DbIjxh39.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"box-DToDoXlU.mjs","names":[],"sources":["../src/layout/border.ts","../src/widgets/box.ts"],"sourcesContent":["import type { StyleBuilder } from \"../style/builder.ts\"\n\nimport { sliceAnsi, stringWidth } from \"@zaly/shared/ansi\"\n\n/**\n * Border-character glyphs for drawing a box outline. Any single-cell string\n * is allowed (including multi-byte glyphs like rounded corners).\n */\nexport interface BorderChars {\n h: string\n v: string\n tl: string\n tr: string\n bl: string\n br: string\n}\n\nexport type BorderSpec = boolean | \"single\" | \"double\" | \"rounded\" | BorderChars\n\n/** @internal */\nexport const borders = {\n double: { bl: \"╚\", br: \"╝\", h: \"═\", tl: \"╔\", tr: \"╗\", v: \"║\" },\n rounded: { bl: \"╰\", br: \"╯\", h: \"─\", tl: \"╭\", tr: \"╮\", v: \"│\" },\n single: { bl: \"└\", br: \"┘\", h: \"─\", tl: \"┌\", tr: \"┐\", v: \"│\" },\n} as const satisfies Record<string, BorderChars>\n\n/** Resolve the `border` style value to a concrete BorderChars, or undefined.\n *\n * @internal*/\nexport function resolveBorder(spec: BorderSpec | undefined): BorderChars | undefined {\n if (spec === undefined || spec === false) return undefined\n if (spec === true) return borders.single\n if (typeof spec === \"string\") return borders[spec]\n return spec\n}\n\nexport type TitleAlign = \"left\" | \"center\" | \"right\"\n\nexport interface DrawBorderOpts {\n /**\n * Optional title embedded in the top border, with a single-cell space on\n * each side. Ellipsis-truncated when it overflows the budget.\n */\n title?: string\n /** Placement of `title` along the top border. Defaults to `\"left\"`. */\n titleAlign?: TitleAlign\n /**\n * Styled wrapper applied to the border glyphs (corners, horizontal,\n * vertical). Use `ctx.style.add(slotOrStyle)` at the call site so the\n * theme is resolved once per render.\n */\n borderStyle?: StyleBuilder\n /** Styled wrapper applied to the title text (including the padding spaces). */\n titleStyle?: StyleBuilder\n}\n\n/** Pass-through wrapper used when no style is supplied. */\nconst identity = (s: string): string => s\n\n/**\n * Wrap pre-rendered inner rows with a border, adding top/bottom border rows\n * and left/right border chars to each inner row. Inner rows must be padded\n * to a uniform width; the output rows are `innerWidth + 2` wide.\n *\n * Border glyphs and the optional title can be styled independently via\n * `borderStyle` / `titleStyle` — each is a pre-bound `StyleBuilder`, so\n * this helper stays purely about geometry and never touches SGR itself.\n *\n * @internal\n */\nexport function drawBorder(\n rows: readonly string[],\n chars: BorderChars,\n opts: DrawBorderOpts = {}\n): string[] {\n const { title, titleAlign } = opts\n const inner = rows.length > 0 ? stringWidth(rows[0]) : 0\n const wrapB = opts.borderStyle ?? identity\n const wrapT = opts.titleStyle ?? identity\n\n const out: string[] = []\n out.push(topRow({ align: titleAlign ?? \"left\", chars, inner, title, wrapB, wrapT }))\n const v = wrapB(chars.v)\n for (const row of rows) out.push(v + row + v)\n out.push(wrapB(chars.bl + chars.h.repeat(inner) + chars.br))\n return out\n}\n\ninterface TopRowArgs {\n align: TitleAlign\n chars: BorderChars\n inner: number\n title: string | undefined\n wrapB: (s: string) => string\n wrapT: (s: string) => string\n}\n\nfunction topRow({ align, chars, inner, title, wrapB, wrapT }: TopRowArgs): string {\n if (title === undefined || title === \"\") {\n return wrapB(chars.tl + chars.h.repeat(inner) + chars.tr)\n }\n // Chrome: h + space + title + space + h (4 cells of chrome around the title)\n const budget = inner - 4\n if (budget <= 0) return wrapB(chars.tl + chars.h.repeat(inner) + chars.tr)\n const shown = truncate(title, budget)\n const totalH = inner - 2 - stringWidth(shown)\n const { leading, trailing } = distributeH(totalH, align)\n const prefix = wrapB(`${chars.tl}${chars.h.repeat(leading)}`)\n const titleSeg = wrapT(` ${shown} `)\n const suffix = wrapB(`${chars.h.repeat(trailing)}${chars.tr}`)\n return prefix + titleSeg + suffix\n}\n\nfunction distributeH(totalH: number, align: TitleAlign): { leading: number; trailing: number } {\n if (align === \"left\") return { leading: 1, trailing: totalH - 1 }\n if (align === \"right\") return { leading: totalH - 1, trailing: 1 }\n // center: leading rounds down so odd slack biases the title slightly left.\n const leading = Math.floor(totalH / 2)\n return { leading, trailing: totalH - leading }\n}\n\nfunction truncate(s: string, width: number): string {\n if (stringWidth(s) <= width) return s\n if (width <= 1) return \"…\".repeat(width)\n return `${sliceAnsi(s, 0, width - 1)}…`\n}\n","import type { RenderCtx } from \"../core/ctx.ts\"\nimport type { State } from \"../core/state.ts\"\nimport type { BorderSpec, TitleAlign } from \"../layout/border.ts\"\nimport type { RowItem } from \"../layout/flex.ts\"\nimport type { AnyStyle, Style } from \"../style/types.ts\"\nimport type { TextContent } from \"./text.ts\"\n\nimport { stringWidth } from \"@zaly/shared/ansi\"\nimport { Node } from \"../core/node.ts\"\nimport { unwrap } from \"../core/reactive.ts\"\nimport { drawBorder, resolveBorder } from \"../layout/border.ts\"\nimport { allocateRow, isFixedWidth, padRow, stackColumn, zipRow } from \"../layout/flex.ts\"\nimport { clamp, resolveSize } from \"../layout/size.ts\"\nimport { textContent } from \"./text.ts\"\n\nexport type Padding =\n | number\n | readonly [v: number, h: number]\n | readonly [t: number, r: number, b: number, l: number]\n\nexport interface BoxStyle extends Style {\n flexDirection?: \"row\" | \"column\"\n gap?: number\n /** Fixed outer height in rows. When unset, the box's height emerges\n * naturally from its content + padding + border. When set, the\n * content area is truncated or padded with blank rows to land at\n * exactly this many rows of *outer* height. Pair with\n * `verticalAlign` to control which edge is anchored. */\n height?: number\n /** Anchor for the content when `height` truncates or extends.\n * `\"top\"` (default) keeps the top edge: extra rows added at the\n * bottom, overflow truncated from the bottom. `\"bottom\"` keeps the\n * bottom edge: extra rows prepended, overflow truncated from the\n * top. Other CSS-like values (`middle`) are not implemented. */\n verticalAlign?: \"top\" | \"bottom\"\n padding?: Padding\n border?: BorderSpec\n borderTitle?: TextContent\n /** Placement of `borderTitle` along the top border. Defaults to `\"left\"`. */\n borderTitleAlign?: TitleAlign\n /**\n * Style applied to the border glyphs. Either a theme slot name (resolved\n * via the active theme) or an inline `Style`. Defaults to the `\"border\"`\n * theme slot when a border is drawn.\n */\n borderStyle?: AnyStyle\n /**\n * Style applied to the border title. Defaults to the `\"borderTitle\"` theme\n * slot when a title is set.\n */\n borderTitleStyle?: AnyStyle\n}\n\nexport class Box<T extends object = {}> extends Node<BoxStyle & T> {\n protected async _render(ctx: RenderCtx): Promise<string[]> {\n const style = this.state\n\n const [padT, padR, padB, padL] = resolvePadding(style.padding)\n const bchars = resolveBorder(style.border)\n const hasBorder = bchars !== undefined\n const chrome = padL + padR + (hasBorder ? 2 : 0)\n\n let outer: number\n let contentRows: string[]\n let inner: number\n\n if (style.width === \"fit\") {\n // `fit`: render children at the parent's max inner width, measure\n // the natural max-row width, then clamp the box to that. Two-pass\n // — the first pass uses the upper bound as a measuring window,\n // the second re-pads each row to the chosen inner width.\n const upper = Math.max(0, ctx.width - chrome)\n const tentative = await this.#layoutChildren(upper, ctx)\n const natural = tentative.reduce((m, r) => Math.max(m, stringWidth(r)), 0)\n const fit = Math.min(ctx.width, natural + chrome)\n outer = clamp(fit, {\n available: ctx.width,\n max: style.maxWidth,\n min: style.minWidth,\n })\n inner = Math.max(0, outer - chrome)\n contentRows = tentative\n } else {\n const requested = resolveSize(style.width ?? \"fill\", ctx.width) ?? ctx.width\n outer = clamp(requested, {\n available: ctx.width,\n max: style.maxWidth,\n min: style.minWidth,\n })\n inner = Math.max(0, outer - chrome)\n contentRows = await this.#layoutChildren(inner, ctx)\n }\n\n const paddedWidth = Math.max(0, outer - (hasBorder ? 2 : 0))\n\n // Ensure each content row is exactly `inner` cells wide (absorbs slack).\n contentRows = contentRows.map((row) => padRow(row, inner))\n\n // Horizontal padding.\n if (padL > 0 || padR > 0) {\n const lp = \" \".repeat(padL)\n const rp = \" \".repeat(padR)\n contentRows = contentRows.map((row) => lp + row + rp)\n }\n\n // Vertical padding.\n const blank = \" \".repeat(paddedWidth)\n const top = Array.from({ length: padT }, () => blank)\n const bot = Array.from({ length: padB }, () => blank)\n let rows = [...top, ...contentRows, ...bot]\n\n // Fixed-height fit. The `height` field is the outer dimension —\n // border takes its 2 rows out before we measure against the\n // content area; padding is already baked into `rows`. `verticalAlign`\n // decides which end gets clipped (when too tall) or padded (when\n // too short). Default `\"top\"` matches the natural reading flow.\n if (style.height !== undefined) {\n const innerH = Math.max(0, style.height - (hasBorder ? 2 : 0))\n if (rows.length > innerH) {\n rows = style.verticalAlign === \"bottom\" ? rows.slice(-innerH) : rows.slice(0, innerH)\n } else if (rows.length < innerH) {\n const filler = Array.from({ length: innerH - rows.length }, () => blank)\n rows = style.verticalAlign === \"bottom\" ? [...filler, ...rows] : [...rows, ...filler]\n }\n }\n\n if (bchars) {\n rows = drawBorder(rows, bchars, {\n borderStyle: ctx.style.add(style.borderStyle ?? \"border\"),\n title: await textContent(style.borderTitle, ctx),\n titleAlign: style.borderTitleAlign,\n titleStyle: ctx.style.add(style.borderTitleStyle ?? \"borderTitle\"),\n })\n }\n\n const wrap = ctx.style.add(style)\n return rows.map((row) => wrap(row))\n }\n\n /**\n * Intrinsic content width derived from children (or the explicit\n * `width` when fixed). Lets parent flex containers size this Box\n * without falling back to a measure-render — important for\n * side-effecting children like `Image` whose first render allocates\n * a KGP placement: rendering twice would emit two placements and the\n * second render's `transmitOnce` returns no bytes, so the bytes from\n * the discarded measure pass would be lost.\n *\n * Row direction → bases sum (plus gaps). Column direction → bases\n * max. Both add chrome (padding + border) at the end.\n *\n * Children without `layout()` contribute 0 — the row allocator's\n * render-measure fallback still kicks in for them at allocation\n * time, but their absence here just means this Box reports a\n * smaller intrinsic. That's the safe direction: under-report and\n * let shrink/grow handle slack, never over-report.\n */\n override async layout(ctx: RenderCtx) {\n const s = this.state\n const [, padR, , padL] = resolvePadding(s.padding)\n const chrome = padL + padR + (resolveBorder(s.border) ? 2 : 0)\n\n // Fixed width wins outright.\n if (isFixedWidth(s.width)) {\n const w = resolveSize(s.width, ctx.width) ?? 0\n return { minWidth: w, width: w }\n }\n\n const kids = layoutNodes(this.children)\n if (kids.length === 0) return { minWidth: chrome, width: chrome }\n\n const isRow = (s.flexDirection ?? \"column\") === \"row\"\n const totalGap = isRow ? (s.gap ?? 0) * Math.max(0, kids.length - 1) : 0\n\n let widthAcc = 0\n let minAcc = 0\n for (const k of kids) {\n // oxlint-disable-next-line no-await-in-loop\n const l = (await k.getLayout(ctx)) ?? { minWidth: 0, width: 0 }\n if (isRow) {\n widthAcc += l.width\n minAcc += l.minWidth\n } else {\n widthAcc = Math.max(widthAcc, l.width)\n minAcc = Math.max(minAcc, l.minWidth)\n }\n }\n\n return { minWidth: minAcc + totalGap + chrome, width: widthAcc + totalGap + chrome }\n }\n\n async #layoutChildren(innerWidth: number, ctx: RenderCtx): Promise<string[]> {\n const children = layoutNodes(this.children)\n if (children.length === 0) return []\n const gap = this.state.gap ?? 0\n return (this.state.flexDirection ?? \"column\") === \"row\"\n ? this.#layoutRow(children, innerWidth, ctx, gap)\n : this.#layoutColumn(children, innerWidth, ctx, gap)\n }\n\n /**\n * Row direction — main axis is horizontal. CSS-style flex:\n * each child sizes to its intrinsic content width unless it claims\n * slack via `flexGrow > 0` or `width: \"fill\"`. Slack distributes by\n * grow weights; deficit distributes by `basis * flexShrink` clamped\n * at each child's `min-content` floor.\n *\n * Sizing strategy per child:\n * 1. `node.layout(ctx)` — sync, content-derived. If implemented,\n * use directly; no render needed for measurement.\n * 2. Otherwise fall back to render-at-innerWidth and measure the\n * widest emitted row. Cached; the paint pass reuses the result\n * whenever the allocated width matches the measurement width.\n *\n * In `fit` mode the parent has no slack to give, so the allocator's\n * available is capped at the natural sum.\n */\n async #layoutRow(\n children: readonly Node[],\n innerWidth: number,\n ctx: RenderCtx,\n gap: number\n ): Promise<string[]> {\n // Phase 1: figure out each child's intrinsic size.\n // - `layout()` short-circuits — sync, no render.\n // - Fallback: render at the upper bound (innerWidth or fixed spec)\n // and measure rows.\n const intrinsics = await Promise.all(children.map((c) => c.getLayout(ctx)))\n // For children with intrinsic sizing, skip the measure render\n // entirely. Others fall back to render-at-upper-bound (their fixed\n // width, or innerWidth if fluid).\n const measureWidths = children.map((c, i) => {\n if (intrinsics[i] !== undefined) return -1\n if (isFixedWidth(c.state.width)) {\n return resolveSize(c.state.width, innerWidth) ?? innerWidth\n }\n return innerWidth\n })\n const measureRows = await Promise.all(\n children.map((c, i): Promise<string[] | undefined> => {\n if (measureWidths[i] === -1) return Promise.resolve(undefined)\n return c.render({ ...ctx, width: measureWidths[i] })\n })\n )\n const naturals = children.map((c, i) => {\n // A fixed `state.width` is what the allocator will actually\n // assign this child — use it directly so the fit-mode\n // `allocAvailable` sum below matches what we'll consume. Falling\n // back to intrinsic when there's no override under-reports a\n // child like `text(\"x\", { width: 2 })` and shrinks every sibling\n // by the missing cells.\n if (isFixedWidth(c.state.width)) {\n return resolveSize(c.state.width, innerWidth) ?? 0\n }\n const intr = intrinsics[i]\n if (intr) return intr.width\n return (measureRows[i] ?? []).reduce((m, r) => Math.max(m, stringWidth(r)), 0)\n })\n const items: RowItem[] = children.map((c, i) => {\n const s = c.state\n return {\n flexGrow: s.flexGrow,\n flexShrink: s.flexShrink,\n intrinsicMin: intrinsics[i]?.minWidth,\n maxWidth: s.maxWidth,\n minWidth: s.minWidth,\n natural: naturals[i],\n width: s.width,\n }\n })\n const allocAvailable =\n this.state.width === \"fit\"\n ? naturals.reduce((a, b) => a + b, 0) + gap * Math.max(0, children.length - 1)\n : innerWidth\n const widths = allocateRow(items, { contentWidth: allocAvailable, gap })\n // Phase 2: paint at allocated widths. Reuse the measure-render when\n // the allocation matches; otherwise render fresh (cached).\n const childRows = await Promise.all(\n children.map((c, i): Promise<string[]> => {\n const cached = measureRows[i]\n if (cached !== undefined && widths[i] === measureWidths[i]) {\n return Promise.resolve(cached)\n }\n return c.render({ ...ctx, width: widths[i] })\n })\n )\n // `zipRow` expects rows to be exactly `widths[i]` wide. Children\n // that emit natural-width rows (text default, code default) get\n // padded here so the contract holds; over-wide rows get clipped.\n const padded = childRows.map((rows, i) => rows.map((row) => padRow(row, widths[i])))\n return zipRow(padded, { gap, widths })\n }\n\n /**\n * Column direction — main axis is vertical, cross axis horizontal.\n * Per CSS `align-items: stretch` (default), children with no\n * explicit `width` fill the cross axis. A child with a fixed `width`\n * (number / `Pct`) takes that width as its cross-axis size, with the\n * leftover padded as right slack (default `flex-start`). `min`/`max`\n * clamp the resolved width.\n *\n * In fit-mode (`width: \"fit\"`), the cross-axis target shrinks to the\n * widest child row instead of stretching to `innerWidth` — symmetric\n * to how row-direction caps allocator-available at the natural sum.\n */\n async #layoutColumn(\n children: readonly Node[],\n innerWidth: number,\n ctx: RenderCtx,\n gap: number\n ): Promise<string[]> {\n const widths = children.map((c) => {\n const s = c.state\n const fixed = isFixedWidth(s.width) ? resolveSize(s.width, innerWidth) : undefined\n const w = fixed ?? innerWidth\n return clamp(w, { available: innerWidth, max: s.maxWidth, min: s.minWidth })\n })\n const childRows = await Promise.all(\n children.map((c, i) => c.render({ ...ctx, width: widths[i] }))\n )\n const target =\n this.state.width === \"fit\"\n ? childRows.reduce((m, rows) => rows.reduce((mm, r) => Math.max(mm, stringWidth(r)), m), 0)\n : innerWidth\n // Pad each child's rows to the cross-axis target. For full-stretch\n // children this absorbs natural-row slack; for fixed-width\n // children it adds right-side slack (default `flex-start`\n // alignment). `stackColumn`'s contract requires uniform width.\n const padded = childRows.map((rows) => rows.map((row) => padRow(row, target)))\n return stackColumn(padded, { gap, width: target })\n }\n}\n\ntype Child = Node | false | null | undefined\n\n/**\n * Factory for `Box`. The style object is required (use `{}` if you don't\n * need any styling) — the previous style-less overload caused TS to fail\n * to discriminate between a Node child and an inferred-empty-state node\n * like `WidgetNode`. Pass `box({}, child1, child2)` for un-styled boxes.\n *\n * Falsy children (`false`, `null`, `undefined`) are filtered out, enabling\n * conditional JSX-like composition: `box({}, cond && child)`.\n */\nexport function box(style: State<BoxStyle>, ...children: Child[]): Box {\n const b = new Box(style)\n for (const c of children) if (c) b.add(c)\n return b\n}\n\n/**\n * Flatten fragment nodes (those that implement the `layoutChildren`\n * protocol — `show`, `errorBoundary`, `suspense`) into their currently\n * active leaves. Recursive: a fragment whose `layoutChildren()`\n * itself contains fragments unfolds completely.\n *\n * The fragment Node still lives in the tree (mounted, propagating\n * invalidates) — layout just sees through it so its children share\n * the parent box's flex distribution, gap, and cross-axis sizing\n * instead of collapsing into a single slot.\n */\nfunction layoutNodes(children: readonly Node[]): readonly Node[] {\n return children.flatMap((c) => c.layoutNodes).filter((c) => unwrap(c.state.visible) ?? true)\n}\n\nfunction resolvePadding(p: Padding | undefined): [t: number, r: number, b: number, l: number] {\n if (p === undefined) return [0, 0, 0, 0]\n if (typeof p === \"number\") return [p, p, p, p]\n if (p.length === 2) return [p[0], p[1], p[0], p[1]]\n return [p[0], p[1], p[2], p[3]]\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"box-DbIjxh39.mjs","names":[],"sources":["../src/layout/border.ts","../src/widgets/box.ts"],"sourcesContent":["import type { StyleBuilder } from \"../style/builder.ts\"\n\nimport { sliceAnsi, stringWidth } from \"@zaly/shared/ansi\"\n\n/**\n * Border-character glyphs for drawing a box outline. Any single-cell string\n * is allowed (including multi-byte glyphs like rounded corners).\n */\nexport interface BorderChars {\n h: string\n v: string\n tl: string\n tr: string\n bl: string\n br: string\n}\n\nexport type BorderSpec = boolean | \"single\" | \"double\" | \"rounded\" | BorderChars\n\n/** @internal */\nexport const borders = {\n double: { bl: \"╚\", br: \"╝\", h: \"═\", tl: \"╔\", tr: \"╗\", v: \"║\" },\n rounded: { bl: \"╰\", br: \"╯\", h: \"─\", tl: \"╭\", tr: \"╮\", v: \"│\" },\n single: { bl: \"└\", br: \"┘\", h: \"─\", tl: \"┌\", tr: \"┐\", v: \"│\" },\n} as const satisfies Record<string, BorderChars>\n\n/** Resolve the `border` style value to a concrete BorderChars, or undefined.\n *\n * @internal*/\nexport function resolveBorder(spec: BorderSpec | undefined): BorderChars | undefined {\n if (spec === undefined || spec === false) return undefined\n if (spec === true) return borders.single\n if (typeof spec === \"string\") return borders[spec]\n return spec\n}\n\nexport type TitleAlign = \"left\" | \"center\" | \"right\"\n\nexport interface DrawBorderOpts {\n /**\n * Optional title embedded in the top border, with a single-cell space on\n * each side. Ellipsis-truncated when it overflows the budget.\n */\n title?: string\n /** Placement of `title` along the top border. Defaults to `\"left\"`. */\n titleAlign?: TitleAlign\n /**\n * Styled wrapper applied to the border glyphs (corners, horizontal,\n * vertical). Use `ctx.style.add(slotOrStyle)` at the call site so the\n * theme is resolved once per render.\n */\n borderStyle?: StyleBuilder\n /** Styled wrapper applied to the title text (including the padding spaces). */\n titleStyle?: StyleBuilder\n}\n\n/** Pass-through wrapper used when no style is supplied. */\nconst identity = (s: string): string => s\n\n/**\n * Wrap pre-rendered inner rows with a border, adding top/bottom border rows\n * and left/right border chars to each inner row. Inner rows must be padded\n * to a uniform width; the output rows are `innerWidth + 2` wide.\n *\n * Border glyphs and the optional title can be styled independently via\n * `borderStyle` / `titleStyle` — each is a pre-bound `StyleBuilder`, so\n * this helper stays purely about geometry and never touches SGR itself.\n *\n * @internal\n */\nexport function drawBorder(\n rows: readonly string[],\n chars: BorderChars,\n opts: DrawBorderOpts = {}\n): string[] {\n const { title, titleAlign } = opts\n const inner = rows.length > 0 ? stringWidth(rows[0]) : 0\n const wrapB = opts.borderStyle ?? identity\n const wrapT = opts.titleStyle ?? identity\n\n const out: string[] = []\n out.push(topRow({ align: titleAlign ?? \"left\", chars, inner, title, wrapB, wrapT }))\n const v = wrapB(chars.v)\n for (const row of rows) out.push(v + row + v)\n out.push(wrapB(chars.bl + chars.h.repeat(inner) + chars.br))\n return out\n}\n\ninterface TopRowArgs {\n align: TitleAlign\n chars: BorderChars\n inner: number\n title: string | undefined\n wrapB: (s: string) => string\n wrapT: (s: string) => string\n}\n\nfunction topRow({ align, chars, inner, title, wrapB, wrapT }: TopRowArgs): string {\n if (title === undefined || title === \"\") {\n return wrapB(chars.tl + chars.h.repeat(inner) + chars.tr)\n }\n // Chrome: h + space + title + space + h (4 cells of chrome around the title)\n const budget = inner - 4\n if (budget <= 0) return wrapB(chars.tl + chars.h.repeat(inner) + chars.tr)\n const shown = truncate(title, budget)\n const totalH = inner - 2 - stringWidth(shown)\n const { leading, trailing } = distributeH(totalH, align)\n const prefix = wrapB(`${chars.tl}${chars.h.repeat(leading)}`)\n const titleSeg = wrapT(` ${shown} `)\n const suffix = wrapB(`${chars.h.repeat(trailing)}${chars.tr}`)\n return prefix + titleSeg + suffix\n}\n\nfunction distributeH(totalH: number, align: TitleAlign): { leading: number; trailing: number } {\n if (align === \"left\") return { leading: 1, trailing: totalH - 1 }\n if (align === \"right\") return { leading: totalH - 1, trailing: 1 }\n // center: leading rounds down so odd slack biases the title slightly left.\n const leading = Math.floor(totalH / 2)\n return { leading, trailing: totalH - leading }\n}\n\nfunction truncate(s: string, width: number): string {\n if (stringWidth(s) <= width) return s\n if (width <= 1) return \"…\".repeat(width)\n return `${sliceAnsi(s, 0, width - 1)}…`\n}\n","import type { RenderCtx } from \"../core/ctx.ts\"\nimport type { State } from \"../core/state.ts\"\nimport type { BorderSpec, TitleAlign } from \"../layout/border.ts\"\nimport type { RowItem } from \"../layout/flex.ts\"\nimport type { AnyStyle, Style } from \"../style/types.ts\"\nimport type { TextContent } from \"./text.ts\"\n\nimport { stringWidth } from \"@zaly/shared/ansi\"\nimport { Node } from \"../core/node.ts\"\nimport { unwrap } from \"../core/reactive.ts\"\nimport { drawBorder, resolveBorder } from \"../layout/border.ts\"\nimport { allocateRow, isFixedWidth, padRow, stackColumn, zipRow } from \"../layout/flex.ts\"\nimport { clamp, resolveSize } from \"../layout/size.ts\"\nimport { textContent } from \"./text.ts\"\n\nexport type Padding =\n | number\n | readonly [v: number, h: number]\n | readonly [t: number, r: number, b: number, l: number]\n\nexport interface BoxStyle extends Style {\n flexDirection?: \"row\" | \"column\"\n gap?: number\n /** Fixed outer height in rows. When unset, the box's height emerges\n * naturally from its content + padding + border. When set, the\n * content area is truncated or padded with blank rows to land at\n * exactly this many rows of *outer* height. Pair with\n * `verticalAlign` to control which edge is anchored. */\n height?: number\n /** Anchor for the content when `height` truncates or extends.\n * `\"top\"` (default) keeps the top edge: extra rows added at the\n * bottom, overflow truncated from the bottom. `\"bottom\"` keeps the\n * bottom edge: extra rows prepended, overflow truncated from the\n * top. Other CSS-like values (`middle`) are not implemented. */\n verticalAlign?: \"top\" | \"bottom\"\n padding?: Padding\n border?: BorderSpec\n borderTitle?: TextContent\n /** Placement of `borderTitle` along the top border. Defaults to `\"left\"`. */\n borderTitleAlign?: TitleAlign\n /**\n * Style applied to the border glyphs. Either a theme slot name (resolved\n * via the active theme) or an inline `Style`. Defaults to the `\"border\"`\n * theme slot when a border is drawn.\n */\n borderStyle?: AnyStyle\n /**\n * Style applied to the border title. Defaults to the `\"borderTitle\"` theme\n * slot when a title is set.\n */\n borderTitleStyle?: AnyStyle\n}\n\nexport class Box<T extends object = {}> extends Node<BoxStyle & T> {\n protected async _render(ctx: RenderCtx): Promise<string[]> {\n const style = this.state\n\n const [padT, padR, padB, padL] = resolvePadding(style.padding)\n const bchars = resolveBorder(style.border)\n const hasBorder = bchars !== undefined\n const chrome = padL + padR + (hasBorder ? 2 : 0)\n\n let outer: number\n let contentRows: string[]\n let inner: number\n\n if (style.width === \"fit\") {\n // `fit`: render children at the parent's max inner width, measure\n // the natural max-row width, then clamp the box to that. Two-pass\n // — the first pass uses the upper bound as a measuring window,\n // the second re-pads each row to the chosen inner width.\n const upper = Math.max(0, ctx.width - chrome)\n const tentative = await this.#layoutChildren(upper, ctx)\n const natural = tentative.reduce((m, r) => Math.max(m, stringWidth(r)), 0)\n const fit = Math.min(ctx.width, natural + chrome)\n outer = clamp(fit, {\n available: ctx.width,\n max: style.maxWidth,\n min: style.minWidth,\n })\n inner = Math.max(0, outer - chrome)\n contentRows = tentative\n } else {\n const requested = resolveSize(style.width ?? \"fill\", ctx.width) ?? ctx.width\n outer = clamp(requested, {\n available: ctx.width,\n max: style.maxWidth,\n min: style.minWidth,\n })\n inner = Math.max(0, outer - chrome)\n contentRows = await this.#layoutChildren(inner, ctx)\n }\n\n const paddedWidth = Math.max(0, outer - (hasBorder ? 2 : 0))\n\n // Ensure each content row is exactly `inner` cells wide (absorbs slack).\n contentRows = contentRows.map((row) => padRow(row, inner))\n\n // Horizontal padding.\n if (padL > 0 || padR > 0) {\n const lp = \" \".repeat(padL)\n const rp = \" \".repeat(padR)\n contentRows = contentRows.map((row) => lp + row + rp)\n }\n\n // Vertical padding.\n const blank = \" \".repeat(paddedWidth)\n const top = Array.from({ length: padT }, () => blank)\n const bot = Array.from({ length: padB }, () => blank)\n let rows = [...top, ...contentRows, ...bot]\n\n // Fixed-height fit. The `height` field is the outer dimension —\n // border takes its 2 rows out before we measure against the\n // content area; padding is already baked into `rows`. `verticalAlign`\n // decides which end gets clipped (when too tall) or padded (when\n // too short). Default `\"top\"` matches the natural reading flow.\n if (style.height !== undefined) {\n const innerH = Math.max(0, style.height - (hasBorder ? 2 : 0))\n if (rows.length > innerH) {\n rows = style.verticalAlign === \"bottom\" ? rows.slice(-innerH) : rows.slice(0, innerH)\n } else if (rows.length < innerH) {\n const filler = Array.from({ length: innerH - rows.length }, () => blank)\n rows = style.verticalAlign === \"bottom\" ? [...filler, ...rows] : [...rows, ...filler]\n }\n }\n\n if (bchars) {\n rows = drawBorder(rows, bchars, {\n borderStyle: ctx.style.add(style.borderStyle ?? \"border\"),\n title: await textContent(style.borderTitle, ctx),\n titleAlign: style.borderTitleAlign,\n titleStyle: ctx.style.add(style.borderTitleStyle ?? \"borderTitle\"),\n })\n }\n\n const wrap = ctx.style.add(style)\n return rows.map((row) => wrap(row))\n }\n\n /**\n * Intrinsic content width derived from children (or the explicit\n * `width` when fixed). Lets parent flex containers size this Box\n * without falling back to a measure-render — important for\n * side-effecting children like `Image` whose first render allocates\n * a KGP placement: rendering twice would emit two placements and the\n * second render's `transmitOnce` returns no bytes, so the bytes from\n * the discarded measure pass would be lost.\n *\n * Row direction → bases sum (plus gaps). Column direction → bases\n * max. Both add chrome (padding + border) at the end.\n *\n * Children without `layout()` contribute 0 — the row allocator's\n * render-measure fallback still kicks in for them at allocation\n * time, but their absence here just means this Box reports a\n * smaller intrinsic. That's the safe direction: under-report and\n * let shrink/grow handle slack, never over-report.\n */\n override async layout(ctx: RenderCtx) {\n const s = this.state\n const [, padR, , padL] = resolvePadding(s.padding)\n const chrome = padL + padR + (resolveBorder(s.border) ? 2 : 0)\n\n // Fixed width wins outright.\n if (isFixedWidth(s.width)) {\n const w = resolveSize(s.width, ctx.width) ?? 0\n return { minWidth: w, width: w }\n }\n\n const kids = layoutNodes(this.children)\n if (kids.length === 0) return { minWidth: chrome, width: chrome }\n\n const isRow = (s.flexDirection ?? \"column\") === \"row\"\n const totalGap = isRow ? (s.gap ?? 0) * Math.max(0, kids.length - 1) : 0\n\n let widthAcc = 0\n let minAcc = 0\n for (const k of kids) {\n // oxlint-disable-next-line no-await-in-loop\n const l = (await k.getLayout(ctx)) ?? { minWidth: 0, width: 0 }\n if (isRow) {\n widthAcc += l.width\n minAcc += l.minWidth\n } else {\n widthAcc = Math.max(widthAcc, l.width)\n minAcc = Math.max(minAcc, l.minWidth)\n }\n }\n\n return { minWidth: minAcc + totalGap + chrome, width: widthAcc + totalGap + chrome }\n }\n\n async #layoutChildren(innerWidth: number, ctx: RenderCtx): Promise<string[]> {\n const children = layoutNodes(this.children)\n if (children.length === 0) return []\n const gap = this.state.gap ?? 0\n return (this.state.flexDirection ?? \"column\") === \"row\"\n ? this.#layoutRow(children, innerWidth, ctx, gap)\n : this.#layoutColumn(children, innerWidth, ctx, gap)\n }\n\n /**\n * Row direction — main axis is horizontal. CSS-style flex:\n * each child sizes to its intrinsic content width unless it claims\n * slack via `flexGrow > 0` or `width: \"fill\"`. Slack distributes by\n * grow weights; deficit distributes by `basis * flexShrink` clamped\n * at each child's `min-content` floor.\n *\n * Sizing strategy per child:\n * 1. `node.layout(ctx)` — sync, content-derived. If implemented,\n * use directly; no render needed for measurement.\n * 2. Otherwise fall back to render-at-innerWidth and measure the\n * widest emitted row. Cached; the paint pass reuses the result\n * whenever the allocated width matches the measurement width.\n *\n * In `fit` mode the parent has no slack to give, so the allocator's\n * available is capped at the natural sum.\n */\n async #layoutRow(\n children: readonly Node[],\n innerWidth: number,\n ctx: RenderCtx,\n gap: number\n ): Promise<string[]> {\n // Phase 1: figure out each child's intrinsic size.\n // - `layout()` short-circuits — sync, no render.\n // - Fallback: render at the upper bound (innerWidth or fixed spec)\n // and measure rows.\n const intrinsics = await Promise.all(children.map((c) => c.getLayout(ctx)))\n // For children with intrinsic sizing, skip the measure render\n // entirely. Others fall back to render-at-upper-bound (their fixed\n // width, or innerWidth if fluid).\n const measureWidths = children.map((c, i) => {\n if (intrinsics[i] !== undefined) return -1\n if (isFixedWidth(c.state.width)) {\n return resolveSize(c.state.width, innerWidth) ?? innerWidth\n }\n return innerWidth\n })\n const measureRows = await Promise.all(\n children.map((c, i): Promise<string[] | undefined> => {\n if (measureWidths[i] === -1) return Promise.resolve(undefined)\n return c.render({ ...ctx, width: measureWidths[i] })\n })\n )\n const naturals = children.map((c, i) => {\n // A fixed `state.width` is what the allocator will actually\n // assign this child — use it directly so the fit-mode\n // `allocAvailable` sum below matches what we'll consume. Falling\n // back to intrinsic when there's no override under-reports a\n // child like `text(\"x\", { width: 2 })` and shrinks every sibling\n // by the missing cells.\n if (isFixedWidth(c.state.width)) {\n return resolveSize(c.state.width, innerWidth) ?? 0\n }\n const intr = intrinsics[i]\n if (intr) return intr.width\n return (measureRows[i] ?? []).reduce((m, r) => Math.max(m, stringWidth(r)), 0)\n })\n const items: RowItem[] = children.map((c, i) => {\n const s = c.state\n return {\n flexGrow: s.flexGrow,\n flexShrink: s.flexShrink,\n intrinsicMin: intrinsics[i]?.minWidth,\n maxWidth: s.maxWidth,\n minWidth: s.minWidth,\n natural: naturals[i],\n width: s.width,\n }\n })\n const allocAvailable =\n this.state.width === \"fit\"\n ? naturals.reduce((a, b) => a + b, 0) + gap * Math.max(0, children.length - 1)\n : innerWidth\n const widths = allocateRow(items, { contentWidth: allocAvailable, gap })\n // Phase 2: paint at allocated widths. Reuse the measure-render when\n // the allocation matches; otherwise render fresh (cached).\n const childRows = await Promise.all(\n children.map((c, i): Promise<string[]> => {\n const cached = measureRows[i]\n if (cached !== undefined && widths[i] === measureWidths[i]) {\n return Promise.resolve(cached)\n }\n return c.render({ ...ctx, width: widths[i] })\n })\n )\n // `zipRow` expects rows to be exactly `widths[i]` wide. Children\n // that emit natural-width rows (text default, code default) get\n // padded here so the contract holds; over-wide rows get clipped.\n const padded = childRows.map((rows, i) => rows.map((row) => padRow(row, widths[i])))\n return zipRow(padded, { gap, widths })\n }\n\n /**\n * Column direction — main axis is vertical, cross axis horizontal.\n * Per CSS `align-items: stretch` (default), children with no\n * explicit `width` fill the cross axis. A child with a fixed `width`\n * (number / `Pct`) takes that width as its cross-axis size, with the\n * leftover padded as right slack (default `flex-start`). `min`/`max`\n * clamp the resolved width.\n *\n * In fit-mode (`width: \"fit\"`), the cross-axis target shrinks to the\n * widest child row instead of stretching to `innerWidth` — symmetric\n * to how row-direction caps allocator-available at the natural sum.\n */\n async #layoutColumn(\n children: readonly Node[],\n innerWidth: number,\n ctx: RenderCtx,\n gap: number\n ): Promise<string[]> {\n const widths = children.map((c) => {\n const s = c.state\n const fixed = isFixedWidth(s.width) ? resolveSize(s.width, innerWidth) : undefined\n const w = fixed ?? innerWidth\n return clamp(w, { available: innerWidth, max: s.maxWidth, min: s.minWidth })\n })\n const childRows = await Promise.all(\n children.map((c, i) => c.render({ ...ctx, width: widths[i] }))\n )\n const target =\n this.state.width === \"fit\"\n ? childRows.reduce((m, rows) => rows.reduce((mm, r) => Math.max(mm, stringWidth(r)), m), 0)\n : innerWidth\n // Pad each child's rows to the cross-axis target. For full-stretch\n // children this absorbs natural-row slack; for fixed-width\n // children it adds right-side slack (default `flex-start`\n // alignment). `stackColumn`'s contract requires uniform width.\n const padded = childRows.map((rows) => rows.map((row) => padRow(row, target)))\n return stackColumn(padded, { gap, width: target })\n }\n}\n\ntype Child = Node | false | null | undefined\n\n/**\n * Factory for `Box`. The style object is required (use `{}` if you don't\n * need any styling) — the previous style-less overload caused TS to fail\n * to discriminate between a Node child and an inferred-empty-state node\n * like `WidgetNode`. Pass `box({}, child1, child2)` for un-styled boxes.\n *\n * Falsy children (`false`, `null`, `undefined`) are filtered out, enabling\n * conditional JSX-like composition: `box({}, cond && child)`.\n */\nexport function box(style: State<BoxStyle>, ...children: Child[]): Box {\n const b = new Box(style)\n for (const c of children) if (c) b.add(c)\n return b\n}\n\n/**\n * Flatten fragment nodes (those that implement the `layoutChildren`\n * protocol — `show`, `errorBoundary`, `suspense`) into their currently\n * active leaves. Recursive: a fragment whose `layoutChildren()`\n * itself contains fragments unfolds completely.\n *\n * The fragment Node still lives in the tree (mounted, propagating\n * invalidates) — layout just sees through it so its children share\n * the parent box's flex distribution, gap, and cross-axis sizing\n * instead of collapsing into a single slot.\n */\nfunction layoutNodes(children: readonly Node[]): readonly Node[] {\n return children.flatMap((c) => c.layoutNodes).filter((c) => unwrap(c.state.visible) ?? true)\n}\n\nfunction resolvePadding(p: Padding | undefined): [t: number, r: number, b: number, l: number] {\n if (p === undefined) return [0, 0, 0, 0]\n if (typeof p === \"number\") return [p, p, p, p]\n if (p.length === 2) return [p[0], p[1], p[0], p[1]]\n return [p[0], p[1], p[2], p[3]]\n}\n"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import { a as hyperlink } from "./ansi-CxgRPLA2.mjs";
2
2
  import { t as allocateRow } from "./flex-CYp4etqn.mjs";
3
- import { n as capitalize, o as formatText, t as calcLayout } from "./text-4arC3odI.mjs";
3
+ import { n as capitalize, o as formatText, t as calcLayout } from "./text-BEuhhxr7.mjs";
4
4
  import { splitAnsi, stringWidth } from "@zaly/shared/ansi";
5
5
  // src/markdown/code.ts
6
6
  /**
@@ -273,4 +273,4 @@ function createCallbacks(ctx) {
273
273
  }
274
274
  export { createCallbacks };
275
275
 
276
- //# sourceMappingURL=callbacks-DW8XKYKy.mjs.map
276
+ //# sourceMappingURL=callbacks-UaBfDStQ.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"callbacks-DW8XKYKy.mjs","names":[],"sources":["../src/markdown/code.ts","../src/markdown/list.ts","../src/markdown/table.ts","../src/markdown/callbacks.ts"],"sourcesContent":["import type { MarkdownCtx } from \"./renderer.ts\"\nimport type { MdCallbacks } from \"./types.ts\"\n\nimport { stringWidth } from \"@zaly/shared/ansi\"\nimport { formatText } from \"../layout/text.ts\"\n\n/**\n * Build the `code` callback. The returned closure captures ctx/style/highlighter\n * and renders each fenced block: syntax-highlight (if available), pad lines to\n * widest-content + 1, apply `mdCodeBlock` as a bg-only backdrop, and prefix an\n * optional title line from `title=\"...\"`.\n */\nexport function createCodeCallback(ctx: MarkdownCtx): NonNullable<MdCallbacks[\"code\"]> {\n const s = ctx.style\n return (text, meta) => {\n text = text.replace(/\\n+$/, \"\") // trim to avoid extra blank lines after the block\n const highlighted = ctx.highlight?.(text, meta?.language) ?? text\n\n // For highlighted output the per-token fgs would clash with the slot's\n // fg; override fg with `\"inherit\"` so only bg + attrs wrap each row\n // and shiki's per-token colors show through on top of the backdrop.\n const wrap = highlighted === text ? s.mdCodeBlock : s.mdCodeBlock.fg(\"inherit\")\n const hpad = 2\n const maxWidth = ctx.width\n const lines = formatText(`\\n${highlighted}\\n`, {\n width: maxWidth - hpad * 2,\n wrap: \"word\",\n })\n let width = Math.max(...lines.map(stringWidth)) + hpad * 2\n width = Math.min(width, maxWidth) // leave room for the border\n // Left-align with a fixed `hpad` of leading whitespace; pad the\n // right to fill the bg out to `width`. Centering would push short\n // lines toward the middle, which reads as visually misaligned code.\n // Lines wider than the available room (width − hpad) drop the\n // leading pad so the bg ends at the cap rather than overshooting.\n const padded = lines.map((line) => {\n const w = stringWidth(line)\n if (w >= width - hpad) return wrap(line)\n return wrap(`${\" \".repeat(hpad)}${line}${\" \".repeat(width - w - hpad)}`)\n })\n const titleLine = meta?.title === undefined ? \"\" : `${s.mdCodeBlockTitle(meta.title)}\\n`\n // Leading `\\n` guards against raw-text siblings that don't emit a\n // paragraph-wrap (e.g. some inline-only list items where the parser\n // drops the paragraph). Containers (`listItem`, document) collapse\n // `\\n{3,}` → `\\n\\n` so paragraph + code doesn't double up.\n return `\\n${titleLine}${padded.join(\"\\n\")}\\n\\n`\n }\n}\n","// oxlint-disable no-multi-assign\nimport type { RenderCtx } from \"../core/ctx.ts\"\nimport type { MdCallbacks } from \"./types.ts\"\n\nimport { splitAnsi, stringWidth } from \"@zaly/shared/ansi\"\n\ninterface ListState {\n block: boolean\n items: string[]\n /** Formatted output, set when the list closes. The parent `listItem`\n * pulls it via `stack[depth+1]` and splices it onto its own body. */\n rendered?: string\n}\n\nconst BULLETS = [\"●\", \"○\", \"◆\", \"◇\"] as const\n\ntype ListCallbacks = Pick<MdCallbacks, \"list\" | \"listItem\">\n\n/**\n * Stack-based list renderer. Each depth has its own `ListState`. Inner\n * lists return `\"\"` and stash their formatted output on `state.rendered`;\n * the parent `listItem` pulls + splices, so inner output never reaches\n * the parent's `children` string. That keeps each level's loose-vs-tight\n * verdict independent — `children.endsWith(\"\\n\\n\")` is then the per-item\n * loose signal (parser only emits `<p>` for items in loose lists).\n *\n * Nested list lands at the end of its parent's body — `text, list, more`\n * is rare in real markdown and not worth the extra plumbing.\n */\nexport function createListCallbacks(ctx: RenderCtx): ListCallbacks {\n const s = ctx.style\n // Lazy-pushed on the first `listItem` at a given depth; consumed\n // either by the parent `listItem` (nested) or by `list(0)` (top).\n const stack: (ListState | undefined)[] = []\n\n return {\n list: (_children, meta) => {\n const state = stack[meta.depth]\n if (state === undefined || state.items.length === 0) return \"\"\n state.rendered = state.items.join(state.block ? \"\\n\\n\" : \"\\n\")\n if (meta.depth > 0) return \"\"\n // Top-level: defensive stack reset + block-contract `\\n\\n` trailer.\n stack.length = 0\n return `${state.rendered}\\n\\n`\n },\n\n listItem: (children, meta) => {\n let marker = s.mdListBullet(\n meta.ordered ? `${(meta.start ?? 1) + meta.index}.` : BULLETS[meta.depth % BULLETS.length]\n )\n if (meta.checked !== undefined) {\n const ref = meta.checked ? \"mdListChecked\" : \"mdListUnchecked\"\n marker += ` ${s.add(ref)(meta.checked ? \"[x]\" : \"[ ]\")}`\n }\n const indent = \" \".repeat(stringWidth(marker))\n // Pull nested list output (if any) off the stack.\n const nested = stack[meta.depth + 1]\n stack[meta.depth + 1] = undefined\n // Trailing `\\n\\n` = loose signal. Nested output is in `rendered`,\n // not `children`, so it can't pollute this check.\n const block = children.endsWith(\"\\n\\n\")\n const trimmed = children.replace(/\\s*\\n+$/, \"\").replace(/\\n{3,}/g, \"\\n\\n\")\n // Splice nested after the prose; loose item adds a blank-row gap.\n let body = trimmed\n if (nested?.rendered !== undefined) {\n const sep = block ? \"\\n\\n\" : \"\\n\"\n body = trimmed === \"\" ? nested.rendered : `${trimmed}${sep}${nested.rendered}`\n }\n const lines = splitAnsi(body)\n const content = lines\n .map((line, l) => {\n if (l === 0) return `${marker} ${line}`\n // Bare empty lines so loose `\\n\\n` separators stay clean.\n return line === \"\" ? \"\" : `${indent}${line}`\n })\n .join(\"\\n\")\n\n const state = (stack[meta.depth] ??= { block: false, items: [] })\n state.block ||= block\n state.items.push(content)\n return \"\"\n },\n }\n}\n","import type { RenderCtx } from \"../core/ctx.ts\"\nimport type { Layout } from \"../core/state.ts\"\nimport type { RowItem } from \"../layout/flex.ts\"\nimport type { MdCallbacks } from \"./types.ts\"\n\nimport { stringWidth } from \"@zaly/shared/ansi\"\nimport { allocateRow } from \"../layout/flex.ts\"\nimport { calcLayout, formatText } from \"../layout/text.ts\"\n\ntype CellAlign = \"left\" | \"center\" | \"right\"\n\ninterface TableCell {\n text: string\n align: CellAlign | undefined\n}\n\ninterface TableState {\n header: TableCell[][]\n body: TableCell[][]\n pending: TableCell[]\n pendingIsHeader: boolean\n}\n\ntype TableCallbacks = Pick<MdCallbacks, \"table\" | \"tbody\" | \"td\" | \"th\" | \"thead\" | \"tr\">\n\n/**\n * Build the table-related callbacks. They share a closure-local\n * `TableState` accumulator: `th`/`td` push cells, `tr` commits rows into\n * header or body, and `table` runs the final layout + reset. Tables can't\n * nest in markdown, so a single top-level slot is enough.\n */\nexport function createTableCallbacks(ctx: RenderCtx): TableCallbacks {\n const { style } = ctx\n let state: TableState | undefined\n const begin = (): TableState =>\n (state ??= { body: [], header: [], pending: [], pendingIsHeader: false })\n\n return {\n table: () => {\n // Children of th/td/tr/thead/tbody are empty strings; all content is\n // in `state`. Format, reset, emit.\n if (state === undefined) return \"\"\n const out = formatTable(state, ctx)\n state = undefined\n return `${out}\\n\\n`\n },\n\n tbody: () => \"\",\n\n td: (children, meta) => {\n const t = begin()\n t.pending.push({ align: meta?.align, text: children })\n t.pendingIsHeader = false\n return \"\"\n },\n\n th: (children, meta) => {\n const t = begin()\n t.pending.push({ align: meta?.align, text: style.mdTableHeader(children) })\n t.pendingIsHeader = true\n return \"\"\n },\n\n thead: () => \"\",\n\n tr: () => {\n if (state === undefined || state.pending.length === 0) return \"\"\n if (state.pendingIsHeader) state.header.push(state.pending)\n else state.body.push(state.pending)\n state.pending = []\n return \"\"\n },\n }\n}\n\nfunction formatTable(state: TableState, ctx: RenderCtx): string {\n const cols: Layout[] = []\n const cells = [...state.header, ...state.body]\n const colCount = Math.max(...cells.map((r) => r.length))\n\n // 1. Calculate min-content and max-content for each column\n for (let c = 0; c < colCount; c++) {\n for (const row of cells) {\n const cell = row[c] as TableCell | undefined\n if (!cell) continue // Safety guard for ragged rows\n const l = calcLayout(cell.text)\n cols[c] ??= { ...l }\n cols[c].width = Math.max(cols[c].width, l.width)\n cols[c].minWidth = Math.max(cols[c].minWidth, l.minWidth)\n }\n }\n const chromeWidth = 3 * colCount + 1\n\n const items: RowItem[] = cols.map((c) => ({\n flex: 1,\n intrinsicMin: c.minWidth,\n natural: c.width,\n }))\n\n const widths = allocateRow(items, { contentWidth: ctx.width - chromeWidth, gap: 0 })\n\n // 4. Render output string using the exact final Column Widths\n // Pre-style each piece of chrome once. Each column span on a rule row is\n // `widths[i] + 2` dashes (one cell of padding on each side of the cell).\n const style = ctx.style\n const v = style.mdTable(\"│\")\n const ruleRow = (left: string, mid: string, right: string): string =>\n style.mdTable(left + widths.map((w) => \"─\".repeat(w + 2)).join(mid) + right)\n const top = ruleRow(\"┌\", \"┬\", \"┐\")\n const sep = ruleRow(\"├\", \"┼\", \"┤\")\n const bot = ruleRow(\"└\", \"┴\", \"┘\")\n\n const renderRow = (row: TableCell[]): string => {\n const formatted = row.map((cell, c) =>\n formatText(cell.text, { indent: false, width: widths[c], wrapBg: false })\n )\n const rows: string[] = []\n const rowCount = Math.max(...formatted.map((r) => r.length))\n for (let r = 0; r < rowCount; r++) {\n const line: string[] = []\n for (let c = 0; c < colCount; c++) {\n const t = formatted[c]?.[r] ?? \"\"\n line.push(padCell(t, widths[c], row[c]?.align))\n }\n rows.push(`${v} ${line.join(` ${v} `)} ${v}`)\n }\n\n return rows.join(\"\\n\")\n }\n\n const lines: string[] = [top]\n for (const row of state.header) lines.push(renderRow(row))\n if (state.header.length > 0 && state.body.length > 0) lines.push(sep)\n for (const row of state.body) lines.push(renderRow(row))\n lines.push(bot)\n return lines.join(\"\\n\")\n}\n\nfunction padCell(text: string, width: number, align: CellAlign | undefined): string {\n const slack = Math.max(0, width - stringWidth(text))\n if (align === \"right\") return \" \".repeat(slack) + text\n if (align === \"center\") {\n const left = Math.floor(slack / 2)\n return \" \".repeat(left) + text + \" \".repeat(slack - left)\n }\n return text + \" \".repeat(slack)\n}\n","import type { AnyStyle, ThemeKey } from \"../style/types.ts\"\nimport type { MarkdownCtx } from \"./renderer.ts\"\nimport type { MdCallbacks } from \"./types.ts\"\n\nimport { stringWidth } from \"@zaly/shared/ansi\"\nimport { capitalize } from \"../layout/text.ts\"\nimport { hyperlink } from \"../style/ansi.ts\"\nimport { createCodeCallback } from \"./code.ts\"\nimport { createListCallbacks } from \"./list.ts\"\nimport { createTableCallbacks } from \"./table.ts\"\n\nconst icons = {\n hr: \"─\",\n quote: \"┃\",\n} as const\n\ntype AlertType = \"NOTE\" | \"TIP\" | \"WARNING\" | \"IMPORTANT\" | \"CAUTION\"\nconst alerts: Record<AlertType, { icon: string; style: AnyStyle }> = {\n CAUTION: { icon: \"✖ \", style: \"error\" },\n IMPORTANT: { icon: \"‼\", style: \"syntaxConstant\" },\n NOTE: { icon: \"ℹ \", style: \"info\" },\n TIP: { icon: \"🛈 \", style: \"success\" },\n WARNING: { icon: \"⚠\", style: \"warn\" },\n}\n\nconst alertRe = /^\\s*\\[!(NOTE|TIP|WARNING|IMPORTANT|CAUTION)\\]\\s*$/\n\n/**\n * Build the `MdCallbacks` that drive the theme-aware rendering. Exposed so\n * callers can invoke `renderMarkdown` directly when they need the string\n * output without the wrapping `Markdown` node (e.g. to embed markdown inside\n * a custom `Text` content function).\n * @internal\n */\nexport function createCallbacks(ctx: MarkdownCtx): MdCallbacks {\n const s = ctx.style\n\n return {\n blockquote: (children) => {\n const lines = children.replace(/\\n+$/, \"\").split(\"\\n\")\n let lineStyle: AnyStyle = \"mdQuote\"\n\n const m = lines.length >= 1 ? lines[0].match(alertRe) : undefined\n if (m) {\n const alertType = m[1] as AlertType\n const alert = alerts[alertType]\n lineStyle = alert.style\n const name = capitalize(alertType.toLowerCase())\n lines[0] = s.add(lineStyle)(`${alert.icon} ${name}`)\n }\n\n // Inner blocks (typically a paragraph) end with trailing newlines; if\n // we prefix those empty lines with \"│ \" they render as styled empty\n // rows between the last line of the quote and the block separator.\n // Trim first so the quote stops cleanly.\n const prefixed = lines.map((line) => `${s.add(lineStyle)(icons.quote)} ${line}`).join(\"\\n\")\n return `${prefixed}\\n\\n`\n },\n\n code: createCodeCallback(ctx),\n\n // Image markers use code spans for parser protection, not code styling.\n codespan: (text) => (/^<img id=\\d+>$/.test(text) ? text : s.mdCode(text)),\n\n emphasis: (children) => s.mdItalic(children),\n\n heading: (children, { level }) => {\n const width = ctx.width\n const padded = children\n .split(\"\\n\")\n .map((line) => line + \" \".repeat(Math.max(0, width - stringWidth(line))))\n .join(\"\\n\")\n return `${s.add(`mdHeading${level}` as ThemeKey)(padded)}\\n\\n`\n },\n\n hr: () => `${s.mdHr(icons.hr.repeat(ctx.width))}\\n\\n`,\n\n html: (children) => children,\n\n link: (children, { href }) => hyperlink(href, s.mdLink(children)),\n\n paragraph: (children) => `${children}\\n\\n`,\n\n strikethrough: (children) => s.mdStrikethrough(children),\n\n strong: (children) => s.mdBold(children),\n\n ...createListCallbacks(ctx),\n ...createTableCallbacks(ctx),\n }\n}\n"],"mappings":";;;;;;;;;;;AAYA,SAAgB,mBAAmB,KAAoD;CACrF,MAAM,IAAI,IAAI;CACd,QAAQ,MAAM,SAAS;EACrB,OAAO,KAAK,QAAQ,QAAQ,EAAE;EAC9B,MAAM,cAAc,IAAI,YAAY,MAAM,MAAM,QAAQ,KAAK;EAK7D,MAAM,OAAO,gBAAgB,OAAO,EAAE,cAAc,EAAE,YAAY,GAAG,SAAS;EAC9E,MAAM,OAAO;EACb,MAAM,WAAW,IAAI;EACrB,MAAM,QAAQ,WAAW,KAAK,YAAY,KAAK;GAC7C,OAAO,WAAW;GAClB,MAAM;EACR,CAAC;EACD,IAAI,QAAQ,KAAK,IAAI,GAAG,MAAM,IAAI,WAAW,CAAC,IAAI;EAClD,QAAQ,KAAK,IAAI,OAAO,QAAQ;EAMhC,MAAM,SAAS,MAAM,KAAK,SAAS;GACjC,MAAM,IAAI,YAAY,IAAI;GAC1B,IAAI,KAAK,QAAQ,MAAM,OAAO,KAAK,IAAI;GACvC,OAAO,KAAK,GAAG,IAAI,OAAO,IAAI,IAAI,OAAO,IAAI,OAAO,QAAQ,IAAI,IAAI,GAAG;EACzE,CAAC;EAMD,OAAO,KALW,MAAM,UAAU,KAAA,IAAY,KAAK,GAAG,EAAE,iBAAiB,KAAK,KAAK,EAAE,MAK7D,OAAO,KAAK,IAAI,EAAE;CAC5C;AACF;;ACjCA,MAAM,UAAU;CAAC;CAAK;CAAK;CAAK;AAAG;;;;;;;;;;;;AAenC,SAAgB,oBAAoB,KAA+B;CACjE,MAAM,IAAI,IAAI;CAGd,MAAM,QAAmC,CAAC;CAE1C,OAAO;EACL,OAAO,WAAW,SAAS;GACzB,MAAM,QAAQ,MAAM,KAAK;GACzB,IAAI,UAAU,KAAA,KAAa,MAAM,MAAM,WAAW,GAAG,OAAO;GAC5D,MAAM,WAAW,MAAM,MAAM,KAAK,MAAM,QAAQ,SAAS,IAAI;GAC7D,IAAI,KAAK,QAAQ,GAAG,OAAO;GAE3B,MAAM,SAAS;GACf,OAAO,GAAG,MAAM,SAAS;EAC3B;EAEA,WAAW,UAAU,SAAS;GAC5B,IAAI,SAAS,EAAE,aACb,KAAK,UAAU,IAAI,KAAK,SAAS,KAAK,KAAK,MAAM,KAAK,QAAQ,KAAK,QAAQ,QAAQ,OACrF;GACA,IAAI,KAAK,YAAY,KAAA,GAAW;IAC9B,MAAM,MAAM,KAAK,UAAU,kBAAkB;IAC7C,UAAU,IAAI,EAAE,IAAI,GAAG,CAAC,CAAC,KAAK,UAAU,QAAQ,KAAK;GACvD;GACA,MAAM,SAAS,KAAK,OAAO,YAAY,MAAM,CAAC;GAE9C,MAAM,SAAS,MAAM,KAAK,QAAQ;GAClC,MAAM,KAAK,QAAQ,KAAK,KAAA;GAGxB,MAAM,QAAQ,SAAS,SAAS,MAAM;GACtC,MAAM,UAAU,SAAS,QAAQ,WAAW,EAAE,CAAC,CAAC,QAAQ,WAAW,MAAM;GAEzE,IAAI,OAAO;GACX,IAAI,QAAQ,aAAa,KAAA,GAEvB,OAAO,YAAY,KAAK,OAAO,WAAW,GAAG,UADjC,QAAQ,SAAS,OACgC,OAAO;GAGtE,MAAM,UADQ,UAAU,IACJ,CAAC,CAClB,KAAK,MAAM,MAAM;IAChB,IAAI,MAAM,GAAG,OAAO,GAAG,OAAO,GAAG;IAEjC,OAAO,SAAS,KAAK,KAAK,GAAG,SAAS;GACxC,CAAC,CAAC,CACD,KAAK,IAAI;GAEZ,MAAM,QAAS,MAAM,KAAK,WAAW;IAAE,OAAO;IAAO,OAAO,CAAC;GAAE;GAC/D,MAAM,UAAU;GAChB,MAAM,MAAM,KAAK,OAAO;GACxB,OAAO;EACT;CACF;AACF;;;;;;;;ACpDA,SAAgB,qBAAqB,KAAgC;CACnE,MAAM,EAAE,UAAU;CAClB,IAAI;CACJ,MAAM,cACH,UAAU;EAAE,MAAM,CAAC;EAAG,QAAQ,CAAC;EAAG,SAAS,CAAC;EAAG,iBAAiB;CAAM;CAEzE,OAAO;EACL,aAAa;GAGX,IAAI,UAAU,KAAA,GAAW,OAAO;GAChC,MAAM,MAAM,YAAY,OAAO,GAAG;GAClC,QAAQ,KAAA;GACR,OAAO,GAAG,IAAI;EAChB;EAEA,aAAa;EAEb,KAAK,UAAU,SAAS;GACtB,MAAM,IAAI,MAAM;GAChB,EAAE,QAAQ,KAAK;IAAE,OAAO,MAAM;IAAO,MAAM;GAAS,CAAC;GACrD,EAAE,kBAAkB;GACpB,OAAO;EACT;EAEA,KAAK,UAAU,SAAS;GACtB,MAAM,IAAI,MAAM;GAChB,EAAE,QAAQ,KAAK;IAAE,OAAO,MAAM;IAAO,MAAM,MAAM,cAAc,QAAQ;GAAE,CAAC;GAC1E,EAAE,kBAAkB;GACpB,OAAO;EACT;EAEA,aAAa;EAEb,UAAU;GACR,IAAI,UAAU,KAAA,KAAa,MAAM,QAAQ,WAAW,GAAG,OAAO;GAC9D,IAAI,MAAM,iBAAiB,MAAM,OAAO,KAAK,MAAM,OAAO;QACrD,MAAM,KAAK,KAAK,MAAM,OAAO;GAClC,MAAM,UAAU,CAAC;GACjB,OAAO;EACT;CACF;AACF;AAEA,SAAS,YAAY,OAAmB,KAAwB;CAC9D,MAAM,OAAiB,CAAC;CACxB,MAAM,QAAQ,CAAC,GAAG,MAAM,QAAQ,GAAG,MAAM,IAAI;CAC7C,MAAM,WAAW,KAAK,IAAI,GAAG,MAAM,KAAK,MAAM,EAAE,MAAM,CAAC;CAGvD,KAAK,IAAI,IAAI,GAAG,IAAI,UAAU,KAC5B,KAAK,MAAM,OAAO,OAAO;EACvB,MAAM,OAAO,IAAI;EACjB,IAAI,CAAC,MAAM;EACX,MAAM,IAAI,WAAW,KAAK,IAAI;EAC9B,KAAK,OAAO,EAAE,GAAG,EAAE;EACnB,KAAK,EAAE,CAAC,QAAQ,KAAK,IAAI,KAAK,EAAE,CAAC,OAAO,EAAE,KAAK;EAC/C,KAAK,EAAE,CAAC,WAAW,KAAK,IAAI,KAAK,EAAE,CAAC,UAAU,EAAE,QAAQ;CAC1D;CAEF,MAAM,cAAc,IAAI,WAAW;CAEnC,MAAM,QAAmB,KAAK,KAAK,OAAO;EACxC,MAAM;EACN,cAAc,EAAE;EAChB,SAAS,EAAE;CACb,EAAE;CAEF,MAAM,SAAS,YAAY,OAAO;EAAE,cAAc,IAAI,QAAQ;EAAa,KAAK;CAAE,CAAC;CAKnF,MAAM,QAAQ,IAAI;CAClB,MAAM,IAAI,MAAM,QAAQ,GAAG;CAC3B,MAAM,WAAW,MAAc,KAAa,UAC1C,MAAM,QAAQ,OAAO,OAAO,KAAK,MAAM,IAAI,OAAO,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG,IAAI,KAAK;CAC7E,MAAM,MAAM,QAAQ,KAAK,KAAK,GAAG;CACjC,MAAM,MAAM,QAAQ,KAAK,KAAK,GAAG;CACjC,MAAM,MAAM,QAAQ,KAAK,KAAK,GAAG;CAEjC,MAAM,aAAa,QAA6B;EAC9C,MAAM,YAAY,IAAI,KAAK,MAAM,MAC/B,WAAW,KAAK,MAAM;GAAE,QAAQ;GAAO,OAAO,OAAO;GAAI,QAAQ;EAAM,CAAC,CAC1E;EACA,MAAM,OAAiB,CAAC;EACxB,MAAM,WAAW,KAAK,IAAI,GAAG,UAAU,KAAK,MAAM,EAAE,MAAM,CAAC;EAC3D,KAAK,IAAI,IAAI,GAAG,IAAI,UAAU,KAAK;GACjC,MAAM,OAAiB,CAAC;GACxB,KAAK,IAAI,IAAI,GAAG,IAAI,UAAU,KAAK;IACjC,MAAM,IAAI,UAAU,EAAE,GAAG,MAAM;IAC/B,KAAK,KAAK,QAAQ,GAAG,OAAO,IAAI,IAAI,EAAE,EAAE,KAAK,CAAC;GAChD;GACA,KAAK,KAAK,GAAG,EAAE,GAAG,KAAK,KAAK,IAAI,EAAE,EAAE,EAAE,GAAG,GAAG;EAC9C;EAEA,OAAO,KAAK,KAAK,IAAI;CACvB;CAEA,MAAM,QAAkB,CAAC,GAAG;CAC5B,KAAK,MAAM,OAAO,MAAM,QAAQ,MAAM,KAAK,UAAU,GAAG,CAAC;CACzD,IAAI,MAAM,OAAO,SAAS,KAAK,MAAM,KAAK,SAAS,GAAG,MAAM,KAAK,GAAG;CACpE,KAAK,MAAM,OAAO,MAAM,MAAM,MAAM,KAAK,UAAU,GAAG,CAAC;CACvD,MAAM,KAAK,GAAG;CACd,OAAO,MAAM,KAAK,IAAI;AACxB;AAEA,SAAS,QAAQ,MAAc,OAAe,OAAsC;CAClF,MAAM,QAAQ,KAAK,IAAI,GAAG,QAAQ,YAAY,IAAI,CAAC;CACnD,IAAI,UAAU,SAAS,OAAO,IAAI,OAAO,KAAK,IAAI;CAClD,IAAI,UAAU,UAAU;EACtB,MAAM,OAAO,KAAK,MAAM,QAAQ,CAAC;EACjC,OAAO,IAAI,OAAO,IAAI,IAAI,OAAO,IAAI,OAAO,QAAQ,IAAI;CAC1D;CACA,OAAO,OAAO,IAAI,OAAO,KAAK;AAChC;;ACvIA,MAAM,QAAQ;CACZ,IAAI;CACJ,OAAO;AACT;AAGA,MAAM,SAA+D;CACnE,SAAS;EAAE,MAAM;EAAM,OAAO;CAAQ;CACtC,WAAW;EAAE,MAAM;EAAK,OAAO;CAAiB;CAChD,MAAM;EAAE,MAAM;EAAM,OAAO;CAAO;CAClC,KAAK;EAAE,MAAM;EAAO,OAAO;CAAU;CACrC,SAAS;EAAE,MAAM;EAAK,OAAO;CAAO;AACtC;AAEA,MAAM,UAAU;;;;;;;;AAShB,SAAgB,gBAAgB,KAA+B;CAC7D,MAAM,IAAI,IAAI;CAEd,OAAO;EACL,aAAa,aAAa;GACxB,MAAM,QAAQ,SAAS,QAAQ,QAAQ,EAAE,CAAC,CAAC,MAAM,IAAI;GACrD,IAAI,YAAsB;GAE1B,MAAM,IAAI,MAAM,UAAU,IAAI,MAAM,EAAE,CAAC,MAAM,OAAO,IAAI,KAAA;GACxD,IAAI,GAAG;IACL,MAAM,YAAY,EAAE;IACpB,MAAM,QAAQ,OAAO;IACrB,YAAY,MAAM;IAClB,MAAM,OAAO,WAAW,UAAU,YAAY,CAAC;IAC/C,MAAM,KAAK,EAAE,IAAI,SAAS,CAAC,CAAC,GAAG,MAAM,KAAK,GAAG,MAAM;GACrD;GAOA,OAAO,GADU,MAAM,KAAK,SAAS,GAAG,EAAE,IAAI,SAAS,CAAC,CAAC,MAAM,KAAK,EAAE,GAAG,MAAM,CAAC,CAAC,KAAK,IACrE,EAAE;EACrB;EAEA,MAAM,mBAAmB,GAAG;EAG5B,WAAW,SAAU,iBAAiB,KAAK,IAAI,IAAI,OAAO,EAAE,OAAO,IAAI;EAEvE,WAAW,aAAa,EAAE,SAAS,QAAQ;EAE3C,UAAU,UAAU,EAAE,YAAY;GAChC,MAAM,QAAQ,IAAI;GAClB,MAAM,SAAS,SACZ,MAAM,IAAI,CAAC,CACX,KAAK,SAAS,OAAO,IAAI,OAAO,KAAK,IAAI,GAAG,QAAQ,YAAY,IAAI,CAAC,CAAC,CAAC,CAAC,CACxE,KAAK,IAAI;GACZ,OAAO,GAAG,EAAE,IAAI,YAAY,OAAmB,CAAC,CAAC,MAAM,EAAE;EAC3D;EAEA,UAAU,GAAG,EAAE,KAAK,MAAM,GAAG,OAAO,IAAI,KAAK,CAAC,EAAE;EAEhD,OAAO,aAAa;EAEpB,OAAO,UAAU,EAAE,WAAW,UAAU,MAAM,EAAE,OAAO,QAAQ,CAAC;EAEhE,YAAY,aAAa,GAAG,SAAS;EAErC,gBAAgB,aAAa,EAAE,gBAAgB,QAAQ;EAEvD,SAAS,aAAa,EAAE,OAAO,QAAQ;EAEvC,GAAG,oBAAoB,GAAG;EAC1B,GAAG,qBAAqB,GAAG;CAC7B;AACF"}
1
+ {"version":3,"file":"callbacks-UaBfDStQ.mjs","names":[],"sources":["../src/markdown/code.ts","../src/markdown/list.ts","../src/markdown/table.ts","../src/markdown/callbacks.ts"],"sourcesContent":["import type { MarkdownCtx } from \"./renderer.ts\"\nimport type { MdCallbacks } from \"./types.ts\"\n\nimport { stringWidth } from \"@zaly/shared/ansi\"\nimport { formatText } from \"../layout/text.ts\"\n\n/**\n * Build the `code` callback. The returned closure captures ctx/style/highlighter\n * and renders each fenced block: syntax-highlight (if available), pad lines to\n * widest-content + 1, apply `mdCodeBlock` as a bg-only backdrop, and prefix an\n * optional title line from `title=\"...\"`.\n */\nexport function createCodeCallback(ctx: MarkdownCtx): NonNullable<MdCallbacks[\"code\"]> {\n const s = ctx.style\n return (text, meta) => {\n text = text.replace(/\\n+$/, \"\") // trim to avoid extra blank lines after the block\n const highlighted = ctx.highlight?.(text, meta?.language) ?? text\n\n // For highlighted output the per-token fgs would clash with the slot's\n // fg; override fg with `\"inherit\"` so only bg + attrs wrap each row\n // and shiki's per-token colors show through on top of the backdrop.\n const wrap = highlighted === text ? s.mdCodeBlock : s.mdCodeBlock.fg(\"inherit\")\n const hpad = 2\n const maxWidth = ctx.width\n const lines = formatText(`\\n${highlighted}\\n`, {\n width: maxWidth - hpad * 2,\n wrap: \"word\",\n })\n let width = Math.max(...lines.map(stringWidth)) + hpad * 2\n width = Math.min(width, maxWidth) // leave room for the border\n // Left-align with a fixed `hpad` of leading whitespace; pad the\n // right to fill the bg out to `width`. Centering would push short\n // lines toward the middle, which reads as visually misaligned code.\n // Lines wider than the available room (width − hpad) drop the\n // leading pad so the bg ends at the cap rather than overshooting.\n const padded = lines.map((line) => {\n const w = stringWidth(line)\n if (w >= width - hpad) return wrap(line)\n return wrap(`${\" \".repeat(hpad)}${line}${\" \".repeat(width - w - hpad)}`)\n })\n const titleLine = meta?.title === undefined ? \"\" : `${s.mdCodeBlockTitle(meta.title)}\\n`\n // Leading `\\n` guards against raw-text siblings that don't emit a\n // paragraph-wrap (e.g. some inline-only list items where the parser\n // drops the paragraph). Containers (`listItem`, document) collapse\n // `\\n{3,}` → `\\n\\n` so paragraph + code doesn't double up.\n return `\\n${titleLine}${padded.join(\"\\n\")}\\n\\n`\n }\n}\n","// oxlint-disable no-multi-assign\nimport type { RenderCtx } from \"../core/ctx.ts\"\nimport type { MdCallbacks } from \"./types.ts\"\n\nimport { splitAnsi, stringWidth } from \"@zaly/shared/ansi\"\n\ninterface ListState {\n block: boolean\n items: string[]\n /** Formatted output, set when the list closes. The parent `listItem`\n * pulls it via `stack[depth+1]` and splices it onto its own body. */\n rendered?: string\n}\n\nconst BULLETS = [\"●\", \"○\", \"◆\", \"◇\"] as const\n\ntype ListCallbacks = Pick<MdCallbacks, \"list\" | \"listItem\">\n\n/**\n * Stack-based list renderer. Each depth has its own `ListState`. Inner\n * lists return `\"\"` and stash their formatted output on `state.rendered`;\n * the parent `listItem` pulls + splices, so inner output never reaches\n * the parent's `children` string. That keeps each level's loose-vs-tight\n * verdict independent — `children.endsWith(\"\\n\\n\")` is then the per-item\n * loose signal (parser only emits `<p>` for items in loose lists).\n *\n * Nested list lands at the end of its parent's body — `text, list, more`\n * is rare in real markdown and not worth the extra plumbing.\n */\nexport function createListCallbacks(ctx: RenderCtx): ListCallbacks {\n const s = ctx.style\n // Lazy-pushed on the first `listItem` at a given depth; consumed\n // either by the parent `listItem` (nested) or by `list(0)` (top).\n const stack: (ListState | undefined)[] = []\n\n return {\n list: (_children, meta) => {\n const state = stack[meta.depth]\n if (state === undefined || state.items.length === 0) return \"\"\n state.rendered = state.items.join(state.block ? \"\\n\\n\" : \"\\n\")\n if (meta.depth > 0) return \"\"\n // Top-level: defensive stack reset + block-contract `\\n\\n` trailer.\n stack.length = 0\n return `${state.rendered}\\n\\n`\n },\n\n listItem: (children, meta) => {\n let marker = s.mdListBullet(\n meta.ordered ? `${(meta.start ?? 1) + meta.index}.` : BULLETS[meta.depth % BULLETS.length]\n )\n if (meta.checked !== undefined) {\n const ref = meta.checked ? \"mdListChecked\" : \"mdListUnchecked\"\n marker += ` ${s.add(ref)(meta.checked ? \"[x]\" : \"[ ]\")}`\n }\n const indent = \" \".repeat(stringWidth(marker))\n // Pull nested list output (if any) off the stack.\n const nested = stack[meta.depth + 1]\n stack[meta.depth + 1] = undefined\n // Trailing `\\n\\n` = loose signal. Nested output is in `rendered`,\n // not `children`, so it can't pollute this check.\n const block = children.endsWith(\"\\n\\n\")\n const trimmed = children.replace(/\\s*\\n+$/, \"\").replace(/\\n{3,}/g, \"\\n\\n\")\n // Splice nested after the prose; loose item adds a blank-row gap.\n let body = trimmed\n if (nested?.rendered !== undefined) {\n const sep = block ? \"\\n\\n\" : \"\\n\"\n body = trimmed === \"\" ? nested.rendered : `${trimmed}${sep}${nested.rendered}`\n }\n const lines = splitAnsi(body)\n const content = lines\n .map((line, l) => {\n if (l === 0) return `${marker} ${line}`\n // Bare empty lines so loose `\\n\\n` separators stay clean.\n return line === \"\" ? \"\" : `${indent}${line}`\n })\n .join(\"\\n\")\n\n const state = (stack[meta.depth] ??= { block: false, items: [] })\n state.block ||= block\n state.items.push(content)\n return \"\"\n },\n }\n}\n","import type { RenderCtx } from \"../core/ctx.ts\"\nimport type { Layout } from \"../core/state.ts\"\nimport type { RowItem } from \"../layout/flex.ts\"\nimport type { MdCallbacks } from \"./types.ts\"\n\nimport { stringWidth } from \"@zaly/shared/ansi\"\nimport { allocateRow } from \"../layout/flex.ts\"\nimport { calcLayout, formatText } from \"../layout/text.ts\"\n\ntype CellAlign = \"left\" | \"center\" | \"right\"\n\ninterface TableCell {\n text: string\n align: CellAlign | undefined\n}\n\ninterface TableState {\n header: TableCell[][]\n body: TableCell[][]\n pending: TableCell[]\n pendingIsHeader: boolean\n}\n\ntype TableCallbacks = Pick<MdCallbacks, \"table\" | \"tbody\" | \"td\" | \"th\" | \"thead\" | \"tr\">\n\n/**\n * Build the table-related callbacks. They share a closure-local\n * `TableState` accumulator: `th`/`td` push cells, `tr` commits rows into\n * header or body, and `table` runs the final layout + reset. Tables can't\n * nest in markdown, so a single top-level slot is enough.\n */\nexport function createTableCallbacks(ctx: RenderCtx): TableCallbacks {\n const { style } = ctx\n let state: TableState | undefined\n const begin = (): TableState =>\n (state ??= { body: [], header: [], pending: [], pendingIsHeader: false })\n\n return {\n table: () => {\n // Children of th/td/tr/thead/tbody are empty strings; all content is\n // in `state`. Format, reset, emit.\n if (state === undefined) return \"\"\n const out = formatTable(state, ctx)\n state = undefined\n return `${out}\\n\\n`\n },\n\n tbody: () => \"\",\n\n td: (children, meta) => {\n const t = begin()\n t.pending.push({ align: meta?.align, text: children })\n t.pendingIsHeader = false\n return \"\"\n },\n\n th: (children, meta) => {\n const t = begin()\n t.pending.push({ align: meta?.align, text: style.mdTableHeader(children) })\n t.pendingIsHeader = true\n return \"\"\n },\n\n thead: () => \"\",\n\n tr: () => {\n if (state === undefined || state.pending.length === 0) return \"\"\n if (state.pendingIsHeader) state.header.push(state.pending)\n else state.body.push(state.pending)\n state.pending = []\n return \"\"\n },\n }\n}\n\nfunction formatTable(state: TableState, ctx: RenderCtx): string {\n const cols: Layout[] = []\n const cells = [...state.header, ...state.body]\n const colCount = Math.max(...cells.map((r) => r.length))\n\n // 1. Calculate min-content and max-content for each column\n for (let c = 0; c < colCount; c++) {\n for (const row of cells) {\n const cell = row[c] as TableCell | undefined\n if (!cell) continue // Safety guard for ragged rows\n const l = calcLayout(cell.text)\n cols[c] ??= { ...l }\n cols[c].width = Math.max(cols[c].width, l.width)\n cols[c].minWidth = Math.max(cols[c].minWidth, l.minWidth)\n }\n }\n const chromeWidth = 3 * colCount + 1\n\n const items: RowItem[] = cols.map((c) => ({\n flex: 1,\n intrinsicMin: c.minWidth,\n natural: c.width,\n }))\n\n const widths = allocateRow(items, { contentWidth: ctx.width - chromeWidth, gap: 0 })\n\n // 4. Render output string using the exact final Column Widths\n // Pre-style each piece of chrome once. Each column span on a rule row is\n // `widths[i] + 2` dashes (one cell of padding on each side of the cell).\n const style = ctx.style\n const v = style.mdTable(\"│\")\n const ruleRow = (left: string, mid: string, right: string): string =>\n style.mdTable(left + widths.map((w) => \"─\".repeat(w + 2)).join(mid) + right)\n const top = ruleRow(\"┌\", \"┬\", \"┐\")\n const sep = ruleRow(\"├\", \"┼\", \"┤\")\n const bot = ruleRow(\"└\", \"┴\", \"┘\")\n\n const renderRow = (row: TableCell[]): string => {\n const formatted = row.map((cell, c) =>\n formatText(cell.text, { indent: false, width: widths[c], wrapBg: false })\n )\n const rows: string[] = []\n const rowCount = Math.max(...formatted.map((r) => r.length))\n for (let r = 0; r < rowCount; r++) {\n const line: string[] = []\n for (let c = 0; c < colCount; c++) {\n const t = formatted[c]?.[r] ?? \"\"\n line.push(padCell(t, widths[c], row[c]?.align))\n }\n rows.push(`${v} ${line.join(` ${v} `)} ${v}`)\n }\n\n return rows.join(\"\\n\")\n }\n\n const lines: string[] = [top]\n for (const row of state.header) lines.push(renderRow(row))\n if (state.header.length > 0 && state.body.length > 0) lines.push(sep)\n for (const row of state.body) lines.push(renderRow(row))\n lines.push(bot)\n return lines.join(\"\\n\")\n}\n\nfunction padCell(text: string, width: number, align: CellAlign | undefined): string {\n const slack = Math.max(0, width - stringWidth(text))\n if (align === \"right\") return \" \".repeat(slack) + text\n if (align === \"center\") {\n const left = Math.floor(slack / 2)\n return \" \".repeat(left) + text + \" \".repeat(slack - left)\n }\n return text + \" \".repeat(slack)\n}\n","import type { AnyStyle, ThemeKey } from \"../style/types.ts\"\nimport type { MarkdownCtx } from \"./renderer.ts\"\nimport type { MdCallbacks } from \"./types.ts\"\n\nimport { stringWidth } from \"@zaly/shared/ansi\"\nimport { capitalize } from \"../layout/text.ts\"\nimport { hyperlink } from \"../style/ansi.ts\"\nimport { createCodeCallback } from \"./code.ts\"\nimport { createListCallbacks } from \"./list.ts\"\nimport { createTableCallbacks } from \"./table.ts\"\n\nconst icons = {\n hr: \"─\",\n quote: \"┃\",\n} as const\n\ntype AlertType = \"NOTE\" | \"TIP\" | \"WARNING\" | \"IMPORTANT\" | \"CAUTION\"\nconst alerts: Record<AlertType, { icon: string; style: AnyStyle }> = {\n CAUTION: { icon: \"✖ \", style: \"error\" },\n IMPORTANT: { icon: \"‼\", style: \"syntaxConstant\" },\n NOTE: { icon: \"ℹ \", style: \"info\" },\n TIP: { icon: \"🛈 \", style: \"success\" },\n WARNING: { icon: \"⚠\", style: \"warn\" },\n}\n\nconst alertRe = /^\\s*\\[!(NOTE|TIP|WARNING|IMPORTANT|CAUTION)\\]\\s*$/\n\n/**\n * Build the `MdCallbacks` that drive the theme-aware rendering. Exposed so\n * callers can invoke `renderMarkdown` directly when they need the string\n * output without the wrapping `Markdown` node (e.g. to embed markdown inside\n * a custom `Text` content function).\n * @internal\n */\nexport function createCallbacks(ctx: MarkdownCtx): MdCallbacks {\n const s = ctx.style\n\n return {\n blockquote: (children) => {\n const lines = children.replace(/\\n+$/, \"\").split(\"\\n\")\n let lineStyle: AnyStyle = \"mdQuote\"\n\n const m = lines.length >= 1 ? lines[0].match(alertRe) : undefined\n if (m) {\n const alertType = m[1] as AlertType\n const alert = alerts[alertType]\n lineStyle = alert.style\n const name = capitalize(alertType.toLowerCase())\n lines[0] = s.add(lineStyle)(`${alert.icon} ${name}`)\n }\n\n // Inner blocks (typically a paragraph) end with trailing newlines; if\n // we prefix those empty lines with \"│ \" they render as styled empty\n // rows between the last line of the quote and the block separator.\n // Trim first so the quote stops cleanly.\n const prefixed = lines.map((line) => `${s.add(lineStyle)(icons.quote)} ${line}`).join(\"\\n\")\n return `${prefixed}\\n\\n`\n },\n\n code: createCodeCallback(ctx),\n\n // Image markers use code spans for parser protection, not code styling.\n codespan: (text) => (/^<img id=\\d+>$/.test(text) ? text : s.mdCode(text)),\n\n emphasis: (children) => s.mdItalic(children),\n\n heading: (children, { level }) => {\n const width = ctx.width\n const padded = children\n .split(\"\\n\")\n .map((line) => line + \" \".repeat(Math.max(0, width - stringWidth(line))))\n .join(\"\\n\")\n return `${s.add(`mdHeading${level}` as ThemeKey)(padded)}\\n\\n`\n },\n\n hr: () => `${s.mdHr(icons.hr.repeat(ctx.width))}\\n\\n`,\n\n html: (children) => children,\n\n link: (children, { href }) => hyperlink(href, s.mdLink(children)),\n\n paragraph: (children) => `${children}\\n\\n`,\n\n strikethrough: (children) => s.mdStrikethrough(children),\n\n strong: (children) => s.mdBold(children),\n\n ...createListCallbacks(ctx),\n ...createTableCallbacks(ctx),\n }\n}\n"],"mappings":"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"}
@@ -1,8 +1,8 @@
1
- import { n as text } from "./text-BArwVVkg.mjs";
1
+ import { n as text } from "./text-H_2kzwaz.mjs";
2
2
  // src/widgets/divider.ts
3
3
  function divider(state = { style: "divider" }) {
4
4
  return text((ctx) => (state.char ?? "─").repeat(state.length ?? ctx.width), state);
5
5
  }
6
6
  export { divider as t };
7
7
 
8
- //# sourceMappingURL=divider-qEj5prKE.mjs.map
8
+ //# sourceMappingURL=divider-BPmnQ0Eu.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"divider-qEj5prKE.mjs","names":[],"sources":["../src/widgets/divider.ts"],"sourcesContent":["import type { Style } from \"../style/types.ts\"\nimport type { Text } from \"./text.ts\"\n\nimport { text } from \"./text.ts\"\n\nexport type DividerState = Style<false> & {\n char?: string\n length?: number\n}\n\nexport function divider(state: DividerState = { style: \"divider\" }): Text {\n return text((ctx) => (state.char ?? \"─\").repeat(state.length ?? ctx.width), state)\n}\n"],"mappings":";;AAUA,SAAgB,QAAQ,QAAsB,EAAE,OAAO,UAAU,GAAS;CACxE,OAAO,MAAM,SAAS,MAAM,QAAQ,IAAA,CAAK,OAAO,MAAM,UAAU,IAAI,KAAK,GAAG,KAAK;AACnF"}
1
+ {"version":3,"file":"divider-BPmnQ0Eu.mjs","names":[],"sources":["../src/widgets/divider.ts"],"sourcesContent":["import type { Style } from \"../style/types.ts\"\nimport type { Text } from \"./text.ts\"\n\nimport { text } from \"./text.ts\"\n\nexport type DividerState = Style<false> & {\n char?: string\n length?: number\n}\n\nexport function divider(state: DividerState = { style: \"divider\" }): Text {\n return text((ctx) => (state.char ?? \"─\").repeat(state.length ?? ctx.width), state)\n}\n"],"mappings":";;AAUA,SAAgB,QAAQ,QAAsB,EAAE,OAAO,UAAU,GAAS;CACxE,OAAO,MAAM,SAAS,MAAM,QAAQ,IAAA,CAAK,OAAO,MAAM,UAAU,IAAI,KAAK,GAAG,KAAK;AACnF"}
package/dist/index.mjs CHANGED
@@ -16,7 +16,7 @@ async function createCtx(opts = {}) {
16
16
  // src/renderer/index.ts
17
17
  /** Factory shorthand for `new Renderer(opts)`. */
18
18
  async function createRenderer(opts = {}) {
19
- const { Renderer } = await import("./renderer-B9sAMA10.mjs");
19
+ const { Renderer } = await import("./renderer-OplFCBMq.mjs");
20
20
  return new Renderer(opts);
21
21
  }
22
22
  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, parseTerminalColor, peek, provideContext, shikiTheme, shikiThemes, signal, toAccessor, untrack, unwrap, useActiveOwner, useContext, withActiveNode, withOwner };
@@ -1,6 +1,6 @@
1
1
  import { S as unwrap, x as untrack } from "./reactive-DkP75TPk.mjs";
2
2
  import { t as Node } from "./node-CXNobGaP.mjs";
3
- import { o as formatText } from "./text-4arC3odI.mjs";
3
+ import { o as formatText } from "./text-BEuhhxr7.mjs";
4
4
  import { t as clipboard } from "./clipboard-UEoX9sup.mjs";
5
5
  import { hash, isPromiseLike } from "@zaly/shared";
6
6
  import { sliceAnsi, splitAnsi, stringWidth } from "@zaly/shared/ansi";
@@ -498,4 +498,4 @@ function countLines(value) {
498
498
  }
499
499
  export { input as n, Input as t };
500
500
 
501
- //# sourceMappingURL=input-BkE4EpBw.mjs.map
501
+ //# sourceMappingURL=input-CtGmVrKa.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"input-BkE4EpBw.mjs","names":[],"sources":["../src/widgets/input.ts"],"sourcesContent":["import type { MaybePromise } from \"@zaly/shared\"\nimport type { DetectedFile } from \"@zaly/shared/detect\"\nimport type { RenderCtx } from \"../core/ctx.ts\"\nimport type { BaseEvents } from \"../core/node.ts\"\nimport type { Reactive } from \"../core/reactive.ts\"\nimport type { StyleState } from \"../core/state.ts\"\nimport type { NodeActionMap } from \"../input/actions.ts\"\nimport type { RoutedKey } from \"../input/router.ts\"\nimport type { Size } from \"../layout/size.ts\"\nimport type { StyleBuilder } from \"../style/builder.ts\"\n\nimport { hash, isPromiseLike } from \"@zaly/shared\"\nimport { sliceAnsi, splitAnsi, stringWidth } from \"@zaly/shared/ansi\"\nimport { fileDetect } from \"@zaly/shared/detect\"\nimport { basename } from \"pathe\"\nimport { Node } from \"../core/node.ts\"\nimport { untrack, unwrap } from \"../core/reactive.ts\"\nimport { clipboard } from \"../input/clipboard.ts\"\nimport { formatText } from \"../layout/text.ts\"\n\nexport interface InputState extends StyleState {\n /** Current text content. May include `\\n` for multi-line input. */\n value?: string\n /** Shown dim when `value` is empty. */\n placeholder?: Reactive<string | undefined>\n /** Cursor position as a char index in `value`. Clamped to `[0, value.length]`. */\n cursor?: number\n /** Render width. Defaults to `fill`. */\n width?: Size\n /** Submitted input history, newest last. */\n history?: readonly string[]\n /** Threshold for showing pasted content as an attachment rather than raw text */\n pasteMaxLines?: number\n pasteMaxChars?: number\n canAttach?: (file: InputAttachment) => boolean\n format?: (value: string, ctx: { style: StyleBuilder }) => MaybePromise<string>\n validate?: (value: string) => boolean\n}\n\nexport type InputValue = { value: string; attachments: InputAttachment[] }\n\nconst PASTE_MAX_LINES = 5\nconst PASTE_MAX_CHARS = 1000\nconst graphemes = new Intl.Segmenter(undefined, { granularity: \"grapheme\" })\n\n/** A clipboard or bracketed paste that exceeds the `pasteMax*` thresholds. **/\ntype Paste = { type: \"paste\"; text: string }\n\n/** A file or image attachment detected from the clipboard. */\nexport type InputAttachment = DetectedFile & { path: string }\n\nexport type InputEvents = BaseEvents & {\n /** Fired when the submitted history changes. */\n history: { history: string[]; added: string }\n /** Fired when plain Enter is pressed. Payload is the current value. */\n submit: { value: string; attachments: InputAttachment[] }\n /** Fired when the user pastes a non-text resource via the\n * `input.paste` action. Images and file references both land here,\n * discriminated by `attachment.kind`. One event fires per image\n * paste; one per file in a multi-file paste.\n *\n * Wrapped under `attachment` because `InputAttachment` carries its\n * own `type` field (MIME) that would shadow the envelope's `type`\n * discriminator if spread directly. */\n attach: { attachment: InputAttachment }\n}\n\ntype FormatEntry = { promise?: Promise<void>; result?: string; input: string }\n\n/**\n * Multi-line text input with auto-grow.\n *\n * Editing commands (`cursorLeft`, `deleteWordBack`, `submit`, …) live on\n * `this.actions` as zero-arg methods. The input router maps keymap\n * bindings like `\"input.cursorLeft\"` onto those methods — users bind\n * different keys by configuring the keymap, not by patching the class.\n * The methods are also callable directly (`input.actions.submit()`),\n * which is handy in tests or for higher-level macros.\n *\n * What this class *still* handles via its `key` event listener:\n * - **Printable character insertion.** An unbound keystroke whose\n * `text` is set and has no ctrl/alt/meta modifier becomes a literal\n * insert at the cursor. There's no action for every printable char\n * in the keymap; the raw-key fallback handles them.\n * - **Paste events.** Whole payload inserted at cursor; always.\n *\n * Everything else (navigation, editing, submit/newline) goes through\n * the router → actions pipeline. Unbound non-printable keys fall back\n * to `emit(\"key\", …)` and bubble to the parent chain.\n *\n * Rendering word-wraps each logical line to `ctx.width`, so long\n * messages naturally grow vertically. The UI surface resizes its\n * reserved footer when the Input's row count changes, so the stream\n * above shifts accordingly.\n */\nexport class Input extends Node<InputState, InputEvents> {\n /** Class-level scope tag used by the input router to bind keymaps. */\n static readonly type = \"input\"\n\n override readonly type = Input.type\n #focused = false\n #history: string[] = []\n #historyDraft = \"\"\n #historyIndex: number | undefined\n #preferredCol: number | undefined\n #staged: ((InputAttachment | Paste) & { id: number; marker: string })[] = []\n #formatCache: Map<string, FormatEntry> & { version?: number } = new Map<string, FormatEntry>()\n #formatGen = 0\n\n /**\n * Editing actions. Parameterless — they close over `this` — so both\n * the router (via keymap dispatch) and application code can invoke\n * them. Add to the keymap in `src/input/actions.ts` to give them\n * default key bindings.\n */\n override actions = {\n \"input.cursorDown\": (): void => {\n const v = this.state.value ?? \"\"\n const { col, line } = posToLineCol(v, this.#cursor())\n if (line >= countLines(v) - 1) {\n this.#historyNext()\n return\n }\n this.#preferredCol ??= col\n this.state.cursor = lineColToPos(v, line + 1, this.#preferredCol)\n },\n \"input.cursorLeft\": (): void => {\n const c = this.#cursor()\n this.#preferredCol = undefined\n this.state.cursor = c > 0 ? c - 1 : c\n },\n \"input.cursorLineEnd\": (): void => {\n const v = this.state.value ?? \"\"\n this.#preferredCol = undefined\n this.state.cursor = lineEndPos(v, this.#cursor())\n },\n \"input.cursorLineStart\": (): void => {\n const v = this.state.value ?? \"\"\n const { line } = posToLineCol(v, this.#cursor())\n this.#preferredCol = undefined\n this.state.cursor = lineColToPos(v, line, 0)\n },\n \"input.cursorRight\": (): void => {\n const v = this.state.value ?? \"\"\n const c = this.#cursor()\n this.#preferredCol = undefined\n this.state.cursor = c < v.length ? c + 1 : c\n },\n \"input.cursorUp\": (): void => {\n const v = this.state.value ?? \"\"\n const { col, line } = posToLineCol(v, this.#cursor())\n if (line === 0) {\n this.#historyPrev()\n return\n }\n this.#preferredCol ??= col\n this.state.cursor = lineColToPos(v, line - 1, this.#preferredCol)\n },\n \"input.deleteCharBack\": (): void => {\n const v = this.state.value ?? \"\"\n const c = this.#cursor()\n this.#preferredCol = undefined\n if (c === 0) return\n this.#historyEdit()\n const marker = this.#markerRange(c, \"back\")\n if (marker) return this.#deleteRange(marker.start, marker.end)\n this.state.set({ cursor: c - 1, value: v.slice(0, c - 1) + v.slice(c) })\n },\n \"input.deleteCharForward\": (): void => {\n const v = this.state.value ?? \"\"\n const c = this.#cursor()\n this.#preferredCol = undefined\n if (c >= v.length) return\n this.#historyEdit()\n const marker = this.#markerRange(c, \"forward\")\n if (marker) return this.#deleteRange(marker.start, marker.end)\n this.state.value = v.slice(0, c) + v.slice(c + 1)\n },\n \"input.deleteWordBack\": (): void => {\n const v = this.state.value ?? \"\"\n const c = this.#cursor()\n this.#preferredCol = undefined\n let i = c\n while (i > 0 && isWhitespace(v[i - 1])) i--\n while (i > 0 && !isWhitespace(v[i - 1])) i--\n if (i === c) return\n this.#historyEdit()\n this.state.set({ cursor: i, value: v.slice(0, i) + v.slice(c) })\n },\n \"input.insertNewline\": (): void => {\n // Smart indent: copy the leading whitespace of the current\n // logical line onto the new line so continuations stay aligned\n // with the bullet / quote / prefix the user typed.\n const v = this.state.value ?? \"\"\n const c = this.#cursor()\n this.#preferredCol = undefined\n let lineStart = c\n while (lineStart > 0 && v[lineStart - 1] !== \"\\n\") lineStart--\n let indent = \"\"\n for (let i = lineStart; i < c; i++) {\n const ch = v[i]\n if (ch === \" \" || ch === \"\\t\") indent += ch\n else break\n }\n const insert = `\\n${indent}`\n this.#historyEdit()\n this.state.set({\n cursor: c + insert.length,\n value: v.slice(0, c) + insert + v.slice(c),\n })\n },\n \"input.insertTab\": (): void => {\n // Two spaces rather than `\\t` — terminal tab stops are\n // unpredictable (often 8 cells) and play badly with our per-row\n // layout. Soft tabs are the safer default; users wanting real\n // tabs can override the action.\n const v = this.state.value ?? \"\"\n const c = this.#cursor()\n this.#preferredCol = undefined\n const tab = \" \"\n this.#historyEdit()\n this.state.set({ cursor: c + tab.length, value: v.slice(0, c) + tab + v.slice(c) })\n },\n // `ctrl-v` queries the OS clipboard (via xclip / wl-paste / pbpaste /\n // PowerShell depending on platform). The richest content wins:\n //\n // - **image bytes** → one `attach` event with a temp PNG path.\n // - **file references** → one `attach` event per file.\n // - **plain text** → inserted at the cursor like a regular paste.\n //\n // Fire-and-forget async — we await the probe and commit the\n // result in a microtask.\n \"input.paste\": (): void => {\n void (async (): Promise<void> => {\n const content = await clipboard.read()\n if (!content) return\n\n // Route through the regular paste handler\n if (content.kind === \"text\") {\n void this.emit(\"paste\", {\n paste: {\n stop: () => void 0,\n stopped: false,\n text: content.text,\n },\n })\n return\n }\n\n const paths: string[] = content.kind === \"image\" ? [content.path] : content.paths\n const files = await Promise.all(paths.map((path) => fileDetect({ path })))\n for (let i = 0; i < paths.length; i++) {\n const path = paths[i]\n const file = files[i]\n if (!file) continue\n this.attach({ ...file, path })\n }\n })()\n },\n \"input.submit\": (): void => {\n const value = this.state.value ?? \"\"\n if (this.state.validate?.(value) === false) return\n const ret = this.consume()\n this.#historyAdd(ret.value)\n void this.emit(\"submit\", ret)\n },\n } satisfies NodeActionMap\n\n constructor(initial: InputState = {}) {\n const value = initial.value ?? \"\"\n super({ cursor: value.length, value, ...initial })\n this.#history = [...(initial.history ?? [])]\n this.on(\"key\", ({ key }) => {\n this.#handleKey(key)\n })\n this.on(\"paste\", ({ paste }) => {\n this.paste(paste.text)\n paste.stop()\n })\n this.on(\"focus\", () => {\n this.#focused = true\n this.invalidate()\n })\n this.on(\"blur\", () => {\n this.#focused = false\n this.invalidate()\n })\n\n this.on(\"mount\", () => {\n this.ctx?.input.events.on(\"terminal-focus\", () => this.invalidate(), {\n signal: this.mountSignal,\n })\n })\n }\n\n get history(): readonly string[] {\n return this.#history\n }\n\n set history(v: readonly string[]) {\n this.#history = [...v]\n this.#historyIndex = undefined\n }\n\n attach(att: InputAttachment | Paste): void {\n if (att.type !== \"paste\" && this.state.canAttach?.(att) === false) {\n // Caller doesn't want this attachment (e.g. model doesn't support it) — fall\n // back to pasting the path as plain text\n return this.insert(att.path)\n }\n\n const id = this.#staged.length + 1\n let prefix = att.type[0].toUpperCase() + att.type.slice(1)\n let suffix = \"\"\n if (att.type === \"paste\") {\n prefix = \"Pasted text\"\n suffix = ` +${countLines(att.text)} lines`\n } else if (![\"image\", \"pdf\"].includes(att.type)) suffix = ` ${basename(att.path)}`\n const marker = `[${prefix} #${id}${suffix}]`\n this.#staged.push({ ...att, id, marker })\n this.insert(marker)\n\n if (att.type !== \"paste\") void this.emit(\"attach\", { attachment: att })\n }\n\n paste(text: string): void {\n if (\n countLines(text) > (this.state.pasteMaxLines ?? PASTE_MAX_LINES) ||\n stringWidth(text) > (this.state.pasteMaxChars ?? PASTE_MAX_CHARS)\n )\n return this.attach({ text, type: \"paste\" })\n this.insert(text)\n }\n\n insert(text: string): void {\n const v = this.state.value ?? \"\"\n const c = this.#cursor()\n this.#historyEdit()\n this.#preferredCol = undefined\n this.state.set({\n cursor: c + text.length,\n value: v.slice(0, c) + text + v.slice(c),\n })\n }\n\n replace(text: string): void {\n this.state.value = \"\"\n this.insert(text)\n }\n\n /** Consume the input's current value and attachments.\n * Pastes are replaced inline with their text; file/image\n * attachments are returned in the `attachments` array and removed from the value. */\n consume(): InputValue {\n let value = this.state.value ?? \"\"\n const atts: InputAttachment[] = []\n for (const att of this.#staged) {\n if (!value.includes(att.marker)) continue\n if (att.type === \"paste\") {\n value = value.replace(att.marker, att.text)\n } else atts.push(att)\n }\n this.#staged = []\n this.#preferredCol = undefined\n this.state.set({ cursor: 0, value: \"\" })\n return { attachments: atts, value }\n }\n\n #cursor(): number {\n const v = this.state.value ?? \"\"\n return Math.max(0, Math.min(v.length, this.state.cursor ?? 0))\n }\n\n #deleteRange(start: number, end: number): void {\n const v = this.state.value ?? \"\"\n this.#historyEdit()\n this.#preferredCol = undefined\n this.state.set({ cursor: start, value: v.slice(0, start) + v.slice(end) })\n }\n\n historyAdd(value: string): void {\n this.#historyAdd(value)\n }\n\n #historyAdd(value: string): void {\n if (value.trim() === \"\") return\n if (this.#history.at(-1) !== value) {\n this.#history.push(value)\n void this.emit(\"history\", { added: value, history: [...this.#history] })\n }\n this.#historyDraft = \"\"\n this.#historyIndex = undefined\n }\n\n #historyEdit(): void {\n this.#historyDraft = \"\"\n this.#historyIndex = undefined\n }\n\n #historyNext(): void {\n if (this.#historyIndex === undefined) return\n const next = this.#historyIndex + 1\n if (next >= this.#history.length) {\n this.#historySet(this.#historyDraft)\n this.#historyDraft = \"\"\n this.#historyIndex = undefined\n return\n }\n this.#historyIndex = next\n this.#historySet(this.#history[next])\n }\n\n #historyPrev(): void {\n if (this.#history.length === 0) return\n if (this.#historyIndex === undefined) {\n this.#historyDraft = this.state.value ?? \"\"\n this.#historyIndex = this.#history.length - 1\n } else if (this.#historyIndex > 0) this.#historyIndex--\n this.#historySet(this.#history[this.#historyIndex])\n }\n\n #historySet(value: string): void {\n this.#preferredCol = undefined\n this.state.set({ cursor: value.length, value })\n }\n\n #markerRange(pos: number, dir: \"back\" | \"forward\"): { start: number; end: number } | undefined {\n const value = this.state.value ?? \"\"\n for (const att of this.#staged) {\n const start = value.indexOf(att.marker)\n if (start === -1) continue\n const end = start + att.marker.length\n if (dir === \"back\" && pos > start && pos <= end) return { end, start }\n if (dir === \"forward\" && pos >= start && pos < end) return { end, start }\n }\n }\n\n // Fallback path for anything the router couldn't resolve to a named\n // action: printable characters go straight into the value. Any other\n // unbound key (f7, unclaimed ctrl-combos, etc.) bubbles untouched.\n #handleKey(ev: RoutedKey): void {\n if (ev.text === undefined || ev.ctrl || ev.alt || ev.meta) return\n this.insert(ev.text)\n ev.stop()\n }\n\n #format(value: string, ctx: RenderCtx): string {\n const format = this.state.format\n if (!format) return value\n\n if (ctx.version !== this.#formatCache.version) {\n this.#formatCache.clear()\n this.#formatCache.version = ctx.version\n }\n\n const key = hash(value)\n const cached = this.#formatCache.get(key)\n if (cached?.result !== undefined) return cached.result\n const streaming = this.#streamingFormat(value)\n if (cached?.promise) return streaming ?? value\n\n const ret = format(value, { style: ctx.style })\n if (!isPromiseLike(ret)) return ret\n\n const gen = ++this.#formatGen\n const entry: FormatEntry = { input: value }\n entry.promise = ret.then(\n (result) => {\n if (this.#formatCache.get(key) !== entry) return\n entry.result = result\n entry.promise = undefined\n if (this.#formatGen === gen && result !== value) untrack(() => this.invalidate())\n },\n () => {\n if (this.#formatCache.get(key) === entry) this.#formatCache.delete(key)\n }\n )\n this.#formatCache.set(key, entry)\n while (this.#formatCache.size > 100)\n this.#formatCache.delete(this.#formatCache.keys().next().value!)\n return streaming ?? value\n }\n\n /** Reuse a highlighted prefix while async formatting catches up. */\n #streamingFormat(value: string): string | undefined {\n let ret: [string, FormatEntry] | undefined\n for (const [key, cached] of this.#formatCache) {\n if (cached.result === undefined) continue\n if (value.startsWith(cached.input) && cached.input.length > (ret?.[1].input.length ?? 0))\n ret = [key, cached]\n }\n if (ret === undefined) return\n this.#formatCache.delete(ret[0])\n this.#formatCache.set(ret[0], ret[1]) // bump to end of LRU\n return ret[1].result + value.slice(ret[1].input.length)\n }\n\n protected async _render(ctx: RenderCtx): Promise<string[]> {\n const value = this.state.value ?? \"\"\n const placeholder = this.state.placeholder ?? \"\"\n const cursor = this.#cursor()\n const focused = this.#focused\n\n // Build the styled content string. Layout (word-wrap, split into\n // rows, pad to width) is delegated to `Text` below — wrap-ansi knows\n // how to close+reopen SGR across line breaks, so the inverse-cursor\n // span survives wrapping.\n let content: string\n if (value === \"\") {\n const ph = unwrap(placeholder)\n content = ph ? ` ${ctx.style.quiet(ph)}` : \"\"\n } else {\n content = value\n for (const att of this.#staged) {\n if (!content.includes(att.marker)) continue\n content = content.replace(att.marker, ctx.style.accent(att.marker))\n }\n content = this.#format(content, ctx)\n }\n\n // Fake cursor. `cursor` is an absolute index in the raw value, while\n // rendering is line-oriented: newlines consume string indices but not\n // cells in any rendered row. Project the cursor to line/column before\n // slicing the formatted line.\n if (focused && this.ctx?.input.terminalFocus) {\n const { col, line } = posToLineCol(value, cursor)\n const lines = splitAnsi(content)\n const target = Math.max(0, Math.min(line, lines.length - 1))\n const row = lines[target] ?? \"\"\n const head = sliceAnsi(row, 0, col)\n const at = sliceAnsi(row, col, col + 1) || \" \"\n const tail = sliceAnsi(row, col + 1)\n lines[target] = head + ctx.style.inverse(at) + tail\n content = lines.join(\"\\n\")\n }\n\n return formatText(content, {\n style: ctx.style.add(this.state),\n width: ctx.width,\n wrap: \"word\",\n })\n }\n}\n\n/**\n * Factory for `Input`. All fields optional; default is an empty,\n * focusable input that fills `ctx.width` and has no placeholder.\n *\n * ```ts\n * input({ placeholder: \"type a message…\" })\n * const i = input({ value: \"draft\" })\n * i.on(\"submit\", (text) => console.log(\"user typed\", text))\n * ```\n */\nexport function input(state: InputState = {}): Input {\n return new Input(state)\n}\n\n// ---------- helpers ----------\n\nfunction isWhitespace(ch: string | undefined): boolean {\n return ch !== undefined && (ch === \" \" || ch === \"\\t\" || ch === \"\\n\")\n}\n\nfunction posToLineCol(value: string, cursor: number): { line: number; col: number } {\n const prefix = value.slice(0, cursor)\n const lineStart = prefix.lastIndexOf(\"\\n\") + 1\n return {\n col: stringWidth(prefix.slice(lineStart)),\n line: prefix.split(\"\\n\").length - 1,\n }\n}\n\nfunction lineColToPos(value: string, line: number, col: number): number {\n const lines = value.split(\"\\n\")\n const clampLine = Math.max(0, Math.min(line, lines.length - 1))\n let abs = 0\n for (let i = 0; i < clampLine; i++) abs += lines[i].length + 1\n\n let cells = 0\n let offset = 0\n for (const { segment } of graphemes.segment(lines[clampLine])) {\n const width = stringWidth(segment)\n if (cells + width > col) break\n cells += width\n offset += segment.length\n }\n return abs + offset\n}\n\nfunction lineEndPos(value: string, cursor: number): number {\n let i = cursor\n while (i < value.length && value[i] !== \"\\n\") i++\n return i\n}\n\nfunction countLines(value: string): number {\n let n = 1\n for (const ch of value) if (ch === \"\\n\") n++\n return n\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"input-CtGmVrKa.mjs","names":[],"sources":["../src/widgets/input.ts"],"sourcesContent":["import type { MaybePromise } from \"@zaly/shared\"\nimport type { DetectedFile } from \"@zaly/shared/detect\"\nimport type { RenderCtx } from \"../core/ctx.ts\"\nimport type { BaseEvents } from \"../core/node.ts\"\nimport type { Reactive } from \"../core/reactive.ts\"\nimport type { StyleState } from \"../core/state.ts\"\nimport type { NodeActionMap } from \"../input/actions.ts\"\nimport type { RoutedKey } from \"../input/router.ts\"\nimport type { Size } from \"../layout/size.ts\"\nimport type { StyleBuilder } from \"../style/builder.ts\"\n\nimport { hash, isPromiseLike } from \"@zaly/shared\"\nimport { sliceAnsi, splitAnsi, stringWidth } from \"@zaly/shared/ansi\"\nimport { fileDetect } from \"@zaly/shared/detect\"\nimport { basename } from \"pathe\"\nimport { Node } from \"../core/node.ts\"\nimport { untrack, unwrap } from \"../core/reactive.ts\"\nimport { clipboard } from \"../input/clipboard.ts\"\nimport { formatText } from \"../layout/text.ts\"\n\nexport interface InputState extends StyleState {\n /** Current text content. May include `\\n` for multi-line input. */\n value?: string\n /** Shown dim when `value` is empty. */\n placeholder?: Reactive<string | undefined>\n /** Cursor position as a char index in `value`. Clamped to `[0, value.length]`. */\n cursor?: number\n /** Render width. Defaults to `fill`. */\n width?: Size\n /** Submitted input history, newest last. */\n history?: readonly string[]\n /** Threshold for showing pasted content as an attachment rather than raw text */\n pasteMaxLines?: number\n pasteMaxChars?: number\n canAttach?: (file: InputAttachment) => boolean\n format?: (value: string, ctx: { style: StyleBuilder }) => MaybePromise<string>\n validate?: (value: string) => boolean\n}\n\nexport type InputValue = { value: string; attachments: InputAttachment[] }\n\nconst PASTE_MAX_LINES = 5\nconst PASTE_MAX_CHARS = 1000\nconst graphemes = new Intl.Segmenter(undefined, { granularity: \"grapheme\" })\n\n/** A clipboard or bracketed paste that exceeds the `pasteMax*` thresholds. **/\ntype Paste = { type: \"paste\"; text: string }\n\n/** A file or image attachment detected from the clipboard. */\nexport type InputAttachment = DetectedFile & { path: string }\n\nexport type InputEvents = BaseEvents & {\n /** Fired when the submitted history changes. */\n history: { history: string[]; added: string }\n /** Fired when plain Enter is pressed. Payload is the current value. */\n submit: { value: string; attachments: InputAttachment[] }\n /** Fired when the user pastes a non-text resource via the\n * `input.paste` action. Images and file references both land here,\n * discriminated by `attachment.kind`. One event fires per image\n * paste; one per file in a multi-file paste.\n *\n * Wrapped under `attachment` because `InputAttachment` carries its\n * own `type` field (MIME) that would shadow the envelope's `type`\n * discriminator if spread directly. */\n attach: { attachment: InputAttachment }\n}\n\ntype FormatEntry = { promise?: Promise<void>; result?: string; input: string }\n\n/**\n * Multi-line text input with auto-grow.\n *\n * Editing commands (`cursorLeft`, `deleteWordBack`, `submit`, …) live on\n * `this.actions` as zero-arg methods. The input router maps keymap\n * bindings like `\"input.cursorLeft\"` onto those methods — users bind\n * different keys by configuring the keymap, not by patching the class.\n * The methods are also callable directly (`input.actions.submit()`),\n * which is handy in tests or for higher-level macros.\n *\n * What this class *still* handles via its `key` event listener:\n * - **Printable character insertion.** An unbound keystroke whose\n * `text` is set and has no ctrl/alt/meta modifier becomes a literal\n * insert at the cursor. There's no action for every printable char\n * in the keymap; the raw-key fallback handles them.\n * - **Paste events.** Whole payload inserted at cursor; always.\n *\n * Everything else (navigation, editing, submit/newline) goes through\n * the router → actions pipeline. Unbound non-printable keys fall back\n * to `emit(\"key\", …)` and bubble to the parent chain.\n *\n * Rendering word-wraps each logical line to `ctx.width`, so long\n * messages naturally grow vertically. The UI surface resizes its\n * reserved footer when the Input's row count changes, so the stream\n * above shifts accordingly.\n */\nexport class Input extends Node<InputState, InputEvents> {\n /** Class-level scope tag used by the input router to bind keymaps. */\n static readonly type = \"input\"\n\n override readonly type = Input.type\n #focused = false\n #history: string[] = []\n #historyDraft = \"\"\n #historyIndex: number | undefined\n #preferredCol: number | undefined\n #staged: ((InputAttachment | Paste) & { id: number; marker: string })[] = []\n #formatCache: Map<string, FormatEntry> & { version?: number } = new Map<string, FormatEntry>()\n #formatGen = 0\n\n /**\n * Editing actions. Parameterless — they close over `this` — so both\n * the router (via keymap dispatch) and application code can invoke\n * them. Add to the keymap in `src/input/actions.ts` to give them\n * default key bindings.\n */\n override actions = {\n \"input.cursorDown\": (): void => {\n const v = this.state.value ?? \"\"\n const { col, line } = posToLineCol(v, this.#cursor())\n if (line >= countLines(v) - 1) {\n this.#historyNext()\n return\n }\n this.#preferredCol ??= col\n this.state.cursor = lineColToPos(v, line + 1, this.#preferredCol)\n },\n \"input.cursorLeft\": (): void => {\n const c = this.#cursor()\n this.#preferredCol = undefined\n this.state.cursor = c > 0 ? c - 1 : c\n },\n \"input.cursorLineEnd\": (): void => {\n const v = this.state.value ?? \"\"\n this.#preferredCol = undefined\n this.state.cursor = lineEndPos(v, this.#cursor())\n },\n \"input.cursorLineStart\": (): void => {\n const v = this.state.value ?? \"\"\n const { line } = posToLineCol(v, this.#cursor())\n this.#preferredCol = undefined\n this.state.cursor = lineColToPos(v, line, 0)\n },\n \"input.cursorRight\": (): void => {\n const v = this.state.value ?? \"\"\n const c = this.#cursor()\n this.#preferredCol = undefined\n this.state.cursor = c < v.length ? c + 1 : c\n },\n \"input.cursorUp\": (): void => {\n const v = this.state.value ?? \"\"\n const { col, line } = posToLineCol(v, this.#cursor())\n if (line === 0) {\n this.#historyPrev()\n return\n }\n this.#preferredCol ??= col\n this.state.cursor = lineColToPos(v, line - 1, this.#preferredCol)\n },\n \"input.deleteCharBack\": (): void => {\n const v = this.state.value ?? \"\"\n const c = this.#cursor()\n this.#preferredCol = undefined\n if (c === 0) return\n this.#historyEdit()\n const marker = this.#markerRange(c, \"back\")\n if (marker) return this.#deleteRange(marker.start, marker.end)\n this.state.set({ cursor: c - 1, value: v.slice(0, c - 1) + v.slice(c) })\n },\n \"input.deleteCharForward\": (): void => {\n const v = this.state.value ?? \"\"\n const c = this.#cursor()\n this.#preferredCol = undefined\n if (c >= v.length) return\n this.#historyEdit()\n const marker = this.#markerRange(c, \"forward\")\n if (marker) return this.#deleteRange(marker.start, marker.end)\n this.state.value = v.slice(0, c) + v.slice(c + 1)\n },\n \"input.deleteWordBack\": (): void => {\n const v = this.state.value ?? \"\"\n const c = this.#cursor()\n this.#preferredCol = undefined\n let i = c\n while (i > 0 && isWhitespace(v[i - 1])) i--\n while (i > 0 && !isWhitespace(v[i - 1])) i--\n if (i === c) return\n this.#historyEdit()\n this.state.set({ cursor: i, value: v.slice(0, i) + v.slice(c) })\n },\n \"input.insertNewline\": (): void => {\n // Smart indent: copy the leading whitespace of the current\n // logical line onto the new line so continuations stay aligned\n // with the bullet / quote / prefix the user typed.\n const v = this.state.value ?? \"\"\n const c = this.#cursor()\n this.#preferredCol = undefined\n let lineStart = c\n while (lineStart > 0 && v[lineStart - 1] !== \"\\n\") lineStart--\n let indent = \"\"\n for (let i = lineStart; i < c; i++) {\n const ch = v[i]\n if (ch === \" \" || ch === \"\\t\") indent += ch\n else break\n }\n const insert = `\\n${indent}`\n this.#historyEdit()\n this.state.set({\n cursor: c + insert.length,\n value: v.slice(0, c) + insert + v.slice(c),\n })\n },\n \"input.insertTab\": (): void => {\n // Two spaces rather than `\\t` — terminal tab stops are\n // unpredictable (often 8 cells) and play badly with our per-row\n // layout. Soft tabs are the safer default; users wanting real\n // tabs can override the action.\n const v = this.state.value ?? \"\"\n const c = this.#cursor()\n this.#preferredCol = undefined\n const tab = \" \"\n this.#historyEdit()\n this.state.set({ cursor: c + tab.length, value: v.slice(0, c) + tab + v.slice(c) })\n },\n // `ctrl-v` queries the OS clipboard (via xclip / wl-paste / pbpaste /\n // PowerShell depending on platform). The richest content wins:\n //\n // - **image bytes** → one `attach` event with a temp PNG path.\n // - **file references** → one `attach` event per file.\n // - **plain text** → inserted at the cursor like a regular paste.\n //\n // Fire-and-forget async — we await the probe and commit the\n // result in a microtask.\n \"input.paste\": (): void => {\n void (async (): Promise<void> => {\n const content = await clipboard.read()\n if (!content) return\n\n // Route through the regular paste handler\n if (content.kind === \"text\") {\n void this.emit(\"paste\", {\n paste: {\n stop: () => void 0,\n stopped: false,\n text: content.text,\n },\n })\n return\n }\n\n const paths: string[] = content.kind === \"image\" ? [content.path] : content.paths\n const files = await Promise.all(paths.map((path) => fileDetect({ path })))\n for (let i = 0; i < paths.length; i++) {\n const path = paths[i]\n const file = files[i]\n if (!file) continue\n this.attach({ ...file, path })\n }\n })()\n },\n \"input.submit\": (): void => {\n const value = this.state.value ?? \"\"\n if (this.state.validate?.(value) === false) return\n const ret = this.consume()\n this.#historyAdd(ret.value)\n void this.emit(\"submit\", ret)\n },\n } satisfies NodeActionMap\n\n constructor(initial: InputState = {}) {\n const value = initial.value ?? \"\"\n super({ cursor: value.length, value, ...initial })\n this.#history = [...(initial.history ?? [])]\n this.on(\"key\", ({ key }) => {\n this.#handleKey(key)\n })\n this.on(\"paste\", ({ paste }) => {\n this.paste(paste.text)\n paste.stop()\n })\n this.on(\"focus\", () => {\n this.#focused = true\n this.invalidate()\n })\n this.on(\"blur\", () => {\n this.#focused = false\n this.invalidate()\n })\n\n this.on(\"mount\", () => {\n this.ctx?.input.events.on(\"terminal-focus\", () => this.invalidate(), {\n signal: this.mountSignal,\n })\n })\n }\n\n get history(): readonly string[] {\n return this.#history\n }\n\n set history(v: readonly string[]) {\n this.#history = [...v]\n this.#historyIndex = undefined\n }\n\n attach(att: InputAttachment | Paste): void {\n if (att.type !== \"paste\" && this.state.canAttach?.(att) === false) {\n // Caller doesn't want this attachment (e.g. model doesn't support it) — fall\n // back to pasting the path as plain text\n return this.insert(att.path)\n }\n\n const id = this.#staged.length + 1\n let prefix = att.type[0].toUpperCase() + att.type.slice(1)\n let suffix = \"\"\n if (att.type === \"paste\") {\n prefix = \"Pasted text\"\n suffix = ` +${countLines(att.text)} lines`\n } else if (![\"image\", \"pdf\"].includes(att.type)) suffix = ` ${basename(att.path)}`\n const marker = `[${prefix} #${id}${suffix}]`\n this.#staged.push({ ...att, id, marker })\n this.insert(marker)\n\n if (att.type !== \"paste\") void this.emit(\"attach\", { attachment: att })\n }\n\n paste(text: string): void {\n if (\n countLines(text) > (this.state.pasteMaxLines ?? PASTE_MAX_LINES) ||\n stringWidth(text) > (this.state.pasteMaxChars ?? PASTE_MAX_CHARS)\n )\n return this.attach({ text, type: \"paste\" })\n this.insert(text)\n }\n\n insert(text: string): void {\n const v = this.state.value ?? \"\"\n const c = this.#cursor()\n this.#historyEdit()\n this.#preferredCol = undefined\n this.state.set({\n cursor: c + text.length,\n value: v.slice(0, c) + text + v.slice(c),\n })\n }\n\n replace(text: string): void {\n this.state.value = \"\"\n this.insert(text)\n }\n\n /** Consume the input's current value and attachments.\n * Pastes are replaced inline with their text; file/image\n * attachments are returned in the `attachments` array and removed from the value. */\n consume(): InputValue {\n let value = this.state.value ?? \"\"\n const atts: InputAttachment[] = []\n for (const att of this.#staged) {\n if (!value.includes(att.marker)) continue\n if (att.type === \"paste\") {\n value = value.replace(att.marker, att.text)\n } else atts.push(att)\n }\n this.#staged = []\n this.#preferredCol = undefined\n this.state.set({ cursor: 0, value: \"\" })\n return { attachments: atts, value }\n }\n\n #cursor(): number {\n const v = this.state.value ?? \"\"\n return Math.max(0, Math.min(v.length, this.state.cursor ?? 0))\n }\n\n #deleteRange(start: number, end: number): void {\n const v = this.state.value ?? \"\"\n this.#historyEdit()\n this.#preferredCol = undefined\n this.state.set({ cursor: start, value: v.slice(0, start) + v.slice(end) })\n }\n\n historyAdd(value: string): void {\n this.#historyAdd(value)\n }\n\n #historyAdd(value: string): void {\n if (value.trim() === \"\") return\n if (this.#history.at(-1) !== value) {\n this.#history.push(value)\n void this.emit(\"history\", { added: value, history: [...this.#history] })\n }\n this.#historyDraft = \"\"\n this.#historyIndex = undefined\n }\n\n #historyEdit(): void {\n this.#historyDraft = \"\"\n this.#historyIndex = undefined\n }\n\n #historyNext(): void {\n if (this.#historyIndex === undefined) return\n const next = this.#historyIndex + 1\n if (next >= this.#history.length) {\n this.#historySet(this.#historyDraft)\n this.#historyDraft = \"\"\n this.#historyIndex = undefined\n return\n }\n this.#historyIndex = next\n this.#historySet(this.#history[next])\n }\n\n #historyPrev(): void {\n if (this.#history.length === 0) return\n if (this.#historyIndex === undefined) {\n this.#historyDraft = this.state.value ?? \"\"\n this.#historyIndex = this.#history.length - 1\n } else if (this.#historyIndex > 0) this.#historyIndex--\n this.#historySet(this.#history[this.#historyIndex])\n }\n\n #historySet(value: string): void {\n this.#preferredCol = undefined\n this.state.set({ cursor: value.length, value })\n }\n\n #markerRange(pos: number, dir: \"back\" | \"forward\"): { start: number; end: number } | undefined {\n const value = this.state.value ?? \"\"\n for (const att of this.#staged) {\n const start = value.indexOf(att.marker)\n if (start === -1) continue\n const end = start + att.marker.length\n if (dir === \"back\" && pos > start && pos <= end) return { end, start }\n if (dir === \"forward\" && pos >= start && pos < end) return { end, start }\n }\n }\n\n // Fallback path for anything the router couldn't resolve to a named\n // action: printable characters go straight into the value. Any other\n // unbound key (f7, unclaimed ctrl-combos, etc.) bubbles untouched.\n #handleKey(ev: RoutedKey): void {\n if (ev.text === undefined || ev.ctrl || ev.alt || ev.meta) return\n this.insert(ev.text)\n ev.stop()\n }\n\n #format(value: string, ctx: RenderCtx): string {\n const format = this.state.format\n if (!format) return value\n\n if (ctx.version !== this.#formatCache.version) {\n this.#formatCache.clear()\n this.#formatCache.version = ctx.version\n }\n\n const key = hash(value)\n const cached = this.#formatCache.get(key)\n if (cached?.result !== undefined) return cached.result\n const streaming = this.#streamingFormat(value)\n if (cached?.promise) return streaming ?? value\n\n const ret = format(value, { style: ctx.style })\n if (!isPromiseLike(ret)) return ret\n\n const gen = ++this.#formatGen\n const entry: FormatEntry = { input: value }\n entry.promise = ret.then(\n (result) => {\n if (this.#formatCache.get(key) !== entry) return\n entry.result = result\n entry.promise = undefined\n if (this.#formatGen === gen && result !== value) untrack(() => this.invalidate())\n },\n () => {\n if (this.#formatCache.get(key) === entry) this.#formatCache.delete(key)\n }\n )\n this.#formatCache.set(key, entry)\n while (this.#formatCache.size > 100)\n this.#formatCache.delete(this.#formatCache.keys().next().value!)\n return streaming ?? value\n }\n\n /** Reuse a highlighted prefix while async formatting catches up. */\n #streamingFormat(value: string): string | undefined {\n let ret: [string, FormatEntry] | undefined\n for (const [key, cached] of this.#formatCache) {\n if (cached.result === undefined) continue\n if (value.startsWith(cached.input) && cached.input.length > (ret?.[1].input.length ?? 0))\n ret = [key, cached]\n }\n if (ret === undefined) return\n this.#formatCache.delete(ret[0])\n this.#formatCache.set(ret[0], ret[1]) // bump to end of LRU\n return ret[1].result + value.slice(ret[1].input.length)\n }\n\n protected async _render(ctx: RenderCtx): Promise<string[]> {\n const value = this.state.value ?? \"\"\n const placeholder = this.state.placeholder ?? \"\"\n const cursor = this.#cursor()\n const focused = this.#focused\n\n // Build the styled content string. Layout (word-wrap, split into\n // rows, pad to width) is delegated to `Text` below — wrap-ansi knows\n // how to close+reopen SGR across line breaks, so the inverse-cursor\n // span survives wrapping.\n let content: string\n if (value === \"\") {\n const ph = unwrap(placeholder)\n content = ph ? ` ${ctx.style.quiet(ph)}` : \"\"\n } else {\n content = value\n for (const att of this.#staged) {\n if (!content.includes(att.marker)) continue\n content = content.replace(att.marker, ctx.style.accent(att.marker))\n }\n content = this.#format(content, ctx)\n }\n\n // Fake cursor. `cursor` is an absolute index in the raw value, while\n // rendering is line-oriented: newlines consume string indices but not\n // cells in any rendered row. Project the cursor to line/column before\n // slicing the formatted line.\n if (focused && this.ctx?.input.terminalFocus) {\n const { col, line } = posToLineCol(value, cursor)\n const lines = splitAnsi(content)\n const target = Math.max(0, Math.min(line, lines.length - 1))\n const row = lines[target] ?? \"\"\n const head = sliceAnsi(row, 0, col)\n const at = sliceAnsi(row, col, col + 1) || \" \"\n const tail = sliceAnsi(row, col + 1)\n lines[target] = head + ctx.style.inverse(at) + tail\n content = lines.join(\"\\n\")\n }\n\n return formatText(content, {\n style: ctx.style.add(this.state),\n width: ctx.width,\n wrap: \"word\",\n })\n }\n}\n\n/**\n * Factory for `Input`. All fields optional; default is an empty,\n * focusable input that fills `ctx.width` and has no placeholder.\n *\n * ```ts\n * input({ placeholder: \"type a message…\" })\n * const i = input({ value: \"draft\" })\n * i.on(\"submit\", (text) => console.log(\"user typed\", text))\n * ```\n */\nexport function input(state: InputState = {}): Input {\n return new Input(state)\n}\n\n// ---------- helpers ----------\n\nfunction isWhitespace(ch: string | undefined): boolean {\n return ch !== undefined && (ch === \" \" || ch === \"\\t\" || ch === \"\\n\")\n}\n\nfunction posToLineCol(value: string, cursor: number): { line: number; col: number } {\n const prefix = value.slice(0, cursor)\n const lineStart = prefix.lastIndexOf(\"\\n\") + 1\n return {\n col: stringWidth(prefix.slice(lineStart)),\n line: prefix.split(\"\\n\").length - 1,\n }\n}\n\nfunction lineColToPos(value: string, line: number, col: number): number {\n const lines = value.split(\"\\n\")\n const clampLine = Math.max(0, Math.min(line, lines.length - 1))\n let abs = 0\n for (let i = 0; i < clampLine; i++) abs += lines[i].length + 1\n\n let cells = 0\n let offset = 0\n for (const { segment } of graphemes.segment(lines[clampLine])) {\n const width = stringWidth(segment)\n if (cells + width > col) break\n cells += width\n offset += segment.length\n }\n return abs + offset\n}\n\nfunction lineEndPos(value: string, cursor: number): number {\n let i = cursor\n while (i < value.length && value[i] !== \"\\n\") i++\n return i\n}\n\nfunction countLines(value: string): number {\n let n = 1\n for (const ch of value) if (ch === \"\\n\") n++\n return n\n}\n"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
- import { n as box } from "./box-DToDoXlU.mjs";
2
- import { n as text } from "./text-BArwVVkg.mjs";
3
- import { n as markdown } from "./markdown-CgRZBhIX.mjs";
1
+ import { n as box } from "./box-DbIjxh39.mjs";
2
+ import { n as text } from "./text-H_2kzwaz.mjs";
3
+ import { n as markdown } from "./markdown-DvDIPT5L.mjs";
4
4
  import { t as widget } from "./widget-sFveyrZE.mjs";
5
5
  import { stringWidth } from "@zaly/shared/ansi";
6
6
  import { hasColors } from "@zaly/shared/env";
@@ -126,4 +126,4 @@ const log = widget((state, ...children) => {
126
126
  });
127
127
  export { log as t };
128
128
 
129
- //# sourceMappingURL=log-96b1_J6G.mjs.map
129
+ //# sourceMappingURL=log-D6gGicwQ.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"log-96b1_J6G.mjs","names":[],"sources":["../src/widgets/log.ts"],"sourcesContent":["import type { LogLevel } from \"@zaly/shared/logger\"\nimport type { RenderCtx } from \"../core/ctx.ts\"\nimport type { Node } from \"../core/node.ts\"\nimport type { State } from \"../core/state.ts\"\nimport type { Color } from \"../style/types.ts\"\nimport type { TextContent } from \"./text.ts\"\n\nimport { stringWidth } from \"@zaly/shared/ansi\"\nimport { hasColors } from \"@zaly/shared/env\"\nimport { box } from \"./box.ts\"\nimport { markdown } from \"./markdown.ts\"\nimport { text } from \"./text.ts\"\nimport { widget } from \"./widget.ts\"\n\nexport type LogStyle = \"badge\" | \"icon\" | \"prompt\" | \"title\" | \"text\" | \"notif\"\n\nexport interface LogState {\n level: LogLevel\n content: TextContent\n title?: string\n /** Rendering of the per-level prefix chunk. Defaults by level. */\n style?: LogStyle\n /** Single-glyph icon for `icon` / `prompt` styles. */\n icon?: string\n /** Theme slot or color name used to tint the prefix. */\n color?: Color<false>\n /** Extra plain text prepended to the auto prefix (unstyled). */\n prefix?: string\n /** Theme slot or color name for the body text. Defaults by level, except */\n textColor?: Color<false>\n markdown?: boolean\n}\n\ninterface LevelDefaults {\n style: LogStyle\n icon?: string\n color?: Color<false>\n textColor?: Color<false>\n}\n\n/** Per-level default prefix style + icon + color. Matches rekal's\n * `defaultStyles` table, adapted to zaly's theme slots. */\nconst defaultLogStyles: Record<LogLevel, LevelDefaults> = {\n cancel: { color: \"warn\", icon: \"✖ \", style: \"icon\" },\n debug: { color: \"info\", icon: \"⚙ \", style: \"prompt\" },\n error: { color: \"error\", icon: \"✖ \", style: \"badge\", textColor: \"error\" },\n fatal: { color: \"error\", icon: \"☢ \", style: \"badge\" },\n info: { color: \"info\", icon: \"ℹ \", style: \"icon\" },\n log: { color: \"muted\", icon: \"●\", style: \"icon\" },\n success: { color: \"success\", icon: \"✔ \", style: \"icon\" },\n trace: { color: \"muted\", icon: \"⠿\", style: \"icon\", textColor: \"muted\" },\n warn: { color: \"warn\", icon: \"⚠\", style: \"badge\", textColor: \"warn\" },\n}\n\nconst noColorStyles: Record<LogStyle, LogStyle> = {\n badge: \"title\",\n icon: \"text\",\n notif: \"text\",\n prompt: \"text\",\n text: \"text\",\n title: \"text\",\n}\n\nfunction renderPrefix(s: LogState, ctx: RenderCtx): string {\n const style = ctx.style\n\n const base = defaultLogStyles[s.level]\n let ls: LogStyle = s.style ?? base.style\n if (!hasColors) ls = noColorStyles[ls]\n\n let icon = s.icon ?? base.icon ?? \"\"\n icon = icon === \"\" ? \"\" : `${icon}${\" \".repeat(2 - stringWidth(icon))}`\n const color: Color<false> = s.color ?? base.color ?? \"inherit\"\n\n // text doesn't get a prefix\n let styledPrefix = \"\"\n if (ls === \"badge\") {\n styledPrefix = style.bg(color).bold(` ${s.level} `)\n } else if (ls === \"icon\") {\n styledPrefix = style.fg(color)(icon)\n } else if (ls === \"prompt\") {\n styledPrefix = style.fg(color).bold(`${icon} ${s.level}`)\n } else if (ls === \"title\") {\n styledPrefix = style.fg(color).bold(`${s.level}:`)\n } else if (ls === \"notif\") {\n styledPrefix = style.fg(color).bold(`${icon} ${s.title ?? s.level}`)\n }\n\n if (s.prefix) styledPrefix = s.prefix + styledPrefix\n if (s.title && ls !== \"notif\") styledPrefix += style.bold(` ${s.title}`)\n return styledPrefix\n}\n\n/**\n * A single log entry — level-styled prefix (icon / badge / prompt / title /\n * plain text) followed by the body content. The body can be any Node, so\n * callers can stack markdown, code, images, etc. inside a log line.\n *\n * ```ts\n * log({ level: \"info\", content: \"hello\" })\n * log({ level: \"error\", content: markdown(\"**boom**\") })\n * ```\n */\nexport const log = widget((state: State<LogState>, ...children: Node[]) => {\n const base = defaultLogStyles[state.level]\n const color: Color = state.color ?? base.color ?? \"inherit\"\n const s = state\n const textColor = state.textColor ?? (s.style === \"notif\" ? \"inherit\" : base.textColor)\n const style = {\n style: textColor\n ? {\n fg: textColor,\n }\n : undefined,\n width: \"fill\",\n } as const\n if (state.content !== \"\") {\n children = [\n (state.markdown ?? true) ? markdown(state.content, style) : text(state.content, style),\n ...children,\n ]\n }\n return box(\n {\n ...(s.style === \"notif\"\n ? {\n border: \"single\",\n borderStyle: { fg: color },\n borderTitle: (ctx) => renderPrefix(state, ctx),\n borderTitleAlign: \"center\",\n padding: [0, 1],\n //style: \"overlay\",\n width: \"fill\",\n }\n : undefined),\n flexDirection: \"row\",\n gap: 1,\n visible: state.visible,\n },\n s.style === \"notif\"\n ? undefined\n : text((ctx) => renderPrefix(state, ctx), { flexShrink: 0, wrap: \"none\" }),\n box({ flexDirection: \"column\", flexGrow: 1, gap: 1 }, ...children)\n )\n})\n"],"mappings":"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"}
1
+ {"version":3,"file":"log-D6gGicwQ.mjs","names":[],"sources":["../src/widgets/log.ts"],"sourcesContent":["import type { LogLevel } from \"@zaly/shared/logger\"\nimport type { RenderCtx } from \"../core/ctx.ts\"\nimport type { Node } from \"../core/node.ts\"\nimport type { State } from \"../core/state.ts\"\nimport type { Color } from \"../style/types.ts\"\nimport type { TextContent } from \"./text.ts\"\n\nimport { stringWidth } from \"@zaly/shared/ansi\"\nimport { hasColors } from \"@zaly/shared/env\"\nimport { box } from \"./box.ts\"\nimport { markdown } from \"./markdown.ts\"\nimport { text } from \"./text.ts\"\nimport { widget } from \"./widget.ts\"\n\nexport type LogStyle = \"badge\" | \"icon\" | \"prompt\" | \"title\" | \"text\" | \"notif\"\n\nexport interface LogState {\n level: LogLevel\n content: TextContent\n title?: string\n /** Rendering of the per-level prefix chunk. Defaults by level. */\n style?: LogStyle\n /** Single-glyph icon for `icon` / `prompt` styles. */\n icon?: string\n /** Theme slot or color name used to tint the prefix. */\n color?: Color<false>\n /** Extra plain text prepended to the auto prefix (unstyled). */\n prefix?: string\n /** Theme slot or color name for the body text. Defaults by level, except */\n textColor?: Color<false>\n markdown?: boolean\n}\n\ninterface LevelDefaults {\n style: LogStyle\n icon?: string\n color?: Color<false>\n textColor?: Color<false>\n}\n\n/** Per-level default prefix style + icon + color. Matches rekal's\n * `defaultStyles` table, adapted to zaly's theme slots. */\nconst defaultLogStyles: Record<LogLevel, LevelDefaults> = {\n cancel: { color: \"warn\", icon: \"✖ \", style: \"icon\" },\n debug: { color: \"info\", icon: \"⚙ \", style: \"prompt\" },\n error: { color: \"error\", icon: \"✖ \", style: \"badge\", textColor: \"error\" },\n fatal: { color: \"error\", icon: \"☢ \", style: \"badge\" },\n info: { color: \"info\", icon: \"ℹ \", style: \"icon\" },\n log: { color: \"muted\", icon: \"●\", style: \"icon\" },\n success: { color: \"success\", icon: \"✔ \", style: \"icon\" },\n trace: { color: \"muted\", icon: \"⠿\", style: \"icon\", textColor: \"muted\" },\n warn: { color: \"warn\", icon: \"⚠\", style: \"badge\", textColor: \"warn\" },\n}\n\nconst noColorStyles: Record<LogStyle, LogStyle> = {\n badge: \"title\",\n icon: \"text\",\n notif: \"text\",\n prompt: \"text\",\n text: \"text\",\n title: \"text\",\n}\n\nfunction renderPrefix(s: LogState, ctx: RenderCtx): string {\n const style = ctx.style\n\n const base = defaultLogStyles[s.level]\n let ls: LogStyle = s.style ?? base.style\n if (!hasColors) ls = noColorStyles[ls]\n\n let icon = s.icon ?? base.icon ?? \"\"\n icon = icon === \"\" ? \"\" : `${icon}${\" \".repeat(2 - stringWidth(icon))}`\n const color: Color<false> = s.color ?? base.color ?? \"inherit\"\n\n // text doesn't get a prefix\n let styledPrefix = \"\"\n if (ls === \"badge\") {\n styledPrefix = style.bg(color).bold(` ${s.level} `)\n } else if (ls === \"icon\") {\n styledPrefix = style.fg(color)(icon)\n } else if (ls === \"prompt\") {\n styledPrefix = style.fg(color).bold(`${icon} ${s.level}`)\n } else if (ls === \"title\") {\n styledPrefix = style.fg(color).bold(`${s.level}:`)\n } else if (ls === \"notif\") {\n styledPrefix = style.fg(color).bold(`${icon} ${s.title ?? s.level}`)\n }\n\n if (s.prefix) styledPrefix = s.prefix + styledPrefix\n if (s.title && ls !== \"notif\") styledPrefix += style.bold(` ${s.title}`)\n return styledPrefix\n}\n\n/**\n * A single log entry — level-styled prefix (icon / badge / prompt / title /\n * plain text) followed by the body content. The body can be any Node, so\n * callers can stack markdown, code, images, etc. inside a log line.\n *\n * ```ts\n * log({ level: \"info\", content: \"hello\" })\n * log({ level: \"error\", content: markdown(\"**boom**\") })\n * ```\n */\nexport const log = widget((state: State<LogState>, ...children: Node[]) => {\n const base = defaultLogStyles[state.level]\n const color: Color = state.color ?? base.color ?? \"inherit\"\n const s = state\n const textColor = state.textColor ?? (s.style === \"notif\" ? \"inherit\" : base.textColor)\n const style = {\n style: textColor\n ? {\n fg: textColor,\n }\n : undefined,\n width: \"fill\",\n } as const\n if (state.content !== \"\") {\n children = [\n (state.markdown ?? true) ? markdown(state.content, style) : text(state.content, style),\n ...children,\n ]\n }\n return box(\n {\n ...(s.style === \"notif\"\n ? {\n border: \"single\",\n borderStyle: { fg: color },\n borderTitle: (ctx) => renderPrefix(state, ctx),\n borderTitleAlign: \"center\",\n padding: [0, 1],\n //style: \"overlay\",\n width: \"fill\",\n }\n : undefined),\n flexDirection: \"row\",\n gap: 1,\n visible: state.visible,\n },\n s.style === \"notif\"\n ? undefined\n : text((ctx) => renderPrefix(state, ctx), { flexShrink: 0, wrap: \"none\" }),\n box({ flexDirection: \"column\", flexGrow: 1, gap: 1 }, ...children)\n )\n})\n"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import { t as Node } from "./node-CXNobGaP.mjs";
2
2
  import { n as inspectFormat, r as isMarkdown } from "./inspect-Cli1beu8.mjs";
3
- import { t as log } from "./log-96b1_J6G.mjs";
3
+ import { t as log } from "./log-D6gGicwQ.mjs";
4
4
  // src/services/logger.ts
5
5
  const ERR_LEVELS = /* @__PURE__ */ new Set([
6
6
  "error",
@@ -67,4 +67,4 @@ var TuiReporter = class {
67
67
  };
68
68
  export { TuiReporter as t };
69
69
 
70
- //# sourceMappingURL=logger-DwfXp8LF.mjs.map
70
+ //# sourceMappingURL=logger-C0WuIlAh.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"logger-DwfXp8LF.mjs","names":[],"sources":["../src/services/logger.ts"],"sourcesContent":["import type { LogEntry, LogLevel, LogReporter } from \"@zaly/shared/logger\"\nimport type { InspectOpts } from \"../style/inspect.ts\"\nimport type { LogState } from \"../widgets/log.ts\"\n\nimport { Node } from \"../core/node.ts\"\nimport { inspectFormat, isMarkdown } from \"../style/inspect.ts\"\nimport { log } from \"../widgets/log.ts\"\n\n/** Minimal stream surface the logger needs. Stream satisfies this. */\nexport interface LoggerStream {\n append: (node: () => Node) => unknown\n}\n\nexport type LogEntryFactory = (level: LogLevel, msg: unknown[]) => Node\n\nexport type LogStyleOverride = Omit<LogState, \"level\" | \"content\">\n\nexport interface TuiReporterOpts extends InspectOpts {\n /** Render string messages as markdown when they look like it.\n * Default: `true`. */\n markdown?: boolean\n /** Per-level overrides for the default `log()` widget style fields. */\n styles?: Partial<Record<LogLevel, LogStyleOverride>>\n /** Override the node produced per entry. Default uses `log()`. */\n factory?: LogEntryFactory\n wrap?: (node: Node) => Node\n /** Fallback writer for when no stream is attached. Defaults to\n * `process.stdout/stderr`. Injectable for tests. */\n write?: (text: string, kind: \"stdout\" | \"stderr\") => void\n}\n\nconst ERR_LEVELS = new Set<LogLevel>([\"error\", \"fatal\", \"warn\"])\n\nfunction defaultWrite(text: string, kind: \"stdout\" | \"stderr\"): void {\n if (typeof process === \"undefined\") return\n const stream = kind === \"stderr\" ? process.stderr : process.stdout\n stream.write(text)\n}\n\nexport class TuiReporter implements LogReporter {\n #stream?: LoggerStream\n #opts: TuiReporterOpts\n #factory: LogEntryFactory\n\n constructor(opts: TuiReporterOpts = {}) {\n this.#opts = opts\n this.#factory = opts.factory ?? ((level, msg) => this.#defaultFactory(level, msg))\n }\n\n attach(stream: LoggerStream): this {\n this.#stream = stream\n return this\n }\n\n detach(): this {\n this.#stream = undefined\n return this\n }\n\n $log({ level, msg }: LogEntry): void {\n if (this.#stream) {\n this.#stream.append(() => {\n const node = this.#factory(level, msg)\n return this.#opts.wrap?.(node) ?? node\n })\n return\n }\n this.#writeFallback(level, msg)\n }\n\n #defaultFactory(level: LogLevel, msg: unknown[]): Node {\n const nodes = msg.filter((m): m is Node => m instanceof Node)\n msg = msg.filter((m) => !(m instanceof Node))\n\n // Convert Error objects to their message unless stacktrace is requested.\n msg = this.#opts.stacktrace ? msg : msg.map((v) => (v instanceof Error ? v.message : v))\n\n // If all remaining messages are strings, join them into one string for the log content.\n if (msg.length && msg.every((m) => typeof m === \"string\")) msg = [msg.join(\" \")]\n\n // If there's exactly one string message, use it as the log content;\n // otherwise, leave it undefined to be formatted with `inspect()`.\n // Text allow markdown if the message looks like markdown and the option is enabled.\n let text = msg.length === 1 && typeof msg[0] === \"string\" ? msg[0] : undefined\n if (msg.length === 0) text = \"\"\n const markdown = (this.#opts.markdown ?? true) && text !== undefined && isMarkdown(text)\n const overrides = this.#opts.styles?.[level] ?? {}\n return log(\n {\n content: text ?? ((ctx) => inspectFormat(msg, { ...this.#opts, style: ctx.style })),\n level,\n markdown,\n ...overrides,\n },\n ...nodes\n )\n }\n\n /** String path for the no-stream fallback — builds a plain inspected\n * line (no prefix chrome) and writes it to stdout/stderr. Keeps the\n * fallback synchronous and ordered. */\n #writeFallback(level: LogLevel, msg: unknown[]): void {\n const str = inspectFormat(msg, this.#opts)\n const kind: \"stdout\" | \"stderr\" = ERR_LEVELS.has(level) ? \"stderr\" : \"stdout\"\n const write = this.#opts.write ?? defaultWrite\n write(`${str}\\n`, kind)\n }\n}\n"],"mappings":";;;;AA+BA,MAAM,6BAAa,IAAI,IAAc;CAAC;CAAS;CAAS;AAAM,CAAC;AAE/D,SAAS,aAAa,MAAc,MAAiC;CACnE,IAAI,OAAO,YAAY,aAAa;CAEpC,CADe,SAAS,WAAW,QAAQ,SAAS,QAAQ,OAAA,CACrD,MAAM,IAAI;AACnB;AAEA,IAAa,cAAb,MAAgD;CAC9C;CACA;CACA;CAEA,YAAY,OAAwB,CAAC,GAAG;EACtC,KAAK,QAAQ;EACb,KAAK,WAAW,KAAK,aAAa,OAAO,QAAQ,KAAK,gBAAgB,OAAO,GAAG;CAClF;CAEA,OAAO,QAA4B;EACjC,KAAK,UAAU;EACf,OAAO;CACT;CAEA,SAAe;EACb,KAAK,UAAU,KAAA;EACf,OAAO;CACT;CAEA,KAAK,EAAE,OAAO,OAAuB;EACnC,IAAI,KAAK,SAAS;GAChB,KAAK,QAAQ,aAAa;IACxB,MAAM,OAAO,KAAK,SAAS,OAAO,GAAG;IACrC,OAAO,KAAK,MAAM,OAAO,IAAI,KAAK;GACpC,CAAC;GACD;EACF;EACA,KAAK,eAAe,OAAO,GAAG;CAChC;CAEA,gBAAgB,OAAiB,KAAsB;EACrD,MAAM,QAAQ,IAAI,QAAQ,MAAiB,aAAa,IAAI;EAC5D,MAAM,IAAI,QAAQ,MAAM,EAAE,aAAa,KAAK;EAG5C,MAAM,KAAK,MAAM,aAAa,MAAM,IAAI,KAAK,MAAO,aAAa,QAAQ,EAAE,UAAU,CAAE;EAGvF,IAAI,IAAI,UAAU,IAAI,OAAO,MAAM,OAAO,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,KAAK,GAAG,CAAC;EAK/E,IAAI,OAAO,IAAI,WAAW,KAAK,OAAO,IAAI,OAAO,WAAW,IAAI,KAAK,KAAA;EACrE,IAAI,IAAI,WAAW,GAAG,OAAO;EAC7B,MAAM,YAAY,KAAK,MAAM,YAAY,SAAS,SAAS,KAAA,KAAa,WAAW,IAAI;EACvF,MAAM,YAAY,KAAK,MAAM,SAAS,UAAU,CAAC;EACjD,OAAO,IACL;GACE,SAAS,UAAU,QAAQ,cAAc,KAAK;IAAE,GAAG,KAAK;IAAO,OAAO,IAAI;GAAM,CAAC;GACjF;GACA;GACA,GAAG;EACL,GACA,GAAG,KACL;CACF;;;;CAKA,eAAe,OAAiB,KAAsB;EACpD,MAAM,MAAM,cAAc,KAAK,KAAK,KAAK;EACzC,MAAM,OAA4B,WAAW,IAAI,KAAK,IAAI,WAAW;EAErE,CADc,KAAK,MAAM,SAAS,aAAA,CAC5B,GAAG,IAAI,KAAK,IAAI;CACxB;AACF"}
1
+ {"version":3,"file":"logger-C0WuIlAh.mjs","names":[],"sources":["../src/services/logger.ts"],"sourcesContent":["import type { LogEntry, LogLevel, LogReporter } from \"@zaly/shared/logger\"\nimport type { InspectOpts } from \"../style/inspect.ts\"\nimport type { LogState } from \"../widgets/log.ts\"\n\nimport { Node } from \"../core/node.ts\"\nimport { inspectFormat, isMarkdown } from \"../style/inspect.ts\"\nimport { log } from \"../widgets/log.ts\"\n\n/** Minimal stream surface the logger needs. Stream satisfies this. */\nexport interface LoggerStream {\n append: (node: () => Node) => unknown\n}\n\nexport type LogEntryFactory = (level: LogLevel, msg: unknown[]) => Node\n\nexport type LogStyleOverride = Omit<LogState, \"level\" | \"content\">\n\nexport interface TuiReporterOpts extends InspectOpts {\n /** Render string messages as markdown when they look like it.\n * Default: `true`. */\n markdown?: boolean\n /** Per-level overrides for the default `log()` widget style fields. */\n styles?: Partial<Record<LogLevel, LogStyleOverride>>\n /** Override the node produced per entry. Default uses `log()`. */\n factory?: LogEntryFactory\n wrap?: (node: Node) => Node\n /** Fallback writer for when no stream is attached. Defaults to\n * `process.stdout/stderr`. Injectable for tests. */\n write?: (text: string, kind: \"stdout\" | \"stderr\") => void\n}\n\nconst ERR_LEVELS = new Set<LogLevel>([\"error\", \"fatal\", \"warn\"])\n\nfunction defaultWrite(text: string, kind: \"stdout\" | \"stderr\"): void {\n if (typeof process === \"undefined\") return\n const stream = kind === \"stderr\" ? process.stderr : process.stdout\n stream.write(text)\n}\n\nexport class TuiReporter implements LogReporter {\n #stream?: LoggerStream\n #opts: TuiReporterOpts\n #factory: LogEntryFactory\n\n constructor(opts: TuiReporterOpts = {}) {\n this.#opts = opts\n this.#factory = opts.factory ?? ((level, msg) => this.#defaultFactory(level, msg))\n }\n\n attach(stream: LoggerStream): this {\n this.#stream = stream\n return this\n }\n\n detach(): this {\n this.#stream = undefined\n return this\n }\n\n $log({ level, msg }: LogEntry): void {\n if (this.#stream) {\n this.#stream.append(() => {\n const node = this.#factory(level, msg)\n return this.#opts.wrap?.(node) ?? node\n })\n return\n }\n this.#writeFallback(level, msg)\n }\n\n #defaultFactory(level: LogLevel, msg: unknown[]): Node {\n const nodes = msg.filter((m): m is Node => m instanceof Node)\n msg = msg.filter((m) => !(m instanceof Node))\n\n // Convert Error objects to their message unless stacktrace is requested.\n msg = this.#opts.stacktrace ? msg : msg.map((v) => (v instanceof Error ? v.message : v))\n\n // If all remaining messages are strings, join them into one string for the log content.\n if (msg.length && msg.every((m) => typeof m === \"string\")) msg = [msg.join(\" \")]\n\n // If there's exactly one string message, use it as the log content;\n // otherwise, leave it undefined to be formatted with `inspect()`.\n // Text allow markdown if the message looks like markdown and the option is enabled.\n let text = msg.length === 1 && typeof msg[0] === \"string\" ? msg[0] : undefined\n if (msg.length === 0) text = \"\"\n const markdown = (this.#opts.markdown ?? true) && text !== undefined && isMarkdown(text)\n const overrides = this.#opts.styles?.[level] ?? {}\n return log(\n {\n content: text ?? ((ctx) => inspectFormat(msg, { ...this.#opts, style: ctx.style })),\n level,\n markdown,\n ...overrides,\n },\n ...nodes\n )\n }\n\n /** String path for the no-stream fallback — builds a plain inspected\n * line (no prefix chrome) and writes it to stdout/stderr. Keeps the\n * fallback synchronous and ordered. */\n #writeFallback(level: LogLevel, msg: unknown[]): void {\n const str = inspectFormat(msg, this.#opts)\n const kind: \"stdout\" | \"stderr\" = ERR_LEVELS.has(level) ? \"stderr\" : \"stdout\"\n const write = this.#opts.write ?? defaultWrite\n write(`${str}\\n`, kind)\n }\n}\n"],"mappings":";;;;AA+BA,MAAM,6BAAa,IAAI,IAAc;CAAC;CAAS;CAAS;AAAM,CAAC;AAE/D,SAAS,aAAa,MAAc,MAAiC;CACnE,IAAI,OAAO,YAAY,aAAa;CAEpC,CADe,SAAS,WAAW,QAAQ,SAAS,QAAQ,OAAA,CACrD,MAAM,IAAI;AACnB;AAEA,IAAa,cAAb,MAAgD;CAC9C;CACA;CACA;CAEA,YAAY,OAAwB,CAAC,GAAG;EACtC,KAAK,QAAQ;EACb,KAAK,WAAW,KAAK,aAAa,OAAO,QAAQ,KAAK,gBAAgB,OAAO,GAAG;CAClF;CAEA,OAAO,QAA4B;EACjC,KAAK,UAAU;EACf,OAAO;CACT;CAEA,SAAe;EACb,KAAK,UAAU,KAAA;EACf,OAAO;CACT;CAEA,KAAK,EAAE,OAAO,OAAuB;EACnC,IAAI,KAAK,SAAS;GAChB,KAAK,QAAQ,aAAa;IACxB,MAAM,OAAO,KAAK,SAAS,OAAO,GAAG;IACrC,OAAO,KAAK,MAAM,OAAO,IAAI,KAAK;GACpC,CAAC;GACD;EACF;EACA,KAAK,eAAe,OAAO,GAAG;CAChC;CAEA,gBAAgB,OAAiB,KAAsB;EACrD,MAAM,QAAQ,IAAI,QAAQ,MAAiB,aAAa,IAAI;EAC5D,MAAM,IAAI,QAAQ,MAAM,EAAE,aAAa,KAAK;EAG5C,MAAM,KAAK,MAAM,aAAa,MAAM,IAAI,KAAK,MAAO,aAAa,QAAQ,EAAE,UAAU,CAAE;EAGvF,IAAI,IAAI,UAAU,IAAI,OAAO,MAAM,OAAO,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,KAAK,GAAG,CAAC;EAK/E,IAAI,OAAO,IAAI,WAAW,KAAK,OAAO,IAAI,OAAO,WAAW,IAAI,KAAK,KAAA;EACrE,IAAI,IAAI,WAAW,GAAG,OAAO;EAC7B,MAAM,YAAY,KAAK,MAAM,YAAY,SAAS,SAAS,KAAA,KAAa,WAAW,IAAI;EACvF,MAAM,YAAY,KAAK,MAAM,SAAS,UAAU,CAAC;EACjD,OAAO,IACL;GACE,SAAS,UAAU,QAAQ,cAAc,KAAK;IAAE,GAAG,KAAK;IAAO,OAAO,IAAI;GAAM,CAAC;GACjF;GACA;GACA,GAAG;EACL,GACA,GAAG,KACL;CACF;;;;CAKA,eAAe,OAAiB,KAAsB;EACpD,MAAM,MAAM,cAAc,KAAK,KAAK,KAAK;EACzC,MAAM,OAA4B,WAAW,IAAI,KAAK,IAAI,WAAW;EAErE,CADc,KAAK,MAAM,SAAS,aAAA,CAC5B,GAAG,IAAI,KAAK,IAAI;CACxB;AACF"}