// ==UserScript==
// @name 一键将网页 AI 对话完美粘贴到 Word、WPS 和 Excel 的效率工具
// @description 一键复制 AI 回复到 Word / Excel,支持 ChatGPT、Claude、Gemini、DeepSeek、豆包、Kimi
// @namespace xiaotianguo
// @version 0.1.0
// @match https://chatgpt.com/*
// @match https://chat.openai.com/*
// @match https://chat.deepseek.com/*
// @match https://claude.ai/*
// @match https://*.claude.ai/*
// @match https://gemini.google.com/*
// @match https://kimi.moonshot.cn/*
// @match https://kimi.com/*
// @match https://www.kimi.com/*
// @match https://www.doubao.com/*
// @match https://doubao.com/*
// @grant clipboardWrite
// ==/UserScript==
(function () {
'use strict';
async function writeClipboard(html, text) {
var _a;
if (!((_a = navigator.clipboard) == null ? void 0 : _a.write)) {
throw new Error("当前浏览器不支持多格式剪贴板写入");
}
const item = new ClipboardItem({
"text/html": new Blob([html], { type: "text/html" }),
"text/plain": new Blob([text], { type: "text/plain" })
});
await navigator.clipboard.write([item]);
}
function escapeHtml(value) {
return value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
}
function normalizeText(value) {
return value.replace(/\u00a0/g, " ").replace(/\s+\n/g, "\n").trim();
}
const BLOCK_SELECTOR = "h1,h2,h3,h4,h5,h6,p,ul,ol,pre,blockquote,table,div";
const CODE_LANGUAGE_LABELS$1 = /* @__PURE__ */ new Set([
"bash",
"shell",
"sh",
"zsh",
"fish",
"console",
"terminal",
"python",
"javascript",
"typescript",
"js",
"ts",
"jsx",
"tsx",
"java",
"go",
"rust",
"c",
"cpp",
"c++",
"c#",
"cs",
"php",
"ruby",
"swift",
"kotlin",
"scala",
"sql",
"html",
"css",
"json",
"yaml",
"yml",
"xml",
"toml",
"ini",
"dockerfile",
"powershell",
"pwsh",
"lua",
"perl",
"r",
"matlab"
]);
function extractBlocks(root) {
const blocks = [];
const nodes = Array.from(root.children);
for (const child of nodes) {
if (isActionUi(child)) continue;
const extracted = extractBlock(child);
blocks.push(...extracted);
}
if (blocks.length === 0) {
const text = normalizeText(root.innerText || root.textContent || "");
if (text) {
blocks.push({ type: "paragraph", children: [{ type: "text", text }] });
}
}
return mergeAdjacentParagraphs(blocks);
}
function hasTable(blocks) {
return blocks.some((block) => block.type === "table");
}
function extractBlock(node) {
if (shouldIgnoreElement(node) || isCodeLanguageLabel(node)) {
return [];
}
const tag = node.tagName.toLowerCase();
if (tag === "div" && shouldFlattenDiv(node)) {
return extractBlocks(node);
}
if (/^h[1-6]$/.test(tag)) {
return [{ type: "heading", level: Number(tag[1]), children: extractInline(node) }];
}
if (tag === "p") {
return [{ type: "paragraph", children: extractInline(node) }];
}
if (tag === "blockquote") {
return [{ type: "blockquote", children: extractBlocks(node) }];
}
if (tag === "pre") {
return [{
type: "code_block",
text: normalizeText(node.innerText || node.textContent || ""),
language: node.getAttribute("data-language") || void 0
}];
}
if (tag === "table") {
return [{ type: "table", rows: extractTable(node) }];
}
if (tag === "ul" || tag === "ol") {
return [extractList(node)];
}
const directBlocks = Array.from(node.querySelectorAll(":scope > " + BLOCK_SELECTOR));
if (directBlocks.length > 0) {
return extractBlocks(node);
}
const text = normalizeText(node.innerText || node.textContent || "");
if (!text) {
return [];
}
return [{ type: "paragraph", children: extractInline(node) }];
}
function extractInline(node) {
const inlines = [];
for (const child of Array.from(node.childNodes)) {
if (child.nodeType === Node.TEXT_NODE) {
const text = child.textContent ?? "";
if (text) inlines.push({ type: "text", text });
continue;
}
if (child.nodeType !== Node.ELEMENT_NODE) continue;
const element = child;
if (isActionUi(element) || shouldIgnoreElement(element)) continue;
const tag = element.tagName.toLowerCase();
if (tag === "br") {
inlines.push({ type: "br" });
continue;
}
if (tag === "strong" || tag === "b") {
inlines.push({ type: "strong", children: extractInline(element) });
continue;
}
if (tag === "em" || tag === "i") {
inlines.push({ type: "em", children: extractInline(element) });
continue;
}
if (tag === "del" || tag === "s") {
inlines.push({ type: "del", children: extractInline(element) });
continue;
}
if (tag === "code") {
inlines.push({ type: "code", text: normalizeText(element.innerText || element.textContent || "") });
continue;
}
if (tag === "a") {
inlines.push({
type: "link",
href: element.getAttribute("href") || "",
children: extractInline(element)
});
continue;
}
const nested = extractInline(element);
if (nested.length > 0) {
inlines.push(...nested);
}
}
return mergeTextNodes(inlines);
}
function extractList(node) {
const items = Array.from(node.children).filter((child) => child instanceof HTMLLIElement).map((li) => {
const directBlockChildren = Array.from(li.children).filter(
(child) => child instanceof HTMLElement && child.matches(BLOCK_SELECTOR)
);
if (directBlockChildren.length === 0) {
return [{ type: "paragraph", children: extractInline(li) }];
}
return directBlockChildren.flatMap((child) => extractBlock(child));
});
return {
type: "list",
ordered: node.tagName.toLowerCase() === "ol",
items
};
}
function extractTable(table) {
const rowElements = Array.from(table.querySelectorAll("tr"));
return rowElements.map((row) => ({
cells: Array.from(row.children).filter((cell) => cell instanceof HTMLTableCellElement).map((cell) => ({
text: normalizeText(cell.innerText || cell.textContent || ""),
html: cell.innerHTML.trim(),
header: cell.tagName.toLowerCase() === "th"
}))
})).filter((row) => row.cells.length > 0);
}
function mergeTextNodes(nodes) {
const merged = [];
for (const node of nodes) {
if (node.type === "text") {
const text = node.text;
if (!text) continue;
const last = merged[merged.length - 1];
if ((last == null ? void 0 : last.type) === "text") {
last.text += text;
} else {
merged.push({ type: "text", text });
}
continue;
}
merged.push(node);
}
return merged;
}
function mergeAdjacentParagraphs(blocks) {
return blocks.filter((block) => {
if (block.type !== "paragraph") return true;
return normalizeText(block.children.map((child) => child.type === "text" ? child.text : "").join("")) !== "" || block.children.length > 0;
});
}
function isCodeLanguageLabel(node) {
const text = normalizeText(node.innerText || node.textContent || "");
if (!text) return false;
if (text.length > 20) return false;
if (text.includes("\n")) return false;
if (!CODE_LANGUAGE_LABELS$1.has(text.toLowerCase())) return false;
const next = node.nextElementSibling;
const prev = node.previousElementSibling;
const adjacentHasCode = [next, prev].some((element) => {
if (!element) return false;
const tag = element.tagName.toLowerCase();
return tag === "pre" || tag === "code" || element.querySelector("pre, code") !== null;
});
return adjacentHasCode;
}
function shouldIgnoreElement(node) {
if (isActionUi(node)) return true;
const tag = node.tagName.toLowerCase();
if (["button", "input", "textarea", "select", "option", "script", "style", "svg", "path"].includes(tag)) {
return true;
}
if (node.getAttribute("role") === "button") {
return true;
}
const ariaLabel = node.getAttribute("aria-label") || "";
if (/(复制|下载|copy|download)/i.test(ariaLabel)) {
return true;
}
const className = typeof node.className === "string" ? node.className : "";
if (/(toolbar|action|operate|copy|download|thinking|reasoning)/i.test(className)) {
return true;
}
const text = normalizeText(node.innerText || node.textContent || "");
if (/^(思考中|思考过程|推理中|已深度思考|thinking|reasoning)/i.test(text)) {
return true;
}
const hasInteractive = node.querySelector('button,[role="button"]') !== null;
const hasContentBlocks = node.querySelector("pre,table,blockquote,ul,ol,p,h1,h2,h3,h4,h5,h6") !== null;
if (hasInteractive && !hasContentBlocks && /^(text|plain\s*text|复制|下载|copy|download|text复制下载)+$/i.test(text.replace(/\s+/g, ""))) {
return true;
}
return false;
}
function shouldFlattenDiv(node) {
return !node.attributes.length || node.attributes.length === 1 && node.hasAttribute("class");
}
function isActionUi(node) {
return node.dataset.aiOfficeCopyUi === "1" || node.closest('[data-ai-office-copy-ui="1"]') !== null;
}
function serializeFirstTableForExcel(blocks) {
const table = blocks.find((block) => block.type === "table");
if (!table || table.type !== "table") {
return null;
}
return {
html: wrapTable(table.rows),
text: table.rows.map((row) => row.cells.map((cell) => cell.text).join(" ")).join("\n")
};
}
function wrapTable(rows) {
const html = rows.map((row) => `
${row.cells.map((cell) => {
const tag = cell.header ? "th" : "td";
return `<${tag}>${cell.html || escapeHtml(cell.text)}${tag}>`;
}).join("")}
`).join("");
return ``;
}
const REMOVE_SELECTORS = [
'[data-ai-office-copy-ui="1"]',
"button",
'[role="button"]',
"svg",
"script",
"style",
".absolute.left-0.top-0.right-0.bottom-0",
'[class*="copy"]',
'[class*="download"]',
'[class*="toolbar"]',
'[class*="action"]'
];
const CODE_LANGUAGE_LABELS = /* @__PURE__ */ new Set([
"bash",
"shell",
"sh",
"zsh",
"fish",
"console",
"terminal",
"python",
"javascript",
"typescript",
"js",
"ts",
"jsx",
"tsx",
"java",
"go",
"rust",
"c",
"cpp",
"c++",
"c#",
"cs",
"php",
"ruby",
"swift",
"kotlin",
"scala",
"sql",
"html",
"css",
"json",
"yaml",
"yml",
"xml",
"toml",
"ini",
"dockerfile",
"powershell",
"pwsh",
"lua",
"perl",
"r",
"matlab",
"运行"
]);
function serializeDomForWord(root) {
const clone = root.cloneNode(true);
for (const selector of REMOVE_SELECTORS) {
clone.querySelectorAll(selector).forEach((node) => node.remove());
}
stripCodeLanguageLabels(clone);
normalizeCodeBlocks(clone);
normalizeStructuredHtml(clone);
normalizeListStyles(clone);
const html = `${clone.innerHTML}`;
const text = renderStructuredText(clone).trim();
return { html, text };
}
function stripCodeLanguageLabels(root) {
root.querySelectorAll("div, span, p").forEach((node) => {
const text = normalizeText(node.innerText || node.textContent || "");
if (!text || text.length > 20 || text.includes("\n")) return;
if (!CODE_LANGUAGE_LABELS.has(text.toLowerCase())) return;
const next = node.nextElementSibling;
const prev = node.previousElementSibling;
const adjacentHasCode = [next, prev].some((element) => {
if (!element) return false;
const cls = typeof element.className === "string" ? element.className : "";
return element.tagName.toLowerCase() === "pre" || element.querySelector("pre, code") !== null || /code|pre/i.test(cls);
});
if (adjacentHasCode) {
node.remove();
}
});
}
function normalizeCodeBlocks(root) {
root.querySelectorAll('[class*="code-content"], [class*="code-area"], [class*="code-block"] pre').forEach((node) => {
const text = normalizeText(node.innerText || node.textContent || "");
if (!text) return;
if (node.tagName.toLowerCase() === "pre") return;
const pre = document.createElement("pre");
const code = document.createElement("code");
code.textContent = text;
pre.appendChild(code);
node.replaceWith(pre);
});
}
function normalizeStructuredHtml(root) {
root.querySelectorAll('.md-box-line-break, [class*="md-box-line-break"], .wrapper-GYqxgQ').forEach((node) => {
node.remove();
});
root.querySelectorAll(".paragraph, .paragraph-pP9ZLC, .paragraph-element").forEach((node) => {
if (node.tagName.toLowerCase() === "p") return;
const p = document.createElement("p");
p.innerHTML = node.innerHTML;
node.replaceWith(p);
});
root.querySelectorAll("li").forEach((li) => {
const children = Array.from(li.children);
if (children.length === 0) return;
const leadingParagraphs = children.filter((child) => child.tagName.toLowerCase() === "p");
if (leadingParagraphs.length > 1) {
const first = leadingParagraphs[0];
for (let i = 1; i < leadingParagraphs.length; i += 1) {
first.innerHTML += "
" + leadingParagraphs[i].innerHTML;
leadingParagraphs[i].remove();
}
}
const firstElement = li.firstElementChild;
if (firstElement && firstElement.tagName.toLowerCase() === "p" && li.childElementCount === 1) {
li.innerHTML = firstElement.innerHTML;
}
});
}
function normalizeListStyles(root) {
root.querySelectorAll("ol").forEach((ol) => {
ol.style.margin = "0.5em 0 0.5em 1.6em";
ol.style.paddingLeft = "1.2em";
});
root.querySelectorAll("li > p:first-child").forEach((p) => {
p.style.margin = "0";
});
root.querySelectorAll("ol > li > ul, ul > li > ul").forEach((ul) => {
ul.style.margin = "0.25em 0 0.25em 1.4em";
ul.style.paddingLeft = "1em";
ul.style.listStyleType = "circle";
});
root.querySelectorAll("ol > li > ol, ul > li > ol").forEach((ol) => {
ol.style.margin = "0.25em 0 0.25em 1.4em";
ol.style.paddingLeft = "1em";
});
}
function renderStructuredText(root) {
const blocks = [];
for (const node of Array.from(root.childNodes)) {
const rendered = renderNodeText(node, 0);
if (rendered) {
blocks.push(rendered);
}
}
return blocks.join("\n\n").replace(/\n{3,}/g, "\n\n");
}
function renderNodeText(node, depth) {
if (node.nodeType === Node.TEXT_NODE) {
return normalizeText(node.textContent || "");
}
if (node.nodeType !== Node.ELEMENT_NODE) return "";
const el = node;
const tag = el.tagName.toLowerCase();
if (tag === "h1" || tag === "h2" || tag === "h3" || tag === "h4" || tag === "h5" || tag === "h6") {
return normalizeText(el.innerText || el.textContent || "");
}
if (tag === "p") {
return normalizeInlineText(el);
}
if (tag === "pre") {
return normalizePreserveLines(el.innerText || el.textContent || "");
}
if (tag === "ul") {
return Array.from(el.children).filter((child) => child instanceof HTMLElement && child.tagName.toLowerCase() === "li").map((li) => renderListItem(li, depth, "-")).join("\n");
}
if (tag === "ol") {
return Array.from(el.children).filter((child) => child instanceof HTMLElement && child.tagName.toLowerCase() === "li").map((li, index) => {
const value = li.value || index + 1;
return renderListItem(li, depth, `${value}.`);
}).join("\n");
}
if (tag === "li") {
return renderListItem(el, depth, "-");
}
const childBlocks = Array.from(el.childNodes).map((child) => renderNodeText(child, depth)).filter(Boolean);
if (childBlocks.length > 0) {
return childBlocks.join("\n");
}
return normalizeText(el.innerText || el.textContent || "");
}
function renderListItem(li, depth, marker) {
const indent = " ".repeat(depth);
const childIndent = " ".repeat(depth + 1);
const inlineParts = [];
const nestedParts = [];
for (const child of Array.from(li.childNodes)) {
if (child.nodeType === Node.TEXT_NODE) {
const text2 = normalizeText(child.textContent || "");
if (text2) inlineParts.push(text2);
continue;
}
if (child.nodeType !== Node.ELEMENT_NODE) continue;
const el = child;
const tag = el.tagName.toLowerCase();
if (tag === "ul" || tag === "ol") {
const nested = renderNodeText(el, depth + 1);
if (nested) nestedParts.push(nested);
continue;
}
if (tag === "pre") {
const code = normalizePreserveLines(el.innerText || el.textContent || "");
if (code) nestedParts.push(code.split("\n").map((line) => `${childIndent}${line}`).join("\n"));
continue;
}
const text = tag === "p" ? normalizeInlineText(el) : normalizeText(el.innerText || el.textContent || "");
if (text) inlineParts.push(text);
}
const firstLine = `${indent}${marker} ${inlineParts.join(" ").trim()}`.trimEnd();
return [firstLine, ...nestedParts].filter(Boolean).join("\n");
}
function normalizeInlineText(el) {
const parts = [];
for (const node of Array.from(el.childNodes)) {
if (node.nodeType === Node.TEXT_NODE) {
const text2 = normalizeText(node.textContent || "");
if (text2) parts.push(text2);
continue;
}
if (node.nodeType !== Node.ELEMENT_NODE) continue;
const child = node;
const tag = child.tagName.toLowerCase();
if (tag === "br") {
parts.push("\n");
continue;
}
const text = normalizeText(child.innerText || child.textContent || "");
if (text) parts.push(text);
}
return parts.join(" ").replace(/\s*\n\s*/g, "\n").replace(/[ \t]{2,}/g, " ").trim();
}
function normalizePreserveLines(value) {
return value.replace(/\r\n/g, "\n").split("\n").map((line) => line.replace(/\s+$/g, "")).join("\n").trim();
}
function serializeForWord(blocks) {
const html = blocks.map(renderBlockHtml).join("");
const text = blocks.map(renderBlockText).join("\n\n").trim();
return { html: wrapHtml(html), text };
}
function wrapHtml(body) {
return `${body}`;
}
function renderBlockHtml(block) {
switch (block.type) {
case "heading":
return `${renderInlineHtml(block.children)}`;
case "paragraph":
return `${renderInlineHtml(block.children)}
`;
case "blockquote":
return `${block.children.map(renderBlockHtml).join("")}
`;
case "list": {
const tag = block.ordered ? "ol" : "ul";
return `<${tag}>${block.items.map((item) => `${renderListItemHtml(item)}`).join("")}${tag}>`;
}
case "code_block":
return `${escapeHtml(block.text)}
`;
case "table":
return renderTableHtml(block.rows);
case "html_block":
return block.html;
}
}
function renderInlineHtml(nodes) {
return nodes.map((node) => {
switch (node.type) {
case "text":
return escapeHtml(node.text);
case "strong":
return `${renderInlineHtml(node.children)}`;
case "em":
return `${renderInlineHtml(node.children)}`;
case "del":
return `${renderInlineHtml(node.children)}`;
case "code":
return `${escapeHtml(node.text)}`;
case "link":
return `${renderInlineHtml(node.children)}`;
case "br":
return "
";
}
}).join("");
}
function renderListItemHtml(blocks) {
if (blocks.length === 0) {
return "";
}
if (blocks.length === 1 && blocks[0].type === "paragraph") {
return renderInlineHtml(blocks[0].children);
}
return blocks.map(renderBlockHtml).join("") || "";
}
function renderBlockText(block) {
switch (block.type) {
case "heading":
case "paragraph":
return normalizeText(renderInlineText(block.children));
case "blockquote":
return block.children.map(renderBlockText).filter(Boolean).map((line) => `> ${line}`).join("\n");
case "list":
return block.items.map((item, index) => `${block.ordered ? `${index + 1}.` : "-"} ${item.map(renderBlockText).join(" ").trim()}`).join("\n");
case "code_block":
return block.text;
case "table":
return block.rows.map((row) => row.cells.map((cell) => cell.text).join(" ")).join("\n");
case "html_block":
return block.text;
}
}
function renderInlineText(nodes) {
return nodes.map((node) => {
switch (node.type) {
case "text":
return node.text;
case "strong":
case "em":
case "del":
case "link":
return renderInlineText(node.children);
case "code":
return node.text;
case "br":
return "\n";
}
}).join("");
}
function renderTableHtml(rows) {
const body = rows.map((row) => `${row.cells.map((cell) => {
const tag = cell.header ? "th" : "td";
return `<${tag}>${cell.html || escapeHtml(cell.text)}${tag}>`;
}).join("")}
`).join("");
return ``;
}
function createActionButton(label, onClick) {
const button = document.createElement("button");
button.type = "button";
button.textContent = label;
button.dataset.aiOfficeCopyUi = "1";
button.style.cssText = [
"border:1px solid rgba(0,0,0,.12)",
"border-radius:8px",
"padding:4px 10px",
"font-size:12px",
"line-height:1.4",
"cursor:pointer",
"background:#fff",
"color:#111",
"box-shadow:0 1px 2px rgba(0,0,0,.06)",
"transition:opacity .15s ease, transform .15s ease, background-color .15s ease, color .15s ease, border-color .15s ease"
].join(";");
button.addEventListener("click", async (event) => {
event.preventDefault();
event.stopPropagation();
if (button.disabled) return;
const originalText = label;
const originalBackground = button.style.background;
const originalColor = button.style.color;
const originalBorderColor = button.style.borderColor;
const setState = (text, styles) => {
button.textContent = text;
if (styles == null ? void 0 : styles.background) button.style.background = styles.background;
if (styles == null ? void 0 : styles.color) button.style.color = styles.color;
if (styles == null ? void 0 : styles.borderColor) button.style.borderColor = styles.borderColor;
if (styles == null ? void 0 : styles.transform) button.style.transform = styles.transform;
};
try {
button.disabled = true;
button.style.opacity = "0.78";
button.style.cursor = "progress";
button.style.transform = "scale(0.98)";
setState("复制中...");
await onClick();
button.style.opacity = "1";
button.style.cursor = "default";
setState("已复制", {
background: "#ecfdf3",
color: "#027a48",
borderColor: "#12b76a",
transform: "scale(1)"
});
await delay(900);
} catch (error) {
button.style.opacity = "1";
button.style.cursor = "default";
setState("复制失败", {
background: "#fef3f2",
color: "#b42318",
borderColor: "#f04438",
transform: "scale(1)"
});
await delay(1200);
throw error;
} finally {
button.disabled = false;
button.style.opacity = "1";
button.style.cursor = "pointer";
button.style.transform = "scale(1)";
button.textContent = originalText;
button.style.background = originalBackground;
button.style.color = originalColor;
button.style.borderColor = originalBorderColor;
}
});
return button;
}
function createActionBar() {
const wrapper = document.createElement("div");
wrapper.dataset.aiOfficeCopyUi = "1";
wrapper.style.cssText = [
"display:flex",
"gap:8px",
"margin-top:10px",
"flex-wrap:wrap",
"align-items:center"
].join(";");
return wrapper;
}
function delay(ms) {
return new Promise((resolve) => window.setTimeout(resolve, ms));
}
function enhanceResponse(target, contentRoot, options) {
if (target.dataset.aiOfficeCopyEnhanced === "1") return;
const source = contentRoot ?? target;
target.dataset.aiOfficeCopyEnhanced = "1";
const actions = createActionBar();
const wordButton = createActionButton("复制为 Word", async () => {
const payload = (options == null ? void 0 : options.preferRawHtml) ? serializeDomForWord(source) : serializeForWord(extractBlocks(source));
if (!payload.text) {
throw new Error("未识别到可复制内容");
}
await writeClipboard(payload.html, payload.text);
});
actions.appendChild(wordButton);
const blocks = extractBlocks(source);
if (hasTable(blocks)) {
const excelButton = createActionButton("复制表格为 Excel", async () => {
const payload = serializeFirstTableForExcel(extractBlocks(source));
if (!payload) {
throw new Error("当前回复未检测到表格");
}
await writeClipboard(payload.html, payload.text);
});
actions.appendChild(excelButton);
}
target.appendChild(actions);
}
function watchWithObserver(run) {
run();
const observer = new MutationObserver(() => run());
observer.observe(document.body, { childList: true, subtree: true });
}
function collectRoots(root, roots = []) {
roots.push(root);
const elements = root instanceof Document || root instanceof ShadowRoot || root instanceof Element ? Array.from(root.querySelectorAll("*")) : [];
for (const element of elements) {
const shadowRoot = element.shadowRoot;
if (shadowRoot) {
collectRoots(shadowRoot, roots);
}
}
return roots;
}
function queryAllDeep(selector) {
const results = [];
const seen = /* @__PURE__ */ new Set();
for (const root of collectRoots(document)) {
root.querySelectorAll(selector).forEach((node) => {
const element = node;
if (seen.has(element)) return;
seen.add(element);
results.push(element);
});
}
return results;
}
function findContentRoot(response, selectors) {
for (const selector of selectors) {
const found = response.matches(selector) ? response : response.querySelector(selector);
if (found) return found;
for (const root of collectRoots(response)) {
const nested = root.querySelector(selector);
if (nested) return nested;
}
}
return response;
}
function enhanceMatches(responseSelectors, contentSelectors, enhance) {
const candidates = [];
const seen = /* @__PURE__ */ new Set();
for (const selector of responseSelectors) {
queryAllDeep(selector).forEach((response) => {
if (seen.has(response)) return;
seen.add(response);
candidates.push(response);
});
}
const topLevelCandidates = candidates.filter((candidate) => {
return !candidates.some((other) => other !== candidate && other.contains(candidate));
});
for (const response of topLevelCandidates) {
if (response.dataset.aiOfficeCopyEnhanced === "1") continue;
if (response.closest('[data-ai-office-copy-enhanced="1"]')) continue;
const content = findContentRoot(response, contentSelectors);
if (content) {
enhance(response, content);
}
}
}
const RESPONSE_SELECTORS$3 = [
'article [data-message-author-role="assistant"]',
"article",
'[data-message-author-role="assistant"]'
];
const CONTENT_SELECTORS$4 = [
'[data-message-author-role="assistant"] .markdown',
'[data-message-author-role="assistant"] .prose',
".markdown",
".prose"
];
const chatgptAdapter = {
name: "chatgpt",
match(url) {
return url.hostname === "chatgpt.com" || url.hostname === "chat.openai.com";
},
start() {
watchWithObserver(() => {
enhanceMatches(RESPONSE_SELECTORS$3, CONTENT_SELECTORS$4, enhanceResponse);
});
}
};
const RESPONSE_SELECTORS$2 = [
".font-claude-response",
'[data-testid="conversation-turn-assistant"]',
"[data-is-streaming] .font-claude-message",
".font-claude-message"
];
const CONTENT_SELECTORS$3 = [
".standard-markdown",
".font-claude-response",
".font-claude-message",
'[data-testid="artifact-panel"] .font-claude-message',
".prose",
".markdown"
];
const claudeAdapter = {
name: "claude",
match(url) {
return url.hostname === "claude.ai" || url.hostname.endsWith(".claude.ai");
},
start() {
watchWithObserver(() => {
enhanceMatches(RESPONSE_SELECTORS$2, CONTENT_SELECTORS$3, enhanceResponse);
});
}
};
const RESPONSE_SELECTORS$1 = [
'[data-role="assistant"]',
".ds-markdown",
".markdown-body"
];
const CONTENT_SELECTORS$2 = [
".ds-markdown",
".markdown-body",
".markdown"
];
const deepseekAdapter = {
name: "deepseek",
match(url) {
return url.hostname === "chat.deepseek.com";
},
start() {
watchWithObserver(() => {
enhanceMatches(RESPONSE_SELECTORS$1, CONTENT_SELECTORS$2, enhanceResponse);
});
}
};
const CONTENT_SELECTORS$1 = [
".container-P2rR72.flow-markdown-body",
".flow-markdown-body",
".content-y8qlFa.code-content",
".code-area-yxsM36",
".code-block-element-R6c8c0"
];
function enhanceDoubaoContent() {
const seen = /* @__PURE__ */ new Set();
for (const selector of CONTENT_SELECTORS$1) {
for (const element of queryAllDeep(selector)) {
if (seen.has(element)) continue;
seen.add(element);
if (element.dataset.aiOfficeCopyEnhanced === "1") continue;
if (element.closest('[data-ai-office-copy-enhanced="1"]')) continue;
enhanceResponse(element, element, { preferRawHtml: true });
}
}
}
const doubaoAdapter = {
name: "doubao",
match(url) {
return url.hostname === "www.doubao.com" || url.hostname === "doubao.com";
},
start() {
watchWithObserver(() => {
enhanceDoubaoContent();
});
}
};
const RESPONSE_SELECTORS = [
"message-content[model-response] .model-response-text",
".model-response-text",
".response-content"
];
const CONTENT_SELECTORS = [
".model-response-text",
".markdown",
".response-content",
".prose"
];
const geminiAdapter = {
name: "gemini",
match(url) {
return url.hostname === "gemini.google.com";
},
start() {
watchWithObserver(() => {
enhanceMatches(RESPONSE_SELECTORS, CONTENT_SELECTORS, enhanceResponse);
});
}
};
const FINAL_MARKDOWN_SELECTORS = [
".chat-content-item.chat-content-item-assistant .markdown-container:not(.toolcall-content-text) > .markdown",
".segment.segment-assistant .markdown-container:not(.toolcall-content-text) > .markdown"
];
function getMarkdownTarget(source) {
return source.closest(".markdown-container:not(.toolcall-content-text)") ?? source;
}
function enhanceKimiContent() {
const seenSources = /* @__PURE__ */ new Set();
const seenTargets = /* @__PURE__ */ new Set();
for (const selector of FINAL_MARKDOWN_SELECTORS) {
for (const source of queryAllDeep(selector)) {
if (seenSources.has(source)) continue;
seenSources.add(source);
const target = getMarkdownTarget(source);
if (seenTargets.has(target)) continue;
seenTargets.add(target);
if (target.dataset.aiOfficeCopyEnhanced === "1") continue;
enhanceResponse(target, source, { preferRawHtml: true });
}
}
}
const kimiAdapter = {
name: "kimi",
match(url) {
return url.hostname === "kimi.moonshot.cn" || url.hostname === "kimi.com" || url.hostname === "www.kimi.com";
},
start() {
watchWithObserver(() => {
enhanceKimiContent();
});
}
};
const adapters = [
chatgptAdapter,
deepseekAdapter,
claudeAdapter,
geminiAdapter,
kimiAdapter,
doubaoAdapter
];
const current = adapters.find((adapter) => adapter.match(new URL(location.href)));
if (current) {
current.start();
console.log(`[ai-office-copy] started: ${current.name}`);
} else {
console.log("[ai-office-copy] no adapter matched");
}
})();