fix: 修复社区暂存主题并为函数图例添加数学排版
CI / docs-check (push) Canceled after 0s
CI / backend-test (push) Canceled after 0s
CI / service-test (push) Canceled after 0s
CI / frontend-test (push) Canceled after 0s
CI / rust-core (push) Canceled after 0s

This commit is contained in:
2026-09-09 22:31:39 +08:00
parent e5d144056c
commit 51c592841d
9 changed files with 293 additions and 23 deletions
@@ -124,7 +124,7 @@ function toggleSource() {
</div>
</section>
<DesktopPackages v-if="isDesktop()" :refresh-key="stagedRefresh" />
<section v-if="candidates.length">
<section v-if="candidates.length" class="saved-candidates panel">
<h2>已保存的声明式候选</h2><p>这些候选尚未应用到人设MCP 或模型运行配置</p>
<details v-for="item in candidates" :key="item.key"><summary>{{ item.key.replace('community-candidate:', '') }}</summary><pre>{{ item.value }}</pre><button class="btn" @click="removeCandidate(item.key)">删除候选</button></details>
</section>
@@ -163,5 +163,6 @@ label { display: grid; gap: var(--space-xs); }
input, select { color: var(--color-text-primary); background: var(--color-background-secondary); border: 1px solid var(--color-border-subtle); padding: var(--space-sm); }
.community-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); gap: var(--space-md); }
.community-card { display: grid; gap: var(--space-sm); text-align: left; padding: var(--space-lg); color: var(--color-text-primary); background: var(--color-background-secondary); border: 1px solid var(--color-border-subtle); border-radius: var(--radius-md); overflow-wrap: anywhere; }
.saved-candidates { margin-top: var(--space-xl); }
pre, dd { white-space: pre-wrap; overflow-wrap: anywhere; max-width: 100%; }
</style>
@@ -12,6 +12,18 @@ import DesktopPackages from './DesktopPackages.vue'
const item = { package_key: 'package-key', source: 'https://example.com/', namespace: 'examples', package_id: 'reviewer', version: '1.0.0', state: 'staged' }
beforeEach(() => { native.invoke.mockReset(); native.workspace.vaultId = 'vault-one' })
function component() { return mount(DesktopPackages, { props: { refreshKey: 0 }, global: { stubs: { AppDialog: { template: '<section><slot /></section>' } } } }) }
it('uses themed surfaces for the section, empty state, and staged package rows', async () => {
native.invoke.mockResolvedValueOnce([])
const empty = component(); await flushPromises()
expect(empty.get('section.desktop-packages').classes()).toContain('panel')
expect(empty.get('.empty-state').text()).toContain('本页没有暂存包')
empty.unmount()
native.invoke.mockResolvedValueOnce([item])
const populated = component(); await flushPromises()
expect(populated.get('.package-list > li').classes()).toContain('item-card')
populated.unmount()
})
it('loads durable staged metadata and previews without issuing install commands', async () => {
native.invoke.mockImplementation(async command => command === 'extension_staged' ? [item] : {
fingerprint: 'fingerprint', dependencies: { packages: [{ ...item, permissions: ['notes.read'] }] }, changes: [{ target: { configuration: {} }, expected_revision: null }],
@@ -53,20 +53,31 @@ onBeforeUnmount(() => ++generation)
</script>
<template>
<section class="desktop-packages" aria-label="桌面已暂存包">
<h2>桌面已暂存包</h2>
<p>暂存包保存在桌面安装库中重启后仍可查看暂存不代表已经安装或获得运行权限</p>
<button class="btn" :disabled="busy" @click="refresh">刷新暂存列表</button>
<p v-if="error" role="alert">{{ error }}</p>
<p v-if="busy" role="status">正在检查</p>
<p v-if="!busy && !packages.length">本页没有暂存包</p>
<ul><li v-for="item in packages" :key="item.package_key">
<strong>{{ item.namespace }}/{{ item.package_id }} · {{ item.version }}</strong>
<p>{{ item.source }}</p><button class="btn" :disabled="busy" @click="choose(item)">查看安装预览</button>
</li></ul>
<button class="btn" :disabled="busy || page === 0" @click="page--; refresh()">上一页</button>
<span> {{ page + 1 }} </span>
<button class="btn" :disabled="busy || packages.length < 20" @click="page++; refresh()">下一页</button>
<section class="desktop-packages panel" aria-label="桌面已暂存包">
<header class="section-heading">
<div>
<h2>桌面已暂存包</h2>
<p>暂存包保存在桌面安装库中重启后仍可查看暂存不代表已经安装或获得运行权限</p>
</div>
<button class="btn" :disabled="busy" @click="refresh">刷新暂存列表</button>
</header>
<p v-if="error" class="notice-banner error-message" role="alert">{{ error }}</p>
<p v-if="busy" class="notice-banner" role="status">正在检查</p>
<div v-if="!busy && !packages.length" class="empty-state">
<strong>本页没有暂存包</strong>
<span>从社区目录校验并暂存包后可在这里查看安装预览</span>
</div>
<ul v-if="packages.length" class="package-list">
<li v-for="item in packages" :key="item.package_key" class="item-card">
<div><strong>{{ item.namespace }}/{{ item.package_id }} · {{ item.version }}</strong><p>{{ item.source }}</p></div>
<button class="btn" :disabled="busy" @click="choose(item)">查看安装预览</button>
</li>
</ul>
<nav class="pagination" aria-label="暂存包分页">
<button class="btn" :disabled="busy || page === 0" @click="page--; refresh()">上一页</button>
<span> {{ page + 1 }} </span>
<button class="btn" :disabled="busy || packages.length < 20" @click="page++; refresh()">下一页</button>
</nav>
<AppDialog v-if="selected" label="桌面安装预览" @close="close">
<h2>{{ selected.package_id }} · {{ selected.version }}</h2>
<p v-if="!workspace.vaultId">请先打开要使用此包的笔记库</p>
@@ -88,9 +99,16 @@ onBeforeUnmount(() => ++generation)
</template>
<style scoped>
.desktop-packages { margin-block: var(--space-xl); }
li { margin-block: var(--space-md); overflow-wrap: anywhere; }
.desktop-packages { display: grid; gap: var(--space-lg); margin-block: var(--space-xl); padding: var(--space-xl); border: 1px solid var(--color-border-default); border-radius: var(--radius-lg); background: var(--color-surface-primary); }
.section-heading { display: flex; align-items: start; justify-content: space-between; gap: var(--space-lg); }
.section-heading h2, .section-heading p, .item-card p { margin: 0; }
.section-heading p, .item-card p, .empty-state span { margin-top: var(--space-xs); color: var(--color-text-secondary); }
.package-list { display: grid; gap: var(--space-md); margin: 0; padding: 0; list-style: none; }
.item-card { display: flex; align-items: center; justify-content: space-between; gap: var(--space-lg); padding: var(--space-lg); color: var(--color-text-primary); background: var(--color-background-secondary); border: 1px solid var(--color-border-subtle); border-radius: var(--radius-md); overflow-wrap: anywhere; }
.empty-state { min-height: 120px; }
.pagination { display: flex; align-items: center; gap: var(--space-sm); color: var(--color-text-secondary); }
label { display: grid; gap: var(--space-xs); }
textarea { width: 100%; color: var(--color-text-primary); background: var(--color-background-secondary); }
pre { white-space: pre-wrap; overflow-wrap: anywhere; }
@media (max-width: 700px) { .section-heading, .item-card { align-items: stretch; flex-direction: column; } }
</style>
@@ -84,6 +84,7 @@ y = x^2 / 8
<span class="step">03</span>
<h2>{{ t('可用数学语法', 'Supported math syntax') }}</h2>
<p>{{ t('支持 +、-、*、/、^,变量 x,常量 pi、e,以及以下单参数函数。2x、2(x+1) 等隐式乘法也可使用。', 'Use +, -, *, /, ^, variable x, constants pi and e, and the single-argument functions below. Implicit multiplication such as 2x and 2(x+1) is also supported.') }}</p>
<p>{{ t('图例会自动使用 LaTeX 数学排版:幂显示为上标,除法显示为分式,函数名使用数学字体。', 'Legends are typeset automatically with LaTeX: powers use superscripts, division uses fractions, and function names use mathematical typography.') }}</p>
<div class="tag-list function-list">
<code v-for="name in ['sin', 'cos', 'tan', 'asin', 'acos', 'atan', 'sinh', 'cosh', 'tanh', 'exp', 'log', 'ln', 'log10', 'log2', 'sqrt', 'abs']" :key="name">{{ name }}(x)</code>
</div>