@abraca/nuxt 2.3.0 → 2.4.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/dist/module.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "compatibility": {
5
5
  "nuxt": ">=4.0.0"
6
6
  },
7
- "version": "2.3.0",
7
+ "version": "2.4.0",
8
8
  "builder": {
9
9
  "@nuxt/module-builder": "1.0.2",
10
10
  "unbuild": "3.6.1"
@@ -1,8 +1,6 @@
1
1
  <script setup>
2
2
  import { ref, computed, shallowRef, watch, onMounted, onUnmounted } from "vue";
3
- import { Raycaster, Vector2 } from "three";
4
- import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
5
- import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader.js";
3
+ import { loadThree } from "../../utils/loadThree";
6
4
  const props = defineProps({
7
5
  url: { type: String, required: true },
8
6
  objectId: { type: String, required: true },
@@ -27,9 +25,19 @@ const panY = ref(0);
27
25
  const ZOOM_MIN = 1.5;
28
26
  const ZOOM_MAX = 12;
29
27
  const loadedModel = shallowRef(null);
28
+ const viewerUnavailable = ref(false);
30
29
  async function loadGLTF(url) {
31
- const loader = new GLTFLoader();
32
- const dracoLoader = new DRACOLoader();
30
+ const three = await loadThree();
31
+ if (!three) {
32
+ viewerUnavailable.value = true;
33
+ return;
34
+ }
35
+ if (!raycaster) {
36
+ raycaster = new three.Raycaster();
37
+ ndc = new three.Vector2();
38
+ }
39
+ const loader = new three.GLTFLoader();
40
+ const dracoLoader = new three.DRACOLoader();
33
41
  dracoLoader.setDecoderPath("https://www.gstatic.com/draco/versioned/decoders/1.5.7/");
34
42
  loader.setDRACOLoader(dracoLoader);
35
43
  const gltf = await loader.loadAsync(url);
@@ -53,12 +61,12 @@ defineExpose({ autoRotateEnabled, wireframe, resetView });
53
61
  const camPos = computed(() => [panX.value, 1.5 + panY.value, zoom.value]);
54
62
  const camTarget = computed(() => [panX.value, panY.value, 0]);
55
63
  const sceneCamera = shallowRef(null);
56
- const raycaster = new Raycaster();
57
- const ndc = new Vector2();
64
+ let raycaster = null;
65
+ let ndc = null;
58
66
  const wrapEl = ref(null);
59
67
  function hitsModel(ev) {
60
68
  const cam = sceneCamera.value;
61
- if (!cam || !wrapEl.value || !loadedModel.value) return true;
69
+ if (!cam || !wrapEl.value || !loadedModel.value || !raycaster) return true;
62
70
  const rect = wrapEl.value.getBoundingClientRect();
63
71
  ndc.x = (ev.clientX - rect.left) / rect.width * 2 - 1;
64
72
  ndc.y = -((ev.clientY - rect.top) / rect.height) * 2 + 1;
@@ -153,7 +161,16 @@ onMounted(() => {
153
161
  @pointerdown="onPointerDown"
154
162
  @wheel.prevent="onWheel"
155
163
  >
156
- <TresCanvas :clear-color="clearColor">
164
+ <div
165
+ v-if="viewerUnavailable"
166
+ class="glb-fallback"
167
+ >
168
+ 3D preview unavailable — install <code>three</code> to view this model.
169
+ </div>
170
+ <TresCanvas
171
+ v-else
172
+ :clear-color="clearColor"
173
+ >
157
174
  <TresPerspectiveCamera
158
175
  :position="camPos"
159
176
  :look-at="camTarget"
@@ -183,5 +200,5 @@ onMounted(() => {
183
200
  </template>
184
201
 
185
202
  <style scoped>
186
- .glb-wrap{cursor:grab;height:100%;touch-action:none;-webkit-user-select:none;-moz-user-select:none;user-select:none;width:100%}.glb-wrap.dragging{cursor:grabbing}
203
+ .glb-wrap{cursor:grab;height:100%;touch-action:none;-webkit-user-select:none;-moz-user-select:none;user-select:none;width:100%}.glb-wrap.dragging{cursor:grabbing}.glb-fallback{align-items:center;display:flex;font-size:.875rem;height:100%;justify-content:center;opacity:.7;padding:1rem;text-align:center;width:100%}.glb-fallback code{font-family:monospace;opacity:.85}
187
204
  </style>
@@ -18,27 +18,27 @@ type __VLS_Props = {
18
18
  labels?: Partial<AbracadabraLocale['renderers']['sheets']>;
19
19
  };
20
20
  declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
21
- toggleBold: () => any;
22
- toggleItalic: () => any;
23
21
  addColumn: () => any;
24
22
  addRow: () => any;
25
23
  "update:editValue": (value: string) => any;
26
24
  commitFormulaBar: () => any;
27
25
  cancelFormulaBar: () => any;
28
26
  startEditFromBar: () => any;
27
+ toggleBold: () => any;
28
+ toggleItalic: () => any;
29
29
  setTextColor: (color: string | undefined) => any;
30
30
  setBgColor: (color: string | undefined) => any;
31
31
  setAlign: (align: string) => any;
32
32
  setNumberFormat: (format: string | undefined) => any;
33
33
  }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
34
- onToggleBold?: (() => any) | undefined;
35
- onToggleItalic?: (() => any) | undefined;
36
34
  onAddColumn?: (() => any) | undefined;
37
35
  onAddRow?: (() => any) | undefined;
38
36
  "onUpdate:editValue"?: ((value: string) => any) | undefined;
39
37
  onCommitFormulaBar?: (() => any) | undefined;
40
38
  onCancelFormulaBar?: (() => any) | undefined;
41
39
  onStartEditFromBar?: (() => any) | undefined;
40
+ onToggleBold?: (() => any) | undefined;
41
+ onToggleItalic?: (() => any) | undefined;
42
42
  onSetTextColor?: ((color: string | undefined) => any) | undefined;
43
43
  onSetBgColor?: ((color: string | undefined) => any) | undefined;
44
44
  onSetAlign?: ((align: string) => any) | undefined;
@@ -18,27 +18,27 @@ type __VLS_Props = {
18
18
  labels?: Partial<AbracadabraLocale['renderers']['sheets']>;
19
19
  };
20
20
  declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
21
- toggleBold: () => any;
22
- toggleItalic: () => any;
23
21
  addColumn: () => any;
24
22
  addRow: () => any;
25
23
  "update:editValue": (value: string) => any;
26
24
  commitFormulaBar: () => any;
27
25
  cancelFormulaBar: () => any;
28
26
  startEditFromBar: () => any;
27
+ toggleBold: () => any;
28
+ toggleItalic: () => any;
29
29
  setTextColor: (color: string | undefined) => any;
30
30
  setBgColor: (color: string | undefined) => any;
31
31
  setAlign: (align: string) => any;
32
32
  setNumberFormat: (format: string | undefined) => any;
33
33
  }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
34
- onToggleBold?: (() => any) | undefined;
35
- onToggleItalic?: (() => any) | undefined;
36
34
  onAddColumn?: (() => any) | undefined;
37
35
  onAddRow?: (() => any) | undefined;
38
36
  "onUpdate:editValue"?: ((value: string) => any) | undefined;
39
37
  onCommitFormulaBar?: (() => any) | undefined;
40
38
  onCancelFormulaBar?: (() => any) | undefined;
41
39
  onStartEditFromBar?: (() => any) | undefined;
40
+ onToggleBold?: (() => any) | undefined;
41
+ onToggleItalic?: (() => any) | undefined;
42
42
  onSetTextColor?: ((color: string | undefined) => any) | undefined;
43
43
  onSetBgColor?: ((color: string | undefined) => any) | undefined;
44
44
  onSetAlign?: ((align: string) => any) | undefined;
@@ -14,13 +14,13 @@ type __VLS_Props = {
14
14
  extraItems?: DropdownMenuItem[][];
15
15
  };
16
16
  declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
17
- logout: () => any;
18
17
  "open-settings": () => any;
19
18
  "open-account": () => any;
19
+ logout: () => any;
20
20
  }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
21
- onLogout?: (() => any) | undefined;
22
21
  "onOpen-settings"?: (() => any) | undefined;
23
22
  "onOpen-account"?: (() => any) | undefined;
23
+ onLogout?: (() => any) | undefined;
24
24
  }>, {
25
25
  color: string;
26
26
  collapsed: boolean;
@@ -14,13 +14,13 @@ type __VLS_Props = {
14
14
  extraItems?: DropdownMenuItem[][];
15
15
  };
16
16
  declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
17
- logout: () => any;
18
17
  "open-settings": () => any;
19
18
  "open-account": () => any;
19
+ logout: () => any;
20
20
  }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
21
- onLogout?: (() => any) | undefined;
22
21
  "onOpen-settings"?: (() => any) | undefined;
23
22
  "onOpen-account"?: (() => any) | undefined;
23
+ onLogout?: (() => any) | undefined;
24
24
  }>, {
25
25
  color: string;
26
26
  collapsed: boolean;
@@ -0,0 +1,18 @@
1
+ /**
2
+ * loadThree — try-import the `three` peer dependency (+ GLTF/DRACO loaders).
3
+ *
4
+ * Returns the pieces <AFileGlbViewer> needs if the consumer installed
5
+ * `three`, or `null` otherwise. Cached after the first call; the first
6
+ * failed lookup logs once in dev. Mirrors loadKatex / loadCodeMirror so
7
+ * `three` stays a truly optional peer and never enters the build graph —
8
+ * a consumer that doesn't render 3D model blocks never needs `three`.
9
+ *
10
+ * Apps that want GLB previews install: `pnpm add three`.
11
+ */
12
+ export interface ThreeBundle {
13
+ Raycaster: any;
14
+ Vector2: any;
15
+ GLTFLoader: any;
16
+ DRACOLoader: any;
17
+ }
18
+ export declare function loadThree(): Promise<ThreeBundle | null>;
@@ -0,0 +1,46 @@
1
+ let cache = null;
2
+ let loading = null;
3
+ let warnedMissing = false;
4
+ export async function loadThree() {
5
+ if (cache) return cache;
6
+ if (loading) return loading;
7
+ loading = (async () => {
8
+ try {
9
+ const threePkg = "three";
10
+ const gltfPkg = "three/examples/jsm/loaders/GLTFLoader.js";
11
+ const dracoPkg = "three/examples/jsm/loaders/DRACOLoader.js";
12
+ const [three, gltf, draco] = await Promise.all([
13
+ import(
14
+ /* @vite-ignore */
15
+ threePkg
16
+ ),
17
+ import(
18
+ /* @vite-ignore */
19
+ gltfPkg
20
+ ),
21
+ import(
22
+ /* @vite-ignore */
23
+ dracoPkg
24
+ )
25
+ ]);
26
+ cache = {
27
+ Raycaster: three.Raycaster,
28
+ Vector2: three.Vector2,
29
+ GLTFLoader: gltf.GLTFLoader,
30
+ DRACOLoader: draco.DRACOLoader
31
+ };
32
+ return cache;
33
+ } catch {
34
+ if (import.meta.dev && !warnedMissing) {
35
+ warnedMissing = true;
36
+ console.warn(
37
+ "[abracadabra] GLB viewer: `three` peer dependency not installed. 3D model blocks render a fallback. Install with `pnpm add three`."
38
+ );
39
+ }
40
+ return null;
41
+ } finally {
42
+ loading = null;
43
+ }
44
+ })();
45
+ return loading;
46
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abraca/nuxt",
3
- "version": "2.3.0",
3
+ "version": "2.4.0",
4
4
  "description": "First-class Nuxt module for the Abracadabra CRDT collaboration platform",
5
5
  "repository": "abracadabra/abracadabra-nuxt",
6
6
  "license": "MIT",
@@ -32,6 +32,10 @@
32
32
  "nanoevents": "^9.1.0"
33
33
  },
34
34
  "peerDependencies": {
35
+ "@abraca/dabra": "^2.3.0",
36
+ "@abraca/convert": "^2.3.0",
37
+ "@abraca/plugin": "^2.3.0",
38
+ "@abraca/schema": "^2.3.0",
35
39
  "@codemirror/autocomplete": "^6.18.0",
36
40
  "@codemirror/commands": "^6.7.0",
37
41
  "@codemirror/lang-css": "^6.3.1",
@@ -72,17 +76,17 @@
72
76
  "lowlight": "^3.0.0",
73
77
  "mapbox-gl": "^3.0.0",
74
78
  "nuxt": "^4.0.0",
79
+ "three": "^0.184.0",
75
80
  "vue": "^3.4.0",
76
- "yjs": "^13.0.0",
77
- "@abraca/dabra": "2.3.0",
78
- "@abraca/plugin": "2.3.0",
79
- "@abraca/convert": "2.3.0",
80
- "@abraca/schema": "2.3.0"
81
+ "yjs": "^13.0.0"
81
82
  },
82
83
  "peerDependenciesMeta": {
83
84
  "@abraca/schema": {
84
85
  "optional": true
85
86
  },
87
+ "three": {
88
+ "optional": true
89
+ },
86
90
  "@codemirror/autocomplete": {
87
91
  "optional": true
88
92
  },
@@ -133,6 +137,9 @@
133
137
  }
134
138
  },
135
139
  "devDependencies": {
140
+ "@abraca/dabra": "^2.3.0",
141
+ "@abraca/convert": "^2.3.0",
142
+ "@abraca/plugin": "^2.3.0",
136
143
  "@codemirror/autocomplete": "^6.18.0",
137
144
  "@codemirror/commands": "^6.7.0",
138
145
  "@codemirror/lang-css": "^6.3.1",
@@ -172,10 +179,7 @@
172
179
  "vue": "^3.5.34",
173
180
  "vue-tsc": "^3.2.8",
174
181
  "ws": "^8.20.0",
175
- "yjs": "^13.6.30",
176
- "@abraca/dabra": "2.3.0",
177
- "@abraca/plugin": "2.3.0",
178
- "@abraca/convert": "2.3.0"
182
+ "yjs": "^13.6.30"
179
183
  },
180
184
  "scripts": {
181
185
  "dev": "npm run dev:prepare && nuxt dev playground",