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,131 @@
|
|
|
1
|
+
import { lastGrapheme } from './intl.js'
|
|
2
|
+
let earlyInputBuffer = ''
|
|
3
|
+
let isCapturing = false
|
|
4
|
+
let readableHandler: (() => void) | null = null
|
|
5
|
+
|
|
6
|
+
export function startCapturingEarlyInput(): void {
|
|
7
|
+
if (!process.stdin.isTTY || isCapturing || process.argv.includes('-p') || process.argv.includes('--print')) {
|
|
8
|
+
return
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
isCapturing = true
|
|
12
|
+
earlyInputBuffer = ''
|
|
13
|
+
|
|
14
|
+
try {
|
|
15
|
+
process.stdin.setEncoding('utf8')
|
|
16
|
+
process.stdin.setRawMode(true)
|
|
17
|
+
process.stdin.ref()
|
|
18
|
+
|
|
19
|
+
readableHandler = () => {
|
|
20
|
+
let chunk = process.stdin.read()
|
|
21
|
+
|
|
22
|
+
while (chunk !== null) {
|
|
23
|
+
if (typeof chunk === 'string') {
|
|
24
|
+
processChunk(chunk)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
chunk = process.stdin.read()
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
process.stdin.on('readable', readableHandler)
|
|
32
|
+
} catch {
|
|
33
|
+
isCapturing = false
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function processChunk(str: string): void {
|
|
38
|
+
let i = 0
|
|
39
|
+
|
|
40
|
+
while (i < str.length) {
|
|
41
|
+
const char = str[i]!
|
|
42
|
+
const code = char.charCodeAt(0)
|
|
43
|
+
|
|
44
|
+
if (code === 3) {
|
|
45
|
+
stopCapturingEarlyInput()
|
|
46
|
+
process.exit(130)
|
|
47
|
+
|
|
48
|
+
return
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (code === 4) {
|
|
52
|
+
stopCapturingEarlyInput()
|
|
53
|
+
|
|
54
|
+
return
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (code === 127 || code === 8) {
|
|
58
|
+
if (earlyInputBuffer.length > 0) {
|
|
59
|
+
const last = lastGrapheme(earlyInputBuffer)
|
|
60
|
+
earlyInputBuffer = earlyInputBuffer.slice(0, -(last.length || 1))
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
i++
|
|
64
|
+
|
|
65
|
+
continue
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (code === 27) {
|
|
69
|
+
i++
|
|
70
|
+
|
|
71
|
+
while (i < str.length && !(str.charCodeAt(i) >= 64 && str.charCodeAt(i) <= 126)) {
|
|
72
|
+
i++
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (i < str.length) {
|
|
76
|
+
i++
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
continue
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (code < 32 && code !== 9 && code !== 10 && code !== 13) {
|
|
83
|
+
i++
|
|
84
|
+
|
|
85
|
+
continue
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (code === 13) {
|
|
89
|
+
earlyInputBuffer += '\n'
|
|
90
|
+
i++
|
|
91
|
+
|
|
92
|
+
continue
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
earlyInputBuffer += char
|
|
96
|
+
i++
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function stopCapturingEarlyInput(): void {
|
|
101
|
+
if (!isCapturing) {
|
|
102
|
+
return
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
isCapturing = false
|
|
106
|
+
|
|
107
|
+
if (readableHandler) {
|
|
108
|
+
process.stdin.removeListener('readable', readableHandler)
|
|
109
|
+
readableHandler = null
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export function consumeEarlyInput(): string {
|
|
114
|
+
stopCapturingEarlyInput()
|
|
115
|
+
const input = earlyInputBuffer.trim()
|
|
116
|
+
earlyInputBuffer = ''
|
|
117
|
+
|
|
118
|
+
return input
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export function hasEarlyInput(): boolean {
|
|
122
|
+
return earlyInputBuffer.trim().length > 0
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export function seedEarlyInput(text: string): void {
|
|
126
|
+
earlyInputBuffer = text
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export function isCapturingEarlyInput(): boolean {
|
|
130
|
+
return isCapturing
|
|
131
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
type TerminalName = string | null
|
|
2
|
+
|
|
3
|
+
function detectTerminal(): TerminalName {
|
|
4
|
+
if (process.env.CURSOR_TRACE_ID) {
|
|
5
|
+
return 'cursor'
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
if (process.env.TERM === 'xterm-ghostty') {
|
|
9
|
+
return 'ghostty'
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
if (process.env.TERM?.includes('kitty')) {
|
|
13
|
+
return 'kitty'
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
if (process.env.TERM_PROGRAM) {
|
|
17
|
+
return process.env.TERM_PROGRAM
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (process.env.TMUX) {
|
|
21
|
+
return 'tmux'
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (process.env.STY) {
|
|
25
|
+
return 'screen'
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (process.env.KITTY_WINDOW_ID) {
|
|
29
|
+
return 'kitty'
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (process.env.WT_SESSION) {
|
|
33
|
+
return 'windows-terminal'
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return process.env.TERM ?? null
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export const env = {
|
|
40
|
+
terminal: detectTerminal()
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Terminals known to correctly implement OSC 52 clipboard writes
|
|
44
|
+
// (ESC ] 52 ; c ; <b64> BEL/ST — osc() in ink/termio/osc.ts emits BEL
|
|
45
|
+
// for most terminals and ST for kitty). When detected, setClipboard() skips the
|
|
46
|
+
// native-tool safety net entirely — running wl-copy/xclip/pbcopy in
|
|
47
|
+
// parallel with OSC 52 races the terminal's own clipboard write and can
|
|
48
|
+
// corrupt it (e.g. wl-copy on Wayland holds the selection in a background
|
|
49
|
+
// daemon; stacking two writes within ~30ms triggers a SIGTERM race).
|
|
50
|
+
// Intentionally conservative: terminals with known flaky or disabled-by-
|
|
51
|
+
// default OSC 52 (iTerm2 disables OSC 52 by default; Alacritty detection
|
|
52
|
+
// is unreliable) are not on this list. Users on those terminals keep the
|
|
53
|
+
// existing behaviour (native safety net fires alongside OSC 52).
|
|
54
|
+
//
|
|
55
|
+
// Lives here in utils/env.ts (rather than ink/terminal.ts) so that
|
|
56
|
+
// ink/termio/osc.ts can import it without creating a circular dependency:
|
|
57
|
+
// ink/terminal.ts already imports `link` from ink/termio/osc.ts.
|
|
58
|
+
const OSC52_CAPABLE_TERMINALS = ['ghostty', 'kitty', 'WezTerm', 'windows-terminal', 'vscode']
|
|
59
|
+
|
|
60
|
+
/** True if this terminal is known to correctly handle OSC 52 clipboard
|
|
61
|
+
* writes, so setClipboard() can skip the native-tool safety net.
|
|
62
|
+
* Accepts an optional terminal name for testability; defaults to the
|
|
63
|
+
* module-level `env.terminal` detected at startup. */
|
|
64
|
+
export function supportsOsc52Clipboard(terminal: string | null = env.terminal): boolean {
|
|
65
|
+
return OSC52_CAPABLE_TERMINALS.includes(terminal ?? '')
|
|
66
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export function isEnvTruthy(envVar: string | boolean | undefined): boolean {
|
|
2
|
+
if (!envVar) {
|
|
3
|
+
return false
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
if (typeof envVar === 'boolean') {
|
|
7
|
+
return envVar
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const v = envVar.toLowerCase().trim()
|
|
11
|
+
|
|
12
|
+
return ['1', 'true', 'yes', 'on'].includes(v)
|
|
13
|
+
}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { chmodSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
|
|
2
|
+
import { tmpdir } from 'node:os'
|
|
3
|
+
import { join } from 'node:path'
|
|
4
|
+
|
|
5
|
+
import { afterEach, beforeEach, describe, expect, it } from 'vitest'
|
|
6
|
+
|
|
7
|
+
import { execFileNoThrow } from './execFileNoThrow.js'
|
|
8
|
+
|
|
9
|
+
// These tests shell out to /bin/sh, use chmodSync(0o755), and rely on
|
|
10
|
+
// POSIX sleep/job control. They will not work on Windows.
|
|
11
|
+
const onWindows = process.platform === 'win32'
|
|
12
|
+
|
|
13
|
+
// We simulate `wl-copy`'s daemonization behavior with a tiny shell script:
|
|
14
|
+
// 1. Fork a short-lived background sleeper that inherits stdio (so the
|
|
15
|
+
// parent process's pipes can never close).
|
|
16
|
+
// 2. Record the sleeper PID to a file so afterEach can clean it up.
|
|
17
|
+
// 3. Exit immediately with status 0.
|
|
18
|
+
//
|
|
19
|
+
// Without resolveOnExit, the await on `'close'` hangs until SIGTERM at
|
|
20
|
+
// timeout — exactly the production wl-copy bug. With resolveOnExit, the
|
|
21
|
+
// promise settles on `'exit'` regardless of the inherited pipes.
|
|
22
|
+
|
|
23
|
+
let scriptDir: string
|
|
24
|
+
let daemonScript: string
|
|
25
|
+
let sleeperPids: number[]
|
|
26
|
+
|
|
27
|
+
/** Read the PID file the daemon script writes, and track it for afterEach cleanup. */
|
|
28
|
+
function trackSleeperPid(pidFile: string): void {
|
|
29
|
+
try {
|
|
30
|
+
const pid = parseInt(readFileSync(pidFile, 'utf8').trim(), 10)
|
|
31
|
+
if (pid > 0) {
|
|
32
|
+
sleeperPids.push(pid)
|
|
33
|
+
}
|
|
34
|
+
} catch {
|
|
35
|
+
// PID file not written or unreadable — sleeper may have already exited.
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
beforeEach(() => {
|
|
40
|
+
sleeperPids = []
|
|
41
|
+
scriptDir = join(tmpdir(), `nastech-execfile-test-${process.pid}-${Date.now()}`)
|
|
42
|
+
mkdirSync(scriptDir, { recursive: true })
|
|
43
|
+
daemonScript = join(scriptDir, 'fake-daemonizer.sh')
|
|
44
|
+
// Posix sh: the `sleep 3 &` child inherits stdin/stdout/stderr from the
|
|
45
|
+
// shell, which inherited them from `spawn(stdio: 'pipe')`. The shell
|
|
46
|
+
// exits but its child (the sleeper) keeps the pipes open. Mirrors how
|
|
47
|
+
// wl-copy double-forks then exits while the daemon holds the selection.
|
|
48
|
+
// The sleeper writes its PID to $1 so we can clean it up reliably.
|
|
49
|
+
writeFileSync(daemonScript, '#!/bin/sh\nsleep 3 &\necho $! > "$1"\nexit 0\n')
|
|
50
|
+
chmodSync(daemonScript, 0o755)
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
afterEach(() => {
|
|
54
|
+
// Kill orphaned sleepers so they don't accumulate across watch runs.
|
|
55
|
+
for (const pid of sleeperPids) {
|
|
56
|
+
try {
|
|
57
|
+
process.kill(pid, 'SIGKILL')
|
|
58
|
+
} catch {
|
|
59
|
+
// Already exited — fine.
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
rmSync(scriptDir, { recursive: true, force: true })
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
describe.skipIf(onWindows)('execFileNoThrow with daemon-style children', () => {
|
|
66
|
+
// Skipped because the bug it documents is a forever-hang. Without
|
|
67
|
+
// resolveOnExit, the 'close' event doesn't fire when the immediate
|
|
68
|
+
// child has exited but a forked daemon still holds stdio open. Even
|
|
69
|
+
// SIGTERM at the timeout doesn't help — the daemon survives it. To
|
|
70
|
+
// verify by hand: remove `it.skip` and watch the test timeout. This
|
|
71
|
+
// test is here so a reviewer reading the resolveOnExit option knows
|
|
72
|
+
// *why* every clipboard-tool spawn in osc.ts wires it on.
|
|
73
|
+
it.skip("(documented hang) without resolveOnExit, await never resolves when daemon inherits stdio", async () => {
|
|
74
|
+
const pidFile = join(scriptDir, 'sleeper-skip.pid')
|
|
75
|
+
const result = await execFileNoThrow(daemonScript, [pidFile], { timeout: 300 })
|
|
76
|
+
trackSleeperPid(pidFile)
|
|
77
|
+
|
|
78
|
+
expect(result.code).toBe(124)
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
it("settles immediately on 'exit' when resolveOnExit is true, regardless of daemon stdio", async () => {
|
|
82
|
+
const pidFile = join(scriptDir, 'sleeper-exit.pid')
|
|
83
|
+
const start = Date.now()
|
|
84
|
+
|
|
85
|
+
const result = await execFileNoThrow(daemonScript, [pidFile], {
|
|
86
|
+
timeout: 2000,
|
|
87
|
+
resolveOnExit: true
|
|
88
|
+
})
|
|
89
|
+
trackSleeperPid(pidFile)
|
|
90
|
+
|
|
91
|
+
const elapsed = Date.now() - start
|
|
92
|
+
|
|
93
|
+
// The shell exits in a few ms. resolveOnExit lets us return on exit
|
|
94
|
+
// (code 0) instead of waiting for the orphaned sleeper to release
|
|
95
|
+
// stdio. Should be well under 200ms even on slow CI.
|
|
96
|
+
expect(result.code).toBe(0)
|
|
97
|
+
expect(elapsed).toBeLessThan(500)
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
it("still surfaces the right code when resolveOnExit'd child exits non-zero", async () => {
|
|
101
|
+
const pidFile = join(scriptDir, 'sleeper-fail.pid')
|
|
102
|
+
const failScript = join(scriptDir, 'fail.sh')
|
|
103
|
+
writeFileSync(failScript, `#!/bin/sh\nsleep 3 &\necho $! > "${pidFile}"\nexit 7\n`)
|
|
104
|
+
chmodSync(failScript, 0o755)
|
|
105
|
+
|
|
106
|
+
const result = await execFileNoThrow(failScript, [], {
|
|
107
|
+
timeout: 2000,
|
|
108
|
+
resolveOnExit: true
|
|
109
|
+
})
|
|
110
|
+
trackSleeperPid(pidFile)
|
|
111
|
+
|
|
112
|
+
expect(result.code).toBe(7)
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
it('settles on timeout=124 when the child itself never exits, even with resolveOnExit', async () => {
|
|
116
|
+
const slowScript = join(scriptDir, 'slow.sh')
|
|
117
|
+
writeFileSync(slowScript, '#!/bin/sh\nsleep 30\n')
|
|
118
|
+
chmodSync(slowScript, 0o755)
|
|
119
|
+
|
|
120
|
+
const result = await execFileNoThrow(slowScript, [], {
|
|
121
|
+
timeout: 200,
|
|
122
|
+
resolveOnExit: true
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
// Child process never exits on its own → timer fires → SIGTERM →
|
|
126
|
+
// child exits → 'exit' fires with non-null signal. The settle()
|
|
127
|
+
// call from the timer registers code=124 first. Either way: 124.
|
|
128
|
+
expect(result.code).toBe(124)
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
it('does not double-resolve when both timer and exit fire', async () => {
|
|
132
|
+
const pidFile = join(scriptDir, 'sleeper-race.pid')
|
|
133
|
+
// Race: child happens to exit right around the timeout. The settled
|
|
134
|
+
// guard ensures only the first resolution wins.
|
|
135
|
+
const result = await execFileNoThrow(daemonScript, [pidFile], {
|
|
136
|
+
timeout: 50, // very tight
|
|
137
|
+
resolveOnExit: true
|
|
138
|
+
})
|
|
139
|
+
trackSleeperPid(pidFile)
|
|
140
|
+
|
|
141
|
+
// Either code=0 (exit beat timer) or code=124 (timer beat exit).
|
|
142
|
+
// Both are valid outcomes; the contract is that the promise settles
|
|
143
|
+
// exactly once and doesn't throw.
|
|
144
|
+
expect([0, 124]).toContain(result.code)
|
|
145
|
+
})
|
|
146
|
+
})
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { spawn, type ChildProcess, type StdioOptions } from 'child_process'
|
|
2
|
+
type ExecFileOptions = {
|
|
3
|
+
input?: string
|
|
4
|
+
timeout?: number
|
|
5
|
+
useCwd?: boolean
|
|
6
|
+
env?: NodeJS.ProcessEnv
|
|
7
|
+
/** Resolve as soon as the child *exits*, instead of waiting for its
|
|
8
|
+
* stdio streams to close. Use this for tools that fork a daemon and
|
|
9
|
+
* let the daemon inherit the parent's stdio (e.g. `wl-copy`): the
|
|
10
|
+
* child exits immediately, but `'close'` never fires because the
|
|
11
|
+
* daemon holds the pipes open.
|
|
12
|
+
*
|
|
13
|
+
* When true, stdout and stderr are set to 'ignore' to prevent the
|
|
14
|
+
* daemon from inheriting those pipe FDs — the caller must not
|
|
15
|
+
* depend on collecting stdout/stderr content. Both will always be
|
|
16
|
+
* empty strings in this mode. */
|
|
17
|
+
resolveOnExit?: boolean
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function execFileNoThrow(
|
|
21
|
+
file: string,
|
|
22
|
+
args: string[],
|
|
23
|
+
options: ExecFileOptions = {}
|
|
24
|
+
): Promise<{
|
|
25
|
+
stdout: string
|
|
26
|
+
stderr: string
|
|
27
|
+
code: number
|
|
28
|
+
error?: string
|
|
29
|
+
}> {
|
|
30
|
+
return new Promise(resolve => {
|
|
31
|
+
// When resolveOnExit is true, ignore stdout/stderr so the daemon
|
|
32
|
+
// doesn't inherit those pipe FDs — prevents handle leaks that can
|
|
33
|
+
// keep the parent process alive. No output data is collected in
|
|
34
|
+
// this mode; both stdout and stderr will be empty strings.
|
|
35
|
+
const stdioConfig: StdioOptions = options.resolveOnExit
|
|
36
|
+
? ['pipe', 'ignore', 'ignore']
|
|
37
|
+
: 'pipe'
|
|
38
|
+
|
|
39
|
+
const child: ChildProcess = spawn(file, args, {
|
|
40
|
+
cwd: options.useCwd ? process.cwd() : undefined,
|
|
41
|
+
env: options.env,
|
|
42
|
+
stdio: stdioConfig
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
let stdout = ''
|
|
46
|
+
let stderr = ''
|
|
47
|
+
let timedOut = false
|
|
48
|
+
let settled = false
|
|
49
|
+
|
|
50
|
+
const settle = (code: number, error?: string) => {
|
|
51
|
+
if (settled) {
|
|
52
|
+
return
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
settled = true
|
|
56
|
+
|
|
57
|
+
if (timer) {
|
|
58
|
+
clearTimeout(timer)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Destroy any remaining streams to release FDs promptly.
|
|
62
|
+
// After settle(), nobody reads from these anymore.
|
|
63
|
+
child.stdout?.destroy()
|
|
64
|
+
child.stderr?.destroy()
|
|
65
|
+
|
|
66
|
+
resolve({ stdout, stderr, code, ...(error ? { error } : {}) })
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const timer = options.timeout
|
|
70
|
+
? setTimeout(() => {
|
|
71
|
+
timedOut = true
|
|
72
|
+
child.kill('SIGTERM')
|
|
73
|
+
|
|
74
|
+
// When resolving on exit, SIGTERM-ing a child that has already
|
|
75
|
+
// exited is a no-op and `'exit'` won't fire again — settle here
|
|
76
|
+
// so the promise doesn't leak. Safe under settled-guard.
|
|
77
|
+
if (options.resolveOnExit) {
|
|
78
|
+
settle(124)
|
|
79
|
+
}
|
|
80
|
+
}, options.timeout)
|
|
81
|
+
: null
|
|
82
|
+
|
|
83
|
+
child.stdout?.on('data', chunk => {
|
|
84
|
+
stdout += String(chunk)
|
|
85
|
+
})
|
|
86
|
+
child.stderr?.on('data', chunk => {
|
|
87
|
+
stderr += String(chunk)
|
|
88
|
+
})
|
|
89
|
+
child.on('error', error => {
|
|
90
|
+
settle(1, String(error))
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
if (options.resolveOnExit) {
|
|
94
|
+
// 'exit' fires when the child process itself exits — even if the
|
|
95
|
+
// daemon it forked still holds the inherited stdio pipes open.
|
|
96
|
+
// When a signal kills the child, code is null — map that to 1
|
|
97
|
+
// so callers don't mistake a signal-terminated run for success.
|
|
98
|
+
child.on('exit', (code, signal) => {
|
|
99
|
+
const exitCode = timedOut ? 124 : (code ?? (signal ? 1 : 0))
|
|
100
|
+
settle(exitCode)
|
|
101
|
+
})
|
|
102
|
+
} else {
|
|
103
|
+
child.on('close', (code, signal) => {
|
|
104
|
+
const exitCode = timedOut ? 124 : (code ?? (signal ? 1 : 0))
|
|
105
|
+
settle(exitCode)
|
|
106
|
+
})
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (options.input) {
|
|
110
|
+
child.stdin?.write(options.input)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
child.stdin?.end()
|
|
114
|
+
})
|
|
115
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
let graphemeSegmenter: Intl.Segmenter | null = null
|
|
2
|
+
let wordSegmenter: Intl.Segmenter | null = null
|
|
3
|
+
|
|
4
|
+
export function getGraphemeSegmenter(): Intl.Segmenter {
|
|
5
|
+
if (!graphemeSegmenter) {
|
|
6
|
+
graphemeSegmenter = new Intl.Segmenter(undefined, {
|
|
7
|
+
granularity: 'grapheme'
|
|
8
|
+
})
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
return graphemeSegmenter
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function firstGrapheme(text: string): string {
|
|
15
|
+
if (!text) {
|
|
16
|
+
return ''
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const segments = getGraphemeSegmenter().segment(text)
|
|
20
|
+
const first = segments[Symbol.iterator]().next().value
|
|
21
|
+
|
|
22
|
+
return first?.segment ?? ''
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function lastGrapheme(text: string): string {
|
|
26
|
+
if (!text) {
|
|
27
|
+
return ''
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
let last = ''
|
|
31
|
+
|
|
32
|
+
for (const { segment } of getGraphemeSegmenter().segment(text)) {
|
|
33
|
+
last = segment
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return last
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function getWordSegmenter(): Intl.Segmenter {
|
|
40
|
+
if (!wordSegmenter) {
|
|
41
|
+
wordSegmenter = new Intl.Segmenter(undefined, { granularity: 'word' })
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return wordSegmenter
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const rtfCache = new Map<string, Intl.RelativeTimeFormat>()
|
|
48
|
+
|
|
49
|
+
export function getRelativeTimeFormat(
|
|
50
|
+
style: 'long' | 'short' | 'narrow',
|
|
51
|
+
numeric: 'always' | 'auto'
|
|
52
|
+
): Intl.RelativeTimeFormat {
|
|
53
|
+
const key = `${style}:${numeric}`
|
|
54
|
+
let rtf = rtfCache.get(key)
|
|
55
|
+
|
|
56
|
+
if (!rtf) {
|
|
57
|
+
rtf = new Intl.RelativeTimeFormat('en', { style, numeric })
|
|
58
|
+
rtfCache.set(key, rtf)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return rtf
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
let cachedTimeZone: string | null = null
|
|
65
|
+
|
|
66
|
+
export function getTimeZone(): string {
|
|
67
|
+
if (!cachedTimeZone) {
|
|
68
|
+
cachedTimeZone = Intl.DateTimeFormat().resolvedOptions().timeZone
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return cachedTimeZone
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
let cachedSystemLocaleLanguage: string | undefined | null = null
|
|
75
|
+
|
|
76
|
+
export function getSystemLocaleLanguage(): string | undefined {
|
|
77
|
+
if (cachedSystemLocaleLanguage === null) {
|
|
78
|
+
try {
|
|
79
|
+
const locale = Intl.DateTimeFormat().resolvedOptions().locale
|
|
80
|
+
cachedSystemLocaleLanguage = new Intl.Locale(locale).language
|
|
81
|
+
} catch {
|
|
82
|
+
cachedSystemLocaleLanguage = undefined
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return cachedSystemLocaleLanguage
|
|
87
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
let _npmSemver: typeof import('semver') | undefined
|
|
2
|
+
|
|
3
|
+
function getNpmSemver(): typeof import('semver') {
|
|
4
|
+
if (!_npmSemver) {
|
|
5
|
+
_npmSemver = require('semver') as typeof import('semver')
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
return _npmSemver
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function gt(a: string, b: string): boolean {
|
|
12
|
+
if (typeof Bun !== 'undefined') {
|
|
13
|
+
return Bun.semver.order(a, b) === 1
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return getNpmSemver().gt(a, b, { loose: true })
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function gte(a: string, b: string): boolean {
|
|
20
|
+
if (typeof Bun !== 'undefined') {
|
|
21
|
+
return Bun.semver.order(a, b) >= 0
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return getNpmSemver().gte(a, b, { loose: true })
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function lt(a: string, b: string): boolean {
|
|
28
|
+
if (typeof Bun !== 'undefined') {
|
|
29
|
+
return Bun.semver.order(a, b) === -1
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return getNpmSemver().lt(a, b, { loose: true })
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function lte(a: string, b: string): boolean {
|
|
36
|
+
if (typeof Bun !== 'undefined') {
|
|
37
|
+
return Bun.semver.order(a, b) <= 0
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return getNpmSemver().lte(a, b, { loose: true })
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function satisfies(version: string, range: string): boolean {
|
|
44
|
+
if (typeof Bun !== 'undefined') {
|
|
45
|
+
return Bun.semver.satisfies(version, range)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return getNpmSemver().satisfies(version, range, { loose: true })
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function order(a: string, b: string): -1 | 0 | 1 {
|
|
52
|
+
if (typeof Bun !== 'undefined') {
|
|
53
|
+
return Bun.semver.order(a, b)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return getNpmSemver().compare(a, b, { loose: true }) as -1 | 0 | 1
|
|
57
|
+
}
|