@opentiny/fluent-editor 4.0.0-alpha.4 → 4.0.0-alpha.5

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 (106) hide show
  1. package/es/modules/ai/constants.es.js +50 -0
  2. package/es/modules/ai/constants.es.js.map +1 -0
  3. package/es/modules/ai/icons.es.js +319 -0
  4. package/es/modules/ai/icons.es.js.map +1 -0
  5. package/es/modules/ai/index.es.js +533 -101
  6. package/es/modules/ai/index.es.js.map +1 -1
  7. package/es/modules/custom-image/blot-formatter.es.js +4 -0
  8. package/es/modules/custom-image/blot-formatter.es.js.map +1 -1
  9. package/es/modules/custom-image/image.es.js +8 -1
  10. package/es/modules/custom-image/image.es.js.map +1 -1
  11. package/es/modules/custom-image/options.es.js +3 -0
  12. package/es/modules/custom-image/options.es.js.map +1 -1
  13. package/es/modules/custom-uploader.es.js +1 -1
  14. package/es/modules/custom-uploader.es.js.map +1 -1
  15. package/es/modules/emoji.es.js +3 -15
  16. package/es/modules/emoji.es.js.map +1 -1
  17. package/es/modules/table-up/index.es.js +16 -10
  18. package/es/modules/table-up/index.es.js.map +1 -1
  19. package/es/modules/toolbar/better-toolbar.es.js +1 -1
  20. package/es/modules/toolbar/better-toolbar.es.js.map +1 -1
  21. package/es/themes/snow.es.js +12 -0
  22. package/es/themes/snow.es.js.map +1 -1
  23. package/es/tools/format-painter.es.js +8 -2
  24. package/es/tools/format-painter.es.js.map +1 -1
  25. package/es/ui/icons.config.es.js +43 -13
  26. package/es/ui/icons.config.es.js.map +1 -1
  27. package/lib/modules/ai/constants.cjs.js +50 -0
  28. package/lib/modules/ai/constants.cjs.js.map +1 -0
  29. package/lib/modules/ai/icons.cjs.js +319 -0
  30. package/lib/modules/ai/icons.cjs.js.map +1 -0
  31. package/lib/modules/ai/index.cjs.js +533 -101
  32. package/lib/modules/ai/index.cjs.js.map +1 -1
  33. package/lib/modules/custom-image/blot-formatter.cjs.js +4 -0
  34. package/lib/modules/custom-image/blot-formatter.cjs.js.map +1 -1
  35. package/lib/modules/custom-image/image.cjs.js +8 -1
  36. package/lib/modules/custom-image/image.cjs.js.map +1 -1
  37. package/lib/modules/custom-image/options.cjs.js +3 -0
  38. package/lib/modules/custom-image/options.cjs.js.map +1 -1
  39. package/lib/modules/custom-uploader.cjs.js +1 -1
  40. package/lib/modules/custom-uploader.cjs.js.map +1 -1
  41. package/lib/modules/emoji.cjs.js +3 -15
  42. package/lib/modules/emoji.cjs.js.map +1 -1
  43. package/lib/modules/table-up/index.cjs.js +16 -10
  44. package/lib/modules/table-up/index.cjs.js.map +1 -1
  45. package/lib/modules/toolbar/better-toolbar.cjs.js +1 -1
  46. package/lib/modules/toolbar/better-toolbar.cjs.js.map +1 -1
  47. package/lib/themes/snow.cjs.js +12 -0
  48. package/lib/themes/snow.cjs.js.map +1 -1
  49. package/lib/tools/format-painter.cjs.js +8 -2
  50. package/lib/tools/format-painter.cjs.js.map +1 -1
  51. package/lib/ui/icons.config.cjs.js +43 -13
  52. package/lib/ui/icons.config.cjs.js.map +1 -1
  53. package/package.json +3 -3
  54. package/style.css +2471 -1509
  55. package/types/config/types/editor-config.interface.d.ts +9 -6
  56. package/types/modules/ai/constants.d.ts +30 -0
  57. package/types/modules/ai/icons.d.ts +21 -0
  58. package/types/modules/ai/index.d.ts +71 -13
  59. package/types/modules/ai/types.d.ts +16 -0
  60. package/types/modules/custom-image/image.d.ts +2 -0
  61. package/types/modules/custom-image/{Options.d.ts → options.d.ts} +1 -0
  62. package/types/modules/emoji.d.ts +0 -1
  63. package/types/modules/table-up/index.d.ts +0 -31
  64. package/types/tools/format-painter.d.ts +1 -2
  65. package/types/ui/icons.config.d.ts +1 -1
  66. package/types/config/types/additional-toolbar-item.interface.d.ts +0 -8
  67. package/types/config/types/content-change.interface.d.ts +0 -13
  68. package/types/config/types/content-save.interface.d.ts +0 -6
  69. package/types/config/types/counter-option.interface.d.ts +0 -9
  70. package/types/config/types/editor-toolbar.interface.d.ts +0 -6
  71. package/types/config/types/file-operation.interface.d.ts +0 -12
  72. package/types/config/types/focus-change.interface.d.ts +0 -4
  73. package/types/config/types/fullscreen-module.interface.d.ts +0 -4
  74. package/types/config/types/help-panel-item.interface.d.ts +0 -5
  75. package/types/config/types/help-panel-option.interface.d.ts +0 -7
  76. package/types/config/types/image-module.interface.d.ts +0 -3
  77. package/types/config/types/image-upload.interface.d.ts +0 -7
  78. package/types/config/types/load-on-demand-module.interface.d.ts +0 -5
  79. package/types/config/types/mention-module.interface.d.ts +0 -8
  80. package/types/config/types/paste-change.interface.d.ts +0 -6
  81. package/types/config/types/quick-menu-module.interface.d.ts +0 -3
  82. package/types/config/types/range.interface.d.ts +0 -4
  83. package/types/config/types/registry-options.interface.d.ts +0 -5
  84. package/types/config/types/selection-change.interface.d.ts +0 -8
  85. package/types/config/types/toolbar-item.interface.d.ts +0 -13
  86. package/types/config/types/validate-error.interface.d.ts +0 -13
  87. package/types/modules/custom-image/BlotFormatter.d.ts +0 -29
  88. package/types/modules/custom-image/actions/CustomResizeAction.d.ts +0 -24
  89. package/types/modules/custom-image/actions/DeleteAction.d.ts +0 -7
  90. package/types/modules/custom-image/image-bar.d.ts +0 -17
  91. package/types/modules/custom-image/specs/BlotSpec.d.ts +0 -13
  92. package/types/modules/custom-image/specs/CustomImageSpec.d.ts +0 -21
  93. package/types/modules/custom-image/specs/ImageSpec.d.ts +0 -10
  94. package/types/modules/emoji/emoji-list/index.d.ts +0 -1
  95. package/types/modules/emoji/emoji-list/people.d.ts +0 -1
  96. package/types/modules/emoji/emoji-list.d.ts +0 -2
  97. package/types/modules/emoji/emoji-map.d.ts +0 -2
  98. package/types/modules/emoji/emoji-sprite.d.ts +0 -1
  99. package/types/modules/emoji/formats/emoji-blot.d.ts +0 -15
  100. package/types/modules/emoji/index.d.ts +0 -3
  101. package/types/modules/emoji/modules/emoji.d.ts +0 -38
  102. package/types/modules/emoji/modules/toolbar-emoji.d.ts +0 -9
  103. package/types/modules/emoji/utils.d.ts +0 -1
  104. package/types/modules/mention/MentionLink.d.ts +0 -16
  105. /package/types/modules/custom-image/actions/{Action.d.ts → action.d.ts} +0 -0
  106. /package/types/modules/mention/{Mention.d.ts → mention.d.ts} +0 -0
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs.js","sources":["../../../../src/modules/ai/index.ts"],"sourcesContent":["import type TypeToolbar from 'quill/modules/toolbar'\r\nimport type FluentEditor from '../../core/fluent-editor'\r\nimport { AI_ICON } from '../../ui/icons.config'\r\n\r\nexport class AI {\r\n toolbar: TypeToolbar\r\n host: string\r\n apiKey: string\r\n model: string\r\n message: string\r\n isBreak: boolean = false // 打断标记\r\n inputValue: string = '' // 存储输入框的值\r\n SEND: string = '发送' // 发送按钮文字\r\n BREAK: string = '停止' // 取消按钮文字\r\n DONE: string = '完成'\r\n REGENERATE: string = '重新生成'\r\n CLOSE: string = '关闭'\r\n private dialogContainerEl: HTMLDivElement | null = null\r\n private wrapContainerEl: HTMLDivElement | null = null\r\n private aiPreTextEl: HTMLSpanElement | null = null\r\n private inputContainerEl: HTMLDivElement | null = null\r\n private inputEl: HTMLInputElement | null = null\r\n private sendButtonEl: HTMLDivElement | null = null\r\n private resultPopupEl: HTMLDivElement | null = null\r\n private actionMenuEl: HTMLDivElement | null = null\r\n\r\n constructor(public quill: FluentEditor, public options: any) {\r\n this.quill = quill\r\n this.toolbar = quill.getModule('toolbar') as TypeToolbar\r\n // 添加AI按钮到工具栏\r\n if (typeof this.toolbar !== 'undefined') {\r\n this.toolbar.addHandler('ai', this.showAIInput.bind(this))\r\n }\r\n\r\n this.host = options.host\r\n this.apiKey = options.apiKey\r\n this.model = options.model\r\n }\r\n\r\n positionElements() {\r\n if (!this.dialogContainerEl) return\r\n const range = this.quill.getSelection()\r\n if (range) {\r\n const bounds = this.quill.getBounds(range.index)\r\n this.dialogContainerEl.style.position = 'absolute'\r\n this.dialogContainerEl.style.top = `${bounds.top + bounds.height}px`\r\n }\r\n }\r\n\r\n // 创建AI提示语元素\r\n private createAiPreTextEl() {\r\n if (!this.aiPreTextEl) {\r\n this.aiPreTextEl = document.createElement('span')\r\n this.aiPreTextEl.className = 'ql-ai-tip'\r\n }\r\n }\r\n\r\n // 创建弹出框\r\n private createElements() {\r\n if (!this.dialogContainerEl) {\r\n this.dialogContainerEl = document.createElement('div')\r\n this.dialogContainerEl.className = 'ql-ai-dialog'\r\n this.wrapContainerEl = document.createElement('div')\r\n this.wrapContainerEl.className = 'ql-ai-wrapper'\r\n this.wrapContainerEl.style.width = `${this.quill.container.clientWidth * 0.9}px`\r\n\r\n // 创建输入框\r\n this.inputContainerEl = document.createElement('div')\r\n this.inputContainerEl.className = 'ql-ai-input'\r\n\r\n // 添加AI图标\r\n const aiIcon = document.createElement('div')\r\n aiIcon.className = 'ql-ai-icon'\r\n aiIcon.innerHTML = AI_ICON\r\n // 添加AI提示语\r\n this.createAiPreTextEl()\r\n\r\n // 增加输入框\r\n this.inputEl = document.createElement('input')\r\n this.inputEl.type = 'text'\r\n this.inputEl.placeholder = '请输入内容'\r\n // 添加发送按钮\r\n this.sendButtonEl = document.createElement('div')\r\n this.sendButtonEl.className = 'ql-ai-send'\r\n\r\n // 创建结果弹窗\r\n this.resultPopupEl = document.createElement('div')\r\n this.resultPopupEl.className = 'ql-ai-result'\r\n\r\n // 添加到编辑器\r\n this.wrapContainerEl.appendChild(this.resultPopupEl)\r\n this.inputContainerEl.appendChild(aiIcon)\r\n this.inputContainerEl.appendChild(this.aiPreTextEl)\r\n this.inputContainerEl.appendChild(this.inputEl)\r\n this.inputContainerEl.appendChild(this.sendButtonEl) // 添加发送按钮\r\n this.wrapContainerEl.appendChild(this.inputContainerEl)\r\n this.dialogContainerEl.appendChild(this.wrapContainerEl)\r\n }\r\n\r\n this.aiPreTextEl.textContent = `${this.model}帮你写:`\r\n this.sendButtonEl.textContent = this.SEND\r\n this.sendButtonEl.style.display = 'block' // 显示发送按钮\r\n this.resultPopupEl.style.display = 'none'\r\n this.quill.container.appendChild(this.dialogContainerEl)\r\n }\r\n\r\n showAIInput() {\r\n // 创建输入框和结果弹窗\r\n this.createElements()\r\n\r\n // 定位到编辑器焦点位置\r\n this.positionElements()\r\n\r\n // 监听发送事件\r\n this.inputEl.addEventListener('keydown', async (e) => {\r\n if (e.key === 'Enter') {\r\n await this.queryAI()\r\n }\r\n })\r\n this.sendButtonEl.addEventListener('click', async (e) => {\r\n if (e.target instanceof HTMLElement && e.target.textContent === this.BREAK) {\r\n this.isBreak = true\r\n }\r\n else {\r\n await this.queryAI()\r\n }\r\n })\r\n\r\n // 添加ESC键监听\r\n const handleKeyDown = (e: KeyboardEvent) => {\r\n if (e.key === 'Escape') {\r\n this.closeAIPanel()\r\n this.quill.container.removeEventListener('keydown', handleKeyDown)\r\n }\r\n }\r\n this.quill.container.addEventListener('keydown', handleKeyDown)\r\n }\r\n\r\n // AI查询\r\n private async queryAI(question?: string): Promise<string> {\r\n this.inputValue = question || this.inputEl.value\r\n this.inputEl.value = '' // 清空输入框\r\n if (this.inputValue.trim() === '') {\r\n return\r\n }\r\n\r\n // 有信息\r\n this.isBreak = false // 重置打断标记,防止重复打断ai\r\n this.sendButtonEl.textContent = this.BREAK\r\n this.sendButtonEl.style.display = 'block'\r\n this.aiPreTextEl.textContent = '按ESC退出 | 正在编写...' // 显示提示语\r\n // 这里实现实际的AI查询逻辑\r\n try {\r\n const response = await fetch(`${this.host}/api/generate`, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n 'Authorization': `Bearer ${this.apiKey}`,\r\n },\r\n body: JSON.stringify({\r\n model: this.model,\r\n prompt: this.inputValue,\r\n stream: true,\r\n }),\r\n })\r\n\r\n if (!response.ok) {\r\n throw new Error(`HTTP error! status: ${response.status}`)\r\n }\r\n\r\n const reader = response.body.getReader()\r\n const decoder = new TextDecoder()\r\n let content = ''\r\n\r\n while (true) {\r\n if (this.isBreak) {\r\n this.isBreak = false\r\n break\r\n }\r\n\r\n const { done, value } = await reader.read()\r\n if (done) break\r\n\r\n const chunk = decoder.decode(value)\r\n const lines = chunk.split('\\n').filter(line => line.trim() !== '')\r\n\r\n for (const line of lines) {\r\n try {\r\n const data = JSON.parse(line)\r\n content += data.response || ''\r\n this.showAIResponse(content)\r\n }\r\n catch (e) {\r\n console.error('解析错误:', e)\r\n }\r\n }\r\n }\r\n\r\n // 创建操作菜单\r\n this.createActionMenu()\r\n if (content) {\r\n this.aiPreTextEl.textContent = '' // 清空提示语\r\n // 隐藏发送按钮\r\n this.sendButtonEl.textContent = this.SEND\r\n this.sendButtonEl.style.display = 'none'\r\n }\r\n return content\r\n }\r\n catch (error) {\r\n console.error('AI查询失败:', error)\r\n return 'AI查询失败,请重试'\r\n }\r\n }\r\n\r\n private showAIResponse(response: string) {\r\n if (!this.resultPopupEl) return\r\n\r\n // 显示结果\r\n this.resultPopupEl.innerHTML = response\r\n this.resultPopupEl.style.display = 'block'\r\n }\r\n\r\n private createActionMenu() {\r\n if (!this.actionMenuEl) {\r\n this.actionMenuEl = document.createElement('div')\r\n this.actionMenuEl.className = 'ql-ai-actions'\r\n\r\n const actions = [this.DONE, this.REGENERATE, this.CLOSE]\r\n actions.forEach((action) => {\r\n const menuItem = document.createElement('div')\r\n menuItem.className = 'ql-ai-action-item'\r\n menuItem.textContent = action\r\n menuItem.addEventListener('click', () => this.handleAction(action))\r\n this.actionMenuEl.appendChild(menuItem)\r\n })\r\n\r\n this.wrapContainerEl.appendChild(this.actionMenuEl)\r\n }\r\n // 展示下拉菜单\r\n this.actionMenuEl.style.display = 'block'\r\n }\r\n\r\n private handleAction(action: string) {\r\n switch (action) {\r\n case this.DONE:\r\n this.insertAIResponse()\r\n break\r\n case this.REGENERATE:\r\n this.regenerateResponse()\r\n break\r\n case this.CLOSE:\r\n this.closeAIPanel()\r\n break\r\n }\r\n }\r\n\r\n private insertAIResponse() {\r\n if (!this.resultPopupEl) return\r\n const range = this.quill.getSelection(true)\r\n if (range) {\r\n // 使用HTML方式插入可以保留格式\r\n this.quill.clipboard.dangerouslyPasteHTML(\r\n range.index,\r\n this.resultPopupEl.innerHTML,\r\n )\r\n }\r\n this.closeAIPanel()\r\n }\r\n\r\n private async regenerateResponse() {\r\n this.actionMenuEl.style.display = 'none'\r\n await this.queryAI(this.inputValue)\r\n }\r\n\r\n private closeAIPanel() {\r\n this.isBreak = true // 停止查询\r\n if (this.dialogContainerEl) {\r\n this.quill.container.removeChild(this.dialogContainerEl)\r\n }\r\n this.dialogContainerEl = null\r\n this.actionMenuEl = null\r\n }\r\n}\r\n"],"names":["AI_ICON"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs.js","sources":["../../../../src/modules/ai/index.ts"],"sourcesContent":["import type TypeToolbar from 'quill/modules/toolbar'\r\nimport type FluentEditor from '../../core/fluent-editor'\r\nimport type { AIOptions, OperationMenuItem, ResultMenuItem } from './types'\r\nimport {\r\n CLOSE,\r\n INPUT_PLACEHOLDER,\r\n INSERT_SUB_CONTENT_TEXT,\r\n INSERT_TEXT,\r\n MENU_ID_MAP,\r\n MENU_TITLE_DATA,\r\n REGENERATE,\r\n REPLACE_SELECT,\r\n RESULT_HEADER_TEXT,\r\n SELECT_PLACEHOLDER,\r\n STOP_ANSWER,\r\n THINK_TEXT,\r\n} from './constants'\r\nimport {\r\n ADJUST_ICON,\r\n AI_ICON,\r\n CALL_ICON,\r\n CLOSE_ICON,\r\n COPY_ICON,\r\n EDITOR_ICON,\r\n INSERT_ICON,\r\n MENU_CLOSE_ICON,\r\n REBUILD_ICON,\r\n REFRESH_ICON,\r\n REPLACE_SELECT_ICON,\r\n RIGHT_ARROW_ICON,\r\n SEND_BTN_ICON,\r\n STOP_ICON,\r\n THINK_ICON,\r\n} from './icons'\r\n\r\nexport class AI {\r\n toolbar: TypeToolbar\r\n host: string\r\n apiKey: string\r\n model: string\r\n message: string\r\n isBreak: boolean = false // 打断标记\r\n textNumber: number // 文本字数限制\r\n private _isSelectRangeMode: boolean = false // 选择/点击模式\r\n private _charCount: number = 0 // 文本字数\r\n private _debounceTimer = null\r\n private _inputPlaceholder: string = ''\r\n private _showOperationMenu: boolean = false\r\n private _isThinking: boolean = false // 思考中\r\n private _showResultPopupEl: boolean = false // 结果弹窗\r\n selectedText: string = '' // 选择的文本\r\n inputValue: string = '' // 存储输入框的值\r\n resultMenuList: ResultMenuItem[] = []\r\n operationMenuList: OperationMenuItem[] = []\r\n private _operationMenuItemList: OperationMenuItem[] = []\r\n\r\n private alertEl: HTMLDivElement | null = null\r\n private alertTimer: number | null = null\r\n private selectionBubbleEl: HTMLDivElement | null = null\r\n private selectionRange: any = null\r\n private dialogContainerEl: HTMLDivElement | null = null\r\n private wrapContainerEl: HTMLDivElement | null = null\r\n private aiIconEl: HTMLSpanElement | null = null\r\n private inputContainerEl: HTMLDivElement | null = null\r\n private inputEl: HTMLInputElement | null = null\r\n private menuContainerEl: HTMLDivElement | null = null\r\n private subMenuEl: HTMLDivElement | null = null\r\n private subMenuEditorEl: HTMLDivElement | null = null\r\n private subMenuToneEl: HTMLDivElement | null = null\r\n private subMenuAdjustEl: HTMLDivElement | null = null\r\n private inputRightEl: HTMLDivElement | null = null\r\n private inputSendBtnEl: HTMLSpanElement | null = null\r\n private inputCloseBtnEl: HTMLSpanElement | null = null\r\n private thinkContainerEl: HTMLDivElement | null = null // 思考元素\r\n private thinkBtnEl: HTMLDivElement | null = null\r\n private resultPopupEl: HTMLDivElement | null = null\r\n private resultPopupHeaderEl: HTMLDivElement | null = null\r\n private resultPopupContentEl: HTMLDivElement | null = null\r\n private resultPopupFooterEl: HTMLDivElement | null = null\r\n private resultPopupFooterTextEl: HTMLSpanElement | null = null\r\n private resultRefreshBtnEl: HTMLSpanElement | null = null\r\n private resultCopyBtnEl: HTMLSpanElement | null = null\r\n // 分享和朗读功能待放开\r\n // private resultShareBtnEl: HTMLSpanElement | null = null\r\n // private resultVoiceBtnEl: HTMLSpanElement | null = null\r\n private actionMenuEl: HTMLDivElement | null = null\r\n\r\n constructor(\r\n public quill: FluentEditor,\r\n public options: AIOptions,\r\n ) {\r\n this.quill = quill\r\n this.toolbar = quill.getModule('toolbar') as TypeToolbar\r\n // 添加AI按钮到工具栏\r\n if (typeof this.toolbar !== 'undefined') {\r\n this.toolbar.addHandler('ai', this.showAIInput.bind(this))\r\n }\r\n\r\n this.quill.on('selection-change', this.handleSelectionChange.bind(this))\r\n\r\n this.host = options.host || 'https://api.deepseek.com/v1'\r\n this.apiKey = options.apiKey\r\n this.model = options.model || 'deepseek-chat'\r\n this.textNumber = options.contentMaxLength || 5000\r\n\r\n this.resultMenuList = [\r\n { text: REPLACE_SELECT, icon: REPLACE_SELECT_ICON },\r\n { text: INSERT_TEXT, icon: INSERT_ICON, selectText: INSERT_SUB_CONTENT_TEXT },\r\n { text: REGENERATE, icon: REBUILD_ICON },\r\n { text: CLOSE, icon: MENU_CLOSE_ICON },\r\n ]\r\n\r\n this.operationMenuList = [\r\n { id: 'editor', text: '编辑调整内容', icon: EDITOR_ICON },\r\n { id: 'tone', text: '改写口吻', icon: CALL_ICON },\r\n { id: 'adjust', text: '整理选区内容', icon: ADJUST_ICON },\r\n ]\r\n }\r\n\r\n // 工具栏启动\r\n showAIInput() {\r\n // 创建输入框和结果弹窗\r\n this.create()\r\n\r\n this.selectionRange = this.quill.getSelection()\r\n if (this.selectionRange.length) {\r\n this.isSelectRangeMode = true\r\n }\r\n else {\r\n this.isSelectRangeMode = false\r\n }\r\n // 定位到编辑器焦点位置\r\n this.positionElements()\r\n\r\n // 添加ESC键监听\r\n const handleKeyDown = (e: KeyboardEvent) => {\r\n if (e.key === 'Escape') {\r\n this.closeAIPanel()\r\n this.quill.container.removeEventListener('keydown', handleKeyDown)\r\n }\r\n }\r\n this.quill.container.addEventListener('keydown', handleKeyDown)\r\n }\r\n\r\n // 气泡启动\r\n private selectTextEvent() {\r\n if (!this.selectionRange) return\r\n this.create()\r\n // 定位到编辑器焦点位置\r\n this.positionElements()\r\n\r\n this.isSelectRangeMode = true\r\n }\r\n\r\n private create() {\r\n this.createResultElement()\r\n this.createOperationMenuElements()\r\n this.createInputBoxElements()\r\n\r\n // 创建事件监听\r\n this.addInputEvent()\r\n this.addResultEvent()\r\n this.handleActionMenuDisplay()\r\n // 添加到编辑器\r\n this.quill.container.appendChild(this.dialogContainerEl)\r\n }\r\n\r\n // 创建结果弹窗\r\n private createResultElement() {\r\n if (!this.resultPopupEl) {\r\n this.resultPopupEl = document.createElement('div')\r\n this.resultPopupEl.className = 'ql-ai-result'\r\n this.resultPopupHeaderEl = document.createElement('div')\r\n this.resultPopupHeaderEl.className = 'ql-ai-result-header'\r\n this.resultPopupHeaderEl.textContent = RESULT_HEADER_TEXT\r\n this.resultPopupContentEl = document.createElement('div')\r\n this.resultPopupContentEl.className = 'ql-ai-result-content'\r\n this.resultPopupFooterEl = document.createElement('div')\r\n this.resultPopupFooterEl.className = 'ql-ai-result-footer'\r\n this.resultPopupFooterTextEl = document.createElement('span')\r\n this.resultPopupFooterTextEl.className = 'ql-ai-result-footer-text'\r\n this.resultPopupFooterTextEl.textContent = `0`\r\n this.resultRefreshBtnEl = document.createElement('span')\r\n this.resultRefreshBtnEl.className = 'ql-ai-result-footer-refresh'\r\n this.resultRefreshBtnEl.innerHTML = REFRESH_ICON\r\n this.resultCopyBtnEl = document.createElement('span')\r\n this.resultCopyBtnEl.className = 'ql-ai-result-footer-copy'\r\n this.resultCopyBtnEl.innerHTML = COPY_ICON\r\n\r\n // 分享和朗读功能待放开\r\n // this.resultShareBtnEl = document.createElement('span')\r\n // this.resultShareBtnEl.className = 'ql-ai-result-footer-share'\r\n // this.resultShareBtnEl.innerHTML = SHARE_ICON\r\n // this.resultVoiceBtnEl = document.createElement('span')\r\n // this.resultVoiceBtnEl.className = 'ql-ai-result-footer-voice'\r\n // this.resultVoiceBtnEl.innerHTML = VOICE_ICON\r\n const resultFooterRightEl: HTMLDivElement = document.createElement('div')\r\n resultFooterRightEl.className = 'ql-ai-result-footer-right'\r\n resultFooterRightEl.appendChild(this.resultRefreshBtnEl)\r\n resultFooterRightEl.appendChild(this.resultCopyBtnEl)\r\n // 分享和朗读功能待放开\r\n // resultFooterRightEl.appendChild(this.resultShareBtnEl)\r\n // resultFooterRightEl.appendChild(this.resultVoiceBtnEl)\r\n this.resultPopupFooterEl.appendChild(this.resultPopupFooterTextEl)\r\n this.resultPopupFooterEl.appendChild(resultFooterRightEl)\r\n this.resultPopupEl.appendChild(this.resultPopupHeaderEl)\r\n this.resultPopupEl.appendChild(this.resultPopupContentEl)\r\n this.resultPopupEl.appendChild(this.resultPopupFooterEl)\r\n }\r\n this.showResultPopupEl = false\r\n }\r\n\r\n private createOperationMenuElements() {\r\n if (!this.menuContainerEl) {\r\n // 创建操作菜单容器\r\n this.menuContainerEl = document.createElement('div')\r\n this.menuContainerEl.className = 'ql-ai-menu-container'\r\n\r\n // 创建主菜单\r\n const mainMenu = document.createElement('div')\r\n mainMenu.className = 'ql-ai-main-menu'\r\n this.operationMenuList.forEach(({ text, icon, id }) => {\r\n const menuItem = document.createElement('div')\r\n menuItem.className = 'ql-ai-menu-item'\r\n menuItem.innerHTML = `${icon}<span>${text}</span>${RIGHT_ARROW_ICON}`\r\n menuItem.addEventListener('mouseenter', (e) => {\r\n e.stopPropagation()\r\n this.subMenuEl.style.display = 'block'\r\n this.subMenuEl.className = `ql-ai-sub-menu ${id}`\r\n this.createOperationMenuItem(id)\r\n })\r\n mainMenu.appendChild(menuItem)\r\n })\r\n if (!this.subMenuEl) {\r\n // 创建子菜单\r\n this.subMenuEl = document.createElement('div')\r\n this.subMenuEl.className = 'ql-ai-sub-menu'\r\n this.subMenuEl.style.display = 'none'\r\n }\r\n\r\n this.menuContainerEl.appendChild(mainMenu)\r\n this.menuContainerEl.appendChild(this.subMenuEl)\r\n }\r\n this.showOperationMenu = false\r\n }\r\n\r\n private createOperationMenuItem(id: string) {\r\n let menuItemBox = this[MENU_ID_MAP[id]]\r\n if (!menuItemBox) {\r\n menuItemBox = document.createElement('div')\r\n }\r\n // 清除子菜单容器中的所有子元素\r\n while (this.subMenuEl.firstChild) {\r\n this.subMenuEl.removeChild(this.subMenuEl.firstChild)\r\n }\r\n\r\n MENU_TITLE_DATA[id].forEach(({ text, icon, id }) => {\r\n const menuItem = document.createElement('div')\r\n menuItem.className = 'ql-ai-menu-item'\r\n menuItem.innerHTML = `${icon || ''}<span>${text}</span>`\r\n menuItem.addEventListener('click', (e) => {\r\n e.stopPropagation()\r\n this.handleOperationMenuItemClick(text, id)\r\n })\r\n menuItemBox.appendChild(menuItem)\r\n })\r\n this.subMenuEl.appendChild(menuItemBox)\r\n }\r\n\r\n private createInputBoxElements() {\r\n if (!this.dialogContainerEl) {\r\n this.dialogContainerEl = document.createElement('div')\r\n this.dialogContainerEl.className = 'ql-ai-dialog'\r\n this.wrapContainerEl = document.createElement('div')\r\n this.wrapContainerEl.className = 'ql-ai-wrapper'\r\n this.wrapContainerEl.style.width = `${this.quill.container.clientWidth - 30}px`\r\n\r\n // 添加AI图标\r\n this.createAIInputIcon()\r\n\r\n // 增加输入框\r\n this.inputEl = document.createElement('input')\r\n this.inputEl.type = 'text'\r\n this.inputPlaceholder = this._isSelectRangeMode ? SELECT_PLACEHOLDER : INPUT_PLACEHOLDER\r\n // 添加发送按钮\r\n this.inputSendBtnEl = document.createElement('span')\r\n this.inputSendBtnEl.className = 'ql-ai-input-right-send'\r\n this.inputSendBtnEl.innerHTML = SEND_BTN_ICON\r\n this.inputCloseBtnEl = document.createElement('span')\r\n this.inputCloseBtnEl.className = 'ql-ai-input-right-close'\r\n this.inputCloseBtnEl.innerHTML = CLOSE_ICON\r\n this.inputRightEl = document.createElement('div')\r\n this.inputRightEl.className = 'ql-ai-input-right'\r\n\r\n // 创建输入框\r\n this.inputContainerEl = document.createElement('div')\r\n this.inputContainerEl.className = 'ql-ai-input'\r\n this.inputContainerEl.appendChild(this.aiIconEl)\r\n this.inputContainerEl.appendChild(this.inputEl)\r\n this.inputRightEl.appendChild(this.inputSendBtnEl)\r\n this.inputRightEl.appendChild(this.inputCloseBtnEl)\r\n this.inputContainerEl.appendChild(this.inputRightEl) // 添加发送按钮\r\n this.wrapContainerEl.appendChild(this.resultPopupEl)\r\n this.wrapContainerEl.appendChild(this.inputContainerEl)\r\n this.wrapContainerEl.appendChild(this.menuContainerEl) // 添加菜单容器\r\n this.dialogContainerEl.appendChild(this.wrapContainerEl)\r\n }\r\n else {\r\n this.dialogContainerEl.style.display = 'block'\r\n }\r\n this.hiddenInputSendBtnEl()\r\n }\r\n\r\n private hiddenInputSendBtnEl(display = 'none') {\r\n if (this.inputEl && this.inputSendBtnEl) {\r\n this.inputSendBtnEl.style.display = display\r\n }\r\n }\r\n\r\n private copyResult() {\r\n if (!this.resultPopupContentEl) return\r\n\r\n try {\r\n const textToCopy = this.resultPopupContentEl.textContent || ''\r\n navigator.clipboard\r\n .writeText(textToCopy)\r\n .then(() => {\r\n this.showAlert('内容已复制到剪贴板')\r\n // 可以在这里添加复制成功的提示\r\n })\r\n .catch((err) => {\r\n this.showAlert(`复制失败:${err}`)\r\n })\r\n }\r\n catch (err) {\r\n this.showAlert(`复制失败:${err}`)\r\n // 兼容不支持clipboard API的浏览器\r\n const textarea = document.createElement('textarea')\r\n textarea.value = this.resultPopupContentEl.textContent || ''\r\n document.body.appendChild(textarea)\r\n textarea.select()\r\n document.execCommand('copy')\r\n document.body.removeChild(textarea)\r\n }\r\n }\r\n\r\n // 分享和朗读功能待放开\r\n // private shareResult() {\r\n // if (!this.resultPopupContentEl) return\r\n\r\n // const textToShare = this.resultPopupContentEl.textContent || ''\r\n // const title = 'AI生成内容分享'\r\n\r\n // if (navigator.share) {\r\n // navigator.share({\r\n // title,\r\n // text: textToShare,\r\n // })\r\n // .catch((err) => {\r\n // this.showAlert(`分享失败:${err}`)\r\n // })\r\n // }\r\n // else {\r\n // // 兼容不支持Web Share API的浏览器\r\n // const shareUrl = `mailto:?subject=${encodeURIComponent(title)}&body=${encodeURIComponent(textToShare)}`\r\n // window.open(shareUrl, '_blank')\r\n // }\r\n // }\r\n // private voiceResult() {\r\n // if (!this.resultPopupContentEl) return\r\n\r\n // const textToSpeak = this.resultPopupContentEl.textContent || ''\r\n\r\n // if ('speechSynthesis' in window) {\r\n // const utterance = new SpeechSynthesisUtterance(textToSpeak)\r\n // utterance.lang = 'zh-CN' // 设置中文语音\r\n // speechSynthesis.speak(utterance)\r\n // }\r\n // else {\r\n // this.showAlert('当前浏览器不支持语音合成API')\r\n // // 可以在这里添加不支持语音的提示\r\n // }\r\n // }\r\n\r\n private addResultEvent() {\r\n if (this.resultRefreshBtnEl) {\r\n this.resultRefreshBtnEl.addEventListener('click', () => {\r\n this.regenerateResponse()\r\n })\r\n }\r\n\r\n if (this.resultCopyBtnEl) {\r\n this.resultCopyBtnEl.addEventListener('click', () => {\r\n this.copyResult()\r\n })\r\n }\r\n\r\n // 分享和朗读功能待放开\r\n // if (this.resultShareBtnEl) {\r\n // this.resultShareBtnEl.addEventListener('click', () => {\r\n // this.shareResult()\r\n // })\r\n // }\r\n // if (this.resultVoiceBtnEl) {\r\n // this.resultVoiceBtnEl.addEventListener('click', () => {\r\n // this.voiceResult()\r\n // })\r\n // }\r\n }\r\n\r\n // 显示选中文本的气泡\r\n private showSelectionBubble() {\r\n if (!this.selectionBubbleEl) {\r\n this.selectionBubbleEl = document.createElement('div')\r\n this.selectionBubbleEl.className = 'ql-ai-selection-bubble'\r\n const icon = AI_ICON.replaceAll('paint_linear_2', 'paint_linear_bubble')\r\n this.selectionBubbleEl.innerHTML = `${icon}<span>AI 智能</span>`\r\n this.selectionBubbleEl.addEventListener('click', () => this.selectTextEvent())\r\n document.body.appendChild(this.selectionBubbleEl)\r\n }\r\n\r\n const { left, top } = this.quill.getBounds(this.selectionRange.index)\r\n const { left: endLeft } = this.quill.getBounds(this.selectionRange.index + this.selectionRange.length)\r\n const width = (endLeft - left) / 2\r\n const editorRect = this.quill.container.getBoundingClientRect()\r\n\r\n this.selectionBubbleEl.style.display = 'flex'\r\n this.selectionBubbleEl.style.left = `${left + editorRect.left + width - 45}px`\r\n this.selectionBubbleEl.style.top = `${top + editorRect.top - 40}px`\r\n }\r\n\r\n // 隐藏选中文本的气泡\r\n private hideSelectionBubble() {\r\n if (this.selectionBubbleEl) {\r\n this.selectionBubbleEl.style.display = 'none'\r\n }\r\n }\r\n\r\n // 处理文本选中变化\r\n private handleSelectionChange(range: any) {\r\n if (range && range.length > 0) {\r\n this.selectionRange = range\r\n this.showSelectionBubble()\r\n this.selectedText = this.quill.getText(range.index, range.length)\r\n }\r\n else {\r\n if (range && range.index !== null) {\r\n this.selectedText = ''\r\n this.closeAIPanel()\r\n }\r\n else {\r\n this.hideSelectionBubble()\r\n }\r\n }\r\n }\r\n\r\n private addInputEvent() {\r\n if (this.inputContainerEl) {\r\n this.inputContainerEl.addEventListener('click', () => {})\r\n }\r\n\r\n // 监听输入事件\r\n if (this.inputEl) {\r\n this.inputEl.addEventListener('input', () => {\r\n this.hiddenInputSendBtnEl(this.inputEl.value.trim() ? 'flex' : 'none')\r\n if (this.menuContainerEl && this._isSelectRangeMode) {\r\n this.showOperationMenu = !this.inputEl.value.trim() && !this._showResultPopupEl\r\n }\r\n })\r\n }\r\n\r\n // 给发送按钮添加点击事件\r\n if (this.inputSendBtnEl) {\r\n this.inputSendBtnEl.addEventListener('click', async () => {\r\n await this.queryAI()\r\n })\r\n }\r\n // 监听发送事件\r\n this.inputEl.addEventListener('keydown', async (e) => {\r\n if (e.key === 'Enter') {\r\n await this.queryAI()\r\n }\r\n })\r\n\r\n // 给关闭按钮添加点击事件\r\n if (this.inputCloseBtnEl) {\r\n this.inputCloseBtnEl.addEventListener('click', () => {\r\n this.closeAIPanel()\r\n })\r\n }\r\n }\r\n\r\n private positionElements() {\r\n if (!this.dialogContainerEl) return\r\n const range = this.selectionRange\r\n if (range) {\r\n const bounds = this.quill.getBounds(range.index)\r\n this.dialogContainerEl.style.position = 'absolute'\r\n this.dialogContainerEl.style.top = `${bounds.top + bounds.height + 20}px`\r\n }\r\n }\r\n\r\n // 添加创建alert元素的方法\r\n private createAlertElement() {\r\n if (!this.alertEl) {\r\n this.alertEl = document.createElement('div')\r\n this.alertEl.className = 'ql-ai-alert'\r\n this.alertEl.style.display = 'none'\r\n document.body.appendChild(this.alertEl)\r\n }\r\n }\r\n\r\n // 添加显示alert的方法\r\n private showAlert(message: string, duration: number = 3000) {\r\n this.createAlertElement()\r\n if (!this.alertEl) return\r\n\r\n // 清除之前的定时器\r\n if (this.alertTimer) {\r\n clearTimeout(this.alertTimer)\r\n this.alertTimer = null\r\n }\r\n\r\n this.alertEl.textContent = message\r\n this.alertEl.style.display = 'block'\r\n\r\n // 自动隐藏\r\n this.alertTimer = setTimeout(() => {\r\n if (this.alertEl) {\r\n this.alertEl.style.display = 'none'\r\n }\r\n this.alertTimer = null\r\n }, duration) as unknown as number\r\n }\r\n\r\n private createAIInputIcon() {\r\n if (!this.aiIconEl) {\r\n this.aiIconEl = document.createElement('span')\r\n this.aiIconEl.className = 'ql-ai-input-pre-icon'\r\n const icon = AI_ICON.replaceAll('paint_linear_2', 'paint_linear_ai_input')\r\n this.aiIconEl.innerHTML = icon\r\n }\r\n }\r\n\r\n // 添加处理子菜单点击的方法\r\n private handleOperationMenuItemClick(text: string, id: string = '') {\r\n let quetion = ''\r\n if (id.startsWith('1-') || id.startsWith('3-')) {\r\n quetion = `将目标文字${text},目标文字为:${this.selectedText}`\r\n }\r\n else if (id.startsWith('2-')) {\r\n quetion = `改写目标文字的口吻,让其变得${text},目标文字为:${this.selectedText}`\r\n }\r\n this.showOperationMenu = false\r\n this.queryAI(quetion)\r\n }\r\n\r\n private createActionMenu() {\r\n if (!this.actionMenuEl) {\r\n this.actionMenuEl = document.createElement('div')\r\n this.actionMenuEl.className = 'ql-ai-actions'\r\n\r\n this.resultMenuList.forEach(({ text, icon }) => {\r\n const menuItem = document.createElement('div')\r\n menuItem.className = 'ql-ai-action-item'\r\n menuItem.innerHTML = `${icon}<span class=\"ql-ai-result-menu-text\">${text}</span>`\r\n menuItem.addEventListener('click', () => this.handleAction(text))\r\n this.actionMenuEl.appendChild(menuItem)\r\n })\r\n\r\n this.wrapContainerEl.appendChild(this.actionMenuEl)\r\n }\r\n const secondMenuItemText = this.actionMenuEl.children[1].querySelector('.ql-ai-result-menu-text') as HTMLDivElement\r\n\r\n const firstChild = this.actionMenuEl.firstChild\r\n\r\n if (!this._isSelectRangeMode) {\r\n if (firstChild instanceof Element) {\r\n firstChild.classList.add('hidden')\r\n }\r\n\r\n secondMenuItemText.textContent = INSERT_TEXT\r\n }\r\n else {\r\n if (firstChild instanceof Element) {\r\n firstChild.classList.remove('hidden')\r\n }\r\n\r\n secondMenuItemText.textContent = INSERT_SUB_CONTENT_TEXT\r\n }\r\n\r\n this.isThinking = false\r\n }\r\n\r\n private handleActionMenuDisplay(value: string = 'none') {\r\n if (this.actionMenuEl) {\r\n this.actionMenuEl.style.display = value\r\n }\r\n }\r\n\r\n private switchInputEl(showInput = true) {\r\n if (this.inputContainerEl) {\r\n this.inputContainerEl.style.display = showInput ? 'flex' : 'none'\r\n }\r\n\r\n this.handleActionMenuDisplay(showInput ? 'block' : 'none')\r\n\r\n if (this.thinkContainerEl) {\r\n this.thinkContainerEl.style.display = showInput ? 'none' : 'flex'\r\n }\r\n }\r\n\r\n // 创建思考元素\r\n private createThinkElements() {\r\n if (!this.thinkContainerEl) {\r\n this.thinkContainerEl = document.createElement('div')\r\n this.thinkContainerEl.className = 'ql-ai-input'\r\n this.thinkContainerEl.innerHTML = `<span class=\"ql-ai-input-pre-icon ql-ai-think-icon\">${THINK_ICON}</span><span class=\"ql-ai-think-text\">${THINK_TEXT}</span>`\r\n this.thinkBtnEl = document.createElement('div')\r\n this.thinkBtnEl.className = 'ql-ai-think-btn'\r\n this.thinkBtnEl.innerHTML = `${STOP_ICON}<span>${STOP_ANSWER}</span>`\r\n this.thinkContainerEl.appendChild(this.thinkBtnEl)\r\n this.wrapContainerEl.appendChild(this.thinkContainerEl) // 添加发送按钮\r\n this.thinkBtnEl.addEventListener('click', () => {\r\n this.isBreak = true\r\n this.isThinking = false\r\n })\r\n }\r\n\r\n this.isThinking = true\r\n }\r\n\r\n // AI查询\r\n private async queryAI(question?: string): Promise<string> {\r\n this.createThinkElements()\r\n this.inputValue = question || this.inputEl.value\r\n if (this.inputValue.trim() === '') {\r\n return\r\n }\r\n\r\n // 有信息\r\n this.isBreak = false // 重置打断标记,防止重复打断ai\r\n // 这里实现实际的AI查询逻辑\r\n try {\r\n const response = await fetch(`${this.host}`, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n 'Authorization': `Bearer ${this.apiKey}`,\r\n },\r\n body: JSON.stringify({\r\n model: this.model,\r\n prompt: this.inputValue,\r\n stream: true,\r\n }),\r\n })\r\n\r\n if (!response.ok) {\r\n throw new Error(`HTTP error! status: ${response.status}`)\r\n }\r\n\r\n const reader = response.body.getReader()\r\n const decoder = new TextDecoder()\r\n let content = ''\r\n\r\n while (true) {\r\n if (this.isBreak) {\r\n this.isBreak = false\r\n break\r\n }\r\n\r\n const { done, value } = await reader.read()\r\n if (done) break\r\n\r\n const chunk = decoder.decode(value)\r\n const lines = chunk.split('\\n').filter(line => line.trim() !== '')\r\n\r\n for (const line of lines) {\r\n try {\r\n const data = JSON.parse(line)\r\n content += data.response || ''\r\n this.showAIResponse(content)\r\n }\r\n catch (e) {\r\n console.error('解析错误:', e)\r\n }\r\n }\r\n }\r\n\r\n // 创建操作菜单\r\n this.createActionMenu()\r\n this.inputEl.value = '' // 清空输入框\r\n this.hiddenInputSendBtnEl()\r\n return content\r\n }\r\n catch (error) {\r\n console.error('AI查询失败:', error)\r\n return 'AI查询失败,请重试'\r\n }\r\n }\r\n\r\n private showAIResponse(response: string) {\r\n if (!this.resultPopupEl) return\r\n\r\n // 显示结果\r\n if (this._charCount <= this.textNumber) {\r\n this.resultPopupContentEl.innerHTML = response\r\n this.charCount = this.resultPopupContentEl.textContent.replace(/\\s+/g, '').length\r\n }\r\n else {\r\n this.isBreak = true\r\n this.charCount = 0\r\n }\r\n this.showResultPopupEl = true\r\n }\r\n\r\n private handleAction(action: string) {\r\n switch (action) {\r\n case REPLACE_SELECT:\r\n this.replaceSelectText()\r\n break\r\n case INSERT_TEXT:\r\n this.insertAIResponse()\r\n break\r\n case REGENERATE:\r\n this.regenerateResponse()\r\n break\r\n case CLOSE:\r\n this.closeAIPanel()\r\n break\r\n }\r\n }\r\n\r\n private replaceSelectText() {\r\n if (!this.resultPopupContentEl) return\r\n const range = this.quill.getSelection(true)\r\n if (range && range.length > 0) {\r\n // 删除选中内容\r\n this.quill.deleteText(range.index, range.length)\r\n // 插入AI生成的内容\r\n this.quill.clipboard.dangerouslyPasteHTML(range.index, this.resultPopupContentEl.innerHTML)\r\n }\r\n this.closeAIPanel()\r\n }\r\n\r\n private insertAIResponse() {\r\n if (!this.resultPopupContentEl) return\r\n const range = this.quill.getSelection(true)\r\n if (range) {\r\n this.quill.clipboard.dangerouslyPasteHTML(range.index + range.length, this.resultPopupContentEl.innerHTML)\r\n }\r\n this.closeAIPanel()\r\n }\r\n\r\n private async regenerateResponse() {\r\n await this.queryAI(this.inputValue)\r\n }\r\n\r\n private closeAIPanel() {\r\n this.isBreak = true // 停止查询\r\n\r\n if (this.dialogContainerEl) {\r\n this.dialogContainerEl.style.display = 'none'\r\n }\r\n\r\n if (this.actionMenuEl) {\r\n this.actionMenuEl.style.display = 'none'\r\n }\r\n\r\n this.showResultPopupEl = false\r\n\r\n if (this.inputEl && this.inputEl.value.trim() !== '') {\r\n this.inputEl.value = '' // 清空输入框\r\n }\r\n this.hideSelectionBubble()\r\n }\r\n\r\n set charCount(value: number) {\r\n // 清除之前的定时器\r\n if (this._debounceTimer) {\r\n clearTimeout(this._debounceTimer)\r\n }\r\n\r\n this._debounceTimer = setTimeout(() => {\r\n this._charCount = value\r\n if (this.resultPopupFooterTextEl) {\r\n this.resultPopupFooterTextEl.textContent = `${this._charCount}/${this.textNumber}`\r\n }\r\n clearTimeout(this._debounceTimer)\r\n this._debounceTimer = null\r\n }, 210)\r\n }\r\n\r\n get charCount() {\r\n return this._charCount\r\n }\r\n\r\n set inputPlaceholder(value: string) {\r\n this._inputPlaceholder = value\r\n if (this.inputEl) {\r\n this.inputEl.placeholder = value\r\n }\r\n }\r\n\r\n get inputPlaceholder() {\r\n return this._inputPlaceholder\r\n }\r\n\r\n set showOperationMenu(value: boolean) {\r\n this._showOperationMenu = value\r\n if (this.menuContainerEl) {\r\n this.menuContainerEl.style.display = value ? 'flex' : 'none'\r\n }\r\n }\r\n\r\n get showOperationMenu() {\r\n return this._showOperationMenu\r\n }\r\n\r\n set isSelectRangeMode(value: boolean) {\r\n this._isSelectRangeMode = value\r\n this.showOperationMenu = value\r\n this.inputPlaceholder = value ? SELECT_PLACEHOLDER : INPUT_PLACEHOLDER\r\n this.hideSelectionBubble()\r\n }\r\n\r\n get isSelectRangeMode() {\r\n return this._isSelectRangeMode\r\n }\r\n\r\n set isThinking(value: boolean) {\r\n this._isThinking = value\r\n this.switchInputEl(!value)\r\n }\r\n\r\n get isThinking() {\r\n return this._isThinking\r\n }\r\n\r\n set showResultPopupEl(value: boolean) {\r\n this._showResultPopupEl = value\r\n if (this.resultPopupEl) {\r\n this.resultPopupEl.style.display = value ? 'block' : 'none'\r\n }\r\n }\r\n\r\n get showResultPopupEl() {\r\n return this._showResultPopupEl\r\n }\r\n}\r\n"],"names":["REPLACE_SELECT","REPLACE_SELECT_ICON","INSERT_TEXT","INSERT_ICON","INSERT_SUB_CONTENT_TEXT","REGENERATE","REBUILD_ICON","CLOSE","MENU_CLOSE_ICON","EDITOR_ICON","CALL_ICON","ADJUST_ICON","RESULT_HEADER_TEXT","REFRESH_ICON","COPY_ICON","RIGHT_ARROW_ICON","MENU_ID_MAP","MENU_TITLE_DATA","id","SELECT_PLACEHOLDER","INPUT_PLACEHOLDER","SEND_BTN_ICON","CLOSE_ICON","AI_ICON","THINK_ICON","THINK_TEXT","STOP_ICON","STOP_ANSWER"],"mappings":"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"}
@@ -23,6 +23,10 @@ class BlotFormatter {
23
23
  });
24
24
  this.quill = quill;
25
25
  this.options = lodashEs.merge({}, options.default, options$1, { arrayMerge: dontMerge });
26
+ if (options$1.allowInvalidUrl !== void 0) {
27
+ this.options.allowInvalidUrl = options$1.allowInvalidUrl;
28
+ }
29
+ image.CustomImage.setOptions(this.options.allowInvalidUrl);
26
30
  this.currentSpec = null;
27
31
  this.actions = [];
28
32
  this.overlay = document.createElement("div");
@@ -1 +1 @@
1
- {"version":3,"file":"blot-formatter.cjs.js","sources":["../../../../src/modules/custom-image/blot-formatter.ts"],"sourcesContent":["import type FluentEditor from '../../core/fluent-editor'\r\nimport type { Action } from './actions'\r\nimport type { BlotFormatterOptions } from './options'\r\nimport type { BlotSpec } from './specs'\r\nimport { merge as deepmerge } from 'lodash-es'\r\nimport Quill from 'quill'\r\nimport { CustomImage } from './image'\r\nimport DefaultOptions from './options'\r\nimport { CustomImageSpec } from './specs'\r\n\r\nconst dontMerge = (_destination: Array<any>, source: Array<any>) => source\r\n\r\nexport class BlotFormatter {\r\n options: BlotFormatterOptions\r\n currentSpec: BlotSpec\r\n specs: BlotSpec[]\r\n overlay: HTMLElement\r\n actions: Action[]\r\n observer: MutationObserver\r\n\r\n static register() {\r\n Quill.register({\r\n 'formats/image': CustomImage,\r\n 'modules/image-spec': CustomImageSpec,\r\n }, true)\r\n }\r\n\r\n constructor(public quill: FluentEditor, options: Partial<BlotFormatterOptions> = {}) {\r\n this.options = deepmerge({}, DefaultOptions, options, { arrayMerge: dontMerge })\r\n this.currentSpec = null\r\n this.actions = []\r\n this.overlay = document.createElement('div')\r\n this.overlay.classList.add(this.options.overlay.className)\r\n if (this.options.overlay.style) {\r\n Object.assign(this.overlay.style, this.options.overlay.style)\r\n }\r\n\r\n // disable native image resizing on firefox\r\n document.execCommand('enableObjectResizing', false, 'false') // eslint-disable-next-line-line no-undef\r\n this.quill.root.addEventListener('click', this.onClick)\r\n this.specs = this.options.specs.map((SpecClass: any) => new SpecClass(this))\r\n this.specs.forEach(spec => spec.init())\r\n }\r\n\r\n show(spec: BlotSpec) {\r\n this.currentSpec = spec\r\n this.currentSpec.setSelection()\r\n this.setUserSelect('none')\r\n this.quill.root.parentNode.appendChild(this.overlay)\r\n this.repositionOverlay()\r\n this.createActions(spec)\r\n\r\n // fix: 图片对齐之后,虚线外框应该跟随移动\r\n const imageDom = spec.getTargetElement()\r\n const win: any = window\r\n const MutationObserver: typeof window.MutationObserver = win.MutationObserver || win.WebKitMutationObserver || win.MozMutationObserver\r\n const element = imageDom.parentNode\r\n this.observer = new MutationObserver((mutationList) => {\r\n for (const mutation of mutationList) {\r\n const target = mutation.target as HTMLElement\r\n const image = target.querySelector('img')\r\n if (image) {\r\n this.repositionOverlay()\r\n }\r\n }\r\n })\r\n this.observer.observe(element, {\r\n attributes: true,\r\n attributeFilter: ['class'],\r\n attributeOldValue: true,\r\n subtree: true,\r\n })\r\n }\r\n\r\n hide() {\r\n if (!this.currentSpec) {\r\n return\r\n }\r\n\r\n const imgDom = this.currentSpec.getTargetElement()\r\n if (imgDom) {\r\n imgDom.classList.remove('current-select-img')\r\n }\r\n\r\n this.currentSpec.onHide()\r\n this.currentSpec = null\r\n this.quill.root.parentNode.removeChild(this.overlay)\r\n this.overlay.style.setProperty('display', 'none')\r\n this.setUserSelect('')\r\n this.destroyActions()\r\n }\r\n\r\n update() {\r\n this.repositionOverlay()\r\n this.actions.forEach(action => action.onUpdate())\r\n }\r\n\r\n createActions(spec: BlotSpec) {\r\n this.actions = spec.getActions().map((ActionClass: any) => {\r\n const action: Action = new ActionClass(this)\r\n action.onCreate()\r\n return action\r\n })\r\n }\r\n\r\n destroyActions() {\r\n this.actions.forEach((action: Action) => action.onDestroy())\r\n this.actions = []\r\n }\r\n\r\n repositionOverlay() {\r\n if (!this.currentSpec) {\r\n return\r\n }\r\n\r\n const overlayTarget = this.currentSpec.getOverlayElement()\r\n if (!overlayTarget) {\r\n return\r\n }\r\n\r\n const parent = this.quill.root.parentElement\r\n const specRect = overlayTarget.getBoundingClientRect()\r\n const parentRect = parent.getBoundingClientRect()\r\n\r\n Object.assign(this.overlay.style, {\r\n display: 'block',\r\n left: `${specRect.left - parentRect.left - 1 + parent.scrollLeft}px`,\r\n top: `${specRect.top - parentRect.top + parent.scrollTop}px`,\r\n width: `${specRect.width}px`,\r\n height: `${specRect.height}px`,\r\n })\r\n }\r\n\r\n setUserSelect(value: string) {\r\n const props: string[] = [\r\n 'userSelect',\r\n 'mozUserSelect',\r\n 'webkitUserSelect',\r\n 'msUserSelect',\r\n ]\r\n\r\n props.forEach((prop: string) => {\r\n // set on contenteditable element and <html>\r\n this.quill.root.style.setProperty(prop, value)\r\n if (document.documentElement) {\r\n document.documentElement.style.setProperty(prop, value)\r\n }\r\n })\r\n }\r\n\r\n onClick = () => {\r\n this.hide()\r\n }\r\n}\r\n"],"names":["options","deepmerge","DefaultOptions","CustomImage","CustomImageSpec","image"],"mappings":"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"}
1
+ {"version":3,"file":"blot-formatter.cjs.js","sources":["../../../../src/modules/custom-image/blot-formatter.ts"],"sourcesContent":["import type FluentEditor from '../../core/fluent-editor'\r\nimport type { Action } from './actions'\r\nimport type { BlotFormatterOptions } from './options'\r\nimport type { BlotSpec } from './specs'\r\nimport { merge as deepmerge } from 'lodash-es'\r\nimport Quill from 'quill'\r\nimport { CustomImage } from './image'\r\nimport DefaultOptions from './options'\r\nimport { CustomImageSpec } from './specs'\r\n\r\nconst dontMerge = (_destination: Array<any>, source: Array<any>) => source\r\n\r\nexport class BlotFormatter {\r\n options: BlotFormatterOptions\r\n currentSpec: BlotSpec\r\n specs: BlotSpec[]\r\n overlay: HTMLElement\r\n actions: Action[]\r\n observer: MutationObserver\r\n\r\n static register() {\r\n Quill.register({\r\n 'formats/image': CustomImage,\r\n 'modules/image-spec': CustomImageSpec,\r\n }, true)\r\n }\r\n\r\n constructor(public quill: FluentEditor, options: Partial<BlotFormatterOptions> = {}) {\r\n this.options = deepmerge({}, DefaultOptions, options, { arrayMerge: dontMerge })\r\n if (options.allowInvalidUrl !== undefined) {\r\n this.options.allowInvalidUrl = options.allowInvalidUrl\r\n }\r\n CustomImage.setOptions(this.options.allowInvalidUrl)\r\n this.currentSpec = null\r\n this.actions = []\r\n this.overlay = document.createElement('div')\r\n this.overlay.classList.add(this.options.overlay.className)\r\n if (this.options.overlay.style) {\r\n Object.assign(this.overlay.style, this.options.overlay.style)\r\n }\r\n\r\n // disable native image resizing on firefox\r\n document.execCommand('enableObjectResizing', false, 'false') // eslint-disable-next-line-line no-undef\r\n this.quill.root.addEventListener('click', this.onClick)\r\n this.specs = this.options.specs.map((SpecClass: any) => new SpecClass(this))\r\n this.specs.forEach(spec => spec.init())\r\n }\r\n\r\n show(spec: BlotSpec) {\r\n this.currentSpec = spec\r\n this.currentSpec.setSelection()\r\n this.setUserSelect('none')\r\n this.quill.root.parentNode.appendChild(this.overlay)\r\n this.repositionOverlay()\r\n this.createActions(spec)\r\n\r\n // fix: 图片对齐之后,虚线外框应该跟随移动\r\n const imageDom = spec.getTargetElement()\r\n const win: any = window\r\n const MutationObserver: typeof window.MutationObserver = win.MutationObserver || win.WebKitMutationObserver || win.MozMutationObserver\r\n const element = imageDom.parentNode\r\n this.observer = new MutationObserver((mutationList) => {\r\n for (const mutation of mutationList) {\r\n const target = mutation.target as HTMLElement\r\n const image = target.querySelector('img')\r\n if (image) {\r\n this.repositionOverlay()\r\n }\r\n }\r\n })\r\n this.observer.observe(element, {\r\n attributes: true,\r\n attributeFilter: ['class'],\r\n attributeOldValue: true,\r\n subtree: true,\r\n })\r\n }\r\n\r\n hide() {\r\n if (!this.currentSpec) {\r\n return\r\n }\r\n\r\n const imgDom = this.currentSpec.getTargetElement()\r\n if (imgDom) {\r\n imgDom.classList.remove('current-select-img')\r\n }\r\n\r\n this.currentSpec.onHide()\r\n this.currentSpec = null\r\n this.quill.root.parentNode.removeChild(this.overlay)\r\n this.overlay.style.setProperty('display', 'none')\r\n this.setUserSelect('')\r\n this.destroyActions()\r\n }\r\n\r\n update() {\r\n this.repositionOverlay()\r\n this.actions.forEach(action => action.onUpdate())\r\n }\r\n\r\n createActions(spec: BlotSpec) {\r\n this.actions = spec.getActions().map((ActionClass: any) => {\r\n const action: Action = new ActionClass(this)\r\n action.onCreate()\r\n return action\r\n })\r\n }\r\n\r\n destroyActions() {\r\n this.actions.forEach((action: Action) => action.onDestroy())\r\n this.actions = []\r\n }\r\n\r\n repositionOverlay() {\r\n if (!this.currentSpec) {\r\n return\r\n }\r\n\r\n const overlayTarget = this.currentSpec.getOverlayElement()\r\n if (!overlayTarget) {\r\n return\r\n }\r\n\r\n const parent = this.quill.root.parentElement\r\n const specRect = overlayTarget.getBoundingClientRect()\r\n const parentRect = parent.getBoundingClientRect()\r\n\r\n Object.assign(this.overlay.style, {\r\n display: 'block',\r\n left: `${specRect.left - parentRect.left - 1 + parent.scrollLeft}px`,\r\n top: `${specRect.top - parentRect.top + parent.scrollTop}px`,\r\n width: `${specRect.width}px`,\r\n height: `${specRect.height}px`,\r\n })\r\n }\r\n\r\n setUserSelect(value: string) {\r\n const props: string[] = [\r\n 'userSelect',\r\n 'mozUserSelect',\r\n 'webkitUserSelect',\r\n 'msUserSelect',\r\n ]\r\n\r\n props.forEach((prop: string) => {\r\n // set on contenteditable element and <html>\r\n this.quill.root.style.setProperty(prop, value)\r\n if (document.documentElement) {\r\n document.documentElement.style.setProperty(prop, value)\r\n }\r\n })\r\n }\r\n\r\n onClick = () => {\r\n this.hide()\r\n }\r\n}\r\n"],"names":["options","deepmerge","DefaultOptions","CustomImage","CustomImageSpec","image"],"mappings":"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"}
@@ -11,6 +11,9 @@ const imageToolbarButtons = require("./actions/image-toolbar-buttons.cjs.js");
11
11
  const Embed = Quill.import("blots/embed");
12
12
  const ATTRIBUTES = ["alt", "height", "width", "image-id"];
13
13
  const _CustomImage = class _CustomImage extends Embed {
14
+ static setOptions(allowInvalidUrl) {
15
+ this.allowInvalidUrl = allowInvalidUrl;
16
+ }
14
17
  static create(value) {
15
18
  const node = super.create(value);
16
19
  const url = typeof value === "string" ? value : value.src;
@@ -56,7 +59,10 @@ const _CustomImage = class _CustomImage extends Embed {
56
59
  }
57
60
  }
58
61
  static sanitize(url) {
59
- return editor_utils.sanitize(url, ["http", "https", "blob", "data"]) ? url : "//:0";
62
+ if (editor_utils.sanitize(url, ["http", "https", "blob", "data"]) || this.allowInvalidUrl) {
63
+ return url;
64
+ }
65
+ return "//:0";
60
66
  }
61
67
  static value(domNode) {
62
68
  const formats = {};
@@ -103,6 +109,7 @@ const _CustomImage = class _CustomImage extends Embed {
103
109
  __publicField(_CustomImage, "blotName", "image");
104
110
  __publicField(_CustomImage, "tagName", "IMG");
105
111
  __publicField(_CustomImage, "ID_SEED", 0);
112
+ __publicField(_CustomImage, "allowInvalidUrl", false);
106
113
  let CustomImage = _CustomImage;
107
114
  exports.CustomImage = CustomImage;
108
115
  //# sourceMappingURL=image.cjs.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"image.cjs.js","sources":["../../../../src/modules/custom-image/image.ts"],"sourcesContent":["import type { Parchment as TypeParchment } from 'quill'\r\nimport type TypeEmbed from 'quill/blots/embed'\r\nimport Quill from 'quill'\r\nimport { isNullOrUndefined, sanitize } from '../../config/editor.utils'\r\nimport { isObject } from '../../utils/is'\r\nimport { ALIGN_ATTR, alignmentHandler } from './actions'\r\n\r\nconst Embed = Quill.import('blots/embed') as typeof TypeEmbed\r\nconst ATTRIBUTES = ['alt', 'height', 'width', 'image-id']\r\n\r\nexport type ImageValue = string | {\r\n src: string\r\n align?: string\r\n height?: number\r\n width?: number\r\n}\r\nexport class CustomImage extends Embed {\r\n static blotName = 'image'\r\n static tagName = 'IMG'\r\n static ID_SEED = 0\r\n declare domNode: HTMLElement\r\n static create(value: ImageValue) {\r\n const node = super.create(value) as HTMLElement\r\n const url = typeof value === 'string' ? value : value.src\r\n if (url) {\r\n const imgURL = this.sanitize(url)\r\n if (!imgURL?.startsWith('data:image')) {\r\n node.dataset.src = imgURL\r\n }\r\n node.setAttribute('src', imgURL)\r\n }\r\n node.setAttribute('data-image-id', `img${CustomImage.ID_SEED++}`)\r\n node.style.verticalAlign = 'baseline'\r\n if (isObject(value)) {\r\n if (value.align && alignmentHandler[value.align]) {\r\n node.setAttribute(ALIGN_ATTR, value.align)\r\n alignmentHandler[value.align](node)\r\n }\r\n if (value.width) {\r\n node.setAttribute('width', String(value.width))\r\n }\r\n if (value.height) {\r\n node.setAttribute('height', String(value.height))\r\n }\r\n }\r\n return node\r\n }\r\n\r\n static formats(domNode: HTMLElement) {\r\n return ATTRIBUTES.reduce((formats, attribute) => {\r\n if (domNode.hasAttribute(attribute)) {\r\n formats[attribute] = domNode.getAttribute(attribute)\r\n }\r\n return formats\r\n }, {})\r\n }\r\n\r\n static match(url: string) {\r\n return /\\.(jpe?g|gif|png)$/.test(url) || /^data:image\\/.+;base64/.test(url)\r\n }\r\n\r\n static register() {\r\n if (/Firefox/i.test(navigator.userAgent)) {\r\n setTimeout(() => {\r\n // Disable image resizing in Firefox\r\n document.execCommand('enableObjectResizing', false, 'false')\r\n }, 1)\r\n }\r\n }\r\n\r\n static sanitize(url: string) {\r\n return sanitize(url, ['http', 'https', 'blob', 'data']) ? url : '//:0'\r\n }\r\n\r\n static value(domNode: HTMLElement) {\r\n const formats: any = {}\r\n const imageSrc = domNode.getAttribute('src')\r\n formats.src = this.sanitize(imageSrc)\r\n formats.imageId = domNode.dataset.imageId\r\n if (domNode.dataset.align) {\r\n formats.align = domNode.dataset.align\r\n }\r\n if (domNode.hasAttribute('width')) {\r\n formats.width = domNode.getAttribute('width')\r\n }\r\n if (domNode.hasAttribute('height')) {\r\n formats.height = domNode.getAttribute('height')\r\n }\r\n return formats\r\n }\r\n\r\n format(name: string, value: any) {\r\n if (ATTRIBUTES.includes(name)) {\r\n if (value) {\r\n this.domNode.setAttribute(name, value)\r\n }\r\n else {\r\n this.domNode.removeAttribute(name)\r\n }\r\n }\r\n else {\r\n super.format(name, value)\r\n }\r\n }\r\n\r\n unWrap() {\r\n this.parent.replaceWith(this as TypeEmbed)\r\n }\r\n\r\n wrap(name: string, value?: any): TypeParchment.Parent\r\n wrap(wrapper: TypeParchment.Parent): TypeParchment.Parent\r\n wrap(name: string | TypeParchment.Parent, value?: any) {\r\n const wrapper = (typeof name === 'string' ? this.scroll.create(name, value) : name) as TypeParchment.Parent\r\n if (!isNullOrUndefined(this.parent)) {\r\n this.parent.insertBefore(wrapper, this.next || undefined)\r\n }\r\n if (typeof wrapper.appendChild !== 'function') {\r\n throw new TypeError(`Cannot wrap ${name}`)\r\n }\r\n wrapper.appendChild(this)\r\n return wrapper\r\n }\r\n}\r\n"],"names":["isObject","alignmentHandler","ALIGN_ATTR","sanitize","isNullOrUndefined"],"mappings":"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"}
1
+ {"version":3,"file":"image.cjs.js","sources":["../../../../src/modules/custom-image/image.ts"],"sourcesContent":["import type { Parchment as TypeParchment } from 'quill'\r\nimport type TypeEmbed from 'quill/blots/embed'\r\nimport Quill from 'quill'\r\nimport { isNullOrUndefined, sanitize } from '../../config/editor.utils'\r\nimport { isObject } from '../../utils/is'\r\nimport { ALIGN_ATTR, alignmentHandler } from './actions'\r\n\r\nconst Embed = Quill.import('blots/embed') as typeof TypeEmbed\r\nconst ATTRIBUTES = ['alt', 'height', 'width', 'image-id']\r\n\r\nexport type ImageValue = string | {\r\n src: string\r\n align?: string\r\n height?: number\r\n width?: number\r\n}\r\nexport class CustomImage extends Embed {\r\n static blotName = 'image'\r\n static tagName = 'IMG'\r\n static ID_SEED = 0\r\n static allowInvalidUrl: boolean = false\r\n declare domNode: HTMLElement\r\n\r\n static setOptions(allowInvalidUrl: boolean) {\r\n this.allowInvalidUrl = allowInvalidUrl\r\n }\r\n\r\n static create(value: ImageValue) {\r\n const node = super.create(value) as HTMLElement\r\n const url = typeof value === 'string' ? value : value.src\r\n if (url) {\r\n const imgURL = this.sanitize(url)\r\n if (!imgURL?.startsWith('data:image')) {\r\n node.dataset.src = imgURL\r\n }\r\n node.setAttribute('src', imgURL)\r\n }\r\n node.setAttribute('data-image-id', `img${CustomImage.ID_SEED++}`)\r\n node.style.verticalAlign = 'baseline'\r\n if (isObject(value)) {\r\n if (value.align && alignmentHandler[value.align]) {\r\n node.setAttribute(ALIGN_ATTR, value.align)\r\n alignmentHandler[value.align](node)\r\n }\r\n if (value.width) {\r\n node.setAttribute('width', String(value.width))\r\n }\r\n if (value.height) {\r\n node.setAttribute('height', String(value.height))\r\n }\r\n }\r\n return node\r\n }\r\n\r\n static formats(domNode: HTMLElement) {\r\n return ATTRIBUTES.reduce((formats, attribute) => {\r\n if (domNode.hasAttribute(attribute)) {\r\n formats[attribute] = domNode.getAttribute(attribute)\r\n }\r\n return formats\r\n }, {})\r\n }\r\n\r\n static match(url: string) {\r\n return /\\.(jpe?g|gif|png)$/.test(url) || /^data:image\\/.+;base64/.test(url)\r\n }\r\n\r\n static register() {\r\n if (/Firefox/i.test(navigator.userAgent)) {\r\n setTimeout(() => {\r\n // Disable image resizing in Firefox\r\n document.execCommand('enableObjectResizing', false, 'false')\r\n }, 1)\r\n }\r\n }\r\n\r\n static sanitize(url: string) {\r\n if (sanitize(url, ['http', 'https', 'blob', 'data']) || this.allowInvalidUrl) {\r\n return url\r\n }\r\n return '//:0'\r\n }\r\n\r\n static value(domNode: HTMLElement) {\r\n const formats: any = {}\r\n const imageSrc = domNode.getAttribute('src')\r\n formats.src = this.sanitize(imageSrc)\r\n formats.imageId = domNode.dataset.imageId\r\n if (domNode.dataset.align) {\r\n formats.align = domNode.dataset.align\r\n }\r\n if (domNode.hasAttribute('width')) {\r\n formats.width = domNode.getAttribute('width')\r\n }\r\n if (domNode.hasAttribute('height')) {\r\n formats.height = domNode.getAttribute('height')\r\n }\r\n return formats\r\n }\r\n\r\n format(name: string, value: any) {\r\n if (ATTRIBUTES.includes(name)) {\r\n if (value) {\r\n this.domNode.setAttribute(name, value)\r\n }\r\n else {\r\n this.domNode.removeAttribute(name)\r\n }\r\n }\r\n else {\r\n super.format(name, value)\r\n }\r\n }\r\n\r\n unWrap() {\r\n this.parent.replaceWith(this as TypeEmbed)\r\n }\r\n\r\n wrap(name: string, value?: any): TypeParchment.Parent\r\n wrap(wrapper: TypeParchment.Parent): TypeParchment.Parent\r\n wrap(name: string | TypeParchment.Parent, value?: any) {\r\n const wrapper = (typeof name === 'string' ? this.scroll.create(name, value) : name) as TypeParchment.Parent\r\n if (!isNullOrUndefined(this.parent)) {\r\n this.parent.insertBefore(wrapper, this.next || undefined)\r\n }\r\n if (typeof wrapper.appendChild !== 'function') {\r\n throw new TypeError(`Cannot wrap ${name}`)\r\n }\r\n wrapper.appendChild(this)\r\n return wrapper\r\n }\r\n}\r\n"],"names":["isObject","alignmentHandler","ALIGN_ATTR","sanitize","isNullOrUndefined"],"mappings":";;;;;;;;;;AAOA,MAAM,QAAQ,MAAM,OAAO,aAAa;AACxC,MAAM,aAAa,CAAC,OAAO,UAAU,SAAS,UAAU;AAQjD,MAAM,eAAN,MAAM,qBAAoB,MAAM;AAAA,EAOrC,OAAO,WAAW,iBAA0B;AAC1C,SAAK,kBAAkB;AAAA,EAAA;AAAA,EAGzB,OAAO,OAAO,OAAmB;AACzB,UAAA,OAAO,MAAM,OAAO,KAAK;AAC/B,UAAM,MAAM,OAAO,UAAU,WAAW,QAAQ,MAAM;AACtD,QAAI,KAAK;AACD,YAAA,SAAS,KAAK,SAAS,GAAG;AAChC,UAAI,EAAC,iCAAQ,WAAW,gBAAe;AACrC,aAAK,QAAQ,MAAM;AAAA,MAAA;AAEhB,WAAA,aAAa,OAAO,MAAM;AAAA,IAAA;AAEjC,SAAK,aAAa,iBAAiB,MAAM,aAAY,SAAS,EAAE;AAChE,SAAK,MAAM,gBAAgB;AACvB,QAAAA,GAAAA,SAAS,KAAK,GAAG;AACnB,UAAI,MAAM,SAASC,oBAAiB,iBAAA,MAAM,KAAK,GAAG;AAC3C,aAAA,aAAaC,gCAAY,MAAM,KAAK;AACxBD,4BAAAA,iBAAA,MAAM,KAAK,EAAE,IAAI;AAAA,MAAA;AAEpC,UAAI,MAAM,OAAO;AACf,aAAK,aAAa,SAAS,OAAO,MAAM,KAAK,CAAC;AAAA,MAAA;AAEhD,UAAI,MAAM,QAAQ;AAChB,aAAK,aAAa,UAAU,OAAO,MAAM,MAAM,CAAC;AAAA,MAAA;AAAA,IAClD;AAEK,WAAA;AAAA,EAAA;AAAA,EAGT,OAAO,QAAQ,SAAsB;AACnC,WAAO,WAAW,OAAO,CAAC,SAAS,cAAc;AAC3C,UAAA,QAAQ,aAAa,SAAS,GAAG;AACnC,gBAAQ,SAAS,IAAI,QAAQ,aAAa,SAAS;AAAA,MAAA;AAE9C,aAAA;AAAA,IACT,GAAG,EAAE;AAAA,EAAA;AAAA,EAGP,OAAO,MAAM,KAAa;AACxB,WAAO,qBAAqB,KAAK,GAAG,KAAK,yBAAyB,KAAK,GAAG;AAAA,EAAA;AAAA,EAG5E,OAAO,WAAW;AAChB,QAAI,WAAW,KAAK,UAAU,SAAS,GAAG;AACxC,iBAAW,MAAM;AAEN,iBAAA,YAAY,wBAAwB,OAAO,OAAO;AAAA,SAC1D,CAAC;AAAA,IAAA;AAAA,EACN;AAAA,EAGF,OAAO,SAAS,KAAa;AACvB,QAAAE,aAAA,SAAS,KAAK,CAAC,QAAQ,SAAS,QAAQ,MAAM,CAAC,KAAK,KAAK,iBAAiB;AACrE,aAAA;AAAA,IAAA;AAEF,WAAA;AAAA,EAAA;AAAA,EAGT,OAAO,MAAM,SAAsB;AACjC,UAAM,UAAe,CAAC;AAChB,UAAA,WAAW,QAAQ,aAAa,KAAK;AACnC,YAAA,MAAM,KAAK,SAAS,QAAQ;AAC5B,YAAA,UAAU,QAAQ,QAAQ;AAC9B,QAAA,QAAQ,QAAQ,OAAO;AACjB,cAAA,QAAQ,QAAQ,QAAQ;AAAA,IAAA;AAE9B,QAAA,QAAQ,aAAa,OAAO,GAAG;AACzB,cAAA,QAAQ,QAAQ,aAAa,OAAO;AAAA,IAAA;AAE1C,QAAA,QAAQ,aAAa,QAAQ,GAAG;AAC1B,cAAA,SAAS,QAAQ,aAAa,QAAQ;AAAA,IAAA;AAEzC,WAAA;AAAA,EAAA;AAAA,EAGT,OAAO,MAAc,OAAY;AAC3B,QAAA,WAAW,SAAS,IAAI,GAAG;AAC7B,UAAI,OAAO;AACJ,aAAA,QAAQ,aAAa,MAAM,KAAK;AAAA,MAAA,OAElC;AACE,aAAA,QAAQ,gBAAgB,IAAI;AAAA,MAAA;AAAA,IACnC,OAEG;AACG,YAAA,OAAO,MAAM,KAAK;AAAA,IAAA;AAAA,EAC1B;AAAA,EAGF,SAAS;AACF,SAAA,OAAO,YAAY,IAAiB;AAAA,EAAA;AAAA,EAK3C,KAAK,MAAqC,OAAa;AAC/C,UAAA,UAAW,OAAO,SAAS,WAAW,KAAK,OAAO,OAAO,MAAM,KAAK,IAAI;AAC9E,QAAI,CAACC,aAAA,kBAAkB,KAAK,MAAM,GAAG;AACnC,WAAK,OAAO,aAAa,SAAS,KAAK,QAAQ,MAAS;AAAA,IAAA;AAEtD,QAAA,OAAO,QAAQ,gBAAgB,YAAY;AAC7C,YAAM,IAAI,UAAU,eAAe,IAAI,EAAE;AAAA,IAAA;AAE3C,YAAQ,YAAY,IAAI;AACjB,WAAA;AAAA,EAAA;AAEX;AAlHE,cADW,cACJ,YAAW;AAClB,cAFW,cAEJ,WAAU;AACjB,cAHW,cAGJ,WAAU;AACjB,cAJW,cAIJ,mBAA2B;AAJ7B,IAAM,cAAN;;"}
@@ -8,6 +8,9 @@ const RIGHT_ALIGN = "align-right";
8
8
  const COPY = "copy";
9
9
  const DOWNLOAD = "download";
10
10
  const DefaultOptions = {
11
+ // 默认情况下,`file://` 格式的本地文件路径在浏览器环境无法读取,因此会被转换成 `//:0`,但是在一些特殊的场景下(比如:Electron),需要获取到图片的原始路径,进行后续的上传处理
12
+ // 注意:该选项一旦设置为 true,本次磁盘路径会暴露出去,这可能会带来安全风险,请确保你了解相关的安全隐患
13
+ allowInvalidUrl: false,
11
14
  specs: [
12
15
  imageSpec.ImageSpec
13
16
  ],
@@ -1 +1 @@
1
- {"version":3,"file":"options.cjs.js","sources":["../../../../src/modules/custom-image/options.ts"],"sourcesContent":["import type { ImageToolbar, ImageToolbarButtons } from './actions'\r\nimport type { BlotSpec } from './specs'\r\nimport { ImageSpec } from './specs'\r\n\r\nexport interface OverlayOptions {\r\n // classname applied to the overlay element\r\n className: string\r\n // style applied to overlay element, or null to prevent styles\r\n style: Record<string, string>\r\n}\r\n\r\nexport interface ResizeOptions {\r\n // class name applied to the resize handles\r\n handleClassName: string\r\n // style applied to resize handles, or null to prevent styles\r\n handleStyle: Record<string, string>\r\n}\r\n\r\nexport interface ToolButtonOption {\r\n name: string\r\n icon: string\r\n isActive?: (el: HTMLElement) => boolean\r\n apply: (this: ImageToolbar, el: HTMLImageElement, toolbarButtons: ImageToolbarButtons) => void\r\n}\r\n\r\nexport interface ToolbarButtonOptions {\r\n buttons: Record<string, ToolButtonOption | boolean>\r\n}\r\n\r\nexport interface ToolbarOptions extends ToolbarButtonOptions {\r\n // class name applied to the root toolbar element\r\n mainClassName: string\r\n // style applied to root toolbar element, or null to prevent styles\r\n mainStyle: Record<string, unknown>\r\n // class name applied to each button in the toolbar\r\n buttonClassName: string\r\n /* whether or not to add the selected style to the buttons.\r\n they'll always get the is-selected class */\r\n addButtonSelectStyle: boolean\r\n // style applied to buttons, or null to prevent styles\r\n buttonStyle: Record<string, unknown>\r\n // style applied to the svgs in the buttons\r\n svgStyle: Record<string, unknown>\r\n}\r\n\r\nexport interface BlotFormatterOptionsInput {\r\n specs: typeof BlotSpec[]\r\n overlay: Partial<OverlayOptions>\r\n resize: Partial<ResizeOptions>\r\n toolbar: Partial<ToolbarOptions>\r\n}\r\nexport interface BlotFormatterOptions {\r\n specs: typeof BlotSpec[]\r\n overlay: OverlayOptions\r\n resize: ResizeOptions\r\n toolbar: ToolbarOptions\r\n}\r\n\r\nexport const LEFT_ALIGN = 'align-left'\r\nexport const CENTER_ALIGN = 'align-center'\r\nexport const RIGHT_ALIGN = 'align-right'\r\nexport const COPY = 'copy'\r\nexport const DOWNLOAD = 'download'\r\nconst DefaultOptions: BlotFormatterOptions = {\r\n specs: [\r\n ImageSpec,\r\n ],\r\n overlay: {\r\n className: 'blot-formatter__overlay',\r\n style: {\r\n position: 'absolute',\r\n boxSizing: 'border-box',\r\n border: '1px dashed #444',\r\n },\r\n },\r\n toolbar: {\r\n mainClassName: 'blot-formatter__toolbar',\r\n mainStyle: {\r\n position: 'absolute',\r\n top: '-12px',\r\n right: '0',\r\n left: '0',\r\n height: '0',\r\n minWidth: '120px',\r\n font: '12px/1.0 Arial, Helvetica, sans-serif',\r\n textAlign: 'center',\r\n color: '#333',\r\n boxSizing: 'border-box',\r\n cursor: 'default',\r\n zIndex: '1',\r\n },\r\n buttonClassName: 'blot-formatter__toolbar-button',\r\n addButtonSelectStyle: true,\r\n buttonStyle: {\r\n display: 'inline-flex',\r\n alignItems: 'center',\r\n justifyContent: 'center',\r\n width: '24px',\r\n height: '24px',\r\n background: 'white',\r\n border: '1px solid #999',\r\n verticalAlign: 'middle',\r\n cursor: 'pointer',\r\n },\r\n svgStyle: {\r\n display: 'inline-block',\r\n width: '16px',\r\n height: '16px',\r\n background: 'white',\r\n verticalAlign: 'middle',\r\n },\r\n buttons: {\r\n [LEFT_ALIGN]: true,\r\n [CENTER_ALIGN]: true,\r\n [RIGHT_ALIGN]: true,\r\n [COPY]: true,\r\n [DOWNLOAD]: true,\r\n },\r\n },\r\n resize: {\r\n handleClassName: 'blot-formatter__resize-handle',\r\n handleStyle: {\r\n position: 'absolute',\r\n height: '12px',\r\n width: '12px',\r\n backgroundColor: 'white',\r\n border: '1px solid #777',\r\n boxSizing: 'border-box',\r\n opacity: '0.80',\r\n },\r\n },\r\n}\r\n\r\nexport default DefaultOptions\r\n"],"names":["ImageSpec"],"mappings":";;;;AA0DO,MAAM,aAAa;AACnB,MAAM,eAAe;AACrB,MAAM,cAAc;AACpB,MAAM,OAAO;AACb,MAAM,WAAW;AACxB,MAAM,iBAAuC;AAAA,EAC3C,OAAO;AAAA,IACLA,UAAAA;AAAAA,EACF;AAAA,EACA,SAAS;AAAA,IACP,WAAW;AAAA,IACX,OAAO;AAAA,MACL,UAAU;AAAA,MACV,WAAW;AAAA,MACX,QAAQ;AAAA,IAAA;AAAA,EAEZ;AAAA,EACA,SAAS;AAAA,IACP,eAAe;AAAA,IACf,WAAW;AAAA,MACT,UAAU;AAAA,MACV,KAAK;AAAA,MACL,OAAO;AAAA,MACP,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,UAAU;AAAA,MACV,MAAM;AAAA,MACN,WAAW;AAAA,MACX,OAAO;AAAA,MACP,WAAW;AAAA,MACX,QAAQ;AAAA,MACR,QAAQ;AAAA,IACV;AAAA,IACA,iBAAiB;AAAA,IACjB,sBAAsB;AAAA,IACtB,aAAa;AAAA,MACX,SAAS;AAAA,MACT,YAAY;AAAA,MACZ,gBAAgB;AAAA,MAChB,OAAO;AAAA,MACP,QAAQ;AAAA,MACR,YAAY;AAAA,MACZ,QAAQ;AAAA,MACR,eAAe;AAAA,MACf,QAAQ;AAAA,IACV;AAAA,IACA,UAAU;AAAA,MACR,SAAS;AAAA,MACT,OAAO;AAAA,MACP,QAAQ;AAAA,MACR,YAAY;AAAA,MACZ,eAAe;AAAA,IACjB;AAAA,IACA,SAAS;AAAA,MACP,CAAC,UAAU,GAAG;AAAA,MACd,CAAC,YAAY,GAAG;AAAA,MAChB,CAAC,WAAW,GAAG;AAAA,MACf,CAAC,IAAI,GAAG;AAAA,MACR,CAAC,QAAQ,GAAG;AAAA,IAAA;AAAA,EAEhB;AAAA,EACA,QAAQ;AAAA,IACN,iBAAiB;AAAA,IACjB,aAAa;AAAA,MACX,UAAU;AAAA,MACV,QAAQ;AAAA,MACR,OAAO;AAAA,MACP,iBAAiB;AAAA,MACjB,QAAQ;AAAA,MACR,WAAW;AAAA,MACX,SAAS;AAAA,IAAA;AAAA,EACX;AAEJ;;;;;;;"}
1
+ {"version":3,"file":"options.cjs.js","sources":["../../../../src/modules/custom-image/options.ts"],"sourcesContent":["import type { ImageToolbar, ImageToolbarButtons } from './actions'\r\nimport type { BlotSpec } from './specs'\r\nimport { ImageSpec } from './specs'\r\n\r\nexport interface OverlayOptions {\r\n // classname applied to the overlay element\r\n className: string\r\n // style applied to overlay element, or null to prevent styles\r\n style: Record<string, string>\r\n}\r\n\r\nexport interface ResizeOptions {\r\n // class name applied to the resize handles\r\n handleClassName: string\r\n // style applied to resize handles, or null to prevent styles\r\n handleStyle: Record<string, string>\r\n}\r\n\r\nexport interface ToolButtonOption {\r\n name: string\r\n icon: string\r\n isActive?: (el: HTMLElement) => boolean\r\n apply: (this: ImageToolbar, el: HTMLImageElement, toolbarButtons: ImageToolbarButtons) => void\r\n}\r\n\r\nexport interface ToolbarButtonOptions {\r\n buttons: Record<string, ToolButtonOption | boolean>\r\n}\r\n\r\nexport interface ToolbarOptions extends ToolbarButtonOptions {\r\n // class name applied to the root toolbar element\r\n mainClassName: string\r\n // style applied to root toolbar element, or null to prevent styles\r\n mainStyle: Record<string, unknown>\r\n // class name applied to each button in the toolbar\r\n buttonClassName: string\r\n /* whether or not to add the selected style to the buttons.\r\n they'll always get the is-selected class */\r\n addButtonSelectStyle: boolean\r\n // style applied to buttons, or null to prevent styles\r\n buttonStyle: Record<string, unknown>\r\n // style applied to the svgs in the buttons\r\n svgStyle: Record<string, unknown>\r\n}\r\n\r\nexport interface BlotFormatterOptionsInput {\r\n specs: typeof BlotSpec[]\r\n overlay: Partial<OverlayOptions>\r\n resize: Partial<ResizeOptions>\r\n toolbar: Partial<ToolbarOptions>\r\n}\r\nexport interface BlotFormatterOptions {\r\n specs: typeof BlotSpec[]\r\n overlay: OverlayOptions\r\n resize: ResizeOptions\r\n toolbar: ToolbarOptions\r\n allowInvalidUrl: boolean\r\n}\r\n\r\nexport const LEFT_ALIGN = 'align-left'\r\nexport const CENTER_ALIGN = 'align-center'\r\nexport const RIGHT_ALIGN = 'align-right'\r\nexport const COPY = 'copy'\r\nexport const DOWNLOAD = 'download'\r\nconst DefaultOptions: BlotFormatterOptions = {\r\n // 默认情况下,`file://` 格式的本地文件路径在浏览器环境无法读取,因此会被转换成 `//:0`,但是在一些特殊的场景下(比如:Electron),需要获取到图片的原始路径,进行后续的上传处理\r\n // 注意:该选项一旦设置为 true,本次磁盘路径会暴露出去,这可能会带来安全风险,请确保你了解相关的安全隐患\r\n allowInvalidUrl: false,\r\n\r\n specs: [\r\n ImageSpec,\r\n ],\r\n overlay: {\r\n className: 'blot-formatter__overlay',\r\n style: {\r\n position: 'absolute',\r\n boxSizing: 'border-box',\r\n border: '1px dashed #444',\r\n },\r\n },\r\n toolbar: {\r\n mainClassName: 'blot-formatter__toolbar',\r\n mainStyle: {\r\n position: 'absolute',\r\n top: '-12px',\r\n right: '0',\r\n left: '0',\r\n height: '0',\r\n minWidth: '120px',\r\n font: '12px/1.0 Arial, Helvetica, sans-serif',\r\n textAlign: 'center',\r\n color: '#333',\r\n boxSizing: 'border-box',\r\n cursor: 'default',\r\n zIndex: '1',\r\n },\r\n buttonClassName: 'blot-formatter__toolbar-button',\r\n addButtonSelectStyle: true,\r\n buttonStyle: {\r\n display: 'inline-flex',\r\n alignItems: 'center',\r\n justifyContent: 'center',\r\n width: '24px',\r\n height: '24px',\r\n background: 'white',\r\n border: '1px solid #999',\r\n verticalAlign: 'middle',\r\n cursor: 'pointer',\r\n },\r\n svgStyle: {\r\n display: 'inline-block',\r\n width: '16px',\r\n height: '16px',\r\n background: 'white',\r\n verticalAlign: 'middle',\r\n },\r\n buttons: {\r\n [LEFT_ALIGN]: true,\r\n [CENTER_ALIGN]: true,\r\n [RIGHT_ALIGN]: true,\r\n [COPY]: true,\r\n [DOWNLOAD]: true,\r\n },\r\n },\r\n resize: {\r\n handleClassName: 'blot-formatter__resize-handle',\r\n handleStyle: {\r\n position: 'absolute',\r\n height: '12px',\r\n width: '12px',\r\n backgroundColor: 'white',\r\n border: '1px solid #777',\r\n boxSizing: 'border-box',\r\n opacity: '0.80',\r\n },\r\n },\r\n}\r\n\r\nexport default DefaultOptions\r\n"],"names":["ImageSpec"],"mappings":";;;;AA2DO,MAAM,aAAa;AACnB,MAAM,eAAe;AACrB,MAAM,cAAc;AACpB,MAAM,OAAO;AACb,MAAM,WAAW;AACxB,MAAM,iBAAuC;AAAA;AAAA;AAAA,EAG3C,iBAAiB;AAAA,EAEjB,OAAO;AAAA,IACLA,UAAAA;AAAAA,EACF;AAAA,EACA,SAAS;AAAA,IACP,WAAW;AAAA,IACX,OAAO;AAAA,MACL,UAAU;AAAA,MACV,WAAW;AAAA,MACX,QAAQ;AAAA,IAAA;AAAA,EAEZ;AAAA,EACA,SAAS;AAAA,IACP,eAAe;AAAA,IACf,WAAW;AAAA,MACT,UAAU;AAAA,MACV,KAAK;AAAA,MACL,OAAO;AAAA,MACP,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,UAAU;AAAA,MACV,MAAM;AAAA,MACN,WAAW;AAAA,MACX,OAAO;AAAA,MACP,WAAW;AAAA,MACX,QAAQ;AAAA,MACR,QAAQ;AAAA,IACV;AAAA,IACA,iBAAiB;AAAA,IACjB,sBAAsB;AAAA,IACtB,aAAa;AAAA,MACX,SAAS;AAAA,MACT,YAAY;AAAA,MACZ,gBAAgB;AAAA,MAChB,OAAO;AAAA,MACP,QAAQ;AAAA,MACR,YAAY;AAAA,MACZ,QAAQ;AAAA,MACR,eAAe;AAAA,MACf,QAAQ;AAAA,IACV;AAAA,IACA,UAAU;AAAA,MACR,SAAS;AAAA,MACT,OAAO;AAAA,MACP,QAAQ;AAAA,MACR,YAAY;AAAA,MACZ,eAAe;AAAA,IACjB;AAAA,IACA,SAAS;AAAA,MACP,CAAC,UAAU,GAAG;AAAA,MACd,CAAC,YAAY,GAAG;AAAA,MAChB,CAAC,WAAW,GAAG;AAAA,MACf,CAAC,IAAI,GAAG;AAAA,MACR,CAAC,QAAQ,GAAG;AAAA,IAAA;AAAA,EAEhB;AAAA,EACA,QAAQ;AAAA,IACN,iBAAiB;AAAA,IACjB,aAAa;AAAA,MACX,UAAU;AAAA,MACV,QAAQ;AAAA,MACR,OAAO;AAAA,MACP,iBAAiB;AAAA,MACjB,QAAQ;AAAA,MACR,WAAW;AAAA,MACX,SAAS;AAAA,IAAA;AAAA,EACX;AAEJ;;;;;;;"}
@@ -55,7 +55,7 @@ class FileUploader extends Uploader {
55
55
  delta.insert({ file: { size: uploads[i].size, title: uploads[i].name, src: url } });
56
56
  }
57
57
  } else {
58
- delta.insert("\n");
58
+ delta.insert(" ");
59
59
  }
60
60
  return delta;
61
61
  }, new Delta().retain(range.index).delete(range.length));
@@ -1 +1 @@
1
- {"version":3,"file":"custom-uploader.cjs.js","sources":["../../../src/modules/custom-uploader.ts"],"sourcesContent":["import type { Range } from 'quill'\r\nimport type TypeUploader from 'quill/modules/uploader'\r\nimport type FluentEditor from '../core/fluent-editor'\r\nimport Quill from 'quill'\r\nimport { isString } from '../utils/is'\r\n\r\nconst Uploader = Quill.import('modules/uploader') as typeof TypeUploader\r\nconst Delta = Quill.import('delta')\r\n\r\ninterface UploaderOptions {\r\n mimetypes: string[]\r\n handler: (this: { quill: Quill }, range: Range, files: File[]) => void\r\n}\r\nexport interface FileUploaderOptions {\r\n mimetypes: string[]\r\n maxSize: number\r\n handler: (this: { quill: FluentEditor }, range: Range, files: File[]) => Promise<(string | false)[]> | (string | false)[]\r\n success: (this: { quill: FluentEditor }, file: File, range: Range) => void\r\n fail: (this: { quill: FluentEditor }, file: File, range: Range) => void\r\n}\r\nexport class FileUploader extends Uploader {\r\n static DEFAULTS = {} as any\r\n // Partial<UploaderOptions> for ts type\r\n declare options: Partial<UploaderOptions> & FileUploaderOptions\r\n constructor(public quill: FluentEditor, options: Partial<FileUploaderOptions>) {\r\n super(quill, options as any)\r\n this.options = this.resolveOptions(options)\r\n // paste handle in clipboard\r\n }\r\n\r\n resolveOptions(options: Partial<FileUploaderOptions> = {}) {\r\n return Object.assign({\r\n mimetypes: ['*'],\r\n maxSize: Number.POSITIVE_INFINITY,\r\n handler(range: Range, files: File[]) {\r\n return files.map(file => URL.createObjectURL(file))\r\n },\r\n success() {},\r\n fail() {},\r\n }, options)\r\n }\r\n\r\n validateFile(file: File) {\r\n return this.options.mimetypes.some(type => (file.type || 'text/plain').match(type.replaceAll('*', '.*'))) && file.size < this.options.maxSize\r\n }\r\n\r\n async getFileUrls(files: File[], range: Range) {\r\n const uploads = files.filter(file => this.validateFile(file))\r\n return this.options.handler.call(this, range, uploads)\r\n }\r\n\r\n async upload(range: Range, files: FileList | File[]) {\r\n const uploads = []\r\n const fails = []\r\n for (const file of Array.from(files)) {\r\n if (this.validateFile(file)) {\r\n uploads.push(file)\r\n }\r\n else {\r\n fails.push(file)\r\n }\r\n }\r\n const result = await this.options.handler.call(this, range, uploads)\r\n const updateDelta = result.reduce((delta, url, i) => {\r\n if (isString(url)) {\r\n const type = uploads[i].type\r\n if (type.startsWith('image/')) {\r\n delta.insert({ image: url })\r\n }\r\n else if (type.startsWith('video/')) {\r\n delta.insert({ video: { src: url } })\r\n }\r\n else {\r\n delta.insert({ file: { size: uploads[i].size, title: uploads[i].name, src: url } })\r\n }\r\n }\r\n else {\r\n delta.insert('\\n')\r\n }\r\n return delta\r\n }, new Delta().retain(range.index).delete(range.length))\r\n this.quill.updateContents(updateDelta, Quill.sources.USER)\r\n this.quill.setSelection(range.index + result.length, Quill.sources.SILENT)\r\n for (const file of fails) {\r\n this.options.fail.call(this, file, range)\r\n }\r\n for (const [i, res] of result.entries()) {\r\n if (isString(res)) {\r\n this.options.success.call(this, files[i], { index: range.index + i, length: 0 })\r\n }\r\n else {\r\n this.options.fail.call(this, files[i], { index: range.index + i, length: 0 })\r\n }\r\n }\r\n }\r\n}\r\n"],"names":["isString"],"mappings":"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"}
1
+ {"version":3,"file":"custom-uploader.cjs.js","sources":["../../../src/modules/custom-uploader.ts"],"sourcesContent":["import type { Range } from 'quill'\r\nimport type TypeUploader from 'quill/modules/uploader'\r\nimport type FluentEditor from '../core/fluent-editor'\r\nimport Quill from 'quill'\r\nimport { isString } from '../utils/is'\r\n\r\nconst Uploader = Quill.import('modules/uploader') as typeof TypeUploader\r\nconst Delta = Quill.import('delta')\r\n\r\ninterface UploaderOptions {\r\n mimetypes: string[]\r\n handler: (this: { quill: Quill }, range: Range, files: File[]) => void\r\n}\r\nexport interface FileUploaderOptions {\r\n mimetypes: string[]\r\n maxSize: number\r\n handler: (this: { quill: FluentEditor }, range: Range, files: File[]) => Promise<(string | false)[]> | (string | false)[]\r\n success: (this: { quill: FluentEditor }, file: File, range: Range) => void\r\n fail: (this: { quill: FluentEditor }, file: File, range: Range) => void\r\n}\r\nexport class FileUploader extends Uploader {\r\n static DEFAULTS = {} as any\r\n // Partial<UploaderOptions> for ts type\r\n declare options: Partial<UploaderOptions> & FileUploaderOptions\r\n constructor(public quill: FluentEditor, options: Partial<FileUploaderOptions>) {\r\n super(quill, options as any)\r\n this.options = this.resolveOptions(options)\r\n // paste handle in clipboard\r\n }\r\n\r\n resolveOptions(options: Partial<FileUploaderOptions> = {}) {\r\n return Object.assign({\r\n mimetypes: ['*'],\r\n maxSize: Number.POSITIVE_INFINITY,\r\n handler(range: Range, files: File[]) {\r\n return files.map(file => URL.createObjectURL(file))\r\n },\r\n success() {},\r\n fail() {},\r\n }, options)\r\n }\r\n\r\n validateFile(file: File) {\r\n return this.options.mimetypes.some(type => (file.type || 'text/plain').match(type.replaceAll('*', '.*'))) && file.size < this.options.maxSize\r\n }\r\n\r\n async getFileUrls(files: File[], range: Range) {\r\n const uploads = files.filter(file => this.validateFile(file))\r\n return this.options.handler.call(this, range, uploads)\r\n }\r\n\r\n async upload(range: Range, files: FileList | File[]) {\r\n const uploads = []\r\n const fails = []\r\n\r\n for (const file of Array.from(files)) {\r\n if (this.validateFile(file)) {\r\n uploads.push(file)\r\n }\r\n else {\r\n fails.push(file)\r\n }\r\n }\r\n\r\n const result = await this.options.handler.call(this, range, uploads)\r\n const updateDelta = result.reduce((delta, url, i) => {\r\n if (isString(url)) {\r\n const type = uploads[i].type\r\n if (type.startsWith('image/')) {\r\n delta.insert({ image: url })\r\n }\r\n else if (type.startsWith('video/')) {\r\n delta.insert({ video: { src: url } })\r\n }\r\n else {\r\n delta.insert({ file: { size: uploads[i].size, title: uploads[i].name, src: url } })\r\n }\r\n }\r\n else {\r\n delta.insert(' ')\r\n }\r\n return delta\r\n }, new Delta().retain(range.index).delete(range.length))\r\n\r\n this.quill.updateContents(updateDelta, Quill.sources.USER)\r\n this.quill.setSelection(range.index + result.length, Quill.sources.SILENT)\r\n\r\n for (const file of fails) {\r\n this.options.fail.call(this, file, range)\r\n }\r\n\r\n for (const [i, res] of result.entries()) {\r\n if (isString(res)) {\r\n this.options.success.call(this, files[i], { index: range.index + i, length: 0 })\r\n }\r\n else {\r\n this.options.fail.call(this, files[i], { index: range.index + i, length: 0 })\r\n }\r\n }\r\n }\r\n}\r\n"],"names":["isString"],"mappings":"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"}
@@ -133,26 +133,14 @@ class EmojiModule {
133
133
  return;
134
134
  }
135
135
  try {
136
- const emojiDelta = this.quill.insertText(selection.index, emoji.native, "user");
136
+ const insertIndex = selection.index;
137
+ this.quill.insertText(insertIndex, emoji.native, "user");
137
138
  this.closeDialog();
138
- this.setSelectionAfterEmoji(emojiDelta);
139
+ this.quill.setSelection(insertIndex + emoji.native.length);
139
140
  } catch (error) {
140
141
  console.error("Failed to insert emoji:", error);
141
142
  }
142
143
  }
143
- // 设置表情插入后的光标位置
144
- setSelectionAfterEmoji(emojiDelta) {
145
- setTimeout(() => {
146
- try {
147
- const newSelection = this.quill.getSelection(true);
148
- if (newSelection && emojiDelta) {
149
- this.quill.setSelection(newSelection.index + emojiDelta.length());
150
- }
151
- } catch (error) {
152
- console.warn("Failed to set selection after emoji insertion:", error);
153
- }
154
- }, 0);
155
- }
156
144
  // 处理外部点击事件
157
145
  handleClickOutside(event) {
158
146
  const button = this.getEmojiButton();
@@ -1 +1 @@
1
- {"version":3,"file":"emoji.cjs.js","sources":["../../../src/modules/emoji.ts"],"sourcesContent":["import type { Delta } from 'quill'\r\nimport type TypeToolbar from 'quill/modules/toolbar'\r\nimport type FluentEditor from '../fluent-editor'\r\nimport data from '@emoji-mart/data'\r\nimport { computePosition } from '@floating-ui/dom'\r\nimport { Picker } from 'emoji-mart'\r\nimport { debounce } from 'lodash-es'\r\n\r\nexport interface EmojiModuleOptions {\r\n theme?: string\r\n locale?: string\r\n set?: string\r\n skinTonePosition?: string\r\n previewPosition?: string\r\n searchPosition?: string\r\n categories?: string[]\r\n maxFrequentRows?: number\r\n perLine?: number\r\n navPosition?: string\r\n noCountryFlags?: boolean\r\n dynamicWidth?: boolean\r\n}\r\n\r\nconst DEFAULT_OPTIONS: EmojiModuleOptions = {\r\n theme: 'light',\r\n set: 'native',\r\n skinTonePosition: 'none',\r\n previewPosition: 'bottom',\r\n searchPosition: 'sticky',\r\n categories: ['frequent', 'people', 'nature', 'foods', 'activity', 'places', 'objects', 'symbols', 'flags'],\r\n maxFrequentRows: 2,\r\n perLine: 8,\r\n navPosition: 'top',\r\n noCountryFlags: false,\r\n dynamicWidth: false,\r\n}\r\n\r\nconst PICKER_DOM_ID = 'emoji-picker'\r\n\r\nconst LOCALE_MAP = {\r\n 'zh-CN': 'zh',\r\n 'en-US': 'en',\r\n} as const\r\n\r\nclass EmojiModule {\r\n private readonly quill: FluentEditor\r\n private readonly options: EmojiModuleOptions\r\n private picker: HTMLElement | null = null\r\n private isPickerVisible = false\r\n private cleanupResizeObserver: (() => void) | null = null\r\n\r\n constructor(quill: FluentEditor, options: EmojiModuleOptions = {}) {\r\n this.quill = quill\r\n\r\n this.options = options\r\n\r\n const toolbar = this.quill.getModule('toolbar') as TypeToolbar\r\n\r\n if (toolbar) {\r\n toolbar.addHandler('emoji', () => {\r\n if (this.isPickerVisible) {\r\n this.closeDialog()\r\n }\r\n else {\r\n this.openDialog()\r\n }\r\n })\r\n }\r\n }\r\n\r\n private getEmojiButton() {\r\n return document.querySelector('.ql-emoji') as HTMLElement | null\r\n }\r\n\r\n private async updatePickerPosition() {\r\n const button = this.getEmojiButton()\r\n const pickerElement = document.getElementById(PICKER_DOM_ID)\r\n\r\n if (!button || !this.picker || !pickerElement) {\r\n return\r\n }\r\n\r\n try {\r\n const { x, y } = await computePosition(button, pickerElement)\r\n this.picker.style.top = `${y}px`\r\n this.picker.style.left = `${x}px`\r\n }\r\n catch (error) {\r\n console.warn('Failed to compute picker position:', error)\r\n }\r\n }\r\n\r\n // 监听容器大小变化,更新表情选择弹窗位置\r\n private setupContainerResizeObserver() {\r\n const container = this.quill.root.parentElement\r\n if (!container) {\r\n return null\r\n }\r\n\r\n const debouncedUpdate = debounce(() => {\r\n this.updatePickerPosition()\r\n }, 100)\r\n\r\n const resizeObserver = new ResizeObserver(() => {\r\n debouncedUpdate()\r\n })\r\n\r\n resizeObserver.observe(container)\r\n\r\n return () => {\r\n resizeObserver.disconnect()\r\n debouncedUpdate.cancel()\r\n }\r\n }\r\n\r\n // 创建表情选择弹窗\r\n private createPicker() {\r\n const pickerConfig = {\r\n ...DEFAULT_OPTIONS,\r\n // emoji-mart 与 tiny-editor 国际化的的 locale 不一致使用 LOCALE_MAP 转换\r\n locale: LOCALE_MAP[this.quill.lang] ?? 'en',\r\n ...this.options,\r\n data,\r\n onEmojiSelect: this.handleEmojiSelect.bind(this),\r\n onClickOutside: this.handleClickOutside.bind(this),\r\n }\r\n\r\n const picker = new Picker(pickerConfig) as unknown as HTMLElement\r\n\r\n // 设置样式和属性\r\n picker.id = PICKER_DOM_ID\r\n picker.style.position = 'absolute'\r\n picker.style.zIndex = '1000'\r\n\r\n return picker\r\n }\r\n\r\n // 打开表情弹窗\r\n public openDialog() {\r\n if (this.picker) {\r\n return\r\n }\r\n\r\n try {\r\n this.picker = this.createPicker()\r\n document.body.appendChild(this.picker)\r\n\r\n this.updatePickerPosition()\r\n this.cleanupResizeObserver = this.setupContainerResizeObserver()\r\n this.isPickerVisible = true\r\n }\r\n catch (error) {\r\n console.error('Failed to open emoji picker:', error)\r\n this.closeDialog()\r\n }\r\n }\r\n\r\n // 关闭表情弹窗\r\n public closeDialog() {\r\n if (!this.picker) {\r\n return\r\n }\r\n\r\n this.isPickerVisible = false\r\n this.picker.remove()\r\n this.picker = null\r\n\r\n this.cleanupResizeObserver?.()\r\n this.cleanupResizeObserver = null\r\n }\r\n\r\n // 处理表情选择事件\r\n private handleEmojiSelect(emoji: { native: string }) {\r\n const selection = this.quill.getSelection(true)\r\n if (!selection) {\r\n console.warn('No selection available for emoji insertion')\r\n return\r\n }\r\n\r\n try {\r\n const emojiDelta = this.quill.insertText(selection.index, emoji.native, 'user')\r\n\r\n this.closeDialog()\r\n\r\n // 异步设置光标位置,确保插入完成后再设置\r\n this.setSelectionAfterEmoji(emojiDelta)\r\n }\r\n catch (error) {\r\n console.error('Failed to insert emoji:', error)\r\n }\r\n }\r\n\r\n // 设置表情插入后的光标位置\r\n private setSelectionAfterEmoji(emojiDelta: Delta) {\r\n setTimeout(() => {\r\n try {\r\n const newSelection = this.quill.getSelection(true)\r\n if (newSelection && emojiDelta) {\r\n this.quill.setSelection(newSelection.index + emojiDelta.length())\r\n }\r\n }\r\n catch (error) {\r\n console.warn('Failed to set selection after emoji insertion:', error)\r\n }\r\n }, 0)\r\n }\r\n\r\n // 处理外部点击事件\r\n private handleClickOutside(event: MouseEvent) {\r\n const button = this.getEmojiButton()\r\n\r\n const target = event.target\r\n\r\n const isClickOnButton = target === button || (target instanceof Element && button?.contains(target))\r\n\r\n // 如果点击的是表情符号按钮或其子元素,则不关闭选择器\r\n if (isClickOnButton) {\r\n return\r\n }\r\n\r\n this.closeDialog()\r\n }\r\n\r\n // 销毁模块,清理资源\r\n public destroy() {\r\n this.cleanupResizeObserver?.()\r\n this.cleanupResizeObserver = null\r\n this.closeDialog()\r\n }\r\n}\r\n\r\nexport { EmojiModule }\r\n"],"names":["computePosition","debounce","data","Picker"],"mappings":"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"}
1
+ {"version":3,"file":"emoji.cjs.js","sources":["../../../src/modules/emoji.ts"],"sourcesContent":["import type TypeToolbar from 'quill/modules/toolbar'\r\nimport type FluentEditor from '../fluent-editor'\r\nimport data from '@emoji-mart/data'\r\nimport { computePosition } from '@floating-ui/dom'\r\nimport { Picker } from 'emoji-mart'\r\nimport { debounce } from 'lodash-es'\r\n\r\nexport interface EmojiModuleOptions {\r\n theme?: string\r\n locale?: string\r\n set?: string\r\n skinTonePosition?: string\r\n previewPosition?: string\r\n searchPosition?: string\r\n categories?: string[]\r\n maxFrequentRows?: number\r\n perLine?: number\r\n navPosition?: string\r\n noCountryFlags?: boolean\r\n dynamicWidth?: boolean\r\n}\r\n\r\nconst DEFAULT_OPTIONS: EmojiModuleOptions = {\r\n theme: 'light',\r\n set: 'native',\r\n skinTonePosition: 'none',\r\n previewPosition: 'bottom',\r\n searchPosition: 'sticky',\r\n categories: ['frequent', 'people', 'nature', 'foods', 'activity', 'places', 'objects', 'symbols', 'flags'],\r\n maxFrequentRows: 2,\r\n perLine: 8,\r\n navPosition: 'top',\r\n noCountryFlags: false,\r\n dynamicWidth: false,\r\n}\r\n\r\nconst PICKER_DOM_ID = 'emoji-picker'\r\n\r\nconst LOCALE_MAP = {\r\n 'zh-CN': 'zh',\r\n 'en-US': 'en',\r\n} as const\r\n\r\nclass EmojiModule {\r\n private readonly quill: FluentEditor\r\n private readonly options: EmojiModuleOptions\r\n private picker: HTMLElement | null = null\r\n private isPickerVisible = false\r\n private cleanupResizeObserver: (() => void) | null = null\r\n\r\n constructor(quill: FluentEditor, options: EmojiModuleOptions = {}) {\r\n this.quill = quill\r\n\r\n this.options = options\r\n\r\n const toolbar = this.quill.getModule('toolbar') as TypeToolbar\r\n\r\n if (toolbar) {\r\n toolbar.addHandler('emoji', () => {\r\n if (this.isPickerVisible) {\r\n this.closeDialog()\r\n }\r\n else {\r\n this.openDialog()\r\n }\r\n })\r\n }\r\n }\r\n\r\n private getEmojiButton() {\r\n return document.querySelector('.ql-emoji') as HTMLElement | null\r\n }\r\n\r\n private async updatePickerPosition() {\r\n const button = this.getEmojiButton()\r\n const pickerElement = document.getElementById(PICKER_DOM_ID)\r\n\r\n if (!button || !this.picker || !pickerElement) {\r\n return\r\n }\r\n\r\n try {\r\n const { x, y } = await computePosition(button, pickerElement)\r\n this.picker.style.top = `${y}px`\r\n this.picker.style.left = `${x}px`\r\n }\r\n catch (error) {\r\n console.warn('Failed to compute picker position:', error)\r\n }\r\n }\r\n\r\n // 监听容器大小变化,更新表情选择弹窗位置\r\n private setupContainerResizeObserver() {\r\n const container = this.quill.root.parentElement\r\n if (!container) {\r\n return null\r\n }\r\n\r\n const debouncedUpdate = debounce(() => {\r\n this.updatePickerPosition()\r\n }, 100)\r\n\r\n const resizeObserver = new ResizeObserver(() => {\r\n debouncedUpdate()\r\n })\r\n\r\n resizeObserver.observe(container)\r\n\r\n return () => {\r\n resizeObserver.disconnect()\r\n debouncedUpdate.cancel()\r\n }\r\n }\r\n\r\n // 创建表情选择弹窗\r\n private createPicker() {\r\n const pickerConfig = {\r\n ...DEFAULT_OPTIONS,\r\n // emoji-mart 与 tiny-editor 国际化的的 locale 不一致使用 LOCALE_MAP 转换\r\n locale: LOCALE_MAP[this.quill.lang] ?? 'en',\r\n ...this.options,\r\n data,\r\n onEmojiSelect: this.handleEmojiSelect.bind(this),\r\n onClickOutside: this.handleClickOutside.bind(this),\r\n }\r\n\r\n const picker = new Picker(pickerConfig) as unknown as HTMLElement\r\n\r\n // 设置样式和属性\r\n picker.id = PICKER_DOM_ID\r\n picker.style.position = 'absolute'\r\n picker.style.zIndex = '1000'\r\n\r\n return picker\r\n }\r\n\r\n // 打开表情弹窗\r\n public openDialog() {\r\n if (this.picker) {\r\n return\r\n }\r\n\r\n try {\r\n this.picker = this.createPicker()\r\n document.body.appendChild(this.picker)\r\n\r\n this.updatePickerPosition()\r\n this.cleanupResizeObserver = this.setupContainerResizeObserver()\r\n this.isPickerVisible = true\r\n }\r\n catch (error) {\r\n console.error('Failed to open emoji picker:', error)\r\n this.closeDialog()\r\n }\r\n }\r\n\r\n // 关闭表情弹窗\r\n public closeDialog() {\r\n if (!this.picker) {\r\n return\r\n }\r\n\r\n this.isPickerVisible = false\r\n this.picker.remove()\r\n this.picker = null\r\n\r\n this.cleanupResizeObserver?.()\r\n this.cleanupResizeObserver = null\r\n }\r\n\r\n // 处理表情选择事件\r\n private handleEmojiSelect(emoji: { native: string }) {\r\n const selection = this.quill.getSelection(true)\r\n if (!selection) {\r\n console.warn('No selection available for emoji insertion')\r\n return\r\n }\r\n\r\n try {\r\n // 记录插入位置\r\n const insertIndex = selection.index\r\n this.quill.insertText(insertIndex, emoji.native, 'user')\r\n\r\n this.closeDialog()\r\n\r\n // 设置光标到表情符号后面\r\n this.quill.setSelection(insertIndex + emoji.native.length)\r\n }\r\n catch (error) {\r\n console.error('Failed to insert emoji:', error)\r\n }\r\n }\r\n\r\n // 处理外部点击事件\r\n private handleClickOutside(event: MouseEvent) {\r\n const button = this.getEmojiButton()\r\n\r\n const target = event.target\r\n\r\n const isClickOnButton = target === button || (target instanceof Element && button?.contains(target))\r\n\r\n // 如果点击的是表情符号按钮或其子元素,则不关闭选择器\r\n if (isClickOnButton) {\r\n return\r\n }\r\n\r\n this.closeDialog()\r\n }\r\n\r\n // 销毁模块,清理资源\r\n public destroy() {\r\n this.cleanupResizeObserver?.()\r\n this.cleanupResizeObserver = null\r\n this.closeDialog()\r\n }\r\n}\r\n\r\nexport { EmojiModule }\r\n"],"names":["computePosition","debounce","data","Picker"],"mappings":"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"}
@@ -1,16 +1,21 @@
1
1
  "use strict";
2
- var __defProp = Object.defineProperty;
3
- var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
4
- var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
5
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
6
3
  require("../../config/index.cjs.js");
4
+ const is = require("../../utils/is.cjs.js");
7
5
  const editor_config = require("../../config/editor.config.cjs.js");
8
6
  function generateTableUp(QuillTableUp) {
9
7
  return class extends QuillTableUp {
10
8
  constructor(quill, options) {
11
9
  super(quill, options);
12
- __publicField(this, "tableSelection");
13
10
  this.quill = quill;
11
+ if (!this.quill.options["format-painter"]) this.quill.options["format-painter"] = {};
12
+ const currentIgnoreFormat = this.quill.options["format-painter"].ignoreFormat || [];
13
+ this.quill.options["format-painter"].ignoreFormat = Array.from(
14
+ /* @__PURE__ */ new Set([
15
+ ...currentIgnoreFormat,
16
+ "table-up-cell-inner"
17
+ ])
18
+ );
14
19
  this.quill.emitter.on(editor_config.CHANGE_LANGUAGE_EVENT, () => {
15
20
  this.options.texts = this.resolveTexts(options.texts);
16
21
  const toolbar = this.quill.getModule("toolbar");
@@ -23,12 +28,13 @@ function generateTableUp(QuillTableUp) {
23
28
  }
24
29
  }
25
30
  }
26
- if (this.tableSelection) {
27
- this.tableSelection.destroy();
28
- }
29
- if (this.options.selection) {
30
- this.tableSelection = new this.options.selection(this, this.quill, this.options.selectionOptions);
31
- }
31
+ Object.keys(this.modules).forEach((key) => {
32
+ if (is.isFunction(this.modules[key].destroy)) {
33
+ this.modules[key].destroy();
34
+ }
35
+ });
36
+ this.modules = {};
37
+ this.initModules();
32
38
  });
33
39
  }
34
40
  resolveTexts(options = {}) {
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs.js","sources":["../../../../src/modules/table-up/index.ts"],"sourcesContent":["import type { Parchment } from 'quill'\r\nimport type Toolbar from 'quill/modules/toolbar'\r\nimport type BaseTheme from 'quill/themes/base'\r\nimport type Picker from 'quill/ui/picker'\r\nimport type { Constructor } from '../../config/types'\r\nimport type FluentEditor from '../../core/fluent-editor'\r\nimport { CHANGE_LANGUAGE_EVENT } from '../../config'\r\n\r\ninterface QuillTheme extends BaseTheme {\r\n pickers: QuillThemePicker[]\r\n}\r\ntype QuillThemePicker = (Picker & { options: HTMLElement })\r\ninterface InternalModule {\r\n show: () => void\r\n hide: () => void\r\n update: () => void\r\n destroy: () => void\r\n}\r\nexport interface InternalTableSelectionModule extends InternalModule {\r\n dragging: boolean\r\n boundary: {\r\n x: number\r\n y: number\r\n x1: number\r\n y1: number\r\n width: number\r\n height: number\r\n } | null\r\n selectedTds: Parchment.Blot[]\r\n cellSelect: HTMLElement\r\n tableMenu?: InternalModule\r\n computeSelectedTds: (\r\n startPoint: {\r\n x: number\r\n y: number\r\n },\r\n endPoint: {\r\n x: number\r\n y: number\r\n }\r\n ) => Parchment.Blot[]\r\n updateWithSelectedTds: () => void\r\n}\r\nexport function generateTableUp(QuillTableUp: Constructor) {\r\n return class extends QuillTableUp {\r\n tableSelection?: InternalTableSelectionModule\r\n constructor(public quill: FluentEditor, options: Partial<any>) {\r\n super(quill, options)\r\n\r\n this.quill.emitter.on(CHANGE_LANGUAGE_EVENT, () => {\r\n this.options.texts = this.resolveTexts(options.texts)\r\n const toolbar = this.quill.getModule('toolbar') as Toolbar\r\n if (toolbar && (this.quill.theme as QuillTheme).pickers) {\r\n const [, select] = (toolbar.controls as [string, HTMLElement][] || []).find(([name]) => name === this.statics.toolName) || []\r\n if (select && select.tagName.toLocaleLowerCase() === 'select') {\r\n const picker = (this.quill.theme as QuillTheme).pickers.find(picker => picker.select === select)\r\n if (picker) {\r\n this.buildCustomSelect(this.options.customSelect, picker)\r\n }\r\n }\r\n }\r\n if (this.tableSelection) {\r\n this.tableSelection.destroy()\r\n }\r\n if (this.options.selection) {\r\n // eslint-disable-next-line new-cap\r\n this.tableSelection = new this.options.selection(this, this.quill, this.options.selectionOptions)\r\n }\r\n })\r\n }\r\n\r\n resolveTexts(options: Record<string, string> = {}) {\r\n return Object.assign({\r\n fullCheckboxText: this.quill.getLangText('fullCheckboxText'),\r\n customBtnText: this.quill.getLangText('customBtnText'),\r\n confirmText: this.quill.getLangText('confirmText'),\r\n cancelText: this.quill.getLangText('cancelText'),\r\n rowText: this.quill.getLangText('rowText'),\r\n colText: this.quill.getLangText('colText'),\r\n notPositiveNumberError: this.quill.getLangText('notPositiveNumberError'),\r\n custom: this.quill.getLangText('custom'),\r\n clear: this.quill.getLangText('clear'),\r\n transparent: this.quill.getLangText('transparent'),\r\n perWidthInsufficient: this.quill.getLangText('perWidthInsufficient'),\r\n CopyCell: this.quill.getLangText('CopyCell'),\r\n CutCell: this.quill.getLangText('CutCell'),\r\n InsertTop: this.quill.getLangText('InsertTop'),\r\n InsertRight: this.quill.getLangText('InsertRight'),\r\n InsertBottom: this.quill.getLangText('InsertBottom'),\r\n InsertLeft: this.quill.getLangText('InsertLeft'),\r\n MergeCell: this.quill.getLangText('MergeCell'),\r\n SplitCell: this.quill.getLangText('SplitCell'),\r\n DeleteRow: this.quill.getLangText('DeleteRow'),\r\n DeleteColumn: this.quill.getLangText('DeleteColumn'),\r\n DeleteTable: this.quill.getLangText('DeleteTable'),\r\n BackgroundColor: this.quill.getLangText('BackgroundColor'),\r\n BorderColor: this.quill.getLangText('BorderColor'),\r\n }, Object.entries(options).reduce((pre, [key, value]) => {\r\n pre[key] = this.quill.getLangText(value)\r\n return pre\r\n }, {} as Record<string, string>))\r\n }\r\n }\r\n}\r\n"],"names":["CHANGE_LANGUAGE_EVENT","picker"],"mappings":";;;;;;;AA2CO,SAAS,gBAAgB,cAA2B;AACzD,SAAO,cAAc,aAAa;AAAA,IAEhC,YAAmB,OAAqB,SAAuB;AAC7D,YAAM,OAAO,OAAO;AAFtB;AACmB,WAAA,QAAA;AAGjB,WAAK,MAAM,QAAQ,GAAGA,cAAA,uBAAuB,MAAM;AACjD,aAAK,QAAQ,QAAQ,KAAK,aAAa,QAAQ,KAAK;AACpD,cAAM,UAAU,KAAK,MAAM,UAAU,SAAS;AAC9C,YAAI,WAAY,KAAK,MAAM,MAAqB,SAAS;AACvD,gBAAM,CAAA,EAAG,MAAM,KAAK,QAAQ,YAAuC,CAAI,GAAA,KAAK,CAAC,CAAC,IAAI,MAAM,SAAS,KAAK,QAAQ,QAAQ,KAAK,CAAC;AAC5H,cAAI,UAAU,OAAO,QAAQ,kBAAA,MAAwB,UAAU;AACvD,kBAAA,SAAU,KAAK,MAAM,MAAqB,QAAQ,KAAK,CAAAC,YAAUA,QAAO,WAAW,MAAM;AAC/F,gBAAI,QAAQ;AACV,mBAAK,kBAAkB,KAAK,QAAQ,cAAc,MAAM;AAAA,YAAA;AAAA,UAC1D;AAAA,QACF;AAEF,YAAI,KAAK,gBAAgB;AACvB,eAAK,eAAe,QAAQ;AAAA,QAAA;AAE1B,YAAA,KAAK,QAAQ,WAAW;AAErB,eAAA,iBAAiB,IAAI,KAAK,QAAQ,UAAU,MAAM,KAAK,OAAO,KAAK,QAAQ,gBAAgB;AAAA,QAAA;AAAA,MAClG,CACD;AAAA,IAAA;AAAA,IAGH,aAAa,UAAkC,IAAI;AACjD,aAAO,OAAO,OAAO;AAAA,QACnB,kBAAkB,KAAK,MAAM,YAAY,kBAAkB;AAAA,QAC3D,eAAe,KAAK,MAAM,YAAY,eAAe;AAAA,QACrD,aAAa,KAAK,MAAM,YAAY,aAAa;AAAA,QACjD,YAAY,KAAK,MAAM,YAAY,YAAY;AAAA,QAC/C,SAAS,KAAK,MAAM,YAAY,SAAS;AAAA,QACzC,SAAS,KAAK,MAAM,YAAY,SAAS;AAAA,QACzC,wBAAwB,KAAK,MAAM,YAAY,wBAAwB;AAAA,QACvE,QAAQ,KAAK,MAAM,YAAY,QAAQ;AAAA,QACvC,OAAO,KAAK,MAAM,YAAY,OAAO;AAAA,QACrC,aAAa,KAAK,MAAM,YAAY,aAAa;AAAA,QACjD,sBAAsB,KAAK,MAAM,YAAY,sBAAsB;AAAA,QACnE,UAAU,KAAK,MAAM,YAAY,UAAU;AAAA,QAC3C,SAAS,KAAK,MAAM,YAAY,SAAS;AAAA,QACzC,WAAW,KAAK,MAAM,YAAY,WAAW;AAAA,QAC7C,aAAa,KAAK,MAAM,YAAY,aAAa;AAAA,QACjD,cAAc,KAAK,MAAM,YAAY,cAAc;AAAA,QACnD,YAAY,KAAK,MAAM,YAAY,YAAY;AAAA,QAC/C,WAAW,KAAK,MAAM,YAAY,WAAW;AAAA,QAC7C,WAAW,KAAK,MAAM,YAAY,WAAW;AAAA,QAC7C,WAAW,KAAK,MAAM,YAAY,WAAW;AAAA,QAC7C,cAAc,KAAK,MAAM,YAAY,cAAc;AAAA,QACnD,aAAa,KAAK,MAAM,YAAY,aAAa;AAAA,QACjD,iBAAiB,KAAK,MAAM,YAAY,iBAAiB;AAAA,QACzD,aAAa,KAAK,MAAM,YAAY,aAAa;AAAA,MAAA,GAChD,OAAO,QAAQ,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,KAAK,KAAK,MAAM;AACvD,YAAI,GAAG,IAAI,KAAK,MAAM,YAAY,KAAK;AAChC,eAAA;AAAA,MACT,GAAG,CAA4B,CAAA,CAAC;AAAA,IAAA;AAAA,EAEpC;AACF;;"}
1
+ {"version":3,"file":"index.cjs.js","sources":["../../../../src/modules/table-up/index.ts"],"sourcesContent":["import type Toolbar from 'quill/modules/toolbar'\r\nimport type BaseTheme from 'quill/themes/base'\r\nimport type Picker from 'quill/ui/picker'\r\nimport type { Constructor } from '../../config/types'\r\nimport type FluentEditor from '../../core/fluent-editor'\r\nimport { CHANGE_LANGUAGE_EVENT } from '../../config'\r\nimport { isFunction } from '../../utils/is'\r\n\r\ninterface QuillTheme extends BaseTheme {\r\n pickers: QuillThemePicker[]\r\n}\r\ntype QuillThemePicker = (Picker & { options: HTMLElement })\r\n\r\nexport function generateTableUp(QuillTableUp: Constructor) {\r\n return class extends QuillTableUp {\r\n constructor(public quill: FluentEditor, options: Partial<any>) {\r\n super(quill, options)\r\n\r\n if (!this.quill.options['format-painter']) this.quill.options['format-painter'] = {}\r\n const currentIgnoreFormat = this.quill.options['format-painter'].ignoreFormat || []\r\n this.quill.options['format-painter'].ignoreFormat = Array.from(\r\n new Set([\r\n ...currentIgnoreFormat,\r\n 'table-up-cell-inner',\r\n ]),\r\n )\r\n\r\n this.quill.emitter.on(CHANGE_LANGUAGE_EVENT, () => {\r\n this.options.texts = this.resolveTexts(options.texts)\r\n const toolbar = this.quill.getModule('toolbar') as Toolbar\r\n if (toolbar && (this.quill.theme as QuillTheme).pickers) {\r\n const [, select] = (toolbar.controls as [string, HTMLElement][] || []).find(([name]) => name === this.statics.toolName) || []\r\n if (select && select.tagName.toLocaleLowerCase() === 'select') {\r\n const picker = (this.quill.theme as QuillTheme).pickers.find(picker => picker.select === select)\r\n if (picker) {\r\n this.buildCustomSelect(this.options.customSelect, picker)\r\n }\r\n }\r\n }\r\n\r\n Object.keys(this.modules).forEach((key) => {\r\n if (isFunction(this.modules[key].destroy)) {\r\n this.modules[key].destroy()\r\n }\r\n })\r\n this.modules = {}\r\n this.initModules()\r\n })\r\n }\r\n\r\n resolveTexts(options: Record<string, string> = {}) {\r\n return Object.assign({\r\n fullCheckboxText: this.quill.getLangText('fullCheckboxText'),\r\n customBtnText: this.quill.getLangText('customBtnText'),\r\n confirmText: this.quill.getLangText('confirmText'),\r\n cancelText: this.quill.getLangText('cancelText'),\r\n rowText: this.quill.getLangText('rowText'),\r\n colText: this.quill.getLangText('colText'),\r\n notPositiveNumberError: this.quill.getLangText('notPositiveNumberError'),\r\n custom: this.quill.getLangText('custom'),\r\n clear: this.quill.getLangText('clear'),\r\n transparent: this.quill.getLangText('transparent'),\r\n perWidthInsufficient: this.quill.getLangText('perWidthInsufficient'),\r\n CopyCell: this.quill.getLangText('CopyCell'),\r\n CutCell: this.quill.getLangText('CutCell'),\r\n InsertTop: this.quill.getLangText('InsertTop'),\r\n InsertRight: this.quill.getLangText('InsertRight'),\r\n InsertBottom: this.quill.getLangText('InsertBottom'),\r\n InsertLeft: this.quill.getLangText('InsertLeft'),\r\n MergeCell: this.quill.getLangText('MergeCell'),\r\n SplitCell: this.quill.getLangText('SplitCell'),\r\n DeleteRow: this.quill.getLangText('DeleteRow'),\r\n DeleteColumn: this.quill.getLangText('DeleteColumn'),\r\n DeleteTable: this.quill.getLangText('DeleteTable'),\r\n BackgroundColor: this.quill.getLangText('BackgroundColor'),\r\n BorderColor: this.quill.getLangText('BorderColor'),\r\n }, Object.entries(options).reduce((pre, [key, value]) => {\r\n pre[key] = this.quill.getLangText(value)\r\n return pre\r\n }, {} as Record<string, string>))\r\n }\r\n }\r\n}\r\n"],"names":["CHANGE_LANGUAGE_EVENT","picker","isFunction"],"mappings":"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"}
@@ -36,7 +36,7 @@ class BetterToolbar extends Toolbar {
36
36
  } else if (editor_utils.isNullOrUndefined(formats[format])) {
37
37
  option = select.querySelector("option[selected]");
38
38
  } else if (!Array.isArray(formats[format])) {
39
- let value = format === "header" ? formats[format].value : formats[format];
39
+ let value = formats[format];
40
40
  if (typeof value === "string") {
41
41
  value = value.replace(/"/g, '\\"');
42
42
  }
@@ -1 +1 @@
1
- {"version":3,"file":"better-toolbar.cjs.js","sources":["../../../../src/modules/toolbar/better-toolbar.ts"],"sourcesContent":["import type TypeToolbar from 'quill/modules/toolbar'\r\nimport Quill from 'quill'\r\nimport { isNullOrUndefined } from '../../config/editor.utils'\r\n\r\nconst Delta = Quill.import('delta')\r\nconst Parchment = Quill.import('parchment')\r\nconst levels = ['error', 'warn', 'log', 'info'] as const\r\nlet level = 'warn' as const\r\n\r\nfunction debuglogger(method: typeof levels[number], ...args) {\r\n if (levels.indexOf(method) <= levels.indexOf(level)) {\r\n console[method](...args) // eslint-disable-line no-console\r\n }\r\n}\r\n\r\nfunction namespace(ns) {\r\n return levels.reduce((logger, method) => {\r\n logger[method] = debuglogger.bind(console, method, ns)\r\n return logger\r\n }, {} as Record<typeof levels[number], (...args: any) => void>)\r\n}\r\n\r\nnamespace.level = (newLevel) => {\r\n level = newLevel\r\n}\r\ndebuglogger.level = namespace.level\r\nconst debug = namespace('quill:toolbar')\r\n\r\nconst Toolbar = Quill.import('modules/toolbar') as typeof TypeToolbar\r\n\r\nexport class BetterToolbar extends Toolbar {\r\n update(range) {\r\n const formats = isNullOrUndefined(range) ? {} : this.quill.getFormat(range) as Record<string, any>\r\n this.controls.forEach((pair) => {\r\n const [format, input] = pair\r\n if (input.tagName === 'SELECT') {\r\n const select = input as HTMLSelectElement\r\n let option\r\n if (isNullOrUndefined(range)) {\r\n option = null\r\n }\r\n else if (isNullOrUndefined(formats[format])) {\r\n option = select.querySelector('option[selected]')\r\n }\r\n else if (!Array.isArray(formats[format])) {\r\n let value = format === 'header' ? formats[format].value : formats[format]\r\n if (typeof value === 'string') {\r\n value = value.replace(/\"/g, '\\\\\"')\r\n }\r\n option = select.querySelector(`option[value=\"${value}\"]`)\r\n }\r\n if (isNullOrUndefined(option)) {\r\n select.value = '' // TODO make configurable?\r\n select.selectedIndex = -1\r\n }\r\n else {\r\n option.selected = true\r\n }\r\n }\r\n else if (isNullOrUndefined(range)) {\r\n input.classList.remove('ql-active')\r\n }\r\n else if (input.hasAttribute('value')) {\r\n // both being null should match (default values)\r\n // '1' should match with 1 (headers)\r\n let isActive\r\n = formats[format] === input.getAttribute('value')\r\n || (!isNullOrUndefined(formats[format])\r\n && (formats[format].value === input.getAttribute('value')\r\n || formats[format].toString() === input.getAttribute('value')))\r\n || (isNullOrUndefined(formats[format]) && !input.getAttribute('value'))\r\n\r\n if (!isActive) {\r\n const checkFormat = formats[format]\r\n if (checkFormat === 'checked' || checkFormat === 'unchecked') {\r\n isActive = input.getAttribute('value') === 'check'\r\n }\r\n }\r\n\r\n if (isActive) {\r\n input.classList.add('ql-active')\r\n }\r\n else {\r\n input.classList.remove('ql-active')\r\n }\r\n }\r\n else {\r\n if (!isNullOrUndefined(formats[format])) {\r\n input.classList.add('ql-active')\r\n }\r\n else {\r\n input.classList.remove('ql-active')\r\n }\r\n }\r\n })\r\n }\r\n\r\n attach(input: HTMLElement) {\r\n let format = Array.from(input.classList).find((className) => {\r\n return className.indexOf('ql-') === 0\r\n })\r\n if (!format) return\r\n format = format.slice('ql-'.length)\r\n\r\n if (input.tagName === 'BUTTON') {\r\n input.setAttribute('type', 'button')\r\n }\r\n\r\n if (this.handlers[format] == null && this.quill.scroll.query(format) == null) {\r\n debug.warn('ignoring attaching to nonexistent format', format, input)\r\n return\r\n }\r\n\r\n const eventName = input.tagName === 'SELECT' ? 'change' : 'click'\r\n input.addEventListener(eventName, (e) => {\r\n let value: boolean | string\r\n\r\n if (input.tagName === 'SELECT') {\r\n const select = input as HTMLSelectElement\r\n if (select.selectedIndex < 0) return\r\n const selected = select.options[select.selectedIndex]\r\n\r\n if (selected.hasAttribute('selected')) {\r\n value = false\r\n }\r\n else {\r\n value = selected.value || false\r\n }\r\n }\r\n else {\r\n const button = input as HTMLButtonElement\r\n if (button.classList.contains('ql-active')) {\r\n value = false\r\n }\r\n else {\r\n value = button.value || !button.hasAttribute('value')\r\n }\r\n e.preventDefault()\r\n }\r\n\r\n this.quill.focus({ preventScroll: format === 'screenshot' })\r\n const [range] = this.quill.selection.getRange()\r\n if (this.handlers[format] != null) {\r\n // @ts-ignore\r\n if (!isNullOrUndefined(window.quillIsIntable) && window.quillIsIntable === true && (format === 'blockquote' || format === 'code-block' || format === 'list' || format === 'indent' || format === 'clean')) {\r\n return\r\n }\r\n this.handlers[format].call(this, value)\r\n }\r\n else if (\r\n // @ts-ignore\r\n this.quill.scroll.query(format).prototype instanceof Parchment.EmbedBlot\r\n ) {\r\n value = prompt(`Enter ${format}`)\r\n if (!value) return\r\n this.quill.updateContents(\r\n new Delta()\r\n .retain(range.index)\r\n .delete(range.length)\r\n .insert({ [format]: value }),\r\n Quill.sources.USER,\r\n )\r\n }\r\n else {\r\n // @ts-ignore\r\n if (!isNullOrUndefined(window.quillIsIntable) && window.quillIsIntable === true && (format === 'blockquote' || format === 'code-block' || format === 'list' || format === 'indent' || format === 'clean')) {\r\n return\r\n }\r\n this.quill.format(format, value, Quill.sources.USER)\r\n }\r\n\r\n this.update(range)\r\n })\r\n this.controls.push([format, input])\r\n }\r\n}\r\n"],"names":["isNullOrUndefined"],"mappings":"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"}
1
+ {"version":3,"file":"better-toolbar.cjs.js","sources":["../../../../src/modules/toolbar/better-toolbar.ts"],"sourcesContent":["import type TypeToolbar from 'quill/modules/toolbar'\r\nimport Quill from 'quill'\r\nimport { isNullOrUndefined } from '../../config/editor.utils'\r\n\r\nconst Delta = Quill.import('delta')\r\nconst Parchment = Quill.import('parchment')\r\nconst levels = ['error', 'warn', 'log', 'info'] as const\r\nlet level = 'warn' as const\r\n\r\nfunction debuglogger(method: typeof levels[number], ...args) {\r\n if (levels.indexOf(method) <= levels.indexOf(level)) {\r\n console[method](...args) // eslint-disable-line no-console\r\n }\r\n}\r\n\r\nfunction namespace(ns) {\r\n return levels.reduce((logger, method) => {\r\n logger[method] = debuglogger.bind(console, method, ns)\r\n return logger\r\n }, {} as Record<typeof levels[number], (...args: any) => void>)\r\n}\r\n\r\nnamespace.level = (newLevel) => {\r\n level = newLevel\r\n}\r\ndebuglogger.level = namespace.level\r\nconst debug = namespace('quill:toolbar')\r\n\r\nconst Toolbar = Quill.import('modules/toolbar') as typeof TypeToolbar\r\n\r\nexport class BetterToolbar extends Toolbar {\r\n update(range) {\r\n const formats = isNullOrUndefined(range) ? {} : this.quill.getFormat(range) as Record<string, any>\r\n this.controls.forEach((pair) => {\r\n const [format, input] = pair\r\n if (input.tagName === 'SELECT') {\r\n const select = input as HTMLSelectElement\r\n let option\r\n if (isNullOrUndefined(range)) {\r\n option = null\r\n }\r\n else if (isNullOrUndefined(formats[format])) {\r\n option = select.querySelector('option[selected]')\r\n }\r\n else if (!Array.isArray(formats[format])) {\r\n let value = formats[format]\r\n if (typeof value === 'string') {\r\n value = value.replace(/\"/g, '\\\\\"')\r\n }\r\n option = select.querySelector(`option[value=\"${value}\"]`)\r\n }\r\n if (isNullOrUndefined(option)) {\r\n select.value = '' // TODO make configurable?\r\n select.selectedIndex = -1\r\n }\r\n else {\r\n option.selected = true\r\n }\r\n }\r\n else if (isNullOrUndefined(range)) {\r\n input.classList.remove('ql-active')\r\n }\r\n else if (input.hasAttribute('value')) {\r\n // both being null should match (default values)\r\n // '1' should match with 1 (headers)\r\n let isActive\r\n = formats[format] === input.getAttribute('value')\r\n || (!isNullOrUndefined(formats[format])\r\n && (formats[format].value === input.getAttribute('value')\r\n || formats[format].toString() === input.getAttribute('value')))\r\n || (isNullOrUndefined(formats[format]) && !input.getAttribute('value'))\r\n\r\n if (!isActive) {\r\n const checkFormat = formats[format]\r\n if (checkFormat === 'checked' || checkFormat === 'unchecked') {\r\n isActive = input.getAttribute('value') === 'check'\r\n }\r\n }\r\n\r\n if (isActive) {\r\n input.classList.add('ql-active')\r\n }\r\n else {\r\n input.classList.remove('ql-active')\r\n }\r\n }\r\n else {\r\n if (!isNullOrUndefined(formats[format])) {\r\n input.classList.add('ql-active')\r\n }\r\n else {\r\n input.classList.remove('ql-active')\r\n }\r\n }\r\n })\r\n }\r\n\r\n attach(input: HTMLElement) {\r\n let format = Array.from(input.classList).find((className) => {\r\n return className.indexOf('ql-') === 0\r\n })\r\n if (!format) return\r\n format = format.slice('ql-'.length)\r\n\r\n if (input.tagName === 'BUTTON') {\r\n input.setAttribute('type', 'button')\r\n }\r\n\r\n if (this.handlers[format] == null && this.quill.scroll.query(format) == null) {\r\n debug.warn('ignoring attaching to nonexistent format', format, input)\r\n return\r\n }\r\n\r\n const eventName = input.tagName === 'SELECT' ? 'change' : 'click'\r\n input.addEventListener(eventName, (e) => {\r\n let value: boolean | string\r\n\r\n if (input.tagName === 'SELECT') {\r\n const select = input as HTMLSelectElement\r\n if (select.selectedIndex < 0) return\r\n const selected = select.options[select.selectedIndex]\r\n\r\n if (selected.hasAttribute('selected')) {\r\n value = false\r\n }\r\n else {\r\n value = selected.value || false\r\n }\r\n }\r\n else {\r\n const button = input as HTMLButtonElement\r\n if (button.classList.contains('ql-active')) {\r\n value = false\r\n }\r\n else {\r\n value = button.value || !button.hasAttribute('value')\r\n }\r\n e.preventDefault()\r\n }\r\n\r\n this.quill.focus({ preventScroll: format === 'screenshot' })\r\n const [range] = this.quill.selection.getRange()\r\n if (this.handlers[format] != null) {\r\n // @ts-ignore\r\n if (!isNullOrUndefined(window.quillIsIntable) && window.quillIsIntable === true && (format === 'blockquote' || format === 'code-block' || format === 'list' || format === 'indent' || format === 'clean')) {\r\n return\r\n }\r\n this.handlers[format].call(this, value)\r\n }\r\n else if (\r\n // @ts-ignore\r\n this.quill.scroll.query(format).prototype instanceof Parchment.EmbedBlot\r\n ) {\r\n value = prompt(`Enter ${format}`)\r\n if (!value) return\r\n this.quill.updateContents(\r\n new Delta()\r\n .retain(range.index)\r\n .delete(range.length)\r\n .insert({ [format]: value }),\r\n Quill.sources.USER,\r\n )\r\n }\r\n else {\r\n // @ts-ignore\r\n if (!isNullOrUndefined(window.quillIsIntable) && window.quillIsIntable === true && (format === 'blockquote' || format === 'code-block' || format === 'list' || format === 'indent' || format === 'clean')) {\r\n return\r\n }\r\n this.quill.format(format, value, Quill.sources.USER)\r\n }\r\n\r\n this.update(range)\r\n })\r\n this.controls.push([format, input])\r\n }\r\n}\r\n"],"names":["isNullOrUndefined"],"mappings":"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"}