dishui 0.0.97 → 0.0.104
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/.release-verified.json +2 -2
- package/dist/ai.js +17 -15
- package/dist/components/Card/Ticket.d.ts +52 -0
- package/dist/components/Card/Ticket2.js +218 -0
- package/dist/components/Card/Ticket2.js.map +1 -0
- package/dist/components/Card/index.d.ts +12 -0
- package/dist/components/Card/index.js +142 -139
- package/dist/components/Card/index.js.map +1 -1
- package/dist/components/ai/AgentRunCard/index.d.ts +38 -0
- package/dist/components/ai/AgentRunCard/index.js +100 -0
- package/dist/components/ai/AgentRunCard/index.js.map +1 -0
- package/dist/components/ai/IssueCard/index.d.ts +39 -0
- package/dist/components/ai/IssueCard/index.js +78 -0
- package/dist/components/ai/IssueCard/index.js.map +1 -0
- package/dist/components/ai/index.d.ts +4 -0
- package/dist/components/pro/Flow/constants.js +3 -3
- package/dist/components/pro/Flow/constants.js.map +1 -1
- package/dist/components/pro/Flow/context/FlowAIContext.d.ts +2 -0
- package/dist/components/pro/Flow/context/FlowAIContext.js +9 -4
- package/dist/components/pro/Flow/context/FlowAIContext.js.map +1 -1
- package/dist/components/pro/Flow/nodes/MediaContent.d.ts +3 -2
- package/dist/components/pro/Flow/nodes/MediaContent.js +16 -16
- package/dist/components/pro/Flow/nodes/MediaContent.js.map +1 -1
- package/dist/components/pro/Flow/nodes/MediaOverlay.js +25 -23
- package/dist/components/pro/Flow/nodes/MediaOverlay.js.map +1 -1
- package/dist/components/pro/Flow/nodes/shapes.js +3 -3
- package/dist/components/pro/Flow/nodes/shapes.js.map +1 -1
- package/dist/components/pro/Flow/panels/InspectorPanel.js +396 -384
- package/dist/components/pro/Flow/panels/InspectorPanel.js.map +1 -1
- package/dist/components/pro/Flow/utils/themeColor.d.ts +2 -2
- package/dist/components/pro/Flow/utils/themeColor.js +2 -2
- package/dist/components/pro/Flow/utils/themeColor.js.map +1 -1
- package/dist/components/pro/Kanban/Kanban.d.ts +3 -0
- package/dist/components/pro/Kanban/Kanban.js +339 -0
- package/dist/components/pro/Kanban/Kanban.js.map +1 -0
- package/dist/components/pro/Kanban/Swimlanes.d.ts +3 -0
- package/dist/components/pro/Kanban/Swimlanes.js +60 -0
- package/dist/components/pro/Kanban/Swimlanes.js.map +1 -0
- package/dist/components/pro/Kanban/index.d.ts +5 -0
- package/dist/components/pro/Kanban/types.d.ts +103 -0
- package/dist/components/pro/Kanban/useKanbanDrag.d.ts +17 -0
- package/dist/components/pro/Kanban/useKanbanDrag.js +69 -0
- package/dist/components/pro/Kanban/useKanbanDrag.js.map +1 -0
- package/dist/components/pro/Kanban/utils.d.ts +13 -0
- package/dist/components/pro/Kanban/utils.js +48 -0
- package/dist/components/pro/Kanban/utils.js.map +1 -0
- package/dist/components/pro/RichTextEditor/AIRewrite.js +158 -127
- package/dist/components/pro/RichTextEditor/AIRewrite.js.map +1 -1
- package/dist/components/pro/RichTextEditor/BlockHandle.d.ts +2 -0
- package/dist/components/pro/RichTextEditor/BlockHandle.js +156 -115
- package/dist/components/pro/RichTextEditor/BlockHandle.js.map +1 -1
- package/dist/components/pro/RichTextEditor/RichTextEditor.js +136 -159
- package/dist/components/pro/RichTextEditor/RichTextEditor.js.map +1 -1
- package/dist/components/pro/RichTextEditor/Toolbar/ColorPicker.js +44 -41
- package/dist/components/pro/RichTextEditor/Toolbar/ColorPicker.js.map +1 -1
- package/dist/components/pro/RichTextEditor/Toolbar/FloatingDialogMenu.js +59 -53
- package/dist/components/pro/RichTextEditor/Toolbar/FloatingDialogMenu.js.map +1 -1
- package/dist/components/pro/RichTextEditor/Toolbar/MarkdownTemplates.js +48 -44
- package/dist/components/pro/RichTextEditor/Toolbar/MarkdownTemplates.js.map +1 -1
- package/dist/components/pro/RichTextEditor/Toolbar/TableOpsMenu.js +67 -63
- package/dist/components/pro/RichTextEditor/Toolbar/TableOpsMenu.js.map +1 -1
- package/dist/components/pro/RichTextEditor/Toolbar/Toolbar.d.ts +1 -0
- package/dist/components/pro/RichTextEditor/Toolbar/Toolbar.js +182 -129
- package/dist/components/pro/RichTextEditor/Toolbar/Toolbar.js.map +1 -1
- package/dist/components/pro/RichTextEditor/Toolbar/ToolbarButton.d.ts +1 -0
- package/dist/components/pro/RichTextEditor/Toolbar/ToolbarButton.js +3 -3
- package/dist/components/pro/RichTextEditor/Toolbar/ToolbarButton.js.map +1 -1
- package/dist/components/pro/RichTextEditor/Toolbar/useAnchoredDropdown.d.ts +3 -0
- package/dist/components/pro/RichTextEditor/Toolbar/useAnchoredDropdown.js +31 -0
- package/dist/components/pro/RichTextEditor/Toolbar/useAnchoredDropdown.js.map +1 -0
- package/dist/components/pro/RichTextEditor/index.d.ts +1 -1
- package/dist/components/pro/RichTextEditor/types.d.ts +78 -11
- package/dist/components/pro/RichTextEditor/types.js +11 -0
- package/dist/components/pro/RichTextEditor/types.js.map +1 -1
- package/dist/dishui.css +1 -1
- package/dist/index-nostyled.d.ts +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.js +128 -122
- package/dist/kanban.js +5 -0
- package/dist/utils/i18n.js +20 -0
- package/dist/utils/i18n.js.map +1 -1
- package/dist/utils/menu.js +1 -1
- package/dist/utils/menu.js.map +1 -1
- package/package.json +5 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AIRewrite.js","names":[],"sources":["../../../../src/components/pro/RichTextEditor/AIRewrite.tsx"],"sourcesContent":["import React, { useState, useRef, useCallback, useEffect } from 'react';\nimport type { Editor } from '@tiptap/react';\nimport type { AIConfig, AIAction, RichTextEditorLabels } from './types';\nimport { defaultLabels } from './types';\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 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 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): AsyncIterable<string> {\n if (config.protocol === 'custom') {\n if (!config.customHandler) throw new Error('Custom AI protocol requires customHandler');\n return config.customHandler({ text, action: action === 'custom' ? 'rewrite' : action, signal });\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 [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 text = selectedText || editor.state.doc.textContent;\n if (!text.trim() && !prompt.trim()) return;\n\n const systemPrompt = action === 'custom'\n ? (prompt || 'Improve the following text. Return only the improved text.')\n : getSystemPrompt(action, customPromptOverride);\n\n const userText = action === 'custom' && prompt\n ? `Instructions: ${prompt}\\n\\nText:\\n${text}`\n : text;\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 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=\"Close\">\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\">Prompt</label>\n <textarea\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=\"Describe what AI should do... (Ctrl+Enter to generate)\"\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 <label className=\"rte-ai-panel-v2__label\">Selected Text</label>\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\">No text selected (will use all content)</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}\n title={labels[labelKey]}\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 ? (\n <><div className=\"rte-ai-panel__spinner\" /> {labels.aiProcessing}</>\n ) : (\n '✨ Generate'\n )}\n </button>\n {isProcessing && (\n <button type=\"button\" className=\"rte-ai-panel-v2__cancel-btn\" onClick={handleReject}>\n Cancel\n </button>\n )}\n </div>\n\n {/* Streaming result */}\n {streamedText && (\n <div className=\"rte-ai-panel-v2__result\">\n <label className=\"rte-ai-panel-v2__label\">Result</label>\n <div className=\"rte-ai-panel-v2__result-text\">{streamedText}</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)}>Dismiss</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 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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"}
|
|
@@ -2,6 +2,8 @@ import React from 'react';
|
|
|
2
2
|
import type { Editor } from '@tiptap/react';
|
|
3
3
|
interface BlockHandleProps {
|
|
4
4
|
editor: Editor;
|
|
5
|
+
/** Keep the left control rail visible instead of revealing it only on hover. */
|
|
6
|
+
persistent?: boolean;
|
|
5
7
|
}
|
|
6
8
|
declare const BlockHandle: React.FC<BlockHandleProps>;
|
|
7
9
|
export default BlockHandle;
|
|
@@ -2,21 +2,21 @@ import { useCallback as e, useEffect as t, useLayoutEffect as n, useRef as r, us
|
|
|
2
2
|
import { Fragment as a, jsx as o, jsxs as s } from "react/jsx-runtime";
|
|
3
3
|
import { NodeSelection as c, Selection as l } from "@tiptap/pm/state";
|
|
4
4
|
//#region src/components/pro/RichTextEditor/BlockHandle.tsx
|
|
5
|
-
var u = ({ editor: u }) => {
|
|
6
|
-
let [
|
|
5
|
+
var u = ({ editor: u, persistent: d = !1 }) => {
|
|
6
|
+
let [f, p] = i(!1), [m, h] = i(0), [g, _] = i(!1), [v, y] = i("add"), [b, x] = i({
|
|
7
7
|
left: 4,
|
|
8
8
|
top: 0
|
|
9
|
-
}),
|
|
10
|
-
|
|
11
|
-
}, []),
|
|
12
|
-
|
|
13
|
-
!
|
|
9
|
+
}), S = r(null), C = r(null), w = r(!1), [T, E] = i(-1), [D, O] = i(!1), [k, A] = i(null), j = r(null), M = r(null), N = r(null), P = r(!1), F = e(() => {
|
|
10
|
+
N.current &&= (clearTimeout(N.current), null);
|
|
11
|
+
}, []), I = e(() => {
|
|
12
|
+
F(), N.current = setTimeout(() => {
|
|
13
|
+
!P.current && !g && !D && !M.current?.contains(document.activeElement) && !j.current?.contains(document.activeElement) && p(!1);
|
|
14
14
|
}, 200);
|
|
15
15
|
}, [
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
]),
|
|
16
|
+
F,
|
|
17
|
+
g,
|
|
18
|
+
D
|
|
19
|
+
]), L = e(() => {
|
|
20
20
|
if (u.isDestroyed) return [];
|
|
21
21
|
let e = u.view.dom.closest(".rte-editor-wrapper");
|
|
22
22
|
if (!e) return [];
|
|
@@ -33,76 +33,102 @@ var u = ({ editor: u }) => {
|
|
|
33
33
|
});
|
|
34
34
|
}
|
|
35
35
|
}), r;
|
|
36
|
-
}, [u]),
|
|
36
|
+
}, [u]), R = e((e) => {
|
|
37
37
|
let t = u.view.dom.closest(".rte-editor-wrapper");
|
|
38
38
|
if (!t) return -1;
|
|
39
39
|
let n = e - t.getBoundingClientRect().top - t.clientTop + t.scrollTop;
|
|
40
|
-
return
|
|
41
|
-
}, [u,
|
|
40
|
+
return L().findIndex((e) => n >= e.domTop && n <= e.domBottom);
|
|
41
|
+
}, [u, L]), z = e((e) => {
|
|
42
|
+
let t = L(), n = R(e);
|
|
43
|
+
if (n < 0 || n >= t.length) return !1;
|
|
44
|
+
let r = t[n], i = u.state.doc.nodeAt(r.pos);
|
|
45
|
+
return i ? (S.current = {
|
|
46
|
+
pos: r.pos,
|
|
47
|
+
node: i
|
|
48
|
+
}, E(n), h(r.domTop), p(!0), !0) : !1;
|
|
49
|
+
}, [
|
|
50
|
+
u,
|
|
51
|
+
R,
|
|
52
|
+
L
|
|
53
|
+
]), B = e((e) => {
|
|
42
54
|
let t = u.view.dom.closest(".rte-editor-wrapper");
|
|
43
55
|
if (!t) return null;
|
|
44
|
-
let n = e - t.getBoundingClientRect().top - t.clientTop + t.scrollTop, r =
|
|
56
|
+
let n = e - t.getBoundingClientRect().top - t.clientTop + t.scrollTop, r = L();
|
|
45
57
|
if (r.length === 0) return null;
|
|
46
|
-
let i =
|
|
58
|
+
let i = S.current, a = i ? r.findIndex((e) => e.pos === i.pos) : -1, o = r.findIndex((e) => n >= e.domTop && n <= e.domBottom);
|
|
59
|
+
if (a > o && o >= 0) return {
|
|
60
|
+
slotIndex: o,
|
|
61
|
+
indicatorTop: r[o].domTop
|
|
62
|
+
};
|
|
63
|
+
let s = 0, c = Infinity, l = r[0].domTop;
|
|
47
64
|
for (let e = 0; e <= r.length; e++) {
|
|
48
|
-
let t = e < r.length ? r[e].domTop : r[r.length - 1].domBottom,
|
|
49
|
-
|
|
65
|
+
let t = e < r.length ? r[e].domTop : r[r.length - 1].domBottom, i = Math.abs(n - t);
|
|
66
|
+
i < c && (c = i, s = e, l = t);
|
|
50
67
|
}
|
|
51
68
|
return {
|
|
52
|
-
slotIndex:
|
|
53
|
-
indicatorTop:
|
|
69
|
+
slotIndex: s,
|
|
70
|
+
indicatorTop: l
|
|
54
71
|
};
|
|
55
|
-
}, [u,
|
|
72
|
+
}, [u, L]);
|
|
56
73
|
t(() => {
|
|
57
74
|
let e = u.view.dom.closest(".rte-editor-wrapper");
|
|
58
75
|
if (!e) return;
|
|
59
76
|
let t = (t) => {
|
|
60
|
-
if (
|
|
61
|
-
let n =
|
|
77
|
+
if (D || C.current || g || P.current || !u.isEditable || u.view.composing || M.current?.contains(t.target) || j.current?.contains(t.target)) return;
|
|
78
|
+
let n = R(t.clientY);
|
|
62
79
|
if (n < 0) {
|
|
63
|
-
|
|
80
|
+
I();
|
|
64
81
|
return;
|
|
65
82
|
}
|
|
66
|
-
let r =
|
|
67
|
-
if (n >= r.length) return;
|
|
83
|
+
let r = L();
|
|
84
|
+
if (n >= r.length || !z(t.clientY)) return;
|
|
68
85
|
let i = Math.max(0, -e.getBoundingClientRect().top);
|
|
69
|
-
|
|
70
|
-
let a = u.state.doc.nodeAt(r[n].pos);
|
|
71
|
-
x.current = a ? {
|
|
72
|
-
pos: r[n].pos,
|
|
73
|
-
node: a
|
|
74
|
-
} : null, P(), f(!0);
|
|
86
|
+
h(Math.max(r[n].domTop, e.scrollTop + i + 4)), F();
|
|
75
87
|
}, n = () => {
|
|
76
|
-
|
|
88
|
+
D || I();
|
|
89
|
+
}, r = (e) => {
|
|
90
|
+
if (!u.isEditable || u.view.composing) return;
|
|
91
|
+
let t = e.touches[0];
|
|
92
|
+
t && z(t.clientY);
|
|
77
93
|
};
|
|
78
|
-
|
|
79
|
-
|
|
94
|
+
e.addEventListener("mousemove", t), e.addEventListener("mouseleave", n), e.addEventListener("touchstart", r);
|
|
95
|
+
let i = d ? requestAnimationFrame(() => {
|
|
96
|
+
let e = L(), t = e[0] ? u.state.doc.nodeAt(e[0].pos) : null;
|
|
97
|
+
e[0] && t && (S.current = {
|
|
98
|
+
pos: e[0].pos,
|
|
99
|
+
node: t
|
|
100
|
+
}, E(0), h(e[0].domTop), p(!0));
|
|
101
|
+
}) : null;
|
|
102
|
+
return () => {
|
|
103
|
+
e.removeEventListener("mousemove", t), e.removeEventListener("mouseleave", n), e.removeEventListener("touchstart", r), i !== null && cancelAnimationFrame(i), F();
|
|
80
104
|
};
|
|
81
105
|
}, [
|
|
82
106
|
u,
|
|
83
|
-
P,
|
|
84
107
|
F,
|
|
85
|
-
|
|
86
|
-
|
|
108
|
+
I,
|
|
109
|
+
D,
|
|
110
|
+
g,
|
|
111
|
+
R,
|
|
87
112
|
L,
|
|
88
|
-
|
|
113
|
+
z,
|
|
114
|
+
d
|
|
89
115
|
]), t(() => {
|
|
90
|
-
if (!
|
|
116
|
+
if (!g) return;
|
|
91
117
|
let e = (e) => {
|
|
92
|
-
|
|
118
|
+
j.current && !j.current.contains(e.target) && !M.current?.contains(e.target) && (P.current = !1, _(!1));
|
|
93
119
|
};
|
|
94
120
|
return document.addEventListener("mousedown", e), () => document.removeEventListener("mousedown", e);
|
|
95
|
-
}, [
|
|
96
|
-
let
|
|
97
|
-
let e =
|
|
121
|
+
}, [g]);
|
|
122
|
+
let V = e(() => {
|
|
123
|
+
let e = S.current;
|
|
98
124
|
return u.isDestroyed || !u.isEditable || u.view.composing || !e || u.state.doc.nodeAt(e.pos) !== e.node ? null : e;
|
|
99
|
-
}, [u]),
|
|
100
|
-
let t =
|
|
125
|
+
}, [u]), H = e((e) => {
|
|
126
|
+
let t = V();
|
|
101
127
|
if (!t) return;
|
|
102
128
|
let n = t.pos + t.node.nodeSize;
|
|
103
|
-
u.chain().focus().insertContentAt(n, e).command(({ tr: e }) => (e.setSelection(l.near(e.doc.resolve(n + 1))), !0)).run(),
|
|
104
|
-
}, [u,
|
|
105
|
-
let t =
|
|
129
|
+
u.chain().focus().insertContentAt(n, e).command(({ tr: e }) => (e.setSelection(l.near(e.doc.resolve(n + 1))), !0)).run(), _(!1);
|
|
130
|
+
}, [u, V]), U = e((e) => {
|
|
131
|
+
let t = V(), n = L();
|
|
106
132
|
if (!t || e < 0 || e > n.length) return;
|
|
107
133
|
let r = n.findIndex((e) => e.pos === t.pos);
|
|
108
134
|
if (r < 0 || e === r || e === r + 1) return;
|
|
@@ -110,67 +136,72 @@ var u = ({ editor: u }) => {
|
|
|
110
136
|
a.insert(o, t.node), a.setSelection(c.isSelectable(t.node) ? c.create(a.doc, o) : l.near(a.doc.resolve(o))), u.view.dispatch(a.scrollIntoView()), u.view.focus();
|
|
111
137
|
}, [
|
|
112
138
|
u,
|
|
113
|
-
|
|
114
|
-
|
|
139
|
+
V,
|
|
140
|
+
L
|
|
115
141
|
]);
|
|
116
142
|
t(() => {
|
|
117
143
|
let e = () => {
|
|
118
|
-
|
|
144
|
+
C.current?.(), S.current = null, P.current = !1, p(d), _(!1), F();
|
|
119
145
|
}, t = ({ transaction: t }) => {
|
|
120
146
|
(t.docChanged || !u.isEditable) && e();
|
|
121
147
|
}, n = (t) => {
|
|
122
|
-
(!(t.target instanceof Node) || !
|
|
148
|
+
(!(t.target instanceof Node) || !j.current?.contains(t.target)) && e();
|
|
123
149
|
};
|
|
124
150
|
u.on("transaction", t);
|
|
125
151
|
let r = u.view.dom;
|
|
126
152
|
return r.addEventListener("compositionstart", e), window.addEventListener("scroll", n, !0), window.addEventListener("resize", e), window.addEventListener("blur", e), () => {
|
|
127
|
-
u.off("transaction", t), r.removeEventListener("compositionstart", e), window.removeEventListener("scroll", n, !0), window.removeEventListener("resize", e), window.removeEventListener("blur", e),
|
|
153
|
+
u.off("transaction", t), r.removeEventListener("compositionstart", e), window.removeEventListener("scroll", n, !0), window.removeEventListener("resize", e), window.removeEventListener("blur", e), C.current?.(), F();
|
|
128
154
|
};
|
|
129
|
-
}, [
|
|
130
|
-
|
|
131
|
-
|
|
155
|
+
}, [
|
|
156
|
+
u,
|
|
157
|
+
F,
|
|
158
|
+
d
|
|
159
|
+
]), n(() => {
|
|
160
|
+
if (!g) return;
|
|
161
|
+
let e = j.current, t = M.current, n = u.view.dom.closest(".rte-editor-wrapper");
|
|
132
162
|
if (!e || !t || !n) return;
|
|
133
163
|
let r = n.getBoundingClientRect(), i = t.getBoundingClientRect(), a = Math.max(8, r.left + n.clientLeft), o = Math.min(window.innerWidth - 8, r.right), s = Math.max(8, r.top + n.clientTop), c = Math.min(window.innerHeight - 8, r.bottom) - i.bottom - 4, l = i.top - s - 4, d = c < e.scrollHeight && l > c;
|
|
134
|
-
e.style.maxHeight = `${Math.max(0, Math.min(320, d ? l : c))}px`, e.style.minWidth = "0", e.style.width = `${Math.max(0, Math.min(220, o - a))}px`,
|
|
164
|
+
e.style.maxHeight = `${Math.max(0, Math.min(320, d ? l : c))}px`, e.style.minWidth = "0", e.style.width = `${Math.max(0, Math.min(220, o - a))}px`, x({
|
|
135
165
|
left: Math.max(a, Math.min(i.left, o - e.offsetWidth)) - r.left - n.clientLeft + n.scrollLeft,
|
|
136
166
|
top: (d ? i.top - e.offsetHeight - 4 : i.bottom + 4) - r.top - n.clientTop + n.scrollTop
|
|
137
167
|
});
|
|
138
|
-
let f =
|
|
168
|
+
let f = v === "actions" ? "button:not(:disabled)" : "button";
|
|
139
169
|
e.querySelector(f)?.focus({ preventScroll: !0 });
|
|
140
170
|
}, [
|
|
141
171
|
u,
|
|
142
|
-
|
|
143
|
-
|
|
172
|
+
g,
|
|
173
|
+
v
|
|
144
174
|
]);
|
|
145
|
-
let
|
|
146
|
-
if (e.preventDefault(), e.stopPropagation(), e.button !== 0 || !z()) return;
|
|
147
|
-
|
|
175
|
+
let W = e((e) => {
|
|
176
|
+
if (e.preventDefault(), e.stopPropagation(), e.button !== 0 || !z(e.clientY) || !V()) return;
|
|
177
|
+
C.current?.(), w.current = !1;
|
|
148
178
|
let t = e.clientX, n = e.clientY, r = !1, i = (e) => {
|
|
149
179
|
if (!r && Math.hypot(e.clientX - t, e.clientY - n) < 5) return;
|
|
150
|
-
r = !0,
|
|
151
|
-
let i =
|
|
152
|
-
i &&
|
|
180
|
+
r = !0, w.current = !0, _(!1), O(!0);
|
|
181
|
+
let i = B(e.clientY);
|
|
182
|
+
i && A({ top: i.indicatorTop });
|
|
153
183
|
}, a = () => {
|
|
154
|
-
document.removeEventListener("mousemove", i), document.removeEventListener("mouseup", s), document.removeEventListener("keydown", o, !0),
|
|
184
|
+
document.removeEventListener("mousemove", i), document.removeEventListener("mouseup", s), document.removeEventListener("keydown", o, !0), C.current = null, O(!1), A(null);
|
|
155
185
|
}, o = (e) => {
|
|
156
|
-
e.key === "Escape" && (e.preventDefault(), e.stopImmediatePropagation(),
|
|
186
|
+
e.key === "Escape" && (e.preventDefault(), e.stopImmediatePropagation(), w.current = !0, a());
|
|
157
187
|
}, s = (e) => {
|
|
158
188
|
if (a(), !r) return;
|
|
159
189
|
let t = u.view.dom.closest(".rte-editor-wrapper")?.getBoundingClientRect();
|
|
160
190
|
if (!t || e.clientX < t.left || e.clientX > t.right || e.clientY < t.top || e.clientY > t.bottom) return;
|
|
161
|
-
let n =
|
|
162
|
-
n &&
|
|
191
|
+
let n = B(e.clientY);
|
|
192
|
+
n && U(n.slotIndex);
|
|
163
193
|
};
|
|
164
|
-
|
|
194
|
+
C.current = a, document.addEventListener("mousemove", i), document.addEventListener("mouseup", s), document.addEventListener("keydown", o, !0);
|
|
165
195
|
}, [
|
|
166
196
|
u,
|
|
167
|
-
z,
|
|
168
197
|
V,
|
|
169
|
-
|
|
170
|
-
|
|
198
|
+
U,
|
|
199
|
+
B,
|
|
200
|
+
z
|
|
201
|
+
]), G = { type: "paragraph" }, K = [
|
|
171
202
|
{
|
|
172
203
|
label: "Text",
|
|
173
|
-
content:
|
|
204
|
+
content: G
|
|
174
205
|
},
|
|
175
206
|
...[
|
|
176
207
|
1,
|
|
@@ -189,7 +220,7 @@ var u = ({ editor: u }) => {
|
|
|
189
220
|
type: "bulletList",
|
|
190
221
|
content: [{
|
|
191
222
|
type: "listItem",
|
|
192
|
-
content: [
|
|
223
|
+
content: [G]
|
|
193
224
|
}]
|
|
194
225
|
}
|
|
195
226
|
},
|
|
@@ -199,7 +230,7 @@ var u = ({ editor: u }) => {
|
|
|
199
230
|
type: "orderedList",
|
|
200
231
|
content: [{
|
|
201
232
|
type: "listItem",
|
|
202
|
-
content: [
|
|
233
|
+
content: [G]
|
|
203
234
|
}]
|
|
204
235
|
}
|
|
205
236
|
},
|
|
@@ -210,7 +241,7 @@ var u = ({ editor: u }) => {
|
|
|
210
241
|
content: [{
|
|
211
242
|
type: "taskItem",
|
|
212
243
|
attrs: { checked: !1 },
|
|
213
|
-
content: [
|
|
244
|
+
content: [G]
|
|
214
245
|
}]
|
|
215
246
|
}
|
|
216
247
|
},
|
|
@@ -218,7 +249,7 @@ var u = ({ editor: u }) => {
|
|
|
218
249
|
label: "Blockquote",
|
|
219
250
|
content: {
|
|
220
251
|
type: "blockquote",
|
|
221
|
-
content: [
|
|
252
|
+
content: [G]
|
|
222
253
|
}
|
|
223
254
|
},
|
|
224
255
|
{
|
|
@@ -237,48 +268,48 @@ var u = ({ editor: u }) => {
|
|
|
237
268
|
type: "tableRow",
|
|
238
269
|
content: Array.from({ length: 3 }, () => ({
|
|
239
270
|
type: t === 0 ? "tableHeader" : "tableCell",
|
|
240
|
-
content: [
|
|
271
|
+
content: [G]
|
|
241
272
|
}))
|
|
242
273
|
}))
|
|
243
274
|
}
|
|
244
275
|
}
|
|
245
|
-
],
|
|
276
|
+
], q = v === "add" ? K.filter((e) => u.schema.nodes[e.content.type]) : [
|
|
246
277
|
{ label: "Duplicate" },
|
|
247
278
|
{ label: "Delete" },
|
|
248
279
|
{
|
|
249
280
|
label: "Move up",
|
|
250
|
-
disabled:
|
|
281
|
+
disabled: T <= 0
|
|
251
282
|
},
|
|
252
283
|
{
|
|
253
284
|
label: "Move down",
|
|
254
|
-
disabled:
|
|
285
|
+
disabled: T >= u.state.doc.childCount - 1
|
|
255
286
|
}
|
|
256
|
-
],
|
|
287
|
+
], J = (e) => {
|
|
257
288
|
if (e.disabled) return;
|
|
258
|
-
let t =
|
|
289
|
+
let t = V();
|
|
259
290
|
if (t) {
|
|
260
|
-
if (e.content)
|
|
291
|
+
if (e.content) H(e.content);
|
|
261
292
|
else if (e.label === "Duplicate") {
|
|
262
293
|
let e = t.pos + t.node.nodeSize, n = u.state.tr.insert(e, t.node);
|
|
263
294
|
n.setSelection(l.near(n.doc.resolve(e + 1))), u.view.dispatch(n.scrollIntoView()), u.view.focus();
|
|
264
295
|
} else e.label === "Delete" ? u.chain().focus().deleteRange({
|
|
265
296
|
from: t.pos,
|
|
266
297
|
to: t.pos + t.node.nodeSize
|
|
267
|
-
}).run() : e.label === "Move up" ?
|
|
298
|
+
}).run() : e.label === "Move up" ? U(T - 1) : e.label === "Move down" && U(T + 2);
|
|
268
299
|
}
|
|
269
300
|
};
|
|
270
|
-
return !
|
|
301
|
+
return !f && !g && !D ? null : /* @__PURE__ */ s(a, { children: [
|
|
271
302
|
/* @__PURE__ */ s("div", {
|
|
272
|
-
ref:
|
|
273
|
-
className: "rte-block-handle"
|
|
274
|
-
style: { top:
|
|
275
|
-
onFocus:
|
|
276
|
-
onBlur:
|
|
303
|
+
ref: M,
|
|
304
|
+
className: `rte-block-handle ${d ? "rte-block-handle--persistent" : ""}`,
|
|
305
|
+
style: { top: m },
|
|
306
|
+
onFocus: F,
|
|
307
|
+
onBlur: I,
|
|
277
308
|
onMouseEnter: () => {
|
|
278
|
-
|
|
309
|
+
P.current = !0, F();
|
|
279
310
|
},
|
|
280
311
|
onMouseLeave: () => {
|
|
281
|
-
|
|
312
|
+
P.current = !1, D || I();
|
|
282
313
|
},
|
|
283
314
|
onMouseDown: (e) => {
|
|
284
315
|
e.stopPropagation(), e.preventDefault();
|
|
@@ -289,12 +320,19 @@ var u = ({ editor: u }) => {
|
|
|
289
320
|
onMouseDown: (e) => {
|
|
290
321
|
e.stopPropagation(), e.preventDefault();
|
|
291
322
|
},
|
|
323
|
+
onTouchStart: (e) => {
|
|
324
|
+
e.stopPropagation(), w.current = !0, V() && (y("add"), _(!g || v !== "add"));
|
|
325
|
+
},
|
|
292
326
|
onClick: (e) => {
|
|
293
|
-
e.stopPropagation(),
|
|
327
|
+
if (e.stopPropagation(), w.current) {
|
|
328
|
+
w.current = !1;
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
V() && (y("add"), _(!g || v !== "add"));
|
|
294
332
|
},
|
|
295
333
|
"aria-label": "Add block",
|
|
296
334
|
"aria-haspopup": "menu",
|
|
297
|
-
"aria-expanded":
|
|
335
|
+
"aria-expanded": g && v === "add",
|
|
298
336
|
title: "Add block",
|
|
299
337
|
children: /* @__PURE__ */ s("svg", {
|
|
300
338
|
width: "14",
|
|
@@ -317,18 +355,21 @@ var u = ({ editor: u }) => {
|
|
|
317
355
|
})
|
|
318
356
|
}), /* @__PURE__ */ o("button", {
|
|
319
357
|
type: "button",
|
|
320
|
-
className: `rte-block-handle__btn rte-block-handle__btn--drag ${
|
|
358
|
+
className: `rte-block-handle__btn rte-block-handle__btn--drag ${D ? "rte-block-handle__btn--dragging" : ""}`,
|
|
321
359
|
title: "Drag to reorder or click for block actions",
|
|
322
360
|
"aria-label": "Block actions",
|
|
323
361
|
"aria-haspopup": "menu",
|
|
324
|
-
"aria-expanded":
|
|
325
|
-
onMouseDown:
|
|
362
|
+
"aria-expanded": g && v === "actions",
|
|
363
|
+
onMouseDown: W,
|
|
364
|
+
onTouchStart: (e) => {
|
|
365
|
+
e.stopPropagation(), w.current = !0, y("actions"), _(!g || v !== "actions");
|
|
366
|
+
},
|
|
326
367
|
onClick: (e) => {
|
|
327
|
-
if (e.stopPropagation(),
|
|
328
|
-
|
|
368
|
+
if (e.stopPropagation(), w.current) {
|
|
369
|
+
w.current = !1;
|
|
329
370
|
return;
|
|
330
371
|
}
|
|
331
|
-
|
|
372
|
+
V() && (y("actions"), _(!g || v !== "actions"));
|
|
332
373
|
},
|
|
333
374
|
children: /* @__PURE__ */ s("svg", {
|
|
334
375
|
width: "14",
|
|
@@ -372,20 +413,20 @@ var u = ({ editor: u }) => {
|
|
|
372
413
|
})
|
|
373
414
|
})]
|
|
374
415
|
}),
|
|
375
|
-
|
|
376
|
-
ref:
|
|
416
|
+
g && /* @__PURE__ */ o("div", {
|
|
417
|
+
ref: j,
|
|
377
418
|
className: "rte-block-handle__menu",
|
|
378
419
|
role: "menu",
|
|
379
|
-
"aria-label":
|
|
420
|
+
"aria-label": v === "add" ? "Add block" : "Block actions",
|
|
380
421
|
style: {
|
|
381
|
-
...
|
|
422
|
+
...b,
|
|
382
423
|
overflowY: "auto"
|
|
383
424
|
},
|
|
384
425
|
onMouseEnter: () => {
|
|
385
|
-
|
|
426
|
+
P.current = !0, F();
|
|
386
427
|
},
|
|
387
428
|
onMouseLeave: () => {
|
|
388
|
-
|
|
429
|
+
P.current = !1;
|
|
389
430
|
},
|
|
390
431
|
onMouseDown: (e) => {
|
|
391
432
|
e.stopPropagation(), e.preventDefault();
|
|
@@ -393,7 +434,7 @@ var u = ({ editor: u }) => {
|
|
|
393
434
|
onKeyDown: (e) => {
|
|
394
435
|
if (e.nativeEvent.isComposing || e.keyCode === 229) return;
|
|
395
436
|
if (e.key === "Escape" || e.key === "Tab") {
|
|
396
|
-
e.key === "Escape" && e.preventDefault(), e.stopPropagation(),
|
|
437
|
+
e.key === "Escape" && e.preventDefault(), e.stopPropagation(), P.current = !1, _(!1), M.current?.querySelector(v === "add" ? ".rte-block-handle__btn--add" : ".rte-block-handle__btn--drag")?.focus({ preventScroll: !0 });
|
|
397
438
|
return;
|
|
398
439
|
}
|
|
399
440
|
if (![
|
|
@@ -408,7 +449,7 @@ var u = ({ editor: u }) => {
|
|
|
408
449
|
let i = t[r];
|
|
409
450
|
i && i.offsetTop < e.currentTarget.scrollTop ? e.currentTarget.scrollTop = i.offsetTop : i && i.offsetTop + i.offsetHeight > e.currentTarget.scrollTop + e.currentTarget.clientHeight && (e.currentTarget.scrollTop = i.offsetTop + i.offsetHeight - e.currentTarget.clientHeight);
|
|
410
451
|
},
|
|
411
|
-
children:
|
|
452
|
+
children: q.map((e) => /* @__PURE__ */ o("button", {
|
|
412
453
|
type: "button",
|
|
413
454
|
role: "menuitem",
|
|
414
455
|
disabled: e.disabled,
|
|
@@ -419,14 +460,14 @@ var u = ({ editor: u }) => {
|
|
|
419
460
|
className: "rte-block-handle__menu-item",
|
|
420
461
|
onMouseDown: (e) => e.stopPropagation(),
|
|
421
462
|
onClick: (t) => {
|
|
422
|
-
t.stopPropagation(),
|
|
463
|
+
t.stopPropagation(), J(e), _(!1), P.current = !1;
|
|
423
464
|
},
|
|
424
465
|
children: e.label
|
|
425
466
|
}, e.label))
|
|
426
467
|
}),
|
|
427
|
-
|
|
468
|
+
D && k && /* @__PURE__ */ o("div", {
|
|
428
469
|
className: "rte-block-handle__drop-indicator",
|
|
429
|
-
style: { top:
|
|
470
|
+
style: { top: k.top }
|
|
430
471
|
})
|
|
431
472
|
] });
|
|
432
473
|
};
|