@ifc-lite/viewer 1.1.7 → 1.6.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 (164) hide show
  1. package/LICENSE +373 -0
  2. package/dist/apple-touch-icon.png +0 -0
  3. package/dist/assets/Arrow.dom-BjDQoB2M.js +20 -0
  4. package/dist/assets/arrow2-bb-jcVEo.js +2 -0
  5. package/dist/assets/arrow2_bg-4Y7xYo54.wasm +0 -0
  6. package/dist/assets/arrow2_bg-BlXl-cSQ.js +1 -0
  7. package/dist/assets/arrow2_bg-BoXCojjR.wasm +0 -0
  8. package/dist/assets/desktop-cache-oPzaWXYE.js +1 -0
  9. package/dist/assets/event-DIOks52T.js +1 -0
  10. package/dist/assets/ifc-cache-BAN4vcd4.js +1 -0
  11. package/dist/assets/ifc-lite_bg-C6kblxf9.wasm +0 -0
  12. package/dist/assets/index-YBtrHPu3.js +65252 -0
  13. package/dist/assets/index-v3mcCUPN.css +1 -0
  14. package/dist/assets/native-bridge-CULtTDX3.js +111 -0
  15. package/dist/assets/wasm-bridge-CjL-lSak.js +1 -0
  16. package/dist/favicon-16x16-cropped.png +0 -0
  17. package/dist/favicon-16x16.png +0 -0
  18. package/dist/favicon-192x192-cropped.png +0 -0
  19. package/dist/favicon-192x192.png +0 -0
  20. package/dist/favicon-32x32-cropped.png +0 -0
  21. package/dist/favicon-32x32.png +0 -0
  22. package/dist/favicon-48x48-cropped.png +0 -0
  23. package/dist/favicon-48x48.png +0 -0
  24. package/dist/favicon-512x512-cropped.png +0 -0
  25. package/dist/favicon-512x512.png +0 -0
  26. package/dist/favicon-64x64-cropped.png +0 -0
  27. package/dist/favicon-64x64.png +0 -0
  28. package/dist/favicon-96x96-cropped.png +0 -0
  29. package/dist/favicon-96x96.png +0 -0
  30. package/dist/favicon-square-512.png +0 -0
  31. package/dist/favicon.ico +0 -0
  32. package/dist/favicon.png +0 -0
  33. package/dist/favicon.svg +3 -0
  34. package/dist/index.html +44 -0
  35. package/dist/logo.png +0 -0
  36. package/dist/manifest.json +48 -0
  37. package/index.html +33 -2
  38. package/package.json +34 -17
  39. package/public/apple-touch-icon.png +0 -0
  40. package/public/favicon-16x16-cropped.png +0 -0
  41. package/public/favicon-16x16.png +0 -0
  42. package/public/favicon-192x192-cropped.png +0 -0
  43. package/public/favicon-192x192.png +0 -0
  44. package/public/favicon-32x32-cropped.png +0 -0
  45. package/public/favicon-32x32.png +0 -0
  46. package/public/favicon-48x48-cropped.png +0 -0
  47. package/public/favicon-48x48.png +0 -0
  48. package/public/favicon-512x512-cropped.png +0 -0
  49. package/public/favicon-512x512.png +0 -0
  50. package/public/favicon-64x64-cropped.png +0 -0
  51. package/public/favicon-64x64.png +0 -0
  52. package/public/favicon-96x96-cropped.png +0 -0
  53. package/public/favicon-96x96.png +0 -0
  54. package/public/favicon-square-512.png +0 -0
  55. package/public/favicon.ico +0 -0
  56. package/public/favicon.png +0 -0
  57. package/public/favicon.svg +3 -0
  58. package/public/logo.png +0 -0
  59. package/public/manifest.json +48 -0
  60. package/src/App.tsx +2 -0
  61. package/src/components/ui/alert.tsx +62 -0
  62. package/src/components/ui/badge.tsx +39 -0
  63. package/src/components/ui/dialog.tsx +120 -0
  64. package/src/components/ui/label.tsx +27 -0
  65. package/src/components/ui/select.tsx +151 -0
  66. package/src/components/ui/switch.tsx +30 -0
  67. package/src/components/ui/table.tsx +120 -0
  68. package/src/components/ui/tabs.tsx +1 -1
  69. package/src/components/viewer/BCFPanel.tsx +1164 -0
  70. package/src/components/viewer/BulkPropertyEditor.tsx +875 -0
  71. package/src/components/viewer/DataConnector.tsx +840 -0
  72. package/src/components/viewer/DrawingSettingsPanel.tsx +536 -0
  73. package/src/components/viewer/EntityContextMenu.tsx +45 -17
  74. package/src/components/viewer/ExportChangesButton.tsx +195 -0
  75. package/src/components/viewer/ExportDialog.tsx +402 -0
  76. package/src/components/viewer/HierarchyPanel.tsx +1132 -218
  77. package/src/components/viewer/IDSPanel.tsx +661 -0
  78. package/src/components/viewer/KeyboardShortcutsDialog.tsx +245 -39
  79. package/src/components/viewer/MainToolbar.tsx +418 -94
  80. package/src/components/viewer/PropertiesPanel.tsx +1355 -91
  81. package/src/components/viewer/PropertyEditor.tsx +611 -0
  82. package/src/components/viewer/Section2DPanel.tsx +3313 -0
  83. package/src/components/viewer/SheetSetupPanel.tsx +502 -0
  84. package/src/components/viewer/StatusBar.tsx +27 -16
  85. package/src/components/viewer/TitleBlockEditor.tsx +437 -0
  86. package/src/components/viewer/ToolOverlays.tsx +935 -127
  87. package/src/components/viewer/ViewerLayout.tsx +40 -11
  88. package/src/components/viewer/Viewport.tsx +1276 -336
  89. package/src/components/viewer/ViewportContainer.tsx +554 -18
  90. package/src/components/viewer/ViewportOverlays.tsx +24 -7
  91. package/src/hooks/useBCF.ts +504 -0
  92. package/src/hooks/useIDS.ts +1065 -0
  93. package/src/hooks/useIfc.ts +1534 -205
  94. package/src/hooks/useIfcCache.ts +279 -0
  95. package/src/hooks/useKeyboardShortcuts.ts +50 -8
  96. package/src/hooks/useModelSelection.ts +61 -0
  97. package/src/hooks/useViewerSelectors.ts +218 -0
  98. package/src/hooks/useWebGPU.ts +80 -0
  99. package/src/index.css +265 -27
  100. package/src/lib/platform.ts +23 -0
  101. package/src/services/cacheService.ts +142 -0
  102. package/src/services/desktop-cache.ts +143 -0
  103. package/src/services/fs-cache.ts +212 -0
  104. package/src/services/ifc-cache.ts +14 -6
  105. package/src/store/constants.ts +85 -0
  106. package/src/store/index.ts +214 -0
  107. package/src/store/slices/bcfSlice.ts +372 -0
  108. package/src/store/slices/cameraSlice.ts +63 -0
  109. package/src/store/slices/dataSlice.test.ts +226 -0
  110. package/src/store/slices/dataSlice.ts +112 -0
  111. package/src/store/slices/drawing2DSlice.ts +340 -0
  112. package/src/store/slices/hoverSlice.ts +40 -0
  113. package/src/store/slices/idsSlice.ts +310 -0
  114. package/src/store/slices/loadingSlice.ts +33 -0
  115. package/src/store/slices/measurementSlice.test.ts +217 -0
  116. package/src/store/slices/measurementSlice.ts +293 -0
  117. package/src/store/slices/modelSlice.test.ts +271 -0
  118. package/src/store/slices/modelSlice.ts +211 -0
  119. package/src/store/slices/mutationSlice.ts +502 -0
  120. package/src/store/slices/sectionSlice.test.ts +125 -0
  121. package/src/store/slices/sectionSlice.ts +58 -0
  122. package/src/store/slices/selectionSlice.test.ts +286 -0
  123. package/src/store/slices/selectionSlice.ts +263 -0
  124. package/src/store/slices/sheetSlice.ts +565 -0
  125. package/src/store/slices/uiSlice.ts +58 -0
  126. package/src/store/slices/visibilitySlice.test.ts +304 -0
  127. package/src/store/slices/visibilitySlice.ts +277 -0
  128. package/src/store/types.test.ts +135 -0
  129. package/src/store/types.ts +248 -0
  130. package/src/store.ts +40 -515
  131. package/src/utils/ifcConfig.ts +82 -0
  132. package/src/utils/localParsingUtils.ts +287 -0
  133. package/src/utils/serverDataModel.ts +783 -0
  134. package/src/utils/spatialHierarchy.ts +283 -0
  135. package/src/utils/viewportUtils.ts +334 -0
  136. package/src/vite-env.d.ts +23 -0
  137. package/src/webgpu-types.d.ts +128 -0
  138. package/src-tauri/Cargo.toml +29 -0
  139. package/src-tauri/build.rs +7 -0
  140. package/src-tauri/capabilities/default.json +18 -0
  141. package/src-tauri/icons/128x128.png +0 -0
  142. package/src-tauri/icons/128x128@2x.png +0 -0
  143. package/src-tauri/icons/32x32.png +0 -0
  144. package/src-tauri/icons/Square107x107Logo.png +0 -0
  145. package/src-tauri/icons/Square142x142Logo.png +0 -0
  146. package/src-tauri/icons/Square150x150Logo.png +0 -0
  147. package/src-tauri/icons/Square284x284Logo.png +0 -0
  148. package/src-tauri/icons/Square30x30Logo.png +0 -0
  149. package/src-tauri/icons/Square310x310Logo.png +0 -0
  150. package/src-tauri/icons/Square44x44Logo.png +0 -0
  151. package/src-tauri/icons/Square71x71Logo.png +0 -0
  152. package/src-tauri/icons/Square89x89Logo.png +0 -0
  153. package/src-tauri/icons/StoreLogo.png +0 -0
  154. package/src-tauri/icons/icon.icns +0 -0
  155. package/src-tauri/icons/icon.ico +0 -0
  156. package/src-tauri/icons/icon.png +0 -0
  157. package/src-tauri/src/lib.rs +21 -0
  158. package/src-tauri/src/main.rs +10 -0
  159. package/src-tauri/tauri.conf.json +39 -0
  160. package/vite.config.ts +174 -26
  161. package/public/ifc-lite_bg.wasm +0 -0
  162. package/public/web-ifc.wasm +0 -0
  163. package/src/components/Viewport.tsx +0 -723
  164. package/src/components/viewer/BoxSelectionOverlay.tsx +0 -53
@@ -2,17 +2,220 @@
2
2
  * License, v. 2.0. If a copy of the MPL was not distributed with this
3
3
  * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
4
 
5
- import { useState, useEffect, useCallback } from 'react';
6
- import { X } from 'lucide-react';
5
+ import { useState, useEffect, useCallback, useMemo } from 'react';
6
+ import { X, Info, Keyboard, Github, ExternalLink, Sparkles, ChevronDown, Zap, Wrench, Plus } from 'lucide-react';
7
7
  import { Button } from '@/components/ui/button';
8
+ import { Tabs, TabsList, TabsTrigger, TabsContent } from '@/components/ui/tabs';
8
9
  import { KEYBOARD_SHORTCUTS } from '@/hooks/useKeyboardShortcuts';
9
10
 
10
- interface KeyboardShortcutsDialogProps {
11
+ const GITHUB_URL = 'https://github.com/louistrue/ifc-lite';
12
+ const INITIAL_RELEASE_COUNT = 5;
13
+
14
+ interface InfoDialogProps {
11
15
  open: boolean;
12
16
  onClose: () => void;
13
17
  }
14
18
 
15
- export function KeyboardShortcutsDialog({ open, onClose }: KeyboardShortcutsDialogProps) {
19
+ function formatBuildDate(iso: string): string {
20
+ try {
21
+ return new Date(iso).toLocaleDateString('en-US', {
22
+ year: 'numeric',
23
+ month: 'short',
24
+ day: 'numeric',
25
+ });
26
+ } catch {
27
+ return iso;
28
+ }
29
+ }
30
+
31
+ const TYPE_CONFIG = {
32
+ feature: { icon: Plus, className: 'text-emerald-500' },
33
+ fix: { icon: Wrench, className: 'text-amber-500' },
34
+ perf: { icon: Zap, className: 'text-blue-500' },
35
+ } as const;
36
+
37
+ function AboutTab() {
38
+ return (
39
+ <div className="space-y-4">
40
+ {/* Header */}
41
+ <div className="text-center pb-4 border-b">
42
+ <h3 className="text-xl font-bold">ifc-lite</h3>
43
+ <p className="text-sm text-muted-foreground mt-1">
44
+ Version {__APP_VERSION__}
45
+ </p>
46
+ <p className="text-xs text-muted-foreground mt-0.5">
47
+ Built {formatBuildDate(__BUILD_DATE__)}
48
+ </p>
49
+ </div>
50
+
51
+ {/* Description */}
52
+ <div className="space-y-2">
53
+ <p className="text-sm">
54
+ A high-performance IFC viewer for BIM models, built with WebGPU.
55
+ </p>
56
+ </div>
57
+
58
+ {/* Features */}
59
+ <div className="space-y-2">
60
+ <h4 className="text-sm font-medium">Features</h4>
61
+ <ul className="text-sm text-muted-foreground space-y-1 list-disc list-inside">
62
+ <li>WebGPU-accelerated 3D rendering</li>
63
+ <li>IFC4 and IFC5/IFCX format support</li>
64
+ <li>Multi-model federation</li>
65
+ <li>Spatial hierarchy navigation</li>
66
+ <li>Section planes and measurements</li>
67
+ <li>Property inspection</li>
68
+ </ul>
69
+ </div>
70
+
71
+ {/* Links */}
72
+ <div className="pt-4 border-t space-y-2">
73
+ <a
74
+ href={GITHUB_URL}
75
+ target="_blank"
76
+ rel="noopener noreferrer"
77
+ className="flex items-center gap-2 text-sm text-muted-foreground hover:text-foreground transition-colors"
78
+ >
79
+ <Github className="h-4 w-4" />
80
+ <span>View on GitHub</span>
81
+ <ExternalLink className="h-3 w-3" />
82
+ </a>
83
+ <a
84
+ href={`${GITHUB_URL}/issues`}
85
+ target="_blank"
86
+ rel="noopener noreferrer"
87
+ className="flex items-center gap-2 text-sm text-muted-foreground hover:text-foreground transition-colors"
88
+ >
89
+ <span className="w-4 text-center">🐛</span>
90
+ <span>Report an issue</span>
91
+ <ExternalLink className="h-3 w-3" />
92
+ </a>
93
+ </div>
94
+
95
+ {/* License */}
96
+ <div className="pt-4 border-t">
97
+ <p className="text-xs text-muted-foreground text-center">
98
+ Licensed under Mozilla Public License 2.0
99
+ </p>
100
+ </div>
101
+ </div>
102
+ );
103
+ }
104
+
105
+ function WhatsNewTab() {
106
+ const [showAll, setShowAll] = useState(false);
107
+ const releases = __RELEASE_HISTORY__;
108
+
109
+ const visibleReleases = useMemo(
110
+ () => (showAll ? releases : releases.slice(0, INITIAL_RELEASE_COUNT)),
111
+ [releases, showAll]
112
+ );
113
+
114
+ const hasMore = releases.length > INITIAL_RELEASE_COUNT;
115
+
116
+ if (releases.length === 0) {
117
+ return (
118
+ <div className="text-center py-8 text-sm text-muted-foreground">
119
+ No release history available.
120
+ </div>
121
+ );
122
+ }
123
+
124
+ return (
125
+ <div className="space-y-4">
126
+ {visibleReleases.map((release, i) => (
127
+ <div key={release.version}>
128
+ <div className="flex items-center gap-2 mb-1.5">
129
+ <span className="text-sm font-semibold">v{release.version}</span>
130
+ {i === 0 && (
131
+ <span className="px-1.5 py-0.5 text-[10px] font-medium bg-emerald-500/15 text-emerald-600 dark:text-emerald-400 rounded">
132
+ latest
133
+ </span>
134
+ )}
135
+ </div>
136
+ <ul className="space-y-1 ml-0.5">
137
+ {release.highlights.map((h) => {
138
+ const { icon: Icon, className } = TYPE_CONFIG[h.type];
139
+ return (
140
+ <li key={h.text} className="flex items-start gap-2 text-sm text-muted-foreground">
141
+ <Icon className={`h-3.5 w-3.5 mt-0.5 shrink-0 ${className}`} />
142
+ <span>{h.text}</span>
143
+ </li>
144
+ );
145
+ })}
146
+ </ul>
147
+ {i < visibleReleases.length - 1 && (
148
+ <div className="border-b mt-3" />
149
+ )}
150
+ </div>
151
+ ))}
152
+
153
+ {hasMore && !showAll && (
154
+ <button
155
+ onClick={() => setShowAll(true)}
156
+ className="flex items-center gap-1.5 text-sm text-muted-foreground hover:text-foreground transition-colors mx-auto"
157
+ >
158
+ <ChevronDown className="h-3.5 w-3.5" />
159
+ Show all {releases.length} releases
160
+ </button>
161
+ )}
162
+
163
+ {/* Legend */}
164
+ <div className="pt-3 border-t flex items-center justify-center gap-4 text-[11px] text-muted-foreground">
165
+ <span className="flex items-center gap-1">
166
+ <Plus className="h-3 w-3 text-emerald-500" /> Feature
167
+ </span>
168
+ <span className="flex items-center gap-1">
169
+ <Wrench className="h-3 w-3 text-amber-500" /> Fix
170
+ </span>
171
+ <span className="flex items-center gap-1">
172
+ <Zap className="h-3 w-3 text-blue-500" /> Perf
173
+ </span>
174
+ </div>
175
+ </div>
176
+ );
177
+ }
178
+
179
+ function ShortcutsTab() {
180
+ // Group shortcuts by category
181
+ const grouped = KEYBOARD_SHORTCUTS.reduce(
182
+ (acc, shortcut) => {
183
+ if (!acc[shortcut.category]) {
184
+ acc[shortcut.category] = [];
185
+ }
186
+ acc[shortcut.category].push(shortcut);
187
+ return acc;
188
+ },
189
+ {} as Record<string, (typeof KEYBOARD_SHORTCUTS)[number][]>
190
+ );
191
+
192
+ return (
193
+ <div className="space-y-4">
194
+ {Object.entries(grouped).map(([category, shortcuts]) => (
195
+ <div key={category}>
196
+ <h3 className="text-sm font-medium text-muted-foreground mb-2">
197
+ {category}
198
+ </h3>
199
+ <div className="space-y-1">
200
+ {shortcuts.map((shortcut) => (
201
+ <div
202
+ key={shortcut.key + shortcut.description}
203
+ className="flex items-center justify-between py-1"
204
+ >
205
+ <span className="text-sm">{shortcut.description}</span>
206
+ <kbd className="px-2 py-0.5 text-xs bg-muted rounded border font-mono">
207
+ {shortcut.key}
208
+ </kbd>
209
+ </div>
210
+ ))}
211
+ </div>
212
+ </div>
213
+ ))}
214
+ </div>
215
+ );
216
+ }
217
+
218
+ export function KeyboardShortcutsDialog({ open, onClose }: InfoDialogProps) {
16
219
  // Close on escape
17
220
  useEffect(() => {
18
221
  const handleKeyDown = (e: KeyboardEvent) => {
@@ -26,54 +229,57 @@ export function KeyboardShortcutsDialog({ open, onClose }: KeyboardShortcutsDial
26
229
 
27
230
  if (!open) return null;
28
231
 
29
- // Group shortcuts by category
30
- const grouped = KEYBOARD_SHORTCUTS.reduce((acc, shortcut) => {
31
- if (!acc[shortcut.category]) {
32
- acc[shortcut.category] = [];
33
- }
34
- acc[shortcut.category].push(shortcut);
35
- return acc;
36
- }, {} as Record<string, typeof KEYBOARD_SHORTCUTS[number][]>);
37
-
38
232
  return (
39
233
  <div className="fixed inset-0 z-50 flex items-center justify-center bg-black/50 backdrop-blur-sm">
40
234
  <div className="bg-card border rounded-lg shadow-xl w-full max-w-md m-4">
41
235
  {/* Header */}
42
236
  <div className="flex items-center justify-between p-4 border-b">
43
- <h2 className="text-lg font-semibold">Keyboard Shortcuts</h2>
237
+ <h2 className="text-lg font-semibold">Info</h2>
44
238
  <Button variant="ghost" size="icon-sm" onClick={onClose}>
45
239
  <X className="h-4 w-4" />
46
240
  </Button>
47
241
  </div>
48
242
 
49
- {/* Content */}
50
- <div className="p-4 max-h-96 overflow-y-auto">
51
- {Object.entries(grouped).map(([category, shortcuts]) => (
52
- <div key={category} className="mb-4 last:mb-0">
53
- <h3 className="text-sm font-medium text-muted-foreground mb-2">
54
- {category}
55
- </h3>
56
- <div className="space-y-1">
57
- {shortcuts.map((shortcut) => (
58
- <div
59
- key={shortcut.key + shortcut.description}
60
- className="flex items-center justify-between py-1"
61
- >
62
- <span className="text-sm">{shortcut.description}</span>
63
- <kbd className="px-2 py-0.5 text-xs bg-muted rounded border font-mono">
64
- {shortcut.key}
65
- </kbd>
66
- </div>
67
- ))}
68
- </div>
69
- </div>
70
- ))}
71
- </div>
243
+ {/* Tabbed Content */}
244
+ <Tabs defaultValue="about" className="w-full">
245
+ <div className="px-4 pt-4">
246
+ <TabsList className="w-full">
247
+ <TabsTrigger value="about" className="flex-1 gap-1.5 data-[state=active]:bg-background data-[state=active]:text-foreground">
248
+ <Info className="h-3.5 w-3.5" />
249
+ About
250
+ </TabsTrigger>
251
+ <TabsTrigger value="whatsnew" className="flex-1 gap-1.5 data-[state=active]:bg-background data-[state=active]:text-foreground">
252
+ <Sparkles className="h-3.5 w-3.5" />
253
+ What's New
254
+ </TabsTrigger>
255
+ <TabsTrigger value="shortcuts" className="flex-1 gap-1.5 data-[state=active]:bg-background data-[state=active]:text-foreground">
256
+ <Keyboard className="h-3.5 w-3.5" />
257
+ Shortcuts
258
+ </TabsTrigger>
259
+ </TabsList>
260
+ </div>
261
+
262
+ <TabsContent value="about" className="p-4 max-h-80 overflow-y-auto">
263
+ <AboutTab />
264
+ </TabsContent>
265
+
266
+ <TabsContent value="whatsnew" className="p-4 max-h-96 overflow-y-auto">
267
+ <WhatsNewTab />
268
+ </TabsContent>
269
+
270
+ <TabsContent value="shortcuts" className="p-4 max-h-80 overflow-y-auto">
271
+ <ShortcutsTab />
272
+ </TabsContent>
273
+ </Tabs>
72
274
 
73
275
  {/* Footer */}
74
276
  <div className="p-4 border-t text-center">
75
277
  <span className="text-xs text-muted-foreground">
76
- Press <kbd className="px-1 py-0.5 bg-muted rounded border font-mono text-xs">?</kbd> to toggle this dialog
278
+ Press{' '}
279
+ <kbd className="px-1 py-0.5 bg-muted rounded border font-mono text-xs">
280
+ ?
281
+ </kbd>{' '}
282
+ to toggle this panel
77
283
  </span>
78
284
  </div>
79
285
  </div>
@@ -81,7 +287,7 @@ export function KeyboardShortcutsDialog({ open, onClose }: KeyboardShortcutsDial
81
287
  );
82
288
  }
83
289
 
84
- // Hook to manage shortcuts dialog state
290
+ // Hook to manage info dialog state (renamed export for backward compatibility)
85
291
  export function useKeyboardShortcutsDialog() {
86
292
  const [open, setOpen] = useState(false);
87
293