@jkwd/inbase 0.1.10 → 0.1.12
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 +21 -0
- package/README.md +22 -9
- package/apps/explorer/package.json +1 -0
- package/apps/explorer/scripts/branch-changes.d.ts +24 -0
- package/apps/explorer/scripts/branch-changes.mjs +237 -0
- package/apps/explorer/scripts/js-source.mjs +12 -15
- package/apps/explorer/scripts/patch-lib.d.ts +5 -0
- package/apps/explorer/scripts/patch-lib.mjs +5 -0
- package/apps/explorer/scripts/scan-target.mjs +46 -13
- package/apps/explorer/scripts/session-store.d.ts +58 -1
- package/apps/explorer/scripts/session-store.mjs +276 -52
- package/apps/explorer/scripts/tree-diff.mjs +121 -0
- package/apps/explorer/src/App.tsx +170 -32
- package/apps/explorer/src/agentIntent.ts +63 -0
- package/apps/explorer/src/branchChanges.ts +54 -0
- package/apps/explorer/src/index.css +153 -0
- package/apps/explorer/src/scene/FileBlock.tsx +55 -5
- package/apps/explorer/src/scene/MapView.tsx +32 -1
- package/apps/explorer/src/scene/World.tsx +6 -1
- package/apps/explorer/src/types.ts +44 -0
- package/apps/explorer/src/ui/HUD.tsx +583 -96
- package/apps/explorer/src/ui/MapContextMenu.tsx +89 -0
- package/apps/explorer/src/userContext.ts +11 -0
- package/apps/explorer/src/userCreated.ts +50 -0
- package/apps/explorer/vite.config.ts +61 -7
- package/bin/inbase.mjs +23 -4
- package/bin/project.mjs +62 -4
- package/bin/session.mjs +231 -125
- package/package.json +20 -2
- package/skill/commands/inbase.md +17 -0
- package/skill/commands/skipinbase.md +9 -0
- package/skill/inbase/SKILL.md +143 -112
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { useEffect, useRef, useState, type ReactNode } from 'react'
|
|
2
|
+
import { persistInitialInstruction } from '../agentIntent'
|
|
2
3
|
import { NameInput } from './NameInput'
|
|
3
4
|
import { SelectionThumbnail } from '../scene/SelectionThumbnail'
|
|
4
5
|
import {
|
|
@@ -7,6 +8,7 @@ import {
|
|
|
7
8
|
type AgentIntent,
|
|
8
9
|
type AgentIntentStatus,
|
|
9
10
|
type AimedRelation,
|
|
11
|
+
type BranchChanges,
|
|
10
12
|
type CodebaseGraph,
|
|
11
13
|
type PatchImportAddition,
|
|
12
14
|
type PatchSymbolAddition,
|
|
@@ -29,10 +31,14 @@ function reviewTitle(status: AgentIntentStatus) {
|
|
|
29
31
|
return 'Visual workflow'
|
|
30
32
|
}
|
|
31
33
|
|
|
34
|
+
function sessionLabel(intent: AgentIntent) {
|
|
35
|
+
return intent.name?.trim() || intent.feature?.trim() || ''
|
|
36
|
+
}
|
|
37
|
+
|
|
32
38
|
function sessionTabLabel(intent: AgentIntent, sessions: AgentIntent[]) {
|
|
33
|
-
const title = intent
|
|
39
|
+
const title = sessionLabel(intent) || reviewTitle(intent.status)
|
|
34
40
|
const same = sessions.filter(
|
|
35
|
-
(item) => (item
|
|
41
|
+
(item) => (sessionLabel(item) || reviewTitle(item.status)) === title,
|
|
36
42
|
)
|
|
37
43
|
if (same.length < 2) return title
|
|
38
44
|
const id = intent.sessionId ?? ''
|
|
@@ -210,18 +216,29 @@ function AddIntentRow({
|
|
|
210
216
|
|
|
211
217
|
function PanelChrome({
|
|
212
218
|
title,
|
|
219
|
+
subtitle,
|
|
213
220
|
minimized = false,
|
|
214
221
|
onMinimize,
|
|
215
222
|
onClose,
|
|
223
|
+
closeLabel = 'Close',
|
|
224
|
+
closeReject = false,
|
|
216
225
|
}: {
|
|
217
226
|
title: ReactNode
|
|
227
|
+
subtitle?: ReactNode
|
|
218
228
|
minimized?: boolean
|
|
219
229
|
onMinimize?: () => void
|
|
220
230
|
onClose?: () => void
|
|
231
|
+
closeLabel?: string
|
|
232
|
+
closeReject?: boolean
|
|
221
233
|
}) {
|
|
222
234
|
return (
|
|
223
235
|
<div className="hud-panel-chrome">
|
|
224
|
-
<div className="hud-panel-chrome-
|
|
236
|
+
<div className="hud-panel-chrome-heading">
|
|
237
|
+
<div className="hud-panel-chrome-title">{title}</div>
|
|
238
|
+
{subtitle ? (
|
|
239
|
+
<div className="hud-panel-chrome-subtitle">{subtitle}</div>
|
|
240
|
+
) : null}
|
|
241
|
+
</div>
|
|
225
242
|
<div className="hud-panel-controls">
|
|
226
243
|
{onMinimize && (
|
|
227
244
|
<button
|
|
@@ -235,9 +252,13 @@ function PanelChrome({
|
|
|
235
252
|
)}
|
|
236
253
|
{onClose && (
|
|
237
254
|
<button
|
|
238
|
-
className=
|
|
255
|
+
className={
|
|
256
|
+
closeReject
|
|
257
|
+
? 'hud-button hud-icon-button hud-panel-control hud-button-reject'
|
|
258
|
+
: 'hud-button hud-icon-button hud-panel-control'
|
|
259
|
+
}
|
|
239
260
|
type="button"
|
|
240
|
-
aria-label=
|
|
261
|
+
aria-label={closeLabel}
|
|
241
262
|
onClick={onClose}
|
|
242
263
|
>
|
|
243
264
|
×
|
|
@@ -248,10 +269,200 @@ function PanelChrome({
|
|
|
248
269
|
)
|
|
249
270
|
}
|
|
250
271
|
|
|
272
|
+
function InitialInstructionField({
|
|
273
|
+
value,
|
|
274
|
+
onChange,
|
|
275
|
+
}: {
|
|
276
|
+
value: string
|
|
277
|
+
onChange: (value: string) => void
|
|
278
|
+
}) {
|
|
279
|
+
return (
|
|
280
|
+
<label className="hud-instruction">
|
|
281
|
+
<textarea
|
|
282
|
+
value={value}
|
|
283
|
+
maxLength={4000}
|
|
284
|
+
rows={4}
|
|
285
|
+
placeholder="What should the LLM build?"
|
|
286
|
+
onChange={(event) => onChange(event.target.value)}
|
|
287
|
+
onKeyDown={(event) => event.stopPropagation()}
|
|
288
|
+
/>
|
|
289
|
+
</label>
|
|
290
|
+
)
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
function blueprintIsDefined(intent: AgentIntent) {
|
|
294
|
+
return (
|
|
295
|
+
(intent.userCreatedBlocks?.length ?? 0) > 0 ||
|
|
296
|
+
(intent.userCreatedIslands?.length ?? 0) > 0 ||
|
|
297
|
+
(intent.blueprintFunctions?.length ?? 0) > 0 ||
|
|
298
|
+
(intent.blueprintVariables?.length ?? 0) > 0 ||
|
|
299
|
+
(intent.blueprintImports?.length ?? 0) > 0
|
|
300
|
+
)
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
function HandshakeSetup({
|
|
304
|
+
instruction,
|
|
305
|
+
onInstructionChange,
|
|
306
|
+
blueprintDefined,
|
|
307
|
+
awaitingAttach,
|
|
308
|
+
attachBlockedBy,
|
|
309
|
+
}: {
|
|
310
|
+
instruction: string
|
|
311
|
+
onInstructionChange: (value: string) => void
|
|
312
|
+
blueprintDefined: boolean
|
|
313
|
+
awaitingAttach: boolean
|
|
314
|
+
attachBlockedBy: string | null
|
|
315
|
+
}) {
|
|
316
|
+
return (
|
|
317
|
+
<div className="hud-setup">
|
|
318
|
+
<section className="hud-setup-section">
|
|
319
|
+
<h2 className="hud-setup-heading">Instructions</h2>
|
|
320
|
+
<InitialInstructionField
|
|
321
|
+
value={instruction}
|
|
322
|
+
onChange={onInstructionChange}
|
|
323
|
+
/>
|
|
324
|
+
</section>
|
|
325
|
+
<section className="hud-setup-section">
|
|
326
|
+
<h2 className="hud-setup-heading">
|
|
327
|
+
Blueprint
|
|
328
|
+
<span
|
|
329
|
+
className="hud-setup-tag"
|
|
330
|
+
data-ready={blueprintDefined ? 'true' : 'false'}
|
|
331
|
+
>
|
|
332
|
+
{blueprintDefined ? 'blueprint defined' : 'no blueprint'}
|
|
333
|
+
</span>
|
|
334
|
+
</h2>
|
|
335
|
+
<p>
|
|
336
|
+
Press <kbd>Space</kbd> for a file and <kbd>B</kbd> for an island.
|
|
337
|
+
On the map, right-click to add a file or folder. Optional.
|
|
338
|
+
</p>
|
|
339
|
+
</section>
|
|
340
|
+
<section className="hud-setup-section">
|
|
341
|
+
<h2 className="hud-setup-heading">Start</h2>
|
|
342
|
+
{attachBlockedBy ? (
|
|
343
|
+
<p>
|
|
344
|
+
An LLM is already attached to {attachBlockedBy}. Stop that session
|
|
345
|
+
before running <kbd>/inbase</kbd>.
|
|
346
|
+
</p>
|
|
347
|
+
) : awaitingAttach ? (
|
|
348
|
+
<p>
|
|
349
|
+
Run <kbd>/inbase</kbd> in a Cursor chat to connect and start.
|
|
350
|
+
</p>
|
|
351
|
+
) : (
|
|
352
|
+
<p>
|
|
353
|
+
Starting from <kbd>/inbase</kbd>…
|
|
354
|
+
</p>
|
|
355
|
+
)}
|
|
356
|
+
</section>
|
|
357
|
+
</div>
|
|
358
|
+
)
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
function sessionLiveStatus(intent: AgentIntent) {
|
|
362
|
+
const ack = intent.lastAck
|
|
363
|
+
const kind = ack?.kind
|
|
364
|
+
const detail = ack?.detail?.trim() || ''
|
|
365
|
+
|
|
366
|
+
if (intent.status === 'finished' || kind === 'finished') {
|
|
367
|
+
return { text: 'Finished', busy: false }
|
|
368
|
+
}
|
|
369
|
+
if (kind === 'stopped' || intent.status === 'rejected') {
|
|
370
|
+
return { text: 'Stopped', busy: false }
|
|
371
|
+
}
|
|
372
|
+
if (kind === 'timeout') {
|
|
373
|
+
return { text: 'LLM wait timed out', busy: false }
|
|
374
|
+
}
|
|
375
|
+
if (intent.awaitingAttach) {
|
|
376
|
+
return { text: 'Waiting for /inbase in Cursor', busy: false }
|
|
377
|
+
}
|
|
378
|
+
if (kind === 'execute') {
|
|
379
|
+
return { text: `LLM received ${detail}`, busy: true }
|
|
380
|
+
}
|
|
381
|
+
if (kind === 'invoke') {
|
|
382
|
+
return { text: `Sent ${detail} — waiting for LLM`, busy: true }
|
|
383
|
+
}
|
|
384
|
+
if (kind === 'replan') {
|
|
385
|
+
return { text: 'LLM received a new instruction', busy: true }
|
|
386
|
+
}
|
|
387
|
+
if (intent.status === 'working') {
|
|
388
|
+
return {
|
|
389
|
+
text: intent.reason
|
|
390
|
+
? `LLM is working on ${intent.reason}`
|
|
391
|
+
: 'LLM is working',
|
|
392
|
+
busy: true,
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
if (intent.status === 'replanning') {
|
|
396
|
+
return { text: 'LLM is revising the plan', busy: true }
|
|
397
|
+
}
|
|
398
|
+
if (intent.status === 'preparing' || kind === 'blueprint') {
|
|
399
|
+
return { text: 'LLM is drafting the plan', busy: true }
|
|
400
|
+
}
|
|
401
|
+
if (kind === 'plan' || intent.status === 'planned') {
|
|
402
|
+
return intent.listening
|
|
403
|
+
? { text: 'LLM is listening — run the next step', busy: false }
|
|
404
|
+
: { text: 'Plan ready', busy: false }
|
|
405
|
+
}
|
|
406
|
+
if (intent.status === 'pending') {
|
|
407
|
+
return intent.listening
|
|
408
|
+
? { text: 'LLM is listening — accept or send an instruction', busy: false }
|
|
409
|
+
: { text: 'Review this step', busy: false }
|
|
410
|
+
}
|
|
411
|
+
if (
|
|
412
|
+
kind === 'attached' ||
|
|
413
|
+
intent.status === 'blueprint' ||
|
|
414
|
+
intent.status === 'blueprint_ask'
|
|
415
|
+
) {
|
|
416
|
+
return { text: 'LLM attached', busy: true }
|
|
417
|
+
}
|
|
418
|
+
if (intent.llmIdle) {
|
|
419
|
+
return { text: 'LLM is idle', busy: false }
|
|
420
|
+
}
|
|
421
|
+
return { text: 'LLM connected', busy: true }
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
function LiveStatus({
|
|
425
|
+
intent,
|
|
426
|
+
showStop = false,
|
|
427
|
+
onStop,
|
|
428
|
+
}: {
|
|
429
|
+
intent: AgentIntent
|
|
430
|
+
showStop?: boolean
|
|
431
|
+
onStop?: () => void
|
|
432
|
+
}) {
|
|
433
|
+
const status = sessionLiveStatus(intent)
|
|
434
|
+
const [flash, setFlash] = useState(false)
|
|
435
|
+
const lastAt = intent.lastAck?.at
|
|
436
|
+
|
|
437
|
+
useEffect(() => {
|
|
438
|
+
if (!lastAt) return
|
|
439
|
+
setFlash(true)
|
|
440
|
+
const timer = window.setTimeout(() => setFlash(false), 700)
|
|
441
|
+
return () => window.clearTimeout(timer)
|
|
442
|
+
}, [lastAt])
|
|
443
|
+
|
|
444
|
+
return (
|
|
445
|
+
<div className="hud-live" data-busy={status.busy} data-flash={flash}>
|
|
446
|
+
{status.busy ? <span className="hud-spinner" aria-hidden="true" /> : null}
|
|
447
|
+
<span>{status.text}</span>
|
|
448
|
+
{showStop && onStop ? (
|
|
449
|
+
<button
|
|
450
|
+
className="hud-button hud-button-reject"
|
|
451
|
+
type="button"
|
|
452
|
+
onClick={onStop}
|
|
453
|
+
>
|
|
454
|
+
Stop
|
|
455
|
+
</button>
|
|
456
|
+
) : null}
|
|
457
|
+
</div>
|
|
458
|
+
)
|
|
459
|
+
}
|
|
460
|
+
|
|
251
461
|
type SessionPanelProps = {
|
|
252
462
|
intent: AgentIntent
|
|
253
463
|
focused: boolean
|
|
254
464
|
naming: boolean
|
|
465
|
+
attachedSession?: AgentIntent | null
|
|
255
466
|
onFocus: () => void
|
|
256
467
|
onWorkflowAction: (
|
|
257
468
|
sessionId: string,
|
|
@@ -265,12 +476,16 @@ function SessionPanel({
|
|
|
265
476
|
intent,
|
|
266
477
|
focused,
|
|
267
478
|
naming,
|
|
479
|
+
attachedSession = null,
|
|
268
480
|
onFocus,
|
|
269
481
|
onWorkflowAction,
|
|
270
482
|
onNavigateDiff,
|
|
271
483
|
}: SessionPanelProps) {
|
|
272
484
|
const [minimized, setMinimized] = useState(false)
|
|
273
485
|
const [instruction, setInstruction] = useState('')
|
|
486
|
+
const [initialInstruction, setInitialInstruction] = useState(
|
|
487
|
+
() => intent.initialInstruction ?? '',
|
|
488
|
+
)
|
|
274
489
|
const sessionId = intent.sessionId
|
|
275
490
|
const pending = intent.status === 'pending' && intent.isActiveDiff
|
|
276
491
|
const askingBlueprint = intent.status === 'blueprint_ask'
|
|
@@ -330,8 +545,31 @@ function SessionPanel({
|
|
|
330
545
|
setInstruction('')
|
|
331
546
|
}, [intent.diffId])
|
|
332
547
|
|
|
548
|
+
useEffect(() => {
|
|
549
|
+
setInitialInstruction(intent.initialInstruction ?? '')
|
|
550
|
+
}, [sessionId])
|
|
551
|
+
|
|
333
552
|
if (!sessionId || !isReviewingIntent(intent.status)) return null
|
|
334
553
|
|
|
554
|
+
const updateInitialInstruction = (value: string) => {
|
|
555
|
+
setInitialInstruction(value)
|
|
556
|
+
persistInitialInstruction(sessionId, value)
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
const showInitialInstruction =
|
|
560
|
+
Boolean(intent.awaitingAttach) &&
|
|
561
|
+
(askingBlueprint || sendingBlueprint || preparing)
|
|
562
|
+
const handshakeSetup = showInitialInstruction
|
|
563
|
+
const llmConnected = intent.awaitingAttach === false
|
|
564
|
+
const showConnectedProgress =
|
|
565
|
+
llmConnected && (askingBlueprint || sendingBlueprint || preparing)
|
|
566
|
+
const attachBlockedBy =
|
|
567
|
+
intent.awaitingAttach &&
|
|
568
|
+
attachedSession &&
|
|
569
|
+
attachedSession.sessionId !== sessionId
|
|
570
|
+
? sessionLabel(attachedSession) || 'another session'
|
|
571
|
+
: null
|
|
572
|
+
|
|
335
573
|
const act = (
|
|
336
574
|
action: WorkflowAction,
|
|
337
575
|
options?: { instruction?: string; step?: number; stepByStep?: boolean },
|
|
@@ -349,12 +587,32 @@ function SessionPanel({
|
|
|
349
587
|
onPointerDown={onFocus}
|
|
350
588
|
>
|
|
351
589
|
<PanelChrome
|
|
352
|
-
title={
|
|
590
|
+
title={
|
|
591
|
+
sessionLabel(intent) ||
|
|
592
|
+
(showConnectedProgress ? 'LLM connected' : reviewTitle(intent.status))
|
|
593
|
+
}
|
|
594
|
+
subtitle={
|
|
595
|
+
sessionLabel(intent)
|
|
596
|
+
? showConnectedProgress
|
|
597
|
+
? 'LLM connected'
|
|
598
|
+
: reviewTitle(intent.status)
|
|
599
|
+
: undefined
|
|
600
|
+
}
|
|
353
601
|
minimized={minimized}
|
|
354
602
|
onMinimize={() => setMinimized((current) => !current)}
|
|
603
|
+
onClose={() => act('stop')}
|
|
604
|
+
closeLabel="Stop session"
|
|
605
|
+
closeReject
|
|
355
606
|
/>
|
|
356
607
|
{!minimized && (
|
|
357
608
|
<>
|
|
609
|
+
{!intent.awaitingAttach && (
|
|
610
|
+
<LiveStatus
|
|
611
|
+
intent={intent}
|
|
612
|
+
showStop={showConnectedProgress || working}
|
|
613
|
+
onStop={() => act('stop')}
|
|
614
|
+
/>
|
|
615
|
+
)}
|
|
358
616
|
<label className="hud-mode-switch">
|
|
359
617
|
<span>Step by step</span>
|
|
360
618
|
<button
|
|
@@ -373,13 +631,35 @@ function SessionPanel({
|
|
|
373
631
|
Accept proposal.
|
|
374
632
|
</p>
|
|
375
633
|
)}
|
|
376
|
-
{
|
|
377
|
-
<
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
634
|
+
{handshakeSetup ? (
|
|
635
|
+
<HandshakeSetup
|
|
636
|
+
instruction={initialInstruction}
|
|
637
|
+
onInstructionChange={updateInitialInstruction}
|
|
638
|
+
blueprintDefined={blueprintIsDefined(intent)}
|
|
639
|
+
awaitingAttach={Boolean(intent.awaitingAttach)}
|
|
640
|
+
attachBlockedBy={attachBlockedBy || null}
|
|
641
|
+
/>
|
|
642
|
+
) : intent.awaitingAttach ? (
|
|
643
|
+
attachedSession &&
|
|
644
|
+
attachedSession.sessionId !== sessionId ? (
|
|
645
|
+
<p className="hud-mode-hint">
|
|
646
|
+
An LLM is already attached to{' '}
|
|
647
|
+
{sessionLabel(attachedSession) || 'another session'}. Stop that
|
|
648
|
+
session before running <kbd>/inbase</kbd>.
|
|
649
|
+
</p>
|
|
650
|
+
) : (
|
|
651
|
+
<p className="hud-mode-hint">
|
|
652
|
+
No LLM is attached. Open a Cursor chat and run{' '}
|
|
653
|
+
<kbd>/inbase</kbd>. It connects to this focused session.
|
|
654
|
+
</p>
|
|
655
|
+
)
|
|
656
|
+
) : null}
|
|
657
|
+
{intent.feature &&
|
|
658
|
+
!handshakeSetup &&
|
|
659
|
+
intent.feature.trim() !== sessionLabel(intent) && (
|
|
660
|
+
<p className="hud-feature">{intent.feature}</p>
|
|
661
|
+
)}
|
|
662
|
+
{askingBlueprint && !handshakeSetup && !showConnectedProgress ? (
|
|
383
663
|
<>
|
|
384
664
|
<p>
|
|
385
665
|
Place files and folders for this chat, then send them as a
|
|
@@ -410,64 +690,9 @@ function SessionPanel({
|
|
|
410
690
|
</button>
|
|
411
691
|
</div>
|
|
412
692
|
</>
|
|
413
|
-
) :
|
|
414
|
-
<>
|
|
415
|
-
<p>
|
|
416
|
-
Walk the map, press <kbd>Space</kbd> for a file and{' '}
|
|
417
|
-
<kbd>B</kbd> for an island. Send the blueprint when the layout
|
|
418
|
-
is ready. You can keep placing files after this handshake.
|
|
419
|
-
</p>
|
|
420
|
-
<div className="hud-decide">
|
|
421
|
-
<button
|
|
422
|
-
className="hud-button hud-button-approve"
|
|
423
|
-
type="button"
|
|
424
|
-
disabled={naming}
|
|
425
|
-
onClick={() => act('blueprint_send')}
|
|
426
|
-
>
|
|
427
|
-
Send blueprint
|
|
428
|
-
</button>
|
|
429
|
-
<button
|
|
430
|
-
className="hud-button hud-button-reject"
|
|
431
|
-
type="button"
|
|
432
|
-
onClick={() => act('stop')}
|
|
433
|
-
>
|
|
434
|
-
Stop
|
|
435
|
-
</button>
|
|
436
|
-
</div>
|
|
437
|
-
</>
|
|
438
|
-
) : intent.status === 'finished' ? (
|
|
693
|
+
) : handshakeSetup ? null : intent.status === 'finished' ? (
|
|
439
694
|
<p>All plan steps were applied.</p>
|
|
440
|
-
) : preparing ? (
|
|
441
|
-
<div className="hud-working">
|
|
442
|
-
<span className="hud-spinner" aria-hidden="true" />
|
|
443
|
-
<span>LLM preparing…</span>
|
|
444
|
-
<button
|
|
445
|
-
className="hud-button hud-button-reject"
|
|
446
|
-
type="button"
|
|
447
|
-
onClick={() => act('stop')}
|
|
448
|
-
>
|
|
449
|
-
Stop
|
|
450
|
-
</button>
|
|
451
|
-
</div>
|
|
452
|
-
) : working ? (
|
|
453
|
-
<div className="hud-working">
|
|
454
|
-
<span className="hud-spinner" aria-hidden="true" />
|
|
455
|
-
<span>
|
|
456
|
-
{intent.status === 'replanning'
|
|
457
|
-
? 'Updating the remaining plan from your instruction…'
|
|
458
|
-
: intent.stalledWait
|
|
459
|
-
? 'The LLM is still waiting on this step…'
|
|
460
|
-
: `Implementing ${stepLabel.toLowerCase()}…`}
|
|
461
|
-
</span>
|
|
462
|
-
<button
|
|
463
|
-
className="hud-button hud-button-reject"
|
|
464
|
-
type="button"
|
|
465
|
-
onClick={() => act('stop')}
|
|
466
|
-
>
|
|
467
|
-
Stop
|
|
468
|
-
</button>
|
|
469
|
-
</div>
|
|
470
|
-
) : (
|
|
695
|
+
) : showConnectedProgress || preparing ? null : (
|
|
471
696
|
<p>
|
|
472
697
|
{stepLabel}
|
|
473
698
|
{intent.reason ? ` · ${intent.reason}` : ''}
|
|
@@ -475,10 +700,11 @@ function SessionPanel({
|
|
|
475
700
|
)}
|
|
476
701
|
{!askingBlueprint && !sendingBlueprint && (
|
|
477
702
|
<>
|
|
478
|
-
{canPlace && (
|
|
703
|
+
{canPlace && !intent.working && (
|
|
479
704
|
<p>
|
|
480
|
-
|
|
481
|
-
|
|
705
|
+
On the map, right-click to add a file or folder.{' '}
|
|
706
|
+
<kbd>Space</kbd> places a file, <kbd>B</kbd> an island while
|
|
707
|
+
walking.
|
|
482
708
|
</p>
|
|
483
709
|
)}
|
|
484
710
|
{intent.steps?.length > 0 && (
|
|
@@ -487,6 +713,11 @@ function SessionPanel({
|
|
|
487
713
|
const proposed = proposalStep === step.index
|
|
488
714
|
const processing = processingStep === step.index
|
|
489
715
|
const accepted = acceptedSteps.has(step.index)
|
|
716
|
+
const creating = processing && !proposed
|
|
717
|
+
const showStepAction =
|
|
718
|
+
creating ||
|
|
719
|
+
(canRunNext && invokeStep?.index === step.index) ||
|
|
720
|
+
(canAcceptProposal && proposed)
|
|
490
721
|
return (
|
|
491
722
|
<li
|
|
492
723
|
key={step.index}
|
|
@@ -496,11 +727,12 @@ function SessionPanel({
|
|
|
496
727
|
<span className="hud-step-index">{step.index}.</span>
|
|
497
728
|
<span className="hud-step-main">
|
|
498
729
|
<span className="hud-step-title">{step.title}</span>
|
|
499
|
-
{
|
|
500
|
-
(canAcceptProposal && proposed)) && (
|
|
730
|
+
{showStepAction && (
|
|
501
731
|
<button
|
|
502
732
|
className="hud-button hud-button-approve hud-run-step"
|
|
503
733
|
type="button"
|
|
734
|
+
disabled={creating}
|
|
735
|
+
aria-busy={creating}
|
|
504
736
|
onClick={() =>
|
|
505
737
|
proposed
|
|
506
738
|
? lastStep
|
|
@@ -513,7 +745,11 @@ function SessionPanel({
|
|
|
513
745
|
})
|
|
514
746
|
}
|
|
515
747
|
>
|
|
516
|
-
{proposed
|
|
748
|
+
{proposed
|
|
749
|
+
? 'Accept proposal'
|
|
750
|
+
: creating
|
|
751
|
+
? 'Creating proposal…'
|
|
752
|
+
: 'Create proposal'}
|
|
517
753
|
</button>
|
|
518
754
|
)}
|
|
519
755
|
</span>
|
|
@@ -677,6 +913,7 @@ function SessionPanel({
|
|
|
677
913
|
rows={3}
|
|
678
914
|
placeholder="Describe what should change in the next diff…"
|
|
679
915
|
onChange={(event) => setInstruction(event.target.value)}
|
|
916
|
+
onKeyDown={(event) => event.stopPropagation()}
|
|
680
917
|
/>
|
|
681
918
|
</label>
|
|
682
919
|
<div className="hud-decide">
|
|
@@ -708,6 +945,131 @@ function SessionPanel({
|
|
|
708
945
|
)
|
|
709
946
|
}
|
|
710
947
|
|
|
948
|
+
function BranchChangesPanel({
|
|
949
|
+
changes,
|
|
950
|
+
}: {
|
|
951
|
+
changes: BranchChanges
|
|
952
|
+
}) {
|
|
953
|
+
const [minimized, setMinimized] = useState(false)
|
|
954
|
+
const addedFunctions = changes.addedFunctions ?? []
|
|
955
|
+
const addedVariables = changes.addedVariables ?? []
|
|
956
|
+
const addedImports = changes.addedImports ?? []
|
|
957
|
+
const changedFunctions = changes.changedFunctions ?? []
|
|
958
|
+
const changedVariables = changes.changedVariables ?? []
|
|
959
|
+
const subtitle =
|
|
960
|
+
changes.branch && changes.base
|
|
961
|
+
? `${changes.branch} vs ${changes.base}`
|
|
962
|
+
: changes.branch
|
|
963
|
+
? changes.branch
|
|
964
|
+
: null
|
|
965
|
+
const hasContent =
|
|
966
|
+
changes.files.length > 0 ||
|
|
967
|
+
(changes.createFolders ?? []).length > 0 ||
|
|
968
|
+
changes.creates.length > 0 ||
|
|
969
|
+
changes.deletes.length > 0 ||
|
|
970
|
+
addedFunctions.length > 0 ||
|
|
971
|
+
addedVariables.length > 0 ||
|
|
972
|
+
addedImports.length > 0 ||
|
|
973
|
+
changedFunctions.length > 0 ||
|
|
974
|
+
changedVariables.length > 0 ||
|
|
975
|
+
(changes.imports ?? []).length > 0
|
|
976
|
+
|
|
977
|
+
return (
|
|
978
|
+
<aside
|
|
979
|
+
className="hud-panel hud-panel-planned hud-panel-done"
|
|
980
|
+
data-minimized={minimized}
|
|
981
|
+
>
|
|
982
|
+
<PanelChrome
|
|
983
|
+
title="Branch changes"
|
|
984
|
+
minimized={minimized}
|
|
985
|
+
onMinimize={() => setMinimized((current) => !current)}
|
|
986
|
+
/>
|
|
987
|
+
{!minimized && (
|
|
988
|
+
<>
|
|
989
|
+
{subtitle && <p className="hud-feature">{subtitle}</p>}
|
|
990
|
+
{!hasContent ? (
|
|
991
|
+
<p>No file changes on this branch.</p>
|
|
992
|
+
) : (
|
|
993
|
+
<MutationFold hasContent>
|
|
994
|
+
{changes.files.length > 0 && (
|
|
995
|
+
<>
|
|
996
|
+
<div className="hud-section-title hud-section-title-edit">
|
|
997
|
+
Changed
|
|
998
|
+
</div>
|
|
999
|
+
<ul>
|
|
1000
|
+
{changes.files.map((id) => (
|
|
1001
|
+
<li className="hud-file-edit" key={id}>
|
|
1002
|
+
{id}
|
|
1003
|
+
</li>
|
|
1004
|
+
))}
|
|
1005
|
+
</ul>
|
|
1006
|
+
</>
|
|
1007
|
+
)}
|
|
1008
|
+
{(changes.createFolders ?? []).length > 0 && (
|
|
1009
|
+
<>
|
|
1010
|
+
<div className="hud-section-title hud-section-title-add">
|
|
1011
|
+
Added islands
|
|
1012
|
+
</div>
|
|
1013
|
+
<ul>
|
|
1014
|
+
{changes.createFolders.map((id) => (
|
|
1015
|
+
<li className="hud-file-add" key={id}>
|
|
1016
|
+
{id}/
|
|
1017
|
+
</li>
|
|
1018
|
+
))}
|
|
1019
|
+
</ul>
|
|
1020
|
+
</>
|
|
1021
|
+
)}
|
|
1022
|
+
{changes.creates.length > 0 && (
|
|
1023
|
+
<>
|
|
1024
|
+
<div className="hud-section-title hud-section-title-add">
|
|
1025
|
+
Added
|
|
1026
|
+
</div>
|
|
1027
|
+
<ul>
|
|
1028
|
+
{changes.creates.map((id) => (
|
|
1029
|
+
<li className="hud-file-add" key={id}>
|
|
1030
|
+
{id}
|
|
1031
|
+
</li>
|
|
1032
|
+
))}
|
|
1033
|
+
</ul>
|
|
1034
|
+
</>
|
|
1035
|
+
)}
|
|
1036
|
+
{changes.deletes.length > 0 && (
|
|
1037
|
+
<>
|
|
1038
|
+
<div className="hud-section-title hud-section-title-remove">
|
|
1039
|
+
Removed
|
|
1040
|
+
</div>
|
|
1041
|
+
<ul>
|
|
1042
|
+
{changes.deletes.map((id) => (
|
|
1043
|
+
<li className="hud-file-remove" key={id}>
|
|
1044
|
+
{id}
|
|
1045
|
+
</li>
|
|
1046
|
+
))}
|
|
1047
|
+
</ul>
|
|
1048
|
+
</>
|
|
1049
|
+
)}
|
|
1050
|
+
<PatchSymbolChanges
|
|
1051
|
+
title="Functions"
|
|
1052
|
+
added={addedFunctions}
|
|
1053
|
+
changed={changedFunctions}
|
|
1054
|
+
/>
|
|
1055
|
+
<PatchSymbolChanges
|
|
1056
|
+
title="Vars"
|
|
1057
|
+
added={addedVariables}
|
|
1058
|
+
changed={changedVariables}
|
|
1059
|
+
/>
|
|
1060
|
+
<PanelList
|
|
1061
|
+
title="Imports"
|
|
1062
|
+
items={importLabels(addedImports)}
|
|
1063
|
+
tone="add"
|
|
1064
|
+
/>
|
|
1065
|
+
</MutationFold>
|
|
1066
|
+
)}
|
|
1067
|
+
</>
|
|
1068
|
+
)}
|
|
1069
|
+
</aside>
|
|
1070
|
+
)
|
|
1071
|
+
}
|
|
1072
|
+
|
|
711
1073
|
type ExplorerInstruction = {
|
|
712
1074
|
id: string
|
|
713
1075
|
keys: string[]
|
|
@@ -751,6 +1113,8 @@ function explorerInstructions({
|
|
|
751
1113
|
importedBy,
|
|
752
1114
|
canStop,
|
|
753
1115
|
sessionCount,
|
|
1116
|
+
showBranchChanges,
|
|
1117
|
+
canShowBranchChanges,
|
|
754
1118
|
}: {
|
|
755
1119
|
canPlace: boolean
|
|
756
1120
|
hasChangeSet: boolean
|
|
@@ -761,6 +1125,8 @@ function explorerInstructions({
|
|
|
761
1125
|
importedBy: boolean
|
|
762
1126
|
canStop: boolean
|
|
763
1127
|
sessionCount: number
|
|
1128
|
+
showBranchChanges: boolean
|
|
1129
|
+
canShowBranchChanges: boolean
|
|
764
1130
|
}): ExplorerInstructionSection[] {
|
|
765
1131
|
const backspace: ExplorerInstruction[] =
|
|
766
1132
|
selectedUserCreated && canPlace
|
|
@@ -781,6 +1147,17 @@ function explorerInstructions({
|
|
|
781
1147
|
keys: ['K'],
|
|
782
1148
|
label: importedBy ? 'Show imports' : 'Show imported by',
|
|
783
1149
|
}
|
|
1150
|
+
const branch: ExplorerInstruction[] = canShowBranchChanges
|
|
1151
|
+
? [
|
|
1152
|
+
{
|
|
1153
|
+
id: 'branch-changes',
|
|
1154
|
+
keys: ['G'],
|
|
1155
|
+
label: showBranchChanges
|
|
1156
|
+
? 'Hide branch changes'
|
|
1157
|
+
: 'Show branch changes',
|
|
1158
|
+
},
|
|
1159
|
+
]
|
|
1160
|
+
: []
|
|
784
1161
|
const stop: ExplorerInstruction[] = canStop
|
|
785
1162
|
? [
|
|
786
1163
|
{
|
|
@@ -821,11 +1198,17 @@ function explorerInstructions({
|
|
|
821
1198
|
{ id: 'aim-line', keys: ['Click'], label: 'Aim a line to fly' },
|
|
822
1199
|
...info,
|
|
823
1200
|
imported,
|
|
1201
|
+
...branch,
|
|
824
1202
|
{
|
|
825
1203
|
id: 'update-model',
|
|
826
1204
|
keys: ['Update model'],
|
|
827
1205
|
label: 'Rescan and rebuild the map',
|
|
828
1206
|
},
|
|
1207
|
+
{
|
|
1208
|
+
id: 'setup-session',
|
|
1209
|
+
keys: ['Setup LLM session'],
|
|
1210
|
+
label: 'Open a session; /inbase connects the focused one',
|
|
1211
|
+
},
|
|
829
1212
|
{ id: 'toggle-map', keys: ['M'], label: 'Toggle map' },
|
|
830
1213
|
{
|
|
831
1214
|
id: 'release',
|
|
@@ -871,8 +1254,8 @@ function explorerInstructions({
|
|
|
871
1254
|
{ id: 'select-island', keys: ['Click'], label: 'Select an island' },
|
|
872
1255
|
{
|
|
873
1256
|
id: 'add-file-folder',
|
|
874
|
-
keys: [],
|
|
875
|
-
label: 'Add file
|
|
1257
|
+
keys: ['Right-click'],
|
|
1258
|
+
label: 'Add file or folder',
|
|
876
1259
|
},
|
|
877
1260
|
]
|
|
878
1261
|
: []),
|
|
@@ -902,11 +1285,17 @@ function explorerInstructions({
|
|
|
902
1285
|
...info,
|
|
903
1286
|
thumbnail,
|
|
904
1287
|
imported,
|
|
1288
|
+
...branch,
|
|
905
1289
|
{
|
|
906
1290
|
id: 'update-model',
|
|
907
1291
|
keys: ['Update model'],
|
|
908
1292
|
label: 'Rescan and rebuild the map',
|
|
909
1293
|
},
|
|
1294
|
+
{
|
|
1295
|
+
id: 'setup-session',
|
|
1296
|
+
keys: ['Setup LLM session'],
|
|
1297
|
+
label: 'Open a session; /inbase connects the focused one',
|
|
1298
|
+
},
|
|
910
1299
|
{ id: 'map-walk', keys: ['M'], label: 'Back to walk' },
|
|
911
1300
|
...stop,
|
|
912
1301
|
],
|
|
@@ -930,6 +1319,7 @@ type HUDProps = {
|
|
|
930
1319
|
intents?: AgentIntent[]
|
|
931
1320
|
focusedSessionId?: string | null
|
|
932
1321
|
onFocusSession?: (sessionId: string) => void
|
|
1322
|
+
onSetupSession?: () => Promise<unknown>
|
|
933
1323
|
onWorkflowAction: (
|
|
934
1324
|
sessionId: string,
|
|
935
1325
|
action: WorkflowAction,
|
|
@@ -940,6 +1330,11 @@ type HUDProps = {
|
|
|
940
1330
|
onWalk: () => void
|
|
941
1331
|
followLook: boolean
|
|
942
1332
|
onToggleFollowLook: () => void
|
|
1333
|
+
showBranchChanges?: boolean
|
|
1334
|
+
branchChanges?: BranchChanges
|
|
1335
|
+
canShowBranchChanges?: boolean
|
|
1336
|
+
llmMakingChanges?: boolean
|
|
1337
|
+
onToggleShowBranchChanges?: () => void
|
|
943
1338
|
onUpdateModel: () => void
|
|
944
1339
|
updatingModel?: boolean
|
|
945
1340
|
importedBy: boolean
|
|
@@ -989,12 +1384,18 @@ export function HUD({
|
|
|
989
1384
|
intents,
|
|
990
1385
|
focusedSessionId = null,
|
|
991
1386
|
onFocusSession,
|
|
1387
|
+
onSetupSession,
|
|
992
1388
|
onWorkflowAction,
|
|
993
1389
|
onNavigateDiff,
|
|
994
1390
|
onOpenMap,
|
|
995
1391
|
onWalk,
|
|
996
1392
|
followLook,
|
|
997
1393
|
onToggleFollowLook,
|
|
1394
|
+
showBranchChanges = false,
|
|
1395
|
+
branchChanges,
|
|
1396
|
+
canShowBranchChanges = false,
|
|
1397
|
+
llmMakingChanges = false,
|
|
1398
|
+
onToggleShowBranchChanges,
|
|
998
1399
|
onUpdateModel,
|
|
999
1400
|
updatingModel = false,
|
|
1000
1401
|
importedBy,
|
|
@@ -1043,9 +1444,13 @@ export function HUD({
|
|
|
1043
1444
|
(item) => item.sessionId && isReviewingIntent(item.status),
|
|
1044
1445
|
)
|
|
1045
1446
|
const canStop = canStopSession(intent)
|
|
1447
|
+
const attachedSession =
|
|
1448
|
+
sessions.find((session) => session.awaitingAttach === false) ?? null
|
|
1046
1449
|
const [walkIntro, setWalkIntro] = useState(false)
|
|
1047
1450
|
const walkIntroSeen = useRef(false)
|
|
1048
1451
|
const [instructionsOpen, setInstructionsOpen] = useState(false)
|
|
1452
|
+
const [setupError, setSetupError] = useState<string | null>(null)
|
|
1453
|
+
const [setupBusy, setSetupBusy] = useState(false)
|
|
1049
1454
|
const [infoVisible, setInfoVisible] = useState(false)
|
|
1050
1455
|
const [infoMinimized, setInfoMinimized] = useState(false)
|
|
1051
1456
|
const [thumbnailVisible, setThumbnailVisible] = useState(true)
|
|
@@ -1053,12 +1458,13 @@ export function HUD({
|
|
|
1053
1458
|
const [thumbnailMaximized, setThumbnailMaximized] = useState(false)
|
|
1054
1459
|
const infoPanelRef = useRef<HTMLDivElement>(null)
|
|
1055
1460
|
const canPlace = Boolean(intent.creationMode)
|
|
1056
|
-
const
|
|
1057
|
-
const
|
|
1058
|
-
const
|
|
1059
|
-
const
|
|
1060
|
-
const
|
|
1061
|
-
const
|
|
1461
|
+
const overlay = showBranchChanges && branchChanges ? branchChanges : intent
|
|
1462
|
+
const previewing = intent.preview || showBranchChanges
|
|
1463
|
+
const addedFunctions = overlay.addedFunctions ?? []
|
|
1464
|
+
const addedVariables = overlay.addedVariables ?? []
|
|
1465
|
+
const addedImports = overlay.addedImports ?? []
|
|
1466
|
+
const changedFunctions = overlay.changedFunctions ?? []
|
|
1467
|
+
const changedVariables = overlay.changedVariables ?? []
|
|
1062
1468
|
const selectedAddedFunctions = selected
|
|
1063
1469
|
? addedFunctions.filter((item) => item.file === selected.id)
|
|
1064
1470
|
: []
|
|
@@ -1285,6 +1691,8 @@ export function HUD({
|
|
|
1285
1691
|
importedBy,
|
|
1286
1692
|
canStop,
|
|
1287
1693
|
sessionCount: sessions.length,
|
|
1694
|
+
showBranchChanges,
|
|
1695
|
+
canShowBranchChanges,
|
|
1288
1696
|
})
|
|
1289
1697
|
const currentInstructionView: InstructionView = mapping
|
|
1290
1698
|
? thumbnailMaximized
|
|
@@ -1368,7 +1776,7 @@ export function HUD({
|
|
|
1368
1776
|
{selected && <div className="hud-chip">{selected.path}</div>}
|
|
1369
1777
|
</div>
|
|
1370
1778
|
|
|
1371
|
-
{sessions.length > 0 && (
|
|
1779
|
+
{(sessions.length > 0 || showBranchChanges) && (
|
|
1372
1780
|
<div className="hud-left-stack">
|
|
1373
1781
|
{sessions.length > 1 && (
|
|
1374
1782
|
<div className="hud-session-tabs" role="tablist" aria-label="LLM sessions">
|
|
@@ -1383,6 +1791,7 @@ export function HUD({
|
|
|
1383
1791
|
aria-selected={active}
|
|
1384
1792
|
data-active={active}
|
|
1385
1793
|
key={session.sessionId}
|
|
1794
|
+
title={sessionTabLabel(session, sessions)}
|
|
1386
1795
|
onClick={() => {
|
|
1387
1796
|
if (session.sessionId) onFocusSession?.(session.sessionId)
|
|
1388
1797
|
}}
|
|
@@ -1393,16 +1802,22 @@ export function HUD({
|
|
|
1393
1802
|
})}
|
|
1394
1803
|
</div>
|
|
1395
1804
|
)}
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1805
|
+
{sessions.length > 0 && (
|
|
1806
|
+
<SessionPanel
|
|
1807
|
+
intent={intent}
|
|
1808
|
+
focused
|
|
1809
|
+
naming={naming}
|
|
1810
|
+
attachedSession={attachedSession}
|
|
1811
|
+
onFocus={() => {
|
|
1812
|
+
if (intent.sessionId) onFocusSession?.(intent.sessionId)
|
|
1813
|
+
}}
|
|
1814
|
+
onWorkflowAction={onWorkflowAction}
|
|
1815
|
+
onNavigateDiff={onNavigateDiff}
|
|
1816
|
+
/>
|
|
1817
|
+
)}
|
|
1818
|
+
{showBranchChanges && branchChanges && (
|
|
1819
|
+
<BranchChangesPanel changes={branchChanges} />
|
|
1820
|
+
)}
|
|
1406
1821
|
</div>
|
|
1407
1822
|
)}
|
|
1408
1823
|
|
|
@@ -1444,7 +1859,7 @@ export function HUD({
|
|
|
1444
1859
|
selectedAddedImports.length > 0) && (
|
|
1445
1860
|
<>
|
|
1446
1861
|
<div className="hud-section-title hud-section-title-edit">
|
|
1447
|
-
LLM changes
|
|
1862
|
+
{showBranchChanges ? 'Branch changes' : 'LLM changes'}
|
|
1448
1863
|
</div>
|
|
1449
1864
|
<PatchSymbolChanges
|
|
1450
1865
|
title="Functions"
|
|
@@ -1823,6 +2238,31 @@ export function HUD({
|
|
|
1823
2238
|
>
|
|
1824
2239
|
{updatingModel ? 'Updating…' : 'Update model'}
|
|
1825
2240
|
</button>
|
|
2241
|
+
{onSetupSession && (
|
|
2242
|
+
<button
|
|
2243
|
+
className="hud-button"
|
|
2244
|
+
type="button"
|
|
2245
|
+
aria-label="Start an LLM session without attaching a chat yet"
|
|
2246
|
+
title={setupError ?? 'Start an LLM session without attaching a chat yet'}
|
|
2247
|
+
disabled={setupBusy}
|
|
2248
|
+
onClick={() => {
|
|
2249
|
+
setInstructionsOpen(false)
|
|
2250
|
+
setSetupError(null)
|
|
2251
|
+
setSetupBusy(true)
|
|
2252
|
+
void onSetupSession()
|
|
2253
|
+
.catch((caught) => {
|
|
2254
|
+
setSetupError(
|
|
2255
|
+
caught instanceof Error
|
|
2256
|
+
? caught.message
|
|
2257
|
+
: 'Could not set up the session',
|
|
2258
|
+
)
|
|
2259
|
+
})
|
|
2260
|
+
.finally(() => setSetupBusy(false))
|
|
2261
|
+
}}
|
|
2262
|
+
>
|
|
2263
|
+
{setupBusy ? 'Starting…' : 'Setup LLM session'}
|
|
2264
|
+
</button>
|
|
2265
|
+
)}
|
|
1826
2266
|
</div>
|
|
1827
2267
|
<div className="hud-icon-row">
|
|
1828
2268
|
{mapping && hasChangeSet && onToggleChangePathsOnly && (
|
|
@@ -1936,6 +2376,53 @@ export function HUD({
|
|
|
1936
2376
|
{importedBy ? 'K show imports' : 'K show imported by'}
|
|
1937
2377
|
</span>
|
|
1938
2378
|
</button>
|
|
2379
|
+
<button
|
|
2380
|
+
className="hud-button hud-icon-button"
|
|
2381
|
+
data-active={showBranchChanges}
|
|
2382
|
+
aria-label={
|
|
2383
|
+
llmMakingChanges
|
|
2384
|
+
? 'Show branch changes unavailable while the LLM is making changes'
|
|
2385
|
+
: showBranchChanges
|
|
2386
|
+
? 'Hide branch changes'
|
|
2387
|
+
: 'Show branch changes'
|
|
2388
|
+
}
|
|
2389
|
+
aria-keyshortcuts="G"
|
|
2390
|
+
aria-pressed={showBranchChanges}
|
|
2391
|
+
aria-disabled={!canShowBranchChanges}
|
|
2392
|
+
type="button"
|
|
2393
|
+
onClick={() => {
|
|
2394
|
+
if (!canShowBranchChanges) return
|
|
2395
|
+
onToggleShowBranchChanges?.()
|
|
2396
|
+
}}
|
|
2397
|
+
>
|
|
2398
|
+
<svg
|
|
2399
|
+
viewBox="0 0 24 24"
|
|
2400
|
+
width="18"
|
|
2401
|
+
height="18"
|
|
2402
|
+
fill="none"
|
|
2403
|
+
stroke="currentColor"
|
|
2404
|
+
strokeWidth="2"
|
|
2405
|
+
strokeLinecap="round"
|
|
2406
|
+
strokeLinejoin="round"
|
|
2407
|
+
aria-hidden="true"
|
|
2408
|
+
>
|
|
2409
|
+
<circle cx="6" cy="5" r="2.4" />
|
|
2410
|
+
<circle cx="6" cy="19" r="2.4" />
|
|
2411
|
+
<circle cx="18" cy="12" r="2.4" />
|
|
2412
|
+
<path d="M6 7.4v9.2" />
|
|
2413
|
+
<path d="M6 12h7.2" />
|
|
2414
|
+
<path d="M13.2 12c2.2 0 2.2-4.6 4.4-4.6" />
|
|
2415
|
+
</svg>
|
|
2416
|
+
<span className="hud-tooltip">
|
|
2417
|
+
{llmMakingChanges
|
|
2418
|
+
? 'Unavailable while the LLM is making changes'
|
|
2419
|
+
: !canShowBranchChanges
|
|
2420
|
+
? 'No git branch to show'
|
|
2421
|
+
: showBranchChanges
|
|
2422
|
+
? 'G hide branch changes'
|
|
2423
|
+
: 'G show branch changes'}
|
|
2424
|
+
</span>
|
|
2425
|
+
</button>
|
|
1939
2426
|
<button
|
|
1940
2427
|
className="hud-button hud-icon-button"
|
|
1941
2428
|
data-active={followLook}
|