@principal-ai/file-city-react 0.5.38 → 0.5.39
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/dist/components/FileCity3D/FileCity3D.d.ts +5 -0
- package/dist/components/FileCity3D/FileCity3D.d.ts.map +1 -1
- package/dist/components/FileCity3D/FileCity3D.js +6 -3
- package/package.json +1 -1
- package/src/components/FileCity3D/FileCity3D.tsx +12 -2
- package/src/stories/ScopeOverlay.stories.tsx +153 -1
|
@@ -63,6 +63,11 @@ export interface ElevatedScopePanel {
|
|
|
63
63
|
label?: string;
|
|
64
64
|
/** Hex color for the label (default white). */
|
|
65
65
|
labelColor?: string;
|
|
66
|
+
/**
|
|
67
|
+
* Absolute label font size in world units. When omitted, falls back to a
|
|
68
|
+
* size derived from the panel's footprint. Always clamped to fit the tile.
|
|
69
|
+
*/
|
|
70
|
+
labelSize?: number;
|
|
66
71
|
/** Click handler. When set, the slab becomes interactive and shows a pointer cursor. */
|
|
67
72
|
onClick?: () => void;
|
|
68
73
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FileCity3D.d.ts","sourceRoot":"","sources":["../../../src/components/FileCity3D/FileCity3D.tsx"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"FileCity3D.d.ts","sourceRoot":"","sources":["../../../src/components/FileCity3D/FileCity3D.tsx"],"names":[],"mappings":"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"}
|
|
@@ -1692,8 +1692,11 @@ function CityScene({ cityData, onBuildingHover, onBuildingClick, hoveredBuilding
|
|
|
1692
1692
|
const topY = y + t / 2;
|
|
1693
1693
|
// Size text to the panel: roughly fit longest reasonable label,
|
|
1694
1694
|
// clamped so tiny tiles still render legibly and huge ones don't
|
|
1695
|
-
// get absurd.
|
|
1696
|
-
|
|
1695
|
+
// get absurd. Callers may override via panel.labelSize, but we
|
|
1696
|
+
// still cap to the tile footprint so the label fits.
|
|
1697
|
+
const tileMax = Math.min(w, d) / 2;
|
|
1698
|
+
const requested = panel.labelSize ?? Math.min(w, d) / 6;
|
|
1699
|
+
const labelSize = Math.max(4, Math.min(tileMax, requested));
|
|
1697
1700
|
const handleClick = panel.onClick
|
|
1698
1701
|
? (e) => {
|
|
1699
1702
|
e.stopPropagation();
|
|
@@ -1711,7 +1714,7 @@ function CityScene({ cityData, onBuildingHover, onBuildingClick, hoveredBuilding
|
|
|
1711
1714
|
document.body.style.cursor = '';
|
|
1712
1715
|
}
|
|
1713
1716
|
: undefined;
|
|
1714
|
-
return (_jsxs("group", { children: [_jsxs("mesh", { position: [cx, y, cz], renderOrder: 10, onClick: handleClick, onPointerOver: handlePointerOver, onPointerOut: handlePointerOut, children: [_jsx("boxGeometry", { args: [w, t, d] }), _jsx("meshBasicMaterial", { color: panel.color, transparent: !isOpaque, opacity: opacity, depthWrite: isOpaque })] }), panel.label && (_jsxs(Text, { position: [cx, topY + 0.05, cz], rotation: [-Math.PI / 2, 0, 0], fontSize: labelSize, color: panel.labelColor ?? '#ffffff', anchorX: "center", anchorY: "middle", maxWidth: w * 0.9, textAlign: "center", renderOrder: 11, children: [panel.label, _jsx("meshBasicMaterial", { attach: "material", color: panel.labelColor ?? '#ffffff', depthWrite: false })] }))] }, panel.id));
|
|
1717
|
+
return (_jsxs("group", { children: [_jsxs("mesh", { position: [cx, y, cz], renderOrder: 10, onClick: handleClick, onPointerOver: handlePointerOver, onPointerOut: handlePointerOut, children: [_jsx("boxGeometry", { args: [w, t, d] }), _jsx("meshBasicMaterial", { color: panel.color, transparent: !isOpaque, opacity: opacity, depthWrite: isOpaque })] }), panel.label && (_jsxs(Text, { position: [cx, topY + 0.05, cz], rotation: [-Math.PI / 2, 0, 0], fontSize: labelSize, color: panel.labelColor ?? '#ffffff', anchorX: "center", anchorY: "middle", maxWidth: w * 0.9, textAlign: "center", renderOrder: 11, frustumCulled: false, children: [panel.label, _jsx("meshBasicMaterial", { attach: "material", color: panel.labelColor ?? '#ffffff', depthWrite: false, depthTest: false })] }))] }, panel.id));
|
|
1715
1718
|
})] }));
|
|
1716
1719
|
}
|
|
1717
1720
|
/**
|
package/package.json
CHANGED
|
@@ -88,6 +88,11 @@ export interface ElevatedScopePanel {
|
|
|
88
88
|
label?: string;
|
|
89
89
|
/** Hex color for the label (default white). */
|
|
90
90
|
labelColor?: string;
|
|
91
|
+
/**
|
|
92
|
+
* Absolute label font size in world units. When omitted, falls back to a
|
|
93
|
+
* size derived from the panel's footprint. Always clamped to fit the tile.
|
|
94
|
+
*/
|
|
95
|
+
labelSize?: number;
|
|
91
96
|
/** Click handler. When set, the slab becomes interactive and shows a pointer cursor. */
|
|
92
97
|
onClick?: () => void;
|
|
93
98
|
}
|
|
@@ -2666,8 +2671,11 @@ function CityScene({
|
|
|
2666
2671
|
const topY = y + t / 2;
|
|
2667
2672
|
// Size text to the panel: roughly fit longest reasonable label,
|
|
2668
2673
|
// clamped so tiny tiles still render legibly and huge ones don't
|
|
2669
|
-
// get absurd.
|
|
2670
|
-
|
|
2674
|
+
// get absurd. Callers may override via panel.labelSize, but we
|
|
2675
|
+
// still cap to the tile footprint so the label fits.
|
|
2676
|
+
const tileMax = Math.min(w, d) / 2;
|
|
2677
|
+
const requested = panel.labelSize ?? Math.min(w, d) / 6;
|
|
2678
|
+
const labelSize = Math.max(4, Math.min(tileMax, requested));
|
|
2671
2679
|
|
|
2672
2680
|
const handleClick = panel.onClick
|
|
2673
2681
|
? (e: ThreeEvent<MouseEvent>) => {
|
|
@@ -2715,12 +2723,14 @@ function CityScene({
|
|
|
2715
2723
|
maxWidth={w * 0.9}
|
|
2716
2724
|
textAlign="center"
|
|
2717
2725
|
renderOrder={11}
|
|
2726
|
+
frustumCulled={false}
|
|
2718
2727
|
>
|
|
2719
2728
|
{panel.label}
|
|
2720
2729
|
<meshBasicMaterial
|
|
2721
2730
|
attach="material"
|
|
2722
2731
|
color={panel.labelColor ?? '#ffffff'}
|
|
2723
2732
|
depthWrite={false}
|
|
2733
|
+
depthTest={false}
|
|
2724
2734
|
/>
|
|
2725
2735
|
</Text>
|
|
2726
2736
|
)}
|
|
@@ -116,6 +116,99 @@ const ELECTRON_DISTRICTS_BY_PATH: Map<string, CityDistrict> = new Map(
|
|
|
116
116
|
(electronAppCityData as CityData).districts.map(d => [d.path, d]),
|
|
117
117
|
);
|
|
118
118
|
|
|
119
|
+
/**
|
|
120
|
+
* Parent directory → list of immediate child directories. Top-level folders
|
|
121
|
+
* (e.g. `electron-app`) live under the empty-string parent. Used to walk the
|
|
122
|
+
* folder hierarchy when generating elevated panels for the file-tree tab.
|
|
123
|
+
*/
|
|
124
|
+
const ELECTRON_FOLDER_CHILDREN: Map<string, string[]> = (() => {
|
|
125
|
+
const m = new Map<string, string[]>();
|
|
126
|
+
const dirs = Array.from(ELECTRON_DIRECTORIES).sort();
|
|
127
|
+
for (const dir of dirs) {
|
|
128
|
+
const slash = dir.lastIndexOf('/');
|
|
129
|
+
const parent = slash >= 0 ? dir.slice(0, slash) : '';
|
|
130
|
+
const arr = m.get(parent);
|
|
131
|
+
if (arr) arr.push(dir);
|
|
132
|
+
else m.set(parent, [dir]);
|
|
133
|
+
}
|
|
134
|
+
return m;
|
|
135
|
+
})();
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Folder path → world bounds spanning every descendant district (including
|
|
139
|
+
* the folder itself, when it is a district). A collapsed folder uses these
|
|
140
|
+
* unioned bounds for its umbrella panel.
|
|
141
|
+
*/
|
|
142
|
+
const ELECTRON_FOLDER_BOUNDS: Map<
|
|
143
|
+
string,
|
|
144
|
+
{ minX: number; maxX: number; minZ: number; maxZ: number }
|
|
145
|
+
> = (() => {
|
|
146
|
+
const m = new Map<
|
|
147
|
+
string,
|
|
148
|
+
{ minX: number; maxX: number; minZ: number; maxZ: number }
|
|
149
|
+
>();
|
|
150
|
+
for (const district of (electronAppCityData as CityData).districts) {
|
|
151
|
+
const b = district.worldBounds;
|
|
152
|
+
let path = district.path;
|
|
153
|
+
while (path) {
|
|
154
|
+
const cur = m.get(path);
|
|
155
|
+
if (!cur) {
|
|
156
|
+
m.set(path, { minX: b.minX, maxX: b.maxX, minZ: b.minZ, maxZ: b.maxZ });
|
|
157
|
+
} else {
|
|
158
|
+
if (b.minX < cur.minX) cur.minX = b.minX;
|
|
159
|
+
if (b.maxX > cur.maxX) cur.maxX = b.maxX;
|
|
160
|
+
if (b.minZ < cur.minZ) cur.minZ = b.minZ;
|
|
161
|
+
if (b.maxZ > cur.maxZ) cur.maxZ = b.maxZ;
|
|
162
|
+
}
|
|
163
|
+
const slash = path.lastIndexOf('/');
|
|
164
|
+
if (slash < 0) break;
|
|
165
|
+
path = path.slice(0, slash);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return m;
|
|
169
|
+
})();
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Folder path → number of descendant files. Used to scale folder-panel
|
|
173
|
+
* label text so larger folders read first when scanning the city.
|
|
174
|
+
*/
|
|
175
|
+
const ELECTRON_FOLDER_FILE_COUNTS: Map<string, number> = (() => {
|
|
176
|
+
const m = new Map<string, number>();
|
|
177
|
+
for (const b of (electronAppCityData as CityData).buildings) {
|
|
178
|
+
let path = b.path;
|
|
179
|
+
const slash = path.lastIndexOf('/');
|
|
180
|
+
path = slash >= 0 ? path.slice(0, slash) : '';
|
|
181
|
+
while (path) {
|
|
182
|
+
m.set(path, (m.get(path) ?? 0) + 1);
|
|
183
|
+
const s = path.lastIndexOf('/');
|
|
184
|
+
if (s < 0) break;
|
|
185
|
+
path = path.slice(0, s);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return m;
|
|
189
|
+
})();
|
|
190
|
+
|
|
191
|
+
const FOLDER_PALETTE = [
|
|
192
|
+
'#3b82f6',
|
|
193
|
+
'#22c55e',
|
|
194
|
+
'#f59e0b',
|
|
195
|
+
'#ec4899',
|
|
196
|
+
'#8b5cf6',
|
|
197
|
+
'#06b6d4',
|
|
198
|
+
'#ef4444',
|
|
199
|
+
'#14b8a6',
|
|
200
|
+
'#a855f7',
|
|
201
|
+
'#eab308',
|
|
202
|
+
];
|
|
203
|
+
|
|
204
|
+
function hashFolderColor(path: string): string {
|
|
205
|
+
let h = 0;
|
|
206
|
+
for (let i = 0; i < path.length; i++) {
|
|
207
|
+
h = (h * 31 + path.charCodeAt(i)) | 0;
|
|
208
|
+
}
|
|
209
|
+
return FOLDER_PALETTE[Math.abs(h) % FOLDER_PALETTE.length];
|
|
210
|
+
}
|
|
211
|
+
|
|
119
212
|
// ---------------------------------------------------------------------------
|
|
120
213
|
// Scope tree paths
|
|
121
214
|
// ---------------------------------------------------------------------------
|
|
@@ -1079,6 +1172,65 @@ const SingleScopeTemplate: React.FC = () => {
|
|
|
1079
1172
|
return panels.length > 0 ? panels : undefined;
|
|
1080
1173
|
}, [activeTab, scopes, scopeTreeModel, treeExpansion]);
|
|
1081
1174
|
|
|
1175
|
+
// Track which folders are expanded in the file tree. The file-tree tab's
|
|
1176
|
+
// elevated panels mirror this: a collapsed folder shows one umbrella tile
|
|
1177
|
+
// covering every descendant district; expanding the folder reveals its
|
|
1178
|
+
// sub-folder tiles (or the buildings themselves at the leaves).
|
|
1179
|
+
const folderTreeExpansion = useFileTreeSelector(
|
|
1180
|
+
treeModel,
|
|
1181
|
+
React.useCallback((model: FileTree) => {
|
|
1182
|
+
const expanded = new Set<string>();
|
|
1183
|
+
for (const dir of ELECTRON_DIRECTORIES) {
|
|
1184
|
+
const item = model.getItem(dir);
|
|
1185
|
+
if (item?.isDirectory() && item.isExpanded()) expanded.add(dir);
|
|
1186
|
+
}
|
|
1187
|
+
return { expanded };
|
|
1188
|
+
}, []),
|
|
1189
|
+
React.useCallback((prev, next) => {
|
|
1190
|
+
if (prev.expanded.size !== next.expanded.size) return false;
|
|
1191
|
+
for (const k of prev.expanded) if (!next.expanded.has(k)) return false;
|
|
1192
|
+
return true;
|
|
1193
|
+
}, []),
|
|
1194
|
+
);
|
|
1195
|
+
|
|
1196
|
+
// Elevated folder panels — driven by the file tree's expansion state.
|
|
1197
|
+
// Recursively walks from the top-level folders: an expanded folder
|
|
1198
|
+
// descends into its child folders; a collapsed folder emits one panel
|
|
1199
|
+
// covering the union of all descendant district bounds.
|
|
1200
|
+
const folderElevatedPanels = React.useMemo<ElevatedScopePanel[] | undefined>(() => {
|
|
1201
|
+
if (activeTab !== 'files') return undefined;
|
|
1202
|
+
const panels: ElevatedScopePanel[] = [];
|
|
1203
|
+
const walk = (folderPath: string): void => {
|
|
1204
|
+
if (folderTreeExpansion.expanded.has(folderPath)) {
|
|
1205
|
+
const children = ELECTRON_FOLDER_CHILDREN.get(folderPath) ?? [];
|
|
1206
|
+
for (const child of children) walk(child);
|
|
1207
|
+
return;
|
|
1208
|
+
}
|
|
1209
|
+
const bounds = ELECTRON_FOLDER_BOUNDS.get(folderPath);
|
|
1210
|
+
if (!bounds) return;
|
|
1211
|
+
const label = folderPath.split('/').pop() ?? folderPath;
|
|
1212
|
+
const fileCount = ELECTRON_FOLDER_FILE_COUNTS.get(folderPath) ?? 0;
|
|
1213
|
+
// sqrt growth keeps ~10x file deltas inside a ~3x label-size delta;
|
|
1214
|
+
// the renderer still clamps to the tile footprint.
|
|
1215
|
+
const labelSize = Math.max(12, Math.min(240, 8 + Math.sqrt(fileCount) * 5));
|
|
1216
|
+
panels.push({
|
|
1217
|
+
id: `folder::${folderPath}`,
|
|
1218
|
+
color: hashFolderColor(folderPath),
|
|
1219
|
+
height: 4,
|
|
1220
|
+
thickness: 2,
|
|
1221
|
+
bounds,
|
|
1222
|
+
label,
|
|
1223
|
+
labelSize,
|
|
1224
|
+
onClick: () => {
|
|
1225
|
+
const item = treeModel.getItem(folderPath);
|
|
1226
|
+
if (item?.isDirectory()) item.toggle();
|
|
1227
|
+
},
|
|
1228
|
+
});
|
|
1229
|
+
};
|
|
1230
|
+
for (const top of ELECTRON_FOLDER_CHILDREN.get('') ?? []) walk(top);
|
|
1231
|
+
return panels.length > 0 ? panels : undefined;
|
|
1232
|
+
}, [activeTab, treeModel, folderTreeExpansion]);
|
|
1233
|
+
|
|
1082
1234
|
const openAddModal = React.useCallback((prefillScopeId?: string) => {
|
|
1083
1235
|
setModalScopeId(prefillScopeId ?? '');
|
|
1084
1236
|
setModalNamespaceName('');
|
|
@@ -1484,7 +1636,7 @@ const SingleScopeTemplate: React.FC = () => {
|
|
|
1484
1636
|
linearScale={0.5}
|
|
1485
1637
|
focusDirectory={focusDirectory}
|
|
1486
1638
|
highlightLayers={cityHighlightLayers}
|
|
1487
|
-
elevatedScopePanels={cityElevatedPanels}
|
|
1639
|
+
elevatedScopePanels={cityElevatedPanels ?? folderElevatedPanels}
|
|
1488
1640
|
animation={{
|
|
1489
1641
|
startFlat: true,
|
|
1490
1642
|
autoStartDelay: null,
|