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.
@@ -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 openDrawer(id) {
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
- clearTimeout(motionState.drawerTimer);
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(() => $("#closeDrawerBtn").focus({ preventScroll: true }), 0);
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
- clearTimeout(motionState.drawerTimer);
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(() => $("#closeDrawerBtn").focus({ preventScroll: true }), 0);
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
- clearTimeout(motionState.drawerTimer);
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
- setDialogOpenState($("#detailDrawer"), false);
91
- $("#drawerBackdrop").classList.add("closing");
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, { conversation: true }) : "";
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 { openDrawer, openSubagentConversation, openExecutionActivity, closeDrawer, renderDrawer };
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 route = event.target.closest("[data-agent-command-route]");
170
- if (route) {
171
- state.agentCommandRoutes.set(route.dataset.agentCommandSession, route.dataset.agentCommandRoute);
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(() => $("#detailDrawer")?.querySelector(`[data-agent-command-session="${CSS.escape(route.dataset.agentCommandSession)}"][data-agent-command-route="${CSS.escape(route.dataset.agentCommandRoute)}"]`)?.focus({ preventScroll: true }));
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", scheduleAgentWorkflowConnections);
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 ($("#detailDrawer").classList.contains("open")) return $("#detailDrawer");
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":"减小终端字号"},
@@ -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>