@gajae-code/coding-agent 0.11.9 → 0.11.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +56 -1
- package/dist/types/cli.d.ts +8 -1
- package/dist/types/config/model-profile-activation.d.ts +1 -0
- package/dist/types/config/model-profile-contract.d.ts +2 -0
- package/dist/types/config/model-registry.d.ts +2 -0
- package/dist/types/config/settings-schema.d.ts +57 -2
- package/dist/types/config/settings.d.ts +1 -0
- package/dist/types/extensibility/extensions/types.d.ts +21 -0
- package/dist/types/gjc-runtime/linux-proc.d.ts +22 -14
- package/dist/types/gjc-runtime/ralplan-runtime.d.ts +60 -0
- package/dist/types/gjc-runtime/state-runtime.d.ts +3 -1
- package/dist/types/gjc-runtime/state-writer.d.ts +1 -0
- package/dist/types/modes/acp/acp-agent.d.ts +14 -9
- package/dist/types/modes/components/queue-pane.d.ts +8 -0
- package/dist/types/modes/components/queued-message-selector.d.ts +6 -0
- package/dist/types/modes/components/welcome.d.ts +1 -0
- package/dist/types/modes/controllers/selector-controller.d.ts +16 -8
- package/dist/types/modes/interactive-mode.d.ts +5 -1
- package/dist/types/runtime/memory-domain.d.ts +9 -0
- package/dist/types/runtime/memory-guard-contract.d.ts +71 -0
- package/dist/types/runtime/memory-guard.d.ts +39 -0
- package/dist/types/runtime/memory-limit.d.ts +11 -0
- package/dist/types/runtime-mcp/manager.d.ts +8 -0
- package/dist/types/sdk/acp/final-text.d.ts +19 -0
- package/dist/types/sdk/acp/mcp.d.ts +23 -0
- package/dist/types/sdk/broker/lifecycle.d.ts +2 -0
- package/dist/types/sdk/bus/chat-daemon-control.d.ts +2 -1
- package/dist/types/sdk/bus/daemon-paths.d.ts +1 -0
- package/dist/types/sdk/bus/index.d.ts +11 -2
- package/dist/types/sdk/bus/operator-runtime.d.ts +1 -0
- package/dist/types/sdk/bus/rate-limit-pool.d.ts +2 -0
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +14 -7
- package/dist/types/sdk/bus/telegram-daemon.d.ts +97 -8
- package/dist/types/sdk/host/control/operations.d.ts +1 -0
- package/dist/types/sdk/lifecycle-session.d.ts +10 -1
- package/dist/types/sdk/session.d.ts +7 -0
- package/dist/types/sdk/startup-capability.d.ts +6 -0
- package/dist/types/session/agent-session.d.ts +11 -11
- package/dist/types/session/fallback-chain-controller.d.ts +18 -2
- package/dist/types/session/internal/managed-session-scope.d.ts +28 -1
- package/dist/types/session/session-manager.d.ts +34 -0
- package/dist/types/task/executor.d.ts +6 -0
- package/dist/types/task/provider-retry-status.d.ts +13 -0
- package/dist/types/task/types.d.ts +39 -0
- package/dist/types/task/ultragoal-redteam-activation.d.ts +33 -0
- package/dist/types/tools/index.d.ts +3 -1
- package/dist/types/tools/resource-gc.d.ts +19 -0
- package/package.json +8 -8
- package/scripts/generate-sdk-operation-inventory.ts +3 -0
- package/scripts/run-test-manifest.ts +6 -4
- package/src/cli/args.ts +14 -8
- package/src/cli/fast-help.ts +1 -1
- package/src/cli/notify-cli.ts +1 -0
- package/src/cli.ts +48 -2
- package/src/commands/sdk.ts +91 -7
- package/src/config/model-profile-activation.ts +1 -0
- package/src/config/model-profile-contract.ts +13 -0
- package/src/config/model-profiles.ts +31 -7
- package/src/config/model-registry.ts +4 -2
- package/src/config/settings-schema.ts +61 -2
- package/src/config/settings.ts +8 -0
- package/src/defaults/gjc/skills/ralplan/SKILL.md +21 -2
- package/src/defaults/gjc/skills/ultragoal/SKILL.md +1 -1
- package/src/eval/js/tool-bridge.ts +1 -1
- package/src/extensibility/extensions/runner.ts +4 -0
- package/src/extensibility/extensions/types.ts +14 -0
- package/src/gjc-runtime/deep-interview-ambiguity.ts +16 -2
- package/src/gjc-runtime/deep-interview-draft.ts +2 -1
- package/src/gjc-runtime/deep-interview-recorder.ts +16 -1
- package/src/gjc-runtime/deep-interview-state.ts +25 -3
- package/src/gjc-runtime/linux-proc.ts +70 -36
- package/src/gjc-runtime/ralplan-runtime.ts +252 -1
- package/src/gjc-runtime/state-runtime.ts +48 -11
- package/src/gjc-runtime/state-writer.ts +1 -1
- package/src/hooks/skill-state.ts +23 -1
- package/src/internal-urls/docs-index.generated.ts +7 -7
- package/src/internal-urls/local-protocol.ts +23 -4
- package/src/modes/acp/acp-agent.ts +554 -60
- package/src/modes/acp/acp-event-mapper.ts +93 -6
- package/src/modes/components/model-selector.ts +15 -1
- package/src/modes/components/notifications-settings-editor.ts +1 -1
- package/src/modes/components/queue-pane.ts +55 -9
- package/src/modes/components/queued-message-selector.ts +53 -10
- package/src/modes/components/tool-status-header.ts +1 -4
- package/src/modes/components/welcome.ts +18 -3
- package/src/modes/controllers/command-controller.ts +4 -0
- package/src/modes/controllers/extension-ui-controller.ts +4 -10
- package/src/modes/controllers/input-controller.ts +44 -3
- package/src/modes/controllers/selector-controller.ts +38 -8
- package/src/modes/interactive-mode.ts +65 -26
- package/src/modes/runtime-init.ts +1 -0
- package/src/prompts/agent-fragments/restricted-bash.md +1 -1
- package/src/prompts/agents/architect.md +8 -1
- package/src/prompts/agents/critic.md +8 -1
- package/src/prompts/agents/executor.md +1 -1
- package/src/prompts/agents/planner.md +8 -1
- package/src/runtime/memory-domain.ts +66 -0
- package/src/runtime/memory-guard-contract.ts +73 -0
- package/src/runtime/memory-guard.ts +292 -0
- package/src/runtime/memory-limit.ts +50 -0
- package/src/runtime-mcp/manager.ts +26 -6
- package/src/sdk/acp/adapter.ts +8 -1
- package/src/sdk/acp/final-text.ts +40 -0
- package/src/sdk/acp/mcp.ts +26 -0
- package/src/sdk/broker/identity.ts +22 -12
- package/src/sdk/broker/lifecycle.ts +82 -1
- package/src/sdk/bus/chat-daemon-cli.ts +2 -2
- package/src/sdk/bus/chat-daemon-control.ts +4 -3
- package/src/sdk/bus/config.ts +1 -1
- package/src/sdk/bus/daemon-paths.ts +2 -0
- package/src/sdk/bus/index.ts +222 -27
- package/src/sdk/bus/notification-service.ts +19 -5
- package/src/sdk/bus/operator-runtime.ts +31 -5
- package/src/sdk/bus/rate-limit-pool.ts +8 -0
- package/src/sdk/bus/telegram-cli.ts +6 -13
- package/src/sdk/bus/telegram-daemon-cli.ts +8 -3
- package/src/sdk/bus/telegram-daemon-contract.ts +15 -7
- package/src/sdk/bus/telegram-daemon-control.ts +14 -5
- package/src/sdk/bus/telegram-daemon.ts +1191 -164
- package/src/sdk/host/control/dispatch.ts +2 -0
- package/src/sdk/host/control/operations.ts +1 -0
- package/src/sdk/host/host.ts +2 -2
- package/src/sdk/lifecycle-session.ts +19 -3
- package/src/sdk/protocol/operation-inventory.generated.json +58 -0
- package/src/sdk/protocol/operation-registry.ts +1 -0
- package/src/sdk/session.ts +24 -1
- package/src/sdk/startup-capability.ts +6 -0
- package/src/session/agent-session.ts +290 -132
- package/src/session/fallback-chain-controller.ts +47 -8
- package/src/session/internal/managed-session-scope.ts +173 -127
- package/src/session/internal/native-publish-outcome.ts +8 -3
- package/src/session/session-manager.ts +608 -25
- package/src/skill-state/workflow-mutation-guard.ts +41 -7
- package/src/task/executor.ts +152 -42
- package/src/task/index.ts +2 -0
- package/src/task/provider-retry-status.ts +88 -0
- package/src/task/render.ts +45 -26
- package/src/task/types.ts +25 -11
- package/src/task/ultragoal-redteam-activation.ts +75 -0
- package/src/tools/bash-allowed-prefixes.ts +45 -1
- package/src/tools/index.ts +3 -1
- package/src/tools/resource-gc.ts +496 -120
- package/src/tools/subagent-render.ts +74 -16
- package/src/tools/subagent.ts +2 -0
- package/src/workflow/workflow-intent-diff.ts +4 -1
- package/vendor/insane-search/MANIFEST.json +4 -1
- package/vendor/insane-search/engine/bias_check.py +5 -0
- package/vendor/insane-search/engine/fetch_chain.py +67 -1
- package/vendor/insane-search/engine/transport.py +13 -2
- package/vendor/insane-search/engine/validators.py +4 -1
|
@@ -197,21 +197,26 @@ export function mapAgentSessionEventToAcpSessionUpdates(
|
|
|
197
197
|
return [toSessionNotification(sessionId, update)];
|
|
198
198
|
}
|
|
199
199
|
case "tool_execution_end": {
|
|
200
|
+
const args = getToolExecutionEndArgs(event, options);
|
|
200
201
|
const resultContent = [...extractDiffToolCallContent(event.result), ...extractToolCallContent(event.result)];
|
|
201
|
-
const content = mergeToolUpdateContent(
|
|
202
|
-
buildToolStartContent(event.toolName, getToolExecutionEndArgs(event, options)),
|
|
203
|
-
resultContent,
|
|
204
|
-
);
|
|
202
|
+
const content = mergeToolUpdateContent(buildToolStartContent(event.toolName, args), resultContent);
|
|
205
203
|
const update: SessionUpdate = {
|
|
206
204
|
sessionUpdate: "tool_call_update",
|
|
207
205
|
toolCallId: event.toolCallId,
|
|
208
206
|
status: event.isError ? "failed" : "completed",
|
|
209
207
|
rawOutput: event.result,
|
|
210
208
|
};
|
|
209
|
+
if (event.isError) {
|
|
210
|
+
update.title = `Failed: ${buildToolTitle(event.toolName, args, undefined)}`;
|
|
211
|
+
update.kind = "other";
|
|
212
|
+
}
|
|
211
213
|
if (content.length > 0) {
|
|
212
214
|
update.content = content;
|
|
213
215
|
}
|
|
214
|
-
const locations =
|
|
216
|
+
const locations = mergeToolLocations(
|
|
217
|
+
extractToolLocations(args, options.cwd),
|
|
218
|
+
extractToolLocationsFromResult(event.result, options.cwd),
|
|
219
|
+
);
|
|
215
220
|
if (locations.length > 0) {
|
|
216
221
|
update.locations = locations;
|
|
217
222
|
}
|
|
@@ -295,14 +300,83 @@ export function mapAgentSessionEventToAcpSessionUpdates(
|
|
|
295
300
|
return [toSessionNotification(sessionId, { sessionUpdate: "session_info_update", _meta: meta })];
|
|
296
301
|
}
|
|
297
302
|
case "auto_retry_start":
|
|
303
|
+
return [
|
|
304
|
+
toSessionNotification(sessionId, {
|
|
305
|
+
sessionUpdate: "session_info_update",
|
|
306
|
+
_meta: {
|
|
307
|
+
gjcPhase: "retrying",
|
|
308
|
+
gjcRetryState: "waiting",
|
|
309
|
+
gjcRetryAttempt: event.attempt,
|
|
310
|
+
gjcRetryMaxAttempts: event.maxAttempts,
|
|
311
|
+
gjcRetryDelayMs: event.delayMs,
|
|
312
|
+
gjcRetryErrorMessage: event.errorMessage,
|
|
313
|
+
gjcRetryUnbounded: event.unbounded ?? false,
|
|
314
|
+
running: true,
|
|
315
|
+
gjcRunning: true,
|
|
316
|
+
},
|
|
317
|
+
}),
|
|
318
|
+
];
|
|
298
319
|
case "auto_retry_end":
|
|
320
|
+
return [
|
|
321
|
+
toSessionNotification(sessionId, {
|
|
322
|
+
sessionUpdate: "session_info_update",
|
|
323
|
+
_meta: {
|
|
324
|
+
gjcPhase: event.success ? "responding" : "retry_failed",
|
|
325
|
+
gjcRetryState: event.success ? "succeeded" : "failed",
|
|
326
|
+
gjcRetryAttempt: event.attempt,
|
|
327
|
+
...(event.finalError ? { gjcRetryFinalError: event.finalError } : {}),
|
|
328
|
+
running: true,
|
|
329
|
+
gjcRunning: true,
|
|
330
|
+
},
|
|
331
|
+
}),
|
|
332
|
+
];
|
|
299
333
|
case "ttsr_triggered":
|
|
334
|
+
return [
|
|
335
|
+
toSessionNotification(sessionId, {
|
|
336
|
+
sessionUpdate: "session_info_update",
|
|
337
|
+
_meta: {
|
|
338
|
+
gjcTtsrTriggered: true,
|
|
339
|
+
gjcTtsrRuleCount: event.rules.length,
|
|
340
|
+
},
|
|
341
|
+
}),
|
|
342
|
+
];
|
|
300
343
|
case "irc_message":
|
|
301
344
|
case "subagent_steer_message":
|
|
345
|
+
return [];
|
|
302
346
|
case "notice":
|
|
347
|
+
return [
|
|
348
|
+
toSessionNotification(sessionId, {
|
|
349
|
+
sessionUpdate: "agent_thought_chunk",
|
|
350
|
+
content: {
|
|
351
|
+
type: "text",
|
|
352
|
+
text: `[${event.level}${event.source ? `:${event.source}` : ""}] ${event.message}\n`,
|
|
353
|
+
},
|
|
354
|
+
}),
|
|
355
|
+
];
|
|
303
356
|
case "thinking_level_changed":
|
|
357
|
+
return [
|
|
358
|
+
toSessionNotification(sessionId, {
|
|
359
|
+
sessionUpdate: "session_info_update",
|
|
360
|
+
_meta: { gjcThinkingLevel: event.thinkingLevel ?? "off" },
|
|
361
|
+
}),
|
|
362
|
+
];
|
|
304
363
|
case "goal_updated":
|
|
305
|
-
return [
|
|
364
|
+
return [
|
|
365
|
+
toSessionNotification(sessionId, {
|
|
366
|
+
sessionUpdate: "session_info_update",
|
|
367
|
+
_meta: {
|
|
368
|
+
gjcGoalActive: event.goal !== null,
|
|
369
|
+
...(event.goal
|
|
370
|
+
? {
|
|
371
|
+
gjcGoalId: event.goal.id,
|
|
372
|
+
gjcGoalStatus: event.goal.status,
|
|
373
|
+
gjcGoalObjective: event.goal.objective,
|
|
374
|
+
}
|
|
375
|
+
: {}),
|
|
376
|
+
...(event.state ? { gjcGoalModeState: event.state } : {}),
|
|
377
|
+
},
|
|
378
|
+
}),
|
|
379
|
+
];
|
|
306
380
|
default:
|
|
307
381
|
return assertNeverAcp(event);
|
|
308
382
|
}
|
|
@@ -547,6 +621,19 @@ function mergeToolUpdateContent(startContent: ToolCallContent[], resultContent:
|
|
|
547
621
|
return merged;
|
|
548
622
|
}
|
|
549
623
|
|
|
624
|
+
function mergeToolLocations(...groups: ToolCallLocation[][]): ToolCallLocation[] {
|
|
625
|
+
const locations: ToolCallLocation[] = [];
|
|
626
|
+
const seen = new Set<string>();
|
|
627
|
+
for (const group of groups) {
|
|
628
|
+
for (const location of group) {
|
|
629
|
+
if (seen.has(location.path)) continue;
|
|
630
|
+
seen.add(location.path);
|
|
631
|
+
locations.push(location);
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
return locations;
|
|
635
|
+
}
|
|
636
|
+
|
|
550
637
|
function isCommandToolName(toolName: string): boolean {
|
|
551
638
|
return toolName === "bash" || toolName === "shell" || toolName === "exec";
|
|
552
639
|
}
|
|
@@ -1245,8 +1245,14 @@ export class ModelSelectorComponent extends Container {
|
|
|
1245
1245
|
new Text(`${prefix}${i === this.#presetScopeIndex ? theme.fg("accent", label) : label}`, 0, 0),
|
|
1246
1246
|
);
|
|
1247
1247
|
}
|
|
1248
|
+
this.#listContainer.addChild(new Spacer(1));
|
|
1249
|
+
this.#listContainer.addChild(
|
|
1250
|
+
new Text(theme.fg("muted", " Enter: apply | d: set as default | Esc: back"), 0, 0),
|
|
1251
|
+
);
|
|
1248
1252
|
} else {
|
|
1249
|
-
this.#listContainer.addChild(
|
|
1253
|
+
this.#listContainer.addChild(
|
|
1254
|
+
new Text(theme.fg("muted", " Press Enter to apply or d to set as default"), 0, 0),
|
|
1255
|
+
);
|
|
1250
1256
|
}
|
|
1251
1257
|
}
|
|
1252
1258
|
|
|
@@ -1558,6 +1564,14 @@ export class ModelSelectorComponent extends Container {
|
|
|
1558
1564
|
}
|
|
1559
1565
|
|
|
1560
1566
|
#handlePresetLandingInput(keyData: string): void {
|
|
1567
|
+
if (keyData === "d" || keyData === "D") {
|
|
1568
|
+
if (this.#previewProfileName) {
|
|
1569
|
+
this.#presetScopeMenuOpen = true;
|
|
1570
|
+
this.#presetScopeIndex = 1;
|
|
1571
|
+
this.#handlePresetEnter();
|
|
1572
|
+
return;
|
|
1573
|
+
}
|
|
1574
|
+
}
|
|
1561
1575
|
if (isPrintableCharacter(keyData)) {
|
|
1562
1576
|
this.#switchToModelMode(keyData);
|
|
1563
1577
|
return;
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { Container, matchesKey, type SelectItem, SelectList, Spacer, Text } from "@gajae-code/tui";
|
|
2
|
+
import { formatKeyHint } from "../../config/keybindings";
|
|
2
3
|
import type { QueuedMessageEditEntry } from "../../session/agent-session";
|
|
3
4
|
import { getSelectListTheme, theme } from "../theme/theme";
|
|
4
5
|
import { DynamicBorder } from "./dynamic-border";
|
|
5
6
|
|
|
6
7
|
const MAX_VISIBLE_QUEUE_MESSAGES = 8;
|
|
7
|
-
|
|
8
|
-
|
|
8
|
+
type QueueSelectorAction = "tui.select.confirm" | "tui.select.cancel";
|
|
9
|
+
type QueueSelectorNavigationAction = "tui.select.up" | "tui.select.down" | "tui.select.pageUp" | "tui.select.pageDown";
|
|
9
10
|
|
|
10
11
|
export class QueuePaneComponent extends Container {
|
|
11
12
|
#selectList: SelectList;
|
|
@@ -13,11 +14,20 @@ export class QueuePaneComponent extends Container {
|
|
|
13
14
|
#selectedIndex: number;
|
|
14
15
|
#onDelete: (entry: QueuedMessageEditEntry, index: number) => void;
|
|
15
16
|
#onMove: (entry: QueuedMessageEditEntry, index: number, direction: "up" | "down") => void;
|
|
16
|
-
|
|
17
|
+
#onSelect: (entry: QueuedMessageEditEntry) => void;
|
|
18
|
+
#onClose: () => void;
|
|
19
|
+
#entries: QueuedMessageEditEntry[];
|
|
20
|
+
#matchesSelectAction: (keyData: string, action: QueueSelectorAction) => boolean;
|
|
21
|
+
#resolveSelectNavigation: (keyData: string) => QueueSelectorNavigationAction | undefined;
|
|
17
22
|
constructor(
|
|
18
23
|
entries: QueuedMessageEditEntry[],
|
|
19
24
|
options: {
|
|
20
25
|
selectedIndex?: number;
|
|
26
|
+
formatKeyHint?: (key: string) => string;
|
|
27
|
+
formatSelectAction?: (action: QueueSelectorAction) => string;
|
|
28
|
+
matchesSelectAction?: (keyData: string, action: QueueSelectorAction) => boolean;
|
|
29
|
+
resolveSelectNavigation?: (keyData: string) => QueueSelectorNavigationAction | undefined;
|
|
30
|
+
onSelect: (entry: QueuedMessageEditEntry) => void;
|
|
21
31
|
onDelete: (entry: QueuedMessageEditEntry, index: number) => void;
|
|
22
32
|
onMove: (entry: QueuedMessageEditEntry, index: number, direction: "up" | "down") => void;
|
|
23
33
|
onClose: () => void;
|
|
@@ -25,23 +35,46 @@ export class QueuePaneComponent extends Container {
|
|
|
25
35
|
) {
|
|
26
36
|
super();
|
|
27
37
|
this.#onDelete = options.onDelete;
|
|
38
|
+
this.#onSelect = options.onSelect;
|
|
28
39
|
this.#onMove = options.onMove;
|
|
40
|
+
this.#onClose = options.onClose;
|
|
41
|
+
this.#entries = entries;
|
|
42
|
+
this.#matchesSelectAction =
|
|
43
|
+
options.matchesSelectAction ??
|
|
44
|
+
((keyData, action) => matchesKey(keyData, action === "tui.select.confirm" ? "enter" : "escape"));
|
|
45
|
+
this.#resolveSelectNavigation = options.resolveSelectNavigation ?? (() => undefined);
|
|
29
46
|
this.#selectedIndex = Math.max(0, Math.min(options.selectedIndex ?? 0, entries.length - 1));
|
|
30
47
|
this.#selectedEntry = entries[this.#selectedIndex];
|
|
31
48
|
const byId = new Map(entries.map(entry => [entry.id, entry]));
|
|
49
|
+
const displayKey = options.formatKeyHint ?? formatKeyHint;
|
|
50
|
+
const selectKeys = `${displayKey("alt+up")}/${displayKey("alt+down")}`;
|
|
51
|
+
const editKey = options.formatSelectAction
|
|
52
|
+
? options.formatSelectAction("tui.select.confirm") || "Disabled"
|
|
53
|
+
: displayKey("enter");
|
|
54
|
+
const deleteKey = displayKey("delete");
|
|
55
|
+
const moveKeys = `${displayKey("ctrl+up")}/${displayKey("ctrl+down")}`;
|
|
56
|
+
const closeKey = options.formatSelectAction
|
|
57
|
+
? options.formatSelectAction("tui.select.cancel") || "Disabled"
|
|
58
|
+
: displayKey("escape");
|
|
59
|
+
const itemHint = `${editKey} edit · ${deleteKey} remove · ${moveKeys} move`;
|
|
60
|
+
const controlsHint = `${selectKeys} select · ${itemHint} · ${closeKey} close`;
|
|
32
61
|
const items: SelectItem[] = entries.map((entry, index) => ({
|
|
33
62
|
value: entry.id,
|
|
34
63
|
label: `${entry.label} ${index + 1}`,
|
|
35
64
|
description: entry.text,
|
|
36
|
-
hint:
|
|
65
|
+
hint: itemHint,
|
|
37
66
|
}));
|
|
38
67
|
|
|
39
68
|
this.addChild(new Spacer(1));
|
|
40
69
|
this.addChild(new Text(theme.bold("Message queue"), 1, 0));
|
|
41
|
-
this.addChild(new Text(theme.fg("muted",
|
|
70
|
+
this.addChild(new Text(theme.fg("muted", controlsHint), 1, 0));
|
|
42
71
|
this.addChild(new Spacer(1));
|
|
43
72
|
this.addChild(new DynamicBorder());
|
|
44
73
|
this.#selectList = new SelectList(items, MAX_VISIBLE_QUEUE_MESSAGES, getSelectListTheme());
|
|
74
|
+
this.#selectList.onSelect = item => {
|
|
75
|
+
const entry = byId.get(item.value);
|
|
76
|
+
if (entry) this.#onSelect(entry);
|
|
77
|
+
};
|
|
45
78
|
this.#selectList.onSelectionChange = item => {
|
|
46
79
|
const index = entries.findIndex(entry => entry.id === item.value);
|
|
47
80
|
this.#selectedIndex = index === -1 ? this.#selectedIndex : index;
|
|
@@ -54,12 +87,25 @@ export class QueuePaneComponent extends Container {
|
|
|
54
87
|
}
|
|
55
88
|
|
|
56
89
|
handleInput(keyData: string): void {
|
|
57
|
-
if (
|
|
58
|
-
this.#
|
|
90
|
+
if (this.#matchesSelectAction(keyData, "tui.select.confirm")) {
|
|
91
|
+
if (this.#selectedEntry) this.#onSelect(this.#selectedEntry);
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
if (this.#matchesSelectAction(keyData, "tui.select.cancel")) {
|
|
95
|
+
this.#onClose();
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
const navigation = this.#resolveSelectNavigation(keyData);
|
|
99
|
+
if (navigation) {
|
|
100
|
+
this.#selectList.handleNavigation(navigation);
|
|
59
101
|
return;
|
|
60
102
|
}
|
|
61
|
-
if (matchesKey(keyData, "
|
|
62
|
-
|
|
103
|
+
if (matchesKey(keyData, "enter") || matchesKey(keyData, "escape")) return;
|
|
104
|
+
if (matchesKey(keyData, "alt+up") || matchesKey(keyData, "alt+down")) {
|
|
105
|
+
const direction = matchesKey(keyData, "alt+up") ? -1 : 1;
|
|
106
|
+
this.#selectedIndex = (this.#selectedIndex + direction + this.#entries.length) % this.#entries.length;
|
|
107
|
+
this.#selectedEntry = this.#entries[this.#selectedIndex];
|
|
108
|
+
this.#selectList.setSelectedIndex(this.#selectedIndex);
|
|
63
109
|
return;
|
|
64
110
|
}
|
|
65
111
|
if (matchesKey(keyData, "ctrl+up") || matchesKey(keyData, "ctrl+shift+up")) {
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { Container, matchesKey, type SelectItem, SelectList, Spacer, Text } from "@gajae-code/tui";
|
|
2
|
+
import { formatKeyHint } from "../../config/keybindings";
|
|
2
3
|
import type { QueuedMessageEditEntry } from "../../session/agent-session";
|
|
3
4
|
import { getSelectListTheme, theme } from "../theme/theme";
|
|
4
5
|
import { DynamicBorder } from "./dynamic-border";
|
|
5
6
|
|
|
6
7
|
const MAX_VISIBLE_QUEUED_MESSAGES = 8;
|
|
7
|
-
const RAW_UP = "\x1b[A";
|
|
8
|
-
const RAW_DOWN = "\x1b[B";
|
|
9
8
|
type QueuedMessageMoveDirection = "up" | "down";
|
|
9
|
+
type QueueSelectorAction = "tui.select.confirm" | "tui.select.cancel";
|
|
10
|
+
type QueueSelectorNavigationAction = "tui.select.up" | "tui.select.down" | "tui.select.pageUp" | "tui.select.pageDown";
|
|
10
11
|
|
|
11
12
|
export type { QueuedMessageMoveDirection };
|
|
12
13
|
|
|
@@ -16,32 +17,61 @@ export class QueuedMessageSelectorComponent extends Container {
|
|
|
16
17
|
#selectedIndex = 0;
|
|
17
18
|
#onDelete: (entry: QueuedMessageEditEntry, selectedIndex: number) => void;
|
|
18
19
|
#onMove: (entry: QueuedMessageEditEntry, selectedIndex: number, direction: QueuedMessageMoveDirection) => void;
|
|
19
|
-
|
|
20
|
+
#onSelect: (entry: QueuedMessageEditEntry) => void;
|
|
21
|
+
#onCancel: () => void;
|
|
22
|
+
#entries: QueuedMessageEditEntry[];
|
|
23
|
+
#matchesSelectAction: (keyData: string, action: QueueSelectorAction) => boolean;
|
|
24
|
+
#resolveSelectNavigation: (keyData: string) => QueueSelectorNavigationAction | undefined;
|
|
20
25
|
constructor(
|
|
21
26
|
entries: QueuedMessageEditEntry[],
|
|
22
27
|
onSelect: (entry: QueuedMessageEditEntry) => void,
|
|
23
28
|
onDelete: (entry: QueuedMessageEditEntry, selectedIndex: number) => void,
|
|
24
29
|
onMove: (entry: QueuedMessageEditEntry, selectedIndex: number, direction: QueuedMessageMoveDirection) => void,
|
|
25
30
|
onCancel: () => void,
|
|
26
|
-
options?: {
|
|
31
|
+
options?: {
|
|
32
|
+
selectedIndex?: number;
|
|
33
|
+
formatKeyHint?: (key: string) => string;
|
|
34
|
+
formatSelectAction?: (action: QueueSelectorAction) => string;
|
|
35
|
+
matchesSelectAction?: (keyData: string, action: QueueSelectorAction) => boolean;
|
|
36
|
+
resolveSelectNavigation?: (keyData: string) => QueueSelectorNavigationAction | undefined;
|
|
37
|
+
},
|
|
27
38
|
) {
|
|
28
39
|
super();
|
|
29
40
|
|
|
30
41
|
this.#onDelete = onDelete;
|
|
31
42
|
this.#onMove = onMove;
|
|
43
|
+
this.#onSelect = onSelect;
|
|
44
|
+
this.#onCancel = onCancel;
|
|
45
|
+
this.#entries = entries;
|
|
46
|
+
this.#matchesSelectAction =
|
|
47
|
+
options?.matchesSelectAction ??
|
|
48
|
+
((keyData, action) => matchesKey(keyData, action === "tui.select.confirm" ? "enter" : "escape"));
|
|
49
|
+
this.#resolveSelectNavigation = options?.resolveSelectNavigation ?? (() => undefined);
|
|
32
50
|
const byId = new Map(entries.map(entry => [entry.id, entry]));
|
|
33
51
|
this.#selectedIndex = Math.max(0, Math.min(options?.selectedIndex ?? 0, entries.length - 1));
|
|
34
52
|
this.#selectedEntry = entries[this.#selectedIndex];
|
|
53
|
+
const displayKey = options?.formatKeyHint ?? formatKeyHint;
|
|
54
|
+
const selectKeys = `${displayKey("alt+up")}/${displayKey("alt+down")}`;
|
|
55
|
+
const editKey = options?.formatSelectAction
|
|
56
|
+
? options.formatSelectAction("tui.select.confirm") || "Disabled"
|
|
57
|
+
: displayKey("enter");
|
|
58
|
+
const deleteKey = displayKey("delete");
|
|
59
|
+
const moveKeys = `${displayKey("ctrl+up")}/${displayKey("ctrl+down")}`;
|
|
60
|
+
const cancelKey = options?.formatSelectAction
|
|
61
|
+
? options.formatSelectAction("tui.select.cancel") || "Disabled"
|
|
62
|
+
: displayKey("escape");
|
|
63
|
+
const itemHint = `${editKey} edit · ${deleteKey} remove · ${moveKeys} move`;
|
|
64
|
+
const controlsHint = `${selectKeys} select · ${itemHint} · ${cancelKey} cancel`;
|
|
35
65
|
const items: SelectItem[] = entries.map((entry, index) => ({
|
|
36
66
|
value: entry.id,
|
|
37
67
|
label: `${entry.label} ${index + 1}`,
|
|
38
68
|
description: entry.text,
|
|
39
|
-
hint:
|
|
69
|
+
hint: itemHint,
|
|
40
70
|
}));
|
|
41
71
|
|
|
42
72
|
this.addChild(new Spacer(1));
|
|
43
73
|
this.addChild(new Text(theme.bold("Queued messages"), 1, 0));
|
|
44
|
-
this.addChild(new Text(theme.fg("muted",
|
|
74
|
+
this.addChild(new Text(theme.fg("muted", controlsHint), 1, 0));
|
|
45
75
|
this.addChild(new Spacer(1));
|
|
46
76
|
this.addChild(new DynamicBorder());
|
|
47
77
|
|
|
@@ -63,12 +93,25 @@ export class QueuedMessageSelectorComponent extends Container {
|
|
|
63
93
|
}
|
|
64
94
|
|
|
65
95
|
handleInput(keyData: string): void {
|
|
66
|
-
if (
|
|
67
|
-
this.#
|
|
96
|
+
if (this.#matchesSelectAction(keyData, "tui.select.confirm")) {
|
|
97
|
+
if (this.#selectedEntry) this.#onSelect(this.#selectedEntry);
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
if (this.#matchesSelectAction(keyData, "tui.select.cancel")) {
|
|
101
|
+
this.#onCancel();
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
const navigation = this.#resolveSelectNavigation(keyData);
|
|
105
|
+
if (navigation) {
|
|
106
|
+
this.#selectList.handleNavigation(navigation);
|
|
68
107
|
return;
|
|
69
108
|
}
|
|
70
|
-
if (matchesKey(keyData, "
|
|
71
|
-
|
|
109
|
+
if (matchesKey(keyData, "enter") || matchesKey(keyData, "escape")) return;
|
|
110
|
+
if (matchesKey(keyData, "alt+up") || matchesKey(keyData, "alt+down")) {
|
|
111
|
+
const direction = matchesKey(keyData, "alt+up") ? -1 : 1;
|
|
112
|
+
this.#selectedIndex = (this.#selectedIndex + direction + this.#entries.length) % this.#entries.length;
|
|
113
|
+
this.#selectedEntry = this.#entries[this.#selectedIndex];
|
|
114
|
+
this.#selectList.setSelectedIndex(this.#selectedIndex);
|
|
72
115
|
return;
|
|
73
116
|
}
|
|
74
117
|
if (matchesKey(keyData, "ctrl+up") || matchesKey(keyData, "ctrl+shift+up")) {
|
|
@@ -70,16 +70,13 @@ export interface StatusLineActionHint {
|
|
|
70
70
|
|
|
71
71
|
const ACTION_HINT_PRIORITY: readonly AppKeybinding[] = [
|
|
72
72
|
"app.message.sendNow",
|
|
73
|
-
"app.message.queue",
|
|
74
73
|
"app.message.followUp",
|
|
74
|
+
"app.message.queue",
|
|
75
75
|
"app.message.dequeue",
|
|
76
76
|
"app.commandPalette.open",
|
|
77
77
|
"app.plan.toggle",
|
|
78
78
|
"app.mode.cycle",
|
|
79
|
-
"app.thinking.cycle",
|
|
80
|
-
"app.model.select",
|
|
81
79
|
"app.model.cycleForward",
|
|
82
|
-
"app.history.search",
|
|
83
80
|
"app.session.togglePath",
|
|
84
81
|
"app.session.toggleSort",
|
|
85
82
|
"app.session.rename",
|
|
@@ -20,6 +20,7 @@ export interface WelcomeComponentOptions {
|
|
|
20
20
|
getViewportRows?: () => number | undefined;
|
|
21
21
|
getReservedBottomRows?: (termWidth: number) => number;
|
|
22
22
|
changelogMarkdown?: string;
|
|
23
|
+
rightGutterWidth?: number;
|
|
23
24
|
collapseChangelog?: boolean;
|
|
24
25
|
buildLabel?: string;
|
|
25
26
|
keyDisplayContext?: KeyDisplayContext;
|
|
@@ -110,7 +111,8 @@ export class WelcomeComponent implements Component {
|
|
|
110
111
|
}
|
|
111
112
|
|
|
112
113
|
render(termWidth: number): string[] {
|
|
113
|
-
const
|
|
114
|
+
const rightGutterWidth = this.#rightGutterWidth(termWidth);
|
|
115
|
+
const boxWidth = Math.max(0, termWidth - rightGutterWidth);
|
|
114
116
|
if (boxWidth < 4) {
|
|
115
117
|
return [];
|
|
116
118
|
}
|
|
@@ -245,7 +247,7 @@ export class WelcomeComponent implements Component {
|
|
|
245
247
|
lines.push(tl + titleStyled + theme.fg("dim", hChar.repeat(afterTitle)) + tr);
|
|
246
248
|
}
|
|
247
249
|
if (outputRows === 1) {
|
|
248
|
-
return lines;
|
|
250
|
+
return this.#withRightGutter(lines, rightGutterWidth);
|
|
249
251
|
}
|
|
250
252
|
|
|
251
253
|
if (showRightColumn) {
|
|
@@ -273,7 +275,7 @@ export class WelcomeComponent implements Component {
|
|
|
273
275
|
lines.push(bl + h.repeat(leftCol) + br);
|
|
274
276
|
}
|
|
275
277
|
|
|
276
|
-
return lines;
|
|
278
|
+
return this.#withRightGutter(lines, rightGutterWidth);
|
|
277
279
|
}
|
|
278
280
|
|
|
279
281
|
/** Center text within a given width */
|
|
@@ -295,6 +297,19 @@ export class WelcomeComponent implements Component {
|
|
|
295
297
|
}
|
|
296
298
|
return str + padding(width - visLen);
|
|
297
299
|
}
|
|
300
|
+
#rightGutterWidth(termWidth: number): number {
|
|
301
|
+
const configured = this.options.rightGutterWidth ?? 0;
|
|
302
|
+
if (!Number.isFinite(configured) || configured <= 0) return 0;
|
|
303
|
+
const gutterWidth = Math.floor(configured);
|
|
304
|
+
return Math.min(gutterWidth, Math.max(0, termWidth - 4));
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
#withRightGutter(lines: string[], rightGutterWidth: number): string[] {
|
|
308
|
+
if (rightGutterWidth <= 0) return lines;
|
|
309
|
+
const gutter = padding(rightGutterWidth);
|
|
310
|
+
return lines.map(line => line + gutter);
|
|
311
|
+
}
|
|
312
|
+
|
|
298
313
|
#targetRows(termWidth: number): number | undefined {
|
|
299
314
|
const viewportRows = this.options.getViewportRows?.();
|
|
300
315
|
if (typeof viewportRows !== "number" || !Number.isFinite(viewportRows) || viewportRows <= 0) {
|
|
@@ -65,6 +65,8 @@ export function buildHelpMarkdown(keybindings: Pick<KeybindingsManager, "getAcce
|
|
|
65
65
|
const displayKey = (action: Keybinding): string => keybindings.getAccessibleDisplayString(action) || "Disabled";
|
|
66
66
|
const sessionNewKey = formatHotkeyMarkdownCode(displayKey("app.session.new"));
|
|
67
67
|
const selectModelKey = formatHotkeyMarkdownCode(displayKey("app.model.select"));
|
|
68
|
+
const queueMessageKey = formatHotkeyMarkdownCode(displayKey("app.message.queue"));
|
|
69
|
+
const dequeueMessageKey = formatHotkeyMarkdownCode(displayKey("app.message.dequeue"));
|
|
68
70
|
const autocompleteNavigationKeys = formatHotkeyMarkdownCode(
|
|
69
71
|
`${displayKey("tui.select.up")}/${displayKey("tui.select.down")}`,
|
|
70
72
|
false,
|
|
@@ -84,6 +86,8 @@ export function buildHelpMarkdown(keybindings: Pick<KeybindingsManager, "getAcce
|
|
|
84
86
|
"| Show session details | `/session info` |",
|
|
85
87
|
"| Delete current session transcript/artifacts | `/session delete` |",
|
|
86
88
|
`| Select a model | \`/model\` or ${selectModelKey} |`,
|
|
89
|
+
`| Queue a message for the next turn | ${queueMessageKey} |`,
|
|
90
|
+
`| Select or edit a queued message | ${dequeueMessageKey} |`,
|
|
87
91
|
"| Show all shortcuts | `?` on an empty prompt or `/hotkeys` |",
|
|
88
92
|
"",
|
|
89
93
|
"**Finding commands**",
|
|
@@ -425,6 +425,7 @@ export class ExtensionUiController {
|
|
|
425
425
|
getSystemPrompt: () => this.ctx.session.systemPrompt,
|
|
426
426
|
clearContext: () => this.ctx.session.clearContext(),
|
|
427
427
|
cycleModel: () => this.ctx.session.cycleModel(),
|
|
428
|
+
setModelProfile: name => this.ctx.session.activateModelProfileForControl(name),
|
|
428
429
|
cycleThinkingLevel: () => this.ctx.session.cycleThinkingLevel(),
|
|
429
430
|
setQueueMode: (kind, mode) => {
|
|
430
431
|
if (kind === "steering" && (mode === "all" || mode === "one-at-a-time")) {
|
|
@@ -446,11 +447,7 @@ export class ExtensionUiController {
|
|
|
446
447
|
operateGoal: (op, objective) => this.ctx.session.operateGoal(op, objective),
|
|
447
448
|
getSkillState: () =>
|
|
448
449
|
this.ctx.session.skills.map(skill => ({ name: skill.name, description: skill.description })),
|
|
449
|
-
getConfigItems: () => (
|
|
450
|
-
steeringMode: this.ctx.session.steeringMode,
|
|
451
|
-
followUpMode: this.ctx.session.followUpMode,
|
|
452
|
-
interruptMode: this.ctx.session.interruptMode,
|
|
453
|
-
}),
|
|
450
|
+
getConfigItems: () => this.ctx.session.getSdkConfigItems(),
|
|
454
451
|
getBranchCandidates: () => this.ctx.sessionManager.getTree(),
|
|
455
452
|
getExtensions: () => this.ctx.session.extensionRunner?.getExtensionPaths() ?? [],
|
|
456
453
|
setSdkPermissionProvider: provider => this.ctx.session.setSdkPermissionProvider(provider),
|
|
@@ -727,6 +724,7 @@ export class ExtensionUiController {
|
|
|
727
724
|
getSystemPrompt: () => this.ctx.session.systemPrompt,
|
|
728
725
|
clearContext: () => this.ctx.session.clearContext(),
|
|
729
726
|
cycleModel: () => this.ctx.session.cycleModel(),
|
|
727
|
+
setModelProfile: name => this.ctx.session.activateModelProfileForControl(name),
|
|
730
728
|
cycleThinkingLevel: () => this.ctx.session.cycleThinkingLevel(),
|
|
731
729
|
setQueueMode: (kind, mode) => {
|
|
732
730
|
if (kind === "steering" && (mode === "all" || mode === "one-at-a-time")) {
|
|
@@ -745,11 +743,7 @@ export class ExtensionUiController {
|
|
|
745
743
|
},
|
|
746
744
|
getSkillState: () =>
|
|
747
745
|
this.ctx.session.skills.map(skill => ({ name: skill.name, description: skill.description })),
|
|
748
|
-
getConfigItems: () => (
|
|
749
|
-
steeringMode: this.ctx.session.steeringMode,
|
|
750
|
-
followUpMode: this.ctx.session.followUpMode,
|
|
751
|
-
interruptMode: this.ctx.session.interruptMode,
|
|
752
|
-
}),
|
|
746
|
+
getConfigItems: () => this.ctx.session.getSdkConfigItems(),
|
|
753
747
|
getBranchCandidates: () => this.ctx.sessionManager.getTree(),
|
|
754
748
|
getExtensions: () => this.ctx.session.extensionRunner?.getExtensionPaths() ?? [],
|
|
755
749
|
setSdkPermissionProvider: provider => this.ctx.session.setSdkPermissionProvider(provider),
|
|
@@ -33,6 +33,13 @@ import type { PasteTextContext } from "../components/custom-editor";
|
|
|
33
33
|
import { QueuePaneComponent } from "../components/queue-pane";
|
|
34
34
|
import { type QueuedMessageMoveDirection, QueuedMessageSelectorComponent } from "../components/queued-message-selector";
|
|
35
35
|
|
|
36
|
+
const QUEUE_SELECTOR_NAVIGATION_ACTIONS = [
|
|
37
|
+
"tui.select.up",
|
|
38
|
+
"tui.select.down",
|
|
39
|
+
"tui.select.pageUp",
|
|
40
|
+
"tui.select.pageDown",
|
|
41
|
+
] as const;
|
|
42
|
+
|
|
36
43
|
interface Expandable {
|
|
37
44
|
setExpanded(expanded: boolean): void;
|
|
38
45
|
setManuallyExpanded?(expanded: boolean): void;
|
|
@@ -151,15 +158,24 @@ export class InputController {
|
|
|
151
158
|
case "app.editor.external":
|
|
152
159
|
return Boolean(getEditorCommand());
|
|
153
160
|
case "app.message.followUp":
|
|
154
|
-
case "app.message.queue":
|
|
155
161
|
return (
|
|
156
162
|
this.ctx.session.isStreaming ||
|
|
157
163
|
this.ctx.session.isCompacting ||
|
|
158
164
|
this.ctx.session.isBashRunning ||
|
|
159
165
|
this.ctx.session.isEvalRunning
|
|
160
166
|
);
|
|
167
|
+
case "app.message.queue":
|
|
168
|
+
return (
|
|
169
|
+
this.ctx.editor.getText().trim().length > 0 &&
|
|
170
|
+
(this.ctx.session.isStreaming ||
|
|
171
|
+
this.ctx.session.isCompacting ||
|
|
172
|
+
this.ctx.session.isBashRunning ||
|
|
173
|
+
this.ctx.session.isEvalRunning)
|
|
174
|
+
);
|
|
161
175
|
case "app.message.dequeue":
|
|
162
|
-
return
|
|
176
|
+
return (
|
|
177
|
+
this.ctx.session.getQueuedMessageEntries().length > 0 || this.ctx.compactionQueuedMessages.length > 0
|
|
178
|
+
);
|
|
163
179
|
case "app.clipboard.copyPrompt":
|
|
164
180
|
return this.ctx.editor.getText().length > 0;
|
|
165
181
|
case "app.session.tree":
|
|
@@ -1037,6 +1053,21 @@ export class InputController {
|
|
|
1037
1053
|
};
|
|
1038
1054
|
const pane = new QueuePaneComponent(entries, {
|
|
1039
1055
|
selectedIndex,
|
|
1056
|
+
formatKeyHint: key => this.ctx.keybindings.formatKeyHint(key),
|
|
1057
|
+
formatSelectAction: action => this.ctx.keybindings.getDisplayString(action),
|
|
1058
|
+
matchesSelectAction: (keyData, action) =>
|
|
1059
|
+
this.ctx.keybindings.getKeys(action).some(key => matchesKey(keyData, key)),
|
|
1060
|
+
resolveSelectNavigation: keyData =>
|
|
1061
|
+
QUEUE_SELECTOR_NAVIGATION_ACTIONS.find(action =>
|
|
1062
|
+
this.ctx.keybindings.getKeys(action).some(key => matchesKey(keyData, key)),
|
|
1063
|
+
),
|
|
1064
|
+
onSelect: entry => {
|
|
1065
|
+
const restored = this.#restoreQueuedMessageToEditor(entry);
|
|
1066
|
+
close();
|
|
1067
|
+
this.ctx.showStatus(
|
|
1068
|
+
restored === 0 ? "Queued message is no longer available" : "Restored queued message to editor",
|
|
1069
|
+
);
|
|
1070
|
+
},
|
|
1040
1071
|
onDelete: (entry, index) => {
|
|
1041
1072
|
const deleted = this.ctx.session.removeQueuedMessageForEditing(entry.id) !== undefined;
|
|
1042
1073
|
const remaining = this.ctx.session.getQueuedMessageEntries();
|
|
@@ -1209,7 +1240,17 @@ export class InputController {
|
|
|
1209
1240
|
this.#restoreEditorFocus();
|
|
1210
1241
|
this.ctx.ui.requestRender();
|
|
1211
1242
|
},
|
|
1212
|
-
{
|
|
1243
|
+
{
|
|
1244
|
+
selectedIndex,
|
|
1245
|
+
formatKeyHint: key => this.ctx.keybindings.formatKeyHint(key),
|
|
1246
|
+
formatSelectAction: action => this.ctx.keybindings.getDisplayString(action),
|
|
1247
|
+
matchesSelectAction: (keyData, action) =>
|
|
1248
|
+
this.ctx.keybindings.getKeys(action).some(key => matchesKey(keyData, key)),
|
|
1249
|
+
resolveSelectNavigation: keyData =>
|
|
1250
|
+
QUEUE_SELECTOR_NAVIGATION_ACTIONS.find(action =>
|
|
1251
|
+
this.ctx.keybindings.getKeys(action).some(key => matchesKey(keyData, key)),
|
|
1252
|
+
),
|
|
1253
|
+
},
|
|
1213
1254
|
);
|
|
1214
1255
|
this.ctx.editorContainer.clear();
|
|
1215
1256
|
this.ctx.editorContainer.addChild(selector);
|