File
Blob: src/client/components/editor/lib/insert-palette.ts
| 1 | import { launchEmojiPicker } from "../controllers/emoji/insert-panel"; |
| 2 | import { insertImageFromSlashMenu } from "../controllers/image/insert-panel"; |
| 3 | import { getSlashMenuItems, type SlashMenuItem } from "../controllers/slash/items"; |
| 4 | import { startGenerateAtRange } from "../controllers/slash/ai-insert"; |
| 5 | import { getAiBusyReason } from "./ai-busy"; |
| 6 | import { runtimeToUploadContext } from "./media-actions"; |
| 7 | import { canInsertPageMentionAtRange } from "./page-mention/can-insert"; |
| 8 | import { launchPageMentionPicker } from "./page-mention/open-picker"; |
| 9 | import type { EditorRuntimeSnapshot } from "../editor-runtime-context"; |
| 10 | import type { EditorAffordance } from "@/client/lib/affordance/editor"; |
| 11 | import type { PageMentionCandidate } from "@/client/components/page-mention/types"; |
| 12 | |
| 13 | interface CreateInsertPaletteItemsOptions { |
| 14 | getRuntime: () => EditorRuntimeSnapshot; |
| 15 | getAffordance: () => EditorAffordance; |
| 16 | getPageMentionCandidates: (excludePageId: string | undefined) => PageMentionCandidate[]; |
| 17 | } |
| 18 | |
| 19 | function canOpenMentions(getAffordance: () => EditorAffordance, editable: boolean) { |
| 20 | return editable && getAffordance().canInsertPageMentions; |
| 21 | } |
| 22 | |
| 23 | export function createInsertPaletteItems({ |
| 24 | getRuntime, |
| 25 | getAffordance, |
| 26 | getPageMentionCandidates, |
| 27 | }: CreateInsertPaletteItemsOptions): SlashMenuItem[] { |
| 28 | return getSlashMenuItems({ |
| 29 | pageMention: { |
| 30 | isAvailable: ({ editor }) => |
| 31 | canOpenMentions(getAffordance, editor.isEditable) && canInsertPageMentionAtRange(editor), |
| 32 | openPicker: ({ editor, range }) => { |
| 33 | if (!canOpenMentions(getAffordance, editor.isEditable)) return; |
| 34 | launchPageMentionPicker(editor, { |
| 35 | range, |
| 36 | candidates: getPageMentionCandidates(getRuntime().pageId), |
| 37 | }); |
| 38 | }, |
| 39 | }, |
| 40 | image: { |
| 41 | isAvailable: ({ editor }) => editor.isEditable && getAffordance().canInsertImages && !!getRuntime().workspaceId, |
| 42 | insertImage: ({ editor, range }) => { |
| 43 | insertImageFromSlashMenu(editor, range, runtimeToUploadContext(getRuntime())); |
| 44 | }, |
| 45 | }, |
| 46 | emoji: { |
| 47 | openPicker: ({ editor, range }) => { |
| 48 | launchEmojiPicker(editor, range); |
| 49 | }, |
| 50 | }, |
| 51 | ai: { |
| 52 | isAvailable: ({ editor }) => { |
| 53 | const runtime = getRuntime(); |
| 54 | return editor.isEditable && getAffordance().canUseAiGenerate && !!runtime.workspaceId && !!runtime.pageId; |
| 55 | }, |
| 56 | isBlocked: ({ editor }) => getAiBusyReason(editor.state), |
| 57 | startGenerate: ({ editor, range, intent }) => { |
| 58 | void startGenerateAtRange({ editor, range, intent, runtime: getRuntime() }); |
| 59 | }, |
| 60 | }, |
| 61 | }); |
| 62 | } |