@ifc-lite/viewer 1.25.2 → 1.27.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/.turbo/turbo-build.log +40 -30
- package/CHANGELOG.md +110 -0
- package/dist/assets/{basketViewActivator-CTgyKI3U.js → basketViewActivator-B3CdrLsb.js} +7 -7
- package/dist/assets/{bcf-7jQby1qi.js → bcf-QeHK_Aud.js} +5 -5
- package/dist/assets/{browser-DXS29_v9.js → browser-BIoDDfBW.js} +1 -1
- package/dist/assets/{cesium-BoVuJvTC.js → cesium-CzZn5yVA.js} +319 -319
- package/dist/assets/{deflate-Cfp9t1Df.js → deflate-B-d0SYQM.js} +1 -1
- package/dist/assets/exceljs.min-DsuzKYnj.js +29 -0
- package/dist/assets/{exporters-DfSvJPi4.js → exporters-B4LbZFeT.js} +1434 -1179
- package/dist/assets/geometry.worker-BdH-E6NB.js +1 -0
- package/dist/assets/{geotiff-xZoE8BkO.js → geotiff-CrVtDRFq.js} +10 -10
- package/dist/assets/html2canvas.esm-Ge7aVWlp.js +5 -0
- package/dist/assets/{ids-Cu73hD0Y.js → ids-DjsGFN10.js} +21 -21
- package/dist/assets/ifc-lite_bg-DsYUIHm3.wasm +0 -0
- package/dist/assets/{index-WSbA5iy6.js → index-COYokSKc.js} +44122 -38782
- package/dist/assets/index-ajK6D32J.css +1 -0
- package/dist/assets/index.es-CY202jA3.js +6866 -0
- package/dist/assets/{jpeg-DhwFEbqb.js → jpeg-D4wOkf5h.js} +1 -1
- package/dist/assets/jspdf.es.min-DIGb9BHN.js +19571 -0
- package/dist/assets/jspdf.plugin.autotable-BBLUVd7n.js +2 -0
- package/dist/assets/{lerc-Dz6BXOVb.js → lerc-DmW0_tgf.js} +1 -1
- package/dist/assets/{lzw-C9z0fG2o.js → lzw-oWetY-d6.js} +1 -1
- package/dist/assets/{maplibre-gl-Do6O5tDc.js → maplibre-gl-BF3Z0idw.js} +1 -1
- package/dist/assets/{native-bridge-RvDmzO-2.js → native-bridge-BX8_tHXE.js} +1 -1
- package/dist/assets/{packbits-jfwifz7C.js → packbits-F8Nkp4NY.js} +1 -1
- package/dist/assets/{pako.esm-Cram60i4.js → pako.esm-n3Pgozwg.js} +1 -1
- package/dist/assets/{parser.worker-C594dWxH.js → parser.worker-D591Zu_-.js} +3 -3
- package/dist/assets/pdf-Dsh3HPZB.js +135 -0
- package/dist/assets/raw-D9iw0tmc.js +1 -0
- package/dist/assets/{sandbox-DDSZ7rek.js → sandbox-BAC3a-eN.js} +4235 -2716
- package/dist/assets/server-client-Cjwnm7il.js +706 -0
- package/dist/assets/{webimage-XFHVyVtC.js → webimage-BLV1dgmd.js} +1 -1
- package/dist/assets/xlsx-Bc2HTrjC.js +142 -0
- package/dist/assets/{zip-BJqVbRkU.js → zip-DFgP-l20.js} +1 -1
- package/dist/assets/{zstd-3q5qcl5V.js → zstd-C_1HxVrA.js} +1 -1
- package/dist/index.html +8 -8
- package/package.json +13 -9
- package/src/components/extensions/FlavorDialog.tsx +18 -2
- package/src/components/extensions/FlavorListView.tsx +12 -3
- package/src/components/mcp/PlaygroundChat.tsx +1 -0
- package/src/components/mcp/data.ts +6 -0
- package/src/components/mcp/playground-dispatcher.ts +277 -0
- package/src/components/mcp/types.ts +2 -1
- package/src/components/ui/combo-input.tsx +163 -0
- package/src/components/ui/tabs.tsx +1 -1
- package/src/components/viewer/ClashBcfExportDialog.tsx +271 -0
- package/src/components/viewer/ClashPanel.tsx +370 -0
- package/src/components/viewer/ClashSettingsDialog.tsx +407 -0
- package/src/components/viewer/CommandPalette.tsx +14 -15
- package/src/components/viewer/MainToolbar.tsx +155 -175
- package/src/components/viewer/PropertiesPanel.tsx +13 -6
- package/src/components/viewer/SearchInline.tsx +62 -2
- package/src/components/viewer/SearchModal.filter.builder.tsx +24 -393
- package/src/components/viewer/SearchModal.filter.editors.tsx +503 -0
- package/src/components/viewer/SearchModal.filter.tsx +64 -1
- package/src/components/viewer/SearchModal.tsx +19 -6
- package/src/components/viewer/ViewerLayout.tsx +5 -0
- package/src/components/viewer/Viewport.tsx +64 -9
- package/src/components/viewer/ViewportContainer.tsx +45 -3
- package/src/components/viewer/bcf/BCFOverlay.tsx +5 -4
- package/src/components/viewer/lists/ColumnHeaderMenu.tsx +84 -0
- package/src/components/viewer/lists/ListBuilder.tsx +789 -280
- package/src/components/viewer/lists/ListGroupingBar.tsx +72 -0
- package/src/components/viewer/lists/ListPanel.tsx +49 -5
- package/src/components/viewer/lists/ListResultsTable.tsx +270 -176
- package/src/components/viewer/lists/list-table-utils.ts +123 -0
- package/src/components/viewer/useGeometryStreaming.ts +21 -1
- package/src/generated/mcp-catalog.json +4 -0
- package/src/hooks/ingest/streamCleanup.test.ts +41 -0
- package/src/hooks/ingest/streamCleanup.ts +45 -0
- package/src/hooks/ingest/viewerModelIngest.ts +64 -42
- package/src/hooks/ingest/watchedGeometryStream.test.ts +78 -0
- package/src/hooks/ingest/watchedGeometryStream.ts +76 -0
- package/src/hooks/source-key.ts +35 -0
- package/src/hooks/useAlignmentLines3D.ts +139 -0
- package/src/hooks/useClash.ts +420 -0
- package/src/hooks/useGridLines3D.ts +140 -0
- package/src/hooks/useIfcFederation.ts +16 -2
- package/src/hooks/useIfcLoader.ts +5 -7
- package/src/lib/clash/persistence.ts +308 -0
- package/src/lib/geo/effective-georef.test.ts +66 -0
- package/src/lib/length-unit-scale.ts +41 -0
- package/src/lib/lists/adapter.ts +136 -11
- package/src/lib/lists/export/csv.ts +47 -0
- package/src/lib/lists/export/index.ts +49 -0
- package/src/lib/lists/export/model.ts +111 -0
- package/src/lib/lists/export/pdf.ts +67 -0
- package/src/lib/lists/export/xlsx.ts +83 -0
- package/src/lib/lists/index.ts +2 -0
- package/src/lib/search/filter-evaluate.test.ts +81 -0
- package/src/lib/search/filter-evaluate.ts +59 -87
- package/src/lib/search/filter-match.ts +167 -0
- package/src/lib/search/filter-rules.test.ts +25 -0
- package/src/lib/search/filter-rules.ts +75 -2
- package/src/lib/search/filter-schema.ts +0 -0
- package/src/lib/slab-edit.test.ts +72 -0
- package/src/lib/slab-edit.ts +159 -19
- package/src/sdk/adapters/export-adapter.ts +3 -3
- package/src/sdk/adapters/query-adapter.ts +3 -3
- package/src/services/extensions/host.ts +13 -0
- package/src/store/constants.ts +33 -25
- package/src/store/index.ts +29 -8
- package/src/store/slices/clashSlice.ts +251 -0
- package/src/store/slices/listSlice.ts +6 -0
- package/src/store/slices/mutationSlice.ts +14 -6
- package/src/store/slices/searchSlice.ts +29 -3
- package/src/store/slices/visibilitySlice.test.ts +23 -5
- package/src/store/slices/visibilitySlice.ts +18 -8
- package/src/utils/nativeSpatialDataStore.ts +6 -0
- package/src/utils/serverDataModel.test.ts +6 -0
- package/src/utils/serverDataModel.ts +7 -0
- package/dist/assets/geometry.worker-Cyn5BybV.js +0 -1
- package/dist/assets/ifc-lite_bg-ksLBP5cA.wasm +0 -0
- package/dist/assets/index-Bws3UAkj.css +0 -1
- package/dist/assets/raw-R2QfzPAR.js +0 -1
- package/dist/assets/server-client-Ctk8_Bof.js +0 -626
|
@@ -0,0 +1,72 @@
|
|
|
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
|
+
* Status / control strip above the results table. Shows the active grouping
|
|
7
|
+
* and sum columns as removable chips, plus expand/collapse and live totals —
|
|
8
|
+
* the connective tissue between the table and the list definition.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { Group, Sigma, X, ChevronsDownUp, ChevronsUpDown } from 'lucide-react';
|
|
12
|
+
import { cn } from '@/lib/utils';
|
|
13
|
+
|
|
14
|
+
interface ListGroupingBarProps {
|
|
15
|
+
groupLabel: string | null;
|
|
16
|
+
sums: { id: string; label: string }[];
|
|
17
|
+
groupCount: number;
|
|
18
|
+
count: number;
|
|
19
|
+
allExpanded: boolean;
|
|
20
|
+
onClearGroup: () => void;
|
|
21
|
+
onRemoveSum: (id: string) => void;
|
|
22
|
+
onToggleExpandAll: () => void;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function Chip({ icon, children, onRemove }: { icon: React.ReactNode; children: React.ReactNode; onRemove: () => void }) {
|
|
26
|
+
return (
|
|
27
|
+
<span className="inline-flex items-center gap-1 rounded-full border border-primary/30 bg-primary/10 py-0.5 pl-2 pr-1 text-[11px] font-medium text-foreground">
|
|
28
|
+
{icon}
|
|
29
|
+
<span className="max-w-[12rem] truncate">{children}</span>
|
|
30
|
+
<button
|
|
31
|
+
onClick={onRemove}
|
|
32
|
+
className="ml-0.5 rounded-full p-0.5 text-muted-foreground hover:bg-primary/20 hover:text-foreground"
|
|
33
|
+
aria-label="Remove"
|
|
34
|
+
>
|
|
35
|
+
<X className="h-3 w-3" />
|
|
36
|
+
</button>
|
|
37
|
+
</span>
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function ListGroupingBar({
|
|
42
|
+
groupLabel, sums, groupCount, count, allExpanded,
|
|
43
|
+
onClearGroup, onRemoveSum, onToggleExpandAll,
|
|
44
|
+
}: ListGroupingBarProps) {
|
|
45
|
+
const grouped = groupLabel !== null;
|
|
46
|
+
return (
|
|
47
|
+
<div className="flex flex-wrap items-center gap-1.5 border-b bg-muted/30 px-3 py-1.5 text-xs">
|
|
48
|
+
{grouped && (
|
|
49
|
+
<button
|
|
50
|
+
onClick={onToggleExpandAll}
|
|
51
|
+
className="mr-0.5 inline-flex items-center gap-1 rounded-md px-1.5 py-0.5 text-[11px] text-muted-foreground hover:bg-muted hover:text-foreground"
|
|
52
|
+
title={allExpanded ? 'Collapse all groups' : 'Expand all groups'}
|
|
53
|
+
>
|
|
54
|
+
{allExpanded ? <ChevronsDownUp className="h-3.5 w-3.5" /> : <ChevronsUpDown className="h-3.5 w-3.5" />}
|
|
55
|
+
</button>
|
|
56
|
+
)}
|
|
57
|
+
|
|
58
|
+
{grouped
|
|
59
|
+
? <Chip icon={<Group className="h-3 w-3 text-primary" />} onRemove={onClearGroup}>Grouped by {groupLabel}</Chip>
|
|
60
|
+
: <span className="text-muted-foreground">No grouping — use a column's <span className="font-medium text-foreground">⋮</span> menu to group or sum</span>}
|
|
61
|
+
|
|
62
|
+
{sums.map((s) => (
|
|
63
|
+
<Chip key={s.id} icon={<Sigma className="h-3 w-3 text-primary" />} onRemove={() => onRemoveSum(s.id)}>{s.label}</Chip>
|
|
64
|
+
))}
|
|
65
|
+
|
|
66
|
+
<span className={cn('ml-auto whitespace-nowrap font-medium text-muted-foreground')}>
|
|
67
|
+
{grouped && <>{groupCount.toLocaleString()} group{groupCount === 1 ? '' : 's'} · </>}
|
|
68
|
+
{count.toLocaleString()} element{count === 1 ? '' : 's'}
|
|
69
|
+
</span>
|
|
70
|
+
</div>
|
|
71
|
+
);
|
|
72
|
+
}
|
|
@@ -34,12 +34,14 @@ import { useViewerStore } from '@/store';
|
|
|
34
34
|
import { useIfc } from '@/hooks/useIfc';
|
|
35
35
|
import {
|
|
36
36
|
executeList,
|
|
37
|
+
summariseListRows,
|
|
37
38
|
LIST_PRESETS,
|
|
38
39
|
importListDefinition,
|
|
39
40
|
exportListDefinition,
|
|
40
41
|
createListDataProvider,
|
|
41
42
|
} from '@/lib/lists';
|
|
42
|
-
import type { ListDefinition, ListResult, ListDataProvider } from '@/lib/lists';
|
|
43
|
+
import type { ListDefinition, ListResult, ListDataProvider, ListGrouping } from '@/lib/lists';
|
|
44
|
+
import type { IfcDataStore } from '@ifc-lite/parser';
|
|
43
45
|
import { ListBuilder } from './ListBuilder';
|
|
44
46
|
import { ListResultsTable } from './ListResultsTable';
|
|
45
47
|
|
|
@@ -64,6 +66,17 @@ export function ListPanel({ onClose }: ListPanelProps) {
|
|
|
64
66
|
const setActiveListId = useViewerStore((s) => s.setActiveListId);
|
|
65
67
|
const setListResult = useViewerStore((s) => s.setListResult);
|
|
66
68
|
const setListExecuting = useViewerStore((s) => s.setListExecuting);
|
|
69
|
+
const pendingListDraft = useViewerStore((s) => s.pendingListDraft);
|
|
70
|
+
const setPendingListDraft = useViewerStore((s) => s.setPendingListDraft);
|
|
71
|
+
|
|
72
|
+
// A draft handed off from "Create list" (search filter) opens straight into
|
|
73
|
+
// the builder for column configuration, then is cleared so it fires once.
|
|
74
|
+
React.useEffect(() => {
|
|
75
|
+
if (!pendingListDraft) return;
|
|
76
|
+
setEditingList(pendingListDraft);
|
|
77
|
+
setView('builder');
|
|
78
|
+
setPendingListDraft(null);
|
|
79
|
+
}, [pendingListDraft, setPendingListDraft]);
|
|
67
80
|
|
|
68
81
|
const importInputRef = React.useRef<HTMLInputElement>(null);
|
|
69
82
|
|
|
@@ -71,21 +84,22 @@ export function ListPanel({ onClose }: ListPanelProps) {
|
|
|
71
84
|
// arrays can never drift out of alignment (skipping a model without
|
|
72
85
|
// an ifcDataStore must not shift every later model's provider index).
|
|
73
86
|
const modelProviderPairs = useMemo(() => {
|
|
74
|
-
const pairs: Array<{ modelId: string; provider: ListDataProvider }> = [];
|
|
87
|
+
const pairs: Array<{ modelId: string; provider: ListDataProvider; store: IfcDataStore }> = [];
|
|
75
88
|
if (models.size > 0) {
|
|
76
89
|
for (const [modelId, model] of models) {
|
|
77
90
|
// Skip native-metadata models — they don't have a parsed
|
|
78
91
|
// IfcDataStore, so the list provider can't query them.
|
|
79
92
|
if (!model.ifcDataStore) continue;
|
|
80
|
-
pairs.push({ modelId, provider: createListDataProvider(model.ifcDataStore) });
|
|
93
|
+
pairs.push({ modelId, provider: createListDataProvider(model.ifcDataStore), store: model.ifcDataStore });
|
|
81
94
|
}
|
|
82
95
|
} else if (ifcDataStore) {
|
|
83
|
-
pairs.push({ modelId: 'default', provider: createListDataProvider(ifcDataStore) });
|
|
96
|
+
pairs.push({ modelId: 'default', provider: createListDataProvider(ifcDataStore), store: ifcDataStore });
|
|
84
97
|
}
|
|
85
98
|
return pairs;
|
|
86
99
|
}, [models, ifcDataStore]);
|
|
87
100
|
|
|
88
101
|
const allProviders = useMemo(() => modelProviderPairs.map((p) => p.provider), [modelProviderPairs]);
|
|
102
|
+
const allStores = useMemo(() => modelProviderPairs.map((p) => p.store), [modelProviderPairs]);
|
|
89
103
|
|
|
90
104
|
const hasData = allProviders.length > 0;
|
|
91
105
|
|
|
@@ -107,11 +121,17 @@ export function ListPanel({ onClose }: ListPanelProps) {
|
|
|
107
121
|
const allRows = resultParts.flatMap(r => r.rows);
|
|
108
122
|
const totalTime = resultParts.reduce((sum, r) => sum + r.executionTime, 0);
|
|
109
123
|
|
|
124
|
+
// Re-derive groups/summary over the merged rows so grouping works
|
|
125
|
+
// across federated models (and isn't dropped on the merge).
|
|
126
|
+
const { groups, summary } = summariseListRows(definition, allRows);
|
|
127
|
+
|
|
110
128
|
setListResult({
|
|
111
129
|
columns: definition.columns,
|
|
112
130
|
rows: allRows,
|
|
113
131
|
totalCount: allRows.length,
|
|
114
132
|
executionTime: totalTime,
|
|
133
|
+
groups,
|
|
134
|
+
summary,
|
|
115
135
|
});
|
|
116
136
|
setView('results');
|
|
117
137
|
} catch (err) {
|
|
@@ -164,6 +184,24 @@ export function ListPanel({ onClose }: ListPanelProps) {
|
|
|
164
184
|
}
|
|
165
185
|
}, [editingList]);
|
|
166
186
|
|
|
187
|
+
// Grouping/summing changed directly from the results table: update the
|
|
188
|
+
// executed definition (so Settings reflects it), persist if it's saved, and
|
|
189
|
+
// re-derive groups/summary over the current rows for a consistent result.
|
|
190
|
+
const handleGroupingFromTable = useCallback((grouping: ListGrouping | undefined) => {
|
|
191
|
+
const def = editingList;
|
|
192
|
+
if (!def) return;
|
|
193
|
+
const next: ListDefinition = { ...def, grouping };
|
|
194
|
+
setEditingList(next);
|
|
195
|
+
if (listDefinitions.some((d) => d.id === def.id)) {
|
|
196
|
+
updateListDefinition(def.id, { grouping });
|
|
197
|
+
}
|
|
198
|
+
const current = useViewerStore.getState().listResult;
|
|
199
|
+
if (current) {
|
|
200
|
+
const summ = summariseListRows(next, current.rows);
|
|
201
|
+
setListResult({ ...current, groups: summ.groups, summary: summ.summary });
|
|
202
|
+
}
|
|
203
|
+
}, [editingList, listDefinitions, updateListDefinition, setListResult]);
|
|
204
|
+
|
|
167
205
|
const handleImport = useCallback(async (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
168
206
|
const file = e.target.files?.[0];
|
|
169
207
|
if (!file) return;
|
|
@@ -251,6 +289,7 @@ export function ListPanel({ onClose }: ListPanelProps) {
|
|
|
251
289
|
{view === 'builder' && hasData && (
|
|
252
290
|
<ListBuilder
|
|
253
291
|
providers={allProviders}
|
|
292
|
+
stores={allStores}
|
|
254
293
|
initial={editingList}
|
|
255
294
|
onSave={handleSaveList}
|
|
256
295
|
onCancel={() => setView('library')}
|
|
@@ -259,7 +298,12 @@ export function ListPanel({ onClose }: ListPanelProps) {
|
|
|
259
298
|
)}
|
|
260
299
|
|
|
261
300
|
{view === 'results' && listResult && (
|
|
262
|
-
<ListResultsTable
|
|
301
|
+
<ListResultsTable
|
|
302
|
+
result={listResult}
|
|
303
|
+
listName={editingList?.name}
|
|
304
|
+
grouping={editingList?.grouping}
|
|
305
|
+
onGroupingChange={handleGroupingFromTable}
|
|
306
|
+
/>
|
|
263
307
|
)}
|
|
264
308
|
|
|
265
309
|
{/* Hidden import input */}
|