import { a as require_react, t as require_jsx_runtime, u as __toESM } from "../index.js";
//#region node_modules/diff/libesm/diff/base.js
var import_react = /* @__PURE__ */ __toESM(require_react(), 1);
var Diff = class {
	diff(oldStr, newStr, options = {}) {
		let callback;
		if (typeof options === "function") {
			callback = options;
			options = {};
		} else if ("callback" in options) callback = options.callback;
		const oldString = this.castInput(oldStr, options);
		const newString = this.castInput(newStr, options);
		const oldTokens = this.removeEmpty(this.tokenize(oldString, options));
		const newTokens = this.removeEmpty(this.tokenize(newString, options));
		return this.diffWithOptionsObj(oldTokens, newTokens, options, callback);
	}
	diffWithOptionsObj(oldTokens, newTokens, options, callback) {
		var _a;
		const done = (value) => {
			value = this.postProcess(value, options);
			if (callback) {
				setTimeout(function() {
					callback(value);
				}, 0);
				return;
			} else return value;
		};
		const newLen = newTokens.length, oldLen = oldTokens.length;
		let editLength = 1;
		let maxEditLength = newLen + oldLen;
		if (options.maxEditLength != null) maxEditLength = Math.min(maxEditLength, options.maxEditLength);
		const maxExecutionTime = (_a = options.timeout) !== null && _a !== void 0 ? _a : Infinity;
		const abortAfterTimestamp = Date.now() + maxExecutionTime;
		const bestPath = [{
			oldPos: -1,
			lastComponent: void 0
		}];
		let newPos = this.extractCommon(bestPath[0], newTokens, oldTokens, 0, options);
		if (bestPath[0].oldPos + 1 >= oldLen && newPos + 1 >= newLen) return done(this.buildValues(bestPath[0].lastComponent, newTokens, oldTokens));
		let minDiagonalToConsider = -Infinity, maxDiagonalToConsider = Infinity;
		const execEditLength = () => {
			for (let diagonalPath = Math.max(minDiagonalToConsider, -editLength); diagonalPath <= Math.min(maxDiagonalToConsider, editLength); diagonalPath += 2) {
				let basePath;
				const removePath = bestPath[diagonalPath - 1], addPath = bestPath[diagonalPath + 1];
				if (removePath) bestPath[diagonalPath - 1] = void 0;
				let canAdd = false;
				if (addPath) {
					const addPathNewPos = addPath.oldPos - diagonalPath;
					canAdd = addPath && 0 <= addPathNewPos && addPathNewPos < newLen;
				}
				const canRemove = removePath && removePath.oldPos + 1 < oldLen;
				if (!canAdd && !canRemove) {
					bestPath[diagonalPath] = void 0;
					continue;
				}
				if (!canRemove || canAdd && removePath.oldPos < addPath.oldPos) basePath = this.addToPath(addPath, true, false, 0, options);
				else basePath = this.addToPath(removePath, false, true, 1, options);
				newPos = this.extractCommon(basePath, newTokens, oldTokens, diagonalPath, options);
				if (basePath.oldPos + 1 >= oldLen && newPos + 1 >= newLen) return done(this.buildValues(basePath.lastComponent, newTokens, oldTokens)) || true;
				else {
					bestPath[diagonalPath] = basePath;
					if (basePath.oldPos + 1 >= oldLen) maxDiagonalToConsider = Math.min(maxDiagonalToConsider, diagonalPath - 1);
					if (newPos + 1 >= newLen) minDiagonalToConsider = Math.max(minDiagonalToConsider, diagonalPath + 1);
				}
			}
			editLength++;
		};
		if (callback) (function exec() {
			setTimeout(function() {
				if (editLength > maxEditLength || Date.now() > abortAfterTimestamp) return callback(void 0);
				if (!execEditLength()) exec();
			}, 0);
		})();
		else while (editLength <= maxEditLength && Date.now() <= abortAfterTimestamp) {
			const ret = execEditLength();
			if (ret) return ret;
		}
	}
	addToPath(path, added, removed, oldPosInc, options) {
		const last = path.lastComponent;
		if (last && !options.oneChangePerToken && last.added === added && last.removed === removed) return {
			oldPos: path.oldPos + oldPosInc,
			lastComponent: {
				count: last.count + 1,
				added,
				removed,
				previousComponent: last.previousComponent
			}
		};
		else return {
			oldPos: path.oldPos + oldPosInc,
			lastComponent: {
				count: 1,
				added,
				removed,
				previousComponent: last
			}
		};
	}
	extractCommon(basePath, newTokens, oldTokens, diagonalPath, options) {
		const newLen = newTokens.length, oldLen = oldTokens.length;
		let oldPos = basePath.oldPos, newPos = oldPos - diagonalPath, commonCount = 0;
		while (newPos + 1 < newLen && oldPos + 1 < oldLen && this.equals(oldTokens[oldPos + 1], newTokens[newPos + 1], options)) {
			newPos++;
			oldPos++;
			commonCount++;
			if (options.oneChangePerToken) basePath.lastComponent = {
				count: 1,
				previousComponent: basePath.lastComponent,
				added: false,
				removed: false
			};
		}
		if (commonCount && !options.oneChangePerToken) basePath.lastComponent = {
			count: commonCount,
			previousComponent: basePath.lastComponent,
			added: false,
			removed: false
		};
		basePath.oldPos = oldPos;
		return newPos;
	}
	equals(left, right, options) {
		if (options.comparator) return options.comparator(left, right);
		else return left === right || !!options.ignoreCase && left.toLowerCase() === right.toLowerCase();
	}
	removeEmpty(array) {
		const ret = [];
		for (let i = 0; i < array.length; i++) if (array[i]) ret.push(array[i]);
		return ret;
	}
	castInput(value, options) {
		return value;
	}
	tokenize(value, options) {
		return Array.from(value);
	}
	join(chars) {
		return chars.join("");
	}
	postProcess(changeObjects, options) {
		return changeObjects;
	}
	get useLongestToken() {
		return false;
	}
	buildValues(lastComponent, newTokens, oldTokens) {
		const components = [];
		let nextComponent;
		while (lastComponent) {
			components.push(lastComponent);
			nextComponent = lastComponent.previousComponent;
			delete lastComponent.previousComponent;
			lastComponent = nextComponent;
		}
		components.reverse();
		const componentLen = components.length;
		let componentPos = 0, newPos = 0, oldPos = 0;
		for (; componentPos < componentLen; componentPos++) {
			const component = components[componentPos];
			if (!component.removed) {
				if (!component.added && this.useLongestToken) {
					let value = newTokens.slice(newPos, newPos + component.count);
					value = value.map(function(value, i) {
						const oldValue = oldTokens[oldPos + i];
						return oldValue.length > value.length ? oldValue : value;
					});
					component.value = this.join(value);
				} else component.value = this.join(newTokens.slice(newPos, newPos + component.count));
				newPos += component.count;
				if (!component.added) oldPos += component.count;
			} else {
				component.value = this.join(oldTokens.slice(oldPos, oldPos + component.count));
				oldPos += component.count;
			}
		}
		return components;
	}
};
//#endregion
//#region node_modules/diff/libesm/diff/character.js
var CharacterDiff = class extends Diff {};
new CharacterDiff();
//#endregion
//#region node_modules/diff/libesm/util/string.js
function longestCommonPrefix(str1, str2) {
	let i;
	for (i = 0; i < str1.length && i < str2.length; i++) if (str1[i] != str2[i]) return str1.slice(0, i);
	return str1.slice(0, i);
}
function longestCommonSuffix(str1, str2) {
	let i;
	if (!str1 || !str2 || str1[str1.length - 1] != str2[str2.length - 1]) return "";
	for (i = 0; i < str1.length && i < str2.length; i++) if (str1[str1.length - (i + 1)] != str2[str2.length - (i + 1)]) return str1.slice(-i);
	return str1.slice(-i);
}
function replacePrefix(string, oldPrefix, newPrefix) {
	if (string.slice(0, oldPrefix.length) != oldPrefix) throw Error(`string ${JSON.stringify(string)} doesn't start with prefix ${JSON.stringify(oldPrefix)}; this is a bug`);
	return newPrefix + string.slice(oldPrefix.length);
}
function replaceSuffix(string, oldSuffix, newSuffix) {
	if (!oldSuffix) return string + newSuffix;
	if (string.slice(-oldSuffix.length) != oldSuffix) throw Error(`string ${JSON.stringify(string)} doesn't end with suffix ${JSON.stringify(oldSuffix)}; this is a bug`);
	return string.slice(0, -oldSuffix.length) + newSuffix;
}
function removePrefix(string, oldPrefix) {
	return replacePrefix(string, oldPrefix, "");
}
function removeSuffix(string, oldSuffix) {
	return replaceSuffix(string, oldSuffix, "");
}
function maximumOverlap(string1, string2) {
	return string2.slice(0, overlapCount(string1, string2));
}
function overlapCount(a, b) {
	let startA = 0;
	if (a.length > b.length) startA = a.length - b.length;
	let endB = b.length;
	if (a.length < b.length) endB = a.length;
	const map = Array(endB);
	let k = 0;
	map[0] = 0;
	for (let j = 1; j < endB; j++) {
		if (b[j] == b[k]) map[j] = map[k];
		else map[j] = k;
		while (k > 0 && b[j] != b[k]) k = map[k];
		if (b[j] == b[k]) k++;
	}
	k = 0;
	for (let i = startA; i < a.length; i++) {
		while (k > 0 && a[i] != b[k]) k = map[k];
		if (a[i] == b[k]) k++;
	}
	return k;
}
/**
* Split a string into segments using a word segmenter, merging consecutive
* segments if they are both whitespace segments. Whitespace segments can
* appear adjacent to one another for two reasons:
* - newlines always get their own segment
* - where a diacritic is attached to a whitespace character in the text, the
*   segment ends after the diacritic, so e.g. " \u0300 " becomes two segments.
* This function therefore runs the segmenter's .segment() method and then
* merges consecutive segments of whitespace into a single part.
*/
function segment(string, segmenter) {
	const parts = [];
	for (const segmentObj of Array.from(segmenter.segment(string))) {
		const segment = segmentObj.segment;
		if (parts.length && /\s/.test(parts[parts.length - 1]) && /\s/.test(segment)) parts[parts.length - 1] += segment;
		else parts.push(segment);
	}
	return parts;
}
function trailingWs(string, segmenter) {
	if (segmenter) return leadingAndTrailingWs(string, segmenter)[1];
	let i;
	for (i = string.length - 1; i >= 0; i--) if (!string[i].match(/\s/)) break;
	return string.substring(i + 1);
}
function leadingWs(string, segmenter) {
	if (segmenter) return leadingAndTrailingWs(string, segmenter)[0];
	const match = string.match(/^\s*/);
	return match ? match[0] : "";
}
function leadingAndTrailingWs(string, segmenter) {
	if (!segmenter) return [leadingWs(string), trailingWs(string)];
	if (segmenter.resolvedOptions().granularity != "word") throw new Error("The segmenter passed must have a granularity of \"word\"");
	const segments = segment(string, segmenter);
	const firstSeg = segments[0];
	const lastSeg = segments[segments.length - 1];
	return [/\s/.test(firstSeg) ? firstSeg : "", /\s/.test(lastSeg) ? lastSeg : ""];
}
//#endregion
//#region node_modules/diff/libesm/diff/word.js
var extendedWordChars = "a-zA-Z0-9_\\u{AD}\\u{C0}-\\u{D6}\\u{D8}-\\u{F6}\\u{F8}-\\u{2C6}\\u{2C8}-\\u{2D7}\\u{2DE}-\\u{2FF}\\u{1E00}-\\u{1EFF}";
var tokenizeIncludingWhitespace = new RegExp(`[${extendedWordChars}]+|\\s+|[^${extendedWordChars}]`, "ug");
var WordDiff = class extends Diff {
	equals(left, right, options) {
		if (options.ignoreCase) {
			left = left.toLowerCase();
			right = right.toLowerCase();
		}
		return left.trim() === right.trim();
	}
	tokenize(value, options = {}) {
		let parts;
		if (options.intlSegmenter) {
			const segmenter = options.intlSegmenter;
			if (segmenter.resolvedOptions().granularity != "word") throw new Error("The segmenter passed must have a granularity of \"word\"");
			parts = segment(value, segmenter);
		} else parts = value.match(tokenizeIncludingWhitespace) || [];
		const tokens = [];
		let prevPart = null;
		parts.forEach((part) => {
			if (/\s/.test(part)) if (prevPart == null) tokens.push(part);
			else tokens.push(tokens.pop() + part);
			else if (prevPart != null && /\s/.test(prevPart)) if (tokens[tokens.length - 1] == prevPart) tokens.push(tokens.pop() + part);
			else tokens.push(prevPart + part);
			else tokens.push(part);
			prevPart = part;
		});
		return tokens;
	}
	join(tokens) {
		return tokens.map((token, i) => {
			if (i == 0) return token;
			else return token.replace(/^\s+/, "");
		}).join("");
	}
	postProcess(changes, options) {
		if (!changes || options.oneChangePerToken) return changes;
		let lastKeep = null;
		let insertion = null;
		let deletion = null;
		changes.forEach((change) => {
			if (change.added) insertion = change;
			else if (change.removed) deletion = change;
			else {
				if (insertion || deletion) dedupeWhitespaceInChangeObjects(lastKeep, deletion, insertion, change, options.intlSegmenter);
				lastKeep = change;
				insertion = null;
				deletion = null;
			}
		});
		if (insertion || deletion) dedupeWhitespaceInChangeObjects(lastKeep, deletion, insertion, null, options.intlSegmenter);
		return changes;
	}
};
new WordDiff();
function dedupeWhitespaceInChangeObjects(startKeep, deletion, insertion, endKeep, segmenter) {
	if (deletion && insertion) {
		const [oldWsPrefix, oldWsSuffix] = leadingAndTrailingWs(deletion.value, segmenter);
		const [newWsPrefix, newWsSuffix] = leadingAndTrailingWs(insertion.value, segmenter);
		if (startKeep) {
			const commonWsPrefix = longestCommonPrefix(oldWsPrefix, newWsPrefix);
			startKeep.value = replaceSuffix(startKeep.value, newWsPrefix, commonWsPrefix);
			deletion.value = removePrefix(deletion.value, commonWsPrefix);
			insertion.value = removePrefix(insertion.value, commonWsPrefix);
		}
		if (endKeep) {
			const commonWsSuffix = longestCommonSuffix(oldWsSuffix, newWsSuffix);
			endKeep.value = replacePrefix(endKeep.value, newWsSuffix, commonWsSuffix);
			deletion.value = removeSuffix(deletion.value, commonWsSuffix);
			insertion.value = removeSuffix(insertion.value, commonWsSuffix);
		}
	} else if (insertion) {
		if (startKeep) {
			const ws = leadingWs(insertion.value, segmenter);
			insertion.value = insertion.value.substring(ws.length);
		}
		if (endKeep) {
			const ws = leadingWs(endKeep.value, segmenter);
			endKeep.value = endKeep.value.substring(ws.length);
		}
	} else if (startKeep && endKeep) {
		const newWsFull = leadingWs(endKeep.value, segmenter), [delWsStart, delWsEnd] = leadingAndTrailingWs(deletion.value, segmenter);
		const newWsStart = longestCommonPrefix(newWsFull, delWsStart);
		deletion.value = removePrefix(deletion.value, newWsStart);
		const newWsEnd = longestCommonSuffix(removePrefix(newWsFull, newWsStart), delWsEnd);
		deletion.value = removeSuffix(deletion.value, newWsEnd);
		endKeep.value = replacePrefix(endKeep.value, newWsFull, newWsEnd);
		startKeep.value = replaceSuffix(startKeep.value, newWsFull, newWsFull.slice(0, newWsFull.length - newWsEnd.length));
	} else if (endKeep) {
		const endKeepWsPrefix = leadingWs(endKeep.value, segmenter);
		const overlap = maximumOverlap(trailingWs(deletion.value, segmenter), endKeepWsPrefix);
		deletion.value = removeSuffix(deletion.value, overlap);
	} else if (startKeep) {
		const overlap = maximumOverlap(trailingWs(startKeep.value, segmenter), leadingWs(deletion.value, segmenter));
		deletion.value = removePrefix(deletion.value, overlap);
	}
}
var WordsWithSpaceDiff = class extends Diff {
	tokenize(value) {
		const regex = new RegExp(`(\\r?\\n)|[${extendedWordChars}]+|[^\\S\\n\\r]+|[^${extendedWordChars}]`, "ug");
		return value.match(regex) || [];
	}
};
var wordsWithSpaceDiff = new WordsWithSpaceDiff();
function diffWordsWithSpace(oldStr, newStr, options) {
	return wordsWithSpaceDiff.diff(oldStr, newStr, options);
}
//#endregion
//#region node_modules/diff/libesm/diff/line.js
var LineDiff = class extends Diff {
	constructor() {
		super(...arguments);
		this.tokenize = tokenize;
	}
	equals(left, right, options) {
		if (options.ignoreWhitespace) {
			if (!options.newlineIsToken || !left.includes("\n")) left = left.trim();
			if (!options.newlineIsToken || !right.includes("\n")) right = right.trim();
		} else if (options.ignoreNewlineAtEof && !options.newlineIsToken) {
			if (left.endsWith("\n")) left = left.slice(0, -1);
			if (right.endsWith("\n")) right = right.slice(0, -1);
		}
		return super.equals(left, right, options);
	}
};
new LineDiff();
function tokenize(value, options) {
	if (options.stripTrailingCr) value = value.replace(/\r\n/g, "\n");
	const retLines = [], linesAndNewlines = value.split(/(\n|\r\n)/);
	if (!linesAndNewlines[linesAndNewlines.length - 1]) linesAndNewlines.pop();
	for (let i = 0; i < linesAndNewlines.length; i++) {
		const line = linesAndNewlines[i];
		if (i % 2 && !options.newlineIsToken) retLines[retLines.length - 1] += line;
		else retLines.push(line);
	}
	return retLines;
}
//#endregion
//#region node_modules/diff/libesm/diff/sentence.js
function isSentenceEndPunct(char) {
	return char == "." || char == "!" || char == "?";
}
var SentenceDiff = class extends Diff {
	tokenize(value) {
		var _a;
		const result = [];
		let tokenStartI = 0;
		for (let i = 0; i < value.length; i++) {
			if (i == value.length - 1) {
				result.push(value.slice(tokenStartI));
				break;
			}
			if (isSentenceEndPunct(value[i]) && value[i + 1].match(/\s/)) {
				result.push(value.slice(tokenStartI, i + 1));
				i = tokenStartI = i + 1;
				while ((_a = value[i + 1]) === null || _a === void 0 ? void 0 : _a.match(/\s/)) i++;
				result.push(value.slice(tokenStartI, i + 1));
				tokenStartI = i + 1;
			}
		}
		return result;
	}
};
new SentenceDiff();
//#endregion
//#region node_modules/diff/libesm/diff/css.js
var CssDiff = class extends Diff {
	tokenize(value) {
		return value.split(/([{}:;,]|\s+)/);
	}
};
new CssDiff();
//#endregion
//#region node_modules/diff/libesm/diff/json.js
var JsonDiff = class extends Diff {
	constructor() {
		super(...arguments);
		this.tokenize = tokenize;
	}
	get useLongestToken() {
		return true;
	}
	castInput(value, options) {
		const { undefinedReplacement, stringifyReplacer = (k, v) => typeof v === "undefined" ? undefinedReplacement : v } = options;
		return typeof value === "string" ? value : JSON.stringify(canonicalize(value, null, null, stringifyReplacer), null, "  ");
	}
	equals(left, right, options) {
		return super.equals(left.replace(/,([\r\n])/g, "$1"), right.replace(/,([\r\n])/g, "$1"), options);
	}
};
new JsonDiff();
function canonicalize(obj, stack, replacementStack, replacer, key) {
	stack = stack || [];
	replacementStack = replacementStack || [];
	if (replacer) obj = replacer(key === void 0 ? "" : key, obj);
	let i;
	for (i = 0; i < stack.length; i += 1) if (stack[i] === obj) return replacementStack[i];
	let canonicalizedObj;
	if ("[object Array]" === Object.prototype.toString.call(obj)) {
		stack.push(obj);
		canonicalizedObj = new Array(obj.length);
		replacementStack.push(canonicalizedObj);
		for (i = 0; i < obj.length; i += 1) canonicalizedObj[i] = canonicalize(obj[i], stack, replacementStack, replacer, String(i));
		stack.pop();
		replacementStack.pop();
		return canonicalizedObj;
	}
	if (obj && obj.toJSON) obj = obj.toJSON();
	if (typeof obj === "object" && obj !== null) {
		stack.push(obj);
		canonicalizedObj = {};
		replacementStack.push(canonicalizedObj);
		const sortedKeys = [];
		let key;
		for (key in obj)
 /* istanbul ignore else */
		if (Object.prototype.hasOwnProperty.call(obj, key)) sortedKeys.push(key);
		sortedKeys.sort();
		for (i = 0; i < sortedKeys.length; i += 1) {
			key = sortedKeys[i];
			canonicalizedObj[key] = canonicalize(obj[key], stack, replacementStack, replacer, key);
		}
		stack.pop();
		replacementStack.pop();
	} else canonicalizedObj = obj;
	return canonicalizedObj;
}
//#endregion
//#region node_modules/diff/libesm/diff/array.js
var ArrayDiff = class extends Diff {
	tokenize(value) {
		return value.slice();
	}
	join(value) {
		return value;
	}
	removeEmpty(value) {
		return value;
	}
};
new ArrayDiff();
//#endregion
//#region app/page.tsx
var import_jsx_runtime = require_jsx_runtime();
var SAMPLE_TEXT = `随着数字技术的不断发展，城市书店正在经历一场深刻的转型。在这个信息快速传播的时代，传统书店不仅承担着售卖图书的功能，而且逐渐成为连接人与城市文化的重要空间。

首先，许多书店开始引入咖啡、展览和讲座等多元业态。这种模式可以有效延长顾客的停留时间，同时也为读者提供更加丰富的文化体验。此外，社交媒体的传播让一些独具设计感的书店成为城市新的打卡地标。

值得注意的是，空间更新并不意味着阅读本身变得不重要。真正能够长期留住读者的，依然是稳定的选书能力、专业的店员以及持续发生的思想交流。综上所述，未来的城市书店需要在商业经营与公共文化价值之间找到平衡。`;
var STAGES = [
	{
		key: "analyzing",
		label: "拆解原文",
		short: "分析"
	},
	{
		key: "planning",
		label: "制定策略",
		short: "策略"
	},
	{
		key: "rewriting",
		label: "润色成稿",
		short: "改写"
	}
];
var DIRECT_STAGES = [
	{
		key: "analyzing",
		label: "读取原文",
		short: "准备"
	},
	{
		key: "planning",
		label: "自然改写",
		short: "改写"
	},
	{
		key: "rewriting",
		label: "整理结果",
		short: "成稿"
	}
];
var COMMON_PHRASES = [
	"随着数字技术的不断发展",
	"在这个信息快速传播的时代",
	"首先",
	"此外",
	"值得注意的是",
	"综上所述",
	"不仅",
	"而且"
];
var TEACHER_STRENGTH_LABELS = [
	"极低",
	"很低",
	"低",
	"偏低",
	"中低",
	"中",
	"中高",
	"偏高",
	"高",
	"极高"
];
var DEFAULT_SETTINGS = {
	provider: "orion",
	model: "Orion",
	apiKey: "",
	endpoint: "http://127.0.0.1:18080/v1/chat/completions",
	useOrionAnalysis: false,
	qualityGateEnabled: false,
	deepThinkingEnabled: false,
	teacherOriginalMode: true,
	strength: 48,
	teacherStrengthLevel: 2,
	structureMode: "flexible"
};
var DEMO_SETTINGS = {
	...DEFAULT_SETTINGS,
	provider: "deepseek",
	model: "deepseek-v4-flash"
};
function countCharacters(text) {
	return text.replace(/\s/g, "").length;
}
function formatBytes(chars) {
	return chars > 9999 ? `${(chars / 1e4).toFixed(1)} 万` : chars.toLocaleString("zh-CN");
}
function stoppedError() {
	const error = /* @__PURE__ */ new Error("改写已手动停止");
	error.name = "AbortError";
	return error;
}
function isStoppedFailure(error) {
	return error instanceof Error && error.name === "AbortError";
}
function sleep(ms, signal) {
	return new Promise((resolve, reject) => {
		if (signal?.aborted) {
			reject(stoppedError());
			return;
		}
		const timer = window.setTimeout(() => {
			signal?.removeEventListener("abort", stop);
			resolve();
		}, ms);
		const stop = () => {
			window.clearTimeout(timer);
			reject(stoppedError());
		};
		signal?.addEventListener("abort", stop, { once: true });
	});
}
function localRewrite(text, strength, structureMode) {
	const replacements = [
		[/随着([^，。]{2,18})的不断发展[，,]?/g, "$1持续发展，"],
		[/在这个([^，。]{2,22})的时代[，,]?/g, "$1越来越快，"],
		[/值得注意的是[，,]?/g, ""],
		[/综上所述[，,]?/g, ""],
		[/首先[，,]?/g, ""],
		[/此外[，,]?/g, ""],
		[/同时也/g, "也"],
		[/可以有效/g, "能"],
		[/逐渐成为/g, "慢慢成为"],
		[/更加丰富的/g, "更完整的"],
		[/承担着([^，。]{2,18})的功能/g, "承担$1的功能"]
	];
	let rewritten = text;
	replacements.forEach(([pattern, replacement]) => {
		rewritten = rewritten.replace(pattern, replacement);
	});
	const paragraphs = rewritten.split(/\n{2,}/).map((paragraph) => paragraph.trim()).filter(Boolean);
	if (structureMode === "flexible" && strength >= 65 && paragraphs.length > 2) {
		const conclusion = paragraphs.pop();
		if (conclusion) paragraphs.splice(1, 0, conclusion);
	}
	return paragraphs.map((paragraph, index) => {
		if (structureMode === "locked" || strength < 65 || index % 2 !== 0) return paragraph;
		const sentences = paragraph.match(/[^。！？!?]+[。！？!?]?/g)?.filter(Boolean) ?? [paragraph];
		if (sentences.length < 3) return paragraph;
		return [
			sentences[1],
			sentences[0],
			...sentences.slice(2)
		].join("");
	}).join("\n\n");
}
function demoAnalysis(text) {
	const phraseHits = COMMON_PHRASES.filter((phrase) => text.includes(phrase));
	const paragraphs = text.split(/\n{2,}/).filter((item) => item.trim()).length;
	return `这篇稿子约 ${countCharacters(text)} 字，共 ${paragraphs} 个自然段。信息主线清楚，但段落按“背景—列举—总结”的固定顺序展开，句式长度接近，连接词使用偏密。${phraseHits.length ? `检测到较明显的模板表达：${phraseHits.slice(0, 5).join("、")}。` : "未发现高频模板词，但仍可调整叙述节奏。"}改写时应保留事实和立场，减少概括性判断，用更具体的落点连接段落。`;
}
function demoPlan(text, structureMode) {
	const firstLine = text.split(/[。！？\n]/).find((item) => item.trim())?.trim() ?? "文章主题";
	if (structureMode === "locked") return `采用结构保护：保留现有 ${text.split(/\n{2,}/).filter((item) => item.trim()).length} 个自然段的数量、顺序和功能，不移动、合并或拆分段落。\n\n1. 第一段保留原切入点，只压缩宏观套话。\n2. 中间段逐段处理机械连接词、同构句式和重复概括。\n3. 保留全部事实、数字、例子和判断边界。\n4. 结尾只删除重复收束，不另加升华或新观点。`;
	return `新的文章从读者最容易感知的变化切入，而不是先讲宏观背景。\n\n1. 用一个具体判断打开话题：${firstLine.slice(0, 34)}。\n2. 提前放出原文结论，建立文章的核心矛盾。\n3. 将案例与原因交错呈现，避免连续列举。\n4. 最后一段回到现实选择，不使用程式化总结句。`;
}
async function readDraftFile(file) {
	const extension = file.name.split(".").pop()?.toLowerCase();
	if (extension === "docx") return (await (await import("./lib-BTjZJiKI.js").then((m) => /* @__PURE__ */ __toESM(m.default, 1))).extractRawText({ arrayBuffer: await file.arrayBuffer() })).value.trim();
	const raw = await file.text();
	if (extension === "html" || extension === "htm") {
		const document = new DOMParser().parseFromString(raw, "text/html");
		document.querySelectorAll("script, style, noscript").forEach((node) => node.remove());
		return (document.body.textContent ?? "").replace(/\n\s*\n\s*\n/g, "\n\n").trim();
	}
	return raw.trim();
}
function Home() {
	const [mode, setMode] = (0, import_react.useState)("single");
	const [source, setSource] = (0, import_react.useState)(SAMPLE_TEXT);
	const [result, setResult] = (0, import_react.useState)("");
	const [, setAnalysis] = (0, import_react.useState)("");
	const [, setPlan] = (0, import_react.useState)("");
	const [, setQuality] = (0, import_react.useState)(null);
	const [stage, setStage] = (0, import_react.useState)("idle");
	const [showDiff, setShowDiff] = (0, import_react.useState)(true);
	const [settingsOpen, setSettingsOpen] = (0, import_react.useState)(false);
	const [settings, setSettings] = (0, import_react.useState)(DEFAULT_SETTINGS);
	const [draftSettings, setDraftSettings] = (0, import_react.useState)(DEFAULT_SETTINGS);
	const [connection, setConnection] = (0, import_react.useState)({
		phase: "checking",
		message: "正在检测模型连接"
	});
	const [rewriteProgress, setRewriteProgress] = (0, import_react.useState)({
		completed: 0,
		total: 0,
		retry: 0,
		message: ""
	});
	const [files, setFiles] = (0, import_react.useState)([]);
	const [activeFileId, setActiveFileId] = (0, import_react.useState)(null);
	const [notice, setNotice] = (0, import_react.useState)("");
	const [announcement, setAnnouncement] = (0, import_react.useState)(null);
	const [announcementOpen, setAnnouncementOpen] = (0, import_react.useState)(false);
	const [announcementUnread, setAnnouncementUnread] = (0, import_react.useState)(false);
	const [errorMessage, setErrorMessage] = (0, import_react.useState)("");
	const fileInputRef = (0, import_react.useRef)(null);
	const abortControllerRef = (0, import_react.useRef)(null);
	const stopRequestedRef = (0, import_react.useRef)(false);
	const workingFileIdRef = (0, import_react.useRef)(null);
	async function probeSettings(candidate, signal) {
		if (candidate.provider !== "orion" && !candidate.apiKey.trim()) {
			setConnection({
				phase: "idle",
				message: "当前为演示模式"
			});
			return false;
		}
		setConnection({
			phase: "checking",
			message: "正在验证接口、密钥和模型"
		});
		try {
			const response = await fetch("/api/rewrite/probe", {
				method: "POST",
				signal,
				headers: { "Content-Type": "application/json" },
				body: JSON.stringify({
					provider: candidate.provider,
					model: candidate.provider === "orion" ? "Orion" : candidate.model,
					apiKey: candidate.provider === "orion" ? "" : candidate.apiKey,
					endpoint: candidate.provider === "orion" ? DEFAULT_SETTINGS.endpoint : candidate.endpoint
				})
			});
			const payload = await response.json();
			const ready = response.ok && payload.ready === true;
			setConnection({
				phase: ready ? "ready" : "error",
				message: payload.message || (ready ? "模型连接正常" : "模型连接检测失败")
			});
			return ready;
		} catch (error) {
			if (signal?.aborted) return false;
			setConnection({
				phase: "error",
				message: error instanceof Error ? error.message : "模型连接检测失败"
			});
			return false;
		}
	}
	(0, import_react.useEffect)(() => {
		let cancelled = false;
		const controller = new AbortController();
		const timer = window.setTimeout(async () => {
			const saved = window.sessionStorage.getItem("mobai-settings");
			let restored = { ...DEFAULT_SETTINGS };
			try {
				if (saved) {
					const parsed = JSON.parse(saved);
					const needsTeacherModeMigration = parsed.teacherOriginalMode !== true;
					const migratedModel = parsed.provider === "deepseek" && (!parsed.model || parsed.model === "deepseek-chat" || parsed.model === "deepseek-reasoner") ? "deepseek-v4-flash" : parsed.model;
					restored = {
						...DEFAULT_SETTINGS,
						...parsed,
						model: parsed.provider === "orion" ? "Orion" : migratedModel || DEFAULT_SETTINGS.model,
						endpoint: parsed.provider === "orion" ? DEFAULT_SETTINGS.endpoint : parsed.endpoint || DEFAULT_SETTINGS.endpoint,
						strength: parsed.structureMode ? parsed.strength ?? DEFAULT_SETTINGS.strength : DEFAULT_SETTINGS.strength,
						teacherStrengthLevel: Number.isFinite(Number(parsed.teacherStrengthLevel)) ? Math.max(0, Math.min(9, Math.round(Number(parsed.teacherStrengthLevel)))) : DEFAULT_SETTINGS.teacherStrengthLevel,
						structureMode: needsTeacherModeMigration ? "flexible" : parsed.structureMode ?? "flexible",
						qualityGateEnabled: needsTeacherModeMigration ? false : parsed.qualityGateEnabled ?? false,
						deepThinkingEnabled: parsed.deepThinkingEnabled ?? false,
						teacherOriginalMode: true
					};
				}
			} catch {
				window.sessionStorage.removeItem("mobai-settings");
			}
			if (!cancelled) {
				setSettings(restored);
				setDraftSettings(restored);
				if (await probeSettings(restored, controller.signal) && !cancelled) setNotice("模型连接检测通过");
			}
		}, 0);
		return () => {
			cancelled = true;
			controller.abort();
			window.clearTimeout(timer);
		};
	}, []);
	(0, import_react.useEffect)(() => {
		if (!notice) return;
		const timer = window.setTimeout(() => setNotice(""), 2800);
		return () => window.clearTimeout(timer);
	}, [notice]);
	(0, import_react.useEffect)(() => {
		let cancelled = false;
		const cachedValue = window.localStorage.getItem("mobai-announcement-cache");
		const dismissedRevision = window.localStorage.getItem("mobai-announcement-dismissed");
		function applyAnnouncement(item) {
			if (cancelled || !item?.published || !item.updated_at) return;
			setAnnouncement(item);
			const unread = item.updated_at !== window.localStorage.getItem("mobai-announcement-dismissed");
			setAnnouncementUnread(unread);
			if (unread) setAnnouncementOpen(true);
		}
		if (cachedValue) try {
			const cached = JSON.parse(cachedValue);
			if (cached.updated_at !== dismissedRevision) applyAnnouncement(cached);
			else setAnnouncement(cached);
		} catch {
			window.localStorage.removeItem("mobai-announcement-cache");
		}
		const controller = new AbortController();
		fetch("/api/notice", {
			cache: "no-store",
			signal: controller.signal
		}).then(async (response) => {
			if (!response.ok) throw new Error("通知同步失败");
			return response.json();
		}).then((payload) => {
			if (cancelled) return;
			if (payload.notice?.published) {
				window.localStorage.setItem("mobai-announcement-cache", JSON.stringify(payload.notice));
				applyAnnouncement(payload.notice);
			} else if (!payload.offline) {
				window.localStorage.removeItem("mobai-announcement-cache");
				setAnnouncement(null);
				setAnnouncementUnread(false);
			}
		}).catch(() => {});
		return () => {
			cancelled = true;
			controller.abort();
		};
	}, []);
	const isWorking = stage === "analyzing" || stage === "planning" || stage === "rewriting";
	const usesDirectRewrite = settings.provider === "orion" || Boolean(settings.apiKey.trim());
	const modelReady = connection.phase === "ready";
	const isModelBlocked = usesDirectRewrite && !modelReady;
	const activeStageIndex = stage === "analyzing" ? 0 : stage === "planning" ? 1 : stage === "rewriting" ? 2 : stage === "done" ? 3 : -1;
	const sourceCount = countCharacters(source);
	const resultCount = countCharacters(result);
	const diffParts = (0, import_react.useMemo)(() => result ? diffWordsWithSpace(source, result) : [], [source, result]);
	const activeFile = files.find((file) => file.id === activeFileId);
	async function callModelStage(requestedStage, text, currentAnalysis = "", currentPlan = "") {
		const provider = settings.provider;
		const model = provider === "orion" ? "Orion" : settings.model;
		const response = await fetch("/api/rewrite", {
			method: "POST",
			signal: abortControllerRef.current?.signal,
			headers: {
				"Content-Type": "application/json",
				Accept: "text/event-stream, application/json"
			},
			body: JSON.stringify({
				stage: requestedStage,
				original: text,
				analysis: currentAnalysis,
				plan: currentPlan,
				strength: settings.strength,
				strengthLevel: settings.teacherStrengthLevel,
				structureMode: settings.structureMode,
				qualityGateEnabled: settings.qualityGateEnabled,
				deepThinkingEnabled: settings.deepThinkingEnabled,
				provider,
				model,
				apiKey: provider === "orion" ? "" : settings.apiKey,
				endpoint: provider === "orion" ? DEFAULT_SETTINGS.endpoint : settings.endpoint
			})
		});
		if (!(response.headers.get("content-type") ?? "").includes("text/event-stream")) {
			const payload = await response.json();
			if (!response.ok || !payload.content) {
				if (payload.quality) setQuality(payload.quality);
				throw new Error(payload.error || "模型没有返回有效内容");
			}
			return {
				content: payload.content.trim(),
				quality: payload.quality
			};
		}
		if (!response.ok || !response.body) throw new Error(`改写服务返回 ${response.status}`);
		const reader = response.body.getReader();
		const decoder = new TextDecoder();
		let buffer = "";
		let completedPayload = null;
		let streamError = "";
		const handleEvent = (block) => {
			let event = "message";
			const dataLines = [];
			for (const line of block.split(/\r?\n/)) {
				if (line.startsWith("event:")) event = line.slice(6).trim();
				if (line.startsWith("data:")) dataLines.push(line.slice(5).trimStart());
			}
			if (!dataLines.length) return;
			const payload = JSON.parse(dataLines.join("\n"));
			if (event === "start") setRewriteProgress({
				completed: 0,
				total: payload.total ?? 0,
				retry: 0,
				message: "正在准备改写批次"
			});
			else if (event === "batch") {
				if (typeof payload.partial === "string") setResult(payload.partial);
				setRewriteProgress({
					completed: payload.completed ?? 0,
					total: payload.total ?? 0,
					retry: 0,
					message: "已收到最新改写结果"
				});
			} else if (event === "retry") setRewriteProgress({
				completed: payload.completed ?? 0,
				total: payload.total ?? 0,
				retry: payload.retry ?? 0,
				message: `连接波动，正在进行第 ${payload.retry ?? 1}/${payload.maxRetries ?? 3} 次重试`
			});
			else if (event === "complete") completedPayload = {
				content: payload.content,
				quality: payload.quality
			};
			else if (event === "error") {
				if (payload.quality) setQuality(payload.quality);
				if (typeof payload.partial === "string") setResult(payload.partial);
				streamError = payload.message || "改写过程中断";
			}
		};
		while (true) {
			const { value, done } = await reader.read();
			buffer += decoder.decode(value, { stream: !done });
			const blocks = buffer.split(/\r?\n\r?\n/);
			buffer = blocks.pop() ?? "";
			blocks.forEach(handleEvent);
			if (done) break;
		}
		if (buffer.trim()) handleEvent(buffer);
		if (streamError) throw new Error(streamError);
		const payload = completedPayload;
		if (!payload?.content) throw new Error("模型流已结束，但没有收到完整正文");
		return {
			content: payload.content.trim(),
			quality: payload.quality
		};
	}
	async function executePipeline(text) {
		if (!(settings.provider === "orion" || Boolean(settings.apiKey.trim()))) {
			setStage("analyzing");
			await sleep(520, abortControllerRef.current?.signal);
			const nextAnalysis = demoAnalysis(text);
			setAnalysis(nextAnalysis);
			setStage("planning");
			await sleep(560, abortControllerRef.current?.signal);
			const nextPlan = demoPlan(text, settings.structureMode);
			setPlan(nextPlan);
			setStage("rewriting");
			await sleep(720, abortControllerRef.current?.signal);
			const nextResult = localRewrite(text, settings.strength, settings.structureMode);
			if (nextResult.trim() === text.trim()) throw new Error("演示规则没有匹配这篇稿件，请在模型设置中启用 Orion");
			return {
				analysis: nextAnalysis,
				plan: nextPlan,
				result: nextResult
			};
		}
		const nextAnalysis = "";
		const nextPlan = "";
		setAnalysis(nextAnalysis);
		setPlan(nextPlan);
		setStage("rewriting");
		const rewriteResponse = await callModelStage("rewrite", text);
		return {
			analysis: nextAnalysis,
			plan: nextPlan,
			result: rewriteResponse.content,
			quality: rewriteResponse.quality
		};
	}
	async function startRewrite() {
		if (isWorking) return;
		if (isModelBlocked) {
			setNotice(connection.phase === "checking" ? "正在检测模型连接" : connection.message);
			setDraftSettings(settings);
			setSettingsOpen(true);
			return;
		}
		if (mode === "batch") {
			if (!files.length) {
				const message = "先导入需要批量改写的文件";
				setErrorMessage(message);
				setStage("error");
				setNotice(message);
				fileInputRef.current?.click();
				return;
			}
			stopRequestedRef.current = false;
			abortControllerRef.current = new AbortController();
			let currentFileId = null;
			setErrorMessage("");
			try {
				for (const file of files) {
					if (stopRequestedRef.current) throw stoppedError();
					currentFileId = file.id;
					workingFileIdRef.current = file.id;
					setActiveFileId(file.id);
					setSource(file.text);
					setResult(file.output?.result ?? "");
					setRewriteProgress({
						completed: 0,
						total: 0,
						retry: 0,
						message: ""
					});
					setErrorMessage("");
					setFiles((current) => current.map((item) => item.id === file.id ? {
						...item,
						status: "working",
						error: void 0
					} : item));
					const output = await executePipeline(file.text);
					setResult(output.result);
					setQuality(output.quality ?? null);
					setFiles((current) => current.map((item) => item.id === file.id ? {
						...item,
						status: "done",
						output,
						error: void 0
					} : item));
				}
				setStage("done");
				setNotice(`已完成 ${files.length} 篇稿件`);
			} catch (error) {
				if (stopRequestedRef.current || isStoppedFailure(error)) {
					const message = "改写已手动停止";
					setStage("stopped");
					setErrorMessage(message);
					setFiles((current) => current.map((item) => item.id === currentFileId ? {
						...item,
						status: "stopped",
						error: message
					} : item));
					setNotice(message);
					return;
				}
				const message = error instanceof Error ? error.message : "批量改写中断";
				setStage("error");
				setErrorMessage(message);
				setFiles((current) => current.map((item) => item.id === currentFileId ? {
					...item,
					status: "error",
					error: message
				} : item));
				setNotice(message);
			} finally {
				abortControllerRef.current = null;
				workingFileIdRef.current = null;
			}
			return;
		}
		if (sourceCount < 20) {
			const message = "至少输入 20 个字再开始改写";
			setErrorMessage(message);
			setStage("error");
			setNotice(message);
			return;
		}
		stopRequestedRef.current = false;
		abortControllerRef.current = new AbortController();
		setErrorMessage("");
		setResult("");
		setRewriteProgress({
			completed: 0,
			total: 0,
			retry: 0,
			message: ""
		});
		setAnalysis("");
		setPlan("");
		setQuality(null);
		try {
			const output = await executePipeline(source);
			setResult(output.result);
			setQuality(output.quality ?? null);
			setStage("done");
			setNotice(settings.provider === "orion" || settings.apiKey ? settings.qualityGateEnabled ? "自然改写完成并通过质量门" : "自然改写完成" : "演示改写完成，可配置模型生成真实新稿");
		} catch (error) {
			if (stopRequestedRef.current || isStoppedFailure(error)) {
				const message = "改写已手动停止";
				setStage("stopped");
				setErrorMessage(message);
				setNotice(message);
				return;
			}
			const message = error instanceof Error ? error.message : "改写失败，请检查模型设置";
			setStage("error");
			setErrorMessage(message);
			setNotice(message);
		} finally {
			abortControllerRef.current = null;
		}
	}
	function stopRewrite() {
		if (!isWorking) return;
		const message = "改写已手动停止";
		stopRequestedRef.current = true;
		abortControllerRef.current?.abort();
		setStage("stopped");
		setErrorMessage(message);
		setNotice(message);
		if (workingFileIdRef.current) {
			const id = workingFileIdRef.current;
			setFiles((current) => current.map((item) => item.id === id ? {
				...item,
				status: "stopped",
				error: message
			} : item));
		}
	}
	async function importFiles(event) {
		const selected = Array.from(event.target.files ?? []);
		if (!selected.length) return;
		try {
			const imported = [];
			for (const file of selected) {
				const text = await readDraftFile(file);
				if (!text) continue;
				imported.push({
					id: `${file.name}-${file.lastModified}-${crypto.randomUUID()}`,
					name: file.name,
					text,
					status: "ready"
				});
			}
			if (!imported.length) throw new Error("文件里没有读取到正文");
			if (mode === "batch" || imported.length > 1) {
				setMode("batch");
				setFiles((current) => [...current, ...imported]);
			} else {
				setSource(imported[0].text);
				setResult("");
				setAnalysis("");
				setPlan("");
				setQuality(null);
				setErrorMessage("");
				setStage("idle");
			}
			setActiveFileId(imported[0].id);
			setSource(imported[0].text);
			setNotice(`已导入 ${imported.length} 个文件`);
		} catch (error) {
			setNotice(error instanceof Error ? error.message : "无法读取这个文件");
		} finally {
			event.target.value = "";
		}
	}
	function selectFile(file) {
		setActiveFileId(file.id);
		setSource(file.text);
		setResult(file.output?.result ?? "");
		setAnalysis(file.output?.analysis ?? "");
		setPlan(file.output?.plan ?? "");
		setQuality(file.output?.quality ?? null);
		setErrorMessage(file.error ?? "");
		setStage(file.status === "done" ? "done" : file.status === "error" ? "error" : file.status === "stopped" ? "stopped" : "idle");
	}
	function removeFile(id) {
		setFiles((current) => current.filter((file) => file.id !== id));
		if (activeFileId === id) {
			setActiveFileId(null);
			setSource("");
			setResult("");
			setQuality(null);
			setErrorMessage("");
			setStage("idle");
		}
	}
	async function saveSettings() {
		const defaultModel = draftSettings.provider === "deepseek" ? "deepseek-v4-flash" : draftSettings.provider === "openai" ? "gpt-4.1-mini" : "Orion";
		const next = {
			...draftSettings,
			model: draftSettings.provider === "orion" ? "Orion" : draftSettings.model.trim() || defaultModel,
			endpoint: draftSettings.provider === "orion" ? DEFAULT_SETTINGS.endpoint : draftSettings.endpoint.trim() || DEFAULT_SETTINGS.endpoint
		};
		setSettings(next);
		window.sessionStorage.setItem("mobai-settings", JSON.stringify(next));
		setSettingsOpen(false);
		setQuality(null);
		setNotice(await probeSettings(next) ? "模型连接检测通过，配置已保存" : next.provider !== "orion" && !next.apiKey ? "已切换到演示模式" : "配置已保存，请检查连接状态");
	}
	async function copyResult() {
		if (!result) return;
		await navigator.clipboard.writeText(result);
		setNotice("新稿已复制");
	}
	function exportResult() {
		if (!result) return;
		const baseName = activeFile?.name.replace(/\.[^.]+$/, "") || "改写稿";
		const url = URL.createObjectURL(new Blob([result], { type: "text/plain;charset=utf-8" }));
		const link = document.createElement("a");
		link.href = url;
		link.download = `${baseName}-墨白改写.txt`;
		link.click();
		URL.revokeObjectURL(url);
		setNotice("新稿已经导出");
	}
	function resetWorkspace() {
		setSource("");
		setResult("");
		setAnalysis("");
		setPlan("");
		setQuality(null);
		setErrorMessage("");
		setStage("idle");
		setFiles([]);
		setActiveFileId(null);
	}
	function closeAnnouncement() {
		if (announcement?.updated_at) window.localStorage.setItem("mobai-announcement-dismissed", announcement.updated_at);
		setAnnouncementUnread(false);
		setAnnouncementOpen(false);
	}
	function openAnnouncement() {
		if (!announcement) {
			setNotice("当前没有软件通知");
			return;
		}
		setAnnouncementOpen(true);
	}
	return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("main", {
		className: "app-shell",
		children: [
			/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("header", {
				className: "topbar",
				children: [
					/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
						className: "brand",
						"aria-label": "墨白",
						children: [
							/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
								className: "brand-mark",
								"aria-hidden": "true",
								children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("i", {})
							}),
							/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
								className: "brand-name",
								children: "墨白"
							}),
							/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
								className: "brand-subtitle",
								children: "自然改写工作台"
							})
						]
					}),
					/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
						className: `phase-track phase-${stage}`,
						"aria-label": "改写进度",
						children: (usesDirectRewrite ? DIRECT_STAGES : STAGES).map((item, index) => {
							const isActive = activeStageIndex === index;
							const isComplete = activeStageIndex > index || stage === "done";
							return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
								className: `phase ${isActive ? "active" : ""} ${isComplete ? "complete" : ""}`,
								children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
									className: "phase-dot",
									children: isComplete ? "✓" : index + 1
								}), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
									className: "phase-copy",
									children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("b", { children: item.short }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("small", { children: item.label })]
								})]
							}, item.key);
						})
					}),
					/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
						className: "top-actions",
						children: [
							/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
								className: `connection-badge ${connection.phase}`,
								title: connection.message,
								children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("i", { "aria-hidden": "true" }), connection.phase === "checking" ? `${settings.provider === "deepseek" ? "DeepSeek" : settings.provider === "openai" ? "OpenAI" : "Orion"} 检测中` : connection.phase === "ready" ? `${settings.provider === "deepseek" ? "DeepSeek" : settings.provider === "openai" ? "OpenAI" : "Orion"} 已就绪` : connection.phase === "error" ? `${settings.provider === "deepseek" ? "DeepSeek" : settings.provider === "openai" ? "OpenAI" : "Orion"} 连接失败` : "演示模式"]
							}),
							/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
								className: `icon-button announcement-button ${announcementUnread ? "unread" : ""}`,
								onClick: openAnnouncement,
								"aria-label": announcementUnread ? "打开新通知" : "打开软件通知",
								children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
									"aria-hidden": "true",
									children: "讯"
								})
							}),
							/* @__PURE__ */ (0, import_jsx_runtime.jsx)("a", {
								className: "icon-button website-button",
								href: "https://www.mb.zhijianku.com/",
								target: "_blank",
								rel: "noreferrer",
								"aria-label": "访问墨白官网",
								title: "访问官网",
								children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
									"aria-hidden": "true",
									children: "网"
								})
							}),
							/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
								className: "icon-button",
								onClick: () => {
									setDraftSettings(settings);
									setSettingsOpen(true);
								},
								"aria-label": "打开模型设置",
								children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
									"aria-hidden": "true",
									children: "•••"
								})
							})
						]
					})
				]
			}),
			/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
				className: "workbench",
				children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("aside", {
					className: "control-panel",
					children: [
						/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
							className: "mode-switch",
							role: "tablist",
							"aria-label": "处理模式",
							children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
								className: mode === "single" ? "selected" : "",
								onClick: () => setMode("single"),
								role: "tab",
								children: "单篇"
							}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
								className: mode === "batch" ? "selected" : "",
								onClick: () => setMode("batch"),
								role: "tab",
								children: "批量"
							})]
						}),
						/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", {
							className: "control-section source-section",
							children: [
								/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
									className: "section-heading",
									children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "稿件" }), (source || files.length > 0) && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
										onClick: resetWorkspace,
										children: "清空"
									})]
								}),
								/* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", {
									ref: fileInputRef,
									className: "visually-hidden",
									type: "file",
									multiple: mode === "batch",
									accept: ".txt,.md,.markdown,.html,.htm,.docx",
									onChange: importFiles
								}),
								/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", {
									className: "upload-card",
									onClick: () => fileInputRef.current?.click(),
									children: [
										/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
											className: "upload-glyph",
											"aria-hidden": "true",
											children: "＋"
										}),
										/* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: mode === "batch" ? "添加一组稿件" : "导入一篇稿件" }),
										/* @__PURE__ */ (0, import_jsx_runtime.jsx)("small", { children: "TXT · MD · HTML · DOCX" })
									]
								}),
								mode === "batch" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
									className: "file-queue",
									"aria-label": "批量文件队列",
									children: files.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
										className: "queue-empty",
										children: "导入文件后会在这里排队。"
									}) : files.map((file, index) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
										className: `queue-item ${activeFileId === file.id ? "active" : ""}`,
										children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", {
											className: "queue-main",
											onClick: () => selectFile(file),
											children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
												className: `queue-index status-${file.status}`,
												children: file.status === "done" ? "✓" : index + 1
											}), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("b", { children: file.name }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("small", { children: [
												formatBytes(countCharacters(file.text)),
												" 字 · ",
												file.status === "working" ? "改写中" : file.status === "done" ? "已完成" : file.status === "error" ? "失败" : file.status === "stopped" ? "已停止" : "待处理"
											] })] })]
										}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
											className: "queue-remove",
											onClick: () => removeFile(file.id),
											"aria-label": `移除 ${file.name}`,
											children: "×"
										})]
									}, file.id))
								})
							]
						}),
						/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", {
							className: "control-section strength-section",
							children: [
								/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
									className: "section-heading",
									children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "改写幅度" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("b", { children: usesDirectRewrite ? TEACHER_STRENGTH_LABELS[settings.teacherStrengthLevel] : `${settings.strength}%` })]
								}),
								/* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", {
									className: "strength-slider",
									type: "range",
									min: usesDirectRewrite ? 0 : 20,
									max: usesDirectRewrite ? 9 : 95,
									step: "1",
									value: usesDirectRewrite ? settings.teacherStrengthLevel : settings.strength,
									onChange: (event) => {
										const value = Number(event.target.value);
										if (usesDirectRewrite) {
											setSettings((current) => ({
												...current,
												teacherStrengthLevel: value
											}));
											setDraftSettings((current) => ({
												...current,
												teacherStrengthLevel: value
											}));
										} else {
											setSettings((current) => ({
												...current,
												strength: value
											}));
											setDraftSettings((current) => ({
												...current,
												strength: value
											}));
										}
									},
									"aria-label": "改写幅度"
								}),
								usesDirectRewrite ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
									className: "slider-labels teacher-slider-labels",
									children: TEACHER_STRENGTH_LABELS.map((label) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: label }, label))
								}) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
									className: "slider-labels",
									children: [
										/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "保守" }),
										/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "平衡" }),
										/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "深度" })
									]
								}),
								/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
									className: "strength-note",
									children: usesDirectRewrite ? `10 档改写幅度；当前为“${TEACHER_STRENGTH_LABELS[settings.teacherStrengthLevel]}”，强度越高改写越明显。` : settings.structureMode === "locked" ? settings.strength < 45 ? "只处理明显套话、重复和不顺的语序。" : settings.strength < 76 ? "加强句式变化，但不移动或拆分任何段落。" : "深度调整段内表达，文章骨架仍保持不变。" : settings.strength < 45 ? "以句式润色为主，只做少量结构调整。" : settings.strength < 76 ? "重新安排部分信息，同时保留事实与观点。" : "明显改变叙述路径，适合结构僵硬的初稿。"
								})
							]
						}),
						/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", {
							className: "control-section model-summary",
							children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
								className: "section-heading",
								children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "生成方式" })
							}), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", {
								onClick: () => {
									setDraftSettings(settings);
									setSettingsOpen(true);
								},
								children: [
									/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
										className: "model-monogram",
										children: settings.provider === "orion" ? "R" : settings.apiKey ? settings.provider === "deepseek" ? "D" : "O" : "演"
									}),
									/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("b", { children: settings.provider === "orion" ? "Orion 本地模型" : settings.apiKey ? settings.model : "本地规则演示" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("small", { children: usesDirectRewrite ? connection.phase === "error" ? connection.message : settings.provider === "deepseek" && settings.deepThinkingEnabled ? "自然改写 · 深度思考开启" : settings.qualityGateEnabled ? "自然改写 · 质量门开启" : "自然改写 · 无附加限制" : "当前只是规则演示，不代表模型质量" })] }),
									/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
										"aria-hidden": "true",
										children: "›"
									})
								]
							})]
						}),
						/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
							className: "action-wrap",
							children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", {
								className: `primary-action ${isWorking ? "stop-action" : ""}`,
								onClick: isWorking ? stopRewrite : startRewrite,
								disabled: !isWorking && isModelBlocked,
								children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: isWorking ? "停止改写" : isModelBlocked ? connection.phase === "checking" ? "正在检测模型" : "检查模型连接" : mode === "batch" ? `改写全部${files.length ? ` · ${files.length}` : ""}` : "开始自然改写" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("i", {
									"aria-hidden": "true",
									children: isWorking ? "■" : "→"
								})]
							}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: usesDirectRewrite ? settings.qualityGateEnabled ? "保持原文顺序进行自然改写，最后执行可选质量门" : "保持原文顺序进行自然改写，模型结果直接展示" : "演示模式不会发送或保存你的文章" })]
						})
					]
				}), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", {
					className: "editor-area",
					children: [
						/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
							className: "editor-toolbar",
							children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
								className: "eyebrow",
								children: "当前稿件"
							}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h1", { children: activeFile?.name || "未命名文章" })] }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
								className: "view-switch",
								role: "group",
								"aria-label": "结果显示方式",
								children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
									className: showDiff ? "active" : "",
									onClick: () => setShowDiff(true),
									children: "差异"
								}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
									className: !showDiff ? "active" : "",
									onClick: () => setShowDiff(false),
									children: "净稿"
								})]
							})]
						}),
						/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
							className: "editor-grid",
							children: [
								/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("article", {
									className: "document-pane original-pane",
									children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("header", { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
										className: "pane-index",
										children: "A"
									}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: "原始稿" })] }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [formatBytes(sourceCount), " 字"] })] }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("textarea", {
										value: source,
										onChange: (event) => {
											setSource(event.target.value);
											setQuality(null);
											setErrorMessage("");
											if (stage === "done" || stage === "error" || stage === "stopped") setStage("idle");
										},
										placeholder: "把需要改写的文章粘贴到这里，或从左侧导入文件……",
										"aria-label": "原始文章"
									})]
								}),
								/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
									className: `rewrite-spine ${isWorking ? "moving" : ""}`,
									"aria-hidden": "true",
									children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: isWorking ? "···" : "→" })
								}),
								/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("article", {
									className: "document-pane result-pane",
									children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("header", { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
										className: "pane-index",
										children: "B"
									}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: "改写稿" })] }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
										className: "pane-actions",
										children: [
											/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: stage === "error" ? "生成失败" : stage === "stopped" ? "已停止" : result ? `${formatBytes(resultCount)} 字` : "等待生成" }),
											result && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
												onClick: copyResult,
												children: "复制"
											}),
											result && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
												onClick: exportResult,
												children: "导出"
											})
										]
									})] }), isWorking ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
										className: "generating-state",
										children: [
											/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
												className: "writing-lines",
												children: [
													/* @__PURE__ */ (0, import_jsx_runtime.jsx)("i", {}),
													/* @__PURE__ */ (0, import_jsx_runtime.jsx)("i", {}),
													/* @__PURE__ */ (0, import_jsx_runtime.jsx)("i", {}),
													/* @__PURE__ */ (0, import_jsx_runtime.jsx)("i", {}),
													/* @__PURE__ */ (0, import_jsx_runtime.jsx)("i", {})
												]
											}),
											/* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: stage === "analyzing" ? "正在拆解原文" : stage === "planning" ? settings.structureMode === "locked" ? "正在制定逐段策略" : "正在重组文章结构" : "正在写出新稿" }),
											/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: usesDirectRewrite ? `保持原始顺序调用${settings.provider === "orion" ? " Orion" : "当前模型"}` : stage === "analyzing" ? "识别事实、语气和模板化表达" : stage === "planning" ? settings.structureMode === "locked" ? "逐段标出需要修改的表达，不改变文章骨架" : "改变信息顺序，安排新的段落任务" : settings.structureMode === "locked" ? "逐段润色并校验段落一一对应" : "按照新结构生成并统一全文语气" }),
											usesDirectRewrite && rewriteProgress.total > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
												className: "stream-progress",
												role: "progressbar",
												"aria-valuemin": 0,
												"aria-valuemax": rewriteProgress.total,
												"aria-valuenow": rewriteProgress.completed,
												children: [
													/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("i", { style: { width: `${Math.round(rewriteProgress.completed / rewriteProgress.total * 100)}%` } }) }),
													/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: rewriteProgress.message || `正在处理 ${rewriteProgress.completed + 1}/${rewriteProgress.total}` }),
													/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("b", { children: [
														rewriteProgress.completed,
														"/",
														rewriteProgress.total
													] })
												]
											})
										]
									}) : (stage === "error" || stage === "stopped") && errorMessage ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
										className: `error-result ${stage === "stopped" ? "stopped" : ""}`,
										role: "alert",
										"aria-live": "assertive",
										children: [
											/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
												className: "error-mark",
												"aria-hidden": "true",
												children: stage === "stopped" ? "■" : "!"
											}),
											/* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: stage === "stopped" ? "改写已停止" : "改写未完成" }),
											/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: errorMessage }),
											/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
												type: "button",
												onClick: startRewrite,
												children: "重新尝试"
											})
										]
									}) : result ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
										className: `result-copy ${showDiff ? "diff-mode" : ""}`,
										"aria-live": "polite",
										children: showDiff ? diffParts.map((part, index) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
											className: part.added ? "diff-added" : part.removed ? "diff-removed" : "",
											children: part.value
										}, `${index}-${part.value.slice(0, 8)}`)) : result
									}) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
										className: "empty-result",
										children: [
											/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
												className: "empty-mark",
												"aria-hidden": "true",
												children: "稿"
											}),
											/* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: "新稿会出现在这里" }),
											/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: usesDirectRewrite ? "系统会保持原始顺序，只调整文字表达。" : settings.structureMode === "locked" ? "系统会保留原有段落顺序，只调整段内语言。" : "系统会先提取内容，再重排结构，最后生成整篇文章。" })
										]
									})]
								})
							]
						}),
						/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("footer", {
							className: "editor-footer",
							children: [
								/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("i", { className: "legend-dot added" }), "绿色是新增表达"] }),
								/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("i", { className: "legend-dot removed" }), "红色是移除内容"] }),
								/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
									className: "footer-note",
									children: "改写结果需要人工核对事实与引用"
								})
							]
						})
					]
				})]
			}),
			announcementOpen && announcement && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
				className: "modal-backdrop",
				role: "presentation",
				onMouseDown: closeAnnouncement,
				children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", {
					className: `announcement-dialog category-${announcement.category}`,
					role: "dialog",
					"aria-modal": "true",
					"aria-labelledby": "announcement-title",
					onMouseDown: (event) => event.stopPropagation(),
					children: [
						/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("header", { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
							className: "announcement-category",
							children: [announcement.category === "update" ? "版本更新" : announcement.category === "important" ? "重要通知" : "软件通知", announcement.version ? ` · ${announcement.version}` : ""]
						}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
							className: "close-button",
							onClick: closeAnnouncement,
							"aria-label": "关闭通知",
							children: "×"
						})] }),
						/* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", {
							id: "announcement-title",
							children: announcement.title
						}),
						/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: announcement.content }),
						/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("footer", { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("small", { children: announcement.published_at ? new Date(announcement.published_at).toLocaleString("zh-CN", { hour12: false }) : "" }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
							className: "text-button",
							onClick: closeAnnouncement,
							children: "我知道了"
						}), announcement.action_url && announcement.action_label && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("a", {
							className: "announcement-action",
							href: announcement.action_url,
							target: "_blank",
							rel: "noreferrer",
							onClick: closeAnnouncement,
							children: announcement.action_label
						})] })] })
					]
				})
			}),
			settingsOpen && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
				className: "modal-backdrop",
				role: "presentation",
				onMouseDown: () => setSettingsOpen(false),
				children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", {
					className: "settings-dialog",
					role: "dialog",
					"aria-modal": "true",
					"aria-labelledby": "settings-title",
					onMouseDown: (event) => event.stopPropagation(),
					children: [
						/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("header", { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
							className: "eyebrow",
							children: "模型连接"
						}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", {
							id: "settings-title",
							children: "选择改写模型"
						})] }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
							className: "close-button",
							onClick: () => setSettingsOpen(false),
							"aria-label": "关闭设置",
							children: "×"
						})] }),
						/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
							className: "provider-grid",
							children: [
								/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", {
									className: draftSettings.provider === "deepseek" ? "active" : "",
									onClick: () => setDraftSettings((current) => ({
										...current,
										provider: "deepseek",
										model: current.provider === "deepseek" ? current.model : "deepseek-v4-flash",
										useOrionAnalysis: false,
										teacherOriginalMode: true,
										structureMode: "flexible"
									})),
									children: [
										/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "D" }),
										/* @__PURE__ */ (0, import_jsx_runtime.jsx)("b", { children: "DeepSeek" }),
										/* @__PURE__ */ (0, import_jsx_runtime.jsx)("small", { children: "自然改写" })
									]
								}),
								/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", {
									className: draftSettings.provider === "openai" ? "active" : "",
									onClick: () => setDraftSettings((current) => ({
										...current,
										provider: "openai",
										model: current.provider === "openai" ? current.model : "gpt-4.1-mini",
										useOrionAnalysis: false,
										deepThinkingEnabled: false,
										teacherOriginalMode: true,
										structureMode: "flexible"
									})),
									children: [
										/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "O" }),
										/* @__PURE__ */ (0, import_jsx_runtime.jsx)("b", { children: "OpenAI" }),
										/* @__PURE__ */ (0, import_jsx_runtime.jsx)("small", { children: "自然改写" })
									]
								}),
								/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", {
									className: draftSettings.provider === "orion" ? "active" : "",
									onClick: () => setDraftSettings((current) => ({
										...current,
										provider: "orion",
										model: "Orion",
										useOrionAnalysis: false,
										qualityGateEnabled: false,
										deepThinkingEnabled: false,
										teacherOriginalMode: true,
										structureMode: "flexible",
										endpoint: DEFAULT_SETTINGS.endpoint
									})),
									children: [
										/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "R" }),
										/* @__PURE__ */ (0, import_jsx_runtime.jsx)("b", { children: "Orion 本地模型" }),
										/* @__PURE__ */ (0, import_jsx_runtime.jsx)("small", { children: "自然改写" })
									]
								})
							]
						}),
						draftSettings.provider !== "orion" && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", {
							className: "field-label",
							children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "模型名称" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", {
								value: draftSettings.model,
								onChange: (event) => setDraftSettings((current) => ({
									...current,
									model: event.target.value
								})),
								placeholder: "例如 deepseek-v4-flash"
							})]
						}), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", {
							className: "field-label",
							children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "API Key" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", {
								type: "password",
								value: draftSettings.apiKey,
								onChange: (event) => setDraftSettings((current) => ({
									...current,
									apiKey: event.target.value
								})),
								placeholder: "输入密钥后启用真实模型",
								autoComplete: "off"
							})]
						})] }),
						draftSettings.provider === "deepseek" && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", {
							className: "assist-option",
							children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", {
								type: "checkbox",
								checked: draftSettings.deepThinkingEnabled,
								onChange: (event) => setDraftSettings((current) => ({
									...current,
									deepThinkingEnabled: event.target.checked
								}))
							}), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("b", { children: "深度思考" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("small", { children: "开启后使用 DeepSeek 思考模式，质量优先但响应时间更长；关闭时使用快速改写。" })] })]
						}),
						/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", {
							className: "assist-option",
							children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", {
								type: "checkbox",
								checked: draftSettings.qualityGateEnabled,
								onChange: (event) => setDraftSettings((current) => ({
									...current,
									qualityGateEnabled: event.target.checked
								}))
							}), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("b", { children: "自动质量门" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("small", { children: "开启后检查段落、数字、事实短语和内容长度；关闭时直接展示模型结果。" })] })]
						}),
						/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
							className: "privacy-note",
							children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("i", {
								"aria-hidden": "true",
								children: "锁"
							}), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { children: [
								/* @__PURE__ */ (0, import_jsx_runtime.jsx)("b", { children: draftSettings.provider === "orion" ? "只连接这台电脑上的模型" : "密钥只保留在当前浏览器会话" }),
								/* @__PURE__ */ (0, import_jsx_runtime.jsx)("br", {}),
								draftSettings.provider === "orion" ? "在本机完成自然改写，不需要云端密钥。" : "使用统一的自然改写流程；页面关闭后自动清除密钥。"
							] })]
						}),
						/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
							className: "dialog-actions",
							children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
								className: "text-button",
								onClick: () => setDraftSettings({ ...DEMO_SETTINGS }),
								children: "使用演示模式"
							}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
								className: "save-button",
								onClick: saveSettings,
								children: "保存并返回"
							})]
						})
					]
				})
			}),
			notice && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
				className: "toast",
				role: "status",
				children: notice
			})
		]
	});
}
//#endregion
export { Home as default };
