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();
}