loadtoagent 1.3.10 → 1.3.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/README.ko.md +6 -3
- package/README.md +6 -3
- package/README.zh-CN.md +6 -3
- package/bin/loadtoagent.js +3 -1
- package/docs/ARCHITECTURE.md +7 -0
- package/docs/MANAGED-TERMINAL-SESSIONS.md +64 -0
- package/main.js +5 -1
- package/package.json +2 -1
- package/preload.js +3 -0
- package/renderer/app-agent-actions.js +83 -35
- package/renderer/app-drawer.js +107 -26
- package/renderer/app-events-dialogs.js +66 -8
- package/renderer/app-events-sessions.js +3 -23
- package/renderer/app.js +11 -1
- package/renderer/i18n-messages.js +33 -0
- package/renderer/index.html +2 -0
- package/renderer/styles-control-room.css +399 -0
- package/renderer/styles-terminal.css +6 -0
- package/renderer/terminal-events.js +30 -4
- package/renderer/terminal-workbench.js +15 -3
- package/renderer/terminal.js +2 -0
- package/src/bridgeServer.js +1 -1
- package/src/ipc/registerTerminalIpc.js +1 -1
- package/src/managedTmuxRuntime.js +53 -0
- package/src/terminalHost.js +8 -2
- package/src/terminalManager.js +173 -33
package/renderer/app-drawer.js
CHANGED
|
@@ -22,7 +22,59 @@ window.LoadToAgentAppFactories.createDrawer = function createDrawer(context = {}
|
|
|
22
22
|
failed: "control.delivery_failed",
|
|
23
23
|
})[phase] || "control.delivery_confirming";
|
|
24
24
|
|
|
25
|
-
function
|
|
25
|
+
function scheduleFlowConnections() {
|
|
26
|
+
requestAnimationFrame(() => {
|
|
27
|
+
window.LoadToAgentApp?.scheduleAgentWorkflowConnections?.();
|
|
28
|
+
window.LoadToAgentApp?.drawAgentWorkflowConnections?.();
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function resolvedPresentation(options = {}) {
|
|
33
|
+
const requested = options.presentation || (options.context ? "context" : "modal");
|
|
34
|
+
if (requested !== "context") return "modal";
|
|
35
|
+
return state.view === "all" && window.matchMedia("(min-width: 1280px)").matches
|
|
36
|
+
? "context"
|
|
37
|
+
: "modal";
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function setDrawerPresentation(presentation) {
|
|
41
|
+
const drawer = $("#detailDrawer");
|
|
42
|
+
const contextual = presentation === "context";
|
|
43
|
+
state.drawerPresentation = presentation;
|
|
44
|
+
drawer.dataset.presentation = presentation;
|
|
45
|
+
drawer.setAttribute("role", contextual ? "complementary" : "dialog");
|
|
46
|
+
if (contextual) drawer.removeAttribute("aria-modal");
|
|
47
|
+
else drawer.setAttribute("aria-modal", "true");
|
|
48
|
+
$("#drawerBackToFlowBtn").classList.toggle("hidden", !contextual);
|
|
49
|
+
$("#drawerResizeHandle").classList.toggle("hidden", !contextual);
|
|
50
|
+
document.body.classList.toggle("conversation-context-open", contextual);
|
|
51
|
+
const currentWidth = Math.round(drawer.getBoundingClientRect().width || 640);
|
|
52
|
+
$("#drawerResizeHandle").setAttribute("aria-valuenow", String(currentWidth));
|
|
53
|
+
if (contextual) {
|
|
54
|
+
$("#drawerBackdrop").classList.add("hidden");
|
|
55
|
+
$("#drawerBackdrop").classList.remove("closing");
|
|
56
|
+
} else {
|
|
57
|
+
$("#drawerBackdrop").classList.remove("hidden");
|
|
58
|
+
$("#drawerBackdrop").classList.remove("closing");
|
|
59
|
+
}
|
|
60
|
+
scheduleFlowConnections();
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function openDrawerSurface(presentation) {
|
|
64
|
+
clearTimeout(motionState.drawerTimer);
|
|
65
|
+
setDrawerPresentation(presentation);
|
|
66
|
+
$("#detailDrawer").classList.add("open");
|
|
67
|
+
if (presentation === "modal") {
|
|
68
|
+
setDialogOpenState($("#detailDrawer"), true);
|
|
69
|
+
} else {
|
|
70
|
+
$("#detailDrawer").removeAttribute("inert");
|
|
71
|
+
$("#detailDrawer").setAttribute("aria-hidden", "false");
|
|
72
|
+
$("#appShell")?.removeAttribute("inert");
|
|
73
|
+
document.body.classList.remove("dialog-open");
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function openDrawer(id, options = {}) {
|
|
26
78
|
rememberDialogTrigger();
|
|
27
79
|
markGuideStep("detail");
|
|
28
80
|
state.selectedId = id;
|
|
@@ -30,19 +82,18 @@ window.LoadToAgentAppFactories.createDrawer = function createDrawer(context = {}
|
|
|
30
82
|
state.drawerExecutionId = null;
|
|
31
83
|
state.drawerTab = "chat";
|
|
32
84
|
state.drawerForceLatest = true;
|
|
33
|
-
|
|
34
|
-
$("#drawerBackdrop").classList.remove("hidden");
|
|
35
|
-
$("#drawerBackdrop").classList.remove("closing");
|
|
36
|
-
$("#detailDrawer").classList.add("open");
|
|
37
|
-
setDialogOpenState($("#detailDrawer"), true);
|
|
85
|
+
openDrawerSurface(resolvedPresentation(options));
|
|
38
86
|
renderDrawer();
|
|
39
87
|
loadSessionDetail(id, true);
|
|
40
|
-
setTimeout(
|
|
88
|
+
setTimeout(
|
|
89
|
+
() => (state.drawerPresentation === "modal" ? $("#closeDrawerBtn") : $("#drawerBackToFlowBtn")).focus({ preventScroll: true }),
|
|
90
|
+
0,
|
|
91
|
+
);
|
|
41
92
|
}
|
|
42
93
|
|
|
43
|
-
function openSubagentConversation(id) {
|
|
94
|
+
function openSubagentConversation(id, options = {}) {
|
|
44
95
|
const child = snapshotSession(id) || state.details.get(id);
|
|
45
|
-
if (!child || !child.parentId) return openDrawer(id);
|
|
96
|
+
if (!child || !child.parentId) return openDrawer(id, options);
|
|
46
97
|
rememberDialogTrigger();
|
|
47
98
|
markGuideStep("detail");
|
|
48
99
|
state.selectedId = id;
|
|
@@ -51,15 +102,14 @@ window.LoadToAgentAppFactories.createDrawer = function createDrawer(context = {}
|
|
|
51
102
|
state.drawerTab = "chat";
|
|
52
103
|
state.agentCommandRoutes.delete(id);
|
|
53
104
|
state.drawerForceLatest = true;
|
|
54
|
-
|
|
55
|
-
$("#drawerBackdrop").classList.remove("hidden");
|
|
56
|
-
$("#drawerBackdrop").classList.remove("closing");
|
|
57
|
-
$("#detailDrawer").classList.add("open");
|
|
58
|
-
setDialogOpenState($("#detailDrawer"), true);
|
|
105
|
+
openDrawerSurface(resolvedPresentation(options));
|
|
59
106
|
renderDrawer();
|
|
60
107
|
loadSessionDetail(id);
|
|
61
108
|
loadSubagentParentDetail(child);
|
|
62
|
-
setTimeout(
|
|
109
|
+
setTimeout(
|
|
110
|
+
() => (state.drawerPresentation === "modal" ? $("#closeDrawerBtn") : $("#drawerBackToFlowBtn")).focus({ preventScroll: true }),
|
|
111
|
+
0,
|
|
112
|
+
);
|
|
63
113
|
}
|
|
64
114
|
|
|
65
115
|
function openExecutionActivity(ownerId, executionId) {
|
|
@@ -72,11 +122,7 @@ window.LoadToAgentAppFactories.createDrawer = function createDrawer(context = {}
|
|
|
72
122
|
state.drawerExecutionId = executionId;
|
|
73
123
|
state.drawerTab = "chat";
|
|
74
124
|
state.drawerForceLatest = false;
|
|
75
|
-
|
|
76
|
-
$("#drawerBackdrop").classList.remove("hidden");
|
|
77
|
-
$("#drawerBackdrop").classList.remove("closing");
|
|
78
|
-
$("#detailDrawer").classList.add("open");
|
|
79
|
-
setDialogOpenState($("#detailDrawer"), true);
|
|
125
|
+
openDrawerSurface("modal");
|
|
80
126
|
renderDrawer();
|
|
81
127
|
loadSessionDetail(ownerId);
|
|
82
128
|
if (owner.parentId) loadSubagentParentDetail(owner);
|
|
@@ -85,15 +131,27 @@ window.LoadToAgentAppFactories.createDrawer = function createDrawer(context = {}
|
|
|
85
131
|
|
|
86
132
|
function closeDrawer(restoreFocus = true) {
|
|
87
133
|
if (!$("#detailDrawer").classList.contains("open")) return;
|
|
134
|
+
const presentation = state.drawerPresentation;
|
|
88
135
|
const drawerGeneration = motionState.dialogGeneration;
|
|
89
136
|
$("#detailDrawer").classList.remove("open");
|
|
90
|
-
|
|
91
|
-
|
|
137
|
+
if (presentation === "modal") {
|
|
138
|
+
setDialogOpenState($("#detailDrawer"), false);
|
|
139
|
+
$("#drawerBackdrop").classList.add("closing");
|
|
140
|
+
} else {
|
|
141
|
+
$("#detailDrawer").setAttribute("inert", "");
|
|
142
|
+
$("#detailDrawer").setAttribute("aria-hidden", "true");
|
|
143
|
+
$("#drawerBackdrop").classList.add("hidden");
|
|
144
|
+
$("#drawerBackdrop").classList.remove("closing");
|
|
145
|
+
document.body.classList.remove("conversation-context-open");
|
|
146
|
+
scheduleFlowConnections();
|
|
147
|
+
}
|
|
92
148
|
clearTimeout(motionState.drawerTimer);
|
|
93
149
|
motionState.drawerTimer = setTimeout(
|
|
94
150
|
() => {
|
|
95
151
|
$("#drawerBackdrop").classList.add("hidden");
|
|
96
152
|
$("#drawerBackdrop").classList.remove("closing");
|
|
153
|
+
setDrawerPresentation("modal");
|
|
154
|
+
$("#drawerBackdrop").classList.add("hidden");
|
|
97
155
|
if (drawerGeneration !== motionState.dialogGeneration) return;
|
|
98
156
|
if (restoreFocus) restoreDialogTrigger(drawerGeneration);
|
|
99
157
|
else motionState.activeDialogTrigger = null;
|
|
@@ -102,6 +160,10 @@ window.LoadToAgentAppFactories.createDrawer = function createDrawer(context = {}
|
|
|
102
160
|
);
|
|
103
161
|
}
|
|
104
162
|
|
|
163
|
+
function backToAgentFlow() {
|
|
164
|
+
closeDrawer();
|
|
165
|
+
}
|
|
166
|
+
|
|
105
167
|
function renderDrawer() {
|
|
106
168
|
const session = selectedSession();
|
|
107
169
|
if (!session) return closeDrawer();
|
|
@@ -124,7 +186,9 @@ window.LoadToAgentAppFactories.createDrawer = function createDrawer(context = {}
|
|
|
124
186
|
$("#detailDrawer").style.setProperty("--drawer-provider", provider.accent);
|
|
125
187
|
$("#drawerProviderMark").style.setProperty("--provider", provider.accent);
|
|
126
188
|
$("#drawerProviderMark").textContent = executionMode && activity?.kind === "shell" ? ">_" : provider.mark;
|
|
127
|
-
$("#drawerProvider").textContent = executionMode
|
|
189
|
+
$("#drawerProvider").textContent = state.drawerPresentation !== "modal" && !executionMode
|
|
190
|
+
? `${t("drawer.agent_flow")} · ${presentationLabel}`
|
|
191
|
+
: executionMode
|
|
128
192
|
? `${activity?.runtime || activity?.tool || t("drawer.execution_unit")} · ${activity ? context.executionActivityStatus(activity) : t("drawer.unknown")}`
|
|
129
193
|
: subagentMode
|
|
130
194
|
? `${t("control.subagent")} · ${presentationLabel}`
|
|
@@ -185,8 +249,14 @@ window.LoadToAgentAppFactories.createDrawer = function createDrawer(context = {}
|
|
|
185
249
|
tab.tabIndex = active ? 0 : -1;
|
|
186
250
|
});
|
|
187
251
|
const activeTab = $(`.drawer-tab[data-tab="${state.drawerTab}"]`);
|
|
188
|
-
if (activeTab) $("#drawerContent").setAttribute("aria-labelledby", activeTab.id);
|
|
189
252
|
const content = $("#drawerContent");
|
|
253
|
+
if (subagentMode && state.drawerPresentation === "context") {
|
|
254
|
+
content.removeAttribute("aria-labelledby");
|
|
255
|
+
content.setAttribute("aria-label", t("control.subagent_conversation"));
|
|
256
|
+
} else {
|
|
257
|
+
content.removeAttribute("aria-label");
|
|
258
|
+
if (activeTab) content.setAttribute("aria-labelledby", activeTab.id);
|
|
259
|
+
}
|
|
190
260
|
rememberDisclosureStates(content);
|
|
191
261
|
const previousTop = content.scrollTop;
|
|
192
262
|
const wasAtBottom = window.LoadToAgentRendererUtils.isScrolledToEnd(content);
|
|
@@ -223,7 +293,11 @@ window.LoadToAgentAppFactories.createDrawer = function createDrawer(context = {}
|
|
|
223
293
|
const composer = $("#drawerComposer");
|
|
224
294
|
const showComposer = !executionMode && !detailLoading && !detailError && state.drawerTab === "chat" && typeof agentCommandComposer === "function";
|
|
225
295
|
composer.classList.toggle("hidden", !showComposer);
|
|
226
|
-
const nextComposerHtml = showComposer ? agentCommandComposer(session, {
|
|
296
|
+
const nextComposerHtml = showComposer ? agentCommandComposer(session, {
|
|
297
|
+
conversation: true,
|
|
298
|
+
delivery,
|
|
299
|
+
deliveryLabel: delivery ? t(deliveryLabelKey(delivery.phase)) : "",
|
|
300
|
+
}) : "";
|
|
227
301
|
const focusedDraft = document.activeElement?.matches?.("[data-agent-command-draft]")
|
|
228
302
|
&& composer.contains(document.activeElement)
|
|
229
303
|
&& document.activeElement.dataset.agentCommandDraft === session.id;
|
|
@@ -272,5 +346,12 @@ window.LoadToAgentAppFactories.createDrawer = function createDrawer(context = {}
|
|
|
272
346
|
}
|
|
273
347
|
}
|
|
274
348
|
|
|
275
|
-
return {
|
|
349
|
+
return {
|
|
350
|
+
openDrawer,
|
|
351
|
+
openSubagentConversation,
|
|
352
|
+
openExecutionActivity,
|
|
353
|
+
closeDrawer,
|
|
354
|
+
backToAgentFlow,
|
|
355
|
+
renderDrawer,
|
|
356
|
+
};
|
|
276
357
|
};
|
|
@@ -6,10 +6,10 @@ window.LoadToAgentAppFactories.createDialogEventBindings = function createDialog
|
|
|
6
6
|
const t = (key, params) => window.LoadToAgentI18n.t(key, params);
|
|
7
7
|
const {
|
|
8
8
|
$, $$, state, providerInfo, visibleProviders = () => state.providers, renderProviderRail, scheduleAgentWorkflowConnections, resumeAgentTerminal, loadSessionDetail,
|
|
9
|
-
closeDrawer, renderDrawer, providerPickerHtml, syncRunComposer, openRunModal, closeRunModal, toast, performUiAction,
|
|
9
|
+
closeDrawer, backToAgentFlow, renderDrawer, providerPickerHtml, syncRunComposer, openRunModal, closeRunModal, toast, performUiAction,
|
|
10
10
|
handleRun, trapDialogFocus, currentDialog, selectView, saveRunDraft = () => {}, safeBackdrop = null,
|
|
11
11
|
copyText = async () => false,
|
|
12
|
-
dispatchAgentCommand, controlManagedRun, quickRespond, prepareReassignment, openSubagentConversation,
|
|
12
|
+
dispatchAgentCommand, openAgentTerminal, controlManagedRun, quickRespond, prepareReassignment, openSubagentConversation,
|
|
13
13
|
} = context;
|
|
14
14
|
|
|
15
15
|
function bindRunComposerEvents() {
|
|
@@ -133,8 +133,49 @@ window.LoadToAgentAppFactories.createDialogEventBindings = function createDialog
|
|
|
133
133
|
|
|
134
134
|
function bindDrawerAndGlobalEvents() {
|
|
135
135
|
$("#closeDrawerBtn").addEventListener("click", closeDrawer);
|
|
136
|
+
$("#drawerBackToFlowBtn").addEventListener("click", backToAgentFlow);
|
|
136
137
|
if (safeBackdrop) safeBackdrop($("#drawerBackdrop"), closeDrawer, $("#detailDrawer"));
|
|
137
138
|
else $("#drawerBackdrop").addEventListener("click", closeDrawer);
|
|
139
|
+
const resizeHandle = $("#drawerResizeHandle");
|
|
140
|
+
const setConversationPanelWidth = (nextWidth) => {
|
|
141
|
+
const maximum = Math.max(560, Math.min(860, window.innerWidth - 700));
|
|
142
|
+
const width = Math.max(560, Math.min(maximum, Math.round(nextWidth)));
|
|
143
|
+
document.documentElement.style.setProperty("--conversation-panel-width", `${width}px`);
|
|
144
|
+
resizeHandle.setAttribute("aria-valuemax", String(maximum));
|
|
145
|
+
resizeHandle.setAttribute("aria-valuenow", String(width));
|
|
146
|
+
scheduleAgentWorkflowConnections();
|
|
147
|
+
};
|
|
148
|
+
resizeHandle.addEventListener("pointerdown", (event) => {
|
|
149
|
+
if (state.drawerPresentation !== "context" || event.button !== 0) return;
|
|
150
|
+
event.preventDefault();
|
|
151
|
+
resizeHandle.setPointerCapture?.(event.pointerId);
|
|
152
|
+
document.body.classList.add("conversation-panel-resizing");
|
|
153
|
+
setConversationPanelWidth(window.innerWidth - event.clientX);
|
|
154
|
+
});
|
|
155
|
+
resizeHandle.addEventListener("pointermove", (event) => {
|
|
156
|
+
if (!document.body.classList.contains("conversation-panel-resizing")) return;
|
|
157
|
+
setConversationPanelWidth(window.innerWidth - event.clientX);
|
|
158
|
+
});
|
|
159
|
+
const finishPanelResize = (event) => {
|
|
160
|
+
if (!document.body.classList.contains("conversation-panel-resizing")) return;
|
|
161
|
+
document.body.classList.remove("conversation-panel-resizing");
|
|
162
|
+
try { resizeHandle.releasePointerCapture?.(event.pointerId); } catch {}
|
|
163
|
+
scheduleAgentWorkflowConnections();
|
|
164
|
+
};
|
|
165
|
+
resizeHandle.addEventListener("pointerup", finishPanelResize);
|
|
166
|
+
resizeHandle.addEventListener("pointercancel", finishPanelResize);
|
|
167
|
+
resizeHandle.addEventListener("keydown", (event) => {
|
|
168
|
+
if (state.drawerPresentation !== "context" || !["ArrowLeft", "ArrowRight", "Home", "End"].includes(event.key)) return;
|
|
169
|
+
event.preventDefault();
|
|
170
|
+
const current = Number.parseFloat(getComputedStyle(document.documentElement).getPropertyValue("--conversation-panel-width"))
|
|
171
|
+
|| $("#detailDrawer").getBoundingClientRect().width;
|
|
172
|
+
const next = event.key === "Home"
|
|
173
|
+
? 560
|
|
174
|
+
: event.key === "End"
|
|
175
|
+
? 860
|
|
176
|
+
: current + (event.key === "ArrowLeft" ? 32 : -32);
|
|
177
|
+
setConversationPanelWidth(next);
|
|
178
|
+
});
|
|
138
179
|
$(".drawer-tabs").addEventListener("click", (event) => {
|
|
139
180
|
const tab = event.target.closest("[data-tab]");
|
|
140
181
|
if (tab) {
|
|
@@ -163,14 +204,23 @@ window.LoadToAgentAppFactories.createDialogEventBindings = function createDialog
|
|
|
163
204
|
$("#detailDrawer").addEventListener("click", async (event) => {
|
|
164
205
|
const subagent = event.target.closest("[data-open-subagent-chat]");
|
|
165
206
|
if (subagent) {
|
|
166
|
-
openSubagentConversation(subagent.dataset.openSubagentChat);
|
|
207
|
+
openSubagentConversation(subagent.dataset.openSubagentChat, { presentation: state.drawerPresentation });
|
|
167
208
|
return;
|
|
168
209
|
}
|
|
169
|
-
const
|
|
170
|
-
if (
|
|
171
|
-
|
|
210
|
+
const inputMode = event.target.closest("[data-agent-command-input-mode]");
|
|
211
|
+
if (inputMode) {
|
|
212
|
+
const sessionId = inputMode.dataset.agentCommandSession;
|
|
213
|
+
const nextMode = inputMode.dataset.agentCommandInputMode;
|
|
214
|
+
state.agentCommandInputModes.set(sessionId, nextMode);
|
|
172
215
|
renderDrawer();
|
|
173
|
-
requestAnimationFrame(() =>
|
|
216
|
+
requestAnimationFrame(() => {
|
|
217
|
+
const target = nextMode === "terminal"
|
|
218
|
+
? $("#detailDrawer")?.querySelector(`[data-agent-command-draft="${CSS.escape(sessionId)}"]`)
|
|
219
|
+
: $("#detailDrawer")?.querySelector(
|
|
220
|
+
`[data-agent-command-session="${CSS.escape(sessionId)}"][data-agent-command-input-mode="terminal"]`,
|
|
221
|
+
);
|
|
222
|
+
target?.focus({ preventScroll: true });
|
|
223
|
+
});
|
|
174
224
|
return;
|
|
175
225
|
}
|
|
176
226
|
const copy = event.target.closest("[data-copy-text]");
|
|
@@ -178,6 +228,11 @@ window.LoadToAgentAppFactories.createDialogEventBindings = function createDialog
|
|
|
178
228
|
await copyText(copy.dataset.copyText);
|
|
179
229
|
return;
|
|
180
230
|
}
|
|
231
|
+
const terminal = event.target.closest("[data-agent-terminal-open]");
|
|
232
|
+
if (terminal) {
|
|
233
|
+
await openAgentTerminal(terminal.dataset.agentTerminalOpen);
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
181
236
|
const resume = event.target.closest("[data-resume-agent]");
|
|
182
237
|
if (resume) {
|
|
183
238
|
if (resume.dataset.busy === "true") return;
|
|
@@ -278,7 +333,10 @@ window.LoadToAgentAppFactories.createDialogEventBindings = function createDialog
|
|
|
278
333
|
} else if (!$("#runModal").classList.contains("hidden")) closeRunModal();
|
|
279
334
|
else closeDrawer();
|
|
280
335
|
});
|
|
281
|
-
window.addEventListener("resize",
|
|
336
|
+
window.addEventListener("resize", () => {
|
|
337
|
+
scheduleAgentWorkflowConnections();
|
|
338
|
+
if (window.innerWidth < 1280 && state.drawerPresentation === "context") closeDrawer(false);
|
|
339
|
+
});
|
|
282
340
|
}
|
|
283
341
|
|
|
284
342
|
function bindDialogAndGlobalEvents() {
|
|
@@ -218,12 +218,6 @@ window.LoadToAgentAppFactories.createSessionEventBindings = function createSessi
|
|
|
218
218
|
|
|
219
219
|
function bindManagementEvents() {
|
|
220
220
|
$("#operationsOverview").addEventListener("click", async (event) => {
|
|
221
|
-
const route = event.target.closest("[data-agent-command-route]");
|
|
222
|
-
if (route) {
|
|
223
|
-
state.agentCommandRoutes.set(route.dataset.agentCommandSession, route.dataset.agentCommandRoute);
|
|
224
|
-
renderSessions("route");
|
|
225
|
-
return;
|
|
226
|
-
}
|
|
227
221
|
const intervention = event.target.closest("[data-supervision-intervention-open]");
|
|
228
222
|
if (intervention) {
|
|
229
223
|
const details = $("#operationsOverview")?.querySelector(`.supervision-intervention[data-disclosure-key="supervision:command:${CSS.escape(intervention.dataset.supervisionInterventionOpen)}"]`);
|
|
@@ -290,12 +284,6 @@ window.LoadToAgentAppFactories.createSessionEventBindings = function createSessi
|
|
|
290
284
|
dispatchAgentCommand(form.dataset.agentCommandForm, form);
|
|
291
285
|
});
|
|
292
286
|
$("#attentionInbox").addEventListener("click", async (event) => {
|
|
293
|
-
const route = event.target.closest("[data-agent-command-route]");
|
|
294
|
-
if (route) {
|
|
295
|
-
state.agentCommandRoutes.set(route.dataset.agentCommandSession, route.dataset.agentCommandRoute);
|
|
296
|
-
renderSessions("route");
|
|
297
|
-
return;
|
|
298
|
-
}
|
|
299
287
|
const filter = event.target.closest("[data-management-inbox-filter]");
|
|
300
288
|
if (filter) {
|
|
301
289
|
state.managementFilter = filter.dataset.managementInboxFilter;
|
|
@@ -446,14 +434,6 @@ window.LoadToAgentAppFactories.createSessionEventBindings = function createSessi
|
|
|
446
434
|
}
|
|
447
435
|
return;
|
|
448
436
|
}
|
|
449
|
-
const route = event.target.closest("[data-agent-command-route]");
|
|
450
|
-
if (route) {
|
|
451
|
-
event.stopPropagation();
|
|
452
|
-
state.agentCommandRoutes.set(route.dataset.agentCommandSession, route.dataset.agentCommandRoute);
|
|
453
|
-
renderSessions("route");
|
|
454
|
-
requestAnimationFrame(() => $("#liveSessionGrid")?.querySelector(`[data-agent-command-session="${CSS.escape(route.dataset.agentCommandSession)}"][data-agent-command-route="${CSS.escape(route.dataset.agentCommandRoute)}"]`)?.focus({ preventScroll: true }));
|
|
455
|
-
return;
|
|
456
|
-
}
|
|
457
437
|
const copy = event.target.closest("[data-copy-text]");
|
|
458
438
|
if (copy) {
|
|
459
439
|
event.stopPropagation();
|
|
@@ -520,18 +500,18 @@ window.LoadToAgentAppFactories.createSessionEventBindings = function createSessi
|
|
|
520
500
|
const open = event.target.closest("[data-open-session]");
|
|
521
501
|
if (open) {
|
|
522
502
|
event.stopPropagation();
|
|
523
|
-
openDrawer(open.dataset.openSession);
|
|
503
|
+
openDrawer(open.dataset.openSession, { context: true });
|
|
524
504
|
return;
|
|
525
505
|
}
|
|
526
506
|
const subagentChat = event.target.closest("[data-open-subagent-chat]");
|
|
527
507
|
if (subagentChat) {
|
|
528
508
|
event.stopPropagation();
|
|
529
|
-
openSubagentConversation(subagentChat.dataset.openSubagentChat);
|
|
509
|
+
openSubagentConversation(subagentChat.dataset.openSubagentChat, { context: true });
|
|
530
510
|
return;
|
|
531
511
|
}
|
|
532
512
|
const node = event.target.closest("[data-graph-focus]");
|
|
533
513
|
if (!node) return;
|
|
534
|
-
if (state.graphFocusId === node.dataset.graphFocus) openDrawer(node.dataset.graphFocus);
|
|
514
|
+
if (state.graphFocusId === node.dataset.graphFocus) openDrawer(node.dataset.graphFocus, { context: true });
|
|
535
515
|
else {
|
|
536
516
|
state.graphFocusId = node.dataset.graphFocus;
|
|
537
517
|
renderSessions("focus");
|
package/renderer/app.js
CHANGED
|
@@ -30,6 +30,7 @@ window.LoadToAgentAppFactories.createCore = function createCore(context = {}) {
|
|
|
30
30
|
selectedId: null,
|
|
31
31
|
drawerTab: "chat",
|
|
32
32
|
drawerMode: "session",
|
|
33
|
+
drawerPresentation: "modal",
|
|
33
34
|
drawerExecutionId: null,
|
|
34
35
|
runProvider: "claude",
|
|
35
36
|
details: new Map(),
|
|
@@ -48,6 +49,7 @@ window.LoadToAgentAppFactories.createCore = function createCore(context = {}) {
|
|
|
48
49
|
agentCommandDrafts: new Map(),
|
|
49
50
|
agentCommandTargets: new Map(),
|
|
50
51
|
agentCommandRoutes: new Map(),
|
|
52
|
+
agentCommandInputModes: new Map(),
|
|
51
53
|
agentCommandSending: new Set(),
|
|
52
54
|
pendingConversationMessages: new Map(),
|
|
53
55
|
stopRequests: new Set(),
|
|
@@ -258,6 +260,11 @@ window.LoadToAgentAppFactories.createCore = function createCore(context = {}) {
|
|
|
258
260
|
else $("#mobileMoreBtn")?.removeAttribute("aria-current");
|
|
259
261
|
}
|
|
260
262
|
function selectView(view, options = {}) {
|
|
263
|
+
if (
|
|
264
|
+
view !== state.view
|
|
265
|
+
&& $("#detailDrawer")?.classList.contains("open")
|
|
266
|
+
&& $("#detailDrawer")?.dataset.presentation !== "modal"
|
|
267
|
+
) context.closeDrawer?.(false);
|
|
261
268
|
state.view = view;
|
|
262
269
|
state.managementFilter = view === "waiting" ? (options.managementFilter || "all") : "all";
|
|
263
270
|
state.visibleLimit = 30;
|
|
@@ -285,7 +292,10 @@ window.LoadToAgentAppFactories.createCore = function createCore(context = {}) {
|
|
|
285
292
|
if (!$("#shortcutHelpModal")?.classList.contains("hidden")) return $("#shortcutHelpModal");
|
|
286
293
|
if (!$("#runModal").classList.contains("hidden")) return $("#runModal");
|
|
287
294
|
if (!$("#tmuxCreateModal").classList.contains("hidden")) return $("#tmuxCreateModal");
|
|
288
|
-
if (
|
|
295
|
+
if (
|
|
296
|
+
$("#detailDrawer").classList.contains("open")
|
|
297
|
+
&& $("#detailDrawer").dataset.presentation === "modal"
|
|
298
|
+
) return $("#detailDrawer");
|
|
289
299
|
return null;
|
|
290
300
|
}
|
|
291
301
|
function dialogFocusable(dialog) {
|
|
@@ -236,6 +236,24 @@
|
|
|
236
236
|
"agent.desktop_app": {"ko":"{provider} 데스크톱 앱","en":"{provider} desktop app","zh-CN":"{provider} 桌面应用"},
|
|
237
237
|
"agent.desktop": {"ko":"데스크톱","en":"desktop","zh-CN":"桌面"},
|
|
238
238
|
"agent.direct_status": {"ko":"입력 채널 연결됨 · {target}","en":"Input channel connected · {target}","zh-CN":"输入通道已连接 · {target}"},
|
|
239
|
+
"agent.input_mode_label": {"ko":"전송 대상","en":"Send to","zh-CN":"发送到"},
|
|
240
|
+
"agent.input_mode_ai": {"ko":"AI에게 요청","en":"Ask AI","zh-CN":"向 AI 提问"},
|
|
241
|
+
"agent.input_mode_terminal": {"ko":"터미널 명령","en":"Terminal command","zh-CN":"终端命令"},
|
|
242
|
+
"agent.return_to_ai_input": {"ko":"대화 입력으로 돌아가기","en":"Back to conversation input","zh-CN":"返回对话输入"},
|
|
243
|
+
"agent.terminal_direct_input": {"ko":"터미널 직접 입력","en":"Direct terminal input","zh-CN":"终端直接输入"},
|
|
244
|
+
"agent.terminal_input_help": {"ko":"이 입력은 {target} 터미널에 직접 전달됩니다.","en":"This input is sent directly to the {target} terminal.","zh-CN":"此输入将直接发送到 {target} 终端。"},
|
|
245
|
+
"agent.ai_input_help": {"ko":"현재 {provider} 대화로 보냅니다.","en":"Send to the current {provider} conversation.","zh-CN":"发送到当前 {provider} 对话。"},
|
|
246
|
+
"agent.send_terminal": {"ko":"터미널로 보내기 ↵","en":"Send to terminal ↵","zh-CN":"发送到终端 ↵"},
|
|
247
|
+
"agent.send_request": {"ko":"보내기 ↵","en":"Send ↵","zh-CN":"发送 ↵"},
|
|
248
|
+
"agent.terminal_placeholder": {"ko":"실행할 터미널 명령을 입력하세요","en":"Enter a terminal command to run","zh-CN":"输入要执行的终端命令"},
|
|
249
|
+
"agent.delivery_receipt_help": {"ko":"실제 대화 기록과 실행 신호를 계속 확인합니다.","en":"The actual conversation log and execution signals stay under observation.","zh-CN":"将持续检查实际对话记录和执行信号。"},
|
|
250
|
+
"agent.reconnect_terminal": {"ko":"터미널 다시 연결","en":"Reconnect terminal","zh-CN":"重新连接终端"},
|
|
251
|
+
"agent.connected_terminal": {"ko":"연결된 터미널","en":"Connected terminal","zh-CN":"已连接的终端"},
|
|
252
|
+
"agent.terminal_connection": {"ko":"터미널 연결","en":"Terminal connection","zh-CN":"终端连接"},
|
|
253
|
+
"agent.terminal_session_active": {"ko":"{target} · 세션 유지 중","en":"{target} · session active","zh-CN":"{target} · 会话保持中"},
|
|
254
|
+
"agent.terminal_unavailable": {"ko":"연결하거나 재개할 수 있는 터미널이 없습니다.","en":"No terminal is available to connect or resume.","zh-CN":"没有可连接或恢复的终端。"},
|
|
255
|
+
"agent.terminal_command_sent_background": {"ko":"터미널 명령을 백그라운드 세션에 보냈습니다.","en":"Terminal command sent to the background session.","zh-CN":"终端命令已发送到后台会话。"},
|
|
256
|
+
"agent.terminal_command_sent": {"ko":"{target}에 터미널 명령을 보냈습니다.","en":"Terminal command sent to {target}.","zh-CN":"终端命令已发送到 {target}。"},
|
|
239
257
|
"agent.choose_terminal_count": {"ko":"{count}개 터미널 중 선택","en":"choose from {count} terminals","zh-CN":"从 {count} 个终端中选择"},
|
|
240
258
|
"agent.handoff_status": {"ko":"외부에서 실행 중 · 관리 터미널로 전환 가능","en":"Running externally · can switch to a managed terminal","zh-CN":"正在外部运行 · 可切换到托管终端"},
|
|
241
259
|
"agent.resume_status": {"ko":"원래 터미널 종료됨 · 같은 AI 대화를 새 터미널에서 재개 가능","en":"Original terminal ended · the same AI conversation can resume in a new terminal","zh-CN":"原终端已结束 · 可在新终端恢复同一 AI 对话"},
|
|
@@ -317,6 +335,13 @@
|
|
|
317
335
|
"run.recheck_installation": {"ko":"설치 상태 다시 확인","en":"Check installation again","zh-CN":"重新检查安装状态"},
|
|
318
336
|
"run.ai_installation_required": {"ko":"AI 설치가 필요합니다","en":"An AI CLI must be installed","zh-CN":"需要安装 AI CLI"},
|
|
319
337
|
"run.preparing": {"ko":"AI 작업을 준비하는 중…","en":"Preparing the AI task…","zh-CN":"正在准备 AI 任务…"},
|
|
338
|
+
"drawer.resize_panel": {"ko":"대화 패널 너비 조절","en":"Resize conversation panel","zh-CN":"调整对话面板宽度"},
|
|
339
|
+
"drawer.back_to_flow": {"ko":"에이전트 흐름으로 돌아가기","en":"Back to agent flow","zh-CN":"返回智能体流程"},
|
|
340
|
+
"drawer.agent_flow": {"ko":"에이전트 흐름","en":"Agent flow","zh-CN":"智能体流程"},
|
|
341
|
+
"drawer.focus_open": {"ko":"대화 집중 모드 열기","en":"Open conversation focus mode","zh-CN":"打开对话专注模式"},
|
|
342
|
+
"drawer.focus_exit": {"ko":"에이전트 흐름과 나란히 보기","en":"Show beside agent flow","zh-CN":"与智能体流程并排显示"},
|
|
343
|
+
"drawer.focus_mode": {"ko":"집중 모드","en":"Focus mode","zh-CN":"专注模式"},
|
|
344
|
+
"drawer.side_by_side": {"ko":"나란히 보기","en":"Side by side","zh-CN":"并排查看"},
|
|
320
345
|
"drawer.subagent_title": {"ko":"{name} · 도움 AI 작업 기록","en":"{name} · Helper-AI work history","zh-CN":"{name} · 协助 AI 工作记录"},
|
|
321
346
|
"drawer.stop_requested": {"ko":"중지 요청 중…","en":"Requesting stop…","zh-CN":"正在请求停止…"},
|
|
322
347
|
"drawer.stop_run": {"ko":"■ 실행 중지","en":"■ Stop run","zh-CN":"■ 停止运行"},
|
|
@@ -799,13 +824,21 @@
|
|
|
799
824
|
"terminal.session.confirm_end": {"ko":"{title} 세션과 실행 중인 프로세스를 끝낼까요?\n이 작업은 터미널을 숨기는 것이 아니라 실제 세션을 종료합니다.","en":"End the {title} session and its running process?\nThis ends the actual session rather than merely hiding the terminal.","zh-CN":"要结束 {title} 会话及其正在运行的进程吗?\n此操作会真正结束会话,而不是仅隐藏终端。"},
|
|
800
825
|
"terminal.session.confirm_end_ai": {"ko":"{title} AI 세션과 실행 중인 작업을 완전히 종료할까요?\n터미널 화면만 닫으려면 ‘터미널 화면 닫기’를 사용하세요.","en":"Completely end the {title} AI session and its running work?\nUse ‘Close terminal view’ to hide only the terminal.","zh-CN":"要完全结束 {title} AI 会话及其正在运行的任务吗?\n若只想隐藏终端,请使用“关闭终端视图”。"},
|
|
801
826
|
"terminal.session.ended": {"ko":"터미널 세션을 종료했습니다.","en":"Terminal session ended.","zh-CN":"终端会话已结束。"},
|
|
827
|
+
"terminal.session.reconnected": {"ko":"실행 중인 tmux 세션에 다시 연결했습니다.","en":"Reconnected to the running tmux session.","zh-CN":"已重新连接到正在运行的 tmux 会话。"},
|
|
802
828
|
"terminal.session.restarted": {"ko":"명령창을 다시 시작했습니다.","en":"Terminal restarted.","zh-CN":"终端已重新启动。"},
|
|
829
|
+
"terminal.session.stopped": {"ko":"tmux 작업을 종료하고 세션 기록은 남겼습니다.","en":"Stopped the tmux work and kept the session record.","zh-CN":"已停止 tmux 工作并保留会话记录。"},
|
|
803
830
|
"terminal.signal.cleared": {"ko":"화면을 정리했습니다.","en":"Screen cleared.","zh-CN":"屏幕已清空。"},
|
|
804
831
|
"terminal.signal.interrupt_sent": {"ko":"Ctrl+C를 보냈습니다.","en":"Sent Ctrl+C.","zh-CN":"已发送 Ctrl+C。"},
|
|
805
832
|
"terminal.signal.key_sent": {"ko":"{key}를 보냈습니다.","en":"Sent {key}.","zh-CN":"已发送 {key}。"},
|
|
806
833
|
"terminal.status.exited": {"ko":"끝남","en":"Ended","zh-CN":"已结束"},
|
|
807
834
|
"terminal.status.failed": {"ko":"열지 못함","en":"Failed to open","zh-CN":"打开失败"},
|
|
808
835
|
"terminal.status.running": {"ko":"세션 유지 중","en":"Session active","zh-CN":"会话保持中"},
|
|
836
|
+
"terminal.status.detached": {"ko":"화면 분리됨","en":"View detached","zh-CN":"界面已分离"},
|
|
837
|
+
"terminal.status.stopped": {"ko":"작업 중지됨","en":"Work stopped","zh-CN":"工作已停止"},
|
|
838
|
+
"terminal.detached_work_continues": {"ko":"화면만 분리되었습니다. tmux 작업은 백그라운드에서 계속됩니다.","en":"Only the view is detached. The tmux work continues in the background.","zh-CN":"仅界面已分离,tmux 工作仍在后台继续。"},
|
|
839
|
+
"terminal.stopped_record_kept": {"ko":"작업은 종료됐고 세션 기록은 보존되어 있습니다.","en":"The work has stopped and the session record is preserved.","zh-CN":"工作已停止,会话记录已保留。"},
|
|
840
|
+
"terminal.reconnect": {"ko":"기존 작업 다시 연결","en":"Reconnect existing work","zh-CN":"重新连接现有工作"},
|
|
841
|
+
"terminal.remove_session_record": {"ko":"세션 기록 제거","en":"Remove session record","zh-CN":"移除会话记录"},
|
|
809
842
|
"terminal.monitor.attention_count": {"ko":"확인 필요 {count}","en":"{count} need attention","zh-CN":"{count} 个需要确认"},
|
|
810
843
|
"terminal.view.font_size": {"ko":"터미널 글자 크기","en":"Terminal font size","zh-CN":"终端字号"},
|
|
811
844
|
"terminal.view.font_decrease": {"ko":"터미널 글자 작게","en":"Decrease terminal font","zh-CN":"减小终端字号"},
|
package/renderer/index.html
CHANGED
|
@@ -389,7 +389,9 @@
|
|
|
389
389
|
|
|
390
390
|
<div id="drawerBackdrop" class="drawer-backdrop hidden"></div>
|
|
391
391
|
<aside id="detailDrawer" class="detail-drawer" role="dialog" aria-modal="true" aria-labelledby="drawerTitle" aria-hidden="true" tabindex="-1" inert>
|
|
392
|
+
<div id="drawerResizeHandle" class="drawer-resize-handle hidden" role="separator" aria-orientation="vertical" aria-label="대화 패널 너비 조절" data-i18n-aria-label="drawer.resize_panel" aria-valuemin="560" aria-valuemax="860" aria-valuenow="640" tabindex="0"></div>
|
|
392
393
|
<div class="drawer-head">
|
|
394
|
+
<button id="drawerBackToFlowBtn" class="drawer-flow-button hidden" type="button" aria-label="에이전트 흐름으로 돌아가기" data-i18n-aria-label="drawer.back_to_flow"><span aria-hidden="true">←</span><b data-i18n="drawer.agent_flow">에이전트 흐름</b></button>
|
|
393
395
|
<div id="drawerProviderMark" class="provider-mark">AI</div>
|
|
394
396
|
<div class="drawer-title"><span id="drawerProvider" data-i18n="ui.ai_task">AI 작업</span><h2 id="drawerTitle" data-i18n="drawer.title">작업 자세히 보기</h2></div>
|
|
395
397
|
<button id="closeDrawerBtn" class="close-button" type="button" aria-label="닫기" data-i18n-aria-label="common.close">×</button>
|