feat: 插件检验器 plugin:check + L6/L7 教程 + 模板接入组件库——性能与样式标准成为机器关卡

This commit is contained in:
Rick Ye
2026-09-11 10:11:55 +08:00
parent f77942ee2b
commit 835b910d42
12 changed files with 471 additions and 30 deletions
+53 -11
View File
@@ -3,23 +3,65 @@
* esbuild src/renderer.tsx --bundle --format=esm --outfile=renderer.mjs \
* --jsx=automatic --alias:react=./shims/react.js --alias:react/jsx-runtime=./shims/jsx-runtime.js
* 产物 renderer.mjs 是自包含单文件,放回插件目录即被宿主装载。
* 样式类名只用 DESIGN.md 里的宿主词汇。
*
* UI 一律用 @aso/plugin-sdk/ui 公共组件:token 与宿主同源、深浅色自动跟随,
* 等待/失败/空态统一呈现(规范见 docs/plugins/07-ui-components.md 与 DESIGN.md)。
* 一切异步操作都要有超时(withTimeout/withRetry 见 '@aso/plugin-sdk'),
* 区块三态:LoadingState → ErrorState(可重试) / EmptyState / 数据。
*/
import { useState } from 'react'
import { useEffect, useState } from 'react'
import { withTimeout } from '@aso/plugin-sdk'
import { Button, EmptyState, ErrorState, LoadingState, SettingRow, Switch } from '@aso/plugin-sdk/ui'
/** 模拟异步数据源:换成真实 IPC / 请求时保持「必超时、必 catch」的形状 */
function fetchItems(): Promise<string[]> {
return withTimeout(Promise.resolve(['条目一', '条目二', '条目三']), 3000, '拉取数据')
}
export default function mount(ctx) {
function Panel() {
const [count, setCount] = useState(0)
const [items, setItems] = useState(null) // null=加载中
const [error, setError] = useState(null)
const [verbose, setVerbose] = useState(false)
const [tick, setTick] = useState(0)
useEffect(() => {
let alive = true
setItems(null)
setError(null)
fetchItems()
.then((data) => {
if (alive) setItems(data)
})
.catch((err) => {
if (alive) setError(err instanceof Error ? err.message : String(err))
})
return () => {
alive = false
}
}, [tick])
return (
<div className="flex h-full flex-col gap-2 p-3">
<div className="flex h-full flex-col gap-2 overflow-y-auto p-3">
<div className="text-[12.5px] text-ink">我的插件面板</div>
<div className="text-[12px] text-ink-3">计数:{count}</div>
<button
className="self-start rounded-control border border-line-strong px-2.5 py-1 text-[11.5px] text-ink hover:bg-hover"
onClick={() => setCount((c) => c + 1)}
>
+1
</button>
{error !== null ? (
<ErrorState title="数据加载失败" description={error} onRetry={() => setTick((t) => t + 1)} />
) : items === null ? (
<LoadingState label="正在拉取数据…" />
) : items.length === 0 ? (
<EmptyState title="暂无数据" description="检查数据源后点击重新加载" />
) : (
items.map((it) => <SettingRow key={it} label={it} description="示例条目" />)
)}
<SettingRow label="详细模式" description="开启后 main 侧输出更详细的日志">
<Switch checked={verbose} onCheckedChange={setVerbose} aria-label="详细模式" />
</SettingRow>
<Button variant="ghost" size="sm" className="self-start" onClick={() => setTick((t) => t + 1)}>
重新加载
</Button>
</div>
)
}