nastech-tui 0.0.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/.prettierrc +11 -0
- package/README.md +346 -0
- package/eslint.config.mjs +111 -0
- package/package.json +51 -0
- package/packages/nastech-ink/ambient.d.ts +83 -0
- package/packages/nastech-ink/index.d.ts +40 -0
- package/packages/nastech-ink/index.js +1 -0
- package/packages/nastech-ink/nastech-ink/ambient.d.ts +83 -0
- package/packages/nastech-ink/nastech-ink/index.d.ts +40 -0
- package/packages/nastech-ink/nastech-ink/index.js +1 -0
- package/packages/nastech-ink/nastech-ink/package.json +54 -0
- package/packages/nastech-ink/nastech-ink/src/bootstrap/state.ts +9 -0
- package/packages/nastech-ink/nastech-ink/src/entry-exports.ts +32 -0
- package/packages/nastech-ink/nastech-ink/src/hooks/use-stderr.ts +15 -0
- package/packages/nastech-ink/nastech-ink/src/hooks/use-stdout.ts +15 -0
- package/packages/nastech-ink/nastech-ink/src/ink/Ansi.tsx +435 -0
- package/packages/nastech-ink/nastech-ink/src/ink/app-mouse.test.ts +123 -0
- package/packages/nastech-ink/nastech-ink/src/ink/bidi.ts +145 -0
- package/packages/nastech-ink/nastech-ink/src/ink/cache-eviction.ts +45 -0
- package/packages/nastech-ink/nastech-ink/src/ink/clearTerminal.ts +68 -0
- package/packages/nastech-ink/nastech-ink/src/ink/colorize.test.ts +60 -0
- package/packages/nastech-ink/nastech-ink/src/ink/colorize.ts +277 -0
- package/packages/nastech-ink/nastech-ink/src/ink/components/AlternateScreen.tsx +133 -0
- package/packages/nastech-ink/nastech-ink/src/ink/components/App.tsx +830 -0
- package/packages/nastech-ink/nastech-ink/src/ink/components/AppContext.ts +20 -0
- package/packages/nastech-ink/nastech-ink/src/ink/components/Box.tsx +294 -0
- package/packages/nastech-ink/nastech-ink/src/ink/components/Button.tsx +236 -0
- package/packages/nastech-ink/nastech-ink/src/ink/components/ClockContext.tsx +133 -0
- package/packages/nastech-ink/nastech-ink/src/ink/components/CursorAdvanceContext.ts +35 -0
- package/packages/nastech-ink/nastech-ink/src/ink/components/CursorDeclarationContext.ts +28 -0
- package/packages/nastech-ink/nastech-ink/src/ink/components/ErrorOverview.tsx +130 -0
- package/packages/nastech-ink/nastech-ink/src/ink/components/Link.tsx +38 -0
- package/packages/nastech-ink/nastech-ink/src/ink/components/Newline.tsx +43 -0
- package/packages/nastech-ink/nastech-ink/src/ink/components/NoSelect.tsx +73 -0
- package/packages/nastech-ink/nastech-ink/src/ink/components/RawAnsi.tsx +61 -0
- package/packages/nastech-ink/nastech-ink/src/ink/components/ScrollBox.tsx +290 -0
- package/packages/nastech-ink/nastech-ink/src/ink/components/Spacer.tsx +23 -0
- package/packages/nastech-ink/nastech-ink/src/ink/components/StdinContext.ts +25 -0
- package/packages/nastech-ink/nastech-ink/src/ink/components/TerminalFocusContext.tsx +63 -0
- package/packages/nastech-ink/nastech-ink/src/ink/components/TerminalSizeContext.tsx +7 -0
- package/packages/nastech-ink/nastech-ink/src/ink/components/Text.test.ts +38 -0
- package/packages/nastech-ink/nastech-ink/src/ink/components/Text.tsx +336 -0
- package/packages/nastech-ink/nastech-ink/src/ink/constants.ts +6 -0
- package/packages/nastech-ink/nastech-ink/src/ink/cursor.ts +5 -0
- package/packages/nastech-ink/nastech-ink/src/ink/devtools.ts +2 -0
- package/packages/nastech-ink/nastech-ink/src/ink/dom.ts +495 -0
- package/packages/nastech-ink/nastech-ink/src/ink/events/click-event.ts +38 -0
- package/packages/nastech-ink/nastech-ink/src/ink/events/cmd-shortcuts.test.ts +65 -0
- package/packages/nastech-ink/nastech-ink/src/ink/events/dispatcher.ts +242 -0
- package/packages/nastech-ink/nastech-ink/src/ink/events/emitter.ts +40 -0
- package/packages/nastech-ink/nastech-ink/src/ink/events/event-handlers.ts +84 -0
- package/packages/nastech-ink/nastech-ink/src/ink/events/event.ts +11 -0
- package/packages/nastech-ink/nastech-ink/src/ink/events/focus-event.ts +18 -0
- package/packages/nastech-ink/nastech-ink/src/ink/events/input-event.ts +176 -0
- package/packages/nastech-ink/nastech-ink/src/ink/events/keyboard-event.ts +57 -0
- package/packages/nastech-ink/nastech-ink/src/ink/events/mouse-event.ts +18 -0
- package/packages/nastech-ink/nastech-ink/src/ink/events/paste-event.ts +10 -0
- package/packages/nastech-ink/nastech-ink/src/ink/events/resize-event.ts +12 -0
- package/packages/nastech-ink/nastech-ink/src/ink/events/terminal-event.ts +107 -0
- package/packages/nastech-ink/nastech-ink/src/ink/events/terminal-focus-event.ts +19 -0
- package/packages/nastech-ink/nastech-ink/src/ink/focus.ts +219 -0
- package/packages/nastech-ink/nastech-ink/src/ink/frame.ts +124 -0
- package/packages/nastech-ink/nastech-ink/src/ink/get-max-width.ts +27 -0
- package/packages/nastech-ink/nastech-ink/src/ink/global.d.ts +1 -0
- package/packages/nastech-ink/nastech-ink/src/ink/hit-test.test.ts +38 -0
- package/packages/nastech-ink/nastech-ink/src/ink/hit-test.ts +224 -0
- package/packages/nastech-ink/nastech-ink/src/ink/hooks/use-animation-frame.ts +62 -0
- package/packages/nastech-ink/nastech-ink/src/ink/hooks/use-app.ts +9 -0
- package/packages/nastech-ink/nastech-ink/src/ink/hooks/use-cursor-advance.ts +33 -0
- package/packages/nastech-ink/nastech-ink/src/ink/hooks/use-declared-cursor.ts +75 -0
- package/packages/nastech-ink/nastech-ink/src/ink/hooks/use-external-process.ts +27 -0
- package/packages/nastech-ink/nastech-ink/src/ink/hooks/use-input.ts +95 -0
- package/packages/nastech-ink/nastech-ink/src/ink/hooks/use-interval.ts +71 -0
- package/packages/nastech-ink/package.json +57 -0
- package/packages/nastech-ink/src/bootstrap/state.ts +9 -0
- package/packages/nastech-ink/src/entry-exports.ts +32 -0
- package/packages/nastech-ink/src/hooks/use-stderr.ts +15 -0
- package/packages/nastech-ink/src/hooks/use-stdout.ts +15 -0
- package/packages/nastech-ink/src/ink/Ansi.tsx +435 -0
- package/packages/nastech-ink/src/ink/app-mouse.test.ts +123 -0
- package/packages/nastech-ink/src/ink/app-rawmode-mouse.test.ts +91 -0
- package/packages/nastech-ink/src/ink/bidi.ts +145 -0
- package/packages/nastech-ink/src/ink/cache-eviction.ts +45 -0
- package/packages/nastech-ink/src/ink/clearTerminal.ts +68 -0
- package/packages/nastech-ink/src/ink/colorize.test.ts +60 -0
- package/packages/nastech-ink/src/ink/colorize.ts +277 -0
- package/packages/nastech-ink/src/ink/components/AlternateScreen.tsx +133 -0
- package/packages/nastech-ink/src/ink/components/App.tsx +855 -0
- package/packages/nastech-ink/src/ink/components/AppContext.ts +20 -0
- package/packages/nastech-ink/src/ink/components/Box.tsx +294 -0
- package/packages/nastech-ink/src/ink/components/Button.tsx +236 -0
- package/packages/nastech-ink/src/ink/components/ClockContext.tsx +133 -0
- package/packages/nastech-ink/src/ink/components/CursorAdvanceContext.ts +35 -0
- package/packages/nastech-ink/src/ink/components/CursorDeclarationContext.ts +28 -0
- package/packages/nastech-ink/src/ink/components/ErrorOverview.tsx +130 -0
- package/packages/nastech-ink/src/ink/components/Link.tsx +38 -0
- package/packages/nastech-ink/src/ink/components/Newline.tsx +43 -0
- package/packages/nastech-ink/src/ink/components/NoSelect.tsx +73 -0
- package/packages/nastech-ink/src/ink/components/RawAnsi.tsx +61 -0
- package/packages/nastech-ink/src/ink/components/ScrollBox.tsx +290 -0
- package/packages/nastech-ink/src/ink/components/Spacer.tsx +23 -0
- package/packages/nastech-ink/src/ink/components/StdinContext.ts +25 -0
- package/packages/nastech-ink/src/ink/components/TerminalFocusContext.tsx +63 -0
- package/packages/nastech-ink/src/ink/components/TerminalSizeContext.tsx +7 -0
- package/packages/nastech-ink/src/ink/components/Text.test.ts +38 -0
- package/packages/nastech-ink/src/ink/components/Text.tsx +336 -0
- package/packages/nastech-ink/src/ink/constants.ts +6 -0
- package/packages/nastech-ink/src/ink/cursor.ts +5 -0
- package/packages/nastech-ink/src/ink/devtools.ts +2 -0
- package/packages/nastech-ink/src/ink/dom.ts +495 -0
- package/packages/nastech-ink/src/ink/events/click-event.ts +38 -0
- package/packages/nastech-ink/src/ink/events/cmd-shortcuts.test.ts +65 -0
- package/packages/nastech-ink/src/ink/events/dispatcher.ts +242 -0
- package/packages/nastech-ink/src/ink/events/emitter.ts +40 -0
- package/packages/nastech-ink/src/ink/events/event-handlers.ts +84 -0
- package/packages/nastech-ink/src/ink/events/event.ts +11 -0
- package/packages/nastech-ink/src/ink/events/focus-event.ts +18 -0
- package/packages/nastech-ink/src/ink/events/input-event.ts +176 -0
- package/packages/nastech-ink/src/ink/events/keyboard-event.ts +57 -0
- package/packages/nastech-ink/src/ink/events/mouse-event.ts +18 -0
- package/packages/nastech-ink/src/ink/events/paste-event.ts +10 -0
- package/packages/nastech-ink/src/ink/events/resize-event.ts +12 -0
- package/packages/nastech-ink/src/ink/events/terminal-event.ts +107 -0
- package/packages/nastech-ink/src/ink/events/terminal-focus-event.ts +19 -0
- package/packages/nastech-ink/src/ink/focus.ts +219 -0
- package/packages/nastech-ink/src/ink/frame.ts +124 -0
- package/packages/nastech-ink/src/ink/get-max-width.ts +27 -0
- package/packages/nastech-ink/src/ink/global.d.ts +1 -0
- package/packages/nastech-ink/src/ink/hit-test.test.ts +38 -0
- package/packages/nastech-ink/src/ink/hit-test.ts +224 -0
- package/packages/nastech-ink/src/ink/hooks/use-animation-frame.ts +62 -0
- package/packages/nastech-ink/src/ink/hooks/use-app.ts +9 -0
- package/packages/nastech-ink/src/ink/hooks/use-cursor-advance.ts +33 -0
- package/packages/nastech-ink/src/ink/hooks/use-declared-cursor.ts +75 -0
- package/packages/nastech-ink/src/ink/hooks/use-external-process.ts +27 -0
- package/packages/nastech-ink/src/ink/hooks/use-input.ts +95 -0
- package/packages/nastech-ink/src/ink/hooks/use-interval.ts +71 -0
- package/packages/nastech-ink/src/ink/hooks/use-search-highlight.ts +56 -0
- package/packages/nastech-ink/src/ink/hooks/use-selection.ts +101 -0
- package/packages/nastech-ink/src/ink/hooks/use-stdin.ts +9 -0
- package/packages/nastech-ink/src/ink/hooks/use-tab-status.ts +71 -0
- package/packages/nastech-ink/src/ink/hooks/use-terminal-focus.ts +18 -0
- package/packages/nastech-ink/src/ink/hooks/use-terminal-title.ts +34 -0
- package/packages/nastech-ink/src/ink/hooks/use-terminal-viewport.ts +100 -0
- package/packages/nastech-ink/src/ink/hyperlinkHover.ts +52 -0
- package/packages/nastech-ink/src/ink/ink-cursor-advance.test.ts +234 -0
- package/packages/nastech-ink/src/ink/ink-resize.test.ts +50 -0
- package/packages/nastech-ink/src/ink/ink.tsx +2705 -0
- package/packages/nastech-ink/src/ink/instances.ts +10 -0
- package/packages/nastech-ink/src/ink/layout/engine.ts +6 -0
- package/packages/nastech-ink/src/ink/layout/geometry.ts +98 -0
- package/packages/nastech-ink/src/ink/layout/node.ts +145 -0
- package/packages/nastech-ink/src/ink/layout/yoga.ts +313 -0
- package/packages/nastech-ink/src/ink/line-width-cache.ts +38 -0
- package/packages/nastech-ink/src/ink/log-update.test.ts +223 -0
- package/packages/nastech-ink/src/ink/log-update.ts +752 -0
- package/packages/nastech-ink/src/ink/lru.ts +14 -0
- package/packages/nastech-ink/src/ink/measure-element.ts +23 -0
- package/packages/nastech-ink/src/ink/measure-text.ts +50 -0
- package/packages/nastech-ink/src/ink/node-cache.ts +53 -0
- package/packages/nastech-ink/src/ink/optimizer.ts +99 -0
- package/packages/nastech-ink/src/ink/output.ts +845 -0
- package/packages/nastech-ink/src/ink/parse-keypress.test.ts +133 -0
- package/packages/nastech-ink/src/ink/parse-keypress.ts +848 -0
- package/packages/nastech-ink/src/ink/reconciler.ts +382 -0
- package/packages/nastech-ink/src/ink/render-border.ts +206 -0
- package/packages/nastech-ink/src/ink/render-node-to-output.ts +1582 -0
- package/packages/nastech-ink/src/ink/render-to-screen.ts +236 -0
- package/packages/nastech-ink/src/ink/renderer.ts +169 -0
- package/packages/nastech-ink/src/ink/root.ts +204 -0
- package/packages/nastech-ink/src/ink/screen.ts +1590 -0
- package/packages/nastech-ink/src/ink/searchHighlight.ts +91 -0
- package/packages/nastech-ink/src/ink/selection.test.ts +82 -0
- package/packages/nastech-ink/src/ink/selection.ts +1143 -0
- package/packages/nastech-ink/src/ink/squash-text-nodes.ts +74 -0
- package/packages/nastech-ink/src/ink/stringWidth.ts +341 -0
- package/packages/nastech-ink/src/ink/styles.ts +750 -0
- package/packages/nastech-ink/src/ink/supports-hyperlinks.ts +51 -0
- package/packages/nastech-ink/src/ink/tabstops.ts +44 -0
- package/packages/nastech-ink/src/ink/terminal-focus-state.ts +52 -0
- package/packages/nastech-ink/src/ink/terminal-querier.ts +222 -0
- package/packages/nastech-ink/src/ink/terminal.test.ts +15 -0
- package/packages/nastech-ink/src/ink/terminal.ts +299 -0
- package/packages/nastech-ink/src/ink/termio/ansi.ts +75 -0
- package/packages/nastech-ink/src/ink/termio/csi.ts +334 -0
- package/packages/nastech-ink/src/ink/termio/dec.ts +99 -0
- package/packages/nastech-ink/src/ink/termio/esc.ts +69 -0
- package/packages/nastech-ink/src/ink/termio/osc.test.ts +191 -0
- package/packages/nastech-ink/src/ink/termio/osc.ts +724 -0
- package/packages/nastech-ink/src/ink/termio/parser.ts +467 -0
- package/packages/nastech-ink/src/ink/termio/sgr.ts +362 -0
- package/packages/nastech-ink/src/ink/termio/tokenize.test.ts +185 -0
- package/packages/nastech-ink/src/ink/termio/tokenize.ts +350 -0
- package/packages/nastech-ink/src/ink/termio/types.ts +230 -0
- package/packages/nastech-ink/src/ink/termio.ts +42 -0
- package/packages/nastech-ink/src/ink/useTerminalNotification.ts +110 -0
- package/packages/nastech-ink/src/ink/warn.ts +15 -0
- package/packages/nastech-ink/src/ink/widest-line.ts +22 -0
- package/packages/nastech-ink/src/ink/wrap-text.test.ts +17 -0
- package/packages/nastech-ink/src/ink/wrap-text.ts +144 -0
- package/packages/nastech-ink/src/ink/wrapAnsi.ts +13 -0
- package/packages/nastech-ink/src/native-ts/yoga-layout/enums.ts +112 -0
- package/packages/nastech-ink/src/native-ts/yoga-layout/index.ts +2326 -0
- package/packages/nastech-ink/src/utils/debug.ts +6 -0
- package/packages/nastech-ink/src/utils/earlyInput.ts +131 -0
- package/packages/nastech-ink/src/utils/env.ts +66 -0
- package/packages/nastech-ink/src/utils/envUtils.ts +13 -0
- package/packages/nastech-ink/src/utils/execFileNoThrow.test.ts +146 -0
- package/packages/nastech-ink/src/utils/execFileNoThrow.ts +115 -0
- package/packages/nastech-ink/src/utils/fullscreen.ts +3 -0
- package/packages/nastech-ink/src/utils/intl.ts +87 -0
- package/packages/nastech-ink/src/utils/log.ts +7 -0
- package/packages/nastech-ink/src/utils/semver.ts +57 -0
- package/packages/nastech-ink/src/utils/sliceAnsi.ts +106 -0
- package/packages/nastech-ink/text-input.d.ts +2 -0
- package/packages/nastech-ink/text-input.js +1 -0
- package/scripts/build.mjs +61 -0
- package/scripts/profile-tui.mjs +121 -0
- package/src/__tests__/activeSessionSwitcher.test.ts +157 -0
- package/src/__tests__/appChromeStatusRule.test.tsx +84 -0
- package/src/__tests__/appChromeStatusRuleDevCredits.test.tsx +73 -0
- package/src/__tests__/approvalAction.test.ts +50 -0
- package/src/__tests__/asCommandDispatch.test.ts +27 -0
- package/src/__tests__/blockLayout.test.ts +122 -0
- package/src/__tests__/clipboard.test.ts +369 -0
- package/src/__tests__/constants.test.ts +53 -0
- package/src/__tests__/createGatewayEventHandler.test.ts +1091 -0
- package/src/__tests__/createSlashHandler.test.ts +822 -0
- package/src/__tests__/creditsCommand.test.ts +144 -0
- package/src/__tests__/cursorDriftRegression.test.ts +114 -0
- package/src/__tests__/details.test.ts +115 -0
- package/src/__tests__/emoji.test.ts +64 -0
- package/src/__tests__/externalLink.test.ts +144 -0
- package/src/__tests__/forceTruecolor.test.ts +191 -0
- package/src/__tests__/gatewayClient.test.ts +394 -0
- package/src/__tests__/gatewayRecovery.test.ts +47 -0
- package/src/__tests__/markdown.test.ts +331 -0
- package/src/__tests__/mathUnicode.test.ts +293 -0
- package/src/__tests__/memoryMonitor.test.ts +102 -0
- package/src/__tests__/messageLine.test.ts +19 -0
- package/src/__tests__/messages.test.ts +92 -0
- package/src/__tests__/orchestratorPromptSession.test.ts +64 -0
- package/src/__tests__/osc52.test.ts +67 -0
- package/src/__tests__/parentLog.test.ts +75 -0
- package/src/__tests__/paths.test.ts +70 -0
- package/src/__tests__/platform.test.ts +556 -0
- package/src/__tests__/precisionWheel.test.ts +44 -0
- package/src/__tests__/prompt.test.ts +31 -0
- package/src/__tests__/providers.test.ts +65 -0
- package/src/__tests__/reasoning.test.ts +76 -0
- package/src/__tests__/rpc.test.ts +27 -0
- package/src/__tests__/scroll.test.ts +99 -0
- package/src/__tests__/slashParity.test.ts +123 -0
- package/src/__tests__/spawnHistoryStore.test.ts +46 -0
- package/src/__tests__/stateIsolation.test.ts +46 -0
- package/src/__tests__/statusBarTicker.test.ts +18 -0
- package/src/__tests__/statusRule.test.ts +32 -0
- package/src/__tests__/streamingMarkdown.test.ts +121 -0
- package/src/__tests__/subagentTree.test.ts +407 -0
- package/src/__tests__/syntax.test.ts +45 -0
- package/src/__tests__/terminalModes.test.ts +39 -0
- package/src/__tests__/terminalParity.test.ts +77 -0
- package/src/__tests__/terminalSetup.test.ts +386 -0
- package/src/__tests__/termux.test.ts +35 -0
- package/src/__tests__/termuxComposerLayout.test.ts +40 -0
- package/src/__tests__/text.test.ts +233 -0
- package/src/__tests__/textInputBurstInput.test.ts +40 -0
- package/src/__tests__/textInputCursorSourceOfTruth.test.ts +50 -0
- package/src/__tests__/textInputFastEcho.test.ts +200 -0
- package/src/__tests__/textInputLineNav.test.ts +55 -0
- package/src/__tests__/textInputPassThrough.test.ts +59 -0
- package/src/__tests__/textInputRightClick.test.ts +48 -0
- package/src/__tests__/textInputWrap.test.ts +151 -0
- package/src/__tests__/theme.test.ts +311 -0
- package/src/__tests__/turnControllerNotice.test.ts +43 -0
- package/src/__tests__/turnStore.test.ts +66 -0
- package/src/__tests__/useCompletion.test.ts +35 -0
- package/src/__tests__/useComposerState.test.ts +59 -0
- package/src/__tests__/useConfigSync.test.ts +460 -0
- package/src/__tests__/useInputHandlers.test.ts +77 -0
- package/src/__tests__/useQueue.test.ts +28 -0
- package/src/__tests__/useSessionLifecycle.test.ts +60 -0
- package/src/__tests__/useVirtualHistoryHeights.test.ts +39 -0
- package/src/__tests__/viewport.test.ts +58 -0
- package/src/__tests__/viewportStore.test.ts +85 -0
- package/src/__tests__/virtualHeights.test.ts +96 -0
- package/src/__tests__/virtualHistoryClamp.test.ts +19 -0
- package/src/__tests__/virtualHistoryOffsetCache.test.ts +282 -0
- package/src/__tests__/wheelAccel.test.ts +138 -0
- package/src/app/createGatewayEventHandler.ts +833 -0
- package/src/app/createSlashHandler.ts +130 -0
- package/src/app/delegationStore.ts +77 -0
- package/src/app/gatewayContext.tsx +19 -0
- package/src/app/gatewayRecovery.ts +35 -0
- package/src/app/inputSelectionStore.ts +15 -0
- package/src/app/interfaces.ts +394 -0
- package/src/app/overlayStore.ts +53 -0
- package/src/app/scroll.ts +71 -0
- package/src/app/setupHandoff.ts +54 -0
- package/src/app/slash/commands/core.ts +648 -0
- package/src/app/slash/commands/credits.ts +57 -0
- package/src/app/slash/commands/debug.ts +48 -0
- package/src/app/slash/commands/ops.ts +717 -0
- package/src/app/slash/commands/session.ts +554 -0
- package/src/app/slash/commands/setup.ts +20 -0
- package/src/app/slash/registry.ts +20 -0
- package/src/app/slash/types.ts +21 -0
- package/src/app/spawnHistoryStore.ts +159 -0
- package/src/app/turnController.ts +866 -0
- package/src/app/turnStore.ts +85 -0
- package/src/app/uiStore.ts +44 -0
- package/src/app/useComposerState.ts +367 -0
- package/src/app/useConfigSync.ts +288 -0
- package/src/app/useInputHandlers.ts +576 -0
- package/src/app/useLongRunToolCharms.ts +69 -0
- package/src/app/useMainApp.ts +1039 -0
- package/src/app/useSessionLifecycle.ts +366 -0
- package/src/app/useSubmission.ts +429 -0
- package/src/app.tsx +25 -0
- package/src/banner.ts +93 -0
- package/src/components/activeSessionSwitcher.tsx +635 -0
- package/src/components/agentsOverlay.tsx +1073 -0
- package/src/components/appChrome.tsx +554 -0
- package/src/components/appLayout.tsx +444 -0
- package/src/components/appOverlays.tsx +254 -0
- package/src/components/branding.tsx +466 -0
- package/src/components/fpsOverlay.tsx +30 -0
- package/src/components/helpHint.tsx +73 -0
- package/src/components/markdown.tsx +1119 -0
- package/src/components/maskedPrompt.tsx +34 -0
- package/src/components/messageLine.tsx +237 -0
- package/src/components/modelPicker.tsx +527 -0
- package/src/components/overlayControls.tsx +50 -0
- package/src/components/pluginsHub.tsx +238 -0
- package/src/components/prompts.tsx +276 -0
- package/src/components/queuedMessages.tsx +64 -0
- package/src/components/sessionPicker.tsx +227 -0
- package/src/components/skillsHub.tsx +308 -0
- package/src/components/streamingAssistant.tsx +110 -0
- package/src/components/streamingMarkdown.tsx +174 -0
- package/src/components/textInput.tsx +1340 -0
- package/src/components/themed.tsx +30 -0
- package/src/components/thinking.tsx +1224 -0
- package/src/components/todoPanel.tsx +93 -0
- package/src/config/env.ts +64 -0
- package/src/config/limits.ts +13 -0
- package/src/config/timing.ts +6 -0
- package/src/content/charms.ts +1 -0
- package/src/content/faces.ts +17 -0
- package/src/content/fortunes.ts +30 -0
- package/src/content/hotkeys.ts +37 -0
- package/src/content/placeholders.ts +13 -0
- package/src/content/setup.ts +17 -0
- package/src/content/verbs.ts +38 -0
- package/src/domain/blockLayout.ts +146 -0
- package/src/domain/details.ts +76 -0
- package/src/domain/messages.ts +91 -0
- package/src/domain/paths.ts +16 -0
- package/src/domain/providers.ts +11 -0
- package/src/domain/roles.ts +9 -0
- package/src/domain/slash.ts +10 -0
- package/src/domain/usage.ts +3 -0
- package/src/domain/viewport.ts +51 -0
- package/src/entry.tsx +104 -0
- package/src/gatewayClient.ts +730 -0
- package/src/gatewayTypes.ts +568 -0
- package/src/hooks/useCompletion.ts +112 -0
- package/src/hooks/useGitBranch.ts +72 -0
- package/src/hooks/useInputHistory.ts +11 -0
- package/src/hooks/useQueue.ts +76 -0
- package/src/hooks/useVirtualHistory.ts +554 -0
- package/src/lib/circularBuffer.ts +48 -0
- package/src/lib/clipboard.ts +182 -0
- package/src/lib/editor.test.ts +74 -0
- package/src/lib/editor.ts +47 -0
- package/src/lib/emoji.ts +55 -0
- package/src/lib/externalCli.ts +16 -0
- package/src/lib/externalLink.ts +435 -0
- package/src/lib/forceTruecolor.ts +60 -0
- package/src/lib/fpsStore.ts +51 -0
- package/src/lib/fuzzy.test.ts +109 -0
- package/src/lib/fuzzy.ts +177 -0
- package/src/lib/gracefulExit.ts +47 -0
- package/src/lib/history.ts +82 -0
- package/src/lib/inputMetrics.ts +203 -0
- package/src/lib/liveProgress.test.ts +116 -0
- package/src/lib/liveProgress.ts +79 -0
- package/src/lib/mathUnicode.ts +770 -0
- package/src/lib/memory.test.ts +155 -0
- package/src/lib/memory.ts +188 -0
- package/src/lib/memoryMonitor.ts +109 -0
- package/src/lib/messages.test.ts +29 -0
- package/src/lib/messages.ts +8 -0
- package/src/lib/openExternalUrl.test.ts +217 -0
- package/src/lib/openExternalUrl.ts +158 -0
- package/src/lib/osc52.ts +73 -0
- package/src/lib/parentLog.ts +57 -0
- package/src/lib/perfPane.tsx +107 -0
- package/src/lib/platform.ts +409 -0
- package/src/lib/precisionWheel.ts +48 -0
- package/src/lib/prompt.ts +35 -0
- package/src/lib/reasoning.ts +55 -0
- package/src/lib/rpc.ts +41 -0
- package/src/lib/subagentTree.ts +355 -0
- package/src/lib/syntax.ts +117 -0
- package/src/lib/terminalModes.ts +51 -0
- package/src/lib/terminalParity.ts +78 -0
- package/src/lib/terminalSetup.ts +444 -0
- package/src/lib/termux.ts +29 -0
- package/src/lib/text.test.ts +18 -0
- package/src/lib/text.ts +339 -0
- package/src/lib/todo.test.ts +21 -0
- package/src/lib/todo.ts +9 -0
- package/src/lib/viewportStore.ts +124 -0
- package/src/lib/virtualHeights.ts +145 -0
- package/src/lib/wheelAccel.ts +190 -0
- package/src/protocol/interpolation.ts +3 -0
- package/src/protocol/paste.ts +1 -0
- package/src/theme.ts +589 -0
- package/src/types/nastech-ink.d.ts +176 -0
- package/src/types.ts +212 -0
- package/tsconfig.build.json +9 -0
- package/tsconfig.json +19 -0
- package/vitest.config.ts +7 -0
|
@@ -0,0 +1,444 @@
|
|
|
1
|
+
import { AlternateScreen, Box, NoSelect, ScrollBox, Text } from '@nastechai/ink'
|
|
2
|
+
import { useStore } from '@nanostores/react'
|
|
3
|
+
import { Fragment, memo, useMemo, useRef } from 'react'
|
|
4
|
+
|
|
5
|
+
import { useGateway } from '../app/gatewayContext.js'
|
|
6
|
+
import type { AppLayoutProps } from '../app/interfaces.js'
|
|
7
|
+
import { $isBlocked, $overlayState, patchOverlayState } from '../app/overlayStore.js'
|
|
8
|
+
import { $uiState } from '../app/uiStore.js'
|
|
9
|
+
import { INLINE_MODE, SHOW_FPS, TERMUX_TUI_MODE } from '../config/env.js'
|
|
10
|
+
import { PLACEHOLDER } from '../content/placeholders.js'
|
|
11
|
+
import {
|
|
12
|
+
COMPOSER_PROMPT_GAP_WIDTH,
|
|
13
|
+
composerPromptWidth,
|
|
14
|
+
inputVisualHeight,
|
|
15
|
+
stableComposerColumns
|
|
16
|
+
} from '../lib/inputMetrics.js'
|
|
17
|
+
import { PerfPane } from '../lib/perfPane.js'
|
|
18
|
+
import { composerPromptText } from '../lib/prompt.js'
|
|
19
|
+
|
|
20
|
+
import { AgentsOverlay } from './agentsOverlay.js'
|
|
21
|
+
import { GoodVibesHeart, StatusRule, StickyPromptTracker, TranscriptScrollbar } from './appChrome.js'
|
|
22
|
+
import { FloatingOverlays, PromptZone } from './appOverlays.js'
|
|
23
|
+
import { Banner, Panel, SessionPanel } from './branding.js'
|
|
24
|
+
import { FpsOverlay } from './fpsOverlay.js'
|
|
25
|
+
import { HelpHint } from './helpHint.js'
|
|
26
|
+
import { MessageLine } from './messageLine.js'
|
|
27
|
+
import { QueuedMessages } from './queuedMessages.js'
|
|
28
|
+
import { LiveTodoPanel, StreamingAssistant } from './streamingAssistant.js'
|
|
29
|
+
import { TextInput, type TextInputMouseApi } from './textInput.js'
|
|
30
|
+
|
|
31
|
+
const PromptPrefix = memo(function PromptPrefix({
|
|
32
|
+
bold = false,
|
|
33
|
+
color,
|
|
34
|
+
promptText,
|
|
35
|
+
width
|
|
36
|
+
}: {
|
|
37
|
+
bold?: boolean
|
|
38
|
+
color: string
|
|
39
|
+
promptText: string
|
|
40
|
+
width: number
|
|
41
|
+
}) {
|
|
42
|
+
const glyphWidth = Math.max(1, width - COMPOSER_PROMPT_GAP_WIDTH)
|
|
43
|
+
|
|
44
|
+
return (
|
|
45
|
+
<Box width={width}>
|
|
46
|
+
<Box width={glyphWidth}>
|
|
47
|
+
<Text bold={bold} color={color}>
|
|
48
|
+
{promptText}
|
|
49
|
+
</Text>
|
|
50
|
+
</Box>
|
|
51
|
+
<Box width={COMPOSER_PROMPT_GAP_WIDTH} />
|
|
52
|
+
</Box>
|
|
53
|
+
)
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
const TranscriptPane = memo(function TranscriptPane({
|
|
57
|
+
actions,
|
|
58
|
+
composer,
|
|
59
|
+
progress,
|
|
60
|
+
transcript
|
|
61
|
+
}: Pick<AppLayoutProps, 'actions' | 'composer' | 'progress' | 'transcript'>) {
|
|
62
|
+
const ui = useStore($uiState)
|
|
63
|
+
|
|
64
|
+
// LiveTodoPanel rides as a child of the latest user-message row so it
|
|
65
|
+
// visually belongs to the prompt and follows it during scroll. -1 when
|
|
66
|
+
// empty → row.index === -1 is always false → no render.
|
|
67
|
+
const lastUserIdx = useMemo(() => {
|
|
68
|
+
const items = transcript.historyItems
|
|
69
|
+
|
|
70
|
+
for (let i = items.length - 1; i >= 0; i--) {
|
|
71
|
+
if (items[i].role === 'user') {
|
|
72
|
+
return i
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return -1
|
|
77
|
+
}, [transcript.historyItems])
|
|
78
|
+
|
|
79
|
+
// Index of the first user-role message; every later user message gets a
|
|
80
|
+
// small dash above it so multi-turn transcripts visually segment by
|
|
81
|
+
// turn. -1 when no user message has been sent yet → no separator ever
|
|
82
|
+
// renders.
|
|
83
|
+
const firstUserIdx = useMemo(
|
|
84
|
+
() => transcript.historyItems.findIndex(m => m.role === 'user'),
|
|
85
|
+
[transcript.historyItems]
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
return (
|
|
89
|
+
<>
|
|
90
|
+
<ScrollBox
|
|
91
|
+
flexDirection="column"
|
|
92
|
+
flexGrow={1}
|
|
93
|
+
flexShrink={1}
|
|
94
|
+
onClick={(e: { cellIsBlank?: boolean }) => {
|
|
95
|
+
if (e.cellIsBlank) {
|
|
96
|
+
actions.clearSelection()
|
|
97
|
+
}
|
|
98
|
+
}}
|
|
99
|
+
ref={transcript.scrollRef}
|
|
100
|
+
stickyScroll
|
|
101
|
+
>
|
|
102
|
+
<Box flexDirection="column" paddingX={1}>
|
|
103
|
+
{transcript.virtualHistory.topSpacer > 0 ? <Box height={transcript.virtualHistory.topSpacer} /> : null}
|
|
104
|
+
|
|
105
|
+
{transcript.virtualRows.slice(transcript.virtualHistory.start, transcript.virtualHistory.end).map(row => (
|
|
106
|
+
<Box flexDirection="column" key={row.key} ref={transcript.virtualHistory.measureRef(row.key)}>
|
|
107
|
+
{row.msg.role === 'user' && firstUserIdx >= 0 && row.index > firstUserIdx && (
|
|
108
|
+
<Box marginTop={1}>
|
|
109
|
+
<Text color={ui.theme.color.border}>───</Text>
|
|
110
|
+
</Box>
|
|
111
|
+
)}
|
|
112
|
+
|
|
113
|
+
{row.msg.kind === 'intro' ? (
|
|
114
|
+
<Box flexDirection="column" paddingTop={1}>
|
|
115
|
+
<Banner maxWidth={Math.max(1, composer.cols - 2)} t={ui.theme} />
|
|
116
|
+
|
|
117
|
+
{row.msg.info && <SessionPanel info={row.msg.info} maxWidth={Math.max(1, composer.cols - 2)} sid={ui.sid} t={ui.theme} />}
|
|
118
|
+
</Box>
|
|
119
|
+
) : row.msg.kind === 'panel' && row.msg.panelData ? (
|
|
120
|
+
<Panel sections={row.msg.panelData.sections} t={ui.theme} title={row.msg.panelData.title} />
|
|
121
|
+
) : (
|
|
122
|
+
<MessageLine
|
|
123
|
+
cols={composer.cols}
|
|
124
|
+
compact={ui.compact}
|
|
125
|
+
detailsMode={ui.detailsMode}
|
|
126
|
+
detailsModeCommandOverride={ui.detailsModeCommandOverride}
|
|
127
|
+
msg={row.msg}
|
|
128
|
+
sections={ui.sections}
|
|
129
|
+
t={ui.theme}
|
|
130
|
+
/>
|
|
131
|
+
)}
|
|
132
|
+
|
|
133
|
+
{row.index === lastUserIdx && <LiveTodoPanel />}
|
|
134
|
+
</Box>
|
|
135
|
+
))}
|
|
136
|
+
|
|
137
|
+
{transcript.virtualHistory.bottomSpacer > 0 ? <Box height={transcript.virtualHistory.bottomSpacer} /> : null}
|
|
138
|
+
|
|
139
|
+
<StreamingAssistant
|
|
140
|
+
cols={composer.cols}
|
|
141
|
+
compact={ui.compact}
|
|
142
|
+
detailsMode={ui.detailsMode}
|
|
143
|
+
detailsModeCommandOverride={ui.detailsModeCommandOverride}
|
|
144
|
+
progress={progress}
|
|
145
|
+
sections={ui.sections}
|
|
146
|
+
/>
|
|
147
|
+
</Box>
|
|
148
|
+
</ScrollBox>
|
|
149
|
+
|
|
150
|
+
<NoSelect flexShrink={0} marginLeft={1}>
|
|
151
|
+
<TranscriptScrollbar scrollRef={transcript.scrollRef} t={ui.theme} />
|
|
152
|
+
</NoSelect>
|
|
153
|
+
|
|
154
|
+
<StickyPromptTracker
|
|
155
|
+
messages={transcript.historyItems}
|
|
156
|
+
offsets={transcript.virtualHistory.offsets}
|
|
157
|
+
onChange={actions.setStickyPrompt}
|
|
158
|
+
scrollRef={transcript.scrollRef}
|
|
159
|
+
/>
|
|
160
|
+
</>
|
|
161
|
+
)
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
const ComposerPane = memo(function ComposerPane({
|
|
165
|
+
actions,
|
|
166
|
+
composer,
|
|
167
|
+
status
|
|
168
|
+
}: Pick<AppLayoutProps, 'actions' | 'composer' | 'status'>) {
|
|
169
|
+
const ui = useStore($uiState)
|
|
170
|
+
const isBlocked = useStore($isBlocked)
|
|
171
|
+
const sh = (composer.inputBuf[0] ?? composer.input).startsWith('!')
|
|
172
|
+
const promptText = composerPromptText(ui.theme.brand.prompt, ui.info?.profile_name, sh, TERMUX_TUI_MODE, composer.cols)
|
|
173
|
+
const promptWidth = composerPromptWidth(promptText)
|
|
174
|
+
const promptBlank = ' '.repeat(promptWidth)
|
|
175
|
+
const inputColumns = stableComposerColumns(composer.cols, promptWidth, TERMUX_TUI_MODE)
|
|
176
|
+
const inputHeight = inputVisualHeight(composer.input, inputColumns)
|
|
177
|
+
const inputMouseRef = useRef<null | TextInputMouseApi>(null)
|
|
178
|
+
|
|
179
|
+
const captureInputDrag = (e: GutterMouseEvent) => {
|
|
180
|
+
if (e.button !== 0) {
|
|
181
|
+
return
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
e.stopImmediatePropagation?.()
|
|
185
|
+
inputMouseRef.current?.startAtBeginning()
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Drag origin matches the input box's top-left, so localRow / localCol
|
|
189
|
+
// map directly into TextInput coords (after backing out the prompt cell).
|
|
190
|
+
const dragFromPromptRow = (e: GutterMouseEvent) => {
|
|
191
|
+
if (e.button !== 0) {
|
|
192
|
+
return
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
e.stopImmediatePropagation?.()
|
|
196
|
+
inputMouseRef.current?.dragAt(e.localRow ?? 0, (e.localCol ?? 0) - promptWidth)
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Spacer rows live on a different vertical origin; only the column is
|
|
200
|
+
// parent-aligned with the input. Force row=0 so vertical drags can't
|
|
201
|
+
// jump the cursor to the wrong wrapped line.
|
|
202
|
+
const dragFromSpacer = (e: GutterMouseEvent) => {
|
|
203
|
+
if (e.button !== 0) {
|
|
204
|
+
return
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
e.stopImmediatePropagation?.()
|
|
208
|
+
inputMouseRef.current?.dragAt(0, (e.localCol ?? 0) - promptWidth)
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const endInputDrag = () => inputMouseRef.current?.end()
|
|
212
|
+
|
|
213
|
+
return (
|
|
214
|
+
<NoSelect
|
|
215
|
+
flexDirection="column"
|
|
216
|
+
flexShrink={0}
|
|
217
|
+
fromLeftEdge
|
|
218
|
+
onClick={(e: { cellIsBlank?: boolean }) => {
|
|
219
|
+
if (e.cellIsBlank) {
|
|
220
|
+
actions.clearSelection()
|
|
221
|
+
}
|
|
222
|
+
}}
|
|
223
|
+
paddingX={1}
|
|
224
|
+
>
|
|
225
|
+
<QueuedMessages
|
|
226
|
+
cols={composer.cols}
|
|
227
|
+
queued={composer.queuedDisplay}
|
|
228
|
+
queueEditIdx={composer.queueEditIdx}
|
|
229
|
+
t={ui.theme}
|
|
230
|
+
/>
|
|
231
|
+
|
|
232
|
+
{ui.bgTasks.size > 0 && (
|
|
233
|
+
<Text color={ui.theme.color.muted}>
|
|
234
|
+
{ui.bgTasks.size} background {ui.bgTasks.size === 1 ? 'task' : 'tasks'} running
|
|
235
|
+
</Text>
|
|
236
|
+
)}
|
|
237
|
+
|
|
238
|
+
{status.showStickyPrompt ? (
|
|
239
|
+
<Text color={ui.theme.color.muted} wrap="truncate-end">
|
|
240
|
+
<Text color={ui.theme.color.label}>↳ </Text>
|
|
241
|
+
|
|
242
|
+
{status.stickyPrompt}
|
|
243
|
+
</Text>
|
|
244
|
+
) : (
|
|
245
|
+
<Box height={1} onMouseDown={captureInputDrag} onMouseDrag={dragFromSpacer} onMouseUp={endInputDrag} />
|
|
246
|
+
)}
|
|
247
|
+
|
|
248
|
+
<StatusRulePane at="top" composer={composer} status={status} />
|
|
249
|
+
|
|
250
|
+
<Box flexDirection="column" marginTop={ui.statusBar === 'top' ? 0 : 1} position="relative">
|
|
251
|
+
<FloatingOverlays
|
|
252
|
+
cols={composer.cols}
|
|
253
|
+
compIdx={composer.compIdx}
|
|
254
|
+
completions={composer.completions}
|
|
255
|
+
onActiveSessionSelect={actions.activateLiveSession}
|
|
256
|
+
onActiveSessionClose={actions.closeLiveSession}
|
|
257
|
+
onModelSelect={actions.onModelSelect}
|
|
258
|
+
onNewLiveSession={actions.newLiveSession}
|
|
259
|
+
onNewPromptSession={actions.newPromptSession}
|
|
260
|
+
onPickerSelect={actions.resumeById}
|
|
261
|
+
pagerPageSize={composer.pagerPageSize}
|
|
262
|
+
/>
|
|
263
|
+
|
|
264
|
+
{composer.input === '?' && !composer.inputBuf.length && <HelpHint t={ui.theme} />}
|
|
265
|
+
|
|
266
|
+
{!isBlocked && (
|
|
267
|
+
<>
|
|
268
|
+
{composer.inputBuf.map((line, i) => (
|
|
269
|
+
<Box key={i}>
|
|
270
|
+
<Box width={promptWidth}>
|
|
271
|
+
{i === 0 ? (
|
|
272
|
+
<PromptPrefix color={ui.theme.color.muted} promptText={promptText} width={promptWidth} />
|
|
273
|
+
) : (
|
|
274
|
+
<Text color={ui.theme.color.muted}>{promptBlank}</Text>
|
|
275
|
+
)}
|
|
276
|
+
</Box>
|
|
277
|
+
|
|
278
|
+
<Text color={ui.theme.color.text}>{line || ' '}</Text>
|
|
279
|
+
</Box>
|
|
280
|
+
))}
|
|
281
|
+
|
|
282
|
+
<Box
|
|
283
|
+
onMouseDown={captureInputDrag}
|
|
284
|
+
onMouseDrag={dragFromPromptRow}
|
|
285
|
+
onMouseUp={endInputDrag}
|
|
286
|
+
position="relative"
|
|
287
|
+
width={Math.max(1, composer.cols - 2)}
|
|
288
|
+
>
|
|
289
|
+
<Box width={promptWidth}>
|
|
290
|
+
{sh ? (
|
|
291
|
+
<PromptPrefix color={ui.theme.color.shellDollar} promptText={promptText} width={promptWidth} />
|
|
292
|
+
) : composer.inputBuf.length ? (
|
|
293
|
+
<Text color={ui.theme.color.prompt}>{promptBlank}</Text>
|
|
294
|
+
) : (
|
|
295
|
+
<PromptPrefix bold color={ui.theme.color.prompt} promptText={promptText} width={promptWidth} />
|
|
296
|
+
)}
|
|
297
|
+
</Box>
|
|
298
|
+
|
|
299
|
+
<Box flexGrow={0} flexShrink={0} height={inputHeight} width={inputColumns}>
|
|
300
|
+
{/* Reserve the transcript scrollbar gutter too so typing never rewraps when the scrollbar column repaints. */}
|
|
301
|
+
<TextInput
|
|
302
|
+
columns={inputColumns}
|
|
303
|
+
mouseApiRef={inputMouseRef}
|
|
304
|
+
onChange={composer.updateInput}
|
|
305
|
+
onPaste={composer.handleTextPaste}
|
|
306
|
+
onSubmit={composer.submit}
|
|
307
|
+
placeholder={composer.empty ? PLACEHOLDER : ui.busy ? 'Ctrl+C to interrupt…' : ''}
|
|
308
|
+
value={composer.input}
|
|
309
|
+
voiceRecordKey={composer.voiceRecordKey}
|
|
310
|
+
/>
|
|
311
|
+
</Box>
|
|
312
|
+
|
|
313
|
+
<Box position="absolute" right={0}>
|
|
314
|
+
<GoodVibesHeart t={ui.theme} tick={status.goodVibesTick} />
|
|
315
|
+
</Box>
|
|
316
|
+
</Box>
|
|
317
|
+
</>
|
|
318
|
+
)}
|
|
319
|
+
</Box>
|
|
320
|
+
|
|
321
|
+
{!composer.empty && !ui.sid && <Text color={ui.theme.color.muted}>⚕ {ui.status}</Text>}
|
|
322
|
+
|
|
323
|
+
<StatusRulePane at="bottom" composer={composer} status={status} />
|
|
324
|
+
</NoSelect>
|
|
325
|
+
)
|
|
326
|
+
})
|
|
327
|
+
|
|
328
|
+
const AgentsOverlayPane = memo(function AgentsOverlayPane() {
|
|
329
|
+
const { gw } = useGateway()
|
|
330
|
+
const ui = useStore($uiState)
|
|
331
|
+
const overlay = useStore($overlayState)
|
|
332
|
+
|
|
333
|
+
return (
|
|
334
|
+
<AgentsOverlay
|
|
335
|
+
gw={gw}
|
|
336
|
+
initialHistoryIndex={overlay.agentsInitialHistoryIndex}
|
|
337
|
+
onClose={() => patchOverlayState({ agents: false, agentsInitialHistoryIndex: 0 })}
|
|
338
|
+
t={ui.theme}
|
|
339
|
+
/>
|
|
340
|
+
)
|
|
341
|
+
})
|
|
342
|
+
|
|
343
|
+
const StatusRulePane = memo(function StatusRulePane({
|
|
344
|
+
at,
|
|
345
|
+
composer,
|
|
346
|
+
status
|
|
347
|
+
}: Pick<AppLayoutProps, 'composer' | 'status'> & { at: 'bottom' | 'top' }) {
|
|
348
|
+
const ui = useStore($uiState)
|
|
349
|
+
|
|
350
|
+
if (ui.statusBar !== at) {
|
|
351
|
+
return null
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
return (
|
|
355
|
+
<Box marginTop={at === 'top' ? 1 : 0}>
|
|
356
|
+
<StatusRule
|
|
357
|
+
bgCount={ui.bgTasks.size}
|
|
358
|
+
busy={ui.busy}
|
|
359
|
+
cols={composer.cols}
|
|
360
|
+
cwdLabel={status.cwdLabel}
|
|
361
|
+
liveSessionCount={ui.liveSessionCount}
|
|
362
|
+
model={ui.info?.model ?? ''}
|
|
363
|
+
modelFast={ui.info?.fast || ui.info?.service_tier === 'priority'}
|
|
364
|
+
modelReasoningEffort={ui.info?.reasoning_effort}
|
|
365
|
+
onSessionCountClick={() => patchOverlayState({ sessions: true })}
|
|
366
|
+
sessionStartedAt={status.sessionStartedAt}
|
|
367
|
+
showCost={ui.showCost}
|
|
368
|
+
status={ui.status}
|
|
369
|
+
statusColor={status.statusColor}
|
|
370
|
+
t={ui.theme}
|
|
371
|
+
turnStartedAt={status.turnStartedAt}
|
|
372
|
+
usage={ui.usage}
|
|
373
|
+
voiceLabel={status.voiceLabel}
|
|
374
|
+
/>
|
|
375
|
+
</Box>
|
|
376
|
+
)
|
|
377
|
+
})
|
|
378
|
+
|
|
379
|
+
export const AppLayout = memo(function AppLayout({
|
|
380
|
+
actions,
|
|
381
|
+
composer,
|
|
382
|
+
mouseTracking,
|
|
383
|
+
progress,
|
|
384
|
+
status,
|
|
385
|
+
transcript
|
|
386
|
+
}: AppLayoutProps) {
|
|
387
|
+
const overlay = useStore($overlayState)
|
|
388
|
+
const ui = useStore($uiState)
|
|
389
|
+
|
|
390
|
+
// Inline mode skips AlternateScreen so the host terminal's native
|
|
391
|
+
// scrollback captures rows scrolled off the top; composer + progress
|
|
392
|
+
// stay anchored via normal flex-column flow.
|
|
393
|
+
const Shell = INLINE_MODE ? Fragment : AlternateScreen
|
|
394
|
+
const shellProps = INLINE_MODE ? {} : { mouseTracking }
|
|
395
|
+
|
|
396
|
+
return (
|
|
397
|
+
<Shell {...shellProps}>
|
|
398
|
+
<Box flexDirection="column" flexGrow={1}>
|
|
399
|
+
<Box flexDirection="row" flexGrow={1}>
|
|
400
|
+
{overlay.agents ? (
|
|
401
|
+
<PerfPane id="agents">
|
|
402
|
+
<AgentsOverlayPane />
|
|
403
|
+
</PerfPane>
|
|
404
|
+
) : (
|
|
405
|
+
<PerfPane id="transcript">
|
|
406
|
+
<TranscriptPane actions={actions} composer={composer} progress={progress} transcript={transcript} />
|
|
407
|
+
</PerfPane>
|
|
408
|
+
)}
|
|
409
|
+
</Box>
|
|
410
|
+
|
|
411
|
+
{!overlay.agents && (
|
|
412
|
+
<>
|
|
413
|
+
<PerfPane id="prompt">
|
|
414
|
+
<PromptZone
|
|
415
|
+
cols={composer.cols}
|
|
416
|
+
onApprovalChoice={actions.answerApproval}
|
|
417
|
+
onClarifyAnswer={actions.answerClarify}
|
|
418
|
+
onSecretSubmit={actions.answerSecret}
|
|
419
|
+
onSudoSubmit={actions.answerSudo}
|
|
420
|
+
/>
|
|
421
|
+
</PerfPane>
|
|
422
|
+
|
|
423
|
+
<PerfPane id="composer">
|
|
424
|
+
<ComposerPane actions={actions} composer={composer} status={status} />
|
|
425
|
+
</PerfPane>
|
|
426
|
+
|
|
427
|
+
{SHOW_FPS && (
|
|
428
|
+
<Box flexShrink={0} justifyContent="flex-end" paddingRight={1}>
|
|
429
|
+
<FpsOverlay t={ui.theme} />
|
|
430
|
+
</Box>
|
|
431
|
+
)}
|
|
432
|
+
</>
|
|
433
|
+
)}
|
|
434
|
+
</Box>
|
|
435
|
+
</Shell>
|
|
436
|
+
)
|
|
437
|
+
})
|
|
438
|
+
|
|
439
|
+
type GutterMouseEvent = {
|
|
440
|
+
button: number
|
|
441
|
+
localCol?: number
|
|
442
|
+
localRow?: number
|
|
443
|
+
stopImmediatePropagation?: () => void
|
|
444
|
+
}
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
import { Box, Text } from '@nastechai/ink'
|
|
2
|
+
import { useStore } from '@nanostores/react'
|
|
3
|
+
|
|
4
|
+
import { useGateway } from '../app/gatewayContext.js'
|
|
5
|
+
import type { AppOverlaysProps } from '../app/interfaces.js'
|
|
6
|
+
import { $overlayState, patchOverlayState } from '../app/overlayStore.js'
|
|
7
|
+
import { $uiSessionId, $uiTheme } from '../app/uiStore.js'
|
|
8
|
+
|
|
9
|
+
import { ActiveSessionSwitcher } from './activeSessionSwitcher.js'
|
|
10
|
+
import { FloatBox } from './appChrome.js'
|
|
11
|
+
import { MaskedPrompt } from './maskedPrompt.js'
|
|
12
|
+
import { ModelPicker } from './modelPicker.js'
|
|
13
|
+
import { OverlayHint } from './overlayControls.js'
|
|
14
|
+
import { ApprovalPrompt, ClarifyPrompt, ConfirmPrompt } from './prompts.js'
|
|
15
|
+
import { SessionPicker } from './sessionPicker.js'
|
|
16
|
+
import { SkillsHub } from './skillsHub.js'
|
|
17
|
+
|
|
18
|
+
const COMPLETION_WINDOW = 16
|
|
19
|
+
|
|
20
|
+
export function PromptZone({
|
|
21
|
+
cols,
|
|
22
|
+
onApprovalChoice,
|
|
23
|
+
onClarifyAnswer,
|
|
24
|
+
onSecretSubmit,
|
|
25
|
+
onSudoSubmit
|
|
26
|
+
}: Pick<AppOverlaysProps, 'cols' | 'onApprovalChoice' | 'onClarifyAnswer' | 'onSecretSubmit' | 'onSudoSubmit'>) {
|
|
27
|
+
const overlay = useStore($overlayState)
|
|
28
|
+
const theme = useStore($uiTheme)
|
|
29
|
+
|
|
30
|
+
if (overlay.approval) {
|
|
31
|
+
return (
|
|
32
|
+
<Box flexDirection="column" flexShrink={0} paddingX={1} paddingY={1}>
|
|
33
|
+
<ApprovalPrompt onChoice={onApprovalChoice} req={overlay.approval} t={theme} />
|
|
34
|
+
</Box>
|
|
35
|
+
)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (overlay.confirm) {
|
|
39
|
+
const req = overlay.confirm
|
|
40
|
+
|
|
41
|
+
const onConfirm = () => {
|
|
42
|
+
patchOverlayState({ confirm: null })
|
|
43
|
+
req.onConfirm()
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const onCancel = () => patchOverlayState({ confirm: null })
|
|
47
|
+
|
|
48
|
+
return (
|
|
49
|
+
<Box flexDirection="column" flexShrink={0} paddingX={1} paddingY={1}>
|
|
50
|
+
<ConfirmPrompt onCancel={onCancel} onConfirm={onConfirm} req={req} t={theme} />
|
|
51
|
+
</Box>
|
|
52
|
+
)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (overlay.clarify) {
|
|
56
|
+
return (
|
|
57
|
+
<Box flexDirection="column" flexShrink={0} paddingX={1} paddingY={1}>
|
|
58
|
+
<ClarifyPrompt
|
|
59
|
+
cols={cols}
|
|
60
|
+
onAnswer={onClarifyAnswer}
|
|
61
|
+
onCancel={() => onClarifyAnswer('')}
|
|
62
|
+
req={overlay.clarify}
|
|
63
|
+
t={theme}
|
|
64
|
+
/>
|
|
65
|
+
</Box>
|
|
66
|
+
)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (overlay.sudo) {
|
|
70
|
+
return (
|
|
71
|
+
<Box flexDirection="column" flexShrink={0} paddingX={1} paddingY={1}>
|
|
72
|
+
<MaskedPrompt cols={cols} icon="🔐" label="sudo password required" onSubmit={onSudoSubmit} t={theme} />
|
|
73
|
+
</Box>
|
|
74
|
+
)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (overlay.secret) {
|
|
78
|
+
return (
|
|
79
|
+
<Box flexDirection="column" flexShrink={0} paddingX={1} paddingY={1}>
|
|
80
|
+
<MaskedPrompt
|
|
81
|
+
cols={cols}
|
|
82
|
+
icon="🔑"
|
|
83
|
+
label={overlay.secret.prompt}
|
|
84
|
+
onSubmit={onSecretSubmit}
|
|
85
|
+
sub={`for ${overlay.secret.envVar}`}
|
|
86
|
+
t={theme}
|
|
87
|
+
/>
|
|
88
|
+
</Box>
|
|
89
|
+
)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return null
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function FloatingOverlays({
|
|
96
|
+
cols,
|
|
97
|
+
compIdx,
|
|
98
|
+
completions,
|
|
99
|
+
onActiveSessionSelect,
|
|
100
|
+
onActiveSessionClose,
|
|
101
|
+
onModelSelect,
|
|
102
|
+
onNewLiveSession,
|
|
103
|
+
onNewPromptSession,
|
|
104
|
+
onPickerSelect,
|
|
105
|
+
pagerPageSize
|
|
106
|
+
}: Pick<
|
|
107
|
+
AppOverlaysProps,
|
|
108
|
+
| 'cols'
|
|
109
|
+
| 'compIdx'
|
|
110
|
+
| 'completions'
|
|
111
|
+
| 'onActiveSessionSelect'
|
|
112
|
+
| 'onActiveSessionClose'
|
|
113
|
+
| 'onModelSelect'
|
|
114
|
+
| 'onNewLiveSession'
|
|
115
|
+
| 'onNewPromptSession'
|
|
116
|
+
| 'onPickerSelect'
|
|
117
|
+
| 'pagerPageSize'
|
|
118
|
+
>) {
|
|
119
|
+
const { gw } = useGateway()
|
|
120
|
+
const overlay = useStore($overlayState)
|
|
121
|
+
const sid = useStore($uiSessionId)
|
|
122
|
+
const theme = useStore($uiTheme)
|
|
123
|
+
|
|
124
|
+
const hasAny =
|
|
125
|
+
overlay.modelPicker ||
|
|
126
|
+
overlay.pager ||
|
|
127
|
+
overlay.picker ||
|
|
128
|
+
overlay.sessions ||
|
|
129
|
+
overlay.skillsHub ||
|
|
130
|
+
completions.length
|
|
131
|
+
|
|
132
|
+
if (!hasAny) {
|
|
133
|
+
return null
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Fixed viewport centered on compIdx — previously the slice end was
|
|
137
|
+
// compIdx + 8 so the dropdown grew from 8 rows to 16 as the user scrolled
|
|
138
|
+
// down, bouncing the height on every keystroke.
|
|
139
|
+
const viewportSize = Math.min(COMPLETION_WINDOW, completions.length)
|
|
140
|
+
|
|
141
|
+
const start = Math.max(0, Math.min(compIdx - Math.floor(COMPLETION_WINDOW / 2), completions.length - viewportSize))
|
|
142
|
+
|
|
143
|
+
return (
|
|
144
|
+
<Box alignItems="flex-start" bottom="100%" flexDirection="column" left={0} position="absolute" right={0}>
|
|
145
|
+
{overlay.picker && (
|
|
146
|
+
<FloatBox color={theme.color.border}>
|
|
147
|
+
<SessionPicker
|
|
148
|
+
gw={gw}
|
|
149
|
+
onCancel={() => patchOverlayState({ picker: false })}
|
|
150
|
+
onSelect={onPickerSelect}
|
|
151
|
+
t={theme}
|
|
152
|
+
/>
|
|
153
|
+
</FloatBox>
|
|
154
|
+
)}
|
|
155
|
+
|
|
156
|
+
{overlay.sessions && (
|
|
157
|
+
<FloatBox color={theme.color.border}>
|
|
158
|
+
<ActiveSessionSwitcher
|
|
159
|
+
currentSessionId={sid}
|
|
160
|
+
gw={gw}
|
|
161
|
+
onCancel={() => patchOverlayState({ sessions: false })}
|
|
162
|
+
onClose={onActiveSessionClose}
|
|
163
|
+
onNew={onNewLiveSession}
|
|
164
|
+
onNewPrompt={onNewPromptSession}
|
|
165
|
+
onSelect={onActiveSessionSelect}
|
|
166
|
+
t={theme}
|
|
167
|
+
/>
|
|
168
|
+
</FloatBox>
|
|
169
|
+
)}
|
|
170
|
+
|
|
171
|
+
{overlay.modelPicker && (
|
|
172
|
+
<FloatBox color={theme.color.border}>
|
|
173
|
+
<ModelPicker
|
|
174
|
+
gw={gw}
|
|
175
|
+
onCancel={() => patchOverlayState({ modelPicker: false })}
|
|
176
|
+
onSelect={onModelSelect}
|
|
177
|
+
sessionId={sid}
|
|
178
|
+
t={theme}
|
|
179
|
+
/>
|
|
180
|
+
</FloatBox>
|
|
181
|
+
)}
|
|
182
|
+
|
|
183
|
+
{overlay.skillsHub && (
|
|
184
|
+
<FloatBox color={theme.color.border}>
|
|
185
|
+
<SkillsHub gw={gw} onClose={() => patchOverlayState({ skillsHub: false })} t={theme} />
|
|
186
|
+
</FloatBox>
|
|
187
|
+
)}
|
|
188
|
+
|
|
189
|
+
{overlay.pager && (
|
|
190
|
+
<FloatBox color={theme.color.border}>
|
|
191
|
+
<Box flexDirection="column" paddingX={1} paddingY={1}>
|
|
192
|
+
{overlay.pager.title && (
|
|
193
|
+
<Box justifyContent="center" marginBottom={1}>
|
|
194
|
+
<Text bold color={theme.color.primary}>
|
|
195
|
+
{overlay.pager.title}
|
|
196
|
+
</Text>
|
|
197
|
+
</Box>
|
|
198
|
+
)}
|
|
199
|
+
|
|
200
|
+
{overlay.pager.lines.slice(overlay.pager.offset, overlay.pager.offset + pagerPageSize).map((line, i) => (
|
|
201
|
+
<Text key={i}>{line}</Text>
|
|
202
|
+
))}
|
|
203
|
+
|
|
204
|
+
<Box marginTop={1}>
|
|
205
|
+
<OverlayHint t={theme}>
|
|
206
|
+
{overlay.pager.offset + pagerPageSize < overlay.pager.lines.length
|
|
207
|
+
? `↑↓/jk line · Enter/Space/PgDn page · b/PgUp back · g/G top/bottom · Esc/q close (${Math.min(overlay.pager.offset + pagerPageSize, overlay.pager.lines.length)}/${overlay.pager.lines.length})`
|
|
208
|
+
: `end · ↑↓/jk · b/PgUp back · g top · Esc/q close (${overlay.pager.lines.length} lines)`}
|
|
209
|
+
</OverlayHint>
|
|
210
|
+
</Box>
|
|
211
|
+
</Box>
|
|
212
|
+
</FloatBox>
|
|
213
|
+
)}
|
|
214
|
+
|
|
215
|
+
{!!completions.length && (
|
|
216
|
+
<FloatBox color={theme.color.primary}>
|
|
217
|
+
<Box flexDirection="column" width={Math.max(28, cols - 6)}>
|
|
218
|
+
{completions.slice(start, start + viewportSize).map((item, i) => {
|
|
219
|
+
const active = start + i === compIdx
|
|
220
|
+
|
|
221
|
+
return (
|
|
222
|
+
<Box
|
|
223
|
+
backgroundColor={active ? theme.color.completionCurrentBg : theme.color.completionBg}
|
|
224
|
+
flexDirection="row"
|
|
225
|
+
key={`${start + i}:${item.text}:${item.display}:${item.meta ?? ''}`}
|
|
226
|
+
width="100%"
|
|
227
|
+
>
|
|
228
|
+
{/* flexShrink=0 — when meta overflows the row, Ink/Yoga
|
|
229
|
+
otherwise shaves the last char off the display column
|
|
230
|
+
(e.g. /goal renders as /goa). */}
|
|
231
|
+
<Box flexShrink={0}>
|
|
232
|
+
<Text bold color={theme.color.label}>
|
|
233
|
+
{' '}
|
|
234
|
+
{item.display}
|
|
235
|
+
</Text>
|
|
236
|
+
</Box>
|
|
237
|
+
{item.meta ? (
|
|
238
|
+
<Text
|
|
239
|
+
backgroundColor={active ? theme.color.completionMetaCurrentBg : theme.color.completionMetaBg}
|
|
240
|
+
color={theme.color.muted}
|
|
241
|
+
>
|
|
242
|
+
{' '}
|
|
243
|
+
{item.meta}
|
|
244
|
+
</Text>
|
|
245
|
+
) : null}
|
|
246
|
+
</Box>
|
|
247
|
+
)
|
|
248
|
+
})}
|
|
249
|
+
</Box>
|
|
250
|
+
</FloatBox>
|
|
251
|
+
)}
|
|
252
|
+
</Box>
|
|
253
|
+
)
|
|
254
|
+
}
|