72 lines
2.8 KiB
TypeScript
72 lines
2.8 KiB
TypeScript
/**
|
|
* 渲染层组件源码(JSX)。构建:
|
|
* 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 是自包含单文件,放回插件目录即被宿主装载。
|
|
*
|
|
* UI 一律用 @aso/plugin-sdk/ui 公共组件:token 与宿主同源、深浅色自动跟随,
|
|
* 等待/失败/空态统一呈现(规范见 docs/plugins/07-ui-components.md 与 DESIGN.md)。
|
|
* 一切异步操作都要有超时(withTimeout/withRetry 见 '@aso/plugin-sdk'),
|
|
* 区块三态:LoadingState → ErrorState(可重试) / EmptyState / 数据。
|
|
*/
|
|
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 [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 overflow-y-auto p-3">
|
|
<div className="text-[12.5px] text-ink">我的插件面板</div>
|
|
|
|
{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>
|
|
)
|
|
}
|
|
|
|
ctx.registerPanel(Panel)
|
|
ctx.registerView(Panel)
|
|
}
|