@sigx/terminal-ui 0.5.0 → 0.6.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.
package/LICENSE CHANGED
@@ -1,21 +1,21 @@
1
- MIT License
2
-
3
- Copyright (c) 2025-2026 Andreas Ekdahl
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
1
+ MIT License
2
+
3
+ Copyright (c) 2025-2026 Andreas Ekdahl
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -1 +1 @@
1
- {"version":3,"file":"Spinner-JpmBtbPo.js","names":[],"sources":["../src/feedback/Spinner.tsx"],"sourcesContent":["/** @jsxImportSource @sigx/runtime-core */\r\nimport { component, onMounted, onUnmounted, type Define } from '@sigx/runtime-core';\r\nimport { resolveColor, GLYPHS, SPINNERS, getTick, subscribeTicker, TICK_MS, type SpinnerVariant } from '@sigx/terminal-zero';\r\n\r\n/**\r\n * Animated spinner that resolves to a success check when `done` (or a danger\r\n * cross when `failed`; `done` wins). `variant` selects a frame set from\r\n * SPINNERS ('dots' default; note 'moon' glyphs are 2 cells wide). Driven by\r\n * the shared ticker — one interval for every animated component, frozen on\r\n * non-TTY output.\r\n */\r\nexport const Spinner = component<\r\n Define.Prop<\"label\", string, false> &\r\n Define.Prop<\"done\", boolean, false> &\r\n Define.Prop<\"failed\", boolean, false> &\r\n Define.Prop<\"interval\", number, false> &\r\n Define.Prop<\"variant\", SpinnerVariant, false> &\r\n Define.Prop<\"color\", string, false>\r\n>(({ props }) => {\r\n let unsub: (() => void) | null = null;\r\n\r\n onMounted(() => { unsub = subscribeTicker(); });\r\n onUnmounted(() => { unsub?.(); });\r\n\r\n return () => {\r\n const done = !!props.done;\r\n const failed = !done && !!props.failed;\r\n const frames = SPINNERS[props.variant || 'dots'] ?? SPINNERS.dots;\r\n // `interval` is honored in multiples of the shared tick (80ms).\r\n const step = Math.max(1, Math.round((props.interval || TICK_MS) / TICK_MS));\r\n const glyph = done ? GLYPHS.check\r\n : failed ? GLYPHS.cross\r\n : frames[Math.floor(getTick() / step) % frames.length];\r\n const glyphColor = resolveColor(done ? 'success' : failed ? 'danger' : (props.color || 'accent'));\r\n return (\r\n <box>\r\n <text color={glyphColor}>{glyph}</text>\r\n {props.label && <text color={resolveColor(failed ? 'danger' : 'fg')}> {props.label}</text>}\r\n </box>\r\n );\r\n };\r\n}, { name: 'Spinner' });\r\n\r\nexport default Spinner;\r\n"],"mappings":";;;;AAWA,IAAa,IAAU,GAOpB,EAAE,eAAY;CACb,IAAI,IAA6B;CAKjC,OAHA,QAAgB;EAAE,IAAQ,GAAiB;GAAI,EAC/C,QAAkB;EAAE,KAAS;GAAI,QAEpB;EACT,IAAM,IAAO,CAAC,CAAC,EAAM,MACf,IAAS,CAAC,KAAQ,CAAC,CAAC,EAAM,QAC1B,IAAS,EAAS,EAAM,WAAW,WAAW,EAAS,MAEvD,IAAO,KAAK,IAAI,GAAG,KAAK,OAAO,EAAM,YAAY,KAAW,EAAQ,CAAC,EACrE,IAAQ,IAAO,EAAO,QACtB,IAAS,EAAO,QAChB,EAAO,KAAK,MAAM,GAAS,GAAG,EAAK,GAAG,EAAO;EAEnD,OACI,kBAAC,OAAD,EAAA,UAAA,CACI,kBAAC,QAAD;GAAM,OAHK,EAAa,IAAO,YAAY,IAAS,WAAY,EAAM,SAAS,SAGlE;aAAa;GAAa,CAAA,EACtC,EAAM,SAAS,kBAAC,QAAD;GAAM,OAAO,EAAa,IAAS,WAAW,KAAK;aAAnD,CAAqD,KAAE,EAAM,MAAa;KACxF,EAAA,CAAA;;GAGf,EAAE,MAAM,WAAW,CAAC"}
1
+ {"version":3,"file":"Spinner-JpmBtbPo.js","names":[],"sources":["../src/feedback/Spinner.tsx"],"sourcesContent":["/** @jsxImportSource @sigx/runtime-core */\nimport { component, onMounted, onUnmounted, type Define } from '@sigx/runtime-core';\nimport { resolveColor, GLYPHS, SPINNERS, getTick, subscribeTicker, TICK_MS, type SpinnerVariant } from '@sigx/terminal-zero';\n\n/**\n * Animated spinner that resolves to a success check when `done` (or a danger\n * cross when `failed`; `done` wins). `variant` selects a frame set from\n * SPINNERS ('dots' default; note 'moon' glyphs are 2 cells wide). Driven by\n * the shared ticker — one interval for every animated component, frozen on\n * non-TTY output.\n */\nexport const Spinner = component<\n Define.Prop<\"label\", string, false> &\n Define.Prop<\"done\", boolean, false> &\n Define.Prop<\"failed\", boolean, false> &\n Define.Prop<\"interval\", number, false> &\n Define.Prop<\"variant\", SpinnerVariant, false> &\n Define.Prop<\"color\", string, false>\n>(({ props }) => {\n let unsub: (() => void) | null = null;\n\n onMounted(() => { unsub = subscribeTicker(); });\n onUnmounted(() => { unsub?.(); });\n\n return () => {\n const done = !!props.done;\n const failed = !done && !!props.failed;\n const frames = SPINNERS[props.variant || 'dots'] ?? SPINNERS.dots;\n // `interval` is honored in multiples of the shared tick (80ms).\n const step = Math.max(1, Math.round((props.interval || TICK_MS) / TICK_MS));\n const glyph = done ? GLYPHS.check\n : failed ? GLYPHS.cross\n : frames[Math.floor(getTick() / step) % frames.length];\n const glyphColor = resolveColor(done ? 'success' : failed ? 'danger' : (props.color || 'accent'));\n return (\n <box>\n <text color={glyphColor}>{glyph}</text>\n {props.label && <text color={resolveColor(failed ? 'danger' : 'fg')}> {props.label}</text>}\n </box>\n );\n };\n}, { name: 'Spinner' });\n\nexport default Spinner;\n"],"mappings":";;;;AAWA,IAAa,IAAU,GAOpB,EAAE,eAAY;CACb,IAAI,IAA6B;CAKjC,OAHA,QAAgB;EAAE,IAAQ,GAAiB;GAAI,EAC/C,QAAkB;EAAE,KAAS;GAAI,QAEpB;EACT,IAAM,IAAO,CAAC,CAAC,EAAM,MACf,IAAS,CAAC,KAAQ,CAAC,CAAC,EAAM,QAC1B,IAAS,EAAS,EAAM,WAAW,WAAW,EAAS,MAEvD,IAAO,KAAK,IAAI,GAAG,KAAK,OAAO,EAAM,YAAY,KAAW,EAAQ,CAAC,EACrE,IAAQ,IAAO,EAAO,QACtB,IAAS,EAAO,QAChB,EAAO,KAAK,MAAM,GAAS,GAAG,EAAK,GAAG,EAAO;EAEnD,OACI,kBAAC,OAAD,EAAA,UAAA,CACI,kBAAC,QAAD;GAAM,OAHK,EAAa,IAAO,YAAY,IAAS,WAAY,EAAM,SAAS,SAGlE;aAAa;GAAa,CAAA,EACtC,EAAM,SAAS,kBAAC,QAAD;GAAM,OAAO,EAAa,IAAS,WAAW,KAAK;aAAnD,CAAqD,KAAE,EAAM,MAAa;KACxF,EAAA,CAAA;;GAGf,EAAE,MAAM,WAAW,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"buttons-D5iaiJ6G.js","names":[],"sources":["../src/buttons/Button.tsx"],"sourcesContent":["/** @jsxImportSource @sigx/runtime-core */\r\nimport { component, onMounted, onUnmounted, signal, type Define } from '@sigx/runtime-core';\r\nimport {\r\n onKey, registerFocusable, unregisterFocusable, focusState,\r\n resolveColor, GLYPHS, PRESS_MS, READY_DELAY_MS,\r\n} from '@sigx/terminal-zero';\r\n\r\nexport const Button = component<\r\n Define.Prop<\"label\", string, false> &\r\n Define.Prop<\"dropShadow\", boolean, false> &\r\n Define.Event<\"click\">\r\n>(({ props, emit }) => {\r\n const id = Math.random().toString(36).slice(2);\r\n let isReady = false; // Prevent immediate click after mount\r\n const isFocused = () => focusState.activeId === id;\r\n const pressed = signal({ value: false as boolean });\r\n let pressTimer: ReturnType<typeof setTimeout> | null = null;\r\n let keyCleanup: (() => void) | null = null;\r\n\r\n const handleKey = (key: string) => {\r\n if (!isFocused() || !isReady) return;\r\n if (key === '\\r' || key === '\\n' || key === ' ') {\r\n pressed.value = true;\r\n if (pressTimer) clearTimeout(pressTimer);\r\n pressTimer = setTimeout(() => { pressed.value = false; pressTimer = null; }, PRESS_MS);\r\n emit('click');\r\n }\r\n };\r\n\r\n onMounted(() => {\r\n registerFocusable(id);\r\n keyCleanup = onKey(handleKey);\r\n setTimeout(() => { isReady = true; }, READY_DELAY_MS);\r\n });\r\n\r\n onUnmounted(() => {\r\n if (keyCleanup) keyCleanup();\r\n unregisterFocusable(id);\r\n if (pressTimer) clearTimeout(pressTimer);\r\n });\r\n\r\n return () => {\r\n const focused = isFocused();\r\n const label = props.label || 'Button';\r\n const isPressed = pressed.value;\r\n const border = resolveColor(focused ? 'accent' : 'line');\r\n const bg = isPressed ? resolveColor('accent') : (focused ? resolveColor('accentSoft') : undefined);\r\n const textColor = isPressed ? resolveColor('accentText') : resolveColor(focused ? 'accent' : 'fg');\r\n\r\n return (\r\n <box\r\n border=\"rounded\"\r\n borderColor={border}\r\n backgroundColor={bg}\r\n dropShadow={props.dropShadow}\r\n shadowColor={resolveColor('shadow')}\r\n >\r\n {focused && <text color={resolveColor('accent')}>{GLYPHS.focusBar} </text>}\r\n <text color={textColor}>{label}</text>\r\n </box>\r\n );\r\n };\r\n}, { name: 'Button' });\r\n\r\nexport default Button;\r\n"],"mappings":";;;;AAOA,IAAa,IAAS,GAInB,EAAE,UAAO,cAAW;CACnB,IAAM,IAAK,KAAK,QAAQ,CAAC,SAAS,GAAG,CAAC,MAAM,EAAE,EAC1C,IAAU,IACR,UAAkB,EAAW,aAAa,GAC1C,IAAU,EAAO,EAAE,OAAO,IAAkB,CAAC,EAC/C,IAAmD,MACnD,IAAkC,MAEhC,KAAa,MAAgB;EAC3B,CAAC,GAAW,IAAI,CAAC,MACjB,MAAQ,QAAQ,MAAQ,QAAQ,MAAQ,SACxC,EAAQ,QAAQ,IACZ,KAAY,aAAa,EAAW,EACxC,IAAa,iBAAiB;GAAyB,AAAvB,EAAQ,QAAQ,IAAO,IAAa;KAAS,EAAS,EACtF,EAAK,QAAQ;;CAgBrB,OAZA,QAAgB;EAGZ,AAFA,EAAkB,EAAG,EACrB,IAAa,EAAM,EAAU,EAC7B,iBAAiB;GAAE,IAAU;KAAS,EAAe;GACvD,EAEF,QAAkB;EAGd,AAFI,KAAY,GAAY,EAC5B,EAAoB,EAAG,EACnB,KAAY,aAAa,EAAW;GAC1C,QAEW;EACT,IAAM,IAAU,GAAW,EACrB,IAAQ,EAAM,SAAS,UACvB,IAAY,EAAQ,OACpB,IAAS,EAAa,IAAU,WAAW,OAAO,EAClD,IAAK,IAAY,EAAa,SAAS,GAAI,IAAU,EAAa,aAAa,GAAG,KAAA,GAClF,IAAwB,EAAZ,IAAyB,eAA6B,IAAU,WAAW,KAAK;EAElG,OACI,kBAAC,OAAD;GACI,QAAO;GACP,aAAa;GACb,iBAAiB;GACjB,YAAY,EAAM;GAClB,aAAa,EAAa,SAAS;aALvC,CAOK,KAAW,kBAAC,QAAD;IAAM,OAAO,EAAa,SAAS;cAAnC,CAAsC,EAAO,UAAS,IAAQ;OAC1E,kBAAC,QAAD;IAAM,OAAO;cAAY;IAAa,CAAA,CACpC;;;GAGf,EAAE,MAAM,UAAU,CAAC"}
1
+ {"version":3,"file":"buttons-D5iaiJ6G.js","names":[],"sources":["../src/buttons/Button.tsx"],"sourcesContent":["/** @jsxImportSource @sigx/runtime-core */\nimport { component, onMounted, onUnmounted, signal, type Define } from '@sigx/runtime-core';\nimport {\n onKey, registerFocusable, unregisterFocusable, focusState,\n resolveColor, GLYPHS, PRESS_MS, READY_DELAY_MS,\n} from '@sigx/terminal-zero';\n\nexport const Button = component<\n Define.Prop<\"label\", string, false> &\n Define.Prop<\"dropShadow\", boolean, false> &\n Define.Event<\"click\">\n>(({ props, emit }) => {\n const id = Math.random().toString(36).slice(2);\n let isReady = false; // Prevent immediate click after mount\n const isFocused = () => focusState.activeId === id;\n const pressed = signal({ value: false as boolean });\n let pressTimer: ReturnType<typeof setTimeout> | null = null;\n let keyCleanup: (() => void) | null = null;\n\n const handleKey = (key: string) => {\n if (!isFocused() || !isReady) return;\n if (key === '\\r' || key === '\\n' || key === ' ') {\n pressed.value = true;\n if (pressTimer) clearTimeout(pressTimer);\n pressTimer = setTimeout(() => { pressed.value = false; pressTimer = null; }, PRESS_MS);\n emit('click');\n }\n };\n\n onMounted(() => {\n registerFocusable(id);\n keyCleanup = onKey(handleKey);\n setTimeout(() => { isReady = true; }, READY_DELAY_MS);\n });\n\n onUnmounted(() => {\n if (keyCleanup) keyCleanup();\n unregisterFocusable(id);\n if (pressTimer) clearTimeout(pressTimer);\n });\n\n return () => {\n const focused = isFocused();\n const label = props.label || 'Button';\n const isPressed = pressed.value;\n const border = resolveColor(focused ? 'accent' : 'line');\n const bg = isPressed ? resolveColor('accent') : (focused ? resolveColor('accentSoft') : undefined);\n const textColor = isPressed ? resolveColor('accentText') : resolveColor(focused ? 'accent' : 'fg');\n\n return (\n <box\n border=\"rounded\"\n borderColor={border}\n backgroundColor={bg}\n dropShadow={props.dropShadow}\n shadowColor={resolveColor('shadow')}\n >\n {focused && <text color={resolveColor('accent')}>{GLYPHS.focusBar} </text>}\n <text color={textColor}>{label}</text>\n </box>\n );\n };\n}, { name: 'Button' });\n\nexport default Button;\n"],"mappings":";;;;AAOA,IAAa,IAAS,GAInB,EAAE,UAAO,cAAW;CACnB,IAAM,IAAK,KAAK,QAAQ,CAAC,SAAS,GAAG,CAAC,MAAM,EAAE,EAC1C,IAAU,IACR,UAAkB,EAAW,aAAa,GAC1C,IAAU,EAAO,EAAE,OAAO,IAAkB,CAAC,EAC/C,IAAmD,MACnD,IAAkC,MAEhC,KAAa,MAAgB;EAC3B,CAAC,GAAW,IAAI,CAAC,MACjB,MAAQ,QAAQ,MAAQ,QAAQ,MAAQ,SACxC,EAAQ,QAAQ,IACZ,KAAY,aAAa,EAAW,EACxC,IAAa,iBAAiB;GAAyB,AAAvB,EAAQ,QAAQ,IAAO,IAAa;KAAS,EAAS,EACtF,EAAK,QAAQ;;CAgBrB,OAZA,QAAgB;EAGZ,AAFA,EAAkB,EAAG,EACrB,IAAa,EAAM,EAAU,EAC7B,iBAAiB;GAAE,IAAU;KAAS,EAAe;GACvD,EAEF,QAAkB;EAGd,AAFI,KAAY,GAAY,EAC5B,EAAoB,EAAG,EACnB,KAAY,aAAa,EAAW;GAC1C,QAEW;EACT,IAAM,IAAU,GAAW,EACrB,IAAQ,EAAM,SAAS,UACvB,IAAY,EAAQ,OACpB,IAAS,EAAa,IAAU,WAAW,OAAO,EAClD,IAAK,IAAY,EAAa,SAAS,GAAI,IAAU,EAAa,aAAa,GAAG,KAAA,GAClF,IAAwB,EAAZ,IAAyB,eAA6B,IAAU,WAAW,KAAK;EAElG,OACI,kBAAC,OAAD;GACI,QAAO;GACP,aAAa;GACb,iBAAiB;GACjB,YAAY,EAAM;GAClB,aAAa,EAAa,SAAS;aALvC,CAOK,KAAW,kBAAC,QAAD;IAAM,OAAO,EAAa,SAAS;cAAnC,CAAsC,EAAO,UAAS,IAAQ;OAC1E,kBAAC,QAAD;IAAM,OAAO;cAAY;IAAa,CAAA,CACpC;;;GAGf,EAAE,MAAM,UAAU,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"data-DKKLsE5K.js","names":[],"sources":["../src/data/Table.tsx","../src/data/QRCode.tsx"],"sourcesContent":["/** @jsxImportSource @sigx/runtime-core */\r\nimport { component, type Define } from '@sigx/runtime-core';\r\nimport { resolveColor } from '@sigx/terminal-zero';\r\n\r\n/**\r\n * A simple text grid. Columns are auto-sized to their widest cell; the header\r\n * is accent-colored and separated from the body by a rule. (Cell widths use\r\n * `string.length` — adequate for the ASCII data tables usually shown in a TUI.)\r\n */\r\nexport const Table = component<\r\n Define.Prop<\"columns\", string[], true> &\r\n Define.Prop<\"rows\", string[][], false>\r\n>(({ props }) => {\r\n return () => {\r\n const cols = props.columns || [];\r\n const rows = props.rows || [];\r\n\r\n const widths = cols.map((c, i) =>\r\n Math.max(c.length, ...rows.map(r => (r[i] ?? '').length), 0)\r\n );\r\n const pad = (s: string, w: number) => s + ' '.repeat(Math.max(0, w - s.length));\r\n\r\n const lineColor = resolveColor('line');\r\n const sep = () => <text color={lineColor}>│</text>;\r\n\r\n const headerCells: any[] = [];\r\n cols.forEach((c, i) => {\r\n if (i > 0) headerCells.push(sep());\r\n headerCells.push(<text color={resolveColor('accent')}>{` ${pad(c, widths[i])} `}</text>);\r\n });\r\n\r\n const ruleWidth = widths.reduce((a, w) => a + w + 2, 0) + Math.max(0, cols.length - 1);\r\n\r\n const bodyRows = rows.map((row) => {\r\n const cells: any[] = [];\r\n cols.forEach((_, i) => {\r\n if (i > 0) cells.push(sep());\r\n cells.push(<text color={resolveColor('fg')}>{` ${pad(row[i] ?? '', widths[i])} `}</text>);\r\n });\r\n return <box>{cells}</box>;\r\n });\r\n\r\n return (\r\n <box>\r\n <box>{headerCells}</box>\r\n <box><text color={lineColor}>{'─'.repeat(ruleWidth)}</text></box>\r\n {bodyRows}\r\n </box>\r\n );\r\n };\r\n}, { name: 'Table' });\r\n\r\nexport default Table;\r\n","/** @jsxImportSource @sigx/runtime-core */\r\nimport { component, type Define } from '@sigx/runtime-core';\r\nimport { generateQR } from '@sigx/terminal-zero';\r\n\r\n/**\r\n * A scannable QR code rendered with half-block characters (two QR rows per\r\n * terminal row). A typical URL fits in ~15 rows × ~29 columns.\r\n *\r\n * Deliberately NOT themed: a QR code needs maximum contrast for camera\r\n * scanners, so it always renders in the terminal's default foreground on its\r\n * default background. `invert` swaps dark/light for terminals or scanners\r\n * that disagree about which is which; modern scanners decode both.\r\n */\r\nexport const QRCode = component<\r\n Define.Prop<'text', string, true> &\r\n Define.Prop<'invert', boolean, false> &\r\n Define.Prop<'quiet', number, false>\r\n>(({ props }) => {\r\n return () => {\r\n const rows = generateQR(props.text ?? '', {\r\n invert: props.invert ?? false,\r\n quiet: props.quiet,\r\n }).split('\\n');\r\n return (\r\n <box>\r\n {rows.flatMap((line, i) => {\r\n const node = <text>{line}</text>;\r\n return i > 0 ? [<br />, node] : [node];\r\n })}\r\n </box>\r\n );\r\n };\r\n}, { name: 'QRCode' });\r\n\r\nexport default QRCode;\r\n"],"mappings":";;;;AASA,IAAa,IAAQ,GAGlB,EAAE,qBACY;CACT,IAAM,IAAO,EAAM,WAAW,EAAE,EAC1B,IAAO,EAAM,QAAQ,EAAE,EAEvB,IAAS,EAAK,KAAK,GAAG,MACxB,KAAK,IAAI,EAAE,QAAQ,GAAG,EAAK,KAAI,OAAM,EAAE,MAAM,IAAI,OAAO,EAAE,EAAE,CAC/D,EACK,KAAO,GAAW,MAAc,IAAI,IAAI,OAAO,KAAK,IAAI,GAAG,IAAI,EAAE,OAAO,CAAC,EAEzE,IAAY,EAAa,OAAO,EAChC,UAAY,kBAAC,QAAD;EAAM,OAAO;YAAW;EAAQ,CAAA,EAE5C,IAAqB,EAAE;CAC7B,EAAK,SAAS,GAAG,MAAM;EAEnB,AADI,IAAI,KAAG,EAAY,KAAK,GAAK,CAAC,EAClC,EAAY,KAAK,kBAAC,QAAD;GAAM,OAAO,EAAa,SAAS;aAAG,IAAI,EAAI,GAAG,EAAO,GAAG,CAAC;GAAU,CAAA,CAAC;GAC1F;CAEF,IAAM,IAAY,EAAO,QAAQ,GAAG,MAAM,IAAI,IAAI,GAAG,EAAE,GAAG,KAAK,IAAI,GAAG,EAAK,SAAS,EAAE,EAEhF,IAAW,EAAK,KAAK,MAAQ;EAC/B,IAAM,IAAe,EAAE;EAKvB,OAJA,EAAK,SAAS,GAAG,MAAM;GAEnB,AADI,IAAI,KAAG,EAAM,KAAK,GAAK,CAAC,EAC5B,EAAM,KAAK,kBAAC,QAAD;IAAM,OAAO,EAAa,KAAK;cAAG,IAAI,EAAI,EAAI,MAAM,IAAI,EAAO,GAAG,CAAC;IAAU,CAAA,CAAC;IAC3F,EACK,kBAAC,OAAD,EAAA,UAAM,GAAY,CAAA;GAC3B;CAEF,OACI,kBAAC,OAAD,EAAA,UAAA;EACI,kBAAC,OAAD,EAAA,UAAM,GAAkB,CAAA;EACxB,kBAAC,OAAD,EAAA,UAAK,kBAAC,QAAD;GAAM,OAAO;aAAY,IAAI,OAAO,EAAU;GAAQ,CAAA,EAAM,CAAA;EAChE;EACC,EAAA,CAAA;GAGf,EAAE,MAAM,SAAS,CAAC,ECrCR,IAAS,GAInB,EAAE,qBAOO,kBAAC,OAAD,EAAA,UALS,EAAW,EAAM,QAAQ,IAAI;CACtC,QAAQ,EAAM,UAAU;CACxB,OAAO,EAAM;CAChB,CAAC,CAAC,MAAM,KAGA,CAAK,SAAS,GAAM,MAAM;CACvB,IAAM,IAAO,kBAAC,QAAD,EAAA,UAAO,GAAY,CAAA;CAChC,OAAO,IAAI,IAAI,CAAC,kBAAC,MAAD,EAAM,CAAA,EAAE,EAAK,GAAG,CAAC,EAAK;EACxC,EACA,CAAA,EAGf,EAAE,MAAM,UAAU,CAAC"}
1
+ {"version":3,"file":"data-DKKLsE5K.js","names":[],"sources":["../src/data/Table.tsx","../src/data/QRCode.tsx"],"sourcesContent":["/** @jsxImportSource @sigx/runtime-core */\nimport { component, type Define } from '@sigx/runtime-core';\nimport { resolveColor } from '@sigx/terminal-zero';\n\n/**\n * A simple text grid. Columns are auto-sized to their widest cell; the header\n * is accent-colored and separated from the body by a rule. (Cell widths use\n * `string.length` — adequate for the ASCII data tables usually shown in a TUI.)\n */\nexport const Table = component<\n Define.Prop<\"columns\", string[], true> &\n Define.Prop<\"rows\", string[][], false>\n>(({ props }) => {\n return () => {\n const cols = props.columns || [];\n const rows = props.rows || [];\n\n const widths = cols.map((c, i) =>\n Math.max(c.length, ...rows.map(r => (r[i] ?? '').length), 0)\n );\n const pad = (s: string, w: number) => s + ' '.repeat(Math.max(0, w - s.length));\n\n const lineColor = resolveColor('line');\n const sep = () => <text color={lineColor}>│</text>;\n\n const headerCells: any[] = [];\n cols.forEach((c, i) => {\n if (i > 0) headerCells.push(sep());\n headerCells.push(<text color={resolveColor('accent')}>{` ${pad(c, widths[i])} `}</text>);\n });\n\n const ruleWidth = widths.reduce((a, w) => a + w + 2, 0) + Math.max(0, cols.length - 1);\n\n const bodyRows = rows.map((row) => {\n const cells: any[] = [];\n cols.forEach((_, i) => {\n if (i > 0) cells.push(sep());\n cells.push(<text color={resolveColor('fg')}>{` ${pad(row[i] ?? '', widths[i])} `}</text>);\n });\n return <box>{cells}</box>;\n });\n\n return (\n <box>\n <box>{headerCells}</box>\n <box><text color={lineColor}>{'─'.repeat(ruleWidth)}</text></box>\n {bodyRows}\n </box>\n );\n };\n}, { name: 'Table' });\n\nexport default Table;\n","/** @jsxImportSource @sigx/runtime-core */\nimport { component, type Define } from '@sigx/runtime-core';\nimport { generateQR } from '@sigx/terminal-zero';\n\n/**\n * A scannable QR code rendered with half-block characters (two QR rows per\n * terminal row). A typical URL fits in ~15 rows × ~29 columns.\n *\n * Deliberately NOT themed: a QR code needs maximum contrast for camera\n * scanners, so it always renders in the terminal's default foreground on its\n * default background. `invert` swaps dark/light for terminals or scanners\n * that disagree about which is which; modern scanners decode both.\n */\nexport const QRCode = component<\n Define.Prop<'text', string, true> &\n Define.Prop<'invert', boolean, false> &\n Define.Prop<'quiet', number, false>\n>(({ props }) => {\n return () => {\n const rows = generateQR(props.text ?? '', {\n invert: props.invert ?? false,\n quiet: props.quiet,\n }).split('\\n');\n return (\n <box>\n {rows.flatMap((line, i) => {\n const node = <text>{line}</text>;\n return i > 0 ? [<br />, node] : [node];\n })}\n </box>\n );\n };\n}, { name: 'QRCode' });\n\nexport default QRCode;\n"],"mappings":";;;;AASA,IAAa,IAAQ,GAGlB,EAAE,qBACY;CACT,IAAM,IAAO,EAAM,WAAW,EAAE,EAC1B,IAAO,EAAM,QAAQ,EAAE,EAEvB,IAAS,EAAK,KAAK,GAAG,MACxB,KAAK,IAAI,EAAE,QAAQ,GAAG,EAAK,KAAI,OAAM,EAAE,MAAM,IAAI,OAAO,EAAE,EAAE,CAC/D,EACK,KAAO,GAAW,MAAc,IAAI,IAAI,OAAO,KAAK,IAAI,GAAG,IAAI,EAAE,OAAO,CAAC,EAEzE,IAAY,EAAa,OAAO,EAChC,UAAY,kBAAC,QAAD;EAAM,OAAO;YAAW;EAAQ,CAAA,EAE5C,IAAqB,EAAE;CAC7B,EAAK,SAAS,GAAG,MAAM;EAEnB,AADI,IAAI,KAAG,EAAY,KAAK,GAAK,CAAC,EAClC,EAAY,KAAK,kBAAC,QAAD;GAAM,OAAO,EAAa,SAAS;aAAG,IAAI,EAAI,GAAG,EAAO,GAAG,CAAC;GAAU,CAAA,CAAC;GAC1F;CAEF,IAAM,IAAY,EAAO,QAAQ,GAAG,MAAM,IAAI,IAAI,GAAG,EAAE,GAAG,KAAK,IAAI,GAAG,EAAK,SAAS,EAAE,EAEhF,IAAW,EAAK,KAAK,MAAQ;EAC/B,IAAM,IAAe,EAAE;EAKvB,OAJA,EAAK,SAAS,GAAG,MAAM;GAEnB,AADI,IAAI,KAAG,EAAM,KAAK,GAAK,CAAC,EAC5B,EAAM,KAAK,kBAAC,QAAD;IAAM,OAAO,EAAa,KAAK;cAAG,IAAI,EAAI,EAAI,MAAM,IAAI,EAAO,GAAG,CAAC;IAAU,CAAA,CAAC;IAC3F,EACK,kBAAC,OAAD,EAAA,UAAM,GAAY,CAAA;GAC3B;CAEF,OACI,kBAAC,OAAD,EAAA,UAAA;EACI,kBAAC,OAAD,EAAA,UAAM,GAAkB,CAAA;EACxB,kBAAC,OAAD,EAAA,UAAK,kBAAC,QAAD;GAAM,OAAO;aAAY,IAAI,OAAO,EAAU;GAAQ,CAAA,EAAM,CAAA;EAChE;EACC,EAAA,CAAA;GAGf,EAAE,MAAM,SAAS,CAAC,ECrCR,IAAS,GAInB,EAAE,qBAOO,kBAAC,OAAD,EAAA,UALS,EAAW,EAAM,QAAQ,IAAI;CACtC,QAAQ,EAAM,UAAU;CACxB,OAAO,EAAM;CAChB,CAAC,CAAC,MAAM,KAGA,CAAK,SAAS,GAAM,MAAM;CACvB,IAAM,IAAO,kBAAC,QAAD,EAAA,UAAO,GAAY,CAAA;CAChC,OAAO,IAAI,IAAI,CAAC,kBAAC,MAAD,EAAM,CAAA,EAAE,EAAK,GAAG,CAAC,EAAK;EACxC,EACA,CAAA,EAGf,EAAE,MAAM,UAAU,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"feedback-1WqdARfx.js","names":[],"sources":["../src/feedback/ProgressBar.tsx","../src/feedback/Badge.tsx"],"sourcesContent":["/** @jsxImportSource @sigx/runtime-core */\r\nimport { component, type Define } from '@sigx/runtime-core';\r\nimport { resolveColor, getColorDepth, GLYPHS, gradient, hexToSGR } from '@sigx/terminal-zero';\r\nimport { GRADIENT_PRESETS } from '../fx/presets';\r\n\r\n/**\r\n * Progress bar in three variants:\r\n * - `solid` — single-color fill (default; the classic bar)\r\n * - `gradient` — fill sampled per-cell across `colors` stops (default\r\n * accent→info), revealed as the bar grows\r\n * - `rainbow` — gradient with the rainbow preset\r\n * `smooth` adds a sub-cell leading edge (eighth blocks). Gradient variants\r\n * fall back to solid at ansi16/none depth.\r\n */\r\nexport const ProgressBar = component<\r\n Define.Prop<\"value\", number, false> &\r\n Define.Prop<\"max\", number, false> &\r\n Define.Prop<\"width\", number, false> &\r\n Define.Prop<\"char\", string, false> &\r\n Define.Prop<\"emptyChar\", string, false> &\r\n Define.Prop<\"color\", string, false> &\r\n Define.Prop<\"variant\", 'solid' | 'gradient' | 'rainbow', false> &\r\n Define.Prop<\"colors\", string[], false> &\r\n Define.Prop<\"smooth\", boolean, false> &\r\n Define.Prop<\"showPercent\", boolean, false>\r\n>(({ props }) => {\r\n return () => {\r\n const value = props.value || 0;\r\n const max = props.max || 100;\r\n const width = props.width || 20;\r\n const barChar = props.char || GLYPHS.barFull;\r\n const emptyChar = props.emptyChar || GLYPHS.barEmpty;\r\n const showPercent = props.showPercent !== false;\r\n\r\n const percentage = Math.min(Math.max(value / max, 0), 1);\r\n const exact = width * percentage;\r\n const filledLen = props.smooth ? Math.floor(exact) : Math.round(exact);\r\n // Sub-cell leading edge: eighth blocks between filled and empty cells.\r\n const edgeIdx = props.smooth ? Math.floor((exact - filledLen) * 8) : 0;\r\n const edge = edgeIdx > 0 ? GLYPHS.barEighths[edgeIdx - 1] : '';\r\n const emptyLen = width - filledLen - (edge ? 1 : 0);\r\n\r\n const depth = getColorDepth();\r\n const variant = props.variant || 'solid';\r\n const useGradient = variant !== 'solid' && depth !== 'ansi16' && depth !== 'none';\r\n\r\n const percentText = showPercent\r\n ? <text color={resolveColor('dim')}>{` ${Math.round(percentage * 100)}%`}</text>\r\n : undefined;\r\n\r\n if (!useGradient) {\r\n const fill = resolveColor(props.color || 'accent');\r\n return (\r\n <box>\r\n <text color={fill}>{barChar.repeat(filledLen) + edge}</text>\r\n <text color={resolveColor('faint')}>{emptyChar.repeat(emptyLen)}</text>\r\n {percentText}\r\n </box>\r\n );\r\n }\r\n\r\n // Sample the gradient across the FULL width so the bar reveals a\r\n // stable gradient as it fills (not a compressed one per frame).\r\n const stops = (variant === 'rainbow'\r\n ? [...GRADIENT_PRESETS.rainbow]\r\n : (props.colors?.length ? props.colors : ['accent', 'info'])\r\n ).map((c) => resolveColor(c));\r\n const cells = gradient(stops, Math.max(width, 2));\r\n\r\n let painted = '';\r\n let lastHex: string | null = null;\r\n for (let i = 0; i < filledLen; i++) {\r\n if (cells[i] !== lastHex) {\r\n painted += hexToSGR(cells[i]);\r\n lastHex = cells[i];\r\n }\r\n painted += barChar;\r\n }\r\n if (edge) {\r\n const hex = cells[Math.min(filledLen, cells.length - 1)];\r\n if (hex !== lastHex) painted += hexToSGR(hex);\r\n painted += edge;\r\n lastHex = hex;\r\n }\r\n if (lastHex !== null) painted += '\\x1b[39m';\r\n\r\n return (\r\n <box>\r\n <text>{painted}</text>\r\n <text color={resolveColor('faint')}>{emptyChar.repeat(emptyLen)}</text>\r\n {percentText}\r\n </box>\r\n );\r\n };\r\n}, { name: 'ProgressBar' });\r\n\r\nexport default ProgressBar;\r\n","/** @jsxImportSource @sigx/runtime-core */\r\nimport { component, type Define } from '@sigx/runtime-core';\r\nimport { resolveColor } from '@sigx/terminal-zero';\r\n\r\n/**\r\n * A small label indicator in one of three styles:\r\n * - `solid` — color fill with `accentText` (uses inline background)\r\n * - `accent` — colored text only (default)\r\n * - `bracket` — `[ label ]` in color, no fill\r\n */\r\nexport const Badge = component<\r\n Define.Prop<\"label\", string, true> &\r\n Define.Prop<\"variant\", 'solid' | 'accent' | 'bracket', false> &\r\n Define.Prop<\"color\", string, false>\r\n>(({ props }) => {\r\n return () => {\r\n const label = props.label ?? '';\r\n const variant = props.variant || 'accent';\r\n const color = props.color || 'accent';\r\n\r\n if (variant === 'solid') {\r\n return (\r\n <box>\r\n <text backgroundColor={resolveColor(color)} color={resolveColor('accentText')}> {label} </text>\r\n </box>\r\n );\r\n }\r\n if (variant === 'bracket') {\r\n return <box><text color={resolveColor(color)}>[ {label} ]</text></box>;\r\n }\r\n return <box><text color={resolveColor(color)}>{label}</text></box>;\r\n };\r\n}, { name: 'Badge' });\r\n\r\nexport default Badge;\r\n"],"mappings":";;;;;;AAcA,IAAa,IAAc,GAWxB,EAAE,qBACY;CACT,IAAM,IAAQ,EAAM,SAAS,GACvB,IAAM,EAAM,OAAO,KACnB,IAAQ,EAAM,SAAS,IACvB,IAAU,EAAM,QAAQ,EAAO,SAC/B,IAAY,EAAM,aAAa,EAAO,UACtC,IAAc,EAAM,gBAAgB,IAEpC,IAAa,KAAK,IAAI,KAAK,IAAI,IAAQ,GAAK,EAAE,EAAE,EAAE,EAClD,IAAQ,IAAQ,GAChB,IAAY,EAAM,SAAS,KAAK,MAAM,EAAM,GAAG,KAAK,MAAM,EAAM,EAEhE,IAAU,EAAM,SAAS,KAAK,OAAO,IAAQ,KAAa,EAAE,GAAG,GAC/D,IAAO,IAAU,IAAI,EAAO,WAAW,IAAU,KAAK,IACtD,IAAW,IAAQ,IAAa,MAEhC,IAAQ,GAAe,EACvB,IAAU,EAAM,WAAW,SAC3B,IAAc,MAAY,WAAW,MAAU,YAAY,MAAU,QAErE,IAAc,IACd,kBAAC,QAAD;EAAM,OAAO,EAAa,MAAM;YAAG,IAAI,KAAK,MAAM,IAAa,IAAI,CAAC;EAAU,CAAA,GAC9E,KAAA;CAEN,IAAI,CAAC,GAED,OACI,kBAAC,OAAD,EAAA,UAAA;EACI,kBAAC,QAAD;GAAM,OAHD,EAAa,EAAM,SAAS,SAGpB;aAAO,EAAQ,OAAO,EAAU,GAAG;GAAY,CAAA;EAC5D,kBAAC,QAAD;GAAM,OAAO,EAAa,QAAQ;aAAG,EAAU,OAAO,EAAS;GAAQ,CAAA;EACtE;EACC,EAAA,CAAA;CAUd,IAAM,IAAQ,GAJC,MAAY,YACrB,CAAC,GAAG,EAAiB,QAAQ,GAC5B,EAAM,QAAQ,SAAS,EAAM,SAAS,CAAC,UAAU,OAAO,EAC7D,KAAK,MAAM,EAAa,EAAE,CACL,EAAO,KAAK,IAAI,GAAO,EAAE,CAAC,EAE7C,IAAU,IACV,IAAyB;CAC7B,KAAK,IAAI,IAAI,GAAG,IAAI,GAAW,KAK3B,AAJI,EAAM,OAAO,MACb,KAAW,EAAS,EAAM,GAAG,EAC7B,IAAU,EAAM,KAEpB,KAAW;CAEf,IAAI,GAAM;EACN,IAAM,IAAM,EAAM,KAAK,IAAI,GAAW,EAAM,SAAS,EAAE;EAGvD,AAFI,MAAQ,MAAS,KAAW,EAAS,EAAI,GAC7C,KAAW,GACX,IAAU;;CAId,OAFI,MAAY,SAAM,KAAW,aAG7B,kBAAC,OAAD,EAAA,UAAA;EACI,kBAAC,QAAD,EAAA,UAAO,GAAe,CAAA;EACtB,kBAAC,QAAD;GAAM,OAAO,EAAa,QAAQ;aAAG,EAAU,OAAO,EAAS;GAAQ,CAAA;EACtE;EACC,EAAA,CAAA;GAGf,EAAE,MAAM,eAAe,CAAC,ECpFd,IAAQ,GAIlB,EAAE,qBACY;CACT,IAAM,IAAQ,EAAM,SAAS,IACvB,IAAU,EAAM,WAAW,UAC3B,IAAQ,EAAM,SAAS;CAY7B,OAVI,MAAY,UAER,kBAAC,OAAD,EAAA,UACI,kBAAC,QAAD;EAAM,iBAAiB,EAAa,EAAM;EAAE,OAAO,EAAa,aAAa;YAA7E;GAA+E;GAAE;GAAM;GAAQ;KAC7F,CAAA,GAGV,MAAY,YACL,kBAAC,OAAD,EAAA,UAAK,kBAAC,QAAD;EAAM,OAAO,EAAa,EAAM;YAAhC;GAAkC;GAAG;GAAM;GAAS;KAAM,CAAA,GAEnE,kBAAC,OAAD,EAAA,UAAK,kBAAC,QAAD;EAAM,OAAO,EAAa,EAAM;YAAG;EAAa,CAAA,EAAM,CAAA;GAEvE,EAAE,MAAM,SAAS,CAAC"}
1
+ {"version":3,"file":"feedback-1WqdARfx.js","names":[],"sources":["../src/feedback/ProgressBar.tsx","../src/feedback/Badge.tsx"],"sourcesContent":["/** @jsxImportSource @sigx/runtime-core */\nimport { component, type Define } from '@sigx/runtime-core';\nimport { resolveColor, getColorDepth, GLYPHS, gradient, hexToSGR } from '@sigx/terminal-zero';\nimport { GRADIENT_PRESETS } from '../fx/presets';\n\n/**\n * Progress bar in three variants:\n * - `solid` — single-color fill (default; the classic bar)\n * - `gradient` — fill sampled per-cell across `colors` stops (default\n * accent→info), revealed as the bar grows\n * - `rainbow` — gradient with the rainbow preset\n * `smooth` adds a sub-cell leading edge (eighth blocks). Gradient variants\n * fall back to solid at ansi16/none depth.\n */\nexport const ProgressBar = component<\n Define.Prop<\"value\", number, false> &\n Define.Prop<\"max\", number, false> &\n Define.Prop<\"width\", number, false> &\n Define.Prop<\"char\", string, false> &\n Define.Prop<\"emptyChar\", string, false> &\n Define.Prop<\"color\", string, false> &\n Define.Prop<\"variant\", 'solid' | 'gradient' | 'rainbow', false> &\n Define.Prop<\"colors\", string[], false> &\n Define.Prop<\"smooth\", boolean, false> &\n Define.Prop<\"showPercent\", boolean, false>\n>(({ props }) => {\n return () => {\n const value = props.value || 0;\n const max = props.max || 100;\n const width = props.width || 20;\n const barChar = props.char || GLYPHS.barFull;\n const emptyChar = props.emptyChar || GLYPHS.barEmpty;\n const showPercent = props.showPercent !== false;\n\n const percentage = Math.min(Math.max(value / max, 0), 1);\n const exact = width * percentage;\n const filledLen = props.smooth ? Math.floor(exact) : Math.round(exact);\n // Sub-cell leading edge: eighth blocks between filled and empty cells.\n const edgeIdx = props.smooth ? Math.floor((exact - filledLen) * 8) : 0;\n const edge = edgeIdx > 0 ? GLYPHS.barEighths[edgeIdx - 1] : '';\n const emptyLen = width - filledLen - (edge ? 1 : 0);\n\n const depth = getColorDepth();\n const variant = props.variant || 'solid';\n const useGradient = variant !== 'solid' && depth !== 'ansi16' && depth !== 'none';\n\n const percentText = showPercent\n ? <text color={resolveColor('dim')}>{` ${Math.round(percentage * 100)}%`}</text>\n : undefined;\n\n if (!useGradient) {\n const fill = resolveColor(props.color || 'accent');\n return (\n <box>\n <text color={fill}>{barChar.repeat(filledLen) + edge}</text>\n <text color={resolveColor('faint')}>{emptyChar.repeat(emptyLen)}</text>\n {percentText}\n </box>\n );\n }\n\n // Sample the gradient across the FULL width so the bar reveals a\n // stable gradient as it fills (not a compressed one per frame).\n const stops = (variant === 'rainbow'\n ? [...GRADIENT_PRESETS.rainbow]\n : (props.colors?.length ? props.colors : ['accent', 'info'])\n ).map((c) => resolveColor(c));\n const cells = gradient(stops, Math.max(width, 2));\n\n let painted = '';\n let lastHex: string | null = null;\n for (let i = 0; i < filledLen; i++) {\n if (cells[i] !== lastHex) {\n painted += hexToSGR(cells[i]);\n lastHex = cells[i];\n }\n painted += barChar;\n }\n if (edge) {\n const hex = cells[Math.min(filledLen, cells.length - 1)];\n if (hex !== lastHex) painted += hexToSGR(hex);\n painted += edge;\n lastHex = hex;\n }\n if (lastHex !== null) painted += '\\x1b[39m';\n\n return (\n <box>\n <text>{painted}</text>\n <text color={resolveColor('faint')}>{emptyChar.repeat(emptyLen)}</text>\n {percentText}\n </box>\n );\n };\n}, { name: 'ProgressBar' });\n\nexport default ProgressBar;\n","/** @jsxImportSource @sigx/runtime-core */\nimport { component, type Define } from '@sigx/runtime-core';\nimport { resolveColor } from '@sigx/terminal-zero';\n\n/**\n * A small label indicator in one of three styles:\n * - `solid` — color fill with `accentText` (uses inline background)\n * - `accent` — colored text only (default)\n * - `bracket` — `[ label ]` in color, no fill\n */\nexport const Badge = component<\n Define.Prop<\"label\", string, true> &\n Define.Prop<\"variant\", 'solid' | 'accent' | 'bracket', false> &\n Define.Prop<\"color\", string, false>\n>(({ props }) => {\n return () => {\n const label = props.label ?? '';\n const variant = props.variant || 'accent';\n const color = props.color || 'accent';\n\n if (variant === 'solid') {\n return (\n <box>\n <text backgroundColor={resolveColor(color)} color={resolveColor('accentText')}> {label} </text>\n </box>\n );\n }\n if (variant === 'bracket') {\n return <box><text color={resolveColor(color)}>[ {label} ]</text></box>;\n }\n return <box><text color={resolveColor(color)}>{label}</text></box>;\n };\n}, { name: 'Badge' });\n\nexport default Badge;\n"],"mappings":";;;;;;AAcA,IAAa,IAAc,GAWxB,EAAE,qBACY;CACT,IAAM,IAAQ,EAAM,SAAS,GACvB,IAAM,EAAM,OAAO,KACnB,IAAQ,EAAM,SAAS,IACvB,IAAU,EAAM,QAAQ,EAAO,SAC/B,IAAY,EAAM,aAAa,EAAO,UACtC,IAAc,EAAM,gBAAgB,IAEpC,IAAa,KAAK,IAAI,KAAK,IAAI,IAAQ,GAAK,EAAE,EAAE,EAAE,EAClD,IAAQ,IAAQ,GAChB,IAAY,EAAM,SAAS,KAAK,MAAM,EAAM,GAAG,KAAK,MAAM,EAAM,EAEhE,IAAU,EAAM,SAAS,KAAK,OAAO,IAAQ,KAAa,EAAE,GAAG,GAC/D,IAAO,IAAU,IAAI,EAAO,WAAW,IAAU,KAAK,IACtD,IAAW,IAAQ,IAAa,MAEhC,IAAQ,GAAe,EACvB,IAAU,EAAM,WAAW,SAC3B,IAAc,MAAY,WAAW,MAAU,YAAY,MAAU,QAErE,IAAc,IACd,kBAAC,QAAD;EAAM,OAAO,EAAa,MAAM;YAAG,IAAI,KAAK,MAAM,IAAa,IAAI,CAAC;EAAU,CAAA,GAC9E,KAAA;CAEN,IAAI,CAAC,GAED,OACI,kBAAC,OAAD,EAAA,UAAA;EACI,kBAAC,QAAD;GAAM,OAHD,EAAa,EAAM,SAAS,SAGpB;aAAO,EAAQ,OAAO,EAAU,GAAG;GAAY,CAAA;EAC5D,kBAAC,QAAD;GAAM,OAAO,EAAa,QAAQ;aAAG,EAAU,OAAO,EAAS;GAAQ,CAAA;EACtE;EACC,EAAA,CAAA;CAUd,IAAM,IAAQ,GAJC,MAAY,YACrB,CAAC,GAAG,EAAiB,QAAQ,GAC5B,EAAM,QAAQ,SAAS,EAAM,SAAS,CAAC,UAAU,OAAO,EAC7D,KAAK,MAAM,EAAa,EAAE,CACL,EAAO,KAAK,IAAI,GAAO,EAAE,CAAC,EAE7C,IAAU,IACV,IAAyB;CAC7B,KAAK,IAAI,IAAI,GAAG,IAAI,GAAW,KAK3B,AAJI,EAAM,OAAO,MACb,KAAW,EAAS,EAAM,GAAG,EAC7B,IAAU,EAAM,KAEpB,KAAW;CAEf,IAAI,GAAM;EACN,IAAM,IAAM,EAAM,KAAK,IAAI,GAAW,EAAM,SAAS,EAAE;EAGvD,AAFI,MAAQ,MAAS,KAAW,EAAS,EAAI,GAC7C,KAAW,GACX,IAAU;;CAId,OAFI,MAAY,SAAM,KAAW,aAG7B,kBAAC,OAAD,EAAA,UAAA;EACI,kBAAC,QAAD,EAAA,UAAO,GAAe,CAAA;EACtB,kBAAC,QAAD;GAAM,OAAO,EAAa,QAAQ;aAAG,EAAU,OAAO,EAAS;GAAQ,CAAA;EACtE;EACC,EAAA,CAAA;GAGf,EAAE,MAAM,eAAe,CAAC,ECpFd,IAAQ,GAIlB,EAAE,qBACY;CACT,IAAM,IAAQ,EAAM,SAAS,IACvB,IAAU,EAAM,WAAW,UAC3B,IAAQ,EAAM,SAAS;CAY7B,OAVI,MAAY,UAER,kBAAC,OAAD,EAAA,UACI,kBAAC,QAAD;EAAM,iBAAiB,EAAa,EAAM;EAAE,OAAO,EAAa,aAAa;YAA7E;GAA+E;GAAE;GAAM;GAAQ;KAC7F,CAAA,GAGV,MAAY,YACL,kBAAC,OAAD,EAAA,UAAK,kBAAC,QAAD;EAAM,OAAO,EAAa,EAAM;YAAhC;GAAkC;GAAG;GAAM;GAAS;KAAM,CAAA,GAEnE,kBAAC,OAAD,EAAA,UAAK,kBAAC,QAAD;EAAM,OAAO,EAAa,EAAM;YAAG;EAAa,CAAA,EAAM,CAAA;GAEvE,EAAE,MAAM,SAAS,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"forms-MgpyoQI5.js","names":[],"sources":["../src/forms/Input.tsx","../src/forms/Checkbox.tsx","../src/forms/Select.tsx","../src/forms/Radio.tsx","../src/forms/MultiSelect.tsx","../src/forms/Confirm.tsx","../src/forms/TextArea.tsx"],"sourcesContent":["/** @jsxImportSource @sigx/runtime-core */\r\nimport { component, onMounted, onUnmounted, type Define } from '@sigx/runtime-core';\r\nimport {\r\n onKey, registerFocusable, unregisterFocusable, focusState, focus, resolveColor,\r\n} from '@sigx/terminal-zero';\r\n\r\nexport const Input = component<\r\n Define.Model<string> &\r\n Define.Prop<\"placeholder\", string, false> &\r\n Define.Prop<\"autofocus\", boolean, false> &\r\n Define.Prop<\"label\", string, false> &\r\n Define.Event<\"input\", string> &\r\n Define.Event<\"submit\", string>\r\n>(({ props, emit }) => {\r\n const id = Math.random().toString(36).slice(2);\r\n let isReady = false;\r\n const isFocused = () => focusState.activeId === id;\r\n const getValue = () => props.model?.value || '';\r\n\r\n const handleKey = (key: string) => {\r\n if (!isFocused() || !isReady) return;\r\n\r\n if (key === '\\r' || key === '\\n') {\r\n emit('submit', getValue());\r\n return;\r\n }\r\n if (key === '\\u007F' || key === '\\b') { // Backspace / Delete\r\n const val = getValue();\r\n if (val.length > 0) {\r\n const newValue = val.slice(0, -1);\r\n if (props.model) props.model.value = newValue;\r\n emit('input', newValue);\r\n }\r\n return;\r\n }\r\n // Printable characters only — control bytes (incl. Tab, which now\r\n // cascades through the key layers to the focus-cycle handler) and\r\n // escape sequences are ignored.\r\n if (key.length !== 1 || key < ' ') return;\r\n\r\n const newValue = getValue() + key;\r\n if (props.model) props.model.value = newValue;\r\n emit('input', newValue);\r\n };\r\n\r\n let keyCleanup: (() => void) | null = null;\r\n\r\n onMounted(() => {\r\n registerFocusable(id);\r\n if (props.autofocus) focus(id);\r\n keyCleanup = onKey(handleKey);\r\n setTimeout(() => { isReady = true; }, 50);\r\n });\r\n\r\n onUnmounted(() => {\r\n if (keyCleanup) keyCleanup();\r\n unregisterFocusable(id);\r\n });\r\n\r\n return () => {\r\n const val = getValue().replace(/[\\r\\n]+/g, ' ');\r\n const placeholder = (props.placeholder || '').replace(/[\\r\\n]+/g, ' ');\r\n const focused = isFocused();\r\n const hasValue = val.length > 0;\r\n\r\n return (\r\n <box border=\"rounded\" borderColor={resolveColor(focused ? 'accent' : 'line')} label={props.label} labelColor={resolveColor(focused ? 'accent' : 'dim')}>\r\n <text color={resolveColor(hasValue ? 'fg' : 'dim')}>{val || placeholder}</text>\r\n {focused && (\r\n // Block cursor: a reverse-video space (exactly one cell).\r\n <text backgroundColor={resolveColor('accent')} color={resolveColor('accentText')}> </text>\r\n )}\r\n </box>\r\n );\r\n };\r\n}, { name: 'Input' });\r\n\r\nexport default Input;\r\n","/** @jsxImportSource @sigx/runtime-core */\r\nimport { component, onMounted, onUnmounted, type Define } from '@sigx/runtime-core';\r\nimport {\r\n onKey, registerFocusable, unregisterFocusable, focusState, focus, resolveColor, GLYPHS,\r\n} from '@sigx/terminal-zero';\r\n\r\nexport const Checkbox = component<\r\n Define.Model<boolean> &\r\n Define.Prop<\"label\", string, false> &\r\n Define.Prop<\"autofocus\", boolean, false> &\r\n Define.Prop<\"disabled\", boolean, false> &\r\n Define.Event<\"change\", boolean>\r\n>(({ props, emit }) => {\r\n const id = Math.random().toString(36).slice(2);\r\n const isFocused = () => focusState.activeId === id;\r\n const checked = () => !!props.model?.value;\r\n\r\n const handleKey = (key: string) => {\r\n if (!isFocused() || props.disabled) return;\r\n if (key === '\\r' || key === ' ') { // Enter or Space toggles\r\n const next = !checked();\r\n if (props.model) props.model.value = next;\r\n emit('change', next);\r\n }\r\n };\r\n\r\n let keyCleanup: (() => void) | null = null;\r\n\r\n onMounted(() => {\r\n registerFocusable(id);\r\n if (props.autofocus) focus(id);\r\n keyCleanup = onKey(handleKey);\r\n });\r\n\r\n onUnmounted(() => {\r\n if (keyCleanup) keyCleanup();\r\n unregisterFocusable(id);\r\n });\r\n\r\n return () => {\r\n const label = props.label || '';\r\n const focused = isFocused();\r\n const isChecked = checked();\r\n const disabled = !!props.disabled;\r\n\r\n const marker = isChecked ? GLYPHS.checkboxOn : GLYPHS.checkboxOff;\r\n const markerColor = disabled\r\n ? resolveColor('faint')\r\n : resolveColor(isChecked ? 'success' : (focused ? 'accent' : 'line'));\r\n const labelColor = disabled\r\n ? resolveColor('faint')\r\n : resolveColor(focused ? 'accent' : 'fg');\r\n\r\n return (\r\n <box>\r\n {focused && <text color={resolveColor('accent')}>{GLYPHS.focusBar} </text>}\r\n <text color={markerColor}>{marker}</text>\r\n {label && <text color={labelColor}> {label}</text>}\r\n </box>\r\n );\r\n };\r\n}, { name: 'Checkbox' });\r\n\r\nexport default Checkbox;\r\n","/** @jsxImportSource @sigx/runtime-core */\r\nimport { component, onMounted, onUnmounted, type Define } from '@sigx/runtime-core';\r\nimport {\r\n onKey, registerFocusable, unregisterFocusable, focusState, focus, resolveColor, GLYPHS,\r\n} from '@sigx/terminal-zero';\r\n\r\nexport interface SelectOption<T = string> {\r\n label: string;\r\n value: T;\r\n description?: string;\r\n}\r\n\r\nexport const Select = component<\r\n Define.Model<string> &\r\n Define.Prop<\"options\", SelectOption[], true> &\r\n Define.Prop<\"label\", string, false> &\r\n Define.Prop<\"autofocus\", boolean, false> &\r\n Define.Prop<\"showDescription\", boolean, false> &\r\n Define.Event<\"change\", string> &\r\n Define.Event<\"submit\", string>\r\n>(({ props, emit }) => {\r\n const id = Math.random().toString(36).slice(2);\r\n let isReady = false;\r\n const isFocused = () => focusState.activeId === id;\r\n\r\n const getCurrentIndex = () => {\r\n const options = props.options || [];\r\n const idx = options.findIndex(o => o.value === props.model?.value);\r\n return idx >= 0 ? idx : 0;\r\n };\r\n\r\n const handleKey = (key: string) => {\r\n if (!isFocused() || !isReady) return;\r\n const options = props.options || [];\r\n if (options.length === 0) return;\r\n const currentIndex = getCurrentIndex();\r\n\r\n if (key === '\\x1B[A' || key === 'k') { // up\r\n const newIndex = currentIndex > 0 ? currentIndex - 1 : options.length - 1;\r\n const newValue = options[newIndex].value;\r\n if (props.model) props.model.value = newValue;\r\n emit('change', newValue);\r\n return;\r\n }\r\n if (key === '\\x1B[B' || key === 'j') { // down\r\n const newIndex = currentIndex < options.length - 1 ? currentIndex + 1 : 0;\r\n const newValue = options[newIndex].value;\r\n if (props.model) props.model.value = newValue;\r\n emit('change', newValue);\r\n return;\r\n }\r\n if (key === '\\r' || key === '\\n') { // submit\r\n emit('submit', props.model?.value || options[0]?.value || '');\r\n return;\r\n }\r\n };\r\n\r\n let keyCleanup: (() => void) | null = null;\r\n\r\n onMounted(() => {\r\n registerFocusable(id);\r\n if (props.autofocus) focus(id);\r\n keyCleanup = onKey(handleKey);\r\n setTimeout(() => { isReady = true; }, 50);\r\n });\r\n\r\n onUnmounted(() => {\r\n if (keyCleanup) keyCleanup();\r\n unregisterFocusable(id);\r\n });\r\n\r\n return () => {\r\n const options = props.options || [];\r\n const focused = isFocused();\r\n const currentValue = props.model?.value || options[0]?.value || '';\r\n const selectedOption = options.find(o => o.value === currentValue);\r\n\r\n const optionElements = options.map((option) => {\r\n const isSelected = option.value === currentValue;\r\n const indicator = isSelected ? GLYPHS.cursor : ' ';\r\n const rowColor = resolveColor(isSelected ? 'accent' : 'fg');\r\n return (\r\n <box>\r\n <text color={resolveColor(isSelected ? 'accent' : 'faint')}>{indicator} </text>\r\n <text color={rowColor}>{option.label}</text>\r\n </box>\r\n );\r\n });\r\n\r\n const descriptionElement = props.showDescription && selectedOption?.description ? (\r\n <box>\r\n <text color={resolveColor('dim')}> ↳ {selectedOption.description}</text>\r\n </box>\r\n ) : null;\r\n\r\n return (\r\n <box>\r\n <box border=\"rounded\" borderColor={resolveColor(focused ? 'accent' : 'line')} label={props.label} labelColor={resolveColor(focused ? 'accent' : 'dim')}>\r\n {optionElements}\r\n </box>\r\n {descriptionElement}\r\n </box>\r\n );\r\n };\r\n}, { name: 'Select' });\r\n\r\nexport default Select;\r\n","/** @jsxImportSource @sigx/runtime-core */\r\nimport { component, onMounted, onUnmounted, type Define } from '@sigx/runtime-core';\r\nimport {\r\n onKey, registerFocusable, unregisterFocusable, focusState, focus, resolveColor, GLYPHS,\r\n} from '@sigx/terminal-zero';\r\n\r\nexport interface RadioOption<T = string> {\r\n label: string;\r\n value: T;\r\n}\r\n\r\n/**\r\n * Single-choice group. Like Select, but every option is always visible with a\r\n * ● / ○ marker. Arrow keys (or j/k) move the selection.\r\n */\r\nexport const Radio = component<\r\n Define.Model<string> &\r\n Define.Prop<\"options\", RadioOption[], true> &\r\n Define.Prop<\"label\", string, false> &\r\n Define.Prop<\"autofocus\", boolean, false> &\r\n Define.Event<\"change\", string>\r\n>(({ props, emit }) => {\r\n const id = Math.random().toString(36).slice(2);\r\n const isFocused = () => focusState.activeId === id;\r\n\r\n const getCurrentIndex = () => {\r\n const options = props.options || [];\r\n const idx = options.findIndex(o => o.value === props.model?.value);\r\n return idx >= 0 ? idx : 0;\r\n };\r\n\r\n const move = (delta: number) => {\r\n const options = props.options || [];\r\n if (options.length === 0) return;\r\n const len = options.length;\r\n const newIndex = (getCurrentIndex() + delta + len) % len;\r\n const newValue = options[newIndex].value;\r\n if (props.model) props.model.value = newValue;\r\n emit('change', newValue);\r\n };\r\n\r\n const handleKey = (key: string) => {\r\n if (!isFocused()) return;\r\n if (key === '\\x1B[A' || key === 'k') move(-1);\r\n else if (key === '\\x1B[B' || key === 'j') move(1);\r\n };\r\n\r\n let keyCleanup: (() => void) | null = null;\r\n\r\n onMounted(() => {\r\n registerFocusable(id);\r\n if (props.autofocus) focus(id);\r\n keyCleanup = onKey(handleKey);\r\n });\r\n\r\n onUnmounted(() => {\r\n if (keyCleanup) keyCleanup();\r\n unregisterFocusable(id);\r\n });\r\n\r\n return () => {\r\n const options = props.options || [];\r\n const focused = isFocused();\r\n const currentValue = props.model?.value || options[0]?.value || '';\r\n\r\n const rows = options.map((option) => {\r\n const isSelected = option.value === currentValue;\r\n const marker = isSelected ? GLYPHS.radioOn : GLYPHS.radioOff;\r\n return (\r\n <box>\r\n <text color={resolveColor(isSelected ? 'accent' : 'faint')}>{marker} </text>\r\n <text color={resolveColor(isSelected ? 'fg' : 'dim')}>{option.label}</text>\r\n </box>\r\n );\r\n });\r\n\r\n return (\r\n <box border=\"rounded\" borderColor={resolveColor(focused ? 'accent' : 'line')} label={props.label} labelColor={resolveColor(focused ? 'accent' : 'dim')}>\r\n {rows}\r\n </box>\r\n );\r\n };\r\n}, { name: 'Radio' });\r\n\r\nexport default Radio;\r\n","/** @jsxImportSource @sigx/runtime-core */\r\nimport { component, onMounted, onUnmounted, signal, type Define } from '@sigx/runtime-core';\r\nimport {\r\n onKey, registerFocusable, unregisterFocusable, focusState, focus, resolveColor, GLYPHS, READY_DELAY_MS,\r\n} from '@sigx/terminal-zero';\r\n\r\nexport interface MultiSelectOption<T = string> {\r\n label: string;\r\n value: T;\r\n description?: string;\r\n /**\r\n * Section header rendered above this option when it differs from the\r\n * previous option's group (e.g. \"connected devices\" / \"available to\r\n * boot\"). Purely visual — the cursor skips headers and indexes stay\r\n * option-based. Pre-sort options by group.\r\n */\r\n group?: string;\r\n}\r\n\r\n/**\r\n * Checkbox list with a movable cursor. Model holds the checked values.\r\n * Keys (while focused): ↑/k ↓/j move, Space toggles, `a` toggles all,\r\n * Enter submits (blocked with a hint when `required` and nothing is checked).\r\n */\r\nexport const MultiSelect = component<\r\n Define.Model<string[]> &\r\n Define.Prop<\"options\", MultiSelectOption[], true> &\r\n Define.Prop<\"label\", string, false> &\r\n Define.Prop<\"autofocus\", boolean, false> &\r\n Define.Prop<\"required\", boolean, false> &\r\n Define.Prop<\"showHint\", boolean, false> &\r\n Define.Event<\"change\", string[]> &\r\n Define.Event<\"submit\", string[]>\r\n>(({ props, emit }) => {\r\n const id = Math.random().toString(36).slice(2);\r\n let isReady = false;\r\n const isFocused = () => focusState.activeId === id;\r\n const state = signal({ cursor: 0, requiredHint: false });\r\n\r\n const getChecked = () => props.model?.value ?? [];\r\n const setChecked = (next: string[]) => {\r\n if (props.model) props.model.value = next;\r\n emit('change', next);\r\n };\r\n\r\n const handleKey = (key: string) => {\r\n if (!isFocused() || !isReady) return;\r\n const options = props.options || [];\r\n if (options.length === 0) return;\r\n\r\n if (key === '\\x1B[A' || key === 'k') {\r\n state.cursor = state.cursor > 0 ? state.cursor - 1 : options.length - 1;\r\n return;\r\n }\r\n if (key === '\\x1B[B' || key === 'j') {\r\n state.cursor = state.cursor < options.length - 1 ? state.cursor + 1 : 0;\r\n return;\r\n }\r\n if (key === ' ') {\r\n const value = options[state.cursor].value;\r\n const checked = getChecked();\r\n setChecked(checked.includes(value) ? checked.filter((v) => v !== value) : [...checked, value]);\r\n state.requiredHint = false;\r\n return;\r\n }\r\n if (key === 'a') {\r\n const all = options.map((o) => o.value);\r\n setChecked(getChecked().length === all.length ? [] : all);\r\n state.requiredHint = false;\r\n return;\r\n }\r\n if (key === '\\r' || key === '\\n') {\r\n const checked = getChecked();\r\n if (props.required && checked.length === 0) {\r\n state.requiredHint = true;\r\n return;\r\n }\r\n emit('submit', checked);\r\n return;\r\n }\r\n };\r\n\r\n let keyCleanup: (() => void) | null = null;\r\n\r\n onMounted(() => {\r\n registerFocusable(id);\r\n if (props.autofocus) focus(id);\r\n keyCleanup = onKey(handleKey);\r\n setTimeout(() => { isReady = true; }, READY_DELAY_MS);\r\n });\r\n\r\n onUnmounted(() => {\r\n if (keyCleanup) keyCleanup();\r\n unregisterFocusable(id);\r\n });\r\n\r\n return () => {\r\n const options = props.options || [];\r\n const focused = isFocused();\r\n const checked = getChecked();\r\n\r\n const rows = options.flatMap((option, i) => {\r\n const onCursor = i === state.cursor;\r\n const isChecked = checked.includes(option.value);\r\n const header = option.group && option.group !== options[i - 1]?.group\r\n ? [<box><text color={resolveColor('dim')}>{option.group}</text></box>]\r\n : [];\r\n return [\r\n ...header,\r\n <box>\r\n <text color={resolveColor('accent')}>{onCursor ? GLYPHS.cursor : ' '} </text>\r\n <text color={resolveColor(isChecked ? 'success' : 'line')}>\r\n {isChecked ? GLYPHS.checkboxOn : GLYPHS.checkboxOff}\r\n </text>\r\n <text color={resolveColor(onCursor ? 'accent' : 'fg')}> {option.label}</text>\r\n {option.description && onCursor && <text color={resolveColor('dim')}> — {option.description}</text>}\r\n </box>,\r\n ];\r\n });\r\n\r\n const hint = state.requiredHint\r\n ? <box><text color={resolveColor('danger')}> select at least one option (space)</text></box>\r\n : props.showHint\r\n ? <box><text color={resolveColor('dim')}> space select · a all · enter confirm</text></box>\r\n : null;\r\n\r\n return (\r\n <box>\r\n <box border=\"rounded\" borderColor={resolveColor(focused ? 'accent' : 'line')} label={props.label} labelColor={resolveColor(focused ? 'accent' : 'dim')}>\r\n {rows}\r\n </box>\r\n {hint}\r\n </box>\r\n );\r\n };\r\n}, { name: 'MultiSelect' });\r\n\r\nexport default MultiSelect;\r\n","/** @jsxImportSource @sigx/runtime-core */\r\nimport { component, onMounted, onUnmounted, type Define } from '@sigx/runtime-core';\r\nimport {\r\n onKey, registerFocusable, unregisterFocusable, focusState, focus, resolveColor, GLYPHS, READY_DELAY_MS,\r\n} from '@sigx/terminal-zero';\r\n\r\n/**\r\n * Yes/No toggle. Keys (while focused): y/Y and n/N answer immediately,\r\n * ←/→ (or h/l) flip the selection, Enter submits the current value.\r\n */\r\nexport const Confirm = component<\r\n Define.Model<boolean> &\r\n Define.Prop<\"label\", string, false> &\r\n Define.Prop<\"activeLabel\", string, false> &\r\n Define.Prop<\"inactiveLabel\", string, false> &\r\n Define.Prop<\"autofocus\", boolean, false> &\r\n Define.Event<\"change\", boolean> &\r\n Define.Event<\"submit\", boolean>\r\n>(({ props, emit }) => {\r\n const id = Math.random().toString(36).slice(2);\r\n let isReady = false;\r\n const isFocused = () => focusState.activeId === id;\r\n\r\n const getValue = () => props.model?.value ?? true;\r\n const setValue = (next: boolean) => {\r\n if (props.model) props.model.value = next;\r\n emit('change', next);\r\n };\r\n\r\n const handleKey = (key: string) => {\r\n if (!isFocused() || !isReady) return;\r\n if (key === 'y' || key === 'Y') {\r\n setValue(true);\r\n emit('submit', true);\r\n return;\r\n }\r\n if (key === 'n' || key === 'N') {\r\n setValue(false);\r\n emit('submit', false);\r\n return;\r\n }\r\n if (key === '\\x1B[D' || key === '\\x1B[C' || key === 'h' || key === 'l') {\r\n setValue(!getValue());\r\n return;\r\n }\r\n if (key === '\\r' || key === '\\n') {\r\n emit('submit', getValue());\r\n return;\r\n }\r\n };\r\n\r\n let keyCleanup: (() => void) | null = null;\r\n\r\n onMounted(() => {\r\n registerFocusable(id);\r\n if (props.autofocus) focus(id);\r\n keyCleanup = onKey(handleKey);\r\n setTimeout(() => { isReady = true; }, READY_DELAY_MS);\r\n });\r\n\r\n onUnmounted(() => {\r\n if (keyCleanup) keyCleanup();\r\n unregisterFocusable(id);\r\n });\r\n\r\n return () => {\r\n const focused = isFocused();\r\n const value = getValue();\r\n const yes = props.activeLabel || 'Yes';\r\n const no = props.inactiveLabel || 'No';\r\n\r\n return (\r\n <box>\r\n <text color={resolveColor(focused ? 'accent' : 'line')}>{focused ? GLYPHS.focusBar : ' '} </text>\r\n {props.label && <text color={resolveColor('fg')}>{props.label} </text>}\r\n <text color={resolveColor(value ? 'accent' : 'dim')}>\r\n {value ? GLYPHS.radioOn : GLYPHS.radioOff} {yes}\r\n </text>\r\n <text> </text>\r\n <text color={resolveColor(value ? 'dim' : 'accent')}>\r\n {value ? GLYPHS.radioOff : GLYPHS.radioOn} {no}\r\n </text>\r\n </box>\r\n );\r\n };\r\n}, { name: 'Confirm' });\r\n\r\nexport default Confirm;\r\n","/** @jsxImportSource @sigx/runtime-core */\r\nimport { component, onMounted, onUnmounted, signal, type Define } from '@sigx/runtime-core';\r\nimport {\r\n onKey, registerFocusable, unregisterFocusable, focusState, focus, resolveColor,\r\n getTerminalSize, GLYPHS, READY_DELAY_MS,\r\n layoutText, cursorToRowCol, insertAt, deleteBefore, deleteAt,\r\n moveLeft, moveRight, moveVertical, moveLineStart, moveLineEnd,\r\n type TextBufferState,\r\n} from '@sigx/terminal-zero';\r\n\r\nconst DEL = String.fromCharCode(127);\r\nconst BS = String.fromCharCode(8);\r\nconst ESC = String.fromCharCode(27);\r\n\r\n/**\r\n * Growing multi-line text input — the Claude-style prompt box. Soft-wraps to\r\n * `width`, grows from one row up to `maxRows` (then scrolls internally,\r\n * keeping the cursor visible), with a movable block cursor.\r\n *\r\n * Keys (while focused): printable characters and paste chunks insert at the\r\n * cursor; ←/→/↑/↓ move (↑/↓ keep a sticky goal column); Home/End jump within\r\n * the visual row; Backspace/Delete edit around the cursor. Enter (`\\r`)\r\n * submits — unless the text ends with `\\`, which is stripped and replaced by\r\n * a newline (continuation). Newlines: Ctrl+J always; ESC+`\\r` and the CSI-u\r\n * Shift/Ctrl+Enter encodings for terminals configured to send them (plain\r\n * Shift+Enter is indistinguishable from Enter on unconfigured terminals).\r\n * NOTE: this deliberately diverges from the prompt engine's `isEnter` (which\r\n * treats `\\r` and `\\n` alike) — an editor needs the distinction.\r\n *\r\n * Paste chunks containing escape sequences keep only the printable prefix\r\n * (bracketed-paste mode is not implemented).\r\n */\r\nexport const TextArea = component<\r\n Define.Model<string> &\r\n Define.Prop<'placeholder', string, false> &\r\n Define.Prop<'promptGlyph', string, false> &\r\n Define.Prop<'width', number, false> &\r\n Define.Prop<'maxRows', number, false> &\r\n Define.Prop<'autofocus', boolean, false> &\r\n Define.Event<'input', string> &\r\n Define.Event<'submit', string>\r\n>(({ props, emit }) => {\r\n const id = Math.random().toString(36).slice(2);\r\n let isReady = false;\r\n const isFocused = () => focusState.activeId === id;\r\n const state = signal({ cursor: 0, goalCol: -1 });\r\n\r\n const getValue = () => props.model?.value ?? '';\r\n const innerWidth = () => Math.max(4, (props.width || Math.max(20, getTerminalSize().columns - 4)) - 2);\r\n\r\n const buf = (): TextBufferState => {\r\n const text = getValue();\r\n return { text, cursor: Math.min(state.cursor, [...text].length) };\r\n };\r\n const apply = (next: TextBufferState, emitInput: boolean) => {\r\n if (props.model) props.model.value = next.text;\r\n state.cursor = next.cursor;\r\n if (emitInput) emit('input', next.text);\r\n };\r\n\r\n const handleKey = (key: string): boolean | void => {\r\n if (!isFocused() || !isReady) return;\r\n const width = innerWidth();\r\n\r\n if (key === '\\r') {\r\n const text = getValue();\r\n if (text.endsWith('\\\\')) {\r\n // Continuation: strip the backslash, insert a newline.\r\n const chars = [...text];\r\n const stripped = { text: chars.slice(0, -1).join(''), cursor: Math.min(buf().cursor, chars.length - 1) };\r\n apply(insertAt(stripped, '\\n'), true);\r\n state.goalCol = -1;\r\n return true;\r\n }\r\n emit('submit', text);\r\n return true;\r\n }\r\n // Newline keys: Ctrl+J (bare \\n) works everywhere; ESC+\\r is what\r\n // terminals configured for Shift+Enter/Alt+Enter send (e.g. Claude\r\n // Code's /terminal-setup binding); \\x1b[13;2u (and ;5u) is the CSI-u\r\n // encoding of Shift+Enter / Ctrl+Enter. Plain Shift+Enter on an\r\n // unconfigured terminal is indistinguishable from Enter (\\r).\r\n if (key === '\\n' || key === ESC + '\\r' || key === ESC + '[13;2u' || key === ESC + '[13;5u') {\r\n apply(insertAt(buf(), '\\n'), true);\r\n state.goalCol = -1;\r\n return true;\r\n }\r\n if (key === DEL || key === BS) {\r\n apply(deleteBefore(buf()), true);\r\n state.goalCol = -1;\r\n return true;\r\n }\r\n if (key === ESC + '[3~') { // forward delete\r\n apply(deleteAt(buf()), true);\r\n state.goalCol = -1;\r\n return true;\r\n }\r\n if (key === ESC + '[D') {\r\n apply(moveLeft(buf()), false);\r\n state.goalCol = -1;\r\n return true;\r\n }\r\n if (key === ESC + '[C') {\r\n apply(moveRight(buf()), false);\r\n state.goalCol = -1;\r\n return true;\r\n }\r\n if (key === ESC + '[A' || key === ESC + '[B') {\r\n const dir = key === ESC + '[A' ? -1 : 1;\r\n const r = moveVertical(buf(), width, dir, state.goalCol >= 0 ? state.goalCol : undefined);\r\n apply(r.state, false);\r\n state.goalCol = r.goalCol;\r\n return true;\r\n }\r\n if (key === ESC + '[H' || key === ESC + '[1~') {\r\n apply(moveLineStart(buf(), width), false);\r\n state.goalCol = -1;\r\n return true;\r\n }\r\n if (key === ESC + '[F' || key === ESC + '[4~') {\r\n apply(moveLineEnd(buf(), width), false);\r\n state.goalCol = -1;\r\n return true;\r\n }\r\n // Printable characters and paste chunks. A chunk starting with ESC is\r\n // a key sequence (handled above or not ours); a mixed chunk keeps only\r\n // the printable prefix.\r\n if (key.length >= 1 && key.charCodeAt(0) !== 27) {\r\n let chunk = key.replace(/\\r\\n|\\r/g, '\\n');\r\n const escIdx = chunk.indexOf(ESC);\r\n if (escIdx >= 0) chunk = chunk.slice(0, escIdx);\r\n // Strip control bytes except newline.\r\n chunk = [...chunk].filter((ch) => ch === '\\n' || ch >= ' ').join('');\r\n if (chunk.length === 0) return;\r\n apply(insertAt(buf(), chunk), true);\r\n state.goalCol = -1;\r\n return true;\r\n }\r\n };\r\n\r\n let keyCleanup: (() => void) | null = null;\r\n\r\n onMounted(() => {\r\n registerFocusable(id);\r\n if (props.autofocus) focus(id);\r\n keyCleanup = onKey(handleKey);\r\n setTimeout(() => { isReady = true; }, READY_DELAY_MS);\r\n });\r\n\r\n onUnmounted(() => {\r\n if (keyCleanup) keyCleanup();\r\n unregisterFocusable(id);\r\n });\r\n\r\n return () => {\r\n const focused = isFocused();\r\n const width = innerWidth();\r\n const maxRows = Math.max(1, props.maxRows || 6);\r\n const glyph = props.promptGlyph || GLYPHS.cursor;\r\n const text = getValue();\r\n const cursor = Math.min(state.cursor, [...text].length);\r\n\r\n const layout = layoutText(text, width);\r\n const pos = cursorToRowCol(layout, cursor);\r\n\r\n // Window the rows so the cursor row is always visible.\r\n const totalRows = layout.rows.length;\r\n const visible = Math.min(totalRows, maxRows);\r\n let top = 0;\r\n if (totalRows > maxRows) {\r\n top = Math.min(Math.max(0, pos.row - maxRows + 1), totalRows - maxRows);\r\n if (pos.row < top) top = pos.row;\r\n }\r\n\r\n const rows = [];\r\n for (let r = top; r < top + visible; r++) {\r\n const row = layout.rows[r];\r\n const prefix = r === 0\r\n ? <text color={resolveColor(focused ? 'accent' : 'dim')}>{glyph} </text>\r\n : <text>{' '}</text>;\r\n\r\n let body;\r\n if (focused && r === pos.row) {\r\n // Split the row around the cursor; invert the glyph under it.\r\n const chars = [...row.text];\r\n const at = cursor - row.start;\r\n const before = chars.slice(0, at).join('');\r\n const under = at < chars.length ? chars[at] : ' ';\r\n const after = at < chars.length ? chars.slice(at + 1).join('') : '';\r\n body = (\r\n <text>\r\n <text color={resolveColor('fg')}>{before}</text>\r\n <text backgroundColor={resolveColor('accent')} color={resolveColor('accentText')}>{under}</text>\r\n <text color={resolveColor('fg')}>{after}</text>\r\n </text>\r\n );\r\n } else {\r\n body = <text color={resolveColor('fg')}>{row.text}</text>;\r\n }\r\n\r\n const isPlaceholderRow = r === 0 && text.length === 0 && props.placeholder;\r\n rows.push(\r\n <box>\r\n {prefix}\r\n {body}\r\n {isPlaceholderRow && <text color={resolveColor('dim')}>{props.placeholder}</text>}\r\n </box>,\r\n );\r\n }\r\n\r\n return <box>{rows}</box>;\r\n };\r\n}, { name: 'TextArea' });\r\n\r\nexport default TextArea;\r\n"],"mappings":"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"}
1
+ {"version":3,"file":"forms-MgpyoQI5.js","names":[],"sources":["../src/forms/Input.tsx","../src/forms/Checkbox.tsx","../src/forms/Select.tsx","../src/forms/Radio.tsx","../src/forms/MultiSelect.tsx","../src/forms/Confirm.tsx","../src/forms/TextArea.tsx"],"sourcesContent":["/** @jsxImportSource @sigx/runtime-core */\nimport { component, onMounted, onUnmounted, type Define } from '@sigx/runtime-core';\nimport {\n onKey, registerFocusable, unregisterFocusable, focusState, focus, resolveColor,\n} from '@sigx/terminal-zero';\n\nexport const Input = component<\n Define.Model<string> &\n Define.Prop<\"placeholder\", string, false> &\n Define.Prop<\"autofocus\", boolean, false> &\n Define.Prop<\"label\", string, false> &\n Define.Event<\"input\", string> &\n Define.Event<\"submit\", string>\n>(({ props, emit }) => {\n const id = Math.random().toString(36).slice(2);\n let isReady = false;\n const isFocused = () => focusState.activeId === id;\n const getValue = () => props.model?.value || '';\n\n const handleKey = (key: string) => {\n if (!isFocused() || !isReady) return;\n\n if (key === '\\r' || key === '\\n') {\n emit('submit', getValue());\n return;\n }\n if (key === '\\u007F' || key === '\\b') { // Backspace / Delete\n const val = getValue();\n if (val.length > 0) {\n const newValue = val.slice(0, -1);\n if (props.model) props.model.value = newValue;\n emit('input', newValue);\n }\n return;\n }\n // Printable characters only — control bytes (incl. Tab, which now\n // cascades through the key layers to the focus-cycle handler) and\n // escape sequences are ignored.\n if (key.length !== 1 || key < ' ') return;\n\n const newValue = getValue() + key;\n if (props.model) props.model.value = newValue;\n emit('input', newValue);\n };\n\n let keyCleanup: (() => void) | null = null;\n\n onMounted(() => {\n registerFocusable(id);\n if (props.autofocus) focus(id);\n keyCleanup = onKey(handleKey);\n setTimeout(() => { isReady = true; }, 50);\n });\n\n onUnmounted(() => {\n if (keyCleanup) keyCleanup();\n unregisterFocusable(id);\n });\n\n return () => {\n const val = getValue().replace(/[\\r\\n]+/g, ' ');\n const placeholder = (props.placeholder || '').replace(/[\\r\\n]+/g, ' ');\n const focused = isFocused();\n const hasValue = val.length > 0;\n\n return (\n <box border=\"rounded\" borderColor={resolveColor(focused ? 'accent' : 'line')} label={props.label} labelColor={resolveColor(focused ? 'accent' : 'dim')}>\n <text color={resolveColor(hasValue ? 'fg' : 'dim')}>{val || placeholder}</text>\n {focused && (\n // Block cursor: a reverse-video space (exactly one cell).\n <text backgroundColor={resolveColor('accent')} color={resolveColor('accentText')}> </text>\n )}\n </box>\n );\n };\n}, { name: 'Input' });\n\nexport default Input;\n","/** @jsxImportSource @sigx/runtime-core */\nimport { component, onMounted, onUnmounted, type Define } from '@sigx/runtime-core';\nimport {\n onKey, registerFocusable, unregisterFocusable, focusState, focus, resolveColor, GLYPHS,\n} from '@sigx/terminal-zero';\n\nexport const Checkbox = component<\n Define.Model<boolean> &\n Define.Prop<\"label\", string, false> &\n Define.Prop<\"autofocus\", boolean, false> &\n Define.Prop<\"disabled\", boolean, false> &\n Define.Event<\"change\", boolean>\n>(({ props, emit }) => {\n const id = Math.random().toString(36).slice(2);\n const isFocused = () => focusState.activeId === id;\n const checked = () => !!props.model?.value;\n\n const handleKey = (key: string) => {\n if (!isFocused() || props.disabled) return;\n if (key === '\\r' || key === ' ') { // Enter or Space toggles\n const next = !checked();\n if (props.model) props.model.value = next;\n emit('change', next);\n }\n };\n\n let keyCleanup: (() => void) | null = null;\n\n onMounted(() => {\n registerFocusable(id);\n if (props.autofocus) focus(id);\n keyCleanup = onKey(handleKey);\n });\n\n onUnmounted(() => {\n if (keyCleanup) keyCleanup();\n unregisterFocusable(id);\n });\n\n return () => {\n const label = props.label || '';\n const focused = isFocused();\n const isChecked = checked();\n const disabled = !!props.disabled;\n\n const marker = isChecked ? GLYPHS.checkboxOn : GLYPHS.checkboxOff;\n const markerColor = disabled\n ? resolveColor('faint')\n : resolveColor(isChecked ? 'success' : (focused ? 'accent' : 'line'));\n const labelColor = disabled\n ? resolveColor('faint')\n : resolveColor(focused ? 'accent' : 'fg');\n\n return (\n <box>\n {focused && <text color={resolveColor('accent')}>{GLYPHS.focusBar} </text>}\n <text color={markerColor}>{marker}</text>\n {label && <text color={labelColor}> {label}</text>}\n </box>\n );\n };\n}, { name: 'Checkbox' });\n\nexport default Checkbox;\n","/** @jsxImportSource @sigx/runtime-core */\nimport { component, onMounted, onUnmounted, type Define } from '@sigx/runtime-core';\nimport {\n onKey, registerFocusable, unregisterFocusable, focusState, focus, resolveColor, GLYPHS,\n} from '@sigx/terminal-zero';\n\nexport interface SelectOption<T = string> {\n label: string;\n value: T;\n description?: string;\n}\n\nexport const Select = component<\n Define.Model<string> &\n Define.Prop<\"options\", SelectOption[], true> &\n Define.Prop<\"label\", string, false> &\n Define.Prop<\"autofocus\", boolean, false> &\n Define.Prop<\"showDescription\", boolean, false> &\n Define.Event<\"change\", string> &\n Define.Event<\"submit\", string>\n>(({ props, emit }) => {\n const id = Math.random().toString(36).slice(2);\n let isReady = false;\n const isFocused = () => focusState.activeId === id;\n\n const getCurrentIndex = () => {\n const options = props.options || [];\n const idx = options.findIndex(o => o.value === props.model?.value);\n return idx >= 0 ? idx : 0;\n };\n\n const handleKey = (key: string) => {\n if (!isFocused() || !isReady) return;\n const options = props.options || [];\n if (options.length === 0) return;\n const currentIndex = getCurrentIndex();\n\n if (key === '\\x1B[A' || key === 'k') { // up\n const newIndex = currentIndex > 0 ? currentIndex - 1 : options.length - 1;\n const newValue = options[newIndex].value;\n if (props.model) props.model.value = newValue;\n emit('change', newValue);\n return;\n }\n if (key === '\\x1B[B' || key === 'j') { // down\n const newIndex = currentIndex < options.length - 1 ? currentIndex + 1 : 0;\n const newValue = options[newIndex].value;\n if (props.model) props.model.value = newValue;\n emit('change', newValue);\n return;\n }\n if (key === '\\r' || key === '\\n') { // submit\n emit('submit', props.model?.value || options[0]?.value || '');\n return;\n }\n };\n\n let keyCleanup: (() => void) | null = null;\n\n onMounted(() => {\n registerFocusable(id);\n if (props.autofocus) focus(id);\n keyCleanup = onKey(handleKey);\n setTimeout(() => { isReady = true; }, 50);\n });\n\n onUnmounted(() => {\n if (keyCleanup) keyCleanup();\n unregisterFocusable(id);\n });\n\n return () => {\n const options = props.options || [];\n const focused = isFocused();\n const currentValue = props.model?.value || options[0]?.value || '';\n const selectedOption = options.find(o => o.value === currentValue);\n\n const optionElements = options.map((option) => {\n const isSelected = option.value === currentValue;\n const indicator = isSelected ? GLYPHS.cursor : ' ';\n const rowColor = resolveColor(isSelected ? 'accent' : 'fg');\n return (\n <box>\n <text color={resolveColor(isSelected ? 'accent' : 'faint')}>{indicator} </text>\n <text color={rowColor}>{option.label}</text>\n </box>\n );\n });\n\n const descriptionElement = props.showDescription && selectedOption?.description ? (\n <box>\n <text color={resolveColor('dim')}> ↳ {selectedOption.description}</text>\n </box>\n ) : null;\n\n return (\n <box>\n <box border=\"rounded\" borderColor={resolveColor(focused ? 'accent' : 'line')} label={props.label} labelColor={resolveColor(focused ? 'accent' : 'dim')}>\n {optionElements}\n </box>\n {descriptionElement}\n </box>\n );\n };\n}, { name: 'Select' });\n\nexport default Select;\n","/** @jsxImportSource @sigx/runtime-core */\nimport { component, onMounted, onUnmounted, type Define } from '@sigx/runtime-core';\nimport {\n onKey, registerFocusable, unregisterFocusable, focusState, focus, resolveColor, GLYPHS,\n} from '@sigx/terminal-zero';\n\nexport interface RadioOption<T = string> {\n label: string;\n value: T;\n}\n\n/**\n * Single-choice group. Like Select, but every option is always visible with a\n * ● / ○ marker. Arrow keys (or j/k) move the selection.\n */\nexport const Radio = component<\n Define.Model<string> &\n Define.Prop<\"options\", RadioOption[], true> &\n Define.Prop<\"label\", string, false> &\n Define.Prop<\"autofocus\", boolean, false> &\n Define.Event<\"change\", string>\n>(({ props, emit }) => {\n const id = Math.random().toString(36).slice(2);\n const isFocused = () => focusState.activeId === id;\n\n const getCurrentIndex = () => {\n const options = props.options || [];\n const idx = options.findIndex(o => o.value === props.model?.value);\n return idx >= 0 ? idx : 0;\n };\n\n const move = (delta: number) => {\n const options = props.options || [];\n if (options.length === 0) return;\n const len = options.length;\n const newIndex = (getCurrentIndex() + delta + len) % len;\n const newValue = options[newIndex].value;\n if (props.model) props.model.value = newValue;\n emit('change', newValue);\n };\n\n const handleKey = (key: string) => {\n if (!isFocused()) return;\n if (key === '\\x1B[A' || key === 'k') move(-1);\n else if (key === '\\x1B[B' || key === 'j') move(1);\n };\n\n let keyCleanup: (() => void) | null = null;\n\n onMounted(() => {\n registerFocusable(id);\n if (props.autofocus) focus(id);\n keyCleanup = onKey(handleKey);\n });\n\n onUnmounted(() => {\n if (keyCleanup) keyCleanup();\n unregisterFocusable(id);\n });\n\n return () => {\n const options = props.options || [];\n const focused = isFocused();\n const currentValue = props.model?.value || options[0]?.value || '';\n\n const rows = options.map((option) => {\n const isSelected = option.value === currentValue;\n const marker = isSelected ? GLYPHS.radioOn : GLYPHS.radioOff;\n return (\n <box>\n <text color={resolveColor(isSelected ? 'accent' : 'faint')}>{marker} </text>\n <text color={resolveColor(isSelected ? 'fg' : 'dim')}>{option.label}</text>\n </box>\n );\n });\n\n return (\n <box border=\"rounded\" borderColor={resolveColor(focused ? 'accent' : 'line')} label={props.label} labelColor={resolveColor(focused ? 'accent' : 'dim')}>\n {rows}\n </box>\n );\n };\n}, { name: 'Radio' });\n\nexport default Radio;\n","/** @jsxImportSource @sigx/runtime-core */\nimport { component, onMounted, onUnmounted, signal, type Define } from '@sigx/runtime-core';\nimport {\n onKey, registerFocusable, unregisterFocusable, focusState, focus, resolveColor, GLYPHS, READY_DELAY_MS,\n} from '@sigx/terminal-zero';\n\nexport interface MultiSelectOption<T = string> {\n label: string;\n value: T;\n description?: string;\n /**\n * Section header rendered above this option when it differs from the\n * previous option's group (e.g. \"connected devices\" / \"available to\n * boot\"). Purely visual — the cursor skips headers and indexes stay\n * option-based. Pre-sort options by group.\n */\n group?: string;\n}\n\n/**\n * Checkbox list with a movable cursor. Model holds the checked values.\n * Keys (while focused): ↑/k ↓/j move, Space toggles, `a` toggles all,\n * Enter submits (blocked with a hint when `required` and nothing is checked).\n */\nexport const MultiSelect = component<\n Define.Model<string[]> &\n Define.Prop<\"options\", MultiSelectOption[], true> &\n Define.Prop<\"label\", string, false> &\n Define.Prop<\"autofocus\", boolean, false> &\n Define.Prop<\"required\", boolean, false> &\n Define.Prop<\"showHint\", boolean, false> &\n Define.Event<\"change\", string[]> &\n Define.Event<\"submit\", string[]>\n>(({ props, emit }) => {\n const id = Math.random().toString(36).slice(2);\n let isReady = false;\n const isFocused = () => focusState.activeId === id;\n const state = signal({ cursor: 0, requiredHint: false });\n\n const getChecked = () => props.model?.value ?? [];\n const setChecked = (next: string[]) => {\n if (props.model) props.model.value = next;\n emit('change', next);\n };\n\n const handleKey = (key: string) => {\n if (!isFocused() || !isReady) return;\n const options = props.options || [];\n if (options.length === 0) return;\n\n if (key === '\\x1B[A' || key === 'k') {\n state.cursor = state.cursor > 0 ? state.cursor - 1 : options.length - 1;\n return;\n }\n if (key === '\\x1B[B' || key === 'j') {\n state.cursor = state.cursor < options.length - 1 ? state.cursor + 1 : 0;\n return;\n }\n if (key === ' ') {\n const value = options[state.cursor].value;\n const checked = getChecked();\n setChecked(checked.includes(value) ? checked.filter((v) => v !== value) : [...checked, value]);\n state.requiredHint = false;\n return;\n }\n if (key === 'a') {\n const all = options.map((o) => o.value);\n setChecked(getChecked().length === all.length ? [] : all);\n state.requiredHint = false;\n return;\n }\n if (key === '\\r' || key === '\\n') {\n const checked = getChecked();\n if (props.required && checked.length === 0) {\n state.requiredHint = true;\n return;\n }\n emit('submit', checked);\n return;\n }\n };\n\n let keyCleanup: (() => void) | null = null;\n\n onMounted(() => {\n registerFocusable(id);\n if (props.autofocus) focus(id);\n keyCleanup = onKey(handleKey);\n setTimeout(() => { isReady = true; }, READY_DELAY_MS);\n });\n\n onUnmounted(() => {\n if (keyCleanup) keyCleanup();\n unregisterFocusable(id);\n });\n\n return () => {\n const options = props.options || [];\n const focused = isFocused();\n const checked = getChecked();\n\n const rows = options.flatMap((option, i) => {\n const onCursor = i === state.cursor;\n const isChecked = checked.includes(option.value);\n const header = option.group && option.group !== options[i - 1]?.group\n ? [<box><text color={resolveColor('dim')}>{option.group}</text></box>]\n : [];\n return [\n ...header,\n <box>\n <text color={resolveColor('accent')}>{onCursor ? GLYPHS.cursor : ' '} </text>\n <text color={resolveColor(isChecked ? 'success' : 'line')}>\n {isChecked ? GLYPHS.checkboxOn : GLYPHS.checkboxOff}\n </text>\n <text color={resolveColor(onCursor ? 'accent' : 'fg')}> {option.label}</text>\n {option.description && onCursor && <text color={resolveColor('dim')}> — {option.description}</text>}\n </box>,\n ];\n });\n\n const hint = state.requiredHint\n ? <box><text color={resolveColor('danger')}> select at least one option (space)</text></box>\n : props.showHint\n ? <box><text color={resolveColor('dim')}> space select · a all · enter confirm</text></box>\n : null;\n\n return (\n <box>\n <box border=\"rounded\" borderColor={resolveColor(focused ? 'accent' : 'line')} label={props.label} labelColor={resolveColor(focused ? 'accent' : 'dim')}>\n {rows}\n </box>\n {hint}\n </box>\n );\n };\n}, { name: 'MultiSelect' });\n\nexport default MultiSelect;\n","/** @jsxImportSource @sigx/runtime-core */\nimport { component, onMounted, onUnmounted, type Define } from '@sigx/runtime-core';\nimport {\n onKey, registerFocusable, unregisterFocusable, focusState, focus, resolveColor, GLYPHS, READY_DELAY_MS,\n} from '@sigx/terminal-zero';\n\n/**\n * Yes/No toggle. Keys (while focused): y/Y and n/N answer immediately,\n * ←/→ (or h/l) flip the selection, Enter submits the current value.\n */\nexport const Confirm = component<\n Define.Model<boolean> &\n Define.Prop<\"label\", string, false> &\n Define.Prop<\"activeLabel\", string, false> &\n Define.Prop<\"inactiveLabel\", string, false> &\n Define.Prop<\"autofocus\", boolean, false> &\n Define.Event<\"change\", boolean> &\n Define.Event<\"submit\", boolean>\n>(({ props, emit }) => {\n const id = Math.random().toString(36).slice(2);\n let isReady = false;\n const isFocused = () => focusState.activeId === id;\n\n const getValue = () => props.model?.value ?? true;\n const setValue = (next: boolean) => {\n if (props.model) props.model.value = next;\n emit('change', next);\n };\n\n const handleKey = (key: string) => {\n if (!isFocused() || !isReady) return;\n if (key === 'y' || key === 'Y') {\n setValue(true);\n emit('submit', true);\n return;\n }\n if (key === 'n' || key === 'N') {\n setValue(false);\n emit('submit', false);\n return;\n }\n if (key === '\\x1B[D' || key === '\\x1B[C' || key === 'h' || key === 'l') {\n setValue(!getValue());\n return;\n }\n if (key === '\\r' || key === '\\n') {\n emit('submit', getValue());\n return;\n }\n };\n\n let keyCleanup: (() => void) | null = null;\n\n onMounted(() => {\n registerFocusable(id);\n if (props.autofocus) focus(id);\n keyCleanup = onKey(handleKey);\n setTimeout(() => { isReady = true; }, READY_DELAY_MS);\n });\n\n onUnmounted(() => {\n if (keyCleanup) keyCleanup();\n unregisterFocusable(id);\n });\n\n return () => {\n const focused = isFocused();\n const value = getValue();\n const yes = props.activeLabel || 'Yes';\n const no = props.inactiveLabel || 'No';\n\n return (\n <box>\n <text color={resolveColor(focused ? 'accent' : 'line')}>{focused ? GLYPHS.focusBar : ' '} </text>\n {props.label && <text color={resolveColor('fg')}>{props.label} </text>}\n <text color={resolveColor(value ? 'accent' : 'dim')}>\n {value ? GLYPHS.radioOn : GLYPHS.radioOff} {yes}\n </text>\n <text> </text>\n <text color={resolveColor(value ? 'dim' : 'accent')}>\n {value ? GLYPHS.radioOff : GLYPHS.radioOn} {no}\n </text>\n </box>\n );\n };\n}, { name: 'Confirm' });\n\nexport default Confirm;\n","/** @jsxImportSource @sigx/runtime-core */\nimport { component, onMounted, onUnmounted, signal, type Define } from '@sigx/runtime-core';\nimport {\n onKey, registerFocusable, unregisterFocusable, focusState, focus, resolveColor,\n getTerminalSize, GLYPHS, READY_DELAY_MS,\n layoutText, cursorToRowCol, insertAt, deleteBefore, deleteAt,\n moveLeft, moveRight, moveVertical, moveLineStart, moveLineEnd,\n type TextBufferState,\n} from '@sigx/terminal-zero';\n\nconst DEL = String.fromCharCode(127);\nconst BS = String.fromCharCode(8);\nconst ESC = String.fromCharCode(27);\n\n/**\n * Growing multi-line text input — the Claude-style prompt box. Soft-wraps to\n * `width`, grows from one row up to `maxRows` (then scrolls internally,\n * keeping the cursor visible), with a movable block cursor.\n *\n * Keys (while focused): printable characters and paste chunks insert at the\n * cursor; ←/→/↑/↓ move (↑/↓ keep a sticky goal column); Home/End jump within\n * the visual row; Backspace/Delete edit around the cursor. Enter (`\\r`)\n * submits — unless the text ends with `\\`, which is stripped and replaced by\n * a newline (continuation). Newlines: Ctrl+J always; ESC+`\\r` and the CSI-u\n * Shift/Ctrl+Enter encodings for terminals configured to send them (plain\n * Shift+Enter is indistinguishable from Enter on unconfigured terminals).\n * NOTE: this deliberately diverges from the prompt engine's `isEnter` (which\n * treats `\\r` and `\\n` alike) — an editor needs the distinction.\n *\n * Paste chunks containing escape sequences keep only the printable prefix\n * (bracketed-paste mode is not implemented).\n */\nexport const TextArea = component<\n Define.Model<string> &\n Define.Prop<'placeholder', string, false> &\n Define.Prop<'promptGlyph', string, false> &\n Define.Prop<'width', number, false> &\n Define.Prop<'maxRows', number, false> &\n Define.Prop<'autofocus', boolean, false> &\n Define.Event<'input', string> &\n Define.Event<'submit', string>\n>(({ props, emit }) => {\n const id = Math.random().toString(36).slice(2);\n let isReady = false;\n const isFocused = () => focusState.activeId === id;\n const state = signal({ cursor: 0, goalCol: -1 });\n\n const getValue = () => props.model?.value ?? '';\n const innerWidth = () => Math.max(4, (props.width || Math.max(20, getTerminalSize().columns - 4)) - 2);\n\n const buf = (): TextBufferState => {\n const text = getValue();\n return { text, cursor: Math.min(state.cursor, [...text].length) };\n };\n const apply = (next: TextBufferState, emitInput: boolean) => {\n if (props.model) props.model.value = next.text;\n state.cursor = next.cursor;\n if (emitInput) emit('input', next.text);\n };\n\n const handleKey = (key: string): boolean | void => {\n if (!isFocused() || !isReady) return;\n const width = innerWidth();\n\n if (key === '\\r') {\n const text = getValue();\n if (text.endsWith('\\\\')) {\n // Continuation: strip the backslash, insert a newline.\n const chars = [...text];\n const stripped = { text: chars.slice(0, -1).join(''), cursor: Math.min(buf().cursor, chars.length - 1) };\n apply(insertAt(stripped, '\\n'), true);\n state.goalCol = -1;\n return true;\n }\n emit('submit', text);\n return true;\n }\n // Newline keys: Ctrl+J (bare \\n) works everywhere; ESC+\\r is what\n // terminals configured for Shift+Enter/Alt+Enter send (e.g. Claude\n // Code's /terminal-setup binding); \\x1b[13;2u (and ;5u) is the CSI-u\n // encoding of Shift+Enter / Ctrl+Enter. Plain Shift+Enter on an\n // unconfigured terminal is indistinguishable from Enter (\\r).\n if (key === '\\n' || key === ESC + '\\r' || key === ESC + '[13;2u' || key === ESC + '[13;5u') {\n apply(insertAt(buf(), '\\n'), true);\n state.goalCol = -1;\n return true;\n }\n if (key === DEL || key === BS) {\n apply(deleteBefore(buf()), true);\n state.goalCol = -1;\n return true;\n }\n if (key === ESC + '[3~') { // forward delete\n apply(deleteAt(buf()), true);\n state.goalCol = -1;\n return true;\n }\n if (key === ESC + '[D') {\n apply(moveLeft(buf()), false);\n state.goalCol = -1;\n return true;\n }\n if (key === ESC + '[C') {\n apply(moveRight(buf()), false);\n state.goalCol = -1;\n return true;\n }\n if (key === ESC + '[A' || key === ESC + '[B') {\n const dir = key === ESC + '[A' ? -1 : 1;\n const r = moveVertical(buf(), width, dir, state.goalCol >= 0 ? state.goalCol : undefined);\n apply(r.state, false);\n state.goalCol = r.goalCol;\n return true;\n }\n if (key === ESC + '[H' || key === ESC + '[1~') {\n apply(moveLineStart(buf(), width), false);\n state.goalCol = -1;\n return true;\n }\n if (key === ESC + '[F' || key === ESC + '[4~') {\n apply(moveLineEnd(buf(), width), false);\n state.goalCol = -1;\n return true;\n }\n // Printable characters and paste chunks. A chunk starting with ESC is\n // a key sequence (handled above or not ours); a mixed chunk keeps only\n // the printable prefix.\n if (key.length >= 1 && key.charCodeAt(0) !== 27) {\n let chunk = key.replace(/\\r\\n|\\r/g, '\\n');\n const escIdx = chunk.indexOf(ESC);\n if (escIdx >= 0) chunk = chunk.slice(0, escIdx);\n // Strip control bytes except newline.\n chunk = [...chunk].filter((ch) => ch === '\\n' || ch >= ' ').join('');\n if (chunk.length === 0) return;\n apply(insertAt(buf(), chunk), true);\n state.goalCol = -1;\n return true;\n }\n };\n\n let keyCleanup: (() => void) | null = null;\n\n onMounted(() => {\n registerFocusable(id);\n if (props.autofocus) focus(id);\n keyCleanup = onKey(handleKey);\n setTimeout(() => { isReady = true; }, READY_DELAY_MS);\n });\n\n onUnmounted(() => {\n if (keyCleanup) keyCleanup();\n unregisterFocusable(id);\n });\n\n return () => {\n const focused = isFocused();\n const width = innerWidth();\n const maxRows = Math.max(1, props.maxRows || 6);\n const glyph = props.promptGlyph || GLYPHS.cursor;\n const text = getValue();\n const cursor = Math.min(state.cursor, [...text].length);\n\n const layout = layoutText(text, width);\n const pos = cursorToRowCol(layout, cursor);\n\n // Window the rows so the cursor row is always visible.\n const totalRows = layout.rows.length;\n const visible = Math.min(totalRows, maxRows);\n let top = 0;\n if (totalRows > maxRows) {\n top = Math.min(Math.max(0, pos.row - maxRows + 1), totalRows - maxRows);\n if (pos.row < top) top = pos.row;\n }\n\n const rows = [];\n for (let r = top; r < top + visible; r++) {\n const row = layout.rows[r];\n const prefix = r === 0\n ? <text color={resolveColor(focused ? 'accent' : 'dim')}>{glyph} </text>\n : <text>{' '}</text>;\n\n let body;\n if (focused && r === pos.row) {\n // Split the row around the cursor; invert the glyph under it.\n const chars = [...row.text];\n const at = cursor - row.start;\n const before = chars.slice(0, at).join('');\n const under = at < chars.length ? chars[at] : ' ';\n const after = at < chars.length ? chars.slice(at + 1).join('') : '';\n body = (\n <text>\n <text color={resolveColor('fg')}>{before}</text>\n <text backgroundColor={resolveColor('accent')} color={resolveColor('accentText')}>{under}</text>\n <text color={resolveColor('fg')}>{after}</text>\n </text>\n );\n } else {\n body = <text color={resolveColor('fg')}>{row.text}</text>;\n }\n\n const isPlaceholderRow = r === 0 && text.length === 0 && props.placeholder;\n rows.push(\n <box>\n {prefix}\n {body}\n {isPlaceholderRow && <text color={resolveColor('dim')}>{props.placeholder}</text>}\n </box>,\n );\n }\n\n return <box>{rows}</box>;\n };\n}, { name: 'TextArea' });\n\nexport default TextArea;\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"fx-igWaVCkB.js","names":[],"sources":["../src/fx/paint.ts","../src/fx/Gradient.tsx","../src/fx/Shimmer.tsx","../src/fx/blockFont.ts","../src/fx/Banner.tsx","../src/fx/PixelArt.tsx"],"sourcesContent":["/**\r\n * Internal helpers for the FX components: build a single string with embedded\r\n * SGR escapes (one text node per line — one prop patch per frame, no per-char\r\n * vdom churn). The renderer passes embedded escapes through intact and its\r\n * truncation/measurement are escape-aware.\r\n */\r\nimport { hexToSGR, resolveColor } from '@sigx/terminal-zero';\r\nimport { GRADIENT_PRESETS, type GradientPreset } from './presets';\r\n\r\n/**\r\n * Colorize per code point. `hexAt(i, len)` returns the hex for visible char\r\n * `i`. Run-length emission: an SGR is only emitted when the sampled color\r\n * changes (spaces keep the current run). Ends with default-fg (`\\x1b[39m`,\r\n * not a full reset — it must not clobber renderer-applied backgrounds) when\r\n * any color was emitted.\r\n */\r\nexport function colorizeByIndex(text: string, hexAt: (i: number, len: number) => string): string {\r\n const chars = [...text];\r\n let out = '';\r\n let lastHex: string | null = null;\r\n let emitted = false;\r\n for (let i = 0; i < chars.length; i++) {\r\n const ch = chars[i];\r\n if (ch === ' ') {\r\n out += ch;\r\n continue;\r\n }\r\n const hex = hexAt(i, chars.length);\r\n if (hex !== lastHex) {\r\n const sgr = hexToSGR(hex);\r\n if (sgr) {\r\n out += sgr;\r\n emitted = true;\r\n }\r\n lastHex = hex;\r\n }\r\n out += ch;\r\n }\r\n return emitted ? out + '\\x1b[39m' : out;\r\n}\r\n\r\n/** Resolve gradient stops: explicit colors win over a preset; tokens → hex. */\r\nexport function resolveStops(colors: string[] | undefined, preset: string | undefined): string[] {\r\n const raw = colors && colors.length > 0\r\n ? colors\r\n : [...GRADIENT_PRESETS[(preset as GradientPreset) || 'sigx'] ?? GRADIENT_PRESETS.sigx];\r\n return raw.map((c) => resolveColor(c));\r\n}\r\n\r\n/**\r\n * Ping-pong a sample sequence (a → b → a without repeating the endpoints), so\r\n * scrolling a non-cyclic palette has no seam.\r\n */\r\nexport function pingPong(samples: string[]): string[] {\r\n if (samples.length <= 2) return samples;\r\n return samples.concat(samples.slice(1, -1).reverse());\r\n}\r\n","/** @jsxImportSource @sigx/runtime-core */\r\nimport { component, onMounted, onUnmounted, type Define } from '@sigx/runtime-core';\r\nimport { resolveColor, getColorDepth, gradient, getTick, subscribeTicker } from '@sigx/terminal-zero';\r\nimport { colorizeByIndex, resolveStops, pingPong } from './paint';\r\nimport type { GradientPreset } from './presets';\r\n\r\n/**\r\n * Text colored across a multi-stop gradient, one sample per character.\r\n * Stops are theme tokens or hex; `animate` scrolls the gradient through the\r\n * text (ping-pong, seamless). Degrades by color depth: a single accent color\r\n * at ansi16 (per-char nearest-16 is speckle noise), plain text at none.\r\n */\r\nexport const Gradient = component<\r\n Define.Prop<\"text\", string, true> &\r\n Define.Prop<\"colors\", string[], false> &\r\n Define.Prop<\"preset\", GradientPreset, false> &\r\n Define.Prop<\"animate\", boolean, false> &\r\n Define.Prop<\"speed\", number, false>\r\n>(({ props }) => {\r\n let unsub: (() => void) | null = null;\r\n\r\n onMounted(() => {\r\n if (props.animate) unsub = subscribeTicker();\r\n });\r\n onUnmounted(() => { unsub?.(); });\r\n\r\n return () => {\r\n // Block root (<box>), like every other component in the library — an\r\n // inline root glues onto whatever line precedes it. For inline\r\n // gradient strings, use colorizeByIndex/paintToken directly.\r\n const text = props.text ?? '';\r\n const depth = getColorDepth();\r\n if (depth === 'none') return <box><text>{text}</text></box>;\r\n if (depth === 'ansi16') return <box><text color={resolveColor('accent')}>{text}</text></box>;\r\n\r\n const stops = resolveStops(props.colors, props.preset);\r\n const len = [...text].length;\r\n const samples = gradient(stops, Math.max(len, 2));\r\n\r\n if (!props.animate) {\r\n return <box><text>{colorizeByIndex(text, (i) => samples[Math.min(i, samples.length - 1)])}</text></box>;\r\n }\r\n const cycle = pingPong(samples);\r\n const phase = Math.floor(getTick() * (props.speed || 1));\r\n return <box><text>{colorizeByIndex(text, (i) => cycle[(i + phase) % cycle.length])}</text></box>;\r\n };\r\n}, { name: 'Gradient' });\r\n\r\nexport default Gradient;\r\n","/** @jsxImportSource @sigx/runtime-core */\r\nimport { component, onMounted, onUnmounted, type Define } from '@sigx/runtime-core';\r\nimport { resolveColor, getColorDepth, mixHex, getTick, subscribeTicker } from '@sigx/terminal-zero';\r\nimport { colorizeByIndex } from './paint';\r\n\r\n/**\r\n * A highlight sweep travelling across muted text — the \"thinking…\" effect.\r\n * `color` is the base (default `dim`), `highlight` the sweep peak (default\r\n * `fg`), `width` the sweep half-width in characters. Static base color at\r\n * ansi16/none depth.\r\n */\r\nexport const Shimmer = component<\r\n Define.Prop<\"text\", string, true> &\r\n Define.Prop<\"color\", string, false> &\r\n Define.Prop<\"highlight\", string, false> &\r\n Define.Prop<\"width\", number, false> &\r\n Define.Prop<\"speed\", number, false>\r\n>(({ props }) => {\r\n let unsub: (() => void) | null = null;\r\n\r\n onMounted(() => { unsub = subscribeTicker(); });\r\n onUnmounted(() => { unsub?.(); });\r\n\r\n return () => {\r\n // Block root (<box>), like every other component in the library — an\r\n // inline root glues onto whatever line precedes it (a thinking…\r\n // shimmer must not land on the input's row).\r\n const text = props.text ?? '';\r\n const baseToken = props.color || 'dim';\r\n const depth = getColorDepth();\r\n if (depth === 'none') return <box><text>{text}</text></box>;\r\n if (depth === 'ansi16') return <box><text color={resolveColor(baseToken)}>{text}</text></box>;\r\n\r\n const base = resolveColor(baseToken);\r\n const highlight = resolveColor(props.highlight || 'fg');\r\n const w = Math.max(1, props.width || 3);\r\n const len = [...text].length;\r\n // The sweep runs off both ends so the text rests between passes.\r\n const span = len + 2 * w;\r\n const pos = (getTick() * (props.speed || 1)) % span - w;\r\n\r\n return (\r\n <box>\r\n <text>\r\n {colorizeByIndex(text, (i) => {\r\n const d = Math.abs(i - pos);\r\n return d < w ? mixHex(base, highlight, 1 - d / w) : base;\r\n })}\r\n </text>\r\n </box>\r\n );\r\n };\r\n}, { name: 'Shimmer' });\r\n\r\nexport default Shimmer;\r\n","/**\r\n * Built-in 5-row block font for <Banner> — full blocks and spaces only, so\r\n * every cell is width-1 (gradient-fillable, no wide-glyph drift). Glyphs are\r\n * authored as '#'/'.' strings for readability and converted once at load.\r\n * No external font dependency.\r\n */\r\n\r\nconst RAW: Record<string, string[]> = {\r\n A: ['.##.', '#..#', '####', '#..#', '#..#'],\r\n B: ['###.', '#..#', '###.', '#..#', '###.'],\r\n C: ['.###', '#...', '#...', '#...', '.###'],\r\n D: ['###.', '#..#', '#..#', '#..#', '###.'],\r\n E: ['####', '#...', '###.', '#...', '####'],\r\n F: ['####', '#...', '###.', '#...', '#...'],\r\n G: ['.###', '#...', '#.##', '#..#', '.##.'],\r\n H: ['#..#', '#..#', '####', '#..#', '#..#'],\r\n I: ['###', '.#.', '.#.', '.#.', '###'],\r\n J: ['..##', '...#', '...#', '#..#', '.##.'],\r\n K: ['#..#', '#.#.', '##..', '#.#.', '#..#'],\r\n L: ['#...', '#...', '#...', '#...', '####'],\r\n M: ['#...#', '##.##', '#.#.#', '#...#', '#...#'],\r\n N: ['#...#', '##..#', '#.#.#', '#..##', '#...#'],\r\n O: ['.##.', '#..#', '#..#', '#..#', '.##.'],\r\n P: ['###.', '#..#', '###.', '#...', '#...'],\r\n Q: ['.##.', '#..#', '#..#', '#.##', '.###'],\r\n R: ['###.', '#..#', '###.', '#.#.', '#..#'],\r\n S: ['.###', '#...', '.##.', '...#', '###.'],\r\n T: ['#####', '..#..', '..#..', '..#..', '..#..'],\r\n U: ['#..#', '#..#', '#..#', '#..#', '.##.'],\r\n V: ['#...#', '#...#', '#...#', '.#.#.', '..#..'],\r\n W: ['#...#', '#...#', '#.#.#', '##.##', '#...#'],\r\n X: ['#...#', '.#.#.', '..#..', '.#.#.', '#...#'],\r\n Y: ['#...#', '.#.#.', '..#..', '..#..', '..#..'],\r\n Z: ['####', '...#', '..#.', '.#..', '####'],\r\n '0': ['.##.', '#..#', '#..#', '#..#', '.##.'],\r\n '1': ['.#.', '##.', '.#.', '.#.', '###'],\r\n '2': ['.##.', '#..#', '..#.', '.#..', '####'],\r\n '3': ['###.', '...#', '.##.', '...#', '###.'],\r\n '4': ['#..#', '#..#', '####', '...#', '...#'],\r\n '5': ['####', '#...', '###.', '...#', '###.'],\r\n '6': ['.##.', '#...', '###.', '#..#', '.##.'],\r\n '7': ['####', '...#', '..#.', '.#..', '.#..'],\r\n '8': ['.##.', '#..#', '.##.', '#..#', '.##.'],\r\n '9': ['.##.', '#..#', '.###', '...#', '.##.'],\r\n ' ': ['..', '..', '..', '..', '..'],\r\n '-': ['...', '...', '###', '...', '...'],\r\n '.': ['.', '.', '.', '.', '#'],\r\n '!': ['#', '#', '#', '.', '#'],\r\n ':': ['.', '#', '.', '#', '.'],\r\n '/': ['...#', '..#.', '..#.', '.#..', '#...'],\r\n};\r\n\r\nexport const BLOCK_FONT_HEIGHT = 5;\r\n\r\nconst FONT: Record<string, string[]> = {};\r\nfor (const [ch, rows] of Object.entries(RAW)) {\r\n FONT[ch] = rows.map((r) => r.replace(/#/g, '█').replace(/\\./g, ' '));\r\n}\r\n\r\n/**\r\n * Render text in the block font: 5 equal-width rows of '█'/space. Input is\r\n * uppercased; unsupported characters render as a space glyph. Glyphs are\r\n * separated by a one-column gap.\r\n */\r\nexport function renderBlock(text: string): string[] {\r\n const rows: string[] = ['', '', '', '', ''];\r\n const chars = [...text.toUpperCase()];\r\n for (let c = 0; c < chars.length; c++) {\r\n const glyph = FONT[chars[c]] ?? FONT[' '];\r\n for (let r = 0; r < BLOCK_FONT_HEIGHT; r++) {\r\n rows[r] += (c > 0 ? ' ' : '') + glyph[r];\r\n }\r\n }\r\n return rows;\r\n}\r\n","/** @jsxImportSource @sigx/runtime-core */\r\nimport { component, onMounted, onUnmounted, type Define } from '@sigx/runtime-core';\r\nimport { resolveColor, getColorDepth, gradient, hexToSGR, getTick, subscribeTicker } from '@sigx/terminal-zero';\r\nimport { renderBlock, BLOCK_FONT_HEIGHT } from './blockFont';\r\nimport { resolveStops, pingPong } from './paint';\r\nimport type { GradientPreset } from './presets';\r\n\r\n/**\r\n * A large gradient-filled headline in the built-in 5-row block font\r\n * (A–Z 0–9 and basic punctuation; other characters render as spaces).\r\n * `direction` picks the gradient axis; `animate` scrolls it.\r\n */\r\nexport const Banner = component<\r\n Define.Prop<\"text\", string, true> &\r\n Define.Prop<\"colors\", string[], false> &\r\n Define.Prop<\"preset\", GradientPreset, false> &\r\n Define.Prop<\"direction\", 'horizontal' | 'vertical' | 'diagonal', false> &\r\n Define.Prop<\"animate\", boolean, false> &\r\n Define.Prop<\"speed\", number, false>\r\n>(({ props }) => {\r\n let unsub: (() => void) | null = null;\r\n\r\n onMounted(() => {\r\n if (props.animate) unsub = subscribeTicker();\r\n });\r\n onUnmounted(() => { unsub?.(); });\r\n\r\n return () => {\r\n const rows = renderBlock(props.text ?? '');\r\n const depth = getColorDepth();\r\n\r\n if (depth === 'none' || depth === 'ansi16') {\r\n const color = depth === 'none' ? undefined : resolveColor('accent');\r\n return (\r\n <box>\r\n {rows.flatMap((row, r) => {\r\n const line = <text color={color}>{row}</text>;\r\n return r > 0 ? [<br />, line] : [line];\r\n })}\r\n </box>\r\n );\r\n }\r\n\r\n const stops = resolveStops(props.colors, props.preset);\r\n const direction = props.direction || 'horizontal';\r\n const W = rows[0]?.length ?? 0;\r\n const H = BLOCK_FONT_HEIGHT;\r\n const axis = direction === 'horizontal' ? W : direction === 'vertical' ? H : W + H - 1;\r\n const samples = gradient(stops, Math.max(axis, 2));\r\n const cycle = props.animate ? pingPong(samples) : samples;\r\n const phase = props.animate ? Math.floor(getTick() * (props.speed || 1)) : 0;\r\n const sampleAt = (col: number, row: number) => {\r\n const i = direction === 'horizontal' ? col : direction === 'vertical' ? row : col + row;\r\n return cycle[(i + phase) % cycle.length];\r\n };\r\n\r\n return (\r\n <box>\r\n {rows.flatMap((row, r) => {\r\n // Run-length SGR per row: only emit when the cell color changes.\r\n let painted = '';\r\n let lastHex: string | null = null;\r\n for (let c = 0; c < row.length; c++) {\r\n const ch = row[c];\r\n if (ch === ' ') {\r\n painted += ch;\r\n continue;\r\n }\r\n const hex = sampleAt(c, r);\r\n if (hex !== lastHex) {\r\n painted += hexToSGR(hex);\r\n lastHex = hex;\r\n }\r\n painted += ch;\r\n }\r\n if (lastHex !== null) painted += '\\x1b[39m';\r\n const line = <text>{painted}</text>;\r\n return r > 0 ? [<br />, line] : [line];\r\n })}\r\n </box>\r\n );\r\n };\r\n}, { name: 'Banner' });\r\n\r\nexport default Banner;\r\n","/** @jsxImportSource @sigx/runtime-core */\r\nimport { component, type Define } from '@sigx/runtime-core';\r\nimport { resolveColor, resolveFg, resolveBg } from '@sigx/terminal-zero';\r\n\r\n/**\r\n * Render a pixel grid as terminal half-blocks (two pixel rows per terminal\r\n * line). Each character in `rows` is a pixel keyed into `palette`; `.` and\r\n * space are transparent. Palette values are theme tokens or `#hex` —\r\n * resolved per color depth (plain block glyphs at depth none).\r\n *\r\n * The pure builder is exported so apps can `printStatic` a logo into the\r\n * scrollback transcript; the component renders it live.\r\n */\r\nexport function renderPixelArt(rows: string[], palette: Record<string, string>): string[] {\r\n const resolve = (ch: string | undefined): string | null => {\r\n if (!ch || ch === '.' || ch === ' ') return null;\r\n const color = palette[ch];\r\n return color ? resolveColor(color) : null;\r\n };\r\n\r\n const lines: string[] = [];\r\n for (let r = 0; r < rows.length; r += 2) {\r\n const upperRow = rows[r];\r\n const lowerRow = rows[r + 1] ?? '';\r\n const cols = Math.max(upperRow.length, lowerRow.length);\r\n let line = '';\r\n let lastFg: string | null = null;\r\n let lastBg: string | null = null;\r\n let emitted = false;\r\n\r\n for (let c = 0; c < cols; c++) {\r\n const upper = resolve(upperRow[c]);\r\n const lower = resolve(lowerRow[c]);\r\n\r\n if (!upper && !lower) {\r\n if (lastBg !== null) {\r\n if (emitted) line += '\\x1b[0m'; // close any open background run\r\n lastFg = null;\r\n lastBg = null;\r\n }\r\n line += ' ';\r\n continue;\r\n }\r\n\r\n // Pick glyph + colors: both → fg=upper on bg=lower '▀';\r\n // upper only → fg '▀'; lower only → fg '▄' (no bg bleed).\r\n const glyph = upper ? '▀' : '▄';\r\n const fg = upper ?? lower;\r\n const bg = upper && lower ? lower : null;\r\n\r\n if (fg !== lastFg || bg !== lastBg) {\r\n if (lastBg !== null && bg === null && emitted) line += '\\x1b[0m';\r\n const sgr = (fg ? resolveFg(fg) : '') + (bg ? resolveBg(bg) : '');\r\n line += sgr;\r\n if (sgr) emitted = true;\r\n lastFg = fg;\r\n lastBg = bg;\r\n }\r\n line += glyph;\r\n }\r\n if (emitted) line += '\\x1b[0m';\r\n lines.push(line);\r\n }\r\n return lines;\r\n}\r\n\r\nexport const PixelArt = component<\r\n Define.Prop<'rows', string[], true> &\r\n Define.Prop<'palette', Record<string, string>, true>\r\n>(({ props }) => {\r\n return () => {\r\n const lines = renderPixelArt(props.rows ?? [], props.palette ?? {});\r\n return (\r\n <box>\r\n {lines.flatMap((line, i) => {\r\n const node = <text>{line}</text>;\r\n return i > 0 ? [<br />, node] : [node];\r\n })}\r\n </box>\r\n );\r\n };\r\n}, { name: 'PixelArt' });\r\n\r\nexport default PixelArt;\r\n"],"mappings":"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"}
1
+ {"version":3,"file":"fx-igWaVCkB.js","names":[],"sources":["../src/fx/paint.ts","../src/fx/Gradient.tsx","../src/fx/Shimmer.tsx","../src/fx/blockFont.ts","../src/fx/Banner.tsx","../src/fx/PixelArt.tsx"],"sourcesContent":["/**\n * Internal helpers for the FX components: build a single string with embedded\n * SGR escapes (one text node per line — one prop patch per frame, no per-char\n * vdom churn). The renderer passes embedded escapes through intact and its\n * truncation/measurement are escape-aware.\n */\nimport { hexToSGR, resolveColor } from '@sigx/terminal-zero';\nimport { GRADIENT_PRESETS, type GradientPreset } from './presets';\n\n/**\n * Colorize per code point. `hexAt(i, len)` returns the hex for visible char\n * `i`. Run-length emission: an SGR is only emitted when the sampled color\n * changes (spaces keep the current run). Ends with default-fg (`\\x1b[39m`,\n * not a full reset — it must not clobber renderer-applied backgrounds) when\n * any color was emitted.\n */\nexport function colorizeByIndex(text: string, hexAt: (i: number, len: number) => string): string {\n const chars = [...text];\n let out = '';\n let lastHex: string | null = null;\n let emitted = false;\n for (let i = 0; i < chars.length; i++) {\n const ch = chars[i];\n if (ch === ' ') {\n out += ch;\n continue;\n }\n const hex = hexAt(i, chars.length);\n if (hex !== lastHex) {\n const sgr = hexToSGR(hex);\n if (sgr) {\n out += sgr;\n emitted = true;\n }\n lastHex = hex;\n }\n out += ch;\n }\n return emitted ? out + '\\x1b[39m' : out;\n}\n\n/** Resolve gradient stops: explicit colors win over a preset; tokens → hex. */\nexport function resolveStops(colors: string[] | undefined, preset: string | undefined): string[] {\n const raw = colors && colors.length > 0\n ? colors\n : [...GRADIENT_PRESETS[(preset as GradientPreset) || 'sigx'] ?? GRADIENT_PRESETS.sigx];\n return raw.map((c) => resolveColor(c));\n}\n\n/**\n * Ping-pong a sample sequence (a → b → a without repeating the endpoints), so\n * scrolling a non-cyclic palette has no seam.\n */\nexport function pingPong(samples: string[]): string[] {\n if (samples.length <= 2) return samples;\n return samples.concat(samples.slice(1, -1).reverse());\n}\n","/** @jsxImportSource @sigx/runtime-core */\nimport { component, onMounted, onUnmounted, type Define } from '@sigx/runtime-core';\nimport { resolveColor, getColorDepth, gradient, getTick, subscribeTicker } from '@sigx/terminal-zero';\nimport { colorizeByIndex, resolveStops, pingPong } from './paint';\nimport type { GradientPreset } from './presets';\n\n/**\n * Text colored across a multi-stop gradient, one sample per character.\n * Stops are theme tokens or hex; `animate` scrolls the gradient through the\n * text (ping-pong, seamless). Degrades by color depth: a single accent color\n * at ansi16 (per-char nearest-16 is speckle noise), plain text at none.\n */\nexport const Gradient = component<\n Define.Prop<\"text\", string, true> &\n Define.Prop<\"colors\", string[], false> &\n Define.Prop<\"preset\", GradientPreset, false> &\n Define.Prop<\"animate\", boolean, false> &\n Define.Prop<\"speed\", number, false>\n>(({ props }) => {\n let unsub: (() => void) | null = null;\n\n onMounted(() => {\n if (props.animate) unsub = subscribeTicker();\n });\n onUnmounted(() => { unsub?.(); });\n\n return () => {\n // Block root (<box>), like every other component in the library — an\n // inline root glues onto whatever line precedes it. For inline\n // gradient strings, use colorizeByIndex/paintToken directly.\n const text = props.text ?? '';\n const depth = getColorDepth();\n if (depth === 'none') return <box><text>{text}</text></box>;\n if (depth === 'ansi16') return <box><text color={resolveColor('accent')}>{text}</text></box>;\n\n const stops = resolveStops(props.colors, props.preset);\n const len = [...text].length;\n const samples = gradient(stops, Math.max(len, 2));\n\n if (!props.animate) {\n return <box><text>{colorizeByIndex(text, (i) => samples[Math.min(i, samples.length - 1)])}</text></box>;\n }\n const cycle = pingPong(samples);\n const phase = Math.floor(getTick() * (props.speed || 1));\n return <box><text>{colorizeByIndex(text, (i) => cycle[(i + phase) % cycle.length])}</text></box>;\n };\n}, { name: 'Gradient' });\n\nexport default Gradient;\n","/** @jsxImportSource @sigx/runtime-core */\nimport { component, onMounted, onUnmounted, type Define } from '@sigx/runtime-core';\nimport { resolveColor, getColorDepth, mixHex, getTick, subscribeTicker } from '@sigx/terminal-zero';\nimport { colorizeByIndex } from './paint';\n\n/**\n * A highlight sweep travelling across muted text — the \"thinking…\" effect.\n * `color` is the base (default `dim`), `highlight` the sweep peak (default\n * `fg`), `width` the sweep half-width in characters. Static base color at\n * ansi16/none depth.\n */\nexport const Shimmer = component<\n Define.Prop<\"text\", string, true> &\n Define.Prop<\"color\", string, false> &\n Define.Prop<\"highlight\", string, false> &\n Define.Prop<\"width\", number, false> &\n Define.Prop<\"speed\", number, false>\n>(({ props }) => {\n let unsub: (() => void) | null = null;\n\n onMounted(() => { unsub = subscribeTicker(); });\n onUnmounted(() => { unsub?.(); });\n\n return () => {\n // Block root (<box>), like every other component in the library — an\n // inline root glues onto whatever line precedes it (a thinking…\n // shimmer must not land on the input's row).\n const text = props.text ?? '';\n const baseToken = props.color || 'dim';\n const depth = getColorDepth();\n if (depth === 'none') return <box><text>{text}</text></box>;\n if (depth === 'ansi16') return <box><text color={resolveColor(baseToken)}>{text}</text></box>;\n\n const base = resolveColor(baseToken);\n const highlight = resolveColor(props.highlight || 'fg');\n const w = Math.max(1, props.width || 3);\n const len = [...text].length;\n // The sweep runs off both ends so the text rests between passes.\n const span = len + 2 * w;\n const pos = (getTick() * (props.speed || 1)) % span - w;\n\n return (\n <box>\n <text>\n {colorizeByIndex(text, (i) => {\n const d = Math.abs(i - pos);\n return d < w ? mixHex(base, highlight, 1 - d / w) : base;\n })}\n </text>\n </box>\n );\n };\n}, { name: 'Shimmer' });\n\nexport default Shimmer;\n","/**\n * Built-in 5-row block font for <Banner> — full blocks and spaces only, so\n * every cell is width-1 (gradient-fillable, no wide-glyph drift). Glyphs are\n * authored as '#'/'.' strings for readability and converted once at load.\n * No external font dependency.\n */\n\nconst RAW: Record<string, string[]> = {\n A: ['.##.', '#..#', '####', '#..#', '#..#'],\n B: ['###.', '#..#', '###.', '#..#', '###.'],\n C: ['.###', '#...', '#...', '#...', '.###'],\n D: ['###.', '#..#', '#..#', '#..#', '###.'],\n E: ['####', '#...', '###.', '#...', '####'],\n F: ['####', '#...', '###.', '#...', '#...'],\n G: ['.###', '#...', '#.##', '#..#', '.##.'],\n H: ['#..#', '#..#', '####', '#..#', '#..#'],\n I: ['###', '.#.', '.#.', '.#.', '###'],\n J: ['..##', '...#', '...#', '#..#', '.##.'],\n K: ['#..#', '#.#.', '##..', '#.#.', '#..#'],\n L: ['#...', '#...', '#...', '#...', '####'],\n M: ['#...#', '##.##', '#.#.#', '#...#', '#...#'],\n N: ['#...#', '##..#', '#.#.#', '#..##', '#...#'],\n O: ['.##.', '#..#', '#..#', '#..#', '.##.'],\n P: ['###.', '#..#', '###.', '#...', '#...'],\n Q: ['.##.', '#..#', '#..#', '#.##', '.###'],\n R: ['###.', '#..#', '###.', '#.#.', '#..#'],\n S: ['.###', '#...', '.##.', '...#', '###.'],\n T: ['#####', '..#..', '..#..', '..#..', '..#..'],\n U: ['#..#', '#..#', '#..#', '#..#', '.##.'],\n V: ['#...#', '#...#', '#...#', '.#.#.', '..#..'],\n W: ['#...#', '#...#', '#.#.#', '##.##', '#...#'],\n X: ['#...#', '.#.#.', '..#..', '.#.#.', '#...#'],\n Y: ['#...#', '.#.#.', '..#..', '..#..', '..#..'],\n Z: ['####', '...#', '..#.', '.#..', '####'],\n '0': ['.##.', '#..#', '#..#', '#..#', '.##.'],\n '1': ['.#.', '##.', '.#.', '.#.', '###'],\n '2': ['.##.', '#..#', '..#.', '.#..', '####'],\n '3': ['###.', '...#', '.##.', '...#', '###.'],\n '4': ['#..#', '#..#', '####', '...#', '...#'],\n '5': ['####', '#...', '###.', '...#', '###.'],\n '6': ['.##.', '#...', '###.', '#..#', '.##.'],\n '7': ['####', '...#', '..#.', '.#..', '.#..'],\n '8': ['.##.', '#..#', '.##.', '#..#', '.##.'],\n '9': ['.##.', '#..#', '.###', '...#', '.##.'],\n ' ': ['..', '..', '..', '..', '..'],\n '-': ['...', '...', '###', '...', '...'],\n '.': ['.', '.', '.', '.', '#'],\n '!': ['#', '#', '#', '.', '#'],\n ':': ['.', '#', '.', '#', '.'],\n '/': ['...#', '..#.', '..#.', '.#..', '#...'],\n};\n\nexport const BLOCK_FONT_HEIGHT = 5;\n\nconst FONT: Record<string, string[]> = {};\nfor (const [ch, rows] of Object.entries(RAW)) {\n FONT[ch] = rows.map((r) => r.replace(/#/g, '█').replace(/\\./g, ' '));\n}\n\n/**\n * Render text in the block font: 5 equal-width rows of '█'/space. Input is\n * uppercased; unsupported characters render as a space glyph. Glyphs are\n * separated by a one-column gap.\n */\nexport function renderBlock(text: string): string[] {\n const rows: string[] = ['', '', '', '', ''];\n const chars = [...text.toUpperCase()];\n for (let c = 0; c < chars.length; c++) {\n const glyph = FONT[chars[c]] ?? FONT[' '];\n for (let r = 0; r < BLOCK_FONT_HEIGHT; r++) {\n rows[r] += (c > 0 ? ' ' : '') + glyph[r];\n }\n }\n return rows;\n}\n","/** @jsxImportSource @sigx/runtime-core */\nimport { component, onMounted, onUnmounted, type Define } from '@sigx/runtime-core';\nimport { resolveColor, getColorDepth, gradient, hexToSGR, getTick, subscribeTicker } from '@sigx/terminal-zero';\nimport { renderBlock, BLOCK_FONT_HEIGHT } from './blockFont';\nimport { resolveStops, pingPong } from './paint';\nimport type { GradientPreset } from './presets';\n\n/**\n * A large gradient-filled headline in the built-in 5-row block font\n * (A–Z 0–9 and basic punctuation; other characters render as spaces).\n * `direction` picks the gradient axis; `animate` scrolls it.\n */\nexport const Banner = component<\n Define.Prop<\"text\", string, true> &\n Define.Prop<\"colors\", string[], false> &\n Define.Prop<\"preset\", GradientPreset, false> &\n Define.Prop<\"direction\", 'horizontal' | 'vertical' | 'diagonal', false> &\n Define.Prop<\"animate\", boolean, false> &\n Define.Prop<\"speed\", number, false>\n>(({ props }) => {\n let unsub: (() => void) | null = null;\n\n onMounted(() => {\n if (props.animate) unsub = subscribeTicker();\n });\n onUnmounted(() => { unsub?.(); });\n\n return () => {\n const rows = renderBlock(props.text ?? '');\n const depth = getColorDepth();\n\n if (depth === 'none' || depth === 'ansi16') {\n const color = depth === 'none' ? undefined : resolveColor('accent');\n return (\n <box>\n {rows.flatMap((row, r) => {\n const line = <text color={color}>{row}</text>;\n return r > 0 ? [<br />, line] : [line];\n })}\n </box>\n );\n }\n\n const stops = resolveStops(props.colors, props.preset);\n const direction = props.direction || 'horizontal';\n const W = rows[0]?.length ?? 0;\n const H = BLOCK_FONT_HEIGHT;\n const axis = direction === 'horizontal' ? W : direction === 'vertical' ? H : W + H - 1;\n const samples = gradient(stops, Math.max(axis, 2));\n const cycle = props.animate ? pingPong(samples) : samples;\n const phase = props.animate ? Math.floor(getTick() * (props.speed || 1)) : 0;\n const sampleAt = (col: number, row: number) => {\n const i = direction === 'horizontal' ? col : direction === 'vertical' ? row : col + row;\n return cycle[(i + phase) % cycle.length];\n };\n\n return (\n <box>\n {rows.flatMap((row, r) => {\n // Run-length SGR per row: only emit when the cell color changes.\n let painted = '';\n let lastHex: string | null = null;\n for (let c = 0; c < row.length; c++) {\n const ch = row[c];\n if (ch === ' ') {\n painted += ch;\n continue;\n }\n const hex = sampleAt(c, r);\n if (hex !== lastHex) {\n painted += hexToSGR(hex);\n lastHex = hex;\n }\n painted += ch;\n }\n if (lastHex !== null) painted += '\\x1b[39m';\n const line = <text>{painted}</text>;\n return r > 0 ? [<br />, line] : [line];\n })}\n </box>\n );\n };\n}, { name: 'Banner' });\n\nexport default Banner;\n","/** @jsxImportSource @sigx/runtime-core */\nimport { component, type Define } from '@sigx/runtime-core';\nimport { resolveColor, resolveFg, resolveBg } from '@sigx/terminal-zero';\n\n/**\n * Render a pixel grid as terminal half-blocks (two pixel rows per terminal\n * line). Each character in `rows` is a pixel keyed into `palette`; `.` and\n * space are transparent. Palette values are theme tokens or `#hex` —\n * resolved per color depth (plain block glyphs at depth none).\n *\n * The pure builder is exported so apps can `printStatic` a logo into the\n * scrollback transcript; the component renders it live.\n */\nexport function renderPixelArt(rows: string[], palette: Record<string, string>): string[] {\n const resolve = (ch: string | undefined): string | null => {\n if (!ch || ch === '.' || ch === ' ') return null;\n const color = palette[ch];\n return color ? resolveColor(color) : null;\n };\n\n const lines: string[] = [];\n for (let r = 0; r < rows.length; r += 2) {\n const upperRow = rows[r];\n const lowerRow = rows[r + 1] ?? '';\n const cols = Math.max(upperRow.length, lowerRow.length);\n let line = '';\n let lastFg: string | null = null;\n let lastBg: string | null = null;\n let emitted = false;\n\n for (let c = 0; c < cols; c++) {\n const upper = resolve(upperRow[c]);\n const lower = resolve(lowerRow[c]);\n\n if (!upper && !lower) {\n if (lastBg !== null) {\n if (emitted) line += '\\x1b[0m'; // close any open background run\n lastFg = null;\n lastBg = null;\n }\n line += ' ';\n continue;\n }\n\n // Pick glyph + colors: both → fg=upper on bg=lower '▀';\n // upper only → fg '▀'; lower only → fg '▄' (no bg bleed).\n const glyph = upper ? '▀' : '▄';\n const fg = upper ?? lower;\n const bg = upper && lower ? lower : null;\n\n if (fg !== lastFg || bg !== lastBg) {\n if (lastBg !== null && bg === null && emitted) line += '\\x1b[0m';\n const sgr = (fg ? resolveFg(fg) : '') + (bg ? resolveBg(bg) : '');\n line += sgr;\n if (sgr) emitted = true;\n lastFg = fg;\n lastBg = bg;\n }\n line += glyph;\n }\n if (emitted) line += '\\x1b[0m';\n lines.push(line);\n }\n return lines;\n}\n\nexport const PixelArt = component<\n Define.Prop<'rows', string[], true> &\n Define.Prop<'palette', Record<string, string>, true>\n>(({ props }) => {\n return () => {\n const lines = renderPixelArt(props.rows ?? [], props.palette ?? {});\n return (\n <box>\n {lines.flatMap((line, i) => {\n const node = <text>{line}</text>;\n return i > 0 ? [<br />, node] : [node];\n })}\n </box>\n );\n };\n}, { name: 'PixelArt' });\n\nexport default PixelArt;\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"layout-CJzMVMDf.js","names":[],"sources":["../src/layout/Card.tsx"],"sourcesContent":["/** @jsxImportSource @sigx/runtime-core */\r\nimport { component, type Define } from '@sigx/runtime-core';\r\nimport { resolveColor } from '@sigx/terminal-zero';\r\n\r\n/** A rounded panel with horizontal padding, an optional title, and shadow. */\r\nexport const Card = component<\r\n Define.Prop<\"title\", string, false> &\r\n Define.Prop<\"dropShadow\", boolean, false> &\r\n Define.Slot<'default'>\r\n>(({ props, slots }) => {\r\n return () => (\r\n <box\r\n border=\"rounded\"\r\n borderColor={resolveColor('line')}\r\n label={props.title}\r\n labelColor={resolveColor('accent')}\r\n padX={1}\r\n dropShadow={props.dropShadow}\r\n shadowColor={resolveColor('shadow')}\r\n >\r\n {slots.default?.()}\r\n </box>\r\n );\r\n}, { name: 'Card' });\r\n\r\nexport default Card;\r\n"],"mappings":";;;;AAKA,IAAa,IAAO,GAIjB,EAAE,UAAO,qBAEJ,kBAAC,OAAD;CACI,QAAO;CACP,aAAa,EAAa,OAAO;CACjC,OAAO,EAAM;CACb,YAAY,EAAa,SAAS;CAClC,MAAM;CACN,YAAY,EAAM;CAClB,aAAa,EAAa,SAAS;WAElC,EAAM,WAAW;CAChB,CAAA,EAEX,EAAE,MAAM,QAAQ,CAAC"}
1
+ {"version":3,"file":"layout-CJzMVMDf.js","names":[],"sources":["../src/layout/Card.tsx"],"sourcesContent":["/** @jsxImportSource @sigx/runtime-core */\nimport { component, type Define } from '@sigx/runtime-core';\nimport { resolveColor } from '@sigx/terminal-zero';\n\n/** A rounded panel with horizontal padding, an optional title, and shadow. */\nexport const Card = component<\n Define.Prop<\"title\", string, false> &\n Define.Prop<\"dropShadow\", boolean, false> &\n Define.Slot<'default'>\n>(({ props, slots }) => {\n return () => (\n <box\n border=\"rounded\"\n borderColor={resolveColor('line')}\n label={props.title}\n labelColor={resolveColor('accent')}\n padX={1}\n dropShadow={props.dropShadow}\n shadowColor={resolveColor('shadow')}\n >\n {slots.default?.()}\n </box>\n );\n}, { name: 'Card' });\n\nexport default Card;\n"],"mappings":";;;;AAKA,IAAa,IAAO,GAIjB,EAAE,UAAO,qBAEJ,kBAAC,OAAD;CACI,QAAO;CACP,aAAa,EAAa,OAAO;CACjC,OAAO,EAAM;CACb,YAAY,EAAa,SAAS;CAClC,MAAM;CACN,YAAY,EAAM;CAClB,aAAa,EAAa,SAAS;WAElC,EAAM,WAAW;CAChB,CAAA,EAEX,EAAE,MAAM,QAAQ,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"navigation-B-nI3g0k.js","names":[],"sources":["../src/navigation/Tabs.tsx","../src/navigation/StatusBar.tsx","../src/navigation/KeyHints.tsx","../src/navigation/SuggestionList.tsx"],"sourcesContent":["/** @jsxImportSource @sigx/runtime-core */\r\nimport { component, onMounted, onUnmounted, type Define } from '@sigx/runtime-core';\r\nimport {\r\n onKey, registerFocusable, unregisterFocusable, focusState, focus, resolveColor,\r\n} from '@sigx/terminal-zero';\r\n\r\nexport interface TabOption<T = string> {\r\n label: string;\r\n value: T;\r\n}\r\n\r\n/** Horizontal tab switcher. Active tab fills accent; ←/→ (or h/l) switch. */\r\nexport const Tabs = component<\r\n Define.Model<string> &\r\n Define.Prop<\"options\", TabOption[], true> &\r\n Define.Prop<\"autofocus\", boolean, false> &\r\n Define.Event<\"change\", string>\r\n>(({ props, emit }) => {\r\n const id = Math.random().toString(36).slice(2);\r\n const isFocused = () => focusState.activeId === id;\r\n\r\n const getCurrentIndex = () => {\r\n const options = props.options || [];\r\n const idx = options.findIndex(o => o.value === props.model?.value);\r\n return idx >= 0 ? idx : 0;\r\n };\r\n\r\n const move = (delta: number) => {\r\n const options = props.options || [];\r\n if (options.length === 0) return;\r\n const len = options.length;\r\n const newIndex = (getCurrentIndex() + delta + len) % len;\r\n const newValue = options[newIndex].value;\r\n if (props.model) props.model.value = newValue;\r\n emit('change', newValue);\r\n };\r\n\r\n const handleKey = (key: string) => {\r\n if (!isFocused()) return;\r\n if (key === '\\x1B[D' || key === 'h') move(-1);\r\n else if (key === '\\x1B[C' || key === 'l') move(1);\r\n };\r\n\r\n let keyCleanup: (() => void) | null = null;\r\n\r\n onMounted(() => {\r\n registerFocusable(id);\r\n if (props.autofocus) focus(id);\r\n keyCleanup = onKey(handleKey);\r\n });\r\n\r\n onUnmounted(() => {\r\n if (keyCleanup) keyCleanup();\r\n unregisterFocusable(id);\r\n });\r\n\r\n return () => {\r\n const options = props.options || [];\r\n const current = props.model?.value || options[0]?.value || '';\r\n const focused = isFocused();\r\n\r\n const tabs: any[] = [];\r\n options.forEach((option, i) => {\r\n const active = option.value === current;\r\n const bg = resolveColor(active ? 'accent' : 'accentSoft');\r\n const fg = resolveColor(active ? 'accentText' : 'dim');\r\n tabs.push(<text backgroundColor={bg} color={fg}> {option.label} </text>);\r\n if (i < options.length - 1) tabs.push(<text> </text>);\r\n });\r\n\r\n return (\r\n <box border=\"rounded\" borderColor={resolveColor(focused ? 'accent' : 'line')}>\r\n {tabs}\r\n </box>\r\n );\r\n };\r\n}, { name: 'Tabs' });\r\n\r\nexport default Tabs;\r\n","/** @jsxImportSource @sigx/runtime-core */\r\nimport { component, type Define } from '@sigx/runtime-core';\r\nimport { resolveColor } from '@sigx/terminal-zero';\r\n\r\nexport interface StatusItem {\r\n key: string;\r\n label: string;\r\n}\r\n\r\n/** Key-hint footer: each item is ` key ` (accent fill) + ` label ` (dim). */\r\nexport const StatusBar = component<\r\n Define.Prop<\"items\", StatusItem[], true>\r\n>(({ props }) => {\r\n return () => {\r\n const items = props.items || [];\r\n const nodes: any[] = [];\r\n items.forEach((item) => {\r\n nodes.push(\r\n <text backgroundColor={resolveColor('accent')} color={resolveColor('accentText')}> {item.key} </text>\r\n );\r\n nodes.push(<text color={resolveColor('dim')}> {item.label} </text>);\r\n });\r\n return <box backgroundColor={resolveColor('chrome')}>{nodes}</box>;\r\n };\r\n}, { name: 'StatusBar' });\r\n\r\nexport default StatusBar;\r\n","/** @jsxImportSource @sigx/runtime-core */\r\nimport { component, type Define } from '@sigx/runtime-core';\r\nimport { resolveColor } from '@sigx/terminal-zero';\r\n\r\nexport interface KeyHint {\r\n key: string;\r\n label: string;\r\n}\r\n\r\n/**\r\n * The dev-server shortcuts footer: `r reload · d devices · q quit`.\r\n * Deliberately lighter than StatusBar (no chips, no background fill) —\r\n * accent keys, dim labels, faint separators, one line.\r\n */\r\nexport const KeyHints = component<\r\n Define.Prop<\"hints\", KeyHint[], true> &\r\n Define.Prop<\"separator\", string, false>\r\n>(({ props }) => {\r\n return () => {\r\n const hints = props.hints || [];\r\n const separator = props.separator || '·';\r\n\r\n return (\r\n <box>\r\n {hints.map((hint, i) => (\r\n <text>\r\n {i > 0 && <text color={resolveColor('faint')}> {separator} </text>}\r\n <text color={resolveColor('accent')}>{hint.key}</text>\r\n <text color={resolveColor('dim')}> {hint.label}</text>\r\n </text>\r\n ))}\r\n </box>\r\n );\r\n };\r\n}, { name: 'KeyHints' });\r\n\r\nexport default KeyHints;\r\n","/** @jsxImportSource @sigx/runtime-core */\r\nimport { component, onMounted, onUnmounted, signal, type Define } from '@sigx/runtime-core';\r\nimport { onKey, resolveColor, GLYPHS } from '@sigx/terminal-zero';\r\n\r\nexport interface SuggestionItem {\r\n value: string;\r\n /** Defaults to value. */\r\n label?: string;\r\n description?: string;\r\n}\r\n\r\n/**\r\n * Intellisense popup for an input: mount it to open it (typically rendered\r\n * conditionally under a TextArea while the value matches a trigger like `/`).\r\n * It registers an OVERLAY-layer key handler that consumes only navigation\r\n * keys — ↑/↓ move, Tab/Enter accept the highlighted item, Esc dismisses —\r\n * while printable characters, backspace, etc. fall through to the input\r\n * below, so filtering keeps working as the user types.\r\n */\r\nexport const SuggestionList = component<\r\n Define.Prop<'items', SuggestionItem[], true> &\r\n Define.Prop<'maxVisible', number, false> &\r\n Define.Event<'accept', string> &\r\n Define.Event<'dismiss'>\r\n>(({ props, emit }) => {\r\n const ESC = String.fromCharCode(27);\r\n const state = signal({ cursor: 0, itemsKey: '' });\r\n\r\n const items = () => props.items ?? [];\r\n const syncCursor = () => {\r\n const key = items().map((i) => i.value).join('\\x00');\r\n if (key !== state.itemsKey) {\r\n state.itemsKey = key;\r\n state.cursor = 0;\r\n }\r\n return Math.min(state.cursor, Math.max(0, items().length - 1));\r\n };\r\n\r\n const handleKey = (key: string): boolean | void => {\r\n const list = items();\r\n if (key === ESC + '[A') {\r\n if (list.length === 0) return true;\r\n state.cursor = syncCursor() > 0 ? syncCursor() - 1 : list.length - 1;\r\n return true;\r\n }\r\n if (key === ESC + '[B') {\r\n if (list.length === 0) return true;\r\n state.cursor = syncCursor() < list.length - 1 ? syncCursor() + 1 : 0;\r\n return true;\r\n }\r\n if (key === '\\t' || key === '\\r') {\r\n if (list.length === 0) return; // nothing to accept — let it fall through\r\n emit('accept', list[syncCursor()].value);\r\n return true;\r\n }\r\n if (key === ESC) {\r\n emit('dismiss');\r\n return true;\r\n }\r\n // Everything else (printables, backspace, \\n) falls through to the\r\n // input below so filtering keeps working.\r\n };\r\n\r\n let keyCleanup: (() => void) | null = null;\r\n\r\n onMounted(() => {\r\n // No READY_DELAY: this mounts in reaction to a key that was already\r\n // dispatched — the very next key is legitimately for the list.\r\n keyCleanup = onKey(handleKey, { layer: 'overlay' });\r\n });\r\n\r\n onUnmounted(() => {\r\n if (keyCleanup) keyCleanup();\r\n });\r\n\r\n return () => {\r\n const list = items();\r\n const cursor = syncCursor();\r\n const maxVisible = Math.max(1, props.maxVisible || 6);\r\n\r\n let top = 0;\r\n if (list.length > maxVisible) {\r\n top = Math.min(Math.max(0, cursor - maxVisible + 1), list.length - maxVisible);\r\n }\r\n const windowed = list.slice(top, top + maxVisible);\r\n\r\n return (\r\n <box>\r\n {windowed.map((item, i) => {\r\n const onCursor = top + i === cursor;\r\n return (\r\n <box>\r\n <text color={resolveColor(onCursor ? 'accent' : 'faint')}>{onCursor ? GLYPHS.cursor : ' '} </text>\r\n <text color={resolveColor(onCursor ? 'accent' : 'fg')}>{item.label ?? item.value}</text>\r\n {item.description && <text color={resolveColor('dim')}> {item.description}</text>}\r\n </box>\r\n );\r\n })}\r\n {list.length > maxVisible && (\r\n <box><text color={resolveColor('faint')}> {cursor + 1}/{list.length}</text></box>\r\n )}\r\n </box>\r\n );\r\n };\r\n}, { name: 'SuggestionList' });\r\n\r\nexport default SuggestionList;\r\n"],"mappings":"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"}
1
+ {"version":3,"file":"navigation-B-nI3g0k.js","names":[],"sources":["../src/navigation/Tabs.tsx","../src/navigation/StatusBar.tsx","../src/navigation/KeyHints.tsx","../src/navigation/SuggestionList.tsx"],"sourcesContent":["/** @jsxImportSource @sigx/runtime-core */\nimport { component, onMounted, onUnmounted, type Define } from '@sigx/runtime-core';\nimport {\n onKey, registerFocusable, unregisterFocusable, focusState, focus, resolveColor,\n} from '@sigx/terminal-zero';\n\nexport interface TabOption<T = string> {\n label: string;\n value: T;\n}\n\n/** Horizontal tab switcher. Active tab fills accent; ←/→ (or h/l) switch. */\nexport const Tabs = component<\n Define.Model<string> &\n Define.Prop<\"options\", TabOption[], true> &\n Define.Prop<\"autofocus\", boolean, false> &\n Define.Event<\"change\", string>\n>(({ props, emit }) => {\n const id = Math.random().toString(36).slice(2);\n const isFocused = () => focusState.activeId === id;\n\n const getCurrentIndex = () => {\n const options = props.options || [];\n const idx = options.findIndex(o => o.value === props.model?.value);\n return idx >= 0 ? idx : 0;\n };\n\n const move = (delta: number) => {\n const options = props.options || [];\n if (options.length === 0) return;\n const len = options.length;\n const newIndex = (getCurrentIndex() + delta + len) % len;\n const newValue = options[newIndex].value;\n if (props.model) props.model.value = newValue;\n emit('change', newValue);\n };\n\n const handleKey = (key: string) => {\n if (!isFocused()) return;\n if (key === '\\x1B[D' || key === 'h') move(-1);\n else if (key === '\\x1B[C' || key === 'l') move(1);\n };\n\n let keyCleanup: (() => void) | null = null;\n\n onMounted(() => {\n registerFocusable(id);\n if (props.autofocus) focus(id);\n keyCleanup = onKey(handleKey);\n });\n\n onUnmounted(() => {\n if (keyCleanup) keyCleanup();\n unregisterFocusable(id);\n });\n\n return () => {\n const options = props.options || [];\n const current = props.model?.value || options[0]?.value || '';\n const focused = isFocused();\n\n const tabs: any[] = [];\n options.forEach((option, i) => {\n const active = option.value === current;\n const bg = resolveColor(active ? 'accent' : 'accentSoft');\n const fg = resolveColor(active ? 'accentText' : 'dim');\n tabs.push(<text backgroundColor={bg} color={fg}> {option.label} </text>);\n if (i < options.length - 1) tabs.push(<text> </text>);\n });\n\n return (\n <box border=\"rounded\" borderColor={resolveColor(focused ? 'accent' : 'line')}>\n {tabs}\n </box>\n );\n };\n}, { name: 'Tabs' });\n\nexport default Tabs;\n","/** @jsxImportSource @sigx/runtime-core */\nimport { component, type Define } from '@sigx/runtime-core';\nimport { resolveColor } from '@sigx/terminal-zero';\n\nexport interface StatusItem {\n key: string;\n label: string;\n}\n\n/** Key-hint footer: each item is ` key ` (accent fill) + ` label ` (dim). */\nexport const StatusBar = component<\n Define.Prop<\"items\", StatusItem[], true>\n>(({ props }) => {\n return () => {\n const items = props.items || [];\n const nodes: any[] = [];\n items.forEach((item) => {\n nodes.push(\n <text backgroundColor={resolveColor('accent')} color={resolveColor('accentText')}> {item.key} </text>\n );\n nodes.push(<text color={resolveColor('dim')}> {item.label} </text>);\n });\n return <box backgroundColor={resolveColor('chrome')}>{nodes}</box>;\n };\n}, { name: 'StatusBar' });\n\nexport default StatusBar;\n","/** @jsxImportSource @sigx/runtime-core */\nimport { component, type Define } from '@sigx/runtime-core';\nimport { resolveColor } from '@sigx/terminal-zero';\n\nexport interface KeyHint {\n key: string;\n label: string;\n}\n\n/**\n * The dev-server shortcuts footer: `r reload · d devices · q quit`.\n * Deliberately lighter than StatusBar (no chips, no background fill) —\n * accent keys, dim labels, faint separators, one line.\n */\nexport const KeyHints = component<\n Define.Prop<\"hints\", KeyHint[], true> &\n Define.Prop<\"separator\", string, false>\n>(({ props }) => {\n return () => {\n const hints = props.hints || [];\n const separator = props.separator || '·';\n\n return (\n <box>\n {hints.map((hint, i) => (\n <text>\n {i > 0 && <text color={resolveColor('faint')}> {separator} </text>}\n <text color={resolveColor('accent')}>{hint.key}</text>\n <text color={resolveColor('dim')}> {hint.label}</text>\n </text>\n ))}\n </box>\n );\n };\n}, { name: 'KeyHints' });\n\nexport default KeyHints;\n","/** @jsxImportSource @sigx/runtime-core */\nimport { component, onMounted, onUnmounted, signal, type Define } from '@sigx/runtime-core';\nimport { onKey, resolveColor, GLYPHS } from '@sigx/terminal-zero';\n\nexport interface SuggestionItem {\n value: string;\n /** Defaults to value. */\n label?: string;\n description?: string;\n}\n\n/**\n * Intellisense popup for an input: mount it to open it (typically rendered\n * conditionally under a TextArea while the value matches a trigger like `/`).\n * It registers an OVERLAY-layer key handler that consumes only navigation\n * keys — ↑/↓ move, Tab/Enter accept the highlighted item, Esc dismisses —\n * while printable characters, backspace, etc. fall through to the input\n * below, so filtering keeps working as the user types.\n */\nexport const SuggestionList = component<\n Define.Prop<'items', SuggestionItem[], true> &\n Define.Prop<'maxVisible', number, false> &\n Define.Event<'accept', string> &\n Define.Event<'dismiss'>\n>(({ props, emit }) => {\n const ESC = String.fromCharCode(27);\n const state = signal({ cursor: 0, itemsKey: '' });\n\n const items = () => props.items ?? [];\n const syncCursor = () => {\n const key = items().map((i) => i.value).join('\\x00');\n if (key !== state.itemsKey) {\n state.itemsKey = key;\n state.cursor = 0;\n }\n return Math.min(state.cursor, Math.max(0, items().length - 1));\n };\n\n const handleKey = (key: string): boolean | void => {\n const list = items();\n if (key === ESC + '[A') {\n if (list.length === 0) return true;\n state.cursor = syncCursor() > 0 ? syncCursor() - 1 : list.length - 1;\n return true;\n }\n if (key === ESC + '[B') {\n if (list.length === 0) return true;\n state.cursor = syncCursor() < list.length - 1 ? syncCursor() + 1 : 0;\n return true;\n }\n if (key === '\\t' || key === '\\r') {\n if (list.length === 0) return; // nothing to accept — let it fall through\n emit('accept', list[syncCursor()].value);\n return true;\n }\n if (key === ESC) {\n emit('dismiss');\n return true;\n }\n // Everything else (printables, backspace, \\n) falls through to the\n // input below so filtering keeps working.\n };\n\n let keyCleanup: (() => void) | null = null;\n\n onMounted(() => {\n // No READY_DELAY: this mounts in reaction to a key that was already\n // dispatched — the very next key is legitimately for the list.\n keyCleanup = onKey(handleKey, { layer: 'overlay' });\n });\n\n onUnmounted(() => {\n if (keyCleanup) keyCleanup();\n });\n\n return () => {\n const list = items();\n const cursor = syncCursor();\n const maxVisible = Math.max(1, props.maxVisible || 6);\n\n let top = 0;\n if (list.length > maxVisible) {\n top = Math.min(Math.max(0, cursor - maxVisible + 1), list.length - maxVisible);\n }\n const windowed = list.slice(top, top + maxVisible);\n\n return (\n <box>\n {windowed.map((item, i) => {\n const onCursor = top + i === cursor;\n return (\n <box>\n <text color={resolveColor(onCursor ? 'accent' : 'faint')}>{onCursor ? GLYPHS.cursor : ' '} </text>\n <text color={resolveColor(onCursor ? 'accent' : 'fg')}>{item.label ?? item.value}</text>\n {item.description && <text color={resolveColor('dim')}> {item.description}</text>}\n </box>\n );\n })}\n {list.length > maxVisible && (\n <box><text color={resolveColor('faint')}> {cursor + 1}/{list.length}</text></box>\n )}\n </box>\n );\n };\n}, { name: 'SuggestionList' });\n\nexport default SuggestionList;\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"presets-uqseGRqO.js","names":[],"sources":["../src/fx/presets.ts"],"sourcesContent":["/**\r\n * Gradient stop presets. Stops are theme tokens OR raw hex: tokens resolve\r\n * through `resolveColor` at render time, so token-based presets (like `sigx`)\r\n * re-color live when the theme changes; fixed-hex presets look the same on\r\n * every theme.\r\n */\r\nexport const GRADIENT_PRESETS = {\r\n sigx: ['accent', 'info', 'success'],\r\n rainbow: ['#ff5f5f', '#f5c518', '#3fd07f', '#34d4d4', '#4a9eff', '#c678dd'],\r\n sunset: ['#ff7e5f', '#feb47b', '#ffcd94'],\r\n ocean: ['#2193b0', '#6dd5ed', '#b8e8f4'],\r\n fire: ['#f12711', '#f5af19'],\r\n} as const;\r\n\r\nexport type GradientPreset = keyof typeof GRADIENT_PRESETS;\r\n"],"mappings":";AAMA,IAAa,IAAmB;CAC5B,MAAM;EAAC;EAAU;EAAQ;EAAU;CACnC,SAAS;EAAC;EAAW;EAAW;EAAW;EAAW;EAAW;EAAU;CAC3E,QAAQ;EAAC;EAAW;EAAW;EAAU;CACzC,OAAO;EAAC;EAAW;EAAW;EAAU;CACxC,MAAM,CAAC,WAAW,UAAU;CAC/B"}
1
+ {"version":3,"file":"presets-uqseGRqO.js","names":[],"sources":["../src/fx/presets.ts"],"sourcesContent":["/**\n * Gradient stop presets. Stops are theme tokens OR raw hex: tokens resolve\n * through `resolveColor` at render time, so token-based presets (like `sigx`)\n * re-color live when the theme changes; fixed-hex presets look the same on\n * every theme.\n */\nexport const GRADIENT_PRESETS = {\n sigx: ['accent', 'info', 'success'],\n rainbow: ['#ff5f5f', '#f5c518', '#3fd07f', '#34d4d4', '#4a9eff', '#c678dd'],\n sunset: ['#ff7e5f', '#feb47b', '#ffcd94'],\n ocean: ['#2193b0', '#6dd5ed', '#b8e8f4'],\n fire: ['#f12711', '#f5af19'],\n} as const;\n\nexport type GradientPreset = keyof typeof GRADIENT_PRESETS;\n"],"mappings":";AAMA,IAAa,IAAmB;CAC5B,MAAM;EAAC;EAAU;EAAQ;EAAU;CACnC,SAAS;EAAC;EAAW;EAAW;EAAW;EAAW;EAAW;EAAU;CAC3E,QAAQ;EAAC;EAAW;EAAW;EAAU;CACzC,OAAO;EAAC;EAAW;EAAW;EAAU;CACxC,MAAM,CAAC,WAAW,UAAU;CAC/B"}
@@ -1 +1 @@
1
- {"version":3,"file":"prompts-BLtLCen_.js","names":[],"sources":["../src/prompts/PromptShell.tsx","../src/prompts/text.tsx","../src/prompts/select.tsx","../src/prompts/multiselect.tsx","../src/prompts/confirm.tsx","../src/prompts/statics.ts","../src/prompts/spinner.tsx","../src/prompts/index.ts"],"sourcesContent":["/** @jsxImportSource @sigx/runtime-core */\r\n/**\r\n * Shared chrome for the imperative prompts:\r\n *\r\n * ◆ Project name ← state glyph + message\r\n * │ my-sigx-app▌ ← body rows (promptRow)\r\n * └ hint or error ← footer closes the bar\r\n *\r\n * Plain vnode-building helpers (not components) — each prompt composes them\r\n * inside its own render function.\r\n *\r\n * Structural glyphs use the `dim` token, not `line`: inline prompts render on\r\n * the terminal's own background (no themed canvas), where `line` — tuned for\r\n * borders on the theme canvas — can be near-invisible.\r\n */\r\nimport { resolveColor } from '@sigx/terminal-zero';\r\n\r\nexport interface FrameOptions {\r\n message: string;\r\n rows: unknown[];\r\n /** Dim guidance under the bar; replaced by `error` when set. */\r\n footer?: string;\r\n /** Validation error — switches the glyph to ▲ danger. */\r\n error?: string;\r\n}\r\n\r\nexport function promptFrame(opts: FrameOptions) {\r\n const hasError = !!opts.error;\r\n return (\r\n <box>\r\n <box>\r\n <text color={resolveColor(hasError ? 'danger' : 'accent')}>{hasError ? '▲' : '◆'}</text>\r\n <text color={resolveColor('fg')}> {opts.message}</text>\r\n </box>\r\n {opts.rows}\r\n <box>\r\n <text color={resolveColor('dim')}>└ </text>\r\n {hasError\r\n ? <text color={resolveColor('danger')}>{opts.error}</text>\r\n : opts.footer\r\n ? <text color={resolveColor('dim')}>{opts.footer}</text>\r\n : null}\r\n </box>\r\n </box>\r\n );\r\n}\r\n\r\n/** One body row behind the `│` gutter. */\r\nexport function promptRow(children: unknown) {\r\n return (\r\n <box>\r\n <text color={resolveColor('dim')}>│ </text>\r\n {children}\r\n </box>\r\n );\r\n}\r\n","/** @jsxImportSource @sigx/runtime-core */\r\nimport { component, signal, onMounted, onUnmounted } from '@sigx/runtime-core';\r\nimport { onKey, resolveColor, READY_DELAY_MS, CANCEL, runPrompt, isEnter, isEsc, isCtrlC, isBackspace, isPrintable } from '@sigx/terminal-zero';\r\nimport { promptFrame, promptRow } from './PromptShell';\r\n\r\nexport interface TextOptions {\r\n message: string;\r\n placeholder?: string;\r\n initialValue?: string;\r\n /** Return an error string to reject the submit and keep editing. */\r\n validate?: (value: string) => string | undefined;\r\n /** Render every typed character as this string (password fields). */\r\n mask?: string;\r\n}\r\n\r\n/**\r\n * Single-line text prompt. Enter validates and resolves; Esc/Ctrl+C resolve\r\n * the CANCEL symbol (check with isCancel). Editing is end-of-line only.\r\n */\r\nexport function text(opts: TextOptions): Promise<string | typeof CANCEL> {\r\n const display = (v: string) => (opts.mask ? opts.mask.repeat([...v].length) : v);\r\n return runPrompt<string>({\r\n message: opts.message,\r\n display,\r\n fallback: () => (opts.initialValue !== undefined ? { ok: true, value: opts.initialValue } : { ok: false }),\r\n build: (done) => {\r\n const View = component(() => {\r\n const state = signal({ value: opts.initialValue ?? '', error: '' });\r\n let off: (() => void) | null = null;\r\n let ready = false;\r\n\r\n onMounted(() => {\r\n setTimeout(() => { ready = true; }, READY_DELAY_MS);\r\n off = onKey((key) => {\r\n if (!ready) return;\r\n if (isEsc(key) || isCtrlC(key)) {\r\n done(CANCEL);\r\n return;\r\n }\r\n if (isEnter(key)) {\r\n const error = opts.validate?.(state.value);\r\n if (error) {\r\n state.error = error;\r\n return;\r\n }\r\n done(state.value);\r\n return;\r\n }\r\n if (isBackspace(key)) {\r\n state.value = state.value.slice(0, -1);\r\n state.error = '';\r\n return;\r\n }\r\n if (isPrintable(key)) {\r\n state.value += key;\r\n state.error = '';\r\n }\r\n });\r\n });\r\n onUnmounted(() => { off?.(); });\r\n\r\n return () => {\r\n const shown = opts.mask ? opts.mask.repeat([...state.value].length) : state.value;\r\n const body = state.value.length === 0 && opts.placeholder\r\n ? <text color={resolveColor('dim')}>{opts.placeholder}</text>\r\n : <text color={resolveColor('fg')}>{shown}</text>;\r\n return promptFrame({\r\n message: opts.message,\r\n error: state.error || undefined,\r\n rows: [promptRow(\r\n <text>\r\n {body}\r\n <text backgroundColor={resolveColor('accent')} color={resolveColor('accentText')}> </text>\r\n </text>,\r\n )],\r\n });\r\n };\r\n }, { name: 'TextPrompt' });\r\n return <View />;\r\n },\r\n });\r\n}\r\n\r\n/** `text` with masked rendering — the summary shows the mask, never the value. */\r\nexport function password(opts: Omit<TextOptions, 'mask' | 'placeholder'> & { mask?: string }): Promise<string | typeof CANCEL> {\r\n return text({ ...opts, mask: opts.mask ?? '•' });\r\n}\r\n","/** @jsxImportSource @sigx/runtime-core */\r\nimport { component, signal, onMounted, onUnmounted } from '@sigx/runtime-core';\r\nimport { onKey, resolveColor, GLYPHS, READY_DELAY_MS, CANCEL, runPrompt, isEnter, isEsc, isCtrlC, isUp, isDown } from '@sigx/terminal-zero';\r\nimport { promptFrame, promptRow } from './PromptShell';\r\n\r\nexport interface PromptOption<T> {\r\n value: T;\r\n /** Defaults to String(value). */\r\n label?: string;\r\n description?: string;\r\n /**\r\n * Section header rendered above this option when it differs from the\r\n * previous option's group (multiselect only; pre-sort options by group).\r\n */\r\n group?: string;\r\n}\r\n\r\nexport function optionLabel<T>(option: PromptOption<T>): string {\r\n return option.label ?? String(option.value);\r\n}\r\n\r\nexport interface SelectOptions<T> {\r\n message: string;\r\n options: PromptOption<T>[];\r\n initialValue?: T;\r\n}\r\n\r\n/** Single-choice prompt: ↑/k ↓/j move (wrapping), Enter resolves the value. */\r\nexport function select<T = string>(opts: SelectOptions<T>): Promise<T | typeof CANCEL> {\r\n const display = (v: T) => {\r\n const hit = opts.options.find((o) => o.value === v);\r\n return hit ? optionLabel(hit) : String(v);\r\n };\r\n return runPrompt<T>({\r\n message: opts.message,\r\n display,\r\n fallback: () => (opts.initialValue !== undefined ? { ok: true, value: opts.initialValue } : { ok: false }),\r\n build: (done) => {\r\n const View = component(() => {\r\n const initial = opts.options.findIndex((o) => o.value === opts.initialValue);\r\n const state = signal({ cursor: initial >= 0 ? initial : 0 });\r\n let off: (() => void) | null = null;\r\n let ready = false;\r\n\r\n onMounted(() => {\r\n setTimeout(() => { ready = true; }, READY_DELAY_MS);\r\n off = onKey((key) => {\r\n if (!ready) return;\r\n if (isEsc(key) || isCtrlC(key)) {\r\n done(CANCEL);\r\n return;\r\n }\r\n if (isUp(key) || key === 'k') {\r\n state.cursor = state.cursor > 0 ? state.cursor - 1 : opts.options.length - 1;\r\n return;\r\n }\r\n if (isDown(key) || key === 'j') {\r\n state.cursor = state.cursor < opts.options.length - 1 ? state.cursor + 1 : 0;\r\n return;\r\n }\r\n if (isEnter(key)) {\r\n done(opts.options[state.cursor].value);\r\n }\r\n });\r\n });\r\n onUnmounted(() => { off?.(); });\r\n\r\n return () => promptFrame({\r\n message: opts.message,\r\n rows: opts.options.map((option, i) => {\r\n const onCursor = i === state.cursor;\r\n return promptRow(\r\n <text>\r\n <text color={resolveColor(onCursor ? 'accent' : 'faint')}>{onCursor ? GLYPHS.cursor : ' '} </text>\r\n <text color={resolveColor(onCursor ? 'accent' : 'fg')}>{optionLabel(option)}</text>\r\n {option.description && onCursor && <text color={resolveColor('dim')}> — {option.description}</text>}\r\n </text>,\r\n );\r\n }),\r\n });\r\n }, { name: 'SelectPrompt' });\r\n return <View />;\r\n },\r\n });\r\n}\r\n","/** @jsxImportSource @sigx/runtime-core */\r\nimport { component, signal, onMounted, onUnmounted } from '@sigx/runtime-core';\r\nimport { onKey, resolveColor, GLYPHS, READY_DELAY_MS, CANCEL, runPrompt, isEnter, isEsc, isCtrlC, isUp, isDown, isSpace } from '@sigx/terminal-zero';\r\nimport { promptFrame, promptRow } from './PromptShell';\r\nimport { optionLabel, type PromptOption } from './select';\r\n\r\nexport interface MultiSelectPromptOptions<T> {\r\n message: string;\r\n options: PromptOption<T>[];\r\n initialValues?: T[];\r\n /** Block an empty Enter and show a hint instead. Default false. */\r\n required?: boolean;\r\n}\r\n\r\n/**\r\n * Multi-choice prompt: ↑/k ↓/j move, Space toggles, `a` toggles all,\r\n * Enter resolves the checked values (in option order).\r\n */\r\nexport function multiselect<T = string>(opts: MultiSelectPromptOptions<T>): Promise<T[] | typeof CANCEL> {\r\n const display = (values: T[]) => {\r\n if (values.length === 0) return 'none';\r\n return opts.options.filter((o) => values.includes(o.value)).map(optionLabel).join(', ');\r\n };\r\n return runPrompt<T[]>({\r\n message: opts.message,\r\n display,\r\n fallback: () => (opts.initialValues !== undefined ? { ok: true, value: opts.initialValues } : { ok: false }),\r\n build: (done) => {\r\n const View = component(() => {\r\n const state = signal({\r\n cursor: 0,\r\n checked: new Set<T>(opts.initialValues ?? []),\r\n requiredHint: false,\r\n });\r\n let off: (() => void) | null = null;\r\n let ready = false;\r\n\r\n const resolveChecked = () =>\r\n opts.options.filter((o) => state.checked.has(o.value)).map((o) => o.value);\r\n\r\n onMounted(() => {\r\n setTimeout(() => { ready = true; }, READY_DELAY_MS);\r\n off = onKey((key) => {\r\n if (!ready) return;\r\n if (isEsc(key) || isCtrlC(key)) {\r\n done(CANCEL);\r\n return;\r\n }\r\n if (isUp(key) || key === 'k') {\r\n state.cursor = state.cursor > 0 ? state.cursor - 1 : opts.options.length - 1;\r\n return;\r\n }\r\n if (isDown(key) || key === 'j') {\r\n state.cursor = state.cursor < opts.options.length - 1 ? state.cursor + 1 : 0;\r\n return;\r\n }\r\n if (isSpace(key)) {\r\n const value = opts.options[state.cursor].value;\r\n const next = new Set(state.checked);\r\n if (next.has(value)) next.delete(value);\r\n else next.add(value);\r\n state.checked = next;\r\n state.requiredHint = false;\r\n return;\r\n }\r\n if (key === 'a') {\r\n state.checked = state.checked.size === opts.options.length\r\n ? new Set<T>()\r\n : new Set(opts.options.map((o) => o.value));\r\n state.requiredHint = false;\r\n return;\r\n }\r\n if (isEnter(key)) {\r\n const values = resolveChecked();\r\n if (opts.required && values.length === 0) {\r\n state.requiredHint = true;\r\n return;\r\n }\r\n done(values);\r\n }\r\n });\r\n });\r\n onUnmounted(() => { off?.(); });\r\n\r\n return () => promptFrame({\r\n message: opts.message,\r\n error: state.requiredHint ? 'select at least one option (space)' : undefined,\r\n footer: 'space toggle · a all · enter confirm',\r\n rows: opts.options.flatMap((option, i) => {\r\n const onCursor = i === state.cursor;\r\n const isChecked = state.checked.has(option.value);\r\n const header = option.group && option.group !== opts.options[i - 1]?.group\r\n ? [promptRow(<text color={resolveColor('dim')}>{option.group}</text>)]\r\n : [];\r\n return [\r\n ...header,\r\n promptRow(\r\n <text>\r\n <text color={resolveColor(onCursor ? 'accent' : 'faint')}>{onCursor ? GLYPHS.cursor : ' '} </text>\r\n <text color={resolveColor(isChecked ? 'success' : 'line')}>\r\n {isChecked ? GLYPHS.checkboxOn : GLYPHS.checkboxOff}\r\n </text>\r\n <text color={resolveColor(onCursor ? 'accent' : 'fg')}> {optionLabel(option)}</text>\r\n {option.description && onCursor && <text color={resolveColor('dim')}> — {option.description}</text>}\r\n </text>,\r\n ),\r\n ];\r\n }),\r\n });\r\n }, { name: 'MultiSelectPrompt' });\r\n return <View />;\r\n },\r\n });\r\n}\r\n","/** @jsxImportSource @sigx/runtime-core */\r\nimport { component, signal, onMounted, onUnmounted } from '@sigx/runtime-core';\r\nimport { onKey, resolveColor, GLYPHS, READY_DELAY_MS, CANCEL, runPrompt, isEnter, isEsc, isCtrlC, isLeft, isRight } from '@sigx/terminal-zero';\r\nimport { promptFrame, promptRow } from './PromptShell';\r\n\r\nexport interface ConfirmOptions {\r\n message: string;\r\n initialValue?: boolean;\r\n /** Label for true. Default 'Yes'. */\r\n active?: string;\r\n /** Label for false. Default 'No'. */\r\n inactive?: string;\r\n}\r\n\r\n/**\r\n * Yes/No prompt: y/n answer immediately, ←/→ (or h/l) flip the selection,\r\n * Enter resolves the current value.\r\n */\r\nexport function confirm(opts: ConfirmOptions): Promise<boolean | typeof CANCEL> {\r\n const yes = opts.active ?? 'Yes';\r\n const no = opts.inactive ?? 'No';\r\n return runPrompt<boolean>({\r\n message: opts.message,\r\n display: (v) => (v ? yes : no),\r\n fallback: () => (opts.initialValue !== undefined ? { ok: true, value: opts.initialValue } : { ok: false }),\r\n build: (done) => {\r\n const View = component(() => {\r\n const state = signal({ value: opts.initialValue ?? true });\r\n let off: (() => void) | null = null;\r\n let ready = false;\r\n\r\n onMounted(() => {\r\n setTimeout(() => { ready = true; }, READY_DELAY_MS);\r\n off = onKey((key) => {\r\n if (!ready) return;\r\n if (isEsc(key) || isCtrlC(key)) {\r\n done(CANCEL);\r\n return;\r\n }\r\n if (key === 'y' || key === 'Y') {\r\n done(true);\r\n return;\r\n }\r\n if (key === 'n' || key === 'N') {\r\n done(false);\r\n return;\r\n }\r\n if (isLeft(key) || isRight(key) || key === 'h' || key === 'l') {\r\n state.value = !state.value;\r\n return;\r\n }\r\n if (isEnter(key)) {\r\n done(state.value);\r\n }\r\n });\r\n });\r\n onUnmounted(() => { off?.(); });\r\n\r\n return () => promptFrame({\r\n message: opts.message,\r\n footer: 'y / n · enter confirm',\r\n rows: [promptRow(\r\n <text>\r\n <text color={resolveColor(state.value ? 'accent' : 'dim')}>\r\n {state.value ? GLYPHS.radioOn : GLYPHS.radioOff} {yes}\r\n </text>\r\n <text> </text>\r\n <text color={resolveColor(state.value ? 'dim' : 'accent')}>\r\n {state.value ? GLYPHS.radioOff : GLYPHS.radioOn} {no}\r\n </text>\r\n </text>,\r\n )],\r\n });\r\n }, { name: 'ConfirmPrompt' });\r\n return <View />;\r\n },\r\n });\r\n}\r\n","/**\r\n * Static framing for prompt flows — plain permanent lines, no mount:\r\n *\r\n * ┌ create sigx app intro\r\n * ◇ Project name · my-app (prompt summaries land between)\r\n * │ a note line note\r\n * └ Done — next: pnpm dev outro\r\n * ■ Cancelled. cancel\r\n */\r\nimport { printStatic } from '@sigx/terminal-zero';\r\nimport { paintToken } from '@sigx/terminal-zero';\r\n\r\n/** Open a prompt flow with a title bar. */\r\nexport function intro(title: string): void {\r\n printStatic(`${paintToken('┌', 'dim')} ${paintToken(title, 'accent')}`);\r\n}\r\n\r\n/** Close a prompt flow. */\r\nexport function outro(message: string): void {\r\n printStatic(`${paintToken('└', 'dim')} ${message}\\n`);\r\n}\r\n\r\n/** An informational block inside a flow. */\r\nexport function note(message: string, title?: string): void {\r\n const lines: string[] = [];\r\n if (title) lines.push(`${paintToken('○', 'accent')} ${title}`);\r\n for (const line of message.split('\\n')) {\r\n lines.push(`${paintToken('│', 'dim')} ${paintToken(line, 'dim')}`);\r\n }\r\n printStatic(lines.join('\\n'));\r\n}\r\n\r\n/** Announce a cancelled flow (pair with isCancel + process.exit). */\r\nexport function cancel(message: string): void {\r\n printStatic(`${paintToken('■', 'danger')} ${message}`);\r\n}\r\n","/** @jsxImportSource @sigx/runtime-core */\r\nimport { component, signal } from '@sigx/runtime-core';\r\nimport { renderTerminal, printStatic, GLYPHS, enqueue, isInteractive, paintToken } from '@sigx/terminal-zero';\r\nimport { Spinner } from '../feedback/Spinner';\r\n\r\nexport interface SpinnerHandle {\r\n start(label?: string): void;\r\n /** Update the label while spinning. */\r\n message(label: string): void;\r\n /** Stop and leave a permanent `✔ label` / `✖ label` line. */\r\n stop(label?: string, code?: 'success' | 'error'): void;\r\n}\r\n\r\n/**\r\n * Imperative spinner for prompt flows:\r\n *\r\n * const s = spinner();\r\n * s.start('Installing dependencies');\r\n * await install();\r\n * s.stop('Dependencies installed');\r\n *\r\n * Queues behind any in-flight prompts and occupies the live region until\r\n * stopped — don't await another prompt between start() and stop() (it would\r\n * wait for the spinner's slot and deadlock). Ctrl+C stays the renderer\r\n * default here (exit): a long task should be interruptible.\r\n * Non-TTY: start() renders nothing; stop() prints the summary line.\r\n */\r\nexport function spinner(): SpinnerHandle {\r\n const state = signal({ label: '' });\r\n let active = false;\r\n let handle: { unmount: () => void } | null = null;\r\n let release: (() => void) | null = null;\r\n\r\n return {\r\n start(label = '') {\r\n if (active) return;\r\n active = true;\r\n state.label = label;\r\n const gate = new Promise<void>((resolve) => { release = resolve; });\r\n void enqueue(() => {\r\n if (!active) return Promise.resolve(); // stopped before its slot came up\r\n if (isInteractive()) {\r\n const View = component(() => () => <Spinner label={state.label} />, { name: 'PromptSpinner' });\r\n handle = renderTerminal(<View />, { mode: 'inline', persistOnExit: false });\r\n }\r\n return gate;\r\n });\r\n },\r\n message(label: string) {\r\n state.label = label;\r\n },\r\n stop(label?: string, code: 'success' | 'error' = 'success') {\r\n if (!active) return;\r\n active = false;\r\n handle?.unmount();\r\n handle = null;\r\n const finalLabel = label ?? state.label;\r\n const mark = code === 'success' ? paintToken(GLYPHS.check, 'success') : paintToken(GLYPHS.cross, 'danger');\r\n printStatic(`${mark} ${finalLabel}`);\r\n release?.();\r\n release = null;\r\n },\r\n };\r\n}\r\n","/**\r\n * Imperative prompts: linear CLI wizards without a JSX step\r\n * machine. Each prompt mounts a one-shot inline UI, resolves on Enter, and\r\n * collapses into a permanent `◇ message · answer` line — a finished wizard\r\n * reads as a tidy transcript in scrollback.\r\n */\r\nexport { CANCEL, isCancel, __setInteractiveOverride } from '@sigx/terminal-zero';\r\nexport { text, password, type TextOptions } from './text';\r\nexport { select, type SelectOptions, type PromptOption } from './select';\r\nexport { multiselect, type MultiSelectPromptOptions } from './multiselect';\r\nexport { confirm, type ConfirmOptions } from './confirm';\r\nexport { intro, outro, note, cancel } from './statics';\r\nexport { spinner, type SpinnerHandle } from './spinner';\r\n\r\nimport { CANCEL, isCancel } from '@sigx/terminal-zero';\r\nimport { text, password } from './text';\r\nimport { select } from './select';\r\nimport { multiselect } from './multiselect';\r\nimport { confirm } from './confirm';\r\nimport { intro, outro, note, cancel } from './statics';\r\nimport { spinner } from './spinner';\r\n\r\n/** Namespace bundle for collision-free imports: `prompt.select(...)`. */\r\nexport const prompt = {\r\n text, password, select, multiselect, confirm,\r\n intro, outro, note, cancel, spinner,\r\n isCancel, CANCEL,\r\n} as const;\r\n"],"mappings":"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"}
1
+ {"version":3,"file":"prompts-BLtLCen_.js","names":[],"sources":["../src/prompts/PromptShell.tsx","../src/prompts/text.tsx","../src/prompts/select.tsx","../src/prompts/multiselect.tsx","../src/prompts/confirm.tsx","../src/prompts/statics.ts","../src/prompts/spinner.tsx","../src/prompts/index.ts"],"sourcesContent":["/** @jsxImportSource @sigx/runtime-core */\n/**\n * Shared chrome for the imperative prompts:\n *\n * ◆ Project name ← state glyph + message\n * │ my-sigx-app▌ ← body rows (promptRow)\n * └ hint or error ← footer closes the bar\n *\n * Plain vnode-building helpers (not components) — each prompt composes them\n * inside its own render function.\n *\n * Structural glyphs use the `dim` token, not `line`: inline prompts render on\n * the terminal's own background (no themed canvas), where `line` — tuned for\n * borders on the theme canvas — can be near-invisible.\n */\nimport { resolveColor } from '@sigx/terminal-zero';\n\nexport interface FrameOptions {\n message: string;\n rows: unknown[];\n /** Dim guidance under the bar; replaced by `error` when set. */\n footer?: string;\n /** Validation error — switches the glyph to ▲ danger. */\n error?: string;\n}\n\nexport function promptFrame(opts: FrameOptions) {\n const hasError = !!opts.error;\n return (\n <box>\n <box>\n <text color={resolveColor(hasError ? 'danger' : 'accent')}>{hasError ? '▲' : '◆'}</text>\n <text color={resolveColor('fg')}> {opts.message}</text>\n </box>\n {opts.rows}\n <box>\n <text color={resolveColor('dim')}>└ </text>\n {hasError\n ? <text color={resolveColor('danger')}>{opts.error}</text>\n : opts.footer\n ? <text color={resolveColor('dim')}>{opts.footer}</text>\n : null}\n </box>\n </box>\n );\n}\n\n/** One body row behind the `│` gutter. */\nexport function promptRow(children: unknown) {\n return (\n <box>\n <text color={resolveColor('dim')}>│ </text>\n {children}\n </box>\n );\n}\n","/** @jsxImportSource @sigx/runtime-core */\nimport { component, signal, onMounted, onUnmounted } from '@sigx/runtime-core';\nimport { onKey, resolveColor, READY_DELAY_MS, CANCEL, runPrompt, isEnter, isEsc, isCtrlC, isBackspace, isPrintable } from '@sigx/terminal-zero';\nimport { promptFrame, promptRow } from './PromptShell';\n\nexport interface TextOptions {\n message: string;\n placeholder?: string;\n initialValue?: string;\n /** Return an error string to reject the submit and keep editing. */\n validate?: (value: string) => string | undefined;\n /** Render every typed character as this string (password fields). */\n mask?: string;\n}\n\n/**\n * Single-line text prompt. Enter validates and resolves; Esc/Ctrl+C resolve\n * the CANCEL symbol (check with isCancel). Editing is end-of-line only.\n */\nexport function text(opts: TextOptions): Promise<string | typeof CANCEL> {\n const display = (v: string) => (opts.mask ? opts.mask.repeat([...v].length) : v);\n return runPrompt<string>({\n message: opts.message,\n display,\n fallback: () => (opts.initialValue !== undefined ? { ok: true, value: opts.initialValue } : { ok: false }),\n build: (done) => {\n const View = component(() => {\n const state = signal({ value: opts.initialValue ?? '', error: '' });\n let off: (() => void) | null = null;\n let ready = false;\n\n onMounted(() => {\n setTimeout(() => { ready = true; }, READY_DELAY_MS);\n off = onKey((key) => {\n if (!ready) return;\n if (isEsc(key) || isCtrlC(key)) {\n done(CANCEL);\n return;\n }\n if (isEnter(key)) {\n const error = opts.validate?.(state.value);\n if (error) {\n state.error = error;\n return;\n }\n done(state.value);\n return;\n }\n if (isBackspace(key)) {\n state.value = state.value.slice(0, -1);\n state.error = '';\n return;\n }\n if (isPrintable(key)) {\n state.value += key;\n state.error = '';\n }\n });\n });\n onUnmounted(() => { off?.(); });\n\n return () => {\n const shown = opts.mask ? opts.mask.repeat([...state.value].length) : state.value;\n const body = state.value.length === 0 && opts.placeholder\n ? <text color={resolveColor('dim')}>{opts.placeholder}</text>\n : <text color={resolveColor('fg')}>{shown}</text>;\n return promptFrame({\n message: opts.message,\n error: state.error || undefined,\n rows: [promptRow(\n <text>\n {body}\n <text backgroundColor={resolveColor('accent')} color={resolveColor('accentText')}> </text>\n </text>,\n )],\n });\n };\n }, { name: 'TextPrompt' });\n return <View />;\n },\n });\n}\n\n/** `text` with masked rendering — the summary shows the mask, never the value. */\nexport function password(opts: Omit<TextOptions, 'mask' | 'placeholder'> & { mask?: string }): Promise<string | typeof CANCEL> {\n return text({ ...opts, mask: opts.mask ?? '•' });\n}\n","/** @jsxImportSource @sigx/runtime-core */\nimport { component, signal, onMounted, onUnmounted } from '@sigx/runtime-core';\nimport { onKey, resolveColor, GLYPHS, READY_DELAY_MS, CANCEL, runPrompt, isEnter, isEsc, isCtrlC, isUp, isDown } from '@sigx/terminal-zero';\nimport { promptFrame, promptRow } from './PromptShell';\n\nexport interface PromptOption<T> {\n value: T;\n /** Defaults to String(value). */\n label?: string;\n description?: string;\n /**\n * Section header rendered above this option when it differs from the\n * previous option's group (multiselect only; pre-sort options by group).\n */\n group?: string;\n}\n\nexport function optionLabel<T>(option: PromptOption<T>): string {\n return option.label ?? String(option.value);\n}\n\nexport interface SelectOptions<T> {\n message: string;\n options: PromptOption<T>[];\n initialValue?: T;\n}\n\n/** Single-choice prompt: ↑/k ↓/j move (wrapping), Enter resolves the value. */\nexport function select<T = string>(opts: SelectOptions<T>): Promise<T | typeof CANCEL> {\n const display = (v: T) => {\n const hit = opts.options.find((o) => o.value === v);\n return hit ? optionLabel(hit) : String(v);\n };\n return runPrompt<T>({\n message: opts.message,\n display,\n fallback: () => (opts.initialValue !== undefined ? { ok: true, value: opts.initialValue } : { ok: false }),\n build: (done) => {\n const View = component(() => {\n const initial = opts.options.findIndex((o) => o.value === opts.initialValue);\n const state = signal({ cursor: initial >= 0 ? initial : 0 });\n let off: (() => void) | null = null;\n let ready = false;\n\n onMounted(() => {\n setTimeout(() => { ready = true; }, READY_DELAY_MS);\n off = onKey((key) => {\n if (!ready) return;\n if (isEsc(key) || isCtrlC(key)) {\n done(CANCEL);\n return;\n }\n if (isUp(key) || key === 'k') {\n state.cursor = state.cursor > 0 ? state.cursor - 1 : opts.options.length - 1;\n return;\n }\n if (isDown(key) || key === 'j') {\n state.cursor = state.cursor < opts.options.length - 1 ? state.cursor + 1 : 0;\n return;\n }\n if (isEnter(key)) {\n done(opts.options[state.cursor].value);\n }\n });\n });\n onUnmounted(() => { off?.(); });\n\n return () => promptFrame({\n message: opts.message,\n rows: opts.options.map((option, i) => {\n const onCursor = i === state.cursor;\n return promptRow(\n <text>\n <text color={resolveColor(onCursor ? 'accent' : 'faint')}>{onCursor ? GLYPHS.cursor : ' '} </text>\n <text color={resolveColor(onCursor ? 'accent' : 'fg')}>{optionLabel(option)}</text>\n {option.description && onCursor && <text color={resolveColor('dim')}> — {option.description}</text>}\n </text>,\n );\n }),\n });\n }, { name: 'SelectPrompt' });\n return <View />;\n },\n });\n}\n","/** @jsxImportSource @sigx/runtime-core */\nimport { component, signal, onMounted, onUnmounted } from '@sigx/runtime-core';\nimport { onKey, resolveColor, GLYPHS, READY_DELAY_MS, CANCEL, runPrompt, isEnter, isEsc, isCtrlC, isUp, isDown, isSpace } from '@sigx/terminal-zero';\nimport { promptFrame, promptRow } from './PromptShell';\nimport { optionLabel, type PromptOption } from './select';\n\nexport interface MultiSelectPromptOptions<T> {\n message: string;\n options: PromptOption<T>[];\n initialValues?: T[];\n /** Block an empty Enter and show a hint instead. Default false. */\n required?: boolean;\n}\n\n/**\n * Multi-choice prompt: ↑/k ↓/j move, Space toggles, `a` toggles all,\n * Enter resolves the checked values (in option order).\n */\nexport function multiselect<T = string>(opts: MultiSelectPromptOptions<T>): Promise<T[] | typeof CANCEL> {\n const display = (values: T[]) => {\n if (values.length === 0) return 'none';\n return opts.options.filter((o) => values.includes(o.value)).map(optionLabel).join(', ');\n };\n return runPrompt<T[]>({\n message: opts.message,\n display,\n fallback: () => (opts.initialValues !== undefined ? { ok: true, value: opts.initialValues } : { ok: false }),\n build: (done) => {\n const View = component(() => {\n const state = signal({\n cursor: 0,\n checked: new Set<T>(opts.initialValues ?? []),\n requiredHint: false,\n });\n let off: (() => void) | null = null;\n let ready = false;\n\n const resolveChecked = () =>\n opts.options.filter((o) => state.checked.has(o.value)).map((o) => o.value);\n\n onMounted(() => {\n setTimeout(() => { ready = true; }, READY_DELAY_MS);\n off = onKey((key) => {\n if (!ready) return;\n if (isEsc(key) || isCtrlC(key)) {\n done(CANCEL);\n return;\n }\n if (isUp(key) || key === 'k') {\n state.cursor = state.cursor > 0 ? state.cursor - 1 : opts.options.length - 1;\n return;\n }\n if (isDown(key) || key === 'j') {\n state.cursor = state.cursor < opts.options.length - 1 ? state.cursor + 1 : 0;\n return;\n }\n if (isSpace(key)) {\n const value = opts.options[state.cursor].value;\n const next = new Set(state.checked);\n if (next.has(value)) next.delete(value);\n else next.add(value);\n state.checked = next;\n state.requiredHint = false;\n return;\n }\n if (key === 'a') {\n state.checked = state.checked.size === opts.options.length\n ? new Set<T>()\n : new Set(opts.options.map((o) => o.value));\n state.requiredHint = false;\n return;\n }\n if (isEnter(key)) {\n const values = resolveChecked();\n if (opts.required && values.length === 0) {\n state.requiredHint = true;\n return;\n }\n done(values);\n }\n });\n });\n onUnmounted(() => { off?.(); });\n\n return () => promptFrame({\n message: opts.message,\n error: state.requiredHint ? 'select at least one option (space)' : undefined,\n footer: 'space toggle · a all · enter confirm',\n rows: opts.options.flatMap((option, i) => {\n const onCursor = i === state.cursor;\n const isChecked = state.checked.has(option.value);\n const header = option.group && option.group !== opts.options[i - 1]?.group\n ? [promptRow(<text color={resolveColor('dim')}>{option.group}</text>)]\n : [];\n return [\n ...header,\n promptRow(\n <text>\n <text color={resolveColor(onCursor ? 'accent' : 'faint')}>{onCursor ? GLYPHS.cursor : ' '} </text>\n <text color={resolveColor(isChecked ? 'success' : 'line')}>\n {isChecked ? GLYPHS.checkboxOn : GLYPHS.checkboxOff}\n </text>\n <text color={resolveColor(onCursor ? 'accent' : 'fg')}> {optionLabel(option)}</text>\n {option.description && onCursor && <text color={resolveColor('dim')}> — {option.description}</text>}\n </text>,\n ),\n ];\n }),\n });\n }, { name: 'MultiSelectPrompt' });\n return <View />;\n },\n });\n}\n","/** @jsxImportSource @sigx/runtime-core */\nimport { component, signal, onMounted, onUnmounted } from '@sigx/runtime-core';\nimport { onKey, resolveColor, GLYPHS, READY_DELAY_MS, CANCEL, runPrompt, isEnter, isEsc, isCtrlC, isLeft, isRight } from '@sigx/terminal-zero';\nimport { promptFrame, promptRow } from './PromptShell';\n\nexport interface ConfirmOptions {\n message: string;\n initialValue?: boolean;\n /** Label for true. Default 'Yes'. */\n active?: string;\n /** Label for false. Default 'No'. */\n inactive?: string;\n}\n\n/**\n * Yes/No prompt: y/n answer immediately, ←/→ (or h/l) flip the selection,\n * Enter resolves the current value.\n */\nexport function confirm(opts: ConfirmOptions): Promise<boolean | typeof CANCEL> {\n const yes = opts.active ?? 'Yes';\n const no = opts.inactive ?? 'No';\n return runPrompt<boolean>({\n message: opts.message,\n display: (v) => (v ? yes : no),\n fallback: () => (opts.initialValue !== undefined ? { ok: true, value: opts.initialValue } : { ok: false }),\n build: (done) => {\n const View = component(() => {\n const state = signal({ value: opts.initialValue ?? true });\n let off: (() => void) | null = null;\n let ready = false;\n\n onMounted(() => {\n setTimeout(() => { ready = true; }, READY_DELAY_MS);\n off = onKey((key) => {\n if (!ready) return;\n if (isEsc(key) || isCtrlC(key)) {\n done(CANCEL);\n return;\n }\n if (key === 'y' || key === 'Y') {\n done(true);\n return;\n }\n if (key === 'n' || key === 'N') {\n done(false);\n return;\n }\n if (isLeft(key) || isRight(key) || key === 'h' || key === 'l') {\n state.value = !state.value;\n return;\n }\n if (isEnter(key)) {\n done(state.value);\n }\n });\n });\n onUnmounted(() => { off?.(); });\n\n return () => promptFrame({\n message: opts.message,\n footer: 'y / n · enter confirm',\n rows: [promptRow(\n <text>\n <text color={resolveColor(state.value ? 'accent' : 'dim')}>\n {state.value ? GLYPHS.radioOn : GLYPHS.radioOff} {yes}\n </text>\n <text> </text>\n <text color={resolveColor(state.value ? 'dim' : 'accent')}>\n {state.value ? GLYPHS.radioOff : GLYPHS.radioOn} {no}\n </text>\n </text>,\n )],\n });\n }, { name: 'ConfirmPrompt' });\n return <View />;\n },\n });\n}\n","/**\n * Static framing for prompt flows — plain permanent lines, no mount:\n *\n * ┌ create sigx app intro\n * ◇ Project name · my-app (prompt summaries land between)\n * │ a note line note\n * └ Done — next: pnpm dev outro\n * ■ Cancelled. cancel\n */\nimport { printStatic } from '@sigx/terminal-zero';\nimport { paintToken } from '@sigx/terminal-zero';\n\n/** Open a prompt flow with a title bar. */\nexport function intro(title: string): void {\n printStatic(`${paintToken('┌', 'dim')} ${paintToken(title, 'accent')}`);\n}\n\n/** Close a prompt flow. */\nexport function outro(message: string): void {\n printStatic(`${paintToken('└', 'dim')} ${message}\\n`);\n}\n\n/** An informational block inside a flow. */\nexport function note(message: string, title?: string): void {\n const lines: string[] = [];\n if (title) lines.push(`${paintToken('○', 'accent')} ${title}`);\n for (const line of message.split('\\n')) {\n lines.push(`${paintToken('│', 'dim')} ${paintToken(line, 'dim')}`);\n }\n printStatic(lines.join('\\n'));\n}\n\n/** Announce a cancelled flow (pair with isCancel + process.exit). */\nexport function cancel(message: string): void {\n printStatic(`${paintToken('■', 'danger')} ${message}`);\n}\n","/** @jsxImportSource @sigx/runtime-core */\nimport { component, signal } from '@sigx/runtime-core';\nimport { renderTerminal, printStatic, GLYPHS, enqueue, isInteractive, paintToken } from '@sigx/terminal-zero';\nimport { Spinner } from '../feedback/Spinner';\n\nexport interface SpinnerHandle {\n start(label?: string): void;\n /** Update the label while spinning. */\n message(label: string): void;\n /** Stop and leave a permanent `✔ label` / `✖ label` line. */\n stop(label?: string, code?: 'success' | 'error'): void;\n}\n\n/**\n * Imperative spinner for prompt flows:\n *\n * const s = spinner();\n * s.start('Installing dependencies');\n * await install();\n * s.stop('Dependencies installed');\n *\n * Queues behind any in-flight prompts and occupies the live region until\n * stopped — don't await another prompt between start() and stop() (it would\n * wait for the spinner's slot and deadlock). Ctrl+C stays the renderer\n * default here (exit): a long task should be interruptible.\n * Non-TTY: start() renders nothing; stop() prints the summary line.\n */\nexport function spinner(): SpinnerHandle {\n const state = signal({ label: '' });\n let active = false;\n let handle: { unmount: () => void } | null = null;\n let release: (() => void) | null = null;\n\n return {\n start(label = '') {\n if (active) return;\n active = true;\n state.label = label;\n const gate = new Promise<void>((resolve) => { release = resolve; });\n void enqueue(() => {\n if (!active) return Promise.resolve(); // stopped before its slot came up\n if (isInteractive()) {\n const View = component(() => () => <Spinner label={state.label} />, { name: 'PromptSpinner' });\n handle = renderTerminal(<View />, { mode: 'inline', persistOnExit: false });\n }\n return gate;\n });\n },\n message(label: string) {\n state.label = label;\n },\n stop(label?: string, code: 'success' | 'error' = 'success') {\n if (!active) return;\n active = false;\n handle?.unmount();\n handle = null;\n const finalLabel = label ?? state.label;\n const mark = code === 'success' ? paintToken(GLYPHS.check, 'success') : paintToken(GLYPHS.cross, 'danger');\n printStatic(`${mark} ${finalLabel}`);\n release?.();\n release = null;\n },\n };\n}\n","/**\n * Imperative prompts: linear CLI wizards without a JSX step\n * machine. Each prompt mounts a one-shot inline UI, resolves on Enter, and\n * collapses into a permanent `◇ message · answer` line — a finished wizard\n * reads as a tidy transcript in scrollback.\n */\nexport { CANCEL, isCancel, __setInteractiveOverride } from '@sigx/terminal-zero';\nexport { text, password, type TextOptions } from './text';\nexport { select, type SelectOptions, type PromptOption } from './select';\nexport { multiselect, type MultiSelectPromptOptions } from './multiselect';\nexport { confirm, type ConfirmOptions } from './confirm';\nexport { intro, outro, note, cancel } from './statics';\nexport { spinner, type SpinnerHandle } from './spinner';\n\nimport { CANCEL, isCancel } from '@sigx/terminal-zero';\nimport { text, password } from './text';\nimport { select } from './select';\nimport { multiselect } from './multiselect';\nimport { confirm } from './confirm';\nimport { intro, outro, note, cancel } from './statics';\nimport { spinner } from './spinner';\n\n/** Namespace bundle for collision-free imports: `prompt.select(...)`. */\nexport const prompt = {\n text, password, select, multiselect, confirm,\n intro, outro, note, cancel, spinner,\n isCancel, CANCEL,\n} as const;\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"tasks-CdVFFOFs.js","names":[],"sources":["../src/tasks/LogPanel.tsx","../src/tasks/TaskList.tsx","../src/tasks/logStore.ts","../src/tasks/LogView.tsx"],"sourcesContent":["/** @jsxImportSource @sigx/runtime-core */\r\nimport { component, type Define } from '@sigx/runtime-core';\r\nimport { resolveColor, getTerminalSize, truncateToWidth } from '@sigx/terminal-zero';\r\nimport type { LogStore } from './logStore';\r\n\r\n/**\r\n * Streaming log tail: the last `height` lines of a stream, dimmed, in\r\n * one of three variants:\r\n * - `bar` — `│` gutter in the `line` color (default)\r\n * - `panel` — rounded bordered box with the title as its label\r\n * - `plain` — bare lines\r\n * Feed it a LogStore (preferred — reactive, `\\r`-aware) or a plain `lines`\r\n * array. Follow-tail is inherent: it always shows the last `height` lines.\r\n * Lines are truncated escape-aware, so tool output containing its own ANSI\r\n * can't overflow the inline live region.\r\n */\r\nexport const LogPanel = component<\r\n Define.Prop<\"store\", LogStore, false> &\r\n Define.Prop<\"lines\", string[], false> &\r\n Define.Prop<\"height\", number, false> &\r\n Define.Prop<\"width\", number, false> &\r\n Define.Prop<\"title\", string, false> &\r\n Define.Prop<\"variant\", 'bar' | 'plain' | 'panel', false> &\r\n Define.Prop<\"color\", string, false>\r\n>(({ props }) => {\r\n return () => {\r\n const height = props.height || 6;\r\n const width = props.width || Math.max(20, getTerminalSize().columns - 4);\r\n const variant = props.variant || 'bar';\r\n const textColor = resolveColor(props.color || 'dim');\r\n // Box borders keep the `line` token (drawn on the themed canvas), but\r\n // the bare bar/title glyphs render on the terminal's own background in\r\n // inline mode, where `line` can be near-invisible — use `dim`.\r\n const borderColor = resolveColor('line');\r\n const glyphColor = resolveColor('dim');\r\n\r\n const src = props.store\r\n ? props.store.tail(height)\r\n : (props.lines ?? []).slice(-height);\r\n\r\n if (variant === 'panel') {\r\n return (\r\n <box border=\"rounded\" borderColor={borderColor} label={props.title} labelColor={resolveColor('accent')} padX={1}>\r\n {src.flatMap((line, i) => {\r\n const row = <text color={textColor}>{truncateToWidth(line, width - 4)}</text>;\r\n return i > 0 ? [<br />, row] : [row];\r\n })}\r\n {src.length === 0 && <text color={resolveColor('faint')}>…</text>}\r\n </box>\r\n );\r\n }\r\n\r\n const gutter = variant === 'bar' ? 2 : 0;\r\n const rows = src.flatMap((line, i) => {\r\n const row = (\r\n <text>\r\n {variant === 'bar' && <text color={glyphColor}>│ </text>}\r\n <text color={textColor}>{truncateToWidth(line, width - gutter)}</text>\r\n </text>\r\n );\r\n return i > 0 ? [<br />, row] : [row];\r\n });\r\n\r\n return (\r\n <box>\r\n {props.title && [\r\n <text color={glyphColor}>{variant === 'bar' ? '┌ ' : ''}</text>,\r\n <text color={resolveColor('dim')}>{props.title}</text>,\r\n <br />,\r\n ]}\r\n {rows}\r\n </box>\r\n );\r\n };\r\n}, { name: 'LogPanel' });\r\n\r\nexport default LogPanel;\r\n","/** @jsxImportSource @sigx/runtime-core */\r\nimport { component, onMounted, onUnmounted, type Define } from '@sigx/runtime-core';\r\nimport { resolveColor, GLYPHS, SPINNERS, getTick, subscribeTicker } from '@sigx/terminal-zero';\r\nimport { LogPanel } from './LogPanel';\r\nimport type { LogStore } from './logStore';\r\n\r\nexport type TaskStatus = 'pending' | 'running' | 'success' | 'fail' | 'skipped';\r\n\r\nexport interface TaskItem {\r\n id: string;\r\n label: string;\r\n status: TaskStatus;\r\n /** Optional dimmed suffix, e.g. a duration or a count. */\r\n detail?: string;\r\n}\r\n\r\n/**\r\n * Build-pipeline task list: one row per task with a status glyph (pending ◌,\r\n * running spinner, ✔/✖, skipped −), in `plain` or `tree` (├─/└─ guides)\r\n * variants. Pass a LogStore via `log` to stream a LogPanel tail under the\r\n * running task. Data-driven: mutate the `tasks` array in place (statuses are\r\n * reactive through the signal proxy).\r\n */\r\nexport const TaskList = component<\r\n Define.Prop<\"tasks\", TaskItem[], true> &\r\n Define.Prop<\"variant\", 'plain' | 'tree', false> &\r\n Define.Prop<\"log\", LogStore, false> &\r\n Define.Prop<\"logHeight\", number, false>\r\n>(({ props }) => {\r\n let unsub: (() => void) | null = null;\r\n\r\n onMounted(() => { unsub = subscribeTicker(); });\r\n onUnmounted(() => { unsub?.(); });\r\n\r\n return () => {\r\n const tasks = props.tasks ?? [];\r\n const tree = props.variant === 'tree';\r\n // `dim`, not `line`: tree guides render on the terminal's own\r\n // background in inline mode, where `line` can be near-invisible.\r\n const guideColor = resolveColor('dim');\r\n\r\n const glyphFor = (status: TaskStatus): [string, string, string] => {\r\n // [glyph, glyph color token, label color token]\r\n switch (status) {\r\n case 'running': return [SPINNERS.dots[getTick() % SPINNERS.dots.length], 'accent', 'fg'];\r\n case 'success': return [GLYPHS.check, 'success', 'fg'];\r\n case 'fail': return [GLYPHS.cross, 'danger', 'danger'];\r\n case 'skipped': return ['−', 'dim', 'dim'];\r\n default: return ['◌', 'faint', 'dim'];\r\n }\r\n };\r\n\r\n return (\r\n <box>\r\n {tasks.flatMap((task, i) => {\r\n const [glyph, glyphToken, labelToken] = glyphFor(task.status);\r\n const guide = tree ? (i === tasks.length - 1 ? '└─ ' : '├─ ') : '';\r\n const row = (\r\n <text>\r\n {tree && <text color={guideColor}>{guide}</text>}\r\n <text color={resolveColor(glyphToken)}>{glyph}</text>\r\n <text color={resolveColor(labelToken)}> {task.label}</text>\r\n {task.detail && <text color={resolveColor('dim')}> ({task.detail})</text>}\r\n </text>\r\n );\r\n const parts = i > 0 ? [<br />, row] : [row];\r\n if (task.status === 'running' && props.log) {\r\n parts.push(<LogPanel store={props.log} height={props.logHeight || 6} variant=\"bar\" />);\r\n }\r\n return parts;\r\n })}\r\n </box>\r\n );\r\n };\r\n}, { name: 'TaskList' });\r\n\r\nexport default TaskList;\r\n","/**\r\n * Reactive log store for streaming tool output (pod install, gradle, …) into\r\n * a LogPanel: a ring buffer of completed lines plus the live partial line.\r\n * Headless and transport-agnostic — pipe anything into it:\r\n *\r\n * proc.stdout.on('data', (d) => store.push(d.toString()));\r\n *\r\n * `\\r` inside a line is resolved with terminal overlay semantics, so progress\r\n * lines (\"Downloading 42%\\r\") render their latest frame, and a shorter frame\r\n * overwrites only the prefix of a longer one — like a real terminal.\r\n */\r\nimport { signal } from '@sigx/reactivity';\r\nimport { resolveColor, GLYPHS, getOutputTarget, printStatic, resolveFg } from '@sigx/terminal-zero';\r\n\r\nexport interface LogStoreOptions {\r\n /** Max retained completed lines (ring buffer). Default 10 000. */\r\n limit?: number;\r\n /**\r\n * Stream completed lines straight to printStatic as they arrive.\r\n * Default: on when stdout is not a TTY (piped/CI logs stay complete and\r\n * ordered), off when interactive (the LogPanel shows the tail instead).\r\n */\r\n passthrough?: boolean;\r\n}\r\n\r\nexport interface LogStore {\r\n /** Whether this store streams lines to printStatic (fixed at creation). */\r\n readonly passthrough: boolean;\r\n push(chunk: string): void;\r\n /** Commit a trailing partial line, if any. */\r\n end(): void;\r\n clear(): void;\r\n /** Completed lines, plus the live partial as the last entry. Reactive. */\r\n lines(): readonly string[];\r\n /** The last `n` of lines(). Reactive. */\r\n tail(n: number): string[];\r\n count(): number;\r\n}\r\n\r\n/** Resolve `\\r` overwrites: each segment overlays the prefix of what it follows. */\r\nfunction overlayCR(line: string): string {\r\n if (!line.includes('\\r')) return line;\r\n return line.split('\\r').reduce((acc, seg) => seg + acc.slice(seg.length));\r\n}\r\n\r\nexport function createLogStore(opts: LogStoreOptions = {}): LogStore {\r\n const limit = opts.limit ?? 10_000;\r\n const passthrough = opts.passthrough ?? !getOutputTarget().isTTY;\r\n const state = signal({ lines: [] as string[], partial: '' });\r\n\r\n const commit = (raw: string) => {\r\n const line = overlayCR(raw);\r\n state.lines.push(line);\r\n if (state.lines.length > limit) {\r\n state.lines.splice(0, state.lines.length - limit);\r\n }\r\n if (passthrough) printStatic(line);\r\n };\r\n\r\n return {\r\n passthrough,\r\n push(chunk: string) {\r\n const buf = state.partial + chunk.replace(/\\r\\n/g, '\\n');\r\n const parts = buf.split('\\n');\r\n state.partial = parts.pop()!;\r\n for (const part of parts) commit(part);\r\n },\r\n end() {\r\n if (state.partial) {\r\n const rest = state.partial;\r\n state.partial = '';\r\n commit(rest);\r\n }\r\n },\r\n clear() {\r\n state.lines.splice(0, state.lines.length);\r\n state.partial = '';\r\n },\r\n lines() {\r\n const all = state.lines.slice();\r\n if (state.partial) all.push(overlayCR(state.partial));\r\n return all;\r\n },\r\n count() {\r\n return state.lines.length + (state.partial ? 1 : 0);\r\n },\r\n tail(n: number) {\r\n if (n <= 0) return [];\r\n return this.lines().slice(-n);\r\n },\r\n };\r\n}\r\n\r\nfunction formatDuration(ms: number): string {\r\n return ms < 1000 ? `${Math.round(ms)}ms` : `${(ms / 1000).toFixed(1)}s`;\r\n}\r\n\r\nexport interface CollapseOptions {\r\n label: string;\r\n ok: boolean;\r\n durationMs?: number;\r\n /** On failure, the full captured log is flushed into scrollback first. */\r\n store?: LogStore;\r\n}\r\n\r\n/**\r\n * Print a task's permanent one-line summary above the live region —\r\n * `✔ pod install (3.2s)` on success; on failure the full log flushes first so\r\n * it's greppable in scrollback, then `✖ label`. The live-region collapse\r\n * itself is the consumer flipping the task off `running` (the panel unmounts\r\n * and the repaint shrinks the region). Inline-mode pattern: in fullscreen,\r\n * static output queues until the alt screen exits, by design.\r\n */\r\nexport function collapseTask(o: CollapseOptions): void {\r\n // resolveFg accepts both hex (truecolor/256) and ANSI names (ansi16) and\r\n // returns '' at depth none — exactly the degradation static lines need.\r\n const sgr = (token: string) => resolveFg(resolveColor(token));\r\n const dim = sgr('dim');\r\n const off = (code: string) => (code ? '\\x1b[39m' : '');\r\n\r\n if (!o.ok && o.store && !o.store.passthrough) {\r\n const lines = o.store.lines();\r\n if (lines.length) printStatic(lines.join('\\n'));\r\n }\r\n\r\n const mark = sgr(o.ok ? 'success' : 'danger');\r\n const glyph = o.ok ? GLYPHS.check : GLYPHS.cross;\r\n const duration = o.durationMs !== undefined\r\n ? ` ${dim}(${formatDuration(o.durationMs)})${off(dim)}`\r\n : '';\r\n printStatic(`${mark}${glyph}${off(mark)} ${o.label}${duration}`);\r\n}\r\n","/** @jsxImportSource @sigx/runtime-core */\r\nimport { component, onMounted, onUnmounted, signal, type Define } from '@sigx/runtime-core';\r\nimport {\r\n onKey, registerFocusable, unregisterFocusable, focusState, focus, resolveColor,\r\n getTerminalSize, truncateToWidth, displayWidth, READY_DELAY_MS,\r\n} from '@sigx/terminal-zero';\r\nimport type { LogStore } from './logStore';\r\n\r\n/**\r\n * Focusable, scrollable log viewer — the \"logs tab\" of a persistent dev TUI.\r\n * Follows the tail by default; scrolling up pauses follow and pins the\r\n * visible window even while the stream keeps growing; scrolling back to the\r\n * bottom (or End / `f`) re-engages follow.\r\n *\r\n * Keys while focused: ↑/k ↓/j scroll one line, PgUp/PgDn a page,\r\n * Home jump to the oldest, End jump to the tail (re-follows), `f` toggle.\r\n */\r\nexport const LogView = component<\r\n Define.Prop<'store', LogStore, false> &\r\n Define.Prop<'lines', string[], false> &\r\n Define.Prop<'height', number, false> &\r\n Define.Prop<'width', number, false> &\r\n Define.Prop<'title', string, false> &\r\n Define.Prop<'autofocus', boolean, false>\r\n>(({ props }) => {\r\n const id = Math.random().toString(36).slice(2);\r\n let isReady = false;\r\n const isFocused = () => focusState.activeId === id;\r\n\r\n // `offset` counts lines back from the tail. `anchorTotal` records the\r\n // stream length when the offset was set, so while paused the window stays\r\n // pinned to the same CONTENT as new lines arrive (drift compensation) —\r\n // without writing signals during render.\r\n const state = signal({ offset: 0, follow: true, anchorTotal: 0 });\r\n\r\n const src = () => (props.store ? props.store.lines() : (props.lines ?? []));\r\n const getHeight = () => props.height || 10;\r\n\r\n const effOffset = (total: number) => {\r\n if (state.follow) return 0;\r\n const drifted = state.offset + Math.max(0, total - state.anchorTotal);\r\n return Math.min(Math.max(0, drifted), Math.max(0, total - getHeight()));\r\n };\r\n\r\n const setOffset = (next: number, total: number) => {\r\n const max = Math.max(0, total - getHeight());\r\n const clamped = Math.min(Math.max(0, next), max);\r\n state.offset = clamped;\r\n state.anchorTotal = total;\r\n state.follow = clamped === 0;\r\n };\r\n\r\n const handleKey = (key: string) => {\r\n if (!isFocused() || !isReady) return;\r\n const total = src().length;\r\n const cur = effOffset(total);\r\n const page = getHeight();\r\n\r\n if (key === '\\x1B[A' || key === 'k') {\r\n setOffset(cur + 1, total);\r\n return;\r\n }\r\n if (key === '\\x1B[B' || key === 'j') {\r\n setOffset(cur - 1, total);\r\n return;\r\n }\r\n if (key === '\\x1b[5~') { // PgUp\r\n setOffset(cur + page, total);\r\n return;\r\n }\r\n if (key === '\\x1b[6~') { // PgDn\r\n setOffset(cur - page, total);\r\n return;\r\n }\r\n if (key === '\\x1b[H' || key === '\\x1b[1~') { // Home → oldest\r\n setOffset(Number.MAX_SAFE_INTEGER, total);\r\n return;\r\n }\r\n if (key === '\\x1b[F' || key === '\\x1b[4~') { // End → tail, re-follow\r\n setOffset(0, total);\r\n return;\r\n }\r\n if (key === 'f') {\r\n if (state.follow) {\r\n // Pause in place.\r\n state.offset = cur;\r\n state.anchorTotal = total;\r\n state.follow = false;\r\n } else {\r\n setOffset(0, total);\r\n }\r\n }\r\n };\r\n\r\n let keyCleanup: (() => void) | null = null;\r\n\r\n onMounted(() => {\r\n registerFocusable(id);\r\n if (props.autofocus) focus(id);\r\n keyCleanup = onKey(handleKey);\r\n setTimeout(() => { isReady = true; }, READY_DELAY_MS);\r\n });\r\n\r\n onUnmounted(() => {\r\n if (keyCleanup) keyCleanup();\r\n unregisterFocusable(id);\r\n });\r\n\r\n return () => {\r\n const focused = isFocused();\r\n const height = getHeight();\r\n const width = props.width || Math.max(20, getTerminalSize().columns - 4);\r\n const all = src();\r\n const total = all.length;\r\n const off = effOffset(total);\r\n const end = total - off;\r\n const start = Math.max(0, end - height);\r\n\r\n // The box sizes itself to its widest line, so pad every line to the\r\n // interior width — the viewport then spans the full configured width\r\n // (terminal width by default) instead of hugging its content.\r\n const inner = Math.max(1, width - 4); // rounded border (2) + padX (2)\r\n const window = all.slice(start, end).map((line) => {\r\n const cut = truncateToWidth(line, inner);\r\n return cut + ' '.repeat(Math.max(0, inner - displayWidth(cut)));\r\n });\r\n while (window.length < height) window.push(' '.repeat(inner)); // stable frame height\r\n\r\n const rows = window.flatMap((line, i) => {\r\n const node = <text color={resolveColor('dim')}>{line}</text>;\r\n return i > 0 ? [<br />, node] : [node];\r\n });\r\n\r\n return (\r\n <box>\r\n <box\r\n border=\"rounded\"\r\n borderColor={resolveColor(focused ? 'accent' : 'line')}\r\n label={props.title}\r\n labelColor={resolveColor(focused ? 'accent' : 'dim')}\r\n padX={1}\r\n >\r\n {rows}\r\n </box>\r\n <box>\r\n <text color={resolveColor('dim')}> {total ? `${start + 1}–${end}/${total}` : '0/0'} · </text>\r\n {state.follow\r\n ? <text color={resolveColor('dim')}>following</text>\r\n : <text color={resolveColor('warn')}>paused</text>}\r\n </box>\r\n </box>\r\n );\r\n };\r\n}, { name: 'LogView' });\r\n\r\nexport default LogView;\r\n"],"mappings":"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"}
1
+ {"version":3,"file":"tasks-CdVFFOFs.js","names":[],"sources":["../src/tasks/LogPanel.tsx","../src/tasks/TaskList.tsx","../src/tasks/logStore.ts","../src/tasks/LogView.tsx"],"sourcesContent":["/** @jsxImportSource @sigx/runtime-core */\nimport { component, type Define } from '@sigx/runtime-core';\nimport { resolveColor, getTerminalSize, truncateToWidth } from '@sigx/terminal-zero';\nimport type { LogStore } from './logStore';\n\n/**\n * Streaming log tail: the last `height` lines of a stream, dimmed, in\n * one of three variants:\n * - `bar` — `│` gutter in the `line` color (default)\n * - `panel` — rounded bordered box with the title as its label\n * - `plain` — bare lines\n * Feed it a LogStore (preferred — reactive, `\\r`-aware) or a plain `lines`\n * array. Follow-tail is inherent: it always shows the last `height` lines.\n * Lines are truncated escape-aware, so tool output containing its own ANSI\n * can't overflow the inline live region.\n */\nexport const LogPanel = component<\n Define.Prop<\"store\", LogStore, false> &\n Define.Prop<\"lines\", string[], false> &\n Define.Prop<\"height\", number, false> &\n Define.Prop<\"width\", number, false> &\n Define.Prop<\"title\", string, false> &\n Define.Prop<\"variant\", 'bar' | 'plain' | 'panel', false> &\n Define.Prop<\"color\", string, false>\n>(({ props }) => {\n return () => {\n const height = props.height || 6;\n const width = props.width || Math.max(20, getTerminalSize().columns - 4);\n const variant = props.variant || 'bar';\n const textColor = resolveColor(props.color || 'dim');\n // Box borders keep the `line` token (drawn on the themed canvas), but\n // the bare bar/title glyphs render on the terminal's own background in\n // inline mode, where `line` can be near-invisible — use `dim`.\n const borderColor = resolveColor('line');\n const glyphColor = resolveColor('dim');\n\n const src = props.store\n ? props.store.tail(height)\n : (props.lines ?? []).slice(-height);\n\n if (variant === 'panel') {\n return (\n <box border=\"rounded\" borderColor={borderColor} label={props.title} labelColor={resolveColor('accent')} padX={1}>\n {src.flatMap((line, i) => {\n const row = <text color={textColor}>{truncateToWidth(line, width - 4)}</text>;\n return i > 0 ? [<br />, row] : [row];\n })}\n {src.length === 0 && <text color={resolveColor('faint')}>…</text>}\n </box>\n );\n }\n\n const gutter = variant === 'bar' ? 2 : 0;\n const rows = src.flatMap((line, i) => {\n const row = (\n <text>\n {variant === 'bar' && <text color={glyphColor}>│ </text>}\n <text color={textColor}>{truncateToWidth(line, width - gutter)}</text>\n </text>\n );\n return i > 0 ? [<br />, row] : [row];\n });\n\n return (\n <box>\n {props.title && [\n <text color={glyphColor}>{variant === 'bar' ? '┌ ' : ''}</text>,\n <text color={resolveColor('dim')}>{props.title}</text>,\n <br />,\n ]}\n {rows}\n </box>\n );\n };\n}, { name: 'LogPanel' });\n\nexport default LogPanel;\n","/** @jsxImportSource @sigx/runtime-core */\nimport { component, onMounted, onUnmounted, type Define } from '@sigx/runtime-core';\nimport { resolveColor, GLYPHS, SPINNERS, getTick, subscribeTicker } from '@sigx/terminal-zero';\nimport { LogPanel } from './LogPanel';\nimport type { LogStore } from './logStore';\n\nexport type TaskStatus = 'pending' | 'running' | 'success' | 'fail' | 'skipped';\n\nexport interface TaskItem {\n id: string;\n label: string;\n status: TaskStatus;\n /** Optional dimmed suffix, e.g. a duration or a count. */\n detail?: string;\n}\n\n/**\n * Build-pipeline task list: one row per task with a status glyph (pending ◌,\n * running spinner, ✔/✖, skipped −), in `plain` or `tree` (├─/└─ guides)\n * variants. Pass a LogStore via `log` to stream a LogPanel tail under the\n * running task. Data-driven: mutate the `tasks` array in place (statuses are\n * reactive through the signal proxy).\n */\nexport const TaskList = component<\n Define.Prop<\"tasks\", TaskItem[], true> &\n Define.Prop<\"variant\", 'plain' | 'tree', false> &\n Define.Prop<\"log\", LogStore, false> &\n Define.Prop<\"logHeight\", number, false>\n>(({ props }) => {\n let unsub: (() => void) | null = null;\n\n onMounted(() => { unsub = subscribeTicker(); });\n onUnmounted(() => { unsub?.(); });\n\n return () => {\n const tasks = props.tasks ?? [];\n const tree = props.variant === 'tree';\n // `dim`, not `line`: tree guides render on the terminal's own\n // background in inline mode, where `line` can be near-invisible.\n const guideColor = resolveColor('dim');\n\n const glyphFor = (status: TaskStatus): [string, string, string] => {\n // [glyph, glyph color token, label color token]\n switch (status) {\n case 'running': return [SPINNERS.dots[getTick() % SPINNERS.dots.length], 'accent', 'fg'];\n case 'success': return [GLYPHS.check, 'success', 'fg'];\n case 'fail': return [GLYPHS.cross, 'danger', 'danger'];\n case 'skipped': return ['−', 'dim', 'dim'];\n default: return ['◌', 'faint', 'dim'];\n }\n };\n\n return (\n <box>\n {tasks.flatMap((task, i) => {\n const [glyph, glyphToken, labelToken] = glyphFor(task.status);\n const guide = tree ? (i === tasks.length - 1 ? '└─ ' : '├─ ') : '';\n const row = (\n <text>\n {tree && <text color={guideColor}>{guide}</text>}\n <text color={resolveColor(glyphToken)}>{glyph}</text>\n <text color={resolveColor(labelToken)}> {task.label}</text>\n {task.detail && <text color={resolveColor('dim')}> ({task.detail})</text>}\n </text>\n );\n const parts = i > 0 ? [<br />, row] : [row];\n if (task.status === 'running' && props.log) {\n parts.push(<LogPanel store={props.log} height={props.logHeight || 6} variant=\"bar\" />);\n }\n return parts;\n })}\n </box>\n );\n };\n}, { name: 'TaskList' });\n\nexport default TaskList;\n","/**\n * Reactive log store for streaming tool output (pod install, gradle, …) into\n * a LogPanel: a ring buffer of completed lines plus the live partial line.\n * Headless and transport-agnostic — pipe anything into it:\n *\n * proc.stdout.on('data', (d) => store.push(d.toString()));\n *\n * `\\r` inside a line is resolved with terminal overlay semantics, so progress\n * lines (\"Downloading 42%\\r\") render their latest frame, and a shorter frame\n * overwrites only the prefix of a longer one — like a real terminal.\n */\nimport { signal } from '@sigx/reactivity';\nimport { resolveColor, GLYPHS, getOutputTarget, printStatic, resolveFg } from '@sigx/terminal-zero';\n\nexport interface LogStoreOptions {\n /** Max retained completed lines (ring buffer). Default 10 000. */\n limit?: number;\n /**\n * Stream completed lines straight to printStatic as they arrive.\n * Default: on when stdout is not a TTY (piped/CI logs stay complete and\n * ordered), off when interactive (the LogPanel shows the tail instead).\n */\n passthrough?: boolean;\n}\n\nexport interface LogStore {\n /** Whether this store streams lines to printStatic (fixed at creation). */\n readonly passthrough: boolean;\n push(chunk: string): void;\n /** Commit a trailing partial line, if any. */\n end(): void;\n clear(): void;\n /** Completed lines, plus the live partial as the last entry. Reactive. */\n lines(): readonly string[];\n /** The last `n` of lines(). Reactive. */\n tail(n: number): string[];\n count(): number;\n}\n\n/** Resolve `\\r` overwrites: each segment overlays the prefix of what it follows. */\nfunction overlayCR(line: string): string {\n if (!line.includes('\\r')) return line;\n return line.split('\\r').reduce((acc, seg) => seg + acc.slice(seg.length));\n}\n\nexport function createLogStore(opts: LogStoreOptions = {}): LogStore {\n const limit = opts.limit ?? 10_000;\n const passthrough = opts.passthrough ?? !getOutputTarget().isTTY;\n const state = signal({ lines: [] as string[], partial: '' });\n\n const commit = (raw: string) => {\n const line = overlayCR(raw);\n state.lines.push(line);\n if (state.lines.length > limit) {\n state.lines.splice(0, state.lines.length - limit);\n }\n if (passthrough) printStatic(line);\n };\n\n return {\n passthrough,\n push(chunk: string) {\n const buf = state.partial + chunk.replace(/\\r\\n/g, '\\n');\n const parts = buf.split('\\n');\n state.partial = parts.pop()!;\n for (const part of parts) commit(part);\n },\n end() {\n if (state.partial) {\n const rest = state.partial;\n state.partial = '';\n commit(rest);\n }\n },\n clear() {\n state.lines.splice(0, state.lines.length);\n state.partial = '';\n },\n lines() {\n const all = state.lines.slice();\n if (state.partial) all.push(overlayCR(state.partial));\n return all;\n },\n count() {\n return state.lines.length + (state.partial ? 1 : 0);\n },\n tail(n: number) {\n if (n <= 0) return [];\n return this.lines().slice(-n);\n },\n };\n}\n\nfunction formatDuration(ms: number): string {\n return ms < 1000 ? `${Math.round(ms)}ms` : `${(ms / 1000).toFixed(1)}s`;\n}\n\nexport interface CollapseOptions {\n label: string;\n ok: boolean;\n durationMs?: number;\n /** On failure, the full captured log is flushed into scrollback first. */\n store?: LogStore;\n}\n\n/**\n * Print a task's permanent one-line summary above the live region —\n * `✔ pod install (3.2s)` on success; on failure the full log flushes first so\n * it's greppable in scrollback, then `✖ label`. The live-region collapse\n * itself is the consumer flipping the task off `running` (the panel unmounts\n * and the repaint shrinks the region). Inline-mode pattern: in fullscreen,\n * static output queues until the alt screen exits, by design.\n */\nexport function collapseTask(o: CollapseOptions): void {\n // resolveFg accepts both hex (truecolor/256) and ANSI names (ansi16) and\n // returns '' at depth none — exactly the degradation static lines need.\n const sgr = (token: string) => resolveFg(resolveColor(token));\n const dim = sgr('dim');\n const off = (code: string) => (code ? '\\x1b[39m' : '');\n\n if (!o.ok && o.store && !o.store.passthrough) {\n const lines = o.store.lines();\n if (lines.length) printStatic(lines.join('\\n'));\n }\n\n const mark = sgr(o.ok ? 'success' : 'danger');\n const glyph = o.ok ? GLYPHS.check : GLYPHS.cross;\n const duration = o.durationMs !== undefined\n ? ` ${dim}(${formatDuration(o.durationMs)})${off(dim)}`\n : '';\n printStatic(`${mark}${glyph}${off(mark)} ${o.label}${duration}`);\n}\n","/** @jsxImportSource @sigx/runtime-core */\nimport { component, onMounted, onUnmounted, signal, type Define } from '@sigx/runtime-core';\nimport {\n onKey, registerFocusable, unregisterFocusable, focusState, focus, resolveColor,\n getTerminalSize, truncateToWidth, displayWidth, READY_DELAY_MS,\n} from '@sigx/terminal-zero';\nimport type { LogStore } from './logStore';\n\n/**\n * Focusable, scrollable log viewer — the \"logs tab\" of a persistent dev TUI.\n * Follows the tail by default; scrolling up pauses follow and pins the\n * visible window even while the stream keeps growing; scrolling back to the\n * bottom (or End / `f`) re-engages follow.\n *\n * Keys while focused: ↑/k ↓/j scroll one line, PgUp/PgDn a page,\n * Home jump to the oldest, End jump to the tail (re-follows), `f` toggle.\n */\nexport const LogView = component<\n Define.Prop<'store', LogStore, false> &\n Define.Prop<'lines', string[], false> &\n Define.Prop<'height', number, false> &\n Define.Prop<'width', number, false> &\n Define.Prop<'title', string, false> &\n Define.Prop<'autofocus', boolean, false>\n>(({ props }) => {\n const id = Math.random().toString(36).slice(2);\n let isReady = false;\n const isFocused = () => focusState.activeId === id;\n\n // `offset` counts lines back from the tail. `anchorTotal` records the\n // stream length when the offset was set, so while paused the window stays\n // pinned to the same CONTENT as new lines arrive (drift compensation) —\n // without writing signals during render.\n const state = signal({ offset: 0, follow: true, anchorTotal: 0 });\n\n const src = () => (props.store ? props.store.lines() : (props.lines ?? []));\n const getHeight = () => props.height || 10;\n\n const effOffset = (total: number) => {\n if (state.follow) return 0;\n const drifted = state.offset + Math.max(0, total - state.anchorTotal);\n return Math.min(Math.max(0, drifted), Math.max(0, total - getHeight()));\n };\n\n const setOffset = (next: number, total: number) => {\n const max = Math.max(0, total - getHeight());\n const clamped = Math.min(Math.max(0, next), max);\n state.offset = clamped;\n state.anchorTotal = total;\n state.follow = clamped === 0;\n };\n\n const handleKey = (key: string) => {\n if (!isFocused() || !isReady) return;\n const total = src().length;\n const cur = effOffset(total);\n const page = getHeight();\n\n if (key === '\\x1B[A' || key === 'k') {\n setOffset(cur + 1, total);\n return;\n }\n if (key === '\\x1B[B' || key === 'j') {\n setOffset(cur - 1, total);\n return;\n }\n if (key === '\\x1b[5~') { // PgUp\n setOffset(cur + page, total);\n return;\n }\n if (key === '\\x1b[6~') { // PgDn\n setOffset(cur - page, total);\n return;\n }\n if (key === '\\x1b[H' || key === '\\x1b[1~') { // Home → oldest\n setOffset(Number.MAX_SAFE_INTEGER, total);\n return;\n }\n if (key === '\\x1b[F' || key === '\\x1b[4~') { // End → tail, re-follow\n setOffset(0, total);\n return;\n }\n if (key === 'f') {\n if (state.follow) {\n // Pause in place.\n state.offset = cur;\n state.anchorTotal = total;\n state.follow = false;\n } else {\n setOffset(0, total);\n }\n }\n };\n\n let keyCleanup: (() => void) | null = null;\n\n onMounted(() => {\n registerFocusable(id);\n if (props.autofocus) focus(id);\n keyCleanup = onKey(handleKey);\n setTimeout(() => { isReady = true; }, READY_DELAY_MS);\n });\n\n onUnmounted(() => {\n if (keyCleanup) keyCleanup();\n unregisterFocusable(id);\n });\n\n return () => {\n const focused = isFocused();\n const height = getHeight();\n const width = props.width || Math.max(20, getTerminalSize().columns - 4);\n const all = src();\n const total = all.length;\n const off = effOffset(total);\n const end = total - off;\n const start = Math.max(0, end - height);\n\n // The box sizes itself to its widest line, so pad every line to the\n // interior width — the viewport then spans the full configured width\n // (terminal width by default) instead of hugging its content.\n const inner = Math.max(1, width - 4); // rounded border (2) + padX (2)\n const window = all.slice(start, end).map((line) => {\n const cut = truncateToWidth(line, inner);\n return cut + ' '.repeat(Math.max(0, inner - displayWidth(cut)));\n });\n while (window.length < height) window.push(' '.repeat(inner)); // stable frame height\n\n const rows = window.flatMap((line, i) => {\n const node = <text color={resolveColor('dim')}>{line}</text>;\n return i > 0 ? [<br />, node] : [node];\n });\n\n return (\n <box>\n <box\n border=\"rounded\"\n borderColor={resolveColor(focused ? 'accent' : 'line')}\n label={props.title}\n labelColor={resolveColor(focused ? 'accent' : 'dim')}\n padX={1}\n >\n {rows}\n </box>\n <box>\n <text color={resolveColor('dim')}> {total ? `${start + 1}–${end}/${total}` : '0/0'} · </text>\n {state.follow\n ? <text color={resolveColor('dim')}>following</text>\n : <text color={resolveColor('warn')}>paused</text>}\n </box>\n </box>\n );\n };\n}, { name: 'LogView' });\n\nexport default LogView;\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"theme-DASuxf6k.js","names":[],"sources":["../src/theme/builtins.ts"],"sourcesContent":["/**\r\n * The five SigX-tui themes. Registered with the @sigx/terminal-zero engine on\r\n * import; `obsidian` becomes the default. Hex values are the single source of\r\n * truth — the renderer degrades them to 256/16-color as needed.\r\n */\r\nimport { registerTheme, setTheme, applyThemeCanvas, type Theme } from '@sigx/terminal-zero';\r\n\r\nexport const THEMES: Record<string, Theme> = {\r\n obsidian: {\r\n name: 'Obsidian', mode: 'dark',\r\n bg: '#16161e', panel: '#1a1b26', chrome: '#13131a', line: '#2a2c3d',\r\n fg: '#c0caf5', dim: '#565f89', faint: '#3b4261', shadow: '#0d0d12',\r\n accent: '#7aa2f7', accentSoft: '#283250', accentText: '#16161e', selSoft: '#272d44',\r\n success: '#9ece6a', warn: '#e0af68', danger: '#f7768e', info: '#7dcfff',\r\n black: '#16161e', red: '#f7768e', green: '#9ece6a', yellow: '#e0af68',\r\n blue: '#7aa2f7', magenta: '#bb9af7', cyan: '#7dcfff', white: '#c0caf5',\r\n },\r\n nord: {\r\n name: 'Nord', mode: 'dark',\r\n bg: '#2e3440', panel: '#3b4252', chrome: '#2b303b', line: '#434c5e',\r\n fg: '#e5e9f0', dim: '#7b8494', faint: '#4c566a', shadow: '#21252e',\r\n accent: '#88c0d0', accentSoft: '#3b4860', accentText: '#2e3440', selSoft: '#3b4456',\r\n success: '#a3be8c', warn: '#ebcb8b', danger: '#bf616a', info: '#81a1c1',\r\n black: '#2e3440', red: '#bf616a', green: '#a3be8c', yellow: '#ebcb8b',\r\n blue: '#81a1c1', magenta: '#b48ead', cyan: '#88c0d0', white: '#e5e9f0',\r\n },\r\n gum: {\r\n name: 'Gum', mode: 'dark',\r\n bg: '#191724', panel: '#1f1d2e', chrome: '#16141f', line: '#2a273f',\r\n fg: '#e0def4', dim: '#6e6a86', faint: '#403d52', shadow: '#100e17',\r\n accent: '#c4a7e7', accentSoft: '#2a2438', accentText: '#191724', selSoft: '#2a273f',\r\n success: '#abc4a0', warn: '#f6c177', danger: '#eb6f92', info: '#9ccfd8',\r\n black: '#191724', red: '#eb6f92', green: '#abc4a0', yellow: '#f6c177',\r\n blue: '#31a1c2', magenta: '#c4a7e7', cyan: '#9ccfd8', white: '#e0def4',\r\n },\r\n paper: {\r\n name: 'Paper', mode: 'light',\r\n bg: '#faf9f5', panel: '#ffffff', chrome: '#f1efe8', line: '#e6e3da',\r\n fg: '#2a2733', dim: '#8b8794', faint: '#c4c0b6', shadow: '#d9d6cc',\r\n accent: '#5b5bd6', accentSoft: '#ecebfb', accentText: '#ffffff', selSoft: '#f1f0fb',\r\n success: '#2a8a4f', warn: '#b07a1a', danger: '#d1495b', info: '#1b8a9a',\r\n black: '#2a2733', red: '#d1495b', green: '#2a8a4f', yellow: '#b07a1a',\r\n blue: '#2f6feb', magenta: '#8250df', cyan: '#1b8a9a', white: '#6e7481',\r\n },\r\n classic: {\r\n name: 'Classic', mode: 'dark',\r\n bg: '#0c0e13', panel: '#11141b', chrome: '#0a0b0f', line: '#1d2129',\r\n fg: '#c9cdd6', dim: '#6b7280', faint: '#3a3f4b', shadow: '#000000',\r\n accent: '#3fd07f', accentSoft: '#10271c', accentText: '#0c0e13', selSoft: '#15211b',\r\n success: '#3fd07f', warn: '#f5c518', danger: '#e5484d', info: '#34d4d4',\r\n black: '#0c0e13', red: '#e5484d', green: '#3fd07f', yellow: '#f5c518',\r\n blue: '#4a9eff', magenta: '#c678dd', cyan: '#34d4d4', white: '#c9cdd6',\r\n },\r\n};\r\n\r\nfor (const [id, theme] of Object.entries(THEMES)) {\r\n registerTheme(id, theme);\r\n}\r\n\r\n// The SigX-tui default.\r\nsetTheme('obsidian');\r\n\r\n// Auto-paint the themed screen canvas from the active theme. Reacts to runtime\r\n// setTheme/setColorDepth. Call disableThemeCanvas() to opt out (transparent /\r\n// inline apps that don't want a themed background).\r\napplyThemeCanvas();\r\n"],"mappings":";;AAOA,IAAa,IAAgC;CACzC,UAAU;EACN,MAAM;EAAY,MAAM;EACxB,IAAI;EAAW,OAAO;EAAW,QAAQ;EAAW,MAAM;EAC1D,IAAI;EAAW,KAAK;EAAW,OAAO;EAAW,QAAQ;EACzD,QAAQ;EAAW,YAAY;EAAW,YAAY;EAAW,SAAS;EAC1E,SAAS;EAAW,MAAM;EAAW,QAAQ;EAAW,MAAM;EAC9D,OAAO;EAAW,KAAK;EAAW,OAAO;EAAW,QAAQ;EAC5D,MAAM;EAAW,SAAS;EAAW,MAAM;EAAW,OAAO;EAChE;CACD,MAAM;EACF,MAAM;EAAQ,MAAM;EACpB,IAAI;EAAW,OAAO;EAAW,QAAQ;EAAW,MAAM;EAC1D,IAAI;EAAW,KAAK;EAAW,OAAO;EAAW,QAAQ;EACzD,QAAQ;EAAW,YAAY;EAAW,YAAY;EAAW,SAAS;EAC1E,SAAS;EAAW,MAAM;EAAW,QAAQ;EAAW,MAAM;EAC9D,OAAO;EAAW,KAAK;EAAW,OAAO;EAAW,QAAQ;EAC5D,MAAM;EAAW,SAAS;EAAW,MAAM;EAAW,OAAO;EAChE;CACD,KAAK;EACD,MAAM;EAAO,MAAM;EACnB,IAAI;EAAW,OAAO;EAAW,QAAQ;EAAW,MAAM;EAC1D,IAAI;EAAW,KAAK;EAAW,OAAO;EAAW,QAAQ;EACzD,QAAQ;EAAW,YAAY;EAAW,YAAY;EAAW,SAAS;EAC1E,SAAS;EAAW,MAAM;EAAW,QAAQ;EAAW,MAAM;EAC9D,OAAO;EAAW,KAAK;EAAW,OAAO;EAAW,QAAQ;EAC5D,MAAM;EAAW,SAAS;EAAW,MAAM;EAAW,OAAO;EAChE;CACD,OAAO;EACH,MAAM;EAAS,MAAM;EACrB,IAAI;EAAW,OAAO;EAAW,QAAQ;EAAW,MAAM;EAC1D,IAAI;EAAW,KAAK;EAAW,OAAO;EAAW,QAAQ;EACzD,QAAQ;EAAW,YAAY;EAAW,YAAY;EAAW,SAAS;EAC1E,SAAS;EAAW,MAAM;EAAW,QAAQ;EAAW,MAAM;EAC9D,OAAO;EAAW,KAAK;EAAW,OAAO;EAAW,QAAQ;EAC5D,MAAM;EAAW,SAAS;EAAW,MAAM;EAAW,OAAO;EAChE;CACD,SAAS;EACL,MAAM;EAAW,MAAM;EACvB,IAAI;EAAW,OAAO;EAAW,QAAQ;EAAW,MAAM;EAC1D,IAAI;EAAW,KAAK;EAAW,OAAO;EAAW,QAAQ;EACzD,QAAQ;EAAW,YAAY;EAAW,YAAY;EAAW,SAAS;EAC1E,SAAS;EAAW,MAAM;EAAW,QAAQ;EAAW,MAAM;EAC9D,OAAO;EAAW,KAAK;EAAW,OAAO;EAAW,QAAQ;EAC5D,MAAM;EAAW,SAAS;EAAW,MAAM;EAAW,OAAO;EAChE;CACJ;AAED,KAAK,IAAM,CAAC,GAAI,MAAU,OAAO,QAAQ,EAAO,EAC5C,EAAc,GAAI,EAAM;AAI5B,EAAS,WAAW,EAKpB,GAAkB"}
1
+ {"version":3,"file":"theme-DASuxf6k.js","names":[],"sources":["../src/theme/builtins.ts"],"sourcesContent":["/**\n * The five SigX-tui themes. Registered with the @sigx/terminal-zero engine on\n * import; `obsidian` becomes the default. Hex values are the single source of\n * truth — the renderer degrades them to 256/16-color as needed.\n */\nimport { registerTheme, setTheme, applyThemeCanvas, type Theme } from '@sigx/terminal-zero';\n\nexport const THEMES: Record<string, Theme> = {\n obsidian: {\n name: 'Obsidian', mode: 'dark',\n bg: '#16161e', panel: '#1a1b26', chrome: '#13131a', line: '#2a2c3d',\n fg: '#c0caf5', dim: '#565f89', faint: '#3b4261', shadow: '#0d0d12',\n accent: '#7aa2f7', accentSoft: '#283250', accentText: '#16161e', selSoft: '#272d44',\n success: '#9ece6a', warn: '#e0af68', danger: '#f7768e', info: '#7dcfff',\n black: '#16161e', red: '#f7768e', green: '#9ece6a', yellow: '#e0af68',\n blue: '#7aa2f7', magenta: '#bb9af7', cyan: '#7dcfff', white: '#c0caf5',\n },\n nord: {\n name: 'Nord', mode: 'dark',\n bg: '#2e3440', panel: '#3b4252', chrome: '#2b303b', line: '#434c5e',\n fg: '#e5e9f0', dim: '#7b8494', faint: '#4c566a', shadow: '#21252e',\n accent: '#88c0d0', accentSoft: '#3b4860', accentText: '#2e3440', selSoft: '#3b4456',\n success: '#a3be8c', warn: '#ebcb8b', danger: '#bf616a', info: '#81a1c1',\n black: '#2e3440', red: '#bf616a', green: '#a3be8c', yellow: '#ebcb8b',\n blue: '#81a1c1', magenta: '#b48ead', cyan: '#88c0d0', white: '#e5e9f0',\n },\n gum: {\n name: 'Gum', mode: 'dark',\n bg: '#191724', panel: '#1f1d2e', chrome: '#16141f', line: '#2a273f',\n fg: '#e0def4', dim: '#6e6a86', faint: '#403d52', shadow: '#100e17',\n accent: '#c4a7e7', accentSoft: '#2a2438', accentText: '#191724', selSoft: '#2a273f',\n success: '#abc4a0', warn: '#f6c177', danger: '#eb6f92', info: '#9ccfd8',\n black: '#191724', red: '#eb6f92', green: '#abc4a0', yellow: '#f6c177',\n blue: '#31a1c2', magenta: '#c4a7e7', cyan: '#9ccfd8', white: '#e0def4',\n },\n paper: {\n name: 'Paper', mode: 'light',\n bg: '#faf9f5', panel: '#ffffff', chrome: '#f1efe8', line: '#e6e3da',\n fg: '#2a2733', dim: '#8b8794', faint: '#c4c0b6', shadow: '#d9d6cc',\n accent: '#5b5bd6', accentSoft: '#ecebfb', accentText: '#ffffff', selSoft: '#f1f0fb',\n success: '#2a8a4f', warn: '#b07a1a', danger: '#d1495b', info: '#1b8a9a',\n black: '#2a2733', red: '#d1495b', green: '#2a8a4f', yellow: '#b07a1a',\n blue: '#2f6feb', magenta: '#8250df', cyan: '#1b8a9a', white: '#6e7481',\n },\n classic: {\n name: 'Classic', mode: 'dark',\n bg: '#0c0e13', panel: '#11141b', chrome: '#0a0b0f', line: '#1d2129',\n fg: '#c9cdd6', dim: '#6b7280', faint: '#3a3f4b', shadow: '#000000',\n accent: '#3fd07f', accentSoft: '#10271c', accentText: '#0c0e13', selSoft: '#15211b',\n success: '#3fd07f', warn: '#f5c518', danger: '#e5484d', info: '#34d4d4',\n black: '#0c0e13', red: '#e5484d', green: '#3fd07f', yellow: '#f5c518',\n blue: '#4a9eff', magenta: '#c678dd', cyan: '#34d4d4', white: '#c9cdd6',\n },\n};\n\nfor (const [id, theme] of Object.entries(THEMES)) {\n registerTheme(id, theme);\n}\n\n// The SigX-tui default.\nsetTheme('obsidian');\n\n// Auto-paint the themed screen canvas from the active theme. Reacts to runtime\n// setTheme/setColorDepth. Call disableThemeCanvas() to opt out (transparent /\n// inline apps that don't want a themed background).\napplyThemeCanvas();\n"],"mappings":";;AAOA,IAAa,IAAgC;CACzC,UAAU;EACN,MAAM;EAAY,MAAM;EACxB,IAAI;EAAW,OAAO;EAAW,QAAQ;EAAW,MAAM;EAC1D,IAAI;EAAW,KAAK;EAAW,OAAO;EAAW,QAAQ;EACzD,QAAQ;EAAW,YAAY;EAAW,YAAY;EAAW,SAAS;EAC1E,SAAS;EAAW,MAAM;EAAW,QAAQ;EAAW,MAAM;EAC9D,OAAO;EAAW,KAAK;EAAW,OAAO;EAAW,QAAQ;EAC5D,MAAM;EAAW,SAAS;EAAW,MAAM;EAAW,OAAO;EAChE;CACD,MAAM;EACF,MAAM;EAAQ,MAAM;EACpB,IAAI;EAAW,OAAO;EAAW,QAAQ;EAAW,MAAM;EAC1D,IAAI;EAAW,KAAK;EAAW,OAAO;EAAW,QAAQ;EACzD,QAAQ;EAAW,YAAY;EAAW,YAAY;EAAW,SAAS;EAC1E,SAAS;EAAW,MAAM;EAAW,QAAQ;EAAW,MAAM;EAC9D,OAAO;EAAW,KAAK;EAAW,OAAO;EAAW,QAAQ;EAC5D,MAAM;EAAW,SAAS;EAAW,MAAM;EAAW,OAAO;EAChE;CACD,KAAK;EACD,MAAM;EAAO,MAAM;EACnB,IAAI;EAAW,OAAO;EAAW,QAAQ;EAAW,MAAM;EAC1D,IAAI;EAAW,KAAK;EAAW,OAAO;EAAW,QAAQ;EACzD,QAAQ;EAAW,YAAY;EAAW,YAAY;EAAW,SAAS;EAC1E,SAAS;EAAW,MAAM;EAAW,QAAQ;EAAW,MAAM;EAC9D,OAAO;EAAW,KAAK;EAAW,OAAO;EAAW,QAAQ;EAC5D,MAAM;EAAW,SAAS;EAAW,MAAM;EAAW,OAAO;EAChE;CACD,OAAO;EACH,MAAM;EAAS,MAAM;EACrB,IAAI;EAAW,OAAO;EAAW,QAAQ;EAAW,MAAM;EAC1D,IAAI;EAAW,KAAK;EAAW,OAAO;EAAW,QAAQ;EACzD,QAAQ;EAAW,YAAY;EAAW,YAAY;EAAW,SAAS;EAC1E,SAAS;EAAW,MAAM;EAAW,QAAQ;EAAW,MAAM;EAC9D,OAAO;EAAW,KAAK;EAAW,OAAO;EAAW,QAAQ;EAC5D,MAAM;EAAW,SAAS;EAAW,MAAM;EAAW,OAAO;EAChE;CACD,SAAS;EACL,MAAM;EAAW,MAAM;EACvB,IAAI;EAAW,OAAO;EAAW,QAAQ;EAAW,MAAM;EAC1D,IAAI;EAAW,KAAK;EAAW,OAAO;EAAW,QAAQ;EACzD,QAAQ;EAAW,YAAY;EAAW,YAAY;EAAW,SAAS;EAC1E,SAAS;EAAW,MAAM;EAAW,QAAQ;EAAW,MAAM;EAC9D,OAAO;EAAW,KAAK;EAAW,OAAO;EAAW,QAAQ;EAC5D,MAAM;EAAW,SAAS;EAAW,MAAM;EAAW,OAAO;EAChE;CACJ;AAED,KAAK,IAAM,CAAC,GAAI,MAAU,OAAO,QAAQ,EAAO,EAC5C,EAAc,GAAI,EAAM;AAI5B,EAAS,WAAW,EAKpB,GAAkB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sigx/terminal-ui",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "SigX-tui design-system components for SignalX terminal UIs, built on @sigx/terminal-zero",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -77,7 +77,7 @@
77
77
  "dependencies": {
78
78
  "@sigx/reactivity": "^0.4.9",
79
79
  "@sigx/runtime-core": "^0.4.9",
80
- "@sigx/terminal-zero": "^0.5.0"
80
+ "@sigx/terminal-zero": "^0.6.0"
81
81
  },
82
82
  "devDependencies": {
83
83
  "@sigx/vite": "^0.4.7",