import { app } from "../../../../../scripts/app.js"; import { api } from "../../../../../scripts/api.js"; import { $el } from "../../../../../scripts/ui.js"; import { getSvgIcon } from "../../utils/icon_utils.js"; export const renderFormatMethods = { encodeContextMenuModel(context = null) { if (!context) return ''; try { return encodeURIComponent(JSON.stringify(context)); } catch (error) { console.warn('Model Resolver: failed to encode context menu model', error); return ''; } }, getContextMenuAttrs(context = null, tooltip = '') { const data = this.encodeContextMenuModel(context); if (!data) return ''; const tooltipAttr = tooltip ? ` data-tooltip="${this.escapeHtml(tooltip)}"` : ''; return ` data-model="${this.escapeHtml(data)}" oncontextmenu="window.MLOpenContextMenu(event, this)"${tooltipAttr}`; }, getContextMenuProps(context = null, tooltip = '') { const data = this.encodeContextMenuModel(context); if (!data) return {}; const props = { "data-model": data, oncontextmenu: (event) => { window.MLOpenContextMenu?.(event, event.currentTarget); } }; if (tooltip) { props["data-tooltip"] = tooltip; } return props; }, setContextMenuElement(element, context = null, tooltip = '') { if (!element) return; const data = this.encodeContextMenuModel(context); if (!data) { element.removeAttribute('data-model'); element.removeAttribute('data-tooltip'); element.oncontextmenu = null; return; } element.dataset.model = data; if (tooltip) { element.dataset.tooltip = tooltip; } else { element.removeAttribute('data-tooltip'); } element.oncontextmenu = (event) => { window.MLOpenContextMenu?.(event, event.currentTarget); }; }, /** * Get a colored confidence badge HTML * @param {number} confidence - Confidence percentage (0-100) * @returns {string} HTML for the badge */ getConfidenceBadge(confidence) { let badgeClass; if (confidence >= 95) { badgeClass = 'mr-badge-high'; } else if (confidence >= 70) { badgeClass = 'mr-badge-medium'; } else { badgeClass = 'mr-badge-low'; } return `${confidence}%`; }, getStatusBadge(label, variant = 'neutral') { return `${label}`; }, getFilenameFromPath(path) { if (!path) return ''; return path.split(/[\/\\]/).pop() || path; }, /** * Format a filename with smart truncation * @param {string} path - Full path or filename * @param {number} maxLength - Maximum display length * @returns {object} { display: truncated name, full: full name } */ formatFilename(path, maxLength = 50) { if (!path) return { display: 'Unknown', full: 'Unknown' }; // Extract just the filename from path const filename = this.getFilenameFromPath(path); if (filename.length <= maxLength) { return { display: filename, full: filename }; } // Smart truncation: keep extension visible const lastDot = filename.lastIndexOf('.'); const ext = lastDot > 0 ? filename.slice(lastDot) : ''; const name = lastDot > 0 ? filename.slice(0, lastDot) : filename; // Calculate how much of the name we can show const availableLength = maxLength - ext.length - 3; // 3 for "..." if (availableLength < 8) { // Too short, just truncate at the end return { display: filename.slice(0, maxLength - 3) + '...', full: filename }; } // Truncate middle of name const frontLength = Math.ceil(availableLength / 2); const backLength = Math.floor(availableLength / 2); const truncated = name.slice(0, frontLength) + '...' + name.slice(-backLength) + ext; return { display: truncated, full: filename }; }, /** * Format a path showing directory context * @param {string} path - Full relative path * @param {number} maxLength - Maximum display length * @returns {object} { display: formatted path, full: full path } */ formatPath(path, maxLength = 60) { if (!path) return { display: 'Unknown', full: 'Unknown' }; if (path.length <= maxLength) { return { display: path, full: path }; } // Try to show meaningful parts: first dir + filename const parts = path.split(/[\/\\]/); const filename = parts.pop() || ''; const firstDir = parts[0] || ''; if (parts.length === 0) { // Just a filename return this.formatFilename(path, maxLength); } // Show first directory + ... + filename const formatted = firstDir + '\\...' + (filename.length > 40 ? this.formatFilename(filename, 40).display : filename); if (formatted.length <= maxLength) { return { display: formatted, full: path }; } // Still too long, just truncate return { display: path.slice(0, maxLength - 3) + '...', full: path }; }, /** * Render a status message with icon * @param {string} message - Message text * @param {string} type - 'error' | 'success' | 'info' | 'warning' * @returns {string} HTML for status message */ renderStatusMessage(message, type = 'info', options = {}) { const icons = { error: '⚠', success: '✓', info: 'ℹ', warning: '⚡' }; const icon = icons[type] || icons.info; const contextMenuModel = options?.contextMenuModel || null; const contextMenuTooltip = options?.contextMenuTooltip || 'Right-click to open download folder'; const contextMenuAttrs = this.getContextMenuAttrs(contextMenuModel, contextMenuTooltip); const className = contextMenuAttrs ? `mr-status mr-status-${type} mr-download-folder-context` : `mr-status mr-status-${type}`; return `