@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,6 @@
|
|
|
1
1
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
|
2
2
|
import { Canvas } from '@react-three/fiber'
|
|
3
|
-
import { emptyIntent, fetchAgentIntent, inspectTargetFile, performAgentAction, persistSessionBlueprint } from './agentIntent'
|
|
3
|
+
import { emptyIntent, fetchAgentIntent, fetchAgentIntents, inspectTargetFile, performAgentAction, persistSessionBlueprint } from './agentIntent'
|
|
4
4
|
import { fetchCodebase } from './codebase'
|
|
5
5
|
import {
|
|
6
6
|
layoutWorld,
|
|
@@ -8,6 +8,8 @@ import {
|
|
|
8
8
|
standInFront,
|
|
9
9
|
relationTravelTarget,
|
|
10
10
|
withPreviewGraph,
|
|
11
|
+
filterGraphToChangePaths,
|
|
12
|
+
mapPointOntoFolder,
|
|
11
13
|
} from './layout'
|
|
12
14
|
import { World } from './scene/World'
|
|
13
15
|
import { HUD } from './ui/HUD'
|
|
@@ -49,6 +51,7 @@ function intentSignature(intent: AgentIntent) {
|
|
|
49
51
|
updatedAt: intent.updatedAt,
|
|
50
52
|
status: intent.status,
|
|
51
53
|
phase: intent.phase,
|
|
54
|
+
stalledWait: intent.stalledWait,
|
|
52
55
|
sessionId: intent.sessionId,
|
|
53
56
|
creationMode: intent.creationMode,
|
|
54
57
|
diffId: intent.diffId,
|
|
@@ -61,6 +64,8 @@ function intentSignature(intent: AgentIntent) {
|
|
|
61
64
|
addedFunctions: intent.addedFunctions,
|
|
62
65
|
addedVariables: intent.addedVariables,
|
|
63
66
|
addedImports: intent.addedImports,
|
|
67
|
+
changedFunctions: intent.changedFunctions,
|
|
68
|
+
changedVariables: intent.changedVariables,
|
|
64
69
|
})
|
|
65
70
|
}
|
|
66
71
|
|
|
@@ -100,7 +105,17 @@ function Explorer({
|
|
|
100
105
|
graph: CodebaseGraph
|
|
101
106
|
onRefreshGraph: () => Promise<void>
|
|
102
107
|
}) {
|
|
103
|
-
const [
|
|
108
|
+
const [intents, setIntents] = useState<AgentIntent[]>([])
|
|
109
|
+
const [focusedSessionId, setFocusedSessionId] = useState<string | null>(null)
|
|
110
|
+
const intent =
|
|
111
|
+
intents.find((item) => item.sessionId === focusedSessionId) ??
|
|
112
|
+
intents[0] ??
|
|
113
|
+
emptyIntent
|
|
114
|
+
const creationIntent =
|
|
115
|
+
intents.find((item) => item.creationMode) ??
|
|
116
|
+
intents.find((item) => item.status === 'blueprint') ??
|
|
117
|
+
null
|
|
118
|
+
const creationMode = Boolean(creationIntent?.creationMode)
|
|
104
119
|
const previewing = intent.preview || isPatchPreview(intent.status)
|
|
105
120
|
const plannedCreates = previewing ? intent.creates : []
|
|
106
121
|
const [userBlocks, setUserBlocks] = useState<UserCreatedBlock[]>([])
|
|
@@ -156,6 +171,59 @@ function Explorer({
|
|
|
156
171
|
if (previewing) markCreatedFolders(world, intent.createFolders ?? [])
|
|
157
172
|
return withUserCreatedLayout(world, userBlocks, userIslands)
|
|
158
173
|
}, [intent.createFolders, previewGraph, previewing, userBlocks, userIslands])
|
|
174
|
+
const changeFileIds = useMemo(() => {
|
|
175
|
+
const ids = new Set<string>()
|
|
176
|
+
for (const id of intent.files) ids.add(id)
|
|
177
|
+
for (const id of intent.creates) ids.add(id)
|
|
178
|
+
for (const id of intent.deletes) ids.add(id)
|
|
179
|
+
for (const block of userBlocks) ids.add(block.id)
|
|
180
|
+
for (const item of blueprintFunctions) ids.add(item.file)
|
|
181
|
+
for (const item of blueprintVariables) ids.add(item.file)
|
|
182
|
+
for (const item of blueprintImports) ids.add(item.file)
|
|
183
|
+
return [...ids]
|
|
184
|
+
}, [
|
|
185
|
+
blueprintFunctions,
|
|
186
|
+
blueprintImports,
|
|
187
|
+
blueprintVariables,
|
|
188
|
+
intent.creates,
|
|
189
|
+
intent.deletes,
|
|
190
|
+
intent.files,
|
|
191
|
+
userBlocks,
|
|
192
|
+
])
|
|
193
|
+
const changeFolderPaths = useMemo(() => {
|
|
194
|
+
const paths = new Set<string>()
|
|
195
|
+
for (const path of intent.createFolders ?? []) paths.add(path)
|
|
196
|
+
for (const island of userIslands) {
|
|
197
|
+
if (island.path) paths.add(island.path)
|
|
198
|
+
else if (island.id) paths.add(island.id)
|
|
199
|
+
}
|
|
200
|
+
return [...paths]
|
|
201
|
+
}, [intent.createFolders, userIslands])
|
|
202
|
+
const hasChangeSet =
|
|
203
|
+
changeFileIds.length > 0 || changeFolderPaths.length > 0
|
|
204
|
+
const changePathGraph = useMemo(() => {
|
|
205
|
+
if (!hasChangeSet) return displayGraph
|
|
206
|
+
return filterGraphToChangePaths(
|
|
207
|
+
displayGraph,
|
|
208
|
+
changeFileIds,
|
|
209
|
+
changeFolderPaths,
|
|
210
|
+
)
|
|
211
|
+
}, [changeFileIds, changeFolderPaths, displayGraph, hasChangeSet])
|
|
212
|
+
const changePathLayout = useMemo(() => {
|
|
213
|
+
if (!hasChangeSet) return layout
|
|
214
|
+
const world = layoutWorld(changePathGraph)
|
|
215
|
+
markCreatedFolders(world, [
|
|
216
|
+
...(intent.createFolders ?? []),
|
|
217
|
+
...userIslands.map((island) => island.path || island.id),
|
|
218
|
+
])
|
|
219
|
+
return world
|
|
220
|
+
}, [
|
|
221
|
+
changePathGraph,
|
|
222
|
+
hasChangeSet,
|
|
223
|
+
intent.createFolders,
|
|
224
|
+
layout,
|
|
225
|
+
userIslands,
|
|
226
|
+
])
|
|
159
227
|
const [mode, setMode] = useState<ViewMode>('map')
|
|
160
228
|
const [landAt, setLandAt] = useState<[number, number]>([
|
|
161
229
|
layout.spawn[0],
|
|
@@ -166,18 +234,39 @@ function Explorer({
|
|
|
166
234
|
const [selectedTick, setSelectedTick] = useState(0)
|
|
167
235
|
const [selectedFolder, setSelectedFolder] = useState<string | null>(null)
|
|
168
236
|
const [aimedRelation, setAimedRelation] = useState<AimedRelation | null>(null)
|
|
237
|
+
const [aimedFileId, setAimedFileId] = useState<string | null>(null)
|
|
238
|
+
const [inspectTick, setInspectTick] = useState(0)
|
|
169
239
|
const [flyTo, setFlyTo] = useState<FlyTo | null>(null)
|
|
170
240
|
const [locked, setLocked] = useState(false)
|
|
171
241
|
const [currentFolder, setCurrentFolder] = useState(graph.targetName)
|
|
172
242
|
const [followLook, setFollowLook] = useState(false)
|
|
173
243
|
const [importedBy, setImportedBy] = useState(false)
|
|
244
|
+
const [changePathsOnly, setChangePathsOnly] = useState(false)
|
|
174
245
|
const lastIntentSig = useRef<string | null>(null)
|
|
175
|
-
const viewedDiffId = useRef<string | null
|
|
176
|
-
const browsingHistory = useRef(
|
|
246
|
+
const viewedDiffId = useRef<Record<string, string | null>>({})
|
|
247
|
+
const browsingHistory = useRef<Record<string, boolean>>({})
|
|
177
248
|
|
|
178
|
-
const applyIntent = useCallback((next: AgentIntent) => {
|
|
179
|
-
|
|
180
|
-
|
|
249
|
+
const applyIntent = useCallback((next: AgentIntent, sessionId?: string) => {
|
|
250
|
+
const targetId = next.sessionId ?? sessionId ?? null
|
|
251
|
+
setIntents((current) => {
|
|
252
|
+
const nextList =
|
|
253
|
+
!targetId || next.status === 'idle' || !next.sessionId
|
|
254
|
+
? current.filter((item) => item.sessionId !== targetId)
|
|
255
|
+
: current.some((item) => item.sessionId === targetId)
|
|
256
|
+
? current.map((item) => (item.sessionId === targetId ? next : item))
|
|
257
|
+
: [...current, next]
|
|
258
|
+
setFocusedSessionId((currentFocus) => {
|
|
259
|
+
if (
|
|
260
|
+
currentFocus &&
|
|
261
|
+
nextList.some((item) => item.sessionId === currentFocus)
|
|
262
|
+
) {
|
|
263
|
+
return currentFocus
|
|
264
|
+
}
|
|
265
|
+
return nextList[0]?.sessionId ?? null
|
|
266
|
+
})
|
|
267
|
+
return nextList
|
|
268
|
+
})
|
|
269
|
+
if (targetId && next.sessionId) viewedDiffId.current[targetId] = next.diffId
|
|
181
270
|
}, [])
|
|
182
271
|
|
|
183
272
|
const rememberWalk = useCallback((x: number, z: number) => {
|
|
@@ -213,6 +302,22 @@ function Explorer({
|
|
|
213
302
|
setMode('walk')
|
|
214
303
|
}, [])
|
|
215
304
|
|
|
305
|
+
const landFromMap = useCallback(
|
|
306
|
+
(x: number, z: number) => {
|
|
307
|
+
if (!changePathsOnly || !hasChangeSet) {
|
|
308
|
+
land(x, z)
|
|
309
|
+
return
|
|
310
|
+
}
|
|
311
|
+
const mapped = mapPointOntoFolder(x, z, changePathLayout, layout, [
|
|
312
|
+
layout.spawn[0],
|
|
313
|
+
layout.spawn[2],
|
|
314
|
+
])
|
|
315
|
+
if (!mapped) return
|
|
316
|
+
land(mapped[0], mapped[1])
|
|
317
|
+
},
|
|
318
|
+
[changePathLayout, changePathsOnly, hasChangeSet, land, layout],
|
|
319
|
+
)
|
|
320
|
+
|
|
216
321
|
const travelToFile = useCallback(
|
|
217
322
|
(fileId: string, fly: boolean) => {
|
|
218
323
|
const placed = layout.files[fileId]
|
|
@@ -244,23 +349,25 @@ function Explorer({
|
|
|
244
349
|
|
|
245
350
|
const runWorkflowAction = useCallback(
|
|
246
351
|
async (
|
|
352
|
+
sessionId: string,
|
|
247
353
|
action: WorkflowAction,
|
|
248
|
-
options: { instruction?: string; step?: number } = {},
|
|
354
|
+
options: { instruction?: string; step?: number; stepByStep?: boolean } = {},
|
|
249
355
|
) => {
|
|
250
|
-
|
|
356
|
+
const current = intents.find((item) => item.sessionId === sessionId)
|
|
357
|
+
if (!current?.sessionId) return
|
|
251
358
|
if (
|
|
252
359
|
(action === 'continue' || action === 'instruct') &&
|
|
253
|
-
(!
|
|
360
|
+
(!current.diffId || !current.isActiveDiff)
|
|
254
361
|
) {
|
|
255
362
|
return
|
|
256
363
|
}
|
|
257
364
|
try {
|
|
258
365
|
const next = await performAgentAction(
|
|
259
366
|
action,
|
|
260
|
-
|
|
367
|
+
current.sessionId,
|
|
261
368
|
{
|
|
262
369
|
...options,
|
|
263
|
-
diffId:
|
|
370
|
+
diffId: current.diffId ?? undefined,
|
|
264
371
|
...(action === 'blueprint_send'
|
|
265
372
|
? {
|
|
266
373
|
userCreatedBlocks: namedCreatedBlocks(userBlocks),
|
|
@@ -272,10 +379,15 @@ function Explorer({
|
|
|
272
379
|
: {}),
|
|
273
380
|
},
|
|
274
381
|
)
|
|
275
|
-
browsingHistory.current = false
|
|
276
|
-
lastIntentSig.current =
|
|
277
|
-
applyIntent(next)
|
|
278
|
-
if (
|
|
382
|
+
browsingHistory.current[sessionId] = false
|
|
383
|
+
lastIntentSig.current = null
|
|
384
|
+
applyIntent(next, sessionId)
|
|
385
|
+
if (
|
|
386
|
+
action === 'invoke' ||
|
|
387
|
+
action === 'continue' ||
|
|
388
|
+
action === 'stop' ||
|
|
389
|
+
action === 'set_step_by_step'
|
|
390
|
+
) {
|
|
279
391
|
await onRefreshGraph()
|
|
280
392
|
}
|
|
281
393
|
} catch {
|
|
@@ -287,9 +399,7 @@ function Explorer({
|
|
|
287
399
|
blueprintFunctions,
|
|
288
400
|
blueprintImports,
|
|
289
401
|
blueprintVariables,
|
|
290
|
-
|
|
291
|
-
intent.isActiveDiff,
|
|
292
|
-
intent.sessionId,
|
|
402
|
+
intents,
|
|
293
403
|
onRefreshGraph,
|
|
294
404
|
userBlocks,
|
|
295
405
|
userIslands,
|
|
@@ -297,28 +407,29 @@ function Explorer({
|
|
|
297
407
|
)
|
|
298
408
|
|
|
299
409
|
const navigateDiff = useCallback(
|
|
300
|
-
async (diffId: string) => {
|
|
410
|
+
async (sessionId: string, diffId: string) => {
|
|
411
|
+
const current = intents.find((item) => item.sessionId === sessionId)
|
|
412
|
+
if (!current) return
|
|
301
413
|
try {
|
|
302
|
-
const latest =
|
|
303
|
-
browsingHistory.current = diffId !== latest
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
// Still show the historical preview if disk replay failed.
|
|
312
|
-
}
|
|
414
|
+
const latest = current.chain.at(-1)?.id
|
|
415
|
+
browsingHistory.current[sessionId] = diffId !== latest
|
|
416
|
+
try {
|
|
417
|
+
await inspectTargetFile({
|
|
418
|
+
sessionId,
|
|
419
|
+
diffId,
|
|
420
|
+
})
|
|
421
|
+
} catch {
|
|
422
|
+
// Still show the historical preview if disk replay failed.
|
|
313
423
|
}
|
|
314
|
-
const next = await fetchAgentIntent(diffId)
|
|
315
|
-
lastIntentSig.current =
|
|
316
|
-
applyIntent(next)
|
|
424
|
+
const next = await fetchAgentIntent(sessionId, diffId)
|
|
425
|
+
lastIntentSig.current = null
|
|
426
|
+
applyIntent(next, sessionId)
|
|
427
|
+
setFocusedSessionId(sessionId)
|
|
317
428
|
} catch {
|
|
318
429
|
// Keep the current chain position if navigation failed.
|
|
319
430
|
}
|
|
320
431
|
},
|
|
321
|
-
[applyIntent,
|
|
432
|
+
[applyIntent, intents],
|
|
322
433
|
)
|
|
323
434
|
|
|
324
435
|
const inspectFile = useCallback(
|
|
@@ -370,7 +481,7 @@ function Explorer({
|
|
|
370
481
|
}, [])
|
|
371
482
|
|
|
372
483
|
useEffect(() => {
|
|
373
|
-
if (!
|
|
484
|
+
if (!creationIntent?.sessionId) {
|
|
374
485
|
setUserBlocks([])
|
|
375
486
|
setUserIslands([])
|
|
376
487
|
setBlueprintFunctions([])
|
|
@@ -380,13 +491,13 @@ function Explorer({
|
|
|
380
491
|
}
|
|
381
492
|
const knownFiles = new Set(graph.files.map((file) => file.id))
|
|
382
493
|
const knownFolders = new Set(graph.folders.map((folder) => folder.path))
|
|
383
|
-
const nextBlocks = parseUserCreatedBlocks(
|
|
494
|
+
const nextBlocks = parseUserCreatedBlocks(creationIntent.userCreatedBlocks).filter(
|
|
384
495
|
(block) => !knownFiles.has(block.id),
|
|
385
496
|
)
|
|
386
|
-
const nextIslands = parseUserCreatedIslands(
|
|
497
|
+
const nextIslands = parseUserCreatedIslands(creationIntent.userCreatedIslands).filter(
|
|
387
498
|
(island) => !knownFolders.has(island.path),
|
|
388
499
|
)
|
|
389
|
-
if (
|
|
500
|
+
if (creationIntent.creationMode) {
|
|
390
501
|
setUserBlocks((current) =>
|
|
391
502
|
current.some((block) => block.naming) || current.length > 0
|
|
392
503
|
? current
|
|
@@ -398,28 +509,29 @@ function Explorer({
|
|
|
398
509
|
: nextIslands,
|
|
399
510
|
)
|
|
400
511
|
setBlueprintFunctions((current) =>
|
|
401
|
-
current.length > 0 ? current :
|
|
512
|
+
current.length > 0 ? current : creationIntent.blueprintFunctions,
|
|
402
513
|
)
|
|
403
514
|
setBlueprintVariables((current) =>
|
|
404
|
-
current.length > 0 ? current :
|
|
515
|
+
current.length > 0 ? current : creationIntent.blueprintVariables,
|
|
405
516
|
)
|
|
406
517
|
setBlueprintImports((current) =>
|
|
407
|
-
current.length > 0 ? current :
|
|
518
|
+
current.length > 0 ? current : creationIntent.blueprintImports,
|
|
408
519
|
)
|
|
409
520
|
return
|
|
410
521
|
}
|
|
411
522
|
setUserBlocks(nextBlocks)
|
|
412
523
|
setUserIslands(nextIslands)
|
|
413
|
-
setBlueprintFunctions(
|
|
414
|
-
setBlueprintVariables(
|
|
415
|
-
setBlueprintImports(
|
|
524
|
+
setBlueprintFunctions(creationIntent.blueprintFunctions)
|
|
525
|
+
setBlueprintVariables(creationIntent.blueprintVariables)
|
|
526
|
+
setBlueprintImports(creationIntent.blueprintImports)
|
|
416
527
|
}, [
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
528
|
+
creationIntent?.blueprintFunctions,
|
|
529
|
+
creationIntent?.blueprintImports,
|
|
530
|
+
creationIntent?.blueprintVariables,
|
|
531
|
+
creationIntent?.creationMode,
|
|
532
|
+
creationIntent?.sessionId,
|
|
533
|
+
creationIntent?.userCreatedBlocks,
|
|
534
|
+
creationIntent?.userCreatedIslands,
|
|
423
535
|
graph,
|
|
424
536
|
])
|
|
425
537
|
|
|
@@ -431,8 +543,8 @@ function Explorer({
|
|
|
431
543
|
variables: PatchSymbolAddition[] = blueprintVariables,
|
|
432
544
|
imports: PatchImportAddition[] = blueprintImports,
|
|
433
545
|
) => {
|
|
434
|
-
if (!
|
|
435
|
-
persistSessionBlueprint(
|
|
546
|
+
if (!creationIntent?.sessionId || !creationIntent.creationMode) return
|
|
547
|
+
persistSessionBlueprint(creationIntent.sessionId, {
|
|
436
548
|
userCreatedBlocks: namedCreatedBlocks(blocks),
|
|
437
549
|
userCreatedIslands: namedCreatedIslands(islands),
|
|
438
550
|
addedFunctions: functions,
|
|
@@ -444,8 +556,8 @@ function Explorer({
|
|
|
444
556
|
blueprintFunctions,
|
|
445
557
|
blueprintImports,
|
|
446
558
|
blueprintVariables,
|
|
447
|
-
|
|
448
|
-
|
|
559
|
+
creationIntent?.creationMode,
|
|
560
|
+
creationIntent?.sessionId,
|
|
449
561
|
userBlocks,
|
|
450
562
|
userIslands,
|
|
451
563
|
],
|
|
@@ -453,7 +565,7 @@ function Explorer({
|
|
|
453
565
|
|
|
454
566
|
const placeBlock = useCallback(
|
|
455
567
|
(spot: { x: number; z: number; folder: string }) => {
|
|
456
|
-
if (!
|
|
568
|
+
if (!creationMode) return
|
|
457
569
|
setUserBlocks((current) => {
|
|
458
570
|
if (current.some((block) => block.naming)) return current
|
|
459
571
|
return [
|
|
@@ -471,12 +583,12 @@ function Explorer({
|
|
|
471
583
|
})
|
|
472
584
|
document.exitPointerLock()
|
|
473
585
|
},
|
|
474
|
-
[
|
|
586
|
+
[creationMode],
|
|
475
587
|
)
|
|
476
588
|
|
|
477
589
|
const placeBlockOnFolder = useCallback(
|
|
478
590
|
(folderPath: string) => {
|
|
479
|
-
if (!
|
|
591
|
+
if (!creationMode) return
|
|
480
592
|
const fileCount = displayGraph.files.filter(
|
|
481
593
|
(file) => file.folder === folderPath,
|
|
482
594
|
).length
|
|
@@ -484,7 +596,7 @@ function Explorer({
|
|
|
484
596
|
if (!spot) return
|
|
485
597
|
placeBlock(spot)
|
|
486
598
|
},
|
|
487
|
-
[displayGraph.files,
|
|
599
|
+
[displayGraph.files, creationMode, layout, placeBlock],
|
|
488
600
|
)
|
|
489
601
|
|
|
490
602
|
const commitBlockName = useCallback(
|
|
@@ -521,7 +633,7 @@ function Explorer({
|
|
|
521
633
|
|
|
522
634
|
const placeIsland = useCallback(
|
|
523
635
|
(parent: string) => {
|
|
524
|
-
if (!
|
|
636
|
+
if (!creationMode) return
|
|
525
637
|
setUserIslands((current) => {
|
|
526
638
|
if (current.some((island) => island.naming)) return current
|
|
527
639
|
return [
|
|
@@ -537,15 +649,15 @@ function Explorer({
|
|
|
537
649
|
})
|
|
538
650
|
document.exitPointerLock()
|
|
539
651
|
},
|
|
540
|
-
[
|
|
652
|
+
[creationMode],
|
|
541
653
|
)
|
|
542
654
|
|
|
543
655
|
const placeIslandOnFolder = useCallback(
|
|
544
656
|
(parent: string) => {
|
|
545
|
-
if (!
|
|
657
|
+
if (!creationMode) return
|
|
546
658
|
placeIsland(parent)
|
|
547
659
|
},
|
|
548
|
-
[
|
|
660
|
+
[creationMode, placeIsland],
|
|
549
661
|
)
|
|
550
662
|
|
|
551
663
|
const commitIslandName = useCallback(
|
|
@@ -579,7 +691,7 @@ function Explorer({
|
|
|
579
691
|
}, [])
|
|
580
692
|
|
|
581
693
|
const deleteSelectedCreatedBlock = useCallback(() => {
|
|
582
|
-
if (!
|
|
694
|
+
if (!creationMode || !selectedId) return false
|
|
583
695
|
const selected = userBlocks.find((block) => block.id === selectedId)
|
|
584
696
|
if (!selected || selected.naming) return false
|
|
585
697
|
const next = userBlocks.filter((block) => block.id !== selectedId)
|
|
@@ -587,7 +699,7 @@ function Explorer({
|
|
|
587
699
|
persistBlueprint(next, userIslands)
|
|
588
700
|
setSelectedId(null)
|
|
589
701
|
return true
|
|
590
|
-
}, [
|
|
702
|
+
}, [creationMode, persistBlueprint, selectedId, userBlocks, userIslands])
|
|
591
703
|
|
|
592
704
|
const selectFile = useCallback(
|
|
593
705
|
(fileId: string | null) => {
|
|
@@ -596,9 +708,17 @@ function Explorer({
|
|
|
596
708
|
setSelectedFolder(null)
|
|
597
709
|
setSelectedTick((tick) => tick + 1)
|
|
598
710
|
}
|
|
599
|
-
if (fileId &&
|
|
711
|
+
if (fileId && creationMode) document.exitPointerLock()
|
|
600
712
|
},
|
|
601
|
-
[
|
|
713
|
+
[creationMode],
|
|
714
|
+
)
|
|
715
|
+
|
|
716
|
+
const inspectBlock = useCallback(
|
|
717
|
+
(fileId: string) => {
|
|
718
|
+
selectFile(fileId)
|
|
719
|
+
setInspectTick((tick) => tick + 1)
|
|
720
|
+
},
|
|
721
|
+
[selectFile],
|
|
602
722
|
)
|
|
603
723
|
|
|
604
724
|
const selectFolder = useCallback((folderPath: string | null) => {
|
|
@@ -606,9 +726,29 @@ function Explorer({
|
|
|
606
726
|
if (folderPath) setSelectedId(null)
|
|
607
727
|
}, [])
|
|
608
728
|
|
|
729
|
+
useEffect(() => {
|
|
730
|
+
if (!changePathsOnly || !hasChangeSet) return
|
|
731
|
+
if (selectedFolder && !changePathLayout.folders[selectedFolder]) {
|
|
732
|
+
setSelectedFolder(null)
|
|
733
|
+
}
|
|
734
|
+
if (
|
|
735
|
+
selectedId &&
|
|
736
|
+
!changePathGraph.files.some((file) => file.id === selectedId)
|
|
737
|
+
) {
|
|
738
|
+
setSelectedId(null)
|
|
739
|
+
}
|
|
740
|
+
}, [
|
|
741
|
+
changePathGraph.files,
|
|
742
|
+
changePathLayout.folders,
|
|
743
|
+
changePathsOnly,
|
|
744
|
+
hasChangeSet,
|
|
745
|
+
selectedFolder,
|
|
746
|
+
selectedId,
|
|
747
|
+
])
|
|
748
|
+
|
|
609
749
|
const addBlueprintFunction = useCallback(
|
|
610
750
|
(fileId: string, rawName: string) => {
|
|
611
|
-
if (!
|
|
751
|
+
if (!creationMode || fileId.startsWith('draft:')) return false
|
|
612
752
|
const name = rawName.trim()
|
|
613
753
|
if (!isBlueprintSymbolName(name)) return false
|
|
614
754
|
const exists =
|
|
@@ -631,7 +771,7 @@ function Explorer({
|
|
|
631
771
|
blueprintImports,
|
|
632
772
|
blueprintVariables,
|
|
633
773
|
displayGraph.files,
|
|
634
|
-
|
|
774
|
+
creationMode,
|
|
635
775
|
persistBlueprint,
|
|
636
776
|
userBlocks,
|
|
637
777
|
userIslands,
|
|
@@ -640,7 +780,7 @@ function Explorer({
|
|
|
640
780
|
|
|
641
781
|
const addBlueprintVariable = useCallback(
|
|
642
782
|
(fileId: string, rawName: string) => {
|
|
643
|
-
if (!
|
|
783
|
+
if (!creationMode || fileId.startsWith('draft:')) return false
|
|
644
784
|
const name = rawName.trim()
|
|
645
785
|
if (!isBlueprintSymbolName(name)) return false
|
|
646
786
|
const exists =
|
|
@@ -663,7 +803,7 @@ function Explorer({
|
|
|
663
803
|
blueprintImports,
|
|
664
804
|
blueprintVariables,
|
|
665
805
|
displayGraph.files,
|
|
666
|
-
|
|
806
|
+
creationMode,
|
|
667
807
|
persistBlueprint,
|
|
668
808
|
userBlocks,
|
|
669
809
|
userIslands,
|
|
@@ -672,7 +812,7 @@ function Explorer({
|
|
|
672
812
|
|
|
673
813
|
const addBlueprintImport = useCallback(
|
|
674
814
|
(fileId: string, raw: string) => {
|
|
675
|
-
if (!
|
|
815
|
+
if (!creationMode || fileId.startsWith('draft:')) return false
|
|
676
816
|
const parsed = parseBlueprintImport(
|
|
677
817
|
raw,
|
|
678
818
|
fileId,
|
|
@@ -702,7 +842,7 @@ function Explorer({
|
|
|
702
842
|
blueprintImports,
|
|
703
843
|
blueprintVariables,
|
|
704
844
|
displayGraph.files,
|
|
705
|
-
|
|
845
|
+
creationMode,
|
|
706
846
|
persistBlueprint,
|
|
707
847
|
userBlocks,
|
|
708
848
|
userIslands,
|
|
@@ -711,7 +851,7 @@ function Explorer({
|
|
|
711
851
|
|
|
712
852
|
const removeBlueprintFunction = useCallback(
|
|
713
853
|
(fileId: string, name: string) => {
|
|
714
|
-
if (!
|
|
854
|
+
if (!creationMode) return
|
|
715
855
|
const next = blueprintFunctions.filter(
|
|
716
856
|
(item) => !(item.file === fileId && item.name === name),
|
|
717
857
|
)
|
|
@@ -722,7 +862,7 @@ function Explorer({
|
|
|
722
862
|
blueprintFunctions,
|
|
723
863
|
blueprintImports,
|
|
724
864
|
blueprintVariables,
|
|
725
|
-
|
|
865
|
+
creationMode,
|
|
726
866
|
persistBlueprint,
|
|
727
867
|
userBlocks,
|
|
728
868
|
userIslands,
|
|
@@ -731,7 +871,7 @@ function Explorer({
|
|
|
731
871
|
|
|
732
872
|
const removeBlueprintVariable = useCallback(
|
|
733
873
|
(fileId: string, name: string) => {
|
|
734
|
-
if (!
|
|
874
|
+
if (!creationMode) return
|
|
735
875
|
const next = blueprintVariables.filter(
|
|
736
876
|
(item) => !(item.file === fileId && item.name === name),
|
|
737
877
|
)
|
|
@@ -742,7 +882,7 @@ function Explorer({
|
|
|
742
882
|
blueprintFunctions,
|
|
743
883
|
blueprintImports,
|
|
744
884
|
blueprintVariables,
|
|
745
|
-
|
|
885
|
+
creationMode,
|
|
746
886
|
persistBlueprint,
|
|
747
887
|
userBlocks,
|
|
748
888
|
userIslands,
|
|
@@ -751,7 +891,7 @@ function Explorer({
|
|
|
751
891
|
|
|
752
892
|
const removeBlueprintImport = useCallback(
|
|
753
893
|
(fileId: string, name: string, from: string) => {
|
|
754
|
-
if (!
|
|
894
|
+
if (!creationMode) return
|
|
755
895
|
const next = blueprintImports.filter(
|
|
756
896
|
(item) =>
|
|
757
897
|
!(item.file === fileId && item.name === name && item.from === from),
|
|
@@ -769,7 +909,7 @@ function Explorer({
|
|
|
769
909
|
blueprintFunctions,
|
|
770
910
|
blueprintImports,
|
|
771
911
|
blueprintVariables,
|
|
772
|
-
|
|
912
|
+
creationMode,
|
|
773
913
|
persistBlueprint,
|
|
774
914
|
userBlocks,
|
|
775
915
|
userIslands,
|
|
@@ -820,6 +960,11 @@ function Explorer({
|
|
|
820
960
|
setImportedBy((current) => !current)
|
|
821
961
|
}, [])
|
|
822
962
|
|
|
963
|
+
const toggleChangePathsOnly = useCallback(() => {
|
|
964
|
+
if (!hasChangeSet) return
|
|
965
|
+
setChangePathsOnly((current) => !current)
|
|
966
|
+
}, [hasChangeSet])
|
|
967
|
+
|
|
823
968
|
useEffect(() => {
|
|
824
969
|
const onKey = (event: KeyboardEvent) => {
|
|
825
970
|
if (event.repeat || event.code !== 'KeyK') return
|
|
@@ -840,19 +985,70 @@ function Explorer({
|
|
|
840
985
|
return () => window.removeEventListener('keydown', onKey)
|
|
841
986
|
}, [toggleImportedBy])
|
|
842
987
|
|
|
988
|
+
useEffect(() => {
|
|
989
|
+
if (mode !== 'map' || !hasChangeSet) return
|
|
990
|
+
const onKey = (event: KeyboardEvent) => {
|
|
991
|
+
if (event.repeat || event.code !== 'KeyC') return
|
|
992
|
+
const target = event.target
|
|
993
|
+
if (
|
|
994
|
+
target instanceof HTMLElement &&
|
|
995
|
+
(target.tagName === 'TEXTAREA' ||
|
|
996
|
+
target.tagName === 'INPUT' ||
|
|
997
|
+
target.tagName === 'SELECT' ||
|
|
998
|
+
target.isContentEditable)
|
|
999
|
+
) {
|
|
1000
|
+
return
|
|
1001
|
+
}
|
|
1002
|
+
event.preventDefault()
|
|
1003
|
+
toggleChangePathsOnly()
|
|
1004
|
+
}
|
|
1005
|
+
window.addEventListener('keydown', onKey)
|
|
1006
|
+
return () => window.removeEventListener('keydown', onKey)
|
|
1007
|
+
}, [hasChangeSet, mode, toggleChangePathsOnly])
|
|
1008
|
+
|
|
843
1009
|
useEffect(() => {
|
|
844
1010
|
let cancelled = false
|
|
845
1011
|
const poll = async () => {
|
|
846
1012
|
try {
|
|
847
|
-
const
|
|
848
|
-
|
|
849
|
-
)
|
|
850
|
-
|
|
1013
|
+
const bundle = await fetchAgentIntents()
|
|
1014
|
+
const merged: AgentIntent[] = []
|
|
1015
|
+
for (const next of bundle.intents) {
|
|
1016
|
+
const sessionId = next.sessionId
|
|
1017
|
+
if (
|
|
1018
|
+
sessionId &&
|
|
1019
|
+
browsingHistory.current[sessionId] &&
|
|
1020
|
+
viewedDiffId.current[sessionId]
|
|
1021
|
+
) {
|
|
1022
|
+
merged.push(
|
|
1023
|
+
await fetchAgentIntent(sessionId, viewedDiffId.current[sessionId] ?? undefined),
|
|
1024
|
+
)
|
|
1025
|
+
} else {
|
|
1026
|
+
merged.push(next)
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
const signature = JSON.stringify(merged.map(intentSignature))
|
|
851
1030
|
if (cancelled || signature === lastIntentSig.current) {
|
|
852
1031
|
return
|
|
853
1032
|
}
|
|
854
1033
|
lastIntentSig.current = signature
|
|
855
|
-
|
|
1034
|
+
setIntents(merged)
|
|
1035
|
+
for (const next of merged) {
|
|
1036
|
+
if (next.sessionId && !browsingHistory.current[next.sessionId]) {
|
|
1037
|
+
viewedDiffId.current[next.sessionId] = next.diffId
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
setFocusedSessionId((current) => {
|
|
1041
|
+
if (current && merged.some((item) => item.sessionId === current)) {
|
|
1042
|
+
return current
|
|
1043
|
+
}
|
|
1044
|
+
if (
|
|
1045
|
+
bundle.focusedSessionId &&
|
|
1046
|
+
merged.some((item) => item.sessionId === bundle.focusedSessionId)
|
|
1047
|
+
) {
|
|
1048
|
+
return bundle.focusedSessionId
|
|
1049
|
+
}
|
|
1050
|
+
return merged[0]?.sessionId ?? null
|
|
1051
|
+
})
|
|
856
1052
|
} catch {
|
|
857
1053
|
// Explorer may be running without the intent endpoint yet.
|
|
858
1054
|
}
|
|
@@ -865,7 +1061,7 @@ function Explorer({
|
|
|
865
1061
|
cancelled = true
|
|
866
1062
|
window.clearInterval(timer)
|
|
867
1063
|
}
|
|
868
|
-
}, [
|
|
1064
|
+
}, [])
|
|
869
1065
|
|
|
870
1066
|
const plannedIds = previewing ? [...intent.files, ...intent.creates] : []
|
|
871
1067
|
const blueprintImportEdges = blueprintImports.flatMap((item) => {
|
|
@@ -883,6 +1079,7 @@ function Explorer({
|
|
|
883
1079
|
<div className="stage">
|
|
884
1080
|
<Canvas
|
|
885
1081
|
shadows={false}
|
|
1082
|
+
dpr={[1, 1.5]}
|
|
886
1083
|
gl={{ antialias: true, toneMappingExposure: 1.25 }}
|
|
887
1084
|
camera={{
|
|
888
1085
|
position: layout.spawn,
|
|
@@ -903,7 +1100,7 @@ function Explorer({
|
|
|
903
1100
|
onSelectFolder={selectFolder}
|
|
904
1101
|
onLockedChange={setLocked}
|
|
905
1102
|
onFolderChange={setCurrentFolder}
|
|
906
|
-
onLand={
|
|
1103
|
+
onLand={landFromMap}
|
|
907
1104
|
onWalkPosition={rememberWalk}
|
|
908
1105
|
onContext={persistUserContext}
|
|
909
1106
|
plannedIds={plannedIds}
|
|
@@ -915,29 +1112,44 @@ function Explorer({
|
|
|
915
1112
|
flyTo={flyTo}
|
|
916
1113
|
aimedRelation={aimedRelation}
|
|
917
1114
|
onAimRelation={setAimedRelation}
|
|
1115
|
+
onAimFile={setAimedFileId}
|
|
1116
|
+
onInspect={inspectBlock}
|
|
918
1117
|
onTravelTo={flyAlongRelation}
|
|
919
1118
|
importedBy={importedBy}
|
|
920
1119
|
namingId={namingId}
|
|
921
1120
|
namingIslandId={namingIslandId}
|
|
922
|
-
onPlaceBlock={
|
|
923
|
-
onPlaceIsland={
|
|
1121
|
+
onPlaceBlock={creationMode ? placeBlock : undefined}
|
|
1122
|
+
onPlaceIsland={creationMode ? placeIsland : undefined}
|
|
924
1123
|
onCommitName={commitBlockName}
|
|
925
1124
|
onCancelName={cancelBlockName}
|
|
926
1125
|
userCreatedBlocks={userBlocks}
|
|
927
1126
|
userCreatedIslands={userIslands}
|
|
1127
|
+
mapGraph={
|
|
1128
|
+
changePathsOnly && hasChangeSet ? changePathGraph : null
|
|
1129
|
+
}
|
|
1130
|
+
mapLayout={
|
|
1131
|
+
changePathsOnly && hasChangeSet ? changePathLayout : null
|
|
1132
|
+
}
|
|
928
1133
|
/>
|
|
929
1134
|
</Canvas>
|
|
930
1135
|
</div>
|
|
931
1136
|
<HUD
|
|
932
1137
|
graph={displayGraph}
|
|
1138
|
+
layout={layout}
|
|
933
1139
|
mode={mode}
|
|
934
1140
|
locked={locked}
|
|
935
1141
|
selectedId={selectedId}
|
|
936
1142
|
selectedTick={selectedTick}
|
|
1143
|
+
inspectTick={inspectTick}
|
|
937
1144
|
selectedFolder={selectedFolder}
|
|
1145
|
+
landAt={landAt}
|
|
938
1146
|
aimedRelation={aimedRelation}
|
|
1147
|
+
aimedFileId={aimedFileId}
|
|
939
1148
|
currentFolder={currentFolder}
|
|
940
1149
|
intent={intent}
|
|
1150
|
+
intents={intents}
|
|
1151
|
+
focusedSessionId={focusedSessionId}
|
|
1152
|
+
onFocusSession={setFocusedSessionId}
|
|
941
1153
|
onWorkflowAction={runWorkflowAction}
|
|
942
1154
|
onNavigateDiff={navigateDiff}
|
|
943
1155
|
onOpenMap={openMap}
|
|
@@ -946,6 +1158,9 @@ function Explorer({
|
|
|
946
1158
|
onToggleFollowLook={toggleFollowLook}
|
|
947
1159
|
importedBy={importedBy}
|
|
948
1160
|
onToggleImportedBy={toggleImportedBy}
|
|
1161
|
+
changePathsOnly={changePathsOnly}
|
|
1162
|
+
hasChangeSet={hasChangeSet}
|
|
1163
|
+
onToggleChangePathsOnly={toggleChangePathsOnly}
|
|
949
1164
|
naming={naming}
|
|
950
1165
|
namingIsland={Boolean(namingIslandId)}
|
|
951
1166
|
onCommitIslandName={(name) => {
|
|
@@ -966,6 +1181,10 @@ function Explorer({
|
|
|
966
1181
|
onMapAddFile={placeBlockOnFolder}
|
|
967
1182
|
onMapAddFolder={placeIslandOnFolder}
|
|
968
1183
|
onInspectFile={inspectFile}
|
|
1184
|
+
onInspectBlock={inspectBlock}
|
|
1185
|
+
plannedIds={plannedIds}
|
|
1186
|
+
createdIds={plannedCreates}
|
|
1187
|
+
deletedIds={deletedIds}
|
|
969
1188
|
/>
|
|
970
1189
|
</>
|
|
971
1190
|
)
|