@phenx-inc/ctlsurf 0.15.0 → 0.15.2

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.
Files changed (31) hide show
  1. package/out/headless/index.mjs +89 -71
  2. package/out/headless/index.mjs.map +3 -3
  3. package/out/main/index.js +90 -72
  4. package/out/preload/index.js +1 -1
  5. package/out/renderer/assets/{cssMode-CJR4iD-G.js → cssMode-CPfLk04b.js} +3 -3
  6. package/out/renderer/assets/{freemarker2-BRI9ISq9.js → freemarker2-CDHpuFqv.js} +1 -1
  7. package/out/renderer/assets/{handlebars-3hL6XIZI.js → handlebars-CgX9Jz36.js} +1 -1
  8. package/out/renderer/assets/{html-Dd5_vBoP.js → html-DdgFccDG.js} +1 -1
  9. package/out/renderer/assets/{htmlMode-DgNtlKcG.js → htmlMode-CRHIwtvP.js} +3 -3
  10. package/out/renderer/assets/{index-5azC4yNn.js → index-DVdWgw7p.js} +50 -38
  11. package/out/renderer/assets/{javascript-D0vPLEJg.js → javascript-CHhxL0s7.js} +2 -2
  12. package/out/renderer/assets/{jsonMode-DBvR0zMu.js → jsonMode-Cy7-eWai.js} +3 -3
  13. package/out/renderer/assets/{liquid-CLoc6Tx7.js → liquid-vMkF9pfk.js} +1 -1
  14. package/out/renderer/assets/{lspLanguageFeatures-CiaMaQoY.js → lspLanguageFeatures-nAygNzuG.js} +1 -1
  15. package/out/renderer/assets/{mdx-DTsq6tlv.js → mdx-C4UVz5dW.js} +1 -1
  16. package/out/renderer/assets/{python-D04DpZDr.js → python-BHemzN9g.js} +1 -1
  17. package/out/renderer/assets/{razor-Di6CJ6s8.js → razor-DH6NPzbo.js} +1 -1
  18. package/out/renderer/assets/{tsMode-mfv6mHOv.js → tsMode-DxBdlGZB.js} +1 -1
  19. package/out/renderer/assets/{typescript-CDA0Hhef.js → typescript-BRCOaBQ5.js} +1 -1
  20. package/out/renderer/assets/{xml-BU0voRuq.js → xml-DVv9gDP1.js} +1 -1
  21. package/out/renderer/assets/{yaml-qzFWnaZJ.js → yaml-jB-C3CdS.js} +1 -1
  22. package/out/renderer/index.html +1 -1
  23. package/package.json +1 -1
  24. package/src/main/index.ts +2 -2
  25. package/src/main/orchestrator.ts +2 -2
  26. package/src/main/ticketStore.ts +100 -66
  27. package/src/preload/index.ts +3 -3
  28. package/src/renderer/App.tsx +2 -2
  29. package/src/renderer/components/FloatingMic.tsx +15 -11
  30. package/src/renderer/components/TerminalPanel.tsx +11 -2
  31. package/src/renderer/components/TicketPanel.tsx +16 -5
package/out/main/index.js CHANGED
@@ -11002,11 +11002,11 @@ function isSameLocalDay(a, b2) {
11002
11002
  function log$4(...args) {
11003
11003
  log$5("[time-tracker]", ...args);
11004
11004
  }
11005
- function findPageByTitle$2(pages, title) {
11005
+ function findPageByTitle$1(pages, title) {
11006
11006
  for (const p2 of pages) {
11007
11007
  if (p2?.title === title) return p2;
11008
11008
  if (p2?.children?.length) {
11009
- const c = findPageByTitle$2(p2.children, title);
11009
+ const c = findPageByTitle$1(p2.children, title);
11010
11010
  if (c) return c;
11011
11011
  }
11012
11012
  }
@@ -11184,7 +11184,7 @@ class TimeTracker {
11184
11184
  }
11185
11185
  if (!folder?.id) return null;
11186
11186
  const folderDetail = await this.api.getFolder(folder.id);
11187
- const agentPage = findPageByTitle$2(folderDetail?.pages || [], AGENT_DATASTORE_PAGE_TITLE$2);
11187
+ const agentPage = findPageByTitle$1(folderDetail?.pages || [], AGENT_DATASTORE_PAGE_TITLE$2);
11188
11188
  if (!agentPage?.id) return null;
11189
11189
  const blockId = await this.findOrAdoptBlock(agentPage.id);
11190
11190
  if (blockId) {
@@ -11289,21 +11289,19 @@ const COLUMNS$1 = [
11289
11289
  function log$3(...args) {
11290
11290
  log$5("[ticket-store]", ...args);
11291
11291
  }
11292
- function findPageByTitle$1(pages, title) {
11292
+ function findPagesByTitle(pages, title) {
11293
+ const found = [];
11293
11294
  for (const p2 of pages) {
11294
- if (p2?.title === title) return p2;
11295
- if (p2?.children?.length) {
11296
- const c = findPageByTitle$1(p2.children, title);
11297
- if (c) return c;
11298
- }
11295
+ if (p2?.title === title) found.push(p2);
11296
+ if (p2?.children?.length) found.push(...findPagesByTitle(p2.children, title));
11299
11297
  }
11300
- return null;
11298
+ return found;
11301
11299
  }
11302
11300
  class TicketStore {
11303
11301
  api;
11304
- // Per-cwd cache of the resolved Tickets datastore: the block (the datastore
11305
- // element) and the Agent Datastore page it lives on. Both ids are handed to
11306
- // the coding agent so it can read/update tickets via the ctlsurf MCP tools.
11302
+ // Per-cwd cache of every resolved Tickets datastore. `primary` is where new
11303
+ // tickets are written and what gets handed to the coding agent; `all` covers
11304
+ // duplicate Agent Datastore pages so listing misses nothing.
11307
11305
  dsCache = /* @__PURE__ */ new Map();
11308
11306
  constructor(api) {
11309
11307
  this.api = api;
@@ -11313,11 +11311,11 @@ class TicketStore {
11313
11311
  const title = input.title?.trim();
11314
11312
  if (!title) return { ok: false, error: "Title is required" };
11315
11313
  try {
11316
- const ds = await this.ensureDatastore(cwd, true);
11317
- if (!ds) {
11314
+ const stores = await this.resolveStores(cwd, true);
11315
+ if (!stores) {
11318
11316
  return { ok: false, error: "No ctlsurf project found for this folder" };
11319
11317
  }
11320
- await this.api.addRow(ds.blockId, {
11318
+ await this.api.addRow(stores.primary.blockId, {
11321
11319
  Title: title,
11322
11320
  Description: input.description?.trim() || "",
11323
11321
  Status: input.status || "Open",
@@ -11331,14 +11329,15 @@ class TicketStore {
11331
11329
  return { ok: false, error: err?.message || String(err) };
11332
11330
  }
11333
11331
  }
11334
- /** Updates an existing ticket row in the project's Tickets datastore. */
11335
- async updateTicket(cwd, rowId, input) {
11332
+ /** Updates an existing ticket row. `blockId` names the store the row lives
11333
+ * in (from listTickets); without it the primary store is assumed. */
11334
+ async updateTicket(cwd, rowId, input, blockId) {
11336
11335
  const title = input.title?.trim();
11337
11336
  if (!title) return { ok: false, error: "Title is required" };
11338
11337
  try {
11339
- const ds = await this.ensureDatastore(cwd, false);
11340
- if (!ds) return { ok: false, error: "No Tickets datastore for this project" };
11341
- await this.api.updateRow(ds.blockId, rowId, {
11338
+ const stores = await this.resolveStores(cwd, false);
11339
+ if (!stores) return { ok: false, error: "No Tickets datastore for this project" };
11340
+ await this.api.updateRow(blockId || stores.primary.blockId, rowId, {
11342
11341
  Title: title,
11343
11342
  Description: input.description?.trim() || "",
11344
11343
  Status: input.status || "Open",
@@ -11351,28 +11350,39 @@ class TicketStore {
11351
11350
  return { ok: false, error: err?.message || String(err) };
11352
11351
  }
11353
11352
  }
11354
- /** Lists existing tickets for the project, newest first. Does not create the
11355
- * datastore — an unconfigured project simply has no tickets yet. */
11353
+ /** Lists existing tickets across every Tickets store in the project's
11354
+ * folder, newest first. Does not create the datastore — an unconfigured
11355
+ * project simply has no tickets yet. */
11356
11356
  async listTickets(cwd) {
11357
11357
  try {
11358
- const ds = await this.ensureDatastore(cwd, false);
11359
- if (!ds) return { ok: true, tickets: [] };
11360
- const res = await this.api.queryRows(ds.blockId, { orderBy: "Created", order: "desc", limit: 200 });
11361
- const tickets = (res?.rows || []).map((r) => ({
11362
- id: r.id,
11363
- title: String(r.data?.Title ?? ""),
11364
- description: String(r.data?.Description ?? ""),
11365
- status: String(r.data?.Status ?? "Open"),
11366
- priority: String(r.data?.Priority ?? "Med"),
11367
- created: r.data?.Created ?? r.created_at ?? null
11368
- }));
11369
- return { ok: true, tickets, blockId: ds.blockId, pageId: ds.pageId };
11358
+ const stores = await this.resolveStores(cwd, false);
11359
+ if (!stores) return { ok: true, tickets: [] };
11360
+ const tickets = [];
11361
+ for (const ds of stores.all) {
11362
+ const res = await this.api.queryRows(ds.blockId, { orderBy: "Created", order: "desc", limit: 200 });
11363
+ for (const r of res?.rows || []) {
11364
+ tickets.push({
11365
+ id: r.id,
11366
+ title: String(r.data?.Title ?? ""),
11367
+ description: String(r.data?.Description ?? ""),
11368
+ status: String(r.data?.Status ?? "Open"),
11369
+ priority: String(r.data?.Priority ?? "Med"),
11370
+ created: r.data?.Created ?? r.created_at ?? null,
11371
+ blockId: ds.blockId,
11372
+ pageId: ds.pageId
11373
+ });
11374
+ }
11375
+ }
11376
+ if (stores.all.length > 1) {
11377
+ tickets.sort((a, b2) => (b2.created || "").localeCompare(a.created || ""));
11378
+ }
11379
+ return { ok: true, tickets, blockId: stores.primary.blockId, pageId: stores.primary.pageId };
11370
11380
  } catch (err) {
11371
11381
  log$3(`listTickets failed for ${cwd}: ${err?.message || err}`);
11372
11382
  return { ok: false, tickets: [], error: err?.message || String(err) };
11373
11383
  }
11374
11384
  }
11375
- async ensureDatastore(cwd, create) {
11385
+ async resolveStores(cwd, create) {
11376
11386
  const cached = this.dsCache.get(cwd);
11377
11387
  if (cached) return cached;
11378
11388
  let folder = null;
@@ -11383,17 +11393,27 @@ class TicketStore {
11383
11393
  }
11384
11394
  if (!folder?.id) return null;
11385
11395
  const folderDetail = await this.api.getFolder(folder.id);
11386
- const agentPage = findPageByTitle$1(folderDetail?.pages || [], AGENT_DATASTORE_PAGE_TITLE$1);
11387
- if (!agentPage?.id) return null;
11388
- const pageId = agentPage.id;
11389
- const blockId = await this.findOrAdoptBlock(pageId);
11390
- if (blockId) {
11391
- await this.ensureColumns(blockId);
11392
- const ds = { blockId, pageId };
11393
- this.dsCache.set(cwd, ds);
11394
- return ds;
11396
+ const agentPages = findPagesByTitle(folderDetail?.pages || [], AGENT_DATASTORE_PAGE_TITLE$1);
11397
+ if (agentPages.length === 0) return null;
11398
+ const rootPath = folder.root_path || cwd;
11399
+ agentPages.sort((a, b2) => Number(b2?.root_path === rootPath) - Number(a?.root_path === rootPath));
11400
+ if (agentPages.length > 1) {
11401
+ log$3(`Found ${agentPages.length} "Agent Datastore" pages for ${cwd}; aggregating tickets across all of them`);
11402
+ }
11403
+ const all = [];
11404
+ for (const page of agentPages) {
11405
+ for (const blockId of await this.findTicketBlocks(page.id)) {
11406
+ all.push({ blockId, pageId: page.id });
11407
+ }
11408
+ }
11409
+ if (all.length > 0) {
11410
+ for (const ds of all) await this.ensureColumns(ds.blockId);
11411
+ const stores = { primary: all[0], all };
11412
+ this.dsCache.set(cwd, stores);
11413
+ return stores;
11395
11414
  }
11396
11415
  if (!create) return null;
11416
+ const pageId = agentPages[0].id;
11397
11417
  const columns = COLUMNS$1.map((c, i) => ({
11398
11418
  id: `col_${i}`,
11399
11419
  name: c.name,
@@ -11408,47 +11428,45 @@ class TicketStore {
11408
11428
  if (created?.id) {
11409
11429
  log$3(`Created "${DATASTORE_TITLE}" datastore on Agent Datastore page for ${cwd}`);
11410
11430
  const ds = { blockId: created.id, pageId };
11411
- this.dsCache.set(cwd, ds);
11412
- return ds;
11431
+ const stores = { primary: ds, all: [ds] };
11432
+ this.dsCache.set(cwd, stores);
11433
+ return stores;
11413
11434
  }
11414
11435
  return null;
11415
11436
  }
11416
- /** Finds an existing Tickets block on the page. Prefers a system_key match
11417
- * (which survives title renames). Falls back to title match for legacy blocks
11418
- * and backfills system_key on the way out. */
11419
- async findOrAdoptBlock(pageId) {
11437
+ /** Finds every Tickets block on the page: system_key matches first, then
11438
+ * title matches for legacy blocks. When no system_key block exists, the
11439
+ * first legacy block is adopted (system_key backfilled so it survives a
11440
+ * later rename); extra title matches are still listed but left unstamped so
11441
+ * the primary can't flip between sessions. */
11442
+ async findTicketBlocks(pageId) {
11420
11443
  const summaries = await this.api.getPageBlockSummaries(pageId) || [];
11421
11444
  const datastoreSummaries = summaries.filter((b2) => b2?.type === "datastore" && b2?.id);
11422
- if (datastoreSummaries.length === 0) return null;
11423
- let titleFallbackId = null;
11424
- let titleFallbackProps = null;
11445
+ if (datastoreSummaries.length === 0) return [];
11446
+ const keyed = [];
11447
+ const legacy = [];
11425
11448
  for (const s of datastoreSummaries) {
11426
11449
  try {
11427
11450
  const block = await this.api.getBlock(s.id);
11428
11451
  const props = block?.props || {};
11429
- if (props.system_key === SYSTEM_KEY) {
11430
- return s.id;
11431
- }
11432
- if (s.title === DATASTORE_TITLE && titleFallbackId === null) {
11433
- titleFallbackId = s.id;
11434
- titleFallbackProps = props;
11435
- }
11452
+ if (props.system_key === SYSTEM_KEY) keyed.push(s.id);
11453
+ else if (s.title === DATASTORE_TITLE) legacy.push({ id: s.id, props });
11436
11454
  } catch (err) {
11437
11455
  log$3(`getBlock(${s.id}) failed during lookup: ${err?.message || err}`);
11438
11456
  }
11439
11457
  }
11440
- if (titleFallbackId) {
11458
+ if (keyed.length === 0 && legacy.length > 0) {
11459
+ const adopt = legacy[0];
11441
11460
  try {
11442
- await this.api.updateBlock(titleFallbackId, {
11443
- props: { ...titleFallbackProps || {}, system_key: SYSTEM_KEY }
11461
+ await this.api.updateBlock(adopt.id, {
11462
+ props: { ...adopt.props, system_key: SYSTEM_KEY }
11444
11463
  });
11445
- log$3(`Backfilled system_key on legacy Tickets block ${titleFallbackId}`);
11464
+ log$3(`Backfilled system_key on legacy Tickets block ${adopt.id}`);
11446
11465
  } catch (err) {
11447
- log$3(`backfill system_key failed on ${titleFallbackId}: ${err?.message || err}`);
11466
+ log$3(`backfill system_key failed on ${adopt.id}: ${err?.message || err}`);
11448
11467
  }
11449
- return titleFallbackId;
11450
11468
  }
11451
- return null;
11469
+ return [...keyed, ...legacy.map((l) => l.id)];
11452
11470
  }
11453
11471
  async ensureColumns(blockId) {
11454
11472
  try {
@@ -23328,13 +23346,13 @@ class Orchestrator {
23328
23346
  }
23329
23347
  return this.ticketStore.addTicket(cwd, input);
23330
23348
  }
23331
- async updateTicketForActiveTab(rowId, input) {
23349
+ async updateTicketForActiveTab(rowId, input, blockId) {
23332
23350
  const cwd = this.getActiveTabProjectPath();
23333
23351
  if (!cwd) return { ok: false, error: "No active terminal tab" };
23334
23352
  if (!this.ctlsurfApi.getApiKey()) {
23335
23353
  return { ok: false, error: "ctlsurf API key not configured" };
23336
23354
  }
23337
- return this.ticketStore.updateTicket(cwd, rowId, input);
23355
+ return this.ticketStore.updateTicket(cwd, rowId, input, blockId);
23338
23356
  }
23339
23357
  async listTicketsForActiveTab() {
23340
23358
  const cwd = this.getActiveTabProjectPath();
@@ -23960,8 +23978,8 @@ electron.ipcMain.handle("tickets:project", () => {
23960
23978
  electron.ipcMain.handle("tickets:add", async (_event, input) => {
23961
23979
  return orchestrator.addTicketForActiveTab(input);
23962
23980
  });
23963
- electron.ipcMain.handle("tickets:update", async (_event, rowId, input) => {
23964
- return orchestrator.updateTicketForActiveTab(rowId, input);
23981
+ electron.ipcMain.handle("tickets:update", async (_event, rowId, input, blockId) => {
23982
+ return orchestrator.updateTicketForActiveTab(rowId, input, blockId);
23965
23983
  });
23966
23984
  electron.ipcMain.handle("tickets:list", async () => {
23967
23985
  return orchestrator.listTicketsForActiveTab();
@@ -73,7 +73,7 @@ const api = {
73
73
  // Tickets (active tab)
74
74
  getTicketProject: () => electron.ipcRenderer.invoke("tickets:project"),
75
75
  addTicket: (input) => electron.ipcRenderer.invoke("tickets:add", input),
76
- updateTicket: (rowId, input) => electron.ipcRenderer.invoke("tickets:update", rowId, input),
76
+ updateTicket: (rowId, input, blockId) => electron.ipcRenderer.invoke("tickets:update", rowId, input, blockId),
77
77
  listTickets: () => electron.ipcRenderer.invoke("tickets:list"),
78
78
  // Chat logging (global)
79
79
  getLogChat: () => electron.ipcRenderer.invoke("logchat:get"),
@@ -1,6 +1,6 @@
1
- import { c as createWebWorker, l as languages } from "./index-5azC4yNn.js";
2
- import { C as CompletionAdapter, H as HoverAdapter, D as DocumentHighlightAdapter, a as DefinitionAdapter, R as ReferenceAdapter, b as DocumentSymbolAdapter, c as RenameAdapter, d as DocumentColorAdapter, F as FoldingRangeAdapter, e as DiagnosticsAdapter, S as SelectionRangeAdapter, f as DocumentFormattingEditProvider, g as DocumentRangeFormattingEditProvider } from "./lspLanguageFeatures-CiaMaQoY.js";
3
- import { h, i, j, t, k } from "./lspLanguageFeatures-CiaMaQoY.js";
1
+ import { c as createWebWorker, l as languages } from "./index-DVdWgw7p.js";
2
+ import { C as CompletionAdapter, H as HoverAdapter, D as DocumentHighlightAdapter, a as DefinitionAdapter, R as ReferenceAdapter, b as DocumentSymbolAdapter, c as RenameAdapter, d as DocumentColorAdapter, F as FoldingRangeAdapter, e as DiagnosticsAdapter, S as SelectionRangeAdapter, f as DocumentFormattingEditProvider, g as DocumentRangeFormattingEditProvider } from "./lspLanguageFeatures-nAygNzuG.js";
3
+ import { h, i, j, t, k } from "./lspLanguageFeatures-nAygNzuG.js";
4
4
  const STOP_WHEN_IDLE_FOR = 2 * 60 * 1e3;
5
5
  class WorkerManager {
6
6
  constructor(defaults) {
@@ -1,4 +1,4 @@
1
- import { l as languages } from "./index-5azC4yNn.js";
1
+ import { l as languages } from "./index-DVdWgw7p.js";
2
2
  const EMPTY_ELEMENTS = [
3
3
  "assign",
4
4
  "flush",
@@ -1,4 +1,4 @@
1
- import { l as languages } from "./index-5azC4yNn.js";
1
+ import { l as languages } from "./index-DVdWgw7p.js";
2
2
  const EMPTY_ELEMENTS = [
3
3
  "area",
4
4
  "base",
@@ -1,4 +1,4 @@
1
- import { l as languages } from "./index-5azC4yNn.js";
1
+ import { l as languages } from "./index-DVdWgw7p.js";
2
2
  const EMPTY_ELEMENTS = [
3
3
  "area",
4
4
  "base",
@@ -1,6 +1,6 @@
1
- import { c as createWebWorker, l as languages } from "./index-5azC4yNn.js";
2
- import { H as HoverAdapter, D as DocumentHighlightAdapter, h as DocumentLinkAdapter, F as FoldingRangeAdapter, b as DocumentSymbolAdapter, S as SelectionRangeAdapter, c as RenameAdapter, f as DocumentFormattingEditProvider, g as DocumentRangeFormattingEditProvider, C as CompletionAdapter } from "./lspLanguageFeatures-CiaMaQoY.js";
3
- import { a, e, d, R, i, j, t, k } from "./lspLanguageFeatures-CiaMaQoY.js";
1
+ import { c as createWebWorker, l as languages } from "./index-DVdWgw7p.js";
2
+ import { H as HoverAdapter, D as DocumentHighlightAdapter, h as DocumentLinkAdapter, F as FoldingRangeAdapter, b as DocumentSymbolAdapter, S as SelectionRangeAdapter, c as RenameAdapter, f as DocumentFormattingEditProvider, g as DocumentRangeFormattingEditProvider, C as CompletionAdapter } from "./lspLanguageFeatures-nAygNzuG.js";
3
+ import { a, e, d, R, i, j, t, k } from "./lspLanguageFeatures-nAygNzuG.js";
4
4
  const STOP_WHEN_IDLE_FOR = 2 * 60 * 1e3;
5
5
  class WorkerManager {
6
6
  constructor(defaults) {
@@ -1,4 +1,4 @@
1
- const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./cssMode-CJR4iD-G.js","./lspLanguageFeatures-CiaMaQoY.js","./htmlMode-DgNtlKcG.js","./jsonMode-DBvR0zMu.js","./javascript-D0vPLEJg.js","./typescript-CDA0Hhef.js"])))=>i.map(i=>d[i]);
1
+ const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./cssMode-CPfLk04b.js","./lspLanguageFeatures-nAygNzuG.js","./htmlMode-CRHIwtvP.js","./jsonMode-Cy7-eWai.js","./javascript-CHhxL0s7.js","./typescript-BRCOaBQ5.js"])))=>i.map(i=>d[i]);
2
2
  function getDefaultExportFromCjs(x) {
3
3
  return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, "default") ? x["default"] : x;
4
4
  }
@@ -19532,6 +19532,11 @@ function getOrCreateTerminal(tabId, onExit) {
19532
19532
  }
19533
19533
  const terminal = new xtermExports.Terminal({
19534
19534
  cursorBlink: true,
19535
+ // Codex and other modern CLIs often render links as OSC 8 hyperlinks
19536
+ // instead of plain URL text. OSC 8 links are handled by xterm core (not
19537
+ // WebLinksAddon), whose default activation calls window.open(). Route them
19538
+ // through the same embedded-panel handler as detected plain-text URLs.
19539
+ linkHandler: { activate: handleTerminalLink },
19535
19540
  // Off by default on macOS; without this a right-click would reselect the
19536
19541
  // word under the cursor and clobber a multi-line table selection.
19537
19542
  rightClickSelectsWord: false,
@@ -19736,7 +19741,7 @@ function TerminalPanel({ tabId, agent, onSpawn, onExit, isActive }) {
19736
19741
  const attached = _terminals.get(tabId);
19737
19742
  if (attached) reassert(attached);
19738
19743
  const onWheel = () => markGesture(tabId);
19739
- terminal.element?.addEventListener("wheel", onWheel, { passive: true });
19744
+ terminal.element?.addEventListener("wheel", onWheel, { capture: true, passive: true });
19740
19745
  let resizeTimeout;
19741
19746
  const handleResize = () => {
19742
19747
  clearTimeout(resizeTimeout);
@@ -19759,7 +19764,7 @@ function TerminalPanel({ tabId, agent, onSpawn, onExit, isActive }) {
19759
19764
  clearTimeout(resizeTimeout);
19760
19765
  window.removeEventListener("resize", handleResize);
19761
19766
  observer.disconnect();
19762
- terminal.element?.removeEventListener("wheel", onWheel);
19767
+ terminal.element?.removeEventListener("wheel", onWheel, true);
19763
19768
  };
19764
19769
  }, [tabId, onExit]);
19765
19770
  reactExports.useEffect(() => {
@@ -20318,7 +20323,7 @@ function VoiceInput({ onTranscript, variant = "titlebar" }) {
20318
20323
  const POS_KEY = "ctlsurf.floatingMicPos";
20319
20324
  const EDGE = 20;
20320
20325
  const TOP_MIN = 46;
20321
- const BOTTOM_GAP = 36;
20326
+ const BOTTOM_GAP = 72;
20322
20327
  function loadPos() {
20323
20328
  try {
20324
20329
  const raw = localStorage.getItem(POS_KEY);
@@ -20366,15 +20371,17 @@ function FloatingMic({ onTranscript, onHide }) {
20366
20371
  };
20367
20372
  }, []);
20368
20373
  reactExports.useEffect(() => {
20369
- if (pos) return;
20370
- const el = elRef.current;
20371
- const w = el?.offsetWidth ?? 64;
20372
- const h2 = el?.offsetHeight ?? 90;
20373
- setPos({
20374
- x: window.innerWidth - w - EDGE,
20375
- y: window.innerHeight - h2 - BOTTOM_GAP
20374
+ setPos((current) => {
20375
+ if (current) return clamp2(current.x, current.y);
20376
+ const el = elRef.current;
20377
+ const w = el?.offsetWidth ?? 64;
20378
+ const h2 = el?.offsetHeight ?? 90;
20379
+ return clamp2(
20380
+ window.innerWidth - w - EDGE,
20381
+ window.innerHeight - h2 - BOTTOM_GAP
20382
+ );
20376
20383
  });
20377
- }, [pos]);
20384
+ }, [clamp2]);
20378
20385
  reactExports.useEffect(() => {
20379
20386
  const onResize = () => setPos((p) => p ? clamp2(p.x, p.y) : p);
20380
20387
  window.addEventListener("resize", onResize);
@@ -208416,7 +208423,7 @@ const lessDefaults = new LanguageServiceDefaultsImpl$3(
208416
208423
  modeConfigurationDefault$2
208417
208424
  );
208418
208425
  function getMode$3() {
208419
- return __vitePreload(() => import("./cssMode-CJR4iD-G.js"), true ? __vite__mapDeps([0,1]) : void 0, import.meta.url);
208426
+ return __vitePreload(() => import("./cssMode-CPfLk04b.js"), true ? __vite__mapDeps([0,1]) : void 0, import.meta.url);
208420
208427
  }
208421
208428
  languages.onLanguage("less", () => {
208422
208429
  getMode$3().then((mode2) => mode2.setupMode(lessDefaults));
@@ -208521,7 +208528,7 @@ const razorLanguageService = registerHTMLLanguageService(
208521
208528
  );
208522
208529
  const razorDefaults = razorLanguageService.defaults;
208523
208530
  function getMode$2() {
208524
- return __vitePreload(() => import("./htmlMode-DgNtlKcG.js"), true ? __vite__mapDeps([2,1]) : void 0, import.meta.url);
208531
+ return __vitePreload(() => import("./htmlMode-CRHIwtvP.js"), true ? __vite__mapDeps([2,1]) : void 0, import.meta.url);
208525
208532
  }
208526
208533
  function registerHTMLLanguageService(languageId, options = optionsDefault, modeConfiguration = getConfigurationDefault(languageId)) {
208527
208534
  const defaults = new LanguageServiceDefaultsImpl$2(languageId, options, modeConfiguration);
@@ -208605,7 +208612,7 @@ const jsonDefaults = new LanguageServiceDefaultsImpl$1(
208605
208612
  );
208606
208613
  const getWorker$1 = () => getMode$1().then((mode2) => mode2.getWorker());
208607
208614
  function getMode$1() {
208608
- return __vitePreload(() => import("./jsonMode-DBvR0zMu.js"), true ? __vite__mapDeps([3,1]) : void 0, import.meta.url);
208615
+ return __vitePreload(() => import("./jsonMode-Cy7-eWai.js"), true ? __vite__mapDeps([3,1]) : void 0, import.meta.url);
208609
208616
  }
208610
208617
  languages.register({
208611
208618
  id: "json",
@@ -208851,7 +208858,7 @@ const getJavaScriptWorker = () => {
208851
208858
  return getMode().then((mode) => mode.getJavaScriptWorker());
208852
208859
  };
208853
208860
  function getMode() {
208854
- return __vitePreload(() => import("./tsMode-mfv6mHOv.js"), true ? [] : void 0, import.meta.url);
208861
+ return __vitePreload(() => import("./tsMode-DxBdlGZB.js"), true ? [] : void 0, import.meta.url);
208855
208862
  }
208856
208863
  languages.onLanguage("typescript", () => {
208857
208864
  return getMode().then((mode) => mode.setupTypeScript(typescriptDefaults));
@@ -209046,49 +209053,49 @@ registerLanguage({
209046
209053
  extensions: [".ftl", ".ftlh", ".ftlx"],
209047
209054
  aliases: ["FreeMarker2", "Apache FreeMarker2"],
209048
209055
  loader: () => {
209049
- return __vitePreload(() => import("./freemarker2-BRI9ISq9.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagAutoInterpolationDollar);
209056
+ return __vitePreload(() => import("./freemarker2-CDHpuFqv.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagAutoInterpolationDollar);
209050
209057
  }
209051
209058
  });
209052
209059
  registerLanguage({
209053
209060
  id: "freemarker2.tag-angle.interpolation-dollar",
209054
209061
  aliases: ["FreeMarker2 (Angle/Dollar)", "Apache FreeMarker2 (Angle/Dollar)"],
209055
209062
  loader: () => {
209056
- return __vitePreload(() => import("./freemarker2-BRI9ISq9.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagAngleInterpolationDollar);
209063
+ return __vitePreload(() => import("./freemarker2-CDHpuFqv.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagAngleInterpolationDollar);
209057
209064
  }
209058
209065
  });
209059
209066
  registerLanguage({
209060
209067
  id: "freemarker2.tag-bracket.interpolation-dollar",
209061
209068
  aliases: ["FreeMarker2 (Bracket/Dollar)", "Apache FreeMarker2 (Bracket/Dollar)"],
209062
209069
  loader: () => {
209063
- return __vitePreload(() => import("./freemarker2-BRI9ISq9.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagBracketInterpolationDollar);
209070
+ return __vitePreload(() => import("./freemarker2-CDHpuFqv.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagBracketInterpolationDollar);
209064
209071
  }
209065
209072
  });
209066
209073
  registerLanguage({
209067
209074
  id: "freemarker2.tag-angle.interpolation-bracket",
209068
209075
  aliases: ["FreeMarker2 (Angle/Bracket)", "Apache FreeMarker2 (Angle/Bracket)"],
209069
209076
  loader: () => {
209070
- return __vitePreload(() => import("./freemarker2-BRI9ISq9.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagAngleInterpolationBracket);
209077
+ return __vitePreload(() => import("./freemarker2-CDHpuFqv.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagAngleInterpolationBracket);
209071
209078
  }
209072
209079
  });
209073
209080
  registerLanguage({
209074
209081
  id: "freemarker2.tag-bracket.interpolation-bracket",
209075
209082
  aliases: ["FreeMarker2 (Bracket/Bracket)", "Apache FreeMarker2 (Bracket/Bracket)"],
209076
209083
  loader: () => {
209077
- return __vitePreload(() => import("./freemarker2-BRI9ISq9.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagBracketInterpolationBracket);
209084
+ return __vitePreload(() => import("./freemarker2-CDHpuFqv.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagBracketInterpolationBracket);
209078
209085
  }
209079
209086
  });
209080
209087
  registerLanguage({
209081
209088
  id: "freemarker2.tag-auto.interpolation-dollar",
209082
209089
  aliases: ["FreeMarker2 (Auto/Dollar)", "Apache FreeMarker2 (Auto/Dollar)"],
209083
209090
  loader: () => {
209084
- return __vitePreload(() => import("./freemarker2-BRI9ISq9.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagAutoInterpolationDollar);
209091
+ return __vitePreload(() => import("./freemarker2-CDHpuFqv.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagAutoInterpolationDollar);
209085
209092
  }
209086
209093
  });
209087
209094
  registerLanguage({
209088
209095
  id: "freemarker2.tag-auto.interpolation-bracket",
209089
209096
  aliases: ["FreeMarker2 (Auto/Bracket)", "Apache FreeMarker2 (Auto/Bracket)"],
209090
209097
  loader: () => {
209091
- return __vitePreload(() => import("./freemarker2-BRI9ISq9.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagAutoInterpolationBracket);
209098
+ return __vitePreload(() => import("./freemarker2-CDHpuFqv.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagAutoInterpolationBracket);
209092
209099
  }
209093
209100
  });
209094
209101
  registerLanguage({
@@ -209109,7 +209116,7 @@ registerLanguage({
209109
209116
  extensions: [".handlebars", ".hbs"],
209110
209117
  aliases: ["Handlebars", "handlebars", "hbs"],
209111
209118
  mimetypes: ["text/x-handlebars-template"],
209112
- loader: () => __vitePreload(() => import("./handlebars-3hL6XIZI.js"), true ? [] : void 0, import.meta.url)
209119
+ loader: () => __vitePreload(() => import("./handlebars-CgX9Jz36.js"), true ? [] : void 0, import.meta.url)
209113
209120
  });
209114
209121
  registerLanguage({
209115
209122
  id: "hcl",
@@ -209122,7 +209129,7 @@ registerLanguage({
209122
209129
  extensions: [".html", ".htm", ".shtml", ".xhtml", ".mdoc", ".jsp", ".asp", ".aspx", ".jshtm"],
209123
209130
  aliases: ["HTML", "htm", "html", "xhtml"],
209124
209131
  mimetypes: ["text/html", "text/x-jshtm", "text/template", "text/ng-template"],
209125
- loader: () => __vitePreload(() => import("./html-Dd5_vBoP.js"), true ? [] : void 0, import.meta.url)
209132
+ loader: () => __vitePreload(() => import("./html-DdgFccDG.js"), true ? [] : void 0, import.meta.url)
209126
209133
  });
209127
209134
  registerLanguage({
209128
209135
  id: "ini",
@@ -209145,7 +209152,7 @@ registerLanguage({
209145
209152
  filenames: ["jakefile"],
209146
209153
  aliases: ["JavaScript", "javascript", "js"],
209147
209154
  mimetypes: ["text/javascript"],
209148
- loader: () => __vitePreload(() => import("./javascript-D0vPLEJg.js"), true ? __vite__mapDeps([4,5]) : void 0, import.meta.url)
209155
+ loader: () => __vitePreload(() => import("./javascript-CHhxL0s7.js"), true ? __vite__mapDeps([4,5]) : void 0, import.meta.url)
209149
209156
  });
209150
209157
  registerLanguage({
209151
209158
  id: "julia",
@@ -209184,7 +209191,7 @@ registerLanguage({
209184
209191
  extensions: [".liquid", ".html.liquid"],
209185
209192
  aliases: ["Liquid", "liquid"],
209186
209193
  mimetypes: ["application/liquid"],
209187
- loader: () => __vitePreload(() => import("./liquid-CLoc6Tx7.js"), true ? [] : void 0, import.meta.url)
209194
+ loader: () => __vitePreload(() => import("./liquid-vMkF9pfk.js"), true ? [] : void 0, import.meta.url)
209188
209195
  });
209189
209196
  registerLanguage({
209190
209197
  id: "m3",
@@ -209202,7 +209209,7 @@ registerLanguage({
209202
209209
  id: "mdx",
209203
209210
  extensions: [".mdx"],
209204
209211
  aliases: ["MDX", "mdx"],
209205
- loader: () => __vitePreload(() => import("./mdx-DTsq6tlv.js"), true ? [] : void 0, import.meta.url)
209212
+ loader: () => __vitePreload(() => import("./mdx-C4UVz5dW.js"), true ? [] : void 0, import.meta.url)
209206
209213
  });
209207
209214
  registerLanguage({
209208
209215
  id: "mips",
@@ -209301,7 +209308,7 @@ registerLanguage({
209301
209308
  extensions: [".py", ".rpy", ".pyw", ".cpy", ".gyp", ".gypi"],
209302
209309
  aliases: ["Python", "py"],
209303
209310
  firstLine: "^#!/.*\\bpython[0-9.-]*\\b",
209304
- loader: () => __vitePreload(() => import("./python-D04DpZDr.js"), true ? [] : void 0, import.meta.url)
209311
+ loader: () => __vitePreload(() => import("./python-BHemzN9g.js"), true ? [] : void 0, import.meta.url)
209305
209312
  });
209306
209313
  registerLanguage({
209307
209314
  id: "qsharp",
@@ -209320,7 +209327,7 @@ registerLanguage({
209320
209327
  extensions: [".cshtml"],
209321
209328
  aliases: ["Razor", "razor"],
209322
209329
  mimetypes: ["text/x-cshtml"],
209323
- loader: () => __vitePreload(() => import("./razor-Di6CJ6s8.js"), true ? [] : void 0, import.meta.url)
209330
+ loader: () => __vitePreload(() => import("./razor-DH6NPzbo.js"), true ? [] : void 0, import.meta.url)
209324
209331
  });
209325
209332
  registerLanguage({
209326
209333
  id: "redis",
@@ -209453,7 +209460,7 @@ registerLanguage({
209453
209460
  aliases: ["TypeScript", "ts", "typescript"],
209454
209461
  mimetypes: ["text/typescript"],
209455
209462
  loader: () => {
209456
- return __vitePreload(() => import("./typescript-CDA0Hhef.js"), true ? [] : void 0, import.meta.url);
209463
+ return __vitePreload(() => import("./typescript-BRCOaBQ5.js"), true ? [] : void 0, import.meta.url);
209457
209464
  }
209458
209465
  });
209459
209466
  registerLanguage({
@@ -209498,14 +209505,14 @@ registerLanguage({
209498
209505
  firstLine: "(\\<\\?xml.*)|(\\<svg)|(\\<\\!doctype\\s+svg)",
209499
209506
  aliases: ["XML", "xml"],
209500
209507
  mimetypes: ["text/xml", "application/xml", "application/xaml+xml", "application/xml-dtd"],
209501
- loader: () => __vitePreload(() => import("./xml-BU0voRuq.js"), true ? [] : void 0, import.meta.url)
209508
+ loader: () => __vitePreload(() => import("./xml-DVv9gDP1.js"), true ? [] : void 0, import.meta.url)
209502
209509
  });
209503
209510
  registerLanguage({
209504
209511
  id: "yaml",
209505
209512
  extensions: [".yaml", ".yml"],
209506
209513
  aliases: ["YAML", "yaml", "YML", "yml"],
209507
209514
  mimetypes: ["application/x-yaml", "text/x-yaml"],
209508
- loader: () => __vitePreload(() => import("./yaml-qzFWnaZJ.js"), true ? [] : void 0, import.meta.url)
209515
+ loader: () => __vitePreload(() => import("./yaml-jB-C3CdS.js"), true ? [] : void 0, import.meta.url)
209509
209516
  });
209510
209517
  var __defProp = Object.defineProperty;
209511
209518
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
@@ -214235,10 +214242,12 @@ function ticketPrompt(t, refs) {
214235
214242
  "ctlsurf references (use the ctlsurf MCP tools to read details or update status):",
214236
214243
  `- Ticket row ID: ${t.id}`
214237
214244
  );
214238
- if (refs) {
214245
+ const blockId = t.blockId ?? refs?.blockId;
214246
+ const pageId = t.pageId ?? refs?.pageId;
214247
+ if (blockId && pageId) {
214239
214248
  lines.push(
214240
- `- Tickets datastore block ID: ${refs.blockId}`,
214241
- `- Agent Datastore page ID: ${refs.pageId}`
214249
+ `- Tickets datastore block ID: ${blockId}`,
214250
+ `- Agent Datastore page ID: ${pageId}`
214242
214251
  );
214243
214252
  }
214244
214253
  return lines.join("\n");
@@ -214307,6 +214316,7 @@ function TicketPane({ onSendToTerminal }) {
214307
214316
  const [listError, setListError] = reactExports.useState(null);
214308
214317
  const [showDone, setShowDone] = reactExports.useState(false);
214309
214318
  const [editingId, setEditingId] = reactExports.useState(null);
214319
+ const [editingBlockId, setEditingBlockId] = reactExports.useState(null);
214310
214320
  const [title, setTitle] = reactExports.useState("");
214311
214321
  const [description, setDescription] = reactExports.useState("");
214312
214322
  const [status2, setStatus] = reactExports.useState(STATUS_OPTIONS[0].value);
@@ -214353,6 +214363,7 @@ function TicketPane({ onSendToTerminal }) {
214353
214363
  }, [refreshList]);
214354
214364
  const openNewForm = reactExports.useCallback(() => {
214355
214365
  setEditingId(null);
214366
+ setEditingBlockId(null);
214356
214367
  setTitle("");
214357
214368
  setDescription("");
214358
214369
  setStatus(STATUS_OPTIONS[0].value);
@@ -214362,6 +214373,7 @@ function TicketPane({ onSendToTerminal }) {
214362
214373
  }, []);
214363
214374
  const openEditForm = reactExports.useCallback((t) => {
214364
214375
  setEditingId(t.id);
214376
+ setEditingBlockId(t.blockId ?? null);
214365
214377
  setTitle(t.title);
214366
214378
  setDescription(t.description);
214367
214379
  setStatus(STATUS_OPTIONS.some((o) => o.value === t.status) ? t.status : STATUS_OPTIONS[0].value);
@@ -214374,7 +214386,7 @@ function TicketPane({ onSendToTerminal }) {
214374
214386
  setSave({ kind: "saving" });
214375
214387
  const payload = { title: title.trim(), description: description.trim(), status: status2, priority };
214376
214388
  try {
214377
- const r = editingId ? await window.worker.updateTicket(editingId, payload) : await window.worker.addTicket(payload);
214389
+ const r = editingId ? await window.worker.updateTicket(editingId, payload, editingBlockId ?? void 0) : await window.worker.addTicket(payload);
214378
214390
  if (r?.ok) {
214379
214391
  setView("list");
214380
214392
  void refreshList();
@@ -214384,7 +214396,7 @@ function TicketPane({ onSendToTerminal }) {
214384
214396
  } catch (err) {
214385
214397
  setSave({ kind: "error", message: err?.message || "Failed to save ticket" });
214386
214398
  }
214387
- }, [title, description, status2, priority, editingId, save.kind, refreshList]);
214399
+ }, [title, description, status2, priority, editingId, editingBlockId, save.kind, refreshList]);
214388
214400
  reactExports.useEffect(() => {
214389
214401
  if (view !== "form") return;
214390
214402
  const onKey = (e) => {