@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
|
@@ -46,6 +46,44 @@ function TagIcon({ size = 16 }: { size?: number }) {
|
|
|
46
46
|
)
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
+
// Paper-plane "send" glyph for the per-ticket dispatch button.
|
|
50
|
+
function SendIcon({ size = 15 }: { size?: number }) {
|
|
51
|
+
return (
|
|
52
|
+
<svg viewBox="0 0 24 24" width={size} height={size} fill="none"
|
|
53
|
+
stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
|
|
54
|
+
<line x1="22" y1="2" x2="11" y2="13" />
|
|
55
|
+
<polygon points="22 2 15 22 11 13 2 9 22 2" />
|
|
56
|
+
</svg>
|
|
57
|
+
)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
interface TicketRefs { blockId: string; pageId: string }
|
|
61
|
+
|
|
62
|
+
// Formats a ticket into a prompt the coding agent can act on directly. Includes
|
|
63
|
+
// the ctlsurf identifiers (row / datastore block / page) so the agent can read
|
|
64
|
+
// the full ticket or flip its status via the ctlsurf MCP tools.
|
|
65
|
+
function ticketPrompt(t: Ticket, refs: TicketRefs | null): string {
|
|
66
|
+
const lines = [
|
|
67
|
+
'Please work on this ticket:',
|
|
68
|
+
`Title: ${t.title || 'Untitled'}`,
|
|
69
|
+
`Priority: ${t.priority}`,
|
|
70
|
+
]
|
|
71
|
+
const desc = t.description?.trim()
|
|
72
|
+
if (desc) lines.push('', desc)
|
|
73
|
+
lines.push(
|
|
74
|
+
'',
|
|
75
|
+
'ctlsurf references (use the ctlsurf MCP tools to read details or update status):',
|
|
76
|
+
`- Ticket row ID: ${t.id}`,
|
|
77
|
+
)
|
|
78
|
+
if (refs) {
|
|
79
|
+
lines.push(
|
|
80
|
+
`- Tickets datastore block ID: ${refs.blockId}`,
|
|
81
|
+
`- Agent Datastore page ID: ${refs.pageId}`,
|
|
82
|
+
)
|
|
83
|
+
}
|
|
84
|
+
return lines.join('\n')
|
|
85
|
+
}
|
|
86
|
+
|
|
49
87
|
interface SegmentedProps {
|
|
50
88
|
options: Option[]
|
|
51
89
|
value: string
|
|
@@ -75,40 +113,55 @@ function Segmented({ options, value, onChange }: SegmentedProps) {
|
|
|
75
113
|
}
|
|
76
114
|
|
|
77
115
|
// A single ticket row. Shared between the open list and the (collapsible)
|
|
78
|
-
// Done section so both render identically.
|
|
79
|
-
|
|
116
|
+
// Done section so both render identically. The card body opens the editor; the
|
|
117
|
+
// trailing send button dispatches the ticket straight to the coding terminal.
|
|
118
|
+
function TicketCard({ ticket, onClick, onSend }: { ticket: Ticket; onClick: () => void; onSend: () => void }) {
|
|
80
119
|
return (
|
|
81
|
-
<
|
|
82
|
-
<
|
|
83
|
-
|
|
84
|
-
<
|
|
85
|
-
|
|
86
|
-
<
|
|
87
|
-
<span className="ticket-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
120
|
+
<div className="ticket-card">
|
|
121
|
+
<button className="ticket-card-open" onClick={onClick} title="Edit ticket">
|
|
122
|
+
<span className="ticket-card-bar" style={{ background: colorOf(STATUS_OPTIONS, ticket.status) }} />
|
|
123
|
+
<div className="ticket-card-main">
|
|
124
|
+
<span className="ticket-card-title">{ticket.title || 'Untitled'}</span>
|
|
125
|
+
<div className="ticket-card-meta">
|
|
126
|
+
<span className="ticket-card-status" style={{ color: colorOf(STATUS_OPTIONS, ticket.status) }}>
|
|
127
|
+
<span className="ticket-seg-dot" style={{ background: colorOf(STATUS_OPTIONS, ticket.status) }} />
|
|
128
|
+
{ticket.status}
|
|
129
|
+
</span>
|
|
130
|
+
{ticket.created && <span className="ticket-card-date">{formatDate(ticket.created)}</span>}
|
|
131
|
+
</div>
|
|
91
132
|
</div>
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
133
|
+
<span
|
|
134
|
+
className="ticket-card-pri"
|
|
135
|
+
style={{ color: colorOf(PRIORITY_OPTIONS, ticket.priority), borderColor: `${colorOf(PRIORITY_OPTIONS, ticket.priority)}66` }}
|
|
136
|
+
>
|
|
137
|
+
{ticket.priority}
|
|
138
|
+
</span>
|
|
139
|
+
</button>
|
|
140
|
+
<button
|
|
141
|
+
type="button"
|
|
142
|
+
className="ticket-card-send"
|
|
143
|
+
onClick={onSend}
|
|
144
|
+
title="Send to coding terminal"
|
|
145
|
+
aria-label="Send ticket to coding terminal"
|
|
96
146
|
>
|
|
97
|
-
|
|
98
|
-
</
|
|
99
|
-
</
|
|
147
|
+
<SendIcon />
|
|
148
|
+
</button>
|
|
149
|
+
</div>
|
|
100
150
|
)
|
|
101
151
|
}
|
|
102
152
|
|
|
103
153
|
// Docked side-pane variant of the ticket UI: header + list/form body + footer.
|
|
104
154
|
// It only mounts when its pane is visible, so it loads on mount and refreshes
|
|
105
155
|
// when the connected project (cwd) changes. Closed via the pane's own header.
|
|
106
|
-
export function TicketPane() {
|
|
156
|
+
export function TicketPane({ onSendToTerminal }: { onSendToTerminal: (text: string) => void }) {
|
|
107
157
|
const [project, setProject] = useState<string | null>(null)
|
|
108
158
|
const [view, setView] = useState<View>('list')
|
|
109
159
|
|
|
110
160
|
// list state
|
|
111
161
|
const [tickets, setTickets] = useState<Ticket[]>([])
|
|
162
|
+
// ctlsurf identifiers for this project's Tickets datastore, sent along when
|
|
163
|
+
// dispatching a ticket so the agent can locate it via the MCP tools.
|
|
164
|
+
const [refs, setRefs] = useState<TicketRefs | null>(null)
|
|
112
165
|
const [listLoading, setListLoading] = useState(false)
|
|
113
166
|
const [listError, setListError] = useState<string | null>(null)
|
|
114
167
|
// Done tickets are tucked into a collapsed section so the list stays focused
|
|
@@ -128,10 +181,13 @@ export function TicketPane() {
|
|
|
128
181
|
setListError(null)
|
|
129
182
|
try {
|
|
130
183
|
const r = await window.worker.listTickets()
|
|
131
|
-
if (r?.ok)
|
|
132
|
-
|
|
184
|
+
if (r?.ok) {
|
|
185
|
+
setTickets(r.tickets || [])
|
|
186
|
+
setRefs(r.blockId && r.pageId ? { blockId: r.blockId, pageId: r.pageId } : null)
|
|
187
|
+
} else { setTickets([]); setRefs(null); setListError(r?.error || 'Failed to load tickets') }
|
|
133
188
|
} catch (err: any) {
|
|
134
189
|
setTickets([])
|
|
190
|
+
setRefs(null)
|
|
135
191
|
setListError(err?.message || 'Failed to load tickets')
|
|
136
192
|
} finally {
|
|
137
193
|
setListLoading(false)
|
|
@@ -251,7 +307,7 @@ export function TicketPane() {
|
|
|
251
307
|
</div>
|
|
252
308
|
)}
|
|
253
309
|
{!listLoading && activeTickets.map(t => (
|
|
254
|
-
<TicketCard key={t.id} ticket={t} onClick={() => openEditForm(t)} />
|
|
310
|
+
<TicketCard key={t.id} ticket={t} onClick={() => openEditForm(t)} onSend={() => onSendToTerminal(ticketPrompt(t, refs))} />
|
|
255
311
|
))}
|
|
256
312
|
{!listLoading && activeTickets.length === 0 && doneTickets.length > 0 && (
|
|
257
313
|
<div className="ticket-list-msg">Nothing open — all caught up.</div>
|
|
@@ -269,7 +325,7 @@ export function TicketPane() {
|
|
|
269
325
|
<span className="ticket-done-count">{doneTickets.length}</span>
|
|
270
326
|
</button>
|
|
271
327
|
{showDone && doneTickets.map(t => (
|
|
272
|
-
<TicketCard key={t.id} ticket={t} onClick={() => openEditForm(t)} />
|
|
328
|
+
<TicketCard key={t.id} ticket={t} onClick={() => openEditForm(t)} onSend={() => onSendToTerminal(ticketPrompt(t, refs))} />
|
|
273
329
|
))}
|
|
274
330
|
</>
|
|
275
331
|
)}
|
|
@@ -78,7 +78,8 @@ function splitIntoChunks(text: string): string[] {
|
|
|
78
78
|
// ─── Preferences ─────────────────────────────────────
|
|
79
79
|
|
|
80
80
|
export function getEngine(): TtsEngineId {
|
|
81
|
-
|
|
81
|
+
// Default to the neural voice; only an explicit 'web' choice opts back out.
|
|
82
|
+
try { return localStorage.getItem(ENGINE_KEY) === 'web' ? 'web' : 'neural' } catch { return 'neural' }
|
|
82
83
|
}
|
|
83
84
|
export function setEngine(id: TtsEngineId): void {
|
|
84
85
|
try { localStorage.setItem(ENGINE_KEY, id) } catch { /* ignore */ }
|
package/src/renderer/styles.css
CHANGED
|
@@ -529,13 +529,88 @@ html, body, #root {
|
|
|
529
529
|
width: 100%;
|
|
530
530
|
height: 100%;
|
|
531
531
|
overflow: hidden;
|
|
532
|
+
display: flex;
|
|
533
|
+
flex-direction: column;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
.ctlsurf-addressbar {
|
|
537
|
+
flex: 0 0 auto;
|
|
538
|
+
display: flex;
|
|
539
|
+
align-items: center;
|
|
540
|
+
gap: 4px;
|
|
541
|
+
padding: 5px 6px;
|
|
542
|
+
background: #1a1b26;
|
|
543
|
+
border-bottom: 1px solid #2a2b3d;
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
.ctlsurf-nav-btn {
|
|
547
|
+
flex: 0 0 auto;
|
|
548
|
+
width: 24px;
|
|
549
|
+
height: 24px;
|
|
550
|
+
display: flex;
|
|
551
|
+
align-items: center;
|
|
552
|
+
justify-content: center;
|
|
553
|
+
padding: 0;
|
|
554
|
+
background: transparent;
|
|
555
|
+
border: none;
|
|
556
|
+
border-radius: 4px;
|
|
557
|
+
color: #a9b1d6;
|
|
558
|
+
font-size: 16px;
|
|
559
|
+
line-height: 1;
|
|
560
|
+
cursor: pointer;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
.ctlsurf-nav-btn:hover:not(:disabled) {
|
|
564
|
+
background: #2a2b3d;
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
.ctlsurf-nav-btn:disabled {
|
|
568
|
+
color: #565f89;
|
|
569
|
+
opacity: 0.5;
|
|
570
|
+
cursor: default;
|
|
532
571
|
}
|
|
533
572
|
|
|
534
|
-
.ctlsurf-
|
|
573
|
+
.ctlsurf-address-form {
|
|
574
|
+
flex: 1 1 auto;
|
|
575
|
+
display: flex;
|
|
576
|
+
min-width: 0;
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
.ctlsurf-address-input {
|
|
580
|
+
width: 100%;
|
|
581
|
+
height: 26px;
|
|
582
|
+
padding: 0 11px;
|
|
583
|
+
background: #16161e;
|
|
584
|
+
border: 1px solid #2a2b3d;
|
|
585
|
+
border-radius: 13px;
|
|
586
|
+
color: #c0caf5;
|
|
587
|
+
font-size: 12px;
|
|
588
|
+
outline: none;
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
.ctlsurf-address-input:focus {
|
|
592
|
+
border-color: #7aa2f7;
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
.ctlsurf-webview-host {
|
|
596
|
+
flex: 1 1 auto;
|
|
597
|
+
position: relative;
|
|
598
|
+
min-height: 0;
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
.ctlsurf-webview-host webview {
|
|
535
602
|
width: 100%;
|
|
536
603
|
height: 100%;
|
|
537
604
|
}
|
|
538
605
|
|
|
606
|
+
.ctlsurf-webview-loading {
|
|
607
|
+
width: 100%;
|
|
608
|
+
height: 100%;
|
|
609
|
+
display: flex;
|
|
610
|
+
align-items: center;
|
|
611
|
+
justify-content: center;
|
|
612
|
+
}
|
|
613
|
+
|
|
539
614
|
/* Status bar */
|
|
540
615
|
.statusbar {
|
|
541
616
|
height: 24px;
|
|
@@ -1541,6 +1616,22 @@ html, body, #root {
|
|
|
1541
1616
|
font-size: 16px;
|
|
1542
1617
|
}
|
|
1543
1618
|
|
|
1619
|
+
/* About section inside the Settings dialog. */
|
|
1620
|
+
.settings-about {
|
|
1621
|
+
border-top: 1px solid #2a2b3d;
|
|
1622
|
+
padding-top: 16px;
|
|
1623
|
+
}
|
|
1624
|
+
.settings-about .about-name {
|
|
1625
|
+
color: #c0caf5;
|
|
1626
|
+
font-size: 14px;
|
|
1627
|
+
font-weight: 600;
|
|
1628
|
+
}
|
|
1629
|
+
.settings-about .about-copyright {
|
|
1630
|
+
color: #9aa5ce;
|
|
1631
|
+
font-size: 12px;
|
|
1632
|
+
margin: 6px 0 8px;
|
|
1633
|
+
}
|
|
1634
|
+
|
|
1544
1635
|
.settings-section {
|
|
1545
1636
|
margin-bottom: 20px;
|
|
1546
1637
|
}
|
|
@@ -2052,24 +2143,57 @@ html, body, #root {
|
|
|
2052
2143
|
.ticket-card {
|
|
2053
2144
|
display: flex;
|
|
2054
2145
|
align-items: stretch;
|
|
2055
|
-
gap: 10px;
|
|
2056
2146
|
background: #15161f;
|
|
2057
2147
|
border: 1px solid #262a3f;
|
|
2058
2148
|
border-radius: 10px;
|
|
2059
|
-
padding: 10px 11px 10px 0;
|
|
2060
|
-
cursor: pointer;
|
|
2061
|
-
text-align: left;
|
|
2062
|
-
font-family: inherit;
|
|
2063
2149
|
overflow: hidden;
|
|
2064
|
-
transition: border-color 0.13s ease, background 0.13s ease
|
|
2150
|
+
transition: border-color 0.13s ease, background 0.13s ease;
|
|
2065
2151
|
}
|
|
2066
2152
|
.ticket-card:hover {
|
|
2067
2153
|
border-color: #3d425f;
|
|
2068
2154
|
background: #181a26;
|
|
2069
2155
|
}
|
|
2070
|
-
|
|
2156
|
+
/* Clickable body of the card — opens the editor. */
|
|
2157
|
+
.ticket-card-open {
|
|
2158
|
+
flex: 1;
|
|
2159
|
+
min-width: 0;
|
|
2160
|
+
display: flex;
|
|
2161
|
+
align-items: stretch;
|
|
2162
|
+
gap: 10px;
|
|
2163
|
+
padding: 10px 11px 10px 0;
|
|
2164
|
+
background: transparent;
|
|
2165
|
+
border: none;
|
|
2166
|
+
color: inherit;
|
|
2167
|
+
cursor: pointer;
|
|
2168
|
+
text-align: left;
|
|
2169
|
+
font-family: inherit;
|
|
2170
|
+
transition: transform 0.06s ease;
|
|
2171
|
+
}
|
|
2172
|
+
.ticket-card-open:active {
|
|
2071
2173
|
transform: scale(0.99);
|
|
2072
2174
|
}
|
|
2175
|
+
/* Trailing button that dispatches the ticket to the coding terminal. */
|
|
2176
|
+
.ticket-card-send {
|
|
2177
|
+
flex-shrink: 0;
|
|
2178
|
+
width: 36px;
|
|
2179
|
+
align-self: stretch;
|
|
2180
|
+
display: flex;
|
|
2181
|
+
align-items: center;
|
|
2182
|
+
justify-content: center;
|
|
2183
|
+
background: transparent;
|
|
2184
|
+
border: none;
|
|
2185
|
+
border-left: 1px solid #262a3f;
|
|
2186
|
+
color: #565f89;
|
|
2187
|
+
cursor: pointer;
|
|
2188
|
+
transition: background 0.13s ease, color 0.13s ease, transform 0.06s ease;
|
|
2189
|
+
}
|
|
2190
|
+
.ticket-card-send:hover {
|
|
2191
|
+
background: #1b2030;
|
|
2192
|
+
color: #7aa2f7;
|
|
2193
|
+
}
|
|
2194
|
+
.ticket-card-send:active {
|
|
2195
|
+
transform: scale(0.9);
|
|
2196
|
+
}
|
|
2073
2197
|
.ticket-card-bar {
|
|
2074
2198
|
width: 3px;
|
|
2075
2199
|
border-radius: 3px;
|
|
@@ -2144,6 +2268,106 @@ html, body, #root {
|
|
|
2144
2268
|
.term-context-menu button:hover {
|
|
2145
2269
|
background: #2a2b3d;
|
|
2146
2270
|
}
|
|
2271
|
+
/* Always-visible utility button (top-right) — opens the snapshot panel. */
|
|
2272
|
+
.term-util-btn {
|
|
2273
|
+
position: absolute;
|
|
2274
|
+
top: 6px;
|
|
2275
|
+
right: 14px;
|
|
2276
|
+
z-index: 6;
|
|
2277
|
+
width: 26px;
|
|
2278
|
+
height: 26px;
|
|
2279
|
+
display: inline-flex;
|
|
2280
|
+
align-items: center;
|
|
2281
|
+
justify-content: center;
|
|
2282
|
+
border-radius: 6px;
|
|
2283
|
+
background: #2a2b3d;
|
|
2284
|
+
border: 1px solid #414868;
|
|
2285
|
+
color: #c0caf5;
|
|
2286
|
+
font-size: 16px;
|
|
2287
|
+
line-height: 1;
|
|
2288
|
+
cursor: pointer;
|
|
2289
|
+
opacity: 0.55;
|
|
2290
|
+
transition: opacity 0.13s ease, background 0.13s ease;
|
|
2291
|
+
}
|
|
2292
|
+
.term-util-btn:hover {
|
|
2293
|
+
opacity: 1;
|
|
2294
|
+
background: #343650;
|
|
2295
|
+
}
|
|
2296
|
+
|
|
2297
|
+
/* Snapshot panel: a frozen, selectable copy of the screen text. */
|
|
2298
|
+
.term-snapshot {
|
|
2299
|
+
position: absolute;
|
|
2300
|
+
inset: 0;
|
|
2301
|
+
z-index: 20;
|
|
2302
|
+
display: flex;
|
|
2303
|
+
flex-direction: column;
|
|
2304
|
+
background: #16161e;
|
|
2305
|
+
border: 1px solid #414868;
|
|
2306
|
+
}
|
|
2307
|
+
.term-snapshot-head {
|
|
2308
|
+
display: flex;
|
|
2309
|
+
align-items: center;
|
|
2310
|
+
justify-content: space-between;
|
|
2311
|
+
gap: 12px;
|
|
2312
|
+
padding: 8px 12px;
|
|
2313
|
+
background: #1f2335;
|
|
2314
|
+
border-bottom: 1px solid #414868;
|
|
2315
|
+
color: #9aa5ce;
|
|
2316
|
+
font-size: 12px;
|
|
2317
|
+
}
|
|
2318
|
+
.term-snapshot-x {
|
|
2319
|
+
background: transparent;
|
|
2320
|
+
border: none;
|
|
2321
|
+
color: #9aa5ce;
|
|
2322
|
+
font-size: 14px;
|
|
2323
|
+
cursor: pointer;
|
|
2324
|
+
padding: 2px 6px;
|
|
2325
|
+
border-radius: 4px;
|
|
2326
|
+
}
|
|
2327
|
+
.term-snapshot-x:hover {
|
|
2328
|
+
background: #2a2b3d;
|
|
2329
|
+
color: #c0caf5;
|
|
2330
|
+
}
|
|
2331
|
+
.term-snapshot-text {
|
|
2332
|
+
flex: 1;
|
|
2333
|
+
min-height: 0;
|
|
2334
|
+
overflow: auto;
|
|
2335
|
+
margin: 0;
|
|
2336
|
+
padding: 10px 12px;
|
|
2337
|
+
white-space: pre;
|
|
2338
|
+
user-select: text;
|
|
2339
|
+
cursor: text;
|
|
2340
|
+
color: #c0caf5;
|
|
2341
|
+
background: #16161e;
|
|
2342
|
+
font-family: 'JetBrains Mono', 'Fira Code', 'SF Mono', monospace;
|
|
2343
|
+
font-size: 13px;
|
|
2344
|
+
line-height: 1.4;
|
|
2345
|
+
}
|
|
2346
|
+
.term-snapshot-text::selection {
|
|
2347
|
+
background: #33467c;
|
|
2348
|
+
}
|
|
2349
|
+
.term-snapshot-actions {
|
|
2350
|
+
display: flex;
|
|
2351
|
+
gap: 6px;
|
|
2352
|
+
padding: 8px 12px;
|
|
2353
|
+
background: #1f2335;
|
|
2354
|
+
border-top: 1px solid #414868;
|
|
2355
|
+
}
|
|
2356
|
+
.term-snapshot-actions button {
|
|
2357
|
+
background: #2a2b3d;
|
|
2358
|
+
border: 1px solid #414868;
|
|
2359
|
+
border-radius: 5px;
|
|
2360
|
+
color: #c0caf5;
|
|
2361
|
+
font-size: 12.5px;
|
|
2362
|
+
padding: 6px 12px;
|
|
2363
|
+
cursor: pointer;
|
|
2364
|
+
}
|
|
2365
|
+
.term-snapshot-actions button:hover {
|
|
2366
|
+
background: #343650;
|
|
2367
|
+
}
|
|
2368
|
+
.term-snapshot-done {
|
|
2369
|
+
margin-left: auto;
|
|
2370
|
+
}
|
|
2147
2371
|
.term-toast {
|
|
2148
2372
|
position: fixed;
|
|
2149
2373
|
bottom: 24px;
|