@phenx-inc/ctlsurf 0.12.1 → 0.13.4
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.
- package/LICENSE +22 -0
- package/out/headless/index.mjs +33 -36
- package/out/headless/index.mjs.map +3 -3
- package/out/main/index.js +47 -40
- package/out/preload/index.js +1 -0
- package/out/renderer/assets/{cssMode-D7r66wvO.js → cssMode-CAEObGNt.js} +3 -3
- package/out/renderer/assets/{freemarker2-jSWJIjPG.js → freemarker2-D63hnnDq.js} +1 -1
- package/out/renderer/assets/{handlebars-BnFyz5RH.js → handlebars-UYo_IYpi.js} +1 -1
- package/out/renderer/assets/{html-Bl5UFavp.js → html-2dvYDi3g.js} +1 -1
- package/out/renderer/assets/{htmlMode-D4VByBT6.js → htmlMode-B6fxDDPF.js} +3 -3
- package/out/renderer/assets/{index-Cc_TqOCL.css → index-BEekQe3-.css} +232 -8
- package/out/renderer/assets/{index-DxhW1emk.js → index-CMb4AqkZ.js} +786 -473
- package/out/renderer/assets/{javascript-CMN1OmwG.js → javascript-8RyFDDdk.js} +2 -2
- package/out/renderer/assets/{jsonMode-CJ5AYSPV.js → jsonMode-CqspkWCb.js} +3 -3
- package/out/renderer/assets/{liquid-BGMIDTEm.js → liquid-DaEnAsYY.js} +1 -1
- package/out/renderer/assets/{lspLanguageFeatures-DGifVYB5.js → lspLanguageFeatures-Czkp5sZb.js} +1 -1
- package/out/renderer/assets/{mdx-B2Z7R10Q.js → mdx-Q-qSZsFg.js} +1 -1
- package/out/renderer/assets/{python-LMDMSSdn.js → python-Sw0S0BJV.js} +1 -1
- package/out/renderer/assets/{razor-BZSrziPu.js → razor-C7zQmabz.js} +1 -1
- package/out/renderer/assets/{tsMode-DF9m6_3n.js → tsMode-BWdOxk6v.js} +1 -1
- package/out/renderer/assets/{typescript-rZeCMf0a.js → typescript-BrdIiKZf.js} +1 -1
- package/out/renderer/assets/{xml-fNSZ-7so.js → xml-DbKLNPOq.js} +1 -1
- package/out/renderer/assets/{yaml-1wxIuvzh.js → yaml-ehupJBsZ.js} +1 -1
- package/out/renderer/index.html +2 -2
- package/package.json +2 -2
- package/src/main/bridge.ts +3 -2
- package/src/main/index.ts +15 -0
- package/src/main/orchestrator.ts +1 -1
- package/src/main/pty.ts +2 -5
- package/src/main/ticketStore.ts +28 -21
- package/src/main/timeTracker.ts +4 -1
- package/src/main/transcripts.ts +3 -2
- package/src/preload/index.ts +2 -0
- package/src/renderer/App.tsx +15 -2
- package/src/renderer/components/CtlsurfPanel.tsx +169 -35
- package/src/renderer/components/SettingsDialog.tsx +13 -0
- package/src/renderer/components/SpeakControls.tsx +5 -0
- package/src/renderer/components/TerminalPanel.tsx +155 -48
- package/src/renderer/components/TicketPanel.tsx +80 -24
- package/src/renderer/lib/speech.ts +2 -1
- package/src/renderer/styles.css +232 -8
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { conf as conf$1, language as language$1 } from "./typescript-
|
|
2
|
-
import "./index-
|
|
1
|
+
import { conf as conf$1, language as language$1 } from "./typescript-BrdIiKZf.js";
|
|
2
|
+
import "./index-CMb4AqkZ.js";
|
|
3
3
|
const conf = conf$1;
|
|
4
4
|
const language = {
|
|
5
5
|
// Set defaultToken to invalid to see what you do not tokenize yet
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { c as createWebWorker, l as languages, e as editor } from "./index-
|
|
2
|
-
import { f as DocumentFormattingEditProvider, g as DocumentRangeFormattingEditProvider, C as CompletionAdapter, H as HoverAdapter, b as DocumentSymbolAdapter, d as DocumentColorAdapter, F as FoldingRangeAdapter, S as SelectionRangeAdapter, e as DiagnosticsAdapter } from "./lspLanguageFeatures-
|
|
3
|
-
import { a, D, h, R, c, i, j, t, k } from "./lspLanguageFeatures-
|
|
1
|
+
import { c as createWebWorker, l as languages, e as editor } from "./index-CMb4AqkZ.js";
|
|
2
|
+
import { f as DocumentFormattingEditProvider, g as DocumentRangeFormattingEditProvider, C as CompletionAdapter, H as HoverAdapter, b as DocumentSymbolAdapter, d as DocumentColorAdapter, F as FoldingRangeAdapter, S as SelectionRangeAdapter, e as DiagnosticsAdapter } from "./lspLanguageFeatures-Czkp5sZb.js";
|
|
3
|
+
import { a, D, h, R, c, i, j, t, k } from "./lspLanguageFeatures-Czkp5sZb.js";
|
|
4
4
|
const STOP_WHEN_IDLE_FOR = 2 * 60 * 1e3;
|
|
5
5
|
class WorkerManager {
|
|
6
6
|
constructor(defaults) {
|
package/out/renderer/assets/{lspLanguageFeatures-DGifVYB5.js → lspLanguageFeatures-Czkp5sZb.js}
RENAMED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { R as Range$1, l as languages, e as editor, U as Uri, M as MarkerSeverity } from "./index-
|
|
1
|
+
import { R as Range$1, l as languages, e as editor, U as Uri, M as MarkerSeverity } from "./index-CMb4AqkZ.js";
|
|
2
2
|
var DocumentUri;
|
|
3
3
|
(function(DocumentUri2) {
|
|
4
4
|
function is(value) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { c as createWebWorker, e as editor, U as Uri, a as MarkerTag, M as MarkerSeverity, l as languages, t as typescriptDefaults, R as Range } from "./index-
|
|
1
|
+
import { c as createWebWorker, e as editor, U as Uri, a as MarkerTag, M as MarkerSeverity, l as languages, t as typescriptDefaults, R as Range } from "./index-CMb4AqkZ.js";
|
|
2
2
|
class WorkerManager {
|
|
3
3
|
constructor(_modeId, _defaults) {
|
|
4
4
|
this._modeId = _modeId;
|
package/out/renderer/index.html
CHANGED
|
@@ -49,8 +49,8 @@
|
|
|
49
49
|
color: #565f89;
|
|
50
50
|
}
|
|
51
51
|
</style>
|
|
52
|
-
<script type="module" crossorigin src="./assets/index-
|
|
53
|
-
<link rel="stylesheet" crossorigin href="./assets/index-
|
|
52
|
+
<script type="module" crossorigin src="./assets/index-CMb4AqkZ.js"></script>
|
|
53
|
+
<link rel="stylesheet" crossorigin href="./assets/index-BEekQe3-.css">
|
|
54
54
|
</head>
|
|
55
55
|
<body>
|
|
56
56
|
<div id="boot-splash">
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@phenx-inc/ctlsurf",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.4",
|
|
4
4
|
"description": "Agent-agnostic terminal and desktop app for ctlsurf — run Claude Code, Codex, or any coding agent with live session logging and remote control",
|
|
5
5
|
"main": "out/main/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"node-pty",
|
|
39
39
|
"xterm"
|
|
40
40
|
],
|
|
41
|
-
"license": "
|
|
41
|
+
"license": "SEE LICENSE IN LICENSE",
|
|
42
42
|
"engines": {
|
|
43
43
|
"node": ">=18"
|
|
44
44
|
},
|
package/src/main/bridge.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { WorkerWsClient } from './workerWs'
|
|
2
2
|
import { Terminal as HeadlessTerminal } from '@xterm/headless/lib-headless/xterm-headless.js'
|
|
3
3
|
import { SerializeAddon } from '@xterm/addon-serialize'
|
|
4
|
+
import { log } from './logger'
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* Conversation Bridge
|
|
@@ -36,7 +37,7 @@ export class ConversationBridge {
|
|
|
36
37
|
this.inputBuffer = ''
|
|
37
38
|
this.terminalCapture.reset()
|
|
38
39
|
this.sessionActive = true
|
|
39
|
-
|
|
40
|
+
log('[bridge] Session started')
|
|
40
41
|
}
|
|
41
42
|
|
|
42
43
|
feedOutput(data: string): void {
|
|
@@ -137,7 +138,7 @@ export class ConversationBridge {
|
|
|
137
138
|
this.sessionActive = false
|
|
138
139
|
this.inputBuffer = ''
|
|
139
140
|
this.outputBuffer = ''
|
|
140
|
-
|
|
141
|
+
log('[bridge] Session ended')
|
|
141
142
|
}
|
|
142
143
|
}
|
|
143
144
|
|
package/src/main/index.ts
CHANGED
|
@@ -254,6 +254,15 @@ function createWindow(): void {
|
|
|
254
254
|
callback(true)
|
|
255
255
|
})
|
|
256
256
|
|
|
257
|
+
// Keep links opened from inside the embedded ctlsurf webview (target=_blank,
|
|
258
|
+
// window.open) in the panel instead of spawning a detached popup window.
|
|
259
|
+
mainWindow.webContents.on('did-attach-webview', (_e, contents) => {
|
|
260
|
+
contents.setWindowOpenHandler(({ url }) => {
|
|
261
|
+
setImmediate(() => contents.loadURL(url))
|
|
262
|
+
return { action: 'deny' }
|
|
263
|
+
})
|
|
264
|
+
})
|
|
265
|
+
|
|
257
266
|
if (process.env.ELECTRON_RENDERER_URL) {
|
|
258
267
|
mainWindow.loadURL(process.env.ELECTRON_RENDERER_URL)
|
|
259
268
|
} else {
|
|
@@ -280,6 +289,12 @@ ipcMain.handle('clipboard:writeTable', (_event, html: string, text: string) => {
|
|
|
280
289
|
return { ok: true }
|
|
281
290
|
})
|
|
282
291
|
|
|
292
|
+
// Plain-text clipboard write, for the terminal right-click "Copy" item.
|
|
293
|
+
ipcMain.handle('clipboard:writeText', (_event, text: string) => {
|
|
294
|
+
clipboard.writeText(text)
|
|
295
|
+
return { ok: true }
|
|
296
|
+
})
|
|
297
|
+
|
|
283
298
|
ipcMain.handle('pty:spawn', async (_event, tabId: string, agent: AgentConfig, cwd: string) => {
|
|
284
299
|
await orchestrator.spawnAgent(tabId, agent, cwd)
|
|
285
300
|
return { ok: true }
|
package/src/main/orchestrator.ts
CHANGED
|
@@ -708,7 +708,7 @@ export class Orchestrator {
|
|
|
708
708
|
return this.ticketStore.updateTicket(cwd, rowId, input)
|
|
709
709
|
}
|
|
710
710
|
|
|
711
|
-
async listTicketsForActiveTab(): Promise<{ ok: boolean; tickets: import('./ticketStore').Ticket[]; error?: string }> {
|
|
711
|
+
async listTicketsForActiveTab(): Promise<{ ok: boolean; tickets: import('./ticketStore').Ticket[]; blockId?: string; pageId?: string; error?: string }> {
|
|
712
712
|
const cwd = this.getActiveTabProjectPath()
|
|
713
713
|
if (!cwd) return { ok: false, tickets: [], error: 'No active terminal tab' }
|
|
714
714
|
if (!this.ctlsurfApi.getApiKey()) {
|
package/src/main/pty.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createRequire } from 'module'
|
|
2
2
|
import { AgentConfig } from './agents'
|
|
3
|
+
import { log } from './logger'
|
|
3
4
|
|
|
4
5
|
// Use createRequire to load native module at runtime, bypassing bundler
|
|
5
6
|
const require = createRequire(import.meta.url)
|
|
@@ -14,11 +15,7 @@ export class PtyManager {
|
|
|
14
15
|
const shell = agent.command
|
|
15
16
|
const args = agent.args || []
|
|
16
17
|
|
|
17
|
-
|
|
18
|
-
console.log(`[pty] Spawning: ${shell} ${args.join(' ')} in ${cwd}`)
|
|
19
|
-
} catch {
|
|
20
|
-
// Ignore EPIPE errors when stdout is closed
|
|
21
|
-
}
|
|
18
|
+
log(`[pty] Spawning: ${shell} ${args.join(' ')} in ${cwd}`)
|
|
22
19
|
|
|
23
20
|
this.process = pty.spawn(shell, args, {
|
|
24
21
|
name: 'xterm-256color',
|
package/src/main/ticketStore.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { CtlsurfApi } from './ctlsurfApi'
|
|
2
|
+
import { log as baseLog } from './logger'
|
|
2
3
|
|
|
3
4
|
const DATASTORE_TITLE = 'Tickets'
|
|
4
5
|
const AGENT_DATASTORE_PAGE_TITLE = 'Agent Datastore'
|
|
@@ -47,7 +48,7 @@ export interface Ticket {
|
|
|
47
48
|
}
|
|
48
49
|
|
|
49
50
|
function log(...args: unknown[]): void {
|
|
50
|
-
|
|
51
|
+
baseLog('[ticket-store]', ...args)
|
|
51
52
|
}
|
|
52
53
|
|
|
53
54
|
function findPageByTitle(pages: any[], title: string): any | null {
|
|
@@ -69,7 +70,10 @@ function findPageByTitle(pages: any[], title: string): any | null {
|
|
|
69
70
|
*/
|
|
70
71
|
export class TicketStore {
|
|
71
72
|
private api: CtlsurfApi
|
|
72
|
-
|
|
73
|
+
// Per-cwd cache of the resolved Tickets datastore: the block (the datastore
|
|
74
|
+
// element) and the Agent Datastore page it lives on. Both ids are handed to
|
|
75
|
+
// the coding agent so it can read/update tickets via the ctlsurf MCP tools.
|
|
76
|
+
private dsCache = new Map<string, { blockId: string; pageId: string }>()
|
|
73
77
|
|
|
74
78
|
constructor(api: CtlsurfApi) {
|
|
75
79
|
this.api = api
|
|
@@ -80,11 +84,11 @@ export class TicketStore {
|
|
|
80
84
|
const title = input.title?.trim()
|
|
81
85
|
if (!title) return { ok: false, error: 'Title is required' }
|
|
82
86
|
try {
|
|
83
|
-
const
|
|
84
|
-
if (!
|
|
87
|
+
const ds = await this.ensureDatastore(cwd, true)
|
|
88
|
+
if (!ds) {
|
|
85
89
|
return { ok: false, error: 'No ctlsurf project found for this folder' }
|
|
86
90
|
}
|
|
87
|
-
await this.api.addRow(blockId, {
|
|
91
|
+
await this.api.addRow(ds.blockId, {
|
|
88
92
|
Title: title,
|
|
89
93
|
Description: input.description?.trim() || '',
|
|
90
94
|
Status: input.status || 'Open',
|
|
@@ -104,9 +108,9 @@ export class TicketStore {
|
|
|
104
108
|
const title = input.title?.trim()
|
|
105
109
|
if (!title) return { ok: false, error: 'Title is required' }
|
|
106
110
|
try {
|
|
107
|
-
const
|
|
108
|
-
if (!
|
|
109
|
-
await this.api.updateRow(blockId, rowId, {
|
|
111
|
+
const ds = await this.ensureDatastore(cwd, false)
|
|
112
|
+
if (!ds) return { ok: false, error: 'No Tickets datastore for this project' }
|
|
113
|
+
await this.api.updateRow(ds.blockId, rowId, {
|
|
110
114
|
Title: title,
|
|
111
115
|
Description: input.description?.trim() || '',
|
|
112
116
|
Status: input.status || 'Open',
|
|
@@ -122,11 +126,11 @@ export class TicketStore {
|
|
|
122
126
|
|
|
123
127
|
/** Lists existing tickets for the project, newest first. Does not create the
|
|
124
128
|
* datastore — an unconfigured project simply has no tickets yet. */
|
|
125
|
-
async listTickets(cwd: string): Promise<{ ok: boolean; tickets: Ticket[]; error?: string }> {
|
|
129
|
+
async listTickets(cwd: string): Promise<{ ok: boolean; tickets: Ticket[]; blockId?: string; pageId?: string; error?: string }> {
|
|
126
130
|
try {
|
|
127
|
-
const
|
|
128
|
-
if (!
|
|
129
|
-
const res = await this.api.queryRows(blockId, { orderBy: 'Created', order: 'desc', limit: 200 })
|
|
131
|
+
const ds = await this.ensureDatastore(cwd, false)
|
|
132
|
+
if (!ds) return { ok: true, tickets: [] }
|
|
133
|
+
const res = await this.api.queryRows(ds.blockId, { orderBy: 'Created', order: 'desc', limit: 200 })
|
|
130
134
|
const tickets: Ticket[] = (res?.rows || []).map(r => ({
|
|
131
135
|
id: r.id,
|
|
132
136
|
title: String(r.data?.Title ?? ''),
|
|
@@ -135,15 +139,15 @@ export class TicketStore {
|
|
|
135
139
|
priority: String(r.data?.Priority ?? 'Med'),
|
|
136
140
|
created: (r.data?.Created as string) ?? r.created_at ?? null,
|
|
137
141
|
}))
|
|
138
|
-
return { ok: true, tickets }
|
|
142
|
+
return { ok: true, tickets, blockId: ds.blockId, pageId: ds.pageId }
|
|
139
143
|
} catch (err: any) {
|
|
140
144
|
log(`listTickets failed for ${cwd}: ${err?.message || err}`)
|
|
141
145
|
return { ok: false, tickets: [], error: err?.message || String(err) }
|
|
142
146
|
}
|
|
143
147
|
}
|
|
144
148
|
|
|
145
|
-
private async ensureDatastore(cwd: string, create: boolean): Promise<string | null> {
|
|
146
|
-
const cached = this.
|
|
149
|
+
private async ensureDatastore(cwd: string, create: boolean): Promise<{ blockId: string; pageId: string } | null> {
|
|
150
|
+
const cached = this.dsCache.get(cwd)
|
|
147
151
|
if (cached) return cached
|
|
148
152
|
|
|
149
153
|
let folder: any = null
|
|
@@ -157,12 +161,14 @@ export class TicketStore {
|
|
|
157
161
|
const folderDetail = await this.api.getFolder(folder.id)
|
|
158
162
|
const agentPage = findPageByTitle(folderDetail?.pages || [], AGENT_DATASTORE_PAGE_TITLE)
|
|
159
163
|
if (!agentPage?.id) return null
|
|
164
|
+
const pageId: string = agentPage.id
|
|
160
165
|
|
|
161
|
-
const blockId = await this.findOrAdoptBlock(
|
|
166
|
+
const blockId = await this.findOrAdoptBlock(pageId)
|
|
162
167
|
if (blockId) {
|
|
163
168
|
await this.ensureColumns(blockId)
|
|
164
|
-
|
|
165
|
-
|
|
169
|
+
const ds = { blockId, pageId }
|
|
170
|
+
this.dsCache.set(cwd, ds)
|
|
171
|
+
return ds
|
|
166
172
|
}
|
|
167
173
|
|
|
168
174
|
if (!create) return null
|
|
@@ -173,15 +179,16 @@ export class TicketStore {
|
|
|
173
179
|
type: c.type,
|
|
174
180
|
...(c.options ? { options: c.options } : {}),
|
|
175
181
|
}))
|
|
176
|
-
const created = await this.api.createBlock(
|
|
182
|
+
const created = await this.api.createBlock(pageId, {
|
|
177
183
|
type: 'datastore',
|
|
178
184
|
title: DATASTORE_TITLE,
|
|
179
185
|
props: { columns, system_key: SYSTEM_KEY },
|
|
180
186
|
})
|
|
181
187
|
if (created?.id) {
|
|
182
188
|
log(`Created "${DATASTORE_TITLE}" datastore on Agent Datastore page for ${cwd}`)
|
|
183
|
-
|
|
184
|
-
|
|
189
|
+
const ds = { blockId: created.id, pageId }
|
|
190
|
+
this.dsCache.set(cwd, ds)
|
|
191
|
+
return ds
|
|
185
192
|
}
|
|
186
193
|
return null
|
|
187
194
|
}
|
package/src/main/timeTracker.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import os from 'os'
|
|
2
2
|
import { randomUUID } from 'crypto'
|
|
3
3
|
import type { CtlsurfApi } from './ctlsurfApi'
|
|
4
|
+
import { log as baseLog } from './logger'
|
|
4
5
|
|
|
5
6
|
const DATASTORE_TITLE = 'Time Tracking'
|
|
6
7
|
const AGENT_DATASTORE_PAGE_TITLE = 'Agent Datastore'
|
|
@@ -56,7 +57,9 @@ function isSameLocalDay(a: number, b: number): boolean {
|
|
|
56
57
|
}
|
|
57
58
|
|
|
58
59
|
function log(...args: unknown[]): void {
|
|
59
|
-
|
|
60
|
+
// Route through the shared logger so terminal/TUI mode (setSilent) suppresses
|
|
61
|
+
// these — they'd otherwise corrupt the PTY display the agent owns.
|
|
62
|
+
baseLog('[time-tracker]', ...args)
|
|
60
63
|
}
|
|
61
64
|
|
|
62
65
|
function findPageByTitle(pages: any[], title: string): any | null {
|
package/src/main/transcripts.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import fs from 'fs'
|
|
2
2
|
import path from 'path'
|
|
3
3
|
import os from 'os'
|
|
4
|
+
import { log } from './logger'
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* Transcript Tailer
|
|
@@ -80,7 +81,7 @@ export class TranscriptTailer {
|
|
|
80
81
|
this.sinceMs = Date.now()
|
|
81
82
|
this.files.clear()
|
|
82
83
|
this.pollTimer = setInterval(() => this.poll(), POLL_INTERVAL_MS)
|
|
83
|
-
|
|
84
|
+
log(`[transcripts] Tailing ${this.agentId} transcripts for ${this.cwd}`)
|
|
84
85
|
}
|
|
85
86
|
|
|
86
87
|
stop(): void {
|
|
@@ -90,7 +91,7 @@ export class TranscriptTailer {
|
|
|
90
91
|
// Final drain so messages written just before exit aren't lost.
|
|
91
92
|
this.poll()
|
|
92
93
|
this.files.clear()
|
|
93
|
-
|
|
94
|
+
log('[transcripts] Stopped')
|
|
94
95
|
}
|
|
95
96
|
|
|
96
97
|
private poll(): void {
|
package/src/preload/index.ts
CHANGED
|
@@ -83,6 +83,8 @@ const api = {
|
|
|
83
83
|
// Clipboard — write a rich-HTML table (+ plain-text fallback) for email paste.
|
|
84
84
|
copyEmailTable: (html: string, text: string): Promise<{ ok: boolean }> =>
|
|
85
85
|
ipcRenderer.invoke('clipboard:writeTable', html, text),
|
|
86
|
+
copyText: (text: string): Promise<{ ok: boolean }> =>
|
|
87
|
+
ipcRenderer.invoke('clipboard:writeText', text),
|
|
86
88
|
getVersion: (): Promise<string> =>
|
|
87
89
|
ipcRenderer.invoke('app:getVersion'),
|
|
88
90
|
getUpdateInfo: (): Promise<{ current: string; latest: string | null; hasUpdate: boolean; checkedAt: number | null }> =>
|
package/src/renderer/App.tsx
CHANGED
|
@@ -60,7 +60,7 @@ declare global {
|
|
|
60
60
|
getTicketProject: () => Promise<{ cwd: string | null; name: string | null }>
|
|
61
61
|
addTicket: (input: { title: string; description?: string; status?: string; priority?: string }) => Promise<{ ok: boolean; error?: string }>
|
|
62
62
|
updateTicket: (rowId: string, input: { title: string; description?: string; status?: string; priority?: string }) => Promise<{ ok: boolean; error?: string }>
|
|
63
|
-
listTickets: () => Promise<{ ok: boolean; tickets: Array<{ id: string; title: string; description: string; status: string; priority: string; created: string | null }>; error?: string }>
|
|
63
|
+
listTickets: () => Promise<{ ok: boolean; tickets: Array<{ id: string; title: string; description: string; status: string; priority: string; created: string | null }>; blockId?: string; pageId?: string; error?: string }>
|
|
64
64
|
getTracking: () => Promise<{ active: boolean }>
|
|
65
65
|
setTracking: (enabled: boolean) => Promise<{ active: boolean }>
|
|
66
66
|
getLogChat: () => Promise<{ enabled: boolean }>
|
|
@@ -88,6 +88,7 @@ declare global {
|
|
|
88
88
|
getUpdateInfo: () => Promise<UpdateInfo>
|
|
89
89
|
onUpdateInfo: (callback: (info: UpdateInfo) => void) => () => void
|
|
90
90
|
copyEmailTable: (html: string, text: string) => Promise<{ ok: boolean }>
|
|
91
|
+
copyText: (text: string) => Promise<{ ok: boolean }>
|
|
91
92
|
}
|
|
92
93
|
}
|
|
93
94
|
}
|
|
@@ -269,6 +270,18 @@ export default function App() {
|
|
|
269
270
|
setTimeout(() => window.worker.writePty(activeTabId, '\r'), 80)
|
|
270
271
|
}, [activeTabId])
|
|
271
272
|
|
|
273
|
+
// Send a prepared prompt (e.g. a ticket) into the active terminal and submit
|
|
274
|
+
// it, so the coding agent can start working immediately. Same paste-then-
|
|
275
|
+
// delayed-\r dance as voice typing — a slightly longer delay since ticket
|
|
276
|
+
// prompts are multi-line and take a touch longer to drain before the Enter.
|
|
277
|
+
const handleSendToTerminal = useCallback((text: string) => {
|
|
278
|
+
const trimmed = text.trim()
|
|
279
|
+
if (!trimmed) return
|
|
280
|
+
window.worker.writePty(activeTabId, trimmed)
|
|
281
|
+
focusTerminal(activeTabId)
|
|
282
|
+
setTimeout(() => window.worker.writePty(activeTabId, '\r'), 120)
|
|
283
|
+
}, [activeTabId])
|
|
284
|
+
|
|
272
285
|
const cwdRef = useRef<string | null>(null)
|
|
273
286
|
|
|
274
287
|
const handleSpawn = useCallback(async (tabId: string, agent: AgentConfig) => {
|
|
@@ -510,7 +523,7 @@ export default function App() {
|
|
|
510
523
|
),
|
|
511
524
|
},
|
|
512
525
|
{ id: 'ctlsurf', label: 'ctlsurf', content: <CtlsurfPanel navigate={ctlsurfNav} /> },
|
|
513
|
-
{ id: 'tickets', label: 'Tickets', content: <TicketPane /> },
|
|
526
|
+
{ id: 'tickets', label: 'Tickets', content: <TicketPane onSendToTerminal={handleSendToTerminal} /> },
|
|
514
527
|
]
|
|
515
528
|
|
|
516
529
|
return (
|