@north-light/crouter 0.3.170 → 0.3.172
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/dist/api/client.d.ts +37 -11
- package/dist/api/client.js +84 -15
- package/dist/api/dto/human.d.ts +53 -39
- package/dist/api/dto/human.js +2 -3
- package/dist/api/dto/inbox.d.ts +0 -1
- package/dist/api/dto/nodes.d.ts +12 -0
- package/dist/api/dto/review-comments.d.ts +137 -0
- package/dist/api/dto/review-comments.js +5 -0
- package/dist/api/dto/reviews.d.ts +109 -0
- package/dist/api/dto/reviews.js +5 -0
- package/dist/api/index.d.ts +2 -0
- package/dist/api/index.js +2 -0
- package/dist/api/routes.d.ts +17 -4
- package/dist/api/routes.js +21 -5
- package/dist/build-root.d.ts +1 -16
- package/dist/build-root.js +1 -25
- package/dist/builtin-memory/00-runtime-base.md +7 -5
- package/dist/builtin-memory/04-base-worker.md +1 -1
- package/dist/builtin-memory/04-orchestration-kernel.md +7 -7
- package/dist/builtin-memory/05-kinds/design/00-base.md +1 -1
- package/dist/builtin-memory/05-kinds/design/01-orchestrator.md +1 -1
- package/dist/builtin-memory/05-kinds/developer/00-base.md +1 -1
- package/dist/builtin-memory/05-kinds/explore/00-base.md +1 -1
- package/dist/builtin-memory/05-kinds/plan/00-base.md +1 -1
- package/dist/builtin-memory/05-kinds/plan/01-orchestrator.md +1 -1
- package/dist/builtin-memory/05-kinds/review/00-base.md +1 -1
- package/dist/builtin-memory/05-kinds/review/companion/00-base.md +20 -0
- package/dist/builtin-memory/05-kinds/spec/00-base.md +3 -7
- package/dist/builtin-memory/05-kinds/spec/01-orchestrator.md +3 -3
- package/dist/builtin-memory/05-kinds/spec/requirements.md +3 -3
- package/dist/builtin-memory/design.md +2 -2
- package/dist/builtin-memory/insights/capture.md +50 -0
- package/dist/builtin-memory/insights/init.md +45 -0
- package/dist/builtin-memory/insights/listen.md +9 -0
- package/dist/builtin-memory/internal/agent-shaping.md +2 -2
- package/dist/builtin-memory/internal/nodes-and-canvas.md +1 -1
- package/dist/builtin-memory/internal/plugins.md +12 -11
- package/dist/builtin-memory/internal/storage-tiers.md +2 -2
- package/dist/builtin-memory/{planning.md → plan/roadmap.md} +3 -3
- package/dist/builtin-memory/plan.md +19 -0
- package/dist/builtin-memory/spec/guide.md +38 -0
- package/dist/builtin-memory/spec/requirements.md +31 -0
- package/dist/builtin-memory/spec/roadmap.md +35 -0
- package/dist/builtin-memory/spec.md +10 -86
- package/dist/builtin-pi-packages/pi-crtr-extensions/README.md +3 -4
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/__tests__/provider-rotation.test.ts +49 -5
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.d.ts +56 -0
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.js +920 -0
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.ts +221 -156
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/statusline.ts +4 -5
- package/dist/cli.js +2 -6
- package/dist/clients/attach/__tests__/attach-chrome-remote.test.js +7 -35
- package/dist/clients/attach/__tests__/attach-keybindings.test.js +32 -16
- package/dist/clients/attach/__tests__/context-message.test.js +142 -6
- package/dist/clients/attach/__tests__/crtr-output.test.js +29 -45
- package/dist/clients/attach/__tests__/diagram.test.js +1 -1
- package/dist/clients/attach/__tests__/file-review-focus.test.js +48 -0
- package/dist/clients/attach/__tests__/group-activity.test.js +266 -0
- package/dist/clients/attach/__tests__/input-controller-extension-command.test.js +32 -0
- package/dist/clients/attach/__tests__/session-identity-refresh.test.js +32 -0
- package/dist/clients/attach/__tests__/titled-editor-preview.test.js +1 -2
- package/dist/clients/attach/chrome/header.d.ts +10 -0
- package/dist/clients/attach/chrome/header.js +30 -0
- package/dist/clients/attach/chrome/recap.d.ts +7 -0
- package/dist/clients/attach/chrome/recap.js +19 -0
- package/dist/clients/attach/chrome/status-line.d.ts +3 -0
- package/dist/clients/attach/chrome/status-line.js +5 -0
- package/dist/clients/attach/chrome/widgets.js +11 -21
- package/dist/clients/attach/command.js +11 -7
- package/dist/clients/attach/config.d.ts +4 -0
- package/dist/clients/attach/config.js +3 -0
- package/dist/clients/attach/input/controller.d.ts +19 -0
- package/dist/clients/attach/input/controller.js +62 -28
- package/dist/clients/attach/input/titled-editor.d.ts +0 -4
- package/dist/clients/attach/input/titled-editor.js +2 -10
- package/dist/clients/attach/overlays/file-review.d.ts +9 -5
- package/dist/clients/attach/overlays/file-review.js +9 -70
- package/dist/clients/attach/overlays/help.d.ts +33 -0
- package/dist/clients/attach/overlays/help.js +204 -0
- package/dist/clients/attach/render/activity-slot.d.ts +4 -0
- package/dist/clients/attach/render/activity-slot.js +13 -0
- package/dist/clients/attach/render/chat-view.d.ts +117 -12
- package/dist/clients/attach/render/chat-view.js +604 -135
- package/dist/clients/attach/render/context-message.d.ts +2 -1
- package/dist/clients/attach/render/context-message.js +17 -13
- package/dist/clients/attach/render/crtr-output.js +1 -3
- package/dist/clients/attach/render/diagram.js +2 -2
- package/dist/clients/attach/render/edit-diff.d.ts +4 -1
- package/dist/clients/attach/render/edit-diff.js +7 -11
- package/dist/clients/attach/render/group-activity.d.ts +32 -0
- package/dist/clients/attach/render/group-activity.js +206 -0
- package/dist/clients/attach/render/group-recap.d.ts +10 -0
- package/dist/clients/attach/render/group-recap.js +93 -0
- package/dist/clients/attach/render/html-markdown.d.ts +17 -0
- package/dist/clients/attach/render/html-markdown.js +228 -0
- package/dist/clients/attach/render/measured-container.d.ts +96 -0
- package/dist/clients/attach/render/measured-container.js +322 -0
- package/dist/clients/attach/render/scroll-animation.d.ts +8 -0
- package/dist/clients/attach/render/scroll-animation.js +80 -0
- package/dist/clients/attach/render/tool-calls.js +6 -6
- package/dist/clients/attach/render/transcript-copy.d.ts +9 -0
- package/dist/clients/attach/render/transcript-copy.js +81 -0
- package/dist/clients/attach/render/viewport.d.ts +29 -0
- package/dist/clients/attach/render/viewport.js +159 -0
- package/dist/clients/attach/session/bindings.d.ts +10 -7
- package/dist/clients/attach/session/bindings.js +23 -8
- package/dist/clients/attach/session/connection.js +4 -5
- package/dist/clients/attach/session/context.d.ts +6 -4
- package/dist/clients/attach/session/frame.d.ts +34 -0
- package/dist/clients/attach/session/frame.js +176 -0
- package/dist/clients/attach/session/frames.d.ts +4 -1
- package/dist/clients/attach/session/frames.js +14 -1
- package/dist/clients/attach/session/identity.js +4 -3
- package/dist/clients/attach/session/input-wiring.d.ts +14 -4
- package/dist/clients/attach/session/input-wiring.js +15 -13
- package/dist/clients/attach/session/invariant.d.ts +9 -0
- package/dist/clients/attach/session/invariant.js +15 -0
- package/dist/clients/attach/session/keys.d.ts +10 -2
- package/dist/clients/attach/session/keys.js +13 -5
- package/dist/clients/attach/session/layout.d.ts +10 -8
- package/dist/clients/attach/session/layout.js +33 -34
- package/dist/clients/attach/session/mouse.d.ts +18 -0
- package/dist/clients/attach/session/mouse.js +64 -0
- package/dist/clients/attach/session/pane-tag.d.ts +3 -0
- package/dist/clients/attach/session/pane-tag.js +51 -8
- package/dist/clients/attach/session/reconnect.d.ts +3 -0
- package/dist/clients/attach/session/reconnect.js +42 -5
- package/dist/clients/attach/session/surface-frame.d.ts +20 -0
- package/dist/clients/attach/session/surface-frame.js +73 -0
- package/dist/clients/attach/session/surface-host.d.ts +64 -0
- package/dist/clients/attach/session/surface-host.js +255 -0
- package/dist/clients/attach/session/whip.d.ts +17 -0
- package/dist/clients/attach/session/whip.js +334 -0
- package/dist/clients/attach/slash/dispatch.d.ts +13 -1
- package/dist/clients/attach/slash/dispatch.js +51 -29
- package/dist/clients/attach/viewer.js +907 -819
- package/dist/clients/conversation/conversation.d.ts +65 -0
- package/dist/clients/conversation/conversation.js +318 -0
- package/dist/clients/conversation/frames.d.ts +30 -0
- package/dist/clients/conversation/frames.js +52 -0
- package/dist/clients/conversation/region.d.ts +51 -0
- package/dist/clients/conversation/region.js +265 -0
- package/dist/clients/conversation/submit.d.ts +38 -0
- package/dist/clients/conversation/submit.js +51 -0
- package/dist/clients/inbox/__tests__/inbox-controller.test.js +247 -0
- package/dist/clients/inbox/__tests__/mount-panel.test.js +776 -0
- package/dist/clients/inbox/controller.d.ts +79 -0
- package/dist/clients/inbox/controller.js +448 -0
- package/dist/clients/inbox/deck-adapter.d.ts +29 -0
- package/dist/clients/inbox/deck-adapter.js +62 -0
- package/dist/clients/inbox/layout.d.ts +8 -0
- package/dist/clients/inbox/layout.js +9 -0
- package/dist/clients/inbox/resolve.d.ts +10 -0
- package/dist/clients/inbox/resolve.js +34 -0
- package/dist/clients/inbox/review/__tests__/editor-roundtrip.test.js +29 -0
- package/dist/clients/inbox/review/__tests__/remap.test.js +196 -0
- package/dist/clients/inbox/review/__tests__/review-core.test.js +341 -0
- package/dist/clients/inbox/review/anchors.d.ts +41 -0
- package/dist/clients/inbox/review/anchors.js +89 -0
- package/dist/clients/inbox/review/columns.d.ts +22 -0
- package/dist/clients/inbox/review/columns.js +40 -0
- package/dist/clients/inbox/review/comments-client.d.ts +66 -0
- package/dist/clients/inbox/review/comments-client.js +98 -0
- package/dist/clients/inbox/review/composition.d.ts +60 -0
- package/dist/clients/inbox/review/composition.js +278 -0
- package/dist/clients/inbox/review/document-region.d.ts +120 -0
- package/dist/clients/inbox/review/document-region.js +902 -0
- package/dist/clients/inbox/review/frame.d.ts +45 -0
- package/dist/clients/inbox/review/frame.js +215 -0
- package/dist/clients/inbox/review/keys.d.ts +67 -0
- package/dist/clients/inbox/review/keys.js +130 -0
- package/dist/clients/inbox/review/launch.d.ts +10 -0
- package/dist/clients/inbox/review/launch.js +129 -0
- package/dist/clients/inbox/review/live-source.d.ts +24 -0
- package/dist/clients/inbox/review/live-source.js +160 -0
- package/dist/clients/inbox/review/nvim.d.ts +13 -0
- package/dist/clients/inbox/review/nvim.js +42 -0
- package/dist/clients/inbox/review/remap.d.ts +51 -0
- package/dist/clients/inbox/review/remap.js +157 -0
- package/dist/clients/inbox/review/review-client.d.ts +30 -0
- package/dist/clients/inbox/review/review-client.js +107 -0
- package/dist/clients/inbox/review/roundtrip.d.ts +5 -0
- package/dist/clients/inbox/review/roundtrip.js +36 -0
- package/dist/clients/inbox/review/state.d.ts +143 -0
- package/dist/clients/inbox/review/state.js +282 -0
- package/dist/clients/inbox/review-adapter.d.ts +29 -0
- package/dist/clients/inbox/review-adapter.js +92 -0
- package/dist/clients/inbox/review-visibility.d.ts +5 -0
- package/dist/clients/inbox/review-visibility.js +19 -0
- package/dist/clients/inbox/surface.d.ts +19 -0
- package/dist/clients/inbox/surface.js +128 -0
- package/dist/clients/inbox/tui/ansi.d.ts +66 -0
- package/dist/clients/inbox/tui/ansi.js +317 -0
- package/dist/clients/inbox/tui/clipboard.d.ts +2 -0
- package/dist/clients/inbox/tui/clipboard.js +26 -0
- package/dist/clients/inbox/tui/input.d.ts +7 -0
- package/dist/clients/inbox/tui/input.js +653 -0
- package/dist/clients/inbox/tui/keys.d.ts +28 -0
- package/dist/clients/inbox/tui/keys.js +111 -0
- package/dist/clients/inbox/tui/panel.d.ts +2 -0
- package/dist/clients/inbox/tui/panel.js +217 -0
- package/dist/clients/inbox/tui/render.d.ts +37 -0
- package/dist/clients/inbox/tui/render.js +476 -0
- package/dist/clients/inbox/tui/types.d.ts +57 -0
- package/dist/clients/inbox/tui/types.js +1 -0
- package/dist/clients/inbox/tui.d.ts +6 -0
- package/dist/clients/inbox/tui.js +74 -0
- package/dist/clients/surfaces/host.d.ts +66 -0
- package/dist/clients/surfaces/host.js +1 -0
- package/dist/clients/surfaces/region.d.ts +16 -0
- package/dist/clients/surfaces/region.js +1 -0
- package/dist/clients/web/__tests__/push-engine.test.js +14 -158
- package/dist/clients/web/dev-server.d.ts +3 -3
- package/dist/clients/web/dev-server.js +9 -23
- package/dist/clients/web/events.js +4 -6
- package/dist/clients/web/push-engine.d.ts +2 -66
- package/dist/clients/web/push-engine.js +30 -161
- package/dist/clients/web/push-registry.d.ts +0 -4
- package/dist/clients/web/push-registry.js +2 -7
- package/dist/clients/web/server.js +3 -35
- package/dist/clients/web/web-client/shared/protocol.d.ts +0 -108
- package/dist/clients/web/web-cmd.js +2 -2
- package/dist/commands/__tests__/human.test.js +27 -15
- package/dist/commands/api-client.js +3 -3
- package/dist/commands/canvas-browse.js +1 -1
- package/dist/commands/canvas-issue.js +1 -1
- package/dist/commands/canvas-use.js +1 -1
- package/dist/commands/human/doc.d.ts +2 -0
- package/dist/commands/human/doc.js +98 -0
- package/dist/commands/human/inbox.d.ts +2 -0
- package/dist/commands/human/inbox.js +101 -0
- package/dist/commands/human/prompts.d.ts +2 -3
- package/dist/commands/human/prompts.js +27 -180
- package/dist/commands/human/queue.d.ts +1 -5
- package/dist/commands/human/queue.js +54 -216
- package/dist/commands/human/review.d.ts +2 -0
- package/dist/commands/human/review.js +298 -0
- package/dist/commands/human/shared.d.ts +2 -69
- package/dist/commands/human/shared.js +1 -181
- package/dist/commands/human.js +15 -20
- package/dist/commands/memory/__tests__/lint.test.d.ts +1 -0
- package/dist/commands/memory/__tests__/lint.test.js +15 -0
- package/dist/commands/memory/delete.js +2 -2
- package/dist/commands/memory/lint.d.ts +1 -0
- package/dist/commands/memory/lint.js +21 -3
- package/dist/commands/memory/read.js +4 -4
- package/dist/commands/node.js +8 -7
- package/dist/commands/pkg/plugin-manage.js +143 -68
- package/dist/commands/surface-inspect.js +2 -2
- package/dist/commands/surface.js +6 -12
- package/dist/commands/sys/__tests__/setup-core.test.js +9 -5
- package/dist/commands/sys/__tests__/setup-front-door.test.js +7 -42
- package/dist/commands/sys/config.js +29 -3
- package/dist/commands/sys/feedback.js +1 -1
- package/dist/commands/sys/setup-core.d.ts +12 -11
- package/dist/commands/sys/setup-core.js +55 -46
- package/dist/commands/sys/setup-wizard.d.ts +4 -1
- package/dist/commands/sys/setup-wizard.js +50 -9
- package/dist/commands/sys/sync-deps.js +1 -1
- package/dist/commands/sys.js +4 -6
- package/dist/core/__tests__/broker-sdk-wiring.test.js +6 -1
- package/dist/core/__tests__/broker-snapshot-history.test.js +9 -1
- package/dist/core/__tests__/dead-node-policy-table.test.js +3 -4
- package/dist/core/__tests__/fixtures/fake-engine.d.ts +8 -5
- package/dist/core/__tests__/fixtures/fake-engine.js +28 -2
- package/dist/core/__tests__/full/broker-provider-retry.test.js +87 -2
- package/dist/core/__tests__/full/broker-whip-interrupt.test.d.ts +1 -0
- package/dist/core/__tests__/full/broker-whip-interrupt.test.js +39 -0
- package/dist/core/__tests__/full/detach-focus.test.js +1 -1
- package/dist/core/__tests__/human-deliver-e2e.test.js +64 -64
- package/dist/core/__tests__/human-deliver.test.js +71 -345
- package/dist/core/__tests__/inline-memory-refs.test.js +2 -12
- package/dist/core/__tests__/memory-resolver-precedence.test.js +12 -15
- package/dist/core/__tests__/migration.test.js +10 -0
- package/dist/core/__tests__/model-pin-durability.test.js +14 -0
- package/dist/core/__tests__/phase4-review-migration.test.d.ts +1 -0
- package/dist/core/__tests__/phase4-review-migration.test.js +100 -0
- package/dist/core/__tests__/phase4-review-store.test.d.ts +1 -0
- package/dist/core/__tests__/phase4-review-store.test.js +143 -0
- package/dist/core/__tests__/remote-canvas-target.test.js +2 -2
- package/dist/core/__tests__/revive.test.js +22 -53
- package/dist/core/__tests__/session-cycles.test.js +48 -18
- package/dist/core/__tests__/spawn-no-kickoff.test.js +8 -12
- package/dist/core/__tests__/stop-guard.test.js +16 -1
- package/dist/core/__tests__/tmux-surface.test.js +21 -25
- package/dist/core/asset-root.d.ts +7 -0
- package/dist/core/asset-root.js +18 -0
- package/dist/core/broker-client/client.d.ts +0 -7
- package/dist/core/broker-client/client.js +4 -10
- package/dist/core/broker-client/transport-relay.d.ts +3 -4
- package/dist/core/broker-client/transport-relay.js +3 -4
- package/dist/core/canvas/__tests__/attention.test.js +1 -1
- package/dist/core/canvas/__tests__/remote-canvas-source.test.js +1 -1
- package/dist/core/canvas/__tests__/remote-transport.test.d.ts +1 -0
- package/dist/core/{view/__tests__/transport-remote.test.js → canvas/__tests__/remote-transport.test.js} +2 -2
- package/dist/core/canvas/__tests__/render-remote.test.js +1 -1
- package/dist/core/canvas/attention.js +4 -4
- package/dist/core/canvas/canvas.d.ts +6 -11
- package/dist/core/canvas/canvas.js +11 -33
- package/dist/core/canvas/db.js +207 -38
- package/dist/core/canvas/extensions.d.ts +1 -1
- package/dist/core/canvas/extensions.js +8 -1
- package/dist/core/canvas/index.d.ts +0 -1
- package/dist/core/canvas/index.js +0 -1
- package/dist/core/canvas/nav-model.d.ts +1 -1
- package/dist/core/canvas/nav-model.js +1 -1
- package/dist/core/canvas/paths.d.ts +5 -0
- package/dist/core/canvas/paths.js +16 -0
- package/dist/core/canvas/remote-canvas-source.js +2 -2
- package/dist/core/canvas/remote-transport.d.ts +7 -0
- package/dist/core/{view/transport-remote.js → canvas/remote-transport.js} +3 -4
- package/dist/core/canvas/source.js +1 -1
- package/dist/core/canvas/status-glyph.js +4 -12
- package/dist/core/canvas/types.d.ts +10 -35
- package/dist/core/clipboard-text.d.ts +8 -0
- package/dist/core/clipboard-text.js +18 -0
- package/dist/core/command-manifests/registry.d.ts +1 -3
- package/dist/core/command-plugins/bundle.d.ts +29 -0
- package/dist/core/command-plugins/bundle.js +198 -0
- package/dist/core/command-plugins/discovery.d.ts +0 -8
- package/dist/core/command-plugins/discovery.js +2 -39
- package/dist/core/command-plugins/transport/http-fetch.d.ts +4 -30
- package/dist/core/command-plugins/transport/http-fetch.js +17 -95
- package/dist/core/command.d.ts +1 -13
- package/dist/core/command.js +1 -16
- package/dist/core/config.d.ts +4 -0
- package/dist/core/config.js +28 -3
- package/dist/core/document-lines.d.ts +4 -0
- package/dist/core/document-lines.js +6 -0
- package/dist/core/feed/feed.d.ts +2 -0
- package/dist/core/feed/feed.js +11 -7
- package/dist/core/human/__tests__/inbox-cancel-worker.d.ts +1 -0
- package/dist/core/human/__tests__/inbox-cancel-worker.js +7 -0
- package/dist/core/human/__tests__/inbox-claim-worker.d.ts +1 -0
- package/dist/core/human/__tests__/inbox-claim-worker.js +17 -0
- package/dist/core/human/__tests__/inbox-core.test.d.ts +1 -0
- package/dist/core/human/__tests__/inbox-core.test.js +144 -0
- package/dist/core/human/__tests__/inbox-stale-claim-worker.d.ts +1 -0
- package/dist/core/human/__tests__/inbox-stale-claim-worker.js +8 -0
- package/dist/core/human/__tests__/visible-paths.test.d.ts +1 -0
- package/dist/{clients/attach → core/human}/__tests__/visible-paths.test.js +9 -0
- package/dist/core/human/claim.d.ts +23 -0
- package/dist/core/human/claim.js +67 -0
- package/dist/core/human/convention.d.ts +30 -0
- package/dist/core/human/convention.js +202 -0
- package/dist/core/human/deck-factories.d.ts +8 -0
- package/dist/core/human/deck-factories.js +15 -0
- package/dist/core/human/deck-schema.d.ts +70 -0
- package/dist/core/human/deck-schema.js +92 -0
- package/dist/core/human/root.d.ts +7 -0
- package/dist/core/human/root.js +37 -0
- package/dist/core/human/scan.d.ts +3 -0
- package/dist/core/human/scan.js +76 -0
- package/dist/core/human/summary.d.ts +8 -0
- package/dist/core/human/summary.js +44 -0
- package/dist/core/human/tickets.d.ts +49 -0
- package/dist/core/human/tickets.js +190 -0
- package/dist/core/human/types.d.ts +136 -0
- package/dist/core/human/types.js +5 -0
- package/dist/{clients/attach → core/human}/visible-paths.d.ts +1 -1
- package/dist/{clients/attach → core/human}/visible-paths.js +3 -2
- package/dist/core/inspector/chrome.d.ts +6 -0
- package/dist/core/inspector/chrome.js +14 -0
- package/dist/core/inspector/contract.d.ts +105 -0
- package/dist/core/inspector/contract.js +1 -0
- package/dist/core/inspector/core.d.ts +4 -5
- package/dist/core/inspector/core.js +3 -5
- package/dist/core/inspector/host.d.ts +46 -0
- package/dist/core/{tui → inspector}/host.js +42 -208
- package/dist/core/inspector/text.d.ts +2 -2
- package/dist/core/inspector/transport.d.ts +6 -0
- package/dist/core/inspector/transport.js +36 -0
- package/dist/core/inspector/tui.d.ts +2 -2
- package/dist/core/inspector/tui.js +15 -15
- package/dist/core/keybindings/__tests__/inbox-affordance.test.js +11 -11
- package/dist/core/keybindings/__tests__/resolve.test.js +10 -19
- package/dist/core/keybindings/attach-control.d.ts +9 -3
- package/dist/core/keybindings/attach-control.js +3 -1
- package/dist/core/keybindings/catalog.d.ts +6 -5
- package/dist/core/keybindings/catalog.js +61 -133
- package/dist/core/keybindings/inbox.d.ts +5 -6
- package/dist/core/keybindings/inbox.js +9 -10
- package/dist/core/keybindings/index.d.ts +3 -3
- package/dist/core/keybindings/index.js +3 -3
- package/dist/core/keybindings/persistence.d.ts +5 -1
- package/dist/core/keybindings/persistence.js +24 -6
- package/dist/core/keybindings/resolve.d.ts +6 -0
- package/dist/core/keybindings/resolve.js +10 -3
- package/dist/core/memory/inline-ref-grammar.js +2 -3
- package/dist/core/memory-resolver.d.ts +27 -29
- package/dist/core/memory-resolver.js +45 -39
- package/dist/core/model-routes.d.ts +33 -0
- package/dist/core/model-routes.js +42 -3
- package/dist/core/preview-registry.js +17 -55
- package/dist/core/profiles/select.js +3 -3
- package/dist/core/{view/remote-canvas-target.d.ts → remote-canvas-target.d.ts} +2 -2
- package/dist/core/{view/remote-canvas-target.js → remote-canvas-target.js} +4 -4
- package/dist/core/review/__tests__/capture-origin.test.d.ts +1 -0
- package/dist/core/review/__tests__/capture-origin.test.js +139 -0
- package/dist/core/review/__tests__/comments.test.d.ts +1 -0
- package/dist/core/review/__tests__/comments.test.js +98 -0
- package/dist/core/review/__tests__/project.test.d.ts +1 -0
- package/dist/core/review/__tests__/project.test.js +71 -0
- package/dist/core/review/__tests__/remap.test.d.ts +1 -0
- package/dist/core/review/__tests__/remap.test.js +159 -0
- package/dist/core/review/__tests__/stage-identity.test.d.ts +1 -0
- package/dist/core/review/__tests__/stage-identity.test.js +140 -0
- package/dist/core/review/birth.d.ts +7 -0
- package/dist/core/review/birth.js +23 -0
- package/dist/core/review/comments.d.ts +69 -0
- package/dist/core/review/comments.js +295 -0
- package/dist/core/review/companion.d.ts +35 -0
- package/dist/core/review/companion.js +205 -0
- package/dist/core/review/document.d.ts +27 -0
- package/dist/core/review/document.js +138 -0
- package/dist/core/review/project.d.ts +13 -0
- package/dist/core/review/project.js +58 -0
- package/dist/core/review/realize.d.ts +19 -0
- package/dist/core/review/realize.js +217 -0
- package/dist/core/review/remap.d.ts +24 -0
- package/dist/core/review/remap.js +156 -0
- package/dist/core/review/signal.d.ts +5 -0
- package/dist/core/review/signal.js +31 -0
- package/dist/core/review/stage.d.ts +26 -0
- package/dist/core/review/stage.js +184 -0
- package/dist/core/review/store.d.ts +46 -0
- package/dist/core/review/store.js +163 -0
- package/dist/core/review/ticket-filter.d.ts +17 -0
- package/dist/core/review/ticket-filter.js +32 -0
- package/dist/core/review/types.d.ts +130 -0
- package/dist/core/review/types.js +23 -0
- package/dist/core/runtime/__tests__/session-visibility.test.d.ts +1 -0
- package/dist/core/runtime/__tests__/session-visibility.test.js +119 -0
- package/dist/core/runtime/bearings.d.ts +5 -17
- package/dist/core/runtime/bearings.js +88 -71
- package/dist/core/runtime/broker-protocol.d.ts +31 -8
- package/dist/core/runtime/broker.d.ts +15 -1
- package/dist/core/runtime/broker.js +573 -141
- package/dist/core/runtime/canvas-extensions.d.ts +4 -5
- package/dist/core/runtime/canvas-extensions.js +5 -5
- package/dist/core/runtime/front-door.js +4 -4
- package/dist/core/runtime/headless-pi.d.ts +24 -0
- package/dist/core/runtime/headless-pi.js +152 -0
- package/dist/core/runtime/kickoff.d.ts +1 -5
- package/dist/core/runtime/kickoff.js +1 -5
- package/dist/core/runtime/model-registry.d.ts +3 -0
- package/dist/core/runtime/model-registry.js +13 -0
- package/dist/core/runtime/naming-persist.d.ts +4 -0
- package/dist/core/runtime/naming-persist.js +27 -1
- package/dist/core/runtime/naming.d.ts +17 -12
- package/dist/core/runtime/naming.js +91 -140
- package/dist/core/runtime/node-read.js +19 -10
- package/dist/core/runtime/nodes.d.ts +9 -14
- package/dist/core/runtime/nodes.js +3 -53
- package/dist/core/runtime/package-health.d.ts +6 -21
- package/dist/core/runtime/package-health.js +12 -123
- package/dist/core/runtime/placement-tmux.d.ts +2 -1
- package/dist/core/runtime/placement-tmux.js +6 -4
- package/dist/core/runtime/placement.d.ts +1 -0
- package/dist/core/runtime/placement.js +4 -8
- package/dist/core/runtime/promote.js +3 -0
- package/dist/core/runtime/recap.d.ts +2 -7
- package/dist/core/runtime/recap.js +35 -69
- package/dist/core/runtime/revive.js +34 -77
- package/dist/core/runtime/session-cycles.d.ts +4 -0
- package/dist/core/runtime/session-cycles.js +14 -1
- package/dist/core/runtime/session-visibility.d.ts +37 -0
- package/dist/core/runtime/session-visibility.js +83 -0
- package/dist/core/runtime/spawn.js +10 -22
- package/dist/core/runtime/stop-guard.d.ts +1 -1
- package/dist/core/runtime/stop-guard.js +18 -13
- package/dist/core/runtime/surface-bg.js +1 -1
- package/dist/core/runtime/tmux-chrome.d.ts +1 -1
- package/dist/core/runtime/tmux-chrome.js +1 -1
- package/dist/core/runtime/tmux.d.ts +35 -7
- package/dist/core/runtime/tmux.js +439 -62
- package/dist/core/runtime/tool-group-summary.d.ts +19 -0
- package/dist/core/runtime/tool-group-summary.js +88 -0
- package/dist/core/scope.d.ts +2 -7
- package/dist/core/scope.js +9 -35
- package/dist/core/substrate/schema.d.ts +2 -2
- package/dist/core/termrender/code-doc.d.ts +12 -0
- package/dist/core/termrender/code-doc.js +92 -0
- package/dist/core/termrender/display.d.ts +12 -0
- package/dist/core/termrender/display.js +19 -0
- package/dist/core/termrender/termrender.d.ts +73 -0
- package/dist/core/termrender/termrender.js +795 -0
- package/dist/core/termrender/version.d.ts +1 -0
- package/dist/core/termrender/version.js +1 -0
- package/dist/core/tui/terminal.js +1 -2
- package/dist/core/user-settings.d.ts +71 -3
- package/dist/core/user-settings.js +83 -1
- package/dist/daemon/api/__tests__/inbox.test.js +44 -133
- package/dist/daemon/api/handlers/human.d.ts +0 -10
- package/dist/daemon/api/handlers/human.js +104 -702
- package/dist/daemon/api/handlers/inbox.js +113 -142
- package/dist/daemon/api/handlers/review-comments.d.ts +2 -0
- package/dist/daemon/api/handlers/review-comments.js +500 -0
- package/dist/daemon/api/handlers/reviews.d.ts +2 -0
- package/dist/daemon/api/handlers/reviews.js +287 -0
- package/dist/daemon/api/map.d.ts +22 -7
- package/dist/daemon/api/map.js +159 -11
- package/dist/daemon/api/server.js +4 -0
- package/dist/daemon/cron-run.js +6 -9
- package/dist/daemon/crtrd.js +17 -33
- package/dist/daemon/fleet.d.ts +1 -8
- package/dist/daemon/fleet.js +4 -30
- package/dist/daemon/human/finish.d.ts +14 -0
- package/dist/daemon/human/finish.js +115 -0
- package/dist/daemon/human/sweep.d.ts +6 -0
- package/dist/daemon/human/sweep.js +49 -0
- package/dist/daemon/messaging/node-message.d.ts +18 -0
- package/dist/daemon/messaging/node-message.js +70 -0
- package/dist/daemon/review/comment-notify.d.ts +26 -0
- package/dist/daemon/review/comment-notify.js +107 -0
- package/dist/daemon/review/deliver.d.ts +7 -0
- package/dist/daemon/review/deliver.js +56 -0
- package/dist/daemon/review/finish.d.ts +22 -0
- package/dist/daemon/review/finish.js +254 -0
- package/dist/daemon/review/sweep.d.ts +11 -0
- package/dist/daemon/review/sweep.js +120 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -2
- package/dist/pi-extensions/__tests__/canvas-context-intro.test.js +42 -64
- package/dist/pi-extensions/__tests__/canvas-recap.test.d.ts +1 -0
- package/dist/pi-extensions/__tests__/canvas-recap.test.js +159 -0
- package/dist/pi-extensions/__tests__/canvas-stophook-agentend.test.js +16 -38
- package/dist/pi-extensions/__tests__/canvas-structured-output.test.d.ts +1 -0
- package/dist/pi-extensions/__tests__/canvas-structured-output.test.js +63 -0
- package/dist/pi-extensions/canvas-context-intro.d.ts +8 -11
- package/dist/pi-extensions/canvas-context-intro.js +50 -104
- package/dist/pi-extensions/canvas-recap.d.ts +15 -2
- package/dist/pi-extensions/canvas-recap.js +192 -69
- package/dist/pi-extensions/canvas-review-boundary.d.ts +59 -0
- package/dist/pi-extensions/canvas-review-boundary.js +81 -0
- package/dist/pi-extensions/canvas-stophook.d.ts +3 -3
- package/dist/pi-extensions/canvas-stophook.js +78 -128
- package/dist/pi-extensions/canvas-structured-output.js +52 -8
- package/dist/shared/generated-context.d.ts +29 -4
- package/dist/shared/generated-context.js +122 -7
- package/dist/shared/tool-groups.d.ts +23 -0
- package/dist/shared/tool-groups.js +60 -0
- package/dist/shared/working-activity.d.ts +8 -0
- package/dist/shared/working-activity.js +26 -0
- package/dist/types.d.ts +36 -2
- package/dist/types.js +51 -0
- package/dist/web-client/assets/index-CCPaLeW7.css +2 -0
- package/dist/web-client/assets/index-kyOWsnZK.js +86 -0
- package/dist/web-client/index.html +2 -2
- package/dist/web-client/sw.js +1 -1
- package/docs/compat/hearth-crtr-v4.md +2 -2
- package/docs/compat/hearth-crtr-v5.md +4 -2
- package/docs/public-api.md +3 -3
- package/package.json +21 -14
- package/runtime.lock.json +4661 -1453
- package/scripts/postinstall.mjs +15 -2
- package/dist/builtin-pi-packages/pi-mode-switch/README.md +0 -36
- package/dist/builtin-pi-packages/pi-mode-switch/bin/mode +0 -36
- package/dist/builtin-pi-packages/pi-mode-switch/extensions/index.ts +0 -449
- package/dist/builtin-pi-packages/pi-mode-switch/package.json +0 -14
- package/dist/builtin-pi-packages/pi-mode-switch/tsconfig.json +0 -9
- package/dist/builtin-views/_lib/states.mjs +0 -161
- package/dist/builtin-views/canvas/core.mjs +0 -657
- package/dist/builtin-views/canvas/text.mjs +0 -58
- package/dist/builtin-views/canvas/tui.mjs +0 -168
- package/dist/builtin-views/canvas/web.jsx +0 -121
- package/dist/builtin-views/chat/core.mjs +0 -684
- package/dist/builtin-views/chat/text.mjs +0 -101
- package/dist/builtin-views/chat/tui.mjs +0 -360
- package/dist/builtin-views/chat/web.jsx +0 -362
- package/dist/builtin-views/git-pr/core.mjs +0 -673
- package/dist/builtin-views/git-pr/text.mjs +0 -84
- package/dist/builtin-views/git-pr/tui.mjs +0 -301
- package/dist/builtin-views/git-pr/web.jsx +0 -216
- package/dist/builtin-views/inbox/_lib/render.mjs +0 -175
- package/dist/builtin-views/inbox/core.mjs +0 -1273
- package/dist/builtin-views/inbox/text.mjs +0 -73
- package/dist/builtin-views/inbox/tui.mjs +0 -314
- package/dist/builtin-views/inbox/web.jsx +0 -188
- package/dist/builtin-views/linkedin/core.mjs +0 -906
- package/dist/builtin-views/linkedin/text.mjs +0 -69
- package/dist/builtin-views/linkedin/tui.mjs +0 -413
- package/dist/builtin-views/linkedin/web.jsx +0 -206
- package/dist/builtin-views/prompt-review/core.mjs +0 -735
- package/dist/builtin-views/prompt-review/text.mjs +0 -15
- package/dist/builtin-views/prompt-review/tui.mjs +0 -196
- package/dist/builtin-views/prompt-review/web.jsx +0 -484
- package/dist/builtin-views/workspace-sidebar/__tests__/core.test.js +0 -50
- package/dist/builtin-views/workspace-sidebar/__tests__/core.test.ts +0 -53
- package/dist/builtin-views/workspace-sidebar/core.mjs +0 -670
- package/dist/builtin-views/workspace-sidebar/text.mjs +0 -53
- package/dist/builtin-views/workspace-sidebar/tui.mjs +0 -141
- package/dist/builtin-views/workspace-sidebar/web.jsx +0 -109
- package/dist/clients/attach/session/mode.d.ts +0 -25
- package/dist/clients/attach/session/mode.js +0 -97
- package/dist/commands/sys/promptstudio.d.ts +0 -2
- package/dist/commands/sys/promptstudio.js +0 -65
- package/dist/commands/view-cycle.d.ts +0 -2
- package/dist/commands/view-cycle.js +0 -130
- package/dist/commands/view-list.d.ts +0 -2
- package/dist/commands/view-list.js +0 -66
- package/dist/commands/view-new.d.ts +0 -2
- package/dist/commands/view-new.js +0 -74
- package/dist/commands/view-pick.d.ts +0 -6
- package/dist/commands/view-pick.js +0 -129
- package/dist/commands/view-run.d.ts +0 -2
- package/dist/commands/view-run.js +0 -214
- package/dist/commands/view.d.ts +0 -2
- package/dist/commands/view.js +0 -31
- package/dist/commands/workspace.d.ts +0 -2
- package/dist/commands/workspace.js +0 -165
- package/dist/core/__tests__/chat-view-reconnect.test.js +0 -105
- package/dist/core/__tests__/full/consult-startup-failure.test.js +0 -124
- package/dist/core/canvas/__tests__/human-work-outbox.test.js +0 -123
- package/dist/core/canvas/human-work-outbox.d.ts +0 -73
- package/dist/core/canvas/human-work-outbox.js +0 -261
- package/dist/core/command-plugins/store.d.ts +0 -16
- package/dist/core/command-plugins/store.js +0 -64
- package/dist/core/keybindings/__tests__/bespoke-consumers.test.js +0 -40
- package/dist/core/runtime/fault-recovery-nudge.d.ts +0 -3
- package/dist/core/runtime/fault-recovery-nudge.js +0 -3
- package/dist/core/tui/__tests__/host-keybindings.test.js +0 -113
- package/dist/core/tui/host.d.ts +0 -66
- package/dist/core/view/__tests__/transport-cache.test.js +0 -62
- package/dist/core/view/bridge.d.ts +0 -10
- package/dist/core/view/bridge.js +0 -31
- package/dist/core/view/chrome.d.ts +0 -9
- package/dist/core/view/chrome.js +0 -22
- package/dist/core/view/contract.d.ts +0 -205
- package/dist/core/view/contract.js +0 -23
- package/dist/core/view/loader.d.ts +0 -31
- package/dist/core/view/loader.js +0 -188
- package/dist/core/view/stream-local.d.ts +0 -3
- package/dist/core/view/stream-local.js +0 -231
- package/dist/core/view/transport-cache.d.ts +0 -8
- package/dist/core/view/transport-cache.js +0 -38
- package/dist/core/view/transport-local.d.ts +0 -7
- package/dist/core/view/transport-local.js +0 -83
- package/dist/core/view/transport-remote.d.ts +0 -2
- package/dist/core/view/transport.d.ts +0 -10
- package/dist/core/view/transport.js +0 -15
- package/dist/pi-extensions/naming-tool.d.ts +0 -24
- package/dist/pi-extensions/naming-tool.js +0 -67
- package/dist/prompts/view.d.ts +0 -7
- package/dist/prompts/view.js +0 -172
- package/dist/web/ViewChrome.d.ts +0 -7
- package/dist/web/ViewChrome.js +0 -28
- package/dist/web/ViewPane.d.ts +0 -39
- package/dist/web/ViewPane.js +0 -48
- package/dist/web/index.d.ts +0 -8
- package/dist/web/index.js +0 -22
- package/dist/web/runtime.d.ts +0 -39
- package/dist/web/runtime.js +0 -232
- package/dist/web/states.d.ts +0 -24
- package/dist/web/states.js +0 -24
- package/dist/web/transport-http.d.ts +0 -5
- package/dist/web/transport-http.js +0 -34
- package/dist/web/transport-stream.d.ts +0 -3
- package/dist/web/transport-stream.js +0 -204
- package/dist/web-client/assets/index-U_NZ66VE.js +0 -79
- package/dist/web-client/assets/index-aaeu4adv.css +0 -2
- /package/dist/{builtin-views/workspace-sidebar/__tests__/core.test.d.ts → clients/attach/__tests__/file-review-focus.test.d.ts} +0 -0
- /package/dist/clients/attach/__tests__/{visible-paths.test.d.ts → group-activity.test.d.ts} +0 -0
- /package/dist/{core/__tests__/chat-view-reconnect.test.d.ts → clients/attach/__tests__/input-controller-extension-command.test.d.ts} +0 -0
- /package/dist/{core/__tests__/full/consult-startup-failure.test.d.ts → clients/attach/__tests__/session-identity-refresh.test.d.ts} +0 -0
- /package/dist/{core/canvas/__tests__/human-work-outbox.test.d.ts → clients/inbox/__tests__/inbox-controller.test.d.ts} +0 -0
- /package/dist/{core/keybindings/__tests__/bespoke-consumers.test.d.ts → clients/inbox/__tests__/mount-panel.test.d.ts} +0 -0
- /package/dist/{core/tui/__tests__/host-keybindings.test.d.ts → clients/inbox/review/__tests__/editor-roundtrip.test.d.ts} +0 -0
- /package/dist/{core/view/__tests__/transport-cache.test.d.ts → clients/inbox/review/__tests__/remap.test.d.ts} +0 -0
- /package/dist/{core/view/__tests__/transport-remote.test.d.ts → clients/inbox/review/__tests__/review-core.test.d.ts} +0 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import { chmodSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import { tmpdir } from 'node:os';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
import { editBufferInEditor } from '../roundtrip.js';
|
|
6
|
+
const temp = mkdtempSync(join(tmpdir(), 'humanloop-editor-roundtrip-'));
|
|
7
|
+
const priorEditor = process.env.EDITOR;
|
|
8
|
+
try {
|
|
9
|
+
const successfulEditor = join(temp, 'success-editor');
|
|
10
|
+
writeFileSync(successfulEditor, '#!/bin/sh\nprintf "edited\\n" > "$1"\n');
|
|
11
|
+
chmodSync(successfulEditor, 0o755);
|
|
12
|
+
process.env.EDITOR = successfulEditor;
|
|
13
|
+
assert.deepEqual(editBufferInEditor('original'), { text: 'edited' }, 'editor readback replaces the buffer and removes one editor-added newline');
|
|
14
|
+
const failingEditor = join(temp, 'failing-editor');
|
|
15
|
+
writeFileSync(failingEditor, '#!/bin/sh\nexit 9\n');
|
|
16
|
+
chmodSync(failingEditor, 0o755);
|
|
17
|
+
process.env.EDITOR = failingEditor;
|
|
18
|
+
const failed = editBufferInEditor('keep this');
|
|
19
|
+
assert.equal(failed.text, 'keep this', 'a failing editor leaves the active input intact');
|
|
20
|
+
assert.match(failed.error ?? '', /exited with status 9/, 'a failing editor reports its status to the host');
|
|
21
|
+
}
|
|
22
|
+
finally {
|
|
23
|
+
if (priorEditor === undefined)
|
|
24
|
+
delete process.env.EDITOR;
|
|
25
|
+
else
|
|
26
|
+
process.env.EDITOR = priorEditor;
|
|
27
|
+
rmSync(temp, { recursive: true, force: true });
|
|
28
|
+
}
|
|
29
|
+
console.log('editor round-trip tests passed');
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import { describe, it } from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { buildLineMap, remapRange } from '../remap.js';
|
|
4
|
+
describe('remap', () => {
|
|
5
|
+
describe('buildLineMap', () => {
|
|
6
|
+
it('identity mapping when content unchanged', () => {
|
|
7
|
+
const lines = ['line 1', 'line 2', 'line 3'];
|
|
8
|
+
const map = buildLineMap(lines, lines);
|
|
9
|
+
assert.equal(map.get(0), 0);
|
|
10
|
+
assert.equal(map.get(1), 1);
|
|
11
|
+
assert.equal(map.get(2), 2);
|
|
12
|
+
});
|
|
13
|
+
it('preserves normalized equivalence', () => {
|
|
14
|
+
const oldLines = ['line 1', ' line 2 ', 'line 3'];
|
|
15
|
+
const newLines = ['line 1', 'line 2', 'line 3'];
|
|
16
|
+
const map = buildLineMap(oldLines, newLines);
|
|
17
|
+
// All lines are normalized equivalent, so they map 1:1
|
|
18
|
+
assert.equal(map.get(0), 0);
|
|
19
|
+
assert.equal(map.get(1), 1);
|
|
20
|
+
assert.equal(map.get(2), 2);
|
|
21
|
+
});
|
|
22
|
+
it('handles common prefix', () => {
|
|
23
|
+
const oldLines = ['a', 'b', 'c', 'x'];
|
|
24
|
+
const newLines = ['a', 'b', 'c', 'x'];
|
|
25
|
+
const map = buildLineMap(oldLines, newLines);
|
|
26
|
+
// All lines match 1:1
|
|
27
|
+
assert.equal(map.get(0), 0);
|
|
28
|
+
assert.equal(map.get(1), 1);
|
|
29
|
+
assert.equal(map.get(2), 2);
|
|
30
|
+
assert.equal(map.get(3), 3);
|
|
31
|
+
});
|
|
32
|
+
it('handles common suffix', () => {
|
|
33
|
+
const oldLines = ['x', 'a', 'b', 'c'];
|
|
34
|
+
const newLines = ['x', 'a', 'b', 'c'];
|
|
35
|
+
const map = buildLineMap(oldLines, newLines);
|
|
36
|
+
assert.equal(map.get(0), 0);
|
|
37
|
+
assert.equal(map.get(1), 1);
|
|
38
|
+
assert.equal(map.get(2), 2);
|
|
39
|
+
assert.equal(map.get(3), 3);
|
|
40
|
+
});
|
|
41
|
+
it('handles insertion above range', () => {
|
|
42
|
+
const oldLines = ['x', 'y', 'z'];
|
|
43
|
+
const newLines = ['a', 'b', 'x', 'y', 'z'];
|
|
44
|
+
const map = buildLineMap(oldLines, newLines);
|
|
45
|
+
// Original lines are shifted down by 2
|
|
46
|
+
assert.equal(map.get(0), 2); // 'x'
|
|
47
|
+
assert.equal(map.get(1), 3); // 'y'
|
|
48
|
+
assert.equal(map.get(2), 4); // 'z'
|
|
49
|
+
});
|
|
50
|
+
it('handles deletion creating gaps', () => {
|
|
51
|
+
const oldLines = ['a', 'b', 'c', 'd', 'e'];
|
|
52
|
+
const newLines = ['a', 'c', 'e'];
|
|
53
|
+
const map = buildLineMap(oldLines, newLines);
|
|
54
|
+
assert.equal(map.get(0), 0); // 'a'
|
|
55
|
+
assert.equal(map.get(1), undefined); // 'b' deleted
|
|
56
|
+
assert.equal(map.get(2), 1); // 'c'
|
|
57
|
+
assert.equal(map.get(3), undefined); // 'd' deleted
|
|
58
|
+
assert.equal(map.get(4), 2); // 'e'
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
describe('remapRange', () => {
|
|
62
|
+
it('returns kept when both endpoints unmoved', () => {
|
|
63
|
+
const oldLines = ['a', 'b', 'c', 'd'];
|
|
64
|
+
const newLines = ['a', 'b', 'c', 'd'];
|
|
65
|
+
const map = buildLineMap(oldLines, newLines);
|
|
66
|
+
const result = remapRange(map, { line: 1, endLine: 3 });
|
|
67
|
+
assert.equal(result.kind, 'kept');
|
|
68
|
+
if (result.kind === 'kept') {
|
|
69
|
+
assert.equal(result.line, 1);
|
|
70
|
+
assert.equal(result.endLine, 3);
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
it('pure insertion above range keeps content identity', () => {
|
|
74
|
+
const oldLines = ['x', 'y', 'z'];
|
|
75
|
+
const newLines = ['a', 'b', 'x', 'y', 'z'];
|
|
76
|
+
const map = buildLineMap(oldLines, newLines);
|
|
77
|
+
// Range [0, 2] in old file → should map to [2, 4] in new
|
|
78
|
+
const result = remapRange(map, { line: 0, endLine: 2 });
|
|
79
|
+
assert.equal(result.kind, 'shifted');
|
|
80
|
+
if (result.kind === 'shifted') {
|
|
81
|
+
assert.equal(result.line, 2);
|
|
82
|
+
assert.equal(result.endLine, 4);
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
it('pure deletion of whole range yields detached', () => {
|
|
86
|
+
const oldLines = ['a', 'b', 'c', 'd', 'e'];
|
|
87
|
+
const newLines = ['a', 'e'];
|
|
88
|
+
const map = buildLineMap(oldLines, newLines);
|
|
89
|
+
// Range [1, 4] contains only deleted lines
|
|
90
|
+
const result = remapRange(map, { line: 1, endLine: 4 });
|
|
91
|
+
assert.equal(result.kind, 'detached');
|
|
92
|
+
});
|
|
93
|
+
it('partial range deletion yields shifted', () => {
|
|
94
|
+
const oldLines = ['a', 'b', 'c', 'd', 'e'];
|
|
95
|
+
const newLines = ['a', 'c', 'e'];
|
|
96
|
+
const map = buildLineMap(oldLines, newLines);
|
|
97
|
+
// Range [0, 3] contains 'a' (survives), 'b' (deleted), 'c' (survives), 'd' (deleted)
|
|
98
|
+
// Should remap to min=0, max=1 (the span of survivors)
|
|
99
|
+
const result = remapRange(map, { line: 0, endLine: 3 });
|
|
100
|
+
assert.equal(result.kind, 'shifted');
|
|
101
|
+
if (result.kind === 'shifted') {
|
|
102
|
+
assert.equal(result.line, 0); // min of survivors
|
|
103
|
+
assert.equal(result.endLine, 2); // max + 1
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
it('reindentation of entire file yields kept', () => {
|
|
107
|
+
const oldLines = [' line 1', ' line 2', ' line 3'];
|
|
108
|
+
const newLines = [' line 1', ' line 2', ' line 3'];
|
|
109
|
+
const map = buildLineMap(oldLines, newLines);
|
|
110
|
+
// After normalization, all lines match 1:1
|
|
111
|
+
const result = remapRange(map, { line: 0, endLine: 3 });
|
|
112
|
+
assert.equal(result.kind, 'kept');
|
|
113
|
+
if (result.kind === 'kept') {
|
|
114
|
+
assert.equal(result.line, 0);
|
|
115
|
+
assert.equal(result.endLine, 3);
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
it('handles range with one survivor in middle', () => {
|
|
119
|
+
const oldLines = [
|
|
120
|
+
'keep1',
|
|
121
|
+
'delete1',
|
|
122
|
+
'delete2',
|
|
123
|
+
'keep2',
|
|
124
|
+
'delete3',
|
|
125
|
+
];
|
|
126
|
+
const newLines = ['keep1', 'keep2'];
|
|
127
|
+
const map = buildLineMap(oldLines, newLines);
|
|
128
|
+
// Range [1, 4] covers delete1, delete2, keep2
|
|
129
|
+
// Only keep2 survives at index 1
|
|
130
|
+
const result = remapRange(map, { line: 1, endLine: 4 });
|
|
131
|
+
assert.equal(result.kind, 'shifted');
|
|
132
|
+
if (result.kind === 'shifted') {
|
|
133
|
+
assert.equal(result.line, 1);
|
|
134
|
+
assert.equal(result.endLine, 2);
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
it('detached when entire range deleted', () => {
|
|
138
|
+
const oldLines = ['a', 'b', 'c', 'd'];
|
|
139
|
+
const newLines = ['a', 'd'];
|
|
140
|
+
const map = buildLineMap(oldLines, newLines);
|
|
141
|
+
// Range [1, 3] contains only 'b' and 'c', both deleted
|
|
142
|
+
const result = remapRange(map, { line: 1, endLine: 3 });
|
|
143
|
+
assert.equal(result.kind, 'detached');
|
|
144
|
+
});
|
|
145
|
+
it('handles range at start', () => {
|
|
146
|
+
const oldLines = ['x', 'keep1', 'keep2', 'keep3'];
|
|
147
|
+
const newLines = ['keep1', 'keep2', 'keep3'];
|
|
148
|
+
const map = buildLineMap(oldLines, newLines);
|
|
149
|
+
// Range [1, 3] should map to [0, 2]
|
|
150
|
+
const result = remapRange(map, { line: 1, endLine: 3 });
|
|
151
|
+
assert.equal(result.kind, 'shifted');
|
|
152
|
+
if (result.kind === 'shifted') {
|
|
153
|
+
assert.equal(result.line, 0);
|
|
154
|
+
assert.equal(result.endLine, 2);
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
it('handles range at end', () => {
|
|
158
|
+
const oldLines = ['keep1', 'keep2', 'keep3', 'x'];
|
|
159
|
+
const newLines = ['keep1', 'keep2', 'keep3'];
|
|
160
|
+
const map = buildLineMap(oldLines, newLines);
|
|
161
|
+
// Range [1, 4] should map to [1, 3]
|
|
162
|
+
const result = remapRange(map, { line: 1, endLine: 4 });
|
|
163
|
+
assert.equal(result.kind, 'shifted');
|
|
164
|
+
if (result.kind === 'shifted') {
|
|
165
|
+
assert.equal(result.line, 1);
|
|
166
|
+
assert.equal(result.endLine, 3);
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
describe('performance', () => {
|
|
171
|
+
it('5000-line file with 20-line edit completes quickly', () => {
|
|
172
|
+
// Build old file: 5000 identical lines
|
|
173
|
+
const oldLines = [];
|
|
174
|
+
for (let i = 0; i < 5000; i++) {
|
|
175
|
+
oldLines.push(`line ${i}`);
|
|
176
|
+
}
|
|
177
|
+
// Build new file: insert 20 lines at position 2000
|
|
178
|
+
const newLines = [
|
|
179
|
+
...oldLines.slice(0, 2000),
|
|
180
|
+
...Array.from({ length: 20 }, (_, i) => `inserted ${i}`),
|
|
181
|
+
...oldLines.slice(2000),
|
|
182
|
+
];
|
|
183
|
+
// Measure time
|
|
184
|
+
const start = performance.now();
|
|
185
|
+
const map = buildLineMap(oldLines, newLines);
|
|
186
|
+
const elapsed = performance.now() - start;
|
|
187
|
+
// Should complete in well under 10s (the ordinary test budget)
|
|
188
|
+
// Even a naive implementation should handle this in <100ms
|
|
189
|
+
assert(elapsed < 1000, `buildLineMap took ${elapsed}ms, should be <1000ms`);
|
|
190
|
+
// Verify a few mappings
|
|
191
|
+
assert.equal(map.get(1999), 1999);
|
|
192
|
+
assert.equal(map.get(2000), 2020); // shifted by 20
|
|
193
|
+
assert.equal(map.get(2001), 2021);
|
|
194
|
+
});
|
|
195
|
+
});
|
|
196
|
+
});
|
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import stringWidth from 'string-width';
|
|
3
|
+
import { deriveAnchorUnits, remapUnitIndex, unitIndexForLine, unitRangeForSpan, } from '../anchors.js';
|
|
4
|
+
import { closeList, commentedUnitSet, commitCompose, currentAnchor, deleteAtListIndex, ensureAnchorVisible, anchorRowRange, initReviewState, moveActiveUnit, moveListIndex, openComposeEdit, openComposeNew, openList, scrollBy, scrollToReveal, textBackspace, textEnd, textHome, textInsert, textVertical, textVerticalWrapped, textWordBackspace, textWordLeft, textWordRight, undoLast, } from '../state.js';
|
|
5
|
+
import { maxHScroll, panBy, renderReviewFrame, } from '../frame.js';
|
|
6
|
+
import { matchReviewKey } from '../keys.js';
|
|
7
|
+
assert.equal(matchReviewKey('\x1bR'), null, 'legacy Alt+Shift+R remains viewer-global');
|
|
8
|
+
assert.equal(matchReviewKey('\x1b[82;4u'), null, 'Kitty Alt+Shift+R remains viewer-global');
|
|
9
|
+
assert.equal(matchReviewKey('\x1bi'), null, 'Alt+I remains owned outside the review surface');
|
|
10
|
+
// A small doc: a heading, a two-bullet list, a paragraph. The list is ONE
|
|
11
|
+
// top-level block but each bullet carries its own leaf span — so anchoring
|
|
12
|
+
// steps bullet-to-bullet, not list-to-list.
|
|
13
|
+
const sourceLines = ['# Title', '', '- line three', '- line four', '', 'line six'];
|
|
14
|
+
const existingComment = {
|
|
15
|
+
id: 'c1',
|
|
16
|
+
revision: 1,
|
|
17
|
+
line: 6,
|
|
18
|
+
endLine: 6,
|
|
19
|
+
lineText: 'line six',
|
|
20
|
+
comment: 'note',
|
|
21
|
+
status: 'open',
|
|
22
|
+
authorKind: 'human',
|
|
23
|
+
createdAt: 'now',
|
|
24
|
+
anchorStatus: 'live',
|
|
25
|
+
};
|
|
26
|
+
const doc = {
|
|
27
|
+
lines: ['TITLE', '', '• row three', '• row four', '', 'row six'],
|
|
28
|
+
rows: [0, null, 1, 1, null, 2],
|
|
29
|
+
spans: [[1, 1], null, [3, 3], [4, 4], null, [6, 6]],
|
|
30
|
+
blocks: [
|
|
31
|
+
{ type: 'paragraph', start: 1, end: 1 },
|
|
32
|
+
{ type: 'paragraph', start: 3, end: 4 },
|
|
33
|
+
{ type: 'paragraph', start: 6, end: 6 },
|
|
34
|
+
],
|
|
35
|
+
};
|
|
36
|
+
const anchor = deriveAnchorUnits(doc);
|
|
37
|
+
const { units } = anchor;
|
|
38
|
+
// deriveAnchorUnits: one unit per leaf span run; rowUnits keys the painter.
|
|
39
|
+
{
|
|
40
|
+
assert.equal(units.length, 4, 'each bullet is its own unit — the list block splits');
|
|
41
|
+
assert.deepEqual(units[0], { start: 1, end: 1, firstRow: 0, lastRow: 0 });
|
|
42
|
+
assert.deepEqual(units[1], { start: 3, end: 3, firstRow: 2, lastRow: 2 });
|
|
43
|
+
assert.deepEqual(units[2], { start: 4, end: 4, firstRow: 3, lastRow: 3 });
|
|
44
|
+
assert.deepEqual(units[3], { start: 6, end: 6, firstRow: 5, lastRow: 5 });
|
|
45
|
+
assert.deepEqual(anchor.rowUnits, [0, null, 1, 2, null, 3]);
|
|
46
|
+
}
|
|
47
|
+
// deriveAnchorUnits: consecutive rows sharing a span (a wrapped bullet)
|
|
48
|
+
// collapse into one unit; a mapped row with no leaf span (an unmapped block)
|
|
49
|
+
// anchors at its whole block's range; a separator between identical spans
|
|
50
|
+
// still splits units.
|
|
51
|
+
{
|
|
52
|
+
const d = {
|
|
53
|
+
lines: ['a1', 'a2 (wrapped)', '', 'chrome'],
|
|
54
|
+
rows: [0, 0, null, 1],
|
|
55
|
+
spans: [[2, 2], [2, 2], null, null],
|
|
56
|
+
blocks: [
|
|
57
|
+
{ type: 'paragraph', start: 1, end: 3 },
|
|
58
|
+
{ type: 'paragraph', start: 5, end: 9 },
|
|
59
|
+
],
|
|
60
|
+
};
|
|
61
|
+
const a = deriveAnchorUnits(d);
|
|
62
|
+
assert.deepEqual(a.units, [
|
|
63
|
+
{ start: 2, end: 2, firstRow: 0, lastRow: 1 },
|
|
64
|
+
{ start: 5, end: 9, firstRow: 3, lastRow: 3 },
|
|
65
|
+
]);
|
|
66
|
+
assert.deepEqual(a.rowUnits, [0, 0, null, 1]);
|
|
67
|
+
}
|
|
68
|
+
// deriveAnchorUnits: a fully unmapped doc degrades to one whole-doc unit.
|
|
69
|
+
{
|
|
70
|
+
const d = { lines: ['x', 'y'], rows: [null, null], spans: [null, null], blocks: [{ type: 'paragraph', start: 1, end: 7 }] };
|
|
71
|
+
const a = deriveAnchorUnits(d);
|
|
72
|
+
assert.deepEqual(a.units, [{ start: 1, end: 7, firstRow: 0, lastRow: 1 }]);
|
|
73
|
+
assert.deepEqual(a.rowUnits, [0, 0]);
|
|
74
|
+
}
|
|
75
|
+
// unitIndexForLine: containment, forward snap across gaps, clamp past the end.
|
|
76
|
+
{
|
|
77
|
+
assert.equal(unitIndexForLine(units, 1), 0);
|
|
78
|
+
assert.equal(unitIndexForLine(units, 2), 1, 'a gap line snaps forward to the next unit');
|
|
79
|
+
assert.equal(unitIndexForLine(units, 3), 1);
|
|
80
|
+
assert.equal(unitIndexForLine(units, 4), 2);
|
|
81
|
+
assert.equal(unitIndexForLine(units, 6), 3);
|
|
82
|
+
assert.equal(unitIndexForLine(units, 99), 3, 'past the last unit clamps to the last unit');
|
|
83
|
+
}
|
|
84
|
+
// unitRangeForSpan: all units overlapping a source range (chrome units carry
|
|
85
|
+
// their block's whole range, so a whole-fence comment covers every fence unit).
|
|
86
|
+
{
|
|
87
|
+
assert.deepEqual(unitRangeForSpan(units, 3, 4), { lo: 1, hi: 2 }, 'a two-bullet comment spans both bullet units');
|
|
88
|
+
assert.deepEqual(unitRangeForSpan(units, 6, 6), { lo: 3, hi: 3 });
|
|
89
|
+
assert.deepEqual(unitRangeForSpan(units, 2, 2), { lo: 1, hi: 1 }, 'a gap-only range snaps forward');
|
|
90
|
+
const fence = deriveAnchorUnits({
|
|
91
|
+
lines: ['┌─', 'code a', 'code b', '└─'],
|
|
92
|
+
rows: [0, 0, 0, 0],
|
|
93
|
+
spans: [[5, 10], [6, 6], [7, 7], [5, 10]],
|
|
94
|
+
blocks: [{ type: 'paragraph', start: 5, end: 10 }],
|
|
95
|
+
});
|
|
96
|
+
assert.equal(fence.units.length, 4, 'fence = top chrome + per-line content + bottom chrome');
|
|
97
|
+
assert.deepEqual(unitRangeForSpan(fence.units, 5, 10), { lo: 0, hi: 3 }, 'a whole-fence comment highlights every fence unit');
|
|
98
|
+
// Narrowest-containing lookup: a code line resolves to its own unit even
|
|
99
|
+
// though the whole-block chrome unit sits earlier in rendered order.
|
|
100
|
+
assert.equal(unitIndexForLine(fence.units, 6), 1, 'a code line resolves to its leaf unit, not the fence chrome');
|
|
101
|
+
assert.equal(unitIndexForLine(fence.units, 7), 2);
|
|
102
|
+
assert.equal(unitIndexForLine(fence.units, 5), 0, 'the fence opener resolves to the chrome unit');
|
|
103
|
+
// Resize remapping: an exact source-range match is preserved (chrome and
|
|
104
|
+
// leaf units share source lines), else the narrowest containing unit wins.
|
|
105
|
+
assert.equal(remapUnitIndex(fence.units, { start: 6, end: 6, firstRow: 9, lastRow: 9 }), 1, 'remap keeps a code-line anchor on its code line');
|
|
106
|
+
assert.equal(remapUnitIndex(fence.units, { start: 5, end: 10, firstRow: 9, lastRow: 9 }), 0, 'remap keeps a chrome anchor on the identical-range unit');
|
|
107
|
+
assert.equal(remapUnitIndex(fence.units, { start: 8, end: 9, firstRow: 0, lastRow: 0 }), 0, 'no exact match falls back to the best unit for its first line');
|
|
108
|
+
}
|
|
109
|
+
// Anchor motion: j/k move unit-to-unit; Shift+j/k extends across units.
|
|
110
|
+
{
|
|
111
|
+
let state = initReviewState(sourceLines, anchor, [], 0);
|
|
112
|
+
assert.deepEqual(currentAnchor(state), { line: 1, endLine: 1 });
|
|
113
|
+
state = moveActiveUnit(state, 1, false);
|
|
114
|
+
assert.deepEqual(currentAnchor(state), { line: 3, endLine: 3 }, 'j moves to a single bullet, not the whole list');
|
|
115
|
+
state = moveActiveUnit(state, 1, true);
|
|
116
|
+
assert.deepEqual(currentAnchor(state), { line: 3, endLine: 4 }, 'shift+j extends the selection downward');
|
|
117
|
+
state = moveActiveUnit(state, 1, true);
|
|
118
|
+
assert.deepEqual(currentAnchor(state), { line: 3, endLine: 6 }, 'shift+j extends across the block boundary');
|
|
119
|
+
state = moveActiveUnit(state, -3, true);
|
|
120
|
+
assert.deepEqual(currentAnchor(state), { line: 1, endLine: 3 }, 'shift+k can extend the selection back past the anchor');
|
|
121
|
+
state = moveActiveUnit(state, 1, false);
|
|
122
|
+
assert.deepEqual(currentAnchor(state), { line: 3, endLine: 3 }, 'a plain j/k collapses the selection');
|
|
123
|
+
// Bounds
|
|
124
|
+
state = moveActiveUnit(state, -100, false);
|
|
125
|
+
assert.equal(state.activeUnit, 0, 'anchor cannot move above the first unit');
|
|
126
|
+
state = moveActiveUnit(state, 100, false);
|
|
127
|
+
assert.equal(state.activeUnit, units.length - 1, 'anchor cannot move past the last unit');
|
|
128
|
+
}
|
|
129
|
+
// currentAnchor over chrome units takes the min/max across the selection.
|
|
130
|
+
{
|
|
131
|
+
const fence = deriveAnchorUnits({
|
|
132
|
+
lines: ['┌─', 'code a', '└─'],
|
|
133
|
+
rows: [0, 0, 0],
|
|
134
|
+
spans: [[5, 8], [6, 6], [5, 8]],
|
|
135
|
+
blocks: [{ type: 'paragraph', start: 5, end: 8 }],
|
|
136
|
+
});
|
|
137
|
+
let state = initReviewState(['', '', '', '', '```', 'a', 'b', '```'], fence, [], 0);
|
|
138
|
+
state = moveActiveUnit(state, 1, false); // content line unit
|
|
139
|
+
assert.deepEqual(currentAnchor(state), { line: 6, endLine: 6 });
|
|
140
|
+
state = moveActiveUnit(state, 1, true); // extend onto bottom chrome (5-8)
|
|
141
|
+
assert.deepEqual(currentAnchor(state), { line: 5, endLine: 8 }, 'chrome in the selection widens to the block range');
|
|
142
|
+
}
|
|
143
|
+
// An existing comment positions the initial anchor on its unit.
|
|
144
|
+
{
|
|
145
|
+
const state = initReviewState(sourceLines, anchor, [existingComment], 0);
|
|
146
|
+
assert.equal(state.activeUnit, 3);
|
|
147
|
+
}
|
|
148
|
+
// Compose emits a daemon mutation payload without changing the projection.
|
|
149
|
+
{
|
|
150
|
+
let state = initReviewState(sourceLines, anchor, [], 0);
|
|
151
|
+
state = moveActiveUnit(state, 1, false); // bullet unit → L3
|
|
152
|
+
state = moveActiveUnit(state, 1, true); // extend → L3-4
|
|
153
|
+
state = openComposeNew(state);
|
|
154
|
+
assert.equal(state.mode, 'compose');
|
|
155
|
+
assert.deepEqual(state.compose?.anchor, { line: 3, endLine: 4 });
|
|
156
|
+
state = { ...state, compose: { ...state.compose, buffer: 'needs a rewrite' } };
|
|
157
|
+
let committed = commitCompose(state);
|
|
158
|
+
state = committed.state;
|
|
159
|
+
assert.equal(state.mode, 'view');
|
|
160
|
+
assert.deepEqual(committed.commit, { editingId: null, anchor: { line: 3, endLine: 4 }, text: 'needs a rewrite' });
|
|
161
|
+
assert.deepEqual(state.comments, [], 'the daemon projection remains unchanged until refreshed');
|
|
162
|
+
// A projection refresh supplies the daemon-created comment before later edits.
|
|
163
|
+
state = { ...state, comments: [existingComment] };
|
|
164
|
+
// Empty compose closes without a mutation.
|
|
165
|
+
state = openComposeNew(state);
|
|
166
|
+
committed = commitCompose(state);
|
|
167
|
+
state = committed.state;
|
|
168
|
+
assert.equal(committed.commit, null);
|
|
169
|
+
assert.equal(state.mode, 'view');
|
|
170
|
+
// Editing from the list preserves selection and identifies the comment for the daemon.
|
|
171
|
+
state = moveActiveUnit(state, -100, false); // move anchor away first
|
|
172
|
+
state = openList(state);
|
|
173
|
+
state = openComposeEdit(state, 0);
|
|
174
|
+
assert.equal(state.compose?.editingId, existingComment.id);
|
|
175
|
+
assert.equal(state.activeUnit, 3, 'editing re-anchors onto the comment\'s last unit');
|
|
176
|
+
assert.equal(state.selectionAnchor, null, 'a single-line comment selects one unit');
|
|
177
|
+
state = { ...state, compose: { ...state.compose, buffer: 'edited note' } };
|
|
178
|
+
committed = commitCompose(state);
|
|
179
|
+
state = committed.state;
|
|
180
|
+
assert.equal(state.mode, 'list', 'editing from the list returns to the list');
|
|
181
|
+
assert.deepEqual(committed.commit, { editingId: existingComment.id, anchor: { line: 6, endLine: 6 }, text: 'edited note' });
|
|
182
|
+
assert.deepEqual(state.comments, [existingComment], 'editing does not locally alter the projection');
|
|
183
|
+
// The commented unit set marks every unit overlapped by the projection.
|
|
184
|
+
assert.deepEqual([...commentedUnitSet(state)], [3]);
|
|
185
|
+
state = closeList(state);
|
|
186
|
+
assert.equal(state.mode, 'view');
|
|
187
|
+
const undone = undoLast(state);
|
|
188
|
+
assert.equal(undone.id, existingComment.id, 'undo identifies the daemon comment to remove');
|
|
189
|
+
assert.deepEqual(undone.state.comments, [existingComment]);
|
|
190
|
+
}
|
|
191
|
+
// List navigation and delete-by-index identify daemon comments without changing the projection.
|
|
192
|
+
{
|
|
193
|
+
const secondComment = { ...existingComment, id: 'c2', line: 3, endLine: 3, lineText: '- line three', comment: 'second' };
|
|
194
|
+
let state = initReviewState(sourceLines, anchor, [existingComment, secondComment], 0);
|
|
195
|
+
state = openList(state);
|
|
196
|
+
state = moveListIndex(state, 1);
|
|
197
|
+
assert.equal(state.listIndex, 1);
|
|
198
|
+
const deleted = deleteAtListIndex(state);
|
|
199
|
+
assert.equal(deleted.id, secondComment.id);
|
|
200
|
+
assert.deepEqual(deleted.state.comments, [existingComment, secondComment]);
|
|
201
|
+
assert.equal(deleted.state.listIndex, 1);
|
|
202
|
+
}
|
|
203
|
+
// anchorRowRange: the rendered rows spanned by a unit range.
|
|
204
|
+
{
|
|
205
|
+
assert.deepEqual(anchorRowRange(units, 1, 2), { first: 2, last: 3 });
|
|
206
|
+
assert.deepEqual(anchorRowRange(units, 0, 3), { first: 0, last: 5 });
|
|
207
|
+
assert.equal(anchorRowRange(units, 9, 9), null);
|
|
208
|
+
}
|
|
209
|
+
// scrollToReveal: margin-aware reveal, top pin for oversize spans, clamping.
|
|
210
|
+
{
|
|
211
|
+
assert.equal(scrollToReveal(0, 10, 11, 8, 30, 2), 6, 'reveals a below-view anchor with a bottom margin');
|
|
212
|
+
assert.equal(scrollToReveal(6, 1, 1, 8, 30, 2), 0, 'reveals an above-view anchor, clamped at the top');
|
|
213
|
+
assert.equal(scrollToReveal(0, 4, 20, 8, 30), 3, 'a taller-than-view span pins its first row one below the indicator row');
|
|
214
|
+
assert.equal(scrollToReveal(0, 0, 20, 8, 30), 0, 'an oversize span starting at the top pins at zero');
|
|
215
|
+
assert.equal(scrollToReveal(3, 5, 6, 8, 30), 3, 'an already-visible anchor leaves the scroll untouched');
|
|
216
|
+
assert.equal(scrollToReveal(50, 25, 25, 8, 30, 2), 22, 'a stale overshoot scroll is clamped into the document');
|
|
217
|
+
// The margin yields before it may push the span's own edges out of view.
|
|
218
|
+
assert.equal(scrollToReveal(0, 10, 16, 8, 30, 2), 10, 'down-reveal of a bodyHeight-1 span keeps its first row visible');
|
|
219
|
+
assert.equal(scrollToReveal(20, 10, 16, 8, 30, 2), 9, 'up-reveal of a bodyHeight-1 span keeps its last row visible');
|
|
220
|
+
assert.equal(scrollToReveal(0, 10, 10, 2, 30, 2), 10, 'a tiny viewport still shows the anchored row itself');
|
|
221
|
+
}
|
|
222
|
+
// ensureAnchorVisible follows the active unit's rendered rows.
|
|
223
|
+
{
|
|
224
|
+
let state = initReviewState(sourceLines, anchor, [], 0);
|
|
225
|
+
state = { ...state, activeUnit: 3, scroll: 0 };
|
|
226
|
+
state = ensureAnchorVisible(state, doc, 2);
|
|
227
|
+
assert.equal(state.scroll, 4, 'the anchored unit\'s rows scroll into view');
|
|
228
|
+
}
|
|
229
|
+
// Scroll clamps to [0, totalLines - bodyHeight] and never goes negative.
|
|
230
|
+
{
|
|
231
|
+
let state = initReviewState(sourceLines, anchor, [], 0);
|
|
232
|
+
state = scrollBy(state, -50, 10, 30);
|
|
233
|
+
assert.equal(state.scroll, 0, 'scroll cannot go negative');
|
|
234
|
+
state = scrollBy(state, 50, 10, 30);
|
|
235
|
+
assert.equal(state.scroll, 20, 'scroll clamps to the maximum');
|
|
236
|
+
state = scrollBy(state, 50, 10, 30);
|
|
237
|
+
assert.equal(state.scroll, 20, 'scroll stays clamped on repeated overshoot');
|
|
238
|
+
}
|
|
239
|
+
// Multi-line text-buffer helpers used by the compose input.
|
|
240
|
+
{
|
|
241
|
+
let r = textInsert('', 0, 'hello');
|
|
242
|
+
assert.deepEqual(r, { buffer: 'hello', cursor: 5 });
|
|
243
|
+
r = textInsert(r.buffer, 5, '\nworld');
|
|
244
|
+
assert.deepEqual(r, { buffer: 'hello\nworld', cursor: 11 });
|
|
245
|
+
assert.equal(textHome(r.buffer, 11), 6, 'home jumps to the start of the current line');
|
|
246
|
+
assert.equal(textEnd(r.buffer, 6), 11, 'end jumps to the end of the current line');
|
|
247
|
+
assert.equal(textVertical(r.buffer, 8, -1), 2, 'up preserves the column on the previous line');
|
|
248
|
+
r = textBackspace(r.buffer, 11);
|
|
249
|
+
assert.deepEqual(r, { buffer: 'hello\nworl', cursor: 10 });
|
|
250
|
+
}
|
|
251
|
+
// Word motion and visual (wrapped) vertical motion in the compose box: alt+arrow
|
|
252
|
+
// steps whole words, and up/down step the rows the box actually paints — a long
|
|
253
|
+
// soft-wrapped comment is many visual rows even though it is one logical line.
|
|
254
|
+
{
|
|
255
|
+
const b = 'alpha beta gamma';
|
|
256
|
+
assert.equal(textWordLeft(b, 16), 11, 'alt+left lands on the start of the word before the cursor');
|
|
257
|
+
assert.equal(textWordLeft(b, 11), 6, 'alt+left from a word start skips to the previous word');
|
|
258
|
+
assert.equal(textWordRight(b, 0), 5, 'alt+right lands just past the current word');
|
|
259
|
+
assert.equal(textWordRight(b, 5), 10, 'alt+right skips the space then the next word');
|
|
260
|
+
assert.deepEqual(textWordBackspace(b, 16), { buffer: 'alpha beta ', cursor: 11 }, 'alt+backspace erases one word');
|
|
261
|
+
// width 6 wraps into rows 'alpha ' | 'beta g' | 'amma'.
|
|
262
|
+
assert.equal(textVerticalWrapped(b, 2, 1, 6), 8, 'down moves to the next wrapped row, same column');
|
|
263
|
+
assert.equal(textVerticalWrapped(b, 8, -1, 6), 2, 'up moves back to the previous wrapped row');
|
|
264
|
+
assert.equal(textVerticalWrapped(b, 2, -1, 6), 2, 'up on the first row is a no-op');
|
|
265
|
+
assert.equal(textVerticalWrapped(b, 15, 1, 6), 15, 'down on the last row is a no-op');
|
|
266
|
+
assert.equal(textVerticalWrapped('ab\ncd', 1, 1, 40), 4, 'hard newlines still separate rows');
|
|
267
|
+
}
|
|
268
|
+
// Render frame: the anchor highlight (gutter bar + tint) covers exactly the
|
|
269
|
+
// active unit's rows — one bullet, not the whole list — plus commented-unit
|
|
270
|
+
// markers and a key-hints-only footer.
|
|
271
|
+
{
|
|
272
|
+
let state = initReviewState(sourceLines, anchor, [], 0);
|
|
273
|
+
state = moveActiveUnit(state, 1, false); // first bullet (rendered row 2)
|
|
274
|
+
const frame = renderReviewFrame(state, 'doc.md', doc, 60, 20);
|
|
275
|
+
assert.equal(frame.length, 20, 'the frame always fills the requested row count');
|
|
276
|
+
const joined = frame.join('\n');
|
|
277
|
+
assert.match(joined, /Review — doc\.md/);
|
|
278
|
+
assert.match(joined, /0 comments/);
|
|
279
|
+
assert.ok(!joined.includes('Anchor:'), 'the view footer carries no anchor caption');
|
|
280
|
+
// Header is 3 rows; body row i shows doc row scroll+i.
|
|
281
|
+
const bodyRow2 = frame[3 + 2];
|
|
282
|
+
const bodyRow3 = frame[3 + 3];
|
|
283
|
+
const bodyRow0 = frame[3];
|
|
284
|
+
assert.ok(bodyRow2.includes('▌') && bodyRow2.includes('\x1b[48;5;236m'), 'the anchored bullet row gets the gutter bar and tint');
|
|
285
|
+
assert.ok(!bodyRow3.includes('▌'), 'the sibling bullet in the same list block is NOT highlighted');
|
|
286
|
+
assert.ok(!bodyRow0.includes('▌'), 'non-anchored rows carry no anchor gutter');
|
|
287
|
+
// A comment on the paragraph marks its rows with the commented gutter.
|
|
288
|
+
state = {
|
|
289
|
+
...state,
|
|
290
|
+
comments: [existingComment],
|
|
291
|
+
};
|
|
292
|
+
const frame2 = renderReviewFrame(state, 'doc.md', doc, 60, 20);
|
|
293
|
+
assert.ok(frame2[3 + 5].includes('▎'), 'commented units get the comment gutter marker');
|
|
294
|
+
assert.ok(!frame2[3 + 3].includes('▎'), 'uncommented sibling units carry no comment marker');
|
|
295
|
+
}
|
|
296
|
+
// ── Regression: a Mermaid diagram wider than the pane (reported bug) ────────
|
|
297
|
+
// Wide diagram rows used to run past the terminal's right edge; the spillover
|
|
298
|
+
// wrapped and was unreachable. Every emitted row must now fit the frame, and
|
|
299
|
+
// h/l must reach the far end of the diagram.
|
|
300
|
+
{
|
|
301
|
+
const wide = '\u2500'.repeat(300);
|
|
302
|
+
const wideDoc = {
|
|
303
|
+
lines: ['TITLE', '', wide, '', 'tail'],
|
|
304
|
+
rows: [0, null, 1, null, 2],
|
|
305
|
+
spans: [[1, 1], null, [3, 3], null, [5, 5]],
|
|
306
|
+
blocks: [
|
|
307
|
+
{ type: 'heading', start: 1, end: 1 },
|
|
308
|
+
{ type: 'mermaid', start: 3, end: 3 },
|
|
309
|
+
{ type: 'paragraph', start: 5, end: 5 },
|
|
310
|
+
],
|
|
311
|
+
};
|
|
312
|
+
const cols = 80;
|
|
313
|
+
let s = initReviewState(['# t', '', 'diagram', '', 'tail'], deriveAnchorUnits(wideDoc), [], 0);
|
|
314
|
+
const widths = (frame) => frame.map((line) => stringWidth(line));
|
|
315
|
+
const frame = renderReviewFrame(s, 'doc.md', wideDoc, cols, 20);
|
|
316
|
+
assert.ok(Math.max(...widths(frame)) <= cols, 'every frame row fits inside the terminal');
|
|
317
|
+
// The readable column stays centered while a full-pane diagram enters and
|
|
318
|
+
// leaves the viewport; only the diagram rows claim the pane.
|
|
319
|
+
const titleIndent = (at) => {
|
|
320
|
+
const title = renderReviewFrame({ ...s, scroll: at }, 'doc.md', wideDoc, 200, 7)[1];
|
|
321
|
+
return title.match(/^ */)?.[0].length ?? 0;
|
|
322
|
+
};
|
|
323
|
+
assert.equal(titleIndent(1), titleIndent(0), 'a visible wide diagram does not pull the review column left');
|
|
324
|
+
assert.equal(titleIndent(2), titleIndent(0), 'the review column remains fixed after scrolling past the diagram');
|
|
325
|
+
assert.ok(frame[3 + 2].includes('\u203a'), 'a diagram continuing to the right says so');
|
|
326
|
+
assert.ok(!frame[3 + 2].includes('\u2039'), 'nothing is hidden to the left at offset 0');
|
|
327
|
+
// h/l pan is bounded by what the visible rows actually overflow by.
|
|
328
|
+
const max = maxHScroll(s, wideDoc, cols, 20);
|
|
329
|
+
assert.equal(max, 300 - (cols - 4), 'pan reach is the widest visible row minus the body rectangle');
|
|
330
|
+
s = panBy(s, 10_000, wideDoc, cols, 20);
|
|
331
|
+
assert.equal(s.hscroll, max, 'panning right clamps at the end of the diagram');
|
|
332
|
+
const panned = renderReviewFrame(s, 'doc.md', wideDoc, cols, 20);
|
|
333
|
+
assert.ok(Math.max(...widths(panned)) <= cols, 'panned rows stay inside the terminal too');
|
|
334
|
+
assert.ok(panned[3 + 2].includes('\u2039'), 'a diagram continuing to the left says so');
|
|
335
|
+
assert.ok(!panned[3 + 2].includes('\u203a'), 'the far edge of the diagram is reachable');
|
|
336
|
+
// Prose rows never move under panning — only overflowing rows do.
|
|
337
|
+
assert.equal(panned[3 + 4].replace(/\x1b\[[0-9;]*m/g, '').trim(), 'tail');
|
|
338
|
+
s = panBy(s, -10_000, wideDoc, cols, 20);
|
|
339
|
+
assert.equal(s.hscroll, 0, 'panning left returns to the diagram start');
|
|
340
|
+
}
|
|
341
|
+
console.log('terminal review tests passed');
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { RenderedDoc } from '../../../core/termrender/termrender.js';
|
|
2
|
+
/** One leaf anchor unit: the finest step j/k moves by. Consecutive rendered
|
|
3
|
+
* rows sharing one source span collapse into a single unit that knows both
|
|
4
|
+
* its source-line bounds and its rendered-row bounds. */
|
|
5
|
+
export interface AnchorUnit {
|
|
6
|
+
/** 1-indexed inclusive source-line bounds. */
|
|
7
|
+
start: number;
|
|
8
|
+
end: number;
|
|
9
|
+
/** 0-indexed inclusive rendered-row bounds in the mapped doc. */
|
|
10
|
+
firstRow: number;
|
|
11
|
+
lastRow: number;
|
|
12
|
+
}
|
|
13
|
+
/** The anchorable shape of a rendered doc: ordered leaf units plus the
|
|
14
|
+
* per-row owning-unit index the painter keys off. */
|
|
15
|
+
export interface AnchorDoc {
|
|
16
|
+
units: AnchorUnit[];
|
|
17
|
+
/** Per rendered row: index into `units`, or null (separator rows). */
|
|
18
|
+
rowUnits: (number | null)[];
|
|
19
|
+
}
|
|
20
|
+
/** Derive leaf anchor units from a mapped render: dedupe runs of consecutive
|
|
21
|
+
* rows carrying the identical source span. A mapped row without a leaf span
|
|
22
|
+
* (an unmapped block) anchors at its whole block's range, so every content
|
|
23
|
+
* row belongs to a unit; separator rows belong to none. */
|
|
24
|
+
export declare function deriveAnchorUnits(doc: RenderedDoc): AnchorDoc;
|
|
25
|
+
/** Best unit for a source line: the NARROWEST unit containing it — code/table
|
|
26
|
+
* chrome units carry their whole block's range and sit before the per-line
|
|
27
|
+
* content units in rendered order, so "first whose end reaches the line"
|
|
28
|
+
* would wrongly pick the chrome over the exact code line. A line no unit
|
|
29
|
+
* contains (a blank separator) snaps forward to the next unit; past the end
|
|
30
|
+
* clamps to the last. */
|
|
31
|
+
export declare function unitIndexForLine(units: AnchorUnit[], line: number): number;
|
|
32
|
+
/** Remap a unit from an old unit list into a re-derived one: an exact
|
|
33
|
+
* source-range match wins (chrome and leaf units can share source lines),
|
|
34
|
+
* else the best unit for its first line. */
|
|
35
|
+
export declare function remapUnitIndex(units: AnchorUnit[], prev: AnchorUnit): number;
|
|
36
|
+
/** First/last unit indices overlapping the source range `line..endLine`;
|
|
37
|
+
* a range touching no unit snaps like unitIndexForLine. */
|
|
38
|
+
export declare function unitRangeForSpan(units: AnchorUnit[], line: number, endLine: number): {
|
|
39
|
+
lo: number;
|
|
40
|
+
hi: number;
|
|
41
|
+
};
|