@principal-ai/file-city-react 0.4.8 → 0.5.1

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.
@@ -1,39 +1,6 @@
1
- import { CityBuilding, CityDistrict } from '@principal-ai/file-city-builder';
1
+ import { CityBuilding, CityDistrict, LayerItem, LayerRenderStrategy, HighlightLayer } from '@principal-ai/file-city-builder';
2
2
  import { FileTypeIconConfig } from '../../utils/fileColorHighlightLayers';
3
- export type LayerRenderStrategy = 'border' | 'fill' | 'glow' | 'pattern' | 'cover' | 'icon' | 'custom';
4
- export interface LayerItem {
5
- path: string;
6
- type: 'file' | 'directory';
7
- renderStrategy?: LayerRenderStrategy;
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
- lucideIcon?: string;
18
- };
19
- customRender?: (ctx: CanvasRenderingContext2D, bounds: {
20
- x: number;
21
- y: number;
22
- width: number;
23
- height: number;
24
- }, scale: number) => void;
25
- }
26
- export interface HighlightLayer {
27
- id: string;
28
- name: string;
29
- enabled: boolean;
30
- color: string;
31
- opacity?: number;
32
- borderWidth?: number;
33
- priority: number;
34
- items: LayerItem[];
35
- dynamic?: boolean;
36
- }
3
+ export type { LayerItem, LayerRenderStrategy, HighlightLayer };
37
4
  /**
38
5
  * LayerIndex provides O(1) path lookups instead of O(n) iteration.
39
6
  * This dramatically improves performance with large numbers of layer items.
@@ -1 +1 @@
1
- {"version":3,"file":"drawLayeredBuildings.d.ts","sourceRoot":"","sources":["../../../src/render/client/drawLayeredBuildings.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"drawLayeredBuildings.d.ts","sourceRoot":"","sources":["../../../src/render/client/drawLayeredBuildings.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,YAAY,EACZ,SAAS,EACT,mBAAmB,EACnB,cAAc,EACf,MAAM,iCAAiC,CAAC;AAEzC,OAAO,EAAE,kBAAkB,EAAE,MAAM,sCAAsC,CAAC;AAI1E,YAAY,EAAE,SAAS,EAAE,mBAAmB,EAAE,cAAc,EAAE,CAAC;AAE/D;;;GAGG;AACH,qBAAa,UAAU;IAErB,OAAO,CAAC,UAAU,CAA6E;IAE/F,OAAO,CAAC,cAAc,CAAuE;IAE7F,OAAO,CAAC,WAAW,CAA6E;gBAEpF,MAAM,EAAE,cAAc,EAAE;IAIpC,OAAO,CAAC,UAAU;IA0BlB;;;;OAIG;IACH,eAAe,CACb,IAAI,EAAE,MAAM,EACZ,SAAS,GAAE,OAAO,GAAG,UAAuB,GAC3C,KAAK,CAAC;QAAE,KAAK,EAAE,cAAc,CAAC;QAAC,IAAI,EAAE,SAAS,CAAA;KAAE,CAAC;CAuCrD;AA8BD,wBAAgB,QAAQ,CACtB,GAAG,EAAE,wBAAwB,EAC7B,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,QAqBjB;AA0VD,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,wBAAwB,EAC7B,SAAS,EAAE,YAAY,EAAE,EACzB,aAAa,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,KAAK;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,EACjE,KAAK,EAAE,MAAM,EAAE,wDAAwD;AACvE,MAAM,EAAE,cAAc,EAAE,EACxB,eAAe,CAAC,EAAE,YAAY,GAAG,IAAI,EACrC,QAAQ,CAAC,EAAE,OAAO,EAClB,qBAAqB,CAAC,EAAE,MAAM,EAC9B,YAAY,CAAC,EAAE;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;CACtB,EACD,eAAe,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE,yDAAyD;AACxF,mBAAmB,GAAE,OAAc,EACnC,YAAY,GAAE,MAAU,EAAE,uDAAuD;AACjF,UAAU,CAAC,EAAE,UAAU,QAgQxB;AAGD,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,wBAAwB,EAC7B,SAAS,EAAE,YAAY,EAAE,EACzB,aAAa,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,KAAK;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,EACjE,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,cAAc,EAAE,EACxB,eAAe,CAAC,EAAE,YAAY,GAAG,IAAI,EACrC,oBAAoB,CAAC,EAAE,MAAM,EAC7B,aAAa,CAAC,EAAE,OAAO,EACvB,gBAAgB,CAAC,EAAE,MAAM,EACzB,qBAAqB,CAAC,EAAE,OAAO,EAC/B,iBAAiB,CAAC,EAAE,OAAO,EAC3B,YAAY,GAAE,MAAU,EAAE,2DAA2D;AACrF,UAAU,CAAC,EAAE,UAAU,EAAE,2CAA2C;AACpE,OAAO,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,kBAAkB,CAAC,QA0K1C"}
@@ -655,12 +655,16 @@ iconMap) {
655
655
  // Draw file type icon if enabled and icon map is provided
656
656
  // Track icon size for positioning file name below
657
657
  let iconBottomY = pos.y;
658
+ let hasIcon = false;
658
659
  if (showFileTypeIcons && iconMap) {
659
660
  const iconConfig = (0, fileTypeIcons_1.getFileTypeIcon)(building.path, iconMap);
660
661
  if (iconConfig) {
662
+ hasIcon = true;
661
663
  // For wide buildings (wider than tall), shift icon up to make room for text below
664
+ // Only shift if text will actually be rendered
665
+ const willShowText = showFileNames && width > 100 && height > 30;
662
666
  const isWide = width > height;
663
- const iconYOffset = isWide ? -height * 0.15 : 0;
667
+ const iconYOffset = (willShowText && isWide) ? -height * 0.15 : 0;
664
668
  const iconY = pos.y + iconYOffset;
665
669
  (0, fileTypeIcons_1.drawFileTypeIcon)(ctx, iconConfig, pos.x, iconY, width, height);
666
670
  // Calculate where the icon ends vertically
@@ -691,13 +695,21 @@ iconMap) {
691
695
  const fontSize = Math.min(30, Math.max(10, Math.floor(Math.min(width, height) * 0.3)));
692
696
  ctx.font = `${fontSize}px -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif`;
693
697
  ctx.textAlign = 'center';
694
- ctx.textBaseline = 'top'; // Changed from 'middle' to 'top'
695
698
  const textMetrics = ctx.measureText(fileName);
696
699
  const textWidth = textMetrics.width;
697
700
  if (textWidth < width - 8) {
698
- // Add spacing between icon and text
699
- const spacing = 4;
700
- const textY = iconBottomY + spacing;
701
+ let textY;
702
+ if (hasIcon) {
703
+ // Position text below icon
704
+ ctx.textBaseline = 'top';
705
+ const spacing = 4;
706
+ textY = iconBottomY + spacing;
707
+ }
708
+ else {
709
+ // Center text vertically when no icon
710
+ ctx.textBaseline = 'middle';
711
+ textY = pos.y;
712
+ }
701
713
  // Text
702
714
  ctx.fillStyle = 'rgba(255, 255, 255, 0.95)';
703
715
  ctx.fillText(fileName, pos.x, textY);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@principal-ai/file-city-react",
3
- "version": "0.4.8",
3
+ "version": "0.5.1",
4
4
  "description": "React components for File City visualization",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -14,7 +14,7 @@
14
14
  "dependencies": {
15
15
  "@principal-ade/industry-theme": "^0.1.3",
16
16
  "@principal-ai/alexandria-core-library": "^0.1.36",
17
- "@principal-ai/file-city-builder": "^0.3.3",
17
+ "@principal-ai/file-city-builder": "^0.4.0",
18
18
  "lucide-react": "^0.563.0",
19
19
  "reactflow": "^11.11.4"
20
20
  },
@@ -1,54 +1,16 @@
1
- import { CityBuilding, CityDistrict } from '@principal-ai/file-city-builder';
1
+ import {
2
+ CityBuilding,
3
+ CityDistrict,
4
+ LayerItem,
5
+ LayerRenderStrategy,
6
+ HighlightLayer,
7
+ } from '@principal-ai/file-city-builder';
2
8
  import { getLucideIconImage } from '../../utils/lucideIconConverter';
3
9
  import { FileTypeIconConfig } from '../../utils/fileColorHighlightLayers';
4
10
  import { getFileTypeIcon, drawFileTypeIcon } from '../../utils/fileTypeIcons';
5
11
 
6
- // Layer types and interfaces
7
- export type LayerRenderStrategy =
8
- | 'border'
9
- | 'fill'
10
- | 'glow'
11
- | 'pattern'
12
- | 'cover'
13
- | 'icon'
14
- | 'custom';
15
-
16
- export interface LayerItem {
17
- path: string;
18
- type: 'file' | 'directory';
19
- renderStrategy?: LayerRenderStrategy;
20
- // Cover-specific options
21
- coverOptions?: {
22
- opacity?: number;
23
- image?: string;
24
- text?: string;
25
- textSize?: number;
26
- backgroundColor?: string;
27
- borderRadius?: number;
28
- icon?: string;
29
- iconSize?: number;
30
- lucideIcon?: string;
31
- };
32
- // Custom render function
33
- customRender?: (
34
- ctx: CanvasRenderingContext2D,
35
- bounds: { x: number; y: number; width: number; height: number },
36
- scale: number,
37
- ) => void;
38
- }
39
-
40
- export interface HighlightLayer {
41
- id: string;
42
- name: string;
43
- enabled: boolean;
44
- color: string;
45
- opacity?: number;
46
- borderWidth?: number;
47
- priority: number; // Higher priority layers render on top
48
- items: LayerItem[];
49
- // Performance optimization - mark frequently changing layers as dynamic
50
- dynamic?: boolean; // If true, this layer changes frequently (e.g., hover effects)
51
- }
12
+ // Re-export for backward compatibility
13
+ export type { LayerItem, LayerRenderStrategy, HighlightLayer };
52
14
 
53
15
  /**
54
16
  * LayerIndex provides O(1) path lookups instead of O(n) iteration.
@@ -932,12 +894,16 @@ export function drawLayeredBuildings(
932
894
  // Draw file type icon if enabled and icon map is provided
933
895
  // Track icon size for positioning file name below
934
896
  let iconBottomY = pos.y;
897
+ let hasIcon = false;
935
898
  if (showFileTypeIcons && iconMap) {
936
899
  const iconConfig = getFileTypeIcon(building.path, iconMap);
937
900
  if (iconConfig) {
901
+ hasIcon = true;
938
902
  // For wide buildings (wider than tall), shift icon up to make room for text below
903
+ // Only shift if text will actually be rendered
904
+ const willShowText = showFileNames && width > 100 && height > 30;
939
905
  const isWide = width > height;
940
- const iconYOffset = isWide ? -height * 0.15 : 0;
906
+ const iconYOffset = (willShowText && isWide) ? -height * 0.15 : 0;
941
907
  const iconY = pos.y + iconYOffset;
942
908
 
943
909
  drawFileTypeIcon(ctx, iconConfig, pos.x, iconY, width, height);
@@ -971,15 +937,23 @@ export function drawLayeredBuildings(
971
937
  const fontSize = Math.min(30, Math.max(10, Math.floor(Math.min(width, height) * 0.3)));
972
938
  ctx.font = `${fontSize}px -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif`;
973
939
  ctx.textAlign = 'center';
974
- ctx.textBaseline = 'top'; // Changed from 'middle' to 'top'
975
940
 
976
941
  const textMetrics = ctx.measureText(fileName);
977
942
  const textWidth = textMetrics.width;
978
943
 
979
944
  if (textWidth < width - 8) {
980
- // Add spacing between icon and text
981
- const spacing = 4;
982
- const textY = iconBottomY + spacing;
945
+ let textY: number;
946
+
947
+ if (hasIcon) {
948
+ // Position text below icon
949
+ ctx.textBaseline = 'top';
950
+ const spacing = 4;
951
+ textY = iconBottomY + spacing;
952
+ } else {
953
+ // Center text vertically when no icon
954
+ ctx.textBaseline = 'middle';
955
+ textY = pos.y;
956
+ }
983
957
 
984
958
  // Text
985
959
  ctx.fillStyle = 'rgba(255, 255, 255, 0.95)';
@@ -11,7 +11,7 @@ const meta = {
11
11
  layout: 'fullscreen',
12
12
  },
13
13
  decorators: [
14
- Story => (
14
+ (Story: React.ComponentType) => (
15
15
  <div style={{ width: '100vw', height: '100vh', backgroundColor: '#1a1a1a' }}>
16
16
  <Story />
17
17
  </div>
@@ -199,7 +199,7 @@ function createAllFileTypesCityData() {
199
199
  };
200
200
  });
201
201
 
202
- const fileTree = buildFileSystemTreeFromFileInfoList(fileInfos);
202
+ const fileTree = buildFileSystemTreeFromFileInfoList(fileInfos, 'all-file-types');
203
203
  const builder = new CodeCityBuilderWithGrid();
204
204
  return builder.buildCityFromFileSystem(fileTree, '', {
205
205
  paddingTop: 2,
@@ -230,6 +230,8 @@ export const AllFileTypesWithColors: Story = {
230
230
  enableZoom={true}
231
231
  buildingBorderRadius={2}
232
232
  districtBorderRadius={4}
233
+ showFileTypeIcons={true}
234
+ showFileNames={true}
233
235
  />
234
236
  <div
235
237
  style={{
@@ -288,7 +290,7 @@ export const TestFilesWithIcons: Story = {
288
290
  };
289
291
  });
290
292
 
291
- const fileTree = buildFileSystemTreeFromFileInfoList(fileInfos);
293
+ const fileTree = buildFileSystemTreeFromFileInfoList(fileInfos, 'test-files');
292
294
  const builder = new CodeCityBuilderWithGrid();
293
295
  const cityData = builder.buildCityFromFileSystem(fileTree, '', {
294
296
  paddingTop: 2,
@@ -50,7 +50,7 @@ const meta = {
50
50
  layout: 'fullscreen',
51
51
  },
52
52
  decorators: [
53
- Story => (
53
+ (Story: React.ComponentType) => (
54
54
  <div style={{ width: '100vw', height: '100vh' }}>
55
55
  <Story />
56
56
  </div>
@@ -12,7 +12,7 @@ const meta = {
12
12
  layout: 'fullscreen',
13
13
  },
14
14
  decorators: [
15
- Story => (
15
+ (Story: React.ComponentType) => (
16
16
  <div style={{ width: '100vw', height: '100vh', backgroundColor: '#1a1a1a' }}>
17
17
  <Story />
18
18
  </div>
@@ -200,7 +200,6 @@ export const WithAbstractionLayer: Story = {
200
200
  color: '#1e40af',
201
201
  priority: 0,
202
202
  items: [],
203
- // @ts-expect-error - abstraction layer specific properties
204
203
  abstractionLayer: true,
205
204
  abstractionConfig: {
206
205
  maxZoomLevel: 2.0,
@@ -208,7 +207,7 @@ export const WithAbstractionLayer: Story = {
208
207
  backgroundColor: '#1e40af',
209
208
  allowRootAbstraction: false,
210
209
  },
211
- },
210
+ } as HighlightLayer,
212
211
  ],
213
212
  fullSize: true,
214
213
  enableZoom: true,
@@ -11,7 +11,7 @@ const meta = {
11
11
  layout: 'fullscreen',
12
12
  },
13
13
  decorators: [
14
- Story => (
14
+ (Story: React.ComponentType) => (
15
15
  <div style={{ height: '100vh', width: '100vw' }}>
16
16
  <Story />
17
17
  </div>
@@ -353,7 +353,7 @@ const meta = {
353
353
  layout: 'fullscreen',
354
354
  },
355
355
  decorators: [
356
- Story => (
356
+ (Story: React.ComponentType) => (
357
357
  <div style={{ width: '100vw', height: '100vh' }}>
358
358
  <Story />
359
359
  </div>