UNPKG

jodit-pro

Version:

PRO Version of Jodit Editor

1,336 lines (1,236 loc) 342 kB
/*! * jodit-pro - PRO Version of Jodit Editor * Author: Chupurnov Valerii <chupurnov@gmail.com> * Version: v4.9.27 * Url: https://xdsoft.net/jodit/pro/ * License(s): SEE LICENSE IN LICENSE.md */ (function webpackUniversalModuleDefinition(root, factory) { if(typeof exports === 'object' && typeof module === 'object') module.exports = factory(); else if(typeof define === 'function' && define.amd) define([], factory); else { var a = factory(); for(var i in a) (typeof exports === 'object' ? exports : root)[i] = a[i]; } })(self, function() { return (self["webpackChunkjodit_pro"] = self["webpackChunkjodit_pro"] || []).push([[830],{ /***/ 473: /***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) { "use strict"; /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ handleBackToList: function() { return /* binding */ handleBackToList; }, /* harmony export */ handleConversationSearch: function() { return /* binding */ handleConversationSearch; }, /* harmony export */ handleConversationSelect: function() { return /* binding */ handleConversationSelect; }, /* harmony export */ handleToolApproval: function() { return /* binding */ handleToolApproval; }, /* harmony export */ handleToolDenial: function() { return /* binding */ handleToolDenial; } /* harmony export */ }); /* harmony import */ var _tool_execution__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(79078); /* harmony import */ var jodit_esm_core_constants__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(82758); // import { setToolCallDenied } from './tool-execution'; /** * Handle tool call approval */ async function handleToolApproval(toolCallId, scope, stateManager, permissionManager, storage) { // Find the pending tool call const pendingToolCalls = stateManager.getPendingToolCalls(); const toolCall = pendingToolCalls.find((tc)=>tc.id === toolCallId); if (!toolCall) { console.error('Tool call not found:', toolCallId); return null; } // Grant permission using tool name (not id) const permission = permissionManager.grantPermission(toolCall.name, scope); const conversation = stateManager.getCurrentConversation(); if (conversation) { conversation.permissions.push(permission); await storage.save(conversation); } // Remove from pending stateManager.removePendingToolCall(toolCallId); return { ...toolCall, status: 'approved' }; } /** * Handle tool call denial */ async function handleToolDenial(jodit, toolCallId, stateManager, storage) { // Find the pending tool call const pendingToolCalls = stateManager.getPendingToolCalls(); const toolCall = pendingToolCalls.find((tc)=>tc.id === toolCallId); if (!toolCall) { return; } let conversation = stateManager.getCurrentConversation(); if (!conversation) { return; } const messagIndex = conversation.messages.findIndex((msg)=>msg.toolCalls?.find((tc)=>tc.id === toolCallId)); if (messagIndex === -1) { return; } const message = conversation.messages[messagIndex]; // Update tool call with denial result const toolCallDenied = { ...toolCall, result: { error: 'Tool call denied by user.' }, status: 'denied' }; const newMessage = (0,_tool_execution__WEBPACK_IMPORTED_MODULE_0__.updateToolCallInMessage)(message, toolCallDenied); const messages = [ ...conversation.messages ]; messages[messagIndex] = newMessage; conversation = { ...conversation, messages, updated: Date.now() }; // Save conversation and update UI stateManager.setCurrentConversation(conversation); await storage.save(conversation); // Emit event for UI to show denied state jodit.e.fire('toolCallDenied.ai-assistant-pro', toolCall); // Remove from pending stateManager.removePendingToolCall(toolCallId); } /** * Handle conversation selection */ async function handleConversationSelect(conversationId, storage, stateManager) { try { stateManager.setLoading(true); const conv = await storage.get(conversationId); if (!conv) { throw new Error('Conversation not found: ' + conversationId); } stateManager.setCurrentConversation({ ...conv }); } catch (error) { if (!jodit_esm_core_constants__WEBPACK_IMPORTED_MODULE_1__.IS_PROD) { console.warn(error); } } finally{ stateManager.setLoading(false); } } /** * Handle back to conversation list */ async function handleBackToList(storage, stateManager) { stateManager.setView('conversationList'); try { stateManager.setLoading(true); const conversations = await storage.list().catch(()=>[]); stateManager.setConversations(conversations); } finally{ stateManager.setLoading(false); } } /** * Handle conversation search */ async function handleConversationSearch(query, storage, stateManager) { stateManager.setLoading(true); try { // Search in storage (includes title and message content) const conversations = await storage.list(query || undefined); // Update state stateManager.setConversations(conversations); // Update view stateManager.setView('conversationList'); } finally{ // Clear loading state stateManager.setLoading(false); } } /***/ }), /***/ 2020: /***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) { "use strict"; /* harmony import */ var _definition__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(96675); /* harmony import */ var _executor__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3091); /* harmony default export */ __webpack_exports__["default"] = ({ ..._definition__WEBPACK_IMPORTED_MODULE_0__, execute: _executor__WEBPACK_IMPORTED_MODULE_1__.execute }); /***/ }), /***/ 3091: /***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) { "use strict"; /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ execute: function() { return /* binding */ execute; } /* harmony export */ }); /* harmony import */ var _core_abort_utils__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(6689); /* harmony import */ var _core_arg_utils__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(49459); /* harmony import */ var _core_block_utils__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(44526); /* harmony import */ var jodit_esm_core_dom__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(22732); /** * Replace a specific block in the document by its index or CSS selector */ async function execute(jodit, args, signal) { const { index, selector } = args; const html = (0,_core_arg_utils__WEBPACK_IMPORTED_MODULE_1__.requireString)(args, 'html'); (0,_core_abort_utils__WEBPACK_IMPORTED_MODULE_0__.throwIfAborted)(signal); // Validate that either index or selector is provided, but not both if (index === undefined && !selector || index !== undefined && selector) { throw new Error('Either index or selector must be provided, but not both'); } const { element: targetBlock, identifier: identifierMessage } = (0,_core_block_utils__WEBPACK_IMPORTED_MODULE_2__.resolveBlock)(jodit, { selector, index, requireExactlyOne: true }); try { // Create new element from HTML const tempDiv = jodit.createInside.div(); tempDiv.innerHTML = html; const newBlock = tempDiv.firstChild; if (!newBlock || !jodit_esm_core_dom__WEBPACK_IMPORTED_MODULE_3__.Dom.isElement(newBlock)) { throw new Error('Invalid HTML: must contain at least one element'); } // Replace the block targetBlock.parentNode?.replaceChild(newBlock, targetBlock); jodit.synchronizeValues(); return { success: true, message: `Block ${identifierMessage} replaced successfully` }; } catch (error) { throw new Error(`Failed to replace block ${identifierMessage}: ${error.message}`); } } /***/ }), /***/ 5618: /***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) { "use strict"; /* harmony import */ var _definition__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(93809); /* harmony import */ var _executor__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(28157); /* harmony default export */ __webpack_exports__["default"] = ({ ..._definition__WEBPACK_IMPORTED_MODULE_0__, execute: _executor__WEBPACK_IMPORTED_MODULE_1__.execute }); /***/ }), /***/ 6689: /***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) { "use strict"; /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ throwIfAborted: function() { return /* binding */ throwIfAborted; } /* harmony export */ }); /* harmony import */ var jodit_esm_core_helpers_utils_error_errors_abort_error__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(93661); /** * Throw AbortError if signal is already aborted. */ function throwIfAborted(signal) { if (signal.aborted) { throw (0,jodit_esm_core_helpers_utils_error_errors_abort_error__WEBPACK_IMPORTED_MODULE_0__.abort)(); } } /***/ }), /***/ 12570: /***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) { "use strict"; /* harmony import */ var _definition__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(62553); /* harmony import */ var _executor__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(56293); /* harmony default export */ __webpack_exports__["default"] = ({ ..._definition__WEBPACK_IMPORTED_MODULE_0__, execute: _executor__WEBPACK_IMPORTED_MODULE_1__.execute }); /***/ }), /***/ 15927: /***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) { "use strict"; /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ getCurrentSelectionContext: function() { return /* binding */ getCurrentSelectionContext; } /* harmony export */ }); /** * Add current selection to context */ function getCurrentSelectionContext(jodit) { if (jodit.s.isCollapsed()) { return null; } const html = jodit.s.html; const range = jodit.s.range; return { html, rangeInfo: { startContainer: range.startContainer.nodeName, startOffset: range.startOffset, endContainer: range.endContainer.nodeName, endOffset: range.endOffset } }; } /***/ }), /***/ 16015: /***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) { "use strict"; /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ UIDialogSettings: function() { return /* binding */ UIDialogSettings; } /* harmony export */ }); /* harmony import */ var _swc_helpers_define_property__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(82749); /* harmony import */ var _swc_helpers_ts_decorate__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(31635); /* harmony import */ var jodit_esm_core_decorators__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(65478); /* harmony import */ var jodit_esm_core_ui__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(67510); /* harmony import */ var jodit_esm_core_ui_form_inputs_select_select__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(12234); /* harmony import */ var jodit_pro_core_ui_form_range_range__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(62357); class UIDialogSettings extends jodit_esm_core_ui__WEBPACK_IMPORTED_MODULE_3__.UIElement { className() { return 'UIDialogSettings'; } setParentView(view) { this.modelSelect?.setParentView(view); this.temperatureInput?.setParentView(view); return super.setParentView(view); } /** * Build settings fields */ async build() { const fieldsContainer = this.container; const currentOptions = this.getConversation().options || {}; // Model selector if (this.options.dialogSettings.models && this.options.dialogSettings.models.length > 0) { const models = this.parseModels(this.options.dialogSettings.models); const currentModel = currentOptions.model ?? this.state.defaultModel ?? models[0].value; this.modelSelect = new jodit_esm_core_ui_form_inputs_select_select__WEBPACK_IMPORTED_MODULE_4__.UISelect(this.j, { label: 'Model', name: 'model', value: currentModel, options: models, onChange: this.onModelChange.bind(this) }); fieldsContainer.appendChild(this.modelSelect.container); } // Temperature slider if (this.options.dialogSettings.temperature) { this.temperatureInput = new jodit_pro_core_ui_form_range_range__WEBPACK_IMPORTED_MODULE_5__.UIRange(this.j, { label: 'Temperature', name: 'temperature', min: this.options.dialogSettings.temperature.min, max: this.options.dialogSettings.temperature.max, value: currentOptions.temperature ?? this.state.defaultTemperature ?? 0.5, onChange: this.onTemperatureChange.bind(this) }); // Set step attribute directly on the input element const inputElement = this.temperatureInput.nativeInput; if (inputElement) { inputElement.setAttribute('step', String(this.options.dialogSettings.temperature.step ?? 0.1)); } fieldsContainer.appendChild(this.temperatureInput.container); } } /** * Parse models from config (supports both formats) */ parseModels(models) { if (models.length === 0) { return []; } // Check if it's array of objects with title/value if (typeof models[0] === 'object' && 'title' in models[0]) { return models.map((m)=>({ text: m.title, value: m.value })); } // It's array of strings return models.map((m)=>({ text: m, value: m })); } /** * Handle model change */ async onModelChange() { if (!this.modelSelect) return; const newModel = this.modelSelect.value; this.updateConversation({ options: { ...this.getConversation().options, model: newModel } }); this.state.defaultModel = newModel; } /** * Handle temperature change */ async onTemperatureChange() { if (!this.temperatureInput) return; const newTemp = parseFloat(this.temperatureInput.value); this.updateConversation({ options: { ...this.getConversation().options, temperature: newTemp } }); this.state.defaultTemperature = newTemp; } onDefaultModelChange() { if (this.modelSelect && this.state.defaultModel) { this.modelSelect.value = this.state.defaultModel; } } onDefaultTemperatureChange() { if (this.temperatureInput && this.state.defaultTemperature !== undefined) { this.temperatureInput.value = String(this.state.defaultTemperature); } } destruct() { this.modelSelect?.destruct(); super.destruct(); } constructor(jodit, getConversation, updateConversation, state, options){ super(jodit), (0,_swc_helpers_define_property__WEBPACK_IMPORTED_MODULE_0__._)(this, "getConversation", void 0), (0,_swc_helpers_define_property__WEBPACK_IMPORTED_MODULE_0__._)(this, "updateConversation", void 0), (0,_swc_helpers_define_property__WEBPACK_IMPORTED_MODULE_0__._)(this, "state", void 0), (0,_swc_helpers_define_property__WEBPACK_IMPORTED_MODULE_0__._)(this, "options", void 0), (0,_swc_helpers_define_property__WEBPACK_IMPORTED_MODULE_0__._)(this, "modelSelect", void 0), (0,_swc_helpers_define_property__WEBPACK_IMPORTED_MODULE_0__._)(this, "temperatureInput", void 0), this.getConversation = getConversation, this.updateConversation = updateConversation, this.state = state, this.options = options, this.modelSelect = null, this.temperatureInput = null; this.build(); } } (0,_swc_helpers_ts_decorate__WEBPACK_IMPORTED_MODULE_1__.__decorate)([ (0,jodit_esm_core_decorators__WEBPACK_IMPORTED_MODULE_2__.watch)('state.defaultModel') ], UIDialogSettings.prototype, "onDefaultModelChange", null); (0,_swc_helpers_ts_decorate__WEBPACK_IMPORTED_MODULE_1__.__decorate)([ (0,jodit_esm_core_decorators__WEBPACK_IMPORTED_MODULE_2__.watch)('state.defaultTemperature') ], UIDialogSettings.prototype, "onDefaultTemperatureChange", null); UIDialogSettings = (0,_swc_helpers_ts_decorate__WEBPACK_IMPORTED_MODULE_1__.__decorate)([ jodit_esm_core_decorators__WEBPACK_IMPORTED_MODULE_2__.component ], UIDialogSettings); /***/ }), /***/ 16745: /***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) { "use strict"; /* harmony import */ var _definition__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(56732); /* harmony import */ var _executor__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(39592); /* harmony default export */ __webpack_exports__["default"] = ({ ..._definition__WEBPACK_IMPORTED_MODULE_0__, execute: _executor__WEBPACK_IMPORTED_MODULE_1__.execute }); /***/ }), /***/ 18549: /***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) { "use strict"; /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ UIFlightPosition: function() { return /* binding */ UIFlightPosition; } /* harmony export */ }); /* harmony import */ var jodit_esm_core_dom_dom__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2748); /*! * Jodit Editor PRO (https://xdsoft.net/jodit/) * See LICENSE.md in the project root for license information. * Copyright (c) 2013-2026 Valerii Chupurnov. All rights reserved. https://xdsoft.net/jodit/pro/ */ const flightStates = new WeakMap(); /** * Flight Position Trait * * Provides smart positioning for a panel relative to an anchor element: * - Panel is always position: fixed * - Width matches anchor element width * - Horizontal position matches anchor element * - Vertical position: stays at viewport bottom OR just below anchor (whichever is higher) * * Similar to TinyMCE AI Assistant positioning behavior. */ class UIFlightPosition { /** * Install flight positioning on an element * @param element - The UI element to position * @param anchor - The anchor element (usually editor container) to track * @param options - Configuration options */ static install(element, anchor, options = {}) { // Remove existing installation if any this.remove(element); const { panelHeight = 250, containerClassName, toolbarSelector = '.jodit-toolbar__box', gap = 16 } = options; // Create container for the panel const container = element.j.c.div('jodit-flight-position'); if (containerClassName) { container.classList.add(containerClassName); } container.style.setProperty('--flight-panel-height', `${panelHeight}px`); // Append panel to container container.appendChild(element.container); // Append container to body document.body.appendChild(container); const stateOptions = { toolbarSelector, gap }; // Create scroll/resize handler const onScroll = ()=>{ this.updatePosition(container, anchor, stateOptions); }; // Create ResizeObserver for anchor size changes const resizeObserver = new ResizeObserver(onScroll); resizeObserver.observe(anchor); // Listen to scroll and resize events window.addEventListener('scroll', onScroll, { passive: true }); window.addEventListener('resize', onScroll, { passive: true }); // Store state const state = { container, anchor, onScroll, resizeObserver, options: stateOptions }; flightStates.set(element, state); // Initial position this.updatePosition(container, anchor, stateOptions); } /** * Update container position based on anchor element * * Positioning logic: * 1. If editor is small and panel doesn't fit → top at toolbar bottom * 2. If fits and editor bottom visible → bottom at editor bottom - gap * 3. If editor bottom not visible → bottom at viewport bottom - gap */ static updatePosition(container, anchor, options) { const { toolbarSelector, gap } = options; const anchorRect = anchor.getBoundingClientRect(); const viewportHeight = window.innerHeight; const panelHeight = container.offsetHeight || 250; // Calculate horizontal position (match anchor) container.style.left = `${anchorRect.left}px`; container.style.width = `${anchorRect.width}px`; // Find toolbar to get its bottom position const toolbar = anchor.querySelector(toolbarSelector); const toolbarBottom = toolbar ? toolbar.getBoundingClientRect().bottom : anchorRect.top; // Available space between toolbar bottom and editor bottom const availableSpace = anchorRect.bottom - toolbarBottom; // Check if panel fits in available space const panelFits = panelHeight <= availableSpace; // Check if editor bottom is visible in viewport const editorBottomVisible = anchorRect.bottom <= viewportHeight; if (!panelFits) { // Case 1: Editor is small and panel doesn't fit // Position panel top at toolbar bottom container.style.bottom = 'auto'; container.style.top = `${toolbarBottom}px`; } else if (editorBottomVisible) { // Case 2: Panel fits and editor bottom is visible // Position panel bottom at editor bottom - gap container.style.top = 'auto'; container.style.bottom = `${viewportHeight - anchorRect.bottom + gap}px`; } else { // Case 3: Editor bottom is not visible (scrolled below viewport) // Position panel bottom at viewport bottom - gap container.style.top = 'auto'; container.style.bottom = `${gap}px`; } } /** * Remove flight positioning from an element */ static remove(element) { const state = flightStates.get(element); if (state) { // Remove event listeners window.removeEventListener('scroll', state.onScroll); window.removeEventListener('resize', state.onScroll); // Disconnect observer state.resizeObserver.disconnect(); // Move panel out of container before removing if (element.container.parentNode === state.container) { state.container.parentNode?.insertBefore(element.container, state.container); } // Remove container jodit_esm_core_dom_dom__WEBPACK_IMPORTED_MODULE_0__.Dom.safeRemove(state.container); // Remove state flightStates.delete(element); } } /** * Check if element has flight positioning installed */ static isInstalled(element) { return flightStates.has(element); } /** * Update panel height */ static setPanelHeight(element, height) { const state = flightStates.get(element); if (state) { state.container.style.setProperty('--flight-panel-height', `${height}px`); } } /** * Force position recalculation */ static refresh(element) { const state = flightStates.get(element); if (state) { this.updatePosition(state.container, state.anchor, state.options); } } } /***/ }), /***/ 19127: /***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) { "use strict"; /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ execute: function() { return /* binding */ execute; } /* harmony export */ }); /* harmony import */ var _core_abort_utils__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(6689); /* harmony import */ var _core_block_utils__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(44526); /** * Apply or remove inline formatting to the current selection or specific block */ async function execute(jodit, args, signal) { const { element, attributes, remove, blockIndex, selector } = args; (0,_core_abort_utils__WEBPACK_IMPORTED_MODULE_0__.throwIfAborted)(signal); if (!element && !attributes) { throw new Error('Either "element" or "attributes" must be provided'); } jodit.s.restore(); // If blockIndex or selector is provided, select that block first if (blockIndex !== undefined || selector) { const { element } = (0,_core_block_utils__WEBPACK_IMPORTED_MODULE_1__.resolveBlock)(jodit, { selector, index: blockIndex }); jodit.s.select(element); } if (jodit.s.isCollapsed()) { throw new Error('No selection found. Please select text to format'); } try { const styleOptions = {}; if (element) { styleOptions.element = element; } if (attributes) { styleOptions.attributes = attributes; } // Use commitStyle to apply or remove formatting if (remove) { // To remove styling, we need to pass the same style with a special flag // or use applyStyle with opposite action jodit.s.commitStyle({ ...styleOptions, mode: 'remove' }); } else { jodit.s.commitStyle(styleOptions); } // Synchronize the editor value jodit.synchronizeValues(); return { success: true, message: remove ? 'Formatting removed successfully' : 'Formatting applied successfully' }; } catch (error) { throw new Error(`Failed to ${remove ? 'remove' : 'apply'} formatting: ${error.message}`); } } /***/ }), /***/ 21020: /***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) { "use strict"; /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ PermissionManager: function() { return /* binding */ PermissionManager; } /* harmony export */ }); /* harmony import */ var _swc_helpers_define_property__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(82749); /** * Permission Manager - handles tool execution permissions */ class PermissionManager { /** * Check if tool requires permission */ requiresPermission(toolName) { const { autoApproveTools, alwaysDenyTools } = this.jodit.o.aiAssistantPro; // Always deny takes precedence if (alwaysDenyTools.includes(toolName)) { return true; // Will be denied anyway } // Auto-approve tools don't need permission dialog if (autoApproveTools.includes(toolName)) { return false; } // Check tool definition const tool = this.toolRegistry.getTool(toolName); if (tool) { return tool.requiresPermission; } // Default to requiring permission for unknown tools return true; } /** * Check if tool is auto-denied */ isAutoDenied(toolName) { const { alwaysDenyTools } = this.jodit.o.aiAssistantPro; return alwaysDenyTools.includes(toolName); } /** * Check if tool is auto-approved */ isAutoApproved(toolName) { const { autoApproveTools } = this.jodit.o.aiAssistantPro; return autoApproveTools.includes(toolName); } /** * Check if permission is already granted * @param toolName - Tool name * @param conversationPermissions - Permissions from current conversation * @returns Permission if granted, null otherwise */ checkPermission(toolName, conversationPermissions) { // Check if auto-approved if (this.isAutoApproved(toolName)) { return { toolName, granted: true, grantedAt: Date.now(), scope: 'once' }; } // Check if auto-denied if (this.isAutoDenied(toolName)) { return null; } // Check permanent permissions const permanent = this.permanentPermissions.get(toolName); if (permanent && permanent.granted) { return permanent; } // Check conversation permissions const conversationPerm = conversationPermissions.find((p)=>p.toolName === toolName && p.granted); if (conversationPerm) { return conversationPerm; } return null; } /** * Grant permission for a tool */ grantPermission(toolName, scope) { const permission = { toolName, granted: true, grantedAt: Date.now(), scope }; // Save permanent permissions if (scope === 'forever') { this.permanentPermissions.set(toolName, permission); this.savePermanentPermissions(); } return permission; } /** * Deny permission for a tool */ denyPermission(toolName) { return { toolName, granted: false, grantedAt: Date.now(), scope: 'once' }; } /** * Filter tool calls based on permissions * Returns array of [approved, needsPermission] */ filterToolCalls(toolCalls, conversationPermissions) { const approved = []; const needsPermission = []; const denied = []; for (const toolCall of toolCalls){ // Check if denied if (this.isAutoDenied(toolCall.name)) { denied.push({ ...toolCall, status: 'denied' }); continue; } // Check if approved if (this.isAutoApproved(toolCall.name)) { approved.push({ ...toolCall, status: 'approved' }); continue; } // Check existing permission const permission = this.checkPermission(toolCall.name, conversationPermissions); if (permission && permission.granted) { approved.push({ ...toolCall, status: 'approved' }); } else if (this.requiresPermission(toolCall.name)) { this.jodit.e.fire('permissionRequested.ai-assistant-pro', toolCall.name); needsPermission.push({ ...toolCall, status: 'pending' }); } else { // No permission required approved.push({ ...toolCall, status: 'approved' }); } } return { approved, needsPermission, denied }; } /** * Clear conversation-specific permissions */ clearConversationPermissions(conversationId) { // Conversation permissions are stored in the conversation object itself // This method is called when a conversation is deleted // Nothing needs to be done here as the conversation is already being deleted // This is a placeholder for future implementation if needed } /** * Load permanent permissions from localStorage */ loadPermanentPermissions() { try { const data = localStorage.getItem(this.PERMANENT_PERMISSIONS_KEY); if (data) { const permissions = JSON.parse(data); permissions.forEach((perm)=>{ if (perm.scope === 'forever' && perm.granted) { this.permanentPermissions.set(perm.toolName, perm); } }); } } catch (error) { console.error('Failed to load permanent permissions:', error); } } /** * Save permanent permissions to localStorage */ savePermanentPermissions() { try { const permissions = Array.from(this.permanentPermissions.values()); localStorage.setItem(this.PERMANENT_PERMISSIONS_KEY, JSON.stringify(permissions)); } catch (error) { console.error('Failed to save permanent permissions:', error); } } /** * Clear all permanent permissions */ clearPermanentPermissions() { this.permanentPermissions.clear(); localStorage.removeItem(this.PERMANENT_PERMISSIONS_KEY); } /** * Get all permanent permissions */ getPermanentPermissions() { return Array.from(this.permanentPermissions.values()); } /** * Revoke permanent permission */ revokePermanentPermission(toolName) { this.permanentPermissions.delete(toolName); this.savePermanentPermissions(); } /** * Destroy manager */ destruct() { this.permanentPermissions.clear(); } constructor(jodit, toolRegistry){ (0,_swc_helpers_define_property__WEBPACK_IMPORTED_MODULE_0__._)(this, "jodit", void 0); (0,_swc_helpers_define_property__WEBPACK_IMPORTED_MODULE_0__._)(this, "toolRegistry", void 0); // Permanent permissions (scope: 'forever') (0,_swc_helpers_define_property__WEBPACK_IMPORTED_MODULE_0__._)(this, "permanentPermissions", void 0); // Storage key for permanent permissions (0,_swc_helpers_define_property__WEBPACK_IMPORTED_MODULE_0__._)(this, "PERMANENT_PERMISSIONS_KEY", void 0); this.jodit = jodit; this.toolRegistry = toolRegistry; this.permanentPermissions = new Map(); this.PERMANENT_PERMISSIONS_KEY = 'jodit-permissions.ai-assistant-pro'; this.loadPermanentPermissions(); } } /***/ }), /***/ 21064: /***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) { "use strict"; /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ WebStorageConversationStorage: function() { return /* binding */ WebStorageConversationStorage; }, /* harmony export */ createStorageProvider: function() { return /* binding */ createStorageProvider; }, /* harmony export */ generateConversationId: function() { return /* binding */ generateConversationId; }, /* harmony export */ generateMessageId: function() { return /* binding */ generateMessageId; } /* harmony export */ }); /* unused harmony export generateToolCallId */ /* harmony import */ var _swc_helpers_define_property__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(82749); /* harmony import */ var jodit_esm_core_storage_async_storage__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(97333); /** * Unified web storage implementation using Jodit's storage providers * Supports localStorage, sessionStorage, and memory storage */ class WebStorageConversationStorage { close() { return this.storage.close(); } /** * Get storage key for a conversation ID */ getConversationKey(id) { return `conv:${id}`; } /** * Get all conversation IDs from storage */ async getAllKeys() { // Since Jodit's storage doesn't provide a way to list keys, // we store a special index key const indexKey = '__conversation_index__'; const index = await this.storage.get(indexKey); if (index && Array.isArray(index)) { return index; } return []; } /** * Update conversation index */ async updateIndex(conversationIds) { const indexKey = '__conversation_index__'; await this.storage.set(indexKey, conversationIds); } /** * Add conversation ID to index */ async addToIndex(conversationId) { const index = await this.getAllKeys(); if (!index.includes(conversationId)) { index.push(conversationId); await this.updateIndex(index); } } /** * Remove conversation ID from index */ async removeFromIndex(conversationId) { const index = await this.getAllKeys(); const updated = index.filter((id)=>id !== conversationId); await this.updateIndex(updated); } /** * List all conversations * @param query - Optional search query to filter conversations by title and message content */ async list(query) { try { const keys = await this.getAllKeys(); const conversations = []; const lowerQuery = query?.toLowerCase().trim(); for (const id of keys){ const conversation = await this.get(id); if (!conversation) { continue; } // If query is provided, filter by title and message content if (lowerQuery) { const titleMatches = conversation.title?.toLowerCase().includes(lowerQuery); // Search in message content const messageMatches = !titleMatches && conversation.messages.some((msg)=>msg.content.toLowerCase().includes(lowerQuery)); // Skip if no matches if (!titleMatches && !messageMatches) { continue; } } conversations.push({ id: conversation.id, title: conversation.title ?? '', created: conversation.created, updated: conversation.updated, messageCount: conversation.messages.length }); } // Sort by updated timestamp (newest first) conversations.sort((a, b)=>b.updated - a.updated); return conversations; } catch (error) { console.error('Failed to list conversations:', error); return []; } } /** * Get a specific conversation by ID */ async get(id) { try { const key = this.getConversationKey(id); const conversation = await this.storage.get(key); return conversation || null; } catch (error) { console.error(`Failed to get conversation ${id}:`, error); return null; } } /** * Save or update a conversation */ async save(conversation) { try { if (conversation.messages.length === 0) { throw new Error('Conversation must have at least one message.'); } // Check if we need to evict old conversations const keys = await this.getAllKeys(); if (keys.length >= this.maxConversations && !keys.includes(conversation.id)) { await this.evictOldest(); } // Save conversation const key = this.getConversationKey(conversation.id); await this.storage.set(key, conversation); // Add to index await this.addToIndex(conversation.id); } catch (error) { // Handle quota exceeded error (for localStorage/sessionStorage) if (error?.name === 'QuotaExceededError' || error?.code === 22 || error?.code === 1014) { // Try to free up space by evicting oldest await this.evictOldest(); // Retry save try { const key = this.getConversationKey(conversation.id); await this.storage.set(key, conversation); await this.addToIndex(conversation.id); } catch (retryError) { throw new Error('Storage quota exceeded. Please delete some conversations.'); } } else { throw new Error(`Failed to save conversation: ${error?.message || 'Unknown error'}`); } } } /** * Delete a conversation */ async delete(id) { try { const key = this.getConversationKey(id); await this.storage.delete(key); await this.removeFromIndex(id); } catch (error) { console.error(`Failed to delete conversation ${id}:`, error); } } /** * Clear all conversations */ async clear() { try { const keys = await this.getAllKeys(); for (const id of keys){ const key = this.getConversationKey(id); await this.storage.delete(key); } // Clear index await this.updateIndex([]); } catch (error) { console.error('Failed to clear conversations:', error); } } /** * Evict oldest conversation (LRU) */ async evictOldest() { try { const conversations = await this.list(); if (conversations.length === 0) { return; } // Sort by updated timestamp (oldest first) conversations.sort((a, b)=>a.updated - b.updated); // Delete oldest const oldest = conversations[0]; await this.delete(oldest.id); } catch (error) { console.error('Failed to evict oldest conversation:', error); } } /** * Get global settings */ async getGlobalSettings() { try { const settings = await this.storage.get('global-settings'); return settings || null; } catch (error) { return null; } } /** * Save global settings */ async saveGlobalSettings(settings) { try { const previous = await this.getGlobalSettings(); const merged = { ...previous, ...settings }; await this.storage.set('global-settings', merged); } catch (error) { console.error('Failed to save global settings:', error); throw new Error('Failed to save global settings'); } } constructor(storageProvider, maxConversations){ (0,_swc_helpers_define_property__WEBPACK_IMPORTED_MODULE_0__._)(this, "storage", void 0); (0,_swc_helpers_define_property__WEBPACK_IMPORTED_MODULE_0__._)(this, "maxConversations", void 0); this.storage = storageProvider; this.maxConversations = maxConversations; } } /** * Factory functions to create storage providers using Jodit's Storage */ function createStorageProvider(type, rootKey) { return jodit_esm_core_storage_async_storage__WEBPACK_IMPORTED_MODULE_1__.AsyncStorage.makeStorage(type, rootKey); } /** * Generate unique conversation ID */ function generateConversationId() { return `conv_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`; } /** * Generate unique message ID */ function generateMessageId() { return `msg_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`; } /** * Generate unique tool call ID */ function generateToolCallId() { return `tc_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`; } /***/ }), /***/ 26612: /***/ (function(module) { module.exports = { // Plugin name 'AI Assistant': 'AI Assistant', 'AI Assistant Pro': 'AI Assistant Pro', // Welcome screen 'Welcome to AI Assistant': 'Welcome to AI Assistant', 'Start your first conversation': 'Start your first conversation', 'Tip: Select text in editor to add context': 'Tip: Select text in editor to add context', // Conversation list 'New Chat': 'New Chat', 'Search conversations...': 'Search conversations...', 'No conversations yet': 'No conversations yet', // Messages 'Ask me anything...': 'Ask me anything...', 'Ask a follow-up...': 'Ask a follow-up...', 'Reply...': 'Reply...', 'Type your message...': 'Type your message...', // Actions Send: 'Send', Cancel: 'Cancel', Retry: 'Retry', Insert: 'Insert', Copy: 'Copy', Edit: 'Edit', Delete: 'Delete', New: 'New', Back: 'Back', // Permissions 'Permission Required': 'Permission Required', Approve: 'Approve', Deny: 'Deny', Once: 'Once', 'This conversation': 'This conversation', Always: 'Always', 'The AI wants to execute:': 'The AI wants to execute:', 'This will modify your document content.': 'This will modify your document content.', // Tool execution 'Executing...': 'Executing...', 'Completed successfully': 'Completed successfully', Failed: 'Failed', 'Pending approval': 'Pending approval', 'Denied by user': 'Denied by user', 'Tool Result': 'Tool Result', 'Tool Execution Error': 'Tool Execution Error', // Context Context: 'Context', Contexts: 'Contexts', 'Add to AI Context': 'Add to AI Context', Remove: 'Remove', 'Clear all': 'Clear all', // Errors 'An error occurred': 'An error occurred', 'Request failed': 'Request failed', 'Connection error': 'Connection error', 'AI Assistant is not configured': 'AI Assistant is not configured', // Status 'Thinking...': 'Thinking...', 'Generating response...': 'Generating response...', // Tools insertHTML: 'Insert HTML', readDocument: 'Read Document', readBlock: 'Read Block', replaceInDocument: 'Replace in Document', replaceInBlock: 'Replace in Block', getSelection: 'Get Selection', // Timestamps 'Just now': 'Just now', // Conversation Conversation: 'Conversation', 'New Conversation': 'New Conversation', Yesterday: 'Yesterday', 'Delete conversation': 'Delete conversation', 'Are you sure you want to delete this conversation?': 'Are you sure you want to delete this conversation?', 'Failed to delete conversation': 'Failed to delete conversation' }; /***/ }), /***/ 27769: /***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) { "use strict"; /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ UIMainPanel: function() { return /* reexport safe */ _main_panel__WEBPACK_IMPORTED_MODULE_0__.UIMainPanel; } /* harmony export */ }); /* harmony import */ var _main_panel__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(51251); /***/ }), /***/ 28157: /***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) { "use strict"; /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ execute: function() { return /* binding */ execute; } /* harmony export */ }); /* harmony import */ var _core_abort_utils__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(6689); /* harmony import */ var jodit_esm_core_dom__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(22732); /** * Read entire editor content in HTML format */ async function execute(jodit, args, signal) { const { start, end, aroundSelection } = args; (0,_core_abort_utils__WEBPACK_IMPORTED_MODULE_0__.throwIfAborted)(signal); let html = jodit.value; const totalLength = html.length; const startPos = typeof start === 'number' ? start : 0; // If aroundSelection is true, get the maximum block around selection if (aroundSelection && !jodit.s.isCollapsed()) { const range = jodit.s.range; let node = range.commonAncestorContainer; // Find the top-level block element while(node && node !== jodit.editor){ if (jodit_esm_core_dom__WEBPACK_IMPORTED_MODULE_1__.Dom.isElement(node) && node.parentNode === jodit.editor) { html = node.outerHTML; break; } node = node.parentNode; } return { html, length: totalLength }; } // Apply start/end range if specified const finalEnd = end !== undefined ? Math.min(end, totalLength) : totalLength; const finalStart = Math.max(0, Math.min(startPos, totalLength)); if (finalStart > 0 || finalEnd < totalLength) { html = html.substring(finalStart, finalEnd); } return { html, length: totalLength, start: finalStart, end: finalEnd }; } /***/ }), /***/ 36142: /***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) { "use strict"; /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ execute: function() { return /* binding */ execute; } /* harmony export */ }); /* harmony import */ var _core_abort_utils__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(6689); /* harmony import */ var _core_arg_utils__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(49459); /** * Replace the current selection with new HTML content */ async function execute(jodit, args, signal) { const html = (0,_core_arg_utils__WEBPACK_IMPORTED_MODULE_1__.requireString)(args, 'html'); const { collapseToEnd } = args; (0,_core_abort_utils__WEBPACK_IMPORTED_MODULE_0__.throwIfAborted)(signal); try { jodit.s.restore(); // Insert HTML at current selection (replaces if selection exists) jodit.s.insertHTML(html); // Collapse cursor to end if requested if (collapseToEnd) { const range = jodit.s.range; range.collapse(false);