@ifc-lite/viewer 1.14.2 → 1.14.3

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 (73) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/dist/assets/{Arrow.dom-CSgnLhN4.js → Arrow.dom-BgkZDIQm.js} +1 -1
  3. package/dist/assets/basketViewActivator-h_M3YbMW.js +1 -0
  4. package/dist/assets/{browser-qSKWrKQW.js → browser-CRQ0bPh1.js} +1 -1
  5. package/dist/assets/ifc-lite_bg-DyBKoGgk.wasm +0 -0
  6. package/dist/assets/{index-4Y4XaV8N.js → index-Be6XjVeM.js} +72324 -61561
  7. package/dist/assets/index-C4VVJRL-.js +229 -0
  8. package/dist/assets/index-DdwD4c-E.css +1 -0
  9. package/dist/assets/{native-bridge-CSFDsEkg.js → native-bridge-DtcJqlOi.js} +1 -1
  10. package/dist/assets/{wasm-bridge-Zf90ysEm.js → wasm-bridge-BJJVu9P2.js} +1 -1
  11. package/dist/index.html +2 -2
  12. package/package.json +21 -20
  13. package/src/App.tsx +17 -1
  14. package/src/components/viewer/BasketPresentationDock.tsx +8 -4
  15. package/src/components/viewer/ChatPanel.tsx +1402 -0
  16. package/src/components/viewer/CodeEditor.tsx +70 -4
  17. package/src/components/viewer/MainToolbar.tsx +1 -1
  18. package/src/components/viewer/ScriptPanel.tsx +351 -184
  19. package/src/components/viewer/UpgradePage.tsx +69 -0
  20. package/src/components/viewer/Viewport.tsx +23 -0
  21. package/src/components/viewer/chat/ChatMessage.tsx +144 -0
  22. package/src/components/viewer/chat/ExecutableCodeBlock.tsx +416 -0
  23. package/src/components/viewer/chat/ModelSelector.tsx +102 -0
  24. package/src/components/viewer/chat/renderTextContent.test.ts +23 -0
  25. package/src/components/viewer/chat/renderTextContent.ts +19 -0
  26. package/src/hooks/useIfcCache.ts +1 -2
  27. package/src/hooks/useSandbox.ts +122 -6
  28. package/src/index.css +10 -0
  29. package/src/lib/attachments.ts +46 -0
  30. package/src/lib/llm/ClerkChatSync.tsx +74 -0
  31. package/src/lib/llm/clerk-auth.ts +62 -0
  32. package/src/lib/llm/code-extractor.ts +50 -0
  33. package/src/lib/llm/context-builder.test.ts +18 -0
  34. package/src/lib/llm/context-builder.ts +305 -0
  35. package/src/lib/llm/free-models.test.ts +118 -0
  36. package/src/lib/llm/message-capabilities.test.ts +131 -0
  37. package/src/lib/llm/message-capabilities.ts +94 -0
  38. package/src/lib/llm/models.ts +197 -0
  39. package/src/lib/llm/repair-loop.test.ts +91 -0
  40. package/src/lib/llm/repair-loop.ts +76 -0
  41. package/src/lib/llm/script-diagnostics.ts +445 -0
  42. package/src/lib/llm/script-edit-ops.test.ts +399 -0
  43. package/src/lib/llm/script-edit-ops.ts +954 -0
  44. package/src/lib/llm/script-preflight.test.ts +513 -0
  45. package/src/lib/llm/script-preflight.ts +990 -0
  46. package/src/lib/llm/script-preservation.test.ts +128 -0
  47. package/src/lib/llm/script-preservation.ts +152 -0
  48. package/src/lib/llm/stream-client.test.ts +97 -0
  49. package/src/lib/llm/stream-client.ts +410 -0
  50. package/src/lib/llm/system-prompt.test.ts +181 -0
  51. package/src/lib/llm/system-prompt.ts +665 -0
  52. package/src/lib/llm/types.ts +150 -0
  53. package/src/lib/scripts/templates/bim-globals.d.ts +226 -7
  54. package/src/lib/scripts/templates/create-building.ts +12 -12
  55. package/src/main.tsx +10 -1
  56. package/src/sdk/adapters/export-adapter.test.ts +24 -0
  57. package/src/sdk/adapters/export-adapter.ts +40 -16
  58. package/src/sdk/adapters/files-adapter.ts +39 -0
  59. package/src/sdk/adapters/model-compat.ts +1 -1
  60. package/src/sdk/adapters/mutate-adapter.ts +20 -6
  61. package/src/sdk/adapters/mutation-view.ts +112 -0
  62. package/src/sdk/adapters/query-adapter.ts +100 -4
  63. package/src/sdk/local-backend.ts +4 -0
  64. package/src/store/index.ts +15 -1
  65. package/src/store/slices/chatSlice.test.ts +325 -0
  66. package/src/store/slices/chatSlice.ts +468 -0
  67. package/src/store/slices/scriptSlice.test.ts +75 -0
  68. package/src/store/slices/scriptSlice.ts +256 -9
  69. package/src/vite-env.d.ts +10 -0
  70. package/vite.config.ts +21 -2
  71. package/dist/assets/ifc-lite_bg-BOvNXJA_.wasm +0 -0
  72. package/dist/assets/index-ByrFvN5A.css +0 -1
  73. package/dist/assets/index-CN7qDq7G.js +0 -216
@@ -12,13 +12,14 @@
12
12
 
13
13
  import { useRef, useEffect } from 'react';
14
14
  import { EditorView, keymap, lineNumbers, highlightActiveLine, highlightActiveLineGutter, drawSelection } from '@codemirror/view';
15
- import { EditorState, Compartment } from '@codemirror/state';
15
+ import { EditorState, Compartment, Transaction } from '@codemirror/state';
16
16
  import { javascript } from '@codemirror/lang-javascript';
17
17
  import { autocompletion, type CompletionContext, type CompletionResult, type Completion } from '@codemirror/autocomplete';
18
- import { defaultKeymap, history, historyKeymap, indentWithTab } from '@codemirror/commands';
18
+ import { defaultKeymap, history, historyKeymap, indentWithTab, undo, redo, undoDepth, redoDepth } from '@codemirror/commands';
19
19
  import { syntaxHighlighting, defaultHighlightStyle, bracketMatching, indentOnInput } from '@codemirror/language';
20
20
  import { highlightSelectionMatches } from '@codemirror/search';
21
21
  import { NAMESPACE_SCHEMAS } from '@ifc-lite/sandbox/schema';
22
+ import type { ScriptEditorSelection, ScriptEditorTextChange } from '@/lib/llm/types';
22
23
 
23
24
  /** Shared structural styles (mode-agnostic) */
24
25
  const baseTheme = EditorView.theme({
@@ -201,20 +202,44 @@ function bimCompletions(context: CompletionContext): CompletionResult | null {
201
202
  interface CodeEditorProps {
202
203
  value: string;
203
204
  onChange: (value: string) => void;
205
+ onSelectionChange?: (selection: ScriptEditorSelection) => void;
206
+ onHistoryChange?: (canUndo: boolean, canRedo: boolean) => void;
207
+ registerApplyAdapter?: ((adapter: {
208
+ apply: (
209
+ nextContent: string,
210
+ selection: ScriptEditorSelection,
211
+ options?: { userEvent?: string; changes?: ScriptEditorTextChange[] },
212
+ ) => void;
213
+ undo: () => void;
214
+ redo: () => void;
215
+ } | null) => void);
204
216
  onRun?: () => void;
205
217
  onSave?: () => void;
206
218
  className?: string;
207
219
  }
208
220
 
209
- export function CodeEditor({ value, onChange, onRun, onSave, className }: CodeEditorProps) {
221
+ export function CodeEditor({
222
+ value,
223
+ onChange,
224
+ onSelectionChange,
225
+ onHistoryChange,
226
+ registerApplyAdapter,
227
+ onRun,
228
+ onSave,
229
+ className,
230
+ }: CodeEditorProps) {
210
231
  const containerRef = useRef<HTMLDivElement>(null);
211
232
  const viewRef = useRef<EditorView | null>(null);
212
233
  const onChangeRef = useRef(onChange);
234
+ const onSelectionChangeRef = useRef(onSelectionChange);
235
+ const onHistoryChangeRef = useRef(onHistoryChange);
213
236
  const onRunRef = useRef(onRun);
214
237
  const onSaveRef = useRef(onSave);
215
238
 
216
239
  // Keep callback refs up to date without recreating the editor
217
240
  onChangeRef.current = onChange;
241
+ onSelectionChangeRef.current = onSelectionChange;
242
+ onHistoryChangeRef.current = onHistoryChange;
218
243
  onRunRef.current = onRun;
219
244
  onSaveRef.current = onSave;
220
245
 
@@ -239,6 +264,16 @@ export function CodeEditor({ value, onChange, onRun, onSave, className }: CodeEd
239
264
  if (update.docChanged) {
240
265
  onChangeRef.current(update.state.doc.toString());
241
266
  }
267
+ if (update.selectionSet || update.docChanged) {
268
+ const main = update.state.selection.main;
269
+ onSelectionChangeRef.current?.({ from: main.from, to: main.to });
270
+ }
271
+ if (update.docChanged) {
272
+ onHistoryChangeRef.current?.(
273
+ undoDepth(update.state) > 0,
274
+ redoDepth(update.state) > 0,
275
+ );
276
+ }
242
277
  });
243
278
 
244
279
  const state = EditorState.create({
@@ -274,6 +309,35 @@ export function CodeEditor({ value, onChange, onRun, onSave, className }: CodeEd
274
309
  });
275
310
 
276
311
  viewRef.current = view;
312
+ const initialSelection = view.state.selection.main;
313
+ onSelectionChangeRef.current?.({ from: initialSelection.from, to: initialSelection.to });
314
+ onHistoryChangeRef.current?.(undoDepth(view.state) > 0, redoDepth(view.state) > 0);
315
+ registerApplyAdapter?.({
316
+ apply: (nextContent, selection, options) => {
317
+ const active = viewRef.current;
318
+ if (!active) return;
319
+ const safeFrom = Math.max(0, Math.min(selection.from, nextContent.length));
320
+ const safeTo = Math.max(safeFrom, Math.min(selection.to, nextContent.length));
321
+ const changes = options?.changes && options.changes.length > 0
322
+ ? options.changes
323
+ : [{ from: 0, to: active.state.doc.length, insert: nextContent }];
324
+ active.dispatch({
325
+ changes,
326
+ selection: { anchor: safeFrom, head: safeTo },
327
+ annotations: options?.userEvent ? [Transaction.userEvent.of(options.userEvent)] : undefined,
328
+ });
329
+ },
330
+ undo: () => {
331
+ const active = viewRef.current;
332
+ if (!active) return;
333
+ undo(active);
334
+ },
335
+ redo: () => {
336
+ const active = viewRef.current;
337
+ if (!active) return;
338
+ redo(active);
339
+ },
340
+ });
277
341
 
278
342
  // Watch for light/dark mode changes on <html> class
279
343
  const observer = new MutationObserver(() => {
@@ -282,13 +346,15 @@ export function CodeEditor({ value, onChange, onRun, onSave, className }: CodeEd
282
346
  observer.observe(document.documentElement, { attributes: true, attributeFilter: ['class'] });
283
347
 
284
348
  return () => {
349
+ onHistoryChangeRef.current?.(false, false);
350
+ registerApplyAdapter?.(null);
285
351
  observer.disconnect();
286
352
  view.destroy();
287
353
  viewRef.current = null;
288
354
  };
289
355
  // Only create once — value is set via initial doc
290
356
  // eslint-disable-next-line react-hooks/exhaustive-deps
291
- }, []);
357
+ }, [registerApplyAdapter]);
292
358
 
293
359
  // Sync external value changes into the editor (e.g., loading a different script)
294
360
  const lastExternalValue = useRef(value);
@@ -680,7 +680,7 @@ export function MainToolbar({ onShowShortcuts }: MainToolbarProps = {} as MainTo
680
680
  size="icon-sm"
681
681
  onClick={(e) => {
682
682
  (e.currentTarget as HTMLButtonElement).blur();
683
- // Close script panel (bottom-panel exclusivity)
683
+ // Close other bottom panels (bottom-panel exclusivity)
684
684
  useViewerStore.getState().setScriptPanelVisible(false);
685
685
  if (!listPanelVisible) {
686
686
  setRightPanelCollapsed(false);