Files
skill-lib/skills/zhihu-write/scripts/prepare_article_editor.js
木炎 51913555ad feat: add initial skill authoring workspace
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 18:34:56 +08:00

107 lines
2.7 KiB
JavaScript

function cleanText(value) {
return String(value || '')
.replace(/\s+/g, ' ')
.replace(/\u200b/g, '')
.trim();
}
function pageText() {
const body = document.body;
return cleanText(body && (body.innerText || body.textContent || ''));
}
function isLoginBlocked(url, text) {
return /\/signin\b|\/signup\b/.test(url) ||
/登录|注册|验证码|安全验证|验证后继续|请先登录/.test(text);
}
function isVisible(node) {
if (!node) {
return false;
}
const rect = typeof node.getBoundingClientRect === 'function' ? node.getBoundingClientRect() : null;
return !rect || rect.width > 0 || rect.height > 0;
}
function attrText(node, name) {
if (!node || typeof node.getAttribute !== 'function') {
return '';
}
return cleanText(node.getAttribute(name) || '');
}
function looksLikeTitleInput(node) {
const signals = [
attrText(node, 'placeholder'),
attrText(node, 'data-placeholder'),
attrText(node, 'aria-label'),
].filter(Boolean);
return signals.some((value) => value.includes('标题'));
}
function findVisibleTitleInput() {
const selectors = [
"textarea[placeholder*='标题']",
"input[placeholder*='标题']",
"textarea[data-placeholder*='标题']",
"input[data-placeholder*='标题']",
"[role='textbox'][aria-label*='标题']",
"[contenteditable='true'][aria-label*='标题']",
"[contenteditable='true'][data-placeholder*='标题']",
];
for (const selector of selectors) {
const node = document.querySelector(selector);
if (isVisible(node)) {
return node;
}
}
return null;
}
function findBodyEditor(titleInput) {
const selectors = [
"div[contenteditable='true'][role='textbox']",
"div.public-DraftEditor-content[contenteditable='true']",
"[role='textbox'][contenteditable='true']",
"[contenteditable='true'][data-placeholder]",
"div[contenteditable='true']",
];
for (const selector of selectors) {
const nodes = Array.from(document.querySelectorAll(selector));
const visible = nodes.find((node) => {
return isVisible(node) && node !== titleInput && !looksLikeTitleInput(node);
});
if (visible) {
return visible;
}
}
return null;
}
const currentUrl = location.href;
const text = pageText();
if (isLoginBlocked(currentUrl, text)) {
return {
status: 'login_required',
current_url: currentUrl,
};
}
const titleInput = findVisibleTitleInput();
const bodyEditor = findBodyEditor(titleInput);
if (titleInput && bodyEditor) {
return {
status: 'editor_ready',
current_url: currentUrl,
title_placeholder: titleInput.getAttribute('placeholder') || '',
};
}
return {
status: 'editor_unavailable',
current_url: currentUrl,
desired_mode: String(args.desired_mode || 'draft'),
};