@threadlabs/looma 0.1.17 → 0.1.18

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 (60) hide show
  1. package/README.md +2 -1
  2. package/dist/{chunk-OY4D3W4U.js → chunk-DTQVRGVL.js} +29 -29
  3. package/dist/cjs/loader.cjs.js +1 -1
  4. package/dist/cjs/looma.cjs.js +1 -1
  5. package/dist/cjs/ui-context-menu.cjs.entry.js +5 -2
  6. package/dist/collection/components/ui-context-menu/ui-context-menu.css +4 -0
  7. package/dist/collection/components/ui-context-menu/ui-context-menu.js +6 -2
  8. package/dist/components/ui-context-menu.js +1 -1
  9. package/dist/esm/loader.js +1 -1
  10. package/dist/esm/looma.js +1 -1
  11. package/dist/esm/ui-context-menu.entry.js +5 -2
  12. package/dist/index.cjs +6 -3
  13. package/dist/index.js +2 -2
  14. package/dist/looma/looma.esm.js +1 -1
  15. package/dist/looma/p-063f5a89.entry.js +1 -0
  16. package/dist/types/components/ui-context-menu/ui-context-menu.d.ts +3 -0
  17. package/dist/{ui-affordance-scope.entry-X7DTKJGL.js → ui-affordance-scope.entry-2W7ZKAGK.js} +1 -1
  18. package/dist/{ui-avatar-group.entry-MC34FKAN.js → ui-avatar-group.entry-SE5JG2FT.js} +1 -1
  19. package/dist/{ui-avatar.entry-OKRQWTQV.js → ui-avatar.entry-46JDI25A.js} +1 -1
  20. package/dist/{ui-badge.entry-RI7JB7KV.js → ui-badge.entry-U7G3MEC5.js} +1 -1
  21. package/dist/{ui-button.entry-PFO5TTPN.js → ui-button.entry-DELD6KMH.js} +1 -1
  22. package/dist/{ui-checkbox.entry-UHFWVISP.js → ui-checkbox.entry-ZUG2P642.js} +1 -1
  23. package/dist/{ui-context-menu.entry-AIXB6U5D.js → ui-context-menu.entry-L4HTK3PV.js} +6 -3
  24. package/dist/{ui-dialog.entry-LPREQH2T.js → ui-dialog.entry-TQPYD2YI.js} +1 -1
  25. package/dist/{ui-disclosure.entry-7XCUIYVZ.js → ui-disclosure.entry-23TII4YU.js} +1 -1
  26. package/dist/{ui-floating-action-button.entry-5PNIMUAW.js → ui-floating-action-button.entry-FOQYAFJI.js} +1 -1
  27. package/dist/{ui-form-field.entry-77YW46SM.js → ui-form-field.entry-SCPG4STY.js} +1 -1
  28. package/dist/{ui-icon-button.entry-4AD6ALPQ.js → ui-icon-button.entry-U45ZVAC3.js} +1 -1
  29. package/dist/{ui-input.entry-E7E6QDTV.js → ui-input.entry-3AIA3G4C.js} +1 -1
  30. package/dist/{ui-menu-item.entry-H3HFLUB6.js → ui-menu-item.entry-3QO5TG2J.js} +1 -1
  31. package/dist/{ui-menu.entry-UAKZU4HG.js → ui-menu.entry-JACWGDDG.js} +1 -1
  32. package/dist/{ui-popover.entry-GVQKX26B.js → ui-popover.entry-2XP4H3S2.js} +1 -1
  33. package/dist/{ui-radio-group.entry-MNSR6UEE.js → ui-radio-group.entry-73IKFMDK.js} +1 -1
  34. package/dist/{ui-radio.entry-6D4WTI5E.js → ui-radio.entry-INJVXUKE.js} +1 -1
  35. package/dist/{ui-search-result-row.entry-J2TILUH2.js → ui-search-result-row.entry-4J732TDC.js} +1 -1
  36. package/dist/{ui-search-shell.entry-FSWHEZQF.js → ui-search-shell.entry-3C2WRVE2.js} +1 -1
  37. package/dist/{ui-select.entry-JRCYYNNW.js → ui-select.entry-3ZLDGAKX.js} +1 -1
  38. package/dist/{ui-switch.entry-YZWMNRP2.js → ui-switch.entry-5MJRLR2P.js} +1 -1
  39. package/dist/{ui-tabs.entry-F2Y344JY.js → ui-tabs.entry-F5H7OPBO.js} +1 -1
  40. package/dist/{ui-textarea.entry-3EZCUUAK.js → ui-textarea.entry-BNJ375JB.js} +1 -1
  41. package/dist/{ui-toast-region.entry-BTV4AW3I.js → ui-toast-region.entry-ZUIPJ2JH.js} +1 -1
  42. package/dist/{ui-tooltip.entry-U644CF5U.js → ui-tooltip.entry-DZ4RMF35.js} +1 -1
  43. package/dist/{ui-top-bar.entry-FGGOREQR.js → ui-top-bar.entry-VKRXIUU6.js} +1 -1
  44. package/dist/{ui-tree-item.entry-XVMHEONY.js → ui-tree-item.entry-NFUQ45OL.js} +1 -1
  45. package/dist/{ui-tree.entry-4R7DYGAM.js → ui-tree.entry-H4CWDYI6.js} +1 -1
  46. package/editor/{chunk-RTHHTPWK.js → chunk-3QMBK3XZ.js} +245 -41
  47. package/editor/{chunk-OHD4OS32.js → chunk-OXFYYLUC.js} +632 -236
  48. package/editor/{chunk-CX4QDKSV.js → chunk-ZOUKEZ77.js} +21 -1
  49. package/editor/extensions/index.d.ts +49 -1
  50. package/editor/extensions/index.js +15 -3
  51. package/editor/index.d.ts +3 -2
  52. package/editor/index.js +16 -4
  53. package/editor/ui.d.ts +9 -1
  54. package/editor/ui.js +2 -2
  55. package/editor.css +170 -0
  56. package/package.json +1 -1
  57. package/styles.css +1 -7
  58. package/vue/editor/index.d.ts +103 -4
  59. package/vue/editor/index.js +137 -63
  60. package/dist/looma/p-ba01f45d.entry.js +0 -1
@@ -16,7 +16,27 @@ var TABLE_CELL_BACKGROUND_OPTIONS = [
16
16
  { action: "background-red", label: "Red", value: TABLE_CELL_BACKGROUND_PRESETS.red, swatch: TABLE_CELL_BACKGROUND_PRESETS.red }
17
17
  ];
18
18
 
19
+ // src/mention-contract.ts
20
+ var DEFAULT_MENTION_RESULT_LIMIT = 8;
21
+ var MAX_MENTION_RESULT_LIMIT = 20;
22
+ function normalizeMentionResultLimit(limit = DEFAULT_MENTION_RESULT_LIMIT) {
23
+ if (!Number.isFinite(limit)) return DEFAULT_MENTION_RESULT_LIMIT;
24
+ return Math.min(MAX_MENTION_RESULT_LIMIT, Math.max(1, Math.trunc(limit)));
25
+ }
26
+ function filterLoomaMentionItems(items, query, limit = DEFAULT_MENTION_RESULT_LIMIT) {
27
+ const normalized = query.trim().toLocaleLowerCase();
28
+ const boundedLimit = normalizeMentionResultLimit(limit);
29
+ return items.filter((item) => {
30
+ if (!normalized) return true;
31
+ return item.label.toLocaleLowerCase().includes(normalized) || item.detail?.toLocaleLowerCase().includes(normalized);
32
+ }).slice(0, boundedLimit);
33
+ }
34
+
19
35
  export {
20
36
  TABLE_CELL_BACKGROUND_PRESETS,
21
- TABLE_CELL_BACKGROUND_OPTIONS
37
+ TABLE_CELL_BACKGROUND_OPTIONS,
38
+ DEFAULT_MENTION_RESULT_LIMIT,
39
+ MAX_MENTION_RESULT_LIMIT,
40
+ normalizeMentionResultLimit,
41
+ filterLoomaMentionItems
22
42
  };
@@ -1,6 +1,7 @@
1
1
  import { AnyExtension, Editor, Range, Extension } from '@tiptap/core';
2
2
  import { d as TableContextMenuActionEventDetail, g as TableOverlayActionEventDetail } from '../table-overlay-DoiLie4h.js';
3
3
  export { a as TABLE_CELL_BACKGROUND_PRESETS } from '../table-overlay-DoiLie4h.js';
4
+ import { PluginKey } from '@tiptap/pm/state';
4
5
  import { LoomaIconName } from '@threadlabs/looma/core';
5
6
 
6
7
  /**
@@ -12,6 +13,7 @@ interface DefaultEditorExtensionsOptions {
12
13
  placeholder?: string;
13
14
  linkOpenOnClick?: boolean;
14
15
  imageInline?: boolean;
16
+ mention?: AnyExtension | false;
15
17
  }
16
18
  /** Complete Looma table support as one Tiptap extension. */
17
19
  declare const LoomaTableKit: AnyExtension;
@@ -93,6 +95,52 @@ declare function normalizeActiveTableColumnWidths(editor: Editor, tableElement:
93
95
  */
94
96
  declare function handleTableOverlayAction(editor: Editor, detail: TableOverlayActionEventDetail): boolean;
95
97
 
98
+ declare const DEFAULT_MENTION_RESULT_LIMIT = 8;
99
+ declare const MAX_MENTION_RESULT_LIMIT = 20;
100
+ interface LoomaMentionItem {
101
+ id: string;
102
+ label: string;
103
+ detail?: string;
104
+ initials?: string;
105
+ }
106
+ interface LoomaMentionProviderContext {
107
+ limit: number;
108
+ }
109
+ type LoomaMentionProvider = (query: string, context: LoomaMentionProviderContext) => readonly LoomaMentionItem[] | Promise<readonly LoomaMentionItem[]>;
110
+ interface LoomaMentionMenuSnapshot {
111
+ active: boolean;
112
+ items: LoomaMentionItem[];
113
+ selectedIndex: number;
114
+ query: string;
115
+ rect: DOMRect | null;
116
+ loading: boolean;
117
+ highlight: ((index: number) => void) | null;
118
+ select: ((index: number) => void) | null;
119
+ }
120
+ interface LoomaMentionOptions {
121
+ items?: readonly LoomaMentionItem[] | LoomaMentionProvider;
122
+ limit?: number;
123
+ menuId?: string;
124
+ onStateChange?: (state: LoomaMentionMenuSnapshot) => void;
125
+ }
126
+ declare function normalizeMentionResultLimit(limit?: number): number;
127
+ declare function filterLoomaMentionItems(items: readonly LoomaMentionItem[], query: string, limit?: number): LoomaMentionItem[];
128
+
129
+ interface MentionPluginState {
130
+ active?: boolean;
131
+ query?: string | null;
132
+ range?: {
133
+ from: number;
134
+ to: number;
135
+ };
136
+ }
137
+ declare const LoomaMentionSuggestionPluginKey: PluginKey<MentionPluginState>;
138
+ /**
139
+ * Domain-neutral Tiptap mention node and suggestion lifecycle. Applications
140
+ * provide either a small static list or a bounded async directory provider.
141
+ */
142
+ declare function createLoomaMentionExtension(options?: LoomaMentionOptions): AnyExtension;
143
+
96
144
  interface LoomaSlashCommandContext {
97
145
  editor: Editor;
98
146
  range: Range;
@@ -126,4 +174,4 @@ interface LoomaSlashCommand {
126
174
  declare const LoomaSlashCommand: Extension<LoomaSlashCommandOptions, any>;
127
175
  declare function createLoomaSlashCommandExtension(options?: Partial<LoomaSlashCommandOptions>): Extension<LoomaSlashCommandOptions, any>;
128
176
 
129
- export { type ActiveTableUiState, type DefaultEditorExtensionsOptions, type InsertTableAtRangeOptions, LoomaSlashCommand, type LoomaSlashCommandContext, LoomaSlashCommand as LoomaSlashCommandItem, type LoomaSlashCommandOptions, type LoomaSlashMenuSnapshot, LoomaTable, LoomaTableCell, LoomaTableHeader, LoomaTableKit, type TableActionCapabilities, type TableCellAlignment, type TableCellBackground, createLoomaSlashCommandExtension, getActiveTableCellAlignment, getActiveTableCellBackground, getActiveTableUiState, getDefaultEditorExtensions, getDefaultSlashCommands, getLoomaTableExtensions, handleTableAction, handleTableOverlayAction, insertTableAtRange, normalizeActiveTableColumnWidths, setActiveTableCellAlignment, setActiveTableCellBackground, shouldShowTextFormattingToolbar };
177
+ export { type ActiveTableUiState, DEFAULT_MENTION_RESULT_LIMIT, type DefaultEditorExtensionsOptions, type InsertTableAtRangeOptions, type LoomaMentionItem, type LoomaMentionMenuSnapshot, type LoomaMentionOptions, type LoomaMentionProvider, type LoomaMentionProviderContext, LoomaMentionSuggestionPluginKey, LoomaSlashCommand, type LoomaSlashCommandContext, LoomaSlashCommand as LoomaSlashCommandItem, type LoomaSlashCommandOptions, type LoomaSlashMenuSnapshot, LoomaTable, LoomaTableCell, LoomaTableHeader, LoomaTableKit, MAX_MENTION_RESULT_LIMIT, type TableActionCapabilities, type TableCellAlignment, type TableCellBackground, createLoomaMentionExtension, createLoomaSlashCommandExtension, filterLoomaMentionItems, getActiveTableCellAlignment, getActiveTableCellBackground, getActiveTableUiState, getDefaultEditorExtensions, getDefaultSlashCommands, getLoomaTableExtensions, handleTableAction, handleTableOverlayAction, insertTableAtRange, normalizeActiveTableColumnWidths, normalizeMentionResultLimit, setActiveTableCellAlignment, setActiveTableCellBackground, shouldShowTextFormattingToolbar };
@@ -1,9 +1,11 @@
1
1
  import {
2
+ LoomaMentionSuggestionPluginKey,
2
3
  LoomaSlashCommand,
3
4
  LoomaTable,
4
5
  LoomaTableCell,
5
6
  LoomaTableHeader,
6
7
  LoomaTableKit,
8
+ createLoomaMentionExtension,
7
9
  createLoomaSlashCommandExtension,
8
10
  getActiveTableCellAlignment,
9
11
  getActiveTableCellBackground,
@@ -18,18 +20,27 @@ import {
18
20
  setActiveTableCellAlignment,
19
21
  setActiveTableCellBackground,
20
22
  shouldShowTextFormattingToolbar
21
- } from "../chunk-OHD4OS32.js";
23
+ } from "../chunk-OXFYYLUC.js";
22
24
  import {
23
- TABLE_CELL_BACKGROUND_PRESETS
24
- } from "../chunk-CX4QDKSV.js";
25
+ DEFAULT_MENTION_RESULT_LIMIT,
26
+ MAX_MENTION_RESULT_LIMIT,
27
+ TABLE_CELL_BACKGROUND_PRESETS,
28
+ filterLoomaMentionItems,
29
+ normalizeMentionResultLimit
30
+ } from "../chunk-ZOUKEZ77.js";
25
31
  export {
32
+ DEFAULT_MENTION_RESULT_LIMIT,
33
+ LoomaMentionSuggestionPluginKey,
26
34
  LoomaSlashCommand,
27
35
  LoomaTable,
28
36
  LoomaTableCell,
29
37
  LoomaTableHeader,
30
38
  LoomaTableKit,
39
+ MAX_MENTION_RESULT_LIMIT,
31
40
  TABLE_CELL_BACKGROUND_PRESETS,
41
+ createLoomaMentionExtension,
32
42
  createLoomaSlashCommandExtension,
43
+ filterLoomaMentionItems,
33
44
  getActiveTableCellAlignment,
34
45
  getActiveTableCellBackground,
35
46
  getActiveTableUiState,
@@ -40,6 +51,7 @@ export {
40
51
  handleTableOverlayAction,
41
52
  insertTableAtRange,
42
53
  normalizeActiveTableColumnWidths,
54
+ normalizeMentionResultLimit,
43
55
  setActiveTableCellAlignment,
44
56
  setActiveTableCellBackground,
45
57
  shouldShowTextFormattingToolbar
package/editor/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  export { A as ActiveCellRect, T as TABLE_CELL_BACKGROUND_OPTIONS, a as TABLE_CELL_BACKGROUND_PRESETS, b as TableCellBackgroundAction, c as TableContextMenuAction, d as TableContextMenuActionEventDetail, e as TableInsertionAction, f as TableOverlayAction, g as TableOverlayActionEventDetail, h as TableOverlayGeometry, d as TableToolbarActionEventDetail, m as measureTableOverlayGeometry, r as resolveTableCellAt } from './table-overlay-DoiLie4h.js';
2
- export { InsertTableEventDetail, SlashMenuAnchorRect, SlashMenuHighlightEventDetail, SlashMenuItem, SlashMenuSelectEventDetail } from './ui.js';
3
- export { ActiveTableUiState, DefaultEditorExtensionsOptions, InsertTableAtRangeOptions, LoomaSlashCommand, LoomaSlashCommandContext, LoomaSlashCommand as LoomaSlashCommandItem, LoomaSlashCommandOptions, LoomaSlashMenuSnapshot, LoomaTable, LoomaTableCell, LoomaTableHeader, LoomaTableKit, TableActionCapabilities, TableCellAlignment, TableCellBackground, createLoomaSlashCommandExtension, getActiveTableCellAlignment, getActiveTableCellBackground, getActiveTableUiState, getDefaultEditorExtensions, getDefaultSlashCommands, getLoomaTableExtensions, handleTableAction, handleTableOverlayAction, insertTableAtRange, normalizeActiveTableColumnWidths, setActiveTableCellAlignment, setActiveTableCellBackground, shouldShowTextFormattingToolbar } from './extensions/index.js';
2
+ export { InsertTableEventDetail, MentionMenuHighlightEventDetail, MentionMenuSelectEventDetail, SlashMenuAnchorRect, SlashMenuHighlightEventDetail, SlashMenuItem, SlashMenuSelectEventDetail } from './ui.js';
3
+ export { ActiveTableUiState, DEFAULT_MENTION_RESULT_LIMIT, DefaultEditorExtensionsOptions, InsertTableAtRangeOptions, LoomaMentionItem, LoomaMentionMenuSnapshot, LoomaMentionOptions, LoomaMentionProvider, LoomaMentionProviderContext, LoomaMentionSuggestionPluginKey, LoomaSlashCommand, LoomaSlashCommandContext, LoomaSlashCommand as LoomaSlashCommandItem, LoomaSlashCommandOptions, LoomaSlashMenuSnapshot, LoomaTable, LoomaTableCell, LoomaTableHeader, LoomaTableKit, MAX_MENTION_RESULT_LIMIT, TableActionCapabilities, TableCellAlignment, TableCellBackground, createLoomaMentionExtension, createLoomaSlashCommandExtension, filterLoomaMentionItems, getActiveTableCellAlignment, getActiveTableCellBackground, getActiveTableUiState, getDefaultEditorExtensions, getDefaultSlashCommands, getLoomaTableExtensions, handleTableAction, handleTableOverlayAction, insertTableAtRange, normalizeActiveTableColumnWidths, normalizeMentionResultLimit, setActiveTableCellAlignment, setActiveTableCellBackground, shouldShowTextFormattingToolbar } from './extensions/index.js';
4
4
  import '@threadlabs/looma/core';
5
5
  import '@tiptap/core';
6
+ import '@tiptap/pm/state';
package/editor/index.js CHANGED
@@ -1,13 +1,15 @@
1
1
  import {
2
2
  measureTableOverlayGeometry,
3
3
  resolveTableCellAt
4
- } from "./chunk-RTHHTPWK.js";
4
+ } from "./chunk-3QMBK3XZ.js";
5
5
  import {
6
+ LoomaMentionSuggestionPluginKey,
6
7
  LoomaSlashCommand,
7
8
  LoomaTable,
8
9
  LoomaTableCell,
9
10
  LoomaTableHeader,
10
11
  LoomaTableKit,
12
+ createLoomaMentionExtension,
11
13
  createLoomaSlashCommandExtension,
12
14
  getActiveTableCellAlignment,
13
15
  getActiveTableCellBackground,
@@ -22,20 +24,29 @@ import {
22
24
  setActiveTableCellAlignment,
23
25
  setActiveTableCellBackground,
24
26
  shouldShowTextFormattingToolbar
25
- } from "./chunk-OHD4OS32.js";
27
+ } from "./chunk-OXFYYLUC.js";
26
28
  import {
29
+ DEFAULT_MENTION_RESULT_LIMIT,
30
+ MAX_MENTION_RESULT_LIMIT,
27
31
  TABLE_CELL_BACKGROUND_OPTIONS,
28
- TABLE_CELL_BACKGROUND_PRESETS
29
- } from "./chunk-CX4QDKSV.js";
32
+ TABLE_CELL_BACKGROUND_PRESETS,
33
+ filterLoomaMentionItems,
34
+ normalizeMentionResultLimit
35
+ } from "./chunk-ZOUKEZ77.js";
30
36
  export {
37
+ DEFAULT_MENTION_RESULT_LIMIT,
38
+ LoomaMentionSuggestionPluginKey,
31
39
  LoomaSlashCommand,
32
40
  LoomaTable,
33
41
  LoomaTableCell,
34
42
  LoomaTableHeader,
35
43
  LoomaTableKit,
44
+ MAX_MENTION_RESULT_LIMIT,
36
45
  TABLE_CELL_BACKGROUND_OPTIONS,
37
46
  TABLE_CELL_BACKGROUND_PRESETS,
47
+ createLoomaMentionExtension,
38
48
  createLoomaSlashCommandExtension,
49
+ filterLoomaMentionItems,
39
50
  getActiveTableCellAlignment,
40
51
  getActiveTableCellBackground,
41
52
  getActiveTableUiState,
@@ -47,6 +58,7 @@ export {
47
58
  insertTableAtRange,
48
59
  measureTableOverlayGeometry,
49
60
  normalizeActiveTableColumnWidths,
61
+ normalizeMentionResultLimit,
50
62
  resolveTableCellAt,
51
63
  setActiveTableCellAlignment,
52
64
  setActiveTableCellBackground,
package/editor/ui.d.ts CHANGED
@@ -12,6 +12,14 @@ interface InsertTableEventDetail {
12
12
  withHeaderRow: boolean;
13
13
  }
14
14
 
15
+ /** ui-editor-mention-menu — bounded, accessible mention suggestions. */
16
+ interface MentionMenuHighlightEventDetail {
17
+ index: number;
18
+ }
19
+ interface MentionMenuSelectEventDetail {
20
+ index: number;
21
+ }
22
+
15
23
  /**
16
24
  * ui-editor-slash-menu — floating slash-command menu.
17
25
  * Apps own the command list and suggestion state; Looma owns the chrome.
@@ -37,4 +45,4 @@ interface SlashMenuSelectEventDetail {
37
45
  */
38
46
  type SlashMenuAnchorRect = Pick<DOMRectReadOnly, "left" | "top" | "right" | "bottom"> | Pick<DOMRectReadOnly, "x" | "y" | "width" | "height">;
39
47
 
40
- export type { InsertTableEventDetail, SlashMenuAnchorRect, SlashMenuHighlightEventDetail, SlashMenuItem, SlashMenuSelectEventDetail };
48
+ export type { InsertTableEventDetail, MentionMenuHighlightEventDetail, MentionMenuSelectEventDetail, SlashMenuAnchorRect, SlashMenuHighlightEventDetail, SlashMenuItem, SlashMenuSelectEventDetail };
package/editor/ui.js CHANGED
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  measureTableOverlayGeometry,
3
3
  resolveTableCellAt
4
- } from "./chunk-RTHHTPWK.js";
4
+ } from "./chunk-3QMBK3XZ.js";
5
5
  import {
6
6
  TABLE_CELL_BACKGROUND_OPTIONS,
7
7
  TABLE_CELL_BACKGROUND_PRESETS
8
- } from "./chunk-CX4QDKSV.js";
8
+ } from "./chunk-ZOUKEZ77.js";
9
9
  export {
10
10
  TABLE_CELL_BACKGROUND_OPTIONS,
11
11
  TABLE_CELL_BACKGROUND_PRESETS,
package/editor.css CHANGED
@@ -384,6 +384,127 @@ ui-editor-slash-menu {
384
384
  user-select: none;
385
385
  }
386
386
 
387
+ /* Mention menu */
388
+ ui-editor-mention-menu {
389
+ user-select: none;
390
+ }
391
+
392
+ .ui-editor-mention-menu {
393
+ display: flex;
394
+ overflow: hidden;
395
+ flex-direction: column;
396
+ background: var(--ui-surface, #fff);
397
+ border: 1px solid var(--ui-border, #e5e7eb);
398
+ border-radius: var(--ui-radius-xl, 1rem);
399
+ box-shadow: var(--ui-shadow-lg, 0 8px 24px rgb(0 0 0 / 0.12));
400
+ }
401
+
402
+ .ui-editor-mention-menu__header {
403
+ display: flex;
404
+ align-items: center;
405
+ justify-content: space-between;
406
+ gap: var(--ui-space-3, 12px);
407
+ padding: var(--ui-space-2, 8px) var(--ui-space-3, 12px);
408
+ color: var(--ui-text-secondary, #4b5563);
409
+ border-bottom: 1px solid var(--ui-border, #e5e7eb);
410
+ font-size: var(--ui-font-size-xs, 0.75rem);
411
+ font-weight: var(--ui-font-semibold, 600);
412
+ }
413
+
414
+ .ui-editor-mention-menu__hint {
415
+ overflow: hidden;
416
+ color: var(--ui-text-muted, #6b7280);
417
+ font-weight: var(--ui-font-normal, 400);
418
+ text-overflow: ellipsis;
419
+ white-space: nowrap;
420
+ }
421
+
422
+ .ui-editor-mention-menu__list {
423
+ max-height: 280px;
424
+ padding: var(--ui-space-1, 4px) 0;
425
+ margin: 0;
426
+ overflow-y: auto;
427
+ overscroll-behavior: contain;
428
+ list-style: none;
429
+ }
430
+
431
+ .ui-editor-mention-menu__item {
432
+ display: flex;
433
+ align-items: center;
434
+ min-height: 44px;
435
+ padding: var(--ui-space-1-5, 6px) var(--ui-space-3, 12px);
436
+ gap: var(--ui-space-3, 12px);
437
+ cursor: pointer;
438
+ }
439
+
440
+ .ui-editor-mention-menu__item--active {
441
+ background: var(--ui-accent-subtle, rgb(59 130 246 / 0.1));
442
+ }
443
+
444
+ .ui-editor-mention-menu__avatar {
445
+ display: inline-flex;
446
+ align-items: center;
447
+ justify-content: center;
448
+ width: 32px;
449
+ height: 32px;
450
+ flex: 0 0 auto;
451
+ color: var(--ui-accent-active, var(--ui-accent-solid, #2563eb));
452
+ background: var(--ui-accent-soft, var(--ui-accent-subtle, #eff6ff));
453
+ border-radius: 999px;
454
+ font-size: var(--ui-font-size-xs, 0.75rem);
455
+ font-weight: var(--ui-font-semibold, 600);
456
+ }
457
+
458
+ .ui-editor-mention-menu__body {
459
+ display: flex;
460
+ min-width: 0;
461
+ flex-direction: column;
462
+ }
463
+
464
+ .ui-editor-mention-menu__label,
465
+ .ui-editor-mention-menu__detail {
466
+ overflow: hidden;
467
+ text-overflow: ellipsis;
468
+ white-space: nowrap;
469
+ }
470
+
471
+ .ui-editor-mention-menu__label {
472
+ color: var(--ui-text-primary, #111827);
473
+ font-size: var(--ui-font-size-sm, 0.875rem);
474
+ font-weight: var(--ui-font-medium, 500);
475
+ }
476
+
477
+ .ui-editor-mention-menu__detail,
478
+ .ui-editor-mention-menu__loading {
479
+ color: var(--ui-text-muted, #6b7280);
480
+ font-size: var(--ui-font-size-xs, 0.75rem);
481
+ }
482
+
483
+ .ui-editor-mention-menu__loading {
484
+ min-height: 44px;
485
+ padding: var(--ui-space-3, 12px);
486
+ }
487
+
488
+ .looma-editor .ProseMirror .looma-mention {
489
+ padding: 0.08em 0.28em;
490
+ color: var(--ui-editor-mention-color, var(--ui-accent-active, var(--ui-accent-solid)));
491
+ background: var(--ui-editor-mention-bg, var(--ui-accent-soft, var(--ui-accent-subtle)));
492
+ border-radius: var(--ui-radius-sm, 4px);
493
+ font-weight: var(--ui-font-medium, 500);
494
+ white-space: nowrap;
495
+ }
496
+
497
+ @media (max-width: 767px) {
498
+ .ui-editor-mention-menu {
499
+ border-bottom: 0;
500
+ border-radius: var(--ui-radius-xl, 1rem) var(--ui-radius-xl, 1rem) 0 0;
501
+ }
502
+
503
+ .ui-editor-mention-menu__list {
504
+ max-height: calc(60dvh - 48px);
505
+ }
506
+ }
507
+
387
508
  /* Toolbar shell */
388
509
  ui-editor-toolbar {
389
510
  position: sticky;
@@ -1177,11 +1298,39 @@ ui-editor-table-overlay {
1177
1298
  border-radius: var(--ui-radius-round, 999px);
1178
1299
  }
1179
1300
 
1301
+ .ui-editor-table-overlay__selector::before {
1302
+ content: "";
1303
+ position: absolute;
1304
+ inset: 50% auto auto 50%;
1305
+ border-radius: var(--ui-radius-round, 999px);
1306
+ background: var(--ui-affordance-guide-color, var(--ui-border-strong, #c8c8c4));
1307
+ opacity: var(--ui-affordance-guide-opacity, 0.42);
1308
+ transform: translate(-50%, -50%);
1309
+ transition: opacity var(--ui-affordance-motion, var(--ui-motion-fast, 120ms)) var(--ui-motion-ease, ease);
1310
+ pointer-events: none;
1311
+ }
1312
+
1180
1313
  .ui-editor-table-overlay__selector .looma-icon {
1181
1314
  display: block;
1182
1315
  width: 14px;
1183
1316
  height: 14px;
1184
1317
  stroke-width: var(--ui-editor-table-affordance-icon-stroke-width, 2);
1318
+ opacity: 0;
1319
+ transform: scale(0.72);
1320
+ transition:
1321
+ opacity var(--ui-affordance-motion, var(--ui-motion-fast, 120ms)) var(--ui-motion-ease, ease),
1322
+ transform var(--ui-affordance-motion, var(--ui-motion-fast, 120ms)) var(--ui-motion-ease, ease);
1323
+ }
1324
+
1325
+ .ui-editor-table-overlay__selector:hover::before,
1326
+ .ui-editor-table-overlay__selector:focus-visible::before {
1327
+ opacity: 0;
1328
+ }
1329
+
1330
+ .ui-editor-table-overlay__selector:hover .looma-icon,
1331
+ .ui-editor-table-overlay__selector:focus-visible .looma-icon {
1332
+ opacity: 1;
1333
+ transform: scale(1);
1185
1334
  }
1186
1335
 
1187
1336
  .ui-editor-table-overlay__selector--row {
@@ -1191,6 +1340,11 @@ ui-editor-table-overlay {
1191
1340
  transform: translateY(-50%);
1192
1341
  }
1193
1342
 
1343
+ .ui-editor-table-overlay__selector--row::before {
1344
+ width: 2px;
1345
+ height: 16px;
1346
+ }
1347
+
1194
1348
  .ui-editor-table-overlay__selector--column {
1195
1349
  top: -9px;
1196
1350
  width: 32px;
@@ -1198,6 +1352,22 @@ ui-editor-table-overlay {
1198
1352
  transform: translateX(-50%);
1199
1353
  }
1200
1354
 
1355
+ .ui-editor-table-overlay__selector--column::before {
1356
+ width: 16px;
1357
+ height: 2px;
1358
+ }
1359
+
1360
+ @media (hover: none), (pointer: coarse) {
1361
+ .ui-editor-table-overlay__selector::before {
1362
+ opacity: 0;
1363
+ }
1364
+
1365
+ .ui-editor-table-overlay__selector .looma-icon {
1366
+ opacity: 1;
1367
+ transform: none;
1368
+ }
1369
+ }
1370
+
1201
1371
  .ui-editor-table-overlay__cell-menu {
1202
1372
  width: 24px;
1203
1373
  height: 24px;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@threadlabs/looma",
3
- "version": "0.1.17",
3
+ "version": "0.1.18",
4
4
  "type": "module",
5
5
  "description": "Looma web components, responsive layout primitives, a complete editor, and supported Vue adapters.",
6
6
  "license": "MIT",
package/styles.css CHANGED
@@ -446,13 +446,7 @@
446
446
  }
447
447
 
448
448
  ui-context-menu {
449
- position: fixed;
450
- z-index: var(--ui-z-overlay, 1000);
451
- min-width: 12rem;
452
- }
453
-
454
- ui-context-menu:not([data-open]) {
455
- display: none !important;
449
+ display: contents;
456
450
  }
457
451
 
458
452
  ui-tabs {
@@ -1,12 +1,14 @@
1
- import { SlashMenuItem, SlashMenuAnchorRect } from '@threadlabs/looma/editor';
1
+ import { LoomaMentionItem, SlashMenuAnchorRect, SlashMenuItem, LoomaMentionProvider } from '@threadlabs/looma/editor';
2
2
  export * from '@threadlabs/looma/editor';
3
3
  import * as vue from 'vue';
4
4
  import { PropType } from 'vue';
5
- import { TableContextMenuActionEventDetail, InsertTableEventDetail, TableOverlayActionEventDetail, SlashMenuHighlightEventDetail, SlashMenuSelectEventDetail } from '@threadlabs/looma/editor/ui';
5
+ import { MentionMenuHighlightEventDetail, MentionMenuSelectEventDetail, TableContextMenuActionEventDetail, InsertTableEventDetail, TableOverlayActionEventDetail, SlashMenuHighlightEventDetail, SlashMenuSelectEventDetail } 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
9
  interface VueEditorAdapterEventMap {
10
+ mentionMenuHighlight: MentionMenuHighlightEventDetail;
11
+ mentionMenuSelect: MentionMenuSelectEventDetail;
10
12
  tableAction: TableContextMenuActionEventDetail;
11
13
  insertTable: InsertTableEventDetail;
12
14
  tableOverlayAction: TableOverlayActionEventDetail;
@@ -34,6 +36,10 @@ declare const EditorSlashMenu: vue.DefineComponent<vue.ExtractPropTypes<{
34
36
  type: PropType<SlashMenuAnchorRect | null>;
35
37
  default: null;
36
38
  };
39
+ loading: {
40
+ type: BooleanConstructor;
41
+ default: boolean;
42
+ };
37
43
  }>, () => vue.VNode<vue.RendererNode, vue.RendererElement, {
38
44
  [key: string]: any;
39
45
  }>, {}, {}, {}, vue.ComponentOptionsMixin, vue.ComponentOptionsMixin, {}, string, vue.PublicProps, Readonly<vue.ExtractPropTypes<{
@@ -57,12 +63,77 @@ declare const EditorSlashMenu: vue.DefineComponent<vue.ExtractPropTypes<{
57
63
  type: PropType<SlashMenuAnchorRect | null>;
58
64
  default: null;
59
65
  };
66
+ loading: {
67
+ type: BooleanConstructor;
68
+ default: boolean;
69
+ };
60
70
  }>> & Readonly<{}>, {
61
71
  open: boolean;
62
- query: string;
63
72
  items: SlashMenuItem[];
73
+ query: string;
64
74
  selectedIndex: number;
65
75
  anchorRect: SlashMenuAnchorRect | null;
76
+ loading: boolean;
77
+ }, {}, {}, {}, string, vue.ComponentProvideOptions, true, {}, any>;
78
+ declare const EditorMentionMenu: vue.DefineComponent<vue.ExtractPropTypes<{
79
+ open: {
80
+ type: BooleanConstructor;
81
+ default: boolean;
82
+ };
83
+ query: {
84
+ type: StringConstructor;
85
+ default: string;
86
+ };
87
+ items: {
88
+ type: PropType<LoomaMentionItem[]>;
89
+ default: () => never[];
90
+ };
91
+ selectedIndex: {
92
+ type: NumberConstructor;
93
+ default: number;
94
+ };
95
+ anchorRect: {
96
+ type: PropType<SlashMenuAnchorRect | null>;
97
+ default: null;
98
+ };
99
+ loading: {
100
+ type: BooleanConstructor;
101
+ default: boolean;
102
+ };
103
+ }>, () => vue.VNode<vue.RendererNode, vue.RendererElement, {
104
+ [key: string]: any;
105
+ }>, {}, {}, {}, vue.ComponentOptionsMixin, vue.ComponentOptionsMixin, {}, string, vue.PublicProps, Readonly<vue.ExtractPropTypes<{
106
+ open: {
107
+ type: BooleanConstructor;
108
+ default: boolean;
109
+ };
110
+ query: {
111
+ type: StringConstructor;
112
+ default: string;
113
+ };
114
+ items: {
115
+ type: PropType<LoomaMentionItem[]>;
116
+ default: () => never[];
117
+ };
118
+ selectedIndex: {
119
+ type: NumberConstructor;
120
+ default: number;
121
+ };
122
+ anchorRect: {
123
+ type: PropType<SlashMenuAnchorRect | null>;
124
+ default: null;
125
+ };
126
+ loading: {
127
+ type: BooleanConstructor;
128
+ default: boolean;
129
+ };
130
+ }>> & Readonly<{}>, {
131
+ open: boolean;
132
+ items: LoomaMentionItem[];
133
+ query: string;
134
+ selectedIndex: number;
135
+ anchorRect: SlashMenuAnchorRect | null;
136
+ loading: boolean;
66
137
  }, {}, {}, {}, string, vue.ComponentProvideOptions, true, {}, any>;
67
138
  declare const EditorToolbar: vue.DefineComponent<{}, () => vue.VNode<vue.RendererNode, vue.RendererElement, {
68
139
  [key: string]: any;
@@ -82,6 +153,7 @@ declare const EditorTableOverlay: vue.DefineComponent<{}, () => vue.VNode<vue.Re
82
153
  declare const EDITOR_ADAPTER_COMPONENT_TAG_MAP: {
83
154
  readonly EditorToolbar: "ui-editor-toolbar";
84
155
  readonly EditorSlashMenu: "ui-editor-slash-menu";
156
+ readonly EditorMentionMenu: "ui-editor-mention-menu";
85
157
  readonly EditorTableContextMenu: "ui-editor-table-context-menu";
86
158
  readonly EditorTableToolbar: "ui-editor-table-toolbar";
87
159
  readonly EditorInsertTableGrid: "ui-editor-insert-table-grid";
@@ -145,6 +217,18 @@ declare const LoomaEditor: vue.DefineComponent<vue.ExtractPropTypes<{
145
217
  type: PropType<AnyExtension[]>;
146
218
  default: () => never[];
147
219
  };
220
+ mentionItems: {
221
+ type: PropType<LoomaMentionItem[]>;
222
+ default: () => never[];
223
+ };
224
+ mentionProvider: {
225
+ type: PropType<LoomaMentionProvider | undefined>;
226
+ default: undefined;
227
+ };
228
+ mentionLimit: {
229
+ type: NumberConstructor;
230
+ default: number;
231
+ };
148
232
  uploadImage: {
149
233
  type: PropType<LoomaImageUploader | undefined>;
150
234
  default: undefined;
@@ -190,6 +274,18 @@ declare const LoomaEditor: vue.DefineComponent<vue.ExtractPropTypes<{
190
274
  type: PropType<AnyExtension[]>;
191
275
  default: () => never[];
192
276
  };
277
+ mentionItems: {
278
+ type: PropType<LoomaMentionItem[]>;
279
+ default: () => never[];
280
+ };
281
+ mentionProvider: {
282
+ type: PropType<LoomaMentionProvider | undefined>;
283
+ default: undefined;
284
+ };
285
+ mentionLimit: {
286
+ type: NumberConstructor;
287
+ default: number;
288
+ };
193
289
  uploadImage: {
194
290
  type: PropType<LoomaImageUploader | undefined>;
195
291
  default: undefined;
@@ -210,8 +306,11 @@ declare const LoomaEditor: vue.DefineComponent<vue.ExtractPropTypes<{
210
306
  modelValue: JSONContent;
211
307
  editable: boolean;
212
308
  extensions: AnyExtension[];
309
+ mentionItems: LoomaMentionItem[];
310
+ mentionProvider: LoomaMentionProvider | undefined;
311
+ mentionLimit: number;
213
312
  uploadImage: LoomaImageUploader | undefined;
214
313
  resolveImageAttributes: LoomaImageAttributeResolver | undefined;
215
314
  }, {}, {}, {}, string, vue.ComponentProvideOptions, true, {}, any>;
216
315
 
217
- export { EDITOR_ADAPTER_COMPONENT_TAG_MAP, EditorInsertTableGrid, EditorSlashMenu, EditorTableContextMenu, EditorTableOverlay, EditorTableToolbar, EditorToolbar, LoomaEditor, type LoomaImageActivationDetail, type LoomaImageActivationTrigger, type LoomaImageAttributeResolver, type LoomaImageDescriptor, type LoomaImageRenditionAttributes, type LoomaImageRenditionErrorDetail, type LoomaImageUploadResult, type LoomaImageUploader, type VueEditorAdapterEventMap };
316
+ export { EDITOR_ADAPTER_COMPONENT_TAG_MAP, EditorInsertTableGrid, EditorMentionMenu, EditorSlashMenu, EditorTableContextMenu, EditorTableOverlay, EditorTableToolbar, EditorToolbar, LoomaEditor, type LoomaImageActivationDetail, type LoomaImageActivationTrigger, type LoomaImageAttributeResolver, type LoomaImageDescriptor, type LoomaImageRenditionAttributes, type LoomaImageRenditionErrorDetail, type LoomaImageUploadResult, type LoomaImageUploader, type VueEditorAdapterEventMap };