@north-light/crouter 0.3.170 → 0.3.171
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/build-root.d.ts +1 -16
- package/dist/build-root.js +1 -25
- package/dist/builtin-memory/insights/capture.md +66 -0
- package/dist/builtin-memory/insights/init.md +45 -0
- package/dist/builtin-memory/internal/plugins.md +12 -11
- package/dist/cli.js +2 -6
- package/dist/clients/attach/__tests__/attach-keybindings.test.js +7 -1
- package/dist/clients/attach/__tests__/context-message.test.js +50 -3
- package/dist/clients/attach/__tests__/diagram.test.js +1 -1
- package/dist/clients/attach/chrome/header.d.ts +10 -0
- package/dist/clients/attach/chrome/header.js +35 -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 +7 -18
- package/dist/clients/attach/input/controller.d.ts +10 -0
- package/dist/clients/attach/input/controller.js +3 -0
- package/dist/clients/attach/overlays/help.d.ts +33 -0
- package/dist/clients/attach/overlays/help.js +204 -0
- package/dist/clients/attach/render/chat-view.d.ts +81 -9
- package/dist/clients/attach/render/chat-view.js +434 -90
- package/dist/clients/attach/render/context-message.js +3 -3
- package/dist/clients/attach/render/diagram.js +2 -2
- package/dist/clients/attach/render/measured-container.d.ts +94 -0
- package/dist/clients/attach/render/measured-container.js +307 -0
- 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 +160 -0
- package/dist/clients/attach/session/bindings.d.ts +5 -1
- package/dist/clients/attach/session/bindings.js +16 -0
- package/dist/clients/attach/session/context.d.ts +5 -3
- package/dist/clients/attach/session/frame.d.ts +34 -0
- package/dist/clients/attach/session/frame.js +173 -0
- package/dist/clients/attach/session/frames.d.ts +2 -0
- package/dist/clients/attach/session/frames.js +3 -0
- package/dist/clients/attach/session/input-wiring.d.ts +10 -4
- package/dist/clients/attach/session/input-wiring.js +13 -13
- package/dist/clients/attach/session/keys.d.ts +4 -1
- package/dist/clients/attach/session/keys.js +9 -4
- package/dist/clients/attach/session/layout.d.ts +10 -8
- package/dist/clients/attach/session/layout.js +32 -31
- package/dist/clients/attach/session/mouse.d.ts +15 -0
- package/dist/clients/attach/session/mouse.js +61 -0
- package/dist/clients/attach/slash/dispatch.d.ts +10 -0
- package/dist/clients/attach/slash/dispatch.js +35 -2
- package/dist/clients/attach/viewer.js +605 -581
- package/dist/clients/web/dev-server.d.ts +4 -1
- package/dist/clients/web/dev-server.js +8 -19
- package/dist/clients/web/web-cmd.js +1 -1
- package/dist/commands/human/prompts.js +2 -1
- package/dist/commands/pkg/plugin-manage.js +143 -68
- package/dist/commands/sys/__tests__/setup-core.test.js +4 -2
- package/dist/commands/sys/setup-core.js +21 -16
- 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.js +2 -1
- package/dist/core/keybindings/__tests__/resolve.test.js +4 -0
- package/dist/core/keybindings/attach-control.d.ts +3 -0
- package/dist/core/keybindings/attach-control.js +1 -0
- package/dist/core/keybindings/catalog.d.ts +2 -2
- package/dist/core/keybindings/catalog.js +8 -0
- package/dist/core/runtime/broker-protocol.d.ts +13 -1
- package/dist/core/runtime/broker.js +213 -5
- package/dist/core/runtime/canvas-extensions.d.ts +3 -0
- package/dist/core/runtime/canvas-extensions.js +3 -0
- package/dist/core/runtime/front-door.js +4 -4
- package/dist/core/runtime/kickoff.d.ts +1 -5
- package/dist/core/runtime/kickoff.js +1 -5
- package/dist/core/runtime/naming.d.ts +7 -0
- package/dist/core/runtime/naming.js +5 -2
- package/dist/core/runtime/node-read.js +15 -0
- package/dist/core/runtime/package-health.d.ts +6 -21
- package/dist/core/runtime/package-health.js +12 -123
- package/dist/core/runtime/stop-guard.d.ts +1 -1
- package/dist/core/runtime/stop-guard.js +3 -3
- package/dist/core/runtime/tool-group-summary.d.ts +19 -0
- package/dist/core/runtime/tool-group-summary.js +117 -0
- package/dist/core/termrender/code-doc.d.ts +12 -0
- package/dist/core/termrender/code-doc.js +91 -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/user-settings.d.ts +14 -0
- package/dist/core/user-settings.js +20 -0
- package/dist/daemon/crtrd.js +5 -6
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- 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-stophook.js +2 -2
- package/dist/pi-extensions/canvas-structured-output.js +52 -8
- package/dist/pi-extensions/summary-tool.d.ts +33 -0
- package/dist/pi-extensions/summary-tool.js +62 -0
- package/dist/shared/generated-context.d.ts +19 -4
- package/dist/shared/generated-context.js +98 -7
- package/dist/shared/tool-groups.d.ts +23 -0
- package/dist/shared/tool-groups.js +60 -0
- package/dist/types.d.ts +11 -0
- package/dist/types.js +1 -0
- package/dist/web-client/assets/{index-U_NZ66VE.js → index-D7I3gHCL.js} +26 -19
- package/dist/web-client/index.html +1 -1
- package/dist/web-client/sw.js +1 -1
- package/package.json +3 -1
- package/runtime.lock.json +62 -3
- package/scripts/postinstall.mjs +14 -1
- package/dist/core/command-plugins/store.d.ts +0 -16
- package/dist/core/command-plugins/store.js +0 -64
- package/dist/core/runtime/fault-recovery-nudge.d.ts +0 -3
- package/dist/core/runtime/fault-recovery-nudge.js +0 -3
|
@@ -1,28 +1,25 @@
|
|
|
1
|
-
// session/layout.ts — the viewer's visual skeleton:
|
|
2
|
-
//
|
|
1
|
+
// session/layout.ts — the viewer's visual skeleton: its containers, editor,
|
|
2
|
+
// and one full-pane frame.
|
|
3
3
|
//
|
|
4
4
|
// Pure construction. Nothing here reads session state, talks to the socket, or
|
|
5
5
|
// handles a key — a reader chasing behavior can skip this file entirely, and a
|
|
6
6
|
// reader asking "where does this line get painted?" needs only this one.
|
|
7
7
|
//
|
|
8
|
-
// The render order is
|
|
9
|
-
//
|
|
10
|
-
// widgets · editor · picker panel · below-editor
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
|
|
14
|
-
// transcript exceeded one screen, which is why the mode badge and the two
|
|
15
|
-
// canvas panels sit directly above the queued panel rather than at the top.
|
|
16
|
-
import { Container, ProcessTerminal, TUI } from '@earendil-works/pi-tui';
|
|
8
|
+
// The render order is asserted in ONE place (`ViewerFrame`'s bottom list).
|
|
9
|
+
// Top to bottom: pinned recap header · virtualized chat · rule · queued ·
|
|
10
|
+
// above-editor extension widgets · editor · picker panel · below-editor
|
|
11
|
+
// extension widgets · roster · status bar. The frame owns the pane rectangle,
|
|
12
|
+
// so every region remains on screen and none can enter terminal scrollback.
|
|
13
|
+
import { Container } from '@earendil-works/pi-tui';
|
|
17
14
|
import { DynamicBorder, getSelectListTheme } from '@earendil-works/pi-coding-agent';
|
|
18
15
|
import { TitledEditor } from '../input/titled-editor.js';
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
const tui = new TUI(procTerminal);
|
|
16
|
+
import { ViewerFrame } from './frame.js';
|
|
17
|
+
/** Build the layout around the transcript viewport. The viewer creates the
|
|
18
|
+
* terminal and chat first because the viewport measures that chat source. */
|
|
19
|
+
export function createLayout(procTerminal, tui, chat, pal, km, viewport) {
|
|
24
20
|
const containers = {
|
|
25
|
-
chat
|
|
21
|
+
chat,
|
|
22
|
+
header: new Container(),
|
|
26
23
|
chrome: new Container(),
|
|
27
24
|
aboveWidgets: new Container(),
|
|
28
25
|
reports: new Container(),
|
|
@@ -31,26 +28,30 @@ export function createLayout(pal, km) {
|
|
|
31
28
|
belowWidgets: new Container(),
|
|
32
29
|
footer: new Container(),
|
|
33
30
|
};
|
|
34
|
-
// The themed rule dividing the
|
|
35
|
-
// `renderExtensionWidgets` re-derives this container
|
|
36
|
-
// widget change (the passive recap replaces the rule), so this is the seed,
|
|
37
|
-
// not the sole writer.
|
|
31
|
+
// The themed rule dividing the transcript from the fixed cockpit below.
|
|
32
|
+
// `renderExtensionWidgets` re-derives this container on every widget change.
|
|
38
33
|
containers.chrome.addChild(new DynamicBorder(pal.border));
|
|
39
34
|
const editorTheme = { borderColor: pal.border, selectList: getSelectListTheme() };
|
|
40
35
|
// TitledEditor paints the session name into its top border (solid-background
|
|
41
36
|
// chip) and tracks a thinking-level border color, both set from live state.
|
|
42
37
|
const editor = new TitledEditor(tui, editorTheme, km, { paddingX: 1 });
|
|
38
|
+
const frame = new ViewerFrame(procTerminal, {
|
|
39
|
+
header: containers.header,
|
|
40
|
+
viewport,
|
|
41
|
+
bottom: [
|
|
42
|
+
{ slot: 'chrome', component: containers.chrome },
|
|
43
|
+
{ slot: 'queued', component: containers.queued },
|
|
44
|
+
{ slot: 'aboveWidgets', component: containers.aboveWidgets },
|
|
45
|
+
{ slot: 'editor', component: editor },
|
|
46
|
+
{ slot: 'pickerPanel', component: containers.pickerPanel },
|
|
47
|
+
{ slot: 'belowWidgets', component: containers.belowWidgets },
|
|
48
|
+
{ slot: 'reports', component: containers.reports },
|
|
49
|
+
{ slot: 'footer', component: containers.footer },
|
|
50
|
+
],
|
|
51
|
+
});
|
|
43
52
|
const mount = () => {
|
|
44
|
-
tui.addChild(
|
|
45
|
-
tui.addChild(containers.chrome);
|
|
46
|
-
tui.addChild(containers.queued);
|
|
47
|
-
tui.addChild(containers.aboveWidgets);
|
|
48
|
-
tui.addChild(editor);
|
|
49
|
-
tui.addChild(containers.pickerPanel);
|
|
50
|
-
tui.addChild(containers.belowWidgets);
|
|
51
|
-
tui.addChild(containers.reports);
|
|
52
|
-
tui.addChild(containers.footer);
|
|
53
|
+
tui.addChild(frame);
|
|
53
54
|
tui.setFocus(editor);
|
|
54
55
|
};
|
|
55
|
-
return { procTerminal, tui, containers, editor, mount };
|
|
56
|
+
return { procTerminal, tui, containers, editor, frame, mount };
|
|
56
57
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { TUI } from '@earendil-works/pi-tui';
|
|
2
|
+
/** Lines moved for one terminal wheel notch. */
|
|
3
|
+
export declare const WHEEL_LINES = 3;
|
|
4
|
+
export interface MouseReporting {
|
|
5
|
+
/** Reporting currently on. */
|
|
6
|
+
enabled(): boolean;
|
|
7
|
+
/** Flip, write the DECSET/DECRST, return the new state. */
|
|
8
|
+
toggle(): boolean;
|
|
9
|
+
/** Turn off unconditionally — teardown and process exit. */
|
|
10
|
+
disable(): void;
|
|
11
|
+
/** Install the SGR listener; returns the remover. */
|
|
12
|
+
install(tui: TUI, onWheel: (direction: 'up' | 'down', lines: number) => void): () => void;
|
|
13
|
+
}
|
|
14
|
+
/** Create per-viewer mouse reporting, seeded from tmux's current mouse option. */
|
|
15
|
+
export declare function createMouseReporting(write: (data: string) => void): MouseReporting;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
// session/mouse.ts — terminal mouse reporting for the attach viewport.
|
|
2
|
+
//
|
|
3
|
+
// Mouse reporting is enabled only when tmux already has mouse on. Outside tmux
|
|
4
|
+
// we leave native terminal selection alone; inside, every mouse sequence is
|
|
5
|
+
// consumed so clicks and drags can never become editor input.
|
|
6
|
+
import { spawnSync } from 'node:child_process';
|
|
7
|
+
import { inTmux } from '../../../core/runtime/placement-tmux.js';
|
|
8
|
+
const MOUSE_REPORTING_ON = '\x1b[?1002h\x1b[?1006h';
|
|
9
|
+
const MOUSE_REPORTING_OFF = '\x1b[?1002l\x1b[?1006l';
|
|
10
|
+
/** Lines moved for one terminal wheel notch. */
|
|
11
|
+
export const WHEEL_LINES = 3;
|
|
12
|
+
function detectMouseDefault() {
|
|
13
|
+
if (!inTmux())
|
|
14
|
+
return false;
|
|
15
|
+
const result = spawnSync('tmux', ['show-options', '-gv', 'mouse'], { encoding: 'utf8' });
|
|
16
|
+
return result.status === 0 && result.stdout?.trim() === 'on';
|
|
17
|
+
}
|
|
18
|
+
/** Create per-viewer mouse reporting, seeded from tmux's current mouse option. */
|
|
19
|
+
export function createMouseReporting(write) {
|
|
20
|
+
let isEnabled = detectMouseDefault();
|
|
21
|
+
if (isEnabled)
|
|
22
|
+
write(MOUSE_REPORTING_ON);
|
|
23
|
+
const disable = () => {
|
|
24
|
+
isEnabled = false;
|
|
25
|
+
try {
|
|
26
|
+
write(MOUSE_REPORTING_OFF);
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
// Terminal restoration is best-effort, including during process exit.
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
process.once('exit', disable);
|
|
33
|
+
return {
|
|
34
|
+
enabled: () => isEnabled,
|
|
35
|
+
toggle: () => {
|
|
36
|
+
isEnabled = !isEnabled;
|
|
37
|
+
write(isEnabled ? MOUSE_REPORTING_ON : MOUSE_REPORTING_OFF);
|
|
38
|
+
return isEnabled;
|
|
39
|
+
},
|
|
40
|
+
disable,
|
|
41
|
+
install: (tui, onWheel) => tui.addInputListener((data) => {
|
|
42
|
+
let consumed = false;
|
|
43
|
+
const withoutSgr = data.replace(/\x1b\[<(\d+);\d+;\d+[Mm]/g, (_match, buttons) => {
|
|
44
|
+
consumed = true;
|
|
45
|
+
const button = Number(buttons) & ~(4 | 8 | 16);
|
|
46
|
+
if (button === 64)
|
|
47
|
+
onWheel('up', WHEEL_LINES);
|
|
48
|
+
if (button === 65)
|
|
49
|
+
onWheel('down', WHEEL_LINES);
|
|
50
|
+
return '';
|
|
51
|
+
});
|
|
52
|
+
const withoutMouse = withoutSgr.replace(/\x1b\[M[\s\S]{3}/g, () => {
|
|
53
|
+
consumed = true;
|
|
54
|
+
return '';
|
|
55
|
+
});
|
|
56
|
+
if (!consumed)
|
|
57
|
+
return undefined;
|
|
58
|
+
return withoutMouse.length > 0 ? { data: withoutMouse } : { consume: true };
|
|
59
|
+
}),
|
|
60
|
+
};
|
|
61
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { AutocompleteItem, SlashCommand } from '@earendil-works/pi-tui';
|
|
2
|
+
import type { CopyTarget } from '../render/transcript-copy.js';
|
|
2
3
|
import type { BrokerSnapshot, ClientRole, ClientToBroker } from '../../../core/runtime/broker-protocol.js';
|
|
3
4
|
/** Everything a slash handler needs: the frame sink, a notice sink, the latest
|
|
4
5
|
* engine state (for read-only commands like `/session`), and the cwd (for the
|
|
@@ -38,6 +39,8 @@ export interface SlashContext {
|
|
|
38
39
|
* registers `/graph` as a stub that calls this hook. Unit C/Q owns the overlay
|
|
39
40
|
* itself and populates this; when absent `/graph` notifies it isn't wired yet. */
|
|
40
41
|
onGraph?: () => void;
|
|
42
|
+
/** Toggle the in-process Help overlay from `/hotkeys`. */
|
|
43
|
+
onHelp?: () => void;
|
|
41
44
|
/** Detach this viewer (`/quit`). A viewer quit DETACHES — it tears down the
|
|
42
45
|
* local TUI/socket and leaves the shared engine running. Wired by viewer.ts to
|
|
43
46
|
* `teardown('detach')`; without it `/quit` cannot actually exit (just sending
|
|
@@ -47,6 +50,8 @@ export interface SlashContext {
|
|
|
47
50
|
/** Copy the last assistant message to the clipboard (`/copy`). Wired by
|
|
48
51
|
* viewer.ts (it owns the ChatView that holds the text + the clipboard writer). */
|
|
49
52
|
onCopy?: () => void;
|
|
53
|
+
/** Copy one viewer-local transcript target. */
|
|
54
|
+
onCopyTarget?: (target: CopyTarget) => void;
|
|
50
55
|
/** OPTIONAL native-picker openers — the InputController fills these from its
|
|
51
56
|
* read-op request channel. When absent (channel unwired) the value-picker
|
|
52
57
|
* builtins fall back to the text-arg notice. */
|
|
@@ -75,6 +80,11 @@ export interface SlashContext {
|
|
|
75
80
|
* viewer-local render state (ChatView owns it), so it is safe for an observer
|
|
76
81
|
* and for a remote attach. Absent → the command notifies it is unwired. */
|
|
77
82
|
onFoldTools?: () => void;
|
|
83
|
+
/** Toggle mouse wheel scrolling reporting on/off. Viewer-local render state
|
|
84
|
+
* (MouseReporting owns the reporting state and the hardware toggle), so it is
|
|
85
|
+
* safe for an observer and for a remote attach. Absent → the command notifies
|
|
86
|
+
* it is unwired. */
|
|
87
|
+
onToggleMouse?: () => void;
|
|
78
88
|
}
|
|
79
89
|
/** Canvas slash-commands — implemented NATIVE in the viewer, the ONLY live host:
|
|
80
90
|
* the broker binds mode:'rpc', so broker pi-extensions never run their
|
|
@@ -42,6 +42,10 @@ export const CANVAS_SLASH_COMMANDS = [
|
|
|
42
42
|
{ name: 'mcp', description: 'MCP servers — status from the adapter’s config/cache; Enter (or /mcp <server>) connects, reconnects, or re-auths' },
|
|
43
43
|
{ name: 'rename', description: 'Rename this node everywhere — node row, viewer window, session chip, tmux pane: /rename <name>' },
|
|
44
44
|
{ name: 'color', description: 'Color this node — tmux pane outline + name chip: /color <color> (red, blue, #rrggbb, …) or /color none' },
|
|
45
|
+
{ name: 'mouse', description: 'Toggle mouse wheel scrolling — off restores tmux drag-select and copy-mode mouse' },
|
|
46
|
+
{ name: 'copy-message', description: 'Copy the newest message visible in the transcript viewport' },
|
|
47
|
+
{ name: 'copy-tool', description: 'Copy the newest tool call output' },
|
|
48
|
+
{ name: 'copy-transcript', description: 'Copy every copyable transcript block' },
|
|
45
49
|
{ name: 'fold-tools', description: 'View setting — fold every FINISHED tool call to its single call line (Alt+C → w → f)' },
|
|
46
50
|
];
|
|
47
51
|
const CANVAS_NAMES = new Set(CANVAS_SLASH_COMMANDS.map((c) => c.name));
|
|
@@ -301,10 +305,14 @@ export function dispatchSlashCommand(text, ctx) {
|
|
|
301
305
|
case 'changelog':
|
|
302
306
|
ctx.notify('/changelog: see https://github.com/earendil-works/pi for release notes (no bundled changelog in this build)');
|
|
303
307
|
return true;
|
|
304
|
-
// --- recognized builtins with no Phase-4 wire path --------------------
|
|
305
308
|
case 'hotkeys':
|
|
306
|
-
ctx.
|
|
309
|
+
if (ctx.onHelp) {
|
|
310
|
+
ctx.onHelp();
|
|
311
|
+
return true;
|
|
312
|
+
}
|
|
313
|
+
ctx.notify('/hotkeys is not available in this viewer');
|
|
307
314
|
return true;
|
|
315
|
+
// --- recognized builtins with no Phase-4 wire path --------------------
|
|
308
316
|
default:
|
|
309
317
|
// A builtin we listed but didn't special-case: treat as unsupported
|
|
310
318
|
// rather than leak it to the LLM as literal text.
|
|
@@ -402,6 +410,31 @@ function dispatchCanvasCommand(name, arg, ctx) {
|
|
|
402
410
|
else
|
|
403
411
|
ctx.notify('The fold-tools view setting is not wired into this viewer');
|
|
404
412
|
return true;
|
|
413
|
+
case 'mouse':
|
|
414
|
+
// Viewer-local render state only — no frame, no CLI, no node mutation.
|
|
415
|
+
if (ctx.onToggleMouse)
|
|
416
|
+
ctx.onToggleMouse();
|
|
417
|
+
else
|
|
418
|
+
ctx.notify('Mouse reporting is not wired into this viewer');
|
|
419
|
+
return true;
|
|
420
|
+
case 'copy-message':
|
|
421
|
+
if (ctx.onCopyTarget)
|
|
422
|
+
ctx.onCopyTarget('message');
|
|
423
|
+
else
|
|
424
|
+
ctx.notify('/copy-message is not available in this viewer');
|
|
425
|
+
return true;
|
|
426
|
+
case 'copy-tool':
|
|
427
|
+
if (ctx.onCopyTarget)
|
|
428
|
+
ctx.onCopyTarget('tool');
|
|
429
|
+
else
|
|
430
|
+
ctx.notify('/copy-tool is not available in this viewer');
|
|
431
|
+
return true;
|
|
432
|
+
case 'copy-transcript':
|
|
433
|
+
if (ctx.onCopyTarget)
|
|
434
|
+
ctx.onCopyTarget('transcript');
|
|
435
|
+
else
|
|
436
|
+
ctx.notify('/copy-transcript is not available in this viewer');
|
|
437
|
+
return true;
|
|
405
438
|
default:
|
|
406
439
|
ctx.notify(`/${name} is not available in attach`);
|
|
407
440
|
return true;
|