@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.
- package/LICENSE +373 -0
- package/dist/apple-touch-icon.png +0 -0
- package/dist/assets/Arrow.dom-BjDQoB2M.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-YBtrHPu3.js +65252 -0
- package/dist/assets/index-v3mcCUPN.css +1 -0
- package/dist/assets/native-bridge-CULtTDX3.js +111 -0
- package/dist/assets/wasm-bridge-CjL-lSak.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
|
@@ -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
|
-
|
|
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
|
-
|
|
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">
|
|
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
|
-
<
|
|
51
|
-
|
|
52
|
-
<
|
|
53
|
-
<
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
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
|
|
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
|
|
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
|
|