@tutti-os/ui-rich-text 0.0.35 → 0.0.37
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -0
- package/dist/at-panel/index.css +4 -0
- package/dist/at-panel/index.css.map +1 -1
- package/dist/at-panel/index.js +2 -2
- package/dist/at-panel/index.js.map +1 -1
- package/dist/at-panel/model.d.ts +1 -1
- package/dist/at-panel/model.js +1 -1
- package/dist/{chunk-DWBETNOQ.js → chunk-HMAUQU6Q.js} +7 -1
- package/dist/{chunk-DWBETNOQ.js.map → chunk-HMAUQU6Q.js.map} +1 -1
- package/dist/editor/index.d.ts +8 -2
- package/dist/editor/index.js +172 -33
- package/dist/editor/index.js.map +1 -1
- package/package.json +3 -3
package/dist/at-panel/model.d.ts
CHANGED
|
@@ -191,7 +191,7 @@ declare function resolveMentionFileThumbnailUrl(input: {
|
|
|
191
191
|
* presentational mapping so the styling stays in one place and identical across
|
|
192
192
|
* surfaces.
|
|
193
193
|
*/
|
|
194
|
-
type MentionRowStatusTone = "amber" | "blue" | "green" | "neutral" | "red";
|
|
194
|
+
type MentionRowStatusTone = "amber" | "blue" | "green" | "neutral" | "purple" | "red";
|
|
195
195
|
type MentionRowStatusVariant = "activity" | "issue";
|
|
196
196
|
/**
|
|
197
197
|
* Text classes for agent activity status signals. Activity rows already include
|
package/dist/at-panel/model.js
CHANGED
|
@@ -190,8 +190,14 @@ function activityMentionStatusBadgeClassName(tone) {
|
|
|
190
190
|
}
|
|
191
191
|
function issueMentionStatusBadgeClassName(tone) {
|
|
192
192
|
switch (tone) {
|
|
193
|
+
case "blue":
|
|
194
|
+
return "bg-[color:color-mix(in_srgb,var(--status-running)_12%,transparent)] text-[var(--status-running)]";
|
|
195
|
+
case "amber":
|
|
196
|
+
return "bg-[color:color-mix(in_srgb,var(--state-warning)_12%,transparent)] text-[var(--state-warning)]";
|
|
193
197
|
case "green":
|
|
194
198
|
return "bg-[color:color-mix(in_srgb,var(--state-success)_12%,transparent)] text-[var(--state-success)]";
|
|
199
|
+
case "purple":
|
|
200
|
+
return "bg-[color-mix(in_srgb,var(--rich-text-mention-issue)_12%,transparent)] text-[var(--rich-text-mention-issue)]";
|
|
195
201
|
case "red":
|
|
196
202
|
return "bg-[var(--on-danger)] text-[var(--state-danger)]";
|
|
197
203
|
default:
|
|
@@ -265,4 +271,4 @@ export {
|
|
|
265
271
|
resolveMentionFileVisualKind,
|
|
266
272
|
resolveMentionFileThumbnailUrl
|
|
267
273
|
};
|
|
268
|
-
//# sourceMappingURL=chunk-
|
|
274
|
+
//# sourceMappingURL=chunk-HMAUQU6Q.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/at-panel/mentionPaletteEntries.ts","../src/at-panel/mentionPaletteModel.ts","../src/at-panel/mentionStatusTone.ts","../src/at-panel/mentionFileVisualKind.ts"],"sourcesContent":["import type {\n MentionPaletteEntry,\n MentionPaletteState\n} from \"./mentionPaletteTypes.ts\";\n\n/**\n * Returns true when at least one group has items or a \"load more\" trigger,\n * i.e. there is something interactive to render beyond category headers.\n */\nfunction hasInteractiveGroupEntries<TItem>(\n groups: MentionPaletteState<TItem>[\"groups\"]\n): boolean {\n return groups.some((group) => group.items.length > 0 || group.hasMore);\n}\n\n/**\n * Flatten the palette state into a stable, ordered list of navigable entries.\n *\n * Mirrors the ordering and key format of `flattenAgentMentionPaletteEntries`\n * from AgentFileMentionPalette.tsx, but is generic over item type.\n *\n * Key formats:\n * category entry → `category:<categoryId>`\n * item entry → `<groupId>:<getItemKey(item, groupId)>`\n * expand entry → `expand:<groupId>`\n */\nexport function flattenMentionPaletteEntries<TItem>(\n state: MentionPaletteState<TItem>,\n getItemKey: (item: TItem, groupId: string) => string\n): MentionPaletteEntry[] {\n // Browse mode with no interactive group content → show category nav only\n if (state.mode === \"browse\" && !hasInteractiveGroupEntries(state.groups)) {\n return state.categories.map((category) => ({\n key: `category:${category.id}`,\n type: \"category\" as const,\n categoryId: category.id\n }));\n }\n\n const entries: MentionPaletteEntry[] = [];\n\n for (const group of state.groups) {\n group.items.forEach((item, index) => {\n entries.push({\n key: `${group.id}:${getItemKey(item, group.id)}`,\n type: \"item\",\n groupId: group.id,\n itemIndex: index\n });\n });\n\n if (group.hasMore) {\n entries.push({\n key: `expand:${group.id}`,\n type: \"expand\",\n groupId: group.id\n });\n }\n }\n\n return entries;\n}\n","import type {\n MentionPaletteCategory,\n MentionPaletteEntry,\n MentionPaletteGroup,\n MentionPaletteState\n} from \"./mentionPaletteTypes.ts\";\nimport { flattenMentionPaletteEntries } from \"./mentionPaletteEntries.ts\";\nimport type { RichTextTriggerQueryMatch } from \"../types/trigger.ts\";\n\nexport interface MentionPaletteModelInput<TItem> {\n activeCategoryId: string;\n categories: readonly MentionPaletteCategory[];\n groups: readonly MentionPaletteGroup<TItem>[];\n loading: boolean;\n query?: string;\n mode?: \"browse\" | \"results\";\n}\n\nexport interface MentionPaletteSectionConfig<\n TMatch extends RichTextTriggerQueryMatch = RichTextTriggerQueryMatch\n> {\n id: string;\n label?: string;\n providerIds?: readonly string[];\n matches?: (match: TMatch) => boolean;\n emptyLabel?: string;\n}\n\nexport interface MentionPaletteCategoryConfig<\n TMatch extends RichTextTriggerQueryMatch = RichTextTriggerQueryMatch\n> {\n id: string;\n label: string;\n providerIds?: readonly string[];\n matches?: (match: TMatch) => boolean;\n emptyLabel?: string;\n sections?: readonly MentionPaletteSectionConfig<TMatch>[];\n}\n\nexport function mentionPaletteGroup<TItem>(input: {\n id: string;\n label?: string;\n items: readonly TItem[];\n emptyLabel?: string;\n}): MentionPaletteGroup<TItem> {\n return {\n id: input.id,\n label: input.label,\n items: input.items,\n totalCount: input.items.length,\n visibleCount: input.items.length,\n hasMore: false,\n emptyLabel: input.emptyLabel\n };\n}\n\nexport function buildMentionPaletteModel<TItem>(\n input: MentionPaletteModelInput<TItem>\n): MentionPaletteState<TItem> {\n return {\n status: input.loading ? \"loading\" : \"ready\",\n query: input.query ?? \"\",\n mode: input.mode ?? \"results\",\n filter: input.activeCategoryId,\n categories: input.categories,\n groups: input.groups,\n error: null\n };\n}\n\nexport function buildMentionPaletteModelFromTriggerMatches<\n TMatch extends RichTextTriggerQueryMatch = RichTextTriggerQueryMatch\n>(input: {\n activeCategoryId: string;\n categories: readonly MentionPaletteCategoryConfig<TMatch>[];\n matches: readonly TMatch[];\n loading: boolean;\n query?: string;\n mode?: \"browse\" | \"results\";\n}): MentionPaletteState<TMatch> {\n const categories = input.categories.map((category) => ({\n id: category.id,\n label: category.label\n }));\n const activeCategory =\n input.categories.find(\n (category) => category.id === input.activeCategoryId\n ) ??\n input.categories[0] ??\n null;\n const groups =\n activeCategory === null\n ? []\n : buildMentionPaletteGroupsForCategory(activeCategory, input.matches);\n\n return buildMentionPaletteModel({\n activeCategoryId: activeCategory?.id ?? input.activeCategoryId,\n categories,\n groups,\n loading: input.loading,\n query: input.query,\n mode: input.mode\n });\n}\n\nfunction buildMentionPaletteGroupsForCategory<\n TMatch extends RichTextTriggerQueryMatch\n>(\n category: MentionPaletteCategoryConfig<TMatch>,\n matches: readonly TMatch[]\n): MentionPaletteGroup<TMatch>[] {\n const categoryMatches = matches.filter((match) =>\n mentionPaletteConfigMatches(category, match)\n );\n if (!category.sections?.length) {\n if (categoryMatches.length === 0 && category.emptyLabel == null) {\n return [];\n }\n return [\n mentionPaletteGroup({\n id: category.id,\n items: categoryMatches,\n emptyLabel: category.emptyLabel\n })\n ];\n }\n\n const sectionItems = new Map<string, TMatch[]>(\n category.sections.map((section) => [section.id, []])\n );\n for (const match of categoryMatches) {\n const section = category.sections.find((candidate) =>\n mentionPaletteConfigMatches(candidate, match)\n );\n if (!section) {\n continue;\n }\n sectionItems.get(section.id)?.push(match);\n }\n\n return category.sections\n .map((section) => {\n const items = sectionItems.get(section.id) ?? [];\n if (items.length === 0 && section.emptyLabel == null) {\n return null;\n }\n return mentionPaletteGroup({\n id: section.id,\n label: section.label,\n items,\n emptyLabel: section.emptyLabel\n });\n })\n .filter((group): group is MentionPaletteGroup<TMatch> => group !== null);\n}\n\nfunction mentionPaletteConfigMatches<TMatch extends RichTextTriggerQueryMatch>(\n config: {\n providerIds?: readonly string[];\n matches?: (match: TMatch) => boolean;\n },\n match: TMatch\n): boolean {\n if (\n config.providerIds != null &&\n !config.providerIds.includes(match.providerId)\n ) {\n return false;\n }\n return config.matches?.(match) ?? true;\n}\n\nexport function moveMentionPaletteHighlight<TItem>(input: {\n state: MentionPaletteState<TItem>;\n currentKey: string | null;\n delta: 1 | -1;\n getItemKey: (item: TItem, groupId: string) => string;\n}): string | null {\n const entries = flattenMentionPaletteEntries(\n input.state,\n input.getItemKey\n ).filter(\n (entry) =>\n entry.type === \"category\" ||\n entry.type === \"item\" ||\n entry.type === \"expand\"\n );\n if (!entries.length) {\n return null;\n }\n const foundIndex = entries.findIndex(\n (entry) => entry.key === input.currentKey\n );\n const currentIndex = foundIndex >= 0 ? foundIndex : input.delta > 0 ? -1 : 0;\n return (\n entries[(currentIndex + input.delta + entries.length) % entries.length]\n ?.key ?? null\n );\n}\n\nexport function repairMentionPaletteHighlight<TItem>(input: {\n state: MentionPaletteState<TItem>;\n currentKey: string | null;\n getItemKey: (item: TItem, groupId: string) => string;\n preferredKey?: string | null;\n}): string | null {\n const entries = flattenMentionPaletteEntries(input.state, input.getItemKey);\n if (entries.length === 0) {\n return null;\n }\n if (\n input.currentKey !== null &&\n entries.some((entry) => entry.key === input.currentKey)\n ) {\n return input.currentKey;\n }\n if (\n input.preferredKey != null &&\n entries.some((entry) => entry.key === input.preferredKey)\n ) {\n return input.preferredKey;\n }\n return entries[0]?.key ?? null;\n}\n\nexport function findMentionPaletteEntry<TItem>(input: {\n state: MentionPaletteState<TItem>;\n key: string | null;\n getItemKey: (item: TItem, groupId: string) => string;\n}): MentionPaletteEntry | null {\n if (input.key === null) {\n return null;\n }\n return (\n flattenMentionPaletteEntries(input.state, input.getItemKey).find(\n (entry) => entry.key === input.key\n ) ?? null\n );\n}\n\nexport function selectedMentionPaletteItem<TItem>(input: {\n state: MentionPaletteState<TItem>;\n key: string | null;\n getItemKey: (item: TItem, groupId: string) => string;\n}): TItem | null {\n for (const group of input.state.groups) {\n const index = group.items.findIndex(\n (candidate) =>\n `${group.id}:${input.getItemKey(candidate, group.id)}` === input.key\n );\n if (index >= 0) {\n return group.items[index] ?? null;\n }\n }\n return null;\n}\n\nexport function nextMentionPaletteCategory<TCategoryId extends string>(\n categories: readonly { id: TCategoryId }[],\n current: TCategoryId,\n delta: 1 | -1\n): TCategoryId {\n const index = categories.findIndex((category) => category.id === current);\n const safeIndex = index >= 0 ? index : 0;\n return categories[\n (safeIndex + delta + categories.length) % categories.length\n ]!.id;\n}\n","/**\n * Tone → className maps for the {@link MentionStatusBadge} rendered inside a\n * {@link MentionRow}. The surface resolves a status into a data-only\n * {@link MentionRowStatusTag} (label + tone + variant); this module owns the\n * presentational mapping so the styling stays in one place and identical across\n * surfaces.\n */\nexport type MentionRowStatusTone =\n | \"amber\"\n | \"blue\"\n | \"green\"\n | \"neutral\"\n | \"red\";\n\nexport type MentionRowStatusVariant = \"activity\" | \"issue\";\n\n/**\n * Text classes for agent activity status signals. Activity rows already include\n * a status dot, so they render as plain signal text instead of a filled badge.\n */\nexport function activityMentionStatusBadgeClassName(\n tone: MentionRowStatusTone\n): string {\n switch (tone) {\n case \"blue\":\n return \"bg-transparent px-0 text-[var(--status-running)]\";\n case \"amber\":\n return \"bg-transparent px-0 text-[var(--state-warning)]\";\n case \"green\":\n return \"bg-transparent px-0 text-[var(--state-success)]\";\n case \"red\":\n return \"bg-transparent px-0 text-[var(--state-danger)]\";\n default:\n return \"bg-transparent px-0 text-[var(--text-secondary)]\";\n }\n}\n\n/**\n * Background/text classes for the issue status badge. Ported verbatim from the\n * agent's `issueMentionStatusBadgeClassName` (keyed by the issue tone the\n * surface resolves).\n */\nexport function issueMentionStatusBadgeClassName(\n tone: MentionRowStatusTone\n): string {\n switch (tone) {\n case \"green\":\n return \"bg-[color:color-mix(in_srgb,var(--state-success)_12%,transparent)] text-[var(--state-success)]\";\n case \"red\":\n return \"bg-[var(--on-danger)] text-[var(--state-danger)]\";\n default:\n return \"bg-[var(--transparency-block)] text-[var(--text-secondary)]\";\n }\n}\n\nexport function mentionStatusBadgeClassName(input: {\n tone: MentionRowStatusTone;\n variant: MentionRowStatusVariant;\n}): string {\n return input.variant === \"issue\"\n ? issueMentionStatusBadgeClassName(input.tone)\n : activityMentionStatusBadgeClassName(input.tone);\n}\n\n/**\n * Map a normalized agent-activity display status to its badge tone. Shared by\n * every `@`-mention surface that renders a session row (agent composer,\n * issue-manager) so the activity status badge color is identical across\n * surfaces. Mirrors the agent composer's local `mentionStatusTone` mapping\n * verbatim (the agent keeps its own copy producing identical values). The label\n * is resolved by each surface; only the tone lives here.\n */\nexport function activityMentionStatusTone(\n status: string\n): MentionRowStatusTone {\n switch (status.trim().toLowerCase()) {\n case \"working\":\n return \"blue\";\n case \"waiting\":\n case \"canceled\":\n return \"amber\";\n case \"completed\":\n case \"idle\":\n return \"green\";\n case \"failed\":\n return \"red\";\n default:\n return \"neutral\";\n }\n}\n\n/**\n * Map an issue status string to its badge tone. Shared by every `@`-mention\n * surface that renders an issue row (agent composer, issue-manager) so the\n * status badge color is identical across surfaces. The label is resolved by\n * each surface's own i18n; only the tone lives here.\n */\nexport function issueMentionStatusTone(status: string): MentionRowStatusTone {\n switch (status.trim().toLowerCase()) {\n case \"completed\":\n return \"green\";\n case \"failed\":\n case \"canceled\":\n return \"red\";\n default:\n return \"neutral\";\n }\n}\n","/**\n * Pure, dependency-free file visual-kind helpers shared by every `@`-mention\n * surface that renders a {@link MentionRow}. The *base* extension → kind\n * resolution lives in each surface (the agent maps file-manager kinds, etc.);\n * this module owns only the surface-agnostic vocabulary and the thumbnail rule\n * so the shared row renderer never imports a workspace feature.\n */\nexport type MentionFileVisualKind =\n | \"back\"\n | \"document\"\n | \"code\"\n | \"markdown\"\n | \"image\"\n | \"video\"\n | \"folder\";\n\nexport interface MentionFileVisualKindInput {\n entryKind?: string | null;\n mentionNavigation?: string | null;\n /**\n * The base visual kind already resolved from the file extension/name by the\n * surface (e.g. the agent's file-manager mapping). Used as the fallback when\n * the entry is neither a back-navigation marker nor a directory.\n */\n baseVisualKind: MentionFileVisualKind;\n}\n\n/**\n * Resolve the row's visual kind from a pre-resolved {@link MentionFileVisualKindInput.baseVisualKind}\n * plus the structural markers (back navigation, directory) that override it.\n */\nexport function resolveMentionFileVisualKind(\n input: MentionFileVisualKindInput\n): MentionFileVisualKind {\n if (input.mentionNavigation === \"agent-generated-folder-back\") {\n return \"back\";\n }\n if (input.entryKind === \"directory\") {\n return \"folder\";\n }\n return input.baseVisualKind;\n}\n\n/**\n * The thumbnail is only shown for image entries with a non-empty thumbnail URL.\n */\nexport function resolveMentionFileThumbnailUrl(input: {\n visualKind: MentionFileVisualKind;\n thumbnailUrl?: string | null;\n}): string | undefined {\n if (input.visualKind !== \"image\") {\n return undefined;\n }\n const thumbnailUrl = input.thumbnailUrl?.trim() ?? \"\";\n return thumbnailUrl || undefined;\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/at-panel/mentionPaletteEntries.ts","../src/at-panel/mentionPaletteModel.ts","../src/at-panel/mentionStatusTone.ts","../src/at-panel/mentionFileVisualKind.ts"],"sourcesContent":["import type {\n MentionPaletteEntry,\n MentionPaletteState\n} from \"./mentionPaletteTypes.ts\";\n\n/**\n * Returns true when at least one group has items or a \"load more\" trigger,\n * i.e. there is something interactive to render beyond category headers.\n */\nfunction hasInteractiveGroupEntries<TItem>(\n groups: MentionPaletteState<TItem>[\"groups\"]\n): boolean {\n return groups.some((group) => group.items.length > 0 || group.hasMore);\n}\n\n/**\n * Flatten the palette state into a stable, ordered list of navigable entries.\n *\n * Mirrors the ordering and key format of `flattenAgentMentionPaletteEntries`\n * from AgentFileMentionPalette.tsx, but is generic over item type.\n *\n * Key formats:\n * category entry → `category:<categoryId>`\n * item entry → `<groupId>:<getItemKey(item, groupId)>`\n * expand entry → `expand:<groupId>`\n */\nexport function flattenMentionPaletteEntries<TItem>(\n state: MentionPaletteState<TItem>,\n getItemKey: (item: TItem, groupId: string) => string\n): MentionPaletteEntry[] {\n // Browse mode with no interactive group content → show category nav only\n if (state.mode === \"browse\" && !hasInteractiveGroupEntries(state.groups)) {\n return state.categories.map((category) => ({\n key: `category:${category.id}`,\n type: \"category\" as const,\n categoryId: category.id\n }));\n }\n\n const entries: MentionPaletteEntry[] = [];\n\n for (const group of state.groups) {\n group.items.forEach((item, index) => {\n entries.push({\n key: `${group.id}:${getItemKey(item, group.id)}`,\n type: \"item\",\n groupId: group.id,\n itemIndex: index\n });\n });\n\n if (group.hasMore) {\n entries.push({\n key: `expand:${group.id}`,\n type: \"expand\",\n groupId: group.id\n });\n }\n }\n\n return entries;\n}\n","import type {\n MentionPaletteCategory,\n MentionPaletteEntry,\n MentionPaletteGroup,\n MentionPaletteState\n} from \"./mentionPaletteTypes.ts\";\nimport { flattenMentionPaletteEntries } from \"./mentionPaletteEntries.ts\";\nimport type { RichTextTriggerQueryMatch } from \"../types/trigger.ts\";\n\nexport interface MentionPaletteModelInput<TItem> {\n activeCategoryId: string;\n categories: readonly MentionPaletteCategory[];\n groups: readonly MentionPaletteGroup<TItem>[];\n loading: boolean;\n query?: string;\n mode?: \"browse\" | \"results\";\n}\n\nexport interface MentionPaletteSectionConfig<\n TMatch extends RichTextTriggerQueryMatch = RichTextTriggerQueryMatch\n> {\n id: string;\n label?: string;\n providerIds?: readonly string[];\n matches?: (match: TMatch) => boolean;\n emptyLabel?: string;\n}\n\nexport interface MentionPaletteCategoryConfig<\n TMatch extends RichTextTriggerQueryMatch = RichTextTriggerQueryMatch\n> {\n id: string;\n label: string;\n providerIds?: readonly string[];\n matches?: (match: TMatch) => boolean;\n emptyLabel?: string;\n sections?: readonly MentionPaletteSectionConfig<TMatch>[];\n}\n\nexport function mentionPaletteGroup<TItem>(input: {\n id: string;\n label?: string;\n items: readonly TItem[];\n emptyLabel?: string;\n}): MentionPaletteGroup<TItem> {\n return {\n id: input.id,\n label: input.label,\n items: input.items,\n totalCount: input.items.length,\n visibleCount: input.items.length,\n hasMore: false,\n emptyLabel: input.emptyLabel\n };\n}\n\nexport function buildMentionPaletteModel<TItem>(\n input: MentionPaletteModelInput<TItem>\n): MentionPaletteState<TItem> {\n return {\n status: input.loading ? \"loading\" : \"ready\",\n query: input.query ?? \"\",\n mode: input.mode ?? \"results\",\n filter: input.activeCategoryId,\n categories: input.categories,\n groups: input.groups,\n error: null\n };\n}\n\nexport function buildMentionPaletteModelFromTriggerMatches<\n TMatch extends RichTextTriggerQueryMatch = RichTextTriggerQueryMatch\n>(input: {\n activeCategoryId: string;\n categories: readonly MentionPaletteCategoryConfig<TMatch>[];\n matches: readonly TMatch[];\n loading: boolean;\n query?: string;\n mode?: \"browse\" | \"results\";\n}): MentionPaletteState<TMatch> {\n const categories = input.categories.map((category) => ({\n id: category.id,\n label: category.label\n }));\n const activeCategory =\n input.categories.find(\n (category) => category.id === input.activeCategoryId\n ) ??\n input.categories[0] ??\n null;\n const groups =\n activeCategory === null\n ? []\n : buildMentionPaletteGroupsForCategory(activeCategory, input.matches);\n\n return buildMentionPaletteModel({\n activeCategoryId: activeCategory?.id ?? input.activeCategoryId,\n categories,\n groups,\n loading: input.loading,\n query: input.query,\n mode: input.mode\n });\n}\n\nfunction buildMentionPaletteGroupsForCategory<\n TMatch extends RichTextTriggerQueryMatch\n>(\n category: MentionPaletteCategoryConfig<TMatch>,\n matches: readonly TMatch[]\n): MentionPaletteGroup<TMatch>[] {\n const categoryMatches = matches.filter((match) =>\n mentionPaletteConfigMatches(category, match)\n );\n if (!category.sections?.length) {\n if (categoryMatches.length === 0 && category.emptyLabel == null) {\n return [];\n }\n return [\n mentionPaletteGroup({\n id: category.id,\n items: categoryMatches,\n emptyLabel: category.emptyLabel\n })\n ];\n }\n\n const sectionItems = new Map<string, TMatch[]>(\n category.sections.map((section) => [section.id, []])\n );\n for (const match of categoryMatches) {\n const section = category.sections.find((candidate) =>\n mentionPaletteConfigMatches(candidate, match)\n );\n if (!section) {\n continue;\n }\n sectionItems.get(section.id)?.push(match);\n }\n\n return category.sections\n .map((section) => {\n const items = sectionItems.get(section.id) ?? [];\n if (items.length === 0 && section.emptyLabel == null) {\n return null;\n }\n return mentionPaletteGroup({\n id: section.id,\n label: section.label,\n items,\n emptyLabel: section.emptyLabel\n });\n })\n .filter((group): group is MentionPaletteGroup<TMatch> => group !== null);\n}\n\nfunction mentionPaletteConfigMatches<TMatch extends RichTextTriggerQueryMatch>(\n config: {\n providerIds?: readonly string[];\n matches?: (match: TMatch) => boolean;\n },\n match: TMatch\n): boolean {\n if (\n config.providerIds != null &&\n !config.providerIds.includes(match.providerId)\n ) {\n return false;\n }\n return config.matches?.(match) ?? true;\n}\n\nexport function moveMentionPaletteHighlight<TItem>(input: {\n state: MentionPaletteState<TItem>;\n currentKey: string | null;\n delta: 1 | -1;\n getItemKey: (item: TItem, groupId: string) => string;\n}): string | null {\n const entries = flattenMentionPaletteEntries(\n input.state,\n input.getItemKey\n ).filter(\n (entry) =>\n entry.type === \"category\" ||\n entry.type === \"item\" ||\n entry.type === \"expand\"\n );\n if (!entries.length) {\n return null;\n }\n const foundIndex = entries.findIndex(\n (entry) => entry.key === input.currentKey\n );\n const currentIndex = foundIndex >= 0 ? foundIndex : input.delta > 0 ? -1 : 0;\n return (\n entries[(currentIndex + input.delta + entries.length) % entries.length]\n ?.key ?? null\n );\n}\n\nexport function repairMentionPaletteHighlight<TItem>(input: {\n state: MentionPaletteState<TItem>;\n currentKey: string | null;\n getItemKey: (item: TItem, groupId: string) => string;\n preferredKey?: string | null;\n}): string | null {\n const entries = flattenMentionPaletteEntries(input.state, input.getItemKey);\n if (entries.length === 0) {\n return null;\n }\n if (\n input.currentKey !== null &&\n entries.some((entry) => entry.key === input.currentKey)\n ) {\n return input.currentKey;\n }\n if (\n input.preferredKey != null &&\n entries.some((entry) => entry.key === input.preferredKey)\n ) {\n return input.preferredKey;\n }\n return entries[0]?.key ?? null;\n}\n\nexport function findMentionPaletteEntry<TItem>(input: {\n state: MentionPaletteState<TItem>;\n key: string | null;\n getItemKey: (item: TItem, groupId: string) => string;\n}): MentionPaletteEntry | null {\n if (input.key === null) {\n return null;\n }\n return (\n flattenMentionPaletteEntries(input.state, input.getItemKey).find(\n (entry) => entry.key === input.key\n ) ?? null\n );\n}\n\nexport function selectedMentionPaletteItem<TItem>(input: {\n state: MentionPaletteState<TItem>;\n key: string | null;\n getItemKey: (item: TItem, groupId: string) => string;\n}): TItem | null {\n for (const group of input.state.groups) {\n const index = group.items.findIndex(\n (candidate) =>\n `${group.id}:${input.getItemKey(candidate, group.id)}` === input.key\n );\n if (index >= 0) {\n return group.items[index] ?? null;\n }\n }\n return null;\n}\n\nexport function nextMentionPaletteCategory<TCategoryId extends string>(\n categories: readonly { id: TCategoryId }[],\n current: TCategoryId,\n delta: 1 | -1\n): TCategoryId {\n const index = categories.findIndex((category) => category.id === current);\n const safeIndex = index >= 0 ? index : 0;\n return categories[\n (safeIndex + delta + categories.length) % categories.length\n ]!.id;\n}\n","/**\n * Tone → className maps for the {@link MentionStatusBadge} rendered inside a\n * {@link MentionRow}. The surface resolves a status into a data-only\n * {@link MentionRowStatusTag} (label + tone + variant); this module owns the\n * presentational mapping so the styling stays in one place and identical across\n * surfaces.\n */\nexport type MentionRowStatusTone =\n | \"amber\"\n | \"blue\"\n | \"green\"\n | \"neutral\"\n | \"purple\"\n | \"red\";\n\nexport type MentionRowStatusVariant = \"activity\" | \"issue\";\n\n/**\n * Text classes for agent activity status signals. Activity rows already include\n * a status dot, so they render as plain signal text instead of a filled badge.\n */\nexport function activityMentionStatusBadgeClassName(\n tone: MentionRowStatusTone\n): string {\n switch (tone) {\n case \"blue\":\n return \"bg-transparent px-0 text-[var(--status-running)]\";\n case \"amber\":\n return \"bg-transparent px-0 text-[var(--state-warning)]\";\n case \"green\":\n return \"bg-transparent px-0 text-[var(--state-success)]\";\n case \"red\":\n return \"bg-transparent px-0 text-[var(--state-danger)]\";\n default:\n return \"bg-transparent px-0 text-[var(--text-secondary)]\";\n }\n}\n\n/**\n * Background/text classes for the issue status badge. Ported verbatim from the\n * agent's `issueMentionStatusBadgeClassName` (keyed by the issue tone the\n * surface resolves).\n */\nexport function issueMentionStatusBadgeClassName(\n tone: MentionRowStatusTone\n): string {\n switch (tone) {\n case \"blue\":\n return \"bg-[color:color-mix(in_srgb,var(--status-running)_12%,transparent)] text-[var(--status-running)]\";\n case \"amber\":\n return \"bg-[color:color-mix(in_srgb,var(--state-warning)_12%,transparent)] text-[var(--state-warning)]\";\n case \"green\":\n return \"bg-[color:color-mix(in_srgb,var(--state-success)_12%,transparent)] text-[var(--state-success)]\";\n case \"purple\":\n return \"bg-[color-mix(in_srgb,var(--rich-text-mention-issue)_12%,transparent)] text-[var(--rich-text-mention-issue)]\";\n case \"red\":\n return \"bg-[var(--on-danger)] text-[var(--state-danger)]\";\n default:\n return \"bg-[var(--transparency-block)] text-[var(--text-secondary)]\";\n }\n}\n\nexport function mentionStatusBadgeClassName(input: {\n tone: MentionRowStatusTone;\n variant: MentionRowStatusVariant;\n}): string {\n return input.variant === \"issue\"\n ? issueMentionStatusBadgeClassName(input.tone)\n : activityMentionStatusBadgeClassName(input.tone);\n}\n\n/**\n * Map a normalized agent-activity display status to its badge tone. Shared by\n * every `@`-mention surface that renders a session row (agent composer,\n * issue-manager) so the activity status badge color is identical across\n * surfaces. Mirrors the agent composer's local `mentionStatusTone` mapping\n * verbatim (the agent keeps its own copy producing identical values). The label\n * is resolved by each surface; only the tone lives here.\n */\nexport function activityMentionStatusTone(\n status: string\n): MentionRowStatusTone {\n switch (status.trim().toLowerCase()) {\n case \"working\":\n return \"blue\";\n case \"waiting\":\n case \"canceled\":\n return \"amber\";\n case \"completed\":\n case \"idle\":\n return \"green\";\n case \"failed\":\n return \"red\";\n default:\n return \"neutral\";\n }\n}\n\n/**\n * Map an issue status string to its badge tone. Shared by every `@`-mention\n * surface that renders an issue row (agent composer, issue-manager) so the\n * status badge color is identical across surfaces. The label is resolved by\n * each surface's own i18n; only the tone lives here.\n */\nexport function issueMentionStatusTone(status: string): MentionRowStatusTone {\n switch (status.trim().toLowerCase()) {\n case \"completed\":\n return \"green\";\n case \"failed\":\n case \"canceled\":\n return \"red\";\n default:\n return \"neutral\";\n }\n}\n","/**\n * Pure, dependency-free file visual-kind helpers shared by every `@`-mention\n * surface that renders a {@link MentionRow}. The *base* extension → kind\n * resolution lives in each surface (the agent maps file-manager kinds, etc.);\n * this module owns only the surface-agnostic vocabulary and the thumbnail rule\n * so the shared row renderer never imports a workspace feature.\n */\nexport type MentionFileVisualKind =\n | \"back\"\n | \"document\"\n | \"code\"\n | \"markdown\"\n | \"image\"\n | \"video\"\n | \"folder\";\n\nexport interface MentionFileVisualKindInput {\n entryKind?: string | null;\n mentionNavigation?: string | null;\n /**\n * The base visual kind already resolved from the file extension/name by the\n * surface (e.g. the agent's file-manager mapping). Used as the fallback when\n * the entry is neither a back-navigation marker nor a directory.\n */\n baseVisualKind: MentionFileVisualKind;\n}\n\n/**\n * Resolve the row's visual kind from a pre-resolved {@link MentionFileVisualKindInput.baseVisualKind}\n * plus the structural markers (back navigation, directory) that override it.\n */\nexport function resolveMentionFileVisualKind(\n input: MentionFileVisualKindInput\n): MentionFileVisualKind {\n if (input.mentionNavigation === \"agent-generated-folder-back\") {\n return \"back\";\n }\n if (input.entryKind === \"directory\") {\n return \"folder\";\n }\n return input.baseVisualKind;\n}\n\n/**\n * The thumbnail is only shown for image entries with a non-empty thumbnail URL.\n */\nexport function resolveMentionFileThumbnailUrl(input: {\n visualKind: MentionFileVisualKind;\n thumbnailUrl?: string | null;\n}): string | undefined {\n if (input.visualKind !== \"image\") {\n return undefined;\n }\n const thumbnailUrl = input.thumbnailUrl?.trim() ?? \"\";\n return thumbnailUrl || undefined;\n}\n"],"mappings":"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","names":[]}
|
package/dist/editor/index.d.ts
CHANGED
|
@@ -10,6 +10,9 @@ interface RichTextTriggerTextOverrides {
|
|
|
10
10
|
removeReferenceActionLabel?: string;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
+
type RichTextTriggerMenuPlacement = "bottom-start" | "top-start" | "auto-start";
|
|
14
|
+
type RichTextTriggerMenuAnchor = "cursor" | "editor";
|
|
15
|
+
|
|
13
16
|
interface RichTextTriggerEditorProps {
|
|
14
17
|
value: string;
|
|
15
18
|
onChange: (value: string) => void;
|
|
@@ -26,9 +29,12 @@ interface RichTextTriggerEditorProps {
|
|
|
26
29
|
textOverrides?: RichTextTriggerTextOverrides;
|
|
27
30
|
overlay?: ReactNode;
|
|
28
31
|
focusSignal?: unknown;
|
|
32
|
+
menuAnchor?: RichTextTriggerMenuAnchor;
|
|
33
|
+
menuPlacement?: RichTextTriggerMenuPlacement;
|
|
34
|
+
menuOffset?: number;
|
|
29
35
|
menuZIndex?: string | number;
|
|
30
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
|
+
declare function RichTextTriggerEditor({ value, onChange, triggerProviders, placeholder, disabled, className, textareaClassName, placeholderClassName, minQueryLength, maxResults, removeDecorationAriaLabel, i18n, textOverrides, overlay, focusSignal, menuAnchor, menuPlacement, menuOffset, menuZIndex }: RichTextTriggerEditorProps): JSX.Element;
|
|
32
38
|
|
|
33
39
|
interface RichTextTriggerTextareaProps {
|
|
34
40
|
value: string;
|
|
@@ -86,4 +92,4 @@ declare function isRichTextTriggerPrefixBoundary(character: string, boundary: Ri
|
|
|
86
92
|
declare function findRichTextTriggerQuery(value: string, caret: number, triggerConfigs: readonly RichTextTriggerConfig[]): RichTextTriggerQueryState | null;
|
|
87
93
|
declare function queryRichTextTriggerMatches(registry: RichTextTriggerRegistry, input: RichTextTriggerQueryInput): Promise<readonly RichTextTriggerQueryMatch[]>;
|
|
88
94
|
|
|
89
|
-
export { RichTextMentionReadonly, type RichTextMentionReadonlyClickPayload, type RichTextMentionReadonlyProps, RichTextReadonlyContent, type RichTextReadonlyContentProps, type RichTextReadonlyWorkspaceReference, RichTextTriggerEditor, type RichTextTriggerEditorProps, type RichTextTriggerQueryState, type RichTextTriggerTextOverrides, RichTextTriggerTextarea, type RichTextTriggerTextareaProps, findRichTextTriggerQuery, isRichTextTriggerPrefixBoundary, queryRichTextTriggerMatches };
|
|
95
|
+
export { RichTextMentionReadonly, type RichTextMentionReadonlyClickPayload, type RichTextMentionReadonlyProps, RichTextReadonlyContent, type RichTextReadonlyContentProps, type RichTextReadonlyWorkspaceReference, RichTextTriggerEditor, type RichTextTriggerEditorProps, type RichTextTriggerMenuAnchor, type RichTextTriggerMenuPlacement, type RichTextTriggerQueryState, type RichTextTriggerTextOverrides, RichTextTriggerTextarea, type RichTextTriggerTextareaProps, findRichTextTriggerQuery, isRichTextTriggerPrefixBoundary, queryRichTextTriggerMatches };
|
package/dist/editor/index.js
CHANGED
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
|
|
23
23
|
// src/editor/RichTextTriggerEditor.tsx
|
|
24
24
|
import {
|
|
25
|
+
useCallback,
|
|
25
26
|
useEffect as useEffect2,
|
|
26
27
|
useLayoutEffect,
|
|
27
28
|
useMemo,
|
|
@@ -113,6 +114,108 @@ function resolveRichTextTriggerText(overrides, removeDecorationAriaLabel, i18n =
|
|
|
113
114
|
}
|
|
114
115
|
var defaultRichTextTriggerText = resolveRichTextTriggerText();
|
|
115
116
|
|
|
117
|
+
// src/editor/richTextTriggerMenuPlacement.ts
|
|
118
|
+
var richTextTriggerMenuEstimatedSize = {
|
|
119
|
+
width: 360,
|
|
120
|
+
height: 256
|
|
121
|
+
};
|
|
122
|
+
var richTextTriggerMenuViewportPadding = 12;
|
|
123
|
+
function resolveRichTextTriggerMenuPlacement(options) {
|
|
124
|
+
const {
|
|
125
|
+
cursorRect,
|
|
126
|
+
editorRect,
|
|
127
|
+
estimatedSize = richTextTriggerMenuEstimatedSize,
|
|
128
|
+
menuAnchor = "cursor",
|
|
129
|
+
menuOffset,
|
|
130
|
+
menuPlacement,
|
|
131
|
+
viewportWidth = 1280,
|
|
132
|
+
viewportHeight
|
|
133
|
+
} = options;
|
|
134
|
+
if (menuAnchor === "editor" && editorRect) {
|
|
135
|
+
return resolveEditorAnchoredPlacement({
|
|
136
|
+
editorRect,
|
|
137
|
+
estimatedSize,
|
|
138
|
+
menuOffset,
|
|
139
|
+
menuPlacement,
|
|
140
|
+
viewportHeight,
|
|
141
|
+
viewportWidth
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
if (menuPlacement === "top-start") {
|
|
145
|
+
return resolveTopStartPlacement(cursorRect, menuOffset);
|
|
146
|
+
}
|
|
147
|
+
if (menuPlacement === "auto-start") {
|
|
148
|
+
const bottomPlacement = resolveBottomStartPlacement(cursorRect, menuOffset);
|
|
149
|
+
const topPlacement = resolveTopStartPlacement(cursorRect, menuOffset);
|
|
150
|
+
const bottomFits = bottomPlacement.point.y + estimatedSize.height <= viewportHeight - richTextTriggerMenuViewportPadding;
|
|
151
|
+
const topFits = topPlacement.point.y - estimatedSize.height >= richTextTriggerMenuViewportPadding;
|
|
152
|
+
if (!bottomFits && topFits) {
|
|
153
|
+
return topPlacement;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return resolveBottomStartPlacement(cursorRect, menuOffset);
|
|
157
|
+
}
|
|
158
|
+
function resolveEditorAnchoredPlacement(options) {
|
|
159
|
+
const {
|
|
160
|
+
editorRect,
|
|
161
|
+
estimatedSize,
|
|
162
|
+
menuOffset,
|
|
163
|
+
menuPlacement,
|
|
164
|
+
viewportHeight,
|
|
165
|
+
viewportWidth
|
|
166
|
+
} = options;
|
|
167
|
+
const maxWidth = Math.max(
|
|
168
|
+
0,
|
|
169
|
+
viewportWidth - richTextTriggerMenuViewportPadding * 2
|
|
170
|
+
);
|
|
171
|
+
const width = Math.min(editorRect.width, maxWidth);
|
|
172
|
+
const left = Math.max(
|
|
173
|
+
richTextTriggerMenuViewportPadding,
|
|
174
|
+
Math.min(
|
|
175
|
+
editorRect.left,
|
|
176
|
+
viewportWidth - richTextTriggerMenuViewportPadding - width
|
|
177
|
+
)
|
|
178
|
+
);
|
|
179
|
+
const spaceAbove = editorRect.top - menuOffset - richTextTriggerMenuViewportPadding;
|
|
180
|
+
const spaceBelow = viewportHeight - editorRect.bottom - menuOffset - richTextTriggerMenuViewportPadding;
|
|
181
|
+
const placeAbove = menuPlacement === "top-start" || menuPlacement === "auto-start" && spaceBelow < estimatedSize.height && spaceAbove > spaceBelow;
|
|
182
|
+
return {
|
|
183
|
+
type: "point",
|
|
184
|
+
alignY: placeAbove ? "end" : "start",
|
|
185
|
+
boundaryPoint: {
|
|
186
|
+
x: Math.round(editorRect.left + editorRect.width / 2),
|
|
187
|
+
y: Math.round((editorRect.top + editorRect.bottom) / 2)
|
|
188
|
+
},
|
|
189
|
+
point: {
|
|
190
|
+
x: Math.round(left),
|
|
191
|
+
y: Math.round(
|
|
192
|
+
placeAbove ? editorRect.top - menuOffset : editorRect.bottom + menuOffset
|
|
193
|
+
)
|
|
194
|
+
},
|
|
195
|
+
width: Math.round(width)
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
function resolveBottomStartPlacement(cursorRect, menuOffset) {
|
|
199
|
+
return {
|
|
200
|
+
type: "point",
|
|
201
|
+
alignY: "start",
|
|
202
|
+
point: {
|
|
203
|
+
x: cursorRect.left,
|
|
204
|
+
y: cursorRect.bottom + menuOffset
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
function resolveTopStartPlacement(cursorRect, menuOffset) {
|
|
209
|
+
return {
|
|
210
|
+
type: "point",
|
|
211
|
+
alignY: "end",
|
|
212
|
+
point: {
|
|
213
|
+
x: cursorRect.left,
|
|
214
|
+
y: cursorRect.top - menuOffset
|
|
215
|
+
}
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
|
|
116
219
|
// src/editor/RichTextTriggerMenuItem.tsx
|
|
117
220
|
import { FileIcon, FolderFilledIcon } from "@tutti-os/ui-system/icons";
|
|
118
221
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -555,9 +658,11 @@ function RichTextTriggerEditor({
|
|
|
555
658
|
textOverrides,
|
|
556
659
|
overlay,
|
|
557
660
|
focusSignal,
|
|
661
|
+
menuAnchor = "cursor",
|
|
662
|
+
menuPlacement = "bottom-start",
|
|
663
|
+
menuOffset = 6,
|
|
558
664
|
menuZIndex
|
|
559
665
|
}) {
|
|
560
|
-
const menuOffset = 6;
|
|
561
666
|
const normalizedValue = normalizeRichTextContent(value);
|
|
562
667
|
const text = resolveRichTextTriggerText(
|
|
563
668
|
textOverrides,
|
|
@@ -586,10 +691,11 @@ function RichTextTriggerEditor({
|
|
|
586
691
|
);
|
|
587
692
|
const [activeIndex, setActiveIndex] = useState2(0);
|
|
588
693
|
const [isLoading, setIsLoading] = useState2(false);
|
|
589
|
-
const [
|
|
590
|
-
null
|
|
591
|
-
);
|
|
694
|
+
const [resolvedMenuAnchor, setResolvedMenuAnchor] = useState2(null);
|
|
592
695
|
latestOnChangeRef.current = onChange;
|
|
696
|
+
const resetMenuPlacement = useCallback(() => {
|
|
697
|
+
setResolvedMenuAnchor(null);
|
|
698
|
+
}, []);
|
|
593
699
|
const editor = useEditor({
|
|
594
700
|
immediatelyRender: false,
|
|
595
701
|
editable: !disabled,
|
|
@@ -619,7 +725,7 @@ function RichTextTriggerEditor({
|
|
|
619
725
|
setMatches([]);
|
|
620
726
|
setActiveIndex(0);
|
|
621
727
|
setIsLoading(false);
|
|
622
|
-
|
|
728
|
+
resetMenuPlacement();
|
|
623
729
|
}, 100);
|
|
624
730
|
},
|
|
625
731
|
onFocus() {
|
|
@@ -779,31 +885,48 @@ function RichTextTriggerEditor({
|
|
|
779
885
|
]);
|
|
780
886
|
useLayoutEffect(() => {
|
|
781
887
|
if (!editor || !query) {
|
|
782
|
-
|
|
888
|
+
resetMenuPlacement();
|
|
783
889
|
return;
|
|
784
890
|
}
|
|
785
|
-
const
|
|
891
|
+
const updateMenuAnchor = () => {
|
|
786
892
|
const coords = editor.view.coordsAtPos(editor.state.selection.from);
|
|
787
|
-
const
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
893
|
+
const editorRect = editor.view.dom.getBoundingClientRect();
|
|
894
|
+
const nextMenuAnchor = resolveRichTextTriggerMenuPlacement({
|
|
895
|
+
cursorRect: coords,
|
|
896
|
+
editorRect,
|
|
897
|
+
menuAnchor,
|
|
898
|
+
menuOffset,
|
|
899
|
+
menuPlacement,
|
|
900
|
+
viewportWidth: typeof window === "undefined" ? 1280 : window.innerWidth,
|
|
901
|
+
viewportHeight: typeof window === "undefined" ? 720 : window.innerHeight
|
|
902
|
+
});
|
|
903
|
+
setResolvedMenuAnchor(nextMenuAnchor);
|
|
792
904
|
};
|
|
793
|
-
|
|
794
|
-
window.addEventListener("resize",
|
|
795
|
-
window.addEventListener("scroll",
|
|
905
|
+
updateMenuAnchor();
|
|
906
|
+
window.addEventListener("resize", updateMenuAnchor);
|
|
907
|
+
window.addEventListener("scroll", updateMenuAnchor, {
|
|
796
908
|
capture: true,
|
|
797
909
|
passive: true
|
|
798
910
|
});
|
|
799
911
|
return () => {
|
|
800
|
-
window.removeEventListener("resize",
|
|
801
|
-
window.removeEventListener("scroll",
|
|
912
|
+
window.removeEventListener("resize", updateMenuAnchor);
|
|
913
|
+
window.removeEventListener("scroll", updateMenuAnchor, true);
|
|
802
914
|
};
|
|
803
|
-
}, [
|
|
915
|
+
}, [
|
|
916
|
+
editor,
|
|
917
|
+
menuAnchor,
|
|
918
|
+
menuOffset,
|
|
919
|
+
menuPlacement,
|
|
920
|
+
query,
|
|
921
|
+
resetMenuPlacement
|
|
922
|
+
]);
|
|
804
923
|
const canQueryTrigger = !!query && activeTriggerConfigs.length > 0 && query.keyword.length >= minQueryLength;
|
|
805
|
-
const isMenuOpen = canQueryTrigger && (isFocused || !!
|
|
924
|
+
const isMenuOpen = canQueryTrigger && (isFocused || !!resolvedMenuAnchor);
|
|
806
925
|
const isEmpty = !editor || serializeRichTextDocumentToContent(editor.getJSON()).trim().length === 0;
|
|
926
|
+
const menuSurfaceStyle = resolveMenuSurfaceStyle(
|
|
927
|
+
resolvedMenuAnchor,
|
|
928
|
+
menuZIndex
|
|
929
|
+
);
|
|
807
930
|
const applyMatch = (match) => {
|
|
808
931
|
if (!editor || !query) {
|
|
809
932
|
return;
|
|
@@ -819,7 +942,7 @@ function RichTextTriggerEditor({
|
|
|
819
942
|
setMatches([]);
|
|
820
943
|
setActiveIndex(0);
|
|
821
944
|
setIsLoading(false);
|
|
822
|
-
|
|
945
|
+
resetMenuPlacement();
|
|
823
946
|
};
|
|
824
947
|
const handleKeyDown = (event) => {
|
|
825
948
|
if (isRichTextImeComposing(event)) {
|
|
@@ -833,7 +956,7 @@ function RichTextTriggerEditor({
|
|
|
833
956
|
setMatches([]);
|
|
834
957
|
setActiveIndex(0);
|
|
835
958
|
setIsLoading(false);
|
|
836
|
-
|
|
959
|
+
resetMenuPlacement();
|
|
837
960
|
return;
|
|
838
961
|
}
|
|
839
962
|
if (matches.length === 0) {
|
|
@@ -880,22 +1003,13 @@ function RichTextTriggerEditor({
|
|
|
880
1003
|
}
|
|
881
1004
|
) }) : null,
|
|
882
1005
|
overlay,
|
|
883
|
-
isMenuOpen &&
|
|
1006
|
+
isMenuOpen && resolvedMenuAnchor ? /* @__PURE__ */ jsx4(
|
|
884
1007
|
ViewportMenuSurface,
|
|
885
1008
|
{
|
|
886
1009
|
open: true,
|
|
887
1010
|
className: "tutti-rich-text-at-menu max-h-64 w-[min(28rem,calc(100vw-24px))] overflow-y-auto p-1",
|
|
888
|
-
placement:
|
|
889
|
-
|
|
890
|
-
point: menuPoint,
|
|
891
|
-
alignX: "start",
|
|
892
|
-
alignY: "start",
|
|
893
|
-
estimatedSize: {
|
|
894
|
-
width: 360,
|
|
895
|
-
height: 256
|
|
896
|
-
}
|
|
897
|
-
},
|
|
898
|
-
style: menuZIndex === void 0 ? void 0 : { zIndex: menuZIndex },
|
|
1011
|
+
placement: resolveViewportMenuSurfacePlacement(resolvedMenuAnchor),
|
|
1012
|
+
style: menuSurfaceStyle,
|
|
899
1013
|
children: matches.length > 0 ? matches.map((match, index) => /* @__PURE__ */ jsx4(
|
|
900
1014
|
RichTextTriggerMenuItem,
|
|
901
1015
|
{
|
|
@@ -916,6 +1030,31 @@ function RichTextTriggerEditor({
|
|
|
916
1030
|
}
|
|
917
1031
|
);
|
|
918
1032
|
}
|
|
1033
|
+
function resolveViewportMenuSurfacePlacement(menuAnchor) {
|
|
1034
|
+
return {
|
|
1035
|
+
type: "point",
|
|
1036
|
+
...menuAnchor.boundaryPoint ? { boundaryPoint: menuAnchor.boundaryPoint } : {},
|
|
1037
|
+
point: menuAnchor.point,
|
|
1038
|
+
alignX: "start",
|
|
1039
|
+
alignY: menuAnchor.alignY,
|
|
1040
|
+
estimatedSize: {
|
|
1041
|
+
width: menuAnchor.width ?? richTextTriggerMenuEstimatedSize.width,
|
|
1042
|
+
height: richTextTriggerMenuEstimatedSize.height
|
|
1043
|
+
}
|
|
1044
|
+
};
|
|
1045
|
+
}
|
|
1046
|
+
function resolveMenuSurfaceStyle(menuAnchor, menuZIndex) {
|
|
1047
|
+
if (!menuAnchor && menuZIndex === void 0) {
|
|
1048
|
+
return void 0;
|
|
1049
|
+
}
|
|
1050
|
+
return {
|
|
1051
|
+
...menuAnchor?.width !== void 0 ? {
|
|
1052
|
+
width: menuAnchor.width,
|
|
1053
|
+
maxWidth: menuAnchor.width
|
|
1054
|
+
} : {},
|
|
1055
|
+
...menuZIndex === void 0 ? {} : { zIndex: menuZIndex }
|
|
1056
|
+
};
|
|
1057
|
+
}
|
|
919
1058
|
function findEditorAtQuery(editor, triggers) {
|
|
920
1059
|
const query = findEditorTriggerQuery(editor, triggers);
|
|
921
1060
|
if (!query) {
|