@threadlabs/looma 0.2.20 → 0.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 (51) hide show
  1. package/dist/{chunk-A5MSXQ7Y.js → chunk-3NVMCHGR.js} +2225 -1255
  2. package/dist/chunk-XDNZIKNI.js +2721 -0
  3. package/dist/declarative-generated.cjs +93 -79
  4. package/dist/declarative-generated.d.cts +7 -1
  5. package/dist/declarative-generated.d.ts +7 -1
  6. package/dist/declarative-generated.js +91 -78
  7. package/dist/declarative.cjs +1499 -1702
  8. package/dist/declarative.d.cts +77 -3
  9. package/dist/declarative.d.ts +77 -3
  10. package/dist/declarative.js +5 -3
  11. package/dist/index.cjs +3732 -3025
  12. package/dist/index.d.cts +211 -35
  13. package/dist/index.d.ts +211 -35
  14. package/dist/index.js +6 -61
  15. package/dist/loader.cjs +3725 -2962
  16. package/dist/loader.js +2 -2
  17. package/editor/{chunk-VAXI7Q6T.js → chunk-U36XNFJN.js} +105 -77
  18. package/editor/extensions/index.d.ts +272 -27
  19. package/editor/index.d.ts +1 -1
  20. package/editor/index.js +1 -1
  21. package/editor/{table-overlay-DoiLie4h.d.ts → table-overlay-DZFGHQDt.d.ts} +48 -6
  22. package/editor/ui.d.ts +15 -11
  23. package/editor/ui.js +1 -1
  24. package/layout/index.cjs +197 -340
  25. package/layout/index.d.cts +11 -0
  26. package/layout/index.d.ts +11 -0
  27. package/layout/index.js +197 -340
  28. package/layout.css +31 -134
  29. package/package.json +24 -11
  30. package/react/index.d.ts +1276 -0
  31. package/react/index.js +1526 -0
  32. package/styles.css +5 -10
  33. package/svelte/index.d.ts +204 -0
  34. package/svelte/index.js +1881 -0
  35. package/theme-dark.css +81 -2
  36. package/theme-high-contrast.css +14 -0
  37. package/theme-light.css +30 -1
  38. package/tokens.css +45 -1
  39. package/vue/chunk-UZMTYDP6.js +4355 -0
  40. package/vue/editor/index.d.ts +78 -8
  41. package/vue/editor/index.js +54 -49
  42. package/vue/index.d.ts +67 -78
  43. package/vue/index.js +3 -9
  44. package/dist/chunk-ECJQ4YGC.js +0 -2925
  45. package/dist/valibot.cjs +0 -31
  46. package/dist/valibot.d.cts +0 -10
  47. package/dist/valibot.d.ts +0 -10
  48. package/dist/valibot.js +0 -9
  49. package/dist/validation-bxcou-l-.d.cts +0 -58
  50. package/dist/validation-bxcou-l-.d.ts +0 -58
  51. package/vue/chunk-P7BZ2KJZ.js +0 -3559
@@ -2,19 +2,24 @@ import { LoomaMentionItem, SlashMenuAnchorRect, SlashMenuItem, LoomaMentionProvi
2
2
  export * from '@threadlabs/looma/editor';
3
3
  import * as vue from 'vue';
4
4
  import { PropType, VNode } from 'vue';
5
- import { MentionMenuHighlightEventDetail, MentionMenuSelectEventDetail, TableContextMenuActionEventDetail, InsertTableEventDetail, TableOverlayActionEventDetail, SlashMenuHighlightEventDetail, SlashMenuSelectEventDetail } from '@threadlabs/looma/editor/ui';
5
+ import { MentionMenuHighlightEventDetail, SlashMenuHighlightEventDetail, MentionMenuSelectEventDetail, SlashMenuSelectEventDetail, TableContextMenuActionEventDetail, TableOverlayActionEventDetail, InsertTableEventDetail } from '@threadlabs/looma/editor/ui';
6
6
  import { JSONContent, AnyExtension, Editor } from '@tiptap/core';
7
7
  import { OverlayTrigger } from '@threadlabs/looma/core';
8
8
 
9
+ /**
10
+ * Native editor event details unwrapped for Vue callback attributes.
11
+ * Multiple menu primitives share `highlight` and `select` because their index
12
+ * semantics are identical; table primitives share the typed action channel.
13
+ */
9
14
  interface VueEditorAdapterEventMap {
10
- mentionMenuHighlight: MentionMenuHighlightEventDetail;
11
- mentionMenuSelect: MentionMenuSelectEventDetail;
12
- tableAction: TableContextMenuActionEventDetail;
13
- insertTable: InsertTableEventDetail;
14
- tableOverlayAction: TableOverlayActionEventDetail;
15
- slashMenuHighlight: SlashMenuHighlightEventDetail;
16
- slashMenuSelect: SlashMenuSelectEventDetail;
15
+ highlight: MentionMenuHighlightEventDetail | SlashMenuHighlightEventDetail;
16
+ select: MentionMenuSelectEventDetail | SlashMenuSelectEventDetail;
17
+ action: TableContextMenuActionEventDetail | TableOverlayActionEventDetail;
18
+ insert: InsertTableEventDetail;
17
19
  }
20
+ /**
21
+ * Vue projection of the slash suggestion menu with lifecycle-safe event wiring.
22
+ */
18
23
  declare const EditorSlashMenu: vue.DefineComponent<vue.ExtractPropTypes<{
19
24
  open: {
20
25
  type: BooleanConstructor;
@@ -75,6 +80,7 @@ declare const EditorSlashMenu: vue.DefineComponent<vue.ExtractPropTypes<{
75
80
  anchorRect: SlashMenuAnchorRect | null;
76
81
  selectedIndex: number;
77
82
  }, {}, {}, {}, string, vue.ComponentProvideOptions, true, {}, any>;
83
+ /** Vue projection of the mention menu using the shared suggestion lifecycle. */
78
84
  declare const EditorMentionMenu: vue.DefineComponent<vue.ExtractPropTypes<{
79
85
  open: {
80
86
  type: BooleanConstructor;
@@ -135,11 +141,24 @@ declare const EditorMentionMenu: vue.DefineComponent<vue.ExtractPropTypes<{
135
141
  anchorRect: SlashMenuAnchorRect | null;
136
142
  selectedIndex: number;
137
143
  }, {}, {}, {}, string, vue.ComponentProvideOptions, true, {}, any>;
144
+ /** Vue projection of the editor toolbar's semantic layout shell. */
138
145
  declare const EditorToolbar: vue.DefineComponent<Record<string, never>>;
146
+ /** Vue projection that unwraps context-menu table actions for callbacks. */
139
147
  declare const EditorTableContextMenu: vue.DefineComponent<Record<string, never>>;
148
+ /** Vue projection that unwraps toolbar table actions for callbacks. */
140
149
  declare const EditorTableToolbar: vue.DefineComponent<Record<string, never>>;
150
+ /** Vue projection that reports the selected table dimensions as insert intent. */
141
151
  declare const EditorInsertTableGrid: vue.DefineComponent<Record<string, never>>;
152
+ /**
153
+ * Vue projection of editor-owned table geometry and boundary actions.
154
+ * Geometry is assigned as a DOM property so structured measurements are never
155
+ * serialized through attributes.
156
+ */
142
157
  declare const EditorTableOverlay: vue.DefineComponent<Record<string, never>>;
158
+ /**
159
+ * Canonical Vue editor export-to-native-tag correspondence used by parity
160
+ * checks and tooling that must enumerate opt-in editor primitives.
161
+ */
143
162
  declare const EDITOR_ADAPTER_COMPONENT_TAG_MAP: {
144
163
  readonly EditorToolbar: "ui-editor-toolbar";
145
164
  readonly EditorSlashMenu: "ui-editor-slash-menu";
@@ -150,6 +169,11 @@ declare const EDITOR_ADAPTER_COMPONENT_TAG_MAP: {
150
169
  readonly EditorTableOverlay: "ui-editor-table-overlay";
151
170
  };
152
171
 
172
+ /**
173
+ * Durable image data stored in the editor document.
174
+ * Delivery-specific attributes are intentionally absent so serialized content
175
+ * remains portable across CDNs, viewport policies, and Vue applications.
176
+ */
153
177
  interface LoomaImageDescriptor {
154
178
  src: string;
155
179
  alt?: string;
@@ -166,20 +190,66 @@ interface LoomaImageRenditionAttributes {
166
190
  decoding?: "async" | "auto" | "sync";
167
191
  fetchPriority?: "auto" | "high" | "low";
168
192
  }
193
+ /**
194
+ * Host policy for deriving transient browser-delivery attributes.
195
+ *
196
+ * @ownership The application owns the resolver and any URL/signing policy it
197
+ * consults; Looma reads its result without persisting it into editor JSON.
198
+ * @failure Throwing or returning `undefined` falls back to the durable source
199
+ * instead of interrupting rendering or mutating the document.
200
+ */
169
201
  type LoomaImageAttributeResolver = (image: LoomaImageDescriptor) => LoomaImageRenditionAttributes | undefined;
202
+ /** User-originated interaction that may request the host's image viewer. */
170
203
  type LoomaImageActivationTrigger = Exclude<OverlayTrigger, "programmatic">;
204
+ /**
205
+ * Image identity and user trigger emitted when an editor image is activated.
206
+ * Hosts may use the stable descriptor to open a viewer without inspecting DOM.
207
+ */
171
208
  interface LoomaImageActivationDetail extends LoomaImageDescriptor {
172
209
  trigger: LoomaImageActivationTrigger;
173
210
  }
211
+ /**
212
+ * Durable image identity reported after a transient responsive rendition fails.
213
+ * The programmatic trigger distinguishes delivery fallback from user intent.
214
+ */
174
215
  interface LoomaImageRenditionErrorDetail extends LoomaImageDescriptor {
175
216
  trigger: "programmatic";
176
217
  }
177
218
 
219
+ /**
220
+ * Host upload result normalized into the editor's durable image descriptor.
221
+ * `url` becomes the stored `src`; optional dimensions preserve layout before
222
+ * the image loads and `responsive` opts into host-provided rendition policy.
223
+ */
178
224
  interface LoomaImageUploadResult extends Omit<LoomaImageDescriptor, "src"> {
179
225
  url: string;
180
226
  }
227
+ /**
228
+ * Application-owned upload boundary used by paste, drop, and file selection.
229
+ *
230
+ * @ownership The application owns network requests, persistence, and returned
231
+ * URLs. Looma owns only editor insertion after the promise fulfills.
232
+ * @failure Rejection leaves the document unchanged and is surfaced through the
233
+ * editor's `uploadError` event so the host chooses retry and user messaging.
234
+ */
181
235
  type LoomaImageUploader = (file: File) => Promise<string | LoomaImageUploadResult>;
236
+ /**
237
+ * Chooses whether formatting controls follow a selection or occupy persistent
238
+ * editor chrome; it does not alter document commands or stored content.
239
+ */
182
240
  type LoomaEditorToolbarMode = "bubble" | "sticky";
241
+ /**
242
+ * Turnkey Vue editor that composes Looma's framework-neutral Tiptap extensions
243
+ * with declarative menus, image delivery, and table interaction chrome.
244
+ *
245
+ * @ownership The component owns its Tiptap instance and transient UI state;
246
+ * callers own controlled `modelValue`, extension instances, and host services.
247
+ * @lifecycle Mount creates one editor and browser listener set, prop watchers
248
+ * synchronize external state, and unmount cancels frames/listeners and destroys
249
+ * the editor-owned resources.
250
+ * @failure Upload and responsive-rendition failures are emitted to the host;
251
+ * durable document content is retained or left unchanged for recovery.
252
+ */
183
253
  declare const LoomaEditor: vue.DefineComponent<vue.ExtractPropTypes<{
184
254
  modelValue: {
185
255
  type: PropType<JSONContent>;
@@ -10,7 +10,7 @@ import {
10
10
  UiEditorToolbar_default,
11
11
  createAdapterComponent,
12
12
  toHTMLElement
13
- } from "../chunk-P7BZ2KJZ.js";
13
+ } from "../chunk-UZMTYDP6.js";
14
14
 
15
15
  // src/editor/index.ts
16
16
  import "@threadlabs/looma/editor";
@@ -23,8 +23,8 @@ import {
23
23
  shallowRef,
24
24
  watchEffect
25
25
  } from "vue";
26
- function createSuggestionMenuAdapter(name, eventPrefix) {
27
- const component = eventPrefix === "slash" ? UiEditorSlashMenu_default : UiEditorMentionMenu_default;
26
+ function createSuggestionMenuAdapter(name) {
27
+ const component = name === "EditorSlashMenu" ? UiEditorSlashMenu_default : UiEditorMentionMenu_default;
28
28
  return defineComponent({
29
29
  name,
30
30
  inheritAttrs: false,
@@ -42,31 +42,26 @@ function createSuggestionMenuAdapter(name, eventPrefix) {
42
42
  const element = elementRef.value;
43
43
  if (!element) return;
44
44
  const callbackAttrs = attrs;
45
- const highlightCallback = eventPrefix === "slash" ? callbackAttrs.onSlashMenuHighlight : callbackAttrs.onMentionMenuHighlight;
46
- const selectCallback = eventPrefix === "slash" ? callbackAttrs.onSlashMenuSelect : callbackAttrs.onMentionMenuSelect;
47
- const eventBase = `looma-editor-${eventPrefix}-menu`;
45
+ const highlightCallback = callbackAttrs.onHighlight;
46
+ const selectCallback = callbackAttrs.onSelect;
48
47
  const highlightHandler = typeof highlightCallback === "function" ? (event) => highlightCallback(event.detail) : void 0;
49
48
  const selectHandler = typeof selectCallback === "function" ? (event) => selectCallback(event.detail) : void 0;
50
- if (highlightHandler) element.addEventListener(`${eventBase}-highlight`, highlightHandler);
51
- if (selectHandler) element.addEventListener(`${eventBase}-select`, selectHandler);
49
+ if (highlightHandler) element.addEventListener("highlight", highlightHandler);
50
+ if (selectHandler) element.addEventListener("select", selectHandler);
52
51
  onCleanup(() => {
53
- if (highlightHandler) element.removeEventListener(`${eventBase}-highlight`, highlightHandler);
54
- if (selectHandler) element.removeEventListener(`${eventBase}-select`, selectHandler);
52
+ if (highlightHandler) element.removeEventListener("highlight", highlightHandler);
53
+ if (selectHandler) element.removeEventListener("select", selectHandler);
55
54
  });
56
55
  });
57
56
  return () => {
58
57
  const callbackAttrs = attrs;
59
58
  const {
60
- onMentionMenuHighlight,
61
- onMentionMenuSelect,
62
- onSlashMenuHighlight,
63
- onSlashMenuSelect,
59
+ onHighlight,
60
+ onSelect,
64
61
  ...forwardedAttrs
65
62
  } = callbackAttrs;
66
- void onMentionMenuHighlight;
67
- void onMentionMenuSelect;
68
- void onSlashMenuHighlight;
69
- void onSlashMenuSelect;
63
+ void onHighlight;
64
+ void onSelect;
70
65
  return h(component, {
71
66
  ...props,
72
67
  ...forwardedAttrs,
@@ -78,27 +73,22 @@ function createSuggestionMenuAdapter(name, eventPrefix) {
78
73
  }
79
74
  });
80
75
  }
81
- var EDITOR_EVENT_BINDINGS = [
82
- ["looma-editor-table-action", "onTableAction"],
83
- ["looma-editor-insert-table", "onInsertTable"],
84
- ["looma-editor-table-overlay-action", "onTableOverlayAction"]
85
- ];
76
+ var ACTION_EVENT_BINDINGS = [["action", "onAction"]];
77
+ var INSERT_EVENT_BINDINGS = [["insert", "onInsert"]];
86
78
  var EditorSlashMenu = createSuggestionMenuAdapter(
87
- "EditorSlashMenu",
88
- "slash"
79
+ "EditorSlashMenu"
89
80
  );
90
81
  var EditorMentionMenu = createSuggestionMenuAdapter(
91
- "EditorMentionMenu",
92
- "mention"
82
+ "EditorMentionMenu"
93
83
  );
94
- var EditorToolbar = createAdapterComponent(UiEditorToolbar_default, "EditorToolbar", EDITOR_EVENT_BINDINGS);
95
- var EditorTableContextMenu = createAdapterComponent(UiEditorTableContextMenu_default, "EditorTableContextMenu", EDITOR_EVENT_BINDINGS);
96
- var EditorTableToolbar = createAdapterComponent(UiEditorTableToolbar_default, "EditorTableToolbar", EDITOR_EVENT_BINDINGS);
97
- var EditorInsertTableGrid = createAdapterComponent(UiEditorInsertTableGrid_default, "EditorInsertTableGrid", EDITOR_EVENT_BINDINGS);
84
+ var EditorToolbar = createAdapterComponent(UiEditorToolbar_default, "EditorToolbar");
85
+ var EditorTableContextMenu = createAdapterComponent(UiEditorTableContextMenu_default, "EditorTableContextMenu", ACTION_EVENT_BINDINGS);
86
+ var EditorTableToolbar = createAdapterComponent(UiEditorTableToolbar_default, "EditorTableToolbar", ACTION_EVENT_BINDINGS);
87
+ var EditorInsertTableGrid = createAdapterComponent(UiEditorInsertTableGrid_default, "EditorInsertTableGrid", INSERT_EVENT_BINDINGS);
98
88
  var EditorTableOverlay = createAdapterComponent(
99
89
  UiEditorTableOverlay_default,
100
90
  "EditorTableOverlay",
101
- EDITOR_EVENT_BINDINGS,
91
+ ACTION_EVENT_BINDINGS,
102
92
  "class",
103
93
  ["geometry"]
104
94
  );
@@ -390,6 +380,8 @@ var LoomaEditor = defineComponent2({
390
380
  type: Object,
391
381
  default: () => ({ ...EMPTY_DOCUMENT })
392
382
  },
383
+ // Deliberate exception to Looma's opt-in boolean rule: an editor that is
384
+ // read-only unless configured would violate the primary UX promised by this wrapper.
393
385
  editable: { type: Boolean, default: true },
394
386
  placeholder: {
395
387
  type: String,
@@ -917,7 +909,7 @@ var LoomaEditor = defineComponent2({
917
909
  const commandButton = (label, icon, active, disabled, run) => {
918
910
  const syncNativeDisabled = (vnode) => {
919
911
  if (!(vnode.el instanceof Element)) return;
920
- const button = vnode.el.querySelector("button");
912
+ const button = vnode.el instanceof HTMLButtonElement ? vnode.el : vnode.el.querySelector("button");
921
913
  if (button) button.disabled = disabled;
922
914
  };
923
915
  return h2(IconButton, {
@@ -978,20 +970,33 @@ var LoomaEditor = defineComponent2({
978
970
  return () => {
979
971
  const instance = editor.value;
980
972
  void editorStateVersion.value;
973
+ const tableActions = [
974
+ "align-left",
975
+ "align-center",
976
+ "align-right",
977
+ "background-none",
978
+ "background-gray",
979
+ "background-yellow",
980
+ "background-blue",
981
+ "background-green",
982
+ "background-red",
983
+ "clear-cells",
984
+ ...tableUi.capabilities.canAddRowBefore ? ["add-row-before"] : [],
985
+ ...tableUi.capabilities.canAddRowAfter ? ["add-row-after"] : [],
986
+ ...tableUi.capabilities.canAddColumnBefore ? ["add-column-before"] : [],
987
+ ...tableUi.capabilities.canAddColumnAfter ? ["add-column-after"] : [],
988
+ ...tableUi.capabilities.canMergeCells ? ["merge-cells"] : [],
989
+ ...tableUi.capabilities.canSplitCell ? ["split-cell"] : [],
990
+ ...tableUi.capabilities.canDeleteRow ? ["delete-row"] : [],
991
+ ...tableUi.capabilities.canDeleteColumn ? ["delete-column"] : [],
992
+ ...tableUi.capabilities.canDeleteTable ? ["delete-table"] : []
993
+ ];
981
994
  const tableProps = {
982
995
  open: true,
983
996
  "cell-alignment": tableUi.alignment,
984
997
  "cell-background": tableUi.background ?? void 0,
985
- "can-add-row-before": tableUi.capabilities.canAddRowBefore,
986
- "can-add-row-after": tableUi.capabilities.canAddRowAfter,
987
- "can-add-column-before": tableUi.capabilities.canAddColumnBefore,
988
- "can-add-column-after": tableUi.capabilities.canAddColumnAfter,
989
- "can-delete-row": tableUi.capabilities.canDeleteRow,
990
- "can-delete-column": tableUi.capabilities.canDeleteColumn,
991
- "can-delete-table": tableUi.capabilities.canDeleteTable,
992
- "can-merge-cells": tableUi.capabilities.canMergeCells,
993
- "can-split-cell": tableUi.capabilities.canSplitCell,
994
- onTableAction: runTableAction
998
+ actions: tableActions,
999
+ onAction: runTableAction
995
1000
  };
996
1001
  return h2("div", {
997
1002
  ...attrs,
@@ -1092,7 +1097,7 @@ var LoomaEditor = defineComponent2({
1092
1097
  }
1093
1098
  }, () => [h2(EditorInsertTableGrid, {
1094
1099
  open: true,
1095
- onInsertTable: (detail) => {
1100
+ onInsert: (detail) => {
1096
1101
  instance?.chain().focus().insertTable(detail).run();
1097
1102
  tablePickerOpen.value = false;
1098
1103
  }
@@ -1103,10 +1108,10 @@ var LoomaEditor = defineComponent2({
1103
1108
  items: managedSlashMenuItems(slash.items),
1104
1109
  selectedIndex: slash.selectedIndex,
1105
1110
  anchorRect: managedMenuAnchorRect(slash.rect),
1106
- onSlashMenuHighlight: ({ index }) => {
1111
+ onHighlight: ({ index }) => {
1107
1112
  slash.selectedIndex = index;
1108
1113
  },
1109
- onSlashMenuSelect: ({ index }) => {
1114
+ onSelect: ({ index }) => {
1110
1115
  slash.select?.(index);
1111
1116
  }
1112
1117
  }) : null,
@@ -1118,11 +1123,11 @@ var LoomaEditor = defineComponent2({
1118
1123
  selectedIndex: mention.selectedIndex,
1119
1124
  anchorRect: managedMenuAnchorRect(mention.rect),
1120
1125
  loading: mention.loading,
1121
- onMentionMenuHighlight: ({ index }) => {
1126
+ onHighlight: ({ index }) => {
1122
1127
  mention.selectedIndex = index;
1123
1128
  mention.highlight?.(index);
1124
1129
  },
1125
- onMentionMenuSelect: ({ index }) => {
1130
+ onSelect: ({ index }) => {
1126
1131
  mention.select?.(index);
1127
1132
  }
1128
1133
  }) : null,
@@ -1142,7 +1147,7 @@ var LoomaEditor = defineComponent2({
1142
1147
  rows: tableUi.rows,
1143
1148
  cols: tableUi.cols,
1144
1149
  geometry: tableUi.geometry,
1145
- onTableOverlayAction: runOverlayAction
1150
+ onAction: runOverlayAction
1146
1151
  })]) : null,
1147
1152
  tableUi.menuOpen ? h2("div", {
1148
1153
  ref: tableMenuShell,