@jkwd/inbase 0.1.9 → 0.1.11
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 +23 -7
- 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 +6 -0
- package/apps/explorer/scripts/patch-lib.mjs +6 -0
- package/apps/explorer/scripts/scan-target.mjs +46 -13
- package/apps/explorer/scripts/session-store.d.ts +64 -1
- package/apps/explorer/scripts/session-store.mjs +369 -88
- package/apps/explorer/scripts/tree-diff.mjs +121 -0
- package/apps/explorer/src/App.tsx +215 -86
- package/apps/explorer/src/agentIntent.ts +78 -0
- package/apps/explorer/src/branchChanges.ts +54 -0
- package/apps/explorer/src/index.css +152 -10
- package/apps/explorer/src/scene/FileBlock.tsx +55 -5
- package/apps/explorer/src/types.ts +46 -0
- package/apps/explorer/src/ui/HUD.tsx +670 -158
- package/apps/explorer/src/userContext.ts +11 -0
- package/apps/explorer/vite.config.ts +65 -5
- package/bin/inbase.mjs +23 -4
- package/bin/project.mjs +62 -4
- package/bin/session.mjs +215 -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 +142 -89
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
|
2
2
|
import { Canvas } from '@react-three/fiber'
|
|
3
|
-
import { emptyIntent, fetchAgentIntent, fetchAgentIntents, inspectTargetFile, performAgentAction, persistSessionBlueprint } from './agentIntent'
|
|
3
|
+
import { emptyIntent, fetchAgentIntent, fetchAgentIntents, inspectTargetFile, performAgentAction, persistSessionBlueprint, persistSessionFocus, setupVisualizerSession } from './agentIntent'
|
|
4
|
+
import { emptyBranchChanges, fetchBranchChanges } from './branchChanges'
|
|
4
5
|
import { fetchCodebase, updateCodebase } from './codebase'
|
|
5
6
|
import {
|
|
6
7
|
layoutWorld,
|
|
@@ -16,6 +17,7 @@ import { HUD } from './ui/HUD'
|
|
|
16
17
|
import {
|
|
17
18
|
fetchUserContext,
|
|
18
19
|
persistFollowLook,
|
|
20
|
+
persistShowBranchChanges,
|
|
19
21
|
persistUserContext,
|
|
20
22
|
} from './userContext'
|
|
21
23
|
import {
|
|
@@ -34,6 +36,7 @@ import {
|
|
|
34
36
|
} from './userCreated'
|
|
35
37
|
import {
|
|
36
38
|
isPatchPreview,
|
|
39
|
+
llmIsMakingChanges,
|
|
37
40
|
type AgentIntent,
|
|
38
41
|
type AimedRelation,
|
|
39
42
|
type CodebaseGraph,
|
|
@@ -52,7 +55,13 @@ function intentSignature(intent: AgentIntent) {
|
|
|
52
55
|
status: intent.status,
|
|
53
56
|
phase: intent.phase,
|
|
54
57
|
stalledWait: intent.stalledWait,
|
|
58
|
+
llmIdle: intent.llmIdle,
|
|
59
|
+
awaitingAttach: intent.awaitingAttach,
|
|
60
|
+
listening: intent.listening,
|
|
61
|
+
lastAck: intent.lastAck,
|
|
55
62
|
sessionId: intent.sessionId,
|
|
63
|
+
name: intent.name,
|
|
64
|
+
feature: intent.feature,
|
|
56
65
|
creationMode: intent.creationMode,
|
|
57
66
|
diffId: intent.diffId,
|
|
58
67
|
chain: intent.chain,
|
|
@@ -75,8 +84,15 @@ export default function App() {
|
|
|
75
84
|
const [updatingModel, setUpdatingModel] = useState(false)
|
|
76
85
|
const updatingModelRef = useRef(false)
|
|
77
86
|
|
|
87
|
+
const graphSig = useRef<string | null>(null)
|
|
78
88
|
const applyGraph = useCallback((next: CodebaseGraph | null, failed: string) => {
|
|
79
89
|
if (next) {
|
|
90
|
+
const signature = JSON.stringify(next)
|
|
91
|
+
if (signature === graphSig.current) {
|
|
92
|
+
setLoadError(null)
|
|
93
|
+
return true
|
|
94
|
+
}
|
|
95
|
+
graphSig.current = signature
|
|
80
96
|
setGraph(next)
|
|
81
97
|
setLoadError(null)
|
|
82
98
|
return true
|
|
@@ -136,17 +152,27 @@ function Explorer({
|
|
|
136
152
|
}) {
|
|
137
153
|
const [intents, setIntents] = useState<AgentIntent[]>([])
|
|
138
154
|
const [focusedSessionId, setFocusedSessionId] = useState<string | null>(null)
|
|
155
|
+
const [wantBranchChanges, setWantBranchChanges] = useState(false)
|
|
156
|
+
const [branchChanges, setBranchChanges] = useState(emptyBranchChanges)
|
|
139
157
|
const intent =
|
|
140
158
|
intents.find((item) => item.sessionId === focusedSessionId) ??
|
|
141
159
|
intents[0] ??
|
|
142
160
|
emptyIntent
|
|
143
|
-
const
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
161
|
+
const canPlace = Boolean(intent.sessionId && intent.creationMode)
|
|
162
|
+
const llmBusy = intents.some(llmIsMakingChanges)
|
|
163
|
+
const llmPreviewing = intent.preview || isPatchPreview(intent.status)
|
|
164
|
+
const showingBranchChanges =
|
|
165
|
+
wantBranchChanges &&
|
|
166
|
+
!llmBusy &&
|
|
167
|
+
!llmPreviewing &&
|
|
168
|
+
branchChanges.available
|
|
169
|
+
const changeSet = llmPreviewing
|
|
170
|
+
? intent
|
|
171
|
+
: showingBranchChanges
|
|
172
|
+
? branchChanges
|
|
173
|
+
: emptyIntent
|
|
174
|
+
const previewing = llmPreviewing || showingBranchChanges
|
|
175
|
+
const plannedCreates = previewing ? changeSet.creates : []
|
|
150
176
|
const [userBlocks, setUserBlocks] = useState<UserCreatedBlock[]>([])
|
|
151
177
|
const [userIslands, setUserIslands] = useState<UserCreatedIsland[]>([])
|
|
152
178
|
const [blueprintFunctions, setBlueprintFunctions] = useState<
|
|
@@ -166,15 +192,15 @@ function Explorer({
|
|
|
166
192
|
return withPreviewGraph(
|
|
167
193
|
graph,
|
|
168
194
|
plannedCreates,
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
195
|
+
changeSet.createLines ?? {},
|
|
196
|
+
changeSet.createFolders ?? [],
|
|
197
|
+
changeSet.imports ?? [],
|
|
172
198
|
)
|
|
173
199
|
}, [
|
|
200
|
+
changeSet.createFolders,
|
|
201
|
+
changeSet.createLines,
|
|
202
|
+
changeSet.imports,
|
|
174
203
|
graph,
|
|
175
|
-
intent.createFolders,
|
|
176
|
-
intent.createLines,
|
|
177
|
-
intent.imports,
|
|
178
204
|
plannedCreates,
|
|
179
205
|
previewing,
|
|
180
206
|
])
|
|
@@ -197,14 +223,20 @@ function Explorer({
|
|
|
197
223
|
)
|
|
198
224
|
const layout = useMemo(() => {
|
|
199
225
|
const world = layoutWorld(previewGraph)
|
|
200
|
-
if (previewing) markCreatedFolders(world,
|
|
226
|
+
if (previewing) markCreatedFolders(world, changeSet.createFolders ?? [])
|
|
201
227
|
return withUserCreatedLayout(world, userBlocks, userIslands)
|
|
202
|
-
}, [
|
|
228
|
+
}, [
|
|
229
|
+
changeSet.createFolders,
|
|
230
|
+
previewGraph,
|
|
231
|
+
previewing,
|
|
232
|
+
userBlocks,
|
|
233
|
+
userIslands,
|
|
234
|
+
])
|
|
203
235
|
const changeFileIds = useMemo(() => {
|
|
204
236
|
const ids = new Set<string>()
|
|
205
|
-
for (const id of
|
|
206
|
-
for (const id of
|
|
207
|
-
for (const id of
|
|
237
|
+
for (const id of changeSet.files) ids.add(id)
|
|
238
|
+
for (const id of changeSet.creates) ids.add(id)
|
|
239
|
+
for (const id of changeSet.deletes) ids.add(id)
|
|
208
240
|
for (const block of userBlocks) ids.add(block.id)
|
|
209
241
|
for (const item of blueprintFunctions) ids.add(item.file)
|
|
210
242
|
for (const item of blueprintVariables) ids.add(item.file)
|
|
@@ -214,20 +246,20 @@ function Explorer({
|
|
|
214
246
|
blueprintFunctions,
|
|
215
247
|
blueprintImports,
|
|
216
248
|
blueprintVariables,
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
249
|
+
changeSet.creates,
|
|
250
|
+
changeSet.deletes,
|
|
251
|
+
changeSet.files,
|
|
220
252
|
userBlocks,
|
|
221
253
|
])
|
|
222
254
|
const changeFolderPaths = useMemo(() => {
|
|
223
255
|
const paths = new Set<string>()
|
|
224
|
-
for (const path of
|
|
256
|
+
for (const path of changeSet.createFolders ?? []) paths.add(path)
|
|
225
257
|
for (const island of userIslands) {
|
|
226
258
|
if (island.path) paths.add(island.path)
|
|
227
259
|
else if (island.id) paths.add(island.id)
|
|
228
260
|
}
|
|
229
261
|
return [...paths]
|
|
230
|
-
}, [
|
|
262
|
+
}, [changeSet.createFolders, userIslands])
|
|
231
263
|
const hasChangeSet =
|
|
232
264
|
changeFileIds.length > 0 || changeFolderPaths.length > 0
|
|
233
265
|
const changePathGraph = useMemo(() => {
|
|
@@ -242,14 +274,14 @@ function Explorer({
|
|
|
242
274
|
if (!hasChangeSet) return layout
|
|
243
275
|
const world = layoutWorld(changePathGraph)
|
|
244
276
|
markCreatedFolders(world, [
|
|
245
|
-
...(
|
|
277
|
+
...(changeSet.createFolders ?? []),
|
|
246
278
|
...userIslands.map((island) => island.path || island.id),
|
|
247
279
|
])
|
|
248
280
|
return world
|
|
249
281
|
}, [
|
|
250
282
|
changePathGraph,
|
|
283
|
+
changeSet.createFolders,
|
|
251
284
|
hasChangeSet,
|
|
252
|
-
intent.createFolders,
|
|
253
285
|
layout,
|
|
254
286
|
userIslands,
|
|
255
287
|
])
|
|
@@ -273,6 +305,8 @@ function Explorer({
|
|
|
273
305
|
const lastIntentSig = useRef<string | null>(null)
|
|
274
306
|
const viewedDiffId = useRef<Record<string, string | null>>({})
|
|
275
307
|
const browsingHistory = useRef<Record<string, boolean>>({})
|
|
308
|
+
const loadedBlueprintSession = useRef<string | null>(null)
|
|
309
|
+
const seenSessionIds = useRef<Set<string>>(new Set())
|
|
276
310
|
|
|
277
311
|
const applyIntent = useCallback((next: AgentIntent, sessionId?: string) => {
|
|
278
312
|
const targetId = next.sessionId ?? sessionId ?? null
|
|
@@ -297,6 +331,19 @@ function Explorer({
|
|
|
297
331
|
if (targetId && next.sessionId) viewedDiffId.current[targetId] = next.diffId
|
|
298
332
|
}, [])
|
|
299
333
|
|
|
334
|
+
const focusSessionPanel = useCallback((sessionId: string) => {
|
|
335
|
+
setFocusedSessionId(sessionId)
|
|
336
|
+
persistSessionFocus(sessionId)
|
|
337
|
+
}, [])
|
|
338
|
+
|
|
339
|
+
const setupLlmSession = useCallback(async () => {
|
|
340
|
+
const next = await setupVisualizerSession()
|
|
341
|
+
lastIntentSig.current = null
|
|
342
|
+
applyIntent(next, next.sessionId ?? undefined)
|
|
343
|
+
if (next.sessionId) setFocusedSessionId(next.sessionId)
|
|
344
|
+
return next
|
|
345
|
+
}, [applyIntent])
|
|
346
|
+
|
|
300
347
|
const rememberWalk = useCallback((x: number, z: number) => {
|
|
301
348
|
walkPos.current = [x, z]
|
|
302
349
|
}, [])
|
|
@@ -414,7 +461,6 @@ function Explorer({
|
|
|
414
461
|
lastIntentSig.current = null
|
|
415
462
|
applyIntent(next, sessionId)
|
|
416
463
|
if (
|
|
417
|
-
action === 'invoke' ||
|
|
418
464
|
action === 'continue' ||
|
|
419
465
|
action === 'stop' ||
|
|
420
466
|
action === 'set_step_by_step'
|
|
@@ -505,6 +551,9 @@ function Explorer({
|
|
|
505
551
|
if (typeof context?.followLook === 'boolean') {
|
|
506
552
|
setFollowLook(context.followLook)
|
|
507
553
|
}
|
|
554
|
+
if (typeof context?.showBranchChanges === 'boolean') {
|
|
555
|
+
setWantBranchChanges(context.showBranchChanges)
|
|
556
|
+
}
|
|
508
557
|
})
|
|
509
558
|
return () => {
|
|
510
559
|
cancelled = true
|
|
@@ -512,7 +561,8 @@ function Explorer({
|
|
|
512
561
|
}, [])
|
|
513
562
|
|
|
514
563
|
useEffect(() => {
|
|
515
|
-
if (!
|
|
564
|
+
if (!intent.sessionId) {
|
|
565
|
+
loadedBlueprintSession.current = null
|
|
516
566
|
setUserBlocks([])
|
|
517
567
|
setUserIslands([])
|
|
518
568
|
setBlueprintFunctions([])
|
|
@@ -522,13 +572,23 @@ function Explorer({
|
|
|
522
572
|
}
|
|
523
573
|
const knownFiles = new Set(graph.files.map((file) => file.id))
|
|
524
574
|
const knownFolders = new Set(graph.folders.map((folder) => folder.path))
|
|
525
|
-
const nextBlocks = parseUserCreatedBlocks(
|
|
575
|
+
const nextBlocks = parseUserCreatedBlocks(intent.userCreatedBlocks).filter(
|
|
526
576
|
(block) => !knownFiles.has(block.id),
|
|
527
577
|
)
|
|
528
|
-
const nextIslands = parseUserCreatedIslands(
|
|
578
|
+
const nextIslands = parseUserCreatedIslands(intent.userCreatedIslands).filter(
|
|
529
579
|
(island) => !knownFolders.has(island.path),
|
|
530
580
|
)
|
|
531
|
-
|
|
581
|
+
const switched = loadedBlueprintSession.current !== intent.sessionId
|
|
582
|
+
if (switched) {
|
|
583
|
+
loadedBlueprintSession.current = intent.sessionId
|
|
584
|
+
setUserBlocks(nextBlocks)
|
|
585
|
+
setUserIslands(nextIslands)
|
|
586
|
+
setBlueprintFunctions(intent.blueprintFunctions)
|
|
587
|
+
setBlueprintVariables(intent.blueprintVariables)
|
|
588
|
+
setBlueprintImports(intent.blueprintImports)
|
|
589
|
+
return
|
|
590
|
+
}
|
|
591
|
+
if (intent.creationMode) {
|
|
532
592
|
setUserBlocks((current) =>
|
|
533
593
|
current.some((block) => block.naming) || current.length > 0
|
|
534
594
|
? current
|
|
@@ -540,29 +600,29 @@ function Explorer({
|
|
|
540
600
|
: nextIslands,
|
|
541
601
|
)
|
|
542
602
|
setBlueprintFunctions((current) =>
|
|
543
|
-
current.length > 0 ? current :
|
|
603
|
+
current.length > 0 ? current : intent.blueprintFunctions,
|
|
544
604
|
)
|
|
545
605
|
setBlueprintVariables((current) =>
|
|
546
|
-
current.length > 0 ? current :
|
|
606
|
+
current.length > 0 ? current : intent.blueprintVariables,
|
|
547
607
|
)
|
|
548
608
|
setBlueprintImports((current) =>
|
|
549
|
-
current.length > 0 ? current :
|
|
609
|
+
current.length > 0 ? current : intent.blueprintImports,
|
|
550
610
|
)
|
|
551
611
|
return
|
|
552
612
|
}
|
|
553
613
|
setUserBlocks(nextBlocks)
|
|
554
614
|
setUserIslands(nextIslands)
|
|
555
|
-
setBlueprintFunctions(
|
|
556
|
-
setBlueprintVariables(
|
|
557
|
-
setBlueprintImports(
|
|
615
|
+
setBlueprintFunctions(intent.blueprintFunctions)
|
|
616
|
+
setBlueprintVariables(intent.blueprintVariables)
|
|
617
|
+
setBlueprintImports(intent.blueprintImports)
|
|
558
618
|
}, [
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
619
|
+
intent.blueprintFunctions,
|
|
620
|
+
intent.blueprintImports,
|
|
621
|
+
intent.blueprintVariables,
|
|
622
|
+
intent.creationMode,
|
|
623
|
+
intent.sessionId,
|
|
624
|
+
intent.userCreatedBlocks,
|
|
625
|
+
intent.userCreatedIslands,
|
|
566
626
|
graph,
|
|
567
627
|
])
|
|
568
628
|
|
|
@@ -574,8 +634,8 @@ function Explorer({
|
|
|
574
634
|
variables: PatchSymbolAddition[] = blueprintVariables,
|
|
575
635
|
imports: PatchImportAddition[] = blueprintImports,
|
|
576
636
|
) => {
|
|
577
|
-
if (!
|
|
578
|
-
persistSessionBlueprint(
|
|
637
|
+
if (!intent.sessionId || !intent.creationMode) return
|
|
638
|
+
persistSessionBlueprint(intent.sessionId, {
|
|
579
639
|
userCreatedBlocks: namedCreatedBlocks(blocks),
|
|
580
640
|
userCreatedIslands: namedCreatedIslands(islands),
|
|
581
641
|
addedFunctions: functions,
|
|
@@ -587,8 +647,8 @@ function Explorer({
|
|
|
587
647
|
blueprintFunctions,
|
|
588
648
|
blueprintImports,
|
|
589
649
|
blueprintVariables,
|
|
590
|
-
|
|
591
|
-
|
|
650
|
+
intent.creationMode,
|
|
651
|
+
intent.sessionId,
|
|
592
652
|
userBlocks,
|
|
593
653
|
userIslands,
|
|
594
654
|
],
|
|
@@ -596,7 +656,7 @@ function Explorer({
|
|
|
596
656
|
|
|
597
657
|
const placeBlock = useCallback(
|
|
598
658
|
(spot: { x: number; z: number; folder: string }) => {
|
|
599
|
-
if (!
|
|
659
|
+
if (!canPlace) return
|
|
600
660
|
setUserBlocks((current) => {
|
|
601
661
|
if (current.some((block) => block.naming)) return current
|
|
602
662
|
return [
|
|
@@ -614,12 +674,12 @@ function Explorer({
|
|
|
614
674
|
})
|
|
615
675
|
document.exitPointerLock()
|
|
616
676
|
},
|
|
617
|
-
[
|
|
677
|
+
[canPlace],
|
|
618
678
|
)
|
|
619
679
|
|
|
620
680
|
const placeBlockOnFolder = useCallback(
|
|
621
681
|
(folderPath: string) => {
|
|
622
|
-
if (!
|
|
682
|
+
if (!canPlace) return
|
|
623
683
|
const fileCount = displayGraph.files.filter(
|
|
624
684
|
(file) => file.folder === folderPath,
|
|
625
685
|
).length
|
|
@@ -627,7 +687,7 @@ function Explorer({
|
|
|
627
687
|
if (!spot) return
|
|
628
688
|
placeBlock(spot)
|
|
629
689
|
},
|
|
630
|
-
[displayGraph.files,
|
|
690
|
+
[displayGraph.files, canPlace, layout, placeBlock],
|
|
631
691
|
)
|
|
632
692
|
|
|
633
693
|
const commitBlockName = useCallback(
|
|
@@ -664,7 +724,7 @@ function Explorer({
|
|
|
664
724
|
|
|
665
725
|
const placeIsland = useCallback(
|
|
666
726
|
(parent: string) => {
|
|
667
|
-
if (!
|
|
727
|
+
if (!canPlace) return
|
|
668
728
|
setUserIslands((current) => {
|
|
669
729
|
if (current.some((island) => island.naming)) return current
|
|
670
730
|
return [
|
|
@@ -680,15 +740,15 @@ function Explorer({
|
|
|
680
740
|
})
|
|
681
741
|
document.exitPointerLock()
|
|
682
742
|
},
|
|
683
|
-
[
|
|
743
|
+
[canPlace],
|
|
684
744
|
)
|
|
685
745
|
|
|
686
746
|
const placeIslandOnFolder = useCallback(
|
|
687
747
|
(parent: string) => {
|
|
688
|
-
if (!
|
|
748
|
+
if (!canPlace) return
|
|
689
749
|
placeIsland(parent)
|
|
690
750
|
},
|
|
691
|
-
[
|
|
751
|
+
[canPlace, placeIsland],
|
|
692
752
|
)
|
|
693
753
|
|
|
694
754
|
const commitIslandName = useCallback(
|
|
@@ -722,7 +782,7 @@ function Explorer({
|
|
|
722
782
|
}, [])
|
|
723
783
|
|
|
724
784
|
const deleteSelectedCreatedBlock = useCallback(() => {
|
|
725
|
-
if (!
|
|
785
|
+
if (!canPlace || !selectedId) return false
|
|
726
786
|
const selected = userBlocks.find((block) => block.id === selectedId)
|
|
727
787
|
if (!selected || selected.naming) return false
|
|
728
788
|
const next = userBlocks.filter((block) => block.id !== selectedId)
|
|
@@ -730,7 +790,7 @@ function Explorer({
|
|
|
730
790
|
persistBlueprint(next, userIslands)
|
|
731
791
|
setSelectedId(null)
|
|
732
792
|
return true
|
|
733
|
-
}, [
|
|
793
|
+
}, [canPlace, persistBlueprint, selectedId, userBlocks, userIslands])
|
|
734
794
|
|
|
735
795
|
const selectFile = useCallback(
|
|
736
796
|
(fileId: string | null) => {
|
|
@@ -739,9 +799,9 @@ function Explorer({
|
|
|
739
799
|
setSelectedFolder(null)
|
|
740
800
|
setSelectedTick((tick) => tick + 1)
|
|
741
801
|
}
|
|
742
|
-
if (fileId &&
|
|
802
|
+
if (fileId && canPlace) document.exitPointerLock()
|
|
743
803
|
},
|
|
744
|
-
[
|
|
804
|
+
[canPlace],
|
|
745
805
|
)
|
|
746
806
|
|
|
747
807
|
const inspectBlock = useCallback(
|
|
@@ -779,7 +839,7 @@ function Explorer({
|
|
|
779
839
|
|
|
780
840
|
const addBlueprintFunction = useCallback(
|
|
781
841
|
(fileId: string, rawName: string) => {
|
|
782
|
-
if (!
|
|
842
|
+
if (!canPlace || fileId.startsWith('draft:')) return false
|
|
783
843
|
const name = rawName.trim()
|
|
784
844
|
if (!isBlueprintSymbolName(name)) return false
|
|
785
845
|
const exists =
|
|
@@ -802,7 +862,7 @@ function Explorer({
|
|
|
802
862
|
blueprintImports,
|
|
803
863
|
blueprintVariables,
|
|
804
864
|
displayGraph.files,
|
|
805
|
-
|
|
865
|
+
canPlace,
|
|
806
866
|
persistBlueprint,
|
|
807
867
|
userBlocks,
|
|
808
868
|
userIslands,
|
|
@@ -811,7 +871,7 @@ function Explorer({
|
|
|
811
871
|
|
|
812
872
|
const addBlueprintVariable = useCallback(
|
|
813
873
|
(fileId: string, rawName: string) => {
|
|
814
|
-
if (!
|
|
874
|
+
if (!canPlace || fileId.startsWith('draft:')) return false
|
|
815
875
|
const name = rawName.trim()
|
|
816
876
|
if (!isBlueprintSymbolName(name)) return false
|
|
817
877
|
const exists =
|
|
@@ -834,7 +894,7 @@ function Explorer({
|
|
|
834
894
|
blueprintImports,
|
|
835
895
|
blueprintVariables,
|
|
836
896
|
displayGraph.files,
|
|
837
|
-
|
|
897
|
+
canPlace,
|
|
838
898
|
persistBlueprint,
|
|
839
899
|
userBlocks,
|
|
840
900
|
userIslands,
|
|
@@ -843,7 +903,7 @@ function Explorer({
|
|
|
843
903
|
|
|
844
904
|
const addBlueprintImport = useCallback(
|
|
845
905
|
(fileId: string, raw: string) => {
|
|
846
|
-
if (!
|
|
906
|
+
if (!canPlace || fileId.startsWith('draft:')) return false
|
|
847
907
|
const parsed = parseBlueprintImport(
|
|
848
908
|
raw,
|
|
849
909
|
fileId,
|
|
@@ -873,7 +933,7 @@ function Explorer({
|
|
|
873
933
|
blueprintImports,
|
|
874
934
|
blueprintVariables,
|
|
875
935
|
displayGraph.files,
|
|
876
|
-
|
|
936
|
+
canPlace,
|
|
877
937
|
persistBlueprint,
|
|
878
938
|
userBlocks,
|
|
879
939
|
userIslands,
|
|
@@ -882,7 +942,7 @@ function Explorer({
|
|
|
882
942
|
|
|
883
943
|
const removeBlueprintFunction = useCallback(
|
|
884
944
|
(fileId: string, name: string) => {
|
|
885
|
-
if (!
|
|
945
|
+
if (!canPlace) return
|
|
886
946
|
const next = blueprintFunctions.filter(
|
|
887
947
|
(item) => !(item.file === fileId && item.name === name),
|
|
888
948
|
)
|
|
@@ -893,7 +953,7 @@ function Explorer({
|
|
|
893
953
|
blueprintFunctions,
|
|
894
954
|
blueprintImports,
|
|
895
955
|
blueprintVariables,
|
|
896
|
-
|
|
956
|
+
canPlace,
|
|
897
957
|
persistBlueprint,
|
|
898
958
|
userBlocks,
|
|
899
959
|
userIslands,
|
|
@@ -902,7 +962,7 @@ function Explorer({
|
|
|
902
962
|
|
|
903
963
|
const removeBlueprintVariable = useCallback(
|
|
904
964
|
(fileId: string, name: string) => {
|
|
905
|
-
if (!
|
|
965
|
+
if (!canPlace) return
|
|
906
966
|
const next = blueprintVariables.filter(
|
|
907
967
|
(item) => !(item.file === fileId && item.name === name),
|
|
908
968
|
)
|
|
@@ -913,7 +973,7 @@ function Explorer({
|
|
|
913
973
|
blueprintFunctions,
|
|
914
974
|
blueprintImports,
|
|
915
975
|
blueprintVariables,
|
|
916
|
-
|
|
976
|
+
canPlace,
|
|
917
977
|
persistBlueprint,
|
|
918
978
|
userBlocks,
|
|
919
979
|
userIslands,
|
|
@@ -922,7 +982,7 @@ function Explorer({
|
|
|
922
982
|
|
|
923
983
|
const removeBlueprintImport = useCallback(
|
|
924
984
|
(fileId: string, name: string, from: string) => {
|
|
925
|
-
if (!
|
|
985
|
+
if (!canPlace) return
|
|
926
986
|
const next = blueprintImports.filter(
|
|
927
987
|
(item) =>
|
|
928
988
|
!(item.file === fileId && item.name === name && item.from === from),
|
|
@@ -940,7 +1000,7 @@ function Explorer({
|
|
|
940
1000
|
blueprintFunctions,
|
|
941
1001
|
blueprintImports,
|
|
942
1002
|
blueprintVariables,
|
|
943
|
-
|
|
1003
|
+
canPlace,
|
|
944
1004
|
persistBlueprint,
|
|
945
1005
|
userBlocks,
|
|
946
1006
|
userIslands,
|
|
@@ -987,6 +1047,18 @@ function Explorer({
|
|
|
987
1047
|
})
|
|
988
1048
|
}, [])
|
|
989
1049
|
|
|
1050
|
+
const canToggleBranchChanges = !llmBusy && (wantBranchChanges || branchChanges.available)
|
|
1051
|
+
|
|
1052
|
+
const toggleShowBranchChanges = useCallback(() => {
|
|
1053
|
+
if (llmBusy) return
|
|
1054
|
+
if (!wantBranchChanges && !branchChanges.available) return
|
|
1055
|
+
setWantBranchChanges((current) => {
|
|
1056
|
+
const next = !current
|
|
1057
|
+
persistShowBranchChanges(next)
|
|
1058
|
+
return next
|
|
1059
|
+
})
|
|
1060
|
+
}, [branchChanges.available, llmBusy, wantBranchChanges])
|
|
1061
|
+
|
|
990
1062
|
const toggleImportedBy = useCallback(() => {
|
|
991
1063
|
setImportedBy((current) => !current)
|
|
992
1064
|
}, [])
|
|
@@ -1016,6 +1088,48 @@ function Explorer({
|
|
|
1016
1088
|
return () => window.removeEventListener('keydown', onKey)
|
|
1017
1089
|
}, [toggleImportedBy])
|
|
1018
1090
|
|
|
1091
|
+
useEffect(() => {
|
|
1092
|
+
const onKey = (event: KeyboardEvent) => {
|
|
1093
|
+
if (event.repeat || event.code !== 'KeyG') return
|
|
1094
|
+
const target = event.target
|
|
1095
|
+
if (
|
|
1096
|
+
target instanceof HTMLElement &&
|
|
1097
|
+
(target.tagName === 'TEXTAREA' ||
|
|
1098
|
+
target.tagName === 'INPUT' ||
|
|
1099
|
+
target.tagName === 'SELECT' ||
|
|
1100
|
+
target.isContentEditable)
|
|
1101
|
+
) {
|
|
1102
|
+
return
|
|
1103
|
+
}
|
|
1104
|
+
if (!canToggleBranchChanges) return
|
|
1105
|
+
event.preventDefault()
|
|
1106
|
+
toggleShowBranchChanges()
|
|
1107
|
+
}
|
|
1108
|
+
window.addEventListener('keydown', onKey)
|
|
1109
|
+
return () => window.removeEventListener('keydown', onKey)
|
|
1110
|
+
}, [canToggleBranchChanges, toggleShowBranchChanges])
|
|
1111
|
+
|
|
1112
|
+
useEffect(() => {
|
|
1113
|
+
let cancelled = false
|
|
1114
|
+
const load = async () => {
|
|
1115
|
+
const next = await fetchBranchChanges()
|
|
1116
|
+
if (!cancelled) setBranchChanges(next)
|
|
1117
|
+
}
|
|
1118
|
+
void load()
|
|
1119
|
+
if (!wantBranchChanges || llmBusy) {
|
|
1120
|
+
return () => {
|
|
1121
|
+
cancelled = true
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
1124
|
+
const timer = window.setInterval(() => {
|
|
1125
|
+
void load()
|
|
1126
|
+
}, 2000)
|
|
1127
|
+
return () => {
|
|
1128
|
+
cancelled = true
|
|
1129
|
+
window.clearInterval(timer)
|
|
1130
|
+
}
|
|
1131
|
+
}, [llmBusy, updatingModel, wantBranchChanges])
|
|
1132
|
+
|
|
1019
1133
|
useEffect(() => {
|
|
1020
1134
|
if (mode !== 'map' || !hasChangeSet) return
|
|
1021
1135
|
const onKey = (event: KeyboardEvent) => {
|
|
@@ -1068,15 +1182,24 @@ function Explorer({
|
|
|
1068
1182
|
viewedDiffId.current[next.sessionId] = next.diffId
|
|
1069
1183
|
}
|
|
1070
1184
|
}
|
|
1185
|
+
const nextIds = new Set(
|
|
1186
|
+
merged
|
|
1187
|
+
.map((item) => item.sessionId)
|
|
1188
|
+
.filter((id): id is string => Boolean(id)),
|
|
1189
|
+
)
|
|
1190
|
+
const serverFocus = bundle.focusedSessionId
|
|
1191
|
+
const appeared =
|
|
1192
|
+
serverFocus != null && !seenSessionIds.current.has(serverFocus)
|
|
1193
|
+
seenSessionIds.current = nextIds
|
|
1071
1194
|
setFocusedSessionId((current) => {
|
|
1072
|
-
if (
|
|
1195
|
+
if (appeared && serverFocus && nextIds.has(serverFocus)) {
|
|
1196
|
+
return serverFocus
|
|
1197
|
+
}
|
|
1198
|
+
if (current && nextIds.has(current)) {
|
|
1073
1199
|
return current
|
|
1074
1200
|
}
|
|
1075
|
-
if (
|
|
1076
|
-
|
|
1077
|
-
merged.some((item) => item.sessionId === bundle.focusedSessionId)
|
|
1078
|
-
) {
|
|
1079
|
-
return bundle.focusedSessionId
|
|
1201
|
+
if (serverFocus && nextIds.has(serverFocus)) {
|
|
1202
|
+
return serverFocus
|
|
1080
1203
|
}
|
|
1081
1204
|
return merged[0]?.sessionId ?? null
|
|
1082
1205
|
})
|
|
@@ -1087,23 +1210,23 @@ function Explorer({
|
|
|
1087
1210
|
void poll()
|
|
1088
1211
|
const timer = window.setInterval(() => {
|
|
1089
1212
|
void poll()
|
|
1090
|
-
},
|
|
1213
|
+
}, 250)
|
|
1091
1214
|
return () => {
|
|
1092
1215
|
cancelled = true
|
|
1093
1216
|
window.clearInterval(timer)
|
|
1094
1217
|
}
|
|
1095
1218
|
}, [])
|
|
1096
1219
|
|
|
1097
|
-
const plannedIds = previewing ? [...
|
|
1220
|
+
const plannedIds = previewing ? [...changeSet.files, ...changeSet.creates] : []
|
|
1098
1221
|
const blueprintImportEdges = blueprintImports.flatMap((item) => {
|
|
1099
1222
|
if (!displayGraph.files.some((file) => file.id === item.from)) return []
|
|
1100
1223
|
return [{ from: item.file, to: item.from }]
|
|
1101
1224
|
})
|
|
1102
1225
|
const plannedImports = [
|
|
1103
|
-
...(previewing ? (
|
|
1226
|
+
...(previewing ? (changeSet.imports ?? []) : []),
|
|
1104
1227
|
...blueprintImportEdges,
|
|
1105
1228
|
]
|
|
1106
|
-
const deletedIds = previewing ?
|
|
1229
|
+
const deletedIds = previewing ? changeSet.deletes : []
|
|
1107
1230
|
|
|
1108
1231
|
return (
|
|
1109
1232
|
<>
|
|
@@ -1138,7 +1261,7 @@ function Explorer({
|
|
|
1138
1261
|
plannedImports={plannedImports}
|
|
1139
1262
|
createdIds={plannedCreates}
|
|
1140
1263
|
deletedIds={deletedIds}
|
|
1141
|
-
createLines={
|
|
1264
|
+
createLines={changeSet.createLines ?? {}}
|
|
1142
1265
|
flyTo={flyTo}
|
|
1143
1266
|
aimedRelation={aimedRelation}
|
|
1144
1267
|
onAimRelation={setAimedRelation}
|
|
@@ -1148,8 +1271,8 @@ function Explorer({
|
|
|
1148
1271
|
importedBy={importedBy}
|
|
1149
1272
|
namingId={namingId}
|
|
1150
1273
|
namingIslandId={namingIslandId}
|
|
1151
|
-
onPlaceBlock={
|
|
1152
|
-
onPlaceIsland={
|
|
1274
|
+
onPlaceBlock={canPlace ? placeBlock : undefined}
|
|
1275
|
+
onPlaceIsland={canPlace ? placeIsland : undefined}
|
|
1153
1276
|
onCommitName={commitBlockName}
|
|
1154
1277
|
onCancelName={cancelBlockName}
|
|
1155
1278
|
userCreatedBlocks={userBlocks}
|
|
@@ -1178,13 +1301,19 @@ function Explorer({
|
|
|
1178
1301
|
intent={intent}
|
|
1179
1302
|
intents={intents}
|
|
1180
1303
|
focusedSessionId={focusedSessionId}
|
|
1181
|
-
onFocusSession={
|
|
1304
|
+
onFocusSession={focusSessionPanel}
|
|
1305
|
+
onSetupSession={setupLlmSession}
|
|
1182
1306
|
onWorkflowAction={runWorkflowAction}
|
|
1183
1307
|
onNavigateDiff={navigateDiff}
|
|
1184
1308
|
onOpenMap={openMap}
|
|
1185
1309
|
onWalk={openWalk}
|
|
1186
1310
|
followLook={followLook}
|
|
1187
1311
|
onToggleFollowLook={toggleFollowLook}
|
|
1312
|
+
showBranchChanges={showingBranchChanges}
|
|
1313
|
+
branchChanges={branchChanges}
|
|
1314
|
+
canShowBranchChanges={canToggleBranchChanges}
|
|
1315
|
+
llmMakingChanges={llmBusy}
|
|
1316
|
+
onToggleShowBranchChanges={toggleShowBranchChanges}
|
|
1188
1317
|
onUpdateModel={onUpdateModel}
|
|
1189
1318
|
updatingModel={updatingModel}
|
|
1190
1319
|
importedBy={importedBy}
|