@jkwd/inbase 0.1.19 → 0.1.21
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 +1 -1
- package/apps/explorer/scripts/patch-lib.d.ts +1 -0
- package/apps/explorer/scripts/patch-lib.mjs +1 -0
- package/apps/explorer/scripts/scan-ignore.mjs +9 -0
- package/apps/explorer/scripts/session-store.d.ts +3 -0
- package/apps/explorer/scripts/session-store.mjs +54 -2
- package/apps/explorer/src/App.tsx +119 -3
- package/apps/explorer/src/agentIntent.ts +10 -1
- package/apps/explorer/src/index.css +9 -0
- package/apps/explorer/src/scene/FileBlock.tsx +27 -0
- package/apps/explorer/src/scene/FolderArea.tsx +21 -0
- package/apps/explorer/src/scene/SelectionThumbnail.tsx +66 -34
- package/apps/explorer/src/scene/World.tsx +22 -3
- package/apps/explorer/src/types.ts +10 -0
- package/apps/explorer/src/ui/CanvasErrorBoundary.tsx +2 -0
- package/apps/explorer/src/ui/EyeIcon.tsx +26 -0
- package/apps/explorer/src/ui/HUD.tsx +183 -24
- package/apps/explorer/src/ui/MapContextMenu.tsx +18 -1
- package/apps/explorer/src/userCreated.ts +97 -0
- package/apps/explorer/vite.config.ts +4 -0
- package/package.json +1 -1
- package/skill/inbase/SKILL.md +17 -20
package/README.md
CHANGED
|
@@ -55,7 +55,7 @@ A normal chat request does not open a session. Use `/inbase` after Setup LLM ses
|
|
|
55
55
|
|
|
56
56
|
If several sessions are open, `/inbase` attaches the oldest session that is still waiting. Sessions that already have an LLM are skipped, and the map window does not need to be focused.
|
|
57
57
|
|
|
58
|
-
Turn on **Make LLM look where I look** if the agent should prefer the island you are standing on and the blocks you are facing. With **Step by step** on, click **Create proposal** to start a step, then **Accept proposal** when the patch is ready. With it off, the LLM implements the full plan; you can still walk Previous/Next over the diffs, then **Accept proposal**. Send an alternative instruction from the HUD to
|
|
58
|
+
Turn on **Make LLM look where I look** if the agent should prefer the island you are standing on and the blocks you are facing. With **Step by step** on, click **Create proposal** to start a step, then **Accept proposal** when the patch is ready. With it off, the LLM implements the full plan; you can still walk Previous/Next over the diffs, then **Accept proposal**. Send an alternative instruction from the HUD to update the current proposal, or **Stop** to end the session.
|
|
59
59
|
|
|
60
60
|
When no LLM is currently making changes, turn on **Show branch changes** (or press **G**) to highlight the current git branch against its base — committed, unstaged, and untracked files — instead of LLM patch files. The control is disabled while an LLM session is writing or reviewing a patch.
|
|
61
61
|
|
|
@@ -17,9 +17,18 @@ export function toPosix(filePath) {
|
|
|
17
17
|
return filePath.split(path.sep).join('/')
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
function isViteDepCache(parts) {
|
|
21
|
+
const viteAt = parts.indexOf('vite')
|
|
22
|
+
if (viteAt < 0) return false
|
|
23
|
+
return parts
|
|
24
|
+
.slice(viteAt + 1)
|
|
25
|
+
.some((part) => part === 'deps' || part.startsWith('deps_temp'))
|
|
26
|
+
}
|
|
27
|
+
|
|
20
28
|
/** True when any path segment is ignored, e.g. apps/web/node_modules/pkg/index.js. */
|
|
21
29
|
export function shouldIgnoreRelativePath(relative) {
|
|
22
30
|
const parts = toPosix(relative).split('/').filter(Boolean)
|
|
31
|
+
if (isViteDepCache(parts)) return true
|
|
23
32
|
if (
|
|
24
33
|
parts.some(
|
|
25
34
|
(part) =>
|
|
@@ -185,6 +185,7 @@ export type SessionBlueprint = {
|
|
|
185
185
|
addedVariables: unknown[]
|
|
186
186
|
addedImports: unknown[]
|
|
187
187
|
notes: unknown[]
|
|
188
|
+
pointers: unknown[]
|
|
188
189
|
}
|
|
189
190
|
export function emptyBlueprint(): SessionBlueprint
|
|
190
191
|
export function readBlueprint(dataDir: string, sessionId?: string): SessionBlueprint
|
|
@@ -224,6 +225,7 @@ export function updateBlueprint(
|
|
|
224
225
|
addedVariables?: unknown[]
|
|
225
226
|
addedImports?: unknown[]
|
|
226
227
|
notes?: unknown[]
|
|
228
|
+
pointers?: unknown[]
|
|
227
229
|
},
|
|
228
230
|
): SessionBlueprint
|
|
229
231
|
export function sendBlueprint(
|
|
@@ -236,6 +238,7 @@ export function sendBlueprint(
|
|
|
236
238
|
addedVariables?: unknown[]
|
|
237
239
|
addedImports?: unknown[]
|
|
238
240
|
notes?: unknown[]
|
|
241
|
+
pointers?: unknown[]
|
|
239
242
|
},
|
|
240
243
|
): DiffManifest
|
|
241
244
|
export function maybeStartVisualizerHandshake(
|
|
@@ -424,7 +424,8 @@ function blueprintHasContent(blueprint) {
|
|
|
424
424
|
(blueprint.addedFunctions?.length ?? 0) > 0 ||
|
|
425
425
|
(blueprint.addedVariables?.length ?? 0) > 0 ||
|
|
426
426
|
(blueprint.addedImports?.length ?? 0) > 0 ||
|
|
427
|
-
(blueprint.notes?.length ?? 0) > 0
|
|
427
|
+
(blueprint.notes?.length ?? 0) > 0 ||
|
|
428
|
+
(blueprint.pointers?.length ?? 0) > 0
|
|
428
429
|
)
|
|
429
430
|
}
|
|
430
431
|
|
|
@@ -801,6 +802,7 @@ export function sessionIntent(
|
|
|
801
802
|
blueprintVariables: blueprint.addedVariables,
|
|
802
803
|
blueprintImports: blueprint.addedImports,
|
|
803
804
|
blueprintNotes: blueprint.notes,
|
|
805
|
+
blueprintPointers: blueprint.pointers,
|
|
804
806
|
}
|
|
805
807
|
}
|
|
806
808
|
|
|
@@ -1020,11 +1022,21 @@ export function autoAdvance(dataDir, sessionId, targetRoot = null) {
|
|
|
1020
1022
|
const active = manifest.diffs.at(-1)
|
|
1021
1023
|
if (!active || active.status !== 'pending') return manifest
|
|
1022
1024
|
if (active.step >= manifest.steps.length) return manifest
|
|
1025
|
+
if (isRevisedProposal(manifest, active)) return manifest
|
|
1023
1026
|
return invokeStep(dataDir, sessionId, active.step + 1, targetRoot)
|
|
1024
1027
|
}
|
|
1025
1028
|
return manifest
|
|
1026
1029
|
}
|
|
1027
1030
|
|
|
1031
|
+
function isRevisedProposal(manifest, active) {
|
|
1032
|
+
return manifest.diffs.some(
|
|
1033
|
+
(entry) =>
|
|
1034
|
+
entry.id !== active.id &&
|
|
1035
|
+
entry.step === active.step &&
|
|
1036
|
+
(entry.status === 'extended' || entry.status === 'extend'),
|
|
1037
|
+
)
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1028
1040
|
export function setStepByStep(dataDir, sessionId, enabled, targetRoot = null) {
|
|
1029
1041
|
const manifest = requireManifest(dataDir, sessionId)
|
|
1030
1042
|
manifest.stepByStep = Boolean(enabled)
|
|
@@ -1277,6 +1289,7 @@ export function updateBlueprint(dataDir, _sessionId, input = {}) {
|
|
|
1277
1289
|
addedVariables: input.addedVariables ?? current.addedVariables,
|
|
1278
1290
|
addedImports: input.addedImports ?? current.addedImports,
|
|
1279
1291
|
notes: input.notes ?? current.notes,
|
|
1292
|
+
pointers: input.pointers ?? current.pointers,
|
|
1280
1293
|
}
|
|
1281
1294
|
return writeBlueprint(dataDir, next)
|
|
1282
1295
|
}
|
|
@@ -1293,7 +1306,8 @@ export function sendBlueprint(dataDir, sessionId, input = {}) {
|
|
|
1293
1306
|
input.addedFunctions ||
|
|
1294
1307
|
input.addedVariables ||
|
|
1295
1308
|
input.addedImports ||
|
|
1296
|
-
input.notes
|
|
1309
|
+
input.notes ||
|
|
1310
|
+
input.pointers
|
|
1297
1311
|
) {
|
|
1298
1312
|
updateBlueprint(dataDir, safeId, input)
|
|
1299
1313
|
}
|
|
@@ -1755,6 +1769,7 @@ export function emptyBlueprint() {
|
|
|
1755
1769
|
addedVariables: [],
|
|
1756
1770
|
addedImports: [],
|
|
1757
1771
|
notes: [],
|
|
1772
|
+
pointers: [],
|
|
1758
1773
|
}
|
|
1759
1774
|
}
|
|
1760
1775
|
|
|
@@ -1849,6 +1864,36 @@ function namedBlueprintNotes(value) {
|
|
|
1849
1864
|
return notes
|
|
1850
1865
|
}
|
|
1851
1866
|
|
|
1867
|
+
function namedBlueprintPointers(value) {
|
|
1868
|
+
if (!Array.isArray(value)) return []
|
|
1869
|
+
const seen = new Set()
|
|
1870
|
+
const pointers = []
|
|
1871
|
+
for (const item of value) {
|
|
1872
|
+
if (!item || typeof item !== 'object') continue
|
|
1873
|
+
const path = typeof item.path === 'string' ? item.path.trim() : ''
|
|
1874
|
+
if (!path) continue
|
|
1875
|
+
let stored = null
|
|
1876
|
+
if (item.kind === 'file' || item.kind === 'folder') {
|
|
1877
|
+
stored = { kind: item.kind, path }
|
|
1878
|
+
} else if (
|
|
1879
|
+
(item.kind === 'function' || item.kind === 'variable') &&
|
|
1880
|
+
typeof item.name === 'string' &&
|
|
1881
|
+
item.name.trim() !== ''
|
|
1882
|
+
) {
|
|
1883
|
+
stored = { kind: item.kind, path, name: item.name.trim() }
|
|
1884
|
+
}
|
|
1885
|
+
if (!stored) continue
|
|
1886
|
+
const key =
|
|
1887
|
+
stored.kind === 'file' || stored.kind === 'folder'
|
|
1888
|
+
? `${stored.kind}:${stored.path}`
|
|
1889
|
+
: `${stored.kind}:${stored.path}:${stored.name}`
|
|
1890
|
+
if (seen.has(key)) continue
|
|
1891
|
+
seen.add(key)
|
|
1892
|
+
pointers.push(stored)
|
|
1893
|
+
}
|
|
1894
|
+
return pointers
|
|
1895
|
+
}
|
|
1896
|
+
|
|
1852
1897
|
function blueprintContentEqual(left, right) {
|
|
1853
1898
|
return (
|
|
1854
1899
|
JSON.stringify({
|
|
@@ -1858,6 +1903,7 @@ function blueprintContentEqual(left, right) {
|
|
|
1858
1903
|
addedVariables: left.addedVariables,
|
|
1859
1904
|
addedImports: left.addedImports,
|
|
1860
1905
|
notes: left.notes,
|
|
1906
|
+
pointers: left.pointers,
|
|
1861
1907
|
}) ===
|
|
1862
1908
|
JSON.stringify({
|
|
1863
1909
|
userCreatedBlocks: right.userCreatedBlocks,
|
|
@@ -1866,6 +1912,7 @@ function blueprintContentEqual(left, right) {
|
|
|
1866
1912
|
addedVariables: right.addedVariables,
|
|
1867
1913
|
addedImports: right.addedImports,
|
|
1868
1914
|
notes: right.notes,
|
|
1915
|
+
pointers: right.pointers,
|
|
1869
1916
|
})
|
|
1870
1917
|
)
|
|
1871
1918
|
}
|
|
@@ -1877,6 +1924,7 @@ function normalizeBlueprint(value) {
|
|
|
1877
1924
|
const addedVariables = namedBlueprintSymbols(value?.addedVariables)
|
|
1878
1925
|
const addedImports = namedBlueprintImportAdditions(value?.addedImports)
|
|
1879
1926
|
const notes = namedBlueprintNotes(value?.notes)
|
|
1927
|
+
const pointers = namedBlueprintPointers(value?.pointers)
|
|
1880
1928
|
const revision =
|
|
1881
1929
|
Number.isInteger(value?.revision) && value.revision >= 0 ? value.revision : 0
|
|
1882
1930
|
return {
|
|
@@ -1889,6 +1937,7 @@ function normalizeBlueprint(value) {
|
|
|
1889
1937
|
addedVariables,
|
|
1890
1938
|
addedImports,
|
|
1891
1939
|
notes,
|
|
1940
|
+
pointers,
|
|
1892
1941
|
}),
|
|
1893
1942
|
sent: true,
|
|
1894
1943
|
userCreatedBlocks,
|
|
@@ -1897,6 +1946,7 @@ function normalizeBlueprint(value) {
|
|
|
1897
1946
|
addedVariables,
|
|
1898
1947
|
addedImports,
|
|
1899
1948
|
notes,
|
|
1949
|
+
pointers,
|
|
1900
1950
|
}
|
|
1901
1951
|
}
|
|
1902
1952
|
|
|
@@ -1935,6 +1985,7 @@ export function writeBlueprint(dataDir, sessionIdOrBlueprint, maybeBlueprint) {
|
|
|
1935
1985
|
addedVariables: namedBlueprintSymbols(next.addedVariables),
|
|
1936
1986
|
addedImports: namedBlueprintImportAdditions(next.addedImports),
|
|
1937
1987
|
notes: namedBlueprintNotes(next.notes),
|
|
1988
|
+
pointers: namedBlueprintPointers(next.pointers),
|
|
1938
1989
|
},
|
|
1939
1990
|
null,
|
|
1940
1991
|
2,
|
|
@@ -1973,6 +2024,7 @@ export function cleanupBlueprint(dataDir, knownFileIds = [], knownFolderPaths =
|
|
|
1973
2024
|
addedVariables: current.addedVariables.filter((item) => !removedFiles.has(item.file)),
|
|
1974
2025
|
addedImports: current.addedImports.filter((item) => !removedFiles.has(item.file)),
|
|
1975
2026
|
notes: current.notes.filter((item) => !removedFiles.has(item.file)),
|
|
2027
|
+
pointers: current.pointers,
|
|
1976
2028
|
}
|
|
1977
2029
|
return writeBlueprint(dataDir, next)
|
|
1978
2030
|
}
|
|
@@ -29,17 +29,22 @@ import {
|
|
|
29
29
|
import {
|
|
30
30
|
defaultBlockSpot,
|
|
31
31
|
dropBlueprintFileNotes,
|
|
32
|
+
dropBlueprintFilePointers,
|
|
32
33
|
dropBlueprintSymbolNote,
|
|
34
|
+
dropBlueprintSymbolPointer,
|
|
35
|
+
findBlueprintPointer,
|
|
33
36
|
isBlueprintSymbolName,
|
|
34
37
|
namedCreatedBlocks,
|
|
35
38
|
namedCreatedIslands,
|
|
36
39
|
parseBlueprintImport,
|
|
37
40
|
parseBlueprintNotes,
|
|
41
|
+
parseBlueprintPointers,
|
|
38
42
|
parseUserCreatedBlocks,
|
|
39
43
|
parseUserCreatedIslands,
|
|
40
44
|
resolveCreatedFile,
|
|
41
45
|
resolveCreatedIsland,
|
|
42
46
|
setBlueprintNote,
|
|
47
|
+
toggleBlueprintPointer,
|
|
43
48
|
withBlueprintIntent,
|
|
44
49
|
withUserCreatedGraph,
|
|
45
50
|
withUserCreatedLayout,
|
|
@@ -51,6 +56,8 @@ import {
|
|
|
51
56
|
type AimedRelation,
|
|
52
57
|
type BlueprintNote,
|
|
53
58
|
type BlueprintNoteKind,
|
|
59
|
+
type BlueprintPointer,
|
|
60
|
+
type BlueprintPointerKind,
|
|
54
61
|
type CodebaseGraph,
|
|
55
62
|
type FlyTo,
|
|
56
63
|
type PatchImportAddition,
|
|
@@ -200,6 +207,9 @@ function Explorer({
|
|
|
200
207
|
PatchImportAddition[]
|
|
201
208
|
>([])
|
|
202
209
|
const [blueprintNotes, setBlueprintNotes] = useState<BlueprintNote[]>([])
|
|
210
|
+
const [blueprintPointers, setBlueprintPointers] = useState<BlueprintPointer[]>(
|
|
211
|
+
[],
|
|
212
|
+
)
|
|
203
213
|
const [blueprintHidden, setBlueprintHidden] = useState(false)
|
|
204
214
|
const userBlocksRef = useRef(userBlocks)
|
|
205
215
|
const userIslandsRef = useRef(userIslands)
|
|
@@ -207,6 +217,7 @@ function Explorer({
|
|
|
207
217
|
const blueprintVariablesRef = useRef(blueprintVariables)
|
|
208
218
|
const blueprintImportsRef = useRef(blueprintImports)
|
|
209
219
|
const blueprintNotesRef = useRef(blueprintNotes)
|
|
220
|
+
const blueprintPointersRef = useRef(blueprintPointers)
|
|
210
221
|
const notesDirty = useRef(false)
|
|
211
222
|
const blueprintPersistGen = useRef(0)
|
|
212
223
|
const notePersistTimer = useRef<number | null>(null)
|
|
@@ -216,6 +227,7 @@ function Explorer({
|
|
|
216
227
|
blueprintVariablesRef.current = blueprintVariables
|
|
217
228
|
blueprintImportsRef.current = blueprintImports
|
|
218
229
|
blueprintNotesRef.current = blueprintNotes
|
|
230
|
+
blueprintPointersRef.current = blueprintPointers
|
|
219
231
|
const namingId = userBlocks.find((block) => block.naming)?.id ?? null
|
|
220
232
|
const namingIslandId = userIslands.find((island) => island.naming)?.id ?? null
|
|
221
233
|
const naming = Boolean(namingId || namingIslandId)
|
|
@@ -296,11 +308,15 @@ function Explorer({
|
|
|
296
308
|
for (const item of blueprintVariables) ids.add(item.file)
|
|
297
309
|
for (const item of blueprintImports) ids.add(item.file)
|
|
298
310
|
for (const note of blueprintNotes) ids.add(note.file)
|
|
311
|
+
for (const pointer of blueprintPointers) {
|
|
312
|
+
if (pointer.kind !== 'folder') ids.add(pointer.path)
|
|
313
|
+
}
|
|
299
314
|
return [...ids]
|
|
300
315
|
}, [
|
|
301
316
|
blueprintFunctions,
|
|
302
317
|
blueprintImports,
|
|
303
318
|
blueprintNotes,
|
|
319
|
+
blueprintPointers,
|
|
304
320
|
blueprintVariables,
|
|
305
321
|
changeSet.creates,
|
|
306
322
|
changeSet.deletes,
|
|
@@ -314,8 +330,11 @@ function Explorer({
|
|
|
314
330
|
if (island.path) paths.add(island.path)
|
|
315
331
|
else if (island.id) paths.add(island.id)
|
|
316
332
|
}
|
|
333
|
+
for (const pointer of blueprintPointers) {
|
|
334
|
+
if (pointer.kind === 'folder') paths.add(pointer.path)
|
|
335
|
+
}
|
|
317
336
|
return [...paths]
|
|
318
|
-
}, [changeSet.createFolders, userIslands])
|
|
337
|
+
}, [blueprintPointers, changeSet.createFolders, userIslands])
|
|
319
338
|
const hasChangeSet =
|
|
320
339
|
changeFileIds.length > 0 || changeFolderPaths.length > 0
|
|
321
340
|
const changePathGraph = useMemo(() => {
|
|
@@ -511,6 +530,7 @@ function Explorer({
|
|
|
511
530
|
addedVariables: blueprintVariables,
|
|
512
531
|
addedImports: blueprintImports,
|
|
513
532
|
notes: blueprintNotes,
|
|
533
|
+
pointers: blueprintPointers,
|
|
514
534
|
}
|
|
515
535
|
: {}),
|
|
516
536
|
},
|
|
@@ -534,6 +554,7 @@ function Explorer({
|
|
|
534
554
|
blueprintFunctions,
|
|
535
555
|
blueprintImports,
|
|
536
556
|
blueprintNotes,
|
|
557
|
+
blueprintPointers,
|
|
537
558
|
blueprintVariables,
|
|
538
559
|
intents,
|
|
539
560
|
onRefreshGraph,
|
|
@@ -618,6 +639,7 @@ function Explorer({
|
|
|
618
639
|
variables: PatchSymbolAddition[] = blueprintVariablesRef.current,
|
|
619
640
|
imports: PatchImportAddition[] = blueprintImportsRef.current,
|
|
620
641
|
notes: BlueprintNote[] = blueprintNotesRef.current,
|
|
642
|
+
pointers: BlueprintPointer[] = blueprintPointersRef.current,
|
|
621
643
|
) => {
|
|
622
644
|
if (notePersistTimer.current != null) {
|
|
623
645
|
window.clearTimeout(notePersistTimer.current)
|
|
@@ -632,6 +654,7 @@ function Explorer({
|
|
|
632
654
|
addedVariables: variables,
|
|
633
655
|
addedImports: imports,
|
|
634
656
|
notes,
|
|
657
|
+
pointers,
|
|
635
658
|
}).finally(() => {
|
|
636
659
|
if (gen !== blueprintPersistGen.current) return
|
|
637
660
|
if (notePersistTimer.current != null) return
|
|
@@ -664,6 +687,7 @@ function Explorer({
|
|
|
664
687
|
addedVariables: blueprintVariablesRef.current,
|
|
665
688
|
addedImports: blueprintImportsRef.current,
|
|
666
689
|
notes: blueprintNotesRef.current,
|
|
690
|
+
pointers: blueprintPointersRef.current,
|
|
667
691
|
})
|
|
668
692
|
notesDirty.current = false
|
|
669
693
|
}
|
|
@@ -684,6 +708,28 @@ function Explorer({
|
|
|
684
708
|
[persistBlueprintSoon],
|
|
685
709
|
)
|
|
686
710
|
|
|
711
|
+
const applyBlueprintPointer = useCallback(
|
|
712
|
+
(next: {
|
|
713
|
+
kind: BlueprintPointerKind
|
|
714
|
+
path: string
|
|
715
|
+
name?: string
|
|
716
|
+
}) => {
|
|
717
|
+
const pointers = toggleBlueprintPointer(blueprintPointersRef.current, next)
|
|
718
|
+
blueprintPointersRef.current = pointers
|
|
719
|
+
setBlueprintPointers(pointers)
|
|
720
|
+
persistBlueprint(
|
|
721
|
+
userBlocksRef.current,
|
|
722
|
+
userIslandsRef.current,
|
|
723
|
+
blueprintFunctionsRef.current,
|
|
724
|
+
blueprintVariablesRef.current,
|
|
725
|
+
blueprintImportsRef.current,
|
|
726
|
+
blueprintNotesRef.current,
|
|
727
|
+
pointers,
|
|
728
|
+
)
|
|
729
|
+
},
|
|
730
|
+
[persistBlueprint],
|
|
731
|
+
)
|
|
732
|
+
|
|
687
733
|
const placeBlock = useCallback(
|
|
688
734
|
(spot: { x: number; z: number; folder: string }) => {
|
|
689
735
|
if (!canPlace) return
|
|
@@ -832,10 +878,23 @@ function Explorer({
|
|
|
832
878
|
if (!selected || selected.naming) return false
|
|
833
879
|
const next = userBlocks.filter((block) => block.id !== selectedId)
|
|
834
880
|
const notes = dropBlueprintFileNotes(blueprintNotesRef.current, [selectedId])
|
|
881
|
+
const pointers = dropBlueprintFilePointers(blueprintPointersRef.current, [
|
|
882
|
+
selectedId,
|
|
883
|
+
])
|
|
835
884
|
blueprintNotesRef.current = notes
|
|
885
|
+
blueprintPointersRef.current = pointers
|
|
836
886
|
setUserBlocks(next)
|
|
837
887
|
setBlueprintNotes(notes)
|
|
838
|
-
|
|
888
|
+
setBlueprintPointers(pointers)
|
|
889
|
+
persistBlueprint(
|
|
890
|
+
next,
|
|
891
|
+
userIslands,
|
|
892
|
+
undefined,
|
|
893
|
+
undefined,
|
|
894
|
+
undefined,
|
|
895
|
+
notes,
|
|
896
|
+
pointers,
|
|
897
|
+
)
|
|
839
898
|
setSelectedId(null)
|
|
840
899
|
return true
|
|
841
900
|
}, [canPlace, persistBlueprint, selectedId, userBlocks, userIslands])
|
|
@@ -1000,9 +1059,17 @@ function Explorer({
|
|
|
1000
1059
|
'function',
|
|
1001
1060
|
name,
|
|
1002
1061
|
)
|
|
1062
|
+
const pointers = dropBlueprintSymbolPointer(
|
|
1063
|
+
blueprintPointersRef.current,
|
|
1064
|
+
fileId,
|
|
1065
|
+
'function',
|
|
1066
|
+
name,
|
|
1067
|
+
)
|
|
1003
1068
|
blueprintNotesRef.current = notes
|
|
1069
|
+
blueprintPointersRef.current = pointers
|
|
1004
1070
|
setBlueprintFunctions(next)
|
|
1005
1071
|
setBlueprintNotes(notes)
|
|
1072
|
+
setBlueprintPointers(pointers)
|
|
1006
1073
|
persistBlueprint(
|
|
1007
1074
|
userBlocks,
|
|
1008
1075
|
userIslands,
|
|
@@ -1010,6 +1077,7 @@ function Explorer({
|
|
|
1010
1077
|
blueprintVariables,
|
|
1011
1078
|
blueprintImports,
|
|
1012
1079
|
notes,
|
|
1080
|
+
pointers,
|
|
1013
1081
|
)
|
|
1014
1082
|
},
|
|
1015
1083
|
[
|
|
@@ -1035,9 +1103,17 @@ function Explorer({
|
|
|
1035
1103
|
'variable',
|
|
1036
1104
|
name,
|
|
1037
1105
|
)
|
|
1106
|
+
const pointers = dropBlueprintSymbolPointer(
|
|
1107
|
+
blueprintPointersRef.current,
|
|
1108
|
+
fileId,
|
|
1109
|
+
'variable',
|
|
1110
|
+
name,
|
|
1111
|
+
)
|
|
1038
1112
|
blueprintNotesRef.current = notes
|
|
1113
|
+
blueprintPointersRef.current = pointers
|
|
1039
1114
|
setBlueprintVariables(next)
|
|
1040
1115
|
setBlueprintNotes(notes)
|
|
1116
|
+
setBlueprintPointers(pointers)
|
|
1041
1117
|
persistBlueprint(
|
|
1042
1118
|
userBlocks,
|
|
1043
1119
|
userIslands,
|
|
@@ -1045,6 +1121,7 @@ function Explorer({
|
|
|
1045
1121
|
next,
|
|
1046
1122
|
blueprintImports,
|
|
1047
1123
|
notes,
|
|
1124
|
+
pointers,
|
|
1048
1125
|
)
|
|
1049
1126
|
},
|
|
1050
1127
|
[
|
|
@@ -1222,6 +1299,11 @@ function Explorer({
|
|
|
1222
1299
|
setBlueprintFunctions(bundle.blueprint.addedFunctions)
|
|
1223
1300
|
setBlueprintVariables(bundle.blueprint.addedVariables)
|
|
1224
1301
|
setBlueprintImports(bundle.blueprint.addedImports)
|
|
1302
|
+
if (!notesDirty.current) {
|
|
1303
|
+
const nextPointers = parseBlueprintPointers(bundle.blueprint.pointers)
|
|
1304
|
+
blueprintPointersRef.current = nextPointers
|
|
1305
|
+
setBlueprintPointers(nextPointers)
|
|
1306
|
+
}
|
|
1225
1307
|
if (!notesDirty.current && !isKeyboardIsolated()) {
|
|
1226
1308
|
const nextNotes = parseBlueprintNotes(bundle.blueprint.notes)
|
|
1227
1309
|
blueprintNotesRef.current = nextNotes
|
|
@@ -1305,7 +1387,8 @@ function Explorer({
|
|
|
1305
1387
|
blueprintFunctions.length > 0 ||
|
|
1306
1388
|
blueprintVariables.length > 0 ||
|
|
1307
1389
|
blueprintImports.length > 0 ||
|
|
1308
|
-
blueprintNotes.length > 0
|
|
1390
|
+
blueprintNotes.length > 0 ||
|
|
1391
|
+
blueprintPointers.length > 0
|
|
1309
1392
|
const blueprintCanCleanup =
|
|
1310
1393
|
userBlocks.some((block) => !block.naming && knownFileIds.has(block.id)) ||
|
|
1311
1394
|
userIslands.some(
|
|
@@ -1325,7 +1408,9 @@ function Explorer({
|
|
|
1325
1408
|
setBlueprintVariables([])
|
|
1326
1409
|
setBlueprintImports([])
|
|
1327
1410
|
blueprintNotesRef.current = []
|
|
1411
|
+
blueprintPointersRef.current = []
|
|
1328
1412
|
setBlueprintNotes([])
|
|
1413
|
+
setBlueprintPointers([])
|
|
1329
1414
|
void persistBlueprintClear()
|
|
1330
1415
|
}, [])
|
|
1331
1416
|
|
|
@@ -1366,6 +1451,13 @@ function Explorer({
|
|
|
1366
1451
|
shadows={false}
|
|
1367
1452
|
dpr={[1, 1.5]}
|
|
1368
1453
|
gl={{ antialias: true, toneMappingExposure: 1.25 }}
|
|
1454
|
+
onCreated={({ gl }) => {
|
|
1455
|
+
gl.domElement.addEventListener(
|
|
1456
|
+
'webglcontextlost',
|
|
1457
|
+
(event) => event.preventDefault(),
|
|
1458
|
+
{ once: false },
|
|
1459
|
+
)
|
|
1460
|
+
}}
|
|
1369
1461
|
camera={{
|
|
1370
1462
|
position: layout.spawn,
|
|
1371
1463
|
fov: 70,
|
|
@@ -1412,6 +1504,20 @@ function Explorer({
|
|
|
1412
1504
|
userCreatedBlocks={visibleBlocks}
|
|
1413
1505
|
userCreatedIslands={visibleIslands}
|
|
1414
1506
|
overlayBlocks={overlayBlocks}
|
|
1507
|
+
pointedFileIds={
|
|
1508
|
+
blueprintHidden
|
|
1509
|
+
? undefined
|
|
1510
|
+
: blueprintPointers.flatMap((item) =>
|
|
1511
|
+
item.kind === 'folder' ? [] : [item.path],
|
|
1512
|
+
)
|
|
1513
|
+
}
|
|
1514
|
+
pointedFolderPaths={
|
|
1515
|
+
blueprintHidden
|
|
1516
|
+
? undefined
|
|
1517
|
+
: blueprintPointers.flatMap((item) =>
|
|
1518
|
+
item.kind === 'folder' ? [item.path] : [],
|
|
1519
|
+
)
|
|
1520
|
+
}
|
|
1415
1521
|
mapGraph={
|
|
1416
1522
|
changePathsOnly && hasChangeSet ? changePathGraph : null
|
|
1417
1523
|
}
|
|
@@ -1470,6 +1576,7 @@ function Explorer({
|
|
|
1470
1576
|
blueprintVariables={blueprintVariables}
|
|
1471
1577
|
blueprintImports={blueprintImports}
|
|
1472
1578
|
blueprintNotes={blueprintNotes}
|
|
1579
|
+
blueprintPointers={blueprintPointers}
|
|
1473
1580
|
onAddBlueprintFunction={addBlueprintFunction}
|
|
1474
1581
|
onAddBlueprintVariable={addBlueprintVariable}
|
|
1475
1582
|
onAddBlueprintImport={addBlueprintImport}
|
|
@@ -1477,6 +1584,7 @@ function Explorer({
|
|
|
1477
1584
|
onRemoveBlueprintVariable={removeBlueprintVariable}
|
|
1478
1585
|
onRemoveBlueprintImport={removeBlueprintImport}
|
|
1479
1586
|
onSetBlueprintNote={applyBlueprintNote}
|
|
1587
|
+
onToggleBlueprintPointer={applyBlueprintPointer}
|
|
1480
1588
|
onMapAddFile={placeBlockOnFolder}
|
|
1481
1589
|
onMapAddFolder={placeIslandOnFolder}
|
|
1482
1590
|
onInspectFile={inspectFile}
|
|
@@ -1493,8 +1601,16 @@ function Explorer({
|
|
|
1493
1601
|
/>
|
|
1494
1602
|
<MapContextMenu
|
|
1495
1603
|
menu={mapMenu}
|
|
1604
|
+
pointed={
|
|
1605
|
+
mapMenu
|
|
1606
|
+
? findBlueprintPointer(blueprintPointers, 'folder', mapMenu.folder)
|
|
1607
|
+
: false
|
|
1608
|
+
}
|
|
1496
1609
|
onAddFile={placeBlockOnFolder}
|
|
1497
1610
|
onAddFolder={placeIslandOnFolder}
|
|
1611
|
+
onPointToFolder={(folder) =>
|
|
1612
|
+
applyBlueprintPointer({ kind: 'folder', path: folder })
|
|
1613
|
+
}
|
|
1498
1614
|
onClose={() => setMapMenu(null)}
|
|
1499
1615
|
/>
|
|
1500
1616
|
</>
|
|
@@ -2,6 +2,7 @@ import type {
|
|
|
2
2
|
AgentIntent,
|
|
3
3
|
AgentIntentBundle,
|
|
4
4
|
BlueprintNote,
|
|
5
|
+
BlueprintPointer,
|
|
5
6
|
PatchImport,
|
|
6
7
|
PatchImportAddition,
|
|
7
8
|
PatchSymbolAddition,
|
|
@@ -11,6 +12,7 @@ import type {
|
|
|
11
12
|
} from './types'
|
|
12
13
|
import {
|
|
13
14
|
parseBlueprintNotes,
|
|
15
|
+
parseBlueprintPointers,
|
|
14
16
|
parseUserCreatedBlocks,
|
|
15
17
|
parseUserCreatedIslands,
|
|
16
18
|
} from './userCreated'
|
|
@@ -133,6 +135,7 @@ export const emptyIntent: AgentIntent = {
|
|
|
133
135
|
blueprintVariables: [],
|
|
134
136
|
blueprintImports: [],
|
|
135
137
|
blueprintNotes: [],
|
|
138
|
+
blueprintPointers: [],
|
|
136
139
|
}
|
|
137
140
|
|
|
138
141
|
function normalize(data: Partial<AgentIntent> | null | undefined): AgentIntent {
|
|
@@ -192,6 +195,7 @@ function normalize(data: Partial<AgentIntent> | null | undefined): AgentIntent {
|
|
|
192
195
|
blueprintVariables: normalizeSymbolAdditions(data?.blueprintVariables),
|
|
193
196
|
blueprintImports: normalizeImportAdditions(data?.blueprintImports),
|
|
194
197
|
blueprintNotes: parseBlueprintNotes(data?.blueprintNotes),
|
|
198
|
+
blueprintPointers: parseBlueprintPointers(data?.blueprintPointers),
|
|
195
199
|
}
|
|
196
200
|
}
|
|
197
201
|
|
|
@@ -206,6 +210,7 @@ function normalizeBlueprint(data: Partial<AgentIntentBundle['blueprint']> | null
|
|
|
206
210
|
addedVariables: normalizeSymbolAdditions(data?.addedVariables),
|
|
207
211
|
addedImports: normalizeImportAdditions(data?.addedImports),
|
|
208
212
|
notes: parseBlueprintNotes(data?.notes),
|
|
213
|
+
pointers: parseBlueprintPointers(data?.pointers),
|
|
209
214
|
}
|
|
210
215
|
}
|
|
211
216
|
|
|
@@ -256,13 +261,15 @@ export async function fetchAgentIntents(): Promise<AgentIntentBundle> {
|
|
|
256
261
|
intent.blueprintFunctions.length > 0 ||
|
|
257
262
|
intent.blueprintVariables.length > 0 ||
|
|
258
263
|
intent.blueprintImports.length > 0 ||
|
|
259
|
-
intent.blueprintNotes.length > 0
|
|
264
|
+
intent.blueprintNotes.length > 0 ||
|
|
265
|
+
intent.blueprintPointers.length > 0,
|
|
260
266
|
userCreatedBlocks: intent.userCreatedBlocks,
|
|
261
267
|
userCreatedIslands: intent.userCreatedIslands,
|
|
262
268
|
addedFunctions: intent.blueprintFunctions,
|
|
263
269
|
addedVariables: intent.blueprintVariables,
|
|
264
270
|
addedImports: intent.blueprintImports,
|
|
265
271
|
notes: intent.blueprintNotes,
|
|
272
|
+
pointers: intent.blueprintPointers,
|
|
266
273
|
}),
|
|
267
274
|
}
|
|
268
275
|
}
|
|
@@ -295,6 +302,7 @@ export async function performAgentAction(
|
|
|
295
302
|
addedVariables?: PatchSymbolAddition[]
|
|
296
303
|
addedImports?: PatchImportAddition[]
|
|
297
304
|
notes?: BlueprintNote[]
|
|
305
|
+
pointers?: BlueprintPointer[]
|
|
298
306
|
} = {},
|
|
299
307
|
) {
|
|
300
308
|
const response = await fetch('/api/agent-intent', {
|
|
@@ -318,6 +326,7 @@ export function persistSessionBlueprint(
|
|
|
318
326
|
addedVariables?: PatchSymbolAddition[]
|
|
319
327
|
addedImports?: PatchImportAddition[]
|
|
320
328
|
notes?: BlueprintNote[]
|
|
329
|
+
pointers?: BlueprintPointer[]
|
|
321
330
|
},
|
|
322
331
|
) {
|
|
323
332
|
return fetch('/api/agent-intent', {
|
|
@@ -64,6 +64,15 @@ button {
|
|
|
64
64
|
font-size: 15px;
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
+
.canvas-error {
|
|
68
|
+
position: absolute;
|
|
69
|
+
inset: 0;
|
|
70
|
+
z-index: 2;
|
|
71
|
+
display: grid;
|
|
72
|
+
place-items: center;
|
|
73
|
+
background: var(--vc-chrome);
|
|
74
|
+
}
|
|
75
|
+
|
|
67
76
|
.canvas-error .hud-button {
|
|
68
77
|
margin-top: 12px;
|
|
69
78
|
}
|
|
@@ -2,6 +2,7 @@ import { memo, Suspense } from 'react'
|
|
|
2
2
|
import { Billboard, Edges, Html, Text } from '@react-three/drei'
|
|
3
3
|
import { CHANGE_HIGHLIGHT, CONFIG, dimColor, fileColor, FILE_SELECTION, MAP_SELECTION, type ChangeKind } from '../theme'
|
|
4
4
|
import { NameInput } from '../ui/NameInput'
|
|
5
|
+
import { EyeIcon } from '../ui/EyeIcon'
|
|
5
6
|
import { MapSelectBorder } from './MapSelectBorder'
|
|
6
7
|
import type { FileNode } from '../types'
|
|
7
8
|
import type { PlacedFile } from '../types'
|
|
@@ -30,6 +31,7 @@ type FileBlockProps = {
|
|
|
30
31
|
highlightMapChange?: boolean
|
|
31
32
|
previewLabels?: boolean
|
|
32
33
|
labelVisible?: boolean
|
|
34
|
+
pointed?: boolean
|
|
33
35
|
onCommitName?: (name: string) => void
|
|
34
36
|
onCancelName?: () => void
|
|
35
37
|
}
|
|
@@ -93,6 +95,7 @@ export const FileBlock = memo(function FileBlock({
|
|
|
93
95
|
highlightMapChange = mapMode,
|
|
94
96
|
previewLabels = false,
|
|
95
97
|
labelVisible = true,
|
|
98
|
+
pointed = false,
|
|
96
99
|
onCommitName,
|
|
97
100
|
onCancelName,
|
|
98
101
|
}: FileBlockProps) {
|
|
@@ -191,6 +194,30 @@ export const FileBlock = memo(function FileBlock({
|
|
|
191
194
|
{mapMode && !naming && mark && (
|
|
192
195
|
<MapChangeMark mark={mark} width={width} depth={depth} height={height} />
|
|
193
196
|
)}
|
|
197
|
+
{pointed && !naming && (
|
|
198
|
+
<Html
|
|
199
|
+
position={
|
|
200
|
+
mapMode
|
|
201
|
+
? mark
|
|
202
|
+
? [width * 0.28, height / 2 + 0.16, -depth * 0.28]
|
|
203
|
+
: [0, height / 2 + 0.12, 0]
|
|
204
|
+
: [0, height / 2 + (planned || highlight ? 0.92 : 0.74), 0]
|
|
205
|
+
}
|
|
206
|
+
center
|
|
207
|
+
occlude={false}
|
|
208
|
+
style={{ pointerEvents: 'none' }}
|
|
209
|
+
zIndexRange={[40, 0]}
|
|
210
|
+
>
|
|
211
|
+
<div
|
|
212
|
+
className="blueprint-eye"
|
|
213
|
+
data-map={mapMode ? 'true' : 'false'}
|
|
214
|
+
role="img"
|
|
215
|
+
aria-label="Keep in mind"
|
|
216
|
+
>
|
|
217
|
+
<EyeIcon size={mapMode ? 16 : 18} title="Keep in mind" />
|
|
218
|
+
</div>
|
|
219
|
+
</Html>
|
|
220
|
+
)}
|
|
194
221
|
{naming && onCommitName && onCancelName && (
|
|
195
222
|
<Html
|
|
196
223
|
position={
|