@jkwd/inbase 0.1.4 → 0.1.5
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 +3 -1
- package/apps/explorer/scripts/session-store.d.ts +20 -0
- package/apps/explorer/scripts/session-store.mjs +318 -32
- package/apps/explorer/src/App.tsx +154 -85
- package/apps/explorer/src/agentIntent.ts +33 -1
- package/apps/explorer/src/index.css +217 -2
- package/apps/explorer/src/layout.ts +46 -0
- package/apps/explorer/src/scene/FileBlock.tsx +110 -145
- package/apps/explorer/src/scene/FolderArea.tsx +17 -4
- package/apps/explorer/src/scene/MapSelectBorder.tsx +3 -1
- package/apps/explorer/src/scene/MapView.tsx +10 -12
- package/apps/explorer/src/scene/Player.tsx +27 -0
- package/apps/explorer/src/scene/RelationLines.tsx +36 -32
- package/apps/explorer/src/scene/SelectionController.tsx +21 -3
- package/apps/explorer/src/scene/SelectionThumbnail.tsx +451 -0
- package/apps/explorer/src/scene/World.tsx +16 -35
- package/apps/explorer/src/theme.ts +7 -1
- package/apps/explorer/src/types.ts +12 -0
- package/apps/explorer/src/ui/HUD.tsx +662 -358
- package/apps/explorer/vite.config.ts +33 -21
- package/bin/session.mjs +16 -2
- package/package.json +1 -1
- package/skill/inbase/SKILL.md +22 -14
|
@@ -235,8 +235,8 @@ button {
|
|
|
235
235
|
}
|
|
236
236
|
|
|
237
237
|
.map-folder-label-selected {
|
|
238
|
-
color: #
|
|
239
|
-
border: 3px solid #
|
|
238
|
+
color: #f3e8ff;
|
|
239
|
+
border: 3px solid #c026ff;
|
|
240
240
|
background: rgba(25, 28, 34, 0.94);
|
|
241
241
|
}
|
|
242
242
|
|
|
@@ -324,6 +324,216 @@ button {
|
|
|
324
324
|
pointer-events: auto;
|
|
325
325
|
}
|
|
326
326
|
|
|
327
|
+
.hud-right-stack {
|
|
328
|
+
position: absolute;
|
|
329
|
+
top: 68px;
|
|
330
|
+
right: 20px;
|
|
331
|
+
bottom: 72px;
|
|
332
|
+
z-index: 5;
|
|
333
|
+
display: flex;
|
|
334
|
+
flex-direction: column;
|
|
335
|
+
gap: 12px;
|
|
336
|
+
width: min(320px, calc(100% - 40px));
|
|
337
|
+
pointer-events: none;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
.hud-right-stack .hud-panel-info {
|
|
341
|
+
position: relative;
|
|
342
|
+
top: auto;
|
|
343
|
+
right: auto;
|
|
344
|
+
z-index: auto;
|
|
345
|
+
display: flex;
|
|
346
|
+
flex: 0 1 auto;
|
|
347
|
+
flex-direction: column;
|
|
348
|
+
width: 100%;
|
|
349
|
+
min-height: 0;
|
|
350
|
+
max-height: 100%;
|
|
351
|
+
overflow: hidden;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
.hud-right-stack .hud-panel-info[data-minimized='true'] {
|
|
355
|
+
flex: 0 0 auto;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
.hud-left-stack {
|
|
359
|
+
position: absolute;
|
|
360
|
+
top: 68px;
|
|
361
|
+
left: 20px;
|
|
362
|
+
bottom: 72px;
|
|
363
|
+
z-index: 5;
|
|
364
|
+
display: flex;
|
|
365
|
+
flex-direction: column;
|
|
366
|
+
gap: 12px;
|
|
367
|
+
width: min(360px, calc(100% - 40px));
|
|
368
|
+
pointer-events: none;
|
|
369
|
+
overflow-x: hidden;
|
|
370
|
+
overflow-y: auto;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
.hud-left-stack .hud-panel-planned {
|
|
374
|
+
position: relative;
|
|
375
|
+
top: auto;
|
|
376
|
+
left: auto;
|
|
377
|
+
right: auto;
|
|
378
|
+
z-index: auto;
|
|
379
|
+
display: flex;
|
|
380
|
+
flex: 1 1 0;
|
|
381
|
+
flex-direction: column;
|
|
382
|
+
width: 100%;
|
|
383
|
+
min-height: 0;
|
|
384
|
+
max-height: 100%;
|
|
385
|
+
overflow-x: hidden;
|
|
386
|
+
overflow-y: auto;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
.hud-left-stack .hud-panel-planned[data-minimized='true'] {
|
|
390
|
+
flex: 0 0 auto;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
.hud-left-stack .hud-panel-planned[data-focused='false'] {
|
|
394
|
+
opacity: 0.86;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
.hud-left-stack .hud-panel-planned[data-focused='true'] {
|
|
398
|
+
box-shadow: 0 0 0 1px #9ad8ff;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
.hud-panel-body {
|
|
402
|
+
min-height: 0;
|
|
403
|
+
flex: 1 1 auto;
|
|
404
|
+
overflow-x: hidden;
|
|
405
|
+
overflow-y: auto;
|
|
406
|
+
overscroll-behavior: contain;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
.hud-right-stack .hud-thumbnail {
|
|
410
|
+
position: relative;
|
|
411
|
+
right: auto;
|
|
412
|
+
bottom: auto;
|
|
413
|
+
left: auto;
|
|
414
|
+
z-index: auto;
|
|
415
|
+
width: 100%;
|
|
416
|
+
margin-top: auto;
|
|
417
|
+
flex: none;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
.hud-thumbnail {
|
|
421
|
+
position: fixed;
|
|
422
|
+
right: 20px;
|
|
423
|
+
bottom: 72px;
|
|
424
|
+
left: auto;
|
|
425
|
+
z-index: 30;
|
|
426
|
+
width: min(320px, calc(100% - 40px));
|
|
427
|
+
pointer-events: auto;
|
|
428
|
+
overflow: hidden;
|
|
429
|
+
background: rgba(25, 28, 34, 0.96);
|
|
430
|
+
border: 1px solid #5d9ec4;
|
|
431
|
+
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.45);
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
.hud-thumbnail-bar {
|
|
435
|
+
display: flex;
|
|
436
|
+
align-items: center;
|
|
437
|
+
justify-content: space-between;
|
|
438
|
+
gap: 8px;
|
|
439
|
+
padding: 4px 6px 4px 12px;
|
|
440
|
+
color: #e7ebf2;
|
|
441
|
+
font-size: 12px;
|
|
442
|
+
letter-spacing: 0.02em;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
.hud-panel-chrome {
|
|
446
|
+
display: flex;
|
|
447
|
+
align-items: center;
|
|
448
|
+
justify-content: space-between;
|
|
449
|
+
gap: 8px;
|
|
450
|
+
margin: -4px 0 8px;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
.hud-panel[data-minimized='true'] {
|
|
454
|
+
overflow: hidden;
|
|
455
|
+
padding-bottom: 12px;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
.hud-panel[data-minimized='true'] .hud-panel-chrome {
|
|
459
|
+
margin-bottom: 0;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
.hud-panel-chrome-title {
|
|
463
|
+
min-width: 0;
|
|
464
|
+
overflow: hidden;
|
|
465
|
+
color: #b7c0ce;
|
|
466
|
+
font-size: 11px;
|
|
467
|
+
font-weight: 600;
|
|
468
|
+
letter-spacing: 0.08em;
|
|
469
|
+
text-transform: uppercase;
|
|
470
|
+
text-overflow: ellipsis;
|
|
471
|
+
white-space: nowrap;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
.hud-panel-info .hud-panel-chrome-title {
|
|
475
|
+
color: #e7ebf2;
|
|
476
|
+
font-size: 16px;
|
|
477
|
+
letter-spacing: 0.02em;
|
|
478
|
+
text-transform: none;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
.hud-panel-controls {
|
|
482
|
+
display: flex;
|
|
483
|
+
flex: none;
|
|
484
|
+
gap: 4px;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
.hud-panel-control {
|
|
488
|
+
width: 28px;
|
|
489
|
+
height: 28px;
|
|
490
|
+
padding: 0;
|
|
491
|
+
font-size: 16px;
|
|
492
|
+
line-height: 1;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
.hud-thumbnail-hide {
|
|
496
|
+
width: 28px;
|
|
497
|
+
height: 28px;
|
|
498
|
+
font-size: 16px;
|
|
499
|
+
line-height: 1;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
.hud-thumbnail-stage {
|
|
503
|
+
position: relative;
|
|
504
|
+
width: 100%;
|
|
505
|
+
height: 220px;
|
|
506
|
+
background: #000;
|
|
507
|
+
touch-action: none;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
.hud-thumbnail-stage canvas {
|
|
511
|
+
display: block;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
.thumbnail-block-label,
|
|
515
|
+
.thumbnail-folder-label {
|
|
516
|
+
pointer-events: none;
|
|
517
|
+
user-select: none;
|
|
518
|
+
white-space: nowrap;
|
|
519
|
+
color: #ffffff;
|
|
520
|
+
font-weight: 800;
|
|
521
|
+
letter-spacing: 0.02em;
|
|
522
|
+
line-height: 1.1;
|
|
523
|
+
text-shadow:
|
|
524
|
+
0 0 4px #000,
|
|
525
|
+
0 1px 2px #000,
|
|
526
|
+
0 0 10px rgba(0, 0, 0, 0.85);
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
.thumbnail-block-label {
|
|
530
|
+
font-size: 13px;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
.thumbnail-folder-label {
|
|
534
|
+
font-size: 15px;
|
|
535
|
+
}
|
|
536
|
+
|
|
327
537
|
.hud-panel-planned {
|
|
328
538
|
left: 20px;
|
|
329
539
|
right: auto;
|
|
@@ -452,12 +662,17 @@ button {
|
|
|
452
662
|
.hud-working {
|
|
453
663
|
display: flex;
|
|
454
664
|
align-items: center;
|
|
665
|
+
flex-wrap: wrap;
|
|
455
666
|
gap: 9px;
|
|
456
667
|
margin-top: 12px;
|
|
457
668
|
color: #d7eef8;
|
|
458
669
|
font-size: 12px;
|
|
459
670
|
}
|
|
460
671
|
|
|
672
|
+
.hud-working .hud-button {
|
|
673
|
+
margin-left: auto;
|
|
674
|
+
}
|
|
675
|
+
|
|
461
676
|
.hud-spinner {
|
|
462
677
|
width: 14px;
|
|
463
678
|
height: 14px;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { CONFIG, fileHeight } from './theme'
|
|
2
|
+
import type { ChangeKind } from './theme'
|
|
2
3
|
import type {
|
|
3
4
|
CodebaseGraph,
|
|
4
5
|
FileNode,
|
|
@@ -506,3 +507,48 @@ export function worldBounds(layout: WorldLayout) {
|
|
|
506
507
|
depth: maxZ - minZ,
|
|
507
508
|
}
|
|
508
509
|
}
|
|
510
|
+
|
|
511
|
+
export function fileChangeKind(
|
|
512
|
+
id: string,
|
|
513
|
+
planned: Set<string>,
|
|
514
|
+
created: Set<string>,
|
|
515
|
+
deleted: Set<string>,
|
|
516
|
+
): ChangeKind | null {
|
|
517
|
+
if (deleted.has(id)) return 'remove'
|
|
518
|
+
if (created.has(id)) return 'add'
|
|
519
|
+
if (planned.has(id)) return 'edit'
|
|
520
|
+
return null
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
export function folderChangeHighlights(
|
|
524
|
+
planned: Set<string>,
|
|
525
|
+
created: Set<string>,
|
|
526
|
+
deleted: Set<string>,
|
|
527
|
+
folders: Record<string, PlacedFolder>,
|
|
528
|
+
): Partial<Record<string, ChangeKind>> {
|
|
529
|
+
const highlighted: Partial<Record<string, ChangeKind>> = {}
|
|
530
|
+
const folderKinds = new Map<string, Set<ChangeKind>>()
|
|
531
|
+
const addFolderKind = (id: string, kind: ChangeKind) => {
|
|
532
|
+
const folder = folderOfFile(id)
|
|
533
|
+
const kinds = folderKinds.get(folder) ?? new Set<ChangeKind>()
|
|
534
|
+
kinds.add(kind)
|
|
535
|
+
folderKinds.set(folder, kinds)
|
|
536
|
+
}
|
|
537
|
+
for (const id of planned) {
|
|
538
|
+
addFolderKind(id, created.has(id) ? 'add' : 'edit')
|
|
539
|
+
}
|
|
540
|
+
for (const id of deleted) addFolderKind(id, 'remove')
|
|
541
|
+
for (const [folder, kinds] of folderKinds) {
|
|
542
|
+
if (kinds.size === 1) highlighted[folder] = [...kinds][0]
|
|
543
|
+
}
|
|
544
|
+
if (planned.size > 0 || deleted.size > 0) {
|
|
545
|
+
for (const folder of Object.values(folders)) {
|
|
546
|
+
if (!folder.added) continue
|
|
547
|
+
const kinds = folderKinds.get(folder.path)
|
|
548
|
+
if (!kinds || kinds.size === 1) {
|
|
549
|
+
highlighted[folder.path] ??= 'add'
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
return highlighted
|
|
554
|
+
}
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import { Suspense } from 'react'
|
|
2
2
|
import { Billboard, Edges, Html, Text } from '@react-three/drei'
|
|
3
|
-
import { CHANGE_HIGHLIGHT, CONFIG, dimColor, fileColor, type ChangeKind } from '../theme'
|
|
3
|
+
import { CHANGE_HIGHLIGHT, CONFIG, dimColor, fileColor, FILE_SELECTION, MAP_SELECTION, type ChangeKind } from '../theme'
|
|
4
4
|
import { NameInput } from '../ui/NameInput'
|
|
5
5
|
import { MapSelectBorder } from './MapSelectBorder'
|
|
6
6
|
import type { FileNode } from '../types'
|
|
7
7
|
import type { PlacedFile } from '../types'
|
|
8
8
|
|
|
9
|
-
const BAR = 0.22
|
|
10
9
|
const SIDE_LABEL_PAD = 0.05
|
|
11
10
|
const SIDE_LABEL_MIN_HEIGHT = CONFIG.eyeHeight * 2.5
|
|
12
11
|
const SIDE_LABELS: { rotationY: number; axis: 'x' | 'z'; sign: 1 | -1 }[] = [
|
|
@@ -28,51 +27,12 @@ type FileBlockProps = {
|
|
|
28
27
|
dimmed: boolean
|
|
29
28
|
naming?: boolean
|
|
30
29
|
mapMode?: boolean
|
|
30
|
+
highlightMapChange?: boolean
|
|
31
|
+
previewLabels?: boolean
|
|
31
32
|
onCommitName?: (name: string) => void
|
|
32
33
|
onCancelName?: () => void
|
|
33
34
|
}
|
|
34
35
|
|
|
35
|
-
function PlannedFrame({
|
|
36
|
-
width,
|
|
37
|
-
height,
|
|
38
|
-
depth,
|
|
39
|
-
color,
|
|
40
|
-
}: {
|
|
41
|
-
width: number
|
|
42
|
-
height: number
|
|
43
|
-
depth: number
|
|
44
|
-
color: string
|
|
45
|
-
}) {
|
|
46
|
-
const ox = width / 2 + BAR / 2
|
|
47
|
-
const oy = height / 2 + BAR / 2
|
|
48
|
-
const oz = depth / 2 + BAR / 2
|
|
49
|
-
const bars: [number, number, number, number, number, number][] = [
|
|
50
|
-
[ox, 0, oz, BAR, height + BAR * 2, BAR],
|
|
51
|
-
[-ox, 0, oz, BAR, height + BAR * 2, BAR],
|
|
52
|
-
[ox, 0, -oz, BAR, height + BAR * 2, BAR],
|
|
53
|
-
[-ox, 0, -oz, BAR, height + BAR * 2, BAR],
|
|
54
|
-
[0, oy, oz, width + BAR * 2, BAR, BAR],
|
|
55
|
-
[0, oy, -oz, width + BAR * 2, BAR, BAR],
|
|
56
|
-
[ox, oy, 0, BAR, BAR, depth],
|
|
57
|
-
[-ox, oy, 0, BAR, BAR, depth],
|
|
58
|
-
[0, -oy, oz, width + BAR * 2, BAR, BAR],
|
|
59
|
-
[0, -oy, -oz, width + BAR * 2, BAR, BAR],
|
|
60
|
-
[ox, -oy, 0, BAR, BAR, depth],
|
|
61
|
-
[-ox, -oy, 0, BAR, BAR, depth],
|
|
62
|
-
]
|
|
63
|
-
|
|
64
|
-
return (
|
|
65
|
-
<group>
|
|
66
|
-
{bars.map(([x, y, z, w, h, d], index) => (
|
|
67
|
-
<mesh key={index} position={[x, y, z]}>
|
|
68
|
-
<boxGeometry args={[w, h, d]} />
|
|
69
|
-
<meshBasicMaterial color={color} toneMapped={false} />
|
|
70
|
-
</mesh>
|
|
71
|
-
))}
|
|
72
|
-
</group>
|
|
73
|
-
)
|
|
74
|
-
}
|
|
75
|
-
|
|
76
36
|
function fileLabel(name: string, changeKind: ChangeKind | null, added: boolean) {
|
|
77
37
|
if (changeKind === 'remove') return `- ${name}`
|
|
78
38
|
if (changeKind === 'add' || added) return `+ ${name}`
|
|
@@ -91,15 +51,18 @@ export function FileBlock({
|
|
|
91
51
|
dimmed,
|
|
92
52
|
naming = false,
|
|
93
53
|
mapMode = false,
|
|
54
|
+
highlightMapChange = mapMode,
|
|
55
|
+
previewLabels = false,
|
|
94
56
|
onCommitName,
|
|
95
57
|
onCancelName,
|
|
96
58
|
}: FileBlockProps) {
|
|
97
59
|
const [width, height, depth] = placed.size
|
|
98
|
-
const highlight =
|
|
60
|
+
const highlight =
|
|
61
|
+
changeKind && highlightMapChange && !selected
|
|
62
|
+
? CHANGE_HIGHLIGHT[changeKind]
|
|
63
|
+
: null
|
|
99
64
|
const color = added || file.userCreated ? '#7ec8e8' : fileColor(file.language)
|
|
100
65
|
const muted = dimColor(color, 0.32)
|
|
101
|
-
const walkSelected = selected && !mapMode
|
|
102
|
-
const mapSelected = selected && mapMode
|
|
103
66
|
const label = fileLabel(file.name, changeKind, added || Boolean(file.userCreated))
|
|
104
67
|
const labelColor = aimed
|
|
105
68
|
? '#9ad8ff'
|
|
@@ -112,62 +75,58 @@ export function FileBlock({
|
|
|
112
75
|
? '#9ad8ff'
|
|
113
76
|
: highlight
|
|
114
77
|
? highlight.color
|
|
115
|
-
:
|
|
116
|
-
?
|
|
78
|
+
: selected
|
|
79
|
+
? FILE_SELECTION.color
|
|
117
80
|
: related
|
|
118
81
|
? '#4e7f72'
|
|
119
82
|
: dimmed
|
|
120
83
|
? muted
|
|
121
84
|
: color
|
|
122
85
|
|
|
86
|
+
const showLabels = !naming && !mapMode
|
|
87
|
+
|
|
123
88
|
return (
|
|
124
89
|
<group position={placed.position}>
|
|
125
|
-
{
|
|
126
|
-
<
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
: dimmed
|
|
166
|
-
? 0.08
|
|
167
|
-
: 0.12
|
|
168
|
-
}
|
|
169
|
-
/>
|
|
170
|
-
{walkSelected && (
|
|
90
|
+
<mesh userData={{ fileId: file.id }} scale={[width, height, depth]}>
|
|
91
|
+
<boxGeometry args={[1, 1, 1]} />
|
|
92
|
+
{mapMode ? (
|
|
93
|
+
<meshBasicMaterial color={meshColor} toneMapped={false} />
|
|
94
|
+
) : (
|
|
95
|
+
<meshLambertMaterial
|
|
96
|
+
color={meshColor}
|
|
97
|
+
emissive={
|
|
98
|
+
aimed
|
|
99
|
+
? '#3a6a80'
|
|
100
|
+
: highlight
|
|
101
|
+
? highlight.emissive
|
|
102
|
+
: selected
|
|
103
|
+
? FILE_SELECTION.emissive
|
|
104
|
+
: related
|
|
105
|
+
? '#1f4a44'
|
|
106
|
+
: added || file.userCreated
|
|
107
|
+
? '#2a5064'
|
|
108
|
+
: dimmed
|
|
109
|
+
? muted
|
|
110
|
+
: color
|
|
111
|
+
}
|
|
112
|
+
emissiveIntensity={
|
|
113
|
+
aimed
|
|
114
|
+
? 0.45
|
|
115
|
+
: highlight
|
|
116
|
+
? 0.95
|
|
117
|
+
: selected
|
|
118
|
+
? 0.55
|
|
119
|
+
: related
|
|
120
|
+
? 0.18
|
|
121
|
+
: added || file.userCreated
|
|
122
|
+
? 0.22
|
|
123
|
+
: dimmed
|
|
124
|
+
? 0.08
|
|
125
|
+
: 0.12
|
|
126
|
+
}
|
|
127
|
+
/>
|
|
128
|
+
)}
|
|
129
|
+
{selected && !mapMode && (
|
|
171
130
|
<Edges
|
|
172
131
|
color="#000000"
|
|
173
132
|
dashed
|
|
@@ -175,26 +134,18 @@ export function FileBlock({
|
|
|
175
134
|
gapSize={0.1}
|
|
176
135
|
lineWidth={2.5}
|
|
177
136
|
toneMapped={false}
|
|
178
|
-
scale={1.002}
|
|
179
137
|
/>
|
|
180
138
|
)}
|
|
181
139
|
</mesh>
|
|
182
|
-
{
|
|
140
|
+
{selected && mapMode && (
|
|
183
141
|
<MapSelectBorder
|
|
184
142
|
width={width}
|
|
185
143
|
depth={depth}
|
|
186
|
-
y={height / 2 + 0.
|
|
144
|
+
y={height / 2 + 0.03}
|
|
145
|
+
stroke={MAP_SELECTION.blockPad}
|
|
187
146
|
userData={{ fileId: file.id }}
|
|
188
147
|
/>
|
|
189
148
|
)}
|
|
190
|
-
{highlight && !mapSelected && (
|
|
191
|
-
<PlannedFrame
|
|
192
|
-
width={width}
|
|
193
|
-
height={height}
|
|
194
|
-
depth={depth}
|
|
195
|
-
color={highlight.color}
|
|
196
|
-
/>
|
|
197
|
-
)}
|
|
198
149
|
{naming && onCommitName && onCancelName && (
|
|
199
150
|
<Html
|
|
200
151
|
position={[0, height / 2 + 0.42, 0]}
|
|
@@ -210,47 +161,61 @@ export function FileBlock({
|
|
|
210
161
|
/>
|
|
211
162
|
</Html>
|
|
212
163
|
)}
|
|
213
|
-
|
|
214
|
-
{
|
|
215
|
-
|
|
216
|
-
<
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
164
|
+
{showLabels && (
|
|
165
|
+
<Suspense fallback={null}>
|
|
166
|
+
{previewLabels ? (
|
|
167
|
+
<Html
|
|
168
|
+
position={[0, height / 2 + 0.55, 0]}
|
|
169
|
+
center
|
|
170
|
+
occlude={false}
|
|
171
|
+
style={{ pointerEvents: 'none' }}
|
|
172
|
+
zIndexRange={[20, 0]}
|
|
222
173
|
>
|
|
223
|
-
{label}
|
|
224
|
-
</
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
174
|
+
<div className="thumbnail-block-label">{label}</div>
|
|
175
|
+
</Html>
|
|
176
|
+
) : (
|
|
177
|
+
<>
|
|
178
|
+
<Billboard position={[0, height / 2 + (planned || highlight ? 0.55 : 0.38), 0]}>
|
|
179
|
+
<Text
|
|
180
|
+
fontSize={0.28}
|
|
181
|
+
color={labelColor}
|
|
182
|
+
anchorX="center"
|
|
183
|
+
anchorY="bottom"
|
|
184
|
+
maxWidth={3.4}
|
|
185
|
+
>
|
|
186
|
+
{label}
|
|
187
|
+
</Text>
|
|
188
|
+
</Billboard>
|
|
189
|
+
{height >= SIDE_LABEL_MIN_HEIGHT &&
|
|
190
|
+
SIDE_LABELS.map((side) => {
|
|
191
|
+
const face = side.axis === 'x' ? width : depth
|
|
192
|
+
const x =
|
|
193
|
+
side.axis === 'x' ? side.sign * (width / 2 + SIDE_LABEL_PAD) : 0
|
|
194
|
+
const z =
|
|
195
|
+
side.axis === 'z' ? side.sign * (depth / 2 + SIDE_LABEL_PAD) : 0
|
|
196
|
+
return (
|
|
197
|
+
<Text
|
|
198
|
+
key={`${side.axis}:${side.sign}`}
|
|
199
|
+
position={[x, -height / 2 + 0.22, z]}
|
|
200
|
+
rotation={[0, side.rotationY, 0]}
|
|
201
|
+
fontSize={0.22}
|
|
202
|
+
color={labelColor}
|
|
203
|
+
anchorX="center"
|
|
204
|
+
anchorY="middle"
|
|
205
|
+
maxWidth={face - 0.2}
|
|
206
|
+
overflowWrap="break-word"
|
|
207
|
+
outlineWidth={0.012}
|
|
208
|
+
outlineColor="#11151c"
|
|
209
|
+
depthOffset={-1}
|
|
210
|
+
>
|
|
211
|
+
{label}
|
|
212
|
+
</Text>
|
|
213
|
+
)
|
|
214
|
+
})}
|
|
215
|
+
</>
|
|
216
|
+
)}
|
|
217
|
+
</Suspense>
|
|
218
|
+
)}
|
|
254
219
|
</group>
|
|
255
220
|
)
|
|
256
221
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Suspense } from 'react'
|
|
2
|
-
import { Text } from '@react-three/drei'
|
|
2
|
+
import { Html, Text } from '@react-three/drei'
|
|
3
3
|
import { CHANGE_HIGHLIGHT, folderFloorColor, MAP_SELECTION, type ChangeKind } from '../theme'
|
|
4
4
|
import type { PlacedFolder } from '../types'
|
|
5
5
|
import { MapSelectBorder } from './MapSelectBorder'
|
|
@@ -10,6 +10,7 @@ type FolderAreaProps = {
|
|
|
10
10
|
selected?: boolean
|
|
11
11
|
mapMode?: boolean
|
|
12
12
|
highlightKind?: ChangeKind | null
|
|
13
|
+
previewLabels?: boolean
|
|
13
14
|
}
|
|
14
15
|
|
|
15
16
|
export function FolderArea({
|
|
@@ -18,10 +19,10 @@ export function FolderArea({
|
|
|
18
19
|
selected = false,
|
|
19
20
|
mapMode = false,
|
|
20
21
|
highlightKind = null,
|
|
22
|
+
previewLabels = false,
|
|
21
23
|
}: FolderAreaProps) {
|
|
22
24
|
const added = Boolean(folder.added)
|
|
23
|
-
const highlight =
|
|
24
|
-
mapMode && highlightKind ? CHANGE_HIGHLIGHT[highlightKind] : null
|
|
25
|
+
const highlight = highlightKind ? CHANGE_HIGHLIGHT[highlightKind] : null
|
|
25
26
|
const addedOnly = added && !highlight
|
|
26
27
|
const outline = highlight ? highlight.color : addedOnly ? '#7ec8e8' : null
|
|
27
28
|
const color = highlight
|
|
@@ -57,6 +58,7 @@ export function FolderArea({
|
|
|
57
58
|
depth={folder.depth}
|
|
58
59
|
y={0.06}
|
|
59
60
|
stroke={MAP_SELECTION.islandPad}
|
|
61
|
+
color={MAP_SELECTION.island}
|
|
60
62
|
/>
|
|
61
63
|
)}
|
|
62
64
|
<mesh position={[0, 0.02, 0]} rotation={[-Math.PI / 2, 0, 0]}>
|
|
@@ -79,7 +81,18 @@ export function FolderArea({
|
|
|
79
81
|
</mesh>
|
|
80
82
|
)}
|
|
81
83
|
<Suspense fallback={null}>
|
|
82
|
-
{!naming && (
|
|
84
|
+
{!naming && previewLabels && (
|
|
85
|
+
<Html
|
|
86
|
+
position={[0, 1.35, -folder.depth / 2 + 1.6]}
|
|
87
|
+
center
|
|
88
|
+
occlude={false}
|
|
89
|
+
style={{ pointerEvents: 'none' }}
|
|
90
|
+
zIndexRange={[20, 0]}
|
|
91
|
+
>
|
|
92
|
+
<div className="thumbnail-folder-label">{label}</div>
|
|
93
|
+
</Html>
|
|
94
|
+
)}
|
|
95
|
+
{!naming && !previewLabels && !mapMode && (
|
|
83
96
|
<Text
|
|
84
97
|
position={[0, 0.05, -folder.depth / 2 + 1.6]}
|
|
85
98
|
rotation={[-Math.PI / 2, 0, 0]}
|