@jkwd/inbase 0.1.1 → 0.1.4
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/apps/explorer/scripts/open-editor.d.ts +6 -0
- package/apps/explorer/scripts/open-editor.mjs +219 -0
- package/apps/explorer/scripts/session-store.d.ts +31 -1
- package/apps/explorer/scripts/session-store.mjs +153 -26
- package/apps/explorer/src/App.tsx +35 -4
- package/apps/explorer/src/agentIntent.ts +21 -0
- package/apps/explorer/src/index.css +81 -8
- package/apps/explorer/src/scene/FileBlock.tsx +1 -1
- package/apps/explorer/src/scene/FolderArea.tsx +5 -2
- package/apps/explorer/src/scene/MapView.tsx +61 -29
- package/apps/explorer/src/scene/World.tsx +4 -7
- package/apps/explorer/src/theme.ts +3 -0
- package/apps/explorer/src/ui/HUD.tsx +144 -109
- package/apps/explorer/vite.config.ts +39 -1
- package/bin/session.mjs +1 -1
- package/package.json +3 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useEffect, useRef, useState } from 'react'
|
|
1
|
+
import { useEffect, useRef, useState, type ReactNode } from 'react'
|
|
2
2
|
import { NameInput } from './NameInput'
|
|
3
3
|
import {
|
|
4
4
|
isReviewingIntent,
|
|
@@ -72,6 +72,25 @@ function PanelList({
|
|
|
72
72
|
)
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
+
function MutationFold({
|
|
76
|
+
hasContent,
|
|
77
|
+
children,
|
|
78
|
+
}: {
|
|
79
|
+
hasContent: boolean
|
|
80
|
+
children: ReactNode
|
|
81
|
+
}) {
|
|
82
|
+
if (!hasContent) return null
|
|
83
|
+
return (
|
|
84
|
+
<details
|
|
85
|
+
className="hud-fold"
|
|
86
|
+
onKeyDown={(event) => event.stopPropagation()}
|
|
87
|
+
>
|
|
88
|
+
<summary className="hud-section-title hud-fold-summary">Mutations</summary>
|
|
89
|
+
{children}
|
|
90
|
+
</details>
|
|
91
|
+
)
|
|
92
|
+
}
|
|
93
|
+
|
|
75
94
|
function AddIntentRow({
|
|
76
95
|
placeholder,
|
|
77
96
|
onAdd,
|
|
@@ -109,6 +128,7 @@ type HUDProps = {
|
|
|
109
128
|
mode: ViewMode
|
|
110
129
|
locked: boolean
|
|
111
130
|
selectedId: string | null
|
|
131
|
+
selectedTick?: number
|
|
112
132
|
selectedFolder?: string | null
|
|
113
133
|
aimedRelation: AimedRelation | null
|
|
114
134
|
currentFolder: string
|
|
@@ -143,6 +163,7 @@ type HUDProps = {
|
|
|
143
163
|
) => void
|
|
144
164
|
onMapAddFile?: (folderPath: string) => void
|
|
145
165
|
onMapAddFolder?: (folderPath: string) => void
|
|
166
|
+
onInspectFile?: (fileId: string) => void
|
|
146
167
|
}
|
|
147
168
|
|
|
148
169
|
export function HUD({
|
|
@@ -150,6 +171,7 @@ export function HUD({
|
|
|
150
171
|
mode,
|
|
151
172
|
locked,
|
|
152
173
|
selectedId,
|
|
174
|
+
selectedTick = 0,
|
|
153
175
|
selectedFolder = null,
|
|
154
176
|
aimedRelation,
|
|
155
177
|
currentFolder,
|
|
@@ -177,6 +199,7 @@ export function HUD({
|
|
|
177
199
|
onRemoveBlueprintImport,
|
|
178
200
|
onMapAddFile,
|
|
179
201
|
onMapAddFolder,
|
|
202
|
+
onInspectFile,
|
|
180
203
|
}: HUDProps) {
|
|
181
204
|
const selected = graph.files.find((file) => file.id === selectedId)
|
|
182
205
|
const selectedFolderNode = graph.folders.find(
|
|
@@ -280,6 +303,11 @@ export function HUD({
|
|
|
280
303
|
const canRunNext =
|
|
281
304
|
Boolean(nextStep) && (planReady || pending) && !working
|
|
282
305
|
const canComplete = pending && lastStep
|
|
306
|
+
const canInspectFile = (fileId: string, userCreated = false) =>
|
|
307
|
+
Boolean(onInspectFile) &&
|
|
308
|
+
!fileId.startsWith('draft:') &&
|
|
309
|
+
!(previewing && (intent.deletes ?? []).includes(fileId)) &&
|
|
310
|
+
(!userCreated || (intent.creates ?? []).includes(fileId))
|
|
283
311
|
const panelDone =
|
|
284
312
|
intent.status === 'finished' ||
|
|
285
313
|
intent.status === 'approved' ||
|
|
@@ -313,8 +341,8 @@ export function HUD({
|
|
|
313
341
|
}, [selectedId, selectedFolder])
|
|
314
342
|
|
|
315
343
|
useEffect(() => {
|
|
316
|
-
if (
|
|
317
|
-
}, [selectedId,
|
|
344
|
+
if (selectedId) setInfoVisible(true)
|
|
345
|
+
}, [selectedId, selectedTick])
|
|
318
346
|
|
|
319
347
|
useEffect(() => {
|
|
320
348
|
if (selectedFolder) setInfoVisible(true)
|
|
@@ -383,7 +411,7 @@ export function HUD({
|
|
|
383
411
|
, <kbd>Space</kbd> place a file, <kbd>B</kbd> place an island
|
|
384
412
|
</>
|
|
385
413
|
) : null}
|
|
386
|
-
, click a block for
|
|
414
|
+
, click a block for info.
|
|
387
415
|
</p>
|
|
388
416
|
</div>
|
|
389
417
|
</div>
|
|
@@ -539,6 +567,7 @@ export function HUD({
|
|
|
539
567
|
nextStep?.index === step.index && !doneSteps.has(step.index)
|
|
540
568
|
}
|
|
541
569
|
>
|
|
570
|
+
<span className="hud-step-index">{step.index}.</span>
|
|
542
571
|
<span className="hud-step-main">
|
|
543
572
|
<span className="hud-step-title">{step.title}</span>
|
|
544
573
|
{((canRunNext && nextStep?.index === step.index) ||
|
|
@@ -587,81 +616,91 @@ export function HUD({
|
|
|
587
616
|
</button>
|
|
588
617
|
</div>
|
|
589
618
|
)}
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
{id}
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
{id}
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
{id}
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
<PanelList
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
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)}
|
|
683
|
+
/>
|
|
684
|
+
<PanelList
|
|
685
|
+
title="Variables"
|
|
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>
|
|
665
704
|
{(planReady || preparing) && (
|
|
666
705
|
<div className="hud-decide">
|
|
667
706
|
<button
|
|
@@ -727,6 +766,15 @@ export function HUD({
|
|
|
727
766
|
<p>
|
|
728
767
|
{selected.lines} lines · {selected.language}
|
|
729
768
|
</p>
|
|
769
|
+
{canInspectFile(selected.id, selected.userCreated) && (
|
|
770
|
+
<button
|
|
771
|
+
className="hud-button hud-inspect"
|
|
772
|
+
type="button"
|
|
773
|
+
onClick={() => onInspectFile?.(selected.id)}
|
|
774
|
+
>
|
|
775
|
+
Inspect file
|
|
776
|
+
</button>
|
|
777
|
+
)}
|
|
730
778
|
{selectedClasses.length > 0 && (
|
|
731
779
|
<>
|
|
732
780
|
<div className="hud-section-title">Classes</div>
|
|
@@ -925,7 +973,18 @@ export function HUD({
|
|
|
925
973
|
) : (
|
|
926
974
|
<ul>
|
|
927
975
|
{folderFiles.map((file) => (
|
|
928
|
-
<li key={file.id}>
|
|
976
|
+
<li key={file.id}>
|
|
977
|
+
<span>{file.path || file.id}</span>
|
|
978
|
+
{canInspectFile(file.id, file.userCreated) && (
|
|
979
|
+
<button
|
|
980
|
+
className="hud-item-inspect"
|
|
981
|
+
type="button"
|
|
982
|
+
onClick={() => onInspectFile?.(file.id)}
|
|
983
|
+
>
|
|
984
|
+
Inspect
|
|
985
|
+
</button>
|
|
986
|
+
)}
|
|
987
|
+
</li>
|
|
929
988
|
))}
|
|
930
989
|
</ul>
|
|
931
990
|
)}
|
|
@@ -958,11 +1017,7 @@ export function HUD({
|
|
|
958
1017
|
<>
|
|
959
1018
|
<span>Scroll zoom</span>
|
|
960
1019
|
<span>Drag pan</span>
|
|
961
|
-
<span>
|
|
962
|
-
{sessionMode
|
|
963
|
-
? 'Click a block for info'
|
|
964
|
-
: 'Click a block for relations'}
|
|
965
|
-
</span>
|
|
1020
|
+
<span>Click a block for info</span>
|
|
966
1021
|
<span>Click an island for its files</span>
|
|
967
1022
|
{creatingBlueprint && (
|
|
968
1023
|
<>
|
|
@@ -971,19 +1026,11 @@ export function HUD({
|
|
|
971
1026
|
</>
|
|
972
1027
|
)}
|
|
973
1028
|
<span>Click a line to fly there</span>
|
|
974
|
-
<span>
|
|
1029
|
+
<span>Ctrl-click an island to walk</span>
|
|
975
1030
|
{selected?.userCreated && creatingBlueprint && (
|
|
976
1031
|
<span>Backspace delete</span>
|
|
977
1032
|
)}
|
|
978
|
-
<span>
|
|
979
|
-
{sessionMode
|
|
980
|
-
? infoVisible
|
|
981
|
-
? 'I hide info'
|
|
982
|
-
: 'Click a block for info'
|
|
983
|
-
: infoVisible
|
|
984
|
-
? 'I hide info'
|
|
985
|
-
: 'I show info'}
|
|
986
|
-
</span>
|
|
1033
|
+
<span>{infoVisible ? 'I hide info' : 'I show info'}</span>
|
|
987
1034
|
{infoVisible && <span>↑↓ scroll info</span>}
|
|
988
1035
|
<span>
|
|
989
1036
|
{importedBy ? 'K show imports' : 'K show imported by'}
|
|
@@ -1004,21 +1051,9 @@ export function HUD({
|
|
|
1004
1051
|
{selected?.userCreated && creatingBlueprint && (
|
|
1005
1052
|
<span>Backspace delete</span>
|
|
1006
1053
|
)}
|
|
1007
|
-
<span>
|
|
1008
|
-
{sessionMode
|
|
1009
|
-
? 'Click a block for info'
|
|
1010
|
-
: 'Click block for relations'}
|
|
1011
|
-
</span>
|
|
1054
|
+
<span>Click a block for info</span>
|
|
1012
1055
|
<span>Aim a line to fly</span>
|
|
1013
|
-
<span>
|
|
1014
|
-
{sessionMode
|
|
1015
|
-
? infoVisible
|
|
1016
|
-
? 'I hide info'
|
|
1017
|
-
: 'Click a block for info'
|
|
1018
|
-
: infoVisible
|
|
1019
|
-
? 'I hide info'
|
|
1020
|
-
: 'I show info'}
|
|
1021
|
-
</span>
|
|
1056
|
+
<span>{infoVisible ? 'I hide info' : 'I show info'}</span>
|
|
1022
1057
|
{infoVisible && <span>↑↓ scroll info</span>}
|
|
1023
1058
|
<span>
|
|
1024
1059
|
{importedBy ? 'K show imports' : 'K show imported by'}
|
|
@@ -7,9 +7,11 @@ import { fileURLToPath } from 'node:url'
|
|
|
7
7
|
import type { IncomingMessage, ServerResponse } from 'node:http'
|
|
8
8
|
import { emptyIntent } from './scripts/patch-lib.mjs'
|
|
9
9
|
import { dataDir, targetRoot } from './scripts/target-config.mjs'
|
|
10
|
+
import { editorFileUri, openInEditor } from './scripts/open-editor.mjs'
|
|
10
11
|
import {
|
|
11
12
|
answerBlueprint,
|
|
12
13
|
continueDiff,
|
|
14
|
+
inspectTargetFile,
|
|
13
15
|
invokeStep,
|
|
14
16
|
readActiveSession,
|
|
15
17
|
requestReplan,
|
|
@@ -105,10 +107,46 @@ function jsonFilePlugin(): Plugin {
|
|
|
105
107
|
|
|
106
108
|
next()
|
|
107
109
|
})
|
|
110
|
+
|
|
111
|
+
server.middlewares.use('/api/inspect-file', (req, res, next) => {
|
|
112
|
+
if (req.method === 'POST') {
|
|
113
|
+
void inspectFile(req, res)
|
|
114
|
+
return
|
|
115
|
+
}
|
|
116
|
+
next()
|
|
117
|
+
})
|
|
108
118
|
},
|
|
109
119
|
}
|
|
110
120
|
}
|
|
111
121
|
|
|
122
|
+
async function inspectFile(req: IncomingMessage, res: ServerResponse) {
|
|
123
|
+
try {
|
|
124
|
+
const body = JSON.parse(await readBody(req)) as {
|
|
125
|
+
sessionId?: string
|
|
126
|
+
diffId?: string
|
|
127
|
+
fileId?: string
|
|
128
|
+
}
|
|
129
|
+
const filePath = inspectTargetFile(dataDir, targetRoot, {
|
|
130
|
+
sessionId: body.sessionId,
|
|
131
|
+
diffId: body.diffId,
|
|
132
|
+
fileId: body.fileId,
|
|
133
|
+
})
|
|
134
|
+
if (!filePath) {
|
|
135
|
+
sendJson(res, 200, { path: null, uri: null, opened: false })
|
|
136
|
+
return
|
|
137
|
+
}
|
|
138
|
+
const opened = openInEditor(filePath)
|
|
139
|
+
sendJson(res, 200, {
|
|
140
|
+
path: filePath,
|
|
141
|
+
uri: editorFileUri(filePath),
|
|
142
|
+
opened,
|
|
143
|
+
})
|
|
144
|
+
} catch (error) {
|
|
145
|
+
const message = error instanceof Error ? error.message : 'invalid request'
|
|
146
|
+
sendJson(res, 400, { error: message })
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
112
150
|
async function decideIntent(req: IncomingMessage, res: ServerResponse) {
|
|
113
151
|
try {
|
|
114
152
|
const body = JSON.parse(await readBody(req)) as {
|
|
@@ -209,7 +247,7 @@ async function decideIntent(req: IncomingMessage, res: ServerResponse) {
|
|
|
209
247
|
addedImports: body.addedImports,
|
|
210
248
|
})
|
|
211
249
|
} else {
|
|
212
|
-
stopSession(dataDir, body.sessionId,
|
|
250
|
+
stopSession(dataDir, body.sessionId, targetRoot)
|
|
213
251
|
}
|
|
214
252
|
const next = sessionIntent(dataDir, body.sessionId, knownFileIds())
|
|
215
253
|
sendJson(res, 200, next ?? { ...emptyIntent })
|
package/bin/session.mjs
CHANGED
|
@@ -201,7 +201,7 @@ export async function proposePatch(args) {
|
|
|
201
201
|
|
|
202
202
|
if (clear) {
|
|
203
203
|
if (!sessionId) usage('propose-patch', '--session <cursor-chat-id> --clear')
|
|
204
|
-
store.stopSession(config.dataDir, sessionId)
|
|
204
|
+
store.stopSession(config.dataDir, sessionId, config.targetRoot)
|
|
205
205
|
console.log(
|
|
206
206
|
`Cleared session ${sessionId}; stored diffs and blueprint drafts were removed.`,
|
|
207
207
|
)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jkwd/inbase",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"description": "A first-person 3D map of a codebase, with a visual coding workflow for Cursor.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -29,6 +29,8 @@
|
|
|
29
29
|
"apps/explorer/src/scene",
|
|
30
30
|
"apps/explorer/src/ui",
|
|
31
31
|
"apps/explorer/scripts/js-source.mjs",
|
|
32
|
+
"apps/explorer/scripts/open-editor.d.ts",
|
|
33
|
+
"apps/explorer/scripts/open-editor.mjs",
|
|
32
34
|
"apps/explorer/scripts/patch-lib.d.ts",
|
|
33
35
|
"apps/explorer/scripts/patch-lib.mjs",
|
|
34
36
|
"apps/explorer/scripts/scan-target.mjs",
|