import { messageFormatting } from '/script.js'; function loadShowdown() { return new Promise((resolve, reject) => { if (window.showdown) { resolve(); return; } const script = document.createElement('script'); script.src = 'https://cdnjs.cloudflare.com/ajax/libs/showdown/2.1.0/showdown.min.js'; script.onload = resolve; script.onerror = reject; document.head.appendChild(script); }); } export async function showContentModal(title, contentUrl) { try { await loadShowdown(); const markdownContent = await $.get(contentUrl); const converter = new showdown.Converter({ tables: true, strikethrough: true, ghCodeBlocks: true }); const htmlContent = converter.makeHtml(markdownContent); const dialogHtml = ` `; const dialogElement = $(dialogHtml).appendTo('body'); const closeDialog = () => { dialogElement[0].close(); dialogElement.remove(); }; dialogElement.find('.popup-button-ok').on('click', closeDialog); dialogElement[0].showModal(); } catch (error) { console.error(`[Amily-翰林院] 紧急报告:加载教程内容 [${title}] 时发生意外:`, error); toastr.error(`无法加载教程: ${error.message}`, "翰林院回报"); } } export function showHtmlModal(title, htmlContent, options = {}) { const { okText = '确认', cancelText = '取消', onOk, onCancel, onShow, showCancel = true, } = options; const buttonsHtml = ` ${showCancel ? `` : ''} `; const dialogHtml = ` `; const dialogElement = $(dialogHtml).appendTo('body'); const closeDialog = () => { dialogElement[0].close(); dialogElement.remove(); }; dialogElement.find('.popup-button-ok').on('click', () => { if (onOk) { const shouldClose = onOk(dialogElement); if (shouldClose !== false) { closeDialog(); } } else { closeDialog(); } }); if (showCancel) { dialogElement.find('.popup-button-cancel').on('click', () => { if (onCancel) { onCancel(); } closeDialog(); }); } dialogElement[0].showModal(); if (onShow) { onShow(dialogElement); } return dialogElement; } function escapeHtml(text) { if (!text) return ''; return text .replace(/&/g, "&") .replace(//g, ">") .replace(/"/g, """) .replace(/'/g, "'"); } export function showSummaryModal(summaryText, callbacks) { const { onConfirm, onRegenerate, onCancel } = callbacks; const modalHtml = `
`; const dialogElement = showHtmlModal('预览与修订', modalHtml, { okText: '确认写入', cancelText: '取消写入', showCancel: true, onOk: (dialog) => { const editedText = dialog.find('textarea').val(); if (onConfirm) { onConfirm(editedText); } }, onCancel: () => { if (onCancel) { onCancel(); } } }); const regenerateButton = $(''); regenerateButton.on('click', () => { if (onRegenerate) { dialogElement[0].close(); onRegenerate(dialogElement); } }); dialogElement.find('.popup-controls').prepend(regenerateButton); } export function showTableFillReviewModal(rawResponse, callbacks = {}) { const { title = '填表响应检查', subtitle = 'AI未返回有效的 指令块。您可以在下方查看/编辑原始响应,并选择后续处理方式。', onApply, onContinue, onRetry, onCancel, } = callbacks; const modalHtml = `
${escapeHtml(subtitle)}
继续补全:让 AI 基于当前文本继续生成剩余内容,结果会追加到文本框后。
重新填表:舍弃当前响应并重新向 AI 请求同一批次的填表。
手动应用:将文本框中的当前内容直接作为最终结果写入表格(跳过格式校验)。
取消:放弃本次填表,任务暂停。
`; const dialogElement = showHtmlModal(title, modalHtml, { okText: '手动应用', cancelText: '取消', showCancel: true, onOk: (dialog) => { const editedText = dialog.find('.amily2-fill-review-text').val(); if (onApply) { onApply(editedText); } }, onCancel: () => { if (onCancel) { onCancel(); } }, }); const textarea = dialogElement.find('.amily2-fill-review-text'); if (typeof onContinue === 'function') { const continueButton = $(''); continueButton.on('click', async () => { const currentText = textarea.val(); textarea.prop('disabled', true); continueButton.prop('disabled', true).html(' 正在请求补全...'); try { const continued = await onContinue(currentText); if (typeof continued === 'string' && continued.length > 0) { textarea.val(continued); } } catch (err) { console.error('[Amily2 填表检查] 补全请求失败:', err); if (window.toastr) toastr.error(`补全失败: ${err.message || err}`, '继续补全'); } finally { textarea.prop('disabled', false); continueButton.prop('disabled', false).html(' 继续补全'); } }); dialogElement.find('.popup-controls').prepend(continueButton); } if (typeof onRetry === 'function') { const retryButton = $(''); retryButton.on('click', () => { dialogElement[0].close(); dialogElement.remove(); onRetry(); }); const okBtn = dialogElement.find('.popup-button-ok'); if (okBtn.length) { retryButton.insertBefore(okBtn); } else { dialogElement.find('.popup-controls').append(retryButton); } } return dialogElement; } const CWB_WARNING_COUNTDOWN = 10; /** * 角色世界书入口警告弹窗,强制倒计时后才可继续。 * @param {Function} onProceed - 用户点击"继续使用"时的回调 * @param {Function} onClose - 用户点击"关闭退出"时的回调(含弹窗关闭前直接离开) */ export function showCwbWarningModal(onProceed, onClose) { const dialogHtml = ` `; const $dialog = $(dialogHtml).appendTo('body'); const close = (cb) => { clearInterval(timer); $dialog[0].close(); $dialog.remove(); cb?.(); }; $dialog.find('.cwb-warning-close').on('click', () => close(onClose)); $dialog.find('.cwb-warning-proceed').on('click', function () { if (!this.disabled) close(onProceed); }); let remaining = CWB_WARNING_COUNTDOWN; const timer = setInterval(() => { remaining -= 1; $dialog.find('.cwb-countdown').text(remaining); if (remaining <= 0) { clearInterval(timer); const $btn = $dialog.find('.cwb-warning-proceed'); $btn.prop('disabled', false).html('继续使用'); } }, 1000); $dialog[0].showModal(); }