@yjjeong/omg 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +85 -0
- package/LICENSE +21 -0
- package/README.md +153 -0
- package/dist/cli/args.d.ts +39 -0
- package/dist/cli/args.d.ts.map +1 -0
- package/dist/cli/args.js +129 -0
- package/dist/cli/args.js.map +1 -0
- package/dist/cli/autoCommit.d.ts +20 -0
- package/dist/cli/autoCommit.d.ts.map +1 -0
- package/dist/cli/autoCommit.js +121 -0
- package/dist/cli/autoCommit.js.map +1 -0
- package/dist/cli/bgTasks.d.ts +57 -0
- package/dist/cli/bgTasks.d.ts.map +1 -0
- package/dist/cli/bgTasks.js +193 -0
- package/dist/cli/bgTasks.js.map +1 -0
- package/dist/cli/compactor.d.ts +48 -0
- package/dist/cli/compactor.d.ts.map +1 -0
- package/dist/cli/compactor.js +73 -0
- package/dist/cli/compactor.js.map +1 -0
- package/dist/cli/doctor.d.ts +77 -0
- package/dist/cli/doctor.d.ts.map +1 -0
- package/dist/cli/doctor.js +279 -0
- package/dist/cli/doctor.js.map +1 -0
- package/dist/cli/gitStatus.d.ts +7 -0
- package/dist/cli/gitStatus.d.ts.map +1 -0
- package/dist/cli/gitStatus.js +70 -0
- package/dist/cli/gitStatus.js.map +1 -0
- package/dist/cli/handoff.d.ts +36 -0
- package/dist/cli/handoff.d.ts.map +1 -0
- package/dist/cli/handoff.js +60 -0
- package/dist/cli/handoff.js.map +1 -0
- package/dist/cli/interactive/brokers.d.ts +31 -0
- package/dist/cli/interactive/brokers.d.ts.map +1 -0
- package/dist/cli/interactive/brokers.js +259 -0
- package/dist/cli/interactive/brokers.js.map +1 -0
- package/dist/cli/interactive/index.d.ts +8 -0
- package/dist/cli/interactive/index.d.ts.map +1 -0
- package/dist/cli/interactive/index.js +731 -0
- package/dist/cli/interactive/index.js.map +1 -0
- package/dist/cli/interactive/proxyQuestions.d.ts +15 -0
- package/dist/cli/interactive/proxyQuestions.d.ts.map +1 -0
- package/dist/cli/interactive/proxyQuestions.js +48 -0
- package/dist/cli/interactive/proxyQuestions.js.map +1 -0
- package/dist/cli/interactive/tabManager.d.ts +32 -0
- package/dist/cli/interactive/tabManager.d.ts.map +1 -0
- package/dist/cli/interactive/tabManager.js +244 -0
- package/dist/cli/interactive/tabManager.js.map +1 -0
- package/dist/cli/interactive/teamHint.d.ts +2 -0
- package/dist/cli/interactive/teamHint.d.ts.map +1 -0
- package/dist/cli/interactive/teamHint.js +14 -0
- package/dist/cli/interactive/teamHint.js.map +1 -0
- package/dist/cli/login.d.ts +13 -0
- package/dist/cli/login.d.ts.map +1 -0
- package/dist/cli/login.js +53 -0
- package/dist/cli/login.js.map +1 -0
- package/dist/cli/modes/demo.d.ts +12 -0
- package/dist/cli/modes/demo.d.ts.map +1 -0
- package/dist/cli/modes/demo.js +48 -0
- package/dist/cli/modes/demo.js.map +1 -0
- package/dist/cli/modes/doctor.d.ts +28 -0
- package/dist/cli/modes/doctor.d.ts.map +1 -0
- package/dist/cli/modes/doctor.js +79 -0
- package/dist/cli/modes/doctor.js.map +1 -0
- package/dist/cli/modes/login.d.ts +5 -0
- package/dist/cli/modes/login.d.ts.map +1 -0
- package/dist/cli/modes/login.js +40 -0
- package/dist/cli/modes/login.js.map +1 -0
- package/dist/cli/modes/mcpServe.d.ts +9 -0
- package/dist/cli/modes/mcpServe.d.ts.map +1 -0
- package/dist/cli/modes/mcpServe.js +112 -0
- package/dist/cli/modes/mcpServe.js.map +1 -0
- package/dist/cli/modes/orchestrator.d.ts +14 -0
- package/dist/cli/modes/orchestrator.d.ts.map +1 -0
- package/dist/cli/modes/orchestrator.js +64 -0
- package/dist/cli/modes/orchestrator.js.map +1 -0
- package/dist/cli/modes/team.d.ts +14 -0
- package/dist/cli/modes/team.d.ts.map +1 -0
- package/dist/cli/modes/team.js +87 -0
- package/dist/cli/modes/team.js.map +1 -0
- package/dist/cli/registry.d.ts +3 -0
- package/dist/cli/registry.d.ts.map +1 -0
- package/dist/cli/registry.js +15 -0
- package/dist/cli/registry.js.map +1 -0
- package/dist/cli/slash/core.d.ts +3 -0
- package/dist/cli/slash/core.d.ts.map +1 -0
- package/dist/cli/slash/core.js +509 -0
- package/dist/cli/slash/core.js.map +1 -0
- package/dist/cli/slash/index.d.ts +3 -0
- package/dist/cli/slash/index.d.ts.map +1 -0
- package/dist/cli/slash/index.js +28 -0
- package/dist/cli/slash/index.js.map +1 -0
- package/dist/cli/slash/modes.d.ts +3 -0
- package/dist/cli/slash/modes.d.ts.map +1 -0
- package/dist/cli/slash/modes.js +530 -0
- package/dist/cli/slash/modes.js.map +1 -0
- package/dist/cli/slash/session.d.ts +3 -0
- package/dist/cli/slash/session.d.ts.map +1 -0
- package/dist/cli/slash/session.js +376 -0
- package/dist/cli/slash/session.js.map +1 -0
- package/dist/cli/slash/shared.d.ts +43 -0
- package/dist/cli/slash/shared.d.ts.map +1 -0
- package/dist/cli/slash/shared.js +6 -0
- package/dist/cli/slash/shared.js.map +1 -0
- package/dist/cli/slash/tabs.d.ts +3 -0
- package/dist/cli/slash/tabs.d.ts.map +1 -0
- package/dist/cli/slash/tabs.js +497 -0
- package/dist/cli/slash/tabs.js.map +1 -0
- package/dist/cli/slash/team.d.ts +3 -0
- package/dist/cli/slash/team.d.ts.map +1 -0
- package/dist/cli/slash/team.js +85 -0
- package/dist/cli/slash/team.js.map +1 -0
- package/dist/cli/slash.d.ts +3 -0
- package/dist/cli/slash.d.ts.map +1 -0
- package/dist/cli/slash.js +8 -0
- package/dist/cli/slash.js.map +1 -0
- package/dist/cli/teamPipeline.d.ts +42 -0
- package/dist/cli/teamPipeline.d.ts.map +1 -0
- package/dist/cli/teamPipeline.js +406 -0
- package/dist/cli/teamPipeline.js.map +1 -0
- package/dist/cli/touchedFiles.d.ts +14 -0
- package/dist/cli/touchedFiles.d.ts.map +1 -0
- package/dist/cli/touchedFiles.js +66 -0
- package/dist/cli/touchedFiles.js.map +1 -0
- package/dist/cli/turn.d.ts +47 -0
- package/dist/cli/turn.d.ts.map +1 -0
- package/dist/cli/turn.js +626 -0
- package/dist/cli/turn.js.map +1 -0
- package/dist/cli/types.d.ts +47 -0
- package/dist/cli/types.d.ts.map +1 -0
- package/dist/cli/types.js +5 -0
- package/dist/cli/types.js.map +1 -0
- package/dist/cli/uiSession.d.ts +10 -0
- package/dist/cli/uiSession.d.ts.map +1 -0
- package/dist/cli/uiSession.js +66 -0
- package/dist/cli/uiSession.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +128 -0
- package/dist/cli.js.map +1 -0
- package/dist/core/adapter.d.ts +47 -0
- package/dist/core/adapter.d.ts.map +1 -0
- package/dist/core/adapter.js +48 -0
- package/dist/core/adapter.js.map +1 -0
- package/dist/core/adapters/anthropic.d.ts +35 -0
- package/dist/core/adapters/anthropic.d.ts.map +1 -0
- package/dist/core/adapters/anthropic.js +203 -0
- package/dist/core/adapters/anthropic.js.map +1 -0
- package/dist/core/adapters/claudeCli.d.ts +126 -0
- package/dist/core/adapters/claudeCli.d.ts.map +1 -0
- package/dist/core/adapters/claudeCli.js +584 -0
- package/dist/core/adapters/claudeCli.js.map +1 -0
- package/dist/core/adapters/cliAdapterBase.d.ts +48 -0
- package/dist/core/adapters/cliAdapterBase.d.ts.map +1 -0
- package/dist/core/adapters/cliAdapterBase.js +105 -0
- package/dist/core/adapters/cliAdapterBase.js.map +1 -0
- package/dist/core/adapters/cliStderr.d.ts +35 -0
- package/dist/core/adapters/cliStderr.d.ts.map +1 -0
- package/dist/core/adapters/cliStderr.js +87 -0
- package/dist/core/adapters/cliStderr.js.map +1 -0
- package/dist/core/adapters/codexCli.d.ts +113 -0
- package/dist/core/adapters/codexCli.d.ts.map +1 -0
- package/dist/core/adapters/codexCli.js +424 -0
- package/dist/core/adapters/codexCli.js.map +1 -0
- package/dist/core/adapters/gemini.d.ts +17 -0
- package/dist/core/adapters/gemini.d.ts.map +1 -0
- package/dist/core/adapters/gemini.js +133 -0
- package/dist/core/adapters/gemini.js.map +1 -0
- package/dist/core/adapters/geminiCli.d.ts +91 -0
- package/dist/core/adapters/geminiCli.d.ts.map +1 -0
- package/dist/core/adapters/geminiCli.js +276 -0
- package/dist/core/adapters/geminiCli.js.map +1 -0
- package/dist/core/adapters/nudges.d.ts +21 -0
- package/dist/core/adapters/nudges.d.ts.map +1 -0
- package/dist/core/adapters/nudges.js +75 -0
- package/dist/core/adapters/nudges.js.map +1 -0
- package/dist/core/adapters/openai.d.ts +17 -0
- package/dist/core/adapters/openai.d.ts.map +1 -0
- package/dist/core/adapters/openai.js +138 -0
- package/dist/core/adapters/openai.js.map +1 -0
- package/dist/core/adapters/parseFailures.d.ts +10 -0
- package/dist/core/adapters/parseFailures.d.ts.map +1 -0
- package/dist/core/adapters/parseFailures.js +35 -0
- package/dist/core/adapters/parseFailures.js.map +1 -0
- package/dist/core/adapters/utils.d.ts +11 -0
- package/dist/core/adapters/utils.d.ts.map +1 -0
- package/dist/core/adapters/utils.js +33 -0
- package/dist/core/adapters/utils.js.map +1 -0
- package/dist/core/askBroker.d.ts +11 -0
- package/dist/core/askBroker.d.ts.map +1 -0
- package/dist/core/askBroker.js +78 -0
- package/dist/core/askBroker.js.map +1 -0
- package/dist/core/atomicWrite.d.ts +5 -0
- package/dist/core/atomicWrite.d.ts.map +1 -0
- package/dist/core/atomicWrite.js +18 -0
- package/dist/core/atomicWrite.js.map +1 -0
- package/dist/core/brokers/askBroker.d.ts +11 -0
- package/dist/core/brokers/askBroker.d.ts.map +1 -0
- package/dist/core/brokers/askBroker.js +78 -0
- package/dist/core/brokers/askBroker.js.map +1 -0
- package/dist/core/brokers/permissionBroker.d.ts +7 -0
- package/dist/core/brokers/permissionBroker.d.ts.map +1 -0
- package/dist/core/brokers/permissionBroker.js +63 -0
- package/dist/core/brokers/permissionBroker.js.map +1 -0
- package/dist/core/brokers/socketBroker.d.ts +35 -0
- package/dist/core/brokers/socketBroker.d.ts.map +1 -0
- package/dist/core/brokers/socketBroker.js +74 -0
- package/dist/core/brokers/socketBroker.js.map +1 -0
- package/dist/core/clipboard.d.ts +4 -0
- package/dist/core/clipboard.d.ts.map +1 -0
- package/dist/core/clipboard.js +46 -0
- package/dist/core/clipboard.js.map +1 -0
- package/dist/core/compactor.d.ts +32 -0
- package/dist/core/compactor.d.ts.map +1 -0
- package/dist/core/compactor.js +91 -0
- package/dist/core/compactor.js.map +1 -0
- package/dist/core/consensusEngine.d.ts +21 -0
- package/dist/core/consensusEngine.d.ts.map +1 -0
- package/dist/core/consensusEngine.js +116 -0
- package/dist/core/consensusEngine.js.map +1 -0
- package/dist/core/context/contextCompressor.d.ts +10 -0
- package/dist/core/context/contextCompressor.d.ts.map +1 -0
- package/dist/core/context/contextCompressor.js +54 -0
- package/dist/core/context/contextCompressor.js.map +1 -0
- package/dist/core/context/contextWindows.d.ts +25 -0
- package/dist/core/context/contextWindows.d.ts.map +1 -0
- package/dist/core/context/contextWindows.js +101 -0
- package/dist/core/context/contextWindows.js.map +1 -0
- package/dist/core/context/summarizer.d.ts +65 -0
- package/dist/core/context/summarizer.d.ts.map +1 -0
- package/dist/core/context/summarizer.js +184 -0
- package/dist/core/context/summarizer.js.map +1 -0
- package/dist/core/context/usageCache.d.ts +19 -0
- package/dist/core/context/usageCache.d.ts.map +1 -0
- package/dist/core/context/usageCache.js +157 -0
- package/dist/core/context/usageCache.js.map +1 -0
- package/dist/core/context/usageWindow.d.ts +19 -0
- package/dist/core/context/usageWindow.d.ts.map +1 -0
- package/dist/core/context/usageWindow.js +73 -0
- package/dist/core/context/usageWindow.js.map +1 -0
- package/dist/core/contextCompressor.d.ts +10 -0
- package/dist/core/contextCompressor.d.ts.map +1 -0
- package/dist/core/contextCompressor.js +54 -0
- package/dist/core/contextCompressor.js.map +1 -0
- package/dist/core/contextWindows.d.ts +5 -0
- package/dist/core/contextWindows.d.ts.map +1 -0
- package/dist/core/contextWindows.js +60 -0
- package/dist/core/contextWindows.js.map +1 -0
- package/dist/core/continuum/digest.d.ts +35 -0
- package/dist/core/continuum/digest.d.ts.map +1 -0
- package/dist/core/continuum/digest.js +91 -0
- package/dist/core/continuum/digest.js.map +1 -0
- package/dist/core/continuum/index.d.ts +6 -0
- package/dist/core/continuum/index.d.ts.map +1 -0
- package/dist/core/continuum/index.js +7 -0
- package/dist/core/continuum/index.js.map +1 -0
- package/dist/core/continuum/inject.d.ts +9 -0
- package/dist/core/continuum/inject.d.ts.map +1 -0
- package/dist/core/continuum/inject.js +78 -0
- package/dist/core/continuum/inject.js.map +1 -0
- package/dist/core/continuum/projectId.d.ts +10 -0
- package/dist/core/continuum/projectId.d.ts.map +1 -0
- package/dist/core/continuum/projectId.js +60 -0
- package/dist/core/continuum/projectId.js.map +1 -0
- package/dist/core/continuum/store.d.ts +24 -0
- package/dist/core/continuum/store.d.ts.map +1 -0
- package/dist/core/continuum/store.js +144 -0
- package/dist/core/continuum/store.js.map +1 -0
- package/dist/core/continuum/types.d.ts +87 -0
- package/dist/core/continuum/types.d.ts.map +1 -0
- package/dist/core/continuum/types.js +9 -0
- package/dist/core/continuum/types.js.map +1 -0
- package/dist/core/dag.d.ts +21 -0
- package/dist/core/dag.d.ts.map +1 -0
- package/dist/core/dag.js +71 -0
- package/dist/core/dag.js.map +1 -0
- package/dist/core/director.d.ts +11 -0
- package/dist/core/director.d.ts.map +1 -0
- package/dist/core/director.js +37 -0
- package/dist/core/director.js.map +1 -0
- package/dist/core/hookedToolBroker.d.ts +25 -0
- package/dist/core/hookedToolBroker.d.ts.map +1 -0
- package/dist/core/hookedToolBroker.js +95 -0
- package/dist/core/hookedToolBroker.js.map +1 -0
- package/dist/core/langHint.d.ts +9 -0
- package/dist/core/langHint.d.ts.map +1 -0
- package/dist/core/langHint.js +33 -0
- package/dist/core/langHint.js.map +1 -0
- package/dist/core/mcp/mcpClient.d.ts +42 -0
- package/dist/core/mcp/mcpClient.d.ts.map +1 -0
- package/dist/core/mcp/mcpClient.js +134 -0
- package/dist/core/mcp/mcpClient.js.map +1 -0
- package/dist/core/mcp/mcpConfig.d.ts +58 -0
- package/dist/core/mcp/mcpConfig.d.ts.map +1 -0
- package/dist/core/mcp/mcpConfig.js +148 -0
- package/dist/core/mcp/mcpConfig.js.map +1 -0
- package/dist/core/mcp/mcpManager.d.ts +31 -0
- package/dist/core/mcp/mcpManager.d.ts.map +1 -0
- package/dist/core/mcp/mcpManager.js +83 -0
- package/dist/core/mcp/mcpManager.js.map +1 -0
- package/dist/core/mcpClient.d.ts +42 -0
- package/dist/core/mcpClient.d.ts.map +1 -0
- package/dist/core/mcpClient.js +134 -0
- package/dist/core/mcpClient.js.map +1 -0
- package/dist/core/mcpConfig.d.ts +24 -0
- package/dist/core/mcpConfig.d.ts.map +1 -0
- package/dist/core/mcpConfig.js +74 -0
- package/dist/core/mcpConfig.js.map +1 -0
- package/dist/core/mcpManager.d.ts +31 -0
- package/dist/core/mcpManager.d.ts.map +1 -0
- package/dist/core/mcpManager.js +83 -0
- package/dist/core/mcpManager.js.map +1 -0
- package/dist/core/memory.d.ts +22 -0
- package/dist/core/memory.d.ts.map +1 -0
- package/dist/core/memory.js +102 -0
- package/dist/core/memory.js.map +1 -0
- package/dist/core/omgToolManifest.d.ts +34 -0
- package/dist/core/omgToolManifest.d.ts.map +1 -0
- package/dist/core/omgToolManifest.js +59 -0
- package/dist/core/omgToolManifest.js.map +1 -0
- package/dist/core/orchestration/consensusEngine.d.ts +21 -0
- package/dist/core/orchestration/consensusEngine.d.ts.map +1 -0
- package/dist/core/orchestration/consensusEngine.js +116 -0
- package/dist/core/orchestration/consensusEngine.js.map +1 -0
- package/dist/core/orchestration/dag.d.ts +21 -0
- package/dist/core/orchestration/dag.d.ts.map +1 -0
- package/dist/core/orchestration/dag.js +71 -0
- package/dist/core/orchestration/dag.js.map +1 -0
- package/dist/core/orchestration/director.d.ts +11 -0
- package/dist/core/orchestration/director.d.ts.map +1 -0
- package/dist/core/orchestration/director.js +37 -0
- package/dist/core/orchestration/director.js.map +1 -0
- package/dist/core/orchestration/orchestrator.d.ts +32 -0
- package/dist/core/orchestration/orchestrator.d.ts.map +1 -0
- package/dist/core/orchestration/orchestrator.js +214 -0
- package/dist/core/orchestration/orchestrator.js.map +1 -0
- package/dist/core/orchestration/roleRouter.d.ts +28 -0
- package/dist/core/orchestration/roleRouter.d.ts.map +1 -0
- package/dist/core/orchestration/roleRouter.js +64 -0
- package/dist/core/orchestration/roleRouter.js.map +1 -0
- package/dist/core/orchestration/teamRunner.d.ts +69 -0
- package/dist/core/orchestration/teamRunner.d.ts.map +1 -0
- package/dist/core/orchestration/teamRunner.js +477 -0
- package/dist/core/orchestration/teamRunner.js.map +1 -0
- package/dist/core/orchestration/types.d.ts +115 -0
- package/dist/core/orchestration/types.d.ts.map +1 -0
- package/dist/core/orchestration/types.js +71 -0
- package/dist/core/orchestration/types.js.map +1 -0
- package/dist/core/orchestrator.d.ts +32 -0
- package/dist/core/orchestrator.d.ts.map +1 -0
- package/dist/core/orchestrator.js +214 -0
- package/dist/core/orchestrator.js.map +1 -0
- package/dist/core/permissionBroker.d.ts +7 -0
- package/dist/core/permissionBroker.d.ts.map +1 -0
- package/dist/core/permissionBroker.js +63 -0
- package/dist/core/permissionBroker.js.map +1 -0
- package/dist/core/planConfig.d.ts +15 -0
- package/dist/core/planConfig.d.ts.map +1 -0
- package/dist/core/planConfig.js +98 -0
- package/dist/core/planConfig.js.map +1 -0
- package/dist/core/presetPersistence.d.ts +4 -0
- package/dist/core/presetPersistence.d.ts.map +1 -0
- package/dist/core/presetPersistence.js +64 -0
- package/dist/core/presetPersistence.js.map +1 -0
- package/dist/core/roleRouter.d.ts +26 -0
- package/dist/core/roleRouter.d.ts.map +1 -0
- package/dist/core/roleRouter.js +62 -0
- package/dist/core/roleRouter.js.map +1 -0
- package/dist/core/sessionList.d.ts +26 -0
- package/dist/core/sessionList.d.ts.map +1 -0
- package/dist/core/sessionList.js +278 -0
- package/dist/core/sessionList.js.map +1 -0
- package/dist/core/slugify.d.ts +2 -0
- package/dist/core/slugify.d.ts.map +1 -0
- package/dist/core/slugify.js +17 -0
- package/dist/core/slugify.js.map +1 -0
- package/dist/core/socketBroker.d.ts +19 -0
- package/dist/core/socketBroker.d.ts.map +1 -0
- package/dist/core/socketBroker.js +48 -0
- package/dist/core/socketBroker.js.map +1 -0
- package/dist/core/state/agentsMdScaffold.d.ts +15 -0
- package/dist/core/state/agentsMdScaffold.d.ts.map +1 -0
- package/dist/core/state/agentsMdScaffold.js +118 -0
- package/dist/core/state/agentsMdScaffold.js.map +1 -0
- package/dist/core/state/atomicWrite.d.ts +5 -0
- package/dist/core/state/atomicWrite.d.ts.map +1 -0
- package/dist/core/state/atomicWrite.js +18 -0
- package/dist/core/state/atomicWrite.js.map +1 -0
- package/dist/core/state/handoff.d.ts +20 -0
- package/dist/core/state/handoff.d.ts.map +1 -0
- package/dist/core/state/handoff.js +71 -0
- package/dist/core/state/handoff.js.map +1 -0
- package/dist/core/state/mcpCatalog.d.ts +11 -0
- package/dist/core/state/mcpCatalog.d.ts.map +1 -0
- package/dist/core/state/mcpCatalog.js +51 -0
- package/dist/core/state/mcpCatalog.js.map +1 -0
- package/dist/core/state/mcpRecommendations.d.ts +7 -0
- package/dist/core/state/mcpRecommendations.d.ts.map +1 -0
- package/dist/core/state/mcpRecommendations.js +57 -0
- package/dist/core/state/mcpRecommendations.js.map +1 -0
- package/dist/core/state/memory.d.ts +22 -0
- package/dist/core/state/memory.d.ts.map +1 -0
- package/dist/core/state/memory.js +102 -0
- package/dist/core/state/memory.js.map +1 -0
- package/dist/core/state/planConfig.d.ts +15 -0
- package/dist/core/state/planConfig.d.ts.map +1 -0
- package/dist/core/state/planConfig.js +98 -0
- package/dist/core/state/planConfig.js.map +1 -0
- package/dist/core/state/presetPersistence.d.ts +4 -0
- package/dist/core/state/presetPersistence.d.ts.map +1 -0
- package/dist/core/state/presetPersistence.js +67 -0
- package/dist/core/state/presetPersistence.js.map +1 -0
- package/dist/core/state/sessionList.d.ts +26 -0
- package/dist/core/state/sessionList.d.ts.map +1 -0
- package/dist/core/state/sessionList.js +278 -0
- package/dist/core/state/sessionList.js.map +1 -0
- package/dist/core/state/stackDetect.d.ts +8 -0
- package/dist/core/state/stackDetect.d.ts.map +1 -0
- package/dist/core/state/stackDetect.js +40 -0
- package/dist/core/state/stackDetect.js.map +1 -0
- package/dist/core/state/tabPersistence.d.ts +29 -0
- package/dist/core/state/tabPersistence.d.ts.map +1 -0
- package/dist/core/state/tabPersistence.js +65 -0
- package/dist/core/state/tabPersistence.js.map +1 -0
- package/dist/core/tabPersistence.d.ts +29 -0
- package/dist/core/tabPersistence.d.ts.map +1 -0
- package/dist/core/tabPersistence.js +65 -0
- package/dist/core/tabPersistence.js.map +1 -0
- package/dist/core/teamRunner.d.ts +42 -0
- package/dist/core/teamRunner.d.ts.map +1 -0
- package/dist/core/teamRunner.js +248 -0
- package/dist/core/teamRunner.js.map +1 -0
- package/dist/core/toolGate.d.ts +23 -0
- package/dist/core/toolGate.d.ts.map +1 -0
- package/dist/core/toolGate.js +51 -0
- package/dist/core/toolGate.js.map +1 -0
- package/dist/core/toolInterceptor.d.ts +7 -0
- package/dist/core/toolInterceptor.d.ts.map +1 -0
- package/dist/core/toolInterceptor.js +108 -0
- package/dist/core/toolInterceptor.js.map +1 -0
- package/dist/core/tools/hookedToolBroker.d.ts +25 -0
- package/dist/core/tools/hookedToolBroker.d.ts.map +1 -0
- package/dist/core/tools/hookedToolBroker.js +95 -0
- package/dist/core/tools/hookedToolBroker.js.map +1 -0
- package/dist/core/tools/omgToolManifest.d.ts +42 -0
- package/dist/core/tools/omgToolManifest.d.ts.map +1 -0
- package/dist/core/tools/omgToolManifest.js +66 -0
- package/dist/core/tools/omgToolManifest.js.map +1 -0
- package/dist/core/tools/toolGate.d.ts +23 -0
- package/dist/core/tools/toolGate.d.ts.map +1 -0
- package/dist/core/tools/toolGate.js +51 -0
- package/dist/core/tools/toolGate.js.map +1 -0
- package/dist/core/tools/toolInterceptor.d.ts +7 -0
- package/dist/core/tools/toolInterceptor.d.ts.map +1 -0
- package/dist/core/tools/toolInterceptor.js +108 -0
- package/dist/core/tools/toolInterceptor.js.map +1 -0
- package/dist/core/tools/tools.d.ts +34 -0
- package/dist/core/tools/tools.d.ts.map +1 -0
- package/dist/core/tools/tools.js +328 -0
- package/dist/core/tools/tools.js.map +1 -0
- package/dist/core/tools.d.ts +34 -0
- package/dist/core/tools.d.ts.map +1 -0
- package/dist/core/tools.js +300 -0
- package/dist/core/tools.js.map +1 -0
- package/dist/core/types.d.ts +107 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +55 -0
- package/dist/core/types.js.map +1 -0
- package/dist/core/usageCache.d.ts +19 -0
- package/dist/core/usageCache.d.ts.map +1 -0
- package/dist/core/usageCache.js +157 -0
- package/dist/core/usageCache.js.map +1 -0
- package/dist/core/usageWindow.d.ts +19 -0
- package/dist/core/usageWindow.d.ts.map +1 -0
- package/dist/core/usageWindow.js +73 -0
- package/dist/core/usageWindow.js.map +1 -0
- package/dist/core/util/bgProtocol.d.ts +5 -0
- package/dist/core/util/bgProtocol.d.ts.map +1 -0
- package/dist/core/util/bgProtocol.js +20 -0
- package/dist/core/util/bgProtocol.js.map +1 -0
- package/dist/core/util/clipboard.d.ts +4 -0
- package/dist/core/util/clipboard.d.ts.map +1 -0
- package/dist/core/util/clipboard.js +46 -0
- package/dist/core/util/clipboard.js.map +1 -0
- package/dist/core/util/clipboardImage.d.ts +3 -0
- package/dist/core/util/clipboardImage.d.ts.map +1 -0
- package/dist/core/util/clipboardImage.js +58 -0
- package/dist/core/util/clipboardImage.js.map +1 -0
- package/dist/core/util/langHint.d.ts +9 -0
- package/dist/core/util/langHint.d.ts.map +1 -0
- package/dist/core/util/langHint.js +33 -0
- package/dist/core/util/langHint.js.map +1 -0
- package/dist/core/util/slugify.d.ts +2 -0
- package/dist/core/util/slugify.d.ts.map +1 -0
- package/dist/core/util/slugify.js +28 -0
- package/dist/core/util/slugify.js.map +1 -0
- package/dist/hooks/hookedToolProxy.mjs +118 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/ipc/dispatcher.d.ts +21 -0
- package/dist/ipc/dispatcher.d.ts.map +1 -0
- package/dist/ipc/dispatcher.js +160 -0
- package/dist/ipc/dispatcher.js.map +1 -0
- package/dist/ipc/events.d.ts +33 -0
- package/dist/ipc/events.d.ts.map +1 -0
- package/dist/ipc/events.js +42 -0
- package/dist/ipc/events.js.map +1 -0
- package/dist/ipc/rpc.d.ts +31 -0
- package/dist/ipc/rpc.d.ts.map +1 -0
- package/dist/ipc/rpc.js +110 -0
- package/dist/ipc/rpc.js.map +1 -0
- package/dist/ipc/stdio.d.ts +11 -0
- package/dist/ipc/stdio.d.ts.map +1 -0
- package/dist/ipc/stdio.js +52 -0
- package/dist/ipc/stdio.js.map +1 -0
- package/dist/mcp/askClient.d.ts +9 -0
- package/dist/mcp/askClient.d.ts.map +1 -0
- package/dist/mcp/askClient.js +70 -0
- package/dist/mcp/askClient.js.map +1 -0
- package/dist/mcp/constants.d.ts +6 -0
- package/dist/mcp/constants.d.ts.map +1 -0
- package/dist/mcp/constants.js +8 -0
- package/dist/mcp/constants.js.map +1 -0
- package/dist/mcp/permissionClient.d.ts +5 -0
- package/dist/mcp/permissionClient.d.ts.map +1 -0
- package/dist/mcp/permissionClient.js +71 -0
- package/dist/mcp/permissionClient.js.map +1 -0
- package/dist/mcp/schemas.d.ts +9 -0
- package/dist/mcp/schemas.d.ts.map +1 -0
- package/dist/mcp/schemas.js +121 -0
- package/dist/mcp/schemas.js.map +1 -0
- package/dist/mcp/server.d.ts +41 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +204 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/policy/loader.d.ts +27 -0
- package/dist/policy/loader.d.ts.map +1 -0
- package/dist/policy/loader.js +89 -0
- package/dist/policy/loader.js.map +1 -0
- package/dist/policy/team-claude-only.yaml +46 -0
- package/dist/policy/team-codex-only.yaml +46 -0
- package/dist/policy/team-codex.yaml +54 -0
- package/dist/policy/team.yaml +104 -0
- package/dist/toolhook/hookedToolProxy.mjs +118 -0
- package/dist/ui/components/App.d.ts +14 -0
- package/dist/ui/components/App.d.ts.map +1 -0
- package/dist/ui/components/App.js +803 -0
- package/dist/ui/components/App.js.map +1 -0
- package/dist/ui/components/AskPicker.d.ts +8 -0
- package/dist/ui/components/AskPicker.d.ts.map +1 -0
- package/dist/ui/components/AskPicker.js +202 -0
- package/dist/ui/components/AskPicker.js.map +1 -0
- package/dist/ui/components/CommandInput.d.ts +63 -0
- package/dist/ui/components/CommandInput.d.ts.map +1 -0
- package/dist/ui/components/CommandInput.js +994 -0
- package/dist/ui/components/CommandInput.js.map +1 -0
- package/dist/ui/components/DiffView.d.ts +19 -0
- package/dist/ui/components/DiffView.d.ts.map +1 -0
- package/dist/ui/components/DiffView.js +93 -0
- package/dist/ui/components/DiffView.js.map +1 -0
- package/dist/ui/components/Dock.d.ts +49 -0
- package/dist/ui/components/Dock.d.ts.map +1 -0
- package/dist/ui/components/Dock.js +52 -0
- package/dist/ui/components/Dock.js.map +1 -0
- package/dist/ui/components/EventLog.d.ts +42 -0
- package/dist/ui/components/EventLog.d.ts.map +1 -0
- package/dist/ui/components/EventLog.js +450 -0
- package/dist/ui/components/EventLog.js.map +1 -0
- package/dist/ui/components/FileSuggest.d.ts +8 -0
- package/dist/ui/components/FileSuggest.d.ts.map +1 -0
- package/dist/ui/components/FileSuggest.js +19 -0
- package/dist/ui/components/FileSuggest.js.map +1 -0
- package/dist/ui/components/HRule.d.ts +27 -0
- package/dist/ui/components/HRule.d.ts.map +1 -0
- package/dist/ui/components/HRule.js +75 -0
- package/dist/ui/components/HRule.js.map +1 -0
- package/dist/ui/components/HintBar.d.ts +2 -0
- package/dist/ui/components/HintBar.d.ts.map +1 -0
- package/dist/ui/components/HintBar.js +19 -0
- package/dist/ui/components/HintBar.js.map +1 -0
- package/dist/ui/components/MarkdownText.d.ts +13 -0
- package/dist/ui/components/MarkdownText.d.ts.map +1 -0
- package/dist/ui/components/MarkdownText.js +255 -0
- package/dist/ui/components/MarkdownText.js.map +1 -0
- package/dist/ui/components/PermissionPrompt.d.ts +8 -0
- package/dist/ui/components/PermissionPrompt.d.ts.map +1 -0
- package/dist/ui/components/PermissionPrompt.js +37 -0
- package/dist/ui/components/PermissionPrompt.js.map +1 -0
- package/dist/ui/components/SessionPicker.d.ts +16 -0
- package/dist/ui/components/SessionPicker.d.ts.map +1 -0
- package/dist/ui/components/SessionPicker.js +116 -0
- package/dist/ui/components/SessionPicker.js.map +1 -0
- package/dist/ui/components/SlashPalette.d.ts +8 -0
- package/dist/ui/components/SlashPalette.d.ts.map +1 -0
- package/dist/ui/components/SlashPalette.js +41 -0
- package/dist/ui/components/SlashPalette.js.map +1 -0
- package/dist/ui/components/Spinner.d.ts +37 -0
- package/dist/ui/components/Spinner.d.ts.map +1 -0
- package/dist/ui/components/Spinner.js +139 -0
- package/dist/ui/components/Spinner.js.map +1 -0
- package/dist/ui/components/StatusBar.d.ts +92 -0
- package/dist/ui/components/StatusBar.d.ts.map +1 -0
- package/dist/ui/components/StatusBar.js +268 -0
- package/dist/ui/components/StatusBar.js.map +1 -0
- package/dist/ui/components/TabChip.d.ts +11 -0
- package/dist/ui/components/TabChip.d.ts.map +1 -0
- package/dist/ui/components/TabChip.js +23 -0
- package/dist/ui/components/TabChip.js.map +1 -0
- package/dist/ui/components/TeamIndicator.d.ts +6 -0
- package/dist/ui/components/TeamIndicator.d.ts.map +1 -0
- package/dist/ui/components/TeamIndicator.js +17 -0
- package/dist/ui/components/TeamIndicator.js.map +1 -0
- package/dist/ui/components/TeamStatusPanel.d.ts +14 -0
- package/dist/ui/components/TeamStatusPanel.d.ts.map +1 -0
- package/dist/ui/components/TeamStatusPanel.js +65 -0
- package/dist/ui/components/TeamStatusPanel.js.map +1 -0
- package/dist/ui/components/ThinkingBlock.d.ts +10 -0
- package/dist/ui/components/ThinkingBlock.d.ts.map +1 -0
- package/dist/ui/components/ThinkingBlock.js +21 -0
- package/dist/ui/components/ThinkingBlock.js.map +1 -0
- package/dist/ui/components/TodoIndicator.d.ts +11 -0
- package/dist/ui/components/TodoIndicator.d.ts.map +1 -0
- package/dist/ui/components/TodoIndicator.js +37 -0
- package/dist/ui/components/TodoIndicator.js.map +1 -0
- package/dist/ui/components/ToolCallCard.d.ts +16 -0
- package/dist/ui/components/ToolCallCard.d.ts.map +1 -0
- package/dist/ui/components/ToolCallCard.js +195 -0
- package/dist/ui/components/ToolCallCard.js.map +1 -0
- package/dist/ui/components/Welcome.d.ts +8 -0
- package/dist/ui/components/Welcome.d.ts.map +1 -0
- package/dist/ui/components/Welcome.js +53 -0
- package/dist/ui/components/Welcome.js.map +1 -0
- package/dist/ui/cursorState.d.ts +29 -0
- package/dist/ui/cursorState.d.ts.map +1 -0
- package/dist/ui/cursorState.js +49 -0
- package/dist/ui/cursorState.js.map +1 -0
- package/dist/ui/diff.d.ts +34 -0
- package/dist/ui/diff.d.ts.map +1 -0
- package/dist/ui/diff.js +184 -0
- package/dist/ui/diff.js.map +1 -0
- package/dist/ui/diffLang.d.ts +2 -0
- package/dist/ui/diffLang.d.ts.map +1 -0
- package/dist/ui/diffLang.js +36 -0
- package/dist/ui/diffLang.js.map +1 -0
- package/dist/ui/emoji.d.ts +4 -0
- package/dist/ui/emoji.d.ts.map +1 -0
- package/dist/ui/emoji.js +73 -0
- package/dist/ui/emoji.js.map +1 -0
- package/dist/ui/fileMentions.d.ts +41 -0
- package/dist/ui/fileMentions.d.ts.map +1 -0
- package/dist/ui/fileMentions.js +135 -0
- package/dist/ui/fileMentions.js.map +1 -0
- package/dist/ui/hooks/useLineEditor.d.ts +44 -0
- package/dist/ui/hooks/useLineEditor.d.ts.map +1 -0
- package/dist/ui/hooks/useLineEditor.js +142 -0
- package/dist/ui/hooks/useLineEditor.js.map +1 -0
- package/dist/ui/inputHistory.d.ts +8 -0
- package/dist/ui/inputHistory.d.ts.map +1 -0
- package/dist/ui/inputHistory.js +56 -0
- package/dist/ui/inputHistory.js.map +1 -0
- package/dist/ui/jsonrpc.d.ts +17 -0
- package/dist/ui/jsonrpc.d.ts.map +1 -0
- package/dist/ui/jsonrpc.js +41 -0
- package/dist/ui/jsonrpc.js.map +1 -0
- package/dist/ui/launcher.d.ts +20 -0
- package/dist/ui/launcher.d.ts.map +1 -0
- package/dist/ui/launcher.js +185 -0
- package/dist/ui/launcher.js.map +1 -0
- package/dist/ui/markdown.d.ts +104 -0
- package/dist/ui/markdown.d.ts.map +1 -0
- package/dist/ui/markdown.js +449 -0
- package/dist/ui/markdown.js.map +1 -0
- package/dist/ui/mouse.d.ts +22 -0
- package/dist/ui/mouse.d.ts.map +1 -0
- package/dist/ui/mouse.js +31 -0
- package/dist/ui/mouse.js.map +1 -0
- package/dist/ui/outbound.d.ts +20 -0
- package/dist/ui/outbound.d.ts.map +1 -0
- package/dist/ui/outbound.js +49 -0
- package/dist/ui/outbound.js.map +1 -0
- package/dist/ui/screenMode.d.ts +25 -0
- package/dist/ui/screenMode.d.ts.map +1 -0
- package/dist/ui/screenMode.js +43 -0
- package/dist/ui/screenMode.js.map +1 -0
- package/dist/ui/slashCatalog.d.ts +18 -0
- package/dist/ui/slashCatalog.d.ts.map +1 -0
- package/dist/ui/slashCatalog.js +243 -0
- package/dist/ui/slashCatalog.js.map +1 -0
- package/dist/ui/syntax.d.ts +11 -0
- package/dist/ui/syntax.d.ts.map +1 -0
- package/dist/ui/syntax.js +78 -0
- package/dist/ui/syntax.js.map +1 -0
- package/dist/ui/theme.d.ts +165 -0
- package/dist/ui/theme.d.ts.map +1 -0
- package/dist/ui/theme.js +394 -0
- package/dist/ui/theme.js.map +1 -0
- package/dist/ui/types.d.ts +203 -0
- package/dist/ui/types.d.ts.map +1 -0
- package/dist/ui/types.js +53 -0
- package/dist/ui/types.js.map +1 -0
- package/package.json +104 -0
|
@@ -0,0 +1,994 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
|
|
3
|
+
import { Box, Text, useApp, useCursor, useInput, useStdin } from "ink";
|
|
4
|
+
import stringWidth from "string-width";
|
|
5
|
+
import { sendCancel, sendInput, sendQuit, sendSlashCommand, } from "../outbound.js";
|
|
6
|
+
import { theme } from "../theme.js";
|
|
7
|
+
import { activeMention, expandFileMentions, extractImageMentions, scanDir, } from "../fileMentions.js";
|
|
8
|
+
import { captureClipboardImage } from "../../core/util/clipboardImage.js";
|
|
9
|
+
import { FileSuggest } from "./FileSuggest.js";
|
|
10
|
+
import { matchSlash, renderHelp } from "../slashCatalog.js";
|
|
11
|
+
import { SlashPalette } from "./SlashPalette.js";
|
|
12
|
+
import { appendInputHistory, loadInputHistory } from "../inputHistory.js";
|
|
13
|
+
import { useLineEditor } from "../hooks/useLineEditor.js";
|
|
14
|
+
import { isMouseSequence } from "../mouse.js";
|
|
15
|
+
// Phase 65: the slash catalog (src/ui/slashCatalog.ts) is the single
|
|
16
|
+
// source of truth for available command names + their descriptions.
|
|
17
|
+
// Phase 135: a paste this big (lines OR chars) collapses to a chip.
|
|
18
|
+
const PASTE_MIN_LINES = 4;
|
|
19
|
+
const PASTE_MIN_CHARS = 300;
|
|
20
|
+
const PROMPT_COLS = 2;
|
|
21
|
+
const MAX_INPUT_VISIBLE_LINES = 6;
|
|
22
|
+
export function absoluteElementOrigin(node) {
|
|
23
|
+
let x = 0;
|
|
24
|
+
let y = 0;
|
|
25
|
+
let cur = node;
|
|
26
|
+
while (cur) {
|
|
27
|
+
x += cur.yogaNode?.getComputedLeft() ?? 0;
|
|
28
|
+
y += cur.yogaNode?.getComputedTop() ?? 0;
|
|
29
|
+
cur = cur.parentNode;
|
|
30
|
+
}
|
|
31
|
+
return { x, y };
|
|
32
|
+
}
|
|
33
|
+
export function inputCursorColumn(leftOfCursor) {
|
|
34
|
+
return PROMPT_COLS + stringWidth(leftOfCursor);
|
|
35
|
+
}
|
|
36
|
+
export function inputViewportStart(totalLines, cursorLine, maxVisibleLines = MAX_INPUT_VISIBLE_LINES) {
|
|
37
|
+
if (totalLines <= maxVisibleLines)
|
|
38
|
+
return 0;
|
|
39
|
+
const maxStart = Math.max(0, totalLines - maxVisibleLines);
|
|
40
|
+
return Math.max(0, Math.min(cursorLine - maxVisibleLines + 1, maxStart));
|
|
41
|
+
}
|
|
42
|
+
export function shouldShowInputHardwareCursor(buffer, suppressCursorWhenEmpty = false, suppressHardwareCursor = false) {
|
|
43
|
+
// Phase 277: compose protection. Once there is text in the buffer the
|
|
44
|
+
// user may be mid-IME-composition, and any hide drops the macOS
|
|
45
|
+
// Hangul compose preview to the bottom of the screen. So never hide
|
|
46
|
+
// the cursor while the buffer is non-empty — a 1-frame stale position
|
|
47
|
+
// during a layout settle is invisible next to a bounced compose box.
|
|
48
|
+
// The layout-settle hide (suppressHardwareCursor) and the
|
|
49
|
+
// hide-while-empty path only apply to the empty input, where there is
|
|
50
|
+
// nothing to compose.
|
|
51
|
+
if (buffer.length > 0)
|
|
52
|
+
return true;
|
|
53
|
+
if (suppressHardwareCursor)
|
|
54
|
+
return false;
|
|
55
|
+
return !suppressCursorWhenEmpty;
|
|
56
|
+
}
|
|
57
|
+
export function inputHardwareCursorPosition(origin, column, visibleLine, fullscreenCursorMode = false) {
|
|
58
|
+
// Ink's log-update normally renders `output + "\n"`, leaving the
|
|
59
|
+
// terminal cursor on the row just below the visible output. Its
|
|
60
|
+
// buildCursorSuffix therefore uses `visibleLineCount - y`.
|
|
61
|
+
//
|
|
62
|
+
// In fullscreen mode Ink deliberately renders `output` without the
|
|
63
|
+
// trailing newline, so the terminal cursor starts on the last visible
|
|
64
|
+
// row instead. Without this +1, every hardware cursor target appears
|
|
65
|
+
// one row above the measured Yoga origin in the alternate screen.
|
|
66
|
+
return {
|
|
67
|
+
x: origin.x + column,
|
|
68
|
+
y: origin.y + visibleLine + (fullscreenCursorMode ? 1 : 0),
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
// Phase 289: macOS 2-set (두벌식) Hangul keyboard. When a Korean input
|
|
72
|
+
// source is active, a held Ctrl still arrives with key.ctrl set, but
|
|
73
|
+
// `input` is the jamo the physical key produces in the Korean layout
|
|
74
|
+
// (Ctrl+P → "ㅔ", Ctrl+O → "ㅐ", Ctrl+N → "ㅜ") rather than the Latin
|
|
75
|
+
// letter. So every `input === "p"` shortcut check silently fails and
|
|
76
|
+
// tab-switch / search / undo die under a Korean source. Evidence
|
|
77
|
+
// (OMG_DEBUG_KEYS): Ctrl+P → input="ㅔ" hex=[e3 85 94] flags=[ctrl].
|
|
78
|
+
// Map each jamo back to its QWERTY base key so the existing ctrl-letter
|
|
79
|
+
// bindings work regardless of the active input source.
|
|
80
|
+
const HANGUL_CTRL_TO_QWERTY = {
|
|
81
|
+
"ㅂ": "q", "ㅈ": "w", "ㄷ": "e", "ㄱ": "r", "ㅅ": "t",
|
|
82
|
+
"ㅛ": "y", "ㅕ": "u", "ㅑ": "i", "ㅐ": "o", "ㅔ": "p",
|
|
83
|
+
"ㅁ": "a", "ㄴ": "s", "ㅇ": "d", "ㄹ": "f", "ㅎ": "g",
|
|
84
|
+
"ㅗ": "h", "ㅓ": "j", "ㅏ": "k", "ㅣ": "l",
|
|
85
|
+
"ㅋ": "z", "ㅌ": "x", "ㅊ": "c", "ㅍ": "v", "ㅠ": "b",
|
|
86
|
+
"ㅜ": "n", "ㅡ": "m",
|
|
87
|
+
};
|
|
88
|
+
/** Phase 289: when Ctrl is held, fold a 두벌식 Hangul jamo back to the
|
|
89
|
+
* Latin key that physically produced it so ctrl-letter shortcuts match.
|
|
90
|
+
* Only rewrites while `ctrl` is true — a bare jamo is intended text. */
|
|
91
|
+
export function normalizeCtrlInput(input, ctrl) {
|
|
92
|
+
if (!ctrl)
|
|
93
|
+
return input;
|
|
94
|
+
return HANGUL_CTRL_TO_QWERTY[input] ?? input;
|
|
95
|
+
}
|
|
96
|
+
function paletteFor(value) {
|
|
97
|
+
if (!value.startsWith("/"))
|
|
98
|
+
return [];
|
|
99
|
+
if (value.includes(" "))
|
|
100
|
+
return [];
|
|
101
|
+
const prefix = value.slice(1).toLowerCase();
|
|
102
|
+
return matchSlash(prefix);
|
|
103
|
+
}
|
|
104
|
+
// Phase 39 word boundary helpers. Word = run of alnum/_/. The
|
|
105
|
+
// classic readline behavior: jump over whitespace, then over the
|
|
106
|
+
// word, stopping at the far boundary.
|
|
107
|
+
function isWordChar(ch) {
|
|
108
|
+
return /[A-Za-z0-9_]/.test(ch);
|
|
109
|
+
}
|
|
110
|
+
function prevWordIdx(s, idx) {
|
|
111
|
+
let i = idx;
|
|
112
|
+
while (i > 0 && !isWordChar(s[i - 1]))
|
|
113
|
+
i--;
|
|
114
|
+
while (i > 0 && isWordChar(s[i - 1]))
|
|
115
|
+
i--;
|
|
116
|
+
return i;
|
|
117
|
+
}
|
|
118
|
+
function nextWordIdx(s, idx) {
|
|
119
|
+
let i = idx;
|
|
120
|
+
while (i < s.length && !isWordChar(s[i]))
|
|
121
|
+
i++;
|
|
122
|
+
while (i < s.length && isWordChar(s[i]))
|
|
123
|
+
i++;
|
|
124
|
+
return i;
|
|
125
|
+
}
|
|
126
|
+
function startOfLine(s, idx) {
|
|
127
|
+
const nl = s.lastIndexOf("\n", Math.max(0, idx - 1));
|
|
128
|
+
return nl === -1 ? 0 : nl + 1;
|
|
129
|
+
}
|
|
130
|
+
function endOfLine(s, idx) {
|
|
131
|
+
const nl = s.indexOf("\n", idx);
|
|
132
|
+
return nl === -1 ? s.length : nl;
|
|
133
|
+
}
|
|
134
|
+
const CSI_ARROW_MODIFIER_RE = /^\x1b\[1;(\d+)([CD])$/;
|
|
135
|
+
const MODIFIED_ARROW_DIRECTION = {
|
|
136
|
+
D: "left",
|
|
137
|
+
C: "right",
|
|
138
|
+
};
|
|
139
|
+
function arrowModifier(rawInput, direction) {
|
|
140
|
+
const match = CSI_ARROW_MODIFIER_RE.exec(rawInput);
|
|
141
|
+
if (!match)
|
|
142
|
+
return null;
|
|
143
|
+
if (MODIFIED_ARROW_DIRECTION[match[2]] !== direction)
|
|
144
|
+
return null;
|
|
145
|
+
return Number(match[1]);
|
|
146
|
+
}
|
|
147
|
+
function isWordArrowSequence(rawInput, direction) {
|
|
148
|
+
const modifier = arrowModifier(rawInput, direction);
|
|
149
|
+
// xterm modifier values: 3/4 = Alt(+Shift), 5/6 = Ctrl(+Shift),
|
|
150
|
+
// 7/8 = Ctrl+Alt(+Shift). All are readline-style word movement.
|
|
151
|
+
return modifier !== null && modifier >= 3 && modifier <= 8;
|
|
152
|
+
}
|
|
153
|
+
function isLineArrowSequence(rawInput, direction) {
|
|
154
|
+
const modifier = arrowModifier(rawInput, direction);
|
|
155
|
+
// macOS terminals commonly encode Cmd+Arrow as CSI 1;9C/D (with
|
|
156
|
+
// adjacent shifted/control-combined variants). Kitty exposes Cmd as
|
|
157
|
+
// key.super; this raw CSI fallback covers terminals that only report
|
|
158
|
+
// a meta-modified arrow to Ink's parser.
|
|
159
|
+
return (modifier !== null &&
|
|
160
|
+
((modifier >= 9 && modifier <= 16) || modifier === 17 || modifier === 18));
|
|
161
|
+
}
|
|
162
|
+
// Phase 63: vertical cursor motion inside a multi-line buffer.
|
|
163
|
+
//
|
|
164
|
+
// Move up: jump to the previous line, preserving the visual column.
|
|
165
|
+
// Returns -1 if the cursor is already on the first line (caller falls
|
|
166
|
+
// back to history).
|
|
167
|
+
function lineUpIdx(s, idx) {
|
|
168
|
+
const lineStart = startOfLine(s, idx);
|
|
169
|
+
if (lineStart === 0)
|
|
170
|
+
return -1;
|
|
171
|
+
const col = idx - lineStart;
|
|
172
|
+
const prevLineEnd = lineStart - 1; // the '\n' itself
|
|
173
|
+
const prevLineStart = startOfLine(s, prevLineEnd);
|
|
174
|
+
const prevLineLen = prevLineEnd - prevLineStart;
|
|
175
|
+
return prevLineStart + Math.min(col, prevLineLen);
|
|
176
|
+
}
|
|
177
|
+
function lineDownIdx(s, idx) {
|
|
178
|
+
const lineEnd = endOfLine(s, idx);
|
|
179
|
+
if (lineEnd === s.length)
|
|
180
|
+
return -1;
|
|
181
|
+
const lineStart = startOfLine(s, idx);
|
|
182
|
+
const col = idx - lineStart;
|
|
183
|
+
const nextLineStart = lineEnd + 1;
|
|
184
|
+
const nextLineEnd = endOfLine(s, nextLineStart);
|
|
185
|
+
const nextLineLen = nextLineEnd - nextLineStart;
|
|
186
|
+
return nextLineStart + Math.min(col, nextLineLen);
|
|
187
|
+
}
|
|
188
|
+
// Phase 39: full-fledged line editor.
|
|
189
|
+
//
|
|
190
|
+
// Keys:
|
|
191
|
+
// printable → insert at cursor; multi-char input from a
|
|
192
|
+
// terminal paste lands as one chunk
|
|
193
|
+
// Enter → submit, unless trailing `\` or Shift/Alt+Enter
|
|
194
|
+
// (then insert newline)
|
|
195
|
+
// Backspace / Delete → delete char before / at cursor
|
|
196
|
+
// ← / → → move cursor by one char
|
|
197
|
+
// Home / End → cursor to start / end of line
|
|
198
|
+
// Cmd-← / Cmd-→ → cursor to start / end of line where supported
|
|
199
|
+
// Opt-← / Opt-→ → move cursor by one word (ESC b / ESC f bytes)
|
|
200
|
+
// Ctrl-A / Ctrl-E → cursor to start / end of line
|
|
201
|
+
// Ctrl-W → delete previous word
|
|
202
|
+
// Ctrl-U → delete from start of line to cursor
|
|
203
|
+
// Ctrl-K → delete from cursor to end of line
|
|
204
|
+
// ↑ / ↓ → history navigation (single-line buffer only)
|
|
205
|
+
// Tab → autocomplete first slash suggestion
|
|
206
|
+
// Ctrl-C → quit
|
|
207
|
+
// Esc → cancel the in-flight stream
|
|
208
|
+
export function CommandInput({ onLocalLine, onClearEvents, onToggleThink, cwd, onSetTheme, onToggleVerbose, onToggleReasoning, onUserMessage, prediction, suppressCursorWhenEmpty, suppressHardwareCursor, fullscreenCursorMode, }) {
|
|
209
|
+
// Phase 205: editor core (buffer/cursor/undo/paste) lives in a hook.
|
|
210
|
+
// History navigation, palette, mention popup, and search remain here
|
|
211
|
+
// because they're intertwined with rendering and external IO.
|
|
212
|
+
const editor = useLineEditor();
|
|
213
|
+
const { buffer, cursor } = editor;
|
|
214
|
+
const { exit } = useApp();
|
|
215
|
+
const { internal_eventEmitter: inputEmitter } = useStdin();
|
|
216
|
+
const rawInputRef = useRef("");
|
|
217
|
+
useEffect(() => {
|
|
218
|
+
const captureRawInput = (input) => {
|
|
219
|
+
rawInputRef.current = input;
|
|
220
|
+
};
|
|
221
|
+
// Capture Ink's unparsed event before `useInput` strips ESC and
|
|
222
|
+
// collapses several CSI modifier variants into the same key shape.
|
|
223
|
+
// This is what lets us distinguish Cmd+Arrow (line movement) from
|
|
224
|
+
// Opt/Ctrl+Arrow (word movement) across macOS terminals.
|
|
225
|
+
inputEmitter.on("input", captureRawInput);
|
|
226
|
+
return () => {
|
|
227
|
+
inputEmitter.removeListener("input", captureRawInput);
|
|
228
|
+
};
|
|
229
|
+
}, [inputEmitter]);
|
|
230
|
+
const historyRef = useRef([]);
|
|
231
|
+
const [historyIdx, setHistoryIdx] = useState(-1);
|
|
232
|
+
const draftRef = useRef("");
|
|
233
|
+
// Phase 114: seed the in-memory ring from persisted history once, so
|
|
234
|
+
// ↑/↓ recall and ghost suggestions span sessions.
|
|
235
|
+
const historyLoadedRef = useRef(false);
|
|
236
|
+
if (!historyLoadedRef.current) {
|
|
237
|
+
historyLoadedRef.current = true;
|
|
238
|
+
// Don't touch the real ~/.omg history file under the test runner.
|
|
239
|
+
if (!process.env.VITEST) {
|
|
240
|
+
try {
|
|
241
|
+
historyRef.current = loadInputHistory();
|
|
242
|
+
}
|
|
243
|
+
catch {
|
|
244
|
+
/* non-persisted fallback */
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
// Phase 125: Ctrl-R reverse history search. `null` when inactive;
|
|
249
|
+
// otherwise the live query + which match (0 = most recent) is shown.
|
|
250
|
+
const [search, setSearch] = useState(null);
|
|
251
|
+
const reverseMatches = (q) => {
|
|
252
|
+
const hist = historyRef.current;
|
|
253
|
+
const seen = new Set();
|
|
254
|
+
const res = [];
|
|
255
|
+
for (let i = hist.length - 1; i >= 0; i--) {
|
|
256
|
+
const h = hist[i];
|
|
257
|
+
if ((q === "" || h.includes(q)) && !seen.has(h)) {
|
|
258
|
+
seen.add(h);
|
|
259
|
+
res.push(h);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
return res;
|
|
263
|
+
};
|
|
264
|
+
// Phase 135 + 126 + paste store now belong to `editor` (Phase 205).
|
|
265
|
+
const palette = useMemo(() => paletteFor(buffer), [buffer]);
|
|
266
|
+
const [paletteSel, setPaletteSel] = useState(0);
|
|
267
|
+
const paletteOpen = palette.length > 0;
|
|
268
|
+
useEffect(() => {
|
|
269
|
+
if (paletteSel >= palette.length)
|
|
270
|
+
setPaletteSel(0);
|
|
271
|
+
}, [palette.length, paletteSel]);
|
|
272
|
+
const acceptPalette = () => {
|
|
273
|
+
if (!paletteOpen)
|
|
274
|
+
return false;
|
|
275
|
+
const choice = palette[paletteSel];
|
|
276
|
+
if (!choice)
|
|
277
|
+
return false;
|
|
278
|
+
const filled = "/" + choice.name + (choice.args ? " " : "");
|
|
279
|
+
setBufAndCursor(filled, filled.length);
|
|
280
|
+
return true;
|
|
281
|
+
};
|
|
282
|
+
const isMultiLine = buffer.includes("\n");
|
|
283
|
+
// Phase 186: ghost is now ONLY the model-predicted next message on
|
|
284
|
+
// an empty prompt (opt-in /predict). The fish-style history-prefix
|
|
285
|
+
// autosuggestion that used to fill mid-buffer was confusing — it
|
|
286
|
+
// ghosted the user's own past lines, which they rarely wanted
|
|
287
|
+
// re-suggested. ↑/↓ history recall is unchanged.
|
|
288
|
+
const ghostMatch = useMemo(() => {
|
|
289
|
+
if (buffer)
|
|
290
|
+
return "";
|
|
291
|
+
if (isMultiLine || historyIdx !== -1 || cursor !== 0)
|
|
292
|
+
return "";
|
|
293
|
+
return prediction ?? "";
|
|
294
|
+
}, [buffer, cursor, isMultiLine, historyIdx, prediction]);
|
|
295
|
+
// Phase 64: @file autocomplete. When the cursor sits inside an
|
|
296
|
+
// `@<token>`, scan the active tab's cwd and show a popup. Selection
|
|
297
|
+
// index is owned here; arrow keys cycle, Tab/Enter accept, Esc
|
|
298
|
+
// closes.
|
|
299
|
+
const mention = useMemo(() => activeMention(buffer, cursor), [buffer, cursor]);
|
|
300
|
+
const fileSuggestions = useMemo(() => {
|
|
301
|
+
if (!mention)
|
|
302
|
+
return [];
|
|
303
|
+
return scanDir(cwd ?? process.cwd(), mention.query);
|
|
304
|
+
}, [mention, cwd]);
|
|
305
|
+
const [mentionSel, setMentionSel] = useState(0);
|
|
306
|
+
// Set to the mention's `start` when the user dismisses the popup
|
|
307
|
+
// with Esc, so the popup stays hidden until they move away from
|
|
308
|
+
// that mention. Comparing by `start` index means a fresh `@` later
|
|
309
|
+
// in the buffer still opens its own popup.
|
|
310
|
+
const [dismissedStart, setDismissedStart] = useState(null);
|
|
311
|
+
const popupOpen = fileSuggestions.length > 0 &&
|
|
312
|
+
(mention?.start ?? -1) !== (dismissedStart ?? -2);
|
|
313
|
+
// Clamp selection when the candidate set shrinks under us.
|
|
314
|
+
useEffect(() => {
|
|
315
|
+
if (mentionSel >= fileSuggestions.length)
|
|
316
|
+
setMentionSel(0);
|
|
317
|
+
}, [fileSuggestions.length, mentionSel]);
|
|
318
|
+
// Phase 113/116: the ghost only shows when no slash palette / @file
|
|
319
|
+
// popup is claiming the completion. Gating here (rather than on the
|
|
320
|
+
// raw prefix) means `/team <prior prompt>` and `!cmd` still get a
|
|
321
|
+
// history ghost once the palette/popup has closed.
|
|
322
|
+
const ghostSuffix = paletteOpen || popupOpen || search ? "" : ghostMatch;
|
|
323
|
+
const acceptGhost = () => {
|
|
324
|
+
if (!ghostSuffix)
|
|
325
|
+
return false;
|
|
326
|
+
const next = buffer + ghostSuffix;
|
|
327
|
+
setBufAndCursor(next, next.length);
|
|
328
|
+
return true;
|
|
329
|
+
};
|
|
330
|
+
const acceptMention = () => {
|
|
331
|
+
if (!mention || !popupOpen)
|
|
332
|
+
return false;
|
|
333
|
+
const choice = fileSuggestions[mentionSel];
|
|
334
|
+
if (!choice)
|
|
335
|
+
return false;
|
|
336
|
+
// Replace the mention's body (everything after `@`) with the
|
|
337
|
+
// selected path. For directories we leave the trailing `/` so
|
|
338
|
+
// the user can keep typing into them; for files we add a single
|
|
339
|
+
// trailing space so the mention is "done".
|
|
340
|
+
const before = buffer.slice(0, mention.start + 1); // include `@`
|
|
341
|
+
const after = buffer.slice(mention.end);
|
|
342
|
+
const inserted = choice.display + (choice.isDir ? "" : " ");
|
|
343
|
+
const next = before + inserted + after;
|
|
344
|
+
const nextCursor = before.length + inserted.length;
|
|
345
|
+
setBufAndCursor(next, nextCursor);
|
|
346
|
+
setMentionSel(0);
|
|
347
|
+
return true;
|
|
348
|
+
};
|
|
349
|
+
const setBufAndCursor = editor.setBuffer;
|
|
350
|
+
const setCursor = editor.setCursor;
|
|
351
|
+
// Phase 205: `insertAt` keeps the history-draft tie-in (any keystroke
|
|
352
|
+
// while scrolled into history snaps back to a fresh draft). The
|
|
353
|
+
// editor returns the new buffer so we don't have to wait a render
|
|
354
|
+
// for the state to settle.
|
|
355
|
+
const insertAt = (text) => {
|
|
356
|
+
const next = editor.insert(text);
|
|
357
|
+
if (historyIdx !== -1) {
|
|
358
|
+
setHistoryIdx(-1);
|
|
359
|
+
draftRef.current = next;
|
|
360
|
+
}
|
|
361
|
+
};
|
|
362
|
+
const deleteRange = editor.deleteRange;
|
|
363
|
+
const pushHistory = (entry) => {
|
|
364
|
+
const hist = historyRef.current;
|
|
365
|
+
if (hist.length > 0 && hist[hist.length - 1] === entry)
|
|
366
|
+
return;
|
|
367
|
+
hist.push(entry);
|
|
368
|
+
if (hist.length > 500)
|
|
369
|
+
hist.shift();
|
|
370
|
+
// Phase 114: persist so the entry survives into the next session.
|
|
371
|
+
// Skipped under the test runner so suites don't write to the real
|
|
372
|
+
// ~/.omg history file.
|
|
373
|
+
if (!process.env.VITEST)
|
|
374
|
+
appendInputHistory(entry);
|
|
375
|
+
};
|
|
376
|
+
const handleSlash = (trimmed) => {
|
|
377
|
+
if (!trimmed.startsWith("/"))
|
|
378
|
+
return false;
|
|
379
|
+
const [head, ...rest] = trimmed.slice(1).split(" ");
|
|
380
|
+
const name = head ?? "";
|
|
381
|
+
const args = rest.join(" ");
|
|
382
|
+
if (name === "quit" || name === "q") {
|
|
383
|
+
sendQuit();
|
|
384
|
+
exit();
|
|
385
|
+
return true;
|
|
386
|
+
}
|
|
387
|
+
if (name === "help" || name === "?") {
|
|
388
|
+
onLocalLine?.(renderHelp());
|
|
389
|
+
return true;
|
|
390
|
+
}
|
|
391
|
+
if (name === "clear") {
|
|
392
|
+
onClearEvents?.();
|
|
393
|
+
sendSlashCommand(name, args);
|
|
394
|
+
return true;
|
|
395
|
+
}
|
|
396
|
+
if (name === "reset") {
|
|
397
|
+
onClearEvents?.();
|
|
398
|
+
sendSlashCommand(name, args);
|
|
399
|
+
return true;
|
|
400
|
+
}
|
|
401
|
+
if (name === "theme") {
|
|
402
|
+
// Phase 81: UI-only toggle. cli.ts never sees this slash.
|
|
403
|
+
const arg = args.trim().toLowerCase();
|
|
404
|
+
const next = arg === "light"
|
|
405
|
+
? "light"
|
|
406
|
+
: arg === "dark"
|
|
407
|
+
? "dark"
|
|
408
|
+
: // No arg = toggle. Default state is dark; pressing once
|
|
409
|
+
// flips to light.
|
|
410
|
+
"light";
|
|
411
|
+
onSetTheme?.(next);
|
|
412
|
+
onLocalLine?.(`[theme] ${next}`);
|
|
413
|
+
return true;
|
|
414
|
+
}
|
|
415
|
+
if (name === "think") {
|
|
416
|
+
const arg = args.trim().toLowerCase();
|
|
417
|
+
const force = arg === "on" || arg === "true" || arg === "1"
|
|
418
|
+
? true
|
|
419
|
+
: arg === "off" || arg === "false" || arg === "0"
|
|
420
|
+
? false
|
|
421
|
+
: undefined;
|
|
422
|
+
onToggleThink?.(force);
|
|
423
|
+
return true;
|
|
424
|
+
}
|
|
425
|
+
if (name === "verbose") {
|
|
426
|
+
// Phase 101: UI-only — full tool-card output, no collapse.
|
|
427
|
+
const arg = args.trim().toLowerCase();
|
|
428
|
+
const force = arg === "on" || arg === "true" || arg === "1"
|
|
429
|
+
? true
|
|
430
|
+
: arg === "off" || arg === "false" || arg === "0"
|
|
431
|
+
? false
|
|
432
|
+
: undefined;
|
|
433
|
+
onToggleVerbose?.(force);
|
|
434
|
+
return true;
|
|
435
|
+
}
|
|
436
|
+
if (name === "reasoning") {
|
|
437
|
+
// Phase 308: UI-only — expand reasoning/thinking inline (live
|
|
438
|
+
// progress), independent of /verbose.
|
|
439
|
+
const arg = args.trim().toLowerCase();
|
|
440
|
+
const force = arg === "on" || arg === "true" || arg === "1"
|
|
441
|
+
? true
|
|
442
|
+
: arg === "off" || arg === "false" || arg === "0"
|
|
443
|
+
? false
|
|
444
|
+
: undefined;
|
|
445
|
+
onToggleReasoning?.(force);
|
|
446
|
+
return true;
|
|
447
|
+
}
|
|
448
|
+
sendSlashCommand(name, args);
|
|
449
|
+
return true;
|
|
450
|
+
};
|
|
451
|
+
const submit = () => {
|
|
452
|
+
const trimmed = buffer.trim();
|
|
453
|
+
// Phase 135: capture the paste-expanded form before the store is
|
|
454
|
+
// cleared by editor.reset().
|
|
455
|
+
const trimmedExpanded = editor.expandPastes(trimmed);
|
|
456
|
+
editor.reset();
|
|
457
|
+
setHistoryIdx(-1);
|
|
458
|
+
draftRef.current = "";
|
|
459
|
+
if (!trimmed)
|
|
460
|
+
return;
|
|
461
|
+
pushHistory(trimmed);
|
|
462
|
+
// Phase 83: claude-code-style shell prefix. A leading `!` routes
|
|
463
|
+
// the rest of the line to /sh — the cli runs it against the
|
|
464
|
+
// active tab's cwd, streams output as a tool card, and the
|
|
465
|
+
// model never sees the input.
|
|
466
|
+
if (trimmed.startsWith("!")) {
|
|
467
|
+
// Phase 233: paste chips are expanded before the shell command
|
|
468
|
+
// is dispatched. Pre-233 a paste in `!cmd` would have stayed as
|
|
469
|
+
// `[paste#1: N lines]` literally and the shell would have run
|
|
470
|
+
// the chip text instead of the pasted content.
|
|
471
|
+
const cmd = editor.expandPastes(trimmed.slice(1)).trim();
|
|
472
|
+
if (cmd)
|
|
473
|
+
sendSlashCommand("sh", cmd);
|
|
474
|
+
return;
|
|
475
|
+
}
|
|
476
|
+
// Phase 233: slash commands also need paste expansion. /team and
|
|
477
|
+
// similar commands take a free-form prompt as their argument; a
|
|
478
|
+
// pasted block must reach the slash dispatcher as full text, not
|
|
479
|
+
// the `[paste#N: M lines]` collapsed chip. Pre-233 the chip
|
|
480
|
+
// stayed literal in slash args, so `/team <pasted prompt>`
|
|
481
|
+
// forwarded only the chip token to the team adapter and the
|
|
482
|
+
// model saw nothing useful.
|
|
483
|
+
if (handleSlash(trimmedExpanded))
|
|
484
|
+
return;
|
|
485
|
+
// Phase 115: echo the user's own message into the log first, so
|
|
486
|
+
// the scrollback reads as a conversation. Uses the paste-expanded
|
|
487
|
+
// text but keeps @file mentions as raw tokens — the user typed the
|
|
488
|
+
// paste content themselves and expects to see it in scrollback,
|
|
489
|
+
// but a `@README.md` should not dump the file body into the log
|
|
490
|
+
// (Phase 196 refinement of the Phase 115 echo).
|
|
491
|
+
onUserMessage?.(trimmedExpanded);
|
|
492
|
+
// Phase 340: pull image attachments BEFORE @file/text handling.
|
|
493
|
+
// 1) `[image#N]` chips from a clipboard paste → editor.expandImages
|
|
494
|
+
// strips the chip tokens and returns their paths.
|
|
495
|
+
// 2) `@photo.png` mentions → extractImageMentions removes the tokens
|
|
496
|
+
// and resolves them to absolute paths (so expandFileMentions below
|
|
497
|
+
// never tries to binary-load an image as text).
|
|
498
|
+
const { text: afterChips, images: chipImages } = editor.expandImages(trimmedExpanded);
|
|
499
|
+
const { text: afterImageMentions, images: atImages, missing: missingImages, } = extractImageMentions(afterChips, { cwd });
|
|
500
|
+
const attachments = [...chipImages, ...atImages];
|
|
501
|
+
if (missingImages.length > 0) {
|
|
502
|
+
onLocalLine?.(`[image] not found: ${missingImages.join(", ")}`);
|
|
503
|
+
}
|
|
504
|
+
if (attachments.length > 0) {
|
|
505
|
+
onLocalLine?.(`[image] attached ${attachments.length} image${attachments.length === 1 ? "" : "s"}`);
|
|
506
|
+
}
|
|
507
|
+
// Phase 55: resolve @path mentions against the active tab's
|
|
508
|
+
// cwd, not the omg process cwd. With per-tab cwd (Phase 54)
|
|
509
|
+
// typing `@README.md` should pull the README from whichever
|
|
510
|
+
// project this tab is rooted in. Paste chips were already spliced
|
|
511
|
+
// back into `trimmedExpanded` (Phase 135).
|
|
512
|
+
const { expanded, loaded, failed } = expandFileMentions(afterImageMentions, {
|
|
513
|
+
cwd,
|
|
514
|
+
});
|
|
515
|
+
if (loaded.length > 0) {
|
|
516
|
+
onLocalLine?.(`[@] loaded ${loaded.length} file${loaded.length === 1 ? "" : "s"}: ${loaded.join(", ")}`);
|
|
517
|
+
}
|
|
518
|
+
if (failed.length > 0) {
|
|
519
|
+
onLocalLine?.(`[@] failed: ${failed.join("; ")}`);
|
|
520
|
+
}
|
|
521
|
+
// Phase 340: never send an empty prompt with an image — codex's exec
|
|
522
|
+
// would fall back to reading (empty) stdin. Give a neutral instruction.
|
|
523
|
+
const outText = expanded.trim().length === 0 && attachments.length > 0
|
|
524
|
+
? "Look at the attached image(s)."
|
|
525
|
+
: expanded;
|
|
526
|
+
sendInput(outText, attachments);
|
|
527
|
+
};
|
|
528
|
+
useInput((input, key) => {
|
|
529
|
+
const rawInput = rawInputRef.current;
|
|
530
|
+
if (isMouseSequence(rawInput) || isMouseSequence(input)) {
|
|
531
|
+
return;
|
|
532
|
+
}
|
|
533
|
+
// Phase 61: opt-in key dump. Set OMG_DEBUG_KEYS=1 and a file at
|
|
534
|
+
// ~/.omg/keylog.txt records every keystroke with its raw bytes
|
|
535
|
+
// + ink's parsed flags. Used to diagnose terminal-specific
|
|
536
|
+
// mismatches (e.g. Warp delivering Opt-N without key.meta).
|
|
537
|
+
if (process.env.OMG_DEBUG_KEYS) {
|
|
538
|
+
try {
|
|
539
|
+
const hex = [...Buffer.from(input, "utf-8")]
|
|
540
|
+
.map((b) => b.toString(16).padStart(2, "0"))
|
|
541
|
+
.join(" ");
|
|
542
|
+
const flags = Object.entries(key)
|
|
543
|
+
.filter(([, v]) => v)
|
|
544
|
+
.map(([k]) => k)
|
|
545
|
+
.join(",");
|
|
546
|
+
process.stderr.write(`[omg-keys] input=${JSON.stringify(input)} hex=[${hex}] flags=[${flags}]\n`);
|
|
547
|
+
}
|
|
548
|
+
catch {
|
|
549
|
+
/* logging must never throw */
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
// Phase 289: fold a Korean (2-set) jamo back to its Latin key while
|
|
553
|
+
// Ctrl is held so the shortcut comparisons below match under a
|
|
554
|
+
// Hangul input source (Ctrl+P arrives as "ㅔ", not "p").
|
|
555
|
+
const ck = normalizeCtrlInput(input, key.ctrl);
|
|
556
|
+
if (key.ctrl && ck === "c") {
|
|
557
|
+
sendQuit();
|
|
558
|
+
exit();
|
|
559
|
+
return;
|
|
560
|
+
}
|
|
561
|
+
// ---------------- Ctrl-R reverse history search (Phase 125) ------
|
|
562
|
+
// Ctrl-R opens the search (or, when already open, steps to the next
|
|
563
|
+
// older match). While open, typing edits the query, Enter accepts
|
|
564
|
+
// the current match into the buffer, Esc cancels; any other control
|
|
565
|
+
// key closes the search and falls through to its normal action.
|
|
566
|
+
if (key.ctrl && ck === "r") {
|
|
567
|
+
setSearch((s) => {
|
|
568
|
+
if (!s)
|
|
569
|
+
return { query: "", idx: 0 };
|
|
570
|
+
const matches = reverseMatches(s.query);
|
|
571
|
+
const nextIdx = matches.length === 0 ? 0 : Math.min(s.idx + 1, matches.length - 1);
|
|
572
|
+
return { query: s.query, idx: nextIdx };
|
|
573
|
+
});
|
|
574
|
+
return;
|
|
575
|
+
}
|
|
576
|
+
if (search) {
|
|
577
|
+
if (key.return) {
|
|
578
|
+
const m = reverseMatches(search.query)[search.idx];
|
|
579
|
+
setSearch(null);
|
|
580
|
+
if (m)
|
|
581
|
+
setBufAndCursor(m, m.length);
|
|
582
|
+
return;
|
|
583
|
+
}
|
|
584
|
+
if (key.escape) {
|
|
585
|
+
setSearch(null);
|
|
586
|
+
return;
|
|
587
|
+
}
|
|
588
|
+
if (key.backspace || key.delete || input === "\x7f" || input === "\b") {
|
|
589
|
+
setSearch((s) => (s ? { query: s.query.slice(0, -1), idx: 0 } : s));
|
|
590
|
+
return;
|
|
591
|
+
}
|
|
592
|
+
if (input && !key.ctrl && !key.meta) {
|
|
593
|
+
setSearch((s) => (s ? { query: s.query + input, idx: 0 } : s));
|
|
594
|
+
return;
|
|
595
|
+
}
|
|
596
|
+
// Unhandled control key — leave search and let it act normally.
|
|
597
|
+
setSearch(null);
|
|
598
|
+
}
|
|
599
|
+
// ---------------- Undo / redo (Phase 126) ----------------------
|
|
600
|
+
// Ctrl-_ (0x1f) or Ctrl-Z undo; Ctrl-Y redo. (Ctrl-Z may be eaten
|
|
601
|
+
// by the shell as SIGTSTP in some terminals, so Ctrl-_ is the
|
|
602
|
+
// reliable binding.)
|
|
603
|
+
if (input === "\x1f" || (key.ctrl && ck === "z")) {
|
|
604
|
+
editor.undo();
|
|
605
|
+
return;
|
|
606
|
+
}
|
|
607
|
+
if (key.ctrl && ck === "y") {
|
|
608
|
+
editor.redo();
|
|
609
|
+
return;
|
|
610
|
+
}
|
|
611
|
+
if (key.escape && input.length <= 1) {
|
|
612
|
+
// Phase 64: when the @file popup is up, Esc closes it instead
|
|
613
|
+
// of cancelling the in-flight stream.
|
|
614
|
+
if (popupOpen && mention) {
|
|
615
|
+
setDismissedStart(mention.start);
|
|
616
|
+
return;
|
|
617
|
+
}
|
|
618
|
+
// A plain ESC keypress means cancel. Multi-byte ESC sequences
|
|
619
|
+
// (arrow keys, Opt-letter) still set key.escape, but their
|
|
620
|
+
// input is longer — those fall through to the matchers below.
|
|
621
|
+
sendCancel();
|
|
622
|
+
return;
|
|
623
|
+
}
|
|
624
|
+
// ---------------- Tab shortcuts (Phase 49 / 68) ---------------
|
|
625
|
+
// Ctrl-N → /new (default-named tab on the same provider).
|
|
626
|
+
// (Ctrl-T would be more "browsery" but macOS PTYs interpret it
|
|
627
|
+
// as SIGINFO before ink can see the byte.)
|
|
628
|
+
if (key.ctrl && ck === "n") {
|
|
629
|
+
sendSlashCommand("new", "");
|
|
630
|
+
return;
|
|
631
|
+
}
|
|
632
|
+
// Phase 68: cycle keys for users whose terminal eats Opt-N.
|
|
633
|
+
// Ctrl-P → previous tab
|
|
634
|
+
// Ctrl-O → next tab
|
|
635
|
+
// The cli's /switch supports the "+1" / "-1" syntax for relative
|
|
636
|
+
// movement.
|
|
637
|
+
if (key.ctrl && ck === "p") {
|
|
638
|
+
sendSlashCommand("switch", "-1");
|
|
639
|
+
return;
|
|
640
|
+
}
|
|
641
|
+
if (key.ctrl && ck === "o") {
|
|
642
|
+
sendSlashCommand("switch", "+1");
|
|
643
|
+
return;
|
|
644
|
+
}
|
|
645
|
+
// Opt-1 .. Opt-9 → /switch <N>. Terminals send Esc + digit.
|
|
646
|
+
// Ink surfaces them as key.meta + input=digit on a typical
|
|
647
|
+
// setup; Warp sometimes delivers the raw two-byte ESC+digit
|
|
648
|
+
// sequence without setting key.meta. Match both shapes.
|
|
649
|
+
if (key.meta && /^[1-9]$/.test(input)) {
|
|
650
|
+
sendSlashCommand("switch", input);
|
|
651
|
+
return;
|
|
652
|
+
}
|
|
653
|
+
if (/^\x1b[1-9]$/.test(input)) {
|
|
654
|
+
sendSlashCommand("switch", input[1]);
|
|
655
|
+
return;
|
|
656
|
+
}
|
|
657
|
+
// ---------------- Cursor motion -----------------------------
|
|
658
|
+
// Home / End — terminals often deliver macOS Cmd+← / Cmd+→ as
|
|
659
|
+
// Home / End while in the alternate screen buffer.
|
|
660
|
+
if (key.home) {
|
|
661
|
+
setCursor(startOfLine(buffer, cursor));
|
|
662
|
+
return;
|
|
663
|
+
}
|
|
664
|
+
if (key.end) {
|
|
665
|
+
setCursor(endOfLine(buffer, cursor));
|
|
666
|
+
return;
|
|
667
|
+
}
|
|
668
|
+
// Kitty keyboard protocol can report Cmd as `super`. Other
|
|
669
|
+
// terminals encode Cmd+Arrow as CSI 1;9D / CSI 1;9C. Ink's
|
|
670
|
+
// high-level key object collapses those together with Opt+Arrow,
|
|
671
|
+
// so use the raw sequence captured above to keep macOS text-field
|
|
672
|
+
// semantics: Cmd = line boundary, Opt/Ctrl = word boundary.
|
|
673
|
+
if (key.leftArrow && (key.super || isLineArrowSequence(rawInput, "left"))) {
|
|
674
|
+
setCursor(startOfLine(buffer, cursor));
|
|
675
|
+
return;
|
|
676
|
+
}
|
|
677
|
+
if (key.rightArrow &&
|
|
678
|
+
(key.super || isLineArrowSequence(rawInput, "right"))) {
|
|
679
|
+
setCursor(endOfLine(buffer, cursor));
|
|
680
|
+
return;
|
|
681
|
+
}
|
|
682
|
+
if (key.leftArrow &&
|
|
683
|
+
(key.ctrl ||
|
|
684
|
+
isWordArrowSequence(rawInput, "left") ||
|
|
685
|
+
(key.meta && !key.super))) {
|
|
686
|
+
setCursor(prevWordIdx(buffer, cursor));
|
|
687
|
+
return;
|
|
688
|
+
}
|
|
689
|
+
if (key.rightArrow &&
|
|
690
|
+
(key.ctrl ||
|
|
691
|
+
isWordArrowSequence(rawInput, "right") ||
|
|
692
|
+
(key.meta && !key.super))) {
|
|
693
|
+
setCursor(nextWordIdx(buffer, cursor));
|
|
694
|
+
return;
|
|
695
|
+
}
|
|
696
|
+
if (key.leftArrow) {
|
|
697
|
+
setCursor(Math.max(0, cursor - 1));
|
|
698
|
+
return;
|
|
699
|
+
}
|
|
700
|
+
if (key.rightArrow) {
|
|
701
|
+
// Phase 113: → at end-of-line accepts the ghost suggestion
|
|
702
|
+
// (fish-style). Mid-line it just moves the cursor.
|
|
703
|
+
if (cursor === buffer.length && acceptGhost())
|
|
704
|
+
return;
|
|
705
|
+
setCursor(Math.min(buffer.length, cursor + 1));
|
|
706
|
+
return;
|
|
707
|
+
}
|
|
708
|
+
// Option/Alt + ← / → — terminals send ESC b / ESC f for word
|
|
709
|
+
// motion. ink usually surfaces them as key.meta + input='b'/'f',
|
|
710
|
+
// but Warp sometimes delivers the raw two-byte ESC sequence
|
|
711
|
+
// without setting key.meta. Match both shapes.
|
|
712
|
+
if ((key.meta && input === "b") || input === "\x1bb") {
|
|
713
|
+
setCursor(prevWordIdx(buffer, cursor));
|
|
714
|
+
return;
|
|
715
|
+
}
|
|
716
|
+
if ((key.meta && input === "f") || input === "\x1bf") {
|
|
717
|
+
setCursor(nextWordIdx(buffer, cursor));
|
|
718
|
+
return;
|
|
719
|
+
}
|
|
720
|
+
if (key.ctrl && input === "a") {
|
|
721
|
+
setCursor(startOfLine(buffer, cursor));
|
|
722
|
+
return;
|
|
723
|
+
}
|
|
724
|
+
if (key.ctrl && input === "e") {
|
|
725
|
+
setCursor(endOfLine(buffer, cursor));
|
|
726
|
+
return;
|
|
727
|
+
}
|
|
728
|
+
// ---------------- Deletions ---------------------------------
|
|
729
|
+
if (key.ctrl && input === "w") {
|
|
730
|
+
deleteRange(prevWordIdx(buffer, cursor), cursor);
|
|
731
|
+
return;
|
|
732
|
+
}
|
|
733
|
+
if (key.ctrl && input === "u") {
|
|
734
|
+
deleteRange(startOfLine(buffer, cursor), cursor);
|
|
735
|
+
return;
|
|
736
|
+
}
|
|
737
|
+
if (key.ctrl && input === "k") {
|
|
738
|
+
deleteRange(cursor, endOfLine(buffer, cursor));
|
|
739
|
+
return;
|
|
740
|
+
}
|
|
741
|
+
// Backspace — terminals send a confusing mix:
|
|
742
|
+
// 0x7F (DEL) - the traditional unix Backspace byte
|
|
743
|
+
// 0x08 (BS) - older terminals + Ctrl-H
|
|
744
|
+
// ESC[3~ → key.delete - macOS "Delete" key (the smaller one)
|
|
745
|
+
// Apple keyboards label the larger key "Delete" but it functions
|
|
746
|
+
// as Backspace, and Warp / a handful of other terminals report
|
|
747
|
+
// *that* key as key.delete with empty input. The user-visible
|
|
748
|
+
// intent is unambiguous: delete the char before the cursor. So
|
|
749
|
+
// we route key.delete to Backspace too. True forward-delete is
|
|
750
|
+
// surfaced through Ctrl-D below, the standard emacs binding.
|
|
751
|
+
if (key.backspace ||
|
|
752
|
+
key.delete ||
|
|
753
|
+
input === "\x7f" ||
|
|
754
|
+
input === "\x08" ||
|
|
755
|
+
(key.ctrl && input === "h")) {
|
|
756
|
+
if (cursor > 0)
|
|
757
|
+
deleteRange(cursor - 1, cursor);
|
|
758
|
+
return;
|
|
759
|
+
}
|
|
760
|
+
// Forward-delete — Ctrl-D. Rarely needed in a single-line input
|
|
761
|
+
// but standard emacs / readline behaviour; cheap to keep.
|
|
762
|
+
if (key.ctrl && input === "d") {
|
|
763
|
+
if (cursor < buffer.length)
|
|
764
|
+
deleteRange(cursor, cursor + 1);
|
|
765
|
+
return;
|
|
766
|
+
}
|
|
767
|
+
// ---------------- @file popup navigation (Phase 64) ----------
|
|
768
|
+
// When the popup is up, ↑/↓ cycle candidates and Tab/Enter
|
|
769
|
+
// accepts. This must run before the multi-line ↑/↓ / history
|
|
770
|
+
// handlers below.
|
|
771
|
+
if (popupOpen) {
|
|
772
|
+
if (key.upArrow) {
|
|
773
|
+
setMentionSel((mentionSel - 1 + fileSuggestions.length) % fileSuggestions.length);
|
|
774
|
+
return;
|
|
775
|
+
}
|
|
776
|
+
if (key.downArrow) {
|
|
777
|
+
setMentionSel((mentionSel + 1) % fileSuggestions.length);
|
|
778
|
+
return;
|
|
779
|
+
}
|
|
780
|
+
if (key.tab) {
|
|
781
|
+
if (acceptMention())
|
|
782
|
+
return;
|
|
783
|
+
}
|
|
784
|
+
// Enter intentionally falls through: if the user typed a full
|
|
785
|
+
// path the popup may still match it; we don't want to swallow
|
|
786
|
+
// their submit. The popup closes naturally once submit fires.
|
|
787
|
+
}
|
|
788
|
+
// ---------------- Slash palette navigation (Phase 65) ---------
|
|
789
|
+
// While typing `/<prefix>` the palette shows candidates with
|
|
790
|
+
// descriptions. ↑/↓ select, Tab/Enter accept. Like the @file
|
|
791
|
+
// popup we don't hijack Enter — submit still wins if the buffer
|
|
792
|
+
// already names a valid slash on its own.
|
|
793
|
+
if (paletteOpen) {
|
|
794
|
+
if (key.upArrow) {
|
|
795
|
+
setPaletteSel((paletteSel - 1 + palette.length) % palette.length);
|
|
796
|
+
return;
|
|
797
|
+
}
|
|
798
|
+
if (key.downArrow) {
|
|
799
|
+
setPaletteSel((paletteSel + 1) % palette.length);
|
|
800
|
+
return;
|
|
801
|
+
}
|
|
802
|
+
if (key.tab) {
|
|
803
|
+
if (acceptPalette())
|
|
804
|
+
return;
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
// Phase 186: Tab accepts the predicted-next-message ghost when
|
|
808
|
+
// the prompt is empty. History cycling was removed alongside the
|
|
809
|
+
// fish-style autosuggestion.
|
|
810
|
+
if (key.tab && !popupOpen && !paletteOpen) {
|
|
811
|
+
if (acceptGhost())
|
|
812
|
+
return;
|
|
813
|
+
}
|
|
814
|
+
// ---------------- Vertical motion / history ----------------
|
|
815
|
+
// Phase 63: ↑/↓ behave like a real editor inside a multi-line
|
|
816
|
+
// buffer — move the cursor between lines, preserving column. At
|
|
817
|
+
// the top/bottom edge, fall through to history navigation so the
|
|
818
|
+
// single-line UX still works.
|
|
819
|
+
if (key.upArrow) {
|
|
820
|
+
if (isMultiLine) {
|
|
821
|
+
const next = lineUpIdx(buffer, cursor);
|
|
822
|
+
if (next >= 0) {
|
|
823
|
+
setCursor(next);
|
|
824
|
+
return;
|
|
825
|
+
}
|
|
826
|
+
// Top line — fall through to history.
|
|
827
|
+
}
|
|
828
|
+
const hist = historyRef.current;
|
|
829
|
+
if (hist.length === 0)
|
|
830
|
+
return;
|
|
831
|
+
const next = historyIdx === -1 ? hist.length - 1 : Math.max(0, historyIdx - 1);
|
|
832
|
+
if (historyIdx === -1)
|
|
833
|
+
draftRef.current = buffer;
|
|
834
|
+
setHistoryIdx(next);
|
|
835
|
+
setBufAndCursor(hist[next], hist[next].length);
|
|
836
|
+
return;
|
|
837
|
+
}
|
|
838
|
+
if (key.downArrow) {
|
|
839
|
+
if (isMultiLine) {
|
|
840
|
+
const next = lineDownIdx(buffer, cursor);
|
|
841
|
+
if (next >= 0) {
|
|
842
|
+
setCursor(next);
|
|
843
|
+
return;
|
|
844
|
+
}
|
|
845
|
+
// Bottom line — fall through to history.
|
|
846
|
+
}
|
|
847
|
+
if (historyIdx === -1)
|
|
848
|
+
return;
|
|
849
|
+
const hist = historyRef.current;
|
|
850
|
+
const next = historyIdx + 1;
|
|
851
|
+
if (next >= hist.length) {
|
|
852
|
+
setHistoryIdx(-1);
|
|
853
|
+
setBufAndCursor(draftRef.current, draftRef.current.length);
|
|
854
|
+
}
|
|
855
|
+
else {
|
|
856
|
+
setHistoryIdx(next);
|
|
857
|
+
setBufAndCursor(hist[next], hist[next].length);
|
|
858
|
+
}
|
|
859
|
+
return;
|
|
860
|
+
}
|
|
861
|
+
// ---------------- Enter -------------------------------------
|
|
862
|
+
if (key.return) {
|
|
863
|
+
// Trailing backslash continues the line as a newline.
|
|
864
|
+
if (buffer.slice(0, cursor).endsWith("\\")) {
|
|
865
|
+
const next = buffer.slice(0, cursor - 1) + "\n" + buffer.slice(cursor);
|
|
866
|
+
setBufAndCursor(next, cursor);
|
|
867
|
+
return;
|
|
868
|
+
}
|
|
869
|
+
// Shift/Alt+Enter — explicit newline.
|
|
870
|
+
if (key.meta || key.shift) {
|
|
871
|
+
insertAt("\n");
|
|
872
|
+
return;
|
|
873
|
+
}
|
|
874
|
+
submit();
|
|
875
|
+
return;
|
|
876
|
+
}
|
|
877
|
+
// ---------------- Printable input ---------------------------
|
|
878
|
+
if (input && !key.ctrl && !key.meta) {
|
|
879
|
+
// Strip bracketed-paste markers if a terminal sent them.
|
|
880
|
+
// Also strip ESC-prefixed control sequences that fell through
|
|
881
|
+
// the matchers above (otherwise an unhandled meta-key would
|
|
882
|
+
// insert its raw ESC byte as literal text).
|
|
883
|
+
const cleaned = input
|
|
884
|
+
.replace(/\x1b\[200~/g, "")
|
|
885
|
+
.replace(/\x1b\[201~/g, "")
|
|
886
|
+
// Phase 146: terminals separate pasted lines with CR (\r), not
|
|
887
|
+
// LF. Left in, the \r survives the control-strip below and makes
|
|
888
|
+
// the cursor jump to column 0 at render, overwriting earlier
|
|
889
|
+
// lines so only the last shows. Normalize CR / CRLF → LF first
|
|
890
|
+
// so multi-line pastes become a real multi-line buffer (and the
|
|
891
|
+
// line count below triggers the paste chip correctly).
|
|
892
|
+
.replace(/\r\n?/g, "\n")
|
|
893
|
+
.replace(/[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g, "");
|
|
894
|
+
if (cleaned) {
|
|
895
|
+
// Phase 135: a big multi-line paste collapses to a chip; the
|
|
896
|
+
// text is stashed and restored at submit. Small input (typing,
|
|
897
|
+
// short pastes) inserts as-is.
|
|
898
|
+
const lineCount = cleaned.split("\n").length;
|
|
899
|
+
if (lineCount >= PASTE_MIN_LINES || cleaned.length > PASTE_MIN_CHARS) {
|
|
900
|
+
insertAt(editor.storePaste(cleaned, lineCount));
|
|
901
|
+
}
|
|
902
|
+
else {
|
|
903
|
+
insertAt(cleaned);
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
else if (/\x1b\[20[01]~/.test(input) || /\x1b\[20[01]~/.test(rawInput ?? "")) {
|
|
907
|
+
// Phase 340: an EMPTY bracketed paste = a non-text paste (most
|
|
908
|
+
// often an image on the clipboard — terminals don't deliver image
|
|
909
|
+
// bytes via the TTY). Probe the OS clipboard; if an image is there,
|
|
910
|
+
// save it and splice an [image#N] chip. Best-effort + terminal-
|
|
911
|
+
// dependent (some terminals send nothing at all on image paste);
|
|
912
|
+
// the @path flow is the guaranteed alternative.
|
|
913
|
+
const img = captureClipboardImage();
|
|
914
|
+
if (img) {
|
|
915
|
+
insertAt(editor.storeImage(img));
|
|
916
|
+
onLocalLine?.("[image] captured from clipboard");
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
});
|
|
921
|
+
// Render — split the buffer at the cursor boundary. The real
|
|
922
|
+
// terminal cursor is positioned by Ink's `useCursor`; we no longer
|
|
923
|
+
// render an inline ▌ glyph here because it overlaps the hardware
|
|
924
|
+
// cursor during IME composition.
|
|
925
|
+
const before = buffer.slice(0, cursor);
|
|
926
|
+
const after = buffer.slice(cursor);
|
|
927
|
+
const beforeLines = before.split("\n");
|
|
928
|
+
const afterLines = after.split("\n");
|
|
929
|
+
// Phase 268/269: Ink 6.7+ exposes a cursor API that handles
|
|
930
|
+
// trailing-`\n` math and cursor visibility, but its position must be
|
|
931
|
+
// relative to the Ink output origin and available before Ink's
|
|
932
|
+
// render commit. `getComputedTop()` is only parent-relative, so
|
|
933
|
+
// summing the wrapper's ancestor Yoga offsets is what prevents IME
|
|
934
|
+
// compose from appearing at row 0 when CommandInput itself is nested
|
|
935
|
+
// below EventLog/Dock/HRule. Column uses `stringWidth` so CJK wide
|
|
936
|
+
// chars (한글, 漢字) count 2 cols each like the terminal renders them.
|
|
937
|
+
const wrapperRef = useRef(null);
|
|
938
|
+
const { setCursorPosition } = useCursor();
|
|
939
|
+
const [cursorOrigin, setCursorOrigin] = useState(null);
|
|
940
|
+
// Visible column of the cursor on its line: prompt + display
|
|
941
|
+
// width of all chars to the left of the cursor on that line.
|
|
942
|
+
const cursorCol = inputCursorColumn(beforeLines[beforeLines.length - 1]);
|
|
943
|
+
const showHardwareCursor = shouldShowInputHardwareCursor(buffer, suppressCursorWhenEmpty, suppressHardwareCursor);
|
|
944
|
+
useLayoutEffect(() => {
|
|
945
|
+
const node = wrapperRef.current;
|
|
946
|
+
if (!node) {
|
|
947
|
+
setCursorOrigin((prev) => (prev === null ? prev : null));
|
|
948
|
+
return;
|
|
949
|
+
}
|
|
950
|
+
const next = absoluteElementOrigin(node);
|
|
951
|
+
setCursorOrigin((prev) => prev && prev.x === next.x && prev.y === next.y ? prev : next);
|
|
952
|
+
});
|
|
953
|
+
// The "cursor line" merges the last line of `before` with the
|
|
954
|
+
// first line of `after`; surrounding lines render as-is.
|
|
955
|
+
const renderLines = [];
|
|
956
|
+
// Lines before the cursor line.
|
|
957
|
+
for (let i = 0; i < beforeLines.length - 1; i++) {
|
|
958
|
+
renderLines.push({ kind: "plain", text: beforeLines[i] });
|
|
959
|
+
}
|
|
960
|
+
// Cursor line.
|
|
961
|
+
renderLines.push({
|
|
962
|
+
kind: "cursor",
|
|
963
|
+
left: beforeLines[beforeLines.length - 1],
|
|
964
|
+
right: afterLines[0],
|
|
965
|
+
});
|
|
966
|
+
// Lines after the cursor line.
|
|
967
|
+
for (let i = 1; i < afterLines.length; i++) {
|
|
968
|
+
renderLines.push({ kind: "plain", text: afterLines[i] });
|
|
969
|
+
}
|
|
970
|
+
const cursorLineInBuffer = beforeLines.length - 1;
|
|
971
|
+
const inputWindowStart = inputViewportStart(renderLines.length, cursorLineInBuffer);
|
|
972
|
+
const visibleRenderLines = renderLines.slice(inputWindowStart, inputWindowStart + MAX_INPUT_VISIBLE_LINES);
|
|
973
|
+
const hiddenTop = inputWindowStart > 0;
|
|
974
|
+
const hiddenBottom = inputWindowStart + visibleRenderLines.length < renderLines.length;
|
|
975
|
+
const hiddenBottomCount = renderLines.length - inputWindowStart - visibleRenderLines.length;
|
|
976
|
+
// Lines within the visible input viewport above the cursor's line.
|
|
977
|
+
const cursorLineWithinInput = (hiddenTop ? 1 : 0) + cursorLineInBuffer - inputWindowStart;
|
|
978
|
+
setCursorPosition(showHardwareCursor && cursorOrigin
|
|
979
|
+
? inputHardwareCursorPosition(cursorOrigin, cursorCol, cursorLineWithinInput, fullscreenCursorMode)
|
|
980
|
+
: undefined);
|
|
981
|
+
// Phase 83 / 85: surface shell-prefix mode by recoloring the
|
|
982
|
+
// prompt arrow magenta (pink) while the buffer leads with `!`.
|
|
983
|
+
// Red read like an error; magenta reads as "different mode".
|
|
984
|
+
const shellMode = buffer.startsWith("!");
|
|
985
|
+
const promptColor = shellMode
|
|
986
|
+
? theme.prompt.shellColor
|
|
987
|
+
: theme.prompt.arrowColor;
|
|
988
|
+
const promptGlyph = theme.prompt.arrow;
|
|
989
|
+
return (_jsxs(Box, { flexDirection: "column", children: [popupOpen && (_jsx(FileSuggest, { items: fileSuggestions, selectedIdx: mentionSel })), paletteOpen && _jsx(SlashPalette, { items: palette, selectedIdx: paletteSel }), search && (_jsxs(Box, { children: [_jsxs(Text, { color: theme.hint.color, children: ["(reverse-i-search)`", search.query, "`: "] }), _jsx(Text, { children: reverseMatches(search.query)[search.idx] ?? "" })] })), _jsxs(Box, { flexDirection: "column", ref: wrapperRef, children: [hiddenTop && (_jsx(Box, { children: _jsxs(Text, { color: theme.hint.color, dimColor: true, children: [" ⋮ ", inputWindowStart, " line", inputWindowStart === 1 ? "" : "s", " above"] }) })), visibleRenderLines.map((row, i) => {
|
|
990
|
+
const absoluteLine = inputWindowStart + i;
|
|
991
|
+
return (_jsxs(Box, { children: [_jsx(Text, { color: promptColor, children: absoluteLine === 0 ? promptGlyph : " " }), row.kind === "plain" ? (_jsx(Text, { children: row.text })) : (_jsxs(_Fragment, { children: [_jsx(Text, { children: row.left }), _jsx(Text, { children: row.right }), ghostSuffix && (_jsx(Text, { color: theme.status.labelColor, dimColor: true, children: ghostSuffix }))] }))] }, absoluteLine));
|
|
992
|
+
}), hiddenBottom && (_jsx(Box, { children: _jsxs(Text, { color: theme.hint.color, dimColor: true, children: [" ⋮ ", hiddenBottomCount, " line", hiddenBottomCount === 1 ? "" : "s", " ", "below"] }) }))] }), isMultiLine && (_jsx(Box, { children: _jsxs(Text, { color: theme.hint.color, children: [" ", "(multi-line \u2014 Enter submits; Shift/Alt+Enter or trailing \\\\ for newline)"] }) })), shellMode && !isMultiLine && (_jsx(Box, { children: _jsxs(Text, { color: theme.prompt.shellColor, children: [" ", "shell mode \u2014 Enter runs the command in the active tab's cwd, model not invoked"] }) }))] }));
|
|
993
|
+
}
|
|
994
|
+
//# sourceMappingURL=CommandInput.js.map
|