@abraca/nuxt 2.0.11 → 2.3.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.
Files changed (130) hide show
  1. package/dist/module.d.mts +68 -0
  2. package/dist/module.json +1 -1
  3. package/dist/module.mjs +99 -4
  4. package/dist/runtime/components/ACodeEditor.d.vue.ts +26 -0
  5. package/dist/runtime/components/ACodeEditor.vue +268 -0
  6. package/dist/runtime/components/ACodeEditor.vue.d.ts +26 -0
  7. package/dist/runtime/components/ADocumentTree.vue +52 -20
  8. package/dist/runtime/components/AEditor.d.vue.ts +20 -13
  9. package/dist/runtime/components/AEditor.vue +55 -2
  10. package/dist/runtime/components/AEditor.vue.d.ts +20 -13
  11. package/dist/runtime/components/ANodePanel.vue +64 -60
  12. package/dist/runtime/components/ANotificationBell.d.vue.ts +1 -1
  13. package/dist/runtime/components/ANotificationBell.vue.d.ts +1 -1
  14. package/dist/runtime/components/ASpaceFormModal.d.vue.ts +2 -2
  15. package/dist/runtime/components/ASpaceFormModal.vue.d.ts +2 -2
  16. package/dist/runtime/components/aware/AMedia.d.vue.ts +1 -1
  17. package/dist/runtime/components/aware/AMedia.vue.d.ts +1 -1
  18. package/dist/runtime/components/aware/APresenceBlobs.d.vue.ts +29 -1
  19. package/dist/runtime/components/aware/APresenceBlobs.vue +54 -8
  20. package/dist/runtime/components/aware/APresenceBlobs.vue.d.ts +29 -1
  21. package/dist/runtime/components/aware/APresenceCursors.d.vue.ts +11 -0
  22. package/dist/runtime/components/aware/APresenceCursors.vue +74 -9
  23. package/dist/runtime/components/aware/APresenceCursors.vue.d.ts +11 -0
  24. package/dist/runtime/components/aware/AToggleGroup.d.vue.ts +28 -13
  25. package/dist/runtime/components/aware/AToggleGroup.vue +56 -20
  26. package/dist/runtime/components/aware/AToggleGroup.vue.d.ts +28 -13
  27. package/dist/runtime/components/docs/ADocsNavigation.d.vue.ts +1 -1
  28. package/dist/runtime/components/docs/ADocsNavigation.vue.d.ts +1 -1
  29. package/dist/runtime/components/docs/ADocsSearch.d.vue.ts +1 -1
  30. package/dist/runtime/components/docs/ADocsSearch.vue.d.ts +1 -1
  31. package/dist/runtime/components/docs/ADocsSearchButton.d.vue.ts +1 -1
  32. package/dist/runtime/components/docs/ADocsSearchButton.vue.d.ts +1 -1
  33. package/dist/runtime/components/docs/ADocsToc.d.vue.ts +2 -2
  34. package/dist/runtime/components/docs/ADocsToc.vue.d.ts +2 -2
  35. package/dist/runtime/components/editor/AEditorRedoButton.d.vue.ts +1 -1
  36. package/dist/runtime/components/editor/AEditorRedoButton.vue.d.ts +1 -1
  37. package/dist/runtime/components/editor/AEditorUndoButton.d.vue.ts +1 -1
  38. package/dist/runtime/components/editor/AEditorUndoButton.vue.d.ts +1 -1
  39. package/dist/runtime/components/editor/ANodeInlineLabel.d.vue.ts +1 -1
  40. package/dist/runtime/components/editor/ANodeInlineLabel.vue.d.ts +1 -1
  41. package/dist/runtime/components/registry/APluginBrowser.d.vue.ts +23 -0
  42. package/dist/runtime/components/registry/APluginBrowser.vue +155 -0
  43. package/dist/runtime/components/registry/APluginBrowser.vue.d.ts +23 -0
  44. package/dist/runtime/components/registry/APluginCapabilityDialog.d.vue.ts +17 -0
  45. package/dist/runtime/components/registry/APluginCapabilityDialog.vue +159 -0
  46. package/dist/runtime/components/registry/APluginCapabilityDialog.vue.d.ts +17 -0
  47. package/dist/runtime/components/registry/APluginCard.d.vue.ts +20 -0
  48. package/dist/runtime/components/registry/APluginCard.vue +91 -0
  49. package/dist/runtime/components/registry/APluginCard.vue.d.ts +20 -0
  50. package/dist/runtime/components/registry/APluginDetail.d.vue.ts +18 -0
  51. package/dist/runtime/components/registry/APluginDetail.vue +252 -0
  52. package/dist/runtime/components/registry/APluginDetail.vue.d.ts +18 -0
  53. package/dist/runtime/components/renderers/ACodeRenderer.d.vue.ts +15 -0
  54. package/dist/runtime/components/renderers/ACodeRenderer.vue +68 -0
  55. package/dist/runtime/components/renderers/ACodeRenderer.vue.d.ts +15 -0
  56. package/dist/runtime/components/renderers/AGraphRenderer.vue +416 -120
  57. package/dist/runtime/components/renderers/AProseRenderer.d.vue.ts +2 -2
  58. package/dist/runtime/components/renderers/AProseRenderer.vue.d.ts +2 -2
  59. package/dist/runtime/components/shell/ABreadcrumbForDoc.d.vue.ts +11 -0
  60. package/dist/runtime/components/shell/ABreadcrumbForDoc.vue +16 -0
  61. package/dist/runtime/components/shell/ABreadcrumbForDoc.vue.d.ts +11 -0
  62. package/dist/runtime/components/shell/ASettingsSection.d.vue.ts +35 -0
  63. package/dist/runtime/components/shell/ASettingsSection.vue +26 -0
  64. package/dist/runtime/components/shell/ASettingsSection.vue.d.ts +35 -0
  65. package/dist/runtime/components/shell/ASidebar.d.vue.ts +1 -1
  66. package/dist/runtime/components/shell/ASidebar.vue.d.ts +1 -1
  67. package/dist/runtime/components/shell/AUserMenu.d.vue.ts +5 -2
  68. package/dist/runtime/components/shell/AUserMenu.vue +4 -0
  69. package/dist/runtime/components/shell/AUserMenu.vue.d.ts +5 -2
  70. package/dist/runtime/composables/useAbracadabraSchema.d.ts +83 -0
  71. package/dist/runtime/composables/useAbracadabraSchema.js +52 -0
  72. package/dist/runtime/composables/useAggregatedPresence.d.ts +1 -6
  73. package/dist/runtime/composables/useCalendarView.d.ts +1 -1
  74. package/dist/runtime/composables/useChat.js +1 -0
  75. package/dist/runtime/composables/useDocBreadcrumb.d.ts +21 -0
  76. package/dist/runtime/composables/useDocBreadcrumb.js +33 -0
  77. package/dist/runtime/composables/useDocEntryTyped.d.ts +60 -0
  78. package/dist/runtime/composables/useDocEntryTyped.js +70 -0
  79. package/dist/runtime/composables/useEditorDragHandle.js +18 -0
  80. package/dist/runtime/composables/useEditorSuggestions.js +2 -1
  81. package/dist/runtime/composables/useInstalledPlugins.d.ts +3 -21
  82. package/dist/runtime/composables/useInstalledPlugins.js +2 -12
  83. package/dist/runtime/composables/useMetaMenuItems.d.ts +21 -0
  84. package/dist/runtime/composables/useMetaMenuItems.js +115 -0
  85. package/dist/runtime/composables/useMetaValidator.d.ts +27 -0
  86. package/dist/runtime/composables/useMetaValidator.js +10 -0
  87. package/dist/runtime/composables/usePluginCatalog.d.ts +161 -0
  88. package/dist/runtime/composables/usePluginCatalog.js +234 -0
  89. package/dist/runtime/composables/useQuery.d.ts +79 -0
  90. package/dist/runtime/composables/useQuery.js +97 -0
  91. package/dist/runtime/composables/useSpaces.js +4 -5
  92. package/dist/runtime/composables/useTableView.d.ts +3 -3
  93. package/dist/runtime/composables/useTypedDoc.d.ts +97 -0
  94. package/dist/runtime/composables/useTypedDoc.js +114 -0
  95. package/dist/runtime/composables/useWebRTC.js +44 -5
  96. package/dist/runtime/extensions/document-meta.js +5 -0
  97. package/dist/runtime/extensions/timeline.d.ts +11 -0
  98. package/dist/runtime/extensions/timeline.js +52 -0
  99. package/dist/runtime/extensions/views/DocumentMetaView.d.vue.ts +4 -0
  100. package/dist/runtime/extensions/views/DocumentMetaView.vue +63 -0
  101. package/dist/runtime/extensions/views/DocumentMetaView.vue.d.ts +4 -0
  102. package/dist/runtime/extensions/views/TimelineItemView.d.vue.ts +4 -0
  103. package/dist/runtime/extensions/views/TimelineItemView.vue +131 -0
  104. package/dist/runtime/extensions/views/TimelineItemView.vue.d.ts +4 -0
  105. package/dist/runtime/extensions/views/TimelineView.d.vue.ts +9 -0
  106. package/dist/runtime/extensions/views/TimelineView.vue +29 -0
  107. package/dist/runtime/extensions/views/TimelineView.vue.d.ts +9 -0
  108. package/dist/runtime/locale.d.ts +2 -0
  109. package/dist/runtime/locale.js +2 -0
  110. package/dist/runtime/plugin-abracadabra.client.js +107 -6
  111. package/dist/runtime/plugin-registry.d.ts +11 -30
  112. package/dist/runtime/plugin-registry.js +2 -82
  113. package/dist/runtime/plugins/core.plugin.js +10 -4
  114. package/dist/runtime/server/api/_abracadabra/spaces.get.d.ts +1 -1
  115. package/dist/runtime/server/plugins/abracadabra-service.js +28 -0
  116. package/dist/runtime/server/utils/docCache.js +24 -3
  117. package/dist/runtime/server/utils/schemaServerSupport.d.ts +52 -0
  118. package/dist/runtime/server/utils/schemaServerSupport.js +51 -0
  119. package/dist/runtime/types.d.ts +63 -46
  120. package/dist/runtime/utils/docTypes.d.ts +15 -0
  121. package/dist/runtime/utils/docTypes.js +20 -0
  122. package/dist/runtime/utils/loadCodeMirror.d.ts +32 -0
  123. package/dist/runtime/utils/loadCodeMirror.js +65 -0
  124. package/dist/runtime/utils/markdownToYjs.d.ts +1 -23
  125. package/dist/runtime/utils/markdownToYjs.js +5 -440
  126. package/dist/runtime/utils/schemaSupport.d.ts +60 -0
  127. package/dist/runtime/utils/schemaSupport.js +40 -0
  128. package/dist/runtime/utils/yjsConvert.d.ts +1 -14
  129. package/dist/runtime/utils/yjsConvert.js +5 -331
  130. package/package.json +84 -23
package/dist/module.d.mts CHANGED
@@ -42,6 +42,7 @@ declare module '@nuxt/schema' {
42
42
  chat?: boolean;
43
43
  media?: boolean;
44
44
  slides?: boolean;
45
+ code?: boolean;
45
46
  };
46
47
  locale: AbracadabraLocale;
47
48
  auth: {
@@ -72,6 +73,20 @@ declare module '@nuxt/schema' {
72
73
  resetToken: string | null;
73
74
  verifyToken: string | null;
74
75
  };
76
+ schema: {
77
+ validate: boolean;
78
+ migrateOnRead: boolean;
79
+ bundleDir: string;
80
+ };
81
+ pluginRegistry: {
82
+ /**
83
+ * Public registry URL — the `abracadabra-registry-rs` instance the
84
+ * `usePluginRegistry()` composable reads from. Default points at the
85
+ * local-dev server (`http://127.0.0.1:8787`); production apps should
86
+ * override to the hosted registry.
87
+ */
88
+ url: string;
89
+ };
75
90
  };
76
91
  }
77
92
  interface RuntimeConfig {
@@ -165,6 +180,13 @@ interface ModuleOptions {
165
180
  * Default: true (lightweight, no extra deps).
166
181
  */
167
182
  slides?: boolean;
183
+ /**
184
+ * Register the `code` page type and the `<ACodeEditor>` primitive
185
+ * (CodeMirror 6 + y-codemirror.next). CodeMirror packages are loaded
186
+ * via dynamic import as optional peer deps — consumers that never use
187
+ * code pages don't pay the bundle cost. Default: true.
188
+ */
189
+ code?: boolean;
168
190
  };
169
191
  /**
170
192
  * Automatically add Vite resolve.dedupe entries for ProseMirror, TipTap, and Yjs.
@@ -248,6 +270,43 @@ interface ModuleOptions {
248
270
  /** Query param holding the email-verification token. Default: 'verify_token'. */
249
271
  verifyToken?: string | null;
250
272
  };
273
+ /**
274
+ * Optional `@abraca/schema` integration.
275
+ *
276
+ * The schema package is an **optional peer dependency**. Apps that
277
+ * never set this block — or never install `@abraca/schema` — are
278
+ * unaffected (Rule 4 of `feedback_schema_free_core.md`).
279
+ *
280
+ * When opted in, consuming code attaches one or more `SchemaRegistry`
281
+ * instances at boot via the `abracadabra:before-boot` Nuxt hook (see
282
+ * `useAbracadabraSchema()` for the runtime side). The block here
283
+ * controls whether the client plugin should auto-call
284
+ * `provider.dm.meta.setSchema(...)`, whether typed-reads should
285
+ * `runMigrations` before returning, and tells operators the path the
286
+ * server's `[extensions.schema] bundle_dir` is loading.
287
+ */
288
+ schema?: {
289
+ /**
290
+ * When true, the client plugin calls `dm.meta.setSchema(merged)`
291
+ * once after boot using the registries attached via the
292
+ * `abracadabra:before-boot` hook. Invalid meta writes throw
293
+ * `MetaValidationError` from `@abraca/dabra`. Default: false.
294
+ */
295
+ validate?: boolean;
296
+ /**
297
+ * When true, the typed-doc composables run `runMigrations(...)`
298
+ * before returning meta on read. Migrations are forward-only and
299
+ * declared per doc-type in `@abraca/schema`. Default: false.
300
+ */
301
+ migrateOnRead?: boolean;
302
+ /**
303
+ * Path to the directory of JSON Schema bundles the Abracadabra
304
+ * server is loading via `[extensions.schema] bundle_dir`. Logged
305
+ * at boot for operator visibility. Not read by the module — the
306
+ * server is authoritative. Default: '' (no advertisement).
307
+ */
308
+ bundleDir?: string;
309
+ };
251
310
  /**
252
311
  * WebRTC P2P configuration. Only used when features.webrtc is true.
253
312
  */
@@ -284,6 +343,15 @@ interface ModuleOptions {
284
343
  /** Disable the service provider even if keys are present. Default: false. */
285
344
  disabled?: boolean;
286
345
  };
346
+ /**
347
+ * Public plugin registry — the `abracadabra-registry-rs` instance used by
348
+ * `usePluginRegistry()` + the `<APluginBrowser>` UI. Default points at the
349
+ * local dev server (`http://127.0.0.1:8787`). Set
350
+ * `ABRACADABRA_PLUGIN_REGISTRY_URL` env var or override here for prod.
351
+ */
352
+ pluginRegistry?: {
353
+ url?: string;
354
+ };
287
355
  }
288
356
  declare const _default: _nuxt_schema.NuxtModule<ModuleOptions, ModuleOptions, false>;
289
357
 
package/dist/module.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "compatibility": {
5
5
  "nuxt": ">=4.0.0"
6
6
  },
7
- "version": "2.0.11",
7
+ "version": "2.3.0",
8
8
  "builder": {
9
9
  "@nuxt/module-builder": "1.0.2",
10
10
  "unbuild": "3.6.1"
package/dist/module.mjs CHANGED
@@ -32,7 +32,8 @@ const module$1 = defineNuxtModule({
32
32
  webrtc: false,
33
33
  chat: false,
34
34
  media: false,
35
- slides: true
35
+ slides: true,
36
+ code: true
36
37
  },
37
38
  addViteDedupe: true,
38
39
  prefix: "",
@@ -52,14 +53,25 @@ const module$1 = defineNuxtModule({
52
53
  webrtc: {
53
54
  iceServers: [{ urls: "stun:stun.l.google.com:19302" }],
54
55
  e2ee: false,
55
- fileTransfer: false
56
+ // Default ON. Opting into WebRTC almost always implies wanting data
57
+ // channels (file transfer is the marquee use case). Apps that want
58
+ // a strictly voice/video mesh can flip to false.
59
+ fileTransfer: true
56
60
  },
57
61
  authQueryKeys: {
58
62
  resetToken: "reset_token",
59
63
  verifyToken: "verify_token"
60
64
  },
65
+ schema: {
66
+ validate: false,
67
+ migrateOnRead: false,
68
+ bundleDir: ""
69
+ },
61
70
  service: {
62
71
  disabled: false
72
+ },
73
+ pluginRegistry: {
74
+ url: process.env.ABRACADABRA_PLUGIN_REGISTRY_URL ?? "http://127.0.0.1:8787"
63
75
  }
64
76
  },
65
77
  setup(options, nuxt) {
@@ -68,6 +80,9 @@ const module$1 = defineNuxtModule({
68
80
  nuxt.options.runtimeConfig.public,
69
81
  {
70
82
  abracadabra: {
83
+ pluginRegistry: {
84
+ url: options.pluginRegistry?.url ?? "http://127.0.0.1:8787"
85
+ },
71
86
  url: options.url,
72
87
  entryDocId: options.entryDocId ?? "",
73
88
  persistAuth: options.persistAuth,
@@ -87,11 +102,16 @@ const module$1 = defineNuxtModule({
87
102
  webrtc: {
88
103
  iceServers: options.webrtc?.iceServers ?? [{ urls: "stun:stun.l.google.com:19302" }],
89
104
  e2ee: options.webrtc?.e2ee ?? false,
90
- fileTransfer: options.webrtc?.fileTransfer ?? false
105
+ fileTransfer: options.webrtc?.fileTransfer ?? true
91
106
  },
92
107
  authQueryKeys: {
93
108
  resetToken: options.authQueryKeys?.resetToken ?? "reset_token",
94
109
  verifyToken: options.authQueryKeys?.verifyToken ?? "verify_token"
110
+ },
111
+ schema: {
112
+ validate: options.schema?.validate ?? false,
113
+ migrateOnRead: options.schema?.migrateOnRead ?? false,
114
+ bundleDir: options.schema?.bundleDir ?? ""
95
115
  }
96
116
  }
97
117
  }
@@ -112,6 +132,36 @@ const module$1 = defineNuxtModule({
112
132
  }
113
133
  }
114
134
  });
135
+ try {
136
+ let resolveEsmTwin = function(cjsPath) {
137
+ const candidates = [
138
+ cjsPath.replace(/\.cjs$/, ".mjs"),
139
+ cjsPath.replace(/\.cjs$/, ".js"),
140
+ path.join(path.dirname(path.dirname(cjsPath)), path.basename(cjsPath, ".cjs") + ".js")
141
+ ];
142
+ for (const c of candidates) {
143
+ if (fs.existsSync(c)) return c;
144
+ }
145
+ if (nuxt.options.dev) {
146
+ console.warn(`[abracadabra] could not find ESM twin for ${cjsPath} \u2014 leaving Nitro alias on .cjs`);
147
+ }
148
+ return cjsPath;
149
+ };
150
+ const nodeRequire = createRequire(`${nuxt.options.rootDir}/`);
151
+ const fs = nodeRequire("node:fs");
152
+ const path = nodeRequire("node:path");
153
+ const yjsCjs = nodeRequire.resolve("yjs");
154
+ const yjsEsm = resolveEsmTwin(yjsCjs);
155
+ const yProtocolsCjs = nodeRequire.resolve("y-protocols/awareness");
156
+ const yProtocolsEsm = resolveEsmTwin(yProtocolsCjs);
157
+ nuxt.options.nitro = defu(nuxt.options.nitro, {
158
+ alias: {
159
+ "yjs": yjsEsm,
160
+ "y-protocols/awareness": yProtocolsEsm
161
+ }
162
+ });
163
+ } catch {
164
+ }
115
165
  if (options.addViteDedupe) {
116
166
  const dedupePackages = [
117
167
  "prosemirror-view",
@@ -136,7 +186,13 @@ const module$1 = defineNuxtModule({
136
186
  "@tiptap/pm",
137
187
  "@tiptap/vue-3",
138
188
  "@tiptap/extension-collaboration",
139
- "@tiptap/extension-collaboration-caret"
189
+ "@tiptap/extension-collaboration-caret",
190
+ // CodeMirror — view + state are singletons (similar to ProseMirror).
191
+ // Two copies cause plugin-key collisions and silent decoration drops.
192
+ "@codemirror/state",
193
+ "@codemirror/view",
194
+ "@codemirror/language",
195
+ "y-codemirror.next"
140
196
  ];
141
197
  nuxt.options.vite = defu(nuxt.options.vite, { resolve: { dedupe: [] } });
142
198
  const existing = nuxt.options.vite.resolve?.dedupe ?? [];
@@ -229,6 +285,29 @@ const module$1 = defineNuxtModule({
229
285
  prebundleDeps.push("jszip");
230
286
  } catch {
231
287
  }
288
+ if (options.features?.code !== false) {
289
+ const cmPeers = [
290
+ "@codemirror/view",
291
+ "@codemirror/state",
292
+ "@codemirror/language",
293
+ "@codemirror/commands",
294
+ "@codemirror/autocomplete",
295
+ "@codemirror/search",
296
+ "@codemirror/lang-javascript",
297
+ "@codemirror/lang-css",
298
+ "@codemirror/lang-vue",
299
+ "@codemirror/lang-json",
300
+ "y-codemirror.next"
301
+ ];
302
+ const nodeRequire = createRequire(`${nuxt.options.rootDir}/`);
303
+ for (const pkg of cmPeers) {
304
+ try {
305
+ nodeRequire.resolve(pkg);
306
+ prebundleDeps.push(pkg);
307
+ } catch {
308
+ }
309
+ }
310
+ }
232
311
  nuxt.options.vite.optimizeDeps.include = [
233
312
  .../* @__PURE__ */ new Set([...includeList, ...prebundleDeps])
234
313
  ];
@@ -243,6 +322,17 @@ const module$1 = defineNuxtModule({
243
322
  "@noble/curves/ed25519.js"
244
323
  ])
245
324
  ];
325
+ nuxt.options.vite.optimizeDeps.include = [
326
+ .../* @__PURE__ */ new Set([
327
+ ...nuxt.options.vite.optimizeDeps.include ?? [],
328
+ "@scure/bip39",
329
+ // Subpath ships with the `.js` extension in the package's exports
330
+ // map (`./wordlists/english.js` → `./wordlists/english.js`).
331
+ // Listing it without the extension throws ERR_PACKAGE_PATH_NOT_EXPORTED.
332
+ "@scure/bip39/wordlists/english.js",
333
+ "@scure/base"
334
+ ])
335
+ ];
246
336
  nuxt.options.css.push(resolver.resolve("./runtime/assets/aware-tokens.css"));
247
337
  if (options.features?.editor !== false) {
248
338
  nuxt.options.css.push(resolver.resolve("./runtime/assets/editor.css"));
@@ -341,6 +431,11 @@ const module$1 = defineNuxtModule({
341
431
  pathPrefix: false
342
432
  });
343
433
  }
434
+ addComponentsDir({
435
+ path: resolver.resolve("./runtime/components/registry"),
436
+ prefix: options.prefix ?? "",
437
+ pathPrefix: false
438
+ });
344
439
  addComponentsDir({
345
440
  path: resolver.resolve("./runtime/components"),
346
441
  prefix: options.prefix ?? "",
@@ -0,0 +1,26 @@
1
+ type Lang = 'javascript' | 'typescript' | 'tsx' | 'jsx' | 'css' | 'vue' | 'json' | 'markdown' | 'plain';
2
+ type __VLS_Props = {
3
+ /** Child Y.Doc provider — exposes `.document` (Y.Doc) and `.awareness`. */
4
+ provider: any | null;
5
+ /** Y type field name. Default `'code'` for Y.Text; in source-view mode this is the XmlFragment name. */
6
+ fieldName?: string;
7
+ /** Syntax-highlight language. */
8
+ language?: Lang;
9
+ /** Read-only editor. Forced true when `asXmlFragment` is set. */
10
+ readOnly?: boolean;
11
+ /**
12
+ * Source-view mode: bind to `provider.document.getXmlFragment(fieldName)` and
13
+ * render its `.toString()` as plain text. One-way — no write-back.
14
+ */
15
+ asXmlFragment?: boolean;
16
+ };
17
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {
18
+ editorView: import("vue").ShallowRef<any, any>;
19
+ }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
20
+ readOnly: boolean;
21
+ language: Lang;
22
+ fieldName: string;
23
+ asXmlFragment: boolean;
24
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
25
+ declare const _default: typeof __VLS_export;
26
+ export default _default;
@@ -0,0 +1,268 @@
1
+ <script setup>
2
+ import { ref, shallowRef, watch, computed, onMounted, onBeforeUnmount } from "vue";
3
+ import { loadCodeMirror } from "../utils/loadCodeMirror";
4
+ import { useNuxtApp } from "#imports";
5
+ const props = defineProps({
6
+ provider: { type: null, required: true },
7
+ fieldName: { type: String, required: false, default: "code" },
8
+ language: { type: String, required: false, default: "javascript" },
9
+ readOnly: { type: Boolean, required: false, default: false },
10
+ asXmlFragment: { type: Boolean, required: false, default: false }
11
+ });
12
+ const containerRef = ref(null);
13
+ const editorView = shallowRef(null);
14
+ const fallbackText = ref("");
15
+ const cmBundle = shallowRef(null);
16
+ const peersMissing = ref(false);
17
+ const effectiveReadOnly = computed(() => props.readOnly || props.asXmlFragment);
18
+ const { userName, userColor, publicKeyB64 } = useNuxtApp().$abracadabra ?? {
19
+ userName: { value: "" },
20
+ userColor: { value: "#888" },
21
+ publicKeyB64: { value: "" }
22
+ };
23
+ function getLanguageExtension(bundle, lang) {
24
+ switch (lang) {
25
+ case "vue":
26
+ return bundle.langVue.vue();
27
+ case "typescript":
28
+ case "tsx":
29
+ return bundle.langJavascript.javascript({ typescript: true, jsx: lang === "tsx" });
30
+ case "javascript":
31
+ case "jsx":
32
+ return bundle.langJavascript.javascript({ jsx: lang === "jsx" });
33
+ case "css":
34
+ return bundle.langCss.css();
35
+ case "json":
36
+ return bundle.langJson.json();
37
+ case "markdown":
38
+ case "plain":
39
+ return [];
40
+ default:
41
+ return bundle.langJavascript.javascript();
42
+ }
43
+ }
44
+ function buildTheme(bundle) {
45
+ return bundle.view.EditorView.theme({
46
+ "&": {
47
+ backgroundColor: "var(--ui-bg)",
48
+ color: "var(--ui-text-highlighted)",
49
+ height: "100%",
50
+ fontSize: "13px",
51
+ fontFamily: 'ui-monospace, "SF Mono", "Fira Code", "Fira Mono", "Roboto Mono", Menlo, Monaco, Consolas, monospace'
52
+ },
53
+ ".cm-content": {
54
+ fontFamily: "inherit",
55
+ caretColor: "var(--ui-text-highlighted)",
56
+ padding: "8px 0"
57
+ },
58
+ ".cm-cursor": {
59
+ borderLeftColor: "var(--ui-text-highlighted)"
60
+ },
61
+ ".cm-activeLine": {
62
+ backgroundColor: "color-mix(in srgb, var(--ui-bg-elevated) 50%, transparent)"
63
+ },
64
+ ".cm-activeLineGutter": {
65
+ backgroundColor: "color-mix(in srgb, var(--ui-bg-elevated) 50%, transparent)"
66
+ },
67
+ ".cm-gutters": {
68
+ fontFamily: "inherit",
69
+ backgroundColor: "var(--ui-bg)",
70
+ color: "var(--ui-text-dimmed)",
71
+ border: "none",
72
+ borderRight: "1px solid var(--ui-border)"
73
+ },
74
+ ".cm-selectionBackground": {
75
+ backgroundColor: "color-mix(in srgb, var(--ui-primary) 25%, transparent) !important"
76
+ },
77
+ "&.cm-focused .cm-selectionBackground": {
78
+ backgroundColor: "color-mix(in srgb, var(--ui-primary) 30%, transparent) !important"
79
+ },
80
+ ".cm-matchingBracket": {
81
+ backgroundColor: "color-mix(in srgb, var(--ui-primary) 20%, transparent)",
82
+ outline: "1px solid color-mix(in srgb, var(--ui-primary) 40%, transparent)"
83
+ },
84
+ ".cm-ySelectionInfo": {
85
+ fontSize: "10px",
86
+ fontFamily: "system-ui, sans-serif",
87
+ padding: "1px 4px",
88
+ borderRadius: "3px",
89
+ opacity: "0.9",
90
+ position: "absolute",
91
+ top: "-1.3em",
92
+ left: "-1px",
93
+ whiteSpace: "nowrap",
94
+ color: "#fff"
95
+ }
96
+ }, { dark: true });
97
+ }
98
+ let xmlObserverCleanup = null;
99
+ function destroyEditor() {
100
+ if (xmlObserverCleanup) {
101
+ xmlObserverCleanup();
102
+ xmlObserverCleanup = null;
103
+ }
104
+ if (editorView.value) {
105
+ editorView.value.destroy();
106
+ editorView.value = null;
107
+ }
108
+ }
109
+ function createEditor(bundle, container) {
110
+ const { EditorView, lineNumbers, highlightActiveLine, highlightActiveLineGutter, drawSelection, rectangularSelection, crosshairCursor, highlightSpecialChars, dropCursor, keymap } = bundle.view;
111
+ const { EditorState } = bundle.state;
112
+ const { defaultKeymap, history, historyKeymap, indentWithTab } = bundle.commands;
113
+ const { syntaxHighlighting, defaultHighlightStyle, indentOnInput, bracketMatching, foldGutter, foldKeymap } = bundle.language;
114
+ const { closeBrackets, closeBracketsKeymap, autocompletion, completionKeymap } = bundle.autocomplete;
115
+ const { searchKeymap, highlightSelectionMatches } = bundle.search;
116
+ const baseExtensions = [
117
+ lineNumbers(),
118
+ highlightActiveLineGutter(),
119
+ highlightSpecialChars(),
120
+ foldGutter(),
121
+ drawSelection(),
122
+ dropCursor(),
123
+ EditorState.allowMultipleSelections.of(true),
124
+ indentOnInput(),
125
+ syntaxHighlighting(defaultHighlightStyle, { fallback: true }),
126
+ bracketMatching(),
127
+ closeBrackets(),
128
+ autocompletion(),
129
+ rectangularSelection(),
130
+ crosshairCursor(),
131
+ highlightActiveLine(),
132
+ highlightSelectionMatches(),
133
+ keymap.of([
134
+ ...closeBracketsKeymap,
135
+ ...defaultKeymap,
136
+ ...searchKeymap,
137
+ ...historyKeymap,
138
+ ...foldKeymap,
139
+ ...completionKeymap,
140
+ indentWithTab
141
+ ]),
142
+ getLanguageExtension(bundle, props.language),
143
+ buildTheme(bundle),
144
+ EditorView.lineWrapping,
145
+ EditorState.readOnly.of(effectiveReadOnly.value)
146
+ ];
147
+ const prov = props.provider;
148
+ const ydoc = prov?.document;
149
+ if (props.asXmlFragment && ydoc) {
150
+ const fragment = ydoc.getXmlFragment(props.fieldName);
151
+ const initial = fragment?.toString?.() ?? "";
152
+ const state = EditorState.create({
153
+ doc: initial,
154
+ extensions: [...baseExtensions, history()]
155
+ });
156
+ const view = new EditorView({ state, parent: container });
157
+ const onFragmentChange = () => {
158
+ const next = fragment?.toString?.() ?? "";
159
+ const current = view.state.doc.toString();
160
+ if (next === current) return;
161
+ view.dispatch({ changes: { from: 0, to: view.state.doc.length, insert: next } });
162
+ };
163
+ fragment?.observeDeep?.(onFragmentChange);
164
+ xmlObserverCleanup = () => fragment?.unobserveDeep?.(onFragmentChange);
165
+ return view;
166
+ }
167
+ if (prov && ydoc) {
168
+ const ytext = ydoc.getText(props.fieldName);
169
+ const awareness = prov.awareness;
170
+ if (awareness && userName?.value && publicKeyB64?.value) {
171
+ awareness.setLocalStateField("user", {
172
+ name: userName.value,
173
+ color: userColor?.value ?? "#888",
174
+ publicKey: publicKeyB64.value
175
+ });
176
+ }
177
+ const extensions = [...baseExtensions];
178
+ if (awareness && !effectiveReadOnly.value) {
179
+ extensions.push(bundle.yCollab.yCollab(ytext, awareness));
180
+ } else {
181
+ extensions.push(history());
182
+ }
183
+ return new EditorView({
184
+ state: EditorState.create({ doc: ytext.toString(), extensions }),
185
+ parent: container
186
+ });
187
+ }
188
+ return new EditorView({
189
+ state: EditorState.create({ doc: "", extensions: [...baseExtensions, history()] }),
190
+ parent: container
191
+ });
192
+ }
193
+ async function mount() {
194
+ destroyEditor();
195
+ if (!containerRef.value) return;
196
+ const bundle = cmBundle.value ?? await loadCodeMirror();
197
+ cmBundle.value = bundle;
198
+ if (!bundle) {
199
+ peersMissing.value = true;
200
+ const prov = props.provider;
201
+ if (props.asXmlFragment && prov?.document) {
202
+ fallbackText.value = prov.document.getXmlFragment(props.fieldName)?.toString?.() ?? "";
203
+ } else if (prov?.document) {
204
+ fallbackText.value = prov.document.getText(props.fieldName)?.toString?.() ?? "";
205
+ }
206
+ return;
207
+ }
208
+ editorView.value = createEditor(bundle, containerRef.value);
209
+ }
210
+ onMounted(() => {
211
+ void mount();
212
+ });
213
+ watch(containerRef, (el) => {
214
+ if (el) void mount();
215
+ });
216
+ watch(
217
+ () => [props.provider, props.fieldName, props.language, props.readOnly, props.asXmlFragment],
218
+ () => {
219
+ void mount();
220
+ }
221
+ );
222
+ watch(
223
+ () => [userName?.value, userColor?.value, publicKeyB64?.value],
224
+ ([n, c, k]) => {
225
+ const awareness = props.provider?.awareness;
226
+ if (!awareness || !n || !k) return;
227
+ awareness.setLocalStateField("user", { name: n, color: c ?? "#888", publicKey: k });
228
+ }
229
+ );
230
+ onBeforeUnmount(destroyEditor);
231
+ defineExpose({ editorView });
232
+ </script>
233
+
234
+ <template>
235
+ <div class="acode-root">
236
+ <ClientOnly>
237
+ <div v-if="peersMissing" class="acode-fallback">
238
+ <p class="acode-fallback__note">
239
+ CodeMirror peer dependencies not installed. Showing a read-only fallback.
240
+ <a
241
+ href="https://codemirror.net/"
242
+ target="_blank"
243
+ rel="noopener"
244
+ class="acode-fallback__link"
245
+ >Install instructions</a>.
246
+ </p>
247
+ <textarea
248
+ :value="fallbackText"
249
+ readonly
250
+ class="acode-fallback__textarea"
251
+ spellcheck="false"
252
+ />
253
+ </div>
254
+ <div
255
+ v-else
256
+ ref="containerRef"
257
+ class="acode-container"
258
+ />
259
+ <template #fallback>
260
+ <div class="acode-container acode-container--ssr" />
261
+ </template>
262
+ </ClientOnly>
263
+ </div>
264
+ </template>
265
+
266
+ <style scoped>
267
+ .acode-root{display:flex;flex-direction:column}.acode-container,.acode-root{flex:1 1 0;min-height:0;overflow:hidden}.acode-container--ssr{background:var(--ui-bg)}.acode-fallback{display:flex;flex:1 1 0;flex-direction:column;gap:.5rem;min-height:0;padding:.5rem}.acode-fallback__note{color:var(--ui-text-muted);font-size:.75rem}.acode-fallback__link{color:var(--ui-primary);text-decoration:underline}.acode-fallback__textarea{background:var(--ui-bg);border:1px solid var(--ui-border);border-radius:var(--ui-radius);color:var(--ui-text-highlighted);flex:1 1 0;font-family:ui-monospace,SF Mono,Fira Code,Fira Mono,Roboto Mono,Menlo,Monaco,Consolas,monospace;font-size:13px;min-height:0;padding:.5rem;resize:none;width:100%}
268
+ </style>
@@ -0,0 +1,26 @@
1
+ type Lang = 'javascript' | 'typescript' | 'tsx' | 'jsx' | 'css' | 'vue' | 'json' | 'markdown' | 'plain';
2
+ type __VLS_Props = {
3
+ /** Child Y.Doc provider — exposes `.document` (Y.Doc) and `.awareness`. */
4
+ provider: any | null;
5
+ /** Y type field name. Default `'code'` for Y.Text; in source-view mode this is the XmlFragment name. */
6
+ fieldName?: string;
7
+ /** Syntax-highlight language. */
8
+ language?: Lang;
9
+ /** Read-only editor. Forced true when `asXmlFragment` is set. */
10
+ readOnly?: boolean;
11
+ /**
12
+ * Source-view mode: bind to `provider.document.getXmlFragment(fieldName)` and
13
+ * render its `.toString()` as plain text. One-way — no write-back.
14
+ */
15
+ asXmlFragment?: boolean;
16
+ };
17
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {
18
+ editorView: import("vue").ShallowRef<any, any>;
19
+ }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
20
+ readOnly: boolean;
21
+ language: Lang;
22
+ fieldName: string;
23
+ asXmlFragment: boolean;
24
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
25
+ declare const _default: typeof __VLS_export;
26
+ export default _default;