@principal-ai/file-city-react 0.3.0
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/builder/cityDataUtils.d.ts +15 -0
- package/dist/builder/cityDataUtils.d.ts.map +1 -0
- package/dist/builder/cityDataUtils.js +348 -0
- package/dist/components/ArchitectureMapHighlightLayers.d.ts +63 -0
- package/dist/components/ArchitectureMapHighlightLayers.d.ts.map +1 -0
- package/dist/components/ArchitectureMapHighlightLayers.js +1040 -0
- package/dist/components/CityViewWithReactFlow.d.ts +14 -0
- package/dist/components/CityViewWithReactFlow.d.ts.map +1 -0
- package/dist/components/CityViewWithReactFlow.js +266 -0
- package/dist/config/files.json +996 -0
- package/dist/hooks/useCodeCityData.d.ts +21 -0
- package/dist/hooks/useCodeCityData.d.ts.map +1 -0
- package/dist/hooks/useCodeCityData.js +57 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +29 -0
- package/dist/render/client/drawLayeredBuildings.d.ts +51 -0
- package/dist/render/client/drawLayeredBuildings.d.ts.map +1 -0
- package/dist/render/client/drawLayeredBuildings.js +650 -0
- package/dist/stories/ArchitectureMapGridLayout.stories.d.ts +73 -0
- package/dist/stories/ArchitectureMapGridLayout.stories.d.ts.map +1 -0
- package/dist/stories/ArchitectureMapGridLayout.stories.js +345 -0
- package/dist/stories/ArchitectureMapHighlightLayers.stories.d.ts +78 -0
- package/dist/stories/ArchitectureMapHighlightLayers.stories.d.ts.map +1 -0
- package/dist/stories/ArchitectureMapHighlightLayers.stories.js +270 -0
- package/dist/stories/CityViewWithReactFlow.stories.d.ts +24 -0
- package/dist/stories/CityViewWithReactFlow.stories.d.ts.map +1 -0
- package/dist/stories/CityViewWithReactFlow.stories.js +778 -0
- package/dist/stories/sample-data.d.ts +4 -0
- package/dist/stories/sample-data.d.ts.map +1 -0
- package/dist/stories/sample-data.js +268 -0
- package/dist/types/react-types.d.ts +17 -0
- package/dist/types/react-types.d.ts.map +1 -0
- package/dist/types/react-types.js +4 -0
- package/dist/utils/fileColorHighlightLayers.d.ts +86 -0
- package/dist/utils/fileColorHighlightLayers.d.ts.map +1 -0
- package/dist/utils/fileColorHighlightLayers.js +283 -0
- package/package.json +49 -0
- package/src/builder/cityDataUtils.ts +430 -0
- package/src/components/ArchitectureMapHighlightLayers.tsx +1518 -0
- package/src/components/CityViewWithReactFlow.tsx +365 -0
- package/src/config/files.json +996 -0
- package/src/hooks/useCodeCityData.ts +82 -0
- package/src/index.ts +64 -0
- package/src/render/client/drawLayeredBuildings.ts +946 -0
- package/src/stories/ArchitectureMapGridLayout.stories.tsx +410 -0
- package/src/stories/ArchitectureMapHighlightLayers.stories.tsx +312 -0
- package/src/stories/CityViewWithReactFlow.stories.tsx +787 -0
- package/src/stories/sample-data.ts +301 -0
- package/src/types/react-types.ts +18 -0
- package/src/utils/fileColorHighlightLayers.ts +378 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sample-data.d.ts","sourceRoot":"","sources":["../../src/stories/sample-data.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAA8B,MAAM,iCAAiC,CAAC;AAGvF,wBAAgB,oBAAoB,IAAI,QAAQ,CAmP/C;AAGD,wBAAgB,yBAAyB,IAAI,QAAQ,CAmDpD"}
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createSampleCityData = createSampleCityData;
|
|
4
|
+
exports.createSmallSampleCityData = createSmallSampleCityData;
|
|
5
|
+
// Helper function to create sample city data for stories
|
|
6
|
+
function createSampleCityData() {
|
|
7
|
+
const buildings = [];
|
|
8
|
+
const districts = [];
|
|
9
|
+
// Define file structure
|
|
10
|
+
const fileStructure = [
|
|
11
|
+
// Source files
|
|
12
|
+
{ path: 'src/index.ts', size: 1500 },
|
|
13
|
+
{ path: 'src/App.tsx', size: 3200 },
|
|
14
|
+
{ path: 'src/components/Header.tsx', size: 1800 },
|
|
15
|
+
{ path: 'src/components/Footer.tsx', size: 1200 },
|
|
16
|
+
{ path: 'src/components/Sidebar.tsx', size: 2100 },
|
|
17
|
+
{ path: 'src/components/Card.tsx', size: 900 },
|
|
18
|
+
{ path: 'src/components/Button.tsx', size: 600 },
|
|
19
|
+
{ path: 'src/utils/helpers.ts', size: 2500 },
|
|
20
|
+
{ path: 'src/utils/api.ts', size: 3100 },
|
|
21
|
+
{ path: 'src/utils/validators.ts', size: 1400 },
|
|
22
|
+
{ path: 'src/hooks/useAuth.ts', size: 800 },
|
|
23
|
+
{ path: 'src/hooks/useData.ts', size: 1100 },
|
|
24
|
+
{ path: 'src/styles/main.css', size: 4500 },
|
|
25
|
+
{ path: 'src/styles/components.css', size: 2800 },
|
|
26
|
+
// Test files
|
|
27
|
+
{ path: 'tests/unit/app.test.ts', size: 2200 },
|
|
28
|
+
{ path: 'tests/unit/header.test.ts', size: 1600 },
|
|
29
|
+
{ path: 'tests/unit/footer.test.tsx', size: 1400 },
|
|
30
|
+
{ path: 'tests/integration/api.test.ts', size: 3400 },
|
|
31
|
+
{ path: '__tests__/components.test.tsx', size: 2900 },
|
|
32
|
+
{ path: '__tests__/utils.test.ts', size: 1900 },
|
|
33
|
+
// Config files
|
|
34
|
+
{ path: 'package.json', size: 1200 },
|
|
35
|
+
{ path: 'tsconfig.json', size: 800 },
|
|
36
|
+
{ path: 'webpack.config.js', size: 2100 },
|
|
37
|
+
{ path: '.eslintrc.js', size: 600 },
|
|
38
|
+
{ path: '.prettierrc', size: 200 },
|
|
39
|
+
{ path: 'README.md', size: 3500 },
|
|
40
|
+
// Documentation
|
|
41
|
+
{ path: 'docs/README.md', size: 4200 },
|
|
42
|
+
{ path: 'docs/API.md', size: 5100 },
|
|
43
|
+
{ path: 'docs/CONTRIBUTING.md', size: 2300 },
|
|
44
|
+
// Build files
|
|
45
|
+
{ path: 'dist/bundle.js', size: 45000 },
|
|
46
|
+
{ path: 'dist/index.html', size: 800 },
|
|
47
|
+
{ path: 'dist/styles.css', size: 12000 },
|
|
48
|
+
// Node modules (sample)
|
|
49
|
+
{ path: 'node_modules/react/index.js', size: 8000 },
|
|
50
|
+
{ path: 'node_modules/react/package.json', size: 1500 },
|
|
51
|
+
{ path: 'node_modules/typescript/lib/typescript.js', size: 65000 },
|
|
52
|
+
{ path: 'node_modules/@types/react/index.d.ts', size: 3200 },
|
|
53
|
+
// Deprecated files
|
|
54
|
+
{ path: 'src/deprecated/OldComponent.tsx', size: 2400 },
|
|
55
|
+
{ path: 'src/deprecated/LegacyAPI.ts', size: 3100 },
|
|
56
|
+
];
|
|
57
|
+
// Create a simple grid layout
|
|
58
|
+
let currentX = 0;
|
|
59
|
+
let currentZ = 0;
|
|
60
|
+
const spacing = 2;
|
|
61
|
+
const maxPerRow = 10;
|
|
62
|
+
let itemsInRow = 0;
|
|
63
|
+
// Group files by directory
|
|
64
|
+
const filesByDir = new Map();
|
|
65
|
+
fileStructure.forEach(file => {
|
|
66
|
+
const dir = file.path.includes('/') ? file.path.substring(0, file.path.lastIndexOf('/')) : '';
|
|
67
|
+
if (!filesByDir.has(dir)) {
|
|
68
|
+
filesByDir.set(dir, []);
|
|
69
|
+
}
|
|
70
|
+
const dirFiles = filesByDir.get(dir);
|
|
71
|
+
if (dirFiles) {
|
|
72
|
+
dirFiles.push(file);
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
// Track district bounds
|
|
76
|
+
const districtBounds = new Map();
|
|
77
|
+
// Process each directory group
|
|
78
|
+
const sortedDirs = Array.from(filesByDir.keys()).sort();
|
|
79
|
+
let districtStartX = 0;
|
|
80
|
+
sortedDirs.forEach(dir => {
|
|
81
|
+
const files = filesByDir.get(dir);
|
|
82
|
+
if (!files)
|
|
83
|
+
return;
|
|
84
|
+
const districtMinX = currentX;
|
|
85
|
+
const districtMinZ = currentZ;
|
|
86
|
+
files.forEach(file => {
|
|
87
|
+
const extension = file.path.includes('.') ? '.' + (file.path.split('.').pop() || '') : '';
|
|
88
|
+
// Calculate building dimensions based on file size
|
|
89
|
+
const height = Math.log(file.size + 1) * 2;
|
|
90
|
+
const width = Math.sqrt(file.size) / 10;
|
|
91
|
+
const depth = width;
|
|
92
|
+
buildings.push({
|
|
93
|
+
path: file.path,
|
|
94
|
+
position: {
|
|
95
|
+
x: currentX + width / 2,
|
|
96
|
+
y: height / 2,
|
|
97
|
+
z: currentZ + depth / 2,
|
|
98
|
+
},
|
|
99
|
+
dimensions: [width, height, depth],
|
|
100
|
+
type: 'file',
|
|
101
|
+
fileExtension: extension,
|
|
102
|
+
size: file.size,
|
|
103
|
+
lastModified: new Date(),
|
|
104
|
+
});
|
|
105
|
+
// Update position for next building
|
|
106
|
+
currentX += width + spacing;
|
|
107
|
+
itemsInRow++;
|
|
108
|
+
if (itemsInRow >= maxPerRow) {
|
|
109
|
+
currentX = districtStartX;
|
|
110
|
+
currentZ += depth + spacing;
|
|
111
|
+
itemsInRow = 0;
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
// Create district bounds
|
|
115
|
+
if (dir) {
|
|
116
|
+
const districtMaxX = currentX > districtMinX ? currentX : districtMinX + 10;
|
|
117
|
+
const districtMaxZ = currentZ > districtMinZ ? currentZ + 5 : districtMinZ + 10;
|
|
118
|
+
districtBounds.set(dir, {
|
|
119
|
+
minX: districtMinX - 1,
|
|
120
|
+
maxX: districtMaxX + 1,
|
|
121
|
+
minZ: districtMinZ - 1,
|
|
122
|
+
maxZ: districtMaxZ + 1,
|
|
123
|
+
});
|
|
124
|
+
// Move to next district area
|
|
125
|
+
currentX = districtMaxX + spacing * 3;
|
|
126
|
+
if (currentX > 100) {
|
|
127
|
+
currentX = 0;
|
|
128
|
+
currentZ = districtMaxZ + spacing * 3;
|
|
129
|
+
}
|
|
130
|
+
districtStartX = currentX;
|
|
131
|
+
itemsInRow = 0;
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
// Create districts from bounds
|
|
135
|
+
const allPaths = new Set(districtBounds.keys());
|
|
136
|
+
const processedPaths = new Set();
|
|
137
|
+
// Helper to create all parent paths
|
|
138
|
+
const getParentPaths = (path) => {
|
|
139
|
+
const parts = path.split('/');
|
|
140
|
+
const parents = [];
|
|
141
|
+
for (let i = 1; i < parts.length; i++) {
|
|
142
|
+
parents.push(parts.slice(0, i).join('/'));
|
|
143
|
+
}
|
|
144
|
+
return parents;
|
|
145
|
+
};
|
|
146
|
+
// Add all parent paths to the set
|
|
147
|
+
allPaths.forEach(path => {
|
|
148
|
+
getParentPaths(path).forEach(parent => allPaths.add(parent));
|
|
149
|
+
});
|
|
150
|
+
// Create districts for all paths
|
|
151
|
+
allPaths.forEach(path => {
|
|
152
|
+
if (processedPaths.has(path))
|
|
153
|
+
return;
|
|
154
|
+
// Find all children of this path
|
|
155
|
+
const children = Array.from(districtBounds.keys()).filter(p => p.startsWith(path + '/') && !p.slice(path.length + 1).includes('/'));
|
|
156
|
+
let bounds;
|
|
157
|
+
if (districtBounds.has(path)) {
|
|
158
|
+
const pathBounds = districtBounds.get(path);
|
|
159
|
+
if (!pathBounds)
|
|
160
|
+
return;
|
|
161
|
+
bounds = pathBounds;
|
|
162
|
+
}
|
|
163
|
+
else if (children.length > 0) {
|
|
164
|
+
// Calculate bounds from children
|
|
165
|
+
const childBounds = children
|
|
166
|
+
.map(c => districtBounds.get(c))
|
|
167
|
+
.filter((b) => b !== undefined);
|
|
168
|
+
if (childBounds.length === 0)
|
|
169
|
+
return;
|
|
170
|
+
bounds = {
|
|
171
|
+
minX: Math.min(...childBounds.map(c => c.minX)),
|
|
172
|
+
maxX: Math.max(...childBounds.map(c => c.maxX)),
|
|
173
|
+
minZ: Math.min(...childBounds.map(c => c.minZ)),
|
|
174
|
+
maxZ: Math.max(...childBounds.map(c => c.maxZ)),
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
return; // Skip if no bounds
|
|
179
|
+
}
|
|
180
|
+
const fileCount = buildings.filter(b => b.path === path || b.path.startsWith(path + '/')).length;
|
|
181
|
+
districts.push({
|
|
182
|
+
path,
|
|
183
|
+
worldBounds: bounds,
|
|
184
|
+
fileCount,
|
|
185
|
+
type: 'directory',
|
|
186
|
+
});
|
|
187
|
+
processedPaths.add(path);
|
|
188
|
+
});
|
|
189
|
+
// Calculate overall bounds
|
|
190
|
+
const allX = buildings.map(b => b.position.x);
|
|
191
|
+
const allZ = buildings.map(b => b.position.z);
|
|
192
|
+
const bounds = {
|
|
193
|
+
minX: Math.min(...allX) - 5,
|
|
194
|
+
maxX: Math.max(...allX) + 5,
|
|
195
|
+
minZ: Math.min(...allZ) - 5,
|
|
196
|
+
maxZ: Math.max(...allZ) + 5,
|
|
197
|
+
};
|
|
198
|
+
return {
|
|
199
|
+
buildings,
|
|
200
|
+
districts,
|
|
201
|
+
bounds,
|
|
202
|
+
metadata: {
|
|
203
|
+
totalFiles: buildings.length,
|
|
204
|
+
totalDirectories: districts.length,
|
|
205
|
+
analyzedAt: new Date(),
|
|
206
|
+
rootPath: '/',
|
|
207
|
+
layoutConfig: {
|
|
208
|
+
paddingTop: 2,
|
|
209
|
+
paddingBottom: 2,
|
|
210
|
+
paddingLeft: 2,
|
|
211
|
+
paddingRight: 2,
|
|
212
|
+
paddingInner: 1,
|
|
213
|
+
paddingOuter: 3,
|
|
214
|
+
},
|
|
215
|
+
},
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
// Create a smaller sample for performance testing
|
|
219
|
+
function createSmallSampleCityData() {
|
|
220
|
+
const buildings = [
|
|
221
|
+
{
|
|
222
|
+
path: 'index.ts',
|
|
223
|
+
position: { x: 5, y: 3, z: 5 },
|
|
224
|
+
dimensions: [4, 6, 4],
|
|
225
|
+
type: 'file',
|
|
226
|
+
fileExtension: '.ts',
|
|
227
|
+
size: 1500,
|
|
228
|
+
lastModified: new Date(),
|
|
229
|
+
},
|
|
230
|
+
{
|
|
231
|
+
path: 'App.tsx',
|
|
232
|
+
position: { x: 12, y: 4, z: 5 },
|
|
233
|
+
dimensions: [5, 8, 5],
|
|
234
|
+
type: 'file',
|
|
235
|
+
fileExtension: '.tsx',
|
|
236
|
+
size: 3200,
|
|
237
|
+
lastModified: new Date(),
|
|
238
|
+
},
|
|
239
|
+
{
|
|
240
|
+
path: 'utils/helpers.ts',
|
|
241
|
+
position: { x: 5, y: 2.5, z: 15 },
|
|
242
|
+
dimensions: [3, 5, 3],
|
|
243
|
+
type: 'file',
|
|
244
|
+
fileExtension: '.ts',
|
|
245
|
+
size: 800,
|
|
246
|
+
lastModified: new Date(),
|
|
247
|
+
},
|
|
248
|
+
];
|
|
249
|
+
const districts = [
|
|
250
|
+
{
|
|
251
|
+
path: 'utils',
|
|
252
|
+
worldBounds: { minX: 2, maxX: 10, minZ: 12, maxZ: 20 },
|
|
253
|
+
fileCount: 1,
|
|
254
|
+
type: 'directory',
|
|
255
|
+
},
|
|
256
|
+
];
|
|
257
|
+
return {
|
|
258
|
+
buildings,
|
|
259
|
+
districts,
|
|
260
|
+
bounds: { minX: 0, maxX: 20, minZ: 0, maxZ: 25 },
|
|
261
|
+
metadata: {
|
|
262
|
+
totalFiles: buildings.length,
|
|
263
|
+
totalDirectories: districts.length,
|
|
264
|
+
analyzedAt: new Date(),
|
|
265
|
+
rootPath: '/',
|
|
266
|
+
},
|
|
267
|
+
};
|
|
268
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { CityBuilding, CityDistrict } from '@principal-ai/file-city-builder';
|
|
2
|
+
export interface MapInteractionState {
|
|
3
|
+
hoveredDistrict: CityDistrict | null;
|
|
4
|
+
hoveredBuilding: CityBuilding | null;
|
|
5
|
+
mousePos: {
|
|
6
|
+
x: number;
|
|
7
|
+
y: number;
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
export interface MapDisplayOptions {
|
|
11
|
+
showGrid: boolean;
|
|
12
|
+
showConnections: boolean;
|
|
13
|
+
maxConnections: number;
|
|
14
|
+
gridSize: number;
|
|
15
|
+
padding: number;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=react-types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"react-types.d.ts","sourceRoot":"","sources":["../../src/types/react-types.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,iCAAiC,CAAC;AAE7E,MAAM,WAAW,mBAAmB;IAClC,eAAe,EAAE,YAAY,GAAG,IAAI,CAAC;IACrC,eAAe,EAAE,YAAY,GAAG,IAAI,CAAC;IACrC,QAAQ,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CACpC;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,OAAO,CAAC;IAClB,eAAe,EAAE,OAAO,CAAC;IACzB,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CACjB"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { HighlightLayer, LayerRenderStrategy } from '../render/client/drawLayeredBuildings';
|
|
2
|
+
export interface ColorLayerConfig {
|
|
3
|
+
color: string;
|
|
4
|
+
renderStrategy: LayerRenderStrategy;
|
|
5
|
+
opacity?: number;
|
|
6
|
+
borderWidth?: number;
|
|
7
|
+
priority?: number;
|
|
8
|
+
coverOptions?: {
|
|
9
|
+
opacity?: number;
|
|
10
|
+
image?: string;
|
|
11
|
+
text?: string;
|
|
12
|
+
textSize?: number;
|
|
13
|
+
backgroundColor?: string;
|
|
14
|
+
borderRadius?: number;
|
|
15
|
+
icon?: string;
|
|
16
|
+
iconSize?: number;
|
|
17
|
+
};
|
|
18
|
+
customRender?: (ctx: CanvasRenderingContext2D, bounds: {
|
|
19
|
+
x: number;
|
|
20
|
+
y: number;
|
|
21
|
+
width: number;
|
|
22
|
+
height: number;
|
|
23
|
+
}, scale: number) => void;
|
|
24
|
+
}
|
|
25
|
+
export interface FileSuffixConfig {
|
|
26
|
+
primary: ColorLayerConfig;
|
|
27
|
+
secondary?: ColorLayerConfig;
|
|
28
|
+
displayName?: string;
|
|
29
|
+
description?: string;
|
|
30
|
+
category?: string;
|
|
31
|
+
source?: string;
|
|
32
|
+
}
|
|
33
|
+
export interface FileSuffixColorConfig {
|
|
34
|
+
version: string;
|
|
35
|
+
description: string;
|
|
36
|
+
lastUpdated: string;
|
|
37
|
+
suffixConfigs: Record<string, FileSuffixConfig>;
|
|
38
|
+
defaultConfig?: FileSuffixConfig;
|
|
39
|
+
includeUnmatched?: boolean;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Creates highlight layers for files based on file extension configurations.
|
|
43
|
+
*
|
|
44
|
+
* @param files - Array of file objects with at least a path property
|
|
45
|
+
* @param config - Optional configuration object with suffix mappings. If not provided, uses default config from files.json
|
|
46
|
+
* @returns Array of HighlightLayer objects for the map visualization
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* // Using default configuration
|
|
50
|
+
* const files = [{ path: 'src/index.ts' }, { path: 'src/App.tsx' }];
|
|
51
|
+
* const layers = createFileColorHighlightLayers(files);
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* // Using custom configuration
|
|
55
|
+
* const customConfig: FileSuffixColorConfig = {
|
|
56
|
+
* version: "1.0.0",
|
|
57
|
+
* description: "Custom colors",
|
|
58
|
+
* lastUpdated: "2025-01-26",
|
|
59
|
+
* suffixConfigs: {
|
|
60
|
+
* ".ts": {
|
|
61
|
+
* primary: {
|
|
62
|
+
* color: "#ff0000",
|
|
63
|
+
* renderStrategy: "border"
|
|
64
|
+
* }
|
|
65
|
+
* }
|
|
66
|
+
* }
|
|
67
|
+
* };
|
|
68
|
+
* const layers = createFileColorHighlightLayers(files, customConfig);
|
|
69
|
+
*/
|
|
70
|
+
export declare function createFileColorHighlightLayers(files: Array<{
|
|
71
|
+
path: string;
|
|
72
|
+
}> | null | undefined, config?: FileSuffixColorConfig): HighlightLayer[];
|
|
73
|
+
/**
|
|
74
|
+
* Get the default file color configuration.
|
|
75
|
+
* This returns the configuration loaded from files.json.
|
|
76
|
+
*/
|
|
77
|
+
export declare function getDefaultFileColorConfig(): FileSuffixColorConfig;
|
|
78
|
+
/**
|
|
79
|
+
* Get a simple color mapping from the configuration.
|
|
80
|
+
* This is useful for backwards compatibility or simpler use cases.
|
|
81
|
+
*
|
|
82
|
+
* @param config - Optional configuration. If not provided, uses default config.
|
|
83
|
+
* @returns Record mapping file extensions to hex color strings
|
|
84
|
+
*/
|
|
85
|
+
export declare function getFileColorMapping(config?: FileSuffixColorConfig): Record<string, string>;
|
|
86
|
+
//# sourceMappingURL=fileColorHighlightLayers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fileColorHighlightLayers.d.ts","sourceRoot":"","sources":["../../src/utils/fileColorHighlightLayers.ts"],"names":[],"mappings":"AACA,OAAO,EACL,cAAc,EAEd,mBAAmB,EACpB,MAAM,uCAAuC,CAAC;AAG/C,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,mBAAmB,CAAC;IACpC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE;QACb,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,eAAe,CAAC,EAAE,MAAM,CAAC;QACzB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,CAAC;IACF,YAAY,CAAC,EAAE,CACb,GAAG,EAAE,wBAAwB,EAC7B,MAAM,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,EAC/D,KAAK,EAAE,MAAM,KACV,IAAI,CAAC;CACX;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,gBAAgB,CAAC;IAC1B,SAAS,CAAC,EAAE,gBAAgB,CAAC;IAC7B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;IAChD,aAAa,CAAC,EAAE,gBAAgB,CAAC;IACjC,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,8BAA8B,CAC5C,KAAK,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,GAAG,IAAI,GAAG,SAAS,EACjD,MAAM,CAAC,EAAE,qBAAqB,GAC7B,cAAc,EAAE,CAiRlB;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,IAAI,qBAAqB,CAEjE;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,CAAC,EAAE,qBAAqB,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAM1F"}
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.createFileColorHighlightLayers = createFileColorHighlightLayers;
|
|
7
|
+
exports.getDefaultFileColorConfig = getDefaultFileColorConfig;
|
|
8
|
+
exports.getFileColorMapping = getFileColorMapping;
|
|
9
|
+
const files_json_1 = __importDefault(require("../config/files.json"));
|
|
10
|
+
/**
|
|
11
|
+
* Creates highlight layers for files based on file extension configurations.
|
|
12
|
+
*
|
|
13
|
+
* @param files - Array of file objects with at least a path property
|
|
14
|
+
* @param config - Optional configuration object with suffix mappings. If not provided, uses default config from files.json
|
|
15
|
+
* @returns Array of HighlightLayer objects for the map visualization
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* // Using default configuration
|
|
19
|
+
* const files = [{ path: 'src/index.ts' }, { path: 'src/App.tsx' }];
|
|
20
|
+
* const layers = createFileColorHighlightLayers(files);
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* // Using custom configuration
|
|
24
|
+
* const customConfig: FileSuffixColorConfig = {
|
|
25
|
+
* version: "1.0.0",
|
|
26
|
+
* description: "Custom colors",
|
|
27
|
+
* lastUpdated: "2025-01-26",
|
|
28
|
+
* suffixConfigs: {
|
|
29
|
+
* ".ts": {
|
|
30
|
+
* primary: {
|
|
31
|
+
* color: "#ff0000",
|
|
32
|
+
* renderStrategy: "border"
|
|
33
|
+
* }
|
|
34
|
+
* }
|
|
35
|
+
* }
|
|
36
|
+
* };
|
|
37
|
+
* const layers = createFileColorHighlightLayers(files, customConfig);
|
|
38
|
+
*/
|
|
39
|
+
function createFileColorHighlightLayers(files, config) {
|
|
40
|
+
if (!files || files.length === 0) {
|
|
41
|
+
return [];
|
|
42
|
+
}
|
|
43
|
+
// Use provided config or fall back to default from files.json
|
|
44
|
+
const colorConfig = config || files_json_1.default;
|
|
45
|
+
const { suffixConfigs, defaultConfig: defaultFileConfig, includeUnmatched = true } = colorConfig;
|
|
46
|
+
// Validation
|
|
47
|
+
if (!suffixConfigs || typeof suffixConfigs !== 'object') {
|
|
48
|
+
console.error('[FileColorHighlightLayers] Invalid suffixConfigs structure');
|
|
49
|
+
return [];
|
|
50
|
+
}
|
|
51
|
+
// Group files by their extension
|
|
52
|
+
const filesBySuffix = new Map();
|
|
53
|
+
const unmatchedFilesBySuffix = new Map();
|
|
54
|
+
const noExtensionFiles = [];
|
|
55
|
+
files.forEach(file => {
|
|
56
|
+
const filePath = file.path;
|
|
57
|
+
const lastSlash = filePath.lastIndexOf('/');
|
|
58
|
+
const fileName = lastSlash === -1 ? filePath : filePath.substring(lastSlash + 1);
|
|
59
|
+
const lastDot = fileName.lastIndexOf('.');
|
|
60
|
+
// Check for exact filename match first (e.g., LICENSE, Makefile)
|
|
61
|
+
if (suffixConfigs[fileName]) {
|
|
62
|
+
if (!filesBySuffix.has(fileName)) {
|
|
63
|
+
filesBySuffix.set(fileName, []);
|
|
64
|
+
}
|
|
65
|
+
const fileNameFiles = filesBySuffix.get(fileName);
|
|
66
|
+
if (fileNameFiles) {
|
|
67
|
+
fileNameFiles.push(filePath);
|
|
68
|
+
}
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
if (lastDot === -1 || lastDot === fileName.length - 1) {
|
|
72
|
+
// No extension or ends with dot
|
|
73
|
+
if (includeUnmatched) {
|
|
74
|
+
noExtensionFiles.push(filePath);
|
|
75
|
+
}
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
const extension = fileName.substring(lastDot).toLowerCase();
|
|
79
|
+
if (suffixConfigs[extension]) {
|
|
80
|
+
if (!filesBySuffix.has(extension)) {
|
|
81
|
+
filesBySuffix.set(extension, []);
|
|
82
|
+
}
|
|
83
|
+
const extFiles = filesBySuffix.get(extension);
|
|
84
|
+
if (extFiles) {
|
|
85
|
+
extFiles.push(filePath);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
else if (includeUnmatched) {
|
|
89
|
+
// Group unmatched files by their extension for individual legend entries
|
|
90
|
+
if (!unmatchedFilesBySuffix.has(extension)) {
|
|
91
|
+
unmatchedFilesBySuffix.set(extension, []);
|
|
92
|
+
}
|
|
93
|
+
const unmatchedExtFiles = unmatchedFilesBySuffix.get(extension);
|
|
94
|
+
if (unmatchedExtFiles) {
|
|
95
|
+
unmatchedExtFiles.push(filePath);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
// Create highlight layers
|
|
100
|
+
const layers = [];
|
|
101
|
+
// Sort by file count (more files first) for consistent ordering
|
|
102
|
+
const sortedSuffixes = Array.from(filesBySuffix.entries()).sort(([, filesA], [, filesB]) => filesB.length - filesA.length);
|
|
103
|
+
// Create layers for matched files
|
|
104
|
+
let basePriority = 1;
|
|
105
|
+
sortedSuffixes.forEach(([suffix, files]) => {
|
|
106
|
+
const suffixConfig = suffixConfigs[suffix];
|
|
107
|
+
// Remove leading dot for extensions, use as-is for exact filenames
|
|
108
|
+
const extensionName = suffix.startsWith('.') ? suffix.substring(1) : suffix;
|
|
109
|
+
// Create primary layer
|
|
110
|
+
const primaryLayer = {
|
|
111
|
+
id: `ext-${extensionName}-primary`,
|
|
112
|
+
name: suffixConfig.displayName || extensionName.toUpperCase(),
|
|
113
|
+
color: suffixConfig.primary.color,
|
|
114
|
+
enabled: true,
|
|
115
|
+
opacity: suffixConfig.primary.opacity ?? 1.0,
|
|
116
|
+
priority: suffixConfig.primary.priority ?? basePriority,
|
|
117
|
+
items: files.map((path) => ({
|
|
118
|
+
path,
|
|
119
|
+
type: 'file',
|
|
120
|
+
renderStrategy: suffixConfig.primary.renderStrategy,
|
|
121
|
+
...(suffixConfig.primary.coverOptions && {
|
|
122
|
+
coverOptions: suffixConfig.primary.coverOptions,
|
|
123
|
+
}),
|
|
124
|
+
...(suffixConfig.primary.customRender && {
|
|
125
|
+
customRender: suffixConfig.primary.customRender,
|
|
126
|
+
}),
|
|
127
|
+
})),
|
|
128
|
+
};
|
|
129
|
+
if (suffixConfig.primary.borderWidth) {
|
|
130
|
+
primaryLayer.borderWidth = suffixConfig.primary.borderWidth;
|
|
131
|
+
}
|
|
132
|
+
layers.push(primaryLayer);
|
|
133
|
+
// Create secondary layer if configured
|
|
134
|
+
if (suffixConfig.secondary) {
|
|
135
|
+
const secondary = suffixConfig.secondary;
|
|
136
|
+
const secondaryLayer = {
|
|
137
|
+
id: `ext-${extensionName}-secondary`,
|
|
138
|
+
name: `${suffixConfig.displayName || extensionName.toUpperCase()} Secondary`,
|
|
139
|
+
color: secondary.color,
|
|
140
|
+
enabled: true,
|
|
141
|
+
opacity: secondary.opacity ?? 1.0,
|
|
142
|
+
priority: secondary.priority ?? basePriority + 100, // Higher priority by default
|
|
143
|
+
items: files.map((path) => ({
|
|
144
|
+
path,
|
|
145
|
+
type: 'file',
|
|
146
|
+
renderStrategy: secondary.renderStrategy,
|
|
147
|
+
...(secondary.coverOptions && { coverOptions: secondary.coverOptions }),
|
|
148
|
+
...(secondary.customRender && { customRender: secondary.customRender }),
|
|
149
|
+
})),
|
|
150
|
+
};
|
|
151
|
+
if (secondary.borderWidth) {
|
|
152
|
+
secondaryLayer.borderWidth = secondary.borderWidth;
|
|
153
|
+
}
|
|
154
|
+
layers.push(secondaryLayer);
|
|
155
|
+
}
|
|
156
|
+
basePriority += 2; // Leave room for primary + secondary layers
|
|
157
|
+
});
|
|
158
|
+
// Add layers for unmatched file extensions (each extension gets its own legend entry)
|
|
159
|
+
if (includeUnmatched && defaultFileConfig) {
|
|
160
|
+
// Sort unmatched extensions by file count
|
|
161
|
+
const sortedUnmatchedSuffixes = Array.from(unmatchedFilesBySuffix.entries()).sort(([, filesA], [, filesB]) => filesB.length - filesA.length);
|
|
162
|
+
sortedUnmatchedSuffixes.forEach(([suffix, files]) => {
|
|
163
|
+
const extensionName = suffix.startsWith('.') ? suffix.substring(1) : suffix;
|
|
164
|
+
const unmatchedLayer = {
|
|
165
|
+
id: `ext-${extensionName}-primary`,
|
|
166
|
+
name: extensionName.toUpperCase(),
|
|
167
|
+
color: defaultFileConfig.primary.color,
|
|
168
|
+
enabled: true,
|
|
169
|
+
opacity: defaultFileConfig.primary.opacity ?? 1.0,
|
|
170
|
+
priority: defaultFileConfig.primary.priority ?? basePriority,
|
|
171
|
+
items: files.map((path) => ({
|
|
172
|
+
path,
|
|
173
|
+
type: 'file',
|
|
174
|
+
renderStrategy: defaultFileConfig.primary.renderStrategy,
|
|
175
|
+
...(defaultFileConfig.primary.coverOptions && {
|
|
176
|
+
coverOptions: defaultFileConfig.primary.coverOptions,
|
|
177
|
+
}),
|
|
178
|
+
...(defaultFileConfig.primary.customRender && {
|
|
179
|
+
customRender: defaultFileConfig.primary.customRender,
|
|
180
|
+
}),
|
|
181
|
+
})),
|
|
182
|
+
};
|
|
183
|
+
if (defaultFileConfig.primary.borderWidth) {
|
|
184
|
+
unmatchedLayer.borderWidth = defaultFileConfig.primary.borderWidth;
|
|
185
|
+
}
|
|
186
|
+
layers.push(unmatchedLayer);
|
|
187
|
+
// Add secondary layer if configured
|
|
188
|
+
if (defaultFileConfig.secondary) {
|
|
189
|
+
const secondary = defaultFileConfig.secondary;
|
|
190
|
+
const unmatchedSecondaryLayer = {
|
|
191
|
+
id: `ext-${extensionName}-secondary`,
|
|
192
|
+
name: `${extensionName.toUpperCase()} Secondary`,
|
|
193
|
+
color: secondary.color,
|
|
194
|
+
enabled: true,
|
|
195
|
+
opacity: secondary.opacity ?? 1.0,
|
|
196
|
+
priority: secondary.priority ?? basePriority + 100,
|
|
197
|
+
items: files.map((path) => ({
|
|
198
|
+
path,
|
|
199
|
+
type: 'file',
|
|
200
|
+
renderStrategy: secondary.renderStrategy,
|
|
201
|
+
...(secondary.coverOptions && { coverOptions: secondary.coverOptions }),
|
|
202
|
+
...(secondary.customRender && { customRender: secondary.customRender }),
|
|
203
|
+
})),
|
|
204
|
+
};
|
|
205
|
+
if (secondary.borderWidth) {
|
|
206
|
+
unmatchedSecondaryLayer.borderWidth = secondary.borderWidth;
|
|
207
|
+
}
|
|
208
|
+
layers.push(unmatchedSecondaryLayer);
|
|
209
|
+
}
|
|
210
|
+
basePriority += 2;
|
|
211
|
+
});
|
|
212
|
+
// Add layer for files with no extension
|
|
213
|
+
if (noExtensionFiles.length > 0) {
|
|
214
|
+
const noExtLayer = {
|
|
215
|
+
id: 'other-files-primary',
|
|
216
|
+
name: 'OTHER',
|
|
217
|
+
color: defaultFileConfig.primary.color,
|
|
218
|
+
enabled: true,
|
|
219
|
+
opacity: defaultFileConfig.primary.opacity ?? 1.0,
|
|
220
|
+
priority: defaultFileConfig.primary.priority ?? basePriority,
|
|
221
|
+
items: noExtensionFiles.map((path) => ({
|
|
222
|
+
path,
|
|
223
|
+
type: 'file',
|
|
224
|
+
renderStrategy: defaultFileConfig.primary.renderStrategy,
|
|
225
|
+
...(defaultFileConfig.primary.coverOptions && {
|
|
226
|
+
coverOptions: defaultFileConfig.primary.coverOptions,
|
|
227
|
+
}),
|
|
228
|
+
...(defaultFileConfig.primary.customRender && {
|
|
229
|
+
customRender: defaultFileConfig.primary.customRender,
|
|
230
|
+
}),
|
|
231
|
+
})),
|
|
232
|
+
};
|
|
233
|
+
if (defaultFileConfig.primary.borderWidth) {
|
|
234
|
+
noExtLayer.borderWidth = defaultFileConfig.primary.borderWidth;
|
|
235
|
+
}
|
|
236
|
+
layers.push(noExtLayer);
|
|
237
|
+
if (defaultFileConfig.secondary) {
|
|
238
|
+
const secondary = defaultFileConfig.secondary;
|
|
239
|
+
const noExtSecondaryLayer = {
|
|
240
|
+
id: 'other-files-secondary',
|
|
241
|
+
name: 'OTHER Secondary',
|
|
242
|
+
color: secondary.color,
|
|
243
|
+
enabled: true,
|
|
244
|
+
opacity: secondary.opacity ?? 1.0,
|
|
245
|
+
priority: secondary.priority ?? basePriority + 100,
|
|
246
|
+
items: noExtensionFiles.map((path) => ({
|
|
247
|
+
path,
|
|
248
|
+
type: 'file',
|
|
249
|
+
renderStrategy: secondary.renderStrategy,
|
|
250
|
+
...(secondary.coverOptions && { coverOptions: secondary.coverOptions }),
|
|
251
|
+
...(secondary.customRender && { customRender: secondary.customRender }),
|
|
252
|
+
})),
|
|
253
|
+
};
|
|
254
|
+
if (secondary.borderWidth) {
|
|
255
|
+
noExtSecondaryLayer.borderWidth = secondary.borderWidth;
|
|
256
|
+
}
|
|
257
|
+
layers.push(noExtSecondaryLayer);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
return layers;
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Get the default file color configuration.
|
|
265
|
+
* This returns the configuration loaded from files.json.
|
|
266
|
+
*/
|
|
267
|
+
function getDefaultFileColorConfig() {
|
|
268
|
+
return files_json_1.default;
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Get a simple color mapping from the configuration.
|
|
272
|
+
* This is useful for backwards compatibility or simpler use cases.
|
|
273
|
+
*
|
|
274
|
+
* @param config - Optional configuration. If not provided, uses default config.
|
|
275
|
+
* @returns Record mapping file extensions to hex color strings
|
|
276
|
+
*/
|
|
277
|
+
function getFileColorMapping(config) {
|
|
278
|
+
const colorConfig = config || files_json_1.default;
|
|
279
|
+
return Object.entries(colorConfig.suffixConfigs).reduce((acc, [extension, suffixConfig]) => {
|
|
280
|
+
acc[extension] = suffixConfig.primary.color;
|
|
281
|
+
return acc;
|
|
282
|
+
}, {});
|
|
283
|
+
}
|