|
@@ -0,0 +1,193 @@
|
|
|
|
|
+import type { CommandPaletteGroup } from "@nuxt/ui"
|
|
|
|
|
+import { computed, type ComputedRef, type Ref } from "vue"
|
|
|
|
|
+import type { PatternOpenPayload } from "@/composables/usePatternPlugin"
|
|
|
|
|
+import type { FormattingHandlers } from "@/lib/formatting"
|
|
|
|
|
+
|
|
|
|
|
+export interface SuggestionActionItem {
|
|
|
|
|
+ label: string
|
|
|
|
|
+ icon?: string
|
|
|
|
|
+ kbds?: string[]
|
|
|
|
|
+ description?: string
|
|
|
|
|
+ disabled?: boolean
|
|
|
|
|
+ class?: string
|
|
|
|
|
+ onSelect: () => void
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+export function buildSlashGroups(
|
|
|
|
|
+ payload: PatternOpenPayload,
|
|
|
|
|
+ handlers: FormattingHandlers | null,
|
|
|
|
|
+ onClose: () => void,
|
|
|
|
|
+): CommandPaletteGroup[] {
|
|
|
|
|
+ const { respond, close: closeSession } = payload
|
|
|
|
|
+
|
|
|
|
|
+ function replace(text: string) {
|
|
|
|
|
+ respond({ text, mode: "replace" })
|
|
|
|
|
+ closeSession("completed")
|
|
|
|
|
+ onClose()
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ function run(fn: () => void) {
|
|
|
|
|
+ respond({ text: "", mode: "replace" })
|
|
|
|
|
+ closeSession("cancelled")
|
|
|
|
|
+ fn()
|
|
|
|
|
+ onClose()
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return [
|
|
|
|
|
+ {
|
|
|
|
|
+ id: "headings",
|
|
|
|
|
+ label: "Headings",
|
|
|
|
|
+ items: [
|
|
|
|
|
+ { label: "Paragraph", icon: "i-lucide-text", onSelect: () => replace("") },
|
|
|
|
|
+ { label: "Heading 1", icon: "i-lucide-heading-1", kbds: ["⌘1"], onSelect: () => replace("# ") },
|
|
|
|
|
+ { label: "Heading 2", icon: "i-lucide-heading-2", kbds: ["⌘2"], onSelect: () => replace("## ") },
|
|
|
|
|
+ { label: "Heading 3", icon: "i-lucide-heading-3", onSelect: () => replace("### ") },
|
|
|
|
|
+ { label: "Heading 4", icon: "i-lucide-heading-4", onSelect: () => replace("#### ") },
|
|
|
|
|
+ { label: "Heading 5", icon: "i-lucide-heading-5", onSelect: () => replace("##### ") },
|
|
|
|
|
+ { label: "Heading 6", icon: "i-lucide-heading-6", onSelect: () => replace("###### ") },
|
|
|
|
|
+ ],
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ id: "formatting",
|
|
|
|
|
+ label: "Formatting",
|
|
|
|
|
+ items: [
|
|
|
|
|
+ { label: "Bold", icon: "i-lucide-bold", kbds: ["⌘B"], onSelect: () => { if (handlers) run(handlers.toggleBold) } },
|
|
|
|
|
+ { label: "Italic", icon: "i-lucide-italic", kbds: ["⌘I"], onSelect: () => { if (handlers) run(handlers.toggleItalic) } },
|
|
|
|
|
+ { label: "Code", icon: "i-lucide-code", kbds: ["⌘`"], onSelect: () => { if (handlers) run(handlers.toggleCode) } },
|
|
|
|
|
+ { label: "Strikethrough", icon: "i-lucide-strikethrough", onSelect: () => { if (handlers) run(handlers.toggleStrikethrough) } },
|
|
|
|
|
+ { label: "Highlight", icon: "i-lucide-highlighter", onSelect: () => { if (handlers) run(handlers.toggleHighlight) } },
|
|
|
|
|
+ ],
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ id: "blocks",
|
|
|
|
|
+ label: "Blocks",
|
|
|
|
|
+ items: [
|
|
|
|
|
+ { label: "Task", icon: "i-lucide-list-todo", onSelect: () => replace("TODO ") },
|
|
|
|
|
+ { label: "Block Math", icon: "i-lucide-square-radical", onSelect: () => replace("$$\n\n$$") },
|
|
|
|
|
+ ],
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ id: "insert",
|
|
|
|
|
+ label: "Insert",
|
|
|
|
|
+ items: [
|
|
|
|
|
+ { label: "Link", icon: "i-lucide-link", onSelect: () => replace("[link](url)") },
|
|
|
|
|
+ { label: "Inline Math", icon: "i-lucide-sigma", onSelect: () => replace("$$") },
|
|
|
|
|
+ { label: "Page Reference", icon: "i-lucide-book-marked", onSelect: () => replace("[[Page Name]]") },
|
|
|
|
|
+ { label: "Block Reference", icon: "i-lucide-arrow-up-from-dot", onSelect: () => replace("((block-id))") },
|
|
|
|
|
+ { label: "Tag", icon: "i-lucide-hash", onSelect: () => replace("#tag") },
|
|
|
|
|
+ ],
|
|
|
|
|
+ },
|
|
|
|
|
+ ]
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const DEMO_PAGES = [
|
|
|
|
|
+ { label: "Getting Started", suffix: "Page" },
|
|
|
|
|
+ { label: "Physics Notes", suffix: "Page" },
|
|
|
|
|
+ { label: "Documentation", suffix: "Page" },
|
|
|
|
|
+ { label: "Architecture", suffix: "Page" },
|
|
|
|
|
+ { label: "API Reference", suffix: "Page" },
|
|
|
|
|
+ { label: "Development Guide", suffix: "Page" },
|
|
|
|
|
+ { label: "Release Notes", suffix: "Page" },
|
|
|
|
|
+ { label: "Meeting Notes", suffix: "Page" },
|
|
|
|
|
+]
|
|
|
|
|
+
|
|
|
|
|
+const DEMO_BLOCKS = [
|
|
|
|
|
+ { label: "Fix login bug", suffix: "abc123" },
|
|
|
|
|
+ { label: "Implement API endpoint", suffix: "def456" },
|
|
|
|
|
+ { label: "Update documentation", suffix: "ghi789" },
|
|
|
|
|
+ { label: "Refactor tests", suffix: "jkl012" },
|
|
|
|
|
+ { label: "Review PR #42", suffix: "mno345" },
|
|
|
|
|
+]
|
|
|
|
|
+
|
|
|
|
|
+const DEMO_TAGS = [
|
|
|
|
|
+ { label: "documentation", suffix: "Tag" },
|
|
|
|
|
+ { label: "development", suffix: "Tag" },
|
|
|
|
|
+ { label: "bug", suffix: "Tag" },
|
|
|
|
|
+ { label: "feature", suffix: "Tag" },
|
|
|
|
|
+ { label: "enhancement", suffix: "Tag" },
|
|
|
|
|
+ { label: "planning", suffix: "Tag" },
|
|
|
|
|
+ { label: "urgent", suffix: "Tag" },
|
|
|
|
|
+]
|
|
|
|
|
+
|
|
|
|
|
+export function buildMentionGroups(
|
|
|
|
|
+ payload: PatternOpenPayload,
|
|
|
|
|
+ onClose: () => void,
|
|
|
|
|
+): CommandPaletteGroup[] {
|
|
|
|
|
+ const { kind, respond, close: closeSession } = payload
|
|
|
|
|
+
|
|
|
|
|
+ function select(text: string) {
|
|
|
|
|
+ const prefix = kind === "tag" ? "#" : ""
|
|
|
|
|
+ const closeDelim = kind === "reference" ? "]]" : kind === "embed" ? "))" : ""
|
|
|
|
|
+ respond({ text: `${prefix}${text}${closeDelim}`, mode: "replace" })
|
|
|
|
|
+ closeSession("completed")
|
|
|
|
|
+ onClose()
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (kind === "reference") {
|
|
|
|
|
+ return [
|
|
|
|
|
+ {
|
|
|
|
|
+ id: "pages",
|
|
|
|
|
+ label: "Pages",
|
|
|
|
|
+ items: DEMO_PAGES.map((p) => ({
|
|
|
|
|
+ label: p.label,
|
|
|
|
|
+ suffix: p.suffix,
|
|
|
|
|
+ icon: "i-lucide-file-text",
|
|
|
|
|
+ onSelect: () => select(p.label),
|
|
|
|
|
+ })),
|
|
|
|
|
+ },
|
|
|
|
|
+ ]
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (kind === "embed") {
|
|
|
|
|
+ return [
|
|
|
|
|
+ {
|
|
|
|
|
+ id: "blocks",
|
|
|
|
|
+ label: "Blocks",
|
|
|
|
|
+ items: DEMO_BLOCKS.map((b) => ({
|
|
|
|
|
+ label: b.label,
|
|
|
|
|
+ suffix: b.suffix,
|
|
|
|
|
+ icon: "i-lucide-message-square",
|
|
|
|
|
+ onSelect: () => select(b.suffix),
|
|
|
|
|
+ })),
|
|
|
|
|
+ },
|
|
|
|
|
+ ]
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (kind === "tag") {
|
|
|
|
|
+ return [
|
|
|
|
|
+ {
|
|
|
|
|
+ id: "tags",
|
|
|
|
|
+ label: "Tags",
|
|
|
|
|
+ items: DEMO_TAGS.map((t) => ({
|
|
|
|
|
+ label: t.label,
|
|
|
|
|
+ suffix: t.suffix,
|
|
|
|
|
+ icon: "i-lucide-hash",
|
|
|
|
|
+ onSelect: () => select(t.label),
|
|
|
|
|
+ })),
|
|
|
|
|
+ },
|
|
|
|
|
+ ]
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return []
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+export function useSlashGroups(
|
|
|
|
|
+ payload: Ref<PatternOpenPayload | null>,
|
|
|
|
|
+ handlers: Ref<FormattingHandlers | null>,
|
|
|
|
|
+ onClose: () => void,
|
|
|
|
|
+): ComputedRef<CommandPaletteGroup[]> {
|
|
|
|
|
+ return computed(() => {
|
|
|
|
|
+ if (!payload.value) return []
|
|
|
|
|
+ return buildSlashGroups(payload.value, handlers.value, onClose)
|
|
|
|
|
+ })
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+export function useMentionGroups(
|
|
|
|
|
+ payload: Ref<PatternOpenPayload | null>,
|
|
|
|
|
+ onClose: () => void,
|
|
|
|
|
+): ComputedRef<CommandPaletteGroup[]> {
|
|
|
|
|
+ return computed(() => {
|
|
|
|
|
+ if (!payload.value) return []
|
|
|
|
|
+ return buildMentionGroups(payload.value, onClose)
|
|
|
|
|
+ })
|
|
|
|
|
+}
|