mixdog 0.9.3 → 0.9.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +112 -38
- package/package.json +10 -3
- package/scripts/bench/lead-review-tasks-r3.json +20 -0
- package/scripts/bench/lead-review-tasks.json +20 -0
- package/scripts/bench/r4-mixed-tasks.json +20 -0
- package/scripts/bench/r5-orchestrated-task.json +7 -0
- package/scripts/bench/review-tasks.json +20 -0
- package/scripts/bench/round-codex.json +114 -0
- package/scripts/bench/round-mixdog-lead-r3.json +269 -0
- package/scripts/bench/round-mixdog-lead.json +269 -0
- package/scripts/bench/round-mixdog.json +126 -0
- package/scripts/bench/round-r10-bigsample.json +679 -0
- package/scripts/bench/round-r11-codexalign.json +257 -0
- package/scripts/bench/round-r13-clientmeta.json +464 -0
- package/scripts/bench/round-r14-betafeatures.json +466 -0
- package/scripts/bench/round-r15-fulldefault.json +462 -0
- package/scripts/bench/round-r16-sessionid.json +466 -0
- package/scripts/bench/round-r17-wirebytes.json +456 -0
- package/scripts/bench/round-r18-prewarm.json +468 -0
- package/scripts/bench/round-r19-clean.json +472 -0
- package/scripts/bench/round-r20-prewarm-clean.json +475 -0
- package/scripts/bench/round-r21-delta-retry.json +473 -0
- package/scripts/bench/round-r22-full-probe.json +693 -0
- package/scripts/bench/round-r23-itemprobe.json +701 -0
- package/scripts/bench/round-r24-shapefix.json +677 -0
- package/scripts/bench/round-r25-serial.json +464 -0
- package/scripts/bench/round-r26-parallel3.json +671 -0
- package/scripts/bench/round-r27-parallel10.json +894 -0
- package/scripts/bench/round-r28-parallel10-stagger.json +882 -0
- package/scripts/bench/round-r29-parallel10-stagger166.json +886 -0
- package/scripts/bench/round-r30-instid.json +253 -0
- package/scripts/bench/round-r31-upgradeprobe.json +256 -0
- package/scripts/bench/round-r32-vs-codex-lead.json +254 -0
- package/scripts/bench/round-r33-vs-codex-codex.json +115 -0
- package/scripts/bench/round-r34-orchestrated.json +120 -0
- package/scripts/bench/round-r35-orchestrated-codex.json +61 -0
- package/scripts/bench/round-r36-orchestrated-capped.json +128 -0
- package/scripts/bench/round-r4-codex.json +114 -0
- package/scripts/bench/round-r4-mixed.json +225 -0
- package/scripts/bench/round-r5-gpt-lead.json +259 -0
- package/scripts/bench/round-r6-codex.json +114 -0
- package/scripts/bench/round-r6-solo.json +257 -0
- package/scripts/bench/round-r7-full.json +254 -0
- package/scripts/bench/round-r8-fulldefault.json +255 -0
- package/scripts/bench-run.mjs +251 -32
- package/scripts/freevar-smoke.mjs +95 -0
- package/scripts/internal-comms-bench.mjs +3 -4
- package/scripts/internal-comms-smoke.mjs +10 -9
- package/scripts/model-catalog-audit.mjs +209 -0
- package/scripts/model-list-sanitize-test.mjs +37 -0
- package/scripts/mouse-probe.mjs +45 -0
- package/scripts/output-style-bench.mjs +13 -6
- package/scripts/output-style-smoke.mjs +4 -4
- package/scripts/provider-toolcall-test.mjs +7 -3
- package/scripts/recall-bench.mjs +76 -13
- package/scripts/recall-quality-cases.json +12 -0
- package/scripts/recall-usecase-cases.json +18 -0
- package/scripts/session-bench.mjs +152 -6
- package/scripts/tool-smoke.mjs +25 -65
- package/scripts/tui-render-smoke.mjs +90 -0
- package/scripts/webhook-smoke.mjs +208 -0
- package/src/agents/debugger/AGENT.md +4 -1
- package/src/agents/heavy-worker/AGENT.md +9 -8
- package/src/agents/maintainer/AGENT.md +4 -0
- package/src/agents/reviewer/AGENT.md +2 -1
- package/src/agents/scheduler-task/AGENT.md +2 -3
- package/src/agents/webhook-handler/AGENT.md +2 -3
- package/src/agents/worker/AGENT.md +10 -7
- package/src/app.mjs +12 -1
- package/src/headless-role.mjs +7 -1
- package/src/lib/rules-builder.cjs +4 -0
- package/src/mixdog-session-runtime.mjs +647 -2056
- package/src/output-styles/default.md +30 -9
- package/src/output-styles/{oneline.md → extreme-minimal.md} +5 -4
- package/src/output-styles/minimal.md +8 -6
- package/src/output-styles/simple.md +21 -7
- package/src/rules/agent/00-common.md +6 -3
- package/src/rules/agent/30-explorer.md +16 -5
- package/src/rules/lead/01-general.md +5 -5
- package/src/rules/lead/lead-brief.md +15 -0
- package/src/rules/lead/lead-tool.md +6 -15
- package/src/rules/shared/01-tool.md +17 -21
- package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch.mjs +8 -3
- package/src/runtime/agent/orchestrator/agent-runtime/agent-loop-policy.mjs +25 -0
- package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +100 -23
- package/src/runtime/agent/orchestrator/agent-runtime/session-builder.mjs +6 -15
- package/src/runtime/agent/orchestrator/agent-trace-format.mjs +362 -0
- package/src/runtime/agent/orchestrator/agent-trace-io.mjs +410 -0
- package/src/runtime/agent/orchestrator/agent-trace.mjs +16 -735
- package/src/runtime/agent/orchestrator/config.mjs +69 -2
- package/src/runtime/agent/orchestrator/providers/anthropic-effort.mjs +62 -20
- package/src/runtime/agent/orchestrator/providers/anthropic-model-resolve.mjs +209 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +489 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +81 -1281
- package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +607 -0
- package/src/runtime/agent/orchestrator/providers/anthropic.mjs +32 -3
- package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +81 -0
- package/src/runtime/agent/orchestrator/providers/gemini-cache.mjs +248 -0
- package/src/runtime/agent/orchestrator/providers/gemini-schema.mjs +303 -0
- package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +505 -0
- package/src/runtime/agent/orchestrator/providers/gemini.mjs +43 -1013
- package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +17 -3
- package/src/runtime/agent/orchestrator/providers/model-catalog.mjs +105 -11
- package/src/runtime/agent/orchestrator/providers/model-list-sanitize.mjs +356 -0
- package/src/runtime/agent/orchestrator/providers/openai-codex-model.mjs +108 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-trace.mjs +58 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +368 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-xai.mjs +760 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +40 -1143
- package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +740 -0
- package/src/runtime/agent/orchestrator/providers/openai-oauth-login.mjs +193 -0
- package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +349 -2131
- package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +143 -1002
- package/src/runtime/agent/orchestrator/providers/openai-ws-delta.mjs +229 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-events.mjs +67 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-pool.mjs +465 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +1105 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +2 -1
- package/src/runtime/agent/orchestrator/providers/provider-catalog-cache.mjs +80 -0
- package/src/runtime/agent/orchestrator/session/compact/budget.mjs +288 -0
- package/src/runtime/agent/orchestrator/session/compact/constants.mjs +85 -0
- package/src/runtime/agent/orchestrator/session/compact/engine.mjs +749 -0
- package/src/runtime/agent/orchestrator/session/compact/messages.mjs +82 -0
- package/src/runtime/agent/orchestrator/session/compact/summary-schema.mjs +315 -0
- package/src/runtime/agent/orchestrator/session/compact/summary.mjs +643 -0
- package/src/runtime/agent/orchestrator/session/compact/text-utils.mjs +326 -0
- package/src/runtime/agent/orchestrator/session/compact.mjs +40 -2282
- package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +14 -2
- package/src/runtime/agent/orchestrator/session/loop/completion-guards.mjs +61 -0
- package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +1 -3
- package/src/runtime/agent/orchestrator/session/loop/recall-fasttrack.mjs +275 -0
- package/src/runtime/agent/orchestrator/session/loop/steering-ladder.mjs +173 -0
- package/src/runtime/agent/orchestrator/session/loop/termination.mjs +58 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +239 -0
- package/src/runtime/agent/orchestrator/session/loop.mjs +278 -402
- package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +471 -0
- package/src/runtime/agent/orchestrator/session/manager/context-meta.mjs +7 -4
- package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +12 -0
- package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +406 -0
- package/src/runtime/agent/orchestrator/session/manager/status-telemetry.mjs +80 -0
- package/src/runtime/agent/orchestrator/session/manager/usage-metrics.mjs +210 -0
- package/src/runtime/agent/orchestrator/session/manager.mjs +166 -1087
- package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +189 -0
- package/src/runtime/agent/orchestrator/session/store.mjs +74 -179
- package/src/runtime/agent/orchestrator/stall-policy.mjs +20 -1
- package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +70 -20
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +22 -2
- package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +35 -44
- package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +40 -0
- package/src/runtime/agent/orchestrator/tools/builtin/rg-runner.mjs +29 -0
- package/src/runtime/agent/orchestrator/tools/builtin/search-builders.mjs +8 -0
- package/src/runtime/agent/orchestrator/tools/builtin/search-path-diagnostics.mjs +126 -0
- package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +81 -92
- package/src/runtime/agent/orchestrator/tools/builtin/shell-job-paths.mjs +161 -0
- package/src/runtime/agent/orchestrator/tools/builtin/shell-job-process.mjs +108 -0
- package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +28 -265
- package/src/runtime/agent/orchestrator/tools/builtin.mjs +0 -6
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +57 -3
- package/src/runtime/agent/orchestrator/tools/code-graph/keyword-match.mjs +82 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/search.mjs +10 -122
- package/src/runtime/agent/orchestrator/tools/code-graph/text-columns.mjs +45 -0
- package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +6 -6
- package/src/runtime/agent/orchestrator/tools/graph-binary-fetcher.mjs +6 -3
- package/src/runtime/agent/orchestrator/tools/patch/constants.mjs +9 -0
- package/src/runtime/agent/orchestrator/tools/patch/dispatch.mjs +171 -0
- package/src/runtime/agent/orchestrator/tools/patch/matcher.mjs +471 -0
- package/src/runtime/agent/orchestrator/tools/patch/native-server.mjs +436 -0
- package/src/runtime/agent/orchestrator/tools/patch/orchestrator.mjs +342 -0
- package/src/runtime/agent/orchestrator/tools/patch/parsing.mjs +359 -0
- package/src/runtime/agent/orchestrator/tools/patch/paths.mjs +340 -0
- package/src/runtime/agent/orchestrator/tools/patch/v4a-convert.mjs +643 -0
- package/src/runtime/agent/orchestrator/tools/patch.mjs +36 -2959
- package/src/runtime/agent/orchestrator/tools/progress-message.mjs +0 -21
- package/src/runtime/agent/orchestrator/tools/shell-command.mjs +9 -72
- package/src/runtime/agent/orchestrator/tools/shell-powershell.mjs +77 -0
- package/src/runtime/agent/orchestrator/tools/shell-state.mjs +154 -0
- package/src/runtime/channels/backends/discord-access.mjs +32 -0
- package/src/runtime/channels/backends/discord-attachments.mjs +65 -0
- package/src/runtime/channels/backends/discord-gateway.mjs +233 -0
- package/src/runtime/channels/backends/discord.mjs +27 -318
- package/src/runtime/channels/backends/telegram.mjs +8 -12
- package/src/runtime/channels/index.mjs +247 -701
- package/src/runtime/channels/lib/backend-dispatch.mjs +46 -0
- package/src/runtime/channels/lib/config.mjs +37 -149
- package/src/runtime/channels/lib/event-pipeline.mjs +22 -5
- package/src/runtime/channels/lib/event-queue.mjs +78 -13
- package/src/runtime/channels/lib/inbound-routing.mjs +74 -0
- package/src/runtime/channels/lib/interaction-workflows.mjs +5 -113
- package/src/runtime/channels/lib/output-forwarder.mjs +1 -1
- package/src/runtime/channels/lib/owner-heartbeat.mjs +75 -0
- package/src/runtime/channels/lib/parent-bridge.mjs +88 -0
- package/src/runtime/channels/lib/runtime-paths.mjs +14 -4
- package/src/runtime/channels/lib/scheduler.mjs +27 -113
- package/src/runtime/channels/lib/session-discovery.mjs +56 -4
- package/src/runtime/channels/lib/tool-dispatch.mjs +158 -0
- package/src/runtime/channels/lib/tool-format.mjs +1 -1
- package/src/runtime/channels/lib/transcript-discovery.mjs +4 -4
- package/src/runtime/channels/lib/voice-runtime-fetcher.mjs +6 -3
- package/src/runtime/channels/lib/voice-transcription.mjs +179 -0
- package/src/runtime/channels/lib/webhook/deliveries.mjs +313 -0
- package/src/runtime/channels/lib/webhook/log.mjs +42 -0
- package/src/runtime/channels/lib/webhook/ngrok.mjs +181 -0
- package/src/runtime/channels/lib/webhook/signature.mjs +60 -0
- package/src/runtime/channels/lib/webhook.mjs +43 -616
- package/src/runtime/channels/tool-defs.mjs +11 -130
- package/src/runtime/memory/index.mjs +210 -1948
- package/src/runtime/memory/lib/core-memory-store.mjs +5 -1
- package/src/runtime/memory/lib/cycle-llm-adapters.mjs +58 -0
- package/src/runtime/memory/lib/cycle-scheduler.mjs +497 -0
- package/src/runtime/memory/lib/embedding-warmup.mjs +58 -0
- package/src/runtime/memory/lib/ko-morph.mjs +195 -0
- package/src/runtime/memory/lib/memory-config-flags.mjs +91 -0
- package/src/runtime/memory/lib/memory-cycle.mjs +1 -1
- package/src/runtime/memory/lib/memory-cycle2-gate.mjs +515 -0
- package/src/runtime/memory/lib/memory-cycle2-mutations.mjs +324 -0
- package/src/runtime/memory/lib/memory-cycle2-shared.mjs +18 -0
- package/src/runtime/memory/lib/memory-cycle2.mjs +24 -842
- package/src/runtime/memory/lib/memory-embed.mjs +149 -0
- package/src/runtime/memory/lib/memory-process-lock.mjs +162 -0
- package/src/runtime/memory/lib/memory-recall-store.mjs +69 -12
- package/src/runtime/memory/lib/memory-text-utils.mjs +46 -0
- package/src/runtime/memory/lib/pg/supervisor.mjs +1 -1
- package/src/runtime/memory/lib/query-handlers.mjs +802 -0
- package/src/runtime/memory/lib/recall-format.mjs +55 -0
- package/src/runtime/memory/lib/runtime-fetcher.mjs +8 -3
- package/src/runtime/memory/lib/transcript-ingest.mjs +425 -0
- package/src/runtime/memory/tool-defs.mjs +5 -13
- package/src/runtime/search/lib/http-fetch.mjs +274 -0
- package/src/runtime/search/lib/ssrf-guard.mjs +333 -0
- package/src/runtime/search/lib/web-tools.mjs +24 -602
- package/src/runtime/shared/atomic-file.mjs +26 -1
- package/src/runtime/shared/config.mjs +14 -4
- package/src/runtime/shared/launcher-control.mjs +2 -2
- package/src/runtime/shared/markdown-frontmatter.mjs +19 -0
- package/src/runtime/shared/schedules-store.mjs +13 -3
- package/src/runtime/shared/tool-execution-contract.mjs +2 -2
- package/src/runtime/shared/tool-primitives.mjs +308 -0
- package/src/runtime/shared/tool-result-summary.mjs +515 -0
- package/src/runtime/shared/tool-surface.mjs +80 -898
- package/src/runtime/shared/transcript-writer.mjs +23 -0
- package/src/runtime/shared/update-checker.mjs +7 -4
- package/src/session-runtime/config-helpers.mjs +119 -2
- package/src/session-runtime/config-lifecycle.mjs +232 -0
- package/src/session-runtime/cwd-plugins.mjs +226 -0
- package/src/session-runtime/mcp-glue.mjs +177 -0
- package/src/session-runtime/model-recency.mjs +111 -0
- package/src/session-runtime/native-search.mjs +247 -0
- package/src/session-runtime/output-styles.mjs +11 -9
- package/src/session-runtime/prewarm.mjs +142 -0
- package/src/session-runtime/provider-models.mjs +278 -0
- package/src/session-runtime/provider-usage.mjs +120 -0
- package/src/session-runtime/quick-model-rows.mjs +205 -0
- package/src/session-runtime/quick-search-models.mjs +47 -0
- package/src/session-runtime/session-hooks.mjs +93 -0
- package/src/session-runtime/settings-api.mjs +352 -0
- package/src/session-runtime/tool-catalog.mjs +29 -29
- package/src/session-runtime/tool-defs.mjs +84 -0
- package/src/session-runtime/warmup-schedulers.mjs +201 -0
- package/src/session-runtime/workflow.mjs +1 -1
- package/src/standalone/agent-tool/helpers.mjs +237 -0
- package/src/standalone/agent-tool/notify.mjs +107 -0
- package/src/standalone/agent-tool/provider-init.mjs +143 -0
- package/src/standalone/agent-tool/render.mjs +152 -0
- package/src/standalone/agent-tool/tool-def.mjs +55 -0
- package/src/standalone/agent-tool.mjs +138 -669
- package/src/standalone/channel-admin.mjs +102 -90
- package/src/standalone/channel-worker.mjs +4 -7
- package/src/standalone/explore-tool.mjs +64 -14
- package/src/standalone/hook-bus/config.mjs +207 -0
- package/src/standalone/hook-bus/constants.mjs +90 -0
- package/src/standalone/hook-bus/handlers.mjs +481 -0
- package/src/standalone/hook-bus/payload.mjs +31 -0
- package/src/standalone/hook-bus/rules.mjs +77 -0
- package/src/standalone/hook-bus.mjs +77 -870
- package/src/standalone/memory-runtime-proxy.mjs +7 -0
- package/src/standalone/opencode-go-login.mjs +5 -1
- package/src/standalone/provider-admin.mjs +1 -16
- package/src/standalone/usage-dashboard.mjs +3 -1
- package/src/tui/App.jsx +1059 -8110
- package/src/tui/app/app-format.mjs +213 -0
- package/src/tui/app/channel-pickers.mjs +508 -0
- package/src/tui/app/clipboard.mjs +67 -0
- package/src/tui/app/core-memory-picker.mjs +210 -0
- package/src/tui/app/extension-pickers.mjs +506 -0
- package/src/tui/app/input-parsers.mjs +193 -0
- package/src/tui/app/maintenance-pickers.mjs +356 -0
- package/src/tui/app/model-options.mjs +334 -0
- package/src/tui/app/model-picker.mjs +365 -0
- package/src/tui/app/onboarding-steps.mjs +400 -0
- package/src/tui/app/project-picker.mjs +247 -0
- package/src/tui/app/provider-setup-picker.mjs +580 -0
- package/src/tui/app/resume-picker.mjs +55 -0
- package/src/tui/app/route-pickers.mjs +419 -0
- package/src/tui/app/settings-picker.mjs +489 -0
- package/src/tui/app/slash-commands.mjs +101 -0
- package/src/tui/app/slash-dispatch.mjs +427 -0
- package/src/tui/app/text-layout.mjs +46 -0
- package/src/tui/app/theme-effort-pickers.mjs +154 -0
- package/src/tui/app/transcript-window.mjs +677 -0
- package/src/tui/app/use-mouse-input.mjs +460 -0
- package/src/tui/app/use-prompt-handlers.mjs +310 -0
- package/src/tui/app/use-transcript-scroll.mjs +512 -0
- package/src/tui/app/use-transcript-window.mjs +607 -0
- package/src/tui/components/ConfirmBar.jsx +10 -7
- package/src/tui/components/Picker.jsx +64 -15
- package/src/tui/components/PromptInput.jsx +33 -102
- package/src/tui/components/SlashCommandPalette.jsx +8 -1
- package/src/tui/components/StatusLine.jsx +69 -15
- package/src/tui/components/TextEntryPanel.jsx +11 -0
- package/src/tui/components/ToolExecution.jsx +52 -594
- package/src/tui/components/TranscriptItem.jsx +105 -0
- package/src/tui/components/UsagePanel.jsx +18 -4
- package/src/tui/components/prompt-input/edit-helpers.mjs +72 -0
- package/src/tui/components/prompt-input/voice-indicator.mjs +39 -0
- package/src/tui/components/tool-execution/ResultBody.jsx +56 -0
- package/src/tui/components/tool-execution/surface-detail.mjs +405 -0
- package/src/tui/components/tool-execution/text-format.mjs +161 -0
- package/src/tui/display-width.mjs +20 -3
- package/src/tui/dist/index.mjs +13553 -12384
- package/src/tui/engine/agent-job-feed.mjs +133 -0
- package/src/tui/engine/notification-plan.mjs +76 -0
- package/src/tui/engine/render-timing.mjs +17 -0
- package/src/tui/engine/tool-approval.mjs +94 -0
- package/src/tui/engine/tool-card-results.mjs +234 -0
- package/src/tui/engine/tool-result-status.mjs +135 -0
- package/src/tui/engine.mjs +170 -574
- package/src/tui/figures.mjs +5 -0
- package/src/tui/index.jsx +65 -1
- package/src/tui/input-editing.mjs +2 -2
- package/src/tui/markdown/format-token.mjs +4 -1
- package/src/tui/statusline-ansi-bridge.mjs +11 -3
- package/src/tui/theme.mjs +6 -0
- package/src/ui/statusline-agents.mjs +213 -0
- package/src/ui/statusline-format.mjs +146 -0
- package/src/ui/statusline-segments.mjs +148 -0
- package/src/ui/statusline.mjs +77 -501
- package/src/ui/tool-card.mjs +0 -1
- package/src/vendor/statusline/bin/statusline-route.mjs +15 -2
- package/src/workflows/default/WORKFLOW.md +16 -18
- package/src/workflows/sequential/WORKFLOW.md +16 -18
- package/vendor/ink/build/display-width.js +19 -3
- package/vendor/ink/build/ink.js +112 -7
- package/vendor/ink/build/log-update.js +17 -3
- package/vendor/ink/build/wrap-text.js +125 -0
- package/scripts/_test-folder-dialog.mjs +0 -30
- package/scripts/fix-brief-fn.mjs +0 -35
- package/scripts/fix-format-tool-surface.mjs +0 -24
- package/scripts/fix-tool-exec-visible.mjs +0 -42
- package/scripts/patch-agent-brief.mjs +0 -48
- package/scripts/patch-app.mjs +0 -21
- package/scripts/patch-app2.mjs +0 -18
- package/scripts/patch-dist-brief.mjs +0 -96
- package/scripts/patch-tool-exec.mjs +0 -70
- package/src/examples/schedules/SCHEDULE.example.md +0 -32
- package/src/examples/webhooks/WEBHOOK.example.md +0 -40
- package/src/runtime/agent/orchestrator/session/manager.reactive-persist.test.mjs +0 -107
- package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.test.mjs +0 -143
- package/src/runtime/agent/orchestrator/tools/builtin/diagnostics-tool.mjs +0 -285
- package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.test.mjs +0 -162
- package/src/runtime/agent/orchestrator/tools/builtin/open-config-tool.mjs +0 -26
- package/src/runtime/channels/lib/holidays.mjs +0 -138
- package/src/runtime/shared/channel-notification-routing.test.mjs +0 -45
- package/src/runtime/shared/task-notification-envelope.test.mjs +0 -107
- package/src/runtime/shared/tool-execution-contract.test.mjs +0 -183
- package/src/standalone/agent-task-status.test.mjs +0 -76
- package/src/tui/components/tool-output-format.test.mjs +0 -399
- package/src/tui/display-width.test.mjs +0 -35
- package/src/tui/engine-runtime-notification.test.mjs +0 -115
- package/src/tui/engine-tool-result-text.test.mjs +0 -75
- package/src/tui/input-editing.selection.test.mjs +0 -75
- package/src/tui/markdown/format-token.test.mjs +0 -354
- package/src/tui/markdown/render-ansi.test.mjs +0 -108
- package/src/tui/markdown/stream-fence.test.mjs +0 -26
- package/src/tui/markdown/streaming-markdown.test.mjs +0 -70
- package/src/tui/paste-fix.test.mjs +0 -119
- package/src/tui/prompt-history-store.test.mjs +0 -52
- package/src/tui/statusline-ansi-bridge.test.mjs +0 -159
- package/src/tui/transcript-tool-failures.test.mjs +0 -111
- package/src/ui/markdown.test.mjs +0 -70
- package/src/ui/statusline-context-label.test.mjs +0 -15
- package/src/vendor/statusline/bin/statusline-lib.mjs +0 -186
- package/src/vendor/statusline/bin/statusline-route.test.mjs +0 -80
|
@@ -0,0 +1,460 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* use-mouse-input.mjs — SGR mouse handling hook for the App shell.
|
|
3
|
+
*
|
|
4
|
+
* Extracted verbatim from App.jsx: the ctrl+wheel zoom passthrough and the
|
|
5
|
+
* big SGR input effect (wheel scroll routing, prompt/transcript/status text
|
|
6
|
+
* selection with word/line multi-click, drag auto-scroll). All shared state
|
|
7
|
+
* stays owned by App and is injected via refs/callbacks so behavior is
|
|
8
|
+
* unchanged; only the zoom-passthrough timer is owned here.
|
|
9
|
+
*/
|
|
10
|
+
import { useCallback, useEffect, useRef } from 'react';
|
|
11
|
+
import { overlayBlocksGlobalTranscriptScroll } from './slash-commands.mjs';
|
|
12
|
+
import { TRANSCRIPT_MEASURED_ROWS } from './transcript-window.mjs';
|
|
13
|
+
|
|
14
|
+
const MOUSE_TRACKING_ON = '\x1b[?1000h\x1b[?1002h\x1b[?1006h';
|
|
15
|
+
const MOUSE_TRACKING_OFF = '\x1b[?1006l\x1b[?1002l\x1b[?1000l';
|
|
16
|
+
// Alternate-scroll mode (DECSET 1007). In the alt-screen, Windows Terminal
|
|
17
|
+
// converts wheel input into arrow keys while this mode is on — and that
|
|
18
|
+
// conversion WINS over WT's native ctrl+wheel font zoom. During the zoom
|
|
19
|
+
// passthrough window we must turn BOTH mouse tracking and alternate scroll
|
|
20
|
+
// off, or ctrl+wheel lands as Up/Down (prompt history) instead of zooming.
|
|
21
|
+
const ALT_SCROLL_OFF = '\x1b[?1007l';
|
|
22
|
+
const ALT_SCROLL_ON = '\x1b[?1007h';
|
|
23
|
+
const MOUSE_MODIFIER_MASK = 4 | 8 | 16;
|
|
24
|
+
const MOUSE_CTRL_MASK = 16;
|
|
25
|
+
// Bit 2 (4) of the SGR button byte = shift held during the click. Wheel/ctrl
|
|
26
|
+
// masking above intentionally strips it for scroll routing; button-press
|
|
27
|
+
// handling below reads it separately (before baseButton = button & 3 drops
|
|
28
|
+
// every modifier bit) so a shift-held left-click can extend an existing
|
|
29
|
+
// selection instead of starting a fresh one.
|
|
30
|
+
const MOUSE_SHIFT_MASK = 4;
|
|
31
|
+
|
|
32
|
+
export function useMouseInput({
|
|
33
|
+
inkInput,
|
|
34
|
+
isRawModeSupported,
|
|
35
|
+
store,
|
|
36
|
+
stdout,
|
|
37
|
+
rows,
|
|
38
|
+
statuslineBandRows,
|
|
39
|
+
dragRef,
|
|
40
|
+
lastClickRef,
|
|
41
|
+
slashPaletteRef,
|
|
42
|
+
scrollFocusRef,
|
|
43
|
+
promptMouseSelectionRef,
|
|
44
|
+
frameRowsRef,
|
|
45
|
+
promptBoxRectRef,
|
|
46
|
+
transcriptViewportRef,
|
|
47
|
+
scrollTargetRef,
|
|
48
|
+
stopSmoothScroll,
|
|
49
|
+
applySelectionRect,
|
|
50
|
+
applySelectionRectThrottled,
|
|
51
|
+
selectionPointAtCurrentScroll,
|
|
52
|
+
buildSpanRect,
|
|
53
|
+
scrollTranscriptRows,
|
|
54
|
+
queueScrollCoalesced,
|
|
55
|
+
setSlashIndex,
|
|
56
|
+
setMeasuredRowsVersion,
|
|
57
|
+
}) {
|
|
58
|
+
const mouseZoomPassthroughTimerRef = useRef(null);
|
|
59
|
+
|
|
60
|
+
const passthroughCtrlWheelZoom = useCallback(() => {
|
|
61
|
+
if (!stdout?.write) return;
|
|
62
|
+
try {
|
|
63
|
+
stdout.write(MOUSE_TRACKING_OFF + ALT_SCROLL_OFF);
|
|
64
|
+
} catch {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
if (mouseZoomPassthroughTimerRef.current) clearTimeout(mouseZoomPassthroughTimerRef.current);
|
|
68
|
+
mouseZoomPassthroughTimerRef.current = setTimeout(() => {
|
|
69
|
+
mouseZoomPassthroughTimerRef.current = null;
|
|
70
|
+
try {
|
|
71
|
+
stdout.write(ALT_SCROLL_ON + MOUSE_TRACKING_ON);
|
|
72
|
+
} catch {
|
|
73
|
+
// The terminal may already be closing.
|
|
74
|
+
}
|
|
75
|
+
}, 700);
|
|
76
|
+
mouseZoomPassthroughTimerRef.current.unref?.();
|
|
77
|
+
}, [stdout]);
|
|
78
|
+
|
|
79
|
+
useEffect(() => () => {
|
|
80
|
+
if (mouseZoomPassthroughTimerRef.current) clearTimeout(mouseZoomPassthroughTimerRef.current);
|
|
81
|
+
}, []);
|
|
82
|
+
|
|
83
|
+
// Optional mouse handling. When index.jsx enables SGR mouse tracking
|
|
84
|
+
// (?1000h button + ?1002h drag-motion + ?1006h SGR coords).
|
|
85
|
+
// Every event arrives as `\x1b[<b;col;rowM`
|
|
86
|
+
// (press/motion) or `\x1b[<b;col;rowm` (release), 1-based col/row. We watch raw
|
|
87
|
+
// stdin and split it two ways, both additive to ink's keyboard handling:
|
|
88
|
+
// • wheel (button 64 up / 65 down) → scroll the transcript
|
|
89
|
+
// • left-button (0) press → drag → release → in-app text selection; dragging
|
|
90
|
+
// against the top/bottom edge scrolls the transcript while selecting.
|
|
91
|
+
// The highlight stays visible after release so the user can confirm the
|
|
92
|
+
// selected region; ESC or a plain click clears it.
|
|
93
|
+
// Because we run a true fullscreen alt-screen, the reported (row,col) maps 1:1
|
|
94
|
+
// to ink's absolute output grid. We keep anchor/focus points instead of a
|
|
95
|
+
// rectangular min/max box so multi-line drags behave like normal text
|
|
96
|
+
// selection, not terminal block selection.
|
|
97
|
+
useEffect(() => {
|
|
98
|
+
if (!inkInput || !isRawModeSupported) return undefined;
|
|
99
|
+
// Match every SGR mouse event: button, col, row, and final M(press)/m(release).
|
|
100
|
+
const MOUSE = /\x1b\[<(\d+);(\d+);(\d+)([Mm])/g;
|
|
101
|
+
const linearSelection = (a, b) => {
|
|
102
|
+
return {
|
|
103
|
+
mode: 'linear',
|
|
104
|
+
x1: a.x,
|
|
105
|
+
y1: a.y,
|
|
106
|
+
x2: b.x,
|
|
107
|
+
y2: b.y,
|
|
108
|
+
};
|
|
109
|
+
};
|
|
110
|
+
// Word/line multi-click drag-extension uses the hoisted buildSpanRect (same
|
|
111
|
+
// logic reachable from the auto-scroll path in scrollTranscriptRows).
|
|
112
|
+
const transcriptViewport = () => {
|
|
113
|
+
const top = Math.max(0, Number(transcriptViewportRef.current?.top) || 0);
|
|
114
|
+
const bottom = Math.max(top, Number(transcriptViewportRef.current?.bottom) || top);
|
|
115
|
+
return { top, bottom };
|
|
116
|
+
};
|
|
117
|
+
const isInTranscriptViewport = (row) => {
|
|
118
|
+
const { top, bottom } = transcriptViewport();
|
|
119
|
+
return row >= top && row <= bottom;
|
|
120
|
+
};
|
|
121
|
+
const clampToTranscriptViewport = (row) => {
|
|
122
|
+
const { top, bottom } = transcriptViewport();
|
|
123
|
+
return Math.max(top, Math.min(bottom, row));
|
|
124
|
+
};
|
|
125
|
+
// [mixdog] Status-bar band = the bottom statuslineBandRows rows. The
|
|
126
|
+
// prompt box occupies the rows reported by PromptInput's measured rect.
|
|
127
|
+
const statusBand = () => {
|
|
128
|
+
const rows = Math.max(1, Number(frameRowsRef.current) || 24);
|
|
129
|
+
const top = Math.max(0, rows - statuslineBandRows);
|
|
130
|
+
return { top, bottom: Math.max(top, rows - 1) };
|
|
131
|
+
};
|
|
132
|
+
const isInStatusBand = (row) => {
|
|
133
|
+
const { top, bottom } = statusBand();
|
|
134
|
+
return row >= top && row <= bottom;
|
|
135
|
+
};
|
|
136
|
+
const clampToStatusBand = (row) => {
|
|
137
|
+
const { top, bottom } = statusBand();
|
|
138
|
+
return Math.max(top, Math.min(bottom, row));
|
|
139
|
+
};
|
|
140
|
+
const promptRect = () => promptBoxRectRef.current;
|
|
141
|
+
const isInPromptBox = (x, y) => {
|
|
142
|
+
const r = promptRect();
|
|
143
|
+
if (!r) return false;
|
|
144
|
+
const top = Math.max(0, Number(r.top) || 0);
|
|
145
|
+
const bottom = top + Math.max(1, Number(r.height) || 1) - 1;
|
|
146
|
+
const left = Math.max(0, Number(r.left) || 0);
|
|
147
|
+
const width = Math.max(1, Number(r.contentWidth) || 1);
|
|
148
|
+
return y >= top && y <= bottom && x >= left && x <= left + width;
|
|
149
|
+
};
|
|
150
|
+
// Map an absolute grid cell to a prompt-draft edit offset via PromptInput's
|
|
151
|
+
// measured box rect + its caret math (offsetAtCell handles wrapping).
|
|
152
|
+
const promptOffsetAt = (x, y) => {
|
|
153
|
+
const r = promptRect();
|
|
154
|
+
const ctl = promptMouseSelectionRef.current;
|
|
155
|
+
if (!r || !ctl) return null;
|
|
156
|
+
const top = Math.max(0, Number(r.top) || 0);
|
|
157
|
+
const left = Math.max(0, Number(r.left) || 0);
|
|
158
|
+
const height = Math.max(1, Number(r.height) || 1);
|
|
159
|
+
const width = Math.max(1, Number(r.contentWidth) || 1);
|
|
160
|
+
// Clamp the mapped row/col to the box's own bounds so a drag that runs
|
|
161
|
+
// outside the prompt (above/below/left/right, e.g. onto the transcript
|
|
162
|
+
// or off-screen) still tracks the nearest edge cell instead of jumping
|
|
163
|
+
// to whatever offset a raw negative/overflowing row would resolve to.
|
|
164
|
+
const row = Math.max(0, Math.min(height - 1, y - top));
|
|
165
|
+
const col = Math.max(0, Math.min(width, x - left));
|
|
166
|
+
return ctl.offsetAtCell(row, col);
|
|
167
|
+
};
|
|
168
|
+
// Clear whichever selection is active (ink-grid rect AND/OR prompt engine).
|
|
169
|
+
const clearAllSelections = () => {
|
|
170
|
+
promptMouseSelectionRef.current?.clear?.();
|
|
171
|
+
applySelectionRect(null);
|
|
172
|
+
};
|
|
173
|
+
const onData = (data) => {
|
|
174
|
+
// ink emits each parsed input event as a string; a mouse SGR sequence
|
|
175
|
+
// arrives whole. Guard so non-mouse keystrokes fall through untouched.
|
|
176
|
+
const s = typeof data === 'string' ? data : String(data ?? '');
|
|
177
|
+
if (s.indexOf('\x1b[<') === -1) return;
|
|
178
|
+
let up = 0;
|
|
179
|
+
let down = 0;
|
|
180
|
+
let m;
|
|
181
|
+
MOUSE.lastIndex = 0;
|
|
182
|
+
while ((m = MOUSE.exec(s)) !== null) {
|
|
183
|
+
const button = Number(m[1]);
|
|
184
|
+
const x = Number(m[2]) - 1; // SGR is 1-based; grid is 0-based
|
|
185
|
+
const y = Number(m[3]) - 1;
|
|
186
|
+
const press = m[4] === 'M';
|
|
187
|
+
const wheelButton = button & ~MOUSE_MODIFIER_MASK;
|
|
188
|
+
if (wheelButton === 64 || wheelButton === 65) {
|
|
189
|
+
if ((button & MOUSE_CTRL_MASK) !== 0) {
|
|
190
|
+
passthroughCtrlWheelZoom();
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
193
|
+
if (wheelButton === 64) up += 1;
|
|
194
|
+
else down += 1;
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
197
|
+
// Low 2 bits = button id; bit 5 (32) = motion-while-pressed flag.
|
|
198
|
+
const baseButton = button & 3;
|
|
199
|
+
const isMotion = (button & 32) !== 0;
|
|
200
|
+
// Shift bit must be read BEFORE baseButton drops every modifier bit
|
|
201
|
+
// (button & 3); MOUSE_MODIFIER_MASK above is scroll-routing-only and
|
|
202
|
+
// deliberately treats shift as noise there.
|
|
203
|
+
const shiftHeld = (button & MOUSE_SHIFT_MASK) !== 0;
|
|
204
|
+
if (baseButton === 0 && press && !isMotion) {
|
|
205
|
+
// Region router: a press decides which surface owns this selection.
|
|
206
|
+
// Prompt box takes priority (it overlaps no transcript rows), then the
|
|
207
|
+
// transcript viewport, then the bottom statusline band. A press
|
|
208
|
+
// anywhere else clears any prior selection (plain click).
|
|
209
|
+
if (isInPromptBox(x, y)) {
|
|
210
|
+
// Clear any ink-grid selection so only one highlight is ever visible.
|
|
211
|
+
applySelectionRect(null);
|
|
212
|
+
const offset = promptOffsetAt(x, y);
|
|
213
|
+
stopSmoothScroll();
|
|
214
|
+
dragRef.current = { anchor: { x, y }, anchorScroll: 0, last: { x, y }, active: true, rect: null, region: 'prompt', anchorSpan: null };
|
|
215
|
+
const ctl = promptMouseSelectionRef.current;
|
|
216
|
+
// Shift+click extends the EXISTING prompt selection (anchor stays
|
|
217
|
+
// put, cursor jumps to the click) instead of starting a fresh
|
|
218
|
+
// zero-width anchor at the click point.
|
|
219
|
+
if (shiftHeld && ctl?.hasSelection?.()) {
|
|
220
|
+
if (offset != null) ctl.extendTo?.(offset, true);
|
|
221
|
+
lastClickRef.current = { x: -1, y: -1, t: 0 };
|
|
222
|
+
continue;
|
|
223
|
+
}
|
|
224
|
+
// Multi-click word/line select (double = word, triple = line),
|
|
225
|
+
// same "qualifying press" window/drift tolerance used by the
|
|
226
|
+
// transcript/status path below. Reuses lastClickRef so a rapid
|
|
227
|
+
// double/triple click on the prompt box behaves the same as one on
|
|
228
|
+
// the transcript. A qualifying press advances the count; anything
|
|
229
|
+
// else (moved too far, too slow, or count already at 3) resets to
|
|
230
|
+
// a fresh single-click anchor.
|
|
231
|
+
const nowPrompt = Date.now();
|
|
232
|
+
const lcPrompt = lastClickRef.current;
|
|
233
|
+
const qualifiesPrompt = (nowPrompt - lcPrompt.t) < 500
|
|
234
|
+
&& Math.abs(lcPrompt.y - y) <= 1
|
|
235
|
+
&& Math.abs(lcPrompt.x - x) <= 2;
|
|
236
|
+
let promptClickCount = qualifiesPrompt ? (lcPrompt.count || 1) + 1 : 1;
|
|
237
|
+
if (promptClickCount > 3) promptClickCount = 1;
|
|
238
|
+
if ((promptClickCount === 2 || promptClickCount === 3) && offset != null) {
|
|
239
|
+
if (promptClickCount === 2) ctl?.selectWordAt?.(offset);
|
|
240
|
+
else ctl?.selectLineAt?.(offset);
|
|
241
|
+
// Click-select is already final (selectWordAt/selectLineAt set the
|
|
242
|
+
// full word/line range). Mark the drag inactive so a subsequent
|
|
243
|
+
// release does NOT fall into the generic prompt release handler
|
|
244
|
+
// below, which calls extendTo(releaseOffset) and would collapse
|
|
245
|
+
// this selection back down to anchor→releaseOffset (or empty on a
|
|
246
|
+
// no-motion release, since anchor was never re-anchored here).
|
|
247
|
+
dragRef.current.active = false;
|
|
248
|
+
lastClickRef.current = { x, y, t: nowPrompt, count: promptClickCount };
|
|
249
|
+
continue;
|
|
250
|
+
} else if (offset != null) {
|
|
251
|
+
ctl?.anchorAt?.(offset);
|
|
252
|
+
}
|
|
253
|
+
lastClickRef.current = { x, y, t: nowPrompt, count: 1 };
|
|
254
|
+
continue;
|
|
255
|
+
}
|
|
256
|
+
const inTranscript = isInTranscriptViewport(y);
|
|
257
|
+
const inStatus = !inTranscript && isInStatusBand(y);
|
|
258
|
+
if (!inTranscript && !inStatus) {
|
|
259
|
+
lastClickRef.current = { x: -1, y: -1, t: 0 };
|
|
260
|
+
dragRef.current.active = false;
|
|
261
|
+
dragRef.current.region = null;
|
|
262
|
+
dragRef.current.anchorSpan = null;
|
|
263
|
+
clearAllSelections();
|
|
264
|
+
continue;
|
|
265
|
+
}
|
|
266
|
+
const region = inTranscript ? 'transcript' : 'status';
|
|
267
|
+
// A press always clears the prompt-box selection (single active region).
|
|
268
|
+
promptMouseSelectionRef.current?.clear?.();
|
|
269
|
+
const now = Date.now();
|
|
270
|
+
// Shift+click extends the existing ink-grid selection in this SAME
|
|
271
|
+
// region from its original anchor to the new click point, instead of
|
|
272
|
+
// starting a fresh anchor here. Only applies to a plain char-drag
|
|
273
|
+
// selection (no anchorSpan) with a live non-empty rect; a word/line
|
|
274
|
+
// anchorSpan or an empty/absent selection falls through to a normal
|
|
275
|
+
// fresh press below.
|
|
276
|
+
if (
|
|
277
|
+
shiftHeld
|
|
278
|
+
&& dragRef.current.region === region
|
|
279
|
+
&& !dragRef.current.anchorSpan
|
|
280
|
+
&& dragRef.current.anchor
|
|
281
|
+
&& dragRef.current.rect
|
|
282
|
+
&& !(dragRef.current.rect.x1 === dragRef.current.rect.x2 && dragRef.current.rect.y1 === dragRef.current.rect.y2)
|
|
283
|
+
) {
|
|
284
|
+
const selectionY = region === 'status' ? clampToStatusBand(y) : clampToTranscriptViewport(y);
|
|
285
|
+
const anchor = region === 'status'
|
|
286
|
+
? dragRef.current.anchor
|
|
287
|
+
: selectionPointAtCurrentScroll(dragRef.current.anchor, dragRef.current.anchorScroll);
|
|
288
|
+
const rect = linearSelection(anchor, { x, y: selectionY });
|
|
289
|
+
stopSmoothScroll();
|
|
290
|
+
dragRef.current = {
|
|
291
|
+
...dragRef.current,
|
|
292
|
+
last: { x, y: selectionY },
|
|
293
|
+
active: true,
|
|
294
|
+
region,
|
|
295
|
+
};
|
|
296
|
+
applySelectionRect(rect);
|
|
297
|
+
lastClickRef.current = { x, y, t: now, count: 1 };
|
|
298
|
+
continue;
|
|
299
|
+
}
|
|
300
|
+
// Multi-click sequence: 2nd consecutive press = word (double-click),
|
|
301
|
+
// 3rd = whole line (triple-click). Each press must land near the prior
|
|
302
|
+
// one within 500ms — up to 2 columns and 1 row of drift (terminals
|
|
303
|
+
// often report a shifted cell on repeat clicks); tighter matching made
|
|
304
|
+
// word selection unreliable. A 4th qualifying press restarts the
|
|
305
|
+
// sequence at 1 (simplest: reset). Works for
|
|
306
|
+
// transcript AND status rows since getWordRectAt/getLineRectAt are
|
|
307
|
+
// grid-based. Copy still happens on Ctrl+C, never here.
|
|
308
|
+
const lc = lastClickRef.current;
|
|
309
|
+
const qualifies = (now - lc.t) < 500
|
|
310
|
+
&& Math.abs(lc.y - y) <= 1
|
|
311
|
+
&& Math.abs(lc.x - x) <= 2;
|
|
312
|
+
let clickCount = qualifies ? (lc.count || 1) + 1 : 1;
|
|
313
|
+
if (clickCount > 3) clickCount = 1;
|
|
314
|
+
if (clickCount === 2 || clickCount === 3) {
|
|
315
|
+
// Word (2) or line (3) select. Snap to the word/line under the cell
|
|
316
|
+
// and record the span on dragRef so a following drag extends by whole
|
|
317
|
+
// words/lines from this span (see buildSpanRect). Leave the drag
|
|
318
|
+
// ARMED (active:true): a release without motion keeps this highlight
|
|
319
|
+
// (buildSpanRect returns the span for an in-span target), while
|
|
320
|
+
// any motion extends it. Mirrors selectWordAt/selectLineAt setting
|
|
321
|
+
// isDragging=true + anchorSpan; the mouse-up finalizes.
|
|
322
|
+
const kind = clickCount === 2 ? 'word' : 'line';
|
|
323
|
+
const wr = kind === 'word' ? store.getWordRectAt?.(x, y) : store.getLineRectAt?.(y);
|
|
324
|
+
if (wr) {
|
|
325
|
+
const lo = { x: wr.x1, y: wr.y1 };
|
|
326
|
+
const hi = { x: wr.x2, y: wr.y2 };
|
|
327
|
+
const rect = linearSelection(lo, hi);
|
|
328
|
+
stopSmoothScroll();
|
|
329
|
+
dragRef.current = {
|
|
330
|
+
anchor: { x, y },
|
|
331
|
+
anchorScroll: region === 'transcript' ? scrollTargetRef.current : 0,
|
|
332
|
+
last: { x, y },
|
|
333
|
+
active: true,
|
|
334
|
+
rect: null,
|
|
335
|
+
region,
|
|
336
|
+
anchorSpan: { lo, hi, kind },
|
|
337
|
+
};
|
|
338
|
+
applySelectionRect(rect);
|
|
339
|
+
lastClickRef.current = { x, y, t: now, count: clickCount };
|
|
340
|
+
continue;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
lastClickRef.current = { x, y, t: now, count: 1 };
|
|
344
|
+
// Left-button press: begin a new selection anchored here.
|
|
345
|
+
// Anchor the drag but do NOT paint a zero-width selection yet; a plain
|
|
346
|
+
// single click should not flash a one-cell highlight. The selection is
|
|
347
|
+
// only rendered once a drag actually extends past the anchor.
|
|
348
|
+
// Status-band selections do NOT scroll, so anchorScroll is irrelevant
|
|
349
|
+
// there; keep the transcript scroll anchor only for the transcript.
|
|
350
|
+
// Plain single press clears any word/line anchorSpan (char-drag mode).
|
|
351
|
+
stopSmoothScroll();
|
|
352
|
+
dragRef.current = {
|
|
353
|
+
anchor: { x, y },
|
|
354
|
+
anchorScroll: region === 'transcript' ? scrollTargetRef.current : 0,
|
|
355
|
+
last: { x, y },
|
|
356
|
+
active: true,
|
|
357
|
+
rect: null,
|
|
358
|
+
region,
|
|
359
|
+
anchorSpan: null,
|
|
360
|
+
};
|
|
361
|
+
} else if (baseButton === 0 && isMotion && dragRef.current.active) {
|
|
362
|
+
const region = dragRef.current.region;
|
|
363
|
+
if (region === 'prompt') {
|
|
364
|
+
// Prompt drag: extend the PromptInput selection to the mapped offset.
|
|
365
|
+
// The cell is clamped to the box rows so a drag outside still tracks
|
|
366
|
+
// the nearest edge of the editable content.
|
|
367
|
+
const offset = promptOffsetAt(x, y);
|
|
368
|
+
dragRef.current.last = { x, y };
|
|
369
|
+
if (offset != null) promptMouseSelectionRef.current?.extendTo?.(offset);
|
|
370
|
+
continue;
|
|
371
|
+
}
|
|
372
|
+
// Drag motion (transcript or status): extend the selection to the
|
|
373
|
+
// current cell, clamped to the owning region's band.
|
|
374
|
+
const selectionY = region === 'status' ? clampToStatusBand(y) : clampToTranscriptViewport(y);
|
|
375
|
+
dragRef.current.last = { x, y: selectionY };
|
|
376
|
+
const span = dragRef.current.anchorSpan;
|
|
377
|
+
if (span) {
|
|
378
|
+
// Word/line multi-click drag: extend by whole words/lines from the
|
|
379
|
+
// anchor span to the word/line under the cursor (see buildSpanRect).
|
|
380
|
+
const rect = buildSpanRect(span, x, selectionY, region, dragRef.current.anchorScroll);
|
|
381
|
+
applySelectionRectThrottled(rect);
|
|
382
|
+
} else {
|
|
383
|
+
const anchor = region === 'status'
|
|
384
|
+
? dragRef.current.anchor
|
|
385
|
+
: selectionPointAtCurrentScroll(dragRef.current.anchor, dragRef.current.anchorScroll);
|
|
386
|
+
const rect = linearSelection(anchor, { x, y: selectionY });
|
|
387
|
+
applySelectionRectThrottled(rect);
|
|
388
|
+
}
|
|
389
|
+
// Auto-scroll-while-dragging is transcript-only (the status band does
|
|
390
|
+
// not scroll).
|
|
391
|
+
if (region === 'transcript') {
|
|
392
|
+
const frameRows = Math.max(1, Number(rows) || 24);
|
|
393
|
+
if (y <= 1) {
|
|
394
|
+
queueScrollCoalesced(3);
|
|
395
|
+
} else if (y >= frameRows - 5) {
|
|
396
|
+
queueScrollCoalesced(-3);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
} else if (!press && dragRef.current.active) {
|
|
400
|
+
const region = dragRef.current.region;
|
|
401
|
+
if (region === 'prompt') {
|
|
402
|
+
// Finalize the prompt selection; highlight persists (copy on Ctrl+C).
|
|
403
|
+
const offset = promptOffsetAt(x, y);
|
|
404
|
+
dragRef.current.active = false;
|
|
405
|
+
promptMouseSelectionRef.current?.extendTo?.(offset, true);
|
|
406
|
+
continue;
|
|
407
|
+
}
|
|
408
|
+
// Button release while dragging: finalize with the release coordinate
|
|
409
|
+
// (the SGR release event carries col/row) and keep the selection
|
|
410
|
+
// visible. Copy is NOT automatic — the user presses Ctrl+C to copy.
|
|
411
|
+
// The highlight stays until ESC or a plain click.
|
|
412
|
+
const span = dragRef.current.anchorSpan;
|
|
413
|
+
const releaseY = region === 'status' ? clampToStatusBand(y) : clampToTranscriptViewport(y);
|
|
414
|
+
dragRef.current.active = false;
|
|
415
|
+
if (span) {
|
|
416
|
+
// Word/line multi-click release: finalize from the span to the
|
|
417
|
+
// word/line at the release cell. A release-without-motion resolves
|
|
418
|
+
// to the span itself (in-span target), so the original word/line
|
|
419
|
+
// highlight stays — never cleared as "empty" like a bare click.
|
|
420
|
+
const rect = buildSpanRect(span, x, releaseY, region, dragRef.current.anchorScroll);
|
|
421
|
+
applySelectionRect(rect);
|
|
422
|
+
} else {
|
|
423
|
+
const anchor = region === 'status'
|
|
424
|
+
? dragRef.current.anchor
|
|
425
|
+
: selectionPointAtCurrentScroll(dragRef.current.anchor, dragRef.current.anchorScroll);
|
|
426
|
+
const rect = linearSelection(anchor, { x, y: releaseY });
|
|
427
|
+
const empty = rect.x1 === rect.x2 && rect.y1 === rect.y2;
|
|
428
|
+
if (empty) {
|
|
429
|
+
applySelectionRect(null); // a plain click clears any prior highlight
|
|
430
|
+
} else {
|
|
431
|
+
// Push the final rect so ink re-renders the visible selection.
|
|
432
|
+
applySelectionRect(rect);
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
// Drag is over: the measured-height harvest was skipped for every
|
|
436
|
+
// motion commit (see the harvest effect's dragRef guard), so force one
|
|
437
|
+
// re-measure now to reconcile any row whose true height drifted from
|
|
438
|
+
// its estimate while the drag was in flight.
|
|
439
|
+
if (TRANSCRIPT_MEASURED_ROWS) setMeasuredRowsVersion((v) => (v + 1) % 1000000);
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
if (up !== 0 || down !== 0) {
|
|
443
|
+
if (dragRef.current.active) return;
|
|
444
|
+
const palette = slashPaletteRef.current;
|
|
445
|
+
if (palette.open && palette.count > 0) {
|
|
446
|
+
const step = down - up;
|
|
447
|
+
if (step !== 0) {
|
|
448
|
+
setSlashIndex((index) => Math.max(0, Math.min(palette.count - 1, index + step)));
|
|
449
|
+
}
|
|
450
|
+
return;
|
|
451
|
+
}
|
|
452
|
+
if (overlayBlocksGlobalTranscriptScroll(scrollFocusRef.current)) return;
|
|
453
|
+
const STEP = 3; // rows per wheel notch; immediate updates feel steadier in Windows Terminal
|
|
454
|
+
queueScrollCoalesced((up - down) * STEP);
|
|
455
|
+
}
|
|
456
|
+
};
|
|
457
|
+
inkInput.on('input', onData);
|
|
458
|
+
return () => { inkInput.off('input', onData); };
|
|
459
|
+
}, [inkInput, isRawModeSupported, store, passthroughCtrlWheelZoom, rows, scrollTranscriptRows, queueScrollCoalesced, applySelectionRect, applySelectionRectThrottled, selectionPointAtCurrentScroll, buildSpanRect]);
|
|
460
|
+
}
|