@jkwd/inbase 0.1.10 → 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 +22 -9
- 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 +5 -0
- package/apps/explorer/scripts/patch-lib.mjs +5 -0
- package/apps/explorer/scripts/scan-target.mjs +46 -13
- package/apps/explorer/scripts/session-store.d.ts +58 -1
- package/apps/explorer/scripts/session-store.mjs +276 -52
- package/apps/explorer/scripts/tree-diff.mjs +121 -0
- package/apps/explorer/src/App.tsx +132 -27
- package/apps/explorer/src/agentIntent.ts +63 -0
- package/apps/explorer/src/branchChanges.ts +54 -0
- package/apps/explorer/src/index.css +124 -0
- package/apps/explorer/src/scene/FileBlock.tsx +55 -5
- package/apps/explorer/src/types.ts +44 -0
- package/apps/explorer/src/ui/HUD.tsx +576 -92
- package/apps/explorer/src/userContext.ts +11 -0
- package/apps/explorer/vite.config.ts +61 -7
- 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 +141 -112
|
@@ -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, persistSessionFocus } 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,
|
|
@@ -53,7 +56,12 @@ function intentSignature(intent: AgentIntent) {
|
|
|
53
56
|
phase: intent.phase,
|
|
54
57
|
stalledWait: intent.stalledWait,
|
|
55
58
|
llmIdle: intent.llmIdle,
|
|
59
|
+
awaitingAttach: intent.awaitingAttach,
|
|
60
|
+
listening: intent.listening,
|
|
61
|
+
lastAck: intent.lastAck,
|
|
56
62
|
sessionId: intent.sessionId,
|
|
63
|
+
name: intent.name,
|
|
64
|
+
feature: intent.feature,
|
|
57
65
|
creationMode: intent.creationMode,
|
|
58
66
|
diffId: intent.diffId,
|
|
59
67
|
chain: intent.chain,
|
|
@@ -76,8 +84,15 @@ export default function App() {
|
|
|
76
84
|
const [updatingModel, setUpdatingModel] = useState(false)
|
|
77
85
|
const updatingModelRef = useRef(false)
|
|
78
86
|
|
|
87
|
+
const graphSig = useRef<string | null>(null)
|
|
79
88
|
const applyGraph = useCallback((next: CodebaseGraph | null, failed: string) => {
|
|
80
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
|
|
81
96
|
setGraph(next)
|
|
82
97
|
setLoadError(null)
|
|
83
98
|
return true
|
|
@@ -137,13 +152,27 @@ function Explorer({
|
|
|
137
152
|
}) {
|
|
138
153
|
const [intents, setIntents] = useState<AgentIntent[]>([])
|
|
139
154
|
const [focusedSessionId, setFocusedSessionId] = useState<string | null>(null)
|
|
155
|
+
const [wantBranchChanges, setWantBranchChanges] = useState(false)
|
|
156
|
+
const [branchChanges, setBranchChanges] = useState(emptyBranchChanges)
|
|
140
157
|
const intent =
|
|
141
158
|
intents.find((item) => item.sessionId === focusedSessionId) ??
|
|
142
159
|
intents[0] ??
|
|
143
160
|
emptyIntent
|
|
144
161
|
const canPlace = Boolean(intent.sessionId && intent.creationMode)
|
|
145
|
-
const
|
|
146
|
-
const
|
|
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 : []
|
|
147
176
|
const [userBlocks, setUserBlocks] = useState<UserCreatedBlock[]>([])
|
|
148
177
|
const [userIslands, setUserIslands] = useState<UserCreatedIsland[]>([])
|
|
149
178
|
const [blueprintFunctions, setBlueprintFunctions] = useState<
|
|
@@ -163,15 +192,15 @@ function Explorer({
|
|
|
163
192
|
return withPreviewGraph(
|
|
164
193
|
graph,
|
|
165
194
|
plannedCreates,
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
195
|
+
changeSet.createLines ?? {},
|
|
196
|
+
changeSet.createFolders ?? [],
|
|
197
|
+
changeSet.imports ?? [],
|
|
169
198
|
)
|
|
170
199
|
}, [
|
|
200
|
+
changeSet.createFolders,
|
|
201
|
+
changeSet.createLines,
|
|
202
|
+
changeSet.imports,
|
|
171
203
|
graph,
|
|
172
|
-
intent.createFolders,
|
|
173
|
-
intent.createLines,
|
|
174
|
-
intent.imports,
|
|
175
204
|
plannedCreates,
|
|
176
205
|
previewing,
|
|
177
206
|
])
|
|
@@ -194,14 +223,20 @@ function Explorer({
|
|
|
194
223
|
)
|
|
195
224
|
const layout = useMemo(() => {
|
|
196
225
|
const world = layoutWorld(previewGraph)
|
|
197
|
-
if (previewing) markCreatedFolders(world,
|
|
226
|
+
if (previewing) markCreatedFolders(world, changeSet.createFolders ?? [])
|
|
198
227
|
return withUserCreatedLayout(world, userBlocks, userIslands)
|
|
199
|
-
}, [
|
|
228
|
+
}, [
|
|
229
|
+
changeSet.createFolders,
|
|
230
|
+
previewGraph,
|
|
231
|
+
previewing,
|
|
232
|
+
userBlocks,
|
|
233
|
+
userIslands,
|
|
234
|
+
])
|
|
200
235
|
const changeFileIds = useMemo(() => {
|
|
201
236
|
const ids = new Set<string>()
|
|
202
|
-
for (const id of
|
|
203
|
-
for (const id of
|
|
204
|
-
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)
|
|
205
240
|
for (const block of userBlocks) ids.add(block.id)
|
|
206
241
|
for (const item of blueprintFunctions) ids.add(item.file)
|
|
207
242
|
for (const item of blueprintVariables) ids.add(item.file)
|
|
@@ -211,20 +246,20 @@ function Explorer({
|
|
|
211
246
|
blueprintFunctions,
|
|
212
247
|
blueprintImports,
|
|
213
248
|
blueprintVariables,
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
249
|
+
changeSet.creates,
|
|
250
|
+
changeSet.deletes,
|
|
251
|
+
changeSet.files,
|
|
217
252
|
userBlocks,
|
|
218
253
|
])
|
|
219
254
|
const changeFolderPaths = useMemo(() => {
|
|
220
255
|
const paths = new Set<string>()
|
|
221
|
-
for (const path of
|
|
256
|
+
for (const path of changeSet.createFolders ?? []) paths.add(path)
|
|
222
257
|
for (const island of userIslands) {
|
|
223
258
|
if (island.path) paths.add(island.path)
|
|
224
259
|
else if (island.id) paths.add(island.id)
|
|
225
260
|
}
|
|
226
261
|
return [...paths]
|
|
227
|
-
}, [
|
|
262
|
+
}, [changeSet.createFolders, userIslands])
|
|
228
263
|
const hasChangeSet =
|
|
229
264
|
changeFileIds.length > 0 || changeFolderPaths.length > 0
|
|
230
265
|
const changePathGraph = useMemo(() => {
|
|
@@ -239,14 +274,14 @@ function Explorer({
|
|
|
239
274
|
if (!hasChangeSet) return layout
|
|
240
275
|
const world = layoutWorld(changePathGraph)
|
|
241
276
|
markCreatedFolders(world, [
|
|
242
|
-
...(
|
|
277
|
+
...(changeSet.createFolders ?? []),
|
|
243
278
|
...userIslands.map((island) => island.path || island.id),
|
|
244
279
|
])
|
|
245
280
|
return world
|
|
246
281
|
}, [
|
|
247
282
|
changePathGraph,
|
|
283
|
+
changeSet.createFolders,
|
|
248
284
|
hasChangeSet,
|
|
249
|
-
intent.createFolders,
|
|
250
285
|
layout,
|
|
251
286
|
userIslands,
|
|
252
287
|
])
|
|
@@ -301,6 +336,14 @@ function Explorer({
|
|
|
301
336
|
persistSessionFocus(sessionId)
|
|
302
337
|
}, [])
|
|
303
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
|
+
|
|
304
347
|
const rememberWalk = useCallback((x: number, z: number) => {
|
|
305
348
|
walkPos.current = [x, z]
|
|
306
349
|
}, [])
|
|
@@ -418,7 +461,6 @@ function Explorer({
|
|
|
418
461
|
lastIntentSig.current = null
|
|
419
462
|
applyIntent(next, sessionId)
|
|
420
463
|
if (
|
|
421
|
-
action === 'invoke' ||
|
|
422
464
|
action === 'continue' ||
|
|
423
465
|
action === 'stop' ||
|
|
424
466
|
action === 'set_step_by_step'
|
|
@@ -509,6 +551,9 @@ function Explorer({
|
|
|
509
551
|
if (typeof context?.followLook === 'boolean') {
|
|
510
552
|
setFollowLook(context.followLook)
|
|
511
553
|
}
|
|
554
|
+
if (typeof context?.showBranchChanges === 'boolean') {
|
|
555
|
+
setWantBranchChanges(context.showBranchChanges)
|
|
556
|
+
}
|
|
512
557
|
})
|
|
513
558
|
return () => {
|
|
514
559
|
cancelled = true
|
|
@@ -1002,6 +1047,18 @@ function Explorer({
|
|
|
1002
1047
|
})
|
|
1003
1048
|
}, [])
|
|
1004
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
|
+
|
|
1005
1062
|
const toggleImportedBy = useCallback(() => {
|
|
1006
1063
|
setImportedBy((current) => !current)
|
|
1007
1064
|
}, [])
|
|
@@ -1031,6 +1088,48 @@ function Explorer({
|
|
|
1031
1088
|
return () => window.removeEventListener('keydown', onKey)
|
|
1032
1089
|
}, [toggleImportedBy])
|
|
1033
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
|
+
|
|
1034
1133
|
useEffect(() => {
|
|
1035
1134
|
if (mode !== 'map' || !hasChangeSet) return
|
|
1036
1135
|
const onKey = (event: KeyboardEvent) => {
|
|
@@ -1111,23 +1210,23 @@ function Explorer({
|
|
|
1111
1210
|
void poll()
|
|
1112
1211
|
const timer = window.setInterval(() => {
|
|
1113
1212
|
void poll()
|
|
1114
|
-
},
|
|
1213
|
+
}, 250)
|
|
1115
1214
|
return () => {
|
|
1116
1215
|
cancelled = true
|
|
1117
1216
|
window.clearInterval(timer)
|
|
1118
1217
|
}
|
|
1119
1218
|
}, [])
|
|
1120
1219
|
|
|
1121
|
-
const plannedIds = previewing ? [...
|
|
1220
|
+
const plannedIds = previewing ? [...changeSet.files, ...changeSet.creates] : []
|
|
1122
1221
|
const blueprintImportEdges = blueprintImports.flatMap((item) => {
|
|
1123
1222
|
if (!displayGraph.files.some((file) => file.id === item.from)) return []
|
|
1124
1223
|
return [{ from: item.file, to: item.from }]
|
|
1125
1224
|
})
|
|
1126
1225
|
const plannedImports = [
|
|
1127
|
-
...(previewing ? (
|
|
1226
|
+
...(previewing ? (changeSet.imports ?? []) : []),
|
|
1128
1227
|
...blueprintImportEdges,
|
|
1129
1228
|
]
|
|
1130
|
-
const deletedIds = previewing ?
|
|
1229
|
+
const deletedIds = previewing ? changeSet.deletes : []
|
|
1131
1230
|
|
|
1132
1231
|
return (
|
|
1133
1232
|
<>
|
|
@@ -1162,7 +1261,7 @@ function Explorer({
|
|
|
1162
1261
|
plannedImports={plannedImports}
|
|
1163
1262
|
createdIds={plannedCreates}
|
|
1164
1263
|
deletedIds={deletedIds}
|
|
1165
|
-
createLines={
|
|
1264
|
+
createLines={changeSet.createLines ?? {}}
|
|
1166
1265
|
flyTo={flyTo}
|
|
1167
1266
|
aimedRelation={aimedRelation}
|
|
1168
1267
|
onAimRelation={setAimedRelation}
|
|
@@ -1203,12 +1302,18 @@ function Explorer({
|
|
|
1203
1302
|
intents={intents}
|
|
1204
1303
|
focusedSessionId={focusedSessionId}
|
|
1205
1304
|
onFocusSession={focusSessionPanel}
|
|
1305
|
+
onSetupSession={setupLlmSession}
|
|
1206
1306
|
onWorkflowAction={runWorkflowAction}
|
|
1207
1307
|
onNavigateDiff={navigateDiff}
|
|
1208
1308
|
onOpenMap={openMap}
|
|
1209
1309
|
onWalk={openWalk}
|
|
1210
1310
|
followLook={followLook}
|
|
1211
1311
|
onToggleFollowLook={toggleFollowLook}
|
|
1312
|
+
showBranchChanges={showingBranchChanges}
|
|
1313
|
+
branchChanges={branchChanges}
|
|
1314
|
+
canShowBranchChanges={canToggleBranchChanges}
|
|
1315
|
+
llmMakingChanges={llmBusy}
|
|
1316
|
+
onToggleShowBranchChanges={toggleShowBranchChanges}
|
|
1212
1317
|
onUpdateModel={onUpdateModel}
|
|
1213
1318
|
updatingModel={updatingModel}
|
|
1214
1319
|
importedBy={importedBy}
|
|
@@ -45,10 +45,26 @@ function normalizeImportAdditions(value: unknown): PatchImportAddition[] {
|
|
|
45
45
|
})
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
+
function normalizeAck(
|
|
49
|
+
value: unknown,
|
|
50
|
+
): AgentIntent['lastAck'] {
|
|
51
|
+
if (!value || typeof value !== 'object') return null
|
|
52
|
+
const kind = (value as { kind?: unknown }).kind
|
|
53
|
+
if (typeof kind !== 'string' || kind.trim() === '') return null
|
|
54
|
+
const detail = (value as { detail?: unknown }).detail
|
|
55
|
+
const at = (value as { at?: unknown }).at
|
|
56
|
+
return {
|
|
57
|
+
kind,
|
|
58
|
+
detail: typeof detail === 'string' ? detail : '',
|
|
59
|
+
at: typeof at === 'string' ? at : null,
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
48
63
|
export const emptyIntent: AgentIntent = {
|
|
49
64
|
updatedAt: null,
|
|
50
65
|
showMap: false,
|
|
51
66
|
status: 'idle',
|
|
67
|
+
name: null,
|
|
52
68
|
feature: null,
|
|
53
69
|
steps: [],
|
|
54
70
|
step: null,
|
|
@@ -76,6 +92,10 @@ export const emptyIntent: AgentIntent = {
|
|
|
76
92
|
working: false,
|
|
77
93
|
stalledWait: false,
|
|
78
94
|
llmIdle: false,
|
|
95
|
+
awaitingAttach: false,
|
|
96
|
+
listening: false,
|
|
97
|
+
lastAck: null,
|
|
98
|
+
initialInstruction: null,
|
|
79
99
|
creationMode: false,
|
|
80
100
|
canEnterBlueprint: false,
|
|
81
101
|
blueprintSessionId: null,
|
|
@@ -91,6 +111,7 @@ function normalize(data: Partial<AgentIntent> | null | undefined): AgentIntent {
|
|
|
91
111
|
updatedAt: data?.updatedAt ?? null,
|
|
92
112
|
showMap: Boolean(data?.showMap),
|
|
93
113
|
status: data?.status ?? 'idle',
|
|
114
|
+
name: data?.name ?? null,
|
|
94
115
|
feature: data?.feature ?? null,
|
|
95
116
|
steps: Array.isArray(data?.steps) ? data.steps : [],
|
|
96
117
|
step: typeof data?.step === 'number' ? data.step : null,
|
|
@@ -121,6 +142,11 @@ function normalize(data: Partial<AgentIntent> | null | undefined): AgentIntent {
|
|
|
121
142
|
working: Boolean(data?.working),
|
|
122
143
|
stalledWait: Boolean(data?.stalledWait),
|
|
123
144
|
llmIdle: Boolean(data?.llmIdle),
|
|
145
|
+
awaitingAttach: Boolean(data?.awaitingAttach),
|
|
146
|
+
listening: Boolean(data?.listening),
|
|
147
|
+
lastAck: normalizeAck(data?.lastAck),
|
|
148
|
+
initialInstruction:
|
|
149
|
+
typeof data?.initialInstruction === 'string' ? data.initialInstruction : null,
|
|
124
150
|
creationMode: Boolean(data?.creationMode),
|
|
125
151
|
canEnterBlueprint: Boolean(data?.canEnterBlueprint),
|
|
126
152
|
blueprintSessionId:
|
|
@@ -224,6 +250,20 @@ export function persistSessionBlueprint(
|
|
|
224
250
|
})
|
|
225
251
|
}
|
|
226
252
|
|
|
253
|
+
export function persistInitialInstruction(sessionId: string, instruction: string) {
|
|
254
|
+
fetch('/api/agent-intent', {
|
|
255
|
+
method: 'POST',
|
|
256
|
+
headers: { 'Content-Type': 'application/json' },
|
|
257
|
+
body: JSON.stringify({
|
|
258
|
+
action: 'set_initial_instruction',
|
|
259
|
+
sessionId,
|
|
260
|
+
instruction,
|
|
261
|
+
}),
|
|
262
|
+
}).catch(() => {
|
|
263
|
+
// Keep the local instruction if the session handshake is no longer open.
|
|
264
|
+
})
|
|
265
|
+
}
|
|
266
|
+
|
|
227
267
|
export function persistSessionFocus(sessionId: string) {
|
|
228
268
|
fetch('/api/agent-intent', {
|
|
229
269
|
method: 'POST',
|
|
@@ -237,6 +277,29 @@ export function persistSessionFocus(sessionId: string) {
|
|
|
237
277
|
})
|
|
238
278
|
}
|
|
239
279
|
|
|
280
|
+
export async function setupVisualizerSession(name?: string) {
|
|
281
|
+
const response = await fetch('/api/agent-intent', {
|
|
282
|
+
method: 'POST',
|
|
283
|
+
headers: { 'Content-Type': 'application/json' },
|
|
284
|
+
body: JSON.stringify({
|
|
285
|
+
action: 'setup_session',
|
|
286
|
+
name,
|
|
287
|
+
}),
|
|
288
|
+
})
|
|
289
|
+
if (!response.ok) {
|
|
290
|
+
const detail = await response.text()
|
|
291
|
+
let message = detail || 'Could not set up the LLM session'
|
|
292
|
+
try {
|
|
293
|
+
const parsed = JSON.parse(detail) as { error?: string }
|
|
294
|
+
if (parsed?.error) message = parsed.error
|
|
295
|
+
} catch {
|
|
296
|
+
// Use the raw body when it is not JSON.
|
|
297
|
+
}
|
|
298
|
+
throw new Error(message)
|
|
299
|
+
}
|
|
300
|
+
return normalize((await response.json()) as AgentIntent)
|
|
301
|
+
}
|
|
302
|
+
|
|
240
303
|
export async function inspectTargetFile(payload: {
|
|
241
304
|
sessionId?: string | null
|
|
242
305
|
diffId?: string | null
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { BranchChanges } from './types'
|
|
2
|
+
|
|
3
|
+
export const emptyBranchChanges: BranchChanges = {
|
|
4
|
+
available: false,
|
|
5
|
+
branch: null,
|
|
6
|
+
base: null,
|
|
7
|
+
files: [],
|
|
8
|
+
creates: [],
|
|
9
|
+
deletes: [],
|
|
10
|
+
createFolders: [],
|
|
11
|
+
createLines: {},
|
|
12
|
+
imports: [],
|
|
13
|
+
addedFunctions: [],
|
|
14
|
+
addedVariables: [],
|
|
15
|
+
addedImports: [],
|
|
16
|
+
changedFunctions: [],
|
|
17
|
+
changedVariables: [],
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function normalize(data: Partial<BranchChanges> | null | undefined): BranchChanges {
|
|
21
|
+
return {
|
|
22
|
+
available: Boolean(data?.available),
|
|
23
|
+
branch: typeof data?.branch === 'string' ? data.branch : null,
|
|
24
|
+
base: typeof data?.base === 'string' ? data.base : null,
|
|
25
|
+
files: Array.isArray(data?.files) ? data.files : [],
|
|
26
|
+
creates: Array.isArray(data?.creates) ? data.creates : [],
|
|
27
|
+
deletes: Array.isArray(data?.deletes) ? data.deletes : [],
|
|
28
|
+
createFolders: Array.isArray(data?.createFolders) ? data.createFolders : [],
|
|
29
|
+
createLines:
|
|
30
|
+
data?.createLines && typeof data.createLines === 'object'
|
|
31
|
+
? data.createLines
|
|
32
|
+
: {},
|
|
33
|
+
imports: Array.isArray(data?.imports) ? data.imports : [],
|
|
34
|
+
addedFunctions: Array.isArray(data?.addedFunctions) ? data.addedFunctions : [],
|
|
35
|
+
addedVariables: Array.isArray(data?.addedVariables) ? data.addedVariables : [],
|
|
36
|
+
addedImports: Array.isArray(data?.addedImports) ? data.addedImports : [],
|
|
37
|
+
changedFunctions: Array.isArray(data?.changedFunctions)
|
|
38
|
+
? data.changedFunctions
|
|
39
|
+
: [],
|
|
40
|
+
changedVariables: Array.isArray(data?.changedVariables)
|
|
41
|
+
? data.changedVariables
|
|
42
|
+
: [],
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export async function fetchBranchChanges(): Promise<BranchChanges> {
|
|
47
|
+
try {
|
|
48
|
+
const response = await fetch(`/api/branch-changes?t=${Date.now()}`)
|
|
49
|
+
if (!response.ok) return emptyBranchChanges
|
|
50
|
+
return normalize((await response.json()) as BranchChanges)
|
|
51
|
+
} catch {
|
|
52
|
+
return emptyBranchChanges
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -251,6 +251,19 @@ button {
|
|
|
251
251
|
background: rgba(25, 28, 34, 0.94);
|
|
252
252
|
}
|
|
253
253
|
|
|
254
|
+
.map-change-mark {
|
|
255
|
+
pointer-events: none;
|
|
256
|
+
user-select: none;
|
|
257
|
+
color: #f4f7fb;
|
|
258
|
+
font-size: 32px;
|
|
259
|
+
font-weight: 800;
|
|
260
|
+
line-height: 1;
|
|
261
|
+
letter-spacing: 0;
|
|
262
|
+
-webkit-text-stroke: 0.09em #11151c;
|
|
263
|
+
paint-order: stroke fill;
|
|
264
|
+
text-shadow: 0 0 0.12em #11151c;
|
|
265
|
+
}
|
|
266
|
+
|
|
254
267
|
.map-you-are-here {
|
|
255
268
|
display: grid;
|
|
256
269
|
justify-items: center;
|
|
@@ -443,6 +456,11 @@ button {
|
|
|
443
456
|
flex: none;
|
|
444
457
|
}
|
|
445
458
|
|
|
459
|
+
.hud-icon-button[aria-disabled='true'] {
|
|
460
|
+
opacity: 0.55;
|
|
461
|
+
cursor: default;
|
|
462
|
+
}
|
|
463
|
+
|
|
446
464
|
.hud-tooltip {
|
|
447
465
|
position: absolute;
|
|
448
466
|
right: 0;
|
|
@@ -642,6 +660,11 @@ button {
|
|
|
642
660
|
margin: -4px 0 8px;
|
|
643
661
|
}
|
|
644
662
|
|
|
663
|
+
.hud-panel-chrome-heading {
|
|
664
|
+
min-width: 0;
|
|
665
|
+
flex: 1 1 auto;
|
|
666
|
+
}
|
|
667
|
+
|
|
645
668
|
.hud-panel[data-minimized='true'] {
|
|
646
669
|
overflow: hidden;
|
|
647
670
|
padding-bottom: 12px;
|
|
@@ -663,6 +686,24 @@ button {
|
|
|
663
686
|
white-space: nowrap;
|
|
664
687
|
}
|
|
665
688
|
|
|
689
|
+
.hud-panel-planned .hud-panel-chrome-title {
|
|
690
|
+
color: #e7ebf2;
|
|
691
|
+
font-size: 14px;
|
|
692
|
+
letter-spacing: 0.02em;
|
|
693
|
+
text-transform: none;
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
.hud-panel-chrome-subtitle {
|
|
697
|
+
overflow: hidden;
|
|
698
|
+
color: #8b95a5;
|
|
699
|
+
font-size: 10px;
|
|
700
|
+
font-weight: 600;
|
|
701
|
+
letter-spacing: 0.08em;
|
|
702
|
+
text-transform: uppercase;
|
|
703
|
+
text-overflow: ellipsis;
|
|
704
|
+
white-space: nowrap;
|
|
705
|
+
}
|
|
706
|
+
|
|
666
707
|
.hud-mode-switch {
|
|
667
708
|
display: flex;
|
|
668
709
|
align-items: center;
|
|
@@ -940,6 +981,52 @@ button {
|
|
|
940
981
|
font-size: 11px;
|
|
941
982
|
}
|
|
942
983
|
|
|
984
|
+
.hud-setup {
|
|
985
|
+
display: grid;
|
|
986
|
+
gap: 16px;
|
|
987
|
+
margin-top: 12px;
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
.hud-setup-heading {
|
|
991
|
+
display: flex;
|
|
992
|
+
align-items: center;
|
|
993
|
+
flex-wrap: wrap;
|
|
994
|
+
gap: 8px;
|
|
995
|
+
margin: 0 0 8px;
|
|
996
|
+
color: #8b95a5;
|
|
997
|
+
font-size: 11px;
|
|
998
|
+
font-weight: 600;
|
|
999
|
+
letter-spacing: 0.08em;
|
|
1000
|
+
text-transform: uppercase;
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
.hud-setup-tag {
|
|
1004
|
+
padding: 2px 6px;
|
|
1005
|
+
border: 1px solid #3a4250;
|
|
1006
|
+
border-radius: 3px;
|
|
1007
|
+
color: #8b95a5;
|
|
1008
|
+
font-size: 10px;
|
|
1009
|
+
font-weight: 600;
|
|
1010
|
+
letter-spacing: 0.04em;
|
|
1011
|
+
text-transform: none;
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
.hud-setup-tag[data-ready='true'] {
|
|
1015
|
+
border-color: #5d9ec4;
|
|
1016
|
+
color: #d7eef8;
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
.hud-setup-section p {
|
|
1020
|
+
margin: 0;
|
|
1021
|
+
color: #b7c0ce;
|
|
1022
|
+
font-size: 12px;
|
|
1023
|
+
line-height: 1.45;
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1026
|
+
.hud-setup-section .hud-instruction {
|
|
1027
|
+
margin-top: 0;
|
|
1028
|
+
}
|
|
1029
|
+
|
|
943
1030
|
.hud-instruction textarea {
|
|
944
1031
|
width: 100%;
|
|
945
1032
|
resize: vertical;
|
|
@@ -965,6 +1052,43 @@ button {
|
|
|
965
1052
|
font-size: 12px;
|
|
966
1053
|
}
|
|
967
1054
|
|
|
1055
|
+
.hud-live {
|
|
1056
|
+
display: flex;
|
|
1057
|
+
align-items: center;
|
|
1058
|
+
flex-wrap: wrap;
|
|
1059
|
+
gap: 9px;
|
|
1060
|
+
margin: 2px 0 10px;
|
|
1061
|
+
padding: 8px 10px;
|
|
1062
|
+
border-radius: 8px;
|
|
1063
|
+
background: rgba(154, 216, 255, 0.08);
|
|
1064
|
+
border: 1px solid rgba(154, 216, 255, 0.22);
|
|
1065
|
+
color: #d7eef8;
|
|
1066
|
+
font-size: 12px;
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1069
|
+
.hud-live[data-busy='false'] {
|
|
1070
|
+
background: rgba(255, 255, 255, 0.04);
|
|
1071
|
+
border-color: rgba(255, 255, 255, 0.12);
|
|
1072
|
+
color: #c5d0d8;
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
.hud-live[data-flash='true'] {
|
|
1076
|
+
animation: hud-live-flash 0.65s ease;
|
|
1077
|
+
}
|
|
1078
|
+
|
|
1079
|
+
.hud-live .hud-button {
|
|
1080
|
+
margin-left: auto;
|
|
1081
|
+
}
|
|
1082
|
+
|
|
1083
|
+
@keyframes hud-live-flash {
|
|
1084
|
+
from {
|
|
1085
|
+
background: rgba(154, 216, 255, 0.32);
|
|
1086
|
+
}
|
|
1087
|
+
to {
|
|
1088
|
+
background: rgba(154, 216, 255, 0.08);
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1091
|
+
|
|
968
1092
|
.hud-working .hud-button {
|
|
969
1093
|
margin-left: auto;
|
|
970
1094
|
}
|