@devalok/shilp-sutra 0.45.0 → 0.45.1
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/dist/_chunks/chat.js +151 -150
- package/dist/_chunks/chat.js.map +1 -1
- package/dist/_chunks/mention.js +5 -0
- package/dist/_chunks/mention.js.map +1 -0
- package/dist/composed/file-preview/video-preview.d.ts.map +1 -1
- package/dist/composed/file-preview.js +18 -16
- package/dist/composed/file-preview.js.map +1 -1
- package/dist/composed/rich-chat-input.d.ts.map +1 -1
- package/dist/composed/rich-chat-input.js +514 -503
- package/dist/composed/rich-chat-input.js.map +1 -1
- package/dist/composed/rich-text-editor.d.ts.map +1 -1
- package/dist/composed/rich-text-editor.js +144 -143
- package/dist/composed/rich-text-editor.js.map +1 -1
- package/dist/ui/chat/message.d.ts.map +1 -1
- package/dist/ui/lib/mention.d.ts +11 -0
- package/dist/ui/lib/mention.d.ts.map +1 -0
- package/docs/components/ui/chat.md +1 -1
- package/llms.txt +1 -1
- package/mcp-manifest.json +1 -1
- package/package.json +6 -1
- package/skill/SKILL.md +1 -1
- package/skill/references/components.md +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rich-text-editor.js","names":[],"sources":["../../src/composed/rich-text-editor.tsx"],"sourcesContent":["'use client'\n\nimport {\n IconAlignCenter,\n IconAlignLeft,\n IconAlignRight,\n IconArrowBackUp,\n IconArrowForwardUp,\n IconBlockquote,\n IconBold,\n IconCode,\n IconH2,\n IconH3,\n IconHighlight,\n IconItalic,\n IconLineDashed,\n IconLink,\n IconList,\n IconListCheck,\n IconListNumbers,\n IconMoodSmile,\n IconPaperclip,\n IconPhoto,\n IconStrikethrough,\n IconUnderline,\n} from '@tabler/icons-react'\nimport Highlight from '@tiptap/extension-highlight'\nimport Image from '@tiptap/extension-image'\nimport { ListKit } from '@tiptap/extension-list'\nimport Mention from '@tiptap/extension-mention'\nimport TextAlign from '@tiptap/extension-text-align'\nimport { Placeholder } from '@tiptap/extensions'\nimport { type Editor,EditorContent, useEditor, useEditorState } from '@tiptap/react'\nimport StarterKit from '@tiptap/starter-kit'\nimport * as React from 'react'\n\nimport { useColorMode } from '../hooks/use-color-mode'\nimport { Button } from '../ui/button'\nimport { Icon } from '../ui/icon'\nimport { cn } from '../ui/lib/utils'\nimport type { EmojiSet } from './emoji-picker'\nimport { loadEmojiData, lookupEmoji } from './extensions/emoji-data'\nimport { EmojiNode } from './extensions/emoji-node'\nimport { createEmojiSuggestion } from './extensions/emoji-suggestion'\nimport { FileAttachment } from './extensions/file-attachment'\nimport { createSuggestionRenderer } from './extensions/mention-suggestion'\n\nconst PROSE_CLASSES = [\n 'prose prose-sm max-w-none',\n 'font-body text-ds-md leading-relaxed text-surface-fg',\n '[&_h2]:text-ds-xl [&_h2]:mb-ds-03 [&_h2]:mt-ds-05 [&_h2]:text-surface-fg',\n '[&_h3]:text-ds-base [&_h3]:font-semibold [&_h3]:mb-ds-02b [&_h3]:mt-ds-04 [&_h3]:text-surface-fg',\n '[&_p]:mb-ds-02b [&_p]:text-surface-fg-muted',\n '[&_ul]:ml-ds-05 [&_ul]:list-disc [&_ol]:ml-ds-05 [&_ol]:list-decimal',\n '[&_li]:text-surface-fg-muted',\n '[&_code]:rounded [&_code]:bg-surface-raised [&_code]:px-ds-02b [&_code]:py-ds-01 [&_code]:text-ds-md [&_code]:text-accent-11',\n '[&_pre]:rounded-surface [&_pre]:bg-surface-raised [&_pre]:p-ds-04',\n '[&_strong]:font-semibold [&_strong]:text-surface-fg',\n '[&_blockquote]:border-l-[3px] [&_blockquote]:border-accent-6 [&_blockquote]:pl-ds-04 [&_blockquote]:italic [&_blockquote]:text-surface-fg-subtle',\n '[&_mark]:rounded-xs [&_mark]:bg-warning-3 [&_mark]:px-[2px]',\n '[&_ul[data-type=\"taskList\"]]:ml-0 [&_ul[data-type=\"taskList\"]]:list-none [&_li[data-type=\"taskItem\"]]:flex [&_li[data-type=\"taskItem\"]]:items-start [&_li[data-type=\"taskItem\"]]:gap-ds-02',\n '[&_hr]:my-ds-04 [&_hr]:border-surface-border-strong',\n '[&_a]:text-accent-11 [&_a]:underline [&_a]:decoration-accent-6 hover:[&_a]:decoration-accent-11',\n '[&_img]:max-w-full [&_img]:rounded-control [&_img]:my-ds-03',\n '[&_.mention]:rounded-control-inner [&_.mention]:bg-accent-2 [&_.mention]:px-ds-02 [&_.mention]:py-[1px] [&_.mention]:font-medium [&_.mention]:text-accent-11',\n] as const\n\ninterface ToolbarButtonProps {\n onClick: () => void\n isActive?: boolean\n disabled?: boolean\n title: string\n children: React.ReactNode\n}\n\nfunction ToolbarButton({\n onClick,\n isActive = false,\n disabled = false,\n title,\n children,\n}: ToolbarButtonProps) {\n return (\n <button\n type=\"button\"\n onClick={onClick}\n disabled={disabled}\n title={title}\n aria-label={title}\n aria-pressed={isActive}\n className={cn(\n 'inline-flex h-ds-xs-plus w-ds-xs-plus items-center justify-center rounded-control transition-colors duration-fast-01 ease-productive-standard',\n 'hover:bg-surface-raised-hover',\n 'disabled:pointer-events-none disabled:opacity-action-disabled',\n isActive\n ? 'bg-surface-raised-hover text-accent-11'\n : 'text-surface-fg-subtle',\n )}\n >\n {children}\n </button>\n )\n}\n\nfunction LinkButton({ editor }: { editor: Editor }) {\n const [showInput, setShowInput] = React.useState(false)\n const [url, setUrl] = React.useState('')\n const inputRef = React.useRef<HTMLInputElement>(null)\n const { isLink } = useEditorState({\n editor,\n selector: ({ editor: e }) => ({ isLink: e.isActive('link') }),\n })\n\n const handleToggle = () => {\n if (isLink) {\n editor.chain().focus().unsetLink().run()\n return\n }\n const previousUrl = editor.getAttributes('link').href || ''\n setUrl(previousUrl)\n setShowInput(true)\n setTimeout(() => inputRef.current?.focus(), 0)\n }\n\n const handleSubmit = (e: React.FormEvent) => {\n e.preventDefault()\n if (url.trim()) {\n editor.chain().focus().setLink({ href: url.trim() }).run()\n }\n setShowInput(false)\n setUrl('')\n }\n\n const handleKeyDown = (e: React.KeyboardEvent) => {\n if (e.key === 'Escape') {\n setShowInput(false)\n setUrl('')\n editor.commands.focus()\n }\n }\n\n return (\n <div className=\"relative\">\n <ToolbarButton onClick={handleToggle} isActive={isLink} title=\"Link\">\n <Icon icon={IconLink} size=\"sm\" />\n </ToolbarButton>\n {showInput && (\n <form\n onSubmit={handleSubmit}\n aria-label=\"Edit link URL\"\n className=\"absolute left-0 top-full z-popover mt-ds-01 flex items-center gap-ds-02 rounded-control bg-surface-overlay p-ds-02 shadow-raised-hover\"\n >\n <input\n ref={inputRef}\n type=\"url\"\n value={url}\n onChange={(e) => setUrl(e.target.value)}\n onKeyDown={handleKeyDown}\n placeholder=\"https://...\"\n className=\"h-ds-sm w-[240px] rounded-control-inner border border-surface-border-strong bg-surface-overlay px-ds-03 text-ds-sm text-surface-fg focus:border-accent-7 focus:outline-hidden\"\n />\n <Button type=\"submit\" variant=\"solid\" size=\"sm\">\n Apply\n </Button>\n </form>\n )}\n </div>\n )\n}\n\nfunction ToolbarDivider() {\n return <div className=\"mx-ds-02 h-[16px] w-px bg-surface-border\" />\n}\n\nfunction Toolbar({ editor, toolbar, onImageClick, onFileClick, onEmojiClick }: {\n editor: Editor\n toolbar?: ToolbarItem[]\n onImageClick?: () => void\n onFileClick?: () => void\n onEmojiClick?: () => void\n}) {\n // v3: useEditorState subscribes to specific state slices so the toolbar\n // re-renders only when active-state or can-undo/redo actually changes.\n const state = useEditorState({\n editor,\n selector: ({ editor: e }) => ({\n isBold: e.isActive('bold'),\n isItalic: e.isActive('italic'),\n isUnderline: e.isActive('underline'),\n isStrike: e.isActive('strike'),\n isHighlight: e.isActive('highlight'),\n isH2: e.isActive('heading', { level: 2 }),\n isH3: e.isActive('heading', { level: 3 }),\n isBlockquote: e.isActive('blockquote'),\n isBulletList: e.isActive('bulletList'),\n isOrderedList: e.isActive('orderedList'),\n isTaskList: e.isActive('taskList'),\n isCodeBlock: e.isActive('codeBlock'),\n isLink: e.isActive('link'),\n isAlignLeft: e.isActive({ textAlign: 'left' }),\n isAlignCenter: e.isActive({ textAlign: 'center' }),\n isAlignRight: e.isActive({ textAlign: 'right' }),\n canUndo: e.can().undo(),\n canRedo: e.can().redo(),\n }),\n })\n\n const show = (item: ToolbarItem) => !toolbar || toolbar.includes(item)\n\n // Each group is an array of ToolbarItem names that belong to it.\n // A divider renders between two adjacent groups only if both have at least one visible item.\n const hasInline = show('bold') || show('italic') || show('underline') || show('strike') || show('highlight')\n const hasBlock = show('h2') || show('h3') || show('blockquote')\n const hasList = show('bulletList') || show('orderedList') || show('taskList') || show('codeBlock')\n const hasMedia = show('link') || (!!onImageClick && show('image')) || (!!onFileClick && show('file')) || show('hr')\n const hasAlign = show('alignLeft') || show('alignCenter') || show('alignRight')\n const hasEmojiGroup = !!onEmojiClick && show('emoji')\n const hasHistory = show('undo') || show('redo')\n\n return (\n <div role=\"toolbar\" aria-label=\"Text formatting\" className=\"flex flex-wrap items-center gap-ds-01 border-b border-surface-border-strong px-ds-04 py-ds-02b\">\n {/* Inline formatting */}\n {show('bold') && (\n <ToolbarButton onClick={() => editor.chain().focus().toggleBold().run()} isActive={state.isBold} title=\"Bold\">\n <Icon icon={IconBold} size=\"sm\" stroke=\"bold\" />\n </ToolbarButton>\n )}\n {show('italic') && (\n <ToolbarButton onClick={() => editor.chain().focus().toggleItalic().run()} isActive={state.isItalic} title=\"Italic\">\n <Icon icon={IconItalic} size=\"sm\" stroke=\"bold\" />\n </ToolbarButton>\n )}\n {show('underline') && (\n <ToolbarButton onClick={() => editor.chain().focus().toggleUnderline().run()} isActive={state.isUnderline} title=\"Underline\">\n <Icon icon={IconUnderline} size=\"sm\" stroke=\"bold\" />\n </ToolbarButton>\n )}\n {show('strike') && (\n <ToolbarButton onClick={() => editor.chain().focus().toggleStrike().run()} isActive={state.isStrike} title=\"Strikethrough\">\n <Icon icon={IconStrikethrough} size=\"sm\" stroke=\"bold\" />\n </ToolbarButton>\n )}\n {show('highlight') && (\n <ToolbarButton onClick={() => editor.chain().focus().toggleHighlight().run()} isActive={state.isHighlight} title=\"Highlight\">\n <Icon icon={IconHighlight} size=\"sm\" stroke=\"bold\" />\n </ToolbarButton>\n )}\n\n {hasInline && hasBlock && <ToolbarDivider />}\n\n {/* Block formatting */}\n {show('h2') && (\n <ToolbarButton onClick={() => editor.chain().focus().toggleHeading({ level: 2 }).run()} isActive={state.isH2} title=\"Heading 2\">\n <Icon icon={IconH2} size=\"sm\" stroke=\"bold\" />\n </ToolbarButton>\n )}\n {show('h3') && (\n <ToolbarButton onClick={() => editor.chain().focus().toggleHeading({ level: 3 }).run()} isActive={state.isH3} title=\"Heading 3\">\n <Icon icon={IconH3} size=\"sm\" stroke=\"bold\" />\n </ToolbarButton>\n )}\n {show('blockquote') && (\n <ToolbarButton onClick={() => editor.chain().focus().toggleBlockquote().run()} isActive={state.isBlockquote} title=\"Blockquote\">\n <Icon icon={IconBlockquote} size=\"sm\" stroke=\"bold\" />\n </ToolbarButton>\n )}\n\n {hasBlock && hasList && <ToolbarDivider />}\n\n {/* Lists */}\n {show('bulletList') && (\n <ToolbarButton onClick={() => editor.chain().focus().toggleBulletList().run()} isActive={state.isBulletList} title=\"Bullet list\">\n <Icon icon={IconList} size=\"sm\" stroke=\"bold\" />\n </ToolbarButton>\n )}\n {show('orderedList') && (\n <ToolbarButton onClick={() => editor.chain().focus().toggleOrderedList().run()} isActive={state.isOrderedList} title=\"Ordered list\">\n <Icon icon={IconListNumbers} size=\"sm\" stroke=\"bold\" />\n </ToolbarButton>\n )}\n {show('taskList') && (\n <ToolbarButton onClick={() => editor.chain().focus().toggleTaskList().run()} isActive={state.isTaskList} title=\"Task list\">\n <Icon icon={IconListCheck} size=\"sm\" stroke=\"bold\" />\n </ToolbarButton>\n )}\n {show('codeBlock') && (\n <ToolbarButton onClick={() => editor.chain().focus().toggleCodeBlock().run()} isActive={state.isCodeBlock} title=\"Code block\">\n <Icon icon={IconCode} size=\"sm\" stroke=\"bold\" />\n </ToolbarButton>\n )}\n\n {hasList && hasMedia && <ToolbarDivider />}\n\n {/* Media & Links */}\n {show('link') && <LinkButton editor={editor} />}\n {onImageClick && show('image') && (\n <ToolbarButton onClick={onImageClick} title=\"Insert image\">\n <Icon icon={IconPhoto} size=\"sm\" />\n </ToolbarButton>\n )}\n {onFileClick && show('file') && (\n <ToolbarButton onClick={onFileClick} title=\"Attach file\">\n <Icon icon={IconPaperclip} size=\"sm\" />\n </ToolbarButton>\n )}\n {show('hr') && (\n <ToolbarButton onClick={() => editor.chain().focus().setHorizontalRule().run()} title=\"Horizontal rule\">\n <Icon icon={IconLineDashed} size=\"sm\" />\n </ToolbarButton>\n )}\n\n {hasMedia && hasAlign && <ToolbarDivider />}\n\n {/* Alignment */}\n {show('alignLeft') && (\n <ToolbarButton onClick={() => editor.chain().focus().setTextAlign('left').run()} isActive={state.isAlignLeft} title=\"Align left\">\n <Icon icon={IconAlignLeft} size=\"sm\" />\n </ToolbarButton>\n )}\n {show('alignCenter') && (\n <ToolbarButton onClick={() => editor.chain().focus().setTextAlign('center').run()} isActive={state.isAlignCenter} title=\"Align center\">\n <Icon icon={IconAlignCenter} size=\"sm\" />\n </ToolbarButton>\n )}\n {show('alignRight') && (\n <ToolbarButton onClick={() => editor.chain().focus().setTextAlign('right').run()} isActive={state.isAlignRight} title=\"Align right\">\n <Icon icon={IconAlignRight} size=\"sm\" />\n </ToolbarButton>\n )}\n\n {hasAlign && (hasEmojiGroup || hasHistory) && <ToolbarDivider />}\n\n {/* Emoji */}\n {onEmojiClick && show('emoji') && (\n <ToolbarButton onClick={onEmojiClick} title=\"Emoji\">\n <Icon icon={IconMoodSmile} size=\"sm\" />\n </ToolbarButton>\n )}\n\n {/* History */}\n {show('undo') && (\n <ToolbarButton onClick={() => editor.chain().focus().undo().run()} disabled={!state.canUndo} title=\"Undo\">\n <Icon icon={IconArrowBackUp} size=\"sm\" />\n </ToolbarButton>\n )}\n {show('redo') && (\n <ToolbarButton onClick={() => editor.chain().focus().redo().run()} disabled={!state.canRedo} title=\"Redo\">\n <Icon icon={IconArrowForwardUp} size=\"sm\" />\n </ToolbarButton>\n )}\n </div>\n )\n}\n\nconst LazyPicker = React.lazy(() => import('@emoji-mart/react'))\n\nfunction EmojiPickerLazy({ set = 'native', onSelect }: { set?: string; onSelect: (emoji: { id: string; native: string }) => void }) {\n const [data, setData] = React.useState<unknown>(null)\n const { colorMode } = useColorMode()\n\n React.useEffect(() => {\n loadEmojiData(set).then((d) => setData(d))\n }, [set])\n\n const fallback = <div className=\"flex h-[350px] w-[352px] items-center justify-center rounded-surface bg-surface-overlay shadow-raised-hover\"><span className=\"text-ds-sm text-surface-fg-subtle\">Loading...</span></div>\n\n if (!data) return fallback\n\n return (\n <React.Suspense fallback={fallback}>\n <LazyPicker\n data={data}\n set={set}\n onEmojiSelect={(emoji: { native: string; id: string }) => onSelect({ id: emoji.id, native: emoji.native })}\n theme={colorMode === 'dark' ? 'dark' : 'light'}\n previewPosition=\"none\"\n skinTonePosition=\"none\"\n />\n </React.Suspense>\n )\n}\n\nexport type ToolbarItem =\n | 'bold' | 'italic' | 'underline' | 'strike' | 'highlight'\n | 'h2' | 'h3' | 'blockquote'\n | 'bulletList' | 'orderedList' | 'taskList' | 'codeBlock'\n | 'link' | 'image' | 'file' | 'hr'\n | 'alignLeft' | 'alignCenter' | 'alignRight'\n | 'emoji' | 'undo' | 'redo'\n\nexport interface MentionItem {\n id: string\n label: string\n avatar?: string\n}\n\n/**\n * A Tiptap-powered rich text editor with a configurable toolbar, @mentions,\n * emoji suggestions, file/image uploads, and task lists.\n * Outputs sanitized HTML via `onChange`.\n */\nexport interface RichTextEditorProps extends Omit<React.ComponentPropsWithoutRef<'div'>, 'onChange' | 'content'> {\n /** Initial HTML content. Updates are NOT reactive — use `onChange` for controlled state. */\n content?: string\n placeholder?: string\n /** Called with the editor's HTML string whenever content changes. */\n onChange?: (html: string) => void\n className?: string\n /** Set to false to render in read-only mode (e.g. for previewing). */\n editable?: boolean\n /** Called when an image is pasted/dropped. Return a URL. If not provided, images inline as base64. */\n onImageUpload?: (file: File) => Promise<string>\n /** Called when a non-image file is dropped/pasted. If not provided, non-image files are ignored. */\n onFileUpload?: (file: File) => Promise<{ url: string; name: string; size: number }>\n /** Whitelist of toolbar items to display. Omit to show all. */\n toolbar?: ToolbarItem[]\n /** Static list of mentionable items (shown when user types @). */\n mentions?: MentionItem[]\n /** Async mention search. Takes precedence over static `mentions` list. */\n onMentionSearch?: (query: string) => Promise<MentionItem[]>\n /** Called when a mention is selected from the suggestion dropdown. */\n onMentionSelect?: (item: MentionItem) => void\n /** Emoji art style. @default 'native' */\n emojiSet?: EmojiSet\n}\n\nconst RichTextEditor = React.forwardRef<HTMLDivElement, RichTextEditorProps>(\n function RichTextEditor({\n content = '',\n placeholder = 'Start writing...',\n onChange,\n className,\n editable = true,\n toolbar,\n onImageUpload,\n onFileUpload,\n mentions,\n onMentionSearch,\n onMentionSelect,\n emojiSet = 'native',\n ...props\n}, ref) {\n const editorRef = React.useRef<ReturnType<typeof useEditor>>(null)\n const isInternalChangeRef = React.useRef(false)\n const [showEmojiPicker, setShowEmojiPicker] = React.useState(false)\n const emojiPickerRef = React.useRef<HTMLDivElement>(null)\n const imageInputRef = React.useRef<HTMLInputElement>(null)\n const fileInputRef = React.useRef<HTMLInputElement>(null)\n\n const handleImageInsert = async (file: File) => {\n const ed = editorRef.current\n if (!ed) return\n if (onImageUpload) {\n const url = await onImageUpload(file)\n if (url && /^https?:\\/\\//i.test(url)) {\n ed.chain().focus().setImage({ src: url }).run()\n }\n } else {\n const reader = new FileReader()\n reader.onload = () => {\n ed.chain().focus().setImage({ src: reader.result as string }).run()\n }\n reader.readAsDataURL(file)\n }\n }\n\n const handleFileInsert = async (file: File) => {\n const ed = editorRef.current\n if (!ed || !onFileUpload) return\n const result = await onFileUpload(file)\n ed.chain().focus().insertContent({\n type: 'fileAttachment',\n attrs: { url: result.url, name: result.name, size: result.size },\n }).run()\n }\n\n const editor = useEditor({\n immediatelyRender: false, // SSR-safe — prevents hydration mismatch in Next.js\n extensions: [\n StarterKit.configure({\n heading: { levels: [2, 3] },\n // v3: Underline + Link are now included in StarterKit\n link: {\n openOnClick: false,\n protocols: ['http', 'https', 'mailto'],\n validate: (href: string) => /^(https?:\\/\\/|mailto:)/i.test(href),\n HTMLAttributes: {\n rel: 'noopener noreferrer',\n target: '_blank',\n },\n },\n }),\n Placeholder.configure({\n placeholder,\n emptyEditorClass:\n 'before:content-[attr(data-placeholder)] before:text-surface-fg-subtle before:float-left before:h-0 before:pointer-events-none',\n }),\n Highlight.configure({ multicolor: false }),\n ListKit.configure({\n bulletList: false, // Already in StarterKit\n orderedList: false, // Already in StarterKit\n listItem: false, // Already in StarterKit\n listKeymap: false, // Already in StarterKit\n taskList: {},\n taskItem: { nested: true },\n }),\n TextAlign.configure({ types: ['heading', 'paragraph'] }),\n Image.configure({\n allowBase64: true,\n HTMLAttributes: {\n class: 'max-w-full rounded-control',\n },\n }),\n FileAttachment,\n ...(mentions || onMentionSearch ? [\n Mention.configure({\n HTMLAttributes: { class: 'mention' },\n suggestion: {\n items: async ({ query }: { query: string }) => {\n if (onMentionSearch) return await onMentionSearch(query)\n if (mentions) return mentions.filter(m => m.label.toLowerCase().includes(query.toLowerCase())).slice(0, 8)\n return []\n },\n render: createSuggestionRenderer(onMentionSelect),\n },\n }),\n ] : []),\n EmojiNode,\n createEmojiSuggestion(emojiSet),\n ],\n content,\n editable,\n editorProps: {\n handleDrop: (_view, event, _slice, moved) => {\n if (moved || !event.dataTransfer?.files.length) return false\n const file = event.dataTransfer.files[0]\n if (!file) return false\n if (file.type.startsWith('image/')) {\n handleImageInsert(file)\n return true\n }\n if (onFileUpload) {\n handleFileInsert(file)\n return true\n }\n return false\n },\n handlePaste: (_view, event) => {\n const file = event.clipboardData?.files[0]\n if (!file) return false\n if (file.type.startsWith('image/')) {\n handleImageInsert(file)\n return true\n }\n if (onFileUpload) {\n handleFileInsert(file)\n return true\n }\n return false\n },\n attributes: {\n class: cn(\n ...PROSE_CLASSES,\n 'focus:outline-hidden',\n 'min-h-[120px] px-ds-04 py-ds-04',\n ),\n },\n },\n onUpdate: ({ editor: ed }) => {\n isInternalChangeRef.current = true\n onChange?.(ed.getHTML())\n queueMicrotask(() => {\n isInternalChangeRef.current = false\n })\n },\n })\n\n React.useEffect(() => {\n editorRef.current = editor\n }, [editor])\n\n // Close emoji picker on click outside\n React.useEffect(() => {\n if (!showEmojiPicker) return\n const handleClickOutside = (e: MouseEvent) => {\n if (emojiPickerRef.current && !emojiPickerRef.current.contains(e.target as Node)) {\n setShowEmojiPicker(false)\n }\n }\n document.addEventListener('mousedown', handleClickOutside)\n return () => document.removeEventListener('mousedown', handleClickOutside)\n }, [showEmojiPicker])\n\n React.useEffect(() => {\n if (isInternalChangeRef.current) return\n if (editor && content !== editor.getHTML()) {\n editor.commands.setContent(content, { emitUpdate: false })\n }\n }, [editor, content])\n\n if (!editor) return null\n\n return (\n <div ref={ref} {...props} className={cn('relative', className)}>\n {/* Emoji picker rendered outside the overflow-hidden box so it isn't clipped */}\n {showEmojiPicker && (\n <div\n ref={emojiPickerRef}\n className=\"absolute bottom-full right-0 z-popover mb-ds-02\"\n onKeyDown={(e) => {\n if (e.key === 'Escape') {\n e.stopPropagation()\n setShowEmojiPicker(false)\n }\n }}\n >\n <EmojiPickerLazy\n set={emojiSet}\n onSelect={async ({ id, native }) => {\n const data = await loadEmojiData(emojiSet)\n const resolved = lookupEmoji(data, id)\n if (resolved) {\n editor.chain().focus().insertContent({\n type: 'emojiNode',\n attrs: { id, native: resolved.native, set: emojiSet, x: resolved.x, y: resolved.y },\n }).run()\n } else {\n editor.chain().focus().insertContent(native).run()\n }\n setShowEmojiPicker(false)\n }}\n />\n </div>\n )}\n <div\n className={cn(\n 'overflow-hidden rounded-surface border border-surface-border-strong bg-surface-raised',\n 'transition-colors ease-productive-standard focus-within:border-surface-border-strong',\n )}\n >\n <input\n ref={imageInputRef}\n type=\"file\"\n accept=\"image/*\"\n aria-label=\"Upload image\"\n className=\"hidden\"\n onChange={(e) => {\n const file = e.target.files?.[0]\n if (file) handleImageInsert(file)\n e.target.value = ''\n }}\n />\n {onFileUpload && (\n <input\n ref={fileInputRef}\n type=\"file\"\n aria-label=\"Upload file\"\n className=\"hidden\"\n onChange={(e) => {\n const file = e.target.files?.[0]\n if (file) handleFileInsert(file)\n e.target.value = ''\n }}\n />\n )}\n {editable && (\n <Toolbar\n editor={editor}\n toolbar={toolbar}\n onImageClick={() => imageInputRef.current?.click()}\n onFileClick={onFileUpload ? () => fileInputRef.current?.click() : undefined}\n onEmojiClick={() => setShowEmojiPicker((prev) => !prev)}\n />\n )}\n <EditorContent editor={editor} />\n </div>\n </div>\n )\n},\n)\n\nRichTextEditor.displayName = 'RichTextEditor'\n\nexport interface RichTextViewerProps extends Omit<React.ComponentPropsWithoutRef<'div'>, 'content'> {\n content: string\n className?: string\n}\n\nconst RichTextViewer = React.forwardRef<HTMLDivElement, RichTextViewerProps>(\n function RichTextViewer({ content, className, ...props }, ref) {\n const editor = useEditor({\n immediatelyRender: false, // SSR-safe — prevents hydration mismatch in Next.js\n extensions: [\n StarterKit.configure({\n heading: { levels: [2, 3] },\n link: {\n openOnClick: true,\n protocols: ['http', 'https', 'mailto'],\n validate: (href: string) => /^(https?:\\/\\/|mailto:)/i.test(href),\n HTMLAttributes: {\n rel: 'noopener noreferrer',\n target: '_blank',\n },\n },\n }),\n Highlight.configure({ multicolor: false }),\n ListKit.configure({\n bulletList: false,\n orderedList: false,\n listItem: false,\n listKeymap: false,\n taskList: {},\n taskItem: { nested: true },\n }),\n TextAlign.configure({ types: ['heading', 'paragraph'] }),\n Image.configure({\n allowBase64: true,\n HTMLAttributes: {\n class: 'max-w-full rounded-control',\n },\n }),\n FileAttachment,\n Mention.configure({\n HTMLAttributes: { class: 'mention' },\n }),\n ],\n content,\n editable: false,\n editorProps: {\n attributes: {\n class: cn(...PROSE_CLASSES),\n },\n },\n })\n\n if (!editor) return null\n\n return (\n <div ref={ref} {...props} className={className}>\n <EditorContent editor={editor} />\n </div>\n )\n},\n)\n\nRichTextViewer.displayName = 'RichTextViewer'\n\nexport { RichTextEditor, RichTextViewer }\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"rich-text-editor.js","names":[],"sources":["../../src/composed/rich-text-editor.tsx"],"sourcesContent":["'use client'\n\nimport {\n IconAlignCenter,\n IconAlignLeft,\n IconAlignRight,\n IconArrowBackUp,\n IconArrowForwardUp,\n IconBlockquote,\n IconBold,\n IconCode,\n IconH2,\n IconH3,\n IconHighlight,\n IconItalic,\n IconLineDashed,\n IconLink,\n IconList,\n IconListCheck,\n IconListNumbers,\n IconMoodSmile,\n IconPaperclip,\n IconPhoto,\n IconStrikethrough,\n IconUnderline,\n} from '@tabler/icons-react'\nimport Highlight from '@tiptap/extension-highlight'\nimport Image from '@tiptap/extension-image'\nimport { ListKit } from '@tiptap/extension-list'\nimport Mention from '@tiptap/extension-mention'\nimport TextAlign from '@tiptap/extension-text-align'\nimport { Placeholder } from '@tiptap/extensions'\nimport { type Editor,EditorContent, useEditor, useEditorState } from '@tiptap/react'\nimport StarterKit from '@tiptap/starter-kit'\nimport * as React from 'react'\n\nimport { useColorMode } from '../hooks/use-color-mode'\nimport { Button } from '../ui/button'\nimport { Icon } from '../ui/icon'\nimport { MENTION_TOKEN_CLASS } from '../ui/lib/mention'\nimport { cn } from '../ui/lib/utils'\nimport type { EmojiSet } from './emoji-picker'\nimport { loadEmojiData, lookupEmoji } from './extensions/emoji-data'\nimport { EmojiNode } from './extensions/emoji-node'\nimport { createEmojiSuggestion } from './extensions/emoji-suggestion'\nimport { FileAttachment } from './extensions/file-attachment'\nimport { createSuggestionRenderer } from './extensions/mention-suggestion'\n\nconst PROSE_CLASSES = [\n 'prose prose-sm max-w-none',\n 'font-body text-ds-md leading-relaxed text-surface-fg',\n '[&_h2]:text-ds-xl [&_h2]:mb-ds-03 [&_h2]:mt-ds-05 [&_h2]:text-surface-fg',\n '[&_h3]:text-ds-base [&_h3]:font-semibold [&_h3]:mb-ds-02b [&_h3]:mt-ds-04 [&_h3]:text-surface-fg',\n '[&_p]:mb-ds-02b [&_p]:text-surface-fg-muted',\n '[&_ul]:ml-ds-05 [&_ul]:list-disc [&_ol]:ml-ds-05 [&_ol]:list-decimal',\n '[&_li]:text-surface-fg-muted',\n '[&_code]:rounded [&_code]:bg-surface-raised [&_code]:px-ds-02b [&_code]:py-ds-01 [&_code]:text-ds-md [&_code]:text-accent-11',\n '[&_pre]:rounded-surface [&_pre]:bg-surface-raised [&_pre]:p-ds-04',\n '[&_strong]:font-semibold [&_strong]:text-surface-fg',\n '[&_blockquote]:border-l-[3px] [&_blockquote]:border-accent-6 [&_blockquote]:pl-ds-04 [&_blockquote]:italic [&_blockquote]:text-surface-fg-subtle',\n '[&_mark]:rounded-xs [&_mark]:bg-warning-3 [&_mark]:px-[2px]',\n '[&_ul[data-type=\"taskList\"]]:ml-0 [&_ul[data-type=\"taskList\"]]:list-none [&_li[data-type=\"taskItem\"]]:flex [&_li[data-type=\"taskItem\"]]:items-start [&_li[data-type=\"taskItem\"]]:gap-ds-02',\n '[&_hr]:my-ds-04 [&_hr]:border-surface-border-strong',\n '[&_a]:text-accent-11 [&_a]:underline [&_a]:decoration-accent-6 hover:[&_a]:decoration-accent-11',\n '[&_img]:max-w-full [&_img]:rounded-control [&_img]:my-ds-03',\n MENTION_TOKEN_CLASS,\n] as const\n\ninterface ToolbarButtonProps {\n onClick: () => void\n isActive?: boolean\n disabled?: boolean\n title: string\n children: React.ReactNode\n}\n\nfunction ToolbarButton({\n onClick,\n isActive = false,\n disabled = false,\n title,\n children,\n}: ToolbarButtonProps) {\n return (\n <button\n type=\"button\"\n onClick={onClick}\n disabled={disabled}\n title={title}\n aria-label={title}\n aria-pressed={isActive}\n className={cn(\n 'inline-flex h-ds-xs-plus w-ds-xs-plus items-center justify-center rounded-control transition-colors duration-fast-01 ease-productive-standard',\n 'hover:bg-surface-raised-hover',\n 'disabled:pointer-events-none disabled:opacity-action-disabled',\n isActive\n ? 'bg-surface-raised-hover text-accent-11'\n : 'text-surface-fg-subtle',\n )}\n >\n {children}\n </button>\n )\n}\n\nfunction LinkButton({ editor }: { editor: Editor }) {\n const [showInput, setShowInput] = React.useState(false)\n const [url, setUrl] = React.useState('')\n const inputRef = React.useRef<HTMLInputElement>(null)\n const { isLink } = useEditorState({\n editor,\n selector: ({ editor: e }) => ({ isLink: e.isActive('link') }),\n })\n\n const handleToggle = () => {\n if (isLink) {\n editor.chain().focus().unsetLink().run()\n return\n }\n const previousUrl = editor.getAttributes('link').href || ''\n setUrl(previousUrl)\n setShowInput(true)\n setTimeout(() => inputRef.current?.focus(), 0)\n }\n\n const handleSubmit = (e: React.FormEvent) => {\n e.preventDefault()\n if (url.trim()) {\n editor.chain().focus().setLink({ href: url.trim() }).run()\n }\n setShowInput(false)\n setUrl('')\n }\n\n const handleKeyDown = (e: React.KeyboardEvent) => {\n if (e.key === 'Escape') {\n setShowInput(false)\n setUrl('')\n editor.commands.focus()\n }\n }\n\n return (\n <div className=\"relative\">\n <ToolbarButton onClick={handleToggle} isActive={isLink} title=\"Link\">\n <Icon icon={IconLink} size=\"sm\" />\n </ToolbarButton>\n {showInput && (\n <form\n onSubmit={handleSubmit}\n aria-label=\"Edit link URL\"\n className=\"absolute left-0 top-full z-popover mt-ds-01 flex items-center gap-ds-02 rounded-control bg-surface-overlay p-ds-02 shadow-raised-hover\"\n >\n <input\n ref={inputRef}\n type=\"url\"\n value={url}\n onChange={(e) => setUrl(e.target.value)}\n onKeyDown={handleKeyDown}\n placeholder=\"https://...\"\n className=\"h-ds-sm w-[240px] rounded-control-inner border border-surface-border-strong bg-surface-overlay px-ds-03 text-ds-sm text-surface-fg focus:border-accent-7 focus:outline-hidden\"\n />\n <Button type=\"submit\" variant=\"solid\" size=\"sm\">\n Apply\n </Button>\n </form>\n )}\n </div>\n )\n}\n\nfunction ToolbarDivider() {\n return <div className=\"mx-ds-02 h-[16px] w-px bg-surface-border\" />\n}\n\nfunction Toolbar({ editor, toolbar, onImageClick, onFileClick, onEmojiClick }: {\n editor: Editor\n toolbar?: ToolbarItem[]\n onImageClick?: () => void\n onFileClick?: () => void\n onEmojiClick?: () => void\n}) {\n // v3: useEditorState subscribes to specific state slices so the toolbar\n // re-renders only when active-state or can-undo/redo actually changes.\n const state = useEditorState({\n editor,\n selector: ({ editor: e }) => ({\n isBold: e.isActive('bold'),\n isItalic: e.isActive('italic'),\n isUnderline: e.isActive('underline'),\n isStrike: e.isActive('strike'),\n isHighlight: e.isActive('highlight'),\n isH2: e.isActive('heading', { level: 2 }),\n isH3: e.isActive('heading', { level: 3 }),\n isBlockquote: e.isActive('blockquote'),\n isBulletList: e.isActive('bulletList'),\n isOrderedList: e.isActive('orderedList'),\n isTaskList: e.isActive('taskList'),\n isCodeBlock: e.isActive('codeBlock'),\n isLink: e.isActive('link'),\n isAlignLeft: e.isActive({ textAlign: 'left' }),\n isAlignCenter: e.isActive({ textAlign: 'center' }),\n isAlignRight: e.isActive({ textAlign: 'right' }),\n canUndo: e.can().undo(),\n canRedo: e.can().redo(),\n }),\n })\n\n const show = (item: ToolbarItem) => !toolbar || toolbar.includes(item)\n\n // Each group is an array of ToolbarItem names that belong to it.\n // A divider renders between two adjacent groups only if both have at least one visible item.\n const hasInline = show('bold') || show('italic') || show('underline') || show('strike') || show('highlight')\n const hasBlock = show('h2') || show('h3') || show('blockquote')\n const hasList = show('bulletList') || show('orderedList') || show('taskList') || show('codeBlock')\n const hasMedia = show('link') || (!!onImageClick && show('image')) || (!!onFileClick && show('file')) || show('hr')\n const hasAlign = show('alignLeft') || show('alignCenter') || show('alignRight')\n const hasEmojiGroup = !!onEmojiClick && show('emoji')\n const hasHistory = show('undo') || show('redo')\n\n return (\n <div role=\"toolbar\" aria-label=\"Text formatting\" className=\"flex flex-wrap items-center gap-ds-01 border-b border-surface-border-strong px-ds-04 py-ds-02b\">\n {/* Inline formatting */}\n {show('bold') && (\n <ToolbarButton onClick={() => editor.chain().focus().toggleBold().run()} isActive={state.isBold} title=\"Bold\">\n <Icon icon={IconBold} size=\"sm\" stroke=\"bold\" />\n </ToolbarButton>\n )}\n {show('italic') && (\n <ToolbarButton onClick={() => editor.chain().focus().toggleItalic().run()} isActive={state.isItalic} title=\"Italic\">\n <Icon icon={IconItalic} size=\"sm\" stroke=\"bold\" />\n </ToolbarButton>\n )}\n {show('underline') && (\n <ToolbarButton onClick={() => editor.chain().focus().toggleUnderline().run()} isActive={state.isUnderline} title=\"Underline\">\n <Icon icon={IconUnderline} size=\"sm\" stroke=\"bold\" />\n </ToolbarButton>\n )}\n {show('strike') && (\n <ToolbarButton onClick={() => editor.chain().focus().toggleStrike().run()} isActive={state.isStrike} title=\"Strikethrough\">\n <Icon icon={IconStrikethrough} size=\"sm\" stroke=\"bold\" />\n </ToolbarButton>\n )}\n {show('highlight') && (\n <ToolbarButton onClick={() => editor.chain().focus().toggleHighlight().run()} isActive={state.isHighlight} title=\"Highlight\">\n <Icon icon={IconHighlight} size=\"sm\" stroke=\"bold\" />\n </ToolbarButton>\n )}\n\n {hasInline && hasBlock && <ToolbarDivider />}\n\n {/* Block formatting */}\n {show('h2') && (\n <ToolbarButton onClick={() => editor.chain().focus().toggleHeading({ level: 2 }).run()} isActive={state.isH2} title=\"Heading 2\">\n <Icon icon={IconH2} size=\"sm\" stroke=\"bold\" />\n </ToolbarButton>\n )}\n {show('h3') && (\n <ToolbarButton onClick={() => editor.chain().focus().toggleHeading({ level: 3 }).run()} isActive={state.isH3} title=\"Heading 3\">\n <Icon icon={IconH3} size=\"sm\" stroke=\"bold\" />\n </ToolbarButton>\n )}\n {show('blockquote') && (\n <ToolbarButton onClick={() => editor.chain().focus().toggleBlockquote().run()} isActive={state.isBlockquote} title=\"Blockquote\">\n <Icon icon={IconBlockquote} size=\"sm\" stroke=\"bold\" />\n </ToolbarButton>\n )}\n\n {hasBlock && hasList && <ToolbarDivider />}\n\n {/* Lists */}\n {show('bulletList') && (\n <ToolbarButton onClick={() => editor.chain().focus().toggleBulletList().run()} isActive={state.isBulletList} title=\"Bullet list\">\n <Icon icon={IconList} size=\"sm\" stroke=\"bold\" />\n </ToolbarButton>\n )}\n {show('orderedList') && (\n <ToolbarButton onClick={() => editor.chain().focus().toggleOrderedList().run()} isActive={state.isOrderedList} title=\"Ordered list\">\n <Icon icon={IconListNumbers} size=\"sm\" stroke=\"bold\" />\n </ToolbarButton>\n )}\n {show('taskList') && (\n <ToolbarButton onClick={() => editor.chain().focus().toggleTaskList().run()} isActive={state.isTaskList} title=\"Task list\">\n <Icon icon={IconListCheck} size=\"sm\" stroke=\"bold\" />\n </ToolbarButton>\n )}\n {show('codeBlock') && (\n <ToolbarButton onClick={() => editor.chain().focus().toggleCodeBlock().run()} isActive={state.isCodeBlock} title=\"Code block\">\n <Icon icon={IconCode} size=\"sm\" stroke=\"bold\" />\n </ToolbarButton>\n )}\n\n {hasList && hasMedia && <ToolbarDivider />}\n\n {/* Media & Links */}\n {show('link') && <LinkButton editor={editor} />}\n {onImageClick && show('image') && (\n <ToolbarButton onClick={onImageClick} title=\"Insert image\">\n <Icon icon={IconPhoto} size=\"sm\" />\n </ToolbarButton>\n )}\n {onFileClick && show('file') && (\n <ToolbarButton onClick={onFileClick} title=\"Attach file\">\n <Icon icon={IconPaperclip} size=\"sm\" />\n </ToolbarButton>\n )}\n {show('hr') && (\n <ToolbarButton onClick={() => editor.chain().focus().setHorizontalRule().run()} title=\"Horizontal rule\">\n <Icon icon={IconLineDashed} size=\"sm\" />\n </ToolbarButton>\n )}\n\n {hasMedia && hasAlign && <ToolbarDivider />}\n\n {/* Alignment */}\n {show('alignLeft') && (\n <ToolbarButton onClick={() => editor.chain().focus().setTextAlign('left').run()} isActive={state.isAlignLeft} title=\"Align left\">\n <Icon icon={IconAlignLeft} size=\"sm\" />\n </ToolbarButton>\n )}\n {show('alignCenter') && (\n <ToolbarButton onClick={() => editor.chain().focus().setTextAlign('center').run()} isActive={state.isAlignCenter} title=\"Align center\">\n <Icon icon={IconAlignCenter} size=\"sm\" />\n </ToolbarButton>\n )}\n {show('alignRight') && (\n <ToolbarButton onClick={() => editor.chain().focus().setTextAlign('right').run()} isActive={state.isAlignRight} title=\"Align right\">\n <Icon icon={IconAlignRight} size=\"sm\" />\n </ToolbarButton>\n )}\n\n {hasAlign && (hasEmojiGroup || hasHistory) && <ToolbarDivider />}\n\n {/* Emoji */}\n {onEmojiClick && show('emoji') && (\n <ToolbarButton onClick={onEmojiClick} title=\"Emoji\">\n <Icon icon={IconMoodSmile} size=\"sm\" />\n </ToolbarButton>\n )}\n\n {/* History */}\n {show('undo') && (\n <ToolbarButton onClick={() => editor.chain().focus().undo().run()} disabled={!state.canUndo} title=\"Undo\">\n <Icon icon={IconArrowBackUp} size=\"sm\" />\n </ToolbarButton>\n )}\n {show('redo') && (\n <ToolbarButton onClick={() => editor.chain().focus().redo().run()} disabled={!state.canRedo} title=\"Redo\">\n <Icon icon={IconArrowForwardUp} size=\"sm\" />\n </ToolbarButton>\n )}\n </div>\n )\n}\n\nconst LazyPicker = React.lazy(() => import('@emoji-mart/react'))\n\nfunction EmojiPickerLazy({ set = 'native', onSelect }: { set?: string; onSelect: (emoji: { id: string; native: string }) => void }) {\n const [data, setData] = React.useState<unknown>(null)\n const { colorMode } = useColorMode()\n\n React.useEffect(() => {\n loadEmojiData(set).then((d) => setData(d))\n }, [set])\n\n const fallback = <div className=\"flex h-[350px] w-[352px] items-center justify-center rounded-surface bg-surface-overlay shadow-raised-hover\"><span className=\"text-ds-sm text-surface-fg-subtle\">Loading...</span></div>\n\n if (!data) return fallback\n\n return (\n <React.Suspense fallback={fallback}>\n <LazyPicker\n data={data}\n set={set}\n onEmojiSelect={(emoji: { native: string; id: string }) => onSelect({ id: emoji.id, native: emoji.native })}\n theme={colorMode === 'dark' ? 'dark' : 'light'}\n previewPosition=\"none\"\n skinTonePosition=\"none\"\n />\n </React.Suspense>\n )\n}\n\nexport type ToolbarItem =\n | 'bold' | 'italic' | 'underline' | 'strike' | 'highlight'\n | 'h2' | 'h3' | 'blockquote'\n | 'bulletList' | 'orderedList' | 'taskList' | 'codeBlock'\n | 'link' | 'image' | 'file' | 'hr'\n | 'alignLeft' | 'alignCenter' | 'alignRight'\n | 'emoji' | 'undo' | 'redo'\n\nexport interface MentionItem {\n id: string\n label: string\n avatar?: string\n}\n\n/**\n * A Tiptap-powered rich text editor with a configurable toolbar, @mentions,\n * emoji suggestions, file/image uploads, and task lists.\n * Outputs sanitized HTML via `onChange`.\n */\nexport interface RichTextEditorProps extends Omit<React.ComponentPropsWithoutRef<'div'>, 'onChange' | 'content'> {\n /** Initial HTML content. Updates are NOT reactive — use `onChange` for controlled state. */\n content?: string\n placeholder?: string\n /** Called with the editor's HTML string whenever content changes. */\n onChange?: (html: string) => void\n className?: string\n /** Set to false to render in read-only mode (e.g. for previewing). */\n editable?: boolean\n /** Called when an image is pasted/dropped. Return a URL. If not provided, images inline as base64. */\n onImageUpload?: (file: File) => Promise<string>\n /** Called when a non-image file is dropped/pasted. If not provided, non-image files are ignored. */\n onFileUpload?: (file: File) => Promise<{ url: string; name: string; size: number }>\n /** Whitelist of toolbar items to display. Omit to show all. */\n toolbar?: ToolbarItem[]\n /** Static list of mentionable items (shown when user types @). */\n mentions?: MentionItem[]\n /** Async mention search. Takes precedence over static `mentions` list. */\n onMentionSearch?: (query: string) => Promise<MentionItem[]>\n /** Called when a mention is selected from the suggestion dropdown. */\n onMentionSelect?: (item: MentionItem) => void\n /** Emoji art style. @default 'native' */\n emojiSet?: EmojiSet\n}\n\nconst RichTextEditor = React.forwardRef<HTMLDivElement, RichTextEditorProps>(\n function RichTextEditor({\n content = '',\n placeholder = 'Start writing...',\n onChange,\n className,\n editable = true,\n toolbar,\n onImageUpload,\n onFileUpload,\n mentions,\n onMentionSearch,\n onMentionSelect,\n emojiSet = 'native',\n ...props\n}, ref) {\n const editorRef = React.useRef<ReturnType<typeof useEditor>>(null)\n const isInternalChangeRef = React.useRef(false)\n const [showEmojiPicker, setShowEmojiPicker] = React.useState(false)\n const emojiPickerRef = React.useRef<HTMLDivElement>(null)\n const imageInputRef = React.useRef<HTMLInputElement>(null)\n const fileInputRef = React.useRef<HTMLInputElement>(null)\n\n const handleImageInsert = async (file: File) => {\n const ed = editorRef.current\n if (!ed) return\n if (onImageUpload) {\n const url = await onImageUpload(file)\n if (url && /^https?:\\/\\//i.test(url)) {\n ed.chain().focus().setImage({ src: url }).run()\n }\n } else {\n const reader = new FileReader()\n reader.onload = () => {\n ed.chain().focus().setImage({ src: reader.result as string }).run()\n }\n reader.readAsDataURL(file)\n }\n }\n\n const handleFileInsert = async (file: File) => {\n const ed = editorRef.current\n if (!ed || !onFileUpload) return\n const result = await onFileUpload(file)\n ed.chain().focus().insertContent({\n type: 'fileAttachment',\n attrs: { url: result.url, name: result.name, size: result.size },\n }).run()\n }\n\n const editor = useEditor({\n immediatelyRender: false, // SSR-safe — prevents hydration mismatch in Next.js\n extensions: [\n StarterKit.configure({\n heading: { levels: [2, 3] },\n // v3: Underline + Link are now included in StarterKit\n link: {\n openOnClick: false,\n protocols: ['http', 'https', 'mailto'],\n validate: (href: string) => /^(https?:\\/\\/|mailto:)/i.test(href),\n HTMLAttributes: {\n rel: 'noopener noreferrer',\n target: '_blank',\n },\n },\n }),\n Placeholder.configure({\n placeholder,\n emptyEditorClass:\n 'before:content-[attr(data-placeholder)] before:text-surface-fg-subtle before:float-left before:h-0 before:pointer-events-none',\n }),\n Highlight.configure({ multicolor: false }),\n ListKit.configure({\n bulletList: false, // Already in StarterKit\n orderedList: false, // Already in StarterKit\n listItem: false, // Already in StarterKit\n listKeymap: false, // Already in StarterKit\n taskList: {},\n taskItem: { nested: true },\n }),\n TextAlign.configure({ types: ['heading', 'paragraph'] }),\n Image.configure({\n allowBase64: true,\n HTMLAttributes: {\n class: 'max-w-full rounded-control',\n },\n }),\n FileAttachment,\n ...(mentions || onMentionSearch ? [\n Mention.configure({\n HTMLAttributes: { class: 'mention' },\n suggestion: {\n items: async ({ query }: { query: string }) => {\n if (onMentionSearch) return await onMentionSearch(query)\n if (mentions) return mentions.filter(m => m.label.toLowerCase().includes(query.toLowerCase())).slice(0, 8)\n return []\n },\n render: createSuggestionRenderer(onMentionSelect),\n },\n }),\n ] : []),\n EmojiNode,\n createEmojiSuggestion(emojiSet),\n ],\n content,\n editable,\n editorProps: {\n handleDrop: (_view, event, _slice, moved) => {\n if (moved || !event.dataTransfer?.files.length) return false\n const file = event.dataTransfer.files[0]\n if (!file) return false\n if (file.type.startsWith('image/')) {\n handleImageInsert(file)\n return true\n }\n if (onFileUpload) {\n handleFileInsert(file)\n return true\n }\n return false\n },\n handlePaste: (_view, event) => {\n const file = event.clipboardData?.files[0]\n if (!file) return false\n if (file.type.startsWith('image/')) {\n handleImageInsert(file)\n return true\n }\n if (onFileUpload) {\n handleFileInsert(file)\n return true\n }\n return false\n },\n attributes: {\n class: cn(\n ...PROSE_CLASSES,\n 'focus:outline-hidden',\n 'min-h-[120px] px-ds-04 py-ds-04',\n ),\n },\n },\n onUpdate: ({ editor: ed }) => {\n isInternalChangeRef.current = true\n onChange?.(ed.getHTML())\n queueMicrotask(() => {\n isInternalChangeRef.current = false\n })\n },\n })\n\n React.useEffect(() => {\n editorRef.current = editor\n }, [editor])\n\n // Close emoji picker on click outside\n React.useEffect(() => {\n if (!showEmojiPicker) return\n const handleClickOutside = (e: MouseEvent) => {\n if (emojiPickerRef.current && !emojiPickerRef.current.contains(e.target as Node)) {\n setShowEmojiPicker(false)\n }\n }\n document.addEventListener('mousedown', handleClickOutside)\n return () => document.removeEventListener('mousedown', handleClickOutside)\n }, [showEmojiPicker])\n\n React.useEffect(() => {\n if (isInternalChangeRef.current) return\n if (editor && content !== editor.getHTML()) {\n editor.commands.setContent(content, { emitUpdate: false })\n }\n }, [editor, content])\n\n if (!editor) return null\n\n return (\n <div ref={ref} {...props} className={cn('relative', className)}>\n {/* Emoji picker rendered outside the overflow-hidden box so it isn't clipped */}\n {showEmojiPicker && (\n <div\n ref={emojiPickerRef}\n className=\"absolute bottom-full right-0 z-popover mb-ds-02\"\n onKeyDown={(e) => {\n if (e.key === 'Escape') {\n e.stopPropagation()\n setShowEmojiPicker(false)\n }\n }}\n >\n <EmojiPickerLazy\n set={emojiSet}\n onSelect={async ({ id, native }) => {\n const data = await loadEmojiData(emojiSet)\n const resolved = lookupEmoji(data, id)\n if (resolved) {\n editor.chain().focus().insertContent({\n type: 'emojiNode',\n attrs: { id, native: resolved.native, set: emojiSet, x: resolved.x, y: resolved.y },\n }).run()\n } else {\n editor.chain().focus().insertContent(native).run()\n }\n setShowEmojiPicker(false)\n }}\n />\n </div>\n )}\n <div\n className={cn(\n 'overflow-hidden rounded-surface border border-surface-border-strong bg-surface-raised',\n 'transition-colors ease-productive-standard focus-within:border-surface-border-strong',\n )}\n >\n <input\n ref={imageInputRef}\n type=\"file\"\n accept=\"image/*\"\n aria-label=\"Upload image\"\n className=\"hidden\"\n onChange={(e) => {\n const file = e.target.files?.[0]\n if (file) handleImageInsert(file)\n e.target.value = ''\n }}\n />\n {onFileUpload && (\n <input\n ref={fileInputRef}\n type=\"file\"\n aria-label=\"Upload file\"\n className=\"hidden\"\n onChange={(e) => {\n const file = e.target.files?.[0]\n if (file) handleFileInsert(file)\n e.target.value = ''\n }}\n />\n )}\n {editable && (\n <Toolbar\n editor={editor}\n toolbar={toolbar}\n onImageClick={() => imageInputRef.current?.click()}\n onFileClick={onFileUpload ? () => fileInputRef.current?.click() : undefined}\n onEmojiClick={() => setShowEmojiPicker((prev) => !prev)}\n />\n )}\n <EditorContent editor={editor} />\n </div>\n </div>\n )\n},\n)\n\nRichTextEditor.displayName = 'RichTextEditor'\n\nexport interface RichTextViewerProps extends Omit<React.ComponentPropsWithoutRef<'div'>, 'content'> {\n content: string\n className?: string\n}\n\nconst RichTextViewer = React.forwardRef<HTMLDivElement, RichTextViewerProps>(\n function RichTextViewer({ content, className, ...props }, ref) {\n const editor = useEditor({\n immediatelyRender: false, // SSR-safe — prevents hydration mismatch in Next.js\n extensions: [\n StarterKit.configure({\n heading: { levels: [2, 3] },\n link: {\n openOnClick: true,\n protocols: ['http', 'https', 'mailto'],\n validate: (href: string) => /^(https?:\\/\\/|mailto:)/i.test(href),\n HTMLAttributes: {\n rel: 'noopener noreferrer',\n target: '_blank',\n },\n },\n }),\n Highlight.configure({ multicolor: false }),\n ListKit.configure({\n bulletList: false,\n orderedList: false,\n listItem: false,\n listKeymap: false,\n taskList: {},\n taskItem: { nested: true },\n }),\n TextAlign.configure({ types: ['heading', 'paragraph'] }),\n Image.configure({\n allowBase64: true,\n HTMLAttributes: {\n class: 'max-w-full rounded-control',\n },\n }),\n FileAttachment,\n Mention.configure({\n HTMLAttributes: { class: 'mention' },\n }),\n ],\n content,\n editable: false,\n editorProps: {\n attributes: {\n class: cn(...PROSE_CLASSES),\n },\n },\n })\n\n if (!editor) return null\n\n return (\n <div ref={ref} {...props} className={className}>\n <EditorContent editor={editor} />\n </div>\n )\n},\n)\n\nRichTextViewer.displayName = 'RichTextViewer'\n\nexport { RichTextEditor, RichTextViewer }\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"message.d.ts","sourceRoot":"","sources":["../../../src/ui/chat/message.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAK9B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAA;
|
|
1
|
+
{"version":3,"file":"message.d.ts","sourceRoot":"","sources":["../../../src/ui/chat/message.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
/**
|
|
3
|
+
* Tailwind classes that style an in-content `@mention` token (`<span class="mention">`).
|
|
4
|
+
*
|
|
5
|
+
* Shared by the rich-text editor, the chat input, AND the read-only Message
|
|
6
|
+
* display so a mention looks identical whether it's being typed or rendered in a
|
|
7
|
+
* timeline. Keep this the single source — three copies drifted before (see #99,
|
|
8
|
+
* where Message never styled the token and `highlight="mention"` went invisible).
|
|
9
|
+
*/
|
|
10
|
+
export declare const MENTION_TOKEN_CLASS = "[&_.mention]:rounded-control-inner [&_.mention]:bg-accent-2 [&_.mention]:px-ds-02 [&_.mention]:py-[1px] [&_.mention]:font-medium [&_.mention]:text-accent-11";
|
|
11
|
+
//# sourceMappingURL=mention.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mention.d.ts","sourceRoot":"","sources":["../../../src/ui/lib/mention.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,eAAO,MAAM,mBAAmB,iKACgI,CAAA"}
|
|
@@ -39,7 +39,7 @@ Compound component: `Message`, `Message.Avatar`, `Message.Content`, `Message.Aut
|
|
|
39
39
|
### Message (root) Props
|
|
40
40
|
variant: "flat" | "bubble" (default: "flat")
|
|
41
41
|
placement: "start" | "end" (default: "start")
|
|
42
|
-
highlight: "mention" | "internal"
|
|
42
|
+
highlight: "mention" | "internal" — "mention" styles in-content @tokens (any <span class="mention">) so they tint (accent-2/accent-11); the row itself stays flat. "internal" tints the whole row (warning-2) for system/internal notes. Both also expose a `data-highlight` attribute hook.
|
|
43
43
|
grouped: boolean — hides avatar/author for consecutive messages (default: false)
|
|
44
44
|
deleted: boolean — renders deleted placeholder (default: false)
|
|
45
45
|
deletedText: string (default: "This message was deleted")
|
package/llms.txt
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @devalok/shilp-sutra
|
|
2
2
|
|
|
3
|
-
> Radix UI + Tailwind 4 (CSS-first) + CVA design system for Devalok apps, v0.45.
|
|
3
|
+
> Radix UI + Tailwind 4 (CSS-first) + CVA design system for Devalok apps, v0.45.1.
|
|
4
4
|
> Built on the same primitives as shadcn/ui but with DIFFERENT prop APIs — never guess from shadcn knowledge; verify every prop.
|
|
5
5
|
> This file is a ROUTER: it tells you what exists and where to get details. Do not look for prop tables here — fetch them per component (MCP tool or per-component doc file below).
|
|
6
6
|
|
package/mcp-manifest.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@devalok/shilp-sutra",
|
|
3
|
-
"version": "0.45.
|
|
3
|
+
"version": "0.45.1",
|
|
4
4
|
"description": "Devalok Design System — accessible React components, OKLCH design tokens, and Tailwind 4 CSS-first setup. Ships with AI-agent setup recipes.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Devalok Design & Strategy Studios <shilp-sutra@devalok.in>",
|
|
@@ -387,6 +387,11 @@
|
|
|
387
387
|
"import": "./dist/ui/table.js",
|
|
388
388
|
"default": "./dist/ui/table.js"
|
|
389
389
|
},
|
|
390
|
+
"./ui/table-row-link": {
|
|
391
|
+
"types": "./dist/ui/table-row-link.d.ts",
|
|
392
|
+
"import": "./dist/ui/table-row-link.js",
|
|
393
|
+
"default": "./dist/ui/table-row-link.js"
|
|
394
|
+
},
|
|
390
395
|
"./ui/tabs": {
|
|
391
396
|
"types": "./dist/ui/tabs.d.ts",
|
|
392
397
|
"import": "./dist/ui/tabs.js",
|
package/skill/SKILL.md
CHANGED
|
@@ -3,7 +3,7 @@ name: shilp-sutra
|
|
|
3
3
|
description: Add, configure, and use components from Devalok's shilp-sutra design system (@devalok/shilp-sutra) — a Tailwind 4 + React 19 + CVA library with 110+ accessible components, OKLCH design tokens, framer-motion animations, and per-component RSC-safe entry points. Use this skill whenever the user mentions shilp-sutra, Devalok, the @devalok npm scope, or asks to install/add/style/theme UI in any React project that already depends on the package — even if they don't name it explicitly. Use it instead of generic shadcn/ui, MUI, or Chakra knowledge when shilp-sutra is in the project. Covers Next.js (App + Pages), Vite, Astro, Remix, TanStack Start setup playbooks; component API and variant reference; brand token customization; Server Component import patterns; and a troubleshoot tree for the thirteen most common breakages.
|
|
4
4
|
license: MIT
|
|
5
5
|
metadata:
|
|
6
|
-
version: "0.45.
|
|
6
|
+
version: "0.45.1"
|
|
7
7
|
author: Devalok Design & Strategy Studios
|
|
8
8
|
homepage: https://github.com/devalok-design/shilp-sutra
|
|
9
9
|
npm: https://www.npmjs.com/package/@devalok/shilp-sutra
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
# @devalok/shilp-sutra
|
|
4
4
|
|
|
5
|
-
> Radix UI + Tailwind 4 (CSS-first) + CVA design system for Devalok apps, v0.45.
|
|
5
|
+
> Radix UI + Tailwind 4 (CSS-first) + CVA design system for Devalok apps, v0.45.1.
|
|
6
6
|
> Built on the same primitives as shadcn/ui but with DIFFERENT prop APIs — never guess from shadcn knowledge; verify every prop.
|
|
7
7
|
> This file is a ROUTER: it tells you what exists and where to get details. Do not look for prop tables here — fetch them per component (MCP tool or per-component doc file below).
|
|
8
8
|
|