@ifc-lite/viewer 1.1.6 → 1.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.
- package/LICENSE +373 -0
- package/dist/apple-touch-icon.png +0 -0
- package/dist/assets/Arrow.dom-B0e15b_b.js +20 -0
- package/dist/assets/arrow2-bb-jcVEo.js +2 -0
- package/dist/assets/arrow2_bg-4Y7xYo54.wasm +0 -0
- package/dist/assets/arrow2_bg-BlXl-cSQ.js +1 -0
- package/dist/assets/arrow2_bg-BoXCojjR.wasm +0 -0
- package/dist/assets/desktop-cache-oPzaWXYE.js +1 -0
- package/dist/assets/event-DIOks52T.js +1 -0
- package/dist/assets/ifc-cache-BAN4vcd4.js +1 -0
- package/dist/assets/ifc-lite_bg-C6kblxf9.wasm +0 -0
- package/dist/assets/index-Dgd6vzw_.js +65252 -0
- package/dist/assets/index-v3mcCUPN.css +1 -0
- package/dist/assets/native-bridge-Ci7NLjlZ.js +111 -0
- package/dist/assets/wasm-bridge-Dc82YpdZ.js +1 -0
- package/dist/favicon-16x16-cropped.png +0 -0
- package/dist/favicon-16x16.png +0 -0
- package/dist/favicon-192x192-cropped.png +0 -0
- package/dist/favicon-192x192.png +0 -0
- package/dist/favicon-32x32-cropped.png +0 -0
- package/dist/favicon-32x32.png +0 -0
- package/dist/favicon-48x48-cropped.png +0 -0
- package/dist/favicon-48x48.png +0 -0
- package/dist/favicon-512x512-cropped.png +0 -0
- package/dist/favicon-512x512.png +0 -0
- package/dist/favicon-64x64-cropped.png +0 -0
- package/dist/favicon-64x64.png +0 -0
- package/dist/favicon-96x96-cropped.png +0 -0
- package/dist/favicon-96x96.png +0 -0
- package/dist/favicon-square-512.png +0 -0
- package/dist/favicon.ico +0 -0
- package/dist/favicon.png +0 -0
- package/dist/favicon.svg +3 -0
- package/dist/index.html +44 -0
- package/dist/logo.png +0 -0
- package/dist/manifest.json +48 -0
- package/index.html +33 -2
- package/package.json +34 -17
- package/public/apple-touch-icon.png +0 -0
- package/public/favicon-16x16-cropped.png +0 -0
- package/public/favicon-16x16.png +0 -0
- package/public/favicon-192x192-cropped.png +0 -0
- package/public/favicon-192x192.png +0 -0
- package/public/favicon-32x32-cropped.png +0 -0
- package/public/favicon-32x32.png +0 -0
- package/public/favicon-48x48-cropped.png +0 -0
- package/public/favicon-48x48.png +0 -0
- package/public/favicon-512x512-cropped.png +0 -0
- package/public/favicon-512x512.png +0 -0
- package/public/favicon-64x64-cropped.png +0 -0
- package/public/favicon-64x64.png +0 -0
- package/public/favicon-96x96-cropped.png +0 -0
- package/public/favicon-96x96.png +0 -0
- package/public/favicon-square-512.png +0 -0
- package/public/favicon.ico +0 -0
- package/public/favicon.png +0 -0
- package/public/favicon.svg +3 -0
- package/public/logo.png +0 -0
- package/public/manifest.json +48 -0
- package/src/App.tsx +2 -0
- package/src/components/ui/alert.tsx +62 -0
- package/src/components/ui/badge.tsx +39 -0
- package/src/components/ui/dialog.tsx +120 -0
- package/src/components/ui/label.tsx +27 -0
- package/src/components/ui/select.tsx +151 -0
- package/src/components/ui/switch.tsx +30 -0
- package/src/components/ui/table.tsx +120 -0
- package/src/components/ui/tabs.tsx +1 -1
- package/src/components/viewer/BCFPanel.tsx +1164 -0
- package/src/components/viewer/BulkPropertyEditor.tsx +875 -0
- package/src/components/viewer/DataConnector.tsx +840 -0
- package/src/components/viewer/DrawingSettingsPanel.tsx +536 -0
- package/src/components/viewer/EntityContextMenu.tsx +45 -17
- package/src/components/viewer/ExportChangesButton.tsx +195 -0
- package/src/components/viewer/ExportDialog.tsx +402 -0
- package/src/components/viewer/HierarchyPanel.tsx +1132 -218
- package/src/components/viewer/IDSPanel.tsx +661 -0
- package/src/components/viewer/KeyboardShortcutsDialog.tsx +245 -39
- package/src/components/viewer/MainToolbar.tsx +418 -94
- package/src/components/viewer/PropertiesPanel.tsx +1355 -91
- package/src/components/viewer/PropertyEditor.tsx +611 -0
- package/src/components/viewer/Section2DPanel.tsx +3313 -0
- package/src/components/viewer/SheetSetupPanel.tsx +502 -0
- package/src/components/viewer/StatusBar.tsx +27 -16
- package/src/components/viewer/TitleBlockEditor.tsx +437 -0
- package/src/components/viewer/ToolOverlays.tsx +935 -127
- package/src/components/viewer/ViewerLayout.tsx +40 -11
- package/src/components/viewer/Viewport.tsx +1276 -336
- package/src/components/viewer/ViewportContainer.tsx +554 -18
- package/src/components/viewer/ViewportOverlays.tsx +24 -7
- package/src/hooks/useBCF.ts +504 -0
- package/src/hooks/useIDS.ts +1065 -0
- package/src/hooks/useIfc.ts +1534 -205
- package/src/hooks/useIfcCache.ts +279 -0
- package/src/hooks/useKeyboardShortcuts.ts +50 -8
- package/src/hooks/useModelSelection.ts +61 -0
- package/src/hooks/useViewerSelectors.ts +218 -0
- package/src/hooks/useWebGPU.ts +80 -0
- package/src/index.css +265 -27
- package/src/lib/platform.ts +23 -0
- package/src/services/cacheService.ts +142 -0
- package/src/services/desktop-cache.ts +143 -0
- package/src/services/fs-cache.ts +212 -0
- package/src/services/ifc-cache.ts +14 -6
- package/src/store/constants.ts +85 -0
- package/src/store/index.ts +214 -0
- package/src/store/slices/bcfSlice.ts +372 -0
- package/src/store/slices/cameraSlice.ts +63 -0
- package/src/store/slices/dataSlice.test.ts +226 -0
- package/src/store/slices/dataSlice.ts +112 -0
- package/src/store/slices/drawing2DSlice.ts +340 -0
- package/src/store/slices/hoverSlice.ts +40 -0
- package/src/store/slices/idsSlice.ts +310 -0
- package/src/store/slices/loadingSlice.ts +33 -0
- package/src/store/slices/measurementSlice.test.ts +217 -0
- package/src/store/slices/measurementSlice.ts +293 -0
- package/src/store/slices/modelSlice.test.ts +271 -0
- package/src/store/slices/modelSlice.ts +211 -0
- package/src/store/slices/mutationSlice.ts +502 -0
- package/src/store/slices/sectionSlice.test.ts +125 -0
- package/src/store/slices/sectionSlice.ts +58 -0
- package/src/store/slices/selectionSlice.test.ts +286 -0
- package/src/store/slices/selectionSlice.ts +263 -0
- package/src/store/slices/sheetSlice.ts +565 -0
- package/src/store/slices/uiSlice.ts +58 -0
- package/src/store/slices/visibilitySlice.test.ts +304 -0
- package/src/store/slices/visibilitySlice.ts +277 -0
- package/src/store/types.test.ts +135 -0
- package/src/store/types.ts +248 -0
- package/src/store.ts +40 -515
- package/src/utils/ifcConfig.ts +82 -0
- package/src/utils/localParsingUtils.ts +287 -0
- package/src/utils/serverDataModel.ts +783 -0
- package/src/utils/spatialHierarchy.ts +283 -0
- package/src/utils/viewportUtils.ts +334 -0
- package/src/vite-env.d.ts +23 -0
- package/src/webgpu-types.d.ts +128 -0
- package/src-tauri/Cargo.toml +29 -0
- package/src-tauri/build.rs +7 -0
- package/src-tauri/capabilities/default.json +18 -0
- package/src-tauri/icons/128x128.png +0 -0
- package/src-tauri/icons/128x128@2x.png +0 -0
- package/src-tauri/icons/32x32.png +0 -0
- package/src-tauri/icons/Square107x107Logo.png +0 -0
- package/src-tauri/icons/Square142x142Logo.png +0 -0
- package/src-tauri/icons/Square150x150Logo.png +0 -0
- package/src-tauri/icons/Square284x284Logo.png +0 -0
- package/src-tauri/icons/Square30x30Logo.png +0 -0
- package/src-tauri/icons/Square310x310Logo.png +0 -0
- package/src-tauri/icons/Square44x44Logo.png +0 -0
- package/src-tauri/icons/Square71x71Logo.png +0 -0
- package/src-tauri/icons/Square89x89Logo.png +0 -0
- package/src-tauri/icons/StoreLogo.png +0 -0
- package/src-tauri/icons/icon.icns +0 -0
- package/src-tauri/icons/icon.ico +0 -0
- package/src-tauri/icons/icon.png +0 -0
- package/src-tauri/src/lib.rs +21 -0
- package/src-tauri/src/main.rs +10 -0
- package/src-tauri/tauri.conf.json +39 -0
- package/vite.config.ts +174 -26
- package/public/ifc-lite_bg.wasm +0 -0
- package/public/web-ifc.wasm +0 -0
- package/src/components/Viewport.tsx +0 -723
- package/src/components/viewer/BoxSelectionOverlay.tsx +0 -53
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
|
4
|
+
|
|
5
|
+
import { useState, useEffect } from 'react';
|
|
6
|
+
|
|
7
|
+
export interface WebGPUStatus {
|
|
8
|
+
supported: boolean;
|
|
9
|
+
checking: boolean;
|
|
10
|
+
reason: string | null;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Robust WebGPU detection hook.
|
|
15
|
+
*
|
|
16
|
+
* Detection method:
|
|
17
|
+
* 1. Check if navigator.gpu exists (basic API availability)
|
|
18
|
+
* 2. Attempt to request a GPU adapter (confirms actual hardware/driver support)
|
|
19
|
+
*
|
|
20
|
+
* This two-step check is necessary because:
|
|
21
|
+
* - Some browsers expose navigator.gpu but fail to provide an adapter
|
|
22
|
+
* - Software rendering may be available but unsuitable for our use case
|
|
23
|
+
* - Driver issues can prevent adapter creation even with WebGPU support
|
|
24
|
+
*/
|
|
25
|
+
export function useWebGPU(): WebGPUStatus {
|
|
26
|
+
const [status, setStatus] = useState<WebGPUStatus>({
|
|
27
|
+
supported: false,
|
|
28
|
+
checking: true,
|
|
29
|
+
reason: null,
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
async function checkWebGPUSupport() {
|
|
34
|
+
// Step 1: Check if WebGPU API is available
|
|
35
|
+
if (!navigator.gpu) {
|
|
36
|
+
setStatus({
|
|
37
|
+
supported: false,
|
|
38
|
+
checking: false,
|
|
39
|
+
reason: 'WebGPU API not available in this browser',
|
|
40
|
+
});
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
try {
|
|
45
|
+
// Step 2: Try to get a GPU adapter
|
|
46
|
+
// This confirms actual hardware/driver support
|
|
47
|
+
const adapter = await navigator.gpu.requestAdapter();
|
|
48
|
+
|
|
49
|
+
if (!adapter) {
|
|
50
|
+
setStatus({
|
|
51
|
+
supported: false,
|
|
52
|
+
checking: false,
|
|
53
|
+
reason: 'No compatible GPU adapter found',
|
|
54
|
+
});
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Optional: Check for required features if needed
|
|
59
|
+
// const features = adapter.features;
|
|
60
|
+
// const limits = adapter.limits;
|
|
61
|
+
|
|
62
|
+
setStatus({
|
|
63
|
+
supported: true,
|
|
64
|
+
checking: false,
|
|
65
|
+
reason: null,
|
|
66
|
+
});
|
|
67
|
+
} catch (error) {
|
|
68
|
+
setStatus({
|
|
69
|
+
supported: false,
|
|
70
|
+
checking: false,
|
|
71
|
+
reason: error instanceof Error ? error.message : 'Failed to initialize WebGPU',
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
checkWebGPUSupport();
|
|
77
|
+
}, []);
|
|
78
|
+
|
|
79
|
+
return status;
|
|
80
|
+
}
|
package/src/index.css
CHANGED
|
@@ -4,6 +4,75 @@
|
|
|
4
4
|
|
|
5
5
|
@import "tailwindcss";
|
|
6
6
|
|
|
7
|
+
/* ═══════════════════════════════════════════════════════════════════════════
|
|
8
|
+
TOKYO NIGHT THEME - Dark Stormy Cyberpunk Vibes
|
|
9
|
+
═══════════════════════════════════════════════════════════════════════════ */
|
|
10
|
+
|
|
11
|
+
/* Tokyo Night Color Palette */
|
|
12
|
+
:root {
|
|
13
|
+
/* Storm - Deep backgrounds */
|
|
14
|
+
--tokyo-storm: #1a1b26;
|
|
15
|
+
--tokyo-night: #16161e;
|
|
16
|
+
--tokyo-bg-dark: #13131a;
|
|
17
|
+
--tokyo-bg-highlight: #1f2335;
|
|
18
|
+
|
|
19
|
+
/* Atmosphere */
|
|
20
|
+
--tokyo-terminal-black: #414868;
|
|
21
|
+
--tokyo-comment: #565f89;
|
|
22
|
+
--tokyo-dark3: #3b4261;
|
|
23
|
+
|
|
24
|
+
/* Foreground */
|
|
25
|
+
--tokyo-fg: #a9b1d6;
|
|
26
|
+
--tokyo-fg-dark: #7982a9;
|
|
27
|
+
--tokyo-fg-gutter: #363b54;
|
|
28
|
+
|
|
29
|
+
/* Neon accents */
|
|
30
|
+
--tokyo-blue: #7aa2f7;
|
|
31
|
+
--tokyo-cyan: #7dcfff;
|
|
32
|
+
--tokyo-magenta: #bb9af7;
|
|
33
|
+
--tokyo-purple: #9d7cd8;
|
|
34
|
+
--tokyo-orange: #ff9e64;
|
|
35
|
+
--tokyo-yellow: #e0af68;
|
|
36
|
+
--tokyo-green: #9ece6a;
|
|
37
|
+
--tokyo-teal: #73daca;
|
|
38
|
+
--tokyo-red: #f7768e;
|
|
39
|
+
|
|
40
|
+
/* Special */
|
|
41
|
+
--tokyo-git-add: #449dab;
|
|
42
|
+
--tokyo-git-change: #6183bb;
|
|
43
|
+
--tokyo-git-delete: #914c54;
|
|
44
|
+
|
|
45
|
+
/* Hierarchy panel - Light mode */
|
|
46
|
+
--hierarchy-selected-bg: #e4e4e7;
|
|
47
|
+
--hierarchy-selected-text: #18181b;
|
|
48
|
+
--hierarchy-text: #52525b;
|
|
49
|
+
--hierarchy-hover-bg: #f4f4f5;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/* Tabs - Light mode */
|
|
53
|
+
:root {
|
|
54
|
+
--tabs-bg: #f4f4f5;
|
|
55
|
+
--tabs-border: #e4e4e7;
|
|
56
|
+
--tab-text: #71717a;
|
|
57
|
+
--tab-active-bg: #ffffff;
|
|
58
|
+
--tab-active-text: #18181b;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.dark {
|
|
62
|
+
/* Hierarchy panel - Dark mode */
|
|
63
|
+
--hierarchy-selected-bg: #1f2335;
|
|
64
|
+
--hierarchy-selected-text: #a9b1d6;
|
|
65
|
+
--hierarchy-text: #7982a9;
|
|
66
|
+
--hierarchy-hover-bg: rgba(31, 35, 53, 0.5);
|
|
67
|
+
|
|
68
|
+
/* Tabs - Dark mode */
|
|
69
|
+
--tabs-bg: #1f2335;
|
|
70
|
+
--tabs-border: #3b4261;
|
|
71
|
+
--tab-text: #565f89;
|
|
72
|
+
--tab-active-bg: #16161e;
|
|
73
|
+
--tab-active-text: #a9b1d6;
|
|
74
|
+
}
|
|
75
|
+
|
|
7
76
|
/* Custom theme configuration */
|
|
8
77
|
@theme {
|
|
9
78
|
--color-background: hsl(0 0% 100%);
|
|
@@ -12,7 +81,7 @@
|
|
|
12
81
|
--color-card-foreground: hsl(240 10% 3.9%);
|
|
13
82
|
--color-popover: hsl(0 0% 100%);
|
|
14
83
|
--color-popover-foreground: hsl(240 10% 3.9%);
|
|
15
|
-
--color-primary:
|
|
84
|
+
--color-primary: var(--tokyo-blue);
|
|
16
85
|
--color-primary-foreground: hsl(0 0% 100%);
|
|
17
86
|
--color-secondary: hsl(240 4.8% 95.9%);
|
|
18
87
|
--color-secondary-foreground: hsl(240 5.9% 10%);
|
|
@@ -20,39 +89,40 @@
|
|
|
20
89
|
--color-muted-foreground: hsl(240 3.8% 46.1%);
|
|
21
90
|
--color-accent: hsl(240 4.8% 95.9%);
|
|
22
91
|
--color-accent-foreground: hsl(240 5.9% 10%);
|
|
23
|
-
--color-destructive:
|
|
92
|
+
--color-destructive: var(--tokyo-red);
|
|
24
93
|
--color-destructive-foreground: hsl(0 0% 98%);
|
|
25
94
|
--color-border: hsl(240 5.9% 90%);
|
|
26
95
|
--color-input: hsl(240 5.9% 90%);
|
|
27
|
-
--color-ring:
|
|
96
|
+
--color-ring: var(--tokyo-blue);
|
|
28
97
|
|
|
29
|
-
--radius-sm: 0.
|
|
30
|
-
--radius-md: 0.
|
|
31
|
-
--radius-lg: 0.
|
|
32
|
-
--radius-xl: 0.
|
|
98
|
+
--radius-sm: 0.125rem;
|
|
99
|
+
--radius-md: 0.25rem;
|
|
100
|
+
--radius-lg: 0.375rem;
|
|
101
|
+
--radius-xl: 0.5rem;
|
|
33
102
|
}
|
|
34
103
|
|
|
35
|
-
/* Dark mode
|
|
104
|
+
/* Dark mode - Tokyo Night Storm */
|
|
105
|
+
:root.dark,
|
|
36
106
|
.dark {
|
|
37
|
-
--color-background:
|
|
38
|
-
--color-foreground:
|
|
39
|
-
--color-card:
|
|
40
|
-
--color-card-foreground:
|
|
41
|
-
--color-popover:
|
|
42
|
-
--color-popover-foreground:
|
|
43
|
-
--color-primary:
|
|
44
|
-
--color-primary-foreground:
|
|
45
|
-
--color-secondary:
|
|
46
|
-
--color-secondary-foreground:
|
|
47
|
-
--color-muted:
|
|
48
|
-
--color-muted-foreground:
|
|
49
|
-
--color-accent:
|
|
50
|
-
--color-accent-foreground:
|
|
51
|
-
--color-destructive:
|
|
52
|
-
--color-destructive-foreground:
|
|
53
|
-
--color-border:
|
|
54
|
-
--color-input:
|
|
55
|
-
--color-ring:
|
|
107
|
+
--color-background: var(--tokyo-storm) !important;
|
|
108
|
+
--color-foreground: var(--tokyo-fg) !important;
|
|
109
|
+
--color-card: var(--tokyo-night) !important;
|
|
110
|
+
--color-card-foreground: var(--tokyo-fg) !important;
|
|
111
|
+
--color-popover: var(--tokyo-night) !important;
|
|
112
|
+
--color-popover-foreground: var(--tokyo-fg) !important;
|
|
113
|
+
--color-primary: var(--tokyo-blue);
|
|
114
|
+
--color-primary-foreground: var(--tokyo-night);
|
|
115
|
+
--color-secondary: var(--tokyo-bg-highlight);
|
|
116
|
+
--color-secondary-foreground: var(--tokyo-fg);
|
|
117
|
+
--color-muted: var(--tokyo-bg-highlight);
|
|
118
|
+
--color-muted-foreground: var(--tokyo-comment);
|
|
119
|
+
--color-accent: var(--tokyo-bg-highlight);
|
|
120
|
+
--color-accent-foreground: var(--tokyo-fg);
|
|
121
|
+
--color-destructive: var(--tokyo-red);
|
|
122
|
+
--color-destructive-foreground: var(--tokyo-fg);
|
|
123
|
+
--color-border: var(--tokyo-dark3);
|
|
124
|
+
--color-input: var(--tokyo-bg-highlight);
|
|
125
|
+
--color-ring: var(--tokyo-blue);
|
|
56
126
|
}
|
|
57
127
|
|
|
58
128
|
/* Base styles */
|
|
@@ -66,6 +136,174 @@ body {
|
|
|
66
136
|
font-feature-settings: "rlig" 1, "calt" 1;
|
|
67
137
|
}
|
|
68
138
|
|
|
139
|
+
/* ═══════════════════════════════════════════════════════════════════════════
|
|
140
|
+
TOKYO NIGHT DARK MODE OVERRIDES
|
|
141
|
+
═══════════════════════════════════════════════════════════════════════════ */
|
|
142
|
+
|
|
143
|
+
.dark body,
|
|
144
|
+
.dark #root {
|
|
145
|
+
background-color: var(--tokyo-storm) !important;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/* Panels and cards */
|
|
149
|
+
.dark .bg-white,
|
|
150
|
+
.dark .bg-card,
|
|
151
|
+
.dark .bg-background {
|
|
152
|
+
background-color: var(--tokyo-night) !important;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
.dark .bg-zinc-50,
|
|
156
|
+
.dark .bg-zinc-100 {
|
|
157
|
+
background-color: var(--tokyo-bg-highlight) !important;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
.dark .bg-zinc-900,
|
|
161
|
+
.dark .bg-zinc-950,
|
|
162
|
+
.dark .bg-black {
|
|
163
|
+
background-color: var(--tokyo-night) !important;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/* Borders */
|
|
167
|
+
.dark .border-zinc-200,
|
|
168
|
+
.dark .border-zinc-700,
|
|
169
|
+
.dark .border-zinc-800 {
|
|
170
|
+
border-color: var(--tokyo-dark3) !important;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
.dark .border-zinc-900,
|
|
174
|
+
.dark .border-zinc-100 {
|
|
175
|
+
border-color: var(--tokyo-terminal-black) !important;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/* Text colors */
|
|
179
|
+
.dark .text-zinc-900,
|
|
180
|
+
.dark .text-zinc-100 {
|
|
181
|
+
color: var(--tokyo-fg) !important;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
.dark .text-zinc-500,
|
|
185
|
+
.dark .text-zinc-400,
|
|
186
|
+
.dark .text-zinc-600 {
|
|
187
|
+
color: var(--tokyo-comment) !important;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
.dark .text-zinc-200 {
|
|
191
|
+
color: var(--tokyo-fg-dark) !important;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/* Primary accent */
|
|
195
|
+
.dark .bg-primary {
|
|
196
|
+
background-color: var(--tokyo-blue) !important;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
.dark .text-primary {
|
|
200
|
+
color: var(--tokyo-blue) !important;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
.dark .border-primary {
|
|
204
|
+
border-color: var(--tokyo-blue) !important;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/* Clean shadows */
|
|
208
|
+
.dark .shadow-lg,
|
|
209
|
+
.dark .shadow-xl {
|
|
210
|
+
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.4) !important;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/* Selection highlight */
|
|
214
|
+
.dark ::selection {
|
|
215
|
+
background-color: var(--tokyo-bg-highlight);
|
|
216
|
+
color: var(--tokyo-cyan);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/* Scrollbar Tokyo Night style */
|
|
220
|
+
.dark .scrollbar-thin {
|
|
221
|
+
scrollbar-color: var(--tokyo-terminal-black) var(--tokyo-night);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
.dark .scrollbar-thin::-webkit-scrollbar-thumb {
|
|
225
|
+
background-color: var(--tokyo-terminal-black);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
.dark .scrollbar-thin::-webkit-scrollbar-thumb:hover {
|
|
229
|
+
background-color: var(--tokyo-comment);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/* Interactive elements */
|
|
233
|
+
.dark .hover\:bg-primary:hover {
|
|
234
|
+
background-color: var(--tokyo-blue) !important;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/* Force dark backgrounds on data-state active tabs */
|
|
238
|
+
.dark [data-state="active"] {
|
|
239
|
+
background-color: var(--tokyo-night) !important;
|
|
240
|
+
color: var(--tokyo-fg) !important;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/* Force dark hover backgrounds */
|
|
244
|
+
.dark .hover\:bg-zinc-50:hover,
|
|
245
|
+
.dark .hover\:bg-zinc-100:hover,
|
|
246
|
+
.dark .hover\:bg-zinc-200:hover,
|
|
247
|
+
.dark .hover\:bg-white:hover {
|
|
248
|
+
background-color: var(--tokyo-bg-highlight) !important;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/* Force dark selected/active backgrounds */
|
|
252
|
+
.dark .data-\[state\=active\]\:bg-white[data-state="active"],
|
|
253
|
+
.dark .data-\[state\=active\]\:bg-zinc-100[data-state="active"] {
|
|
254
|
+
background-color: var(--tokyo-night) !important;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/* Hierarchy panel hover */
|
|
258
|
+
.hierarchy-item:not(.selected):hover {
|
|
259
|
+
background-color: var(--hierarchy-hover-bg) !important;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/* Tab triggers */
|
|
263
|
+
.tab-trigger {
|
|
264
|
+
background-color: transparent !important;
|
|
265
|
+
color: var(--tab-text) !important;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
.tab-trigger[data-state="active"] {
|
|
269
|
+
background-color: var(--tab-active-bg) !important;
|
|
270
|
+
color: var(--tab-active-text) !important;
|
|
271
|
+
font-weight: 700;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/* Quantity cards - cyan accent */
|
|
275
|
+
.dark .border-blue-200,
|
|
276
|
+
.dark .border-blue-800,
|
|
277
|
+
.dark .border-blue-900 {
|
|
278
|
+
border-color: var(--tokyo-cyan) !important;
|
|
279
|
+
border-opacity: 0.3;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
.dark .text-blue-700,
|
|
283
|
+
.dark .text-blue-400 {
|
|
284
|
+
color: var(--tokyo-cyan) !important;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
.dark .bg-blue-50\/20,
|
|
288
|
+
.dark .bg-blue-950\/20 {
|
|
289
|
+
background-color: rgba(125, 207, 255, 0.05) !important;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/* Success/Location - teal accent */
|
|
293
|
+
.dark .border-emerald-500\/30 {
|
|
294
|
+
border-color: rgba(115, 218, 202, 0.3) !important;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
.dark .text-emerald-800,
|
|
298
|
+
.dark .text-emerald-400 {
|
|
299
|
+
color: var(--tokyo-teal) !important;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
.dark .bg-emerald-50\/50,
|
|
303
|
+
.dark .bg-emerald-900\/10 {
|
|
304
|
+
background-color: rgba(115, 218, 202, 0.05) !important;
|
|
305
|
+
}
|
|
306
|
+
|
|
69
307
|
/* Custom scrollbar */
|
|
70
308
|
.scrollbar-thin {
|
|
71
309
|
scrollbar-width: thin;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Platform detection utilities for runtime bridge pattern
|
|
7
|
+
* Routes to WASM (browser) or native (Tauri desktop) implementations
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Detects if running in Tauri desktop environment
|
|
12
|
+
*/
|
|
13
|
+
export function isTauri(): boolean {
|
|
14
|
+
return typeof window !== 'undefined' && '__TAURI_INTERNALS__' in window;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Platform-specific cache implementation
|
|
19
|
+
* Returns 'indexeddb' for browser, 'filesystem' for desktop
|
|
20
|
+
*/
|
|
21
|
+
export function getCacheType(): 'indexeddb' | 'filesystem' {
|
|
22
|
+
return isTauri() ? 'filesystem' : 'indexeddb';
|
|
23
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Platform-agnostic cache service
|
|
7
|
+
* Dynamically loads the appropriate cache implementation based on platform:
|
|
8
|
+
* - Tauri (desktop): Uses native filesystem via desktop-cache.ts
|
|
9
|
+
* - Web: Uses IndexedDB via ifc-cache.ts
|
|
10
|
+
*
|
|
11
|
+
* Extracted from useIfc.ts for reusability and testability
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { isTauri } from '../utils/ifcConfig.js';
|
|
15
|
+
|
|
16
|
+
// ============================================================================
|
|
17
|
+
// Types
|
|
18
|
+
// ============================================================================
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Result from cache lookup
|
|
22
|
+
*/
|
|
23
|
+
export interface CacheResult {
|
|
24
|
+
/** Serialized cache buffer containing data store and geometry */
|
|
25
|
+
buffer: ArrayBuffer;
|
|
26
|
+
/** Original IFC source file for on-demand property extraction */
|
|
27
|
+
sourceBuffer?: ArrayBuffer;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Function signature for getting cached data
|
|
32
|
+
*/
|
|
33
|
+
export type GetCachedFn = (key: string) => Promise<CacheResult | null>;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Function signature for setting cached data
|
|
37
|
+
*/
|
|
38
|
+
export type SetCachedFn = (
|
|
39
|
+
key: string,
|
|
40
|
+
data: ArrayBuffer,
|
|
41
|
+
fileName: string,
|
|
42
|
+
fileSize: number,
|
|
43
|
+
sourceBuffer?: ArrayBuffer
|
|
44
|
+
) => Promise<void>;
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Function signature for deleting cached data
|
|
48
|
+
*/
|
|
49
|
+
export type DeleteCachedFn = (key: string) => Promise<void>;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Cache service interface
|
|
53
|
+
*/
|
|
54
|
+
export interface ICacheService {
|
|
55
|
+
getCached: GetCachedFn;
|
|
56
|
+
setCached: SetCachedFn;
|
|
57
|
+
deleteCached: DeleteCachedFn;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// ============================================================================
|
|
61
|
+
// Service Singleton
|
|
62
|
+
// ============================================================================
|
|
63
|
+
|
|
64
|
+
/** Cached service instance - loaded once per session */
|
|
65
|
+
let cacheService: ICacheService | null = null;
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Get the cache service for the current platform
|
|
69
|
+
* Lazily loads the appropriate implementation
|
|
70
|
+
*/
|
|
71
|
+
export async function getCacheService(): Promise<ICacheService> {
|
|
72
|
+
if (cacheService) return cacheService;
|
|
73
|
+
|
|
74
|
+
if (isTauri) {
|
|
75
|
+
// Desktop: Use Tauri native filesystem
|
|
76
|
+
const mod = await import('./desktop-cache.js');
|
|
77
|
+
cacheService = {
|
|
78
|
+
getCached: mod.getCached,
|
|
79
|
+
setCached: mod.setCached,
|
|
80
|
+
deleteCached: mod.deleteCached,
|
|
81
|
+
};
|
|
82
|
+
} else {
|
|
83
|
+
// Web: Use IndexedDB
|
|
84
|
+
const mod = await import('./ifc-cache.js');
|
|
85
|
+
cacheService = {
|
|
86
|
+
getCached: mod.getCached,
|
|
87
|
+
setCached: mod.setCached,
|
|
88
|
+
deleteCached: mod.deleteCached,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return cacheService;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// ============================================================================
|
|
96
|
+
// Convenience Functions
|
|
97
|
+
// ============================================================================
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Get cached data by key
|
|
101
|
+
* @param key - Cache key (typically xxhash64 of source file)
|
|
102
|
+
* @returns Cache result with buffer and optional source, or null if not found
|
|
103
|
+
*/
|
|
104
|
+
export async function getCached(key: string): Promise<CacheResult | null> {
|
|
105
|
+
const service = await getCacheService();
|
|
106
|
+
return service.getCached(key);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Store data in cache
|
|
111
|
+
* @param key - Cache key (typically xxhash64 of source file)
|
|
112
|
+
* @param data - Serialized cache buffer
|
|
113
|
+
* @param fileName - Original file name for logging
|
|
114
|
+
* @param fileSize - Original file size for logging
|
|
115
|
+
* @param sourceBuffer - Optional source file for on-demand extraction
|
|
116
|
+
*/
|
|
117
|
+
export async function setCached(
|
|
118
|
+
key: string,
|
|
119
|
+
data: ArrayBuffer,
|
|
120
|
+
fileName: string,
|
|
121
|
+
fileSize: number,
|
|
122
|
+
sourceBuffer?: ArrayBuffer
|
|
123
|
+
): Promise<void> {
|
|
124
|
+
const service = await getCacheService();
|
|
125
|
+
return service.setCached(key, data, fileName, fileSize, sourceBuffer);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Delete a cache entry by key
|
|
130
|
+
* @param key - Cache key to delete
|
|
131
|
+
*/
|
|
132
|
+
export async function deleteCached(key: string): Promise<void> {
|
|
133
|
+
const service = await getCacheService();
|
|
134
|
+
return service.deleteCached(key);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Reset the cache service singleton (useful for testing)
|
|
139
|
+
*/
|
|
140
|
+
export function resetCacheService(): void {
|
|
141
|
+
cacheService = null;
|
|
142
|
+
}
|