@crouton-kit/crouter 0.3.70 → 0.3.78
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 +1 -1
- package/dist/builtin-memory/00-runtime-base.md +3 -10
- package/dist/builtin-memory/04-base-worker.md +18 -0
- package/dist/builtin-memory/04-orchestration-kernel.md +1 -1
- package/dist/builtin-pi-packages/pi-crtr-extensions/README.md +13 -34
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/__tests__/provider-rotation.test.ts +622 -724
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.ts +231 -68
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/statusline.ts +2 -9
- package/dist/builtin-pi-packages/pi-crtr-extensions/lib/subscription-state.ts +361 -90
- package/dist/builtin-pi-packages/pi-crtr-extensions/package-lock.json +2 -2
- package/dist/builtin-pi-packages/pi-mode-switch/README.md +11 -39
- package/dist/builtin-pi-packages/pi-mode-switch/extensions/index.ts +20 -15
- package/dist/builtin-pi-packages/pi-mode-switch/package.json +1 -1
- package/dist/builtin-views/canvas/tui.mjs +8 -9
- package/dist/builtin-views/chat/tui.mjs +12 -12
- package/dist/builtin-views/git-pr/tui.mjs +7 -8
- package/dist/builtin-views/inbox/tui.mjs +27 -41
- package/dist/builtin-views/linkedin/tui.mjs +23 -37
- package/dist/builtin-views/prompt-review/tui.mjs +11 -11
- package/dist/builtin-views/settings/tui.mjs +11 -11
- package/dist/builtin-views/workspace-sidebar/tui.mjs +8 -9
- package/dist/clients/attach/__tests__/attach-chrome-remote.test.js +3 -3
- package/dist/clients/attach/__tests__/crtr-output-render.test.js +1 -1
- package/dist/clients/attach/__tests__/mermaid-render.test.d.ts +1 -0
- package/dist/clients/attach/__tests__/mermaid-render.test.js +20 -0
- package/dist/clients/attach/__tests__/oauth-dialog-lifecycle.test.d.ts +1 -0
- package/dist/clients/attach/__tests__/oauth-dialog-lifecycle.test.js +110 -0
- package/dist/clients/attach/attach-cmd.d.ts +1 -1
- package/dist/clients/attach/attach-cmd.js +738 -735
- package/dist/clients/attach/canvas-panels.js +2 -3
- package/dist/clients/attach/chat-view.d.ts +7 -8
- package/dist/clients/attach/chat-view.js +119 -79
- package/dist/clients/attach/edit-diff-render.d.ts +6 -0
- package/dist/clients/attach/edit-diff-render.js +263 -0
- package/dist/clients/attach/input-controller.d.ts +28 -2
- package/dist/clients/attach/input-controller.js +38 -5
- package/dist/clients/attach/mermaid-render.js +5 -2
- package/dist/clients/attach/pickers.d.ts +8 -7
- package/dist/clients/attach/pickers.js +11 -17
- package/dist/clients/attach/slash-commands.d.ts +9 -0
- package/dist/clients/attach/slash-commands.js +127 -7
- package/dist/clients/attach/titled-editor.d.ts +12 -1
- package/dist/clients/attach/titled-editor.js +103 -8
- package/dist/commands/canvas-browse.js +2 -2
- package/dist/commands/memory/lint.js +39 -5
- package/dist/commands/memory/write.js +1 -0
- package/dist/commands/node.js +9 -2
- package/dist/commands/surface-tmux-spread.js +1 -3
- package/dist/commands/sys/__tests__/config-keybindings.test.d.ts +1 -0
- package/dist/commands/sys/__tests__/config-keybindings.test.js +55 -0
- package/dist/commands/sys/__tests__/config-model-ladders.test.d.ts +1 -0
- package/dist/commands/sys/__tests__/config-model-ladders.test.js +121 -0
- package/dist/commands/sys/config.js +18 -21
- package/dist/commands/sys/setup-core.d.ts +12 -1
- package/dist/commands/sys/setup-core.js +23 -3
- package/dist/commands/sys/setup.js +42 -12
- package/dist/core/__tests__/base-worker-prompt.test.d.ts +1 -0
- package/dist/core/__tests__/base-worker-prompt.test.js +24 -0
- package/dist/core/__tests__/canvas-inbox-watcher-hold.test.js +232 -1
- package/dist/core/__tests__/fault-classifier.test.js +15 -0
- package/dist/core/__tests__/full/broker-dialogs.test.js +7 -2
- package/dist/core/__tests__/host-teardown-process-group.test.js +15 -4
- package/dist/core/__tests__/stream-watchdog.test.d.ts +1 -0
- package/dist/core/__tests__/stream-watchdog.test.js +70 -0
- package/dist/core/__tests__/tmux-surface.test.js +72 -0
- package/dist/core/canvas/browse/__tests__/model.test.js +23 -5
- package/dist/core/canvas/browse/__tests__/render.test.js +1 -0
- package/dist/core/canvas/browse/app.js +164 -5
- package/dist/core/canvas/browse/model.d.ts +10 -6
- package/dist/core/canvas/browse/model.js +20 -12
- package/dist/core/canvas/browse/pins.d.ts +4 -0
- package/dist/core/canvas/browse/pins.js +29 -0
- package/dist/core/canvas/browse/render.d.ts +13 -1
- package/dist/core/canvas/browse/render.js +72 -15
- package/dist/core/canvas/nav-model.js +5 -11
- package/dist/core/canvas/paths.d.ts +5 -0
- package/dist/core/canvas/paths.js +7 -0
- package/dist/core/config.d.ts +53 -2
- package/dist/core/config.js +228 -110
- package/dist/core/fault-classifier.js +1 -1
- package/dist/core/fs-utils.d.ts +1 -0
- package/dist/core/fs-utils.js +15 -1
- package/dist/core/keybindings/__tests__/resolve.test.d.ts +1 -0
- package/dist/core/keybindings/__tests__/resolve.test.js +224 -0
- package/dist/core/keybindings/catalog.d.ts +14 -0
- package/dist/core/keybindings/catalog.js +256 -0
- package/dist/core/keybindings/index.d.ts +5 -0
- package/dist/core/keybindings/index.js +4 -0
- package/dist/core/keybindings/match.d.ts +29 -0
- package/dist/core/keybindings/match.js +99 -0
- package/dist/core/keybindings/persistence.d.ts +14 -0
- package/dist/core/keybindings/persistence.js +31 -0
- package/dist/core/keybindings/resolve.d.ts +14 -0
- package/dist/core/keybindings/resolve.js +305 -0
- package/dist/core/keybindings/types.d.ts +37 -0
- package/dist/core/keybindings/types.js +1 -0
- package/dist/core/memory-resolver.d.ts +1 -1
- package/dist/core/memory-resolver.js +10 -3
- package/dist/core/predicate.d.ts +5 -3
- package/dist/core/predicate.js +5 -3
- package/dist/core/runtime/broker-protocol.d.ts +33 -5
- package/dist/core/runtime/broker.js +163 -14
- package/dist/core/runtime/launch.d.ts +39 -6
- package/dist/core/runtime/launch.js +78 -20
- package/dist/core/runtime/naming.js +3 -3
- package/dist/core/runtime/pi-cli.d.ts +6 -0
- package/dist/core/runtime/pi-cli.js +16 -2
- package/dist/core/runtime/placement.d.ts +2 -2
- package/dist/core/runtime/placement.js +4 -1
- package/dist/core/runtime/promote.js +4 -0
- package/dist/core/runtime/recap.d.ts +3 -3
- package/dist/core/runtime/recap.js +47 -44
- package/dist/core/runtime/recycle.js +6 -1
- package/dist/core/runtime/reset.js +5 -0
- package/dist/core/runtime/session-list-cache.d.ts +23 -0
- package/dist/core/runtime/session-list-cache.js +270 -0
- package/dist/core/runtime/spawn.js +51 -32
- package/dist/core/runtime/stream-watchdog.d.ts +26 -0
- package/dist/core/runtime/stream-watchdog.js +75 -0
- package/dist/core/runtime/tmux-chrome.d.ts +1 -1
- package/dist/core/runtime/tmux-chrome.js +2 -2
- package/dist/core/runtime/tmux.d.ts +18 -15
- package/dist/core/runtime/tmux.js +216 -110
- package/dist/core/tui/__tests__/host-keybindings.test.d.ts +1 -0
- package/dist/core/tui/__tests__/host-keybindings.test.js +112 -0
- package/dist/core/tui/host.d.ts +30 -1
- package/dist/core/tui/host.js +148 -34
- package/dist/core/view/contract.d.ts +17 -3
- package/dist/pi-extensions/__tests__/canvas-stophook-agentend.test.js +41 -0
- package/dist/pi-extensions/__tests__/canvas-stophook-context-nudge.test.d.ts +1 -0
- package/dist/pi-extensions/__tests__/canvas-stophook-context-nudge.test.js +121 -0
- package/dist/pi-extensions/canvas-inbox-watcher.d.ts +1 -1
- package/dist/pi-extensions/canvas-inbox-watcher.js +203 -25
- package/dist/pi-extensions/canvas-recap.d.ts +4 -0
- package/dist/pi-extensions/canvas-recap.js +38 -37
- package/dist/pi-extensions/canvas-stophook.d.ts +11 -0
- package/dist/pi-extensions/canvas-stophook.js +33 -4
- package/dist/types.d.ts +3 -13
- package/dist/types.js +2 -26
- package/dist/web-client/assets/{index-DiFuLcp6.js → index--md2ylfi.js} +1 -1
- package/dist/web-client/index.html +1 -1
- package/dist/web-client/sw.js +1 -1
- package/package.json +3 -3
- package/dist/pi-extensions/canvas-nav.d.ts +0 -43
- package/dist/pi-extensions/canvas-nav.js +0 -640
- package/dist/pi-extensions/widget-order-bus.d.ts +0 -6
- package/dist/pi-extensions/widget-order-bus.js +0 -34
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
# pi-mode-switch
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
cycles plan / accept-edits. Whenever the mode changes, the new mode's operating
|
|
5
|
-
instructions are injected (invisibly) before your next message.
|
|
3
|
+
Cycle between operating modes with crouter's configurable **Cycle mode** binding (default **Alt+M**) — the way Claude's Shift+Tab cycles plan / accept-edits. Whenever the mode changes, the new mode's operating instructions are injected invisibly before your next message.
|
|
6
4
|
|
|
7
5
|
## Modes
|
|
8
6
|
|
|
@@ -14,51 +12,25 @@ instructions are injected (invisibly) before your next message.
|
|
|
14
12
|
|
|
15
13
|
## Usage
|
|
16
14
|
|
|
17
|
-
- `Alt+M` — cycle `normal → spec → plan → normal`
|
|
18
|
-
- `/mode` —
|
|
15
|
+
- the configured `crtr.mode.cycle` binding (default `Alt+M`) — cycle `normal → spec → plan → normal`
|
|
16
|
+
- `/mode` — cycle without using the shortcut
|
|
19
17
|
- `/mode <normal\|spec\|plan>` — jump to a specific mode
|
|
20
|
-
- the agent can switch its own mode by running the bundled `bin/mode` CLI through
|
|
21
|
-
its bash tool — e.g. `bash <plugin>/bin/mode normal` after a spec/plan is
|
|
22
|
-
approved. The command prints that mode's guidance, so the text only enters
|
|
23
|
-
context at the moment of the switch (no always-on tool)
|
|
18
|
+
- the agent can switch its own mode by running the bundled `bin/mode` CLI through its bash tool — e.g. `bash <plugin>/bin/mode normal` after a spec/plan is approved. The command prints that mode's guidance, so the text only enters context at the moment of the switch (no always-on tool)
|
|
24
19
|
|
|
25
|
-
The current mode renders as a badge on the **bottom-right of the input box**,
|
|
26
|
-
diagonally opposite the session-name badge (top-left). Mode persists across
|
|
27
|
-
`--resume`.
|
|
20
|
+
The current mode renders as a badge on the **bottom-right of the input box**, diagonally opposite the session-name badge (top-left). Mode persists across `--resume`.
|
|
28
21
|
|
|
29
22
|
## How it works
|
|
30
23
|
|
|
31
|
-
The extension tracks the active mode and the mode whose guidance was last
|
|
32
|
-
injected. On `before_agent_start` it compares the two: if they differ, it
|
|
33
|
-
prepends that mode's guidance to the turn (as a hidden `mode-switch-context`
|
|
34
|
-
message) and records the injection. Stay in the same mode and nothing extra is
|
|
35
|
-
added. Returning to `normal` also strips stale guidance from context.
|
|
24
|
+
The extension tracks the active mode and the mode whose guidance was last injected. On `before_agent_start` it compares the two: if they differ, it prepends that mode's guidance to the turn as a hidden `mode-switch-context` message and records the injection. Stay in the same mode and nothing extra is added. Returning to `normal` also strips stale guidance from context.
|
|
36
25
|
|
|
37
|
-
The agent can also switch its own mode without a permanent tool. The bundled
|
|
38
|
-
`bin/mode` CLI, run through the normal bash tool, writes the requested mode to
|
|
39
|
-
`<artifact-root>/mode-switch/request.json` and prints that mode's operating
|
|
40
|
-
instructions to stdout. The extension mirrors each mode's guidance to
|
|
41
|
-
`mode-switch/guidance/<mode>.txt` at session start (single source of truth stays
|
|
42
|
-
in the extension), watches the request file to keep the badge and session state in
|
|
43
|
-
sync, and consumes each request on read so a later Alt+M isn't clobbered. The
|
|
44
|
-
guidance reaches the agent through the command's own output, so it only enters
|
|
45
|
-
context at the moment of the switch — no tool sits in the context window, and
|
|
46
|
-
there's no coupling to any external runtime, just files.
|
|
26
|
+
The agent can also switch its own mode without a permanent tool. The bundled `bin/mode` CLI, run through the normal bash tool, writes the requested mode to `<artifact-root>/mode-switch/request.json` and prints that mode's operating instructions to stdout. The extension mirrors each mode's guidance to `mode-switch/guidance/<mode>.txt` at session start, watches the request file to keep the badge and session state in sync, and applies each tokenized request once. The guidance reaches the agent through the command's own output, so it only enters context at the moment of the switch — no tool sits in the context window, and there's no coupling to an external runtime beyond the shared files.
|
|
47
27
|
|
|
48
|
-
This is guidance-only — it does not hard-restrict tools. The instructions tell
|
|
49
|
-
the agent not to edit in spec/plan mode; combine with a tool gate if you want
|
|
50
|
-
enforcement.
|
|
28
|
+
This is guidance-only — it does not hard-restrict tools. The instructions tell the agent not to edit in spec/plan mode; combine with a tool gate if you want enforcement.
|
|
51
29
|
|
|
52
|
-
The input badge is drawn by wrapping whatever editor is already installed (
|
|
53
|
-
session-name badge extension) in a transparent Proxy that overrides only
|
|
54
|
-
`render()`. It composes instead of replacing, so other editor decorations keep
|
|
55
|
-
working.
|
|
30
|
+
The input badge is drawn by wrapping whatever editor is already installed (for example, a session-name badge extension) in a transparent Proxy that overrides only `render()`. It composes instead of replacing, so other editor decorations keep working.
|
|
56
31
|
|
|
57
32
|
## Install
|
|
58
33
|
|
|
59
|
-
|
|
60
|
-
pi install /Users/silasrhyneer/Code/cli/pi-mode-switch # user scope
|
|
61
|
-
pi install -l /Users/silasrhyneer/Code/cli/pi-mode-switch # project scope
|
|
62
|
-
```
|
|
34
|
+
This package ships inside crouter. `crtr sys setup` registers its built package path with pi; there is no separate repository or package to install.
|
|
63
35
|
|
|
64
|
-
Run `/reload` in pi after
|
|
36
|
+
Run `/reload` in pi after setup. The extension resolves `crtr.mode.cycle` from the sparse `keybindings` object in `~/.crouter/config.json`; missing overrides use Alt+M, multiple gestures all register, and an empty list disables the shortcut. `/mode` remains available when the shortcut is disabled. Pi's stock actions and `~/.pi/agent/keybindings.json` remain untouched.
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Mode Switch Extension
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* cycles plan / accept-edits. Modes here:
|
|
4
|
+
* Use the configured crouter mode-cycle binding to cycle between operating
|
|
5
|
+
* modes, the way Claude's Shift+Tab cycles plan / accept-edits. Modes here:
|
|
6
6
|
*
|
|
7
7
|
* normal → full access (default)
|
|
8
8
|
* spec → write a specification before any code
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* instructions are prepended (invisibly) to the next agent turn. Stay in the
|
|
14
14
|
* same mode across turns and nothing extra is injected.
|
|
15
15
|
*
|
|
16
|
-
* -
|
|
16
|
+
* - The configured shortcut or /mode cycles; /mode <name> jumps to a mode
|
|
17
17
|
* - The agent can switch its own mode by running the bundled `bin/mode` CLI via
|
|
18
18
|
* its bash tool; the command prints the new mode's guidance to stdout, so that
|
|
19
19
|
* text only enters context at the moment of the switch (no always-on tool)
|
|
@@ -31,6 +31,7 @@ import { type FSWatcher, mkdirSync, readFileSync, rmSync, watch, writeFileSync }
|
|
|
31
31
|
import { dirname, join } from "node:path";
|
|
32
32
|
import { fileURLToPath } from "node:url";
|
|
33
33
|
import { workspaceRoot } from "../../../core/artifact.js";
|
|
34
|
+
import { resolveUserKeybindings } from "../../../core/keybindings/index.js";
|
|
34
35
|
|
|
35
36
|
type Mode = "normal" | "spec" | "plan";
|
|
36
37
|
|
|
@@ -62,7 +63,7 @@ function modeSwitchDir(cwd: string = process.cwd(), nodeId: string = process.env
|
|
|
62
63
|
return join(workspaceRoot(cwd), "mode-switch", nodeId);
|
|
63
64
|
}
|
|
64
65
|
|
|
65
|
-
// The
|
|
66
|
+
// The order used by the shortcut and /mode command.
|
|
66
67
|
const CYCLE: Mode[] = ["normal", "spec", "plan"];
|
|
67
68
|
|
|
68
69
|
interface ModeDef {
|
|
@@ -214,9 +215,8 @@ export default function modeSwitchExtension(pi: ExtensionAPI): void {
|
|
|
214
215
|
}
|
|
215
216
|
|
|
216
217
|
// Apply a mode change to state and persistence. Shared by every path: the
|
|
217
|
-
//
|
|
218
|
-
//
|
|
219
|
-
// stdout).
|
|
218
|
+
// Shortcut/command changes add a UI toast and inject guidance on the next
|
|
219
|
+
// turn; the CLI path has already handed the agent its guidance via stdout.
|
|
220
220
|
function applyMode(next: Mode): void {
|
|
221
221
|
currentMode = next;
|
|
222
222
|
repaint();
|
|
@@ -241,8 +241,8 @@ export default function modeSwitchExtension(pi: ExtensionAPI): void {
|
|
|
241
241
|
// requested mode to <root>/mode-switch/request.json. We pick that up live via
|
|
242
242
|
// fs.watch (with a before_agent_start reconcile as a backstop) only to sync the
|
|
243
243
|
// badge and persisted state — never to inject guidance, since the CLI already
|
|
244
|
-
// printed it to the agent's bash output.
|
|
245
|
-
//
|
|
244
|
+
// printed it to the agent's bash output. Token gating ensures a later
|
|
245
|
+
// shortcut-driven change can't be clobbered by a stale request.
|
|
246
246
|
function applyFromRequest(next: Mode, source: "cli" | "attach"): void {
|
|
247
247
|
if (next === currentMode) return;
|
|
248
248
|
applyMode(next);
|
|
@@ -266,7 +266,7 @@ export default function modeSwitchExtension(pi: ExtensionAPI): void {
|
|
|
266
266
|
// Apply each CLI request at most once (token-gated) and never delete it, so
|
|
267
267
|
// every live instance — including the one actually on screen — converges to the
|
|
268
268
|
// requested mode regardless of /reload leftovers. A lingering request whose
|
|
269
|
-
// token we already applied can't clobber a later
|
|
269
|
+
// token we already applied can't clobber a later shortcut-driven change.
|
|
270
270
|
if (parsed.token && parsed.token === lastAppliedToken) return;
|
|
271
271
|
lastAppliedToken = parsed.token;
|
|
272
272
|
if (parsed.mode && (CYCLE as string[]).includes(parsed.mode)) {
|
|
@@ -291,11 +291,16 @@ export default function modeSwitchExtension(pi: ExtensionAPI): void {
|
|
|
291
291
|
setMode(CYCLE[(idx + 1) % CYCLE.length], ctx);
|
|
292
292
|
}
|
|
293
293
|
|
|
294
|
-
//
|
|
295
|
-
pi
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
294
|
+
// Resolve once when the extension loads. Crouter's sparse user config owns
|
|
295
|
+
// this semantic action; pi's stock action bindings remain independently owned
|
|
296
|
+
// by pi and are neither read nor rewritten here.
|
|
297
|
+
const bindings = resolveUserKeybindings();
|
|
298
|
+
for (const gesture of bindings.gestures("crtr.mode.cycle")) {
|
|
299
|
+
pi.registerShortcut(gesture, {
|
|
300
|
+
description: "Cycle mode (normal → spec → plan)",
|
|
301
|
+
handler: async (ctx) => cycleMode(ctx),
|
|
302
|
+
});
|
|
303
|
+
}
|
|
299
304
|
|
|
300
305
|
// /mode cycles; /mode <name> jumps to a specific mode.
|
|
301
306
|
pi.registerCommand("mode", {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-mode-switch",
|
|
3
3
|
"version": "0.1.0",
|
|
4
|
-
"description": "
|
|
4
|
+
"description": "Cycle between spec, plan, and normal modes with crouter's configurable mode binding. Injects mode-specific guidance whenever the mode changes.",
|
|
5
5
|
"keywords": ["pi-package", "modes", "spec", "plan"],
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"peerDependencies": {
|
|
@@ -127,12 +127,12 @@ export function render(state, draw, content) {
|
|
|
127
127
|
if (state.totalNodes === 0) {
|
|
128
128
|
emptyState(draw, content, {
|
|
129
129
|
headline: 'No nodes on the canvas',
|
|
130
|
-
secondary: ['Spawn one with `crtr node new`.', '
|
|
130
|
+
secondary: ['Spawn one with `crtr node new`.', 'Use refresh to update.'],
|
|
131
131
|
});
|
|
132
132
|
} else {
|
|
133
133
|
emptyState(draw, content, {
|
|
134
134
|
headline: 'All caught up',
|
|
135
|
-
secondary: [`${state.totalNodes} node${state.totalNodes === 1 ? '' : 's'} finished — none active.`, '
|
|
135
|
+
secondary: [`${state.totalNodes} node${state.totalNodes === 1 ? '' : 's'} finished — none active.`, 'Use refresh to update.'],
|
|
136
136
|
});
|
|
137
137
|
}
|
|
138
138
|
return;
|
|
@@ -156,14 +156,13 @@ export function render(state, draw, content) {
|
|
|
156
156
|
// ── keymap ───────────────────────────────────────────────────────────────
|
|
157
157
|
|
|
158
158
|
/**
|
|
159
|
-
* Read-only navigation
|
|
160
|
-
*
|
|
161
|
-
* from these bindings' `hint` fields (the single source of truth).
|
|
159
|
+
* Read-only navigation. No async actions from input — this is a monitor, not a
|
|
160
|
+
* controller. Footer hints come from these bindings' `hint` fields.
|
|
162
161
|
* @type {import('../../core/view/contract.js').KeyBinding<CanvasState>[]}
|
|
163
162
|
*/
|
|
164
163
|
export const keymap = [
|
|
165
|
-
{
|
|
166
|
-
{
|
|
167
|
-
{
|
|
168
|
-
{
|
|
164
|
+
{ bindingId: 'crtr.view.canvas.down', intent: 'cursorDown', hint: { label: 'move' } },
|
|
165
|
+
{ bindingId: 'crtr.view.canvas.up', intent: 'cursorUp' },
|
|
166
|
+
{ bindingId: 'crtr.view.canvas.refresh', intent: 'refresh', hint: { label: 'refresh' } },
|
|
167
|
+
{ bindingId: 'crtr.view.canvas.quit', intent: 'quit', hint: { label: 'quit' } },
|
|
169
168
|
];
|
|
@@ -275,7 +275,7 @@ function renderTakeover(state, draw, content) {
|
|
|
275
275
|
errorState(draw, content, {
|
|
276
276
|
headline: 'Missing target',
|
|
277
277
|
cause: 'run crtr view run chat --target <node-id>',
|
|
278
|
-
hint: '
|
|
278
|
+
hint: 'Use the view quit control.',
|
|
279
279
|
});
|
|
280
280
|
return;
|
|
281
281
|
}
|
|
@@ -285,7 +285,7 @@ function renderTakeover(state, draw, content) {
|
|
|
285
285
|
glyphFg: '33',
|
|
286
286
|
headline: 'Node asleep',
|
|
287
287
|
explanation: 'node is asleep / focus or revive it first',
|
|
288
|
-
nextStep: '
|
|
288
|
+
nextStep: 'Reconnect after revive.',
|
|
289
289
|
});
|
|
290
290
|
return;
|
|
291
291
|
}
|
|
@@ -293,7 +293,7 @@ function renderTakeover(state, draw, content) {
|
|
|
293
293
|
errorState(draw, content, {
|
|
294
294
|
headline: 'Node not found',
|
|
295
295
|
cause: 'check the target',
|
|
296
|
-
hint: '
|
|
296
|
+
hint: 'Use the view quit control.',
|
|
297
297
|
});
|
|
298
298
|
return;
|
|
299
299
|
}
|
|
@@ -354,15 +354,15 @@ const canCompose = (state) => state.target != null && state.conn !== 'no-broker'
|
|
|
354
354
|
|
|
355
355
|
/** @type {import('../../core/view/contract.js').KeyBinding<ChatState>[]} */
|
|
356
356
|
export const keymap = [
|
|
357
|
-
{ capture: 'setDraft', when: canCompose, hint: {
|
|
358
|
-
{
|
|
359
|
-
{
|
|
360
|
-
{
|
|
361
|
-
{
|
|
362
|
-
{
|
|
363
|
-
{
|
|
364
|
-
{
|
|
365
|
-
{
|
|
357
|
+
{ capture: 'setDraft', when: canCompose, hint: { label: 'compose' } },
|
|
358
|
+
{ bindingId: 'crtr.view.chat.submit', intent: 'submitDraft', when: canCompose, hint: { label: 'send' } },
|
|
359
|
+
{ bindingId: 'crtr.view.chat.contextual-cancel', intent: 'abort', when: isControllerStreaming },
|
|
360
|
+
{ bindingId: 'crtr.view.chat.contextual-cancel', intent: 'clearNotice', when: hasNotice, payload: (state) => state.notices[state.notices.length - 1]?.id },
|
|
361
|
+
{ bindingId: 'crtr.view.chat.contextual-cancel', intent: 'setDraft', when: hasDraft, payload: () => '' },
|
|
362
|
+
{ bindingId: 'crtr.view.chat.reconnect', intent: 'reconnect', when: canCompose, hint: { label: 'reconnect' } },
|
|
363
|
+
{ bindingId: 'crtr.view.chat.request-control', intent: 'requestControl', when: isObserver, hint: { label: 'control' } },
|
|
364
|
+
{ bindingId: 'crtr.view.chat.release-control', intent: 'releaseControl', when: isController, hint: { label: 'yield' } },
|
|
365
|
+
{ bindingId: 'crtr.view.chat.quit', intent: 'quit', hint: { label: 'quit' } },
|
|
366
366
|
];
|
|
367
367
|
|
|
368
368
|
export default { render, keymap };
|
|
@@ -275,7 +275,7 @@ export function render(state, draw, content) {
|
|
|
275
275
|
if (g.files.length === 0 && state.prs.length === 0 && !state.prNote) {
|
|
276
276
|
emptyState(draw, bodyRect, {
|
|
277
277
|
headline: 'All clear',
|
|
278
|
-
secondary: [`${g.branch} — clean working tree, no open PRs.`, '
|
|
278
|
+
secondary: [`${g.branch} — clean working tree, no open PRs.`, 'Use refresh to update.'],
|
|
279
279
|
});
|
|
280
280
|
return;
|
|
281
281
|
}
|
|
@@ -289,14 +289,13 @@ export function render(state, draw, content) {
|
|
|
289
289
|
// ── keymap ───────────────────────────────────────────────────────────────
|
|
290
290
|
|
|
291
291
|
/**
|
|
292
|
-
* Read-only navigation
|
|
293
|
-
*
|
|
294
|
-
* from these bindings' `hint` fields (the single source of truth).
|
|
292
|
+
* Read-only navigation. No async actions from input — this is a monitor, not a
|
|
293
|
+
* controller. Footer hints come from these bindings' `hint` fields.
|
|
295
294
|
* @type {import('../../core/view/contract.js').KeyBinding<GitPrState>[]}
|
|
296
295
|
*/
|
|
297
296
|
export const keymap = [
|
|
298
|
-
{
|
|
299
|
-
{
|
|
300
|
-
{
|
|
301
|
-
{
|
|
297
|
+
{ bindingId: 'crtr.view.git-pr.down', intent: 'cursorDown', hint: { label: 'move' } },
|
|
298
|
+
{ bindingId: 'crtr.view.git-pr.up', intent: 'cursorUp' },
|
|
299
|
+
{ bindingId: 'crtr.view.git-pr.refresh', intent: 'refresh', hint: { label: 'refresh' } },
|
|
300
|
+
{ bindingId: 'crtr.view.git-pr.quit', intent: 'quit', hint: { label: 'quit' } },
|
|
302
301
|
];
|
|
@@ -33,7 +33,7 @@ function renderGuided(draw, content, guide) {
|
|
|
33
33
|
errorState(draw, content, {
|
|
34
34
|
headline,
|
|
35
35
|
cause: d.explanation,
|
|
36
|
-
hint: d.nextStep || '
|
|
36
|
+
hint: d.nextStep || 'Refresh to retry.',
|
|
37
37
|
});
|
|
38
38
|
return;
|
|
39
39
|
}
|
|
@@ -149,11 +149,10 @@ function renderThreadBody(state, draw, rect, reactTarget) {
|
|
|
149
149
|
}
|
|
150
150
|
}
|
|
151
151
|
|
|
152
|
-
/** The compose bar (reply mode): hairline + label + draft + cursor
|
|
152
|
+
/** The compose bar (reply mode): hairline + label + draft + cursor. */
|
|
153
153
|
function renderComposer(state, draw, right) {
|
|
154
|
-
const hairRow = right.row + right.height -
|
|
155
|
-
const barRow = right.row + right.height -
|
|
156
|
-
const hintRow = right.row + right.height - 1;
|
|
154
|
+
const hairRow = right.row + right.height - 2;
|
|
155
|
+
const barRow = right.row + right.height - 1;
|
|
157
156
|
draw.hline(hairRow, right.col, right.col + right.width);
|
|
158
157
|
const label = '✎ Reply ';
|
|
159
158
|
const labelW = Array.from(label).length;
|
|
@@ -166,18 +165,12 @@ function renderComposer(state, draw, right) {
|
|
|
166
165
|
{ text: shown },
|
|
167
166
|
{ text: '█', style: { fg: '33' } },
|
|
168
167
|
], right.width);
|
|
169
|
-
draw.spans(hintRow, right.col, [
|
|
170
|
-
{ text: 'enter', style: { bold: true } }, { text: ' send', style: { dim: true } },
|
|
171
|
-
{ text: ' · ', style: { dim: true } },
|
|
172
|
-
{ text: 'esc', style: { bold: true } }, { text: ' cancel', style: { dim: true } },
|
|
173
|
-
], right.width);
|
|
174
168
|
}
|
|
175
169
|
|
|
176
|
-
/** The react picker bar (react mode): hairline + emoji chip row
|
|
170
|
+
/** The react picker bar (react mode): hairline + emoji chip row. */
|
|
177
171
|
function renderReactBar(state, draw, right) {
|
|
178
|
-
const hairRow = right.row + right.height -
|
|
179
|
-
const barRow = right.row + right.height -
|
|
180
|
-
const hintRow = right.row + right.height - 1;
|
|
172
|
+
const hairRow = right.row + right.height - 2;
|
|
173
|
+
const barRow = right.row + right.height - 1;
|
|
181
174
|
draw.hline(hairRow, right.col, right.col + right.width);
|
|
182
175
|
/** @type {object[]} */
|
|
183
176
|
const spans = [{ text: '☺ React ', style: { fg: '33', bold: true } }];
|
|
@@ -190,13 +183,6 @@ function renderReactBar(state, draw, right) {
|
|
|
190
183
|
}
|
|
191
184
|
});
|
|
192
185
|
draw.spans(barRow, right.col, spans, right.width);
|
|
193
|
-
draw.spans(hintRow, right.col, [
|
|
194
|
-
{ text: '←/→', style: { bold: true } }, { text: ' pick', style: { dim: true } },
|
|
195
|
-
{ text: ' · ', style: { dim: true } },
|
|
196
|
-
{ text: 'enter', style: { bold: true } }, { text: ' react', style: { dim: true } },
|
|
197
|
-
{ text: ' · ', style: { dim: true } },
|
|
198
|
-
{ text: 'esc', style: { bold: true } }, { text: ' cancel', style: { dim: true } },
|
|
199
|
-
], right.width);
|
|
200
186
|
}
|
|
201
187
|
|
|
202
188
|
/**
|
|
@@ -210,7 +196,7 @@ function renderDetail(state, draw, right) {
|
|
|
210
196
|
centeredStack(draw, right, [
|
|
211
197
|
[{ text: '✉ ', style: { dim: true } }, { text: 'No conversation open', style: { dim: true } }],
|
|
212
198
|
[{ text: '' }],
|
|
213
|
-
[{ text: '
|
|
199
|
+
[{ text: 'Open a conversation to view it.', style: { dim: true } }],
|
|
214
200
|
]);
|
|
215
201
|
return;
|
|
216
202
|
}
|
|
@@ -233,7 +219,7 @@ function renderDetail(state, draw, right) {
|
|
|
233
219
|
|
|
234
220
|
const composing = state.mode === 'reply';
|
|
235
221
|
const reacting = state.mode === 'react';
|
|
236
|
-
const composerRows = composing || reacting ?
|
|
222
|
+
const composerRows = composing || reacting ? 2 : 0;
|
|
237
223
|
const bodyTop = right.row + headerRows + 1;
|
|
238
224
|
const bodyBottom = right.row + right.height - 1 - composerRows;
|
|
239
225
|
const bodyRect = { row: bodyTop, col: right.col, width: right.width, height: Math.max(0, bodyBottom - bodyTop + 1) };
|
|
@@ -269,7 +255,7 @@ export function render(state, draw, content) {
|
|
|
269
255
|
}
|
|
270
256
|
emptyState(draw, content, {
|
|
271
257
|
headline: 'All caught up',
|
|
272
|
-
secondary: ['No messages in your inbox.', '
|
|
258
|
+
secondary: ['No messages in your inbox.', 'Refresh to check again.'],
|
|
273
259
|
});
|
|
274
260
|
return;
|
|
275
261
|
}
|
|
@@ -290,23 +276,23 @@ const reactMode = (s) => s.mode === 'react';
|
|
|
290
276
|
/** @type {import('../../core/view/contract.js').KeyBinding<InboxState>[]} */
|
|
291
277
|
export const keymap = [
|
|
292
278
|
// list
|
|
293
|
-
{
|
|
294
|
-
{
|
|
295
|
-
{
|
|
296
|
-
{
|
|
297
|
-
{
|
|
298
|
-
{
|
|
299
|
-
{
|
|
300
|
-
{
|
|
301
|
-
{
|
|
302
|
-
{
|
|
279
|
+
{ bindingId: 'crtr.view.inbox.down', intent: 'cursorDown', when: listMode, hint: { label: 'move' } },
|
|
280
|
+
{ bindingId: 'crtr.view.inbox.up', intent: 'cursorUp', when: listMode },
|
|
281
|
+
{ bindingId: 'crtr.view.inbox.open', intent: 'open', payload: (s) => s.cursor, when: listMode, hint: { label: 'open' } },
|
|
282
|
+
{ bindingId: 'crtr.view.inbox.reply', intent: 'startReply', when: listMode, hint: { label: 'reply' } },
|
|
283
|
+
{ bindingId: 'crtr.view.inbox.react', intent: 'startReact', when: listMode, hint: { label: 'react' } },
|
|
284
|
+
{ bindingId: 'crtr.view.inbox.filter', intent: 'cycleFilter', when: listMode, hint: { label: 'filter' } },
|
|
285
|
+
{ bindingId: 'crtr.view.inbox.refresh', intent: 'refresh', when: listMode, hint: { label: 'refresh' } },
|
|
286
|
+
{ bindingId: 'crtr.view.inbox.connect-linkedin', intent: 'connectLinkedin', when: listMode },
|
|
287
|
+
{ bindingId: 'crtr.view.inbox.connect-gmail', intent: 'connectGmail', when: listMode, hint: { label: 'connect' } },
|
|
288
|
+
{ bindingId: 'crtr.view.inbox.quit', intent: 'quit', when: listMode, hint: { label: 'quit' } },
|
|
303
289
|
// reply
|
|
304
|
-
{ capture: 'setDraft', when: replyMode, hint: {
|
|
305
|
-
{
|
|
306
|
-
{
|
|
290
|
+
{ capture: 'setDraft', when: replyMode, hint: { label: 'reply' } },
|
|
291
|
+
{ bindingId: 'crtr.view.inbox.submit-reply', intent: 'submitReply', when: replyMode, hint: { label: 'send' } },
|
|
292
|
+
{ bindingId: 'crtr.view.inbox.cancel', intent: 'cancelCompose', when: replyMode, hint: { label: 'cancel' } },
|
|
307
293
|
// react
|
|
308
|
-
{
|
|
309
|
-
{
|
|
310
|
-
{
|
|
311
|
-
{
|
|
294
|
+
{ bindingId: 'crtr.view.inbox.reaction-previous', intent: 'reactPrev', when: reactMode, hint: { label: 'pick' } },
|
|
295
|
+
{ bindingId: 'crtr.view.inbox.reaction-next', intent: 'reactNext', when: reactMode },
|
|
296
|
+
{ bindingId: 'crtr.view.inbox.submit-reaction', intent: 'submitReact', when: reactMode, hint: { label: 'react' } },
|
|
297
|
+
{ bindingId: 'crtr.view.inbox.cancel', intent: 'cancelCompose', when: reactMode, hint: { label: 'cancel' } },
|
|
312
298
|
];
|
|
@@ -274,9 +274,8 @@ function renderThreadBody(state, draw, rect, reactTarget) {
|
|
|
274
274
|
|
|
275
275
|
/** The compose bar (reply mode). @param {LiState} state @param {Draw} draw @param {Rect} right */
|
|
276
276
|
function renderComposer(state, draw, right) {
|
|
277
|
-
const hairRow = right.row + right.height -
|
|
278
|
-
const barRow = right.row + right.height -
|
|
279
|
-
const hintRow = right.row + right.height - 1;
|
|
277
|
+
const hairRow = right.row + right.height - 2;
|
|
278
|
+
const barRow = right.row + right.height - 1;
|
|
280
279
|
draw.hline(hairRow, right.col, right.col + right.width);
|
|
281
280
|
const label = '✎ Reply ';
|
|
282
281
|
const labelW = Array.from(label).length;
|
|
@@ -289,18 +288,12 @@ function renderComposer(state, draw, right) {
|
|
|
289
288
|
{ text: shown },
|
|
290
289
|
{ text: '█', style: { fg: '33' } },
|
|
291
290
|
], right.width);
|
|
292
|
-
draw.spans(hintRow, right.col, [
|
|
293
|
-
{ text: 'enter', style: { bold: true } }, { text: ' send', style: { dim: true } },
|
|
294
|
-
{ text: ' · ', style: { dim: true } },
|
|
295
|
-
{ text: 'esc', style: { bold: true } }, { text: ' cancel', style: { dim: true } },
|
|
296
|
-
], right.width);
|
|
297
291
|
}
|
|
298
292
|
|
|
299
293
|
/** The react picker bar (react mode). @param {LiState} state @param {Draw} draw @param {Rect} right */
|
|
300
294
|
function renderReactBar(state, draw, right) {
|
|
301
|
-
const hairRow = right.row + right.height -
|
|
302
|
-
const barRow = right.row + right.height -
|
|
303
|
-
const hintRow = right.row + right.height - 1;
|
|
295
|
+
const hairRow = right.row + right.height - 2;
|
|
296
|
+
const barRow = right.row + right.height - 1;
|
|
304
297
|
draw.hline(hairRow, right.col, right.col + right.width);
|
|
305
298
|
/** @type {Span[]} */
|
|
306
299
|
const spans = [{ text: '☺ React ', style: { fg: '33', bold: true } }];
|
|
@@ -313,13 +306,6 @@ function renderReactBar(state, draw, right) {
|
|
|
313
306
|
}
|
|
314
307
|
});
|
|
315
308
|
draw.spans(barRow, right.col, spans, right.width);
|
|
316
|
-
draw.spans(hintRow, right.col, [
|
|
317
|
-
{ text: '←/→', style: { bold: true } }, { text: ' pick', style: { dim: true } },
|
|
318
|
-
{ text: ' · ', style: { dim: true } },
|
|
319
|
-
{ text: 'enter', style: { bold: true } }, { text: ' react', style: { dim: true } },
|
|
320
|
-
{ text: ' · ', style: { dim: true } },
|
|
321
|
-
{ text: 'esc', style: { bold: true } }, { text: ' cancel', style: { dim: true } },
|
|
322
|
-
], right.width);
|
|
323
309
|
}
|
|
324
310
|
|
|
325
311
|
/** Right pane — the open thread + the compose/react bar when in a mode. @param {LiState} state @param {Draw} draw @param {Rect} right */
|
|
@@ -331,7 +317,7 @@ function renderThread(state, draw, right) {
|
|
|
331
317
|
centeredStack(draw, right, [
|
|
332
318
|
[{ text: '✉ ', style: { dim: true } }, { text: 'No conversation open', style: { dim: true } }],
|
|
333
319
|
[{ text: '' }],
|
|
334
|
-
[{ text: '
|
|
320
|
+
[{ text: 'Open a conversation to view it.', style: { dim: true } }],
|
|
335
321
|
]);
|
|
336
322
|
return;
|
|
337
323
|
}
|
|
@@ -345,7 +331,7 @@ function renderThread(state, draw, right) {
|
|
|
345
331
|
|
|
346
332
|
const composing = state.mode === 'reply';
|
|
347
333
|
const reacting = state.mode === 'react';
|
|
348
|
-
const composerRows = composing || reacting ?
|
|
334
|
+
const composerRows = composing || reacting ? 2 : 0;
|
|
349
335
|
const bodyTop = right.row + 2;
|
|
350
336
|
const bodyBottom = right.row + right.height - 1 - composerRows;
|
|
351
337
|
const bodyRect = { row: bodyTop, col: right.col, width: right.width, height: Math.max(0, bodyBottom - bodyTop + 1) };
|
|
@@ -380,7 +366,7 @@ export function render(state, draw, content) {
|
|
|
380
366
|
}
|
|
381
367
|
emptyState(draw, content, {
|
|
382
368
|
headline: 'All caught up',
|
|
383
|
-
secondary: ['No conversations in your inbox.', '
|
|
369
|
+
secondary: ['No conversations in your inbox.', 'Refresh to check again.'],
|
|
384
370
|
});
|
|
385
371
|
return;
|
|
386
372
|
}
|
|
@@ -403,25 +389,25 @@ const isComposeOrReact = (s) => s.mode === 'reply' || s.mode === 'react';
|
|
|
403
389
|
/**
|
|
404
390
|
* Input→intent map. List-mode navigation + entry into compose/react; the compose
|
|
405
391
|
* bar uses a `capture` binding (the host's line-editor dispatches setDraft with
|
|
406
|
-
* the next draft)
|
|
407
|
-
*
|
|
392
|
+
* the next draft). Footer hints come from these `hint` fields (single source of
|
|
393
|
+
* truth).
|
|
408
394
|
* @type {import('../../core/view/contract.js').KeyBinding<LiState>[]}
|
|
409
395
|
*/
|
|
410
396
|
export const keymap = [
|
|
411
397
|
// ── List mode ──
|
|
412
|
-
{
|
|
413
|
-
{
|
|
414
|
-
{
|
|
415
|
-
{
|
|
416
|
-
{
|
|
417
|
-
{
|
|
418
|
-
{
|
|
419
|
-
// ── Reply mode ──
|
|
420
|
-
{ capture: 'setDraft', when: isReply },
|
|
421
|
-
{
|
|
422
|
-
{
|
|
398
|
+
{ bindingId: 'crtr.view.linkedin.down', intent: 'cursorDown', when: isList, hint: { label: 'move' } },
|
|
399
|
+
{ bindingId: 'crtr.view.linkedin.up', intent: 'cursorUp', when: isList },
|
|
400
|
+
{ bindingId: 'crtr.view.linkedin.open', intent: 'openThread', payload: (s) => s.convCursor, when: isList, hint: { label: 'open' } },
|
|
401
|
+
{ bindingId: 'crtr.view.linkedin.reply', intent: 'startReply', when: isList, hint: { label: 'reply' } },
|
|
402
|
+
{ bindingId: 'crtr.view.linkedin.react', intent: 'startReact', when: isList, hint: { label: 'react' } },
|
|
403
|
+
{ bindingId: 'crtr.view.linkedin.refresh', intent: 'refresh', when: isList, hint: { label: 'refresh' } },
|
|
404
|
+
{ bindingId: 'crtr.view.linkedin.quit', intent: 'quit', when: isList, hint: { label: 'quit' } },
|
|
405
|
+
// ── Reply mode ──
|
|
406
|
+
{ capture: 'setDraft', when: isReply, hint: { label: 'reply' } },
|
|
407
|
+
{ bindingId: 'crtr.view.linkedin.submit-reply', intent: 'submitReply', when: isReply, hint: { label: 'send' } },
|
|
408
|
+
{ bindingId: 'crtr.view.linkedin.cancel', intent: 'cancelCompose', when: isComposeOrReact, hint: { label: 'cancel' } },
|
|
423
409
|
// ── React mode ──
|
|
424
|
-
{
|
|
425
|
-
{
|
|
426
|
-
{
|
|
410
|
+
{ bindingId: 'crtr.view.linkedin.reaction-previous', intent: 'reactPrev', when: isReact, hint: { label: 'pick' } },
|
|
411
|
+
{ bindingId: 'crtr.view.linkedin.reaction-next', intent: 'reactNext', when: isReact },
|
|
412
|
+
{ bindingId: 'crtr.view.linkedin.submit-reaction', intent: 'submitReact', when: isReact, hint: { label: 'react' } },
|
|
427
413
|
];
|
|
@@ -107,14 +107,14 @@ export function render(state, draw, content) {
|
|
|
107
107
|
return;
|
|
108
108
|
}
|
|
109
109
|
if (state.review.length === 0) {
|
|
110
|
-
emptyState(draw, content, { headline: 'No prompt layers', secondary: ['
|
|
110
|
+
emptyState(draw, content, { headline: 'No prompt layers', secondary: ['Refresh to try again.'] });
|
|
111
111
|
return;
|
|
112
112
|
}
|
|
113
113
|
|
|
114
114
|
const headerRows = 3;
|
|
115
115
|
draw.text(content.row, content.col, 'Prompt Studio', { bold: true });
|
|
116
116
|
draw.text(content.row + 1, content.col, configHeadline(state.config), { dim: true });
|
|
117
|
-
draw.text(content.row + 2, content.col, `
|
|
117
|
+
draw.text(content.row + 2, content.col, `Layer navigation · comparison · raw/layered · export · refresh${state.lastExportPath ? ` · ${state.lastExportPath}` : ''}`, { dim: true });
|
|
118
118
|
|
|
119
119
|
const body = { row: content.row + headerRows + 1, col: content.col, width: content.width, height: Math.max(0, content.height - headerRows - 1) };
|
|
120
120
|
const cols = draw.columns(body, [3, 5, 4]);
|
|
@@ -184,13 +184,13 @@ export function render(state, draw, content) {
|
|
|
184
184
|
}
|
|
185
185
|
|
|
186
186
|
export const keymap = [
|
|
187
|
-
{
|
|
188
|
-
{
|
|
189
|
-
{
|
|
190
|
-
{
|
|
191
|
-
{
|
|
192
|
-
{
|
|
193
|
-
{
|
|
194
|
-
{
|
|
195
|
-
{
|
|
187
|
+
{ bindingId: 'crtr.view.prompt-review.down', intent: 'cursorDown', hint: { label: 'layer' } },
|
|
188
|
+
{ bindingId: 'crtr.view.prompt-review.up', intent: 'cursorUp' },
|
|
189
|
+
{ bindingId: 'crtr.view.prompt-review.compare-previous', intent: 'compareCursorUp', hint: { label: 'compare' } },
|
|
190
|
+
{ bindingId: 'crtr.view.prompt-review.compare-next', intent: 'compareCursorDown' },
|
|
191
|
+
{ bindingId: 'crtr.view.prompt-review.toggle-mode', intent: 'setViewMode', payload: (s) => (s.viewMode === 'raw' ? 'layered' : 'raw'), hint: { label: 'raw/layered' } },
|
|
192
|
+
{ bindingId: 'crtr.view.prompt-review.expand', intent: 'toggleLayerExpanded', payload: (s) => s.selectedLayerId || (s.review && s.review[s.cursor] && s.review[s.cursor].id) || '', hint: { label: 'expand' } },
|
|
193
|
+
{ bindingId: 'crtr.view.prompt-review.export', intent: 'exportComments', hint: { label: 'export' } },
|
|
194
|
+
{ bindingId: 'crtr.view.prompt-review.refresh', intent: 'refresh', hint: { label: 'refresh' } },
|
|
195
|
+
{ bindingId: 'crtr.view.prompt-review.quit', intent: 'quit', hint: { label: 'quit' } },
|
|
196
196
|
];
|