codex-autorunner 1.0.0__py3-none-any.whl → 1.2.0__py3-none-any.whl
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.
- codex_autorunner/__init__.py +12 -1
- codex_autorunner/agents/codex/harness.py +1 -1
- codex_autorunner/agents/opencode/client.py +113 -4
- codex_autorunner/agents/opencode/constants.py +3 -0
- codex_autorunner/agents/opencode/harness.py +6 -1
- codex_autorunner/agents/opencode/runtime.py +59 -18
- codex_autorunner/agents/opencode/supervisor.py +4 -0
- codex_autorunner/agents/registry.py +36 -7
- codex_autorunner/bootstrap.py +226 -4
- codex_autorunner/cli.py +5 -1174
- codex_autorunner/codex_cli.py +20 -84
- codex_autorunner/core/__init__.py +20 -0
- codex_autorunner/core/about_car.py +119 -1
- codex_autorunner/core/app_server_ids.py +59 -0
- codex_autorunner/core/app_server_threads.py +17 -2
- codex_autorunner/core/app_server_utils.py +165 -0
- codex_autorunner/core/archive.py +349 -0
- codex_autorunner/core/codex_runner.py +6 -2
- codex_autorunner/core/config.py +433 -4
- codex_autorunner/core/context_awareness.py +38 -0
- codex_autorunner/core/docs.py +0 -122
- codex_autorunner/core/drafts.py +58 -4
- codex_autorunner/core/exceptions.py +4 -0
- codex_autorunner/core/filebox.py +265 -0
- codex_autorunner/core/flows/controller.py +96 -2
- codex_autorunner/core/flows/models.py +13 -0
- codex_autorunner/core/flows/reasons.py +52 -0
- codex_autorunner/core/flows/reconciler.py +134 -0
- codex_autorunner/core/flows/runtime.py +57 -4
- codex_autorunner/core/flows/store.py +142 -7
- codex_autorunner/core/flows/transition.py +27 -15
- codex_autorunner/core/flows/ux_helpers.py +272 -0
- codex_autorunner/core/flows/worker_process.py +32 -6
- codex_autorunner/core/git_utils.py +62 -0
- codex_autorunner/core/hub.py +291 -20
- codex_autorunner/core/lifecycle_events.py +253 -0
- codex_autorunner/core/notifications.py +14 -2
- codex_autorunner/core/path_utils.py +2 -1
- codex_autorunner/core/pma_audit.py +224 -0
- codex_autorunner/core/pma_context.py +496 -0
- codex_autorunner/core/pma_dispatch_interceptor.py +284 -0
- codex_autorunner/core/pma_lifecycle.py +527 -0
- codex_autorunner/core/pma_queue.py +367 -0
- codex_autorunner/core/pma_safety.py +221 -0
- codex_autorunner/core/pma_state.py +115 -0
- codex_autorunner/core/ports/__init__.py +28 -0
- codex_autorunner/{integrations/agents → core/ports}/agent_backend.py +13 -8
- codex_autorunner/core/ports/backend_orchestrator.py +41 -0
- codex_autorunner/{integrations/agents → core/ports}/run_event.py +23 -6
- codex_autorunner/core/prompt.py +0 -80
- codex_autorunner/core/prompts.py +56 -172
- codex_autorunner/core/redaction.py +0 -4
- codex_autorunner/core/review_context.py +11 -9
- codex_autorunner/core/runner_controller.py +35 -33
- codex_autorunner/core/runner_state.py +147 -0
- codex_autorunner/core/runtime.py +829 -0
- codex_autorunner/core/sqlite_utils.py +13 -4
- codex_autorunner/core/state.py +7 -10
- codex_autorunner/core/state_roots.py +62 -0
- codex_autorunner/core/supervisor_protocol.py +15 -0
- codex_autorunner/core/templates/__init__.py +39 -0
- codex_autorunner/core/templates/git_mirror.py +234 -0
- codex_autorunner/core/templates/provenance.py +56 -0
- codex_autorunner/core/templates/scan_cache.py +120 -0
- codex_autorunner/core/text_delta_coalescer.py +54 -0
- codex_autorunner/core/ticket_linter_cli.py +218 -0
- codex_autorunner/core/ticket_manager_cli.py +494 -0
- codex_autorunner/core/time_utils.py +11 -0
- codex_autorunner/core/types.py +18 -0
- codex_autorunner/core/update.py +4 -5
- codex_autorunner/core/update_paths.py +28 -0
- codex_autorunner/core/usage.py +164 -12
- codex_autorunner/core/utils.py +125 -15
- codex_autorunner/flows/review/__init__.py +17 -0
- codex_autorunner/{core/review.py → flows/review/service.py} +37 -34
- codex_autorunner/flows/ticket_flow/definition.py +52 -3
- codex_autorunner/integrations/agents/__init__.py +11 -19
- codex_autorunner/integrations/agents/backend_orchestrator.py +302 -0
- codex_autorunner/integrations/agents/codex_adapter.py +90 -0
- codex_autorunner/integrations/agents/codex_backend.py +177 -25
- codex_autorunner/integrations/agents/opencode_adapter.py +108 -0
- codex_autorunner/integrations/agents/opencode_backend.py +305 -32
- codex_autorunner/integrations/agents/runner.py +86 -0
- codex_autorunner/integrations/agents/wiring.py +279 -0
- codex_autorunner/integrations/app_server/client.py +7 -60
- codex_autorunner/integrations/app_server/env.py +2 -107
- codex_autorunner/{core/app_server_events.py → integrations/app_server/event_buffer.py} +15 -8
- codex_autorunner/integrations/telegram/adapter.py +65 -0
- codex_autorunner/integrations/telegram/config.py +46 -0
- codex_autorunner/integrations/telegram/constants.py +1 -1
- codex_autorunner/integrations/telegram/doctor.py +228 -6
- codex_autorunner/integrations/telegram/handlers/callbacks.py +7 -0
- codex_autorunner/integrations/telegram/handlers/commands/execution.py +236 -74
- codex_autorunner/integrations/telegram/handlers/commands/files.py +314 -75
- codex_autorunner/integrations/telegram/handlers/commands/flows.py +1496 -71
- codex_autorunner/integrations/telegram/handlers/commands/workspace.py +498 -37
- codex_autorunner/integrations/telegram/handlers/commands_runtime.py +206 -48
- codex_autorunner/integrations/telegram/handlers/commands_spec.py +20 -3
- codex_autorunner/integrations/telegram/handlers/messages.py +27 -1
- codex_autorunner/integrations/telegram/handlers/selections.py +61 -1
- codex_autorunner/integrations/telegram/helpers.py +22 -1
- codex_autorunner/integrations/telegram/runtime.py +9 -4
- codex_autorunner/integrations/telegram/service.py +45 -10
- codex_autorunner/integrations/telegram/state.py +38 -0
- codex_autorunner/integrations/telegram/ticket_flow_bridge.py +338 -43
- codex_autorunner/integrations/telegram/transport.py +13 -4
- codex_autorunner/integrations/templates/__init__.py +27 -0
- codex_autorunner/integrations/templates/scan_agent.py +312 -0
- codex_autorunner/routes/__init__.py +37 -76
- codex_autorunner/routes/agents.py +2 -137
- codex_autorunner/routes/analytics.py +2 -238
- codex_autorunner/routes/app_server.py +2 -131
- codex_autorunner/routes/base.py +2 -596
- codex_autorunner/routes/file_chat.py +4 -833
- codex_autorunner/routes/flows.py +4 -977
- codex_autorunner/routes/messages.py +4 -456
- codex_autorunner/routes/repos.py +2 -196
- codex_autorunner/routes/review.py +2 -147
- codex_autorunner/routes/sessions.py +2 -175
- codex_autorunner/routes/settings.py +2 -168
- codex_autorunner/routes/shared.py +2 -275
- codex_autorunner/routes/system.py +4 -193
- codex_autorunner/routes/usage.py +2 -86
- codex_autorunner/routes/voice.py +2 -119
- codex_autorunner/routes/workspace.py +2 -270
- codex_autorunner/server.py +4 -4
- codex_autorunner/static/agentControls.js +61 -16
- codex_autorunner/static/app.js +126 -14
- codex_autorunner/static/archive.js +826 -0
- codex_autorunner/static/archiveApi.js +37 -0
- codex_autorunner/static/autoRefresh.js +7 -7
- codex_autorunner/static/chatUploads.js +137 -0
- codex_autorunner/static/dashboard.js +224 -171
- codex_autorunner/static/docChatCore.js +185 -13
- codex_autorunner/static/fileChat.js +68 -40
- codex_autorunner/static/fileboxUi.js +159 -0
- codex_autorunner/static/hub.js +114 -131
- codex_autorunner/static/index.html +375 -49
- codex_autorunner/static/messages.js +568 -87
- codex_autorunner/static/notifications.js +255 -0
- codex_autorunner/static/pma.js +1167 -0
- codex_autorunner/static/preserve.js +17 -0
- codex_autorunner/static/settings.js +128 -6
- codex_autorunner/static/smartRefresh.js +52 -0
- codex_autorunner/static/streamUtils.js +57 -0
- codex_autorunner/static/styles.css +9798 -6143
- codex_autorunner/static/tabs.js +152 -11
- codex_autorunner/static/templateReposSettings.js +225 -0
- codex_autorunner/static/terminal.js +18 -0
- codex_autorunner/static/ticketChatActions.js +165 -3
- codex_autorunner/static/ticketChatStream.js +17 -119
- codex_autorunner/static/ticketEditor.js +137 -15
- codex_autorunner/static/ticketTemplates.js +798 -0
- codex_autorunner/static/tickets.js +821 -98
- codex_autorunner/static/turnEvents.js +27 -0
- codex_autorunner/static/turnResume.js +33 -0
- codex_autorunner/static/utils.js +39 -0
- codex_autorunner/static/workspace.js +389 -82
- codex_autorunner/static/workspaceFileBrowser.js +15 -13
- codex_autorunner/surfaces/__init__.py +5 -0
- codex_autorunner/surfaces/cli/__init__.py +6 -0
- codex_autorunner/surfaces/cli/cli.py +2534 -0
- codex_autorunner/surfaces/cli/codex_cli.py +20 -0
- codex_autorunner/surfaces/cli/pma_cli.py +817 -0
- codex_autorunner/surfaces/telegram/__init__.py +3 -0
- codex_autorunner/surfaces/web/__init__.py +1 -0
- codex_autorunner/surfaces/web/app.py +2223 -0
- codex_autorunner/surfaces/web/hub_jobs.py +192 -0
- codex_autorunner/surfaces/web/middleware.py +587 -0
- codex_autorunner/surfaces/web/pty_session.py +370 -0
- codex_autorunner/surfaces/web/review.py +6 -0
- codex_autorunner/surfaces/web/routes/__init__.py +82 -0
- codex_autorunner/surfaces/web/routes/agents.py +138 -0
- codex_autorunner/surfaces/web/routes/analytics.py +284 -0
- codex_autorunner/surfaces/web/routes/app_server.py +132 -0
- codex_autorunner/surfaces/web/routes/archive.py +357 -0
- codex_autorunner/surfaces/web/routes/base.py +615 -0
- codex_autorunner/surfaces/web/routes/file_chat.py +1117 -0
- codex_autorunner/surfaces/web/routes/filebox.py +227 -0
- codex_autorunner/surfaces/web/routes/flows.py +1354 -0
- codex_autorunner/surfaces/web/routes/messages.py +490 -0
- codex_autorunner/surfaces/web/routes/pma.py +1652 -0
- codex_autorunner/surfaces/web/routes/repos.py +197 -0
- codex_autorunner/surfaces/web/routes/review.py +148 -0
- codex_autorunner/surfaces/web/routes/sessions.py +176 -0
- codex_autorunner/surfaces/web/routes/settings.py +169 -0
- codex_autorunner/surfaces/web/routes/shared.py +277 -0
- codex_autorunner/surfaces/web/routes/system.py +196 -0
- codex_autorunner/surfaces/web/routes/templates.py +634 -0
- codex_autorunner/surfaces/web/routes/usage.py +89 -0
- codex_autorunner/surfaces/web/routes/voice.py +120 -0
- codex_autorunner/surfaces/web/routes/workspace.py +271 -0
- codex_autorunner/surfaces/web/runner_manager.py +25 -0
- codex_autorunner/surfaces/web/schemas.py +469 -0
- codex_autorunner/surfaces/web/static_assets.py +490 -0
- codex_autorunner/surfaces/web/static_refresh.py +86 -0
- codex_autorunner/surfaces/web/terminal_sessions.py +78 -0
- codex_autorunner/tickets/__init__.py +8 -1
- codex_autorunner/tickets/agent_pool.py +53 -4
- codex_autorunner/tickets/files.py +37 -16
- codex_autorunner/tickets/lint.py +50 -0
- codex_autorunner/tickets/models.py +6 -1
- codex_autorunner/tickets/outbox.py +50 -2
- codex_autorunner/tickets/runner.py +396 -57
- codex_autorunner/web/__init__.py +5 -1
- codex_autorunner/web/app.py +2 -1949
- codex_autorunner/web/hub_jobs.py +2 -191
- codex_autorunner/web/middleware.py +2 -586
- codex_autorunner/web/pty_session.py +2 -369
- codex_autorunner/web/runner_manager.py +2 -24
- codex_autorunner/web/schemas.py +2 -376
- codex_autorunner/web/static_assets.py +4 -441
- codex_autorunner/web/static_refresh.py +2 -85
- codex_autorunner/web/terminal_sessions.py +2 -77
- codex_autorunner/workspace/paths.py +49 -33
- codex_autorunner-1.2.0.dist-info/METADATA +150 -0
- codex_autorunner-1.2.0.dist-info/RECORD +339 -0
- codex_autorunner/core/adapter_utils.py +0 -21
- codex_autorunner/core/engine.py +0 -2653
- codex_autorunner/core/static_assets.py +0 -55
- codex_autorunner-1.0.0.dist-info/METADATA +0 -246
- codex_autorunner-1.0.0.dist-info/RECORD +0 -251
- /codex_autorunner/{routes → surfaces/web/routes}/terminal_images.py +0 -0
- {codex_autorunner-1.0.0.dist-info → codex_autorunner-1.2.0.dist-info}/WHEEL +0 -0
- {codex_autorunner-1.0.0.dist-info → codex_autorunner-1.2.0.dist-info}/entry_points.txt +0 -0
- {codex_autorunner-1.0.0.dist-info → codex_autorunner-1.2.0.dist-info}/licenses/LICENSE +0 -0
- {codex_autorunner-1.0.0.dist-info → codex_autorunner-1.2.0.dist-info}/top_level.txt +0 -0
|
@@ -1,13 +1,19 @@
|
|
|
1
1
|
// GENERATED FILE - do not edit directly. Source: static_src/
|
|
2
|
-
import { api, flash } from "./utils.js";
|
|
2
|
+
import { api, confirmModal, flash, setButtonLoading } from "./utils.js";
|
|
3
3
|
import { initAgentControls, getSelectedAgent, getSelectedModel, getSelectedReasoning } from "./agentControls.js";
|
|
4
4
|
import { fetchWorkspace, ingestSpecToTickets, listTickets, fetchWorkspaceTree, uploadWorkspaceFiles, downloadWorkspaceZip, createWorkspaceFolder, writeWorkspace, } from "./workspaceApi.js";
|
|
5
|
-
import { applyDraft, discardDraft, fetchPendingDraft, sendFileChat, interruptFileChat, } from "./fileChat.js";
|
|
5
|
+
import { applyDraft, discardDraft, fetchPendingDraft, sendFileChat, interruptFileChat, newClientTurnId, streamTurnEvents, } from "./fileChat.js";
|
|
6
6
|
import { DocEditor } from "./docEditor.js";
|
|
7
7
|
import { WorkspaceFileBrowser } from "./workspaceFileBrowser.js";
|
|
8
8
|
import { createDocChat } from "./docChatCore.js";
|
|
9
|
+
import { initChatPasteUpload } from "./chatUploads.js";
|
|
9
10
|
import { initDocChatVoice } from "./docChatVoice.js";
|
|
10
11
|
import { renderDiff } from "./diffRenderer.js";
|
|
12
|
+
import { createSmartRefresh } from "./smartRefresh.js";
|
|
13
|
+
import { subscribe } from "./bus.js";
|
|
14
|
+
import { isRepoHealthy } from "./health.js";
|
|
15
|
+
import { loadPendingTurn, savePendingTurn, clearPendingTurn } from "./turnResume.js";
|
|
16
|
+
import { resumeFileChatTurn } from "./turnEvents.js";
|
|
11
17
|
const state = {
|
|
12
18
|
target: null,
|
|
13
19
|
content: "",
|
|
@@ -20,6 +26,7 @@ const state = {
|
|
|
20
26
|
};
|
|
21
27
|
const WORKSPACE_CHAT_EVENT_LIMIT = 8;
|
|
22
28
|
const WORKSPACE_CHAT_EVENT_MAX = 50;
|
|
29
|
+
const WORKSPACE_PENDING_KEY = "car.workspace.pendingTurn";
|
|
23
30
|
const workspaceChat = createDocChat({
|
|
24
31
|
idPrefix: "workspace-chat",
|
|
25
32
|
storage: { keyPrefix: "car-workspace-chat-", maxMessages: 50, version: 1 },
|
|
@@ -43,6 +50,34 @@ const workspaceChat = createDocChat({
|
|
|
43
50
|
},
|
|
44
51
|
});
|
|
45
52
|
const WORKSPACE_DOC_KINDS = new Set(["active_context", "decisions", "spec"]);
|
|
53
|
+
const WORKSPACE_REFRESH_REASONS = ["initial", "background", "manual"];
|
|
54
|
+
let workspaceRefreshCount = 0;
|
|
55
|
+
let currentTurnEventsController = null;
|
|
56
|
+
function hashString(value) {
|
|
57
|
+
let hash = 5381;
|
|
58
|
+
for (let i = 0; i < value.length; i += 1) {
|
|
59
|
+
hash = (hash * 33) ^ value.charCodeAt(i);
|
|
60
|
+
}
|
|
61
|
+
return (hash >>> 0).toString(36);
|
|
62
|
+
}
|
|
63
|
+
function workspaceTreeSignature(nodes) {
|
|
64
|
+
const parts = [];
|
|
65
|
+
const walk = (list) => {
|
|
66
|
+
list.forEach((node) => {
|
|
67
|
+
parts.push([
|
|
68
|
+
node.path || "",
|
|
69
|
+
node.type || "",
|
|
70
|
+
node.is_pinned ? "1" : "0",
|
|
71
|
+
node.modified_at || "",
|
|
72
|
+
node.size ?? "",
|
|
73
|
+
].join("|"));
|
|
74
|
+
if (node.children?.length)
|
|
75
|
+
walk(node.children);
|
|
76
|
+
});
|
|
77
|
+
};
|
|
78
|
+
walk(nodes || []);
|
|
79
|
+
return parts.join("::");
|
|
80
|
+
}
|
|
46
81
|
function els() {
|
|
47
82
|
return {
|
|
48
83
|
fileList: document.getElementById("workspace-file-list"),
|
|
@@ -52,6 +87,13 @@ function els() {
|
|
|
52
87
|
statusMobile: document.getElementById("workspace-status-mobile"),
|
|
53
88
|
uploadBtn: document.getElementById("workspace-upload"),
|
|
54
89
|
uploadInput: document.getElementById("workspace-upload-input"),
|
|
90
|
+
mobileMenuToggle: document.getElementById("workspace-mobile-menu-toggle"),
|
|
91
|
+
mobileDropdown: document.getElementById("workspace-mobile-dropdown"),
|
|
92
|
+
mobileUpload: document.getElementById("workspace-mobile-upload"),
|
|
93
|
+
mobileNewFolder: document.getElementById("workspace-mobile-new-folder"),
|
|
94
|
+
mobileNewFile: document.getElementById("workspace-mobile-new-file"),
|
|
95
|
+
mobileDownload: document.getElementById("workspace-mobile-download"),
|
|
96
|
+
mobileGenerate: document.getElementById("workspace-mobile-generate"),
|
|
55
97
|
newFolderBtn: document.getElementById("workspace-new-folder"),
|
|
56
98
|
newFileBtn: document.getElementById("workspace-new-file"),
|
|
57
99
|
downloadAllBtn: document.getElementById("workspace-download-all"),
|
|
@@ -143,6 +185,13 @@ function setStatus(text) {
|
|
|
143
185
|
if (statusMobile)
|
|
144
186
|
statusMobile.textContent = text;
|
|
145
187
|
}
|
|
188
|
+
function setWorkspaceRefreshing(active) {
|
|
189
|
+
const { reloadBtn, reloadBtnMobile } = els();
|
|
190
|
+
workspaceRefreshCount = Math.max(0, workspaceRefreshCount + (active ? 1 : -1));
|
|
191
|
+
const isRefreshing = workspaceRefreshCount > 0;
|
|
192
|
+
setButtonLoading(reloadBtn, isRefreshing);
|
|
193
|
+
setButtonLoading(reloadBtnMobile, isRefreshing);
|
|
194
|
+
}
|
|
146
195
|
function renderPatch() {
|
|
147
196
|
const { patchMain, patchBody, patchSummary, patchMeta, textarea, saveBtn, reloadBtn } = els();
|
|
148
197
|
if (!patchMain || !patchBody)
|
|
@@ -187,15 +236,33 @@ function renderPatch() {
|
|
|
187
236
|
function renderChat() {
|
|
188
237
|
workspaceChat.render();
|
|
189
238
|
}
|
|
239
|
+
function closeMobileMenu() {
|
|
240
|
+
const dropdown = els().mobileDropdown;
|
|
241
|
+
if (dropdown)
|
|
242
|
+
dropdown.classList.add("hidden");
|
|
243
|
+
}
|
|
244
|
+
function toggleMobileMenu() {
|
|
245
|
+
const dropdown = els().mobileDropdown;
|
|
246
|
+
if (dropdown)
|
|
247
|
+
dropdown.classList.toggle("hidden");
|
|
248
|
+
}
|
|
190
249
|
function updateDownloadButton() {
|
|
191
|
-
const { downloadAllBtn } = els();
|
|
192
|
-
if (!downloadAllBtn)
|
|
193
|
-
return;
|
|
250
|
+
const { downloadAllBtn, mobileDownload } = els();
|
|
194
251
|
const currentPath = state.browser?.getCurrentPath() || "";
|
|
195
252
|
const isRoot = !currentPath;
|
|
196
253
|
const folderName = currentPath.split("/").pop() || "";
|
|
197
|
-
|
|
198
|
-
|
|
254
|
+
const download = () => downloadWorkspaceZip(isRoot ? undefined : currentPath);
|
|
255
|
+
if (downloadAllBtn) {
|
|
256
|
+
downloadAllBtn.title = isRoot ? "Download all as ZIP" : `Download ${folderName}/ as ZIP`;
|
|
257
|
+
downloadAllBtn.onclick = download;
|
|
258
|
+
}
|
|
259
|
+
if (mobileDownload) {
|
|
260
|
+
mobileDownload.textContent = isRoot ? "Download ZIP (all)" : `Download ${folderName || "folder"}`;
|
|
261
|
+
mobileDownload.onclick = () => {
|
|
262
|
+
closeMobileMenu();
|
|
263
|
+
download();
|
|
264
|
+
};
|
|
265
|
+
}
|
|
199
266
|
}
|
|
200
267
|
let createMode = null;
|
|
201
268
|
function listFolderPaths(nodes, base = "") {
|
|
@@ -272,7 +339,7 @@ async function handleCreateSubmit() {
|
|
|
272
339
|
flash("File created", "success");
|
|
273
340
|
}
|
|
274
341
|
closeCreateModal();
|
|
275
|
-
await loadFiles(createMode === "file" ? path : state.target?.path || undefined);
|
|
342
|
+
await loadFiles(createMode === "file" ? path : state.target?.path || undefined, "manual");
|
|
276
343
|
if (createMode === "file") {
|
|
277
344
|
state.browser?.select(path);
|
|
278
345
|
}
|
|
@@ -281,12 +348,45 @@ async function handleCreateSubmit() {
|
|
|
281
348
|
flash(err.message || "Failed to create item", "error");
|
|
282
349
|
}
|
|
283
350
|
}
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
const
|
|
289
|
-
|
|
351
|
+
const workspaceTreeRefresh = createSmartRefresh({
|
|
352
|
+
getSignature: (payload) => workspaceTreeSignature(payload.tree || []),
|
|
353
|
+
render: (payload) => {
|
|
354
|
+
state.files = payload.tree;
|
|
355
|
+
const { fileList, fileSelect, breadcrumbs } = els();
|
|
356
|
+
if (!fileList)
|
|
357
|
+
return;
|
|
358
|
+
if (!state.browser) {
|
|
359
|
+
state.browser = new WorkspaceFileBrowser({
|
|
360
|
+
container: fileList,
|
|
361
|
+
selectEl: fileSelect,
|
|
362
|
+
breadcrumbsEl: breadcrumbs,
|
|
363
|
+
onSelect: (file) => {
|
|
364
|
+
state.target = { path: file.path, isPinned: Boolean(file.is_pinned) };
|
|
365
|
+
workspaceChat.setTarget(target());
|
|
366
|
+
void refreshWorkspaceFile(file.path, "manual");
|
|
367
|
+
},
|
|
368
|
+
onPathChange: () => updateDownloadButton(),
|
|
369
|
+
onRefresh: () => loadFiles(state.target?.path, "manual"),
|
|
370
|
+
onConfirm: (message) => window.workspaceConfirm?.(message) ?? confirmModal(message),
|
|
371
|
+
});
|
|
372
|
+
}
|
|
373
|
+
const defaultPath = payload.defaultPath ?? state.target?.path ?? undefined;
|
|
374
|
+
state.browser.setTree(payload.tree, defaultPath || undefined);
|
|
375
|
+
updateDownloadButton();
|
|
376
|
+
if (state.target) {
|
|
377
|
+
workspaceChat.setTarget(target());
|
|
378
|
+
}
|
|
379
|
+
},
|
|
380
|
+
onSkip: () => {
|
|
381
|
+
updateDownloadButton();
|
|
382
|
+
},
|
|
383
|
+
});
|
|
384
|
+
const workspaceContentRefresh = createSmartRefresh({
|
|
385
|
+
getSignature: (payload) => `${payload.path}::${hashString(payload.content || "")}`,
|
|
386
|
+
render: async (payload, ctx) => {
|
|
387
|
+
if (payload.path !== state.target?.path)
|
|
388
|
+
return;
|
|
389
|
+
state.content = payload.content;
|
|
290
390
|
if (state.docEditor) {
|
|
291
391
|
state.docEditor.destroy();
|
|
292
392
|
}
|
|
@@ -298,9 +398,9 @@ async function loadWorkspaceFile(path) {
|
|
|
298
398
|
textarea,
|
|
299
399
|
saveButton: saveBtn,
|
|
300
400
|
statusEl: status,
|
|
301
|
-
onLoad: async () =>
|
|
401
|
+
onLoad: async () => payload.content,
|
|
302
402
|
onSave: async (content) => {
|
|
303
|
-
const saved = await writeWorkspaceContent(path, content);
|
|
403
|
+
const saved = await writeWorkspaceContent(payload.path, content);
|
|
304
404
|
state.content = saved;
|
|
305
405
|
if (saved !== content) {
|
|
306
406
|
textarea.value = saved;
|
|
@@ -309,7 +409,25 @@ async function loadWorkspaceFile(path) {
|
|
|
309
409
|
});
|
|
310
410
|
await loadPendingDraft();
|
|
311
411
|
renderPatch();
|
|
312
|
-
|
|
412
|
+
if (ctx.reason !== "background") {
|
|
413
|
+
setStatus("Loaded");
|
|
414
|
+
}
|
|
415
|
+
},
|
|
416
|
+
});
|
|
417
|
+
async function refreshWorkspaceFile(path, reason = "manual") {
|
|
418
|
+
if (!WORKSPACE_REFRESH_REASONS.includes(reason)) {
|
|
419
|
+
reason = "manual";
|
|
420
|
+
}
|
|
421
|
+
const isInitial = reason === "initial";
|
|
422
|
+
if (isInitial) {
|
|
423
|
+
state.loading = true;
|
|
424
|
+
setStatus("Loading…");
|
|
425
|
+
}
|
|
426
|
+
else {
|
|
427
|
+
setWorkspaceRefreshing(true);
|
|
428
|
+
}
|
|
429
|
+
try {
|
|
430
|
+
await workspaceContentRefresh.refresh(async () => ({ path, content: await readWorkspaceContent(path) }), { reason });
|
|
313
431
|
}
|
|
314
432
|
catch (err) {
|
|
315
433
|
const message = err.message || "Failed to load workspace file";
|
|
@@ -318,6 +436,9 @@ async function loadWorkspaceFile(path) {
|
|
|
318
436
|
}
|
|
319
437
|
finally {
|
|
320
438
|
state.loading = false;
|
|
439
|
+
if (!isInitial) {
|
|
440
|
+
setWorkspaceRefreshing(false);
|
|
441
|
+
}
|
|
321
442
|
}
|
|
322
443
|
}
|
|
323
444
|
async function loadPendingDraft() {
|
|
@@ -327,7 +448,7 @@ async function loadPendingDraft() {
|
|
|
327
448
|
async function reloadWorkspace() {
|
|
328
449
|
if (!state.target)
|
|
329
450
|
return;
|
|
330
|
-
await
|
|
451
|
+
await refreshWorkspaceFile(state.target.path, "manual");
|
|
331
452
|
}
|
|
332
453
|
async function maybeShowGenerate() {
|
|
333
454
|
try {
|
|
@@ -340,9 +461,12 @@ async function maybeShowGenerate() {
|
|
|
340
461
|
catch {
|
|
341
462
|
state.hasTickets = true;
|
|
342
463
|
}
|
|
343
|
-
const
|
|
344
|
-
|
|
345
|
-
|
|
464
|
+
const { generateBtn, mobileGenerate } = els();
|
|
465
|
+
const hidden = state.hasTickets;
|
|
466
|
+
if (generateBtn)
|
|
467
|
+
generateBtn.classList.toggle("hidden", hidden);
|
|
468
|
+
if (mobileGenerate)
|
|
469
|
+
mobileGenerate.classList.toggle("hidden", hidden);
|
|
346
470
|
}
|
|
347
471
|
async function generateTickets() {
|
|
348
472
|
try {
|
|
@@ -360,7 +484,7 @@ async function applyWorkspaceDraft() {
|
|
|
360
484
|
try {
|
|
361
485
|
const isStale = Boolean(state.draft?.is_stale);
|
|
362
486
|
if (isStale) {
|
|
363
|
-
const confirmForce =
|
|
487
|
+
const confirmForce = await confirmModal("This draft is stale because the file changed after it was created. Force apply anyway?");
|
|
364
488
|
if (!confirmForce)
|
|
365
489
|
return;
|
|
366
490
|
}
|
|
@@ -393,6 +517,138 @@ async function discardWorkspaceDraft() {
|
|
|
393
517
|
flash(err.message || "Failed to discard draft", "error");
|
|
394
518
|
}
|
|
395
519
|
}
|
|
520
|
+
function clearTurnEventsStream() {
|
|
521
|
+
if (currentTurnEventsController) {
|
|
522
|
+
try {
|
|
523
|
+
currentTurnEventsController.abort();
|
|
524
|
+
}
|
|
525
|
+
catch {
|
|
526
|
+
// ignore
|
|
527
|
+
}
|
|
528
|
+
currentTurnEventsController = null;
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
function clearPendingTurnState() {
|
|
532
|
+
clearTurnEventsStream();
|
|
533
|
+
clearPendingTurn(WORKSPACE_PENDING_KEY);
|
|
534
|
+
}
|
|
535
|
+
function maybeStartTurnEventsFromUpdate(update) {
|
|
536
|
+
const meta = update;
|
|
537
|
+
const threadId = typeof meta.thread_id === "string" ? meta.thread_id : "";
|
|
538
|
+
const turnId = typeof meta.turn_id === "string" ? meta.turn_id : "";
|
|
539
|
+
const agent = typeof meta.agent === "string" ? meta.agent : undefined;
|
|
540
|
+
if (!threadId || !turnId)
|
|
541
|
+
return;
|
|
542
|
+
clearTurnEventsStream();
|
|
543
|
+
currentTurnEventsController = streamTurnEvents({ agent, threadId, turnId }, {
|
|
544
|
+
onEvent: (event) => {
|
|
545
|
+
workspaceChat.applyAppEvent(event);
|
|
546
|
+
workspaceChat.renderEvents();
|
|
547
|
+
workspaceChat.render();
|
|
548
|
+
},
|
|
549
|
+
});
|
|
550
|
+
}
|
|
551
|
+
function applyChatUpdate(update) {
|
|
552
|
+
const hasDraft = update.has_draft ?? update.hasDraft;
|
|
553
|
+
if (hasDraft === false) {
|
|
554
|
+
state.draft = null;
|
|
555
|
+
if (typeof update.content === "string") {
|
|
556
|
+
state.content = update.content;
|
|
557
|
+
const textarea = els().textarea;
|
|
558
|
+
if (textarea)
|
|
559
|
+
textarea.value = state.content;
|
|
560
|
+
}
|
|
561
|
+
renderPatch();
|
|
562
|
+
}
|
|
563
|
+
else if (hasDraft === true || update.patch || update.content) {
|
|
564
|
+
state.draft = {
|
|
565
|
+
target: target(),
|
|
566
|
+
content: update.content || "",
|
|
567
|
+
patch: update.patch || "",
|
|
568
|
+
agent_message: update.agent_message,
|
|
569
|
+
created_at: update.created_at,
|
|
570
|
+
base_hash: update.base_hash,
|
|
571
|
+
current_hash: update.current_hash,
|
|
572
|
+
is_stale: Boolean(update.is_stale),
|
|
573
|
+
};
|
|
574
|
+
renderPatch();
|
|
575
|
+
}
|
|
576
|
+
if (update.message || update.agent_message) {
|
|
577
|
+
const text = update.message || update.agent_message || "";
|
|
578
|
+
if (text)
|
|
579
|
+
workspaceChat.addAssistantMessage(text);
|
|
580
|
+
}
|
|
581
|
+
workspaceChat.render();
|
|
582
|
+
}
|
|
583
|
+
function applyFinalResult(result) {
|
|
584
|
+
const chatState = workspaceChat.state;
|
|
585
|
+
const status = String(result.status || "");
|
|
586
|
+
if (status === "ok") {
|
|
587
|
+
applyChatUpdate(result);
|
|
588
|
+
chatState.status = "done";
|
|
589
|
+
chatState.error = "";
|
|
590
|
+
chatState.streamText = "";
|
|
591
|
+
clearPendingTurnState();
|
|
592
|
+
renderChat();
|
|
593
|
+
return;
|
|
594
|
+
}
|
|
595
|
+
if (status === "error") {
|
|
596
|
+
const detail = String(result.detail || "Chat failed");
|
|
597
|
+
chatState.status = "error";
|
|
598
|
+
chatState.error = detail;
|
|
599
|
+
renderChat();
|
|
600
|
+
flash(detail, "error");
|
|
601
|
+
clearPendingTurnState();
|
|
602
|
+
return;
|
|
603
|
+
}
|
|
604
|
+
if (status === "interrupted") {
|
|
605
|
+
chatState.status = "interrupted";
|
|
606
|
+
chatState.error = "";
|
|
607
|
+
chatState.streamText = "";
|
|
608
|
+
renderChat();
|
|
609
|
+
clearPendingTurnState();
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
async function resumePendingWorkspaceTurn() {
|
|
613
|
+
const pending = loadPendingTurn(WORKSPACE_PENDING_KEY);
|
|
614
|
+
if (!pending)
|
|
615
|
+
return;
|
|
616
|
+
const chatState = workspaceChat.state;
|
|
617
|
+
chatState.status = "running";
|
|
618
|
+
chatState.statusText = "Recovering previous turn…";
|
|
619
|
+
workspaceChat.render();
|
|
620
|
+
workspaceChat.renderMessages();
|
|
621
|
+
try {
|
|
622
|
+
const outcome = await resumeFileChatTurn(pending.clientTurnId, {
|
|
623
|
+
onEvent: (event) => {
|
|
624
|
+
workspaceChat.applyAppEvent(event);
|
|
625
|
+
workspaceChat.renderEvents();
|
|
626
|
+
workspaceChat.render();
|
|
627
|
+
},
|
|
628
|
+
onResult: (result) => applyFinalResult(result),
|
|
629
|
+
onError: (msg) => {
|
|
630
|
+
chatState.statusText = msg;
|
|
631
|
+
renderChat();
|
|
632
|
+
},
|
|
633
|
+
});
|
|
634
|
+
currentTurnEventsController = outcome.controller;
|
|
635
|
+
if (outcome.lastResult && outcome.lastResult.status) {
|
|
636
|
+
applyFinalResult(outcome.lastResult);
|
|
637
|
+
return;
|
|
638
|
+
}
|
|
639
|
+
// If still running but no event stream yet, poll again shortly.
|
|
640
|
+
if (!outcome.controller) {
|
|
641
|
+
window.setTimeout(() => {
|
|
642
|
+
void resumePendingWorkspaceTurn();
|
|
643
|
+
}, 1000);
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
catch (err) {
|
|
647
|
+
const msg = err.message || "Failed to resume turn";
|
|
648
|
+
chatState.statusText = msg;
|
|
649
|
+
renderChat();
|
|
650
|
+
}
|
|
651
|
+
}
|
|
396
652
|
async function sendChat() {
|
|
397
653
|
const { chatInput, chatSend, chatCancel } = els();
|
|
398
654
|
const message = (chatInput?.value || "").trim();
|
|
@@ -407,6 +663,7 @@ async function sendChat() {
|
|
|
407
663
|
chatState.error = "";
|
|
408
664
|
chatState.statusText = "queued";
|
|
409
665
|
chatState.streamText = "";
|
|
666
|
+
chatState.contextUsagePercent = null;
|
|
410
667
|
workspaceChat.clearEvents();
|
|
411
668
|
workspaceChat.addUserMessage(message);
|
|
412
669
|
renderChat();
|
|
@@ -414,6 +671,14 @@ async function sendChat() {
|
|
|
414
671
|
chatInput.value = "";
|
|
415
672
|
chatSend?.setAttribute("disabled", "true");
|
|
416
673
|
chatCancel?.classList.remove("hidden");
|
|
674
|
+
clearTurnEventsStream();
|
|
675
|
+
const clientTurnId = newClientTurnId("workspace");
|
|
676
|
+
savePendingTurn(WORKSPACE_PENDING_KEY, {
|
|
677
|
+
clientTurnId,
|
|
678
|
+
message,
|
|
679
|
+
startedAtMs: Date.now(),
|
|
680
|
+
target: target(),
|
|
681
|
+
});
|
|
417
682
|
const agent = getSelectedAgent();
|
|
418
683
|
const model = getSelectedModel(agent) || undefined;
|
|
419
684
|
const reasoning = getSelectedReasoning(agent) || undefined;
|
|
@@ -432,43 +697,20 @@ async function sendChat() {
|
|
|
432
697
|
workspaceChat.applyAppEvent(event);
|
|
433
698
|
workspaceChat.renderEvents();
|
|
434
699
|
},
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
if (hasDraft === false) {
|
|
438
|
-
chatState.draft = null;
|
|
439
|
-
if (typeof update.content === "string") {
|
|
440
|
-
state.content = update.content;
|
|
441
|
-
const textarea = els().textarea;
|
|
442
|
-
if (textarea)
|
|
443
|
-
textarea.value = state.content;
|
|
444
|
-
}
|
|
445
|
-
renderPatch();
|
|
446
|
-
}
|
|
447
|
-
else if (hasDraft === true || update.patch || update.content) {
|
|
448
|
-
state.draft = {
|
|
449
|
-
target: target(),
|
|
450
|
-
content: update.content || "",
|
|
451
|
-
patch: update.patch || "",
|
|
452
|
-
agent_message: update.agent_message,
|
|
453
|
-
created_at: update.created_at,
|
|
454
|
-
base_hash: update.base_hash,
|
|
455
|
-
current_hash: update.current_hash,
|
|
456
|
-
is_stale: Boolean(update.is_stale),
|
|
457
|
-
};
|
|
458
|
-
renderPatch();
|
|
459
|
-
}
|
|
460
|
-
if (update.message || update.agent_message) {
|
|
461
|
-
const text = update.message || update.agent_message || "";
|
|
462
|
-
if (text)
|
|
463
|
-
workspaceChat.addAssistantMessage(text);
|
|
464
|
-
}
|
|
700
|
+
onTokenUsage: (percent) => {
|
|
701
|
+
chatState.contextUsagePercent = percent;
|
|
465
702
|
renderChat();
|
|
466
703
|
},
|
|
704
|
+
onUpdate: (update) => {
|
|
705
|
+
applyChatUpdate(update);
|
|
706
|
+
maybeStartTurnEventsFromUpdate(update);
|
|
707
|
+
},
|
|
467
708
|
onError: (msg) => {
|
|
468
709
|
chatState.status = "error";
|
|
469
710
|
chatState.error = msg;
|
|
470
711
|
renderChat();
|
|
471
712
|
flash(msg, "error");
|
|
713
|
+
clearPendingTurnState();
|
|
472
714
|
},
|
|
473
715
|
onInterrupted: (msg) => {
|
|
474
716
|
chatState.status = "interrupted";
|
|
@@ -476,6 +718,7 @@ async function sendChat() {
|
|
|
476
718
|
chatState.streamText = "";
|
|
477
719
|
renderChat();
|
|
478
720
|
flash(msg, "info");
|
|
721
|
+
clearPendingTurnState();
|
|
479
722
|
},
|
|
480
723
|
onDone: () => {
|
|
481
724
|
if (chatState.streamText) {
|
|
@@ -484,8 +727,9 @@ async function sendChat() {
|
|
|
484
727
|
}
|
|
485
728
|
chatState.status = "done";
|
|
486
729
|
renderChat();
|
|
730
|
+
clearPendingTurnState();
|
|
487
731
|
},
|
|
488
|
-
}, { agent, model, reasoning });
|
|
732
|
+
}, { agent, model, reasoning, clientTurnId });
|
|
489
733
|
}
|
|
490
734
|
catch (err) {
|
|
491
735
|
const msg = err.message || "Chat failed";
|
|
@@ -494,6 +738,7 @@ async function sendChat() {
|
|
|
494
738
|
chatStateLocal.error = msg;
|
|
495
739
|
renderChat();
|
|
496
740
|
flash(msg, "error");
|
|
741
|
+
clearPendingTurnState();
|
|
497
742
|
}
|
|
498
743
|
finally {
|
|
499
744
|
chatSend?.removeAttribute("disabled");
|
|
@@ -515,7 +760,9 @@ async function cancelChat() {
|
|
|
515
760
|
}
|
|
516
761
|
chatState.status = "interrupted";
|
|
517
762
|
chatState.streamText = "";
|
|
763
|
+
chatState.contextUsagePercent = null;
|
|
518
764
|
renderChat();
|
|
765
|
+
clearPendingTurnState();
|
|
519
766
|
}
|
|
520
767
|
async function resetThread() {
|
|
521
768
|
if (!state.target)
|
|
@@ -528,7 +775,9 @@ async function resetThread() {
|
|
|
528
775
|
const chatState = workspaceChat.state;
|
|
529
776
|
chatState.messages = [];
|
|
530
777
|
chatState.streamText = "";
|
|
778
|
+
chatState.contextUsagePercent = null;
|
|
531
779
|
workspaceChat.clearEvents();
|
|
780
|
+
clearPendingTurnState();
|
|
532
781
|
renderChat();
|
|
533
782
|
flash("New workspace chat thread", "success");
|
|
534
783
|
}
|
|
@@ -536,36 +785,25 @@ async function resetThread() {
|
|
|
536
785
|
flash(err.message || "Failed to reset thread", "error");
|
|
537
786
|
}
|
|
538
787
|
}
|
|
539
|
-
async function loadFiles(defaultPath) {
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
state.browser = new WorkspaceFileBrowser({
|
|
547
|
-
container: fileList,
|
|
548
|
-
selectEl: fileSelect,
|
|
549
|
-
breadcrumbsEl: breadcrumbs,
|
|
550
|
-
onSelect: (file) => {
|
|
551
|
-
state.target = { path: file.path, isPinned: Boolean(file.is_pinned) };
|
|
552
|
-
workspaceChat.setTarget(target());
|
|
553
|
-
void loadWorkspaceFile(file.path);
|
|
554
|
-
},
|
|
555
|
-
onPathChange: () => updateDownloadButton(),
|
|
556
|
-
onRefresh: () => loadFiles(state.target?.path),
|
|
557
|
-
onConfirm: (message) => window.workspaceConfirm?.(message) ??
|
|
558
|
-
Promise.resolve(confirm(message)),
|
|
559
|
-
});
|
|
788
|
+
async function loadFiles(defaultPath, reason = "manual") {
|
|
789
|
+
if (!WORKSPACE_REFRESH_REASONS.includes(reason)) {
|
|
790
|
+
reason = "manual";
|
|
791
|
+
}
|
|
792
|
+
const isInitial = reason === "initial";
|
|
793
|
+
if (!isInitial) {
|
|
794
|
+
setWorkspaceRefreshing(true);
|
|
560
795
|
}
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
796
|
+
try {
|
|
797
|
+
await workspaceTreeRefresh.refresh(async () => ({ tree: await fetchWorkspaceTree(), defaultPath }), { reason });
|
|
798
|
+
}
|
|
799
|
+
finally {
|
|
800
|
+
if (!isInitial) {
|
|
801
|
+
setWorkspaceRefreshing(false);
|
|
802
|
+
}
|
|
565
803
|
}
|
|
566
804
|
}
|
|
567
805
|
export async function initWorkspace() {
|
|
568
|
-
const { generateBtn, uploadBtn, uploadInput, newFolderBtn, saveBtn, saveBtnMobile, reloadBtn, reloadBtnMobile, patchApply, patchDiscard, patchReload, chatSend, chatCancel, chatNewThread, } = els();
|
|
806
|
+
const { generateBtn, uploadBtn, uploadInput, mobileMenuToggle, mobileDropdown, mobileUpload, mobileNewFolder, mobileNewFile, mobileDownload, mobileGenerate, newFolderBtn, saveBtn, saveBtnMobile, reloadBtn, reloadBtnMobile, patchApply, patchDiscard, patchReload, chatSend, chatCancel, chatNewThread, } = els();
|
|
569
807
|
if (!document.getElementById("workspace"))
|
|
570
808
|
return;
|
|
571
809
|
initAgentControls({
|
|
@@ -578,10 +816,11 @@ export async function initWorkspace() {
|
|
|
578
816
|
inputId: "workspace-chat-input",
|
|
579
817
|
});
|
|
580
818
|
await maybeShowGenerate();
|
|
581
|
-
await loadFiles();
|
|
819
|
+
await loadFiles(undefined, "initial");
|
|
582
820
|
workspaceChat.setTarget(target());
|
|
821
|
+
void resumePendingWorkspaceTurn();
|
|
583
822
|
const reloadEverything = async () => {
|
|
584
|
-
await loadFiles(state.target?.path);
|
|
823
|
+
await loadFiles(state.target?.path, "manual");
|
|
585
824
|
await reloadWorkspace();
|
|
586
825
|
};
|
|
587
826
|
saveBtn?.addEventListener("click", () => void state.docEditor?.save(true));
|
|
@@ -597,7 +836,7 @@ export async function initWorkspace() {
|
|
|
597
836
|
try {
|
|
598
837
|
await uploadWorkspaceFiles(files, subdir || undefined);
|
|
599
838
|
flash(`Uploaded ${files.length} file${files.length === 1 ? "" : "s"}`, "success");
|
|
600
|
-
await loadFiles(state.target?.path);
|
|
839
|
+
await loadFiles(state.target?.path, "manual");
|
|
601
840
|
}
|
|
602
841
|
catch (err) {
|
|
603
842
|
flash(err.message || "Upload failed", "error");
|
|
@@ -606,6 +845,54 @@ export async function initWorkspace() {
|
|
|
606
845
|
uploadInput.value = "";
|
|
607
846
|
}
|
|
608
847
|
});
|
|
848
|
+
// Mobile action sheet
|
|
849
|
+
const handleMobileToggle = (evt) => {
|
|
850
|
+
evt.preventDefault();
|
|
851
|
+
evt.stopPropagation();
|
|
852
|
+
toggleMobileMenu();
|
|
853
|
+
};
|
|
854
|
+
mobileMenuToggle?.addEventListener("pointerdown", handleMobileToggle);
|
|
855
|
+
mobileMenuToggle?.addEventListener("click", (evt) => {
|
|
856
|
+
evt.preventDefault(); // swallow synthetic click after pointerdown
|
|
857
|
+
});
|
|
858
|
+
mobileMenuToggle?.addEventListener("keydown", (evt) => {
|
|
859
|
+
if (evt.key === "Enter" || evt.key === " ") {
|
|
860
|
+
handleMobileToggle(evt);
|
|
861
|
+
}
|
|
862
|
+
});
|
|
863
|
+
document.addEventListener("pointerdown", (evt) => {
|
|
864
|
+
if (!mobileDropdown || mobileDropdown.classList.contains("hidden"))
|
|
865
|
+
return;
|
|
866
|
+
if (evt.target instanceof Node && mobileDropdown.contains(evt.target))
|
|
867
|
+
return;
|
|
868
|
+
closeMobileMenu();
|
|
869
|
+
});
|
|
870
|
+
document.addEventListener("keydown", (evt) => {
|
|
871
|
+
if (evt.key === "Escape" && mobileDropdown && !mobileDropdown.classList.contains("hidden")) {
|
|
872
|
+
closeMobileMenu();
|
|
873
|
+
}
|
|
874
|
+
});
|
|
875
|
+
mobileUpload?.addEventListener("click", () => {
|
|
876
|
+
closeMobileMenu();
|
|
877
|
+
uploadInput?.click();
|
|
878
|
+
});
|
|
879
|
+
mobileNewFolder?.addEventListener("click", () => {
|
|
880
|
+
closeMobileMenu();
|
|
881
|
+
openCreateModal("folder");
|
|
882
|
+
});
|
|
883
|
+
mobileNewFile?.addEventListener("click", () => {
|
|
884
|
+
closeMobileMenu();
|
|
885
|
+
openCreateModal("file");
|
|
886
|
+
});
|
|
887
|
+
mobileDownload?.addEventListener("click", () => {
|
|
888
|
+
closeMobileMenu();
|
|
889
|
+
const currentPath = state.browser?.getCurrentPath() || "";
|
|
890
|
+
downloadWorkspaceZip(currentPath || undefined);
|
|
891
|
+
});
|
|
892
|
+
mobileGenerate?.addEventListener("click", () => {
|
|
893
|
+
closeMobileMenu();
|
|
894
|
+
void generateTickets();
|
|
895
|
+
});
|
|
609
896
|
newFolderBtn?.addEventListener("click", () => openCreateModal("folder"));
|
|
610
897
|
els().newFileBtn?.addEventListener("click", () => openCreateModal("file"));
|
|
611
898
|
generateBtn?.addEventListener("click", () => void generateTickets());
|
|
@@ -623,6 +910,13 @@ export async function initWorkspace() {
|
|
|
623
910
|
void sendChat();
|
|
624
911
|
}
|
|
625
912
|
});
|
|
913
|
+
initChatPasteUpload({
|
|
914
|
+
textarea: chatInput,
|
|
915
|
+
basePath: "/api/filebox",
|
|
916
|
+
box: "inbox",
|
|
917
|
+
insertStyle: "both",
|
|
918
|
+
pathPrefix: ".codex-autorunner/filebox",
|
|
919
|
+
});
|
|
626
920
|
}
|
|
627
921
|
const { createModal, createClose, createCancel, createSubmit } = els();
|
|
628
922
|
createClose?.addEventListener("click", () => closeCreateModal());
|
|
@@ -669,4 +963,17 @@ export async function initWorkspace() {
|
|
|
669
963
|
if (evt.target === confirmModal)
|
|
670
964
|
closeConfirm(false);
|
|
671
965
|
});
|
|
966
|
+
subscribe("repo:health", (payload) => {
|
|
967
|
+
const status = payload?.status || "";
|
|
968
|
+
if (status !== "ok" && status !== "degraded")
|
|
969
|
+
return;
|
|
970
|
+
if (!isRepoHealthy())
|
|
971
|
+
return;
|
|
972
|
+
void loadFiles(state.target?.path, "background");
|
|
973
|
+
const textarea = els().textarea;
|
|
974
|
+
const hasLocalEdits = textarea ? textarea.value !== state.content : false;
|
|
975
|
+
if (state.target && !state.draft && !hasLocalEdits) {
|
|
976
|
+
void refreshWorkspaceFile(state.target.path, "background");
|
|
977
|
+
}
|
|
978
|
+
});
|
|
672
979
|
}
|