@posthog/wizard 2.79.1 → 2.80.1
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/README.md +187 -57
- package/dist/{PickerMenu-dG5DQCZA.js → PickerMenu-BGGeYqd-.js} +112 -12
- package/dist/PickerMenu-BGGeYqd-.js.map +1 -0
- package/dist/README.md +9 -6
- package/dist/{agent-interface-FkbaBJwz.js → agent-interface-CemBvTAA.js} +130 -324
- package/dist/agent-interface-CemBvTAA.js.map +1 -0
- package/dist/{agent-prompt-loader-D0weQ-7k.js → agent-prompt-loader-DPnj6SwU.js} +6 -5
- package/dist/agent-prompt-loader-DPnj6SwU.js.map +1 -0
- package/dist/analytics-B3ROGuIr.js +2 -0
- package/dist/analytics-D21z3E5l.js +732 -0
- package/dist/analytics-D21z3E5l.js.map +1 -0
- package/dist/{api-CrbCIQOz.js → api-DtGIqQ2U.js} +2 -3
- package/dist/api-DtGIqQ2U.js.map +1 -0
- package/dist/api-IhROOQNv.js +2 -0
- package/dist/api-key-login-OEntFvu5.js +2 -0
- package/dist/api-key-login-h3E2G3zu.js +55 -0
- package/dist/api-key-login-h3E2G3zu.js.map +1 -0
- package/dist/bin.js +2 -2
- package/dist/bin.js.map +1 -1
- package/dist/{bounded-fs-vAq-T-K7.js → bounded-fs-CAsuF6al.js} +3 -3
- package/dist/{bounded-fs-vAq-T-K7.js.map → bounded-fs-CAsuF6al.js.map} +1 -1
- package/dist/{ci-install-COcV8SY2.js → ci-install-CEYE02t-.js} +22 -26
- package/dist/ci-install-CEYE02t-.js.map +1 -0
- package/dist/console-log-O828zyiq.js +32 -0
- package/dist/console-log-O828zyiq.js.map +1 -0
- package/dist/{debug-kAHwWGg7.js → debug-BfQPD2Hg.js} +18 -52
- package/dist/debug-BfQPD2Hg.js.map +1 -0
- package/dist/{defaults-a9TelDhJ.js → defaults-DDVl3tKN.js} +2 -51
- package/dist/defaults-DDVl3tKN.js.map +1 -0
- package/dist/{emit-BSetXzDV.js → emit-RfAewcWv.js} +1 -1
- package/dist/{emit-BSetXzDV.js.map → emit-RfAewcWv.js.map} +1 -1
- package/dist/{errors-C2domSkK.js → errors-CHrJzAZb.js} +4 -4
- package/dist/{errors-C2domSkK.js.map → errors-CHrJzAZb.js.map} +1 -1
- package/dist/family-picker-BKs-tTyu.js +46 -0
- package/dist/family-picker-BKs-tTyu.js.map +1 -0
- package/dist/{gateway-C9-GlLO9.js → gateway-D3gIlR6b.js} +122 -11
- package/dist/gateway-D3gIlR6b.js.map +1 -0
- package/dist/headless-BJjhP0SI.js +10 -0
- package/dist/headless-BJjhP0SI.js.map +1 -0
- package/dist/headless-DIX68UUA.js +3 -0
- package/dist/host-resolution-D8GhEVoX.js +114 -0
- package/dist/host-resolution-D8GhEVoX.js.map +1 -0
- package/dist/install-Bw8tvDvS.js +2 -0
- package/dist/{add-mcp-server-to-clients-DIaTRWMY.js → install-paPCzPHI.js} +34 -100
- package/dist/install-paPCzPHI.js.map +1 -0
- package/dist/interactive-tdxZKWax.js +11 -0
- package/dist/interactive-tdxZKWax.js.map +1 -0
- package/dist/{telemetry-B7zJdRmA.js → links-Cyj6v8kg.js} +4 -13
- package/dist/links-Cyj6v8kg.js.map +1 -0
- package/dist/{main-CgKxbwMP.js → main-0p-YxUNa.js} +233 -490
- package/dist/main-0p-YxUNa.js.map +1 -0
- package/dist/{mcp-DnAyW0-w.js → mcp-D3Z5MXhA.js} +3 -4
- package/dist/{mcp-DnAyW0-w.js.map → mcp-D3Z5MXhA.js.map} +1 -1
- package/dist/mcp-install-smoke-test.ts +2 -2
- package/dist/{mcp-prompt-streaming-DDWQ-FCA.js → mcp-prompt-streaming-COVbWnHt.js} +12 -12
- package/dist/mcp-prompt-streaming-COVbWnHt.js.map +1 -0
- package/dist/mode-label-DB3ol_sj.js +9 -0
- package/dist/mode-label-DB3ol_sj.js.map +1 -0
- package/dist/{models-WcwUnSKb.js → models-D1SnCzvl.js} +2 -2
- package/dist/{models-WcwUnSKb.js.map → models-D1SnCzvl.js.map} +1 -1
- package/dist/non-interactive-DxRBc73O.js +18 -0
- package/dist/non-interactive-DxRBc73O.js.map +1 -0
- package/dist/{oauth-session-DYM2Hrl2.js → oauth-session-Dde79Ycm.js} +3 -3
- package/dist/{oauth-session-DYM2Hrl2.js.map → oauth-session-Dde79Ycm.js.map} +1 -1
- package/dist/{orchestrator-tools-DbgNBFGA.js → orchestrator-tools-BSNhzzkT.js} +3 -3
- package/dist/{orchestrator-tools-DbgNBFGA.js.map → orchestrator-tools-BSNhzzkT.js.map} +1 -1
- package/dist/{package-json-Atg34fa4.js → package-json-2UrhE6kP.js} +1 -1
- package/dist/{package-json-Atg34fa4.js.map → package-json-2UrhE6kP.js.map} +1 -1
- package/dist/package-json-Dblip2_X.js +2 -0
- package/dist/package-manager-BRXlXo0p.js +105 -0
- package/dist/package-manager-BRXlXo0p.js.map +1 -0
- package/dist/{pi-8KsYsPmM.js → pi-cM_4Gd_9.js} +19 -20
- package/dist/pi-cM_4Gd_9.js.map +1 -0
- package/dist/playground-DTKy0XHT.js +12 -0
- package/dist/playground-DTKy0XHT.js.map +1 -0
- package/dist/posthog-integration-bwHwE87a.js +10291 -0
- package/dist/posthog-integration-bwHwE87a.js.map +1 -0
- package/dist/programs-BhtyA3lE.js +3 -0
- package/dist/programs-SeT1qNik.js +5348 -0
- package/dist/programs-SeT1qNik.js.map +1 -0
- package/dist/provisioning-BcXfeCMc.js +2 -0
- package/dist/{provisioning-Bo2NRxFz.js → provisioning-JbJtq7hq.js} +5 -67
- package/dist/provisioning-JbJtq7hq.js.map +1 -0
- package/dist/{queue-tools-C8mXOh7e.js → queue-tools-BIZSJyt6.js} +7 -9
- package/dist/queue-tools-BIZSJyt6.js.map +1 -0
- package/dist/results-DtKICAdz.js +55 -0
- package/dist/results-DtKICAdz.js.map +1 -0
- package/dist/run-CDurA5tO.js +248 -0
- package/dist/run-CDurA5tO.js.map +1 -0
- package/dist/run-DDCvLILJ.js +246 -0
- package/dist/run-DDCvLILJ.js.map +1 -0
- package/dist/run-tool-CTq9OOdT.js +2 -0
- package/dist/run-tool-DML8JJvt.js +2420 -0
- package/dist/run-tool-DML8JJvt.js.map +1 -0
- package/dist/runners-Dhu4pZTN.js +253 -0
- package/dist/runners-Dhu4pZTN.js.map +1 -0
- package/dist/{security-C71pkwqH.js → security-DEH8Rn0_.js} +5 -5
- package/dist/security-DEH8Rn0_.js.map +1 -0
- package/dist/setup-utils-C5-WM-hS.js +118 -0
- package/dist/setup-utils-C5-WM-hS.js.map +1 -0
- package/dist/smoke-test.sh +1 -1
- package/dist/{playground-BlYRBMj7.js → start-playground-DL4mVEUx.js} +63 -54
- package/dist/start-playground-DL4mVEUx.js.map +1 -0
- package/dist/{subagent-D1TrTchE.js → subagent-CnFvAq6E.js} +3 -3
- package/dist/{subagent-D1TrTchE.js.map → subagent-CnFvAq6E.js.map} +1 -1
- package/dist/{task-DqRlQBfs.js → task-BOScSm4W.js} +17 -18
- package/dist/task-BOScSm4W.js.map +1 -0
- package/dist/{tasks-ChWufsdO.js → tasks-BfJhVky2.js} +1 -1
- package/dist/tasks-BfJhVky2.js.map +1 -0
- package/dist/telemetry-CP4rGicd.js +13 -0
- package/dist/telemetry-CP4rGicd.js.map +1 -0
- package/dist/terminal-J3OD3dN0.js +16162 -0
- package/dist/terminal-J3OD3dN0.js.map +1 -0
- package/dist/tools-CCMV3DmB.js +544 -0
- package/dist/tools-CCMV3DmB.js.map +1 -0
- package/dist/{tools-CkJ2y9Wt.js → tools-Cz8tDptL.js} +9 -9
- package/dist/tools-Cz8tDptL.js.map +1 -0
- package/dist/tui-BUZ2PWqN.js +2 -0
- package/dist/tui-Csf7VJxt.js +25 -0
- package/dist/tui-Csf7VJxt.js.map +1 -0
- package/dist/{steps-BPcg3_3Q.js → upload-step-CEacB2RV.js} +17 -19
- package/dist/upload-step-CEacB2RV.js.map +1 -0
- package/dist/urls-BDELGZHq.js +67 -0
- package/dist/urls-BDELGZHq.js.map +1 -0
- package/dist/warlock-smoke-test.ts +1 -2
- package/dist/{wizard-abort-BPdv07np.js → wizard-abort-D3-75KyV.js} +41 -32
- package/dist/wizard-abort-D3-75KyV.js.map +1 -0
- package/dist/yara-hooks-CZ1tKxy_.js +2 -0
- package/dist/{yara-hooks-BnxejtXK.js → yara-hooks-D8YDyX7o.js} +283 -21
- package/dist/yara-hooks-D8YDyX7o.js.map +1 -0
- package/package.json +7 -8
- package/dist/PickerMenu-dG5DQCZA.js.map +0 -1
- package/dist/add-mcp-server-to-clients-DIaTRWMY.js.map +0 -1
- package/dist/add-mcp-server-to-clients-artmN7mz.js +0 -2
- package/dist/agent-BEjARD2a.js +0 -6
- package/dist/agent-BWFhuLgo.js +0 -3178
- package/dist/agent-BWFhuLgo.js.map +0 -1
- package/dist/agent-interface-FkbaBJwz.js.map +0 -1
- package/dist/agent-prompt-loader-D0weQ-7k.js.map +0 -1
- package/dist/analytics-DFtbRjYh.js +0 -2
- package/dist/analytics-DXK1gc12.js +0 -301
- package/dist/analytics-DXK1gc12.js.map +0 -1
- package/dist/api-Cm9UtKZh.js +0 -2
- package/dist/api-CrbCIQOz.js.map +0 -1
- package/dist/check-screens.tsx +0 -190
- package/dist/ci-install-COcV8SY2.js.map +0 -1
- package/dist/constants-DHCBSfnx.js +0 -260
- package/dist/constants-DHCBSfnx.js.map +0 -1
- package/dist/debug-BuYHGr6_.js +0 -2
- package/dist/debug-kAHwWGg7.js.map +0 -1
- package/dist/defaults-a9TelDhJ.js.map +0 -1
- package/dist/endpoints-BVMYm9F4.js +0 -108
- package/dist/endpoints-BVMYm9F4.js.map +0 -1
- package/dist/env-api-key-BP-NFFXt.js +0 -17
- package/dist/env-api-key-BP-NFFXt.js.map +0 -1
- package/dist/env-api-key-CaWEDj_9.js +0 -2
- package/dist/environment-BATlQxcQ.js +0 -39
- package/dist/environment-BATlQxcQ.js.map +0 -1
- package/dist/environment-CwD8OPYj.js +0 -2
- package/dist/file-C5JMsyR1.js +0 -2
- package/dist/file-DcRKycV8.js +0 -60
- package/dist/file-DcRKycV8.js.map +0 -1
- package/dist/file-watcher-Brq2nnHV.js +0 -123
- package/dist/file-watcher-Brq2nnHV.js.map +0 -1
- package/dist/gateway-C9-GlLO9.js.map +0 -1
- package/dist/headless-ui-CqI2gXlD.js +0 -44
- package/dist/headless-ui-CqI2gXlD.js.map +0 -1
- package/dist/interactive-B_Ti_CsS.js +0 -11
- package/dist/interactive-B_Ti_CsS.js.map +0 -1
- package/dist/local-dev-McsurOC-.js +0 -135
- package/dist/local-dev-McsurOC-.js.map +0 -1
- package/dist/logging-ui-Dza4D0MK.js +0 -226
- package/dist/logging-ui-Dza4D0MK.js.map +0 -1
- package/dist/main-CgKxbwMP.js.map +0 -1
- package/dist/mcp-prompt-streaming-DDWQ-FCA.js.map +0 -1
- package/dist/mint-failure-u8xQKNOn.js +0 -6392
- package/dist/mint-failure-u8xQKNOn.js.map +0 -1
- package/dist/non-interactive-DyVC1AGc.js +0 -18
- package/dist/non-interactive-DyVC1AGc.js.map +0 -1
- package/dist/package-json-D8Ga03tK.js +0 -2
- package/dist/pi-8KsYsPmM.js.map +0 -1
- package/dist/playground-BlYRBMj7.js.map +0 -1
- package/dist/posthog-Bt0Aplb8.js +0 -124
- package/dist/posthog-Bt0Aplb8.js.map +0 -1
- package/dist/posthog-CUgCEhH0.js +0 -2
- package/dist/provisioning-Bo2NRxFz.js.map +0 -1
- package/dist/provisioning-OGFGlzf7.js +0 -2
- package/dist/queue-tools-C8mXOh7e.js.map +0 -1
- package/dist/registry-C06e_x-p.js +0 -2
- package/dist/registry-CD5RQt38.js +0 -3464
- package/dist/registry-CD5RQt38.js.map +0 -1
- package/dist/run-agent-legacy-ChacZImU.js +0 -2
- package/dist/run-agent-legacy-DOlM6DQu.js +0 -3540
- package/dist/run-agent-legacy-DOlM6DQu.js.map +0 -1
- package/dist/runners-ov3EXF1n.js +0 -834
- package/dist/runners-ov3EXF1n.js.map +0 -1
- package/dist/security-C71pkwqH.js.map +0 -1
- package/dist/setup-utils-B26Lf2yF.js +0 -1512
- package/dist/setup-utils-B26Lf2yF.js.map +0 -1
- package/dist/setup-utils-CVS86NYR.js +0 -2
- package/dist/start-tui-Bo2L0bar.js +0 -6962
- package/dist/start-tui-Bo2L0bar.js.map +0 -1
- package/dist/steps-BPcg3_3Q.js.map +0 -1
- package/dist/store-BTzcvRGT.js +0 -992
- package/dist/store-BTzcvRGT.js.map +0 -1
- package/dist/store-BpqySPXm.js +0 -5
- package/dist/task-DqRlQBfs.js.map +0 -1
- package/dist/task-stream-Bw6JaLCX.js +0 -363
- package/dist/task-stream-Bw6JaLCX.js.map +0 -1
- package/dist/tasks-ChWufsdO.js.map +0 -1
- package/dist/telemetry-B7zJdRmA.js.map +0 -1
- package/dist/terminal-zgkEfoCL.js +0 -7196
- package/dist/terminal-zgkEfoCL.js.map +0 -1
- package/dist/tools-CkJ2y9Wt.js.map +0 -1
- package/dist/ui-Q6-RvERF.js +0 -15
- package/dist/ui-Q6-RvERF.js.map +0 -1
- package/dist/version-CHhJjz_H.js +0 -6
- package/dist/version-CHhJjz_H.js.map +0 -1
- package/dist/wizard-abort-BPdv07np.js.map +0 -1
- package/dist/wizard-abort-Br1qMDNa.js +0 -3
- package/dist/wizard-session-2w4Ru-Wz.js +0 -131
- package/dist/wizard-session-2w4Ru-Wz.js.map +0 -1
- package/dist/wizard-session-DGaUHOF3.js +0 -2
- package/dist/wizard-ui-DVqrYaOM.js +0 -16
- package/dist/wizard-ui-DVqrYaOM.js.map +0 -1
- package/dist/yara-hooks-BnxejtXK.js.map +0 -1
- package/dist/yara-hooks-DSuExwb4.js +0 -2
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PickerMenu-BGGeYqd-.js","names":[],"sources":["../src/tui/styles.ts","../src/tui/primitives/picker-filter.ts","../src/tui/primitives/PromptLabel.tsx","../src/tui/primitives/ConfirmButton.tsx","../src/tui/primitives/content-types.ts","../src/tui/primitives/layout-helpers.ts","../src/tui/hooks/useStdoutDimensions.ts","../src/tui/hooks/keyboard-hints-utils.ts","../src/tui/hooks/useKeyboardHints.tsx","../src/tui/hooks/useKeyBindings.ts","../src/tui/primitives/PickerMenu.tsx"],"sourcesContent":["/**\n * Shared style constants for TUI layout primitives.\n */\n\nexport enum HAlign {\n Left = 'flex-start',\n Center = 'center',\n Right = 'flex-end',\n}\n\nexport enum VAlign {\n Top = 'flex-start',\n Center = 'center',\n Bottom = 'flex-end',\n}\n\nexport const Colors = {\n primary: 'cyan',\n accent: '#DC9300',\n titleColor: '#3D2800',\n success: 'green',\n error: 'red',\n muted: 'gray',\n} as const;\n\nexport const Icons = {\n diamond: '\\u25C6',\n diamondOpen: '\\u25C7',\n check: '\\u2714',\n warning: '\\u26A0',\n squareFilled: '\\u25FC',\n squareOpen: '\\u25FB',\n triangleRight: '\\u25B6',\n triangleSmallRight: '\\u25B8',\n bullet: '\\u2022',\n} as const;\n","/**\n * Filter-as-you-type matching for PickerMenu. Split out from the component so the\n * ranking is testable on its own — the component only decides when to call it.\n */\n\nimport Fuse, { type IFuseOptions } from 'fuse.js';\n\n/** The searchable face of a picker option. PickerMenu's `PickerOption<T>` satisfies it. */\nexport interface Searchable {\n label: string;\n hint?: string;\n description?: string;\n}\n\n/**\n * Fuzzy fallback config. `ignoreLocation` because a term matching at the end of a\n * description is as good as one at the start, and Fuse's default distance window\n * would score the late match away. 0.5 is the loosest threshold that still bottoms\n * out: below it `gthb` misses `GitHub` entirely, which is the whole point of having\n * a fuzzy pass; above it the tail grows without surfacing anything new at the top.\n */\nexport const FUSE_OPTIONS: IFuseOptions<Searchable> = {\n keys: [\n { name: 'label', weight: 3 },\n { name: 'hint', weight: 1 },\n { name: 'description', weight: 1 },\n ],\n ignoreLocation: true,\n threshold: 0.5,\n includeScore: true,\n};\n\nexport function buildPickerIndex<T extends Searchable>(options: T[]): Fuse<T> {\n return new Fuse(options, FUSE_OPTIONS as IFuseOptions<T>);\n}\n\nfunction haystack(option: Searchable): string {\n return `${option.label} ${option.hint ?? ''} ${\n option.description ?? ''\n }`.toLowerCase();\n}\n\n/**\n * Rank options against a query, best first, or `null` for an empty query.\n *\n * Two passes, literal before fuzzy. Anything that literally contains every\n * whitespace-separated term wins outright, in the list's own order — that covers how\n * people actually type (`git iss` → `GitHub issues`) and keeps the common case as\n * tight as a plain substring filter, which fuzzy matching on its own is not: Fuse at\n * a threshold loose enough to forgive `gthb` also lets a third of the list through\n * on `replay`.\n *\n * Only when nothing matches literally does Fuse get a turn, so a typo degrades to a\n * ranked guess instead of an empty list. Terms are AND-ed there too, each matched\n * fuzzily on its own — Fuse's bitap pass over the whole query would otherwise need\n * `git iss` to be a near-edit of the text it's matching. Scores are distances (0 is\n * exact) and sum across terms, hence the ascending sort; ties keep the order the\n * first term put them in, since the sort is stable.\n */\nexport function rankOptions<T extends Searchable>(\n fuse: Fuse<T>,\n options: T[],\n query: string,\n): T[] | null {\n const terms = query.toLowerCase().split(/\\s+/).filter(Boolean);\n if (!terms.length) {\n return null;\n }\n\n const literal = options.filter((option) =>\n terms.every((term) => haystack(option).includes(term)),\n );\n if (literal.length) {\n return literal;\n }\n\n let surviving: Map<T, number> | null = null;\n for (const term of terms) {\n const hits = new Map<T, number>(\n fuse.search(term).map(({ item, score }) => [item, score ?? 0]),\n );\n if (surviving === null) {\n surviving = hits;\n continue;\n }\n // Deleting the current key mid-iteration is well-defined for a Map.\n for (const [option, total] of surviving) {\n const score = hits.get(option);\n if (score === undefined) {\n surviving.delete(option);\n } else {\n surviving.set(option, total + score);\n }\n }\n }\n\n return [...(surviving ?? [])]\n .sort(([, a], [, b]) => a - b)\n .map(([option]) => option);\n}\n","/**\n * PromptLabel — Compact inline label for input prompts.\n *\n * Renders: [!] message\n * where [!] is black text on accent background.\n */\n\nimport { Box, Text } from 'ink';\nimport { Colors } from '@tui/styles';\n\ninterface PromptLabelProps {\n message?: string;\n}\n\nexport const PromptLabel = ({ message }: PromptLabelProps) => {\n // No message → no row, so label-less pickers don't get a stray blank line.\n if (!message) return null;\n return (\n <Box>\n <Text bold color={Colors.accent}>\n {' '}\n {message}\n </Text>\n </Box>\n );\n};\n","/**\n * ConfirmButton — the confirm row used to submit a selection.\n *\n * Pure render. Multi-select menus (PickerMenu mode=\"multi\", GroupedPickerMenu)\n * append this below their options as the final focusable row: the user toggles\n * options with enter, then arrows down onto this row and presses enter to\n * submit. This replaces the older \"enter anywhere submits\" pattern, which\n * confused people who expected enter to toggle the focused item.\n *\n * Renders flat, mirroring the option rows — a focus triangle and the label,\n * accent and bold when focused, dimmed otherwise — so it lines up under the\n * options instead of sitting in a separate boxed target.\n */\n\nimport { Text } from 'ink';\nimport { Icons, Colors } from '@tui/styles';\n\ninterface ConfirmButtonProps {\n /** Button text. Defaults to \"Confirm\". */\n label?: string;\n /** Whether the cursor is currently on the button. */\n focused: boolean;\n /** Optional selected-item count, rendered as \"Label (n)\" when > 0. */\n count?: number;\n}\n\nexport const ConfirmButton = ({\n label = 'Confirm',\n focused,\n count,\n}: ConfirmButtonProps) => {\n const text = count && count > 0 ? `${label} (${count})` : label;\n return (\n <Text\n color={focused ? Colors.accent : undefined}\n bold={focused}\n dimColor={!focused}\n >\n {focused ? Icons.triangleSmallRight : ' '} {text}\n </Text>\n );\n};\n","/**\n * Content block types and type guards.\n *\n * Extracted from ContentSequencer so that pure-logic modules (like\n * layout-helpers) can import them without pulling in Ink/React.\n */\n\nimport type { ReactNode } from 'react';\nimport type { TextRevealMode } from './TextBlock.js';\n\n/** Object form — string or ReactNode content with per-block overrides. */\nexport interface ContentObjectBlock {\n content: string | ReactNode;\n mode?: TextRevealMode;\n animationInterval?: number;\n sentenceInterval?: number;\n pause?: number;\n persist?: boolean;\n /**\n * When the sequencer advances past this block, should it render dim?\n * Defaults to `true` — the standard \"completed step\" treatment. Set to\n * `false` to keep the block at full opacity even after it's complete\n * (useful for headings or greetings that should stay readable while\n * later blocks animate in).\n *\n * Only meaningful for blocks whose `content` is a string (TextBlock\n * renders the dim treatment). NodeBlocks render whatever JSX you pass\n * verbatim regardless of completion state.\n */\n dimWhenComplete?: boolean;\n}\n\n/** Lines block — reveals ReactNode lines one at a time. */\nexport interface ContentLinesBlock {\n type: 'lines';\n lines: ReactNode[];\n interval?: number;\n pause?: number;\n}\n\n/** Clear block — page break that hides all prior blocks. */\nexport interface ContentClearBlock {\n type: 'clear';\n pause?: number;\n}\n\n/** A content block in the sequence. Bare strings are sugar for { content: '...' }. */\nexport type ContentBlock =\n | string\n | ContentObjectBlock\n | ContentLinesBlock\n | ContentClearBlock;\n\n/** Type guard for lines blocks. */\nexport function isLinesBlock(block: ContentBlock): block is ContentLinesBlock {\n return typeof block !== 'string' && 'type' in block && block.type === 'lines';\n}\n\n/** Type guard for clear blocks. */\nexport function isClearBlock(block: ContentBlock): block is ContentClearBlock {\n return typeof block !== 'string' && 'type' in block && block.type === 'clear';\n}\n\n/** Type guard for object blocks (text or node content). */\nexport function isObjectBlock(\n block: ContentBlock,\n): block is ContentObjectBlock {\n return typeof block !== 'string' && !('type' in block);\n}\n","/**\n * Layout helpers — pure functions for height estimation and viewport eviction.\n *\n * These are the core of the responsive content system. They estimate how many\n * terminal rows a content block will occupy and determine which blocks fit\n * within a given height budget.\n */\n\nimport type { ContentBlock } from './content-types.js';\nimport { isLinesBlock, isClearBlock, isObjectBlock } from './content-types.js';\n\n/**\n * Estimate the number of terminal rows a content block will occupy,\n * including 1 row of marginBottom.\n */\nexport function estimateBlockHeight(\n block: ContentBlock,\n availableWidth: number,\n): number {\n if (typeof block === 'string') {\n return wordWrap(block, availableWidth).length + 1; // +1 for marginBottom\n }\n\n if (isClearBlock(block)) return 0;\n\n if (isLinesBlock(block)) {\n return block.lines.length + 1;\n }\n\n if (isObjectBlock(block)) {\n if (typeof block.content === 'string') {\n return wordWrap(block.content, availableWidth).length + 1;\n }\n return 4; // conservative fixed estimate for ReactNode\n }\n\n return 1;\n}\n\n/**\n * Given all blocks, the active index, available width, and a row budget,\n * return [startIdx, endIdx] — the range of blocks to render.\n *\n * Always includes activeIdx. Walks backward to include as many completed\n * blocks as fit within maxHeight.\n */\nexport function computeVisibleRange(\n blocks: ContentBlock[],\n activeIdx: number,\n availableWidth: number,\n maxHeight: number,\n): [number, number] {\n // Reserve a 2-row buffer so resize-induced estimate drift doesn't\n // cause overflow=\"hidden\" to clip the margin between blocks.\n const budget = Math.max(4, maxHeight - 2);\n\n let totalHeight = estimateBlockHeight(blocks[activeIdx], availableWidth);\n let start = activeIdx;\n\n for (let i = activeIdx - 1; i >= 0; i--) {\n // Clear blocks act as a hard boundary — don't show anything before them\n if (isClearBlock(blocks[i])) break;\n const h = estimateBlockHeight(blocks[i], availableWidth);\n if (totalHeight + h > budget) break;\n totalHeight += h;\n start = i;\n }\n\n return [start, activeIdx];\n}\n\n/**\n * Word-wrap text at clean word boundaries. Always returns pre-wrapped text\n * joined with \\n — this avoids Ink's native wrap which can leave leading\n * spaces on continuation lines.\n *\n * Uses a 1-char safety margin so slight width estimate mismatches don't\n * cause Ink to re-wrap our already-wrapped lines.\n */\nexport function wordWrap(text: string, availableWidth: number): string[] {\n const safeWidth = Math.max(10, availableWidth - 1);\n const words = text.split(/\\s+/);\n const lines: string[] = [];\n let currentLine = '';\n\n for (const word of words) {\n if (\n currentLine.length + word.length + 1 > safeWidth &&\n currentLine.length > 0\n ) {\n lines.push(currentLine);\n currentLine = word;\n } else {\n currentLine += (currentLine.length > 0 ? ' ' : '') + word;\n }\n }\n if (currentLine.length > 0) {\n lines.push(currentLine);\n }\n\n return lines;\n}\n\n/**\n * Word-wrap text and return only the last `maxRows` lines.\n * Used for intra-block truncation when a single text block exceeds the viewport.\n * Also used for normal rendering to avoid Ink's leading-space wrap artifacts.\n */\nexport function wrapAndTruncate(\n text: string,\n availableWidth: number,\n maxRows: number,\n): string {\n const lines = wordWrap(text, availableWidth);\n\n if (lines.length <= maxRows) {\n return lines.join('\\n');\n }\n\n return lines.slice(-maxRows).join('\\n');\n}\n","/**\n * useStdoutDimensions — Returns [columns, rows] and re-renders on terminal resize.\n *\n * Ink's useStdout() does not subscribe to resize events, so layout only updates\n * when something else causes a re-render. This hook listens to the stream's\n * 'resize' event (Node TTY) and updates state so the component re-renders\n * with the new dimensions.\n */\n\nimport { useStdout } from 'ink';\nimport { useState, useEffect } from 'react';\n\nexport function useStdoutDimensions(): [number, number] {\n const { stdout } = useStdout();\n const [size, setSize] = useState<[number, number]>(() => [\n stdout.columns || 80,\n stdout.rows || 24,\n ]);\n\n useEffect(() => {\n const cols = stdout.columns || 80;\n const rows = stdout.rows || 24;\n setSize([cols, rows]);\n\n const stream = stdout as NodeJS.WriteStream & {\n on?(event: string, fn: () => void): void;\n };\n if (typeof stream.on !== 'function') return;\n\n const onResize = () => {\n const c = stdout.columns || 80;\n const r = stdout.rows || 24;\n if (c > 0 && r > 0) setSize([c, r]);\n };\n stream.on('resize', onResize);\n return () => {\n stream.off?.('resize', onResize);\n };\n }, [stdout]);\n\n return size;\n}\n","/**\n * Pure utility functions for keyboard hints — no React or Ink dependencies.\n * Extracted for testability in a node Jest environment.\n */\n\nexport interface KeyboardHint {\n label: string;\n action: string;\n priority: number;\n}\n\n/** Well-known key matches corresponding to Ink's key.* properties. */\nexport enum KeyMatch {\n UpArrow = 'upArrow',\n DownArrow = 'downArrow',\n LeftArrow = 'leftArrow',\n RightArrow = 'rightArrow',\n Return = 'return',\n Escape = 'escape',\n Space = 'space',\n Backspace = 'backspace',\n /**\n * Any single printable character typed without a modifier. Broad by design,\n * so a binding using it must sit last — the first match wins, which is what\n * lets a specific char binding still take precedence over free typing.\n */\n Printable = 'printable',\n}\n\n/** A key match: either a KeyMatch enum value or a literal character string (e.g. 'a', 's'). */\nexport type KeyMatchOrChar = KeyMatch | (string & NonNullable<unknown>);\n\n/** Default priorities by key type. */\nconst DEFAULT_PRIORITY: Record<string, number> = {\n [KeyMatch.UpArrow]: 0,\n [KeyMatch.DownArrow]: 0,\n [KeyMatch.LeftArrow]: 1,\n [KeyMatch.RightArrow]: 1,\n [KeyMatch.Space]: 10,\n [KeyMatch.Printable]: 12,\n [KeyMatch.Backspace]: 13,\n [KeyMatch.Escape]: 20,\n [KeyMatch.Return]: 21,\n};\n\n/** Get the default display priority for a key match. */\nexport function getDefaultPriority(\n match: KeyMatchOrChar | KeyMatchOrChar[],\n): number {\n const first = Array.isArray(match) ? match[0] : match;\n return DEFAULT_PRIORITY[first] ?? 15;\n}\n\n/** Check if a KeyMatchOrChar matches the given input string and key flags. */\nexport function matchesKey(\n m: KeyMatchOrChar,\n input: string,\n key: { [k: string]: unknown },\n): boolean {\n switch (m) {\n case KeyMatch.UpArrow:\n return !!key.upArrow;\n case KeyMatch.DownArrow:\n return !!key.downArrow;\n case KeyMatch.LeftArrow:\n return !!key.leftArrow;\n case KeyMatch.RightArrow:\n return !!key.rightArrow;\n case KeyMatch.Return:\n return !!key.return;\n case KeyMatch.Escape:\n return !!key.escape;\n case KeyMatch.Space:\n return input === ' ';\n case KeyMatch.Backspace:\n return !!key.backspace || !!key.delete;\n case KeyMatch.Printable:\n // Ink sets a flag for every non-text key while still passing a non-empty\n // `input`, so any flag but shift rules out typing.\n // Any single non-control character, not just ASCII — a filter query can\n // hold an accented or non-Latin letter.\n return (\n input.length === 1 &&\n input >= ' ' &&\n input !== '\\x7f' &&\n Object.entries(key).every(([flag, on]) => !on || flag === 'shift')\n );\n default:\n return input === m;\n }\n}\n\n/** Serialize hints for comparison. */\nexport function hintsKey(hints: KeyboardHint[]): string {\n return hints.map((h) => `${h.label}:${h.action}`).join('|');\n}\n\n/** Deduplicate hints by label+action and sort by priority. */\nexport function deduplicateAndSort(hints: KeyboardHint[]): KeyboardHint[] {\n const seen = new Set<string>();\n const deduped: KeyboardHint[] = [];\n for (const hint of hints) {\n const k = `${hint.label}:${hint.action}`;\n if (!seen.has(k)) {\n seen.add(k);\n deduped.push(hint);\n }\n }\n deduped.sort((a, b) => a.priority - b.priority);\n return deduped;\n}\n","/**\n * KeyboardHintsProvider — Context for collecting and displaying keyboard hints.\n *\n * Input components register their hints via useKeyBindings. The provider\n * flattens, deduplicates, and sorts them. The hints bar stays visible for as\n * long as a screen has registered hints — it never auto-dismisses.\n */\n\nimport {\n createContext,\n useCallback,\n useContext,\n useRef,\n useState,\n type ReactNode,\n} from 'react';\nimport {\n hintsKey,\n deduplicateAndSort,\n type KeyboardHint,\n} from './keyboard-hints-utils.js';\n\nexport type { KeyboardHint } from './keyboard-hints-utils.js';\n\ninterface KeyboardHintsContextValue {\n register(id: string, hints: KeyboardHint[]): void;\n unregister(id: string): void;\n hints: KeyboardHint[];\n}\n\nconst KeyboardHintsContext = createContext<KeyboardHintsContextValue>({\n register: () => undefined,\n unregister: () => undefined,\n hints: [],\n});\n\nexport const useKeyboardHintsContext = () => useContext(KeyboardHintsContext);\n\nexport const KeyboardHintsProvider = ({\n children,\n}: {\n children: ReactNode;\n}) => {\n const registrationsRef = useRef(new Map<string, KeyboardHint[]>());\n const [hints, setHints] = useState<KeyboardHint[]>([]);\n const prevHintsKeyRef = useRef('');\n\n const recompute = useCallback(() => {\n const all: KeyboardHint[] = [];\n for (const h of registrationsRef.current.values()) {\n all.push(...h);\n }\n const deduped = deduplicateAndSort(all);\n\n const newKey = hintsKey(deduped);\n if (newKey !== prevHintsKeyRef.current) {\n prevHintsKeyRef.current = newKey;\n setHints(deduped);\n }\n }, []);\n\n const register = useCallback(\n (id: string, h: KeyboardHint[]) => {\n registrationsRef.current.set(id, h);\n recompute();\n },\n [recompute],\n );\n\n const unregister = useCallback(\n (id: string) => {\n registrationsRef.current.delete(id);\n recompute();\n },\n [recompute],\n );\n\n return (\n <KeyboardHintsContext.Provider value={{ register, unregister, hints }}>\n {children}\n </KeyboardHintsContext.Provider>\n );\n};\n","/**\n * useKeyBindings — Declarative keyboard input + automatic hint registration.\n *\n * Replaces raw `useInput` in input components. Define bindings as data;\n * the hook wires up the Ink input handler AND registers hints in the\n * KeyboardHintsProvider. One source of truth for keys and their labels.\n */\n\nimport { useInput, type Key } from 'ink';\nimport { useEffect, useRef } from 'react';\nimport { useKeyboardHintsContext } from './useKeyboardHints.js';\nimport {\n matchesKey,\n getDefaultPriority,\n KeyMatch,\n type KeyboardHint,\n type KeyMatchOrChar,\n} from './keyboard-hints-utils.js';\n\nexport { KeyMatch };\nexport type { KeyMatchOrChar } from './keyboard-hints-utils.js';\n\nexport interface KeyBinding {\n /** Which key(s) trigger this binding. Array = multiple keys, one hint. */\n match: KeyMatchOrChar | KeyMatchOrChar[];\n /** Display label in hints bar (e.g. \"↑↓\", \"space\", \"enter\") */\n label: string;\n /** Description in hints bar (e.g. \"navigate\", \"toggle\") */\n action: string;\n /** Ordering priority (lower = further left). Defaults based on key type. */\n priority?: number;\n /** Handler called when the key matches. */\n handler: (input: string, key: Key) => void;\n}\n\n/**\n * Declarative key bindings hook. Replaces `useInput` in input components.\n * Registers hints automatically with the KeyboardHintsProvider.\n *\n * @param id Unique identifier for this component's hints registration\n * @param bindings Array of key binding definitions\n */\nexport function useKeyBindings(id: string, bindings: KeyBinding[]): void {\n const ctx = useKeyboardHintsContext();\n\n // Extract hints and register. Use a serialized key to avoid unnecessary updates.\n const hintsRef = useRef<string>('');\n const hints: KeyboardHint[] = bindings.map((b) => ({\n label: b.label,\n action: b.action,\n priority: b.priority ?? getDefaultPriority(b.match),\n }));\n const serialized = hints\n .map((h) => `${h.label}:${h.action}:${h.priority}`)\n .join('|');\n\n useEffect(() => {\n if (serialized !== hintsRef.current) {\n hintsRef.current = serialized;\n ctx.register(id, hints);\n }\n return () => ctx.unregister(id);\n // eslint-disable-next-line\n }, [id, serialized]);\n\n // Wire up input handling\n useInput((input, key) => {\n for (const binding of bindings) {\n const matches = Array.isArray(binding.match)\n ? binding.match\n : [binding.match];\n if (matches.some((m) => matchesKey(m, input, key))) {\n binding.handler(input, key);\n return;\n }\n }\n });\n}\n","/**\n * PickerMenu — Single and multi select.\n * Single mode: custom renderer with small triangle indicator; enter selects.\n * Multi mode: checkbox glyphs toggled with enter, plus a focusable\n * Confirm button below the options. The cursor moves onto the button and\n * enter submits — see MultiPickerMenu for the rationale.\n *\n * Key bindings are declared via useKeyBindings, which auto-registers\n * hints in the KeyboardHintsBar.\n */\n\nimport { Box, Text } from 'ink';\nimport { useEffect, useMemo, useState } from 'react';\nimport { buildPickerIndex, rankOptions } from './picker-filter.js';\nimport { Icons, Colors } from '@tui/styles';\nimport { PromptLabel } from './PromptLabel.js';\nimport { ConfirmButton } from './ConfirmButton.js';\nimport { wordWrap } from './layout-helpers.js';\nimport { useStdoutDimensions } from '@tui/hooks/useStdoutDimensions';\nimport {\n useKeyBindings,\n KeyMatch,\n type KeyBinding,\n type KeyMatchOrChar,\n} from '@tui/hooks/useKeyBindings';\n\nexport interface PickerOption<T> {\n label: string;\n value: T;\n hint?: string;\n /**\n * Multi-select only: a secondary explanation rendered dimmed and wrapped on\n * its own line(s) beneath the label, for choices that need more than a title.\n * When unset, the row renders exactly as a label-only row.\n */\n description?: string;\n /** Glyph rendered before the label, in its own color — unaffected by\n * focus and disabled styling. */\n icon?: { glyph: string; color?: string };\n /** Dimmed and unselectable; navigation skips over it. */\n disabled?: boolean;\n /**\n * Section heading: unselectable (navigation skips it, like `disabled`) but\n * rendered as a bold header rather than a muted row. Set `disabled` too so\n * the skip logic treats it as non-selectable.\n */\n header?: boolean;\n /** Indent the label one level, e.g. items nested under a `header` row. */\n indent?: boolean;\n /**\n * Multi-select only: marks this option mutually exclusive with every other\n * option. Selecting it clears all other picks; selecting any non-exclusive\n * option clears it. Used e.g. for a browser connector that can't be\n * installed alongside local editors.\n */\n exclusive?: boolean;\n}\n\n/**\n * Step through a column's options in `dir`, wrapping, until an enabled\n * option is found. Returns `from` unchanged if the column is entirely\n * disabled.\n */\nfunction stepEnabled<T>(\n options: PickerOption<T>[],\n rows: number,\n from: number,\n dir: 1 | -1,\n): number {\n const col = Math.floor(from / rows);\n const colStart = col * rows;\n const colLen = Math.min(rows, options.length - colStart);\n let row = from % rows;\n for (let i = 0; i < colLen; i++) {\n row = (row + dir + colLen) % colLen;\n const idx = colStart + row;\n if (!options[idx]?.disabled) return idx;\n }\n return from;\n}\n\n/** First enabled option in [start, end), for initial focus and for entering\n * a page from the Confirm button. */\nfunction firstEnabled<T>(\n options: PickerOption<T>[],\n start = 0,\n end = options.length,\n): number {\n for (let i = start; i < end; i++) {\n if (!options[i]?.disabled) return i;\n }\n return start;\n}\n\n/** Last enabled option in [start, end), for wrapping from the button onto\n * the bottom of the page. */\nfunction lastEnabled<T>(\n options: PickerOption<T>[],\n start = 0,\n end = options.length,\n): number {\n for (let i = end - 1; i >= start; i--) {\n if (!options[i]?.disabled) return i;\n }\n return end - 1;\n}\n\n/**\n * Rows the surrounding screen or overlay consumes above and below a picker —\n * border + padding, title, prompt text, and the keyboard-hints bar. A\n * deliberately generous estimate: overshooting just shows a few fewer rows,\n * whereas undershooting lets a long list overflow the viewport (the bug this\n * windowing guards against). Mirrors GroupedPickerMenu's budgeting.\n */\nconst CHROME_OVERHEAD = 13;\n/**\n * Max visual rows a picker renders regardless of terminal height. Without a\n * ceiling a tall terminal lets a long list fill the whole viewport, which\n * reads as a wall of options; ~12 rows keeps the menu scannable and leaves\n * known breathing room above and below.\n */\nconst MAX_LIST_ROWS = 12;\n/** Extra rows a multi-select adds below its options: marginTop + Confirm button. */\nconst CONFIRM_CHROME = 3;\n/** Lists shorter than this never page — tall rows would split 2-3 options into pages of one. */\nconst MIN_COUNT_TO_PAGE = 5;\n/** Width the multi-select wraps option descriptions to (matches the render). */\nconst DESCRIPTION_WIDTH = 56;\n\ninterface PickerViewport {\n needsScroll: boolean;\n /** First option index on the current page. */\n start: number;\n /** One past the last option index on the current page. */\n end: number;\n hiddenAbove: number;\n hiddenBelow: number;\n /** Focus target one page over (wrapping), for the n/p keys. */\n pageStep: (focused: number, dir: 1 | -1) => number;\n}\n\n/**\n * Pages a single-column option list to the terminal height. The visible page\n * is derived from the focused index — no scroll state — so ↑/↓ flip pages as\n * focus crosses a page edge and n/p jump a whole page. Pages hold a fixed\n * option count sized to the tallest row (`rowCost`), trading a sparser page\n * on mixed-height lists for arithmetic-only paging. Engages only for\n * single-column pickers — multi-column grids already compress vertically.\n */\nfunction usePickerViewport(\n count: number,\n rowCost: number,\n chromeBelow: number,\n enabled: boolean,\n focused: number,\n): PickerViewport {\n const [, termRows] = useStdoutDimensions();\n const budget = Math.max(\n 5,\n Math.min(termRows - CHROME_OVERHEAD - chromeBelow, MAX_LIST_ROWS),\n );\n const needsScroll =\n enabled && count >= MIN_COUNT_TO_PAGE && count * rowCost > budget;\n // Reserve two rows for the \"↑/↓ N more\" indicators.\n const perPage = needsScroll\n ? Math.max(1, Math.floor((budget - 2) / rowCost))\n : count;\n const pageCount = Math.max(1, Math.ceil(count / perPage));\n const start = Math.floor(focused / perPage) * perPage;\n const end = Math.min(start + perPage, count);\n return {\n needsScroll,\n start,\n end,\n hiddenAbove: start,\n hiddenBelow: count - end,\n pageStep: (f, dir) =>\n ((Math.floor(f / perPage) + dir + pageCount) % pageCount) * perPage,\n };\n}\n\ninterface PickerMenuProps<T> {\n message?: string;\n options: PickerOption<T>[];\n mode?: 'single' | 'multi';\n centered?: boolean;\n columns?: 1 | 2 | 3 | 4;\n /**\n * Vertical space between options, in TUI rows. Defaults to 0 — i.e.\n * options stack tightly. Set to 1+ when the option labels are long\n * (wrap across multiple lines) or for visual breathing room.\n */\n optionMarginBottom?: number;\n /**\n * Filter-as-you-type. Defaults on once the list is long enough to page, where\n * finding one option means walking pages. Pass `false` to force it off.\n */\n filterable?: boolean;\n onSelect: (value: T | T[]) => void;\n}\n\n/** Lists at least this long get filtering. Shorter ones fit on a page or two,\n * where a filter row costs more rows than it saves. */\nconst FILTER_MIN_OPTIONS = 10;\n\nexport const PickerMenu = <T,>({\n message,\n options,\n mode = 'single',\n centered = false,\n columns = 1,\n optionMarginBottom = 0,\n filterable,\n onSelect,\n}: PickerMenuProps<T>) => {\n const canFilter = filterable ?? options.length >= FILTER_MIN_OPTIONS;\n const [filter, setFilter] = useState('');\n const fuse = useMemo(\n () => (canFilter ? buildPickerIndex(options) : null),\n [options, canFilter],\n );\n const visible = useMemo(\n () => (fuse ? rankOptions(fuse, options, filter) ?? options : options),\n [fuse, options, filter],\n );\n\n // Keyed by label, and owned here rather than by MultiPickerMenu: filtering\n // rewrites the list under the ticks, and the remount below would drop them.\n const [selected, setSelected] = useState<Set<string>>(new Set());\n\n const shared = {\n message,\n options: visible,\n centered,\n optionMarginBottom,\n onSelect,\n filter: canFilter ? filter : null,\n setFilter,\n totalCount: options.length,\n };\n\n // A filtered list is a fresh list with its own focus and paging, so remount on\n // each query rather than leaving stale indices to be re-validated.\n const key = String(filter);\n\n if (mode === 'multi') {\n return (\n <MultiPickerMenu\n key={key}\n {...shared}\n columns={columns}\n allOptions={options}\n selected={selected}\n setSelected={setSelected}\n />\n );\n }\n\n return <SinglePickerMenu key={key} {...shared} columns={columns} />;\n};\n\n/**\n * Backspace and free typing. Append these last: `Printable` matches almost any\n * key, and bindings resolve in order, so anything specific has to be registered\n * first to keep working. Both share a label/action so the hints bar shows one.\n */\nfunction filterBindings(\n filter: string,\n setFilter: (query: string) => void,\n): KeyBinding[] {\n return [\n {\n match: KeyMatch.Backspace,\n label: 'type',\n action: 'filter',\n handler: () => setFilter(filter.slice(0, -1)),\n },\n {\n match: KeyMatch.Printable,\n label: 'type',\n action: 'filter',\n handler: (input: string) => setFilter(filter + input),\n },\n ];\n}\n\n/** The filter row above a filterable list: what's typed, and how much it left. */\nconst FilterRow = ({\n filter,\n shown,\n total,\n}: {\n filter: string;\n shown: number;\n total: number;\n}) => (\n // Indented to sit under PromptLabel, which carries a leading space.\n <Box paddingLeft={1}>\n <Text dimColor>\n {filter\n ? `Filter: ${filter} (${shown} of ${total})`\n : `Type to filter ${total} options`}\n </Text>\n </Box>\n);\n\n/** Custom single-select with triangle indicator and accent highlight. */\nconst SinglePickerMenu = <T,>({\n message,\n options,\n centered = false,\n columns = 1,\n optionMarginBottom = 0,\n filter,\n setFilter,\n totalCount,\n onSelect,\n}: {\n message?: string;\n options: PickerOption<T>[];\n centered?: boolean;\n columns?: number;\n optionMarginBottom?: number;\n /** Current filter query, or null when this picker isn't filterable. */\n filter: string | null;\n setFilter: (query: string) => void;\n totalCount: number;\n onSelect: (value: T | T[]) => void;\n}) => {\n const [focused, setFocused] = useState(() => firstEnabled(options));\n const rows = Math.ceil(options.length / columns);\n // Single-select rows are label-only (no descriptions): one line plus margin.\n const viewport = usePickerViewport(\n options.length,\n 1 + optionMarginBottom,\n filter !== null ? 1 : 0,\n columns === 1,\n focused,\n );\n\n // Re-validate focus when the options change while mounted \\u2014 a list\n // that shrinks or disables entries can leave `focused` pointing at a\n // missing or disabled option, which would make enter a no-op.\n useEffect(() => {\n if (focused >= options.length || options[focused]?.disabled) {\n setFocused(firstEnabled(options));\n }\n }, [options, focused]);\n\n const bindings: KeyBinding[] = [\n {\n match: [KeyMatch.UpArrow, KeyMatch.DownArrow],\n label: '\\u2191\\u2193',\n action: 'navigate',\n handler: (_input, key) => {\n const dir = key.upArrow ? -1 : 1;\n // `start`/`end` derive from `focused`, so stepping past a page edge\n // flips the page rather than needing a bound here.\n setFocused(stepEnabled(options, rows, focused, dir));\n },\n },\n // Only on a non-filterable list. Once a filter row exists `n` and `p` are\n // characters, not commands — so they go for good, not just once a query is\n // typed, and the arrows carry paging instead by walking the whole list.\n ...(viewport.needsScroll && filter === null\n ? [\n {\n match: ['n', 'p'] as KeyMatchOrChar[],\n label: 'n/p',\n action: 'page',\n handler: (input: string) => {\n const target = viewport.pageStep(focused, input === 'n' ? 1 : -1);\n setFocused(\n options[target]?.disabled\n ? stepEnabled(options, rows, target, 1)\n : target,\n );\n },\n },\n ]\n : []),\n {\n match: KeyMatch.Return,\n label: 'enter',\n action: 'select',\n handler: () => {\n const selected = options[focused];\n if (selected && !selected.disabled) {\n onSelect(selected.value);\n }\n },\n },\n ];\n\n if (columns > 1) {\n bindings.splice(1, 0, {\n match: [KeyMatch.LeftArrow, KeyMatch.RightArrow],\n label: '\\u2190\\u2192',\n action: 'navigate',\n handler: (_input, key) => {\n const col = Math.floor(focused / rows);\n const row = focused % rows;\n\n let next = focused;\n if (key.leftArrow) {\n const prevCol = col > 0 ? col - 1 : columns - 1;\n next = Math.min(prevCol * rows + row, options.length - 1);\n }\n if (key.rightArrow) {\n const nextCol = col < columns - 1 ? col + 1 : 0;\n next = Math.min(nextCol * rows + row, options.length - 1);\n }\n // Landing on a disabled option slides to the column's nearest\n // enabled one.\n if (options[next]?.disabled) {\n next = stepEnabled(options, rows, next, 1);\n }\n setFocused(next);\n },\n });\n }\n\n if (filter !== null) {\n bindings.push(...filterBindings(filter, setFilter));\n }\n\n useKeyBindings('single-picker', bindings);\n\n // Chunk options into columns (column-first ordering)\n const columnArrays: PickerOption<T>[][] = [];\n for (let c = 0; c < columns; c++) {\n columnArrays.push(options.slice(c * rows, c * rows + rows));\n }\n\n const align = centered ? 'center' : undefined;\n\n const renderOption = (opt: PickerOption<T>, flatIdx: number) => {\n const isFocused = flatIdx === focused;\n const base = opt.hint ? `${opt.label} (${opt.hint})` : opt.label;\n const label = opt.indent ? ` ${base}` : base;\n return (\n <Box key={flatIdx} gap={1} marginBottom={optionMarginBottom}>\n <Text\n color={isFocused ? Colors.accent : undefined}\n dimColor={!isFocused}\n >\n {isFocused && !opt.header ? Icons.triangleSmallRight : ' '}\n </Text>\n {opt.icon && <Text color={opt.icon.color}>{opt.icon.glyph}</Text>}\n <Text\n color={\n opt.header\n ? undefined\n : opt.disabled\n ? Colors.muted\n : isFocused\n ? Colors.accent\n : undefined\n }\n bold={opt.header || (isFocused && !opt.disabled)}\n dimColor={!opt.header && (!isFocused || opt.disabled)}\n >\n {label}\n </Text>\n </Box>\n );\n };\n\n return (\n <Box flexDirection=\"column\" alignItems={align}>\n <PromptLabel message={message} />\n {filter !== null && (\n <FilterRow filter={filter} shown={options.length} total={totalCount} />\n )}\n {filter && options.length === 0 ? (\n <Text dimColor>No options match. Backspace to widen the filter.</Text>\n ) : viewport.needsScroll ? (\n <Box flexDirection=\"column\">\n <Text dimColor>\n {viewport.hiddenAbove > 0\n ? `↑ ${viewport.hiddenAbove} more${\n filter === null ? ' [P] for previous page' : ''\n }`\n : ' '}\n </Text>\n {options\n .slice(viewport.start, viewport.end)\n .map((opt, relIdx) => renderOption(opt, viewport.start + relIdx))}\n <Text dimColor>\n {viewport.hiddenBelow > 0\n ? `↓ ${viewport.hiddenBelow} more${\n filter === null ? ' [N] for next page' : ''\n }`\n : ' '}\n </Text>\n </Box>\n ) : (\n <Box flexDirection=\"row\" gap={4}>\n {columnArrays.map((colOpts, colIdx) => (\n <Box key={colIdx} flexDirection=\"column\">\n {colOpts.map((opt, rowIdx) =>\n renderOption(opt, colIdx * rows + rowIdx),\n )}\n </Box>\n ))}\n </Box>\n )}\n </Box>\n );\n};\n\n/**\n * Custom multi-select with checkbox glyphs and accent highlight.\n *\n * Interaction model (shared with GroupedPickerMenu):\n * - \\u2191\\u2193 move the cursor through the options AND onto the Confirm button,\n * which lives just past the last option.\n * - enter toggles the focused option (no more \"space toggles but enter\n * advances\" split that tripped people up). Space is kept as an\n * undocumented alias, but the hints bar advertises only enter.\n * - moving onto the Confirm button and pressing enter submits the\n * current selection.\n */\nconst MultiPickerMenu = <T,>({\n message,\n options,\n centered = false,\n columns = 1,\n optionMarginBottom = 0,\n filter,\n setFilter,\n totalCount,\n allOptions,\n selected,\n setSelected,\n onSelect,\n}: {\n message?: string;\n options: PickerOption<T>[];\n centered?: boolean;\n columns?: number;\n optionMarginBottom?: number;\n /** Current filter query, or null when this picker isn't filterable. */\n filter: string | null;\n setFilter: (query: string) => void;\n totalCount: number;\n /** Every option, filtered or not. Ticks outlive the query, so resolving and\n * submitting them has to happen against the full list. */\n allOptions: PickerOption<T>[];\n selected: Set<string>;\n setSelected: (update: (prev: Set<string>) => Set<string>) => void;\n onSelect: (value: T | T[]) => void;\n}) => {\n const [focused, setFocused] = useState(() => firstEnabled(options));\n // When true, the cursor is on the Confirm button rather than an option.\n const [onButton, setOnButton] = useState(false);\n const rows = Math.ceil(options.length / columns);\n // A row is its label line plus any margin; a description adds one line per\n // wrapped line beneath the label. Pages size to the tallest row.\n const rowCost = options.reduce(\n (max, opt) =>\n Math.max(\n max,\n 1 +\n optionMarginBottom +\n (opt.description\n ? wordWrap(opt.description, DESCRIPTION_WIDTH).length\n : 0),\n ),\n 1,\n );\n const viewport = usePickerViewport(\n options.length,\n rowCost,\n CONFIRM_CHROME + (filter !== null ? 1 : 0),\n columns === 1,\n focused,\n );\n\n // Re-validate focus when the options change while mounted — a list\n // that shrinks or disables entries can leave `focused` pointing at a\n // missing or disabled option, which would make enter a no-op.\n useEffect(() => {\n if (focused >= options.length || options[focused]?.disabled) {\n setFocused(firstEnabled(options));\n }\n }, [options, focused]);\n\n const confirm = () => {\n // In `allOptions` order, so a pick made under one filter still submits\n // after the query moved on.\n const values = allOptions\n .filter((option) => selected.has(option.label))\n .map((option) => option.value);\n onSelect(values);\n };\n\n const bindings: KeyBinding[] = [\n {\n match: [KeyMatch.UpArrow, KeyMatch.DownArrow],\n label: '\\u2191\\u2193',\n action: 'navigate',\n handler: (_input, key) => {\n const dir = key.upArrow ? -1 : 1;\n if (onButton) {\n // Button \\u2192 back onto the current page (bottom for \\u2191, top for \\u2193).\n setOnButton(false);\n setFocused(\n dir === -1\n ? lastEnabled(options, viewport.start, viewport.end)\n : firstEnabled(options, viewport.start, viewport.end),\n );\n return;\n }\n const col = Math.floor(focused / rows);\n const row = focused % rows;\n const colLen = Math.min(rows, options.length - col * rows);\n // Nearest enabled option above/below in this column; leaving the\n // column lands on the button.\n let r = row + dir;\n while (r >= 0 && r < colLen && options[col * rows + r]?.disabled) {\n r += dir;\n }\n if (r >= 0 && r < colLen) {\n setFocused(col * rows + r);\n } else {\n setOnButton(true);\n }\n },\n },\n // Only on a non-filterable list. Once a filter row exists `n` and `p` are\n // characters, not commands — so they go for good, not just once a query is\n // typed, and the arrows carry paging instead by walking the whole list.\n ...(viewport.needsScroll && filter === null\n ? [\n {\n match: ['n', 'p'] as KeyMatchOrChar[],\n label: 'n/p',\n action: 'page',\n handler: (input: string) => {\n const target = viewport.pageStep(focused, input === 'n' ? 1 : -1);\n setOnButton(false);\n setFocused(\n options[target]?.disabled\n ? stepEnabled(options, rows, target, 1)\n : target,\n );\n },\n },\n ]\n : []),\n {\n // Space is only an alias for enter when there's no filter to type into.\n match:\n filter !== null ? KeyMatch.Return : [KeyMatch.Space, KeyMatch.Return],\n label: 'enter',\n action: 'select',\n handler: () => {\n if (onButton) {\n confirm();\n return;\n }\n // `focused` can point past the end: filtering to zero matches leaves\n // nothing under the cursor, and enter is the natural thing to try there.\n const option = options[focused];\n if (!option || option.disabled) return;\n const label = option.label;\n setSelected((prev) => {\n const next = new Set(prev);\n if (next.has(label)) {\n next.delete(label);\n return next;\n }\n // Enforce mutual exclusivity: an exclusive option clears every other\n // pick; any other option clears previously-picked exclusive ones.\n if (option.exclusive) {\n return new Set([label]);\n }\n // Against `allOptions`: the pick being cleared may be off-screen\n // under the current query.\n for (const picked of next) {\n if (allOptions.find((o) => o.label === picked)?.exclusive) {\n next.delete(picked);\n }\n }\n next.add(label);\n return next;\n });\n },\n },\n ];\n\n if (columns > 1) {\n bindings.splice(1, 0, {\n match: [KeyMatch.LeftArrow, KeyMatch.RightArrow],\n label: '\\u2190\\u2192',\n action: 'navigate',\n handler: (_input, key) => {\n if (onButton) return;\n const col = Math.floor(focused / rows);\n const row = focused % rows;\n\n let next = focused;\n if (key.leftArrow) {\n const prevCol = col > 0 ? col - 1 : columns - 1;\n next = Math.min(prevCol * rows + row, options.length - 1);\n }\n if (key.rightArrow) {\n const nextCol = col < columns - 1 ? col + 1 : 0;\n next = Math.min(nextCol * rows + row, options.length - 1);\n }\n // Landing on a disabled option slides to the column's nearest\n // enabled one.\n if (options[next]?.disabled) {\n next = stepEnabled(options, rows, next, 1);\n }\n setFocused(next);\n },\n });\n }\n\n if (filter !== null) {\n bindings.push(...filterBindings(filter, setFilter));\n }\n\n useKeyBindings('multi-picker', bindings);\n\n const columnArrays: PickerOption<T>[][] = [];\n for (let c = 0; c < columns; c++) {\n columnArrays.push(options.slice(c * rows, c * rows + rows));\n }\n\n const renderOption = (opt: PickerOption<T>, flatIdx: number) => {\n const isFocused = !onButton && flatIdx === focused;\n const isSelected = selected.has(opt.label);\n const label = opt.hint ? `${opt.label} (${opt.hint})` : opt.label;\n const checkbox = isSelected ? Icons.squareFilled : Icons.squareOpen;\n return (\n <Box\n key={flatIdx}\n flexDirection=\"column\"\n marginBottom={optionMarginBottom}\n >\n <Box gap={1}>\n <Text\n color={isSelected ? 'white' : Colors.muted}\n dimColor={!isFocused && !isSelected}\n >\n {checkbox}\n </Text>\n {opt.icon && <Text color={opt.icon.color}>{opt.icon.glyph}</Text>}\n <Text\n color={\n opt.disabled\n ? Colors.muted\n : isFocused\n ? Colors.accent\n : undefined\n }\n bold={isFocused && !opt.disabled}\n dimColor={!isFocused || opt.disabled}\n >\n {label}\n </Text>\n </Box>\n {/* Optional dimmed, wrapped explanation under the label. The explicit\n width forces Ink to wrap (an unconstrained Box shrinks to its\n content and never wraps). Renders only when set, so label-only rows\n are byte-for-byte unchanged. */}\n {opt.description && (\n <Box marginLeft={4} width={DESCRIPTION_WIDTH}>\n <Text dimColor wrap=\"wrap\">\n {opt.description}\n </Text>\n </Box>\n )}\n </Box>\n );\n };\n\n return (\n <Box flexDirection=\"column\" alignItems={centered ? 'center' : undefined}>\n <PromptLabel message={message} />\n {filter !== null && (\n <FilterRow filter={filter} shown={options.length} total={totalCount} />\n )}\n {filter && options.length === 0 && (\n <Text dimColor>No options match. Backspace to widen the filter.</Text>\n )}\n {viewport.needsScroll ? (\n <Box\n flexDirection=\"column\"\n marginLeft={centered ? 0 : 2}\n marginTop={message ? 1 : 0}\n >\n <Text dimColor>\n {viewport.hiddenAbove > 0\n ? `↑ ${viewport.hiddenAbove} more${\n filter === null ? ' [P] for previous page' : ''\n }`\n : ' '}\n </Text>\n {options\n .slice(viewport.start, viewport.end)\n .map((opt, relIdx) => renderOption(opt, viewport.start + relIdx))}\n <Text dimColor>\n {viewport.hiddenBelow > 0\n ? `↓ ${viewport.hiddenBelow} more${\n filter === null ? ' [N] for next page' : ''\n }`\n : ' '}\n </Text>\n </Box>\n ) : (\n <Box\n flexDirection=\"row\"\n gap={4}\n marginLeft={centered ? 0 : 2}\n marginTop={message ? 1 : 0}\n >\n {columnArrays.map((colOpts, colIdx) => (\n <Box key={colIdx} flexDirection=\"column\">\n {colOpts.map((opt, rowIdx) =>\n renderOption(opt, colIdx * rows + rowIdx),\n )}\n </Box>\n ))}\n </Box>\n )}\n <Box marginTop={1} marginLeft={centered ? 0 : 2}>\n <ConfirmButton focused={onButton} count={selected.size} />\n </Box>\n </Box>\n );\n};\n"],"mappings":"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"}
|
package/dist/README.md
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
# scripts/
|
|
2
2
|
|
|
3
3
|
Helper scripts. The build-related ones (`generate-version.cjs`,
|
|
4
|
-
`smoke-test*.sh`, `
|
|
5
|
-
|
|
4
|
+
`smoke-test*.sh`, `warlock-smoke-test.ts`) are wired into `package.json`.
|
|
5
|
+
Scripts import the wizard only through public entries, like the e2e harness; see
|
|
6
|
+
[`ARCHITECTURE.md`](../e2e-harness/ARCHITECTURE.md#the-pieces). The rest below
|
|
7
|
+
are **manual, runnable tools** for headless e2e + snapshots — each is a
|
|
6
8
|
standalone `tsx` entry, named `*.no-jest.ts` so Jest ignores it.
|
|
7
9
|
|
|
8
10
|
Run from the repo root, e.g. `npx tsx scripts/<name>.no-jest.ts`.
|
|
9
11
|
|
|
10
|
-
Both e2e routes share one primitive: the **real TUI host** runs `
|
|
12
|
+
Both e2e routes share one primitive: the **real TUI host** runs `runTui` (the
|
|
11
13
|
real ink render) and is driven purely by store state manipulation; a PTY parent
|
|
12
14
|
([`e2e-harness/tui-capture.ts`](../e2e-harness/tui-capture.ts), node-pty +
|
|
13
15
|
`@xterm/headless`) captures the real rendered screen.
|
|
@@ -46,6 +48,7 @@ exploration does not need either secret. See
|
|
|
46
48
|
|
|
47
49
|
The control plane lives in [`e2e-harness/`](../e2e-harness/) — out of `src/`, so
|
|
48
50
|
none of it ships in prod. `WizardCiDriver` (read/act over the store), the
|
|
49
|
-
screen→action registry, the
|
|
50
|
-
|
|
51
|
-
|
|
51
|
+
screen→action registry, the profile loader (`profiles.ts`, which reads each
|
|
52
|
+
`src/programs/<id>/test/e2e.json`), and `tui-capture` (real-TUI PTY capture).
|
|
53
|
+
See [`ARCHITECTURE.md`](../e2e-harness/ARCHITECTURE.md) for how the two routes
|
|
54
|
+
drive these (env strip, scoped project id, gotchas).
|