← Files 大米ARCHIVED FILE
assets/workbench.html
78.9 KB · Nov 6, 2026 · 07:24 UTC
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>大米工作台</title>
<style>/* 总后台同款:Ant Design 默认字体、14px 正文、32px 控件、6px 圆角与品牌蓝。
页面背景沿用 src/global.css 的 slate-100;不由宿主字体或色值覆盖后台主题。 */
:root {
color-scheme: light;
--canvas: #f1f5f9;
--surface: #fff;
--text: rgba(0, 0, 0, .88);
--muted: rgba(0, 0, 0, .65);
--subtle: rgba(0, 0, 0, .45);
--line: #e5e7eb;
--control-line: #d9d9d9;
--soft: #fafafa;
--accent: #1677ff;
--accent-hover: #40a9ff;
--accent-soft: #e6f4ff;
--success: #52c41a;
--danger: #ff4d4f;
font: 14px/1.5715 -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
}
:root[data-theme="dark"] {
color-scheme: dark;
--canvas: #141414;
--surface: #1f1f1f;
--text: rgba(255, 255, 255, .85);
--muted: rgba(255, 255, 255, .65);
--subtle: rgba(255, 255, 255, .45);
--line: #303030;
--control-line: #424242;
--soft: #262626;
--accent: #3c89e8;
--accent-hover: #69b1ff;
--accent-soft: #111d2c;
--success: #49aa19;
--danger: #ff7875;
}
* { box-sizing: border-box; }
[hidden] { display: none !important; }
body { margin: 0; background: var(--canvas); color: var(--text); }
button, input, select, textarea { font: inherit; }
button, .button {
display: inline-flex; align-items: center; justify-content: center; gap: 6px;
min-height: 32px; padding: 4px 12px; color: inherit; background: var(--surface);
border: 1px solid var(--control-line); border-radius: 6px; font-weight: 400;
text-decoration: none; cursor: pointer; transition: border-color .15s, color .15s, background-color .15s;
}
button:hover:not(:disabled), .button:hover { color: var(--accent); border-color: var(--accent); }
button:disabled { cursor: not-allowed; color: var(--subtle); }
button:focus-visible, a:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
input, select, textarea {
min-width: 0; min-height: 32px; padding: 4px 11px; border: 1px solid var(--control-line);
border-radius: 6px; background: var(--surface); color: var(--text);
}
input:hover, select:hover, textarea:hover { border-color: var(--accent); }
input:focus, select:focus, textarea:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 2px var(--accent-soft); }
input::placeholder, textarea::placeholder { color: var(--subtle); opacity: 1; }
select { cursor: pointer; }
h1, h2, h3, p { margin: 0; }
h1 { font-size: 20px; line-height: 28px; font-weight: 600; }
h2, h3 { font-size: 14px; font-weight: 600; }
.icon { width: 16px; height: 16px; flex: 0 0 16px; fill: none; stroke: currentColor; stroke-width: 1.4; stroke-linecap: round; stroke-linejoin: round; }
.icon-sprite { position: absolute; width: 0; height: 0; overflow: hidden; }
.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; }
.meta, .eyebrow { color: var(--muted); font-size: 12px; font-weight: 400; }
.eyebrow { display: block; margin-bottom: 8px; }
.text-button { min-height: 24px; padding: 0; border: 0; color: var(--accent); background: transparent; font-size: 14px; white-space: nowrap; }
.text-button:hover:not(:disabled) { color: var(--accent-hover); background: transparent; }
.primary { color: #fff; background: var(--accent); border-color: var(--accent); box-shadow: 0 2px 0 rgba(5, 145, 255, .1); }
.primary:hover:not(:disabled) { color: #fff; background: var(--accent-hover); border-color: var(--accent-hover); }
.primary:disabled { background: var(--soft); border-color: var(--line); box-shadow: none; }
.empty { padding: 32px 16px; text-align: center; color: var(--subtle); font-size: 14px; overflow-wrap: anywhere; }
.error { color: var(--danger); }
.actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 16px; }
/* 统一工具栏;站点范围在所有视图保留。 */
#app { max-width: 1280px; margin: 0 auto; padding: 0 24px 32px; }
.topbar { display: flex; align-items: center; justify-content: space-between; gap: 16px; min-height: 64px; }
.brand, .topbar-actions { display: flex; align-items: center; gap: 10px; }
.brand { min-width: 0; }
.brand img { flex: 0 0 24px; }
.brand strong { font-size: 16px; font-weight: 600; white-space: nowrap; }
.version { color: var(--subtle); font-size: 12px; white-space: nowrap; }
.connection { display: inline-flex; align-items: center; gap: 6px; color: var(--muted); font-size: 12px; white-space: nowrap; }
.connection::before { content: ''; width: 6px; height: 6px; border-radius: 50%; background: var(--control-line); }
.connection.ready::before { background: var(--success); }
.tabs { display: flex; gap: 28px; border-bottom: 1px solid var(--line); }
.tabs button { min-height: 42px; padding: 10px 0; border: 0; border-bottom: 2px solid transparent; border-radius: 0; background: transparent; color: var(--muted); }
.tabs button:hover:not(:disabled) { color: var(--accent); }
.tabs button[aria-current="page"] { color: var(--accent); border-bottom-color: var(--accent); font-weight: 500; }
.workspace { padding-top: 20px; }
.workspace-heading { display: flex; justify-content: space-between; align-items: center; gap: 16px; margin-bottom: 16px; }
.project-field { width: 220px; flex-shrink: 0; }
.project-field select { width: 100%; text-overflow: ellipsis; }
#notice { padding: 8px 12px; margin-bottom: 16px; border: 1px solid var(--danger); border-radius: 6px; color: var(--danger); background: var(--surface); overflow-wrap: anywhere; }
.view.loading { cursor: progress; }
/* 与后台相同的白色面板、细边框和紧凑行高。 */
.metrics { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 16px; margin-bottom: 16px; }
.metric { display: flex; align-items: center; justify-content: flex-start; gap: 12px; min-width: 0; min-height: 88px; padding: 16px; border: 1px solid var(--line); border-radius: 8px; background: var(--surface); text-align: left; }
.metric-icon { display: inline-flex; align-items: center; justify-content: center; width: 32px; height: 32px; flex-shrink: 0; border-radius: 6px; background: var(--accent-soft); color: var(--accent); }
.metric strong { display: block; margin-top: 2px; font-size: 24px; line-height: 30px; font-weight: 500; font-variant-numeric: tabular-nums; color: var(--text); }
.metric:disabled { color: var(--text); }
.metric-arrow { margin-left: auto; color: var(--subtle); }
.overview-status { margin: -4px 0 12px; }
.dashboard { display: grid; grid-template-columns: minmax(0, 1.35fr) minmax(280px, 1fr); gap: 16px; align-items: start; }
.dashboard-main, .dashboard-side { min-width: 0; }
.dashboard-side { display: grid; gap: 16px; }
.panel { min-width: 0; overflow: hidden; padding: 0 16px; background: var(--surface); border: 1px solid var(--line); border-radius: 8px; }
.panel-heading { display: flex; align-items: center; justify-content: space-between; gap: 12px; min-height: 56px; padding: 12px 0; border-bottom: 1px solid var(--line); }
.panel-heading h2 { flex-shrink: 0; }
.panel-footer { display: flex; align-items: center; justify-content: space-between; gap: 12px; min-height: 44px; padding: 8px 0; }
.panel-footer .text-button { font-size: 12px; }
.site-search { display: flex; align-items: center; gap: 6px; width: 200px; min-width: 0; padding: 0 10px; color: var(--subtle); border: 1px solid var(--control-line); border-radius: 6px; }
.site-search input { width: 100%; padding: 4px 0; border: 0; background: transparent; }
.site-search:focus-within { border-color: var(--accent); box-shadow: 0 0 0 2px var(--accent-soft); }
.site-search input:focus { box-shadow: none; }
.site-row { display: flex; align-items: center; gap: 12px; width: 100%; padding: 12px 0; border: 0; border-bottom: 1px solid var(--line); border-radius: 0; background: transparent; text-align: left; }
.site-row:hover:not(:disabled) { color: var(--text); background: var(--soft); }
.site-avatar { display: flex; align-items: center; justify-content: center; width: 32px; height: 32px; flex-shrink: 0; border-radius: 6px; color: var(--accent); background: var(--accent-soft); font-size: 14px; font-weight: 500; }
.site-copy { display: block; min-width: 0; flex: 1; }
.site-copy strong { display: block; overflow: hidden; font-size: 14px; font-weight: 500; text-overflow: ellipsis; white-space: nowrap; }
.site-copy .meta { display: block; margin-top: 2px; }
.row-action { display: inline-flex; color: var(--subtle); }
.preview-row { display: flex; align-items: flex-start; gap: 8px; width: 100%; padding: 12px 0; border: 0; border-bottom: 1px solid var(--line); border-radius: 0; background: transparent; text-align: left; }
.preview-row:last-child { border-bottom: 0; }
.preview-row:hover:not(:disabled) { color: var(--text); background: var(--soft); }
.unread-dot { display: inline-block; flex: 0 0 6px; width: 6px; height: 6px; margin-top: 8px; border-radius: 50%; background: var(--accent); }
.preview-copy { min-width: 0; flex: 1; }
.preview-copy strong { display: block; overflow: hidden; font-size: 14px; font-weight: 500; text-overflow: ellipsis; white-space: nowrap; }
.preview-copy .meta { display: block; margin-top: 2px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.preview-date { color: var(--subtle); font-size: 12px; white-space: nowrap; }
.ai-panel { padding-bottom: 16px; }
.ai-panel .panel-heading { border: 0; }
.ai-actions { display: grid; gap: 8px; }
.ai-actions button { justify-content: flex-start; width: 100%; min-height: 36px; }
.ai-actions .arrow { margin-left: auto; }
.ai-panel p { margin-top: 10px; }
.site-intro { padding: 14px 0; border-bottom: 1px solid var(--line); }
.health-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px; padding: 16px 0; }
.health-item { min-width: 0; }
.health-item strong { display: block; margin: 4px 0; overflow-wrap: anywhere; font-size: 14px; font-weight: 500; }
.health-item p { overflow-wrap: anywhere; }
#site-health > .actions { padding: 12px 0; margin: 0; border-top: 1px solid var(--line); }
/* 阅读、草稿与内容保持原有权限和工具边界。 */
.filters { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; padding: 12px; background: var(--surface); border: 1px solid var(--line); border-radius: 8px; }
.filters label { min-width: 0; }
.filters label.search { flex: 1; }
.filters label.search input { width: 100%; }
#feedback-count { margin: 12px 0; }
.inbox { display: grid; grid-template-columns: minmax(240px, .8fr) minmax(0, 1.4fr); gap: 16px; align-items: start; }
.inbox-list { padding: 0; }
.feedback-item { display: block; width: 100%; padding: 12px 16px; border: 0; border-bottom: 1px solid var(--line); border-radius: 0; background: transparent; text-align: left; }
.feedback-item:hover:not(:disabled) { color: var(--text); background: var(--soft); }
.feedback-item:last-child { border-bottom: 0; }
.feedback-item.selected { background: var(--accent-soft); box-shadow: inset 2px 0 var(--accent); }
.feedback-item strong { display: block; margin: 6px 0; overflow-wrap: anywhere; font-size: 14px; font-weight: 500; }
.feedback-item .meta { overflow-wrap: anywhere; }
.badge { display: inline-flex; margin-right: 6px; padding: 0 6px; border: 1px solid var(--line); border-radius: 4px; background: var(--soft); color: var(--muted); font-size: 12px; line-height: 20px; }
.badge.unread { border-color: var(--accent-soft); color: var(--accent); background: var(--accent-soft); }
#load-more { width: calc(100% - 32px); margin: 12px 16px; }
.detail { min-height: 280px; padding: 16px; }
.detail h2 { margin-bottom: 8px; overflow-wrap: anywhere; font-size: 16px; }
.body-text { margin-top: 16px; white-space: pre-wrap; overflow-wrap: anywhere; }
.draft { margin-top: 16px; padding: 12px; border: 1px solid var(--line); border-radius: 6px; background: var(--soft); overflow-wrap: anywhere; }
.draft p { margin-top: 8px; white-space: pre-wrap; }
.draft h3:not(:first-child) { margin-top: 12px; }
.draft .meta { display: block; margin-top: 8px; }
.content-section { margin-bottom: 16px; }
.section-heading { display: flex; align-items: center; justify-content: space-between; min-height: 48px; }
.table-wrap { overflow-x: auto; }
table { width: 100%; border-collapse: collapse; font-size: 14px; }
th, td { padding: 12px; border-bottom: 1px solid var(--line); text-align: left; vertical-align: middle; }
th { background: var(--soft); font-weight: 600; }
td:first-child { overflow-wrap: anywhere; }
td:last-child { width: 88px; }
tbody tr:last-child td { border-bottom: 0; }
td button { white-space: nowrap; }
.task-panel { margin-top: 16px; padding: 12px; border: 1px solid var(--line); border-radius: 6px; background: var(--accent-soft); }
.task-panel label { display: block; margin: 10px 0 6px; font-size: 12px; }
.task-panel textarea { width: 100%; resize: vertical; }
@media (max-width: 860px) {
#app { padding-inline: 16px; }
.dashboard, .inbox { grid-template-columns: minmax(0, 1fr); }
.dashboard-side { grid-template-columns: repeat(2, minmax(0, 1fr)); align-items: start; }
.metric-arrow { display: none; }
.feedback-list { max-height: 300px; overflow-y: auto; }
}
@media (max-width: 560px) {
#app { padding-inline: 12px; }
.topbar { flex-wrap: wrap; gap: 10px; padding: 14px 0 10px; }
.topbar-actions { width: 100%; justify-content: flex-end; gap: 8px; }
.connection { margin-right: auto; }
.workspace { padding-top: 16px; }
.workspace-heading { gap: 12px; }
.project-field { width: min(62%, 220px); }
.metrics { gap: 8px; }
.metric { min-height: 76px; padding: 12px; }
.metric-icon { display: none; }
.metric strong { font-size: 22px; line-height: 28px; }
.dashboard-side { grid-template-columns: minmax(0, 1fr); }
.panel-heading { min-height: 52px; }
.site-search { width: min(65%, 210px); }
.filters label:not(.search) { flex: 1 1 calc(50% - 4px); }
.filters select { width: 100%; }
.filters label.search { flex-basis: calc(100% - 70px); }
.preview-date { display: none; }
.health-grid { gap: 12px; }
th, td { padding-inline: 8px; }
}
@media (prefers-reduced-motion: reduce) { * { transition: none !important; } }
</style>
</head>
<body>
<svg class="icon-sprite" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
<symbol id="icon-sites" viewBox="0 0 20 20"><rect x="3" y="3" width="14" height="14" rx="2"/><path d="M3 7h14M6 5h.1M9 5h.1"/></symbol>
<symbol id="icon-feedback" viewBox="0 0 20 20"><path d="M4 3h12a1 1 0 0 1 1 1v10a1 1 0 0 1-1 1H7l-4 3V4a1 1 0 0 1 1-1Z M6 7h8M6 10h4"/></symbol>
<symbol id="icon-draft" viewBox="0 0 20 20"><path d="M11 2H5a1 1 0 0 0-1 1v14a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V7L11 2Z M11 2v5h5M7 10h6M7 13h6"/></symbol>
<symbol id="icon-spark" viewBox="0 0 20 20"><path d="m8 3 2 5 5 2-5 2-2 5-2-5-5-2 5-2 2-5ZM16 1v5m-2.5-2.5h5"/></symbol>
<symbol id="icon-arrow" viewBox="0 0 20 20"><path d="M4 10h12m-5-5 5 5-5 5"/></symbol>
<symbol id="icon-search" viewBox="0 0 20 20"><circle cx="8.5" cy="8.5" r="5.5"/><path d="m13 13 4 4"/></symbol>
<symbol id="icon-refresh" viewBox="0 0 20 20"><path d="M16.5 7A7 7 0 1 0 17 12M17 3v5h-5"/></symbol>
<symbol id="icon-external" viewBox="0 0 20 20"><path d="M11 3h6v6m0-6-9 9M8 4H4v12h12v-4"/></symbol>
</svg>
<main id="app" aria-busy="true">
<header class="topbar">
<div class="brand">
<img src="data:image/png;base64,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" width="24" height="24" alt="">
<strong>大米工作台</strong><span class="version">v2.4.1</span>
</div>
<div class="topbar-actions">
<span id="connection" class="connection" role="status">正在连接</span>
<button id="refresh" type="button" disabled><svg class="icon" aria-hidden="true"><use href="#icon-refresh"/></svg>刷新</button>
<a id="open-backend" class="button backend-link" href="https://quick-deploy.com/" target="_blank" rel="noopener noreferrer">总后台<svg class="icon" aria-hidden="true"><use href="#icon-external"/></svg></a>
</div>
</header>
<nav aria-label="工作台视图" class="tabs">
<button type="button" data-view="overview" aria-current="page">概览</button>
<button type="button" data-view="feedback">反馈</button>
<button type="button" data-view="content">内容</button>
</nav>
<div class="workspace">
<section class="workspace-heading">
<h1 id="workspace-title">概览</h1>
<label class="project-field"><span class="sr-only">当前站点范围</span><select id="project" disabled><option value="">全部站点</option></select></label>
</section>
<div id="notice" role="alert" hidden></div>
<section id="overview" class="view">
<div class="metrics">
<article class="metric"><span class="metric-icon"><svg class="icon" aria-hidden="true"><use href="#icon-sites"/></svg></span><div><span id="site-count-label" class="meta">站点</span><strong id="site-count">—</strong></div></article>
<button id="unread-metric" class="metric" type="button" disabled><span class="metric-icon"><svg class="icon" aria-hidden="true"><use href="#icon-feedback"/></svg></span><span><span class="meta">未读反馈</span><strong id="unread-count">—</strong></span><svg class="icon metric-arrow" aria-hidden="true"><use href="#icon-arrow"/></svg></button>
<button id="draft-metric" class="metric" type="button" disabled><span class="metric-icon"><svg class="icon" aria-hidden="true"><use href="#icon-draft"/></svg></span><span><span class="meta">回复草稿</span><strong id="draft-count">—</strong></span><svg class="icon metric-arrow" aria-hidden="true"><use href="#icon-arrow"/></svg></button>
</div>
<p id="overview-status" class="meta overview-status" role="status" hidden></p>
<div class="dashboard">
<div class="dashboard-main">
<section id="sites-panel" class="panel">
<header class="panel-heading"><h2>站点</h2><label class="site-search"><svg class="icon" aria-hidden="true"><use href="#icon-search"/></svg><span class="sr-only">搜索站点</span><input id="site-query" type="search" placeholder="搜索站点" maxlength="255"></label></header>
<div id="site-list"><div class="empty">加载中…</div></div>
<div class="panel-footer"><span id="site-list-count" class="meta"></span><button id="more-sites" type="button" class="text-button" hidden>查看更多<svg class="icon" aria-hidden="true"><use href="#icon-arrow"/></svg></button></div>
</section>
<section id="site-summary" class="panel" hidden>
<header class="panel-heading"><h2>站点状态</h2><button id="all-sites" type="button" class="text-button">全部站点<svg class="icon" aria-hidden="true"><use href="#icon-arrow"/></svg></button></header>
<div id="site-health"></div>
</section>
</div>
<aside class="dashboard-side">
<section class="panel">
<header class="panel-heading"><h2>未读反馈</h2><button id="all-feedback" type="button" class="text-button" disabled>全部<svg class="icon" aria-hidden="true"><use href="#icon-arrow"/></svg></button></header>
<div id="unread-list"><div class="empty">加载中…</div></div>
</section>
<section class="panel ai-panel">
<header class="panel-heading"><h2>AI 助手</h2></header>
<div class="ai-actions">
<button id="ai-feedback" type="button" disabled><svg class="icon" aria-hidden="true"><use href="#icon-feedback"/></svg><span>整理反馈</span><svg class="icon arrow" aria-hidden="true"><use href="#icon-arrow"/></svg></button>
<button id="ai-publish" type="button" disabled><svg class="icon" aria-hidden="true"><use href="#icon-draft"/></svg><span>检查发布</span><svg class="icon arrow" aria-hidden="true"><use href="#icon-arrow"/></svg></button>
<button id="ai-content" type="button" disabled><svg class="icon" aria-hidden="true"><use href="#icon-spark"/></svg><span>规划内容</span><svg class="icon arrow" aria-hidden="true"><use href="#icon-arrow"/></svg></button>
</div>
<p id="ai-hint" class="meta">先选择站点</p>
</section>
</aside>
</div>
</section>
<section id="feedback" class="view" hidden>
<form id="feedback-filters" class="filters">
<label><span class="sr-only">阅读状态</span><select id="read-filter"><option value="unread">未读反馈</option><option value="">全部反馈</option><option value="read">已读反馈</option></select></label>
<label><span class="sr-only">回复草稿</span><select id="draft-filter"><option value="">草稿不限</option><option value="1">有草稿</option><option value="0">无草稿</option></select></label>
<label class="search"><span class="sr-only">关键词</span><input id="feedback-query" type="search" placeholder="搜索反馈" maxlength="255"></label>
<button class="primary" type="submit">查询</button>
</form>
<p id="feedback-count" class="meta" role="status"></p>
<div class="inbox">
<div class="panel inbox-list"><div id="feedback-list" class="feedback-list"></div><button type="button" id="load-more" hidden>加载更多</button></div>
<article id="feedback-detail" class="panel detail"><div class="empty">选择反馈</div></article>
</div>
</section>
<section id="content" class="view" hidden></section>
<section id="task-panel" class="task-panel" hidden>
<p id="task-status" role="status"></p>
<div id="task-fallback" hidden><label for="task-text">复制任务到对话继续处理</label><textarea id="task-text" rows="4" readonly></textarea></div>
</section>
</div>
</main>
<script type="module">// ---- 标准 MCP Apps 通信;只接受直接宿主消息,不持有认证信息 ----
function createDamiBridge(handlers) {
const pending = new Map();
let sequence = 0;
let closed = false;
let ready = false;
let host = {};
const send = message => window.parent.postMessage({ jsonrpc: '2.0', ...message }, '*');
function request(method, params, timeout = 45_000) {
if (closed || window.parent === window) return Promise.reject(new Error('请从已连接的大米插件打开工作台。'));
const id = `dami-${++sequence}`;
return new Promise((resolve, reject) => {
const timer = setTimeout(() => {
pending.delete(id);
reject(new Error('请求未取得结果。写操作可能已生效,请先刷新核实,勿重复提交。'));
}, timeout);
pending.set(id, { resolve, reject, timer });
send({ id, method, params });
});
}
function close() {
closed = true;
for (const item of pending.values()) {
clearTimeout(item.timer);
item.reject(new Error('工作台已关闭。'));
}
pending.clear();
window.removeEventListener('message', receive);
}
function receive(event) {
if (event.source !== window.parent || !event.data || event.data.jsonrpc !== '2.0') return;
const message = event.data;
const item = pending.get(message.id);
if (item && !message.method) {
pending.delete(message.id);
clearTimeout(item.timer);
if (message.error) item.reject(new Error(message.error.message || '宿主拒绝请求。'));
else item.resolve(message.result);
return;
}
if (message.method === 'ui/resource-teardown') {
send({ id: message.id, result: {} });
close();
} else if (message.method === 'ui/notifications/tool-result') handlers.onResult(message.params);
else if (message.method === 'ui/notifications/host-context-changed') handlers.onContext(message.params || {});
else if (message.method === 'ui/notifications/tool-cancelled') handlers.onError('打开工作台的请求已取消。');
else if (message.method && message.id !== undefined) {
send({ id: message.id, error: { code: -32601, message: 'Method not supported' } });
}
}
window.addEventListener('message', receive);
window.addEventListener('pagehide', close, { once: true });
return {
async connect() {
host = await request('ui/initialize', {
appInfo: { name: 'Dami Workbench', version: '2.4.1' },
appCapabilities: { availableDisplayModes: ['inline', 'fullscreen'] },
protocolVersion: '2026-01-26',
}, 12_000);
if (host.protocolVersion !== '2026-01-26') throw new Error('宿主的 MCP Apps 协议版本不兼容。');
handlers.onContext(host.hostContext || {});
ready = true;
send({ method: 'ui/notifications/initialized', params: {} });
return host.hostCapabilities || {};
},
async callTool(name, args = {}) {
if (!ready || !host.hostCapabilities?.serverTools) throw new Error('当前宿主未提供工作台工具调用能力,请在对话中继续。');
const result = await request('tools/call', { name, arguments: args });
let payload = result?.structuredContent;
if (!payload) {
try { payload = JSON.parse(result?.content?.find(item => item.type === 'text')?.text || '{}'); }
catch { throw new Error('工具未返回可识别的数据。'); }
}
if (!payload || typeof payload !== 'object') throw new Error('工具未返回可识别的数据。');
if (result?.isError || payload.error) throw new Error(payload.error?.message || '工具执行失败,请刷新连接或检查权限。');
if (!Object.hasOwn(payload, 'data')) throw new Error('工具结果缺少数据,请刷新插件连接。');
return payload.data;
},
async context(value) {
if (!ready || !host.hostCapabilities?.updateModelContext) return;
await request('ui/update-model-context', { structuredContent: value }, 10_000);
},
async message(text) {
if (!ready || !host.hostCapabilities?.message) throw new Error('当前宿主不支持发送到对话,请复制下方任务文本。');
const result = await request('ui/message', { role: 'user', content: [{ type: 'text', text }] });
if (result?.isError) throw new Error('任务未送达对话,请复制下方任务文本。');
},
openLink(url) {
if (!ready || !host.hostCapabilities?.openLinks) return null;
return request('ui/open-link', { url }).then(result => {
if (result?.isError) throw new Error('当前宿主未能打开总后台,请在浏览器访问该链接。');
});
},
resize(height) {
if (ready && !closed) send({ method: 'ui/notifications/size-changed', params: { height } });
},
};
}
// ---- 纯展示与宿主适配;业务读写全部使用已登记 MCP 工具 ----
const $ = id => document.getElementById(id);
const pendingControls = new Set();
const state = { projects: [], projectId: '', view: 'overview', feedback: [], detail: null,
cursor: 0, more: false, feedbackTotal: 0, feedbackQuery: {}, siteLimit: 6, epoch: 0, detailEpoch: 0,
taskEpoch: 0, bootstrapped: false, busy: 0, deepLink: null };
const statusLabels = { unread: '未读', read: '已读', new: '新反馈', processing: '处理中', replied: '已回复', closed: '已关闭',
pending: '待处理', preparing: '准备中', waiting: '等待中', running: '进行中', translating: '翻译中',
publishing: '发布中', pushing: '推送中', success: '成功', succeeded: '成功', failed: '失败',
draft: '草稿', published: '已发布', archived: '已归档', sent: '已发送', cleared: '已清空' };
function element(tag, className = '', text = '') {
const node = document.createElement(tag);
node.className = className;
node.textContent = String(text ?? '');
return node;
}
function button(text, action, className = '') {
const node = element('button', className, text);
node.type = 'button';
node.addEventListener('click', () => perform(action, node));
return node;
}
function icon(name) {
const node = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
const use = document.createElementNS(node.namespaceURI, 'use');
node.classList.add('icon');
node.setAttribute('aria-hidden', 'true');
use.setAttribute('href', `#icon-${name}`);
node.append(use);
return node;
}
function notice(message = '') {
$('notice').textContent = message;
$('notice').hidden = !message;
}
async function perform(action, control) {
if (control) { pendingControls.add(control); control.disabled = true; }
state.busy++;
$('app').setAttribute('aria-busy', 'true');
try { await action(); }
catch (error) { notice(error.message || '操作失败,请刷新后核实。'); }
finally {
pendingControls.delete(control);
if (control?.isConnected) control.disabled = false;
state.busy--;
$('app').setAttribute('aria-busy', String(state.busy > 0));
updateAiControls();
}
}
function list(data) {
if (Array.isArray(data)) return data;
if (Array.isArray(data?.list)) return data.list;
throw new Error('后台列表格式已变化,请刷新插件连接。');
}
function date(value, compact = false) {
if (!value) return '尚无记录';
const time = new Date(typeof value === 'number' ? value * 1000 : value);
if (Number.isNaN(time.getTime())) return '时间未知';
return compact ? time.toLocaleDateString('zh-CN', { month: '2-digit', day: '2-digit' })
: time.toLocaleString('zh-CN', { hour12: false });
}
function projectName(id) {
return state.projects.find(item => item.project_id === id)?.app_name || id;
}
function selectedContext() {
return { project_id: state.projectId || null, view: state.view,
feedback_id: state.detail?.feedback?.id || null,
selection_only: true };
}
async function syncContext() {
try { await bridge.context(selectedContext()); }
catch { /* 选中项同步失败不阻止读取;任务消息仍携带明确 ID。 */ }
}
async function askAgent(instruction, projectId = state.projectId) {
const taskEpoch = ++state.taskEpoch;
const scope = projectId ? `目标 project_id=${JSON.stringify(projectId)}。` : '范围为当前账号可访问的全部站点。';
const text = `使用大米 MCP。${scope}${instruction}`;
$('task-panel').hidden = false;
$('task-fallback').hidden = true;
$('task-text').value = text;
$('task-status').textContent = '正在交给对话…';
try {
await bridge.message(text);
if (taskEpoch === state.taskEpoch) $('task-status').textContent = '任务已交给对话,请在对话中查看处理进展。';
} catch (error) {
if (taskEpoch !== state.taskEpoch) return;
$('task-status').textContent = error.message;
$('task-fallback').hidden = false;
}
}
function heading(title) {
const node = element('div', 'section-heading');
const text = element('div');
text.append(element('h2', '', title));
node.append(text);
return node;
}
function healthItem(label, value, description, failed = false) {
const node = element('article', 'health-item');
node.append(element('span', 'meta', label), element('strong', failed ? 'error' : '', value));
if (description) node.append(element('p', 'meta', description));
return node;
}
// ---- 站点目录仅搜索已授权列表;不逐站请求或生成虚假运行指标 ----
function projectRow(project) {
const name = project.app_name || project.project_id;
const row = button('', () => selectProject(project.project_id), 'site-row');
const copy = element('span', 'site-copy');
copy.append(element('strong', '', name), element('span', 'meta', project.now_lang || '—'));
row.title = String(project.app_desc || name);
const action = element('span', 'row-action');
action.append(icon('arrow'));
row.append(element('span', 'site-avatar', Array.from(name)[0]?.toUpperCase() || '站'), copy, action);
row.setAttribute('aria-label', `查看站点:${name}`);
return row;
}
function renderProjects() {
const query = $('site-query').value.trim().toLocaleLowerCase();
const matches = state.projects.filter(item => [item.app_name, item.app_desc, item.project_id]
.some(value => String(value || '').toLocaleLowerCase().includes(query)));
const visible = matches.slice(0, state.siteLimit);
const message = state.projects.length ? '未找到站点' : '暂无站点,请联系管理员分配';
$('site-list').replaceChildren(...(visible.length ? visible.map(projectRow) : [element('div', 'empty', message)]));
$('site-list-count').textContent = `${visible.length} / ${matches.length}`;
$('more-sites').hidden = visible.length >= matches.length;
}
function updateAiControls() {
$('ai-feedback').disabled = pendingControls.has($('ai-feedback')) || !state.bootstrapped || !state.projects.length;
for (const id of ['ai-publish', 'ai-content']) $(id).disabled = pendingControls.has($(id)) || !state.bootstrapped || !state.projectId;
$('ai-hint').hidden = !!state.projectId;
}
function feedbackTotal(data) {
const total = Number(data?.total);
if (data?.total === null || data?.total === undefined || !Number.isSafeInteger(total) || total < 0) {
throw new Error('反馈总数格式已变化,请刷新连接。');
}
return total;
}
function previewFeedback(item) {
const row = button('', () => viewFeedback('unread', item.id), 'preview-row');
const copy = element('span', 'preview-copy');
copy.append(element('strong', '', item.subject || '无主题'), element('span', 'meta', projectName(item.project_id)));
const timestamp = element('span', 'preview-date', date(item.created_at, true));
timestamp.title = date(item.created_at);
row.append(element('span', 'unread-dot'), copy, timestamp);
return row;
}
function showFeedbackSummary(result, countId, preview) {
if (result.status === 'fulfilled') {
try {
$(countId).textContent = String(feedbackTotal(result.value));
if (preview) {
const rows = list(result.value).map(previewFeedback);
$('unread-list').replaceChildren(...(rows.length ? rows : [element('div', 'empty', '暂无未读反馈。')]));
}
return true;
} catch (error) { result = { status: 'rejected', reason: error }; }
}
$(countId).textContent = '—';
if (preview) $('unread-list').replaceChildren(element('div', 'empty error', `读取失败:${result.reason.message}`));
return false;
}
async function loadFeedbackSummary(epoch, projectId) {
const unreadArgs = { after_id: 0, size: 3, read_status: 'unread' };
const draftArgs = { after_id: 0, size: 1, has_draft: '1' };
if (projectId) { unreadArgs.project_id = projectId; draftArgs.project_id = projectId; }
const [unread, drafts] = await Promise.allSettled([
bridge.callTool('list_feedback', unreadArgs), bridge.callTool('list_feedback', draftArgs),
]);
if (epoch !== state.epoch) return;
const unreadOk = showFeedbackSummary(unread, 'unread-count', true);
const draftsOk = showFeedbackSummary(drafts, 'draft-count', false);
$('overview-status').hidden = unreadOk && draftsOk;
$('overview-status').textContent = unreadOk && draftsOk ? '' : '部分数据未能加载,请刷新重试';
}
async function loadOverview(epoch) {
const projectId = state.projectId;
$('sites-panel').hidden = !!projectId;
$('site-summary').hidden = !projectId;
$('site-count-label').textContent = projectId ? '当前站点' : '站点';
$('site-count').textContent = String(projectId ? 1 : state.projects.length);
$('unread-count').textContent = $('draft-count').textContent = '—';
$('overview-status').hidden = true;
$('unread-list').replaceChildren(element('div', 'empty', '加载中…'));
for (const id of ['unread-metric', 'draft-metric', 'all-feedback']) $(id).disabled = false;
renderProjects();
updateAiControls();
await Promise.all([loadFeedbackSummary(epoch, projectId), projectId ? loadSiteHealth(epoch, projectId) : null]);
}
async function loadSiteHealth(epoch, projectId) {
$('site-health').replaceChildren(element('div', 'empty', '加载中…'));
const values = await Promise.allSettled([
bridge.callTool('get_site', { project_id: projectId }),
bridge.callTool('get_publish_status', { project_id: projectId }),
bridge.callTool('get_publish_readiness', { project_id: projectId }),
]);
if (epoch !== state.epoch) return;
const grid = element('div', 'health-grid');
const [site, publication, readiness] = values;
if (site.status === 'fulfilled') {
grid.append(healthItem('源语言', site.value.config_lang || '—', `默认语言:${site.value.now_lang || '—'}`));
grid.append(healthItem('最近推送', date(site.value.pushed_at)));
} else grid.append(healthItem('站点信息', '读取失败', site.reason.message, true));
if (publication.status === 'fulfilled') {
const task = publication.value.task;
grid.append(healthItem('发布状态', task ? statusLabels[task.status] || task.status : '暂无任务',
task ? task.error_message || date(task.updated_at) : '', task?.status === 'failed'));
} else grid.append(healthItem('发布状态', '读取失败', publication.reason.message, true));
if (readiness.status === 'fulfilled') {
const value = readiness.value;
grid.append(healthItem('发布准备', value.blocked ? '处理中' : value.ready ? '已就绪' : '未就绪',
value.blocked ? '有任务正在执行' : value.ready ? '' : '请检查翻译与待完成项'));
} else grid.append(healthItem('发布准备', '检查失败', readiness.reason.message, true));
const intro = element('div', 'site-intro');
intro.append(element('h3', '', projectName(projectId)));
intro.title = state.projects.find(item => item.project_id === projectId)?.app_desc || '';
const actions = element('div', 'actions');
actions.append(button('查看反馈', () => viewFeedback('all')), button('查看内容', () => selectView('content')));
$('site-health').replaceChildren(intro, grid, actions);
}
function renderFeedbackList() {
const rows = state.feedback.map(item => {
const node = button('', () => openFeedback(item.id), 'feedback-item');
node.dataset.feedbackId = String(item.id);
if (state.detail?.feedback?.id === item.id) node.classList.add('selected');
node.append(element('span', item.read_status === 'unread' ? 'badge unread' : 'badge', statusLabels[item.read_status] || item.read_status),
element('span', 'meta', projectName(item.project_id)), element('strong', '', item.subject || '无主题'),
element('div', 'meta', `${item.user_email || '站内反馈'} · ${date(item.created_at)}`));
return node;
});
$('feedback-list').replaceChildren(...(rows.length ? rows : [element('div', 'empty', '没有符合条件的反馈。')]));
$('load-more').hidden = !state.more;
$('feedback-count').textContent = `${state.feedback.length} / ${state.feedbackTotal} 条`;
}
function feedbackQuery() {
const args = {};
if (state.projectId) args.project_id = state.projectId;
if ($('read-filter').value) args.read_status = $('read-filter').value;
if ($('draft-filter').value) args.has_draft = $('draft-filter').value;
if ($('feedback-query').value.trim()) args.keyword = $('feedback-query').value.trim();
return args;
}
async function loadFeedback(epoch, append = false) {
// 分页沿用已提交条件,编辑筛选但尚未查询时不能混入另一组结果。
const args = { ...state.feedbackQuery, after_id: append ? state.cursor : 0, size: 30 };
const data = await bridge.callTool('list_feedback', args);
if (epoch !== state.epoch) return;
const items = list(data);
const next = Number(data.next_after_id || 0);
if (!append) state.feedbackTotal = feedbackTotal(data);
state.feedback = append ? [...state.feedback, ...items.filter(item => !state.feedback.some(old => old.id === item.id))] : items;
state.more = state.feedback.length < state.feedbackTotal && items.length > 0 && next > args.after_id;
state.cursor = next;
renderFeedbackList();
}
async function viewFeedback(filter, id) {
$('read-filter').value = filter === 'unread' ? 'unread' : '';
$('draft-filter').value = filter === 'draft' ? '1' : '';
$('feedback-query').value = '';
const opening = selectView('feedback');
const epoch = state.epoch;
await opening;
if (id && epoch === state.epoch) await openFeedback(id);
}
function renderFeedbackDetail() {
const data = state.detail;
const item = data.feedback;
const root = $('feedback-detail');
root.replaceChildren(element('span', 'eyebrow', projectName(item.project_id)), element('h2', '', item.subject || '无主题'),
element('p', 'meta', `${item.user_email || '站内反馈'} · ${date(item.created_at)} · ${statusLabels[item.status] || item.status}`));
if (item.content_truncated) root.append(element('p', 'meta', '正文较长,当前展示的是截断内容。'));
root.append(element('div', 'body-text', item.content_text || '正文为空'));
if (item.draft_reply_original) {
const draft = element('section', 'draft');
draft.append(element('h3', '', `回复草稿 · ${statusLabels[item.draft_status] || item.draft_status || '已保存'}`),
element('p', '', item.draft_subject_original || ''), element('p', '', item.draft_reply_original));
if (item.draft_reply_zh) draft.append(element('h3', '', '中文审核稿'), element('p', '', item.draft_reply_zh));
root.append(draft);
}
const actions = element('div', 'actions');
actions.append(button('AI 起草', () => askAgent(`读取 feedback_id=${item.id} 的正文、站点事实与草稿版本,起草并保存双语回复。本次不发送邮件、不改变阅读状态。`, item.project_id), 'primary'),
button(item.read_status === 'read' ? '标为未读' : '标为已读', async () => {
const epoch = state.epoch;
await bridge.callTool('set_feedback_read_status', { feedback_id: item.id, read_status: item.read_status === 'read' ? 'unread' : 'read' });
if (epoch !== state.epoch || state.detail?.feedback?.id !== item.id) return;
await openFeedback(item.id);
if (epoch === state.epoch) await loadFeedback(epoch);
}));
root.append(actions);
for (const reply of data.replies || []) {
const box = element('section', 'draft');
box.append(element('h3', '', '历史回复'), element('p', '', reply.reply_content || reply.content || ''), element('span', 'meta', date(reply.created_at)));
root.append(box);
}
}
async function openFeedback(id) {
const epoch = state.epoch;
const detailEpoch = ++state.detailEpoch;
state.detail = null;
$('feedback-detail').replaceChildren(element('div', 'empty', '正在读取所选反馈…'));
renderFeedbackList();
let data;
try { data = await bridge.callTool('get_feedback', { feedback_id: Number(id) }); }
catch (error) {
if (epoch !== state.epoch || detailEpoch !== state.detailEpoch) return;
$('feedback-detail').replaceChildren(element('div', 'empty error', '反馈详情读取失败,请重新选择或刷新。'));
throw error;
}
if (epoch !== state.epoch || detailEpoch !== state.detailEpoch) return;
if (!data?.feedback || (state.projectId && data.feedback.project_id !== state.projectId)) throw new Error('反馈不属于当前站点,请重新选择。');
state.detail = data;
renderFeedbackDetail();
renderFeedbackList();
await syncContext();
}
function contentTable(rows, kind, projectId) {
if (!rows.length) return element('div', 'empty', kind === 'page' ? '暂无页面' : '暂无博客');
const wrap = element('div', 'table-wrap');
const table = element('table');
const head = element('tr');
for (const label of ['名称', '状态', '操作']) head.append(element('th', '', label));
const thead = element('thead'); thead.append(head); table.append(thead);
const body = element('tbody');
for (const item of rows) {
const row = element('tr');
row.append(element('td', '', item.title || item.slug || `#${item.id}`), element('td', '', statusLabels[item.status] || item.status || '—'));
const action = element('td');
action.append(button('AI 查看', () => askAgent(`读取${kind === 'page' ? '页面' : '博客文章'} id=${Number(item.id)} 的详情、合同和发布状态,说明当前情况与建议。本次先读取,不修改。`, projectId), 'text-button'));
row.append(action); body.append(row);
}
table.append(body); wrap.append(table); return wrap;
}
async function loadContent(epoch) {
if (!state.projectId) { $('content').replaceChildren(element('div', 'empty', '先选择站点')); return; }
const projectId = state.projectId;
const results = await Promise.allSettled([
bridge.callTool('list_pages', { project_id: projectId }),
bridge.callTool('list_blog_posts', { project_id: projectId, page: 1, size: 20 }),
]);
if (epoch !== state.epoch) return;
const nodes = [];
for (const [index, result] of results.entries()) {
const section = element('section', 'panel content-section');
section.append(heading(index === 0 ? '页面' : '博客(最近 20 篇)'));
section.append(result.status === 'fulfilled' ? contentTable(list(result.value), index === 0 ? 'page' : 'blog', projectId)
: element('div', 'empty error', `读取失败:${result.reason.message}`));
nodes.push(section);
}
const actions = element('div', 'actions');
actions.append(button('AI 规划', () => askAgent('读取站点定位、现有页面和博客,给出内容规划建议。本次先规划,不创建或发布。', projectId), 'primary'));
$('content').replaceChildren(...nodes, actions);
}
async function refreshView() {
notice();
const epoch = ++state.epoch;
state.detailEpoch++;
state.taskEpoch++;
state.detail = null;
$('task-panel').hidden = true;
$('feedback-detail').replaceChildren(element('div', 'empty', '选择反馈'));
const view = $(state.view);
if (state.view === 'feedback') {
state.feedbackQuery = feedbackQuery();
state.feedback = [];
state.more = false;
$('feedback-count').textContent = '正在查询…';
$('feedback-list').replaceChildren(element('div', 'empty', '正在读取反馈…'));
$('load-more').hidden = true;
} else if (state.view === 'content') view.replaceChildren(element('div', 'empty', '正在读取当前站点数据…'));
view.classList.add('loading');
try {
const load = { overview: loadOverview, feedback: loadFeedback, content: loadContent }[state.view];
await load(epoch);
if (epoch === state.epoch) await syncContext();
} catch (error) {
if (epoch !== state.epoch) return;
if (state.view === 'feedback') {
$('feedback-count').textContent = '查询失败';
$('feedback-list').replaceChildren(element('div', 'empty error', '未能读取反馈,请重试。'));
} else if (state.view === 'content') view.replaceChildren(element('div', 'empty error', '未能读取内容,请重试。'));
throw error;
} finally { if (epoch === state.epoch) view.classList.remove('loading'); }
}
async function selectView(view) {
if (!['overview', 'feedback', 'content'].includes(view)) return;
setView(view);
await refreshView();
}
function setView(view) {
state.view = view;
for (const node of document.querySelectorAll('[data-view]')) {
node.setAttribute('aria-current', node.dataset.view === view ? 'page' : 'false');
$(node.dataset.view).hidden = node.dataset.view !== view;
}
updateHeading();
}
function updateHeading() {
const titles = { overview: '概览', feedback: '用户反馈', content: '页面与博客' };
$('workspace-title').textContent = titles[state.view];
}
function setProject(id) {
if (id && !state.projects.some(item => item.project_id === id)) throw new Error('当前连接不能访问此站点。');
state.projectId = id;
$('project').value = id;
updateHeading();
updateAiControls();
}
async function selectProject(id) {
setProject(id);
await refreshView();
}
async function bootstrap(data) {
const initial = !state.bootstrapped;
state.projects = data.projects;
state.bootstrapped = true;
const options = [element('option', '', '全部站点')]; options[0].value = '';
for (const item of data.projects) {
const option = element('option', '', item.app_name || item.project_id); option.value = item.project_id; options.push(option);
}
$('project').replaceChildren(...options);
$('project').disabled = false;
$('refresh').disabled = false;
if (['overview', 'feedback', 'content'].includes(data.view)) setView(data.view);
const accessible = data.projects.some(item => item.project_id === data.project_id);
setProject(accessible ? data.project_id : '');
if (initial && state.deepLink) applyDeepLink(state.deepLink);
await refreshView();
}
function applyDeepLink(path) {
const url = new URL(path, 'https://dami.invalid');
if (url.origin !== 'https://dami.invalid') return;
const project = url.searchParams.get('project_id');
if (project !== null) setProject(project);
const view = url.searchParams.get('view');
if (['overview', 'feedback', 'content'].includes(view)) setView(view);
}
async function followDeepLink(path) {
applyDeepLink(path);
await refreshView();
}
const bridge = createDamiBridge({
onResult(result) {
const data = result?.structuredContent?.data;
if (result?.isError) { notice(result?.structuredContent?.error?.message || '工作台打开失败,请检查连接。'); return; }
if (Array.isArray(data?.projects)) perform(() => bootstrap(data));
},
onError: notice,
onContext(context) {
if (context.theme === 'light' || context.theme === 'dark') document.documentElement.dataset.theme = context.theme;
const link = context['openai/deepLink'];
const path = typeof link === 'string' ? link : link?.url;
if (typeof path === 'string' && path.startsWith('/') && path !== state.deepLink) {
state.deepLink = path;
if (state.bootstrapped) perform(() => followDeepLink(path));
}
},
});
$('project').addEventListener('change', event => perform(() => selectProject(event.target.value)));
$('open-backend').addEventListener('click', event => {
const opening = bridge.openLink($('open-backend').href);
if (opening) { event.preventDefault(); perform(() => opening); }
});
$('site-query').addEventListener('input', () => { state.siteLimit = 6; renderProjects(); });
$('more-sites').addEventListener('click', () => { state.siteLimit += 6; renderProjects(); });
$('all-sites').addEventListener('click', () => perform(() => selectProject('')));
$('unread-metric').addEventListener('click', () => perform(() => viewFeedback('unread')));
$('draft-metric').addEventListener('click', () => perform(() => viewFeedback('draft')));
$('all-feedback').addEventListener('click', () => perform(() => viewFeedback('unread')));
$('ai-feedback').addEventListener('click', () => perform(() => askAgent('读取当前范围的未读反馈,按问题归类并列出优先处理建议。本次先整理,不标记已读、不保存草稿或发送邮件。'), $('ai-feedback')));
$('ai-publish').addEventListener('click', () => perform(() => askAgent('读取站点配置、发布准备与任务状态,说明问题和建议。本次先检查,不修改或发布。'), $('ai-publish')));
$('ai-content').addEventListener('click', () => perform(() => askAgent('读取站点定位、现有页面和博客,给出内容规划建议。本次先规划,不创建或发布。'), $('ai-content')));
$('refresh').addEventListener('click', () => perform(async () => {
const data = await bridge.callTool('list_projects');
await bootstrap({ projects: list(data), project_id: state.projectId, view: state.view });
}, $('refresh')));
for (const node of document.querySelectorAll('[data-view]')) node.addEventListener('click', () => {
if (state.bootstrapped) perform(() => selectView(node.dataset.view));
});
$('feedback-filters').addEventListener('submit', event => { event.preventDefault(); perform(refreshView); });
$('load-more').addEventListener('click', () => perform(() => loadFeedback(state.epoch, true), $('load-more')));
if (typeof ResizeObserver !== 'undefined') new ResizeObserver(() => bridge.resize(Math.ceil($('app').getBoundingClientRect().height))).observe($('app'));
perform(async () => {
await bridge.connect();
$('connection').textContent = '已连接';
$('connection').classList.add('ready');
// 入口结果优先;仅宿主未转发初始结果时读取项目,不重复调用渲染工具。
setTimeout(() => {
if (!state.bootstrapped) perform(async () => bootstrap({ projects: list(await bridge.callTool('list_projects')) }));
}, 4000);
});
</script>
</body>
</html>
SHA-256: 8be97f734a457d250fdf1cf91d646d566220b24658946c8a603dfebab84274d3