@principal-ai/file-city-react 0.4.7 → 0.5.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.
@@ -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,QA8J1C"}
@@ -653,10 +653,35 @@ iconMap) {
653
653
  }
654
654
  }
655
655
  // Draw file type icon if enabled and icon map is provided
656
+ // Track icon size for positioning file name below
657
+ let iconBottomY = pos.y;
656
658
  if (showFileTypeIcons && iconMap) {
657
659
  const iconConfig = (0, fileTypeIcons_1.getFileTypeIcon)(building.path, iconMap);
658
660
  if (iconConfig) {
659
- (0, fileTypeIcons_1.drawFileTypeIcon)(ctx, iconConfig, pos.x, pos.y, width, height);
661
+ // For wide buildings (wider than tall), shift icon up to make room for text below
662
+ const isWide = width > height;
663
+ const iconYOffset = isWide ? -height * 0.15 : 0;
664
+ const iconY = pos.y + iconYOffset;
665
+ (0, fileTypeIcons_1.drawFileTypeIcon)(ctx, iconConfig, pos.x, iconY, width, height);
666
+ // Calculate where the icon ends vertically
667
+ const minDimension = Math.min(width, height);
668
+ if (iconConfig.type === 'emoji') {
669
+ const sizeScale = iconConfig.size || 0.75;
670
+ const emojiSize = minDimension * sizeScale;
671
+ iconBottomY = iconY + emojiSize / 2;
672
+ }
673
+ else if (iconConfig.type === 'lucide') {
674
+ const sizeScale = iconConfig.size || 0.5;
675
+ const actualIconSize = minDimension * sizeScale;
676
+ // Account for background circle if present
677
+ if (iconConfig.backgroundColor) {
678
+ const bgRadius = actualIconSize * 0.7;
679
+ iconBottomY = iconY + bgRadius;
680
+ }
681
+ else {
682
+ iconBottomY = iconY + actualIconSize / 2;
683
+ }
684
+ }
660
685
  }
661
686
  }
662
687
  // Draw filename if enabled
@@ -666,18 +691,16 @@ iconMap) {
666
691
  const fontSize = Math.min(30, Math.max(10, Math.floor(Math.min(width, height) * 0.3)));
667
692
  ctx.font = `${fontSize}px -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif`;
668
693
  ctx.textAlign = 'center';
669
- ctx.textBaseline = 'middle';
694
+ ctx.textBaseline = 'top'; // Changed from 'middle' to 'top'
670
695
  const textMetrics = ctx.measureText(fileName);
671
696
  const textWidth = textMetrics.width;
672
697
  if (textWidth < width - 8) {
673
- // Background for contrast
674
- const textPadding = 2;
675
- const textHeight = fontSize * 1.2;
676
- ctx.fillStyle = 'rgba(0, 0, 0, 0.7)';
677
- ctx.fillRect(pos.x - textWidth / 2 - textPadding, pos.y - textHeight / 2, textWidth + textPadding * 2, textHeight);
698
+ // Add spacing between icon and text
699
+ const spacing = 4;
700
+ const textY = iconBottomY + spacing;
678
701
  // Text
679
702
  ctx.fillStyle = 'rgba(255, 255, 255, 0.95)';
680
- ctx.fillText(fileName, pos.x, pos.y);
703
+ ctx.fillText(fileName, pos.x, textY);
681
704
  }
682
705
  ctx.restore();
683
706
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@principal-ai/file-city-react",
3
- "version": "0.4.7",
3
+ "version": "0.5.0",
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.
@@ -930,10 +892,35 @@ export function drawLayeredBuildings(
930
892
  }
931
893
 
932
894
  // Draw file type icon if enabled and icon map is provided
895
+ // Track icon size for positioning file name below
896
+ let iconBottomY = pos.y;
933
897
  if (showFileTypeIcons && iconMap) {
934
898
  const iconConfig = getFileTypeIcon(building.path, iconMap);
935
899
  if (iconConfig) {
936
- drawFileTypeIcon(ctx, iconConfig, pos.x, pos.y, width, height);
900
+ // For wide buildings (wider than tall), shift icon up to make room for text below
901
+ const isWide = width > height;
902
+ const iconYOffset = isWide ? -height * 0.15 : 0;
903
+ const iconY = pos.y + iconYOffset;
904
+
905
+ drawFileTypeIcon(ctx, iconConfig, pos.x, iconY, width, height);
906
+
907
+ // Calculate where the icon ends vertically
908
+ const minDimension = Math.min(width, height);
909
+ if (iconConfig.type === 'emoji') {
910
+ const sizeScale = iconConfig.size || 0.75;
911
+ const emojiSize = minDimension * sizeScale;
912
+ iconBottomY = iconY + emojiSize / 2;
913
+ } else if (iconConfig.type === 'lucide') {
914
+ const sizeScale = iconConfig.size || 0.5;
915
+ const actualIconSize = minDimension * sizeScale;
916
+ // Account for background circle if present
917
+ if (iconConfig.backgroundColor) {
918
+ const bgRadius = actualIconSize * 0.7;
919
+ iconBottomY = iconY + bgRadius;
920
+ } else {
921
+ iconBottomY = iconY + actualIconSize / 2;
922
+ }
923
+ }
937
924
  }
938
925
  }
939
926
 
@@ -946,26 +933,19 @@ export function drawLayeredBuildings(
946
933
  const fontSize = Math.min(30, Math.max(10, Math.floor(Math.min(width, height) * 0.3)));
947
934
  ctx.font = `${fontSize}px -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif`;
948
935
  ctx.textAlign = 'center';
949
- ctx.textBaseline = 'middle';
936
+ ctx.textBaseline = 'top'; // Changed from 'middle' to 'top'
950
937
 
951
938
  const textMetrics = ctx.measureText(fileName);
952
939
  const textWidth = textMetrics.width;
953
940
 
954
941
  if (textWidth < width - 8) {
955
- // Background for contrast
956
- const textPadding = 2;
957
- const textHeight = fontSize * 1.2;
958
- ctx.fillStyle = 'rgba(0, 0, 0, 0.7)';
959
- ctx.fillRect(
960
- pos.x - textWidth / 2 - textPadding,
961
- pos.y - textHeight / 2,
962
- textWidth + textPadding * 2,
963
- textHeight,
964
- );
942
+ // Add spacing between icon and text
943
+ const spacing = 4;
944
+ const textY = iconBottomY + spacing;
965
945
 
966
946
  // Text
967
947
  ctx.fillStyle = 'rgba(255, 255, 255, 0.95)';
968
- ctx.fillText(fileName, pos.x, pos.y);
948
+ ctx.fillText(fileName, pos.x, textY);
969
949
  }
970
950
 
971
951
  ctx.restore();