@phenx-inc/ctlsurf 0.8.0 → 0.9.0

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 (37) hide show
  1. package/out/headless/index.mjs +144 -1
  2. package/out/headless/index.mjs.map +2 -2
  3. package/out/main/index.js +144 -0
  4. package/out/preload/index.js +2 -0
  5. package/out/renderer/assets/{cssMode-BQN8v2ok.js → cssMode-BdYp0fp9.js} +3 -3
  6. package/out/renderer/assets/{freemarker2-DbxGYYVp.js → freemarker2-Cya-9zr-.js} +1 -1
  7. package/out/renderer/assets/{handlebars-3auU1CAd.js → handlebars-EJs9W5Ub.js} +1 -1
  8. package/out/renderer/assets/{html-D8xFiRmI.js → html-Ex4qy2lh.js} +1 -1
  9. package/out/renderer/assets/{htmlMode-M3MApZ4n.js → htmlMode-CCm0aJ6C.js} +3 -3
  10. package/out/renderer/assets/{index---H6cxNl.js → index-Cj7K9-Ba.js} +265 -242
  11. package/out/renderer/assets/{index-B-iM7dFC.css → index-nvsNYP5c.css} +34 -0
  12. package/out/renderer/assets/{javascript-BO_ViZM5.js → javascript-B2niCouI.js} +2 -2
  13. package/out/renderer/assets/{jsonMode-CKp2zvZu.js → jsonMode-BgCUHaFo.js} +3 -3
  14. package/out/renderer/assets/{liquid-C1eHcrht.js → liquid-DOQmSo4G.js} +1 -1
  15. package/out/renderer/assets/{lspLanguageFeatures-CHWJx_Tl.js → lspLanguageFeatures-8SIcRAMh.js} +1 -1
  16. package/out/renderer/assets/{mdx-Qqdtk7fL.js → mdx-DuKdGNKE.js} +1 -1
  17. package/out/renderer/assets/{python-DKu7rNbs.js → python-BUa4XFG-.js} +1 -1
  18. package/out/renderer/assets/{razor-BOMpCo6z.js → razor-BUw8jho0.js} +1 -1
  19. package/out/renderer/assets/{transformers.web-DtSCnG36.js → transformers.web-_PuZoIho.js} +824 -19
  20. package/out/renderer/assets/{tsMode-yAjlPR-D.js → tsMode-8FLRnobw.js} +1 -1
  21. package/out/renderer/assets/{typescript-BiJRCUcL.js → typescript-rdwAjCi4.js} +1 -1
  22. package/out/renderer/assets/{xml-D4PvYeQq.js → xml-HOzT59Qg.js} +1 -1
  23. package/out/renderer/assets/{yaml-BeHVkmnS.js → yaml-B81JfLsW.js} +1 -1
  24. package/out/renderer/index.html +64 -2
  25. package/out/repro-toggle.mjs +305 -0
  26. package/package.json +1 -1
  27. package/src/main/index.ts +2 -0
  28. package/src/main/orchestrator.ts +13 -1
  29. package/src/main/transcripts.ts +134 -0
  30. package/src/preload/index.ts +3 -0
  31. package/src/renderer/App.tsx +25 -11
  32. package/src/renderer/components/FloatingMic.tsx +48 -0
  33. package/src/renderer/components/VoiceInput.tsx +36 -149
  34. package/src/renderer/index.html +62 -0
  35. package/src/renderer/lib/localWhisper.ts +46 -2
  36. package/src/renderer/lib/speech.ts +18 -0
  37. package/src/renderer/styles.css +34 -0
package/out/main/index.js CHANGED
@@ -6282,6 +6282,138 @@ function capLength(str) {
6282
6282
  if (str.length <= MAX_ENTRY_CHARS) return str;
6283
6283
  return str.slice(0, MAX_ENTRY_CHARS) + `… [truncated, ${str.length} total chars]`;
6284
6284
  }
6285
+ function readLatestAssistantReply(opts) {
6286
+ try {
6287
+ const cwd = stripTrailingSep(opts.cwd);
6288
+ if (opts.agentId === "claude") {
6289
+ const file = mostRecentClaudeFile(cwd, opts.claudeProjectsDir);
6290
+ if (!file) return null;
6291
+ const text = lastClaudeAssistant(file, cwd);
6292
+ return text ? capLength(text) : null;
6293
+ }
6294
+ const codexRoot = opts.codexSessionsDir || path.join(os.homedir(), ".codex", "sessions");
6295
+ for (const file of recentCodexRollouts(codexRoot)) {
6296
+ const text = lastCodexAgentMessage(file, cwd);
6297
+ if (text) return capLength(text);
6298
+ }
6299
+ return null;
6300
+ } catch {
6301
+ return null;
6302
+ }
6303
+ }
6304
+ function readLines(file) {
6305
+ try {
6306
+ return fs.readFileSync(file, "utf-8").split("\n");
6307
+ } catch {
6308
+ return [];
6309
+ }
6310
+ }
6311
+ function mostRecentClaudeFile(cwd, root) {
6312
+ const projectsRoot = root || path.join(os.homedir(), ".claude", "projects");
6313
+ const slug = cwd.replace(/[^a-zA-Z0-9]/g, "-");
6314
+ const dir = path.join(projectsRoot, slug);
6315
+ let names;
6316
+ try {
6317
+ names = fs.readdirSync(dir);
6318
+ } catch {
6319
+ return null;
6320
+ }
6321
+ let best = null;
6322
+ let bestMs = -Infinity;
6323
+ for (const name of names) {
6324
+ if (!name.endsWith(".jsonl")) continue;
6325
+ const fp = path.join(dir, name);
6326
+ try {
6327
+ const ms = fs.statSync(fp).mtimeMs;
6328
+ if (ms > bestMs) {
6329
+ bestMs = ms;
6330
+ best = fp;
6331
+ }
6332
+ } catch {
6333
+ }
6334
+ }
6335
+ return best;
6336
+ }
6337
+ function recentCodexRollouts(root) {
6338
+ const dirs = /* @__PURE__ */ new Set();
6339
+ const now = Date.now();
6340
+ for (const ms of [now, now - 24 * 60 * 60 * 1e3]) {
6341
+ const d = new Date(ms);
6342
+ const yyyy = String(d.getFullYear());
6343
+ const mm = String(d.getMonth() + 1).padStart(2, "0");
6344
+ const dd = String(d.getDate()).padStart(2, "0");
6345
+ dirs.add(path.join(root, yyyy, mm, dd));
6346
+ }
6347
+ const files = [];
6348
+ for (const dir of dirs) {
6349
+ let names;
6350
+ try {
6351
+ names = fs.readdirSync(dir);
6352
+ } catch {
6353
+ continue;
6354
+ }
6355
+ for (const name of names) {
6356
+ if (!name.startsWith("rollout-") || !name.endsWith(".jsonl")) continue;
6357
+ const fp = path.join(dir, name);
6358
+ try {
6359
+ files.push({ fp, ms: fs.statSync(fp).mtimeMs });
6360
+ } catch {
6361
+ }
6362
+ }
6363
+ }
6364
+ return files.sort((a, b2) => b2.ms - a.ms).map((f) => f.fp);
6365
+ }
6366
+ function lastClaudeAssistant(file, cwd) {
6367
+ const lines = readLines(file);
6368
+ for (let i = lines.length - 1; i >= 0; i--) {
6369
+ const trimmed = lines[i].trim();
6370
+ if (!trimmed) continue;
6371
+ let obj;
6372
+ try {
6373
+ obj = JSON.parse(trimmed);
6374
+ } catch {
6375
+ continue;
6376
+ }
6377
+ if (!obj || typeof obj !== "object") continue;
6378
+ if (obj.isMeta || obj.isSidechain || obj.type !== "assistant") continue;
6379
+ if (typeof obj.cwd === "string" && stripTrailingSep(obj.cwd) !== cwd) continue;
6380
+ const text = extractClaudeText(obj.message?.content, false);
6381
+ if (text) return text;
6382
+ }
6383
+ return null;
6384
+ }
6385
+ function lastCodexAgentMessage(file, cwd) {
6386
+ const lines = readLines(file);
6387
+ for (const line of lines) {
6388
+ const trimmed = line.trim();
6389
+ if (!trimmed) continue;
6390
+ let obj;
6391
+ try {
6392
+ obj = JSON.parse(trimmed);
6393
+ } catch {
6394
+ continue;
6395
+ }
6396
+ if (obj?.type === "session_meta") {
6397
+ const metaCwd = obj.payload?.cwd;
6398
+ if (typeof metaCwd === "string" && stripTrailingSep(metaCwd) !== cwd) return null;
6399
+ break;
6400
+ }
6401
+ }
6402
+ for (let i = lines.length - 1; i >= 0; i--) {
6403
+ const trimmed = lines[i].trim();
6404
+ if (!trimmed) continue;
6405
+ let obj;
6406
+ try {
6407
+ obj = JSON.parse(trimmed);
6408
+ } catch {
6409
+ continue;
6410
+ }
6411
+ if (obj?.type !== "event_msg" || obj.payload?.type !== "agent_message") continue;
6412
+ const text = typeof obj.payload.message === "string" ? obj.payload.message.trim() : "";
6413
+ if (text && !isCodexNoise(text)) return text;
6414
+ }
6415
+ return null;
6416
+ }
6285
6417
  var bufferUtil = { exports: {} };
6286
6418
  var constants;
6287
6419
  var hasRequiredConstants;
@@ -11011,6 +11143,17 @@ class Orchestrator {
11011
11143
  get speakRepliesEnabled() {
11012
11144
  return !!this.settings.speakReplies;
11013
11145
  }
11146
+ // Latest assistant reply from a tab's transcript, for on-demand "read aloud".
11147
+ // Defaults to the active tab. Returns null when the tab's agent has no native
11148
+ // transcript (only Claude Code / Codex do) or nothing has been said yet. Reads
11149
+ // the transcript directly, so it works regardless of the speakReplies setting.
11150
+ getLastReply(tabId) {
11151
+ const id = tabId || this.activeTabId;
11152
+ if (!id) return null;
11153
+ const tab = this.tabs.get(id);
11154
+ if (!tab || !supportsTranscriptLogging(tab.agent.id)) return null;
11155
+ return readLatestAssistantReply({ agentId: tab.agent.id, cwd: tab.cwd });
11156
+ }
11014
11157
  setLogChat(enabled) {
11015
11158
  this.settings.logChat = enabled;
11016
11159
  this.saveSettings();
@@ -11793,6 +11936,7 @@ electron.ipcMain.handle("speak:set", (_event, enabled) => {
11793
11936
  orchestrator.setSpeakReplies(!!enabled);
11794
11937
  return { enabled: orchestrator.speakRepliesEnabled };
11795
11938
  });
11939
+ electron.ipcMain.handle("agent:getLastReply", () => orchestrator.getLastReply());
11796
11940
  electron.ipcMain.handle("tracking:get", () => ({ active: orchestrator.isActiveTabTracking() }));
11797
11941
  electron.ipcMain.handle("tracking:set", async (_event, enabled) => {
11798
11942
  await orchestrator.setActiveTabTracking(enabled);
@@ -69,6 +69,8 @@ const api = {
69
69
  electron.ipcRenderer.on("agent:message", listener);
70
70
  return () => electron.ipcRenderer.removeListener("agent:message", listener);
71
71
  },
72
+ // Latest assistant reply from the active tab's transcript (null if none).
73
+ getLastReply: () => electron.ipcRenderer.invoke("agent:getLastReply"),
72
74
  // Filesystem
73
75
  readDir: (dirPath) => electron.ipcRenderer.invoke("fs:readDir", dirPath),
74
76
  readFile: (filePath) => electron.ipcRenderer.invoke("fs:readFile", filePath),
@@ -1,6 +1,6 @@
1
- import { c as createWebWorker, l as languages } from "./index---H6cxNl.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-CHWJx_Tl.js";
3
- import { h, i, j, t, k } from "./lspLanguageFeatures-CHWJx_Tl.js";
1
+ import { c as createWebWorker, l as languages } from "./index-Cj7K9-Ba.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-8SIcRAMh.js";
3
+ import { h, i, j, t, k } from "./lspLanguageFeatures-8SIcRAMh.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---H6cxNl.js";
1
+ import { l as languages } from "./index-Cj7K9-Ba.js";
2
2
  const EMPTY_ELEMENTS = [
3
3
  "assign",
4
4
  "flush",
@@ -1,4 +1,4 @@
1
- import { l as languages } from "./index---H6cxNl.js";
1
+ import { l as languages } from "./index-Cj7K9-Ba.js";
2
2
  const EMPTY_ELEMENTS = [
3
3
  "area",
4
4
  "base",
@@ -1,4 +1,4 @@
1
- import { l as languages } from "./index---H6cxNl.js";
1
+ import { l as languages } from "./index-Cj7K9-Ba.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---H6cxNl.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-CHWJx_Tl.js";
3
- import { a, e, d, R, i, j, t, k } from "./lspLanguageFeatures-CHWJx_Tl.js";
1
+ import { c as createWebWorker, l as languages } from "./index-Cj7K9-Ba.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-8SIcRAMh.js";
3
+ import { a, e, d, R, i, j, t, k } from "./lspLanguageFeatures-8SIcRAMh.js";
4
4
  const STOP_WHEN_IDLE_FOR = 2 * 60 * 1e3;
5
5
  class WorkerManager {
6
6
  constructor(defaults) {