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
|
@@ -2,14 +2,16 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Ticket Editor Modal - handles creating, editing, and deleting tickets
|
|
4
4
|
*/
|
|
5
|
-
import { api, flash, updateUrlParams, splitMarkdownFrontmatter } from "./utils.js";
|
|
5
|
+
import { api, confirmModal, flash, updateUrlParams, splitMarkdownFrontmatter } from "./utils.js";
|
|
6
6
|
import { publish } from "./bus.js";
|
|
7
7
|
import { clearTicketChatHistory } from "./ticketChatStorage.js";
|
|
8
|
-
import { setTicketIndex, sendTicketChat, cancelTicketChat, applyTicketPatch, discardTicketPatch, loadTicketPending, renderTicketChat, resetTicketChatState, ticketChatState, } from "./ticketChatActions.js";
|
|
8
|
+
import { setTicketIndex, sendTicketChat, cancelTicketChat, applyTicketPatch, discardTicketPatch, loadTicketPending, renderTicketChat, resetTicketChatState, ticketChatState, resumeTicketPendingTurn, } from "./ticketChatActions.js";
|
|
9
9
|
import { initAgentControls } from "./agentControls.js";
|
|
10
10
|
import { initTicketVoice } from "./ticketVoice.js";
|
|
11
11
|
import { initTicketChatEvents, renderTicketEvents, renderTicketMessages } from "./ticketChatEvents.js";
|
|
12
|
+
import { initChatPasteUpload } from "./chatUploads.js";
|
|
12
13
|
import { DocEditor } from "./docEditor.js";
|
|
14
|
+
import { initTicketTemplates } from "./ticketTemplates.js";
|
|
13
15
|
const DEFAULT_FRONTMATTER = {
|
|
14
16
|
agent: "codex",
|
|
15
17
|
done: false,
|
|
@@ -30,6 +32,74 @@ const state = {
|
|
|
30
32
|
// Autosave debounce timer
|
|
31
33
|
const AUTOSAVE_DELAY_MS = 1000;
|
|
32
34
|
let ticketDocEditor = null;
|
|
35
|
+
let ticketNavCache = [];
|
|
36
|
+
function isTypingTarget(target) {
|
|
37
|
+
if (!(target instanceof HTMLElement))
|
|
38
|
+
return false;
|
|
39
|
+
const tag = target.tagName;
|
|
40
|
+
return tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT" || target.isContentEditable;
|
|
41
|
+
}
|
|
42
|
+
async function fetchTicketList() {
|
|
43
|
+
const data = (await api("/api/flows/ticket_flow/tickets"));
|
|
44
|
+
const list = (data?.tickets || []).filter((ticket) => typeof ticket.index === "number");
|
|
45
|
+
list.sort((a, b) => (a.index ?? 0) - (b.index ?? 0));
|
|
46
|
+
return list;
|
|
47
|
+
}
|
|
48
|
+
async function updateTicketNavButtons() {
|
|
49
|
+
const { prevBtn, nextBtn } = els();
|
|
50
|
+
if (!prevBtn || !nextBtn)
|
|
51
|
+
return;
|
|
52
|
+
if (state.mode !== "edit" || state.ticketIndex == null) {
|
|
53
|
+
prevBtn.disabled = true;
|
|
54
|
+
nextBtn.disabled = true;
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
try {
|
|
58
|
+
const list = await fetchTicketList();
|
|
59
|
+
ticketNavCache = list;
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
// If fetch fails, fall back to the last known list.
|
|
63
|
+
}
|
|
64
|
+
const list = ticketNavCache;
|
|
65
|
+
if (!list.length) {
|
|
66
|
+
prevBtn.disabled = true;
|
|
67
|
+
nextBtn.disabled = true;
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
const idx = list.findIndex((ticket) => ticket.index === state.ticketIndex);
|
|
71
|
+
const hasPrev = idx > 0;
|
|
72
|
+
const hasNext = idx >= 0 && idx < list.length - 1;
|
|
73
|
+
prevBtn.disabled = !hasPrev;
|
|
74
|
+
nextBtn.disabled = !hasNext;
|
|
75
|
+
}
|
|
76
|
+
async function navigateTicket(delta) {
|
|
77
|
+
if (state.mode !== "edit" || state.ticketIndex == null)
|
|
78
|
+
return;
|
|
79
|
+
await performAutosave();
|
|
80
|
+
let list = ticketNavCache;
|
|
81
|
+
if (!list.length) {
|
|
82
|
+
try {
|
|
83
|
+
list = await fetchTicketList();
|
|
84
|
+
ticketNavCache = list;
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
const idx = list.findIndex((ticket) => ticket.index === state.ticketIndex);
|
|
91
|
+
const target = idx >= 0 ? list[idx + delta] : null;
|
|
92
|
+
if (target && target.index != null) {
|
|
93
|
+
try {
|
|
94
|
+
const data = (await api(`/api/flows/ticket_flow/tickets/${target.index}`));
|
|
95
|
+
openTicketEditor(data);
|
|
96
|
+
}
|
|
97
|
+
catch (err) {
|
|
98
|
+
flash(`Failed to navigate to ticket: ${err.message}`, "error");
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
void updateTicketNavButtons();
|
|
102
|
+
}
|
|
33
103
|
function els() {
|
|
34
104
|
return {
|
|
35
105
|
modal: document.getElementById("ticket-editor-modal"),
|
|
@@ -40,6 +110,8 @@ function els() {
|
|
|
40
110
|
newBtn: document.getElementById("ticket-new-btn"),
|
|
41
111
|
insertCheckboxBtn: document.getElementById("ticket-insert-checkbox"),
|
|
42
112
|
undoBtn: document.getElementById("ticket-undo-btn"),
|
|
113
|
+
prevBtn: document.getElementById("ticket-nav-prev"),
|
|
114
|
+
nextBtn: document.getElementById("ticket-nav-next"),
|
|
43
115
|
autosaveStatus: document.getElementById("ticket-autosave-status"),
|
|
44
116
|
// Frontmatter form elements
|
|
45
117
|
fmAgent: document.getElementById("ticket-fm-agent"),
|
|
@@ -234,20 +306,24 @@ function extractFrontmatter(ticket) {
|
|
|
234
306
|
/**
|
|
235
307
|
* Build full markdown content from frontmatter form + body textarea
|
|
236
308
|
*/
|
|
309
|
+
function yamlQuote(value) {
|
|
310
|
+
// Use JSON.stringify for simple, safe double-quoted scalars (handles colons, quotes, newlines).
|
|
311
|
+
return JSON.stringify(value ?? "");
|
|
312
|
+
}
|
|
237
313
|
function buildTicketContent() {
|
|
238
314
|
const { content } = els();
|
|
239
315
|
const fm = getFrontmatterFromForm();
|
|
240
316
|
const body = content?.value || "";
|
|
241
|
-
// Reconstruct frontmatter YAML
|
|
317
|
+
// Reconstruct frontmatter YAML with quoted scalars to tolerate special characters.
|
|
242
318
|
const lines = ["---"];
|
|
243
|
-
lines.push(`agent: ${fm.agent}`);
|
|
319
|
+
lines.push(`agent: ${yamlQuote(fm.agent)}`);
|
|
244
320
|
lines.push(`done: ${fm.done}`);
|
|
245
321
|
if (fm.title)
|
|
246
|
-
lines.push(`title: ${fm.title}`);
|
|
322
|
+
lines.push(`title: ${yamlQuote(fm.title)}`);
|
|
247
323
|
if (fm.model)
|
|
248
|
-
lines.push(`model: ${fm.model}`);
|
|
324
|
+
lines.push(`model: ${yamlQuote(fm.model)}`);
|
|
249
325
|
if (fm.reasoning)
|
|
250
|
-
lines.push(`reasoning: ${fm.reasoning}`);
|
|
326
|
+
lines.push(`reasoning: ${yamlQuote(fm.reasoning)}`);
|
|
251
327
|
lines.push("---");
|
|
252
328
|
lines.push("");
|
|
253
329
|
lines.push(body);
|
|
@@ -414,8 +490,12 @@ async function performAutosave() {
|
|
|
414
490
|
// Notify that tickets changed
|
|
415
491
|
publish("tickets:updated", {});
|
|
416
492
|
}
|
|
417
|
-
catch {
|
|
493
|
+
catch (err) {
|
|
494
|
+
// Surface the failure to the user and let DocEditor keep the "dirty" state
|
|
495
|
+
// so a retry is attempted instead of falsely reporting success.
|
|
418
496
|
setAutosaveStatus("error");
|
|
497
|
+
flash(err?.message || "Failed to save ticket", "error");
|
|
498
|
+
throw err;
|
|
419
499
|
}
|
|
420
500
|
}
|
|
421
501
|
/**
|
|
@@ -526,19 +606,21 @@ export function openTicketEditor(ticket) {
|
|
|
526
606
|
renderTicketChat();
|
|
527
607
|
renderTicketEvents();
|
|
528
608
|
renderTicketMessages();
|
|
609
|
+
void resumeTicketPendingTurn(ticket?.index ?? null);
|
|
529
610
|
state.isOpen = true;
|
|
530
611
|
modal.classList.remove("hidden");
|
|
531
612
|
// Update URL with ticket index
|
|
532
613
|
if (ticket?.index != null) {
|
|
533
614
|
updateUrlParams({ ticket: ticket.index });
|
|
534
615
|
}
|
|
535
|
-
|
|
616
|
+
if (ticket?.path) {
|
|
617
|
+
publish("ticket-editor:opened", { path: ticket.path, index: ticket.index ?? null });
|
|
618
|
+
}
|
|
619
|
+
void updateTicketNavButtons();
|
|
620
|
+
// Focus on title field for new tickets
|
|
536
621
|
if (state.mode === "create" && fmTitle) {
|
|
537
622
|
fmTitle.focus();
|
|
538
623
|
}
|
|
539
|
-
else {
|
|
540
|
-
content.focus();
|
|
541
|
-
}
|
|
542
624
|
}
|
|
543
625
|
/**
|
|
544
626
|
* Close the ticket editor modal (autosaves on close)
|
|
@@ -549,7 +631,9 @@ export function closeTicketEditor() {
|
|
|
549
631
|
return;
|
|
550
632
|
// Autosave on close if there are changes
|
|
551
633
|
if (hasUnsavedChanges()) {
|
|
552
|
-
|
|
634
|
+
// Fire-and-forget: swallow rejection because the error is already flashed
|
|
635
|
+
// inside performAutosave and DocEditor keeps the buffer dirty for retry.
|
|
636
|
+
void performAutosave().catch(() => { });
|
|
553
637
|
}
|
|
554
638
|
// Cancel any running chat
|
|
555
639
|
if (ticketChatState.status === "running") {
|
|
@@ -568,9 +652,12 @@ export function closeTicketEditor() {
|
|
|
568
652
|
ticketDocEditor = null;
|
|
569
653
|
// Clear ticket from URL
|
|
570
654
|
updateUrlParams({ ticket: null });
|
|
655
|
+
void updateTicketNavButtons();
|
|
571
656
|
// Reset chat state
|
|
572
657
|
resetTicketChatState();
|
|
573
658
|
setTicketIndex(null);
|
|
659
|
+
// Notify that editor was closed (for selection state cleanup)
|
|
660
|
+
publish("ticket-editor:closed", {});
|
|
574
661
|
}
|
|
575
662
|
/**
|
|
576
663
|
* Save the current ticket (triggers immediate autosave)
|
|
@@ -586,7 +673,7 @@ export async function deleteTicket() {
|
|
|
586
673
|
flash("Cannot delete: no ticket selected", "error");
|
|
587
674
|
return;
|
|
588
675
|
}
|
|
589
|
-
const confirmed =
|
|
676
|
+
const confirmed = await confirmModal(`Delete TICKET-${String(state.ticketIndex).padStart(3, "0")}.md? This cannot be undone.`);
|
|
590
677
|
if (!confirmed)
|
|
591
678
|
return;
|
|
592
679
|
setButtonsLoading(true);
|
|
@@ -618,7 +705,7 @@ export async function deleteTicket() {
|
|
|
618
705
|
* Initialize the ticket editor - wire up event listeners
|
|
619
706
|
*/
|
|
620
707
|
export function initTicketEditor() {
|
|
621
|
-
const { modal, content, deleteBtn, closeBtn, newBtn, insertCheckboxBtn, undoBtn, fmAgent, fmModel, fmReasoning, fmDone, fmTitle, chatInput, chatSendBtn, chatCancelBtn, patchApplyBtn, patchDiscardBtn, agentSelect, modelSelect, reasoningSelect, } = els();
|
|
708
|
+
const { modal, content, deleteBtn, closeBtn, newBtn, insertCheckboxBtn, undoBtn, prevBtn, nextBtn, fmAgent, fmModel, fmReasoning, fmDone, fmTitle, chatInput, chatSendBtn, chatCancelBtn, patchApplyBtn, patchDiscardBtn, agentSelect, modelSelect, reasoningSelect, } = els();
|
|
622
709
|
if (!modal)
|
|
623
710
|
return;
|
|
624
711
|
// Prevent double initialization
|
|
@@ -635,6 +722,8 @@ export function initTicketEditor() {
|
|
|
635
722
|
void initTicketVoice();
|
|
636
723
|
// Initialize rich chat experience (events toggle, etc.)
|
|
637
724
|
initTicketChatEvents();
|
|
725
|
+
// Initialize ticket templates picker
|
|
726
|
+
initTicketTemplates();
|
|
638
727
|
// Button handlers
|
|
639
728
|
if (deleteBtn)
|
|
640
729
|
deleteBtn.addEventListener("click", () => void deleteTicket());
|
|
@@ -646,6 +735,16 @@ export function initTicketEditor() {
|
|
|
646
735
|
insertCheckboxBtn.addEventListener("click", insertCheckbox);
|
|
647
736
|
if (undoBtn)
|
|
648
737
|
undoBtn.addEventListener("click", undoChange);
|
|
738
|
+
if (prevBtn)
|
|
739
|
+
prevBtn.addEventListener("click", (e) => {
|
|
740
|
+
e.preventDefault();
|
|
741
|
+
void navigateTicket(-1);
|
|
742
|
+
});
|
|
743
|
+
if (nextBtn)
|
|
744
|
+
nextBtn.addEventListener("click", (e) => {
|
|
745
|
+
e.preventDefault();
|
|
746
|
+
void navigateTicket(1);
|
|
747
|
+
});
|
|
649
748
|
// Autosave on content changes
|
|
650
749
|
if (content) {
|
|
651
750
|
content.addEventListener("input", onContentChange);
|
|
@@ -694,6 +793,13 @@ export function initTicketEditor() {
|
|
|
694
793
|
chatInput.style.height = "auto";
|
|
695
794
|
chatInput.style.height = Math.min(chatInput.scrollHeight, 100) + "px";
|
|
696
795
|
});
|
|
796
|
+
initChatPasteUpload({
|
|
797
|
+
textarea: chatInput,
|
|
798
|
+
basePath: "/api/filebox",
|
|
799
|
+
box: "inbox",
|
|
800
|
+
insertStyle: "both",
|
|
801
|
+
pathPrefix: ".codex-autorunner/filebox",
|
|
802
|
+
});
|
|
697
803
|
}
|
|
698
804
|
// Close on backdrop click
|
|
699
805
|
modal.addEventListener("click", (e) => {
|
|
@@ -718,6 +824,22 @@ export function initTicketEditor() {
|
|
|
718
824
|
undoChange();
|
|
719
825
|
}
|
|
720
826
|
});
|
|
827
|
+
// Left/Right arrows navigate between tickets when editor is open and not typing
|
|
828
|
+
document.addEventListener("keydown", (e) => {
|
|
829
|
+
if (!state.isOpen)
|
|
830
|
+
return;
|
|
831
|
+
// Check for navigation keys
|
|
832
|
+
if (e.key !== "ArrowLeft" && e.key !== "ArrowRight")
|
|
833
|
+
return;
|
|
834
|
+
// Don't interfere with typing
|
|
835
|
+
if (isTypingTarget(e.target))
|
|
836
|
+
return;
|
|
837
|
+
// Require Alt modifier for navigation (no Ctrl/Meta/Shift)
|
|
838
|
+
if (!e.altKey || e.ctrlKey || e.metaKey || e.shiftKey)
|
|
839
|
+
return;
|
|
840
|
+
e.preventDefault();
|
|
841
|
+
void navigateTicket(e.key === "ArrowLeft" ? -1 : 1);
|
|
842
|
+
});
|
|
721
843
|
// Enter key creates new TODO checkbox when on a checkbox line
|
|
722
844
|
if (content) {
|
|
723
845
|
content.addEventListener("keydown", (e) => {
|