@jkwd/inbase 0.1.6 → 0.1.8

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.
@@ -42,12 +42,18 @@ function symbolLabels(items: PatchSymbolAddition[]) {
42
42
  }))
43
43
  }
44
44
 
45
+ function importLabel(item: Pick<PatchImportAddition, 'name' | 'from'>) {
46
+ const from = fileBase(item.from)
47
+ return item.name === item.from || item.name === from
48
+ ? from
49
+ : `${item.name} from ${from}`
50
+ }
51
+
45
52
  function importLabels(items: PatchImportAddition[]) {
46
53
  const files = new Set(items.map((item) => item.file))
47
54
  const showFile = files.size > 1
48
55
  return items.map((item) => {
49
- const what =
50
- item.name === item.from ? item.from : `${item.name} from ${item.from}`
56
+ const what = importLabel(item)
51
57
  return {
52
58
  key: `${item.file}:${item.name}:${item.from}`,
53
59
  label: showFile ? `${what} · ${fileBase(item.file)}` : what,
@@ -692,6 +698,212 @@ function SessionPanel({
692
698
  )
693
699
  }
694
700
 
701
+ type ExplorerInstruction = {
702
+ id: string
703
+ keys: string[]
704
+ label: string
705
+ }
706
+
707
+ type InstructionView = 'walk' | '3dview' | 'map'
708
+
709
+ type ExplorerInstructionSection = {
710
+ id: InstructionView
711
+ title: string
712
+ items: ExplorerInstruction[]
713
+ }
714
+
715
+ function InstructionList({ items }: { items: ExplorerInstruction[] }) {
716
+ return (
717
+ <ul className="hud-instructions-list">
718
+ {items.map((hint) => (
719
+ <li className="hud-instruction" key={hint.id}>
720
+ {hint.keys.length > 0 && (
721
+ <span className="hud-instruction-keys">
722
+ {hint.keys.map((key) => (
723
+ <kbd key={key}>{key}</kbd>
724
+ ))}
725
+ </span>
726
+ )}
727
+ <span className="hud-instruction-label">{hint.label}</span>
728
+ </li>
729
+ ))}
730
+ </ul>
731
+ )
732
+ }
733
+
734
+ function explorerInstructions({
735
+ creatingBlueprint,
736
+ hasChangeSet,
737
+ changePathsOnly,
738
+ selectedUserCreated,
739
+ infoVisible,
740
+ thumbnailVisible,
741
+ importedBy,
742
+ canStop,
743
+ sessionCount,
744
+ }: {
745
+ creatingBlueprint: boolean
746
+ hasChangeSet: boolean
747
+ changePathsOnly: boolean
748
+ selectedUserCreated: boolean
749
+ infoVisible: boolean
750
+ thumbnailVisible: boolean
751
+ importedBy: boolean
752
+ canStop: boolean
753
+ sessionCount: number
754
+ }): ExplorerInstructionSection[] {
755
+ const backspace: ExplorerInstruction[] =
756
+ selectedUserCreated && creatingBlueprint
757
+ ? [{ id: 'backspace', keys: ['Backspace'], label: 'Delete' }]
758
+ : []
759
+ const info: ExplorerInstruction[] = [
760
+ {
761
+ id: 'info',
762
+ keys: ['I'],
763
+ label: infoVisible ? 'Hide info' : 'Show info',
764
+ },
765
+ ...(infoVisible
766
+ ? [{ id: 'scroll-info', keys: ['↑', '↓'], label: 'Scroll info' }]
767
+ : []),
768
+ ]
769
+ const imported: ExplorerInstruction = {
770
+ id: 'imported',
771
+ keys: ['K'],
772
+ label: importedBy ? 'Show imports' : 'Show imported by',
773
+ }
774
+ const stop: ExplorerInstruction[] = canStop
775
+ ? [
776
+ {
777
+ id: 'stop',
778
+ keys: ['Stop'],
779
+ label:
780
+ sessionCount > 1
781
+ ? 'Ends the focused LLM session'
782
+ : 'Ends this LLM session',
783
+ },
784
+ ]
785
+ : []
786
+ const thumbnail: ExplorerInstruction = {
787
+ id: 'thumbnail',
788
+ keys: ['T'],
789
+ label: thumbnailVisible ? 'Hide 3D view' : 'Show 3D view',
790
+ }
791
+ return [
792
+ {
793
+ id: 'walk',
794
+ title: 'Walk',
795
+ items: [
796
+ { id: 'wasd', keys: ['W', 'A', 'S', 'D'], label: 'Walk' },
797
+ { id: 'mouse-look', keys: ['Mouse'], label: 'Look around' },
798
+ { id: 'shift', keys: ['Shift'], label: 'Sprint' },
799
+ ...(creatingBlueprint
800
+ ? [
801
+ { id: 'space', keys: ['Space'], label: 'Place file' },
802
+ { id: 'b-island', keys: ['B'], label: 'Place island' },
803
+ ]
804
+ : []),
805
+ ...backspace,
806
+ {
807
+ id: 'dblclick-info',
808
+ keys: ['Double-click'],
809
+ label: 'A block for info',
810
+ },
811
+ { id: 'aim-line', keys: ['Click'], label: 'Aim a line to fly' },
812
+ ...info,
813
+ imported,
814
+ {
815
+ id: 'update-model',
816
+ keys: ['Update model'],
817
+ label: 'Rescan and rebuild the map',
818
+ },
819
+ { id: 'toggle-map', keys: ['M'], label: 'Toggle map' },
820
+ {
821
+ id: 'release',
822
+ keys: ['Double-click', 'Esc'],
823
+ label: 'Release mouse',
824
+ },
825
+ ...stop,
826
+ ],
827
+ },
828
+ {
829
+ id: '3dview',
830
+ title: '3D view',
831
+ items: [
832
+ { id: 'scroll-zoom', keys: ['Scroll'], label: 'Zoom' },
833
+ { id: 'drag-pan', keys: ['Drag'], label: 'Pan' },
834
+ {
835
+ id: 'cmd-drag-rotate',
836
+ keys: ['⌘', 'Ctrl', 'Drag'],
837
+ label: 'Rotate',
838
+ },
839
+ {
840
+ id: 'dblclick-reset',
841
+ keys: ['Double-click'],
842
+ label: 'Reset view',
843
+ },
844
+ thumbnail,
845
+ ],
846
+ },
847
+ {
848
+ id: 'map',
849
+ title: 'Map',
850
+ items: [
851
+ { id: 'scroll-zoom', keys: ['Scroll'], label: 'Zoom' },
852
+ { id: 'drag-pan', keys: ['Drag'], label: 'Pan' },
853
+ { id: 'click-block', keys: ['Click'], label: 'A block for info' },
854
+ {
855
+ id: 'click-island',
856
+ keys: ['Click'],
857
+ label: 'An island for its files',
858
+ },
859
+ ...(creatingBlueprint
860
+ ? [
861
+ { id: 'select-island', keys: ['Click'], label: 'Select an island' },
862
+ {
863
+ id: 'add-file-folder',
864
+ keys: [],
865
+ label: 'Add file / Add folder in panel',
866
+ },
867
+ ]
868
+ : []),
869
+ { id: 'click-line', keys: ['Click'], label: 'A line to fly there' },
870
+ {
871
+ id: 'ctrl-click-walk',
872
+ keys: ['Ctrl', 'Click'],
873
+ label: 'An island to walk',
874
+ },
875
+ {
876
+ id: 'gold-pin',
877
+ keys: [],
878
+ label: 'Gold pin is your walk position',
879
+ },
880
+ ...(hasChangeSet
881
+ ? [
882
+ {
883
+ id: 'toggle-paths',
884
+ keys: ['C'],
885
+ label: changePathsOnly
886
+ ? 'Show all paths'
887
+ : 'Show only changed paths',
888
+ },
889
+ ]
890
+ : []),
891
+ ...backspace,
892
+ ...info,
893
+ thumbnail,
894
+ imported,
895
+ {
896
+ id: 'update-model',
897
+ keys: ['Update model'],
898
+ label: 'Rescan and rebuild the map',
899
+ },
900
+ { id: 'map-walk', keys: ['M'], label: 'Back to walk' },
901
+ ...stop,
902
+ ],
903
+ },
904
+ ]
905
+ }
906
+
695
907
  type HUDProps = {
696
908
  graph: CodebaseGraph
697
909
  layout: WorldLayout
@@ -704,7 +916,6 @@ type HUDProps = {
704
916
  landAt: [number, number]
705
917
  aimedRelation: AimedRelation | null
706
918
  aimedFileId?: string | null
707
- currentFolder: string
708
919
  intent: AgentIntent
709
920
  intents?: AgentIntent[]
710
921
  focusedSessionId?: string | null
@@ -719,6 +930,8 @@ type HUDProps = {
719
930
  onWalk: () => void
720
931
  followLook: boolean
721
932
  onToggleFollowLook: () => void
933
+ onUpdateModel: () => void
934
+ updatingModel?: boolean
722
935
  importedBy: boolean
723
936
  onToggleImportedBy: () => void
724
937
  changePathsOnly?: boolean
@@ -762,7 +975,6 @@ export function HUD({
762
975
  landAt,
763
976
  aimedRelation,
764
977
  aimedFileId = null,
765
- currentFolder,
766
978
  intent,
767
979
  intents,
768
980
  focusedSessionId = null,
@@ -773,6 +985,8 @@ export function HUD({
773
985
  onWalk,
774
986
  followLook,
775
987
  onToggleFollowLook,
988
+ onUpdateModel,
989
+ updatingModel = false,
776
990
  importedBy,
777
991
  onToggleImportedBy,
778
992
  changePathsOnly = false,
@@ -821,10 +1035,12 @@ export function HUD({
821
1035
  const canStop = canStopSession(intent)
822
1036
  const [walkIntro, setWalkIntro] = useState(false)
823
1037
  const walkIntroSeen = useRef(false)
1038
+ const [instructionsOpen, setInstructionsOpen] = useState(false)
824
1039
  const [infoVisible, setInfoVisible] = useState(false)
825
1040
  const [infoMinimized, setInfoMinimized] = useState(false)
826
1041
  const [thumbnailVisible, setThumbnailVisible] = useState(true)
827
1042
  const [thumbnailMinimized, setThumbnailMinimized] = useState(false)
1043
+ const [thumbnailMaximized, setThumbnailMaximized] = useState(false)
828
1044
  const infoPanelRef = useRef<HTMLDivElement>(null)
829
1045
  const creatingBlueprint = sessions.some(
830
1046
  (item) => item.creationMode || item.status === 'blueprint',
@@ -1000,7 +1216,12 @@ export function HUD({
1000
1216
  }
1001
1217
  event.preventDefault()
1002
1218
  setThumbnailVisible((visible) => {
1003
- if (!visible) setThumbnailMinimized(false)
1219
+ if (!visible) {
1220
+ setThumbnailMinimized(false)
1221
+ setThumbnailMaximized(false)
1222
+ } else {
1223
+ setThumbnailMaximized(false)
1224
+ }
1004
1225
  return !visible
1005
1226
  })
1006
1227
  }
@@ -1033,6 +1254,36 @@ export function HUD({
1033
1254
  return () => window.removeEventListener('keydown', onKey, true)
1034
1255
  }, [infoVisible, selectedId, selectedFolder])
1035
1256
 
1257
+ useEffect(() => {
1258
+ if (!instructionsOpen) return
1259
+ document.exitPointerLock()
1260
+ const onKey = (event: KeyboardEvent) => {
1261
+ if (event.code !== 'Escape') return
1262
+ event.preventDefault()
1263
+ event.stopPropagation()
1264
+ setInstructionsOpen(false)
1265
+ }
1266
+ window.addEventListener('keydown', onKey, true)
1267
+ return () => window.removeEventListener('keydown', onKey, true)
1268
+ }, [instructionsOpen])
1269
+
1270
+ const instructionSections = explorerInstructions({
1271
+ creatingBlueprint,
1272
+ hasChangeSet,
1273
+ changePathsOnly,
1274
+ selectedUserCreated: Boolean(selected?.userCreated),
1275
+ infoVisible,
1276
+ thumbnailVisible,
1277
+ importedBy,
1278
+ canStop,
1279
+ sessionCount: sessions.length,
1280
+ })
1281
+ const currentInstructionView: InstructionView = mapping
1282
+ ? thumbnailMaximized
1283
+ ? '3dview'
1284
+ : 'map'
1285
+ : 'walk'
1286
+
1036
1287
  return (
1037
1288
  <div className="hud">
1038
1289
  {mode === 'walk' && !locked && walkIntro && !naming && (
@@ -1076,9 +1327,6 @@ export function HUD({
1076
1327
  )}
1077
1328
 
1078
1329
  <div className="hud-top">
1079
- <div className="hud-chip">
1080
- {mapping ? `Map of ${graph.targetName}` : `You are in ${currentFolder}`}
1081
- </div>
1082
1330
  <div className="hud-mode">
1083
1331
  <button
1084
1332
  className="hud-button"
@@ -1303,7 +1551,9 @@ export function HUD({
1303
1551
  ) : (
1304
1552
  <ul>
1305
1553
  {importers.map((file) => (
1306
- <li key={file.id}>{file.id}</li>
1554
+ <li key={file.id} title={file.id}>
1555
+ {file.name}
1556
+ </li>
1307
1557
  ))}
1308
1558
  </ul>
1309
1559
  )
@@ -1317,8 +1567,9 @@ export function HUD({
1317
1567
  className={
1318
1568
  intendedImportFrom.has(id) ? 'hud-intended' : undefined
1319
1569
  }
1570
+ title={id}
1320
1571
  >
1321
- {id}
1572
+ {fileBase(id)}
1322
1573
  </span>
1323
1574
  {canEditBlueprint && intendedImportFrom.has(id) && (
1324
1575
  <button
@@ -1345,10 +1596,8 @@ export function HUD({
1345
1596
  ))}
1346
1597
  {extraBlueprintImports.map((item) => (
1347
1598
  <li key={`bp-${item.name}-${item.from}`}>
1348
- <span className="hud-intended">
1349
- {item.name === item.from
1350
- ? item.from
1351
- : `${item.name} from ${item.from}`}
1599
+ <span className="hud-intended" title={item.from}>
1600
+ {importLabel(item)}
1352
1601
  </span>
1353
1602
  {canEditBlueprint && (
1354
1603
  <button
@@ -1460,93 +1709,92 @@ export function HUD({
1460
1709
  landAt={landAt}
1461
1710
  importedBy={importedBy}
1462
1711
  minimized={thumbnailMinimized}
1712
+ maximized={thumbnailMaximized}
1463
1713
  plannedIds={plannedIds}
1464
1714
  createdIds={createdIds}
1465
1715
  deletedIds={deletedIds}
1466
- onMinimize={() => setThumbnailMinimized((current) => !current)}
1716
+ onMinimize={() => {
1717
+ setThumbnailMaximized(false)
1718
+ setThumbnailMinimized((current) => !current)
1719
+ }}
1720
+ onMaximize={() => {
1721
+ setThumbnailMinimized(false)
1722
+ setThumbnailMaximized((current) => !current)
1723
+ }}
1467
1724
  onHide={() => {
1468
1725
  setThumbnailVisible(false)
1469
1726
  setThumbnailMinimized(false)
1727
+ setThumbnailMaximized(false)
1470
1728
  }}
1471
1729
  />
1472
1730
  )}
1473
1731
  </div>
1474
1732
 
1733
+ {instructionsOpen && (
1734
+ <div
1735
+ className="hud-instructions-overlay"
1736
+ onClick={() => setInstructionsOpen(false)}
1737
+ >
1738
+ <div
1739
+ className="hud-instructions-card"
1740
+ role="dialog"
1741
+ aria-modal="true"
1742
+ aria-labelledby="hud-instructions-title"
1743
+ onClick={(event) => event.stopPropagation()}
1744
+ >
1745
+ <div className="hud-instructions-header">
1746
+ <h1 id="hud-instructions-title">Instructions</h1>
1747
+ <button
1748
+ className="hud-button"
1749
+ type="button"
1750
+ onClick={() => setInstructionsOpen(false)}
1751
+ >
1752
+ Close
1753
+ </button>
1754
+ </div>
1755
+ <div className="hud-instructions-sections">
1756
+ {instructionSections.map((section) => (
1757
+ <section
1758
+ className="hud-instructions-section"
1759
+ data-current={section.id === currentInstructionView}
1760
+ key={section.id}
1761
+ aria-labelledby={`hud-instructions-${section.id}`}
1762
+ >
1763
+ <h2 id={`hud-instructions-${section.id}`}>
1764
+ {section.title}
1765
+ {section.id === currentInstructionView && (
1766
+ <span className="hud-instructions-current">Current</span>
1767
+ )}
1768
+ </h2>
1769
+ <InstructionList items={section.items} />
1770
+ </section>
1771
+ ))}
1772
+ </div>
1773
+ </div>
1774
+ </div>
1775
+ )}
1776
+
1475
1777
  <div className="hud-bottom">
1476
- <div className="hud-hints">
1477
- {mapping ? (
1478
- <>
1479
- <span>Scroll zoom</span>
1480
- <span>Drag pan</span>
1481
- <span>Click a block for info</span>
1482
- <span>Click an island for its files</span>
1483
- {creatingBlueprint && (
1484
- <>
1485
- <span>Select an island</span>
1486
- <span>Add file / Add folder in panel</span>
1487
- </>
1488
- )}
1489
- <span>Click a line to fly there</span>
1490
- <span>Ctrl-click an island to walk</span>
1491
- {hasChangeSet && (
1492
- <span>
1493
- {changePathsOnly
1494
- ? 'C show all paths'
1495
- : 'C show only changed paths'}
1496
- </span>
1497
- )}
1498
- {selected?.userCreated && creatingBlueprint && (
1499
- <span>Backspace delete</span>
1500
- )}
1501
- <span>{infoVisible ? 'I hide info' : 'I show info'}</span>
1502
- {infoVisible && <span>↑↓ scroll info</span>}
1503
- <span>
1504
- {thumbnailVisible ? 'T hide 3D view' : 'T show 3D view'}
1505
- </span>
1506
- <span>
1507
- {importedBy ? 'K show imports' : 'K show imported by'}
1508
- </span>
1509
- <span>M back to walk</span>
1510
- {canStop && (
1511
- <span>
1512
- {sessions.length > 1
1513
- ? 'Stop ends the focused LLM session'
1514
- : 'Stop ends this LLM session'}
1515
- </span>
1516
- )}
1517
- </>
1518
- ) : (
1519
- <>
1520
- <span>WASD walk</span>
1521
- <span>Mouse look</span>
1522
- <span>Shift sprint</span>
1523
- {creatingBlueprint && (
1524
- <>
1525
- <span>Space place file</span>
1526
- <span>B place island</span>
1527
- </>
1528
- )}
1529
- {selected?.userCreated && creatingBlueprint && (
1530
- <span>Backspace delete</span>
1531
- )}
1532
- <span>Double-click a block for info</span>
1533
- <span>Aim a line to fly</span>
1534
- <span>{infoVisible ? 'I hide info' : 'I show info'}</span>
1535
- {infoVisible && <span>↑↓ scroll info</span>}
1536
- <span>
1537
- {importedBy ? 'K show imports' : 'K show imported by'}
1538
- </span>
1539
- <span>M toggle map</span>
1540
- <span>Double-click or Esc release mouse</span>
1541
- {canStop && (
1542
- <span>
1543
- {sessions.length > 1
1544
- ? 'Stop ends the focused LLM session'
1545
- : 'Stop ends this LLM session'}
1546
- </span>
1547
- )}
1548
- </>
1549
- )}
1778
+ <div className="hud-bottom-actions">
1779
+ <button
1780
+ className="hud-button"
1781
+ data-active={instructionsOpen}
1782
+ type="button"
1783
+ aria-haspopup="dialog"
1784
+ aria-expanded={instructionsOpen}
1785
+ onClick={() => setInstructionsOpen((open) => !open)}
1786
+ >
1787
+ Instructions
1788
+ </button>
1789
+ <button
1790
+ className="hud-button"
1791
+ type="button"
1792
+ aria-label="Rescan the project and rebuild the map"
1793
+ disabled={updatingModel}
1794
+ onClick={onUpdateModel}
1795
+ >
1796
+ {updatingModel ? 'Updating…' : 'Update model'}
1797
+ </button>
1550
1798
  </div>
1551
1799
  <div className="hud-icon-row">
1552
1800
  {mapping && hasChangeSet && onToggleChangePathsOnly && (
@@ -1600,7 +1848,12 @@ export function HUD({
1600
1848
  type="button"
1601
1849
  onClick={() => {
1602
1850
  setThumbnailVisible((visible) => {
1603
- if (!visible) setThumbnailMinimized(false)
1851
+ if (!visible) {
1852
+ setThumbnailMinimized(false)
1853
+ setThumbnailMaximized(false)
1854
+ } else {
1855
+ setThumbnailMaximized(false)
1856
+ }
1604
1857
  return !visible
1605
1858
  })
1606
1859
  }}
@@ -10,8 +10,8 @@ import { dataDir, targetRoot } from './scripts/target-config.mjs'
10
10
  import { editorFileUri, openInEditor } from './scripts/open-editor.mjs'
11
11
  import {
12
12
  answerBlueprint,
13
+ clearDiffSessions,
13
14
  continueDiff,
14
- discardInactiveDiffSessions,
15
15
  inspectTargetFile,
16
16
  invokeStep,
17
17
  listSessionIntents,
@@ -56,7 +56,9 @@ function rescanTarget(when: string) {
56
56
  })
57
57
  if (scan.status !== 0) {
58
58
  console.error(scan.stderr || scan.stdout || `scan failed ${when}`)
59
+ return false
59
60
  }
61
+ return true
60
62
  }
61
63
 
62
64
  function knownFileIds() {
@@ -82,8 +84,8 @@ function jsonFilePlugin(): Plugin {
82
84
  return {
83
85
  name: 'visual-coder-json-files',
84
86
  configureServer(server) {
85
- discardInactiveDiffSessions(dataDir, targetRoot)
86
- rescanTarget('after discarding inactive sessions')
87
+ clearDiffSessions(dataDir, targetRoot)
88
+ rescanTarget('after clearing diff sessions')
87
89
  server.middlewares.use('/api/user-context', (req, res, next) => {
88
90
  if (req.method === 'GET') {
89
91
  sendJson(res, 200, readUserContext())
@@ -101,6 +103,14 @@ function jsonFilePlugin(): Plugin {
101
103
  sendJson(res, 200, readCodebase())
102
104
  return
103
105
  }
106
+ if (req.method === 'POST') {
107
+ if (!rescanTarget('on user request')) {
108
+ sendJson(res, 500, { error: 'scan failed' })
109
+ return
110
+ }
111
+ sendJson(res, 200, readCodebase())
112
+ return
113
+ }
104
114
  next()
105
115
  })
106
116
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jkwd/inbase",
3
- "version": "0.1.6",
3
+ "version": "0.1.8",
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",