feat: 插件检验器 plugin:check + L6/L7 教程 + 模板接入组件库——性能与样式标准成为机器关卡
This commit is contained in:
@@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user