@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.
Files changed (51) hide show
  1. package/dist/builder/cityDataUtils.d.ts +15 -0
  2. package/dist/builder/cityDataUtils.d.ts.map +1 -0
  3. package/dist/builder/cityDataUtils.js +348 -0
  4. package/dist/components/ArchitectureMapHighlightLayers.d.ts +63 -0
  5. package/dist/components/ArchitectureMapHighlightLayers.d.ts.map +1 -0
  6. package/dist/components/ArchitectureMapHighlightLayers.js +1040 -0
  7. package/dist/components/CityViewWithReactFlow.d.ts +14 -0
  8. package/dist/components/CityViewWithReactFlow.d.ts.map +1 -0
  9. package/dist/components/CityViewWithReactFlow.js +266 -0
  10. package/dist/config/files.json +996 -0
  11. package/dist/hooks/useCodeCityData.d.ts +21 -0
  12. package/dist/hooks/useCodeCityData.d.ts.map +1 -0
  13. package/dist/hooks/useCodeCityData.js +57 -0
  14. package/dist/index.d.ts +14 -0
  15. package/dist/index.d.ts.map +1 -0
  16. package/dist/index.js +29 -0
  17. package/dist/render/client/drawLayeredBuildings.d.ts +51 -0
  18. package/dist/render/client/drawLayeredBuildings.d.ts.map +1 -0
  19. package/dist/render/client/drawLayeredBuildings.js +650 -0
  20. package/dist/stories/ArchitectureMapGridLayout.stories.d.ts +73 -0
  21. package/dist/stories/ArchitectureMapGridLayout.stories.d.ts.map +1 -0
  22. package/dist/stories/ArchitectureMapGridLayout.stories.js +345 -0
  23. package/dist/stories/ArchitectureMapHighlightLayers.stories.d.ts +78 -0
  24. package/dist/stories/ArchitectureMapHighlightLayers.stories.d.ts.map +1 -0
  25. package/dist/stories/ArchitectureMapHighlightLayers.stories.js +270 -0
  26. package/dist/stories/CityViewWithReactFlow.stories.d.ts +24 -0
  27. package/dist/stories/CityViewWithReactFlow.stories.d.ts.map +1 -0
  28. package/dist/stories/CityViewWithReactFlow.stories.js +778 -0
  29. package/dist/stories/sample-data.d.ts +4 -0
  30. package/dist/stories/sample-data.d.ts.map +1 -0
  31. package/dist/stories/sample-data.js +268 -0
  32. package/dist/types/react-types.d.ts +17 -0
  33. package/dist/types/react-types.d.ts.map +1 -0
  34. package/dist/types/react-types.js +4 -0
  35. package/dist/utils/fileColorHighlightLayers.d.ts +86 -0
  36. package/dist/utils/fileColorHighlightLayers.d.ts.map +1 -0
  37. package/dist/utils/fileColorHighlightLayers.js +283 -0
  38. package/package.json +49 -0
  39. package/src/builder/cityDataUtils.ts +430 -0
  40. package/src/components/ArchitectureMapHighlightLayers.tsx +1518 -0
  41. package/src/components/CityViewWithReactFlow.tsx +365 -0
  42. package/src/config/files.json +996 -0
  43. package/src/hooks/useCodeCityData.ts +82 -0
  44. package/src/index.ts +64 -0
  45. package/src/render/client/drawLayeredBuildings.ts +946 -0
  46. package/src/stories/ArchitectureMapGridLayout.stories.tsx +410 -0
  47. package/src/stories/ArchitectureMapHighlightLayers.stories.tsx +312 -0
  48. package/src/stories/CityViewWithReactFlow.stories.tsx +787 -0
  49. package/src/stories/sample-data.ts +301 -0
  50. package/src/types/react-types.ts +18 -0
  51. package/src/utils/fileColorHighlightLayers.ts +378 -0
@@ -0,0 +1,4 @@
1
+ import { CityData } from '@principal-ai/file-city-builder';
2
+ export declare function createSampleCityData(): CityData;
3
+ export declare function createSmallSampleCityData(): CityData;
4
+ //# sourceMappingURL=sample-data.d.ts.map
@@ -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,4 @@
1
+ "use strict";
2
+ // React-specific types for code city visualization
3
+ // These extend the core types from @principal-ai/file-city-builder
4
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -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
+ }