@phenx-inc/ctlsurf 0.3.6 → 0.3.8
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/out/headless/index.mjs +276 -56
- package/out/headless/index.mjs.map +3 -3
- package/out/main/index.js +152 -94
- package/out/preload/index.js +3 -0
- package/out/renderer/assets/{cssMode-DQW-brNd.js → cssMode-CYoo4t9f.js} +3 -3
- package/out/renderer/assets/{freemarker2-DxgOckH2.js → freemarker2--UQnPZsn.js} +1 -1
- package/out/renderer/assets/{handlebars-BX1Wpk_3.js → handlebars-DVDrmX0C.js} +1 -1
- package/out/renderer/assets/{html-t-KXioI0.js → html-D1-cXoLy.js} +1 -1
- package/out/renderer/assets/{htmlMode-Dya7iUjr.js → htmlMode-f5nBuprq.js} +3 -3
- package/out/renderer/assets/{index-D6JBcQ20.css → index-65hyKM_8.css} +16 -0
- package/out/renderer/assets/{index-DNqZidnO.js → index-D23nru43.js} +64 -23
- package/out/renderer/assets/{javascript-DZzW2adn.js → javascript-CcarFzBL.js} +2 -2
- package/out/renderer/assets/{jsonMode-D_Wv7XH8.js → jsonMode-BvF-xK9U.js} +3 -3
- package/out/renderer/assets/{liquid-BJAHAm2T.js → liquid-CHLtUKl2.js} +1 -1
- package/out/renderer/assets/{lspLanguageFeatures-BgMd-KJk.js → lspLanguageFeatures-B9aNeatS.js} +1 -1
- package/out/renderer/assets/{mdx-B6Zod3ry.js → mdx-HGDrkifZ.js} +1 -1
- package/out/renderer/assets/{python-Cgt13-KH.js → python-B_dPzjJ6.js} +1 -1
- package/out/renderer/assets/{razor-BcwFJGYS.js → razor-CHheM4ot.js} +1 -1
- package/out/renderer/assets/{tsMode-BTjzM6fl.js → tsMode-CdC3i1gG.js} +1 -1
- package/out/renderer/assets/{typescript-DZYDQEUb.js → typescript-BX6guVRK.js} +1 -1
- package/out/renderer/assets/{xml-CloiUoIW.js → xml-CpS-pOPE.js} +1 -1
- package/out/renderer/assets/{yaml-CdKdpE-z.js → yaml-Du0AjOHW.js} +1 -1
- package/out/renderer/index.html +2 -2
- package/package.json +1 -1
- package/src/main/bridge.ts +9 -3
- package/src/main/headless.ts +35 -2
- package/src/main/index.ts +10 -39
- package/src/main/orchestrator.ts +29 -1
- package/src/main/timeTracker.ts +74 -45
- package/src/main/tui.ts +20 -8
- package/src/main/updateCheck.ts +40 -0
- package/src/preload/index.ts +6 -0
- package/src/renderer/App.tsx +2 -0
- package/src/renderer/components/AgentPicker.tsx +38 -3
- package/src/renderer/styles.css +16 -0
package/src/main/timeTracker.ts
CHANGED
|
@@ -30,8 +30,9 @@ function formatStarted(ms: number): string {
|
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
interface SessionState {
|
|
33
|
-
blockId: string
|
|
34
|
-
rowId: string
|
|
33
|
+
blockId: string | null
|
|
34
|
+
rowId: string | null
|
|
35
|
+
sessionUuid: string
|
|
35
36
|
cwd: string
|
|
36
37
|
agentName: string
|
|
37
38
|
idleTimeoutMin: number
|
|
@@ -83,55 +84,72 @@ export class TimeTracker {
|
|
|
83
84
|
if (this.sessions.has(tabId)) {
|
|
84
85
|
await this.endSession(tabId)
|
|
85
86
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
87
|
+
const startedAt = Date.now()
|
|
88
|
+
const state: SessionState = {
|
|
89
|
+
blockId: null,
|
|
90
|
+
rowId: null,
|
|
91
|
+
sessionUuid: randomUUID(),
|
|
92
|
+
cwd,
|
|
93
|
+
agentName,
|
|
94
|
+
idleTimeoutMin,
|
|
95
|
+
startedAt,
|
|
96
|
+
lastActivity: startedAt,
|
|
97
|
+
activeMs: 0,
|
|
98
|
+
idleTimeoutMs: Math.max(1, idleTimeoutMin) * 60 * 1000,
|
|
99
|
+
firstCheckpointTimer: null,
|
|
100
|
+
checkpointTimer: null,
|
|
101
|
+
ended: false,
|
|
102
|
+
}
|
|
103
|
+
this.sessions.set(tabId, state)
|
|
104
|
+
|
|
105
|
+
await this.tryResolve(tabId)
|
|
106
|
+
|
|
107
|
+
state.firstCheckpointTimer = setTimeout(() => {
|
|
108
|
+
void this.checkpoint(tabId)
|
|
109
|
+
const live = this.sessions.get(tabId)
|
|
110
|
+
if (live && !live.ended) {
|
|
111
|
+
live.checkpointTimer = setInterval(() => {
|
|
112
|
+
void this.checkpoint(tabId)
|
|
113
|
+
}, CHECKPOINT_INTERVAL_MS)
|
|
91
114
|
}
|
|
92
|
-
|
|
93
|
-
|
|
115
|
+
}, FIRST_CHECKPOINT_DELAY_MS)
|
|
116
|
+
|
|
117
|
+
const pending = !state.blockId || !state.rowId
|
|
118
|
+
log(`Started tracking tab=${tabId} agent="${agentName}" cwd=${cwd}${pending ? ' (pending datastore — will retry on each checkpoint)' : ''}`)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/** Attempts to locate (or create) the datastore + add the session row.
|
|
122
|
+
* Returns true once the session is resolved (blockId + rowId set).
|
|
123
|
+
* Safe to call repeatedly: re-running while pending will keep retrying;
|
|
124
|
+
* once resolved it's a no-op. */
|
|
125
|
+
private async tryResolve(tabId: string): Promise<boolean> {
|
|
126
|
+
const s = this.sessions.get(tabId)
|
|
127
|
+
if (!s) return false
|
|
128
|
+
if (s.blockId && s.rowId) return true
|
|
129
|
+
try {
|
|
130
|
+
const blockId = await this.ensureDatastore(s.cwd)
|
|
131
|
+
if (!blockId) return false
|
|
94
132
|
const row = await this.api.addRow(blockId, {
|
|
95
|
-
Started: formatStarted(startedAt),
|
|
96
|
-
'Active Time':
|
|
97
|
-
'Last Updated': new Date(
|
|
98
|
-
Agent: agentName,
|
|
133
|
+
Started: formatStarted(s.startedAt),
|
|
134
|
+
'Active Time': Math.round(s.activeMs / 60000),
|
|
135
|
+
'Last Updated': new Date().toISOString(),
|
|
136
|
+
Agent: s.agentName,
|
|
99
137
|
Worker: os.hostname(),
|
|
100
|
-
Session: sessionUuid,
|
|
138
|
+
Session: s.sessionUuid,
|
|
101
139
|
Notes: '',
|
|
102
140
|
})
|
|
103
141
|
const rowId = row?.id
|
|
104
142
|
if (!rowId) {
|
|
105
|
-
log(
|
|
106
|
-
return
|
|
143
|
+
log(`addRow returned no id for tab=${tabId}; will retry on next checkpoint`)
|
|
144
|
+
return false
|
|
107
145
|
}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
agentName,
|
|
113
|
-
idleTimeoutMin,
|
|
114
|
-
startedAt,
|
|
115
|
-
lastActivity: startedAt,
|
|
116
|
-
activeMs: 0,
|
|
117
|
-
idleTimeoutMs: Math.max(1, idleTimeoutMin) * 60 * 1000,
|
|
118
|
-
firstCheckpointTimer: null,
|
|
119
|
-
checkpointTimer: null,
|
|
120
|
-
ended: false,
|
|
121
|
-
}
|
|
122
|
-
state.firstCheckpointTimer = setTimeout(() => {
|
|
123
|
-
void this.checkpoint(tabId)
|
|
124
|
-
const live = this.sessions.get(tabId)
|
|
125
|
-
if (live && !live.ended) {
|
|
126
|
-
live.checkpointTimer = setInterval(() => {
|
|
127
|
-
void this.checkpoint(tabId)
|
|
128
|
-
}, CHECKPOINT_INTERVAL_MS)
|
|
129
|
-
}
|
|
130
|
-
}, FIRST_CHECKPOINT_DELAY_MS)
|
|
131
|
-
this.sessions.set(tabId, state)
|
|
132
|
-
log(`Started tracking tab=${tabId} agent="${agentName}" cwd=${cwd}`)
|
|
146
|
+
s.blockId = blockId
|
|
147
|
+
s.rowId = rowId
|
|
148
|
+
log(`Resolved datastore for tab=${tabId} (cwd=${s.cwd})`)
|
|
149
|
+
return true
|
|
133
150
|
} catch (err: any) {
|
|
134
|
-
log(`
|
|
151
|
+
log(`tryResolve failed for tab=${tabId}: ${err?.message || err}`)
|
|
152
|
+
return false
|
|
135
153
|
}
|
|
136
154
|
}
|
|
137
155
|
|
|
@@ -175,14 +193,21 @@ export class TimeTracker {
|
|
|
175
193
|
async endSession(tabId: string): Promise<void> {
|
|
176
194
|
const s = this.sessions.get(tabId)
|
|
177
195
|
if (!s || s.ended) return
|
|
178
|
-
s.ended = true
|
|
179
196
|
if (s.firstCheckpointTimer) clearTimeout(s.firstCheckpointTimer)
|
|
180
197
|
if (s.checkpointTimer) clearInterval(s.checkpointTimer)
|
|
181
198
|
try {
|
|
182
|
-
|
|
199
|
+
if (!s.blockId || !s.rowId) {
|
|
200
|
+
await this.tryResolve(tabId)
|
|
201
|
+
}
|
|
202
|
+
if (s.blockId && s.rowId) {
|
|
203
|
+
await this.writeRow(s, Date.now())
|
|
204
|
+
} else {
|
|
205
|
+
log(`endSession for tab=${tabId}: never resolved datastore; ${Math.round(s.activeMs / 60000)}min not recorded`)
|
|
206
|
+
}
|
|
183
207
|
} catch (err: any) {
|
|
184
208
|
log(`endSession write failed: ${err?.message || err}`)
|
|
185
209
|
}
|
|
210
|
+
s.ended = true
|
|
186
211
|
this.sessions.delete(tabId)
|
|
187
212
|
}
|
|
188
213
|
|
|
@@ -194,13 +219,16 @@ export class TimeTracker {
|
|
|
194
219
|
private async checkpoint(tabId: string): Promise<void> {
|
|
195
220
|
const s = this.sessions.get(tabId)
|
|
196
221
|
if (!s || s.ended) return
|
|
222
|
+
if (!s.blockId || !s.rowId) {
|
|
223
|
+
if (!(await this.tryResolve(tabId))) return
|
|
224
|
+
}
|
|
197
225
|
try {
|
|
198
226
|
await this.writeRow(s, Date.now())
|
|
199
227
|
} catch (err: any) {
|
|
200
228
|
log(`checkpoint failed: ${err?.message || err}; retrying in 2s`)
|
|
201
229
|
setTimeout(() => {
|
|
202
230
|
const live = this.sessions.get(tabId)
|
|
203
|
-
if (!live || live.ended) return
|
|
231
|
+
if (!live || live.ended || !live.blockId || !live.rowId) return
|
|
204
232
|
this.writeRow(live, Date.now()).catch((err2: any) => {
|
|
205
233
|
log(`checkpoint retry failed: ${err2?.message || err2}`)
|
|
206
234
|
})
|
|
@@ -209,6 +237,7 @@ export class TimeTracker {
|
|
|
209
237
|
}
|
|
210
238
|
|
|
211
239
|
private async writeRow(s: SessionState, _endTimeMs: number): Promise<void> {
|
|
240
|
+
if (!s.blockId || !s.rowId) return
|
|
212
241
|
const activeMin = Math.round(s.activeMs / 60000)
|
|
213
242
|
await this.api.updateRow(s.blockId, s.rowId, {
|
|
214
243
|
'Active Time': activeMin,
|
package/src/main/tui.ts
CHANGED
|
@@ -134,14 +134,15 @@ export class Tui {
|
|
|
134
134
|
*/
|
|
135
135
|
showAgentPicker(
|
|
136
136
|
agents: { name: string; description: string }[],
|
|
137
|
-
options: { initialTrackTime: boolean },
|
|
138
|
-
): Promise<{ agentIdx: number; trackTime: boolean }> {
|
|
137
|
+
options: { initialTrackTime: boolean; initialLogChat: boolean },
|
|
138
|
+
): Promise<{ agentIdx: number; trackTime: boolean; logChat: boolean }> {
|
|
139
139
|
return new Promise((resolve) => {
|
|
140
140
|
let selected = 0
|
|
141
141
|
let trackTime = options.initialTrackTime
|
|
142
|
+
let logChat = options.initialLogChat
|
|
142
143
|
const modalWidth = 44
|
|
143
|
-
// +4 for borders/title/sep, +
|
|
144
|
-
const modalHeight = agents.length + 4 +
|
|
144
|
+
// +4 for borders/title/sep, +3 for separator + track-time row + log-chat row
|
|
145
|
+
const modalHeight = agents.length + 4 + 3
|
|
145
146
|
const startCol = Math.max(1, Math.floor((this.cols - modalWidth) / 2))
|
|
146
147
|
const startRow = Math.max(1, Math.floor((this.rows - modalHeight) / 2))
|
|
147
148
|
|
|
@@ -197,10 +198,18 @@ export class Tui {
|
|
|
197
198
|
const trackPad = ' '.repeat(Math.max(0, modalWidth - 2 - trackContentLen))
|
|
198
199
|
this.write(`${CSI}${trackRow};${startCol}H${BG_MODAL}${FG_DIM}│${RESET}${BG_MODAL}${trackContent}${trackPad}${FG_DIM}│${RESET}`)
|
|
199
200
|
|
|
200
|
-
const
|
|
201
|
+
const logRow = trackRow + 1
|
|
202
|
+
const logCheckbox = logChat ? `${FG_GREEN}[\u2713]${RESET}${BG_MODAL}` : `${FG_DIM}[ ]${RESET}${BG_MODAL}`
|
|
203
|
+
const logLabelFg = logChat ? FG_WHITE : FG_DIM
|
|
204
|
+
const logContent = ` ${logCheckbox} ${logLabelFg}Log chat${RESET}${BG_MODAL}`
|
|
205
|
+
const logContentLen = 2 + 3 + 1 + 'Log chat'.length
|
|
206
|
+
const logPad = ' '.repeat(Math.max(0, modalWidth - 2 - logContentLen))
|
|
207
|
+
this.write(`${CSI}${logRow};${startCol}H${BG_MODAL}${FG_DIM}\u2502${RESET}${BG_MODAL}${logContent}${logPad}${FG_DIM}\u2502${RESET}`)
|
|
208
|
+
|
|
209
|
+
const botRow = logRow + 1
|
|
201
210
|
this.write(`${CSI}${botRow};${startCol}H${BG_MODAL}${FG_DIM}${botBorder}${RESET}`)
|
|
202
211
|
|
|
203
|
-
const hint = '\u2191\u2193
|
|
212
|
+
const hint = '\u2191\u2193 nav \u00B7 Enter \u00B7 t track \u00B7 l log \u00B7 q quit'
|
|
204
213
|
const hintCol = Math.max(1, Math.floor((this.cols - hint.length) / 2))
|
|
205
214
|
this.write(`${CSI}${botRow + 2};${hintCol}H${FG_DIM}${hint}${RESET}`)
|
|
206
215
|
}
|
|
@@ -221,12 +230,15 @@ export class Tui {
|
|
|
221
230
|
} else if (key === '\x1b[B' || key === 'j') {
|
|
222
231
|
selected = (selected + 1) % agents.length
|
|
223
232
|
drawModal()
|
|
224
|
-
} else if (key === 't' || key === 'T'
|
|
233
|
+
} else if (key === 't' || key === 'T') {
|
|
225
234
|
trackTime = !trackTime
|
|
226
235
|
drawModal()
|
|
236
|
+
} else if (key === 'l' || key === 'L') {
|
|
237
|
+
logChat = !logChat
|
|
238
|
+
drawModal()
|
|
227
239
|
} else if (key === '\r' || key === '\n') {
|
|
228
240
|
cleanup()
|
|
229
|
-
resolve({ agentIdx: selected, trackTime })
|
|
241
|
+
resolve({ agentIdx: selected, trackTime, logChat })
|
|
230
242
|
} else if (key === 'q' || key === '\x1b' || key === '\x03') {
|
|
231
243
|
cleanup()
|
|
232
244
|
this.write(`${CSI}?25h`)
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import https from 'https'
|
|
2
|
+
|
|
3
|
+
export const NPM_PACKAGE = '@phenx-inc/ctlsurf'
|
|
4
|
+
|
|
5
|
+
export function compareSemver(a: string, b: string): number {
|
|
6
|
+
const pa = a.split('.').map(n => parseInt(n, 10) || 0)
|
|
7
|
+
const pb = b.split('.').map(n => parseInt(n, 10) || 0)
|
|
8
|
+
for (let i = 0; i < 3; i++) {
|
|
9
|
+
const ai = pa[i] || 0
|
|
10
|
+
const bi = pb[i] || 0
|
|
11
|
+
if (ai !== bi) return ai - bi
|
|
12
|
+
}
|
|
13
|
+
return 0
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function fetchLatestNpmVersion(timeoutMs = 8000): Promise<string | null> {
|
|
17
|
+
return new Promise((resolve) => {
|
|
18
|
+
const url = `https://registry.npmjs.org/${encodeURIComponent(NPM_PACKAGE)}/latest`
|
|
19
|
+
const req = https.get(url, { headers: { 'Accept': 'application/json' } }, (res) => {
|
|
20
|
+
if (res.statusCode !== 200) {
|
|
21
|
+
res.resume()
|
|
22
|
+
resolve(null)
|
|
23
|
+
return
|
|
24
|
+
}
|
|
25
|
+
let body = ''
|
|
26
|
+
res.setEncoding('utf8')
|
|
27
|
+
res.on('data', (chunk) => { body += chunk })
|
|
28
|
+
res.on('end', () => {
|
|
29
|
+
try {
|
|
30
|
+
const json = JSON.parse(body)
|
|
31
|
+
resolve(typeof json?.version === 'string' ? json.version : null)
|
|
32
|
+
} catch {
|
|
33
|
+
resolve(null)
|
|
34
|
+
}
|
|
35
|
+
})
|
|
36
|
+
})
|
|
37
|
+
req.on('error', () => resolve(null))
|
|
38
|
+
req.setTimeout(timeoutMs, () => { req.destroy(); resolve(null) })
|
|
39
|
+
})
|
|
40
|
+
}
|
package/src/preload/index.ts
CHANGED
|
@@ -82,6 +82,12 @@ const api = {
|
|
|
82
82
|
setTracking: (enabled: boolean): Promise<{ active: boolean }> =>
|
|
83
83
|
ipcRenderer.invoke('tracking:set', enabled),
|
|
84
84
|
|
|
85
|
+
// Chat logging (global)
|
|
86
|
+
getLogChat: (): Promise<{ enabled: boolean }> =>
|
|
87
|
+
ipcRenderer.invoke('logchat:get'),
|
|
88
|
+
setLogChat: (enabled: boolean): Promise<{ enabled: boolean }> =>
|
|
89
|
+
ipcRenderer.invoke('logchat:set', enabled),
|
|
90
|
+
|
|
85
91
|
// Filesystem
|
|
86
92
|
readDir: (dirPath: string): Promise<Array<{ name: string; path: string; isDirectory: boolean }>> =>
|
|
87
93
|
ipcRenderer.invoke('fs:readDir', dirPath),
|
package/src/renderer/App.tsx
CHANGED
|
@@ -46,6 +46,8 @@ declare global {
|
|
|
46
46
|
deleteProfile: (profileId: string) => Promise<{ ok: boolean }>
|
|
47
47
|
getTracking: () => Promise<{ active: boolean }>
|
|
48
48
|
setTracking: (enabled: boolean) => Promise<{ active: boolean }>
|
|
49
|
+
getLogChat: () => Promise<{ enabled: boolean }>
|
|
50
|
+
setLogChat: (enabled: boolean) => Promise<{ enabled: boolean }>
|
|
49
51
|
createProject: () => Promise<{ ok: boolean; folder_id?: string; error?: string }>
|
|
50
52
|
getWebviewInfo: () => Promise<{
|
|
51
53
|
frontendUrl: string; pageUrl?: string; authenticated: boolean;
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react'
|
|
2
|
+
|
|
1
3
|
interface AgentConfig {
|
|
2
4
|
id: string
|
|
3
5
|
name: string
|
|
@@ -14,9 +16,32 @@ interface AgentPickerProps {
|
|
|
14
16
|
}
|
|
15
17
|
|
|
16
18
|
export function AgentPicker({ agents, cwd, onSelect, onChangeCwd }: AgentPickerProps) {
|
|
17
|
-
const home = typeof window !== 'undefined' ? '' : ''
|
|
18
|
-
// Shorten home dir for display
|
|
19
19
|
const displayPath = cwd.replace(/^\/Users\/[^/]+/, '~')
|
|
20
|
+
const [logChat, setLogChat] = useState(false)
|
|
21
|
+
const [loaded, setLoaded] = useState(false)
|
|
22
|
+
|
|
23
|
+
useEffect(() => {
|
|
24
|
+
let cancelled = false
|
|
25
|
+
window.worker.getLogChat().then(r => {
|
|
26
|
+
if (cancelled) return
|
|
27
|
+
setLogChat(!!r?.enabled)
|
|
28
|
+
setLoaded(true)
|
|
29
|
+
}).catch(() => setLoaded(true))
|
|
30
|
+
return () => { cancelled = true }
|
|
31
|
+
}, [])
|
|
32
|
+
|
|
33
|
+
const toggleLogChat = async () => {
|
|
34
|
+
const next = !logChat
|
|
35
|
+
setLogChat(next)
|
|
36
|
+
try { await window.worker.setLogChat(next) } catch { /* ignore */ }
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const handleSelect = async (agent: AgentConfig) => {
|
|
40
|
+
if (loaded) {
|
|
41
|
+
try { await window.worker.setLogChat(logChat) } catch { /* ignore */ }
|
|
42
|
+
}
|
|
43
|
+
onSelect(agent)
|
|
44
|
+
}
|
|
20
45
|
|
|
21
46
|
return (
|
|
22
47
|
<div className="agent-picker-overlay">
|
|
@@ -35,13 +60,23 @@ export function AgentPicker({ agents, cwd, onSelect, onChangeCwd }: AgentPickerP
|
|
|
35
60
|
<button
|
|
36
61
|
key={a.id}
|
|
37
62
|
className="agent-picker-item"
|
|
38
|
-
onClick={() =>
|
|
63
|
+
onClick={() => handleSelect(a)}
|
|
39
64
|
>
|
|
40
65
|
<span className="agent-picker-name">{a.name}</span>
|
|
41
66
|
<span className="agent-picker-desc">{a.description}</span>
|
|
42
67
|
</button>
|
|
43
68
|
))}
|
|
44
69
|
</div>
|
|
70
|
+
|
|
71
|
+
<label className="agent-picker-option" onClick={(e) => e.stopPropagation()}>
|
|
72
|
+
<input
|
|
73
|
+
type="checkbox"
|
|
74
|
+
checked={logChat}
|
|
75
|
+
onChange={toggleLogChat}
|
|
76
|
+
/>
|
|
77
|
+
<span>Log chat to ctlsurf</span>
|
|
78
|
+
</label>
|
|
79
|
+
|
|
45
80
|
<div className="agent-picker-hint">Select an agent to start</div>
|
|
46
81
|
</div>
|
|
47
82
|
</div>
|
package/src/renderer/styles.css
CHANGED
|
@@ -821,6 +821,22 @@ html, body, #root {
|
|
|
821
821
|
color: #414868;
|
|
822
822
|
}
|
|
823
823
|
|
|
824
|
+
.agent-picker-option {
|
|
825
|
+
margin-top: 16px;
|
|
826
|
+
display: flex;
|
|
827
|
+
align-items: center;
|
|
828
|
+
gap: 8px;
|
|
829
|
+
font-size: 12px;
|
|
830
|
+
color: #a9b1d6;
|
|
831
|
+
cursor: pointer;
|
|
832
|
+
user-select: none;
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
.agent-picker-option input[type="checkbox"] {
|
|
836
|
+
cursor: pointer;
|
|
837
|
+
accent-color: #7aa2f7;
|
|
838
|
+
}
|
|
839
|
+
|
|
824
840
|
/* Settings dialog */
|
|
825
841
|
.settings-overlay {
|
|
826
842
|
position: fixed;
|