@jkwd/inbase 0.1.4 → 0.1.6
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/README.md +6 -1
- package/apps/explorer/scripts/patch-lib.d.ts +13 -0
- package/apps/explorer/scripts/patch-lib.mjs +162 -25
- package/apps/explorer/scripts/session-store.d.ts +37 -0
- package/apps/explorer/scripts/session-store.mjs +415 -53
- package/apps/explorer/src/App.tsx +307 -88
- package/apps/explorer/src/agentIntent.ts +42 -1
- package/apps/explorer/src/index.css +312 -5
- package/apps/explorer/src/layout.ts +111 -1
- package/apps/explorer/src/scene/Bridge.tsx +23 -5
- package/apps/explorer/src/scene/FileBlock.tsx +113 -144
- package/apps/explorer/src/scene/FolderArea.tsx +35 -16
- package/apps/explorer/src/scene/MapSelectBorder.tsx +3 -1
- package/apps/explorer/src/scene/MapView.tsx +77 -22
- package/apps/explorer/src/scene/Player.tsx +36 -1
- package/apps/explorer/src/scene/RelationLines.tsx +36 -32
- package/apps/explorer/src/scene/SelectionController.tsx +57 -16
- package/apps/explorer/src/scene/SelectionThumbnail.tsx +895 -0
- package/apps/explorer/src/scene/World.tsx +42 -44
- package/apps/explorer/src/theme.ts +17 -4
- package/apps/explorer/src/types.ts +17 -0
- package/apps/explorer/src/ui/HUD.tsx +946 -385
- package/apps/explorer/vite.config.ts +44 -22
- package/bin/session.mjs +34 -8
- package/package.json +1 -1
- package/skill/inbase/SKILL.md +35 -22
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { useEffect, useRef, useState, type ReactNode } from 'react'
|
|
2
2
|
import { NameInput } from './NameInput'
|
|
3
|
+
import { SelectionThumbnail } from '../scene/SelectionThumbnail'
|
|
3
4
|
import {
|
|
5
|
+
canStopSession,
|
|
4
6
|
isReviewingIntent,
|
|
5
7
|
type AgentIntent,
|
|
6
8
|
type AgentIntentStatus,
|
|
@@ -9,6 +11,7 @@ import {
|
|
|
9
11
|
type PatchImportAddition,
|
|
10
12
|
type PatchSymbolAddition,
|
|
11
13
|
type ViewMode,
|
|
14
|
+
type WorldLayout,
|
|
12
15
|
type WorkflowAction,
|
|
13
16
|
} from '../types'
|
|
14
17
|
|
|
@@ -55,17 +58,83 @@ function importLabels(items: PatchImportAddition[]) {
|
|
|
55
58
|
function PanelList({
|
|
56
59
|
title,
|
|
57
60
|
items,
|
|
61
|
+
tone,
|
|
58
62
|
}: {
|
|
59
63
|
title: string
|
|
60
64
|
items: Array<{ key: string; label: string }>
|
|
65
|
+
tone?: 'add' | 'edit' | 'remove'
|
|
61
66
|
}) {
|
|
62
67
|
if (items.length === 0) return null
|
|
68
|
+
const titleClass =
|
|
69
|
+
tone === 'add'
|
|
70
|
+
? 'hud-section-title hud-section-title-add'
|
|
71
|
+
: tone === 'edit'
|
|
72
|
+
? 'hud-section-title hud-section-title-edit'
|
|
73
|
+
: tone === 'remove'
|
|
74
|
+
? 'hud-section-title hud-section-title-remove'
|
|
75
|
+
: 'hud-section-title'
|
|
76
|
+
const itemClass =
|
|
77
|
+
tone === 'add'
|
|
78
|
+
? 'hud-file-add'
|
|
79
|
+
: tone === 'edit'
|
|
80
|
+
? 'hud-file-edit'
|
|
81
|
+
: tone === 'remove'
|
|
82
|
+
? 'hud-file-remove'
|
|
83
|
+
: undefined
|
|
63
84
|
return (
|
|
64
85
|
<>
|
|
65
|
-
<div className=
|
|
86
|
+
<div className={titleClass}>{title}</div>
|
|
66
87
|
<ul>
|
|
67
88
|
{items.map((item) => (
|
|
68
|
-
<li key={item.key}>
|
|
89
|
+
<li className={itemClass} key={item.key}>
|
|
90
|
+
{item.label}
|
|
91
|
+
</li>
|
|
92
|
+
))}
|
|
93
|
+
</ul>
|
|
94
|
+
</>
|
|
95
|
+
)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function symbolChangeClass(kind: 'add' | 'edit' | null | undefined) {
|
|
99
|
+
if (kind === 'add') return 'hud-file-add'
|
|
100
|
+
if (kind === 'edit') return 'hud-file-edit'
|
|
101
|
+
return undefined
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function extraAddedSymbols(
|
|
105
|
+
existing: Array<{ name: string }>,
|
|
106
|
+
added: PatchSymbolAddition[],
|
|
107
|
+
) {
|
|
108
|
+
const have = new Set(existing.map((item) => item.name))
|
|
109
|
+
return added.filter((item) => !have.has(item.name))
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function PatchSymbolChanges({
|
|
113
|
+
title,
|
|
114
|
+
added,
|
|
115
|
+
changed,
|
|
116
|
+
}: {
|
|
117
|
+
title: string
|
|
118
|
+
added: PatchSymbolAddition[]
|
|
119
|
+
changed: PatchSymbolAddition[]
|
|
120
|
+
}) {
|
|
121
|
+
if (added.length === 0 && changed.length === 0) return null
|
|
122
|
+
const addedNames = new Set(added.map((item) => item.name))
|
|
123
|
+
return (
|
|
124
|
+
<>
|
|
125
|
+
<div className="hud-section-title">{title}</div>
|
|
126
|
+
<ul>
|
|
127
|
+
{changed
|
|
128
|
+
.filter((item) => !addedNames.has(item.name))
|
|
129
|
+
.map((item) => (
|
|
130
|
+
<li className="hud-file-edit" key={`edit-${item.file}:${item.name}`}>
|
|
131
|
+
{item.name}
|
|
132
|
+
</li>
|
|
133
|
+
))}
|
|
134
|
+
{added.map((item) => (
|
|
135
|
+
<li className="hud-file-add" key={`add-${item.file}:${item.name}`}>
|
|
136
|
+
{item.name}
|
|
137
|
+
</li>
|
|
69
138
|
))}
|
|
70
139
|
</ul>
|
|
71
140
|
</>
|
|
@@ -123,27 +192,538 @@ function AddIntentRow({
|
|
|
123
192
|
)
|
|
124
193
|
}
|
|
125
194
|
|
|
195
|
+
function PanelChrome({
|
|
196
|
+
title,
|
|
197
|
+
minimized = false,
|
|
198
|
+
onMinimize,
|
|
199
|
+
onClose,
|
|
200
|
+
}: {
|
|
201
|
+
title: ReactNode
|
|
202
|
+
minimized?: boolean
|
|
203
|
+
onMinimize?: () => void
|
|
204
|
+
onClose?: () => void
|
|
205
|
+
}) {
|
|
206
|
+
return (
|
|
207
|
+
<div className="hud-panel-chrome">
|
|
208
|
+
<div className="hud-panel-chrome-title">{title}</div>
|
|
209
|
+
<div className="hud-panel-controls">
|
|
210
|
+
{onMinimize && (
|
|
211
|
+
<button
|
|
212
|
+
className="hud-button hud-icon-button hud-panel-control"
|
|
213
|
+
type="button"
|
|
214
|
+
aria-label={minimized ? 'Restore' : 'Minimize'}
|
|
215
|
+
onClick={onMinimize}
|
|
216
|
+
>
|
|
217
|
+
{minimized ? '+' : '−'}
|
|
218
|
+
</button>
|
|
219
|
+
)}
|
|
220
|
+
{onClose && (
|
|
221
|
+
<button
|
|
222
|
+
className="hud-button hud-icon-button hud-panel-control"
|
|
223
|
+
type="button"
|
|
224
|
+
aria-label="Close"
|
|
225
|
+
onClick={onClose}
|
|
226
|
+
>
|
|
227
|
+
×
|
|
228
|
+
</button>
|
|
229
|
+
)}
|
|
230
|
+
</div>
|
|
231
|
+
</div>
|
|
232
|
+
)
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
type SessionPanelProps = {
|
|
236
|
+
intent: AgentIntent
|
|
237
|
+
focused: boolean
|
|
238
|
+
naming: boolean
|
|
239
|
+
onFocus: () => void
|
|
240
|
+
onWorkflowAction: (
|
|
241
|
+
sessionId: string,
|
|
242
|
+
action: WorkflowAction,
|
|
243
|
+
options?: { instruction?: string; step?: number; stepByStep?: boolean },
|
|
244
|
+
) => void
|
|
245
|
+
onNavigateDiff: (sessionId: string, diffId: string) => void
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function SessionPanel({
|
|
249
|
+
intent,
|
|
250
|
+
focused,
|
|
251
|
+
naming,
|
|
252
|
+
onFocus,
|
|
253
|
+
onWorkflowAction,
|
|
254
|
+
onNavigateDiff,
|
|
255
|
+
}: SessionPanelProps) {
|
|
256
|
+
const [minimized, setMinimized] = useState(false)
|
|
257
|
+
const [instruction, setInstruction] = useState('')
|
|
258
|
+
const sessionId = intent.sessionId
|
|
259
|
+
const pending = intent.status === 'pending' && intent.isActiveDiff
|
|
260
|
+
const askingBlueprint = intent.status === 'blueprint_ask'
|
|
261
|
+
const creatingBlueprint = intent.creationMode || intent.status === 'blueprint'
|
|
262
|
+
const preparing = intent.status === 'preparing'
|
|
263
|
+
const planReady = intent.status === 'planned'
|
|
264
|
+
const working = intent.status === 'working' || intent.status === 'replanning'
|
|
265
|
+
const previewing = intent.preview
|
|
266
|
+
const chainIndex = intent.chainIndex ?? 0
|
|
267
|
+
const previousDiff = intent.chain[chainIndex - 1]
|
|
268
|
+
const nextDiff = intent.chain[chainIndex + 1]
|
|
269
|
+
const stepLabel =
|
|
270
|
+
intent.step && intent.steps?.length > 0
|
|
271
|
+
? `Step ${intent.step} of ${intent.steps.length}`
|
|
272
|
+
: 'Patch'
|
|
273
|
+
const lastStep =
|
|
274
|
+
typeof intent.step === 'number' &&
|
|
275
|
+
intent.steps.length > 0 &&
|
|
276
|
+
intent.step >= intent.steps.length
|
|
277
|
+
const stepByStep = intent.stepByStep !== false
|
|
278
|
+
const addedFunctions = intent.addedFunctions ?? []
|
|
279
|
+
const addedVariables = intent.addedVariables ?? []
|
|
280
|
+
const addedImports = intent.addedImports ?? []
|
|
281
|
+
const changedFunctions = intent.changedFunctions ?? []
|
|
282
|
+
const changedVariables = intent.changedVariables ?? []
|
|
283
|
+
const doneSteps = new Set(
|
|
284
|
+
intent.status === 'finished'
|
|
285
|
+
? intent.steps.map((step) => step.index)
|
|
286
|
+
: intent.chain
|
|
287
|
+
.filter(
|
|
288
|
+
(entry) =>
|
|
289
|
+
entry.status === 'applied' || entry.status === 'extended',
|
|
290
|
+
)
|
|
291
|
+
.map((entry) => entry.step),
|
|
292
|
+
)
|
|
293
|
+
if (intent.status === 'approved' && typeof intent.step === 'number') {
|
|
294
|
+
doneSteps.add(intent.step)
|
|
295
|
+
}
|
|
296
|
+
if (pending && typeof intent.step === 'number' && !lastStep) {
|
|
297
|
+
doneSteps.add(intent.step)
|
|
298
|
+
}
|
|
299
|
+
const nextStep =
|
|
300
|
+
intent.status === 'finished'
|
|
301
|
+
? null
|
|
302
|
+
: (intent.steps.find((step) => !doneSteps.has(step.index)) ?? null)
|
|
303
|
+
const canRunNext =
|
|
304
|
+
Boolean(nextStep) && (planReady || pending) && !working
|
|
305
|
+
const canComplete = pending && lastStep
|
|
306
|
+
const panelDone =
|
|
307
|
+
intent.status === 'finished' ||
|
|
308
|
+
intent.status === 'approved' ||
|
|
309
|
+
doneSteps.size > 0
|
|
310
|
+
|
|
311
|
+
useEffect(() => {
|
|
312
|
+
setInstruction('')
|
|
313
|
+
}, [intent.diffId])
|
|
314
|
+
|
|
315
|
+
if (!sessionId || !isReviewingIntent(intent.status)) return null
|
|
316
|
+
|
|
317
|
+
const act = (
|
|
318
|
+
action: WorkflowAction,
|
|
319
|
+
options?: { instruction?: string; step?: number; stepByStep?: boolean },
|
|
320
|
+
) => onWorkflowAction(sessionId, action, options)
|
|
321
|
+
|
|
322
|
+
return (
|
|
323
|
+
<aside
|
|
324
|
+
className={
|
|
325
|
+
panelDone
|
|
326
|
+
? 'hud-panel hud-panel-planned hud-panel-done'
|
|
327
|
+
: 'hud-panel hud-panel-planned'
|
|
328
|
+
}
|
|
329
|
+
data-minimized={minimized}
|
|
330
|
+
data-focused={focused}
|
|
331
|
+
onPointerDown={onFocus}
|
|
332
|
+
>
|
|
333
|
+
<PanelChrome
|
|
334
|
+
title={reviewTitle(intent.status)}
|
|
335
|
+
minimized={minimized}
|
|
336
|
+
onMinimize={() => setMinimized((current) => !current)}
|
|
337
|
+
/>
|
|
338
|
+
{!minimized && (
|
|
339
|
+
<>
|
|
340
|
+
<label className="hud-mode-switch">
|
|
341
|
+
<span>Step by step</span>
|
|
342
|
+
<button
|
|
343
|
+
className="hud-switch"
|
|
344
|
+
type="button"
|
|
345
|
+
role="switch"
|
|
346
|
+
aria-checked={stepByStep}
|
|
347
|
+
aria-label="Step by step"
|
|
348
|
+
onKeyDown={(event) => event.stopPropagation()}
|
|
349
|
+
onClick={() => act('set_step_by_step', { stepByStep: !stepByStep })}
|
|
350
|
+
/>
|
|
351
|
+
</label>
|
|
352
|
+
{!stepByStep && (
|
|
353
|
+
<p className="hud-mode-hint">
|
|
354
|
+
LLM implements the full plan. You can still walk the diffs, then
|
|
355
|
+
Complete.
|
|
356
|
+
</p>
|
|
357
|
+
)}
|
|
358
|
+
{intent.feature && <p className="hud-feature">{intent.feature}</p>}
|
|
359
|
+
{askingBlueprint ? (
|
|
360
|
+
<>
|
|
361
|
+
<p>
|
|
362
|
+
{intent.canEnterBlueprint
|
|
363
|
+
? 'Place files and folders for this chat, then send them as a blueprint for the LLM?'
|
|
364
|
+
: `Blueprint edit mode is active in another chat (${intent.blueprintSessionId}). Finish or stop it before starting here.`}
|
|
365
|
+
</p>
|
|
366
|
+
<div className="hud-decide">
|
|
367
|
+
<button
|
|
368
|
+
className="hud-button hud-button-approve"
|
|
369
|
+
type="button"
|
|
370
|
+
disabled={!intent.canEnterBlueprint}
|
|
371
|
+
onClick={() => act('blueprint_yes')}
|
|
372
|
+
>
|
|
373
|
+
Create blueprint
|
|
374
|
+
</button>
|
|
375
|
+
<button
|
|
376
|
+
className="hud-button hud-button-approve"
|
|
377
|
+
type="button"
|
|
378
|
+
onClick={() => act('blueprint_no')}
|
|
379
|
+
>
|
|
380
|
+
Let LLM continue
|
|
381
|
+
</button>
|
|
382
|
+
<button
|
|
383
|
+
className="hud-button hud-button-reject"
|
|
384
|
+
type="button"
|
|
385
|
+
onClick={() => act('stop')}
|
|
386
|
+
>
|
|
387
|
+
Stop
|
|
388
|
+
</button>
|
|
389
|
+
</div>
|
|
390
|
+
</>
|
|
391
|
+
) : creatingBlueprint ? (
|
|
392
|
+
<>
|
|
393
|
+
<p>
|
|
394
|
+
Walk the map, press <kbd>Space</kbd> for a file and{' '}
|
|
395
|
+
<kbd>B</kbd> for an island. Send the blueprint when the layout
|
|
396
|
+
is ready.
|
|
397
|
+
</p>
|
|
398
|
+
<div className="hud-decide">
|
|
399
|
+
<button
|
|
400
|
+
className="hud-button hud-button-approve"
|
|
401
|
+
type="button"
|
|
402
|
+
disabled={naming}
|
|
403
|
+
onClick={() => act('blueprint_send')}
|
|
404
|
+
>
|
|
405
|
+
Send blueprint
|
|
406
|
+
</button>
|
|
407
|
+
<button
|
|
408
|
+
className="hud-button hud-button-reject"
|
|
409
|
+
type="button"
|
|
410
|
+
onClick={() => act('stop')}
|
|
411
|
+
>
|
|
412
|
+
Stop
|
|
413
|
+
</button>
|
|
414
|
+
</div>
|
|
415
|
+
</>
|
|
416
|
+
) : intent.status === 'finished' ? (
|
|
417
|
+
<p>All plan steps were applied.</p>
|
|
418
|
+
) : preparing ? (
|
|
419
|
+
<div className="hud-working">
|
|
420
|
+
<span className="hud-spinner" aria-hidden="true" />
|
|
421
|
+
<span>LLM preparing…</span>
|
|
422
|
+
<button
|
|
423
|
+
className="hud-button hud-button-reject"
|
|
424
|
+
type="button"
|
|
425
|
+
onClick={() => act('stop')}
|
|
426
|
+
>
|
|
427
|
+
Stop
|
|
428
|
+
</button>
|
|
429
|
+
</div>
|
|
430
|
+
) : working ? (
|
|
431
|
+
<div className="hud-working">
|
|
432
|
+
<span className="hud-spinner" aria-hidden="true" />
|
|
433
|
+
<span>
|
|
434
|
+
{intent.status === 'replanning'
|
|
435
|
+
? 'Updating the remaining plan from your instruction…'
|
|
436
|
+
: intent.stalledWait
|
|
437
|
+
? 'The LLM is still waiting on this step…'
|
|
438
|
+
: `Implementing ${stepLabel.toLowerCase()}…`}
|
|
439
|
+
</span>
|
|
440
|
+
<button
|
|
441
|
+
className="hud-button hud-button-reject"
|
|
442
|
+
type="button"
|
|
443
|
+
onClick={() => act('stop')}
|
|
444
|
+
>
|
|
445
|
+
Stop
|
|
446
|
+
</button>
|
|
447
|
+
</div>
|
|
448
|
+
) : (
|
|
449
|
+
<p>
|
|
450
|
+
{stepLabel}
|
|
451
|
+
{intent.reason ? ` · ${intent.reason}` : ''}
|
|
452
|
+
</p>
|
|
453
|
+
)}
|
|
454
|
+
{!askingBlueprint && !creatingBlueprint && (
|
|
455
|
+
<>
|
|
456
|
+
{intent.steps?.length > 0 && (
|
|
457
|
+
<ol className="hud-steps">
|
|
458
|
+
{intent.steps.map((step) => (
|
|
459
|
+
<li
|
|
460
|
+
key={step.index}
|
|
461
|
+
data-current={
|
|
462
|
+
intent.status !== 'finished' &&
|
|
463
|
+
step.index === intent.step &&
|
|
464
|
+
!doneSteps.has(step.index)
|
|
465
|
+
}
|
|
466
|
+
data-done={doneSteps.has(step.index)}
|
|
467
|
+
data-next={
|
|
468
|
+
nextStep?.index === step.index &&
|
|
469
|
+
!doneSteps.has(step.index)
|
|
470
|
+
}
|
|
471
|
+
>
|
|
472
|
+
<span className="hud-step-index">{step.index}.</span>
|
|
473
|
+
<span className="hud-step-main">
|
|
474
|
+
<span className="hud-step-title">{step.title}</span>
|
|
475
|
+
{((stepByStep &&
|
|
476
|
+
canRunNext &&
|
|
477
|
+
nextStep?.index === step.index) ||
|
|
478
|
+
(canComplete && step.index === intent.step)) && (
|
|
479
|
+
<button
|
|
480
|
+
className="hud-button hud-button-approve hud-run-step"
|
|
481
|
+
type="button"
|
|
482
|
+
onClick={() =>
|
|
483
|
+
canComplete && step.index === intent.step
|
|
484
|
+
? act('continue')
|
|
485
|
+
: act('invoke', {
|
|
486
|
+
step: step.index,
|
|
487
|
+
})
|
|
488
|
+
}
|
|
489
|
+
>
|
|
490
|
+
{canComplete && step.index === intent.step
|
|
491
|
+
? 'Complete'
|
|
492
|
+
: 'Run step'}
|
|
493
|
+
</button>
|
|
494
|
+
)}
|
|
495
|
+
</span>
|
|
496
|
+
</li>
|
|
497
|
+
))}
|
|
498
|
+
</ol>
|
|
499
|
+
)}
|
|
500
|
+
{intent.chain.length > 0 && (
|
|
501
|
+
<div className="hud-chain">
|
|
502
|
+
<button
|
|
503
|
+
className="hud-button"
|
|
504
|
+
type="button"
|
|
505
|
+
disabled={!previousDiff}
|
|
506
|
+
onClick={() =>
|
|
507
|
+
previousDiff && onNavigateDiff(sessionId, previousDiff.id)
|
|
508
|
+
}
|
|
509
|
+
>
|
|
510
|
+
Previous
|
|
511
|
+
</button>
|
|
512
|
+
<span>
|
|
513
|
+
Diff {chainIndex + 1} of {intent.chain.length}
|
|
514
|
+
</span>
|
|
515
|
+
<button
|
|
516
|
+
className="hud-button"
|
|
517
|
+
type="button"
|
|
518
|
+
disabled={!nextDiff}
|
|
519
|
+
onClick={() =>
|
|
520
|
+
nextDiff && onNavigateDiff(sessionId, nextDiff.id)
|
|
521
|
+
}
|
|
522
|
+
>
|
|
523
|
+
Next
|
|
524
|
+
</button>
|
|
525
|
+
</div>
|
|
526
|
+
)}
|
|
527
|
+
<MutationFold
|
|
528
|
+
hasContent={
|
|
529
|
+
previewing &&
|
|
530
|
+
(intent.files.length > 0 ||
|
|
531
|
+
(intent.createFolders ?? []).length > 0 ||
|
|
532
|
+
intent.creates.length > 0 ||
|
|
533
|
+
intent.deletes.length > 0 ||
|
|
534
|
+
addedFunctions.length > 0 ||
|
|
535
|
+
addedVariables.length > 0 ||
|
|
536
|
+
addedImports.length > 0 ||
|
|
537
|
+
changedFunctions.length > 0 ||
|
|
538
|
+
changedVariables.length > 0 ||
|
|
539
|
+
(intent.imports ?? []).length > 0)
|
|
540
|
+
}
|
|
541
|
+
>
|
|
542
|
+
{intent.files.length > 0 && (
|
|
543
|
+
<>
|
|
544
|
+
<div className="hud-section-title hud-section-title-edit">
|
|
545
|
+
Changed
|
|
546
|
+
</div>
|
|
547
|
+
<ul>
|
|
548
|
+
{intent.files.map((id) => (
|
|
549
|
+
<li className="hud-file-edit" key={id}>
|
|
550
|
+
{id}
|
|
551
|
+
</li>
|
|
552
|
+
))}
|
|
553
|
+
</ul>
|
|
554
|
+
</>
|
|
555
|
+
)}
|
|
556
|
+
{(intent.createFolders ?? []).length > 0 && (
|
|
557
|
+
<>
|
|
558
|
+
<div className="hud-section-title hud-section-title-add">
|
|
559
|
+
Added islands
|
|
560
|
+
</div>
|
|
561
|
+
<ul>
|
|
562
|
+
{intent.createFolders.map((id) => (
|
|
563
|
+
<li className="hud-file-add" key={id}>
|
|
564
|
+
{id}/
|
|
565
|
+
</li>
|
|
566
|
+
))}
|
|
567
|
+
</ul>
|
|
568
|
+
</>
|
|
569
|
+
)}
|
|
570
|
+
{intent.creates.length > 0 && (
|
|
571
|
+
<>
|
|
572
|
+
<div className="hud-section-title hud-section-title-add">
|
|
573
|
+
Added
|
|
574
|
+
</div>
|
|
575
|
+
<ul>
|
|
576
|
+
{intent.creates.map((id) => (
|
|
577
|
+
<li className="hud-file-add" key={id}>
|
|
578
|
+
{id}
|
|
579
|
+
</li>
|
|
580
|
+
))}
|
|
581
|
+
</ul>
|
|
582
|
+
</>
|
|
583
|
+
)}
|
|
584
|
+
{intent.deletes.length > 0 && (
|
|
585
|
+
<>
|
|
586
|
+
<div className="hud-section-title hud-section-title-remove">
|
|
587
|
+
Removed
|
|
588
|
+
</div>
|
|
589
|
+
<ul>
|
|
590
|
+
{intent.deletes.map((id) => (
|
|
591
|
+
<li className="hud-file-remove" key={id}>
|
|
592
|
+
{id}
|
|
593
|
+
</li>
|
|
594
|
+
))}
|
|
595
|
+
</ul>
|
|
596
|
+
</>
|
|
597
|
+
)}
|
|
598
|
+
<PanelList
|
|
599
|
+
title="Changed functions"
|
|
600
|
+
items={symbolLabels(changedFunctions)}
|
|
601
|
+
tone="edit"
|
|
602
|
+
/>
|
|
603
|
+
<PanelList
|
|
604
|
+
title="Added functions"
|
|
605
|
+
items={symbolLabels(addedFunctions)}
|
|
606
|
+
tone="add"
|
|
607
|
+
/>
|
|
608
|
+
<PanelList
|
|
609
|
+
title="Changed variables"
|
|
610
|
+
items={symbolLabels(changedVariables)}
|
|
611
|
+
tone="edit"
|
|
612
|
+
/>
|
|
613
|
+
<PanelList
|
|
614
|
+
title="Added variables"
|
|
615
|
+
items={symbolLabels(addedVariables)}
|
|
616
|
+
tone="add"
|
|
617
|
+
/>
|
|
618
|
+
{addedImports.length > 0 && (
|
|
619
|
+
<PanelList title="Imports" items={importLabels(addedImports)} />
|
|
620
|
+
)}
|
|
621
|
+
{addedImports.length === 0 && (intent.imports ?? []).length > 0 && (
|
|
622
|
+
<>
|
|
623
|
+
<div className="hud-section-title">Imports</div>
|
|
624
|
+
<ul>
|
|
625
|
+
{intent.imports.map((edge) => (
|
|
626
|
+
<li key={`${edge.from}->${edge.to}`}>
|
|
627
|
+
{edge.from.split('/').pop()} → {edge.to.split('/').pop()}
|
|
628
|
+
</li>
|
|
629
|
+
))}
|
|
630
|
+
</ul>
|
|
631
|
+
</>
|
|
632
|
+
)}
|
|
633
|
+
</MutationFold>
|
|
634
|
+
{planReady && (
|
|
635
|
+
<div className="hud-decide">
|
|
636
|
+
<button
|
|
637
|
+
className="hud-button hud-button-reject"
|
|
638
|
+
type="button"
|
|
639
|
+
onClick={() => act('stop')}
|
|
640
|
+
>
|
|
641
|
+
Stop
|
|
642
|
+
</button>
|
|
643
|
+
</div>
|
|
644
|
+
)}
|
|
645
|
+
{pending && (
|
|
646
|
+
<>
|
|
647
|
+
<label className="hud-instruction">
|
|
648
|
+
<span>Alternative instruction for the LLM</span>
|
|
649
|
+
<textarea
|
|
650
|
+
value={instruction}
|
|
651
|
+
maxLength={4000}
|
|
652
|
+
rows={3}
|
|
653
|
+
placeholder="Describe what should change in the next diff…"
|
|
654
|
+
onChange={(event) => setInstruction(event.target.value)}
|
|
655
|
+
/>
|
|
656
|
+
</label>
|
|
657
|
+
<div className="hud-decide">
|
|
658
|
+
{lastStep && (
|
|
659
|
+
<button
|
|
660
|
+
className="hud-button hud-button-approve"
|
|
661
|
+
type="button"
|
|
662
|
+
onClick={() => act('continue')}
|
|
663
|
+
>
|
|
664
|
+
Complete
|
|
665
|
+
</button>
|
|
666
|
+
)}
|
|
667
|
+
<button
|
|
668
|
+
className="hud-button hud-button-extend"
|
|
669
|
+
type="button"
|
|
670
|
+
disabled={!instruction.trim()}
|
|
671
|
+
onClick={() =>
|
|
672
|
+
act('instruct', { instruction })
|
|
673
|
+
}
|
|
674
|
+
>
|
|
675
|
+
Send instruction
|
|
676
|
+
</button>
|
|
677
|
+
<button
|
|
678
|
+
className="hud-button hud-button-reject"
|
|
679
|
+
type="button"
|
|
680
|
+
onClick={() => act('stop')}
|
|
681
|
+
>
|
|
682
|
+
Stop
|
|
683
|
+
</button>
|
|
684
|
+
</div>
|
|
685
|
+
</>
|
|
686
|
+
)}
|
|
687
|
+
</>
|
|
688
|
+
)}
|
|
689
|
+
</>
|
|
690
|
+
)}
|
|
691
|
+
</aside>
|
|
692
|
+
)
|
|
693
|
+
}
|
|
694
|
+
|
|
126
695
|
type HUDProps = {
|
|
127
696
|
graph: CodebaseGraph
|
|
697
|
+
layout: WorldLayout
|
|
128
698
|
mode: ViewMode
|
|
129
699
|
locked: boolean
|
|
130
700
|
selectedId: string | null
|
|
131
701
|
selectedTick?: number
|
|
702
|
+
inspectTick?: number
|
|
132
703
|
selectedFolder?: string | null
|
|
704
|
+
landAt: [number, number]
|
|
133
705
|
aimedRelation: AimedRelation | null
|
|
706
|
+
aimedFileId?: string | null
|
|
134
707
|
currentFolder: string
|
|
135
708
|
intent: AgentIntent
|
|
709
|
+
intents?: AgentIntent[]
|
|
710
|
+
focusedSessionId?: string | null
|
|
711
|
+
onFocusSession?: (sessionId: string) => void
|
|
136
712
|
onWorkflowAction: (
|
|
713
|
+
sessionId: string,
|
|
137
714
|
action: WorkflowAction,
|
|
138
|
-
options?: { instruction?: string; step?: number },
|
|
715
|
+
options?: { instruction?: string; step?: number; stepByStep?: boolean },
|
|
139
716
|
) => void
|
|
140
|
-
onNavigateDiff: (diffId: string) => void
|
|
717
|
+
onNavigateDiff: (sessionId: string, diffId: string) => void
|
|
141
718
|
onOpenMap: () => void
|
|
142
719
|
onWalk: () => void
|
|
143
720
|
followLook: boolean
|
|
144
721
|
onToggleFollowLook: () => void
|
|
145
722
|
importedBy: boolean
|
|
146
723
|
onToggleImportedBy: () => void
|
|
724
|
+
changePathsOnly?: boolean
|
|
725
|
+
hasChangeSet?: boolean
|
|
726
|
+
onToggleChangePathsOnly?: () => void
|
|
147
727
|
naming?: boolean
|
|
148
728
|
namingIsland?: boolean
|
|
149
729
|
onCommitIslandName?: (name: string) => void
|
|
@@ -164,18 +744,29 @@ type HUDProps = {
|
|
|
164
744
|
onMapAddFile?: (folderPath: string) => void
|
|
165
745
|
onMapAddFolder?: (folderPath: string) => void
|
|
166
746
|
onInspectFile?: (fileId: string) => void
|
|
747
|
+
onInspectBlock?: (fileId: string) => void
|
|
748
|
+
plannedIds?: string[]
|
|
749
|
+
createdIds?: string[]
|
|
750
|
+
deletedIds?: string[]
|
|
167
751
|
}
|
|
168
752
|
|
|
169
753
|
export function HUD({
|
|
170
754
|
graph,
|
|
755
|
+
layout,
|
|
171
756
|
mode,
|
|
172
757
|
locked,
|
|
173
758
|
selectedId,
|
|
174
759
|
selectedTick = 0,
|
|
760
|
+
inspectTick = 0,
|
|
175
761
|
selectedFolder = null,
|
|
762
|
+
landAt,
|
|
176
763
|
aimedRelation,
|
|
764
|
+
aimedFileId = null,
|
|
177
765
|
currentFolder,
|
|
178
766
|
intent,
|
|
767
|
+
intents,
|
|
768
|
+
focusedSessionId = null,
|
|
769
|
+
onFocusSession,
|
|
179
770
|
onWorkflowAction,
|
|
180
771
|
onNavigateDiff,
|
|
181
772
|
onOpenMap,
|
|
@@ -184,6 +775,9 @@ export function HUD({
|
|
|
184
775
|
onToggleFollowLook,
|
|
185
776
|
importedBy,
|
|
186
777
|
onToggleImportedBy,
|
|
778
|
+
changePathsOnly = false,
|
|
779
|
+
hasChangeSet = false,
|
|
780
|
+
onToggleChangePathsOnly,
|
|
187
781
|
naming = false,
|
|
188
782
|
namingIsland = false,
|
|
189
783
|
onCommitIslandName,
|
|
@@ -200,6 +794,10 @@ export function HUD({
|
|
|
200
794
|
onMapAddFile,
|
|
201
795
|
onMapAddFolder,
|
|
202
796
|
onInspectFile,
|
|
797
|
+
onInspectBlock,
|
|
798
|
+
plannedIds = [],
|
|
799
|
+
createdIds = [],
|
|
800
|
+
deletedIds = [],
|
|
203
801
|
}: HUDProps) {
|
|
204
802
|
const selected = graph.files.find((file) => file.id === selectedId)
|
|
205
803
|
const selectedFolderNode = graph.folders.find(
|
|
@@ -217,34 +815,26 @@ export function HUD({
|
|
|
217
815
|
? graph.files.filter((file) => file.imports.includes(selected.id))
|
|
218
816
|
: []
|
|
219
817
|
const mapping = mode === 'map'
|
|
220
|
-
const
|
|
818
|
+
const sessions = (intents ?? [intent]).filter(
|
|
819
|
+
(item) => item.sessionId && isReviewingIntent(item.status),
|
|
820
|
+
)
|
|
821
|
+
const canStop = canStopSession(intent)
|
|
221
822
|
const [walkIntro, setWalkIntro] = useState(false)
|
|
222
823
|
const walkIntroSeen = useRef(false)
|
|
223
|
-
const [instruction, setInstruction] = useState('')
|
|
224
824
|
const [infoVisible, setInfoVisible] = useState(false)
|
|
225
|
-
const
|
|
226
|
-
const
|
|
227
|
-
const
|
|
228
|
-
const
|
|
229
|
-
const
|
|
230
|
-
|
|
231
|
-
|
|
825
|
+
const [infoMinimized, setInfoMinimized] = useState(false)
|
|
826
|
+
const [thumbnailVisible, setThumbnailVisible] = useState(true)
|
|
827
|
+
const [thumbnailMinimized, setThumbnailMinimized] = useState(false)
|
|
828
|
+
const infoPanelRef = useRef<HTMLDivElement>(null)
|
|
829
|
+
const creatingBlueprint = sessions.some(
|
|
830
|
+
(item) => item.creationMode || item.status === 'blueprint',
|
|
831
|
+
)
|
|
232
832
|
const previewing = intent.preview
|
|
233
|
-
const reviewing = isReviewingIntent(intent.status) || intent.chain.length > 0
|
|
234
|
-
const chainIndex = intent.chainIndex ?? 0
|
|
235
|
-
const previousDiff = intent.chain[chainIndex - 1]
|
|
236
|
-
const nextDiff = intent.chain[chainIndex + 1]
|
|
237
|
-
const stepLabel =
|
|
238
|
-
intent.step && intent.steps?.length > 0
|
|
239
|
-
? `Step ${intent.step} of ${intent.steps.length}`
|
|
240
|
-
: 'Patch'
|
|
241
|
-
const lastStep =
|
|
242
|
-
typeof intent.step === 'number' &&
|
|
243
|
-
intent.steps.length > 0 &&
|
|
244
|
-
intent.step >= intent.steps.length
|
|
245
833
|
const addedFunctions = intent.addedFunctions ?? []
|
|
246
834
|
const addedVariables = intent.addedVariables ?? []
|
|
247
835
|
const addedImports = intent.addedImports ?? []
|
|
836
|
+
const changedFunctions = intent.changedFunctions ?? []
|
|
837
|
+
const changedVariables = intent.changedVariables ?? []
|
|
248
838
|
const selectedAddedFunctions = selected
|
|
249
839
|
? addedFunctions.filter((item) => item.file === selected.id)
|
|
250
840
|
: []
|
|
@@ -254,6 +844,12 @@ export function HUD({
|
|
|
254
844
|
const selectedAddedImports = selected
|
|
255
845
|
? addedImports.filter((item) => item.file === selected.id)
|
|
256
846
|
: []
|
|
847
|
+
const selectedChangedFunctions = selected
|
|
848
|
+
? changedFunctions.filter((item) => item.file === selected.id)
|
|
849
|
+
: []
|
|
850
|
+
const selectedChangedVariables = selected
|
|
851
|
+
? changedVariables.filter((item) => item.file === selected.id)
|
|
852
|
+
: []
|
|
257
853
|
const selectedClasses = selected
|
|
258
854
|
? selected.symbols.filter((symbol) => symbol.kind === 'class')
|
|
259
855
|
: []
|
|
@@ -263,6 +859,26 @@ export function HUD({
|
|
|
263
859
|
const selectedVariables = selected
|
|
264
860
|
? selected.symbols.filter((symbol) => symbol.kind === 'variable')
|
|
265
861
|
: []
|
|
862
|
+
const functionChange = new Map<string, 'add' | 'edit'>([
|
|
863
|
+
...selectedChangedFunctions.map(
|
|
864
|
+
(item) => [item.name, 'edit'] as const,
|
|
865
|
+
),
|
|
866
|
+
...selectedAddedFunctions.map((item) => [item.name, 'add'] as const),
|
|
867
|
+
])
|
|
868
|
+
const variableChange = new Map<string, 'add' | 'edit'>([
|
|
869
|
+
...selectedChangedVariables.map(
|
|
870
|
+
(item) => [item.name, 'edit'] as const,
|
|
871
|
+
),
|
|
872
|
+
...selectedAddedVariables.map((item) => [item.name, 'add'] as const),
|
|
873
|
+
])
|
|
874
|
+
const extraAddedFunctions = extraAddedSymbols(
|
|
875
|
+
selectedFunctions,
|
|
876
|
+
selectedAddedFunctions,
|
|
877
|
+
)
|
|
878
|
+
const extraAddedVariables = extraAddedSymbols(
|
|
879
|
+
selectedVariables,
|
|
880
|
+
selectedAddedVariables,
|
|
881
|
+
)
|
|
266
882
|
const selectedBlueprintFunctions = selected
|
|
267
883
|
? blueprintFunctions.filter((item) => item.file === selected.id)
|
|
268
884
|
: []
|
|
@@ -280,38 +896,11 @@ export function HUD({
|
|
|
280
896
|
)
|
|
281
897
|
const canEditBlueprint =
|
|
282
898
|
creatingBlueprint && Boolean(selected) && !selected?.id.startsWith('draft:')
|
|
283
|
-
const doneSteps = new Set(
|
|
284
|
-
intent.status === 'finished'
|
|
285
|
-
? intent.steps.map((step) => step.index)
|
|
286
|
-
: intent.chain
|
|
287
|
-
.filter(
|
|
288
|
-
(entry) =>
|
|
289
|
-
entry.status === 'applied' || entry.status === 'extended',
|
|
290
|
-
)
|
|
291
|
-
.map((entry) => entry.step),
|
|
292
|
-
)
|
|
293
|
-
if (intent.status === 'approved' && typeof intent.step === 'number') {
|
|
294
|
-
doneSteps.add(intent.step)
|
|
295
|
-
}
|
|
296
|
-
if (pending && typeof intent.step === 'number' && !lastStep) {
|
|
297
|
-
doneSteps.add(intent.step)
|
|
298
|
-
}
|
|
299
|
-
const nextStep =
|
|
300
|
-
intent.status === 'finished'
|
|
301
|
-
? null
|
|
302
|
-
: (intent.steps.find((step) => !doneSteps.has(step.index)) ?? null)
|
|
303
|
-
const canRunNext =
|
|
304
|
-
Boolean(nextStep) && (planReady || pending) && !working
|
|
305
|
-
const canComplete = pending && lastStep
|
|
306
899
|
const canInspectFile = (fileId: string, userCreated = false) =>
|
|
307
900
|
Boolean(onInspectFile) &&
|
|
308
901
|
!fileId.startsWith('draft:') &&
|
|
309
902
|
!(previewing && (intent.deletes ?? []).includes(fileId)) &&
|
|
310
903
|
(!userCreated || (intent.creates ?? []).includes(fileId))
|
|
311
|
-
const panelDone =
|
|
312
|
-
intent.status === 'finished' ||
|
|
313
|
-
intent.status === 'approved' ||
|
|
314
|
-
doneSteps.size > 0
|
|
315
904
|
|
|
316
905
|
useEffect(() => {
|
|
317
906
|
if (mode !== 'walk') {
|
|
@@ -332,22 +921,38 @@ export function HUD({
|
|
|
332
921
|
return () => window.clearTimeout(timer)
|
|
333
922
|
}, [locked, mode, naming])
|
|
334
923
|
|
|
335
|
-
useEffect(() => {
|
|
336
|
-
setInstruction('')
|
|
337
|
-
}, [intent.diffId])
|
|
338
|
-
|
|
339
924
|
useEffect(() => {
|
|
340
925
|
infoPanelRef.current?.scrollTo({ top: 0 })
|
|
341
926
|
}, [selectedId, selectedFolder])
|
|
342
927
|
|
|
343
928
|
useEffect(() => {
|
|
929
|
+
if (mode === 'walk') return
|
|
344
930
|
if (selectedId) setInfoVisible(true)
|
|
345
|
-
}, [selectedId, selectedTick])
|
|
931
|
+
}, [mode, selectedId, selectedTick])
|
|
932
|
+
|
|
933
|
+
useEffect(() => {
|
|
934
|
+
if (inspectTick > 0) {
|
|
935
|
+
setInfoVisible(true)
|
|
936
|
+
setInfoMinimized(false)
|
|
937
|
+
}
|
|
938
|
+
}, [inspectTick])
|
|
346
939
|
|
|
347
940
|
useEffect(() => {
|
|
348
941
|
if (selectedFolder) setInfoVisible(true)
|
|
349
942
|
}, [selectedFolder])
|
|
350
943
|
|
|
944
|
+
useEffect(() => {
|
|
945
|
+
if (!locked) return
|
|
946
|
+
setInfoVisible(false)
|
|
947
|
+
setInfoMinimized(false)
|
|
948
|
+
}, [locked])
|
|
949
|
+
|
|
950
|
+
const infoOpen = infoVisible && Boolean(selected || selectedFolderNode)
|
|
951
|
+
|
|
952
|
+
useEffect(() => {
|
|
953
|
+
if (infoOpen) document.exitPointerLock()
|
|
954
|
+
}, [infoOpen])
|
|
955
|
+
|
|
351
956
|
useEffect(() => {
|
|
352
957
|
const onKey = (event: KeyboardEvent) => {
|
|
353
958
|
if (event.repeat || event.code !== 'KeyI') return
|
|
@@ -362,11 +967,46 @@ export function HUD({
|
|
|
362
967
|
return
|
|
363
968
|
}
|
|
364
969
|
event.preventDefault()
|
|
365
|
-
|
|
970
|
+
if (infoVisible) {
|
|
971
|
+
setInfoVisible(false)
|
|
972
|
+
setInfoMinimized(false)
|
|
973
|
+
return
|
|
974
|
+
}
|
|
975
|
+
const blockId = aimedFileId ?? selectedId
|
|
976
|
+
if (mode === 'walk') {
|
|
977
|
+
if (!blockId) return
|
|
978
|
+
onInspectBlock?.(blockId)
|
|
979
|
+
}
|
|
980
|
+
setInfoMinimized(false)
|
|
981
|
+
setInfoVisible(true)
|
|
982
|
+
}
|
|
983
|
+
window.addEventListener('keydown', onKey)
|
|
984
|
+
return () => window.removeEventListener('keydown', onKey)
|
|
985
|
+
}, [aimedFileId, infoVisible, mode, onInspectBlock, selectedId])
|
|
986
|
+
|
|
987
|
+
useEffect(() => {
|
|
988
|
+
if (!mapping) return
|
|
989
|
+
const onKey = (event: KeyboardEvent) => {
|
|
990
|
+
if (event.repeat || event.code !== 'KeyT') return
|
|
991
|
+
const target = event.target
|
|
992
|
+
if (
|
|
993
|
+
target instanceof HTMLElement &&
|
|
994
|
+
(target.tagName === 'TEXTAREA' ||
|
|
995
|
+
target.tagName === 'INPUT' ||
|
|
996
|
+
target.tagName === 'SELECT' ||
|
|
997
|
+
target.isContentEditable)
|
|
998
|
+
) {
|
|
999
|
+
return
|
|
1000
|
+
}
|
|
1001
|
+
event.preventDefault()
|
|
1002
|
+
setThumbnailVisible((visible) => {
|
|
1003
|
+
if (!visible) setThumbnailMinimized(false)
|
|
1004
|
+
return !visible
|
|
1005
|
+
})
|
|
366
1006
|
}
|
|
367
1007
|
window.addEventListener('keydown', onKey)
|
|
368
1008
|
return () => window.removeEventListener('keydown', onKey)
|
|
369
|
-
}, [])
|
|
1009
|
+
}, [mapping])
|
|
370
1010
|
|
|
371
1011
|
useEffect(() => {
|
|
372
1012
|
if (!infoVisible || (!selectedId && !selectedFolder)) return
|
|
@@ -400,8 +1040,9 @@ export function HUD({
|
|
|
400
1040
|
<div className="hud-gate-card">
|
|
401
1041
|
<h1>Walk</h1>
|
|
402
1042
|
<p>
|
|
403
|
-
Click to look around.
|
|
404
|
-
<kbd>M</kbd> again to return
|
|
1043
|
+
Click to look around. Double-click to release the mouse. Press{' '}
|
|
1044
|
+
<kbd>M</kbd> to open the map, press <kbd>M</kbd> again to return
|
|
1045
|
+
here.
|
|
405
1046
|
</p>
|
|
406
1047
|
<p>
|
|
407
1048
|
<kbd>W</kbd> <kbd>A</kbd> <kbd>S</kbd> <kbd>D</kbd> walk,{' '}
|
|
@@ -411,7 +1052,7 @@ export function HUD({
|
|
|
411
1052
|
, <kbd>Space</kbd> place a file, <kbd>B</kbd> place an island
|
|
412
1053
|
</>
|
|
413
1054
|
) : null}
|
|
414
|
-
, click a block for info.
|
|
1055
|
+
, double-click a block or press <kbd>I</kbd> for info.
|
|
415
1056
|
</p>
|
|
416
1057
|
</div>
|
|
417
1058
|
</div>
|
|
@@ -455,313 +1096,57 @@ export function HUD({
|
|
|
455
1096
|
>
|
|
456
1097
|
Walk
|
|
457
1098
|
</button>
|
|
1099
|
+
{canStop && (
|
|
1100
|
+
<button
|
|
1101
|
+
className="hud-button hud-button-reject"
|
|
1102
|
+
type="button"
|
|
1103
|
+
aria-label="Stop LLM session"
|
|
1104
|
+
onClick={() =>
|
|
1105
|
+
intent.sessionId && onWorkflowAction(intent.sessionId, 'stop')
|
|
1106
|
+
}
|
|
1107
|
+
>
|
|
1108
|
+
Stop
|
|
1109
|
+
</button>
|
|
1110
|
+
)}
|
|
458
1111
|
</div>
|
|
459
1112
|
{selected && <div className="hud-chip">{selected.path}</div>}
|
|
460
1113
|
</div>
|
|
461
1114
|
|
|
462
|
-
{
|
|
463
|
-
<
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
<>
|
|
476
|
-
<p>
|
|
477
|
-
{intent.canEnterBlueprint
|
|
478
|
-
? 'Place files and islands for this chat, then send them as a blueprint for the LLM?'
|
|
479
|
-
: `Blueprint edit mode is active in another chat (${intent.blueprintSessionId}). Finish or stop it before starting here.`}
|
|
480
|
-
</p>
|
|
481
|
-
<div className="hud-decide">
|
|
482
|
-
<button
|
|
483
|
-
className="hud-button hud-button-approve"
|
|
484
|
-
type="button"
|
|
485
|
-
disabled={!intent.canEnterBlueprint}
|
|
486
|
-
onClick={() => onWorkflowAction('blueprint_yes')}
|
|
487
|
-
>
|
|
488
|
-
Yes
|
|
489
|
-
</button>
|
|
490
|
-
<button
|
|
491
|
-
className="hud-button"
|
|
492
|
-
type="button"
|
|
493
|
-
onClick={() => onWorkflowAction('blueprint_no')}
|
|
494
|
-
>
|
|
495
|
-
No
|
|
496
|
-
</button>
|
|
497
|
-
<button
|
|
498
|
-
className="hud-button hud-button-reject"
|
|
499
|
-
type="button"
|
|
500
|
-
onClick={() => onWorkflowAction('stop')}
|
|
501
|
-
>
|
|
502
|
-
Stop
|
|
503
|
-
</button>
|
|
504
|
-
</div>
|
|
505
|
-
</>
|
|
506
|
-
) : creatingBlueprint ? (
|
|
507
|
-
<>
|
|
508
|
-
<p>
|
|
509
|
-
Walk the map, press <kbd>Space</kbd> for a file and{' '}
|
|
510
|
-
<kbd>B</kbd> for an island. Send the blueprint when the layout
|
|
511
|
-
is ready.
|
|
512
|
-
</p>
|
|
513
|
-
<div className="hud-decide">
|
|
514
|
-
<button
|
|
515
|
-
className="hud-button hud-button-approve"
|
|
516
|
-
type="button"
|
|
517
|
-
disabled={naming}
|
|
518
|
-
onClick={() => onWorkflowAction('blueprint_send')}
|
|
519
|
-
>
|
|
520
|
-
Send blueprint
|
|
521
|
-
</button>
|
|
522
|
-
<button
|
|
523
|
-
className="hud-button hud-button-reject"
|
|
524
|
-
type="button"
|
|
525
|
-
onClick={() => onWorkflowAction('stop')}
|
|
526
|
-
>
|
|
527
|
-
Stop
|
|
528
|
-
</button>
|
|
529
|
-
</div>
|
|
530
|
-
</>
|
|
531
|
-
) : intent.status === 'finished' ? (
|
|
532
|
-
<p>All plan steps were applied.</p>
|
|
533
|
-
) : preparing ? (
|
|
534
|
-
<div className="hud-working">
|
|
535
|
-
<span className="hud-spinner" aria-hidden="true" />
|
|
536
|
-
<span>LLM preparing…</span>
|
|
537
|
-
</div>
|
|
538
|
-
) : working ? (
|
|
539
|
-
<div className="hud-working">
|
|
540
|
-
<span className="hud-spinner" aria-hidden="true" />
|
|
541
|
-
<span>
|
|
542
|
-
{intent.status === 'replanning'
|
|
543
|
-
? 'Updating the remaining plan from your instruction…'
|
|
544
|
-
: `Implementing ${stepLabel.toLowerCase()}…`}
|
|
545
|
-
</span>
|
|
546
|
-
</div>
|
|
547
|
-
) : (
|
|
548
|
-
<p>
|
|
549
|
-
{stepLabel}
|
|
550
|
-
{intent.reason ? ` · ${intent.reason}` : ''}
|
|
551
|
-
</p>
|
|
552
|
-
)}
|
|
553
|
-
{!askingBlueprint && !creatingBlueprint && (
|
|
554
|
-
<>
|
|
555
|
-
{intent.steps?.length > 0 && (
|
|
556
|
-
<ol className="hud-steps">
|
|
557
|
-
{intent.steps.map((step) => (
|
|
558
|
-
<li
|
|
559
|
-
key={step.index}
|
|
560
|
-
data-current={
|
|
561
|
-
intent.status !== 'finished' &&
|
|
562
|
-
step.index === intent.step &&
|
|
563
|
-
!doneSteps.has(step.index)
|
|
564
|
-
}
|
|
565
|
-
data-done={doneSteps.has(step.index)}
|
|
566
|
-
data-next={
|
|
567
|
-
nextStep?.index === step.index && !doneSteps.has(step.index)
|
|
568
|
-
}
|
|
569
|
-
>
|
|
570
|
-
<span className="hud-step-index">{step.index}.</span>
|
|
571
|
-
<span className="hud-step-main">
|
|
572
|
-
<span className="hud-step-title">{step.title}</span>
|
|
573
|
-
{((canRunNext && nextStep?.index === step.index) ||
|
|
574
|
-
(canComplete && step.index === intent.step)) && (
|
|
575
|
-
<button
|
|
576
|
-
className="hud-button hud-button-approve hud-run-step"
|
|
577
|
-
type="button"
|
|
578
|
-
onClick={() =>
|
|
579
|
-
canComplete && step.index === intent.step
|
|
580
|
-
? onWorkflowAction('continue')
|
|
581
|
-
: onWorkflowAction('invoke', {
|
|
582
|
-
step: step.index,
|
|
583
|
-
})
|
|
584
|
-
}
|
|
585
|
-
>
|
|
586
|
-
{canComplete && step.index === intent.step
|
|
587
|
-
? 'Complete'
|
|
588
|
-
: 'Run step'}
|
|
589
|
-
</button>
|
|
590
|
-
)}
|
|
591
|
-
</span>
|
|
592
|
-
</li>
|
|
593
|
-
))}
|
|
594
|
-
</ol>
|
|
595
|
-
)}
|
|
596
|
-
{intent.chain.length > 0 && (
|
|
597
|
-
<div className="hud-chain">
|
|
598
|
-
<button
|
|
599
|
-
className="hud-button"
|
|
600
|
-
type="button"
|
|
601
|
-
disabled={!previousDiff}
|
|
602
|
-
onClick={() => previousDiff && onNavigateDiff(previousDiff.id)}
|
|
603
|
-
>
|
|
604
|
-
Previous
|
|
605
|
-
</button>
|
|
606
|
-
<span>
|
|
607
|
-
Diff {chainIndex + 1} of {intent.chain.length}
|
|
608
|
-
</span>
|
|
609
|
-
<button
|
|
610
|
-
className="hud-button"
|
|
611
|
-
type="button"
|
|
612
|
-
disabled={!nextDiff}
|
|
613
|
-
onClick={() => nextDiff && onNavigateDiff(nextDiff.id)}
|
|
614
|
-
>
|
|
615
|
-
Next
|
|
616
|
-
</button>
|
|
617
|
-
</div>
|
|
618
|
-
)}
|
|
619
|
-
<MutationFold
|
|
620
|
-
hasContent={
|
|
621
|
-
previewing &&
|
|
622
|
-
(intent.files.length > 0 ||
|
|
623
|
-
(intent.createFolders ?? []).length > 0 ||
|
|
624
|
-
intent.creates.length > 0 ||
|
|
625
|
-
intent.deletes.length > 0 ||
|
|
626
|
-
addedFunctions.length > 0 ||
|
|
627
|
-
addedVariables.length > 0 ||
|
|
628
|
-
addedImports.length > 0 ||
|
|
629
|
-
(intent.imports ?? []).length > 0)
|
|
630
|
-
}
|
|
631
|
-
>
|
|
632
|
-
{intent.files.length > 0 && (
|
|
633
|
-
<>
|
|
634
|
-
<div className="hud-section-title hud-section-title-edit">Changed</div>
|
|
635
|
-
<ul>
|
|
636
|
-
{intent.files.map((id) => (
|
|
637
|
-
<li className="hud-file-edit" key={id}>
|
|
638
|
-
{id}
|
|
639
|
-
</li>
|
|
640
|
-
))}
|
|
641
|
-
</ul>
|
|
642
|
-
</>
|
|
643
|
-
)}
|
|
644
|
-
{(intent.createFolders ?? []).length > 0 && (
|
|
645
|
-
<>
|
|
646
|
-
<div className="hud-section-title hud-section-title-add">Added islands</div>
|
|
647
|
-
<ul>
|
|
648
|
-
{intent.createFolders.map((id) => (
|
|
649
|
-
<li className="hud-file-add" key={id}>
|
|
650
|
-
{id}/
|
|
651
|
-
</li>
|
|
652
|
-
))}
|
|
653
|
-
</ul>
|
|
654
|
-
</>
|
|
655
|
-
)}
|
|
656
|
-
{intent.creates.length > 0 && (
|
|
657
|
-
<>
|
|
658
|
-
<div className="hud-section-title hud-section-title-add">Added</div>
|
|
659
|
-
<ul>
|
|
660
|
-
{intent.creates.map((id) => (
|
|
661
|
-
<li className="hud-file-add" key={id}>
|
|
662
|
-
{id}
|
|
663
|
-
</li>
|
|
664
|
-
))}
|
|
665
|
-
</ul>
|
|
666
|
-
</>
|
|
667
|
-
)}
|
|
668
|
-
{intent.deletes.length > 0 && (
|
|
669
|
-
<>
|
|
670
|
-
<div className="hud-section-title hud-section-title-remove">Removed</div>
|
|
671
|
-
<ul>
|
|
672
|
-
{intent.deletes.map((id) => (
|
|
673
|
-
<li className="hud-file-remove" key={id}>
|
|
674
|
-
{id}
|
|
675
|
-
</li>
|
|
676
|
-
))}
|
|
677
|
-
</ul>
|
|
678
|
-
</>
|
|
679
|
-
)}
|
|
680
|
-
<PanelList
|
|
681
|
-
title="Functions"
|
|
682
|
-
items={symbolLabels(addedFunctions)}
|
|
1115
|
+
{sessions.length > 0 && (
|
|
1116
|
+
<div className="hud-left-stack">
|
|
1117
|
+
{sessions.map((session) => (
|
|
1118
|
+
<SessionPanel
|
|
1119
|
+
key={session.sessionId}
|
|
1120
|
+
intent={session}
|
|
1121
|
+
focused={session.sessionId === (focusedSessionId ?? intent.sessionId)}
|
|
1122
|
+
naming={naming}
|
|
1123
|
+
onFocus={() => {
|
|
1124
|
+
if (session.sessionId) onFocusSession?.(session.sessionId)
|
|
1125
|
+
}}
|
|
1126
|
+
onWorkflowAction={onWorkflowAction}
|
|
1127
|
+
onNavigateDiff={onNavigateDiff}
|
|
683
1128
|
/>
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
items={symbolLabels(addedVariables)}
|
|
687
|
-
/>
|
|
688
|
-
{addedImports.length > 0 && (
|
|
689
|
-
<PanelList title="Imports" items={importLabels(addedImports)} />
|
|
690
|
-
)}
|
|
691
|
-
{addedImports.length === 0 && (intent.imports ?? []).length > 0 && (
|
|
692
|
-
<>
|
|
693
|
-
<div className="hud-section-title">Imports</div>
|
|
694
|
-
<ul>
|
|
695
|
-
{intent.imports.map((edge) => (
|
|
696
|
-
<li key={`${edge.from}->${edge.to}`}>
|
|
697
|
-
{edge.from.split('/').pop()} → {edge.to.split('/').pop()}
|
|
698
|
-
</li>
|
|
699
|
-
))}
|
|
700
|
-
</ul>
|
|
701
|
-
</>
|
|
702
|
-
)}
|
|
703
|
-
</MutationFold>
|
|
704
|
-
{(planReady || preparing) && (
|
|
705
|
-
<div className="hud-decide">
|
|
706
|
-
<button
|
|
707
|
-
className="hud-button hud-button-reject"
|
|
708
|
-
type="button"
|
|
709
|
-
onClick={() => onWorkflowAction('stop')}
|
|
710
|
-
>
|
|
711
|
-
Stop
|
|
712
|
-
</button>
|
|
713
|
-
</div>
|
|
714
|
-
)}
|
|
715
|
-
{pending && (
|
|
716
|
-
<>
|
|
717
|
-
<label className="hud-instruction">
|
|
718
|
-
<span>Alternative instruction for the LLM</span>
|
|
719
|
-
<textarea
|
|
720
|
-
value={instruction}
|
|
721
|
-
maxLength={4000}
|
|
722
|
-
rows={3}
|
|
723
|
-
placeholder="Describe what should change in the next diff…"
|
|
724
|
-
onChange={(event) => setInstruction(event.target.value)}
|
|
725
|
-
/>
|
|
726
|
-
</label>
|
|
727
|
-
<div className="hud-decide">
|
|
728
|
-
{lastStep && (
|
|
729
|
-
<button
|
|
730
|
-
className="hud-button hud-button-approve"
|
|
731
|
-
type="button"
|
|
732
|
-
onClick={() => onWorkflowAction('continue')}
|
|
733
|
-
>
|
|
734
|
-
Complete
|
|
735
|
-
</button>
|
|
736
|
-
)}
|
|
737
|
-
<button
|
|
738
|
-
className="hud-button hud-button-extend"
|
|
739
|
-
type="button"
|
|
740
|
-
disabled={!instruction.trim()}
|
|
741
|
-
onClick={() =>
|
|
742
|
-
onWorkflowAction('instruct', { instruction })
|
|
743
|
-
}
|
|
744
|
-
>
|
|
745
|
-
Send instruction
|
|
746
|
-
</button>
|
|
747
|
-
<button
|
|
748
|
-
className="hud-button hud-button-reject"
|
|
749
|
-
type="button"
|
|
750
|
-
onClick={() => onWorkflowAction('stop')}
|
|
751
|
-
>
|
|
752
|
-
Stop
|
|
753
|
-
</button>
|
|
754
|
-
</div>
|
|
755
|
-
</>
|
|
756
|
-
)}
|
|
757
|
-
</>
|
|
758
|
-
)}
|
|
759
|
-
</aside>
|
|
1129
|
+
))}
|
|
1130
|
+
</div>
|
|
760
1131
|
)}
|
|
761
1132
|
|
|
1133
|
+
<div className="hud-right-stack">
|
|
762
1134
|
{selected && infoVisible && (
|
|
763
|
-
<aside
|
|
764
|
-
|
|
1135
|
+
<aside
|
|
1136
|
+
className="hud-panel hud-panel-info"
|
|
1137
|
+
data-minimized={infoMinimized}
|
|
1138
|
+
>
|
|
1139
|
+
<PanelChrome
|
|
1140
|
+
title={selected.name}
|
|
1141
|
+
minimized={infoMinimized}
|
|
1142
|
+
onMinimize={() => setInfoMinimized((current) => !current)}
|
|
1143
|
+
onClose={() => {
|
|
1144
|
+
setInfoVisible(false)
|
|
1145
|
+
setInfoMinimized(false)
|
|
1146
|
+
}}
|
|
1147
|
+
/>
|
|
1148
|
+
{!infoMinimized && (
|
|
1149
|
+
<div ref={infoPanelRef} className="hud-panel-body">
|
|
765
1150
|
<p className="path">{selected.path}</p>
|
|
766
1151
|
<p>
|
|
767
1152
|
{selected.lines} lines · {selected.language}
|
|
@@ -775,13 +1160,45 @@ export function HUD({
|
|
|
775
1160
|
Inspect file
|
|
776
1161
|
</button>
|
|
777
1162
|
)}
|
|
1163
|
+
{previewing &&
|
|
1164
|
+
(selectedChangedFunctions.length > 0 ||
|
|
1165
|
+
selectedAddedFunctions.length > 0 ||
|
|
1166
|
+
selectedChangedVariables.length > 0 ||
|
|
1167
|
+
selectedAddedVariables.length > 0 ||
|
|
1168
|
+
selectedAddedImports.length > 0) && (
|
|
1169
|
+
<>
|
|
1170
|
+
<div className="hud-section-title hud-section-title-edit">
|
|
1171
|
+
LLM changes
|
|
1172
|
+
</div>
|
|
1173
|
+
<PatchSymbolChanges
|
|
1174
|
+
title="Functions"
|
|
1175
|
+
added={selectedAddedFunctions}
|
|
1176
|
+
changed={selectedChangedFunctions}
|
|
1177
|
+
/>
|
|
1178
|
+
<PatchSymbolChanges
|
|
1179
|
+
title="Vars"
|
|
1180
|
+
added={selectedAddedVariables}
|
|
1181
|
+
changed={selectedChangedVariables}
|
|
1182
|
+
/>
|
|
1183
|
+
<PanelList
|
|
1184
|
+
title="Imports"
|
|
1185
|
+
items={importLabels(selectedAddedImports)}
|
|
1186
|
+
tone="add"
|
|
1187
|
+
/>
|
|
1188
|
+
</>
|
|
1189
|
+
)}
|
|
778
1190
|
{selectedClasses.length > 0 && (
|
|
779
1191
|
<>
|
|
780
1192
|
<div className="hud-section-title">Classes</div>
|
|
781
1193
|
<ul>
|
|
782
1194
|
{selectedClasses.map((symbol) => (
|
|
783
1195
|
<li key={`class-${symbol.name}`}>
|
|
784
|
-
<span
|
|
1196
|
+
<span
|
|
1197
|
+
className={
|
|
1198
|
+
symbolChangeClass(functionChange.get(symbol.name)) ??
|
|
1199
|
+
(symbol.intended ? 'hud-intended' : undefined)
|
|
1200
|
+
}
|
|
1201
|
+
>
|
|
785
1202
|
{symbol.name}
|
|
786
1203
|
</span>
|
|
787
1204
|
</li>
|
|
@@ -791,13 +1208,19 @@ export function HUD({
|
|
|
791
1208
|
)}
|
|
792
1209
|
<div className="hud-section-title">Functions</div>
|
|
793
1210
|
{selectedFunctions.length === 0 &&
|
|
1211
|
+
extraAddedFunctions.length === 0 &&
|
|
794
1212
|
selectedBlueprintFunctions.length === 0 ? (
|
|
795
1213
|
<p>No functions</p>
|
|
796
1214
|
) : (
|
|
797
1215
|
<ul>
|
|
798
1216
|
{selectedFunctions.map((symbol) => (
|
|
799
1217
|
<li key={`fn-${symbol.name}`}>
|
|
800
|
-
<span
|
|
1218
|
+
<span
|
|
1219
|
+
className={
|
|
1220
|
+
symbolChangeClass(functionChange.get(symbol.name)) ??
|
|
1221
|
+
(symbol.intended ? 'hud-intended' : undefined)
|
|
1222
|
+
}
|
|
1223
|
+
>
|
|
801
1224
|
{symbol.name}
|
|
802
1225
|
</span>
|
|
803
1226
|
{canEditBlueprint && symbol.intended && (
|
|
@@ -814,6 +1237,11 @@ export function HUD({
|
|
|
814
1237
|
)}
|
|
815
1238
|
</li>
|
|
816
1239
|
))}
|
|
1240
|
+
{extraAddedFunctions.map((item) => (
|
|
1241
|
+
<li key={`fn-add-${item.name}`}>
|
|
1242
|
+
<span className="hud-file-add">{item.name}</span>
|
|
1243
|
+
</li>
|
|
1244
|
+
))}
|
|
817
1245
|
</ul>
|
|
818
1246
|
)}
|
|
819
1247
|
{canEditBlueprint && onAddBlueprintFunction && (
|
|
@@ -824,13 +1252,19 @@ export function HUD({
|
|
|
824
1252
|
)}
|
|
825
1253
|
<div className="hud-section-title">Vars</div>
|
|
826
1254
|
{selectedVariables.length === 0 &&
|
|
1255
|
+
extraAddedVariables.length === 0 &&
|
|
827
1256
|
selectedBlueprintVariables.length === 0 ? (
|
|
828
1257
|
<p>No vars</p>
|
|
829
1258
|
) : (
|
|
830
1259
|
<ul>
|
|
831
1260
|
{selectedVariables.map((symbol) => (
|
|
832
1261
|
<li key={`var-${symbol.name}`}>
|
|
833
|
-
<span
|
|
1262
|
+
<span
|
|
1263
|
+
className={
|
|
1264
|
+
symbolChangeClass(variableChange.get(symbol.name)) ??
|
|
1265
|
+
(symbol.intended ? 'hud-intended' : undefined)
|
|
1266
|
+
}
|
|
1267
|
+
>
|
|
834
1268
|
{symbol.name}
|
|
835
1269
|
</span>
|
|
836
1270
|
{canEditBlueprint && symbol.intended && (
|
|
@@ -847,6 +1281,11 @@ export function HUD({
|
|
|
847
1281
|
)}
|
|
848
1282
|
</li>
|
|
849
1283
|
))}
|
|
1284
|
+
{extraAddedVariables.map((item) => (
|
|
1285
|
+
<li key={`var-add-${item.name}`}>
|
|
1286
|
+
<span className="hud-file-add">{item.name}</span>
|
|
1287
|
+
</li>
|
|
1288
|
+
))}
|
|
850
1289
|
</ul>
|
|
851
1290
|
)}
|
|
852
1291
|
{canEditBlueprint && onAddBlueprintVariable && (
|
|
@@ -855,22 +1294,6 @@ export function HUD({
|
|
|
855
1294
|
onAdd={(name) => onAddBlueprintVariable(selected.id, name)}
|
|
856
1295
|
/>
|
|
857
1296
|
)}
|
|
858
|
-
{previewing && (
|
|
859
|
-
<>
|
|
860
|
-
<PanelList
|
|
861
|
-
title="Added functions"
|
|
862
|
-
items={symbolLabels(selectedAddedFunctions)}
|
|
863
|
-
/>
|
|
864
|
-
<PanelList
|
|
865
|
-
title="Added variables"
|
|
866
|
-
items={symbolLabels(selectedAddedVariables)}
|
|
867
|
-
/>
|
|
868
|
-
<PanelList
|
|
869
|
-
title="Added imports"
|
|
870
|
-
items={importLabels(selectedAddedImports)}
|
|
871
|
-
/>
|
|
872
|
-
</>
|
|
873
|
-
)}
|
|
874
1297
|
<div className="hud-section-title">
|
|
875
1298
|
{importedBy ? 'Imported by' : 'Imports'}
|
|
876
1299
|
</div>
|
|
@@ -953,12 +1376,27 @@ export function HUD({
|
|
|
953
1376
|
onAdd={(raw) => onAddBlueprintImport(selected.id, raw)}
|
|
954
1377
|
/>
|
|
955
1378
|
)}
|
|
1379
|
+
</div>
|
|
1380
|
+
)}
|
|
956
1381
|
</aside>
|
|
957
1382
|
)}
|
|
958
1383
|
|
|
959
1384
|
{!selected && selectedFolderNode && infoVisible && (
|
|
960
|
-
<aside
|
|
961
|
-
|
|
1385
|
+
<aside
|
|
1386
|
+
className="hud-panel hud-panel-info"
|
|
1387
|
+
data-minimized={infoMinimized}
|
|
1388
|
+
>
|
|
1389
|
+
<PanelChrome
|
|
1390
|
+
title={selectedFolderNode.name}
|
|
1391
|
+
minimized={infoMinimized}
|
|
1392
|
+
onMinimize={() => setInfoMinimized((current) => !current)}
|
|
1393
|
+
onClose={() => {
|
|
1394
|
+
setInfoVisible(false)
|
|
1395
|
+
setInfoMinimized(false)
|
|
1396
|
+
}}
|
|
1397
|
+
/>
|
|
1398
|
+
{!infoMinimized && (
|
|
1399
|
+
<div ref={infoPanelRef} className="hud-panel-body">
|
|
962
1400
|
<p className="path">
|
|
963
1401
|
{selectedFolderNode.path === '.'
|
|
964
1402
|
? graph.targetName
|
|
@@ -974,7 +1412,7 @@ export function HUD({
|
|
|
974
1412
|
<ul>
|
|
975
1413
|
{folderFiles.map((file) => (
|
|
976
1414
|
<li key={file.id}>
|
|
977
|
-
<span>{file.
|
|
1415
|
+
<span>{file.name}</span>
|
|
978
1416
|
{canInspectFile(file.id, file.userCreated) && (
|
|
979
1417
|
<button
|
|
980
1418
|
className="hud-item-inspect"
|
|
@@ -1008,9 +1446,32 @@ export function HUD({
|
|
|
1008
1446
|
</button>
|
|
1009
1447
|
</div>
|
|
1010
1448
|
)}
|
|
1449
|
+
</div>
|
|
1450
|
+
)}
|
|
1011
1451
|
</aside>
|
|
1012
1452
|
)}
|
|
1013
1453
|
|
|
1454
|
+
{mapping && thumbnailVisible && (
|
|
1455
|
+
<SelectionThumbnail
|
|
1456
|
+
graph={graph}
|
|
1457
|
+
layout={layout}
|
|
1458
|
+
selectedId={selectedId}
|
|
1459
|
+
selectedFolder={selectedFolder}
|
|
1460
|
+
landAt={landAt}
|
|
1461
|
+
importedBy={importedBy}
|
|
1462
|
+
minimized={thumbnailMinimized}
|
|
1463
|
+
plannedIds={plannedIds}
|
|
1464
|
+
createdIds={createdIds}
|
|
1465
|
+
deletedIds={deletedIds}
|
|
1466
|
+
onMinimize={() => setThumbnailMinimized((current) => !current)}
|
|
1467
|
+
onHide={() => {
|
|
1468
|
+
setThumbnailVisible(false)
|
|
1469
|
+
setThumbnailMinimized(false)
|
|
1470
|
+
}}
|
|
1471
|
+
/>
|
|
1472
|
+
)}
|
|
1473
|
+
</div>
|
|
1474
|
+
|
|
1014
1475
|
<div className="hud-bottom">
|
|
1015
1476
|
<div className="hud-hints">
|
|
1016
1477
|
{mapping ? (
|
|
@@ -1027,15 +1488,32 @@ export function HUD({
|
|
|
1027
1488
|
)}
|
|
1028
1489
|
<span>Click a line to fly there</span>
|
|
1029
1490
|
<span>Ctrl-click an island to walk</span>
|
|
1491
|
+
{hasChangeSet && (
|
|
1492
|
+
<span>
|
|
1493
|
+
{changePathsOnly
|
|
1494
|
+
? 'C show all paths'
|
|
1495
|
+
: 'C show only changed paths'}
|
|
1496
|
+
</span>
|
|
1497
|
+
)}
|
|
1030
1498
|
{selected?.userCreated && creatingBlueprint && (
|
|
1031
1499
|
<span>Backspace delete</span>
|
|
1032
1500
|
)}
|
|
1033
1501
|
<span>{infoVisible ? 'I hide info' : 'I show info'}</span>
|
|
1034
1502
|
{infoVisible && <span>↑↓ scroll info</span>}
|
|
1503
|
+
<span>
|
|
1504
|
+
{thumbnailVisible ? 'T hide 3D view' : 'T show 3D view'}
|
|
1505
|
+
</span>
|
|
1035
1506
|
<span>
|
|
1036
1507
|
{importedBy ? 'K show imports' : 'K show imported by'}
|
|
1037
1508
|
</span>
|
|
1038
1509
|
<span>M back to walk</span>
|
|
1510
|
+
{canStop && (
|
|
1511
|
+
<span>
|
|
1512
|
+
{sessions.length > 1
|
|
1513
|
+
? 'Stop ends the focused LLM session'
|
|
1514
|
+
: 'Stop ends this LLM session'}
|
|
1515
|
+
</span>
|
|
1516
|
+
)}
|
|
1039
1517
|
</>
|
|
1040
1518
|
) : (
|
|
1041
1519
|
<>
|
|
@@ -1051,7 +1529,7 @@ export function HUD({
|
|
|
1051
1529
|
{selected?.userCreated && creatingBlueprint && (
|
|
1052
1530
|
<span>Backspace delete</span>
|
|
1053
1531
|
)}
|
|
1054
|
-
<span>
|
|
1532
|
+
<span>Double-click a block for info</span>
|
|
1055
1533
|
<span>Aim a line to fly</span>
|
|
1056
1534
|
<span>{infoVisible ? 'I hide info' : 'I show info'}</span>
|
|
1057
1535
|
{infoVisible && <span>↑↓ scroll info</span>}
|
|
@@ -1059,11 +1537,94 @@ export function HUD({
|
|
|
1059
1537
|
{importedBy ? 'K show imports' : 'K show imported by'}
|
|
1060
1538
|
</span>
|
|
1061
1539
|
<span>M toggle map</span>
|
|
1062
|
-
<span>Esc release mouse</span>
|
|
1540
|
+
<span>Double-click or Esc release mouse</span>
|
|
1541
|
+
{canStop && (
|
|
1542
|
+
<span>
|
|
1543
|
+
{sessions.length > 1
|
|
1544
|
+
? 'Stop ends the focused LLM session'
|
|
1545
|
+
: 'Stop ends this LLM session'}
|
|
1546
|
+
</span>
|
|
1547
|
+
)}
|
|
1063
1548
|
</>
|
|
1064
1549
|
)}
|
|
1065
1550
|
</div>
|
|
1066
1551
|
<div className="hud-icon-row">
|
|
1552
|
+
{mapping && hasChangeSet && onToggleChangePathsOnly && (
|
|
1553
|
+
<button
|
|
1554
|
+
className="hud-button hud-icon-button"
|
|
1555
|
+
data-active={changePathsOnly}
|
|
1556
|
+
aria-label={
|
|
1557
|
+
changePathsOnly
|
|
1558
|
+
? 'Show all folder paths'
|
|
1559
|
+
: 'Show only changed paths'
|
|
1560
|
+
}
|
|
1561
|
+
aria-keyshortcuts="C"
|
|
1562
|
+
aria-pressed={changePathsOnly}
|
|
1563
|
+
type="button"
|
|
1564
|
+
onClick={onToggleChangePathsOnly}
|
|
1565
|
+
>
|
|
1566
|
+
<svg
|
|
1567
|
+
viewBox="0 0 24 24"
|
|
1568
|
+
width="18"
|
|
1569
|
+
height="18"
|
|
1570
|
+
fill="none"
|
|
1571
|
+
stroke="currentColor"
|
|
1572
|
+
strokeWidth="2"
|
|
1573
|
+
strokeLinecap="round"
|
|
1574
|
+
strokeLinejoin="round"
|
|
1575
|
+
aria-hidden="true"
|
|
1576
|
+
>
|
|
1577
|
+
<circle cx="12" cy="5" r="2.4" />
|
|
1578
|
+
<path d="M12 7.4v4.2" />
|
|
1579
|
+
<path d="M12 11.6 6.2 16" />
|
|
1580
|
+
<path d="M12 11.6 17.8 16" />
|
|
1581
|
+
<circle cx="6.2" cy="18" r="2.1" />
|
|
1582
|
+
<circle cx="17.8" cy="18" r="2.1" />
|
|
1583
|
+
</svg>
|
|
1584
|
+
<span className="hud-tooltip">
|
|
1585
|
+
{changePathsOnly
|
|
1586
|
+
? 'C show all paths'
|
|
1587
|
+
: 'C show only changed paths'}
|
|
1588
|
+
</span>
|
|
1589
|
+
</button>
|
|
1590
|
+
)}
|
|
1591
|
+
{mapping && (
|
|
1592
|
+
<button
|
|
1593
|
+
className="hud-button hud-icon-button"
|
|
1594
|
+
data-active={thumbnailVisible}
|
|
1595
|
+
aria-label={
|
|
1596
|
+
thumbnailVisible ? 'Hide 3D view' : 'Show 3D view'
|
|
1597
|
+
}
|
|
1598
|
+
aria-keyshortcuts="T"
|
|
1599
|
+
aria-pressed={thumbnailVisible}
|
|
1600
|
+
type="button"
|
|
1601
|
+
onClick={() => {
|
|
1602
|
+
setThumbnailVisible((visible) => {
|
|
1603
|
+
if (!visible) setThumbnailMinimized(false)
|
|
1604
|
+
return !visible
|
|
1605
|
+
})
|
|
1606
|
+
}}
|
|
1607
|
+
>
|
|
1608
|
+
<svg
|
|
1609
|
+
viewBox="0 0 24 24"
|
|
1610
|
+
width="18"
|
|
1611
|
+
height="18"
|
|
1612
|
+
fill="none"
|
|
1613
|
+
stroke="currentColor"
|
|
1614
|
+
strokeWidth="2"
|
|
1615
|
+
strokeLinecap="round"
|
|
1616
|
+
strokeLinejoin="round"
|
|
1617
|
+
aria-hidden="true"
|
|
1618
|
+
>
|
|
1619
|
+
<rect x="3" y="5" width="11" height="14" rx="1.5" />
|
|
1620
|
+
<path d="M16 8h5v11H9v-3" />
|
|
1621
|
+
<path d="M6.5 16.5 9 12l2 2.5 1.5-2L15 16.5" />
|
|
1622
|
+
</svg>
|
|
1623
|
+
<span className="hud-tooltip">
|
|
1624
|
+
{thumbnailVisible ? 'T hide 3D view' : 'T show 3D view'}
|
|
1625
|
+
</span>
|
|
1626
|
+
</button>
|
|
1627
|
+
)}
|
|
1067
1628
|
<button
|
|
1068
1629
|
className="hud-button hud-icon-button"
|
|
1069
1630
|
data-active={importedBy}
|