dishui 0.0.105 β†’ 0.0.108

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.
Files changed (64) hide show
  1. package/dist/.release-verified.json +2 -2
  2. package/dist/components/ChoicePicker/index.js +8 -6
  3. package/dist/components/ChoicePicker/index.js.map +1 -1
  4. package/dist/components/Column/index.d.ts +3 -2
  5. package/dist/components/Column/index.js +5 -17
  6. package/dist/components/Column/index.js.map +1 -1
  7. package/dist/components/ConfirmDialog/index.d.ts +20 -0
  8. package/dist/components/ConfirmDialog/index.js +84 -0
  9. package/dist/components/ConfirmDialog/index.js.map +1 -0
  10. package/dist/components/DateTimeInput/index.js +28 -26
  11. package/dist/components/DateTimeInput/index.js.map +1 -1
  12. package/dist/components/Input/index.js +1 -1
  13. package/dist/components/Input/index.js.map +1 -1
  14. package/dist/components/MultipleChoice/index.js +2 -1
  15. package/dist/components/MultipleChoice/index.js.map +1 -1
  16. package/dist/components/Row/index.d.ts +3 -2
  17. package/dist/components/Row/index.js +5 -17
  18. package/dist/components/Row/index.js.map +1 -1
  19. package/dist/components/Tab/index.js +80 -0
  20. package/dist/components/Tab/index.js.map +1 -0
  21. package/dist/components/Tabs/index.js +28 -33
  22. package/dist/components/Tabs/index.js.map +1 -1
  23. package/dist/components/Tabs/tabNavigation.d.ts +2 -0
  24. package/dist/components/Tabs/tabNavigation.js +14 -0
  25. package/dist/components/Tabs/tabNavigation.js.map +1 -0
  26. package/dist/components/pro/CodeEditor/editors/MarkdownEditor.d.ts +1 -1
  27. package/dist/components/pro/CodeEditor/editors/MarkdownEditor.js +25 -26
  28. package/dist/components/pro/CodeEditor/editors/MarkdownEditor.js.map +1 -1
  29. package/dist/components/pro/FileExplorer/Breadcrumb.js +8 -11
  30. package/dist/components/pro/FileExplorer/Breadcrumb.js.map +1 -1
  31. package/dist/components/pro/Flow/Flow.js +184 -182
  32. package/dist/components/pro/Flow/Flow.js.map +1 -1
  33. package/dist/components/pro/Flow/archify/convert.js +26 -26
  34. package/dist/components/pro/Flow/archify/convert.js.map +1 -1
  35. package/dist/components/pro/Flow/hooks/useLayoutActions.js +13 -11
  36. package/dist/components/pro/Flow/hooks/useLayoutActions.js.map +1 -1
  37. package/dist/components/pro/RichTextEditor/AIRewrite.d.ts +1 -0
  38. package/dist/components/pro/RichTextEditor/AIRewrite.js +109 -88
  39. package/dist/components/pro/RichTextEditor/AIRewrite.js.map +1 -1
  40. package/dist/components/pro/RichTextEditor/BubbleToolbar.js +73 -83
  41. package/dist/components/pro/RichTextEditor/BubbleToolbar.js.map +1 -1
  42. package/dist/components/pro/RichTextEditor/RichTextEditor.js +133 -132
  43. package/dist/components/pro/RichTextEditor/RichTextEditor.js.map +1 -1
  44. package/dist/components/pro/RichTextEditor/Toolbar/ColorPicker.d.ts +2 -0
  45. package/dist/components/pro/RichTextEditor/Toolbar/ColorPicker.js +73 -49
  46. package/dist/components/pro/RichTextEditor/Toolbar/ColorPicker.js.map +1 -1
  47. package/dist/components/pro/RichTextEditor/Toolbar/SelectDropdown.d.ts +1 -0
  48. package/dist/components/pro/RichTextEditor/Toolbar/SelectDropdown.js +12 -14
  49. package/dist/components/pro/RichTextEditor/Toolbar/SelectDropdown.js.map +1 -1
  50. package/dist/components/pro/RichTextEditor/extensions/CodeBlockView.js +113 -113
  51. package/dist/components/pro/RichTextEditor/extensions/CodeBlockView.js.map +1 -1
  52. package/dist/components/pro/RichTextEditor/types.d.ts +2 -0
  53. package/dist/components/pro/RichTextEditor/types.js.map +1 -1
  54. package/dist/css.d.ts +1 -0
  55. package/dist/dishui.css +1 -1
  56. package/dist/index-nostyled.d.ts +2 -0
  57. package/dist/index.d.ts +4 -0
  58. package/dist/index.js +113 -111
  59. package/dist/layouts/Sidebar.js +201 -107
  60. package/dist/layouts/Sidebar.js.map +1 -1
  61. package/dist/utils/flexLayout.d.ts +5 -0
  62. package/dist/utils/flexLayout.js +19 -0
  63. package/dist/utils/flexLayout.js.map +1 -0
  64. package/package.json +63 -57
@@ -1 +1 @@
1
- {"version":3,"file":"AIRewrite.js","names":[],"sources":["../../../../src/components/pro/RichTextEditor/AIRewrite.tsx"],"sourcesContent":["import React, { useState, useRef, useCallback, useEffect, useId } from 'react';\nimport type { Editor } from '@tiptap/react';\nimport type { AIConfig, AIAction, RichTextEditorLabels } from './types';\nimport { defaultLabels } from './types';\nimport StreamingText from '../../ai/StreamingText';\nimport LoadingState from '../../ai/LoadingState';\n\ninterface AIRewriteProps {\n editor: Editor;\n aiConfig: AIConfig;\n labels?: RichTextEditorLabels;\n initialSelection: string;\n selectionRange: { from: number; to: number };\n onClose: () => void;\n}\n\nconst QUICK_ACTIONS: { key: AIAction; labelKey: keyof RichTextEditorLabels; icon: string }[] = [\n { key: 'rewrite', labelKey: 'aiRewrite', icon: '✏' },\n { key: 'shorten', labelKey: 'aiShorten', icon: 'πŸ“' },\n { key: 'expand', labelKey: 'aiExpand', icon: 'πŸ“„' },\n { key: 'fix_grammar', labelKey: 'aiFixGrammar', icon: 'βœ“' },\n { key: 'translate_en', labelKey: 'aiTranslateEn', icon: 'EN' },\n { key: 'translate_zh', labelKey: 'aiTranslateZh', icon: 'δΈ­' },\n { key: 'professional', labelKey: 'aiProfessional', icon: 'πŸ’Ό' },\n { key: 'casual', labelKey: 'aiCasual', icon: 'πŸ’¬' },\n];\n\nfunction getSystemPrompt(action: AIAction | 'custom', customPrompt?: string): string {\n if (action === 'custom') return customPrompt || 'Follow the user instructions.';\n const prompts: Record<AIAction, string> = {\n rewrite: 'Rewrite the following text to improve clarity and readability. Keep the same meaning and tone. Return only the rewritten text.',\n shorten: 'Shorten the following text while preserving its key meaning. Be concise. Return only the shortened text.',\n expand: 'Expand the following text with more details and examples while keeping the same tone. Return only the expanded text.',\n fix_grammar: 'Fix any grammar, spelling, and punctuation errors in the following text. Return only the corrected text.',\n translate_en: 'Translate the following text to English. Return only the translated text.',\n translate_zh: 'Translate the following text to Chinese. Return only the translated text.',\n professional: 'Rewrite the following text in a professional, formal tone. Return only the rewritten text.',\n casual: 'Rewrite the following text in a casual, friendly tone. Return only the rewritten text.',\n };\n return customPrompt || prompts[action];\n}\n\nasync function* readSSE(response: Response): AsyncGenerator<string> {\n const reader = response.body?.getReader();\n if (!reader) throw new Error('No response body');\n const decoder = new TextDecoder();\n let buffer = '';\n let data: string[] = [];\n try {\n while (true) {\n const { done, value } = await reader.read();\n buffer += done ? decoder.decode() : decoder.decode(value, { stream: true });\n // Normalize CRLF only when the complete line is available (chunks may split it).\n let newline: number;\n while ((newline = buffer.indexOf('\\n')) !== -1) {\n const line = buffer.slice(0, newline).replace(/\\r$/, '');\n buffer = buffer.slice(newline + 1);\n if (!line) {\n if (data.length) yield data.join('\\n');\n data = [];\n } else if (line.startsWith('data:')) {\n data.push(line.slice(5).replace(/^ /, ''));\n }\n }\n if (done) {\n if (buffer.startsWith('data:')) data.push(buffer.slice(5).replace(/^ /, '').replace(/\\r$/, ''));\n if (data.length) yield data.join('\\n');\n return;\n }\n }\n } finally {\n try { await reader.cancel(); } catch { /* The request may already be aborted. */ }\n reader.releaseLock();\n }\n}\n\nasync function* streamOpenAI(\n config: AIConfig,\n text: string,\n systemPrompt: string,\n signal: AbortSignal,\n): AsyncGenerator<string> {\n if (!config.gateway || !config.model) throw new Error('OpenAI protocol requires gateway and model');\n const body = JSON.stringify({\n model: config.model,\n messages: [\n { role: 'system', content: systemPrompt },\n { role: 'user', content: text },\n ],\n stream: true,\n });\n\n const response = await fetch(config.gateway, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...(config.apiKey ? { Authorization: `Bearer ${config.apiKey}` } : {}),\n ...config.headers,\n },\n body,\n signal,\n });\n\n if (!response.ok) throw new Error(`AI API error: ${response.status} ${response.statusText}`);\n for await (const payload of readSSE(response)) {\n if (payload.trim() === '[DONE]') return;\n let data;\n try { data = JSON.parse(payload); } catch { continue; }\n if (data?.error) throw new Error(data.error.message || 'AI stream error');\n const content = data?.choices?.[0]?.delta?.content;\n if (typeof content === 'string') yield content;\n }\n}\n\nasync function* streamAnthropic(\n config: AIConfig,\n text: string,\n systemPrompt: string,\n signal: AbortSignal,\n): AsyncGenerator<string> {\n if (!config.gateway || !config.model) throw new Error('Anthropic protocol requires gateway and model');\n const body = JSON.stringify({\n model: config.model,\n max_tokens: 4096,\n system: systemPrompt,\n messages: [{ role: 'user', content: text }],\n stream: true,\n });\n\n const response = await fetch(config.gateway, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...(config.apiKey ? { 'x-api-key': config.apiKey } : {}),\n 'anthropic-version': '2023-06-01',\n ...config.headers,\n },\n body,\n signal,\n });\n\n if (!response.ok) throw new Error(`AI API error: ${response.status} ${response.statusText}`);\n for await (const payload of readSSE(response)) {\n let data;\n try { data = JSON.parse(payload); } catch { continue; }\n if (data?.type === 'error') throw new Error(data.error?.message || 'AI stream error');\n if (data?.type === 'message_stop') return;\n if (data?.type === 'content_block_delta' && typeof data.delta?.text === 'string') yield data.delta.text;\n }\n}\n\nfunction getStream(\n config: AIConfig,\n text: string,\n systemPrompt: string,\n action: AIAction | 'custom',\n signal: AbortSignal,\n request?: { prompt: string; selectedText: string; mode: 'replace' | 'insert' },\n): AsyncIterable<string> {\n if (config.protocol === 'custom') {\n if (!config.customHandler) throw new Error('Custom AI protocol requires customHandler');\n return config.customHandler({\n text,\n action: action === 'custom' ? 'rewrite' : action,\n systemPrompt,\n signal,\n prompt: request?.prompt ?? '',\n selectedText: request?.selectedText ?? text,\n mode: request?.mode ?? 'replace',\n });\n }\n if (config.protocol === 'anthropic') return streamAnthropic(config, text, systemPrompt, signal);\n return streamOpenAI(config, text, systemPrompt, signal);\n}\n\nconst AIRewrite: React.FC<AIRewriteProps> = ({ editor, aiConfig, labels: propsLabels, initialSelection, selectionRange, onClose }) => {\n const labels = { ...defaultLabels, ...propsLabels };\n const promptId = useId();\n const [prompt, setPrompt] = useState('');\n const [isProcessing, setIsProcessing] = useState(false);\n const [streamedText, setStreamedText] = useState('');\n const [error, setError] = useState<string | null>(null);\n const abortRef = useRef<AbortController | null>(null);\n const promptRef = useRef<HTMLTextAreaElement>(null);\n const [editable, setEditable] = useState(() => !editor.isDestroyed && editor.isEditable);\n const [invalid, setInvalid] = useState(false);\n const sourceDoc = useRef(editor.state.doc);\n const invalidRef = useRef(false);\n\n const cancelRequest = useCallback(() => {\n const request = abortRef.current;\n abortRef.current = null;\n request?.abort();\n }, []);\n\n useEffect(() => {\n sourceDoc.current = editor.state.doc;\n invalidRef.current = false;\n setInvalid(false);\n setStreamedText('');\n setIsProcessing(false);\n setError(null);\n const invalidate = () => {\n setEditable(!editor.isDestroyed && editor.isEditable);\n if (editor.isDestroyed || !editor.isEditable || editor.state.doc !== sourceDoc.current) {\n invalidRef.current = true;\n setInvalid(true);\n cancelRequest();\n setIsProcessing(false);\n setStreamedText('');\n setError('The document or editing permissions changed. Close and reopen AI Assistant.');\n }\n };\n invalidate();\n editor.on('update', invalidate);\n editor.on('transaction', invalidate);\n editor.on('destroy', invalidate);\n return () => {\n cancelRequest();\n editor.off('update', invalidate);\n editor.off('transaction', invalidate);\n editor.off('destroy', invalidate);\n };\n }, [editor, initialSelection, selectionRange.from, selectionRange.to, cancelRequest]);\n\n const selectedText = initialSelection;\n\n useEffect(() => {\n if (promptRef.current) promptRef.current.focus();\n }, []);\n\n const handleGenerate = useCallback(async (action: AIAction | 'custom' = 'custom', customPromptOverride?: string) => {\n if (editor.isDestroyed || !editor.isEditable || invalidRef.current || abortRef.current) return;\n const mode = selectedText ? 'replace' : 'insert';\n if (mode === 'insert' && !prompt.trim()) return;\n\n const actionPrompt = action === 'custom'\n ? (mode === 'replace'\n ? 'Edit the selected text according to the user instruction. Return only the replacement text.'\n : 'Generate text according to the user instruction. Return only the text to insert.')\n : getSystemPrompt(action, customPromptOverride);\n const systemPrompt = [aiConfig.systemPrompt, actionPrompt].filter(Boolean).join('\\n\\n');\n const userText = mode === 'replace'\n ? (prompt.trim()\n ? `Instruction:\\n${prompt.trim()}\\n\\nSelected text:\\n${selectedText}`\n : selectedText)\n : prompt.trim();\n\n setIsProcessing(true);\n setStreamedText('');\n setError(null);\n\n const abort = new AbortController();\n abortRef.current = abort;\n\n try {\n let result = '';\n for await (const chunk of getStream(aiConfig, userText, systemPrompt, action, abort.signal, {\n prompt: prompt.trim(),\n selectedText,\n mode,\n })) {\n if (abort.signal.aborted || abortRef.current !== abort) break;\n result += chunk;\n setStreamedText(result);\n }\n } catch (err: unknown) {\n if (abort.signal.aborted || abortRef.current !== abort) return;\n setStreamedText('');\n setError(String(err instanceof Error ? err.message : err));\n } finally {\n if (abortRef.current === abort) {\n abortRef.current = null;\n setIsProcessing(false);\n }\n }\n }, [editor, aiConfig, selectedText, prompt]);\n\n const handleAccept = useCallback(() => {\n if (!streamedText || abortRef.current || editor.isDestroyed || !editor.isEditable || invalidRef.current) return;\n const { from, to } = selectionRange;\n if (editor.state.doc !== sourceDoc.current || !Number.isInteger(from) || !Number.isInteger(to) || from < 0 || to < from || to > editor.state.doc.content.size) {\n setError('The original selection is no longer valid. Close and reopen AI Assistant.');\n return;\n }\n // AI output is plain text, never HTML. Use the captured range even if the cursor moved.\n editor.view.dispatch(editor.state.tr.insertText(streamedText, from, to));\n setStreamedText('');\n setPrompt('');\n onClose();\n }, [editor, streamedText, selectionRange, onClose]);\n\n const handleReject = useCallback(() => {\n cancelRequest();\n setStreamedText('');\n setIsProcessing(false);\n setError(null);\n }, [cancelRequest]);\n\n const handleKeyDown = useCallback((e: React.KeyboardEvent) => {\n if (e.key === 'Enter' && (e.metaKey || e.ctrlKey)) {\n e.preventDefault();\n handleGenerate();\n }\n }, [handleGenerate]);\n\n return (\n <div className=\"rte-ai-panel-v2\">\n <div className=\"rte-ai-panel-v2__header\">\n <span className=\"rte-ai-panel-v2__title\">✨ {labels.ai || 'AI Assistant'}</span>\n <button type=\"button\" className=\"rte-ai-panel-v2__close-btn\" onClick={() => { cancelRequest(); onClose(); }} title={labels.aiClose} aria-label={labels.aiClose}>\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\" /><line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\" />\n </svg>\n </button>\n </div>\n {/* Prompt input */}\n <div className=\"rte-ai-panel-v2__prompt-section\">\n <label className=\"rte-ai-panel-v2__label\" htmlFor={promptId}>{labels.aiPromptLabel}</label>\n <textarea\n id={promptId}\n ref={promptRef}\n className=\"rte-ai-panel-v2__prompt-input\"\n value={prompt}\n onChange={(e) => setPrompt(e.target.value)}\n onKeyDown={handleKeyDown}\n placeholder={labels.aiPromptPlaceholder}\n rows={3}\n disabled={isProcessing || !editable || invalid}\n />\n </div>\n\n {/* Selected text display */}\n <div className=\"rte-ai-panel-v2__selection-section\">\n <span className=\"rte-ai-panel-v2__label\">{labels.aiSelectedText}</span>\n <div className=\"rte-ai-panel-v2__selection-box\">\n {selectedText ? (\n <span>{selectedText.length > 300 ? selectedText.slice(0, 300) + '...' : selectedText}</span>\n ) : (\n <span className=\"rte-ai-panel-v2__empty\">{labels.aiInsertHint}</span>\n )}\n </div>\n </div>\n\n {/* Quick actions */}\n <div className=\"rte-ai-panel-v2__quick-actions\">\n {QUICK_ACTIONS.map(({ key, labelKey, icon }) => (\n <button\n key={key}\n type=\"button\"\n className=\"rte-ai-panel-v2__quick-btn\"\n onClick={() => handleGenerate(key)}\n disabled={isProcessing || !editable || invalid || !selectedText.trim()}\n title={selectedText ? labels[labelKey] : labels.aiInsertHint}\n >\n <span>{icon}</span>\n <span>{labels[labelKey]}</span>\n </button>\n ))}\n </div>\n\n {/* Generate button */}\n <div className=\"rte-ai-panel-v2__actions\">\n <button\n type=\"button\"\n className=\"rte-ai-panel-v2__generate-btn\"\n onClick={() => handleGenerate()}\n disabled={isProcessing || !editable || invalid || (!prompt.trim() && !selectedText.trim())}\n >\n {isProcessing ? labels.aiProcessing : `✨ ${labels.aiGenerate}`}\n </button>\n {isProcessing && (\n <button type=\"button\" className=\"rte-ai-panel-v2__cancel-btn\" onClick={handleReject}>\n {labels.aiCancel}\n </button>\n )}\n </div>\n\n {/* Streaming result */}\n {isProcessing && !streamedText && (\n <LoadingState\n className=\"rte-ai-panel-v2__loading\"\n label={labels.aiProcessing}\n variant=\"dots\"\n layout=\"block\"\n size=\"sm\"\n />\n )}\n {streamedText && (\n <div className=\"rte-ai-panel-v2__result\">\n <div className=\"rte-ai-panel-v2__result-header\">\n <span className=\"rte-ai-panel-v2__label\">{labels.aiResult}</span>\n <span className={`rte-ai-panel-v2__stream-status ${isProcessing ? 'rte-ai-panel-v2__stream-status--live' : ''}`} role=\"status\">\n <span className=\"rte-ai-panel-v2__stream-dot\" aria-hidden=\"true\" />\n {isProcessing ? labels.aiProcessing : `${streamedText.length} chars`}\n </span>\n </div>\n <div className=\"rte-ai-panel-v2__result-text\">\n <StreamingText text={streamedText} streaming={isProcessing} />\n </div>\n <div className=\"rte-ai-panel-v2__result-actions\">\n <button type=\"button\" className=\"rte-ai-panel-v2__accept-btn\" disabled={isProcessing || !editable || invalid} onClick={handleAccept}>\n {labels.aiAccept}\n </button>\n <button type=\"button\" className=\"rte-ai-panel-v2__reject-btn\" onClick={handleReject}>\n {labels.aiReject}\n </button>\n </div>\n </div>\n )}\n\n {/* Error */}\n {error && (\n <div className=\"rte-ai-panel-v2__error\">\n <span>{error}</span>\n <button type=\"button\" onClick={() => setError(null)}>{labels.aiDismiss}</button>\n </div>\n )}\n </div>\n );\n};\n\nexport default AIRewrite;\n"],"mappings":"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"}
1
+ {"version":3,"file":"AIRewrite.js","names":[],"sources":["../../../../src/components/pro/RichTextEditor/AIRewrite.tsx"],"sourcesContent":["import React, { useState, useRef, useCallback, useEffect, useId } from 'react';\nimport type { Editor } from '@tiptap/react';\nimport type { AIConfig, AIAction, RichTextEditorLabels } from './types';\nimport { defaultLabels } from './types';\nimport StreamingText from '../../ai/StreamingText';\nimport LoadingState from '../../ai/LoadingState';\n\ninterface AIRewriteProps {\n editor: Editor;\n aiConfig: AIConfig;\n labels?: RichTextEditorLabels;\n initialSelection: string;\n selectionRange: { from: number; to: number };\n followSelection?: boolean;\n onClose: () => void;\n}\n\nconst QUICK_ACTIONS: { key: AIAction; labelKey: keyof RichTextEditorLabels; icon: string }[] = [\n { key: 'rewrite', labelKey: 'aiRewrite', icon: '✏' },\n { key: 'shorten', labelKey: 'aiShorten', icon: 'πŸ“' },\n { key: 'expand', labelKey: 'aiExpand', icon: 'πŸ“„' },\n { key: 'fix_grammar', labelKey: 'aiFixGrammar', icon: 'βœ“' },\n { key: 'translate_en', labelKey: 'aiTranslateEn', icon: 'EN' },\n { key: 'translate_zh', labelKey: 'aiTranslateZh', icon: 'δΈ­' },\n { key: 'professional', labelKey: 'aiProfessional', icon: 'πŸ’Ό' },\n { key: 'casual', labelKey: 'aiCasual', icon: 'πŸ’¬' },\n];\n\nfunction getSystemPrompt(action: AIAction | 'custom', customPrompt?: string): string {\n if (action === 'custom') return customPrompt || 'Follow the user instructions.';\n const prompts: Record<AIAction, string> = {\n rewrite: 'Rewrite the following text to improve clarity and readability. Keep the same meaning and tone. Return only the rewritten text.',\n shorten: 'Shorten the following text while preserving its key meaning. Be concise. Return only the shortened text.',\n expand: 'Expand the following text with more details and examples while keeping the same tone. Return only the expanded text.',\n fix_grammar: 'Fix any grammar, spelling, and punctuation errors in the following text. Return only the corrected text.',\n translate_en: 'Translate the following text to English. Return only the translated text.',\n translate_zh: 'Translate the following text to Chinese. Return only the translated text.',\n professional: 'Rewrite the following text in a professional, formal tone. Return only the rewritten text.',\n casual: 'Rewrite the following text in a casual, friendly tone. Return only the rewritten text.',\n };\n return customPrompt || prompts[action];\n}\n\nasync function* readSSE(response: Response): AsyncGenerator<string> {\n const reader = response.body?.getReader();\n if (!reader) throw new Error('No response body');\n const decoder = new TextDecoder();\n let buffer = '';\n let data: string[] = [];\n try {\n while (true) {\n const { done, value } = await reader.read();\n buffer += done ? decoder.decode() : decoder.decode(value, { stream: true });\n // Normalize CRLF only when the complete line is available (chunks may split it).\n let newline: number;\n while ((newline = buffer.indexOf('\\n')) !== -1) {\n const line = buffer.slice(0, newline).replace(/\\r$/, '');\n buffer = buffer.slice(newline + 1);\n if (!line) {\n if (data.length) yield data.join('\\n');\n data = [];\n } else if (line.startsWith('data:')) {\n data.push(line.slice(5).replace(/^ /, ''));\n }\n }\n if (done) {\n if (buffer.startsWith('data:')) data.push(buffer.slice(5).replace(/^ /, '').replace(/\\r$/, ''));\n if (data.length) yield data.join('\\n');\n return;\n }\n }\n } finally {\n try { await reader.cancel(); } catch { /* The request may already be aborted. */ }\n reader.releaseLock();\n }\n}\n\nasync function* streamOpenAI(\n config: AIConfig,\n text: string,\n systemPrompt: string,\n signal: AbortSignal,\n): AsyncGenerator<string> {\n if (!config.gateway || !config.model) throw new Error('OpenAI protocol requires gateway and model');\n const body = JSON.stringify({\n model: config.model,\n messages: [\n { role: 'system', content: systemPrompt },\n { role: 'user', content: text },\n ],\n stream: true,\n });\n\n const response = await fetch(config.gateway, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...(config.apiKey ? { Authorization: `Bearer ${config.apiKey}` } : {}),\n ...config.headers,\n },\n body,\n signal,\n });\n\n if (!response.ok) throw new Error(`AI API error: ${response.status} ${response.statusText}`);\n for await (const payload of readSSE(response)) {\n if (payload.trim() === '[DONE]') return;\n let data;\n try { data = JSON.parse(payload); } catch { continue; }\n if (data?.error) throw new Error(data.error.message || 'AI stream error');\n const content = data?.choices?.[0]?.delta?.content;\n if (typeof content === 'string') yield content;\n }\n}\n\nasync function* streamAnthropic(\n config: AIConfig,\n text: string,\n systemPrompt: string,\n signal: AbortSignal,\n): AsyncGenerator<string> {\n if (!config.gateway || !config.model) throw new Error('Anthropic protocol requires gateway and model');\n const body = JSON.stringify({\n model: config.model,\n max_tokens: 4096,\n system: systemPrompt,\n messages: [{ role: 'user', content: text }],\n stream: true,\n });\n\n const response = await fetch(config.gateway, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...(config.apiKey ? { 'x-api-key': config.apiKey } : {}),\n 'anthropic-version': '2023-06-01',\n ...config.headers,\n },\n body,\n signal,\n });\n\n if (!response.ok) throw new Error(`AI API error: ${response.status} ${response.statusText}`);\n for await (const payload of readSSE(response)) {\n let data;\n try { data = JSON.parse(payload); } catch { continue; }\n if (data?.type === 'error') throw new Error(data.error?.message || 'AI stream error');\n if (data?.type === 'message_stop') return;\n if (data?.type === 'content_block_delta' && typeof data.delta?.text === 'string') yield data.delta.text;\n }\n}\n\nfunction getStream(\n config: AIConfig,\n text: string,\n systemPrompt: string,\n action: AIAction | 'custom',\n signal: AbortSignal,\n request?: { prompt: string; selectedText: string; mode: 'replace' | 'insert' },\n): AsyncIterable<string> {\n if (config.protocol === 'custom') {\n if (!config.customHandler) throw new Error('Custom AI protocol requires customHandler');\n return config.customHandler({\n text,\n action: action === 'custom' ? 'rewrite' : action,\n systemPrompt,\n signal,\n prompt: request?.prompt ?? '',\n selectedText: request?.selectedText ?? text,\n mode: request?.mode ?? 'replace',\n });\n }\n if (config.protocol === 'anthropic') return streamAnthropic(config, text, systemPrompt, signal);\n return streamOpenAI(config, text, systemPrompt, signal);\n}\n\nconst AIRewrite: React.FC<AIRewriteProps> = ({ editor, aiConfig, labels: propsLabels, initialSelection, selectionRange, followSelection = false, onClose }) => {\n const labels = { ...defaultLabels, ...propsLabels };\n const promptId = useId();\n const [prompt, setPrompt] = useState('');\n const [isProcessing, setIsProcessing] = useState(false);\n const [streamedText, setStreamedText] = useState('');\n const [error, setError] = useState<string | null>(null);\n const abortRef = useRef<AbortController | null>(null);\n const promptRef = useRef<HTMLTextAreaElement>(null);\n const [editable, setEditable] = useState(() => !editor.isDestroyed && editor.isEditable);\n const [invalid, setInvalid] = useState(false);\n const sourceDoc = useRef(editor.state.doc);\n const invalidRef = useRef(false);\n const [selectionSnapshot, setSelectionSnapshot] = useState(() => ({ from: selectionRange.from, to: selectionRange.to, text: initialSelection }));\n const snapshotRef = useRef(selectionSnapshot);\n\n const cancelRequest = useCallback(() => {\n const request = abortRef.current;\n abortRef.current = null;\n request?.abort();\n }, []);\n\n useEffect(() => {\n snapshotRef.current = { from: selectionRange.from, to: selectionRange.to, text: initialSelection };\n setSelectionSnapshot(snapshotRef.current);\n }, [initialSelection, selectionRange.from, selectionRange.to]);\n\n useEffect(() => {\n sourceDoc.current = editor.state.doc;\n invalidRef.current = false;\n setInvalid(false);\n setStreamedText('');\n setIsProcessing(false);\n setError(null);\n const invalidate = () => {\n setEditable(!editor.isDestroyed && editor.isEditable);\n if (editor.isDestroyed || !editor.isEditable || editor.state.doc !== sourceDoc.current) {\n invalidRef.current = true;\n setInvalid(true);\n cancelRequest();\n setIsProcessing(false);\n setStreamedText('');\n setError('The document or editing permissions changed. Close and reopen AI Assistant.');\n }\n };\n const onSelection = () => {\n if (!followSelection || editor.isDestroyed || !editor.isEditable || editor.state.doc !== sourceDoc.current || invalidRef.current) return;\n const { from, to } = editor.state.selection;\n const previous = snapshotRef.current;\n // Ignore collapsed selections caused by focusing the AI prompt or toolbar.\n if (from === to || (from === previous.from && to === previous.to)) return;\n const next = { from, to, text: editor.state.doc.textBetween(from, to, ' ') };\n snapshotRef.current = next;\n setSelectionSnapshot(next);\n cancelRequest();\n setIsProcessing(false);\n setStreamedText('');\n setError(null);\n };\n invalidate();\n editor.on('update', invalidate);\n editor.on('transaction', invalidate);\n editor.on('selectionUpdate', onSelection);\n editor.on('destroy', invalidate);\n return () => {\n cancelRequest();\n editor.off('update', invalidate);\n editor.off('transaction', invalidate);\n editor.off('selectionUpdate', onSelection);\n editor.off('destroy', invalidate);\n };\n }, [editor, cancelRequest, followSelection]);\n\n const selectedText = selectionSnapshot.text;\n\n useEffect(() => {\n if (promptRef.current) promptRef.current.focus();\n }, []);\n\n const handleGenerate = useCallback(async (action: AIAction | 'custom' = 'custom', customPromptOverride?: string) => {\n if (editor.isDestroyed || !editor.isEditable || invalidRef.current || abortRef.current) return;\n const mode = selectedText ? 'replace' : 'insert';\n if (mode === 'insert' && !prompt.trim()) return;\n\n const actionPrompt = action === 'custom'\n ? (mode === 'replace'\n ? 'Edit the selected text according to the user instruction. Return only the replacement text.'\n : 'Generate text according to the user instruction. Return only the text to insert.')\n : getSystemPrompt(action, customPromptOverride);\n const systemPrompt = [aiConfig.systemPrompt, actionPrompt].filter(Boolean).join('\\n\\n');\n const userText = mode === 'replace'\n ? (prompt.trim()\n ? `Instruction:\\n${prompt.trim()}\\n\\nSelected text:\\n${selectedText}`\n : selectedText)\n : prompt.trim();\n\n setIsProcessing(true);\n setStreamedText('');\n setError(null);\n\n const abort = new AbortController();\n abortRef.current = abort;\n\n try {\n let result = '';\n for await (const chunk of getStream(aiConfig, userText, systemPrompt, action, abort.signal, {\n prompt: prompt.trim(),\n selectedText,\n mode,\n })) {\n if (abort.signal.aborted || abortRef.current !== abort) break;\n result += chunk;\n setStreamedText(result);\n }\n } catch (err: unknown) {\n if (abort.signal.aborted || abortRef.current !== abort) return;\n setStreamedText('');\n setError(String(err instanceof Error ? err.message : err));\n } finally {\n if (abortRef.current === abort) {\n abortRef.current = null;\n setIsProcessing(false);\n }\n }\n }, [editor, aiConfig, selectedText, prompt]);\n\n const handleAccept = useCallback(() => {\n if (!streamedText || abortRef.current || editor.isDestroyed || !editor.isEditable || invalidRef.current) return;\n const { from, to } = snapshotRef.current;\n if (editor.state.doc !== sourceDoc.current || !Number.isInteger(from) || !Number.isInteger(to) || from < 0 || to < from || to > editor.state.doc.content.size) {\n setError('The original selection is no longer valid. Close and reopen AI Assistant.');\n return;\n }\n // AI output is plain text, never HTML. Use the captured range even if the cursor moved.\n editor.view.dispatch(editor.state.tr.insertText(streamedText, from, to));\n setStreamedText('');\n setPrompt('');\n onClose();\n }, [editor, streamedText, onClose]);\n\n const handleReject = useCallback(() => {\n cancelRequest();\n setStreamedText('');\n setIsProcessing(false);\n setError(null);\n }, [cancelRequest]);\n\n const handleKeyDown = useCallback((e: React.KeyboardEvent) => {\n if (e.key === 'Enter' && (e.metaKey || e.ctrlKey)) {\n e.preventDefault();\n handleGenerate();\n }\n }, [handleGenerate]);\n\n return (\n <div className=\"rte-ai-panel-v2\">\n <div className=\"rte-ai-panel-v2__header\">\n <span className=\"rte-ai-panel-v2__title\">✨ {labels.ai || 'AI Assistant'}</span>\n <button type=\"button\" className=\"rte-ai-panel-v2__close-btn\" onClick={() => { cancelRequest(); onClose(); }} title={labels.aiClose} aria-label={labels.aiClose}>\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\" /><line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\" />\n </svg>\n </button>\n </div>\n {/* Prompt input */}\n <div className=\"rte-ai-panel-v2__prompt-section\">\n <label className=\"rte-ai-panel-v2__label\" htmlFor={promptId}>{labels.aiPromptLabel}</label>\n <textarea\n id={promptId}\n ref={promptRef}\n className=\"rte-ai-panel-v2__prompt-input\"\n value={prompt}\n onChange={(e) => setPrompt(e.target.value)}\n onKeyDown={handleKeyDown}\n placeholder={labels.aiPromptPlaceholder}\n rows={3}\n disabled={isProcessing || !editable || invalid}\n />\n </div>\n\n {/* Selected text display */}\n <div className=\"rte-ai-panel-v2__selection-section\">\n <span className=\"rte-ai-panel-v2__label\">{labels.aiSelectedText}</span>\n <div className=\"rte-ai-panel-v2__selection-box\">\n {selectedText ? (\n <span>{selectedText.length > 300 ? selectedText.slice(0, 300) + '...' : selectedText}</span>\n ) : (\n <span className=\"rte-ai-panel-v2__empty\">{labels.aiInsertHint}</span>\n )}\n </div>\n </div>\n\n {/* Quick actions */}\n <div className=\"rte-ai-panel-v2__quick-actions\">\n {QUICK_ACTIONS.map(({ key, labelKey, icon }) => (\n <button\n key={key}\n type=\"button\"\n className=\"rte-ai-panel-v2__quick-btn\"\n onClick={() => handleGenerate(key)}\n disabled={isProcessing || !editable || invalid || !selectedText.trim()}\n title={selectedText ? labels[labelKey] : labels.aiInsertHint}\n >\n <span>{icon}</span>\n <span>{labels[labelKey]}</span>\n </button>\n ))}\n </div>\n\n {/* Generate button */}\n <div className=\"rte-ai-panel-v2__actions\">\n <button\n type=\"button\"\n className=\"rte-ai-panel-v2__generate-btn\"\n onClick={() => handleGenerate()}\n disabled={isProcessing || !editable || invalid || (!prompt.trim() && !selectedText.trim())}\n >\n {isProcessing ? labels.aiProcessing : `✨ ${labels.aiGenerate}`}\n </button>\n {isProcessing && (\n <button type=\"button\" className=\"rte-ai-panel-v2__cancel-btn\" onClick={handleReject}>\n {labels.aiCancel}\n </button>\n )}\n </div>\n\n {/* Streaming result */}\n {isProcessing && !streamedText && (\n <LoadingState\n className=\"rte-ai-panel-v2__loading\"\n label={labels.aiProcessing}\n variant=\"dots\"\n layout=\"block\"\n size=\"sm\"\n />\n )}\n {streamedText && (\n <div className=\"rte-ai-panel-v2__result\">\n <div className=\"rte-ai-panel-v2__result-header\">\n <span className=\"rte-ai-panel-v2__label\">{labels.aiResult}</span>\n <span className={`rte-ai-panel-v2__stream-status ${isProcessing ? 'rte-ai-panel-v2__stream-status--live' : ''}`} role=\"status\">\n <span className=\"rte-ai-panel-v2__stream-dot\" aria-hidden=\"true\" />\n {isProcessing ? labels.aiProcessing : `${streamedText.length} chars`}\n </span>\n </div>\n <div className=\"rte-ai-panel-v2__result-text\">\n <StreamingText text={streamedText} streaming={isProcessing} />\n </div>\n <div className=\"rte-ai-panel-v2__result-actions\">\n <button type=\"button\" className=\"rte-ai-panel-v2__accept-btn\" disabled={isProcessing || !editable || invalid} onClick={handleAccept}>\n {labels.aiAccept}\n </button>\n <button type=\"button\" className=\"rte-ai-panel-v2__reject-btn\" onClick={handleReject}>\n {labels.aiReject}\n </button>\n </div>\n </div>\n )}\n\n {/* Error */}\n {error && (\n <div className=\"rte-ai-panel-v2__error\">\n <span>{error}</span>\n <button type=\"button\" onClick={() => setError(null)}>{labels.aiDismiss}</button>\n </div>\n )}\n </div>\n );\n};\n\nexport default AIRewrite;\n"],"mappings":"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"}
@@ -1,68 +1,65 @@
1
1
  import { ColorPicker as e } from "./Toolbar/ColorPicker.js";
2
- import { FONT_FAMILIES as t, FONT_SIZES as n } from "./Toolbar/SelectDropdown.js";
3
- import { useEffect as r, useLayoutEffect as i, useRef as a, useState as o } from "react";
4
- import { Fragment as s, jsx as c, jsxs as l } from "react/jsx-runtime";
5
- import { useEditorState as u } from "@tiptap/react";
6
- import { TextSelection as d } from "@tiptap/pm/state";
2
+ import { FONT_FAMILIES as t, FONT_SIZES as n, SelectDropdown as r } from "./Toolbar/SelectDropdown.js";
3
+ import { useCallback as i, useEffect as a, useId as o, useLayoutEffect as s, useRef as c, useState as l } from "react";
4
+ import { Fragment as u, jsx as d, jsxs as f } from "react/jsx-runtime";
5
+ import { useEditorState as p } from "@tiptap/react";
6
+ import { TextSelection as m } from "@tiptap/pm/state";
7
7
  //#region src/components/pro/RichTextEditor/BubbleToolbar.tsx
8
- var f = ({ editor: f, onAddComment: p }) => {
9
- u({
10
- editor: f,
8
+ var h = ({ editor: h, onAddComment: g }) => {
9
+ p({
10
+ editor: h,
11
11
  selector: ({ transactionNumber: e }) => e
12
12
  });
13
- let [m, h] = o(!1), [g, _] = o({
13
+ let [_, v] = l(!1), [y, b] = l({
14
14
  x: 0,
15
15
  y: 0
16
- }), v = a(null);
17
- if (r(() => {
18
- if (f.isDestroyed) return;
19
- let e = f.view.dom.ownerDocument, t = e.defaultView;
16
+ }), x = c(null), S = o(), C = i((e) => e instanceof Element && e.closest(`[data-rte-palette-owner="${S}"]`) !== null, [S]);
17
+ if (a(() => {
18
+ if (h.isDestroyed) return;
19
+ let e = h.view.dom.ownerDocument, t = e.defaultView;
20
20
  if (!t) return;
21
21
  let n = null, r = () => {
22
- n !== null && t.cancelAnimationFrame(n), n = null, h(!1);
22
+ n !== null && t.cancelAnimationFrame(n), n = null, v(!1);
23
23
  }, i = () => {
24
- if (n = null, f.isDestroyed) {
24
+ if (n = null, h.isDestroyed) {
25
25
  r();
26
26
  return;
27
27
  }
28
- let { from: t, to: i } = f.state.selection;
29
- if (!f.isEditable || !(f.state.selection instanceof d) || t === i || !f.isFocused && !v.current?.contains(e.activeElement)) {
30
- h(!1);
28
+ let { from: t, to: i } = h.state.selection;
29
+ if (!h.isEditable || !(h.state.selection instanceof m) || t === i || !h.isFocused && !x.current?.contains(e.activeElement) && !C(e.activeElement)) {
30
+ v(!1);
31
31
  return;
32
32
  }
33
- let a = f.view.coordsAtPos(t), o = f.view.coordsAtPos(i), s = f.view.dom.closest(".rte-editor-wrapper");
33
+ let a = h.view.coordsAtPos(t), o = h.view.coordsAtPos(i), s = h.view.dom.closest(".rte-editor-wrapper");
34
34
  if (!s) {
35
35
  r();
36
36
  return;
37
37
  }
38
38
  let c = s.getBoundingClientRect(), l = (a.left + o.left) / 2 - c.left - s.clientLeft + s.scrollLeft, u = Math.min(a.top, o.top) - c.top - s.clientTop + s.scrollTop - 44;
39
- _({
39
+ b({
40
40
  x: l,
41
41
  y: u
42
- }), h(!0);
42
+ }), v(!0);
43
43
  }, a = () => {
44
44
  n !== null && t.cancelAnimationFrame(n), n = t.requestAnimationFrame(i);
45
45
  }, o = ({ event: e }) => {
46
- e.relatedTarget instanceof Node && v.current?.contains(e.relatedTarget) || r();
46
+ e.relatedTarget instanceof Node && (x.current?.contains(e.relatedTarget) || C(e.relatedTarget)) || r();
47
47
  };
48
- return f.on("transaction", a), f.on("selectionUpdate", a), f.on("focus", a), f.on("blur", o), f.on("destroy", r), e.addEventListener("scroll", a, !0), t.addEventListener("resize", a), i(), () => {
49
- n !== null && t.cancelAnimationFrame(n), f.off("transaction", a), f.off("selectionUpdate", a), f.off("focus", a), f.off("blur", o), f.off("destroy", r), e.removeEventListener("scroll", a, !0), t.removeEventListener("resize", a);
48
+ return h.on("transaction", a), h.on("selectionUpdate", a), h.on("focus", a), h.on("blur", o), h.on("destroy", r), e.addEventListener("scroll", a, !0), t.addEventListener("resize", a), i(), () => {
49
+ n !== null && t.cancelAnimationFrame(n), h.off("transaction", a), h.off("selectionUpdate", a), h.off("focus", a), h.off("blur", o), h.off("destroy", r), e.removeEventListener("scroll", a, !0), t.removeEventListener("resize", a);
50
50
  };
51
- }, [f]), i(() => {
52
- if (!m || !v.current) return;
53
- let e = v.current, t = e.parentElement;
51
+ }, [h, C]), s(() => {
52
+ if (!_ || !x.current) return;
53
+ let e = x.current, t = e.parentElement;
54
54
  if (!t) return;
55
- let n = t.clientWidth, r = e.offsetWidth / 2, i = Math.min(r + 4, n / 2), a = Math.max(n - r - 4, n / 2), o = Math.min(Math.max(g.x, i), a);
56
- o !== g.x && _((e) => ({
57
- ...e,
58
- x: o
59
- }));
60
- }, [m, g.x]), !m || f.isDestroyed || !f.isEditable) return null;
61
- let y = (e, t, n, r) => /* @__PURE__ */ c("button", {
55
+ let n = e.offsetWidth;
56
+ e.style.left = `${Math.max(0, Math.min(y.x - n / 2, t.clientWidth - n))}px`;
57
+ }, [_, y.x]), !_ || h.isDestroyed || !h.isEditable) return null;
58
+ let w = (e, t, n, r) => /* @__PURE__ */ d("button", {
62
59
  type: "button",
63
60
  className: `rte-bubble__btn ${n ? "rte-bubble__btn--active" : ""}`,
64
61
  onClick: () => {
65
- !f.isDestroyed && f.isEditable && e();
62
+ !h.isDestroyed && h.isEditable && e();
66
63
  },
67
64
  title: t,
68
65
  "aria-label": t,
@@ -70,124 +67,117 @@ var f = ({ editor: f, onAddComment: p }) => {
70
67
  onMouseDown: (e) => e.preventDefault(),
71
68
  children: r
72
69
  });
73
- return /* @__PURE__ */ l("div", {
74
- ref: v,
70
+ return /* @__PURE__ */ f("div", {
71
+ ref: x,
75
72
  className: "rte-bubble",
76
73
  role: "group",
77
74
  "aria-label": "Selection formatting",
78
75
  onBlur: (e) => {
79
- !e.currentTarget.contains(e.relatedTarget) && !f.isFocused && h(!1);
76
+ !e.currentTarget.contains(e.relatedTarget) && !C(e.relatedTarget) && !h.isFocused && v(!1);
80
77
  },
81
78
  onKeyDown: (e) => {
82
- e.key === "Escape" && (e.preventDefault(), f.commands.focus(), h(!1));
83
- },
84
- style: {
85
- left: g.x,
86
- top: g.y
79
+ e.key === "Escape" && (e.preventDefault(), h.commands.focus(), v(!1));
87
80
  },
81
+ style: { top: y.y },
88
82
  children: [
89
- y(() => f.chain().focus().toggleBold().run(), "Bold", f.isActive("bold"), /* @__PURE__ */ c("strong", {
83
+ w(() => h.chain().focus().toggleBold().run(), "Bold", h.isActive("bold"), /* @__PURE__ */ d("strong", {
90
84
  style: { fontSize: 13 },
91
85
  children: "B"
92
86
  })),
93
- y(() => f.chain().focus().toggleItalic().run(), "Italic", f.isActive("italic"), /* @__PURE__ */ c("em", {
87
+ w(() => h.chain().focus().toggleItalic().run(), "Italic", h.isActive("italic"), /* @__PURE__ */ d("em", {
94
88
  style: { fontSize: 13 },
95
89
  children: "I"
96
90
  })),
97
- y(() => f.chain().focus().toggleUnderline().run(), "Underline", f.isActive("underline"), /* @__PURE__ */ c("span", {
91
+ w(() => h.chain().focus().toggleUnderline().run(), "Underline", h.isActive("underline"), /* @__PURE__ */ d("span", {
98
92
  style: {
99
93
  fontSize: 13,
100
94
  textDecoration: "underline"
101
95
  },
102
96
  children: "U"
103
97
  })),
104
- y(() => f.chain().focus().toggleStrike().run(), "Strike", f.isActive("strike"), /* @__PURE__ */ c("span", {
98
+ w(() => h.chain().focus().toggleStrike().run(), "Strike", h.isActive("strike"), /* @__PURE__ */ d("span", {
105
99
  style: {
106
100
  fontSize: 13,
107
101
  textDecoration: "line-through"
108
102
  },
109
103
  children: "S"
110
104
  })),
111
- y(() => f.chain().focus().toggleCode().run(), "Code", f.isActive("code"), /* @__PURE__ */ c("span", {
105
+ w(() => h.chain().focus().toggleCode().run(), "Code", h.isActive("code"), /* @__PURE__ */ d("span", {
112
106
  style: {
113
107
  fontSize: 11,
114
108
  fontFamily: "monospace"
115
109
  },
116
110
  children: "</>"
117
111
  })),
118
- y(() => {
119
- let e = window.prompt("URL:", f.getAttributes("link").href || "");
120
- if (e === null || f.isDestroyed || !f.isEditable) return;
121
- let t = f.chain().focus().extendMarkRange("link");
112
+ w(() => {
113
+ let e = window.prompt("URL:", h.getAttributes("link").href || "");
114
+ if (e === null || h.isDestroyed || !h.isEditable) return;
115
+ let t = h.chain().focus().extendMarkRange("link");
122
116
  e.trim() === "" ? t.unsetLink().run() : t.setLink({ href: e.trim() }).run();
123
- }, "Link", f.isActive("link"), /* @__PURE__ */ l("svg", {
117
+ }, "Link", h.isActive("link"), /* @__PURE__ */ f("svg", {
124
118
  width: "14",
125
119
  height: "14",
126
120
  viewBox: "0 0 24 24",
127
121
  fill: "none",
128
122
  stroke: "currentColor",
129
123
  strokeWidth: "2",
130
- children: [/* @__PURE__ */ c("path", { d: "M10 13a5 5 0 007.54.54l3-3a5 5 0 00-7.07-7.07l-1.72 1.71" }), /* @__PURE__ */ c("path", { d: "M14 11a5 5 0 00-7.54-.54l-3 3a5 5 0 007.07 7.07l1.71-1.71" })]
124
+ children: [/* @__PURE__ */ d("path", { d: "M10 13a5 5 0 007.54.54l3-3a5 5 0 00-7.07-7.07l-1.72 1.71" }), /* @__PURE__ */ d("path", { d: "M14 11a5 5 0 00-7.54-.54l-3 3a5 5 0 007.07 7.07l1.71-1.71" })]
131
125
  })),
132
- /* @__PURE__ */ c("div", { className: "rte-bubble__divider" }),
133
- /* @__PURE__ */ c(e, {
134
- value: f.getAttributes("textStyle").color || "",
126
+ /* @__PURE__ */ d("div", { className: "rte-bubble__divider" }),
127
+ /* @__PURE__ */ d(e, {
128
+ value: h.getAttributes("textStyle").color || "",
135
129
  onChange: (e) => {
136
- !f.isDestroyed && f.isEditable && (e ? f.chain().focus().setColor(e).run() : f.chain().focus().unsetColor().run());
130
+ !h.isDestroyed && h.isEditable && (e ? h.chain().focus().setColor(e).run() : h.chain().focus().unsetColor().run());
137
131
  },
138
132
  title: "Text color",
139
133
  buttonClassName: "rte-bubble__btn",
134
+ portalOwner: S,
140
135
  kind: "text"
141
136
  }),
142
- /* @__PURE__ */ c(e, {
143
- value: f.getAttributes("highlight").color || "",
137
+ /* @__PURE__ */ d(e, {
138
+ value: h.getAttributes("highlight").color || "",
144
139
  onChange: (e) => {
145
- !f.isDestroyed && f.isEditable && (e ? f.chain().focus().toggleHighlight({ color: e }).run() : f.chain().focus().unsetHighlight().run());
140
+ !h.isDestroyed && h.isEditable && (e ? h.chain().focus().toggleHighlight({ color: e }).run() : h.chain().focus().unsetHighlight().run());
146
141
  },
147
142
  title: "Highlight color",
148
143
  buttonClassName: "rte-bubble__btn",
144
+ portalOwner: S,
149
145
  kind: "highlight"
150
146
  }),
151
- /* @__PURE__ */ c("div", { className: "rte-bubble__divider" }),
152
- /* @__PURE__ */ c("select", {
147
+ /* @__PURE__ */ d("div", { className: "rte-bubble__divider" }),
148
+ /* @__PURE__ */ d(r, {
153
149
  className: "rte-bubble__select",
154
- value: f.getAttributes("textStyle").fontFamily || "",
150
+ width: 96,
151
+ value: h.getAttributes("textStyle").fontFamily || "",
152
+ options: t,
155
153
  onChange: (e) => {
156
- !f.isDestroyed && f.isEditable && (e.target.value ? f.chain().focus().setFontFamily(e.target.value).run() : f.chain().focus().unsetFontFamily().run());
154
+ !h.isDestroyed && h.isEditable && (e ? h.chain().focus().setFontFamily(e).run() : h.chain().focus().unsetFontFamily().run());
157
155
  },
158
- title: "Font family",
159
- "aria-label": "Font family",
160
- children: t.map((e) => /* @__PURE__ */ c("option", {
161
- value: e.value,
162
- children: e.label
163
- }, e.value))
156
+ title: "Font family"
164
157
  }),
165
- /* @__PURE__ */ c("select", {
158
+ /* @__PURE__ */ d(r, {
166
159
  className: "rte-bubble__select rte-bubble__select--size",
167
- value: f.getAttributes("textStyle").fontSize || "",
160
+ width: 72,
161
+ value: h.getAttributes("textStyle").fontSize || "",
162
+ options: n,
168
163
  onChange: (e) => {
169
- !f.isDestroyed && f.isEditable && (e.target.value ? f.chain().focus().setFontSize(e.target.value).run() : f.chain().focus().unsetFontSize().run());
164
+ !h.isDestroyed && h.isEditable && (e ? h.chain().focus().setFontSize(e).run() : h.chain().focus().unsetFontSize().run());
170
165
  },
171
- title: "Font size",
172
- "aria-label": "Font size",
173
- children: n.map((e) => /* @__PURE__ */ c("option", {
174
- value: e.value,
175
- children: e.label
176
- }, e.value))
166
+ title: "Font size"
177
167
  }),
178
- p && /* @__PURE__ */ l(s, { children: [/* @__PURE__ */ c("div", { className: "rte-bubble__divider" }), y(p, "Comment", void 0, /* @__PURE__ */ c("svg", {
168
+ g && /* @__PURE__ */ f(u, { children: [/* @__PURE__ */ d("div", { className: "rte-bubble__divider" }), w(g, "Comment", void 0, /* @__PURE__ */ d("svg", {
179
169
  width: "14",
180
170
  height: "14",
181
171
  viewBox: "0 0 24 24",
182
172
  fill: "none",
183
173
  stroke: "currentColor",
184
174
  strokeWidth: "2",
185
- children: /* @__PURE__ */ c("path", { d: "M21 15a2 2 0 01-2 2H7l-4 4V5a2 2 0 012-2h14a2 2 0 012 2z" })
175
+ children: /* @__PURE__ */ d("path", { d: "M21 15a2 2 0 01-2 2H7l-4 4V5a2 2 0 012-2h14a2 2 0 012 2z" })
186
176
  }))] })
187
177
  ]
188
178
  });
189
179
  };
190
180
  //#endregion
191
- export { f as default };
181
+ export { h as default };
192
182
 
193
183
  //# sourceMappingURL=BubbleToolbar.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"BubbleToolbar.js","names":[],"sources":["../../../../src/components/pro/RichTextEditor/BubbleToolbar.tsx"],"sourcesContent":["import React, { useState, useEffect, useLayoutEffect, useRef } from 'react';\nimport { useEditorState, type Editor } from '@tiptap/react';\nimport { TextSelection } from '@tiptap/pm/state';\nimport { ColorPicker } from './Toolbar/ColorPicker';\nimport { FONT_FAMILIES, FONT_SIZES } from './Toolbar/SelectDropdown';\n\ninterface BubbleToolbarProps {\n editor: Editor;\n onAddComment?: () => void;\n}\n\nconst BubbleToolbar: React.FC<BubbleToolbarProps> = ({ editor, onAddComment }) => {\n useEditorState({ editor, selector: ({ transactionNumber }) => transactionNumber });\n const [visible, setVisible] = useState(false);\n const [pos, setPos] = useState({ x: 0, y: 0 });\n const ref = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n if (editor.isDestroyed) return;\n const doc = editor.view.dom.ownerDocument;\n const win = doc.defaultView;\n if (!win) return;\n let frame: number | null = null;\n const hide = () => {\n if (frame !== null) win.cancelAnimationFrame(frame);\n frame = null;\n setVisible(false);\n };\n const update = () => {\n frame = null;\n if (editor.isDestroyed) { hide(); return; }\n const { from, to } = editor.state.selection;\n if (!editor.isEditable || !(editor.state.selection instanceof TextSelection) || from === to || (!editor.isFocused && !ref.current?.contains(doc.activeElement))) {\n setVisible(false);\n return;\n }\n const coords = editor.view.coordsAtPos(from);\n const endCoords = editor.view.coordsAtPos(to);\n const wrapperEl = editor.view.dom.closest<HTMLElement>('.rte-editor-wrapper');\n if (!wrapperEl) { hide(); return; }\n const rect = wrapperEl.getBoundingClientRect();\n\n const centerX = (coords.left + endCoords.left) / 2 - rect.left - wrapperEl.clientLeft + wrapperEl.scrollLeft;\n const topY = Math.min(coords.top, endCoords.top) - rect.top - wrapperEl.clientTop + wrapperEl.scrollTop - 44;\n\n setPos({ x: centerX, y: topY });\n setVisible(true);\n };\n\n const scheduleUpdate = () => {\n if (frame !== null) win.cancelAnimationFrame(frame);\n frame = win.requestAnimationFrame(update);\n };\n const onBlur = ({ event }: { event: FocusEvent }) => {\n if (event.relatedTarget instanceof Node && ref.current?.contains(event.relatedTarget)) return;\n hide();\n };\n editor.on('transaction', scheduleUpdate);\n editor.on('selectionUpdate', scheduleUpdate);\n editor.on('focus', scheduleUpdate);\n editor.on('blur', onBlur);\n editor.on('destroy', hide);\n doc.addEventListener('scroll', scheduleUpdate, true);\n win.addEventListener('resize', scheduleUpdate);\n update();\n return () => {\n if (frame !== null) win.cancelAnimationFrame(frame);\n editor.off('transaction', scheduleUpdate);\n editor.off('selectionUpdate', scheduleUpdate);\n editor.off('focus', scheduleUpdate);\n editor.off('blur', onBlur);\n editor.off('destroy', hide);\n doc.removeEventListener('scroll', scheduleUpdate, true);\n win.removeEventListener('resize', scheduleUpdate);\n };\n }, [editor]);\n\n // Keep the bubble inside the wrapper horizontally: with the extra color /\n // font controls it can be wider than a phone-size editor, and transform:\n // translateX(-50%) would otherwise clip it at either edge.\n useLayoutEffect(() => {\n if (!visible || !ref.current) return;\n const el = ref.current;\n const wrapper = el.parentElement;\n if (!wrapper) return;\n const w = wrapper.clientWidth;\n const half = el.offsetWidth / 2;\n const min = Math.min(half + 4, w / 2);\n const max = Math.max(w - half - 4, w / 2);\n const clamped = Math.min(Math.max(pos.x, min), max);\n if (clamped !== pos.x) setPos((p) => ({ ...p, x: clamped }));\n }, [visible, pos.x]);\n\n if (!visible || editor.isDestroyed || !editor.isEditable) return null;\n\n const btn = (onClick: () => void, title: string, isActive: boolean | undefined, children: React.ReactNode) => (\n <button\n type=\"button\"\n className={`rte-bubble__btn ${isActive ? 'rte-bubble__btn--active' : ''}`}\n onClick={() => { if (!editor.isDestroyed && editor.isEditable) onClick(); }}\n title={title}\n aria-label={title}\n aria-pressed={isActive}\n onMouseDown={(e) => e.preventDefault()}\n >\n {children}\n </button>\n );\n\n return (\n <div ref={ref} className=\"rte-bubble\" role=\"group\" aria-label=\"Selection formatting\"\n onBlur={(event) => {\n if (!event.currentTarget.contains(event.relatedTarget) && !editor.isFocused) setVisible(false);\n }}\n onKeyDown={(event) => {\n if (event.key === 'Escape') {\n event.preventDefault();\n editor.commands.focus();\n setVisible(false);\n }\n }}\n style={{ left: pos.x, top: pos.y }}>\n {btn(() => editor.chain().focus().toggleBold().run(), 'Bold', editor.isActive('bold'),\n <strong style={{ fontSize: 13 }}>B</strong>)}\n {btn(() => editor.chain().focus().toggleItalic().run(), 'Italic', editor.isActive('italic'),\n <em style={{ fontSize: 13 }}>I</em>)}\n {btn(() => editor.chain().focus().toggleUnderline().run(), 'Underline', editor.isActive('underline'),\n <span style={{ fontSize: 13, textDecoration: 'underline' }}>U</span>)}\n {btn(() => editor.chain().focus().toggleStrike().run(), 'Strike', editor.isActive('strike'),\n <span style={{ fontSize: 13, textDecoration: 'line-through' }}>S</span>)}\n {btn(() => editor.chain().focus().toggleCode().run(), 'Code', editor.isActive('code'),\n <span style={{ fontSize: 11, fontFamily: 'monospace' }}>&lt;/&gt;</span>)}\n {btn(() => {\n const url = window.prompt('URL:', editor.getAttributes('link').href || '');\n if (url === null || editor.isDestroyed || !editor.isEditable) return;\n const chain = editor.chain().focus().extendMarkRange('link');\n if (url.trim() === '') chain.unsetLink().run();\n else chain.setLink({ href: url.trim() }).run();\n }, 'Link', editor.isActive('link'),\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\"><path d=\"M10 13a5 5 0 007.54.54l3-3a5 5 0 00-7.07-7.07l-1.72 1.71\" /><path d=\"M14 11a5 5 0 00-7.54-.54l-3 3a5 5 0 007.07 7.07l1.71-1.71\" /></svg>)}\n <div className=\"rte-bubble__divider\" />\n <ColorPicker\n value={editor.getAttributes('textStyle').color || ''}\n onChange={(color) => {\n if (editor.isDestroyed || !editor.isEditable) return;\n if (color) editor.chain().focus().setColor(color).run();\n else editor.chain().focus().unsetColor().run();\n }}\n title=\"Text color\"\n buttonClassName=\"rte-bubble__btn\"\n kind=\"text\"\n />\n <ColorPicker\n value={editor.getAttributes('highlight').color || ''}\n onChange={(color) => {\n if (editor.isDestroyed || !editor.isEditable) return;\n if (color) editor.chain().focus().toggleHighlight({ color }).run();\n else editor.chain().focus().unsetHighlight().run();\n }}\n title=\"Highlight color\"\n buttonClassName=\"rte-bubble__btn\"\n kind=\"highlight\"\n />\n <div className=\"rte-bubble__divider\" />\n <select\n className=\"rte-bubble__select\"\n value={editor.getAttributes('textStyle').fontFamily || ''}\n onChange={(e) => {\n if (editor.isDestroyed || !editor.isEditable) return;\n if (e.target.value) editor.chain().focus().setFontFamily(e.target.value).run();\n else editor.chain().focus().unsetFontFamily().run();\n }}\n title=\"Font family\"\n aria-label=\"Font family\"\n >\n {FONT_FAMILIES.map((opt) => (\n <option key={opt.value} value={opt.value}>{opt.label}</option>\n ))}\n </select>\n <select\n className=\"rte-bubble__select rte-bubble__select--size\"\n value={editor.getAttributes('textStyle').fontSize || ''}\n onChange={(e) => {\n if (editor.isDestroyed || !editor.isEditable) return;\n if (e.target.value) (editor.chain().focus() as any).setFontSize(e.target.value).run();\n else (editor.chain().focus() as any).unsetFontSize().run();\n }}\n title=\"Font size\"\n aria-label=\"Font size\"\n >\n {FONT_SIZES.map((opt) => (\n <option key={opt.value} value={opt.value}>{opt.label}</option>\n ))}\n </select>\n {onAddComment && (\n <>\n <div className=\"rte-bubble__divider\" />\n {btn(onAddComment, 'Comment', undefined,\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\"><path d=\"M21 15a2 2 0 01-2 2H7l-4 4V5a2 2 0 012-2h14a2 2 0 012 2z\" /></svg>)}\n </>\n )}\n </div>\n );\n};\n\nexport default BubbleToolbar;\n"],"mappings":"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"}
1
+ {"version":3,"file":"BubbleToolbar.js","names":[],"sources":["../../../../src/components/pro/RichTextEditor/BubbleToolbar.tsx"],"sourcesContent":["import React, { useState, useEffect, useLayoutEffect, useRef, useId, useCallback } from 'react';\nimport { useEditorState, type Editor } from '@tiptap/react';\nimport { TextSelection } from '@tiptap/pm/state';\nimport { ColorPicker } from './Toolbar/ColorPicker';\nimport { SelectDropdown, FONT_FAMILIES, FONT_SIZES } from './Toolbar/SelectDropdown';\n\ninterface BubbleToolbarProps {\n editor: Editor;\n onAddComment?: () => void;\n}\n\nconst BubbleToolbar: React.FC<BubbleToolbarProps> = ({ editor, onAddComment }) => {\n useEditorState({ editor, selector: ({ transactionNumber }) => transactionNumber });\n const [visible, setVisible] = useState(false);\n const [pos, setPos] = useState({ x: 0, y: 0 });\n const ref = useRef<HTMLDivElement>(null);\n const bubbleId = useId();\n const ownsPalette = useCallback((target: EventTarget | null) =>\n target instanceof Element && target.closest(`[data-rte-palette-owner=\"${bubbleId}\"]`) !== null, [bubbleId]);\n\n useEffect(() => {\n if (editor.isDestroyed) return;\n const doc = editor.view.dom.ownerDocument;\n const win = doc.defaultView;\n if (!win) return;\n let frame: number | null = null;\n const hide = () => {\n if (frame !== null) win.cancelAnimationFrame(frame);\n frame = null;\n setVisible(false);\n };\n const update = () => {\n frame = null;\n if (editor.isDestroyed) { hide(); return; }\n const { from, to } = editor.state.selection;\n if (!editor.isEditable || !(editor.state.selection instanceof TextSelection) || from === to || (!editor.isFocused && !ref.current?.contains(doc.activeElement) && !ownsPalette(doc.activeElement))) {\n setVisible(false);\n return;\n }\n const coords = editor.view.coordsAtPos(from);\n const endCoords = editor.view.coordsAtPos(to);\n const wrapperEl = editor.view.dom.closest<HTMLElement>('.rte-editor-wrapper');\n if (!wrapperEl) { hide(); return; }\n const rect = wrapperEl.getBoundingClientRect();\n\n const centerX = (coords.left + endCoords.left) / 2 - rect.left - wrapperEl.clientLeft + wrapperEl.scrollLeft;\n const topY = Math.min(coords.top, endCoords.top) - rect.top - wrapperEl.clientTop + wrapperEl.scrollTop - 44;\n\n setPos({ x: centerX, y: topY });\n setVisible(true);\n };\n\n const scheduleUpdate = () => {\n if (frame !== null) win.cancelAnimationFrame(frame);\n frame = win.requestAnimationFrame(update);\n };\n const onBlur = ({ event }: { event: FocusEvent }) => {\n if (event.relatedTarget instanceof Node && (ref.current?.contains(event.relatedTarget) || ownsPalette(event.relatedTarget))) return;\n hide();\n };\n editor.on('transaction', scheduleUpdate);\n editor.on('selectionUpdate', scheduleUpdate);\n editor.on('focus', scheduleUpdate);\n editor.on('blur', onBlur);\n editor.on('destroy', hide);\n doc.addEventListener('scroll', scheduleUpdate, true);\n win.addEventListener('resize', scheduleUpdate);\n update();\n return () => {\n if (frame !== null) win.cancelAnimationFrame(frame);\n editor.off('transaction', scheduleUpdate);\n editor.off('selectionUpdate', scheduleUpdate);\n editor.off('focus', scheduleUpdate);\n editor.off('blur', onBlur);\n editor.off('destroy', hide);\n doc.removeEventListener('scroll', scheduleUpdate, true);\n win.removeEventListener('resize', scheduleUpdate);\n };\n }, [editor, ownsPalette]);\n\n // A transform here turns fixed-position color palettes into descendants of\n // the scrolling editor, where overflow clips them. Position the bubble's\n // left edge directly instead of centering it with translateX(-50%).\n useLayoutEffect(() => {\n if (!visible || !ref.current) return;\n const el = ref.current;\n const wrapper = el.parentElement;\n if (!wrapper) return;\n const width = el.offsetWidth;\n el.style.left = `${Math.max(0, Math.min(pos.x - width / 2, wrapper.clientWidth - width))}px`;\n }, [visible, pos.x]);\n\n if (!visible || editor.isDestroyed || !editor.isEditable) return null;\n\n const btn = (onClick: () => void, title: string, isActive: boolean | undefined, children: React.ReactNode) => (\n <button\n type=\"button\"\n className={`rte-bubble__btn ${isActive ? 'rte-bubble__btn--active' : ''}`}\n onClick={() => { if (!editor.isDestroyed && editor.isEditable) onClick(); }}\n title={title}\n aria-label={title}\n aria-pressed={isActive}\n onMouseDown={(e) => e.preventDefault()}\n >\n {children}\n </button>\n );\n\n return (\n <div ref={ref} className=\"rte-bubble\" role=\"group\" aria-label=\"Selection formatting\"\n onBlur={(event) => {\n if (!event.currentTarget.contains(event.relatedTarget) && !ownsPalette(event.relatedTarget) && !editor.isFocused) setVisible(false);\n }}\n onKeyDown={(event) => {\n if (event.key === 'Escape') {\n event.preventDefault();\n editor.commands.focus();\n setVisible(false);\n }\n }}\n style={{ top: pos.y }}>\n {btn(() => editor.chain().focus().toggleBold().run(), 'Bold', editor.isActive('bold'),\n <strong style={{ fontSize: 13 }}>B</strong>)}\n {btn(() => editor.chain().focus().toggleItalic().run(), 'Italic', editor.isActive('italic'),\n <em style={{ fontSize: 13 }}>I</em>)}\n {btn(() => editor.chain().focus().toggleUnderline().run(), 'Underline', editor.isActive('underline'),\n <span style={{ fontSize: 13, textDecoration: 'underline' }}>U</span>)}\n {btn(() => editor.chain().focus().toggleStrike().run(), 'Strike', editor.isActive('strike'),\n <span style={{ fontSize: 13, textDecoration: 'line-through' }}>S</span>)}\n {btn(() => editor.chain().focus().toggleCode().run(), 'Code', editor.isActive('code'),\n <span style={{ fontSize: 11, fontFamily: 'monospace' }}>&lt;/&gt;</span>)}\n {btn(() => {\n const url = window.prompt('URL:', editor.getAttributes('link').href || '');\n if (url === null || editor.isDestroyed || !editor.isEditable) return;\n const chain = editor.chain().focus().extendMarkRange('link');\n if (url.trim() === '') chain.unsetLink().run();\n else chain.setLink({ href: url.trim() }).run();\n }, 'Link', editor.isActive('link'),\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\"><path d=\"M10 13a5 5 0 007.54.54l3-3a5 5 0 00-7.07-7.07l-1.72 1.71\" /><path d=\"M14 11a5 5 0 00-7.54-.54l-3 3a5 5 0 007.07 7.07l1.71-1.71\" /></svg>)}\n <div className=\"rte-bubble__divider\" />\n <ColorPicker\n value={editor.getAttributes('textStyle').color || ''}\n onChange={(color) => {\n if (editor.isDestroyed || !editor.isEditable) return;\n if (color) editor.chain().focus().setColor(color).run();\n else editor.chain().focus().unsetColor().run();\n }}\n title=\"Text color\"\n buttonClassName=\"rte-bubble__btn\"\n portalOwner={bubbleId}\n kind=\"text\"\n />\n <ColorPicker\n value={editor.getAttributes('highlight').color || ''}\n onChange={(color) => {\n if (editor.isDestroyed || !editor.isEditable) return;\n if (color) editor.chain().focus().toggleHighlight({ color }).run();\n else editor.chain().focus().unsetHighlight().run();\n }}\n title=\"Highlight color\"\n buttonClassName=\"rte-bubble__btn\"\n portalOwner={bubbleId}\n kind=\"highlight\"\n />\n <div className=\"rte-bubble__divider\" />\n <SelectDropdown\n className=\"rte-bubble__select\"\n width={96}\n value={editor.getAttributes('textStyle').fontFamily || ''}\n options={FONT_FAMILIES}\n onChange={(value) => {\n if (editor.isDestroyed || !editor.isEditable) return;\n if (value) editor.chain().focus().setFontFamily(value).run();\n else editor.chain().focus().unsetFontFamily().run();\n }}\n title=\"Font family\"\n />\n <SelectDropdown\n className=\"rte-bubble__select rte-bubble__select--size\"\n width={72}\n value={editor.getAttributes('textStyle').fontSize || ''}\n options={FONT_SIZES}\n onChange={(value) => {\n if (editor.isDestroyed || !editor.isEditable) return;\n if (value) (editor.chain().focus() as any).setFontSize(value).run();\n else (editor.chain().focus() as any).unsetFontSize().run();\n }}\n title=\"Font size\"\n />\n {onAddComment && (\n <>\n <div className=\"rte-bubble__divider\" />\n {btn(onAddComment, 'Comment', undefined,\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\"><path d=\"M21 15a2 2 0 01-2 2H7l-4 4V5a2 2 0 012-2h14a2 2 0 012 2z\" /></svg>)}\n </>\n )}\n </div>\n );\n};\n\nexport default BubbleToolbar;\n"],"mappings":"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"}