feat(plugin-sdk): add TypeScript SDK for Infinite Canvas plugins with automatic JSX and build support

This commit is contained in:
HouYunFei
2026-07-15 15:31:48 +08:00
parent 8d3f244524
commit 75aafc115f
82 changed files with 3228 additions and 3288 deletions
@@ -33,7 +33,7 @@ const modelGroups: ModelGroup[] = [
const webdavDomainKeys: AppSyncDomainKey[] = ["canvas", "assets", "image-workbench", "video-workbench"];
const webdavDomainLabels: Record<AppSyncDomainKey, string> = {
canvas: "画布",
assets: "我的素材",
assets: "我的资产",
"image-workbench": "生图工作台",
"video-workbench": "视频创作台",
};
@@ -141,7 +141,7 @@ export function AppConfigPanel({ showDoneButton = false, initialTab = "channels"
try {
const result = await syncAppDataToWebdav(webdav, updateWebdavProgress);
updateWebdavConfig("lastSyncedAt", result.syncedAt);
message.success(`同步完成:${result.projects} 个画布,${result.assets} 个素材,${result.imageLogs + result.videoLogs} 条记录,本次上传 ${result.uploadedFiles} 个文件 ${formatBytes(result.uploadedBytes)}`);
message.success(`同步完成:${result.projects} 个画布,${result.assets} 个资产,${result.imageLogs + result.videoLogs} 条记录,本次上传 ${result.uploadedFiles} 个文件 ${formatBytes(result.uploadedBytes)}`);
} catch (error) {
setWebdavSyncStatus(error instanceof Error ? error.message : "WebDAV 同步失败");
message.error(error instanceof Error ? error.message : "WebDAV 同步失败");
@@ -252,7 +252,7 @@ export function AppConfigPanel({ showDoneButton = false, initialTab = "channels"
<Cloud className="size-4" />
WebDAV 同步
</div>
<div className="mt-1 text-xs text-stone-500">同步画布、我的素材、生成记录和本地媒体文件,不包含 AI API Key;浏览器会直接连接 WebDAV 服务。</div>
<div className="mt-1 text-xs text-stone-500">同步画布、我的资产、生成记录和本地媒体文件,不包含 AI API Key;浏览器会直接连接 WebDAV 服务。</div>
</div>
<div className="text-xs text-stone-500">{webdav.lastSyncedAt ? `上次同步 ${formatWebdavTime(webdav.lastSyncedAt)}` : "尚未同步"}</div>
</div>
@@ -1,5 +1,5 @@
import type { CSSProperties } from "react";
import { BookOpen, Keyboard, Settings2 } from "lucide-react";
import { BookOpen, Keyboard, Puzzle, Settings2 } from "lucide-react";
import { AnimatedThemeToggler } from "@/components/ui/animated-theme-toggler";
import { GitHubLink } from "@/components/layout/github-link";
@@ -14,9 +14,10 @@ type UserStatusActionsProps = {
showConfig?: boolean;
variant?: "default" | "canvas";
onOpenShortcuts?: () => void;
onOpenPlugins?: () => void;
};
export function UserStatusActions({ showConfig = true, variant = "default", onOpenShortcuts }: UserStatusActionsProps) {
export function UserStatusActions({ showConfig = true, variant = "default", onOpenShortcuts, onOpenPlugins }: UserStatusActionsProps) {
const theme = useThemeStore((state) => state.theme);
const setTheme = useThemeStore((state) => state.setTheme);
const openConfigDialog = useConfigStore((state) => state.openConfigDialog);
@@ -29,6 +30,11 @@ export function UserStatusActions({ showConfig = true, variant = "default", onOp
return (
<div className="inline-flex shrink-0 items-center gap-1">
{onOpenPlugins ? (
<button type="button" className={naturalIconClass} style={iconStyle} onClick={onOpenPlugins} aria-label="节点插件" title="节点插件">
<Puzzle className="size-4" />
</button>
) : null}
<a href={DOCS_URL} target="_blank" rel="noopener noreferrer" className={naturalIconClass} style={iconStyle} aria-label="文档" title="文档">
<BookOpen className="size-4" />
</a>