@tutti-os/ui-rich-text 0.0.14 → 0.0.16

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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/plugins/mention.ts","../src/plugins/trigger.ts","../src/plugins/triggerRegistry.ts","../src/plugins/mentionRegistry.ts"],"sourcesContent":["import type {\n RichTextMentionAttrs,\n RichTextMentionInsert,\n RichTextMentionPresentation,\n RichTextMentionPlugin,\n RichTextResolvedMention,\n RichTextResolvedMentionView\n} from \"../types/mention.ts\";\n\nconst richTextMentionTrigger = \"@\";\n\nfunction normalizeOptionalString(value?: string | null): string | undefined {\n const trimmed = value?.trim();\n return trimmed ? trimmed : undefined;\n}\n\nfunction normalizeMentionLabel(value: string): string {\n return value.trim().replace(/^@+/, \"\").trim();\n}\n\nfunction normalizeStringRecord(\n values?: Readonly<Record<string, string>> | null\n): Readonly<Record<string, string>> | undefined {\n if (!values) {\n return undefined;\n }\n\n const nextEntries = Object.entries(values)\n .map(([key, value]) => [key.trim(), value.trim()] as const)\n .filter(([key, value]) => key.length > 0 && value.length > 0);\n\n if (nextEntries.length === 0) {\n return undefined;\n }\n\n return Object.freeze(Object.fromEntries(nextEntries));\n}\n\nfunction normalizeMentionPresentation(\n presentation?: RichTextMentionPresentation | null\n): RichTextMentionPresentation | undefined {\n if (!presentation) {\n return undefined;\n }\n\n const normalized: RichTextMentionPresentation = {};\n for (const key of richTextMentionPresentationKeys) {\n const value = normalizeOptionalString(presentation[key]);\n if (value) {\n normalized[key] = value;\n }\n }\n\n return Object.keys(normalized).length > 0\n ? Object.freeze(normalized)\n : undefined;\n}\n\nconst richTextMentionPresentationKeys = [\n \"agentProviderId\",\n \"agentIconUrl\",\n \"iconUrl\",\n \"thumbnailUrl\",\n \"subtitle\",\n \"description\",\n \"participant\",\n \"status\",\n \"statusDataStatus\",\n \"statusLabel\",\n \"statusPulse\",\n \"userAvatarPlaceholderUrl\"\n] as const satisfies readonly (keyof RichTextMentionPresentation)[];\n\nexport function createRichTextMentionAttrs(\n providerId: string,\n mention: RichTextMentionInsert\n): RichTextMentionAttrs {\n const normalizedProviderId = providerId.trim();\n const entityId = mention.entityId.trim();\n const label = normalizeMentionLabel(mention.label);\n\n if (!normalizedProviderId) {\n throw new Error(\"Rich text mention provider id is required.\");\n }\n if (!entityId) {\n throw new Error(\"Rich text mention entityId is required.\");\n }\n if (!label) {\n throw new Error(\"Rich text mention label is required.\");\n }\n\n const attrs: RichTextMentionAttrs = {\n trigger: richTextMentionTrigger,\n providerId: normalizedProviderId,\n entityId,\n label\n };\n const scope = normalizeStringRecord(mention.scope);\n const presentation = normalizeMentionPresentation(mention.presentation);\n if (scope) {\n attrs.scope = scope;\n }\n if (presentation) {\n attrs.presentation = presentation;\n }\n return attrs;\n}\n\nexport function isRichTextMentionAttrs(\n value: unknown\n): value is RichTextMentionAttrs {\n if (!value || typeof value !== \"object\") {\n return false;\n }\n\n const candidate = value as Partial<RichTextMentionAttrs>;\n return (\n candidate.trigger === richTextMentionTrigger &&\n typeof candidate.providerId === \"string\" &&\n candidate.providerId.trim().length > 0 &&\n typeof candidate.entityId === \"string\" &&\n candidate.entityId.trim().length > 0 &&\n typeof candidate.label === \"string\" &&\n candidate.label.trim().length > 0\n );\n}\n\nexport function getRichTextMentionDisplayText(\n attrs: RichTextMentionAttrs\n): string {\n const label = normalizeMentionLabel(attrs.label);\n return label ? `@${label}` : \"\";\n}\n\nexport function resolveRichTextMentionView(\n mention: RichTextMentionAttrs,\n resolved?: RichTextResolvedMention | null\n): RichTextResolvedMentionView {\n const state = resolved?.state ?? \"active\";\n const label = normalizeMentionLabel(resolved?.label ?? mention.label);\n const tooltip = normalizeOptionalString(resolved?.tooltip);\n\n return {\n state,\n label,\n tooltip,\n presentation: normalizeMentionPresentation(\n resolved?.presentation ?? mention.presentation\n ),\n entity: resolved?.entity,\n interactive: state === \"active\"\n };\n}\n\nexport function createRichTextMentionPlugin<TItem, TResolved = unknown>(\n plugin: RichTextMentionPlugin<TItem, TResolved>\n): RichTextMentionPlugin<TItem, TResolved> {\n const id = plugin.id.trim();\n if (!id) {\n throw new Error(\"Rich text mention plugin id is required.\");\n }\n\n return {\n ...plugin,\n id,\n trigger: richTextMentionTrigger\n };\n}\n","import { createRichTextMentionAttrs } from \"./mention.ts\";\nimport {\n createRichTextMentionMarkdown,\n createRichTextLinkMarkdown\n} from \"../core/richTextDocument.ts\";\nimport type {\n RichTextTriggerProvider,\n RichTextMarkdownLinkInsertResult,\n RichTextMentionTriggerInsertResult,\n RichTextTriggerInsertResult,\n RichTextTextInsertResult\n} from \"../types/trigger.ts\";\n\nfunction normalizeRequiredString(value: string, fieldName: string): string {\n const trimmed = value.trim();\n if (!trimmed) {\n throw new Error(`Rich text ${fieldName} is required.`);\n }\n return trimmed;\n}\n\nexport function createRichTextTextInsertResult(\n text: string\n): RichTextTextInsertResult {\n return {\n kind: \"text\",\n text\n };\n}\n\nexport function createRichTextMarkdownLinkInsertResult(\n label: string,\n href: string\n): RichTextMarkdownLinkInsertResult {\n return {\n kind: \"markdown-link\",\n label: normalizeRequiredString(label, \"insert label\"),\n href: normalizeRequiredString(href, \"insert href\")\n };\n}\n\nexport function createRichTextMentionInsertResult(\n mention: RichTextMentionTriggerInsertResult[\"mention\"]\n): RichTextMentionTriggerInsertResult {\n return {\n kind: \"mention\",\n mention\n };\n}\n\nexport function renderRichTextTriggerInsertResult(\n providerId: string,\n result: RichTextTriggerInsertResult\n): string {\n switch (result.kind) {\n case \"mention\":\n return createRichTextMentionMarkdown(\n createRichTextMentionAttrs(providerId, result.mention)\n );\n case \"markdown-link\":\n return createRichTextLinkMarkdown({\n name: result.label,\n path: result.href,\n kind: result.href.endsWith(\"/\") ? \"folder\" : \"file\"\n });\n case \"text\":\n return result.text;\n default:\n return \"\";\n }\n}\n\nexport function createRichTextTriggerProvider<TItem>(\n provider: RichTextTriggerProvider<TItem>\n): RichTextTriggerProvider<TItem> {\n const id = normalizeRequiredString(provider.id, \"provider id\");\n\n return {\n ...provider,\n id\n };\n}\n","import type {\n RichTextTrigger,\n RichTextTriggerBoundary,\n RichTextTriggerConfig,\n RichTextTriggerInsertResult,\n RichTextTriggerProvider,\n RichTextTriggerQueryInput,\n RichTextTriggerQueryMatch,\n RichTextTriggerRegistry\n} from \"../types/trigger.ts\";\n\nfunction normalizeProviderId(providerId: string): string {\n return providerId.trim();\n}\n\nfunction normalizeProviderTrigger(\n provider: RichTextTriggerProvider\n): RichTextTrigger {\n return provider.trigger;\n}\n\nfunction normalizeProviderBoundary(\n provider: RichTextTriggerProvider\n): RichTextTriggerBoundary {\n return provider.boundary ?? \"punctuation\";\n}\n\nfunction normalizeProviderTriggerConfig(\n provider: RichTextTriggerProvider\n): RichTextTriggerConfig {\n return {\n trigger: normalizeProviderTrigger(provider),\n boundary: normalizeProviderBoundary(provider)\n };\n}\n\nfunction normalizeOptionalIconUrl(\n value: string | null | undefined\n): string | undefined {\n const trimmed = value?.trim();\n return trimmed ? trimmed : undefined;\n}\n\nfunction resolveInsertResultIconUrl(\n insertResult: RichTextTriggerInsertResult\n): string | undefined {\n if (insertResult.kind !== \"mention\") {\n return undefined;\n }\n return normalizeOptionalIconUrl(insertResult.mention.presentation?.iconUrl);\n}\n\nasync function resolveItemIconUrl<TItem>(\n provider: RichTextTriggerProvider<TItem>,\n item: TItem,\n insertResult: RichTextTriggerInsertResult\n): Promise<string | undefined> {\n if (provider.getItemIconUrl) {\n try {\n const iconUrl = normalizeOptionalIconUrl(\n await Promise.resolve(provider.getItemIconUrl(item))\n );\n if (iconUrl) {\n return iconUrl;\n }\n } catch {\n return resolveInsertResultIconUrl(insertResult);\n }\n }\n return resolveInsertResultIconUrl(insertResult);\n}\n\nexport function createRichTextTriggerRegistry(\n providers: readonly RichTextTriggerProvider[]\n): RichTextTriggerRegistry {\n const providerMap = new Map<string, RichTextTriggerProvider>();\n const triggerConfigKeys = new Set<string>();\n const triggerConfigs: RichTextTriggerConfig[] = [];\n\n for (const provider of providers) {\n const providerId = normalizeProviderId(provider.id);\n if (!providerId) {\n throw new Error(\"Rich text trigger provider id is required.\");\n }\n if (providerMap.has(providerId)) {\n throw new Error(`Duplicate rich text trigger provider id: ${providerId}`);\n }\n providerMap.set(providerId, provider);\n const triggerConfig = normalizeProviderTriggerConfig(provider);\n const triggerConfigKey = `${triggerConfig.trigger}:${triggerConfig.boundary}`;\n if (!triggerConfigKeys.has(triggerConfigKey)) {\n triggerConfigKeys.add(triggerConfigKey);\n triggerConfigs.push(triggerConfig);\n }\n }\n\n async function query(\n input: RichTextTriggerQueryInput\n ): Promise<readonly RichTextTriggerQueryMatch[]> {\n if (input.abortSignal?.aborted) {\n return [];\n }\n\n const matches = await Promise.all(\n [...providerMap.values()]\n .filter(\n (provider) => normalizeProviderTrigger(provider) === input.trigger\n )\n .map(async (provider) => {\n if (input.abortSignal?.aborted) {\n return [];\n }\n const items = await provider.query(input);\n if (input.abortSignal?.aborted) {\n return [];\n }\n return Promise.all(\n items.map(async (item) => {\n const insertResult = provider.toInsertResult(item);\n return {\n providerId: provider.id,\n trigger: input.trigger,\n key: provider.getItemKey(item),\n label: provider.getItemLabel(item),\n subtitle: provider.getItemSubtitle?.(item) || undefined,\n iconUrl: await resolveItemIconUrl(provider, item, insertResult),\n keywords: provider.getItemKeywords?.(item),\n item,\n insertResult\n };\n })\n );\n })\n );\n\n const flatMatches = matches.flat();\n const limit = input.maxResults;\n if (typeof limit === \"number\" && limit >= 0) {\n return flatMatches.slice(0, limit);\n }\n return flatMatches;\n }\n\n return {\n listProviders: () => [...providerMap.values()],\n getProvider: (providerId: string) =>\n providerMap.get(normalizeProviderId(providerId)),\n listTriggerConfigs: () => [...triggerConfigs],\n query\n };\n}\n","import {\n createRichTextMentionAttrs,\n resolveRichTextMentionView\n} from \"./mention.ts\";\nimport type {\n RichTextMentionPlugin,\n RichTextMentionQueryInput,\n RichTextMentionQueryMatch,\n RichTextMentionRegistry,\n RichTextMentionResolveInput,\n RichTextResolvedMentionView\n} from \"../types/mention.ts\";\n\nfunction normalizePluginId(pluginId: string): string {\n return pluginId.trim();\n}\n\nexport function createRichTextMentionRegistry(\n plugins: readonly RichTextMentionPlugin[]\n): RichTextMentionRegistry {\n const pluginMap = new Map<string, RichTextMentionPlugin>();\n\n for (const plugin of plugins) {\n const pluginId = normalizePluginId(plugin.id);\n if (!pluginId) {\n throw new Error(\"Rich text mention plugin id is required.\");\n }\n if (pluginMap.has(pluginId)) {\n throw new Error(`Duplicate rich text mention plugin id: ${pluginId}`);\n }\n pluginMap.set(pluginId, plugin);\n }\n\n async function query(\n input: RichTextMentionQueryInput\n ): Promise<readonly RichTextMentionQueryMatch[]> {\n const matches = await Promise.all(\n [...pluginMap.values()].map(async (plugin) => {\n const items = await plugin.query(input);\n return items.map<RichTextMentionQueryMatch>((item) => ({\n pluginId: plugin.id,\n key: plugin.getItemKey(item),\n label: plugin.getItemLabel(item),\n subtitle: plugin.getItemSubtitle?.(item) || undefined,\n keywords: plugin.getItemKeywords?.(item),\n item,\n mention: createRichTextMentionAttrs(plugin.id, plugin.toMention(item))\n }));\n })\n );\n\n const flatMatches = matches.flat();\n const limit = input.maxResults;\n if (typeof limit === \"number\" && limit >= 0) {\n return flatMatches.slice(0, limit);\n }\n return flatMatches;\n }\n\n async function resolve(\n input: RichTextMentionResolveInput\n ): Promise<RichTextResolvedMentionView> {\n const plugin = pluginMap.get(input.mention.providerId);\n if (!plugin) {\n return resolveRichTextMentionView(input.mention, {\n state: \"missing\"\n });\n }\n\n if (!plugin.resolveMention) {\n return resolveRichTextMentionView(input.mention, {\n state: \"active\"\n });\n }\n\n return resolveRichTextMentionView(\n input.mention,\n await plugin.resolveMention(input)\n );\n }\n\n return {\n listPlugins: () => [...pluginMap.values()],\n getPlugin: (pluginId: string) => pluginMap.get(normalizePluginId(pluginId)),\n query,\n resolve\n };\n}\n"],"mappings":"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","names":[]}
@@ -28,11 +28,6 @@ interface RichTextTriggerEditorProps {
28
28
  focusSignal?: unknown;
29
29
  menuZIndex?: string | number;
30
30
  }
31
- declare global {
32
- interface Window {
33
- __tuttiRichTextDebugLog?: (event: string, payload: unknown) => void;
34
- }
35
- }
36
31
  declare function RichTextTriggerEditor({ value, onChange, triggerProviders, placeholder, disabled, className, textareaClassName, placeholderClassName, minQueryLength, maxResults, removeDecorationAriaLabel, i18n, textOverrides, overlay, focusSignal, menuZIndex }: RichTextTriggerEditorProps): JSX.Element;
37
32
 
38
33
  interface RichTextTriggerTextareaProps {
@@ -7,7 +7,7 @@ import {
7
7
  getRichTextMentionDisplayText,
8
8
  renderRichTextTriggerInsertResult,
9
9
  resolveRichTextMentionView
10
- } from "../chunk-QK6NMNYN.js";
10
+ } from "../chunk-YFXORJLD.js";
11
11
  import {
12
12
  findRichTextMarkdownLinks,
13
13
  isRichTextMentionHref,
@@ -114,12 +114,14 @@ function resolveRichTextTriggerText(overrides, removeDecorationAriaLabel, i18n =
114
114
  var defaultRichTextTriggerText = resolveRichTextTriggerText();
115
115
 
116
116
  // src/editor/RichTextTriggerMenuItem.tsx
117
+ import { FileIcon, FolderFilledIcon } from "@tutti-os/ui-system/icons";
117
118
  import { jsx, jsxs } from "react/jsx-runtime";
118
119
  function RichTextTriggerMenuItem({
120
+ iconUrl,
119
121
  label,
120
122
  selected,
121
123
  subtitle,
122
- thumbnailUrl,
124
+ workspaceReferenceFileKind,
123
125
  onSelect
124
126
  }) {
125
127
  return /* @__PURE__ */ jsxs(
@@ -137,7 +139,13 @@ function RichTextTriggerMenuItem({
137
139
  onSelect();
138
140
  },
139
141
  children: [
140
- /* @__PURE__ */ jsx(RichTextTriggerMenuThumbnail, { thumbnailUrl }),
142
+ /* @__PURE__ */ jsx(
143
+ RichTextTriggerMenuIcon,
144
+ {
145
+ iconUrl,
146
+ workspaceReferenceFileKind
147
+ }
148
+ ),
141
149
  /* @__PURE__ */ jsxs("span", { className: "flex min-w-0 flex-auto flex-col items-start gap-0.5", children: [
142
150
  /* @__PURE__ */ jsx("span", { className: "w-full overflow-hidden text-ellipsis whitespace-nowrap text-[13px] leading-5 font-semibold", children: label }),
143
151
  subtitle ? /* @__PURE__ */ jsx("span", { className: "w-full overflow-hidden text-ellipsis whitespace-nowrap text-[11px] leading-4 text-[var(--text-secondary)]", children: subtitle }) : null
@@ -146,17 +154,30 @@ function RichTextTriggerMenuItem({
146
154
  }
147
155
  );
148
156
  }
149
- function RichTextTriggerMenuThumbnail({
150
- thumbnailUrl
157
+ function RichTextTriggerMenuIcon({
158
+ iconUrl,
159
+ workspaceReferenceFileKind
151
160
  }) {
152
- const normalizedThumbnailUrl = thumbnailUrl?.trim() ?? "";
161
+ const normalizedIconUrl = iconUrl?.trim() ?? "";
162
+ if (workspaceReferenceFileKind) {
163
+ const Icon = workspaceReferenceFileKind === "folder" ? FolderFilledIcon : FileIcon;
164
+ return /* @__PURE__ */ jsx(
165
+ "span",
166
+ {
167
+ "aria-hidden": "true",
168
+ className: "inline-grid size-4 flex-none place-items-center text-[var(--folder)]",
169
+ "data-rich-text-trigger-icon": "true",
170
+ children: /* @__PURE__ */ jsx(Icon, { className: "size-4" })
171
+ }
172
+ );
173
+ }
153
174
  return /* @__PURE__ */ jsx(
154
175
  "span",
155
176
  {
156
177
  "aria-hidden": "true",
157
178
  className: "inline-grid size-4 flex-none place-items-center overflow-hidden rounded bg-[var(--bg-block,var(--transparency-block))]",
158
- "data-rich-text-trigger-thumbnail": "true",
159
- children: normalizedThumbnailUrl ? /* @__PURE__ */ jsx(
179
+ "data-rich-text-trigger-icon": "true",
180
+ children: normalizedIconUrl ? /* @__PURE__ */ jsx(
160
181
  "img",
161
182
  {
162
183
  alt: "",
@@ -164,7 +185,7 @@ function RichTextTriggerMenuThumbnail({
164
185
  decoding: "async",
165
186
  draggable: false,
166
187
  loading: "lazy",
167
- src: normalizedThumbnailUrl
188
+ src: normalizedIconUrl
168
189
  }
169
190
  ) : /* @__PURE__ */ jsx("span", { className: "block size-3 rounded-[3px] bg-[var(--transparency-block)]" })
170
191
  }
@@ -283,7 +304,10 @@ var MentionReference = Node.create({
283
304
  ];
284
305
  },
285
306
  addNodeView() {
286
- return ReactNodeViewRenderer(MentionReferenceNodeView);
307
+ return ReactNodeViewRenderer(MentionReferenceNodeView, {
308
+ as: "span",
309
+ className: "inline-flex max-w-full align-baseline"
310
+ });
287
311
  }
288
312
  });
289
313
 
@@ -397,7 +421,8 @@ function WorkspaceReferenceNodeView({
397
421
  removable: isEditable,
398
422
  removeButtonProps: isEditable ? {
399
423
  "aria-label": removeActionAriaLabel,
400
- onMouseDown: handleRemove
424
+ onPointerDown: handleRemove,
425
+ tabIndex: -1
401
426
  } : void 0
402
427
  }
403
428
  ) }),
@@ -486,6 +511,20 @@ var WorkspaceReference = Node2.create({
486
511
 
487
512
  // src/editor/RichTextTriggerEditor.tsx
488
513
  import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
514
+ var RICH_TEXT_MENTION_PRESENTATION_KEYS = [
515
+ "agentProviderId",
516
+ "agentIconUrl",
517
+ "iconUrl",
518
+ "thumbnailUrl",
519
+ "subtitle",
520
+ "description",
521
+ "participant",
522
+ "status",
523
+ "statusDataStatus",
524
+ "statusLabel",
525
+ "statusPulse",
526
+ "userAvatarPlaceholderUrl"
527
+ ];
489
528
  function RichTextTriggerEditor({
490
529
  value,
491
530
  onChange,
@@ -652,24 +691,10 @@ function RichTextTriggerEditor({
652
691
  }
653
692
  const updateQueryState = () => {
654
693
  const nextQuery = findEditorAtQuery(editor, activeTriggerConfigs);
655
- logRichTextTriggerDebug("query-state", {
656
- activeTriggerConfigs,
657
- focused: editor.isFocused,
658
- query: nextQuery,
659
- selection: {
660
- empty: editor.state.selection.empty,
661
- from: editor.state.selection.from,
662
- to: editor.state.selection.to
663
- },
664
- textBeforeCursor: readEditorTextBeforeCursor(editor)
665
- });
666
694
  setQuery(nextQuery);
667
695
  };
668
696
  const updateFocus = () => {
669
697
  const nextFocused = editor.isFocused;
670
- logRichTextTriggerDebug("focus-state", {
671
- focused: nextFocused
672
- });
673
698
  setIsFocused(nextFocused);
674
699
  updateQueryState();
675
700
  };
@@ -693,11 +718,6 @@ function RichTextTriggerEditor({
693
718
  return;
694
719
  }
695
720
  if (query.keyword.length < minQueryLength) {
696
- logRichTextTriggerDebug("query-skipped", {
697
- minQueryLength,
698
- query,
699
- reason: "keyword-too-short"
700
- });
701
721
  setMatches([]);
702
722
  setActiveIndex(0);
703
723
  setIsLoading(false);
@@ -705,11 +725,6 @@ function RichTextTriggerEditor({
705
725
  }
706
726
  const abortController = new AbortController();
707
727
  setIsLoading(true);
708
- logRichTextTriggerDebug("query-start", {
709
- maxResults,
710
- minQueryLength,
711
- query
712
- });
713
728
  void queryRichTextTriggerMatches(registry, {
714
729
  abortSignal: abortController.signal,
715
730
  keyword: query.keyword,
@@ -728,16 +743,6 @@ function RichTextTriggerEditor({
728
743
  if (abortController.signal.aborted) {
729
744
  return;
730
745
  }
731
- logRichTextTriggerDebug("query-result", {
732
- matchCount: nextMatches.length,
733
- matches: nextMatches.map((match) => ({
734
- key: match.key,
735
- label: match.label,
736
- providerId: match.providerId,
737
- trigger: match.trigger
738
- })),
739
- query
740
- });
741
746
  setMatches(nextMatches);
742
747
  setActiveIndex(
743
748
  (current) => nextMatches.length === 0 ? 0 : Math.max(0, Math.min(current, nextMatches.length - 1))
@@ -760,10 +765,6 @@ function RichTextTriggerEditor({
760
765
  ]);
761
766
  useLayoutEffect(() => {
762
767
  if (!editor || !query) {
763
- logRichTextTriggerDebug("menu-point-cleared", {
764
- hasEditor: Boolean(editor),
765
- query
766
- });
767
768
  setMenuPoint(null);
768
769
  return;
769
770
  }
@@ -773,16 +774,6 @@ function RichTextTriggerEditor({
773
774
  x: coords.left,
774
775
  y: coords.bottom + menuOffset
775
776
  };
776
- logRichTextTriggerDebug("menu-point", {
777
- coords: {
778
- bottom: coords.bottom,
779
- left: coords.left,
780
- right: coords.right,
781
- top: coords.top
782
- },
783
- menuPoint: nextMenuPoint,
784
- query
785
- });
786
777
  setMenuPoint(nextMenuPoint);
787
778
  };
788
779
  updateMenuPoint();
@@ -799,27 +790,6 @@ function RichTextTriggerEditor({
799
790
  const canQueryTrigger = !!query && activeTriggerConfigs.length > 0 && query.keyword.length >= minQueryLength;
800
791
  const isMenuOpen = canQueryTrigger && (isFocused || !!menuPoint);
801
792
  const isEmpty = !editor || serializeRichTextDocumentToContent(editor.getJSON()).trim().length === 0;
802
- useEffect2(() => {
803
- logRichTextTriggerDebug("menu-state", {
804
- activeIndex,
805
- canQueryTrigger,
806
- isFocused,
807
- isLoading,
808
- isMenuOpen,
809
- matchesLength: matches.length,
810
- menuPoint,
811
- query
812
- });
813
- }, [
814
- activeIndex,
815
- canQueryTrigger,
816
- isFocused,
817
- isLoading,
818
- isMenuOpen,
819
- matches.length,
820
- menuPoint,
821
- query
822
- ]);
823
793
  const applyMatch = (match) => {
824
794
  if (!editor || !query) {
825
795
  return;
@@ -918,7 +888,10 @@ function RichTextTriggerEditor({
918
888
  label: match.label,
919
889
  selected: index === activeIndex,
920
890
  subtitle: match.subtitle,
921
- thumbnailUrl: match.thumbnailUrl,
891
+ iconUrl: match.iconUrl,
892
+ workspaceReferenceFileKind: getWorkspaceReferenceFileKind(
893
+ match.insertResult
894
+ ),
922
895
  onSelect: () => applyMatch(match)
923
896
  },
924
897
  `${match.providerId}:${match.key}`
@@ -936,21 +909,6 @@ function findEditorAtQuery(editor, triggers) {
936
909
  }
937
910
  return query;
938
911
  }
939
- function readEditorTextBeforeCursor(editor) {
940
- const { selection } = editor.state;
941
- if (!selection.empty) {
942
- return null;
943
- }
944
- const { $from } = selection;
945
- if (!$from.parent.isTextblock) {
946
- return null;
947
- }
948
- return $from.parent.textBetween(0, $from.parentOffset, "\n", "\uFFFC");
949
- }
950
- function logRichTextTriggerDebug(event, payload) {
951
- console.info(`[tutti-rich-text-trigger] ${event}`, payload);
952
- window.__tuttiRichTextDebugLog?.(event, payload);
953
- }
954
912
  function findEditorTriggerQuery(editor, triggers) {
955
913
  const { selection } = editor.state;
956
914
  if (!selection.empty) {
@@ -1006,6 +964,12 @@ function renderInsertResultAsEditorContent(providerId, result) {
1006
964
  return null;
1007
965
  }
1008
966
  }
967
+ function getWorkspaceReferenceFileKind(result) {
968
+ if (result.kind !== "markdown-link") {
969
+ return void 0;
970
+ }
971
+ return result.href.endsWith("/") ? "folder" : "file";
972
+ }
1009
973
  function collectHydratableMentionNodes(editor) {
1010
974
  const mentions = [];
1011
975
  editor.state.doc.descendants((node, pos) => {
@@ -1075,13 +1039,7 @@ function normalizeMentionPresentation(value) {
1075
1039
  }
1076
1040
  const source = value;
1077
1041
  const next = {};
1078
- for (const key of [
1079
- "iconUrl",
1080
- "thumbnailUrl",
1081
- "subtitle",
1082
- "description",
1083
- "status"
1084
- ]) {
1042
+ for (const key of RICH_TEXT_MENTION_PRESENTATION_KEYS) {
1085
1043
  const fieldValue = source[key];
1086
1044
  if (typeof fieldValue !== "string") {
1087
1045
  continue;
@@ -1763,7 +1721,7 @@ function RichTextTriggerTextarea({
1763
1721
  label: match.label,
1764
1722
  selected: index === activeIndex,
1765
1723
  subtitle: match.subtitle,
1766
- thumbnailUrl: match.thumbnailUrl,
1724
+ iconUrl: match.iconUrl,
1767
1725
  onSelect: () => applyMatch(match)
1768
1726
  },
1769
1727
  `${match.providerId}:${match.key}`