iter(g323): 文档站收录组合配方区,折叠展开即看骨架与要点

This commit is contained in:
HIsheR
2026-10-27 01:13:26 +08:00
parent 3a637c60c8
commit f8421077bd
2 changed files with 30 additions and 5 deletions
+9
View File
@@ -3293,6 +3293,15 @@
"motivation": "g319 给 `ua info` 加了逗号批量,但 Agent 查组件的主通道是 MCP(get_component 一次仍只能查一个);拼一个界面要 3–5 次工具往返,MCP 侧同样需要批量,并抽出 CLI / MCP 共用的名字解析函数避免两处口径漂移。", "motivation": "g319 给 `ua info` 加了逗号批量,但 Agent 查组件的主通道是 MCP(get_component 一次仍只能查一个);拼一个界面要 3–5 次工具往返,MCP 侧同样需要批量,并抽出 CLI / MCP 共用的名字解析函数避免两处口径漂移。",
"acceptance": "① get_component 的 name 支持逗号分隔多值:json 返回数组、md 返回以水平线分隔的多段片段,单值输出结构不变;② 不存在的 id 报错并标明序号;③ 名字解析抽成 CLI / MCP 共用函数;④ 工具 description 同步更新;⑤ npm run check 通过。", "acceptance": "① get_component 的 name 支持逗号分隔多值:json 返回数组、md 返回以水平线分隔的多段片段,单值输出结构不变;② 不存在的 id 报错并标明序号;③ 名字解析抽成 CLI / MCP 共用函数;④ 工具 description 同步更新;⑤ npm run check 通过。",
"notes": "" "notes": ""
},
{
"id": "g323",
"domain": "tooling",
"status": "open",
"title": "文档站收录组合配方区(骨架 + 要点展开即看)",
"motivation": "21 条配方此前只在 CLI / MCP / llms-full.txt 三条 Agent 通道可查,人类版文档站完全没有入口——浏览器里翻配方只能回 recipes.md 源文件;文档站已有组件/令牌/搜索/亮暗主题,配方是最后一块缺口。",
"acceptance": "① apps/docs/index.html 生成「组合配方」卡:每条配方一个 details 折叠块(标题 + 组件 chips + 代码块 + 要点),导航栏补「配方」锚点;② 搜索态不渲染配方卡(不属于任何包,避免稀释过滤结果);③ 配方数据由 parseRecipes 生成而非手写,recipes.md 改动后 npm run docs 即同步;④ npm run check 通过。",
"notes": ""
} }
] ]
} }
+21 -5
View File
@@ -380,6 +380,7 @@ function esc(s) {
function renderDocs(index, tokens) { function renderDocs(index, tokens) {
const data = JSON.stringify(index.packages).replace(/</g, '\\u003c'); const data = JSON.stringify(index.packages).replace(/</g, '\\u003c');
const tokData = JSON.stringify(tokens).replace(/</g, '\\u003c'); const tokData = JSON.stringify(tokens).replace(/</g, '\\u003c');
const recipeData = JSON.stringify(parseRecipes().map((r) => ({ id: r.id, title: r.title, components: recipeComps(r), code: r.code, notes: r.notes }))).replace(/</g, '\\u003c'); // 配方随文档站一起生成:人类也能在浏览器里翻骨架与要点
const generatedAt = esc(index.generatedAt); const generatedAt = esc(index.generatedAt);
// 文档站外壳配色从 tokens.json 派生:手写十六进制会跟令牌脱节(改暗色 accent 后这里还留着旧值, // 文档站外壳配色从 tokens.json 派生:手写十六进制会跟令牌脱节(改暗色 accent 后这里还留着旧值,
// 页面与演示 iframe 两个蓝各显示一种),派生后单一来源,改令牌即全站跟随。 // 页面与演示 iframe 两个蓝各显示一种),派生后单一来源,改令牌即全站跟随。
@@ -424,6 +425,11 @@ table.props th{background:var(--surface);font-weight:500;color:var(--muted)}
pre{background:var(--code-bg);color:var(--code-text);border-radius:8px;padding:12px 14px;overflow-x:auto;font:12.5px/1.6 Consolas,Menlo,monospace;margin:8px 0} pre{background:var(--code-bg);color:var(--code-text);border-radius:8px;padding:12px 14px;overflow-x:auto;font:12.5px/1.6 Consolas,Menlo,monospace;margin:8px 0}
code{font-family:Consolas,Menlo,monospace;font-size:12.5px} code{font-family:Consolas,Menlo,monospace;font-size:12.5px}
li code,td code{background:var(--code-bg);color:var(--code-text);padding:1px 6px;border-radius:4px} li code,td code{background:var(--code-bg);color:var(--code-text);padding:1px 6px;border-radius:4px}
details.recipe{border:1px dashed var(--border);border-radius:8px;margin:8px 0;background:var(--bg)}
details.recipe summary{cursor:pointer;padding:8px 12px;font-weight:500;font-size:13px}
details.recipe[open] summary{border-bottom:1px dashed var(--border)}
details.recipe .body{padding:10px 12px}
details.recipe .tag{margin:0 6px 4px 0}
iframe.demo{width:100%;height:430px;border:1px dashed var(--border);border-radius:10px;margin-top:10px;background:var(--surface)} iframe.demo{width:100%;height:430px;border:1px dashed var(--border);border-radius:10px;margin-top:10px;background:var(--surface)}
.sw{display:flex;align-items:center;gap:10px;margin:6px 0} .sw{display:flex;align-items:center;gap:10px;margin:6px 0}
.sw i{width:26px;height:26px;border-radius:6px;border:1px solid var(--border);flex:none} .sw i{width:26px;height:26px;border-radius:6px;border:1px solid var(--border);flex:none}
@@ -448,6 +454,7 @@ footer{max-width:960px;margin:0 auto;padding:0 20px 40px;color:var(--muted);font
<script> <script>
var DATA=${data}; var DATA=${data};
var TOKENS=${tokData}; var TOKENS=${tokData};
var RECIPES=${recipeData};
var ORDER={skin-dom:0,'headless-core':1,'design-tokens':2}; var ORDER={skin-dom:0,'headless-core':1,'design-tokens':2};
function esc(s){return String(s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;')} function esc(s){return String(s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;')}
function propsTable(c){ function propsTable(c){
@@ -541,10 +548,19 @@ function tokensCard(){
}).join(''); }).join('');
return '<section class="card"><h2>@ui-arch/tokens <span class="badge">design-tokens</span></h2><p class="desc">皮肤包只允许消费 --ua-* CSS 变量;暗色主题覆盖值不换名(light / dark)。</p><h3>颜色令牌</h3><div class="swatches">'+sw+'</div></section>'; return '<section class="card"><h2>@ui-arch/tokens <span class="badge">design-tokens</span></h2><p class="desc">皮肤包只允许消费 --ua-* CSS 变量;暗色主题覆盖值不换名(light / dark)。</p><h3>颜色令牌</h3><div class="swatches">'+sw+'</div></section>';
} }
function render(list){ function recipesCard(){
if(!RECIPES.length)return '';
var items=RECIPES.map(function(r){
var chips=(r.components||[]).map(function(c){return '<span class="tag">'+esc(c)+'</span>'}).join('');
var notes=(r.notes||[]).map(function(n){return '<li>'+esc(n)+'</li>'}).join('');
return '<details class="recipe"><summary>配方 '+esc(r.id)+':'+esc(r.title)+'</summary><div class="body"><div>'+chips+'</div><pre><code>'+esc(r.code)+'</code></pre>'+(notes?'<ul>'+notes+'</ul>':'')+'</div></details>';
}).join('');
return '<section class="card" id="recipes"><h2>组合配方 <span class="badge">recipes</span></h2><p class="desc">跨组件拼装的最小可运行骨架与要点,展开即看代码;Agent 侧对应 MCP get_recipe / ua recipes。</p>'+items+'</section>';
}
function render(list,q){
var app=document.getElementById('app'); var app=document.getElementById('app');
if(!list.length){app.innerHTML='<p class="empty">无匹配结果</p>';var none=document.getElementById('hit');if(none)none.textContent='无匹配结果';return} if(!list.length){app.innerHTML='<p class="empty">无匹配结果</p>';var none=document.getElementById('hit');if(none)none.textContent='无匹配结果';return}
app.innerHTML=quickstart()+list.map(pkgCard).join('')+(list.some(function(p){return p.name.indexOf('tokens')>=0})?'':tokensCard()); app.innerHTML=quickstart()+list.map(pkgCard).join('')+(list.some(function(p){return p.name.indexOf('tokens')>=0})?'':tokensCard())+(q?'':recipesCard()); // 搜索态不渲染配方卡:它不属于任何包,避免过滤结果被无关区块稀释
var hit=document.getElementById('hit'); // 搜索结果对读屏播报 var hit=document.getElementById('hit'); // 搜索结果对读屏播报
if(hit)hit.textContent='匹配 '+list.length+' 个包'; if(hit)hit.textContent='匹配 '+list.length+' 个包';
} }
@@ -554,11 +570,11 @@ function norm(p){
return parts.join(' ').toLowerCase(); return parts.join(' ').toLowerCase();
} }
var ordered=DATA.slice().sort(function(a,b){return (ORDER[a.type]??9)-(ORDER[b.type]??9)}); var ordered=DATA.slice().sort(function(a,b){return (ORDER[a.type]??9)-(ORDER[b.type]??9)});
document.getElementById('nav').innerHTML=ordered.map(function(p){return '<a href="#p-'+esc(p.name.replace(/[^\\w-]/g,''))+'">'+esc(p.name)+'</a>'}).join('')+'<a href="#top-tokens">令牌</a>'; document.getElementById('nav').innerHTML=ordered.map(function(p){return '<a href="#p-'+esc(p.name.replace(/[^\\w-]/g,''))+'">'+esc(p.name)+'</a>'}).join('')+'<a href="#recipes">配方</a><a href="#top-tokens">令牌</a>';
render(ordered); render(ordered,'');
document.getElementById('q').addEventListener('input',function(e){ document.getElementById('q').addEventListener('input',function(e){
var q=e.target.value.trim().toLowerCase(); var q=e.target.value.trim().toLowerCase();
render(q?ordered.filter(function(p){return norm(p).indexOf(q)>=0}):ordered); render(q?ordered.filter(function(p){return norm(p).indexOf(q)>=0}):ordered,q);
}); });
// —— 主题:默认跟随系统(不写 data-theme),用户选择后持久化;iframe 演示带参重载联动 —— // —— 主题:默认跟随系统(不写 data-theme),用户选择后持久化;iframe 演示带参重载联动 ——
var THEME_KEY='ua-docs-theme'; var THEME_KEY='ua-docs-theme';