@jkwd/inbase 0.1.19 → 0.1.20
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/session-store.d.ts +3 -0
- package/apps/explorer/scripts/session-store.mjs +54 -2
- package/apps/explorer/src/App.tsx +112 -3
- package/apps/explorer/src/agentIntent.ts +10 -1
- 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 +16 -3
- package/apps/explorer/src/scene/World.tsx +22 -3
- package/apps/explorer/src/types.ts +10 -0
- package/apps/explorer/src/ui/EyeIcon.tsx +26 -0
- package/apps/explorer/src/ui/HUD.tsx +179 -23
- 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
|
@@ -30,6 +30,8 @@ type SelectionThumbnailProps = {
|
|
|
30
30
|
plannedIds?: string[]
|
|
31
31
|
createdIds?: string[]
|
|
32
32
|
deletedIds?: string[]
|
|
33
|
+
pointedFileIds?: string[]
|
|
34
|
+
pointedFolderPaths?: string[]
|
|
33
35
|
onMinimize?: () => void
|
|
34
36
|
onMaximize?: () => void
|
|
35
37
|
onHide: () => void
|
|
@@ -464,6 +466,8 @@ function ThumbnailScene({
|
|
|
464
466
|
planned,
|
|
465
467
|
created,
|
|
466
468
|
deleted,
|
|
469
|
+
pointedFiles,
|
|
470
|
+
pointedFolders,
|
|
467
471
|
zoom,
|
|
468
472
|
zoomRef,
|
|
469
473
|
panRef,
|
|
@@ -481,6 +485,8 @@ function ThumbnailScene({
|
|
|
481
485
|
planned: Set<string>
|
|
482
486
|
created: Set<string>
|
|
483
487
|
deleted: Set<string>
|
|
488
|
+
pointedFiles: Set<string>
|
|
489
|
+
pointedFolders: Set<string>
|
|
484
490
|
zoom: number
|
|
485
491
|
zoomRef: { current: number }
|
|
486
492
|
panRef: { current: Vec3 }
|
|
@@ -496,15 +502,18 @@ function ThumbnailScene({
|
|
|
496
502
|
const selected = file.id === target.selectedFileId
|
|
497
503
|
const related = target.relatedIds.has(file.id)
|
|
498
504
|
const changeKind = fileChangeKind(file.id, planned, created, deleted)
|
|
505
|
+
const pointed = pointedFiles.has(file.id)
|
|
499
506
|
const dimmed =
|
|
500
507
|
Boolean(target.selectedFileId) &&
|
|
501
508
|
!selected &&
|
|
502
509
|
!related &&
|
|
503
|
-
!changeKind
|
|
510
|
+
!changeKind &&
|
|
511
|
+
!pointed
|
|
504
512
|
let priority = placed.size[1]
|
|
505
513
|
if (selected) priority += 1000
|
|
506
514
|
else if (related) priority += 400
|
|
507
515
|
else if (changeKind) priority += 300
|
|
516
|
+
else if (pointed) priority += 250
|
|
508
517
|
else if (file.userCreated) priority += 80
|
|
509
518
|
if (dimmed) priority -= 200
|
|
510
519
|
return {
|
|
@@ -517,7 +526,7 @@ function ThumbnailScene({
|
|
|
517
526
|
priority,
|
|
518
527
|
}
|
|
519
528
|
}),
|
|
520
|
-
|
|
529
|
+
[created, deleted, planned, pointedFiles, target],
|
|
521
530
|
)
|
|
522
531
|
|
|
523
532
|
return (
|
|
@@ -554,12 +563,14 @@ function ThumbnailScene({
|
|
|
554
563
|
<FolderArea
|
|
555
564
|
folder={target.folder}
|
|
556
565
|
highlightKind={highlightedFolders[target.folder.path] ?? null}
|
|
566
|
+
pointed={pointedFolders.has(target.folder.path)}
|
|
557
567
|
previewLabels={zoom < 1.45}
|
|
558
568
|
/>
|
|
559
569
|
{target.files.map(({ file, placed }) => {
|
|
560
570
|
const selected = file.id === target.selectedFileId
|
|
561
571
|
const related = target.relatedIds.has(file.id)
|
|
562
572
|
const changeKind = fileChangeKind(file.id, planned, created, deleted)
|
|
573
|
+
const pointed = pointedFiles.has(file.id)
|
|
563
574
|
return (
|
|
564
575
|
<FileBlock
|
|
565
576
|
key={file.id}
|
|
@@ -570,6 +581,7 @@ function ThumbnailScene({
|
|
|
570
581
|
planned={Boolean(changeKind)}
|
|
571
582
|
changeKind={changeKind}
|
|
572
583
|
added={created.has(file.id) || file.userCreated}
|
|
584
|
+
pointed={pointed}
|
|
573
585
|
highlightMapChange
|
|
574
586
|
previewLabels
|
|
575
587
|
labelVisible={visibleLabelIds.has(file.id)}
|
|
@@ -577,7 +589,8 @@ function ThumbnailScene({
|
|
|
577
589
|
Boolean(target.selectedFileId) &&
|
|
578
590
|
!selected &&
|
|
579
591
|
!related &&
|
|
580
|
-
!changeKind
|
|
592
|
+
!changeKind &&
|
|
593
|
+
!pointed
|
|
581
594
|
}
|
|
582
595
|
/>
|
|
583
596
|
)
|
|
@@ -73,6 +73,8 @@ type WorldProps = {
|
|
|
73
73
|
userCreatedBlocks?: UserCreatedBlock[]
|
|
74
74
|
userCreatedIslands?: UserCreatedIsland[]
|
|
75
75
|
overlayBlocks?: UserCreatedBlock[]
|
|
76
|
+
pointedFileIds?: string[]
|
|
77
|
+
pointedFolderPaths?: string[]
|
|
76
78
|
namingIslandId?: string | null
|
|
77
79
|
mapGraph?: CodebaseGraph | null
|
|
78
80
|
mapLayout?: WorldLayout | null
|
|
@@ -117,11 +119,15 @@ export function World({
|
|
|
117
119
|
userCreatedBlocks = [],
|
|
118
120
|
userCreatedIslands = [],
|
|
119
121
|
overlayBlocks = [],
|
|
122
|
+
pointedFileIds = [],
|
|
123
|
+
pointedFolderPaths = [],
|
|
120
124
|
mapGraph = null,
|
|
121
125
|
mapLayout = null,
|
|
122
126
|
}: WorldProps) {
|
|
123
127
|
const created = new Set(createdIds)
|
|
124
128
|
const deleted = new Set(deletedIds)
|
|
129
|
+
const pointedFiles = new Set(pointedFileIds)
|
|
130
|
+
const pointedFolders = new Set(pointedFolderPaths)
|
|
125
131
|
const mapping = mode === 'map'
|
|
126
132
|
const viewGraph = mapping && mapGraph ? mapGraph : graph
|
|
127
133
|
const viewLayout = mapping && mapLayout ? mapLayout : layout
|
|
@@ -169,17 +175,19 @@ export function World({
|
|
|
169
175
|
if (selectedId) ids.add(selectedId)
|
|
170
176
|
if (namingId) ids.add(namingId)
|
|
171
177
|
if (aimedRelation?.flyTo) ids.add(aimedRelation.flyTo)
|
|
178
|
+
for (const id of pointedFileIds) ids.add(id)
|
|
172
179
|
if (ghostKey) {
|
|
173
180
|
for (const id of ghostKey.split('|')) ids.add(id)
|
|
174
181
|
}
|
|
175
182
|
return ids
|
|
176
|
-
}, [aimedRelation?.flyTo, ghostKey, namingId, selectedId])
|
|
183
|
+
}, [aimedRelation?.flyTo, ghostKey, namingId, pointedFileIds, selectedId])
|
|
177
184
|
const keepFolderPaths = useMemo(() => {
|
|
178
185
|
const paths = new Set<string>()
|
|
179
186
|
if (selectedFolder) paths.add(selectedFolder)
|
|
180
187
|
if (namingIslandId) paths.add(namingIslandId)
|
|
188
|
+
for (const path of pointedFolderPaths) paths.add(path)
|
|
181
189
|
return paths
|
|
182
|
-
}, [namingIslandId, selectedFolder])
|
|
190
|
+
}, [namingIslandId, pointedFolderPaths, selectedFolder])
|
|
183
191
|
const originLod = useMemo(() => {
|
|
184
192
|
if (mapping) return null
|
|
185
193
|
return computeWalkLod({
|
|
@@ -254,6 +262,7 @@ export function World({
|
|
|
254
262
|
highlightKind={
|
|
255
263
|
mapping ? highlightedFolders[folder.path] ?? null : null
|
|
256
264
|
}
|
|
265
|
+
pointed={pointedFolders.has(folder.path)}
|
|
257
266
|
labelVisible={!lod || lod.folderLabels.has(folder.path)}
|
|
258
267
|
onCommitName={
|
|
259
268
|
folder.path === namingIslandId && onCommitIslandName
|
|
@@ -285,14 +294,22 @@ export function World({
|
|
|
285
294
|
const changeKind = fileChangeKind(file.id, planned, created, deleted)
|
|
286
295
|
const naming = file.id === namingId
|
|
287
296
|
const aimed = file.id === aimedRelation?.flyTo
|
|
297
|
+
const pointed = pointedFiles.has(file.id)
|
|
288
298
|
const detailed =
|
|
289
|
-
selected ||
|
|
299
|
+
selected ||
|
|
300
|
+
isRelated ||
|
|
301
|
+
isPlanned ||
|
|
302
|
+
naming ||
|
|
303
|
+
aimed ||
|
|
304
|
+
pointed ||
|
|
305
|
+
Boolean(changeKind)
|
|
290
306
|
if (lod && !lod.files.has(file.id)) return null
|
|
291
307
|
const dimmed =
|
|
292
308
|
hasFocus &&
|
|
293
309
|
!selected &&
|
|
294
310
|
!isRelated &&
|
|
295
311
|
!changeKind &&
|
|
312
|
+
!pointed &&
|
|
296
313
|
!patchLinked.has(file.id) &&
|
|
297
314
|
!folderFileIds.has(file.id)
|
|
298
315
|
if (lod && !detailed && !lod.labels.has(file.id)) {
|
|
@@ -310,6 +327,7 @@ export function World({
|
|
|
310
327
|
changeKind={changeKind}
|
|
311
328
|
added={created.has(file.id) || file.userCreated}
|
|
312
329
|
aimed={aimed}
|
|
330
|
+
pointed={pointed}
|
|
313
331
|
dimmed={dimmed}
|
|
314
332
|
naming={naming}
|
|
315
333
|
mapMode={mapping}
|
|
@@ -347,6 +365,7 @@ export function World({
|
|
|
347
365
|
planned={false}
|
|
348
366
|
changeKind="add"
|
|
349
367
|
added
|
|
368
|
+
pointed={pointedFiles.has(file.id)}
|
|
350
369
|
dimmed={false}
|
|
351
370
|
mapMode={mapping}
|
|
352
371
|
labelVisible
|
|
@@ -225,6 +225,14 @@ export type BlueprintNote = {
|
|
|
225
225
|
note: string
|
|
226
226
|
}
|
|
227
227
|
|
|
228
|
+
export type BlueprintPointerKind = 'file' | 'folder' | 'function' | 'variable'
|
|
229
|
+
|
|
230
|
+
export type BlueprintPointer = {
|
|
231
|
+
kind: BlueprintPointerKind
|
|
232
|
+
path: string
|
|
233
|
+
name?: string
|
|
234
|
+
}
|
|
235
|
+
|
|
228
236
|
export type AimedRelation = {
|
|
229
237
|
from: string
|
|
230
238
|
to: string
|
|
@@ -275,6 +283,7 @@ export type SharedBlueprint = {
|
|
|
275
283
|
addedVariables: PatchSymbolAddition[]
|
|
276
284
|
addedImports: PatchImportAddition[]
|
|
277
285
|
notes: BlueprintNote[]
|
|
286
|
+
pointers: BlueprintPointer[]
|
|
278
287
|
}
|
|
279
288
|
|
|
280
289
|
export type AgentIntentBundle = {
|
|
@@ -341,4 +350,5 @@ export type AgentIntent = {
|
|
|
341
350
|
blueprintVariables: PatchSymbolAddition[]
|
|
342
351
|
blueprintImports: PatchImportAddition[]
|
|
343
352
|
blueprintNotes: BlueprintNote[]
|
|
353
|
+
blueprintPointers: BlueprintPointer[]
|
|
344
354
|
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export function EyeIcon({
|
|
2
|
+
size = 18,
|
|
3
|
+
title,
|
|
4
|
+
}: {
|
|
5
|
+
size?: number
|
|
6
|
+
title?: string
|
|
7
|
+
}) {
|
|
8
|
+
return (
|
|
9
|
+
<svg
|
|
10
|
+
viewBox="0 0 24 24"
|
|
11
|
+
width={size}
|
|
12
|
+
height={size}
|
|
13
|
+
fill="none"
|
|
14
|
+
stroke="currentColor"
|
|
15
|
+
strokeWidth="2"
|
|
16
|
+
strokeLinecap="round"
|
|
17
|
+
strokeLinejoin="round"
|
|
18
|
+
aria-hidden={title ? undefined : true}
|
|
19
|
+
role={title ? 'img' : undefined}
|
|
20
|
+
>
|
|
21
|
+
{title ? <title>{title}</title> : null}
|
|
22
|
+
<path d="M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7-10-7-10-7Z" />
|
|
23
|
+
<circle cx="12" cy="12" r="3" />
|
|
24
|
+
</svg>
|
|
25
|
+
)
|
|
26
|
+
}
|
|
@@ -10,6 +10,8 @@ import {
|
|
|
10
10
|
type AimedRelation,
|
|
11
11
|
type BlueprintNote,
|
|
12
12
|
type BlueprintNoteKind,
|
|
13
|
+
type BlueprintPointer,
|
|
14
|
+
type BlueprintPointerKind,
|
|
13
15
|
type BranchChanges,
|
|
14
16
|
type CodebaseGraph,
|
|
15
17
|
type PatchImportAddition,
|
|
@@ -18,7 +20,8 @@ import {
|
|
|
18
20
|
type WorldLayout,
|
|
19
21
|
type WorkflowAction,
|
|
20
22
|
} from '../types'
|
|
21
|
-
import { findBlueprintNote } from '../userCreated'
|
|
23
|
+
import { findBlueprintNote, findBlueprintPointer } from '../userCreated'
|
|
24
|
+
import { EyeIcon } from './EyeIcon'
|
|
22
25
|
import { beginKeyboardIsolation, shouldIgnoreShortcut } from '../keyboard'
|
|
23
26
|
|
|
24
27
|
function reviewTitle(status: AgentIntentStatus) {
|
|
@@ -311,25 +314,43 @@ function BlueprintSymbolRow({
|
|
|
311
314
|
className,
|
|
312
315
|
hasNote,
|
|
313
316
|
noteOpen,
|
|
317
|
+
pointed,
|
|
314
318
|
canEdit,
|
|
315
319
|
canRemove,
|
|
316
320
|
onRemove,
|
|
317
321
|
onOpenNote,
|
|
322
|
+
onTogglePoint,
|
|
318
323
|
}: {
|
|
319
324
|
name: string
|
|
320
325
|
className?: string
|
|
321
326
|
hasNote: boolean
|
|
322
327
|
noteOpen?: boolean
|
|
328
|
+
pointed?: boolean
|
|
323
329
|
canEdit: boolean
|
|
324
330
|
canRemove?: boolean
|
|
325
331
|
onRemove?: () => void
|
|
326
332
|
onOpenNote: () => void
|
|
333
|
+
onTogglePoint?: () => void
|
|
327
334
|
}) {
|
|
328
335
|
return (
|
|
329
336
|
<li>
|
|
330
337
|
<span className={className}>{name}</span>
|
|
331
338
|
{canEdit && (
|
|
332
339
|
<div className="hud-item-actions">
|
|
340
|
+
{onTogglePoint && (
|
|
341
|
+
<button
|
|
342
|
+
className="hud-item-point"
|
|
343
|
+
type="button"
|
|
344
|
+
data-pointed={pointed ? 'true' : 'false'}
|
|
345
|
+
aria-label={
|
|
346
|
+
pointed ? `Stop pointing to ${name}` : `Point to ${name}`
|
|
347
|
+
}
|
|
348
|
+
aria-pressed={Boolean(pointed)}
|
|
349
|
+
onClick={onTogglePoint}
|
|
350
|
+
>
|
|
351
|
+
<EyeIcon size={13} />
|
|
352
|
+
</button>
|
|
353
|
+
)}
|
|
333
354
|
<button
|
|
334
355
|
className="hud-item-note"
|
|
335
356
|
type="button"
|
|
@@ -570,7 +591,8 @@ function blueprintIsDefined(intent: AgentIntent) {
|
|
|
570
591
|
(intent.blueprintFunctions?.length ?? 0) > 0 ||
|
|
571
592
|
(intent.blueprintVariables?.length ?? 0) > 0 ||
|
|
572
593
|
(intent.blueprintImports?.length ?? 0) > 0 ||
|
|
573
|
-
(intent.blueprintNotes?.length ?? 0) > 0
|
|
594
|
+
(intent.blueprintNotes?.length ?? 0) > 0 ||
|
|
595
|
+
(intent.blueprintPointers?.length ?? 0) > 0
|
|
574
596
|
)
|
|
575
597
|
}
|
|
576
598
|
|
|
@@ -680,6 +702,11 @@ function sessionLiveStatus(intent: AgentIntent) {
|
|
|
680
702
|
if (intent.awaitingAttach) {
|
|
681
703
|
return { text: 'Waiting for /inbase in Cursor', busy: false }
|
|
682
704
|
}
|
|
705
|
+
if (intent.status === 'pending') {
|
|
706
|
+
return intent.listening
|
|
707
|
+
? { text: 'LLM is listening — accept or send an instruction', busy: false }
|
|
708
|
+
: { text: 'Review this step', busy: false }
|
|
709
|
+
}
|
|
683
710
|
if (kind === 'execute') {
|
|
684
711
|
return { text: `LLM received ${detail}`, busy: true }
|
|
685
712
|
}
|
|
@@ -708,11 +735,6 @@ function sessionLiveStatus(intent: AgentIntent) {
|
|
|
708
735
|
? { text: 'LLM is listening — run the next step', busy: false }
|
|
709
736
|
: { text: 'Plan ready', busy: false }
|
|
710
737
|
}
|
|
711
|
-
if (intent.status === 'pending') {
|
|
712
|
-
return intent.listening
|
|
713
|
-
? { text: 'LLM is listening — accept or send an instruction', busy: false }
|
|
714
|
-
: { text: 'Review this step', busy: false }
|
|
715
|
-
}
|
|
716
738
|
if (
|
|
717
739
|
kind === 'attached' ||
|
|
718
740
|
intent.status === 'blueprint' ||
|
|
@@ -797,13 +819,16 @@ function SessionPanel({
|
|
|
797
819
|
const [contextBusy, setContextBusy] = useState(false)
|
|
798
820
|
const [contextError, setContextError] = useState<string | null>(null)
|
|
799
821
|
const sessionId = intent.sessionId
|
|
800
|
-
const
|
|
822
|
+
const latestEntry = intent.isActiveDiff ? intent.chain.at(-1) : null
|
|
823
|
+
const pending =
|
|
824
|
+
latestEntry?.status === 'pending' ||
|
|
825
|
+
(intent.status === 'pending' && intent.isActiveDiff)
|
|
801
826
|
const askingBlueprint = intent.status === 'blueprint_ask'
|
|
802
827
|
const sendingBlueprint = intent.status === 'blueprint'
|
|
803
828
|
const canPlace = Boolean(intent.creationMode)
|
|
804
829
|
const preparing = intent.status === 'preparing'
|
|
805
|
-
const planReady = intent.status === 'planned'
|
|
806
830
|
const working = intent.status === 'working' || intent.status === 'replanning'
|
|
831
|
+
const planReady = intent.status === 'planned' && !pending
|
|
807
832
|
const previewing = intent.preview
|
|
808
833
|
const chainIndex = intent.chainIndex ?? 0
|
|
809
834
|
const previousDiff = intent.chain[chainIndex - 1]
|
|
@@ -812,10 +837,6 @@ function SessionPanel({
|
|
|
812
837
|
intent.step && intent.steps?.length > 0
|
|
813
838
|
? `Step ${intent.step} of ${intent.steps.length}`
|
|
814
839
|
: 'Patch'
|
|
815
|
-
const lastStep =
|
|
816
|
-
typeof intent.step === 'number' &&
|
|
817
|
-
intent.steps.length > 0 &&
|
|
818
|
-
intent.step >= intent.steps.length
|
|
819
840
|
const stepByStep = intent.stepByStep !== false
|
|
820
841
|
const addedFunctions = intent.addedFunctions ?? []
|
|
821
842
|
const addedVariables = intent.addedVariables ?? []
|
|
@@ -826,25 +847,27 @@ function SessionPanel({
|
|
|
826
847
|
intent.status === 'finished'
|
|
827
848
|
? intent.steps.map((step) => step.index)
|
|
828
849
|
: intent.chain
|
|
829
|
-
.filter(
|
|
830
|
-
(entry) =>
|
|
831
|
-
entry.status === 'applied' || entry.status === 'extended',
|
|
832
|
-
)
|
|
850
|
+
.filter((entry) => entry.status === 'applied')
|
|
833
851
|
.map((entry) => entry.step),
|
|
834
852
|
)
|
|
835
853
|
if (intent.status === 'approved' && typeof intent.step === 'number') {
|
|
836
854
|
acceptedSteps.add(intent.step)
|
|
837
855
|
}
|
|
838
|
-
const proposalStep =
|
|
839
|
-
|
|
856
|
+
const proposalStep = pending
|
|
857
|
+
? (latestEntry?.status === 'pending' ? latestEntry.step : intent.step)
|
|
858
|
+
: null
|
|
840
859
|
const processingStep =
|
|
841
860
|
working && typeof intent.step === 'number' ? intent.step : null
|
|
842
861
|
const invokeStep =
|
|
843
|
-
planReady && !working
|
|
862
|
+
planReady && !working && proposalStep === null
|
|
844
863
|
? (intent.steps.find((step) => !acceptedSteps.has(step.index)) ?? null)
|
|
845
864
|
: null
|
|
846
865
|
const canRunNext = stepByStep && Boolean(invokeStep)
|
|
847
866
|
const canAcceptProposal = proposalStep !== null
|
|
867
|
+
const lastStep =
|
|
868
|
+
typeof proposalStep === 'number' &&
|
|
869
|
+
intent.steps.length > 0 &&
|
|
870
|
+
proposalStep >= intent.steps.length
|
|
848
871
|
const panelDone =
|
|
849
872
|
intent.status === 'finished' ||
|
|
850
873
|
intent.status === 'approved' ||
|
|
@@ -1071,7 +1094,7 @@ function SessionPanel({
|
|
|
1071
1094
|
{intent.steps.map((step) => {
|
|
1072
1095
|
const proposed = proposalStep === step.index
|
|
1073
1096
|
const processing = processingStep === step.index
|
|
1074
|
-
const accepted = acceptedSteps.has(step.index)
|
|
1097
|
+
const accepted = acceptedSteps.has(step.index) && !proposed
|
|
1075
1098
|
const creating = processing && !proposed
|
|
1076
1099
|
const showStepAction =
|
|
1077
1100
|
creating ||
|
|
@@ -1270,7 +1293,7 @@ function SessionPanel({
|
|
|
1270
1293
|
value={instruction}
|
|
1271
1294
|
maxLength={4000}
|
|
1272
1295
|
rows={3}
|
|
1273
|
-
placeholder="Describe what should change in
|
|
1296
|
+
placeholder="Describe what should change in this proposal…"
|
|
1274
1297
|
onChange={(event) => setInstruction(event.target.value)}
|
|
1275
1298
|
onKeyDown={(event) => event.stopPropagation()}
|
|
1276
1299
|
/>
|
|
@@ -1546,6 +1569,11 @@ function explorerInstructions({
|
|
|
1546
1569
|
? [
|
|
1547
1570
|
{ id: 'space', keys: ['Space'], label: 'Place file' },
|
|
1548
1571
|
{ id: 'b-island', keys: ['B'], label: 'Place island' },
|
|
1572
|
+
{
|
|
1573
|
+
id: 'point-to',
|
|
1574
|
+
keys: ['Point to'],
|
|
1575
|
+
label: 'Keep a file, folder, or function in mind',
|
|
1576
|
+
},
|
|
1549
1577
|
]
|
|
1550
1578
|
: []),
|
|
1551
1579
|
...backspace,
|
|
@@ -1629,7 +1657,7 @@ function explorerInstructions({
|
|
|
1629
1657
|
{
|
|
1630
1658
|
id: 'add-file-folder',
|
|
1631
1659
|
keys: ['Right-click'],
|
|
1632
|
-
label: 'Add file or folder',
|
|
1660
|
+
label: 'Add file or folder, or point to a folder',
|
|
1633
1661
|
},
|
|
1634
1662
|
]
|
|
1635
1663
|
: []),
|
|
@@ -1739,6 +1767,7 @@ type HUDProps = {
|
|
|
1739
1767
|
blueprintVariables?: PatchSymbolAddition[]
|
|
1740
1768
|
blueprintImports?: PatchImportAddition[]
|
|
1741
1769
|
blueprintNotes?: BlueprintNote[]
|
|
1770
|
+
blueprintPointers?: BlueprintPointer[]
|
|
1742
1771
|
onAddBlueprintFunction?: (fileId: string, name: string) => boolean
|
|
1743
1772
|
onAddBlueprintVariable?: (fileId: string, name: string) => boolean
|
|
1744
1773
|
onAddBlueprintImport?: (fileId: string, raw: string) => boolean
|
|
@@ -1755,6 +1784,11 @@ type HUDProps = {
|
|
|
1755
1784
|
name?: string
|
|
1756
1785
|
note: string
|
|
1757
1786
|
}) => void
|
|
1787
|
+
onToggleBlueprintPointer?: (next: {
|
|
1788
|
+
kind: BlueprintPointerKind
|
|
1789
|
+
path: string
|
|
1790
|
+
name?: string
|
|
1791
|
+
}) => void
|
|
1758
1792
|
onMapAddFile?: (folderPath: string) => void
|
|
1759
1793
|
onMapAddFolder?: (folderPath: string) => void
|
|
1760
1794
|
onInspectFile?: (fileId: string) => void
|
|
@@ -1814,6 +1848,7 @@ export function HUD({
|
|
|
1814
1848
|
blueprintVariables = [],
|
|
1815
1849
|
blueprintImports = [],
|
|
1816
1850
|
blueprintNotes = [],
|
|
1851
|
+
blueprintPointers = [],
|
|
1817
1852
|
onAddBlueprintFunction,
|
|
1818
1853
|
onAddBlueprintVariable,
|
|
1819
1854
|
onAddBlueprintImport,
|
|
@@ -1821,6 +1856,7 @@ export function HUD({
|
|
|
1821
1856
|
onRemoveBlueprintVariable,
|
|
1822
1857
|
onRemoveBlueprintImport,
|
|
1823
1858
|
onSetBlueprintNote,
|
|
1859
|
+
onToggleBlueprintPointer,
|
|
1824
1860
|
onMapAddFile,
|
|
1825
1861
|
onMapAddFolder,
|
|
1826
1862
|
onInspectFile,
|
|
@@ -1950,6 +1986,16 @@ export function HUD({
|
|
|
1950
1986
|
const selectedFileNote = selected
|
|
1951
1987
|
? findBlueprintNote(blueprintNotes, selected.id, 'file')
|
|
1952
1988
|
: ''
|
|
1989
|
+
const selectedFilePointed = selected
|
|
1990
|
+
? findBlueprintPointer(blueprintPointers, 'file', selected.id)
|
|
1991
|
+
: false
|
|
1992
|
+
const selectedFolderPointed = selectedFolderNode
|
|
1993
|
+
? findBlueprintPointer(
|
|
1994
|
+
blueprintPointers,
|
|
1995
|
+
'folder',
|
|
1996
|
+
selectedFolderNode.path,
|
|
1997
|
+
)
|
|
1998
|
+
: false
|
|
1953
1999
|
const openFileNote = () => {
|
|
1954
2000
|
if (!selected || !onSetBlueprintNote) return
|
|
1955
2001
|
setInstructionsOpen(false)
|
|
@@ -2282,6 +2328,20 @@ export function HUD({
|
|
|
2282
2328
|
Inspect file
|
|
2283
2329
|
</button>
|
|
2284
2330
|
)}
|
|
2331
|
+
{canEditBlueprint && onToggleBlueprintPointer && (
|
|
2332
|
+
<button
|
|
2333
|
+
className="hud-button hud-inspect hud-point"
|
|
2334
|
+
type="button"
|
|
2335
|
+
data-pointed={selectedFilePointed ? 'true' : 'false'}
|
|
2336
|
+
aria-pressed={selectedFilePointed}
|
|
2337
|
+
onClick={() =>
|
|
2338
|
+
onToggleBlueprintPointer({ kind: 'file', path: selected.id })
|
|
2339
|
+
}
|
|
2340
|
+
>
|
|
2341
|
+
<EyeIcon size={15} />
|
|
2342
|
+
{selectedFilePointed ? 'Stop pointing' : 'Point to file'}
|
|
2343
|
+
</button>
|
|
2344
|
+
)}
|
|
2285
2345
|
{canEditBlueprint && onSetBlueprintNote && (
|
|
2286
2346
|
<button
|
|
2287
2347
|
className="hud-button hud-inspect"
|
|
@@ -2373,10 +2433,26 @@ export function HUD({
|
|
|
2373
2433
|
}
|
|
2374
2434
|
canEdit={Boolean(canEditBlueprint && onSetBlueprintNote)}
|
|
2375
2435
|
canRemove={Boolean(canEditBlueprint && symbol.intended)}
|
|
2436
|
+
pointed={findBlueprintPointer(
|
|
2437
|
+
blueprintPointers,
|
|
2438
|
+
'function',
|
|
2439
|
+
selected.id,
|
|
2440
|
+
symbol.name,
|
|
2441
|
+
)}
|
|
2376
2442
|
onRemove={() =>
|
|
2377
2443
|
onRemoveBlueprintFunction?.(selected.id, symbol.name)
|
|
2378
2444
|
}
|
|
2379
2445
|
onOpenNote={() => openSymbolNote('function', symbol.name)}
|
|
2446
|
+
onTogglePoint={
|
|
2447
|
+
onToggleBlueprintPointer
|
|
2448
|
+
? () =>
|
|
2449
|
+
onToggleBlueprintPointer({
|
|
2450
|
+
kind: 'function',
|
|
2451
|
+
path: selected.id,
|
|
2452
|
+
name: symbol.name,
|
|
2453
|
+
})
|
|
2454
|
+
: undefined
|
|
2455
|
+
}
|
|
2380
2456
|
/>
|
|
2381
2457
|
))}
|
|
2382
2458
|
{extraAddedFunctions.map((item) => (
|
|
@@ -2398,7 +2474,23 @@ export function HUD({
|
|
|
2398
2474
|
noteEditor.name === item.name
|
|
2399
2475
|
}
|
|
2400
2476
|
canEdit={Boolean(canEditBlueprint && onSetBlueprintNote)}
|
|
2477
|
+
pointed={findBlueprintPointer(
|
|
2478
|
+
blueprintPointers,
|
|
2479
|
+
'function',
|
|
2480
|
+
selected.id,
|
|
2481
|
+
item.name,
|
|
2482
|
+
)}
|
|
2401
2483
|
onOpenNote={() => openSymbolNote('function', item.name)}
|
|
2484
|
+
onTogglePoint={
|
|
2485
|
+
onToggleBlueprintPointer
|
|
2486
|
+
? () =>
|
|
2487
|
+
onToggleBlueprintPointer({
|
|
2488
|
+
kind: 'function',
|
|
2489
|
+
path: selected.id,
|
|
2490
|
+
name: item.name,
|
|
2491
|
+
})
|
|
2492
|
+
: undefined
|
|
2493
|
+
}
|
|
2402
2494
|
/>
|
|
2403
2495
|
))}
|
|
2404
2496
|
</ul>
|
|
@@ -2439,10 +2531,26 @@ export function HUD({
|
|
|
2439
2531
|
}
|
|
2440
2532
|
canEdit={Boolean(canEditBlueprint && onSetBlueprintNote)}
|
|
2441
2533
|
canRemove={Boolean(canEditBlueprint && symbol.intended)}
|
|
2534
|
+
pointed={findBlueprintPointer(
|
|
2535
|
+
blueprintPointers,
|
|
2536
|
+
'variable',
|
|
2537
|
+
selected.id,
|
|
2538
|
+
symbol.name,
|
|
2539
|
+
)}
|
|
2442
2540
|
onRemove={() =>
|
|
2443
2541
|
onRemoveBlueprintVariable?.(selected.id, symbol.name)
|
|
2444
2542
|
}
|
|
2445
2543
|
onOpenNote={() => openSymbolNote('variable', symbol.name)}
|
|
2544
|
+
onTogglePoint={
|
|
2545
|
+
onToggleBlueprintPointer
|
|
2546
|
+
? () =>
|
|
2547
|
+
onToggleBlueprintPointer({
|
|
2548
|
+
kind: 'variable',
|
|
2549
|
+
path: selected.id,
|
|
2550
|
+
name: symbol.name,
|
|
2551
|
+
})
|
|
2552
|
+
: undefined
|
|
2553
|
+
}
|
|
2446
2554
|
/>
|
|
2447
2555
|
))}
|
|
2448
2556
|
{extraAddedVariables.map((item) => (
|
|
@@ -2464,7 +2572,23 @@ export function HUD({
|
|
|
2464
2572
|
noteEditor.name === item.name
|
|
2465
2573
|
}
|
|
2466
2574
|
canEdit={Boolean(canEditBlueprint && onSetBlueprintNote)}
|
|
2575
|
+
pointed={findBlueprintPointer(
|
|
2576
|
+
blueprintPointers,
|
|
2577
|
+
'variable',
|
|
2578
|
+
selected.id,
|
|
2579
|
+
item.name,
|
|
2580
|
+
)}
|
|
2467
2581
|
onOpenNote={() => openSymbolNote('variable', item.name)}
|
|
2582
|
+
onTogglePoint={
|
|
2583
|
+
onToggleBlueprintPointer
|
|
2584
|
+
? () =>
|
|
2585
|
+
onToggleBlueprintPointer({
|
|
2586
|
+
kind: 'variable',
|
|
2587
|
+
path: selected.id,
|
|
2588
|
+
name: item.name,
|
|
2589
|
+
})
|
|
2590
|
+
: undefined
|
|
2591
|
+
}
|
|
2468
2592
|
/>
|
|
2469
2593
|
))}
|
|
2470
2594
|
</ul>
|
|
@@ -2608,6 +2732,24 @@ export function HUD({
|
|
|
2608
2732
|
))}
|
|
2609
2733
|
</ul>
|
|
2610
2734
|
)}
|
|
2735
|
+
{canPlace && onToggleBlueprintPointer && (
|
|
2736
|
+
<button
|
|
2737
|
+
className="hud-button hud-inspect hud-point"
|
|
2738
|
+
type="button"
|
|
2739
|
+
data-pointed={selectedFolderPointed ? 'true' : 'false'}
|
|
2740
|
+
aria-pressed={selectedFolderPointed}
|
|
2741
|
+
disabled={naming || selectedFolderNode.path.startsWith('draft:')}
|
|
2742
|
+
onClick={() =>
|
|
2743
|
+
onToggleBlueprintPointer({
|
|
2744
|
+
kind: 'folder',
|
|
2745
|
+
path: selectedFolderNode.path,
|
|
2746
|
+
})
|
|
2747
|
+
}
|
|
2748
|
+
>
|
|
2749
|
+
<EyeIcon size={15} />
|
|
2750
|
+
{selectedFolderPointed ? 'Stop pointing' : 'Point to folder'}
|
|
2751
|
+
</button>
|
|
2752
|
+
)}
|
|
2611
2753
|
{canPlace && mapping && onMapAddFile && onMapAddFolder && (
|
|
2612
2754
|
<div className="hud-decide hud-map-blueprint">
|
|
2613
2755
|
<button
|
|
@@ -2646,6 +2788,20 @@ export function HUD({
|
|
|
2646
2788
|
plannedIds={plannedIds}
|
|
2647
2789
|
createdIds={createdIds}
|
|
2648
2790
|
deletedIds={deletedIds}
|
|
2791
|
+
pointedFileIds={
|
|
2792
|
+
blueprintHidden
|
|
2793
|
+
? []
|
|
2794
|
+
: blueprintPointers.flatMap((item) =>
|
|
2795
|
+
item.kind === 'folder' ? [] : [item.path],
|
|
2796
|
+
)
|
|
2797
|
+
}
|
|
2798
|
+
pointedFolderPaths={
|
|
2799
|
+
blueprintHidden
|
|
2800
|
+
? []
|
|
2801
|
+
: blueprintPointers.flatMap((item) =>
|
|
2802
|
+
item.kind === 'folder' ? [item.path] : [],
|
|
2803
|
+
)
|
|
2804
|
+
}
|
|
2649
2805
|
onMinimize={() => {
|
|
2650
2806
|
setThumbnailMaximized(false)
|
|
2651
2807
|
setThumbnailMinimized((current) => !current)
|
|
@@ -10,15 +10,19 @@ export type MapContextMenuState = {
|
|
|
10
10
|
|
|
11
11
|
type MapContextMenuProps = {
|
|
12
12
|
menu: MapContextMenuState | null
|
|
13
|
+
pointed?: boolean
|
|
13
14
|
onAddFile: (folder: string) => void
|
|
14
15
|
onAddFolder: (folder: string) => void
|
|
16
|
+
onPointToFolder?: (folder: string) => void
|
|
15
17
|
onClose: () => void
|
|
16
18
|
}
|
|
17
19
|
|
|
18
20
|
export function MapContextMenu({
|
|
19
21
|
menu,
|
|
22
|
+
pointed = false,
|
|
20
23
|
onAddFile,
|
|
21
24
|
onAddFolder,
|
|
25
|
+
onPointToFolder,
|
|
22
26
|
onClose,
|
|
23
27
|
}: MapContextMenuProps) {
|
|
24
28
|
useEffect(() => {
|
|
@@ -48,7 +52,7 @@ export function MapContextMenu({
|
|
|
48
52
|
|
|
49
53
|
const pad = 8
|
|
50
54
|
const width = 176
|
|
51
|
-
const height = 84
|
|
55
|
+
const height = onPointToFolder ? 126 : 84
|
|
52
56
|
const left = Math.min(
|
|
53
57
|
Math.max(pad, menu.x),
|
|
54
58
|
window.innerWidth - width - pad,
|
|
@@ -85,6 +89,19 @@ export function MapContextMenu({
|
|
|
85
89
|
>
|
|
86
90
|
Add folder
|
|
87
91
|
</button>
|
|
92
|
+
{onPointToFolder && (
|
|
93
|
+
<button
|
|
94
|
+
type="button"
|
|
95
|
+
role="menuitem"
|
|
96
|
+
aria-pressed={pointed}
|
|
97
|
+
onClick={() => {
|
|
98
|
+
onPointToFolder(menu.folder)
|
|
99
|
+
onClose()
|
|
100
|
+
}}
|
|
101
|
+
>
|
|
102
|
+
{pointed ? 'Stop pointing' : 'Point to folder'}
|
|
103
|
+
</button>
|
|
104
|
+
)}
|
|
88
105
|
</div>,
|
|
89
106
|
document.body,
|
|
90
107
|
)
|