@snap-rail/framework-shadcn 0.0.0-stage → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -2
- package/docs/framework-authoring.md +59 -0
- package/lib/fallback.js +307 -0
- package/lib/index.js +3036 -0
- package/lib/runtime.js +2827 -0
- package/lib/semantic.js +96 -0
- package/lib/session.js +48 -0
- package/lib/settings-pages.js +68 -0
- package/lib/slots.js +95 -0
- package/lib/types/Shell.d.ts +13 -0
- package/lib/types/Shell.js +17 -0
- package/lib/types/client.d.ts +41 -0
- package/lib/types/client.js +39 -0
- package/lib/types/fallback.d.ts +15 -0
- package/lib/types/fallback.js +52 -0
- package/lib/types/index.d.ts +17 -0
- package/lib/types/index.js +17 -0
- package/lib/types/runtime.d.ts +53 -0
- package/lib/types/runtime.js +75 -0
- package/lib/types/semantic.d.ts +55 -0
- package/lib/types/semantic.js +63 -0
- package/lib/types/session.d.ts +43 -0
- package/lib/types/session.js +76 -0
- package/lib/types/settings-dialog.d.ts +17 -0
- package/lib/types/settings-dialog.js +138 -0
- package/lib/types/settings-pages.d.ts +68 -0
- package/lib/types/settings-pages.js +85 -0
- package/lib/types/settings-plugins-page.d.ts +29 -0
- package/lib/types/settings-plugins-page.js +466 -0
- package/lib/types/settings-update-page.d.ts +17 -0
- package/lib/types/settings-update-page.js +124 -0
- package/lib/types/slots.d.ts +114 -0
- package/lib/types/slots.js +119 -0
- package/lib/types/ui/badge.d.ts +17 -0
- package/lib/types/ui/badge.js +29 -0
- package/lib/types/ui/button.d.ts +20 -0
- package/lib/types/ui/button.js +39 -0
- package/lib/types/ui/card.d.ts +14 -0
- package/lib/types/ui/card.js +18 -0
- package/lib/types/ui/checkbox.d.ts +10 -0
- package/lib/types/ui/checkbox.js +13 -0
- package/lib/types/ui/collapsible.d.ts +24 -0
- package/lib/types/ui/collapsible.js +31 -0
- package/lib/types/ui/dialog.d.ts +22 -0
- package/lib/types/ui/dialog.js +29 -0
- package/lib/types/ui/drag-scroll.d.ts +13 -0
- package/lib/types/ui/drag-scroll.js +85 -0
- package/lib/types/ui/hooks.d.ts +16 -0
- package/lib/types/ui/hooks.js +26 -0
- package/lib/types/ui/icons.d.ts +21 -0
- package/lib/types/ui/icons.js +18 -0
- package/lib/types/ui/index.d.ts +32 -0
- package/lib/types/ui/index.js +32 -0
- package/lib/types/ui/input.d.ts +8 -0
- package/lib/types/ui/input.js +6 -0
- package/lib/types/ui/label.d.ts +9 -0
- package/lib/types/ui/label.js +12 -0
- package/lib/types/ui/led.d.ts +13 -0
- package/lib/types/ui/led.js +11 -0
- package/lib/types/ui/number-input.d.ts +35 -0
- package/lib/types/ui/number-input.js +48 -0
- package/lib/types/ui/progress.d.ts +10 -0
- package/lib/types/ui/progress.js +14 -0
- package/lib/types/ui/radio-group.d.ts +12 -0
- package/lib/types/ui/radio-group.js +17 -0
- package/lib/types/ui/schema-form.d.ts +51 -0
- package/lib/types/ui/schema-form.js +124 -0
- package/lib/types/ui/scroll-area.d.ts +10 -0
- package/lib/types/ui/scroll-area.js +13 -0
- package/lib/types/ui/switch.d.ts +10 -0
- package/lib/types/ui/switch.js +13 -0
- package/lib/types/ui/table.d.ts +20 -0
- package/lib/types/ui/table.js +28 -0
- package/lib/types/ui/tabs.d.ts +16 -0
- package/lib/types/ui/tabs.js +23 -0
- package/lib/types/ui/textarea.d.ts +10 -0
- package/lib/types/ui/textarea.js +6 -0
- package/lib/types/ui/theme.d.ts +30 -0
- package/lib/types/ui/theme.js +33 -0
- package/lib/types/ui/tooltip.d.ts +23 -0
- package/lib/types/ui/tooltip.js +15 -0
- package/lib/types/ui/touch-select.d.ts +29 -0
- package/lib/types/ui/touch-select.js +37 -0
- package/lib/types/ui/utils.d.ts +13 -0
- package/lib/types/ui/utils.js +16 -0
- package/lib/types/variables.d.ts +168 -0
- package/lib/types/variables.js +282 -0
- package/lib/types/workflows.d.ts +98 -0
- package/lib/types/workflows.js +95 -0
- package/lib/ui/index.js +1101 -0
- package/lib/ui/theme.css +271 -0
- package/lib/variables.js +260 -0
- package/lib/workflows.js +85 -0
- package/package.json +138 -3
- package/src/ui/theme.css +271 -0
- package/templates/ui-extension/README.md +17 -0
- package/templates/ui-view/README.md +17 -0
package/README.md
CHANGED
|
@@ -1,3 +1,21 @@
|
|
|
1
|
-
#
|
|
1
|
+
# snap-rail-framework-shadcn
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
界面框架插件(clientOnly、单活 `snapRail.exclusive: 'framework'`):槽位词汇、
|
|
4
|
+
renderer runtime + Shell、shadcn 风格共享原语、主题令牌(预编译 tailwind 输出
|
|
5
|
+
随 zip 内联)、settings/session/variables/workflows 服务与 fallback 布局。
|
|
6
|
+
|
|
7
|
+
组件插件经 `requires: ['@snap-rail/framework-shadcn@^0.1']` 绑定本框架(绑定即依赖);框架不匹配装不上。
|
|
8
|
+
构建链:tsc(lib/types)→ tailwind(lib-client/theme.css)→ tsdown(lib 面 +
|
|
9
|
+
clientBundle 工厂包)。
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
pnpm install # @snap-rail/* 来自 Gitea npm registry(见 .npmrc)
|
|
13
|
+
pnpm build # tsc + tailwind + tsdown
|
|
14
|
+
pnpm test # 槽位/主题/原语/变量/工作流/fallback/binding(happy-dom)
|
|
15
|
+
pnpm pack # snap-rail-plugin pack . --out dist(本地)
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
发版:打 `v*` tag → CI 构建、测试、签名打包并上传到本仓 Release。
|
|
19
|
+
|
|
20
|
+
注:跨套件的集成用例(引导整套件页面的 boot)随套件仓走——框架仓只测
|
|
21
|
+
框架自身与通用住户缝。
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# 框架作者指南
|
|
2
|
+
|
|
3
|
+
snap-rail 的 UI 框架是**普通插件**:zip 发行、互斥组 `framework` 单活、
|
|
4
|
+
clientOnly。任何满足这份契约的包都能与官方框架平权竞争——换框架 = 换生态
|
|
5
|
+
是双向的。本指南是一个框架包要对外承诺的全部面。
|
|
6
|
+
|
|
7
|
+
## 一、框架包 = 一包一体两面
|
|
8
|
+
|
|
9
|
+
- **运行时(终端 zip)**:clientOnly,client 入口默认导出
|
|
10
|
+
`{ seeds: { ui, slots, semantic, settings-pages?, session?, workflows?,
|
|
11
|
+
variables?, runtime, fallback? } }`——壳 require 后逐 id
|
|
12
|
+
`system.seed('<包>/<key>', 实例)`,所有 Component 与你共享同一份实例。
|
|
13
|
+
词汇表是**运行时值**(`register(ctx, IDS.menu, …)` 的参数),所以 SDK
|
|
14
|
+
不是附带类型,就是这份代码。
|
|
15
|
+
- **开发期 SDK(npm 包)**:同一个包的 `exports` 子路径——
|
|
16
|
+
`./slots`(词汇 + 类型)、`./semantic`(view/layout/navigation 贡献
|
|
17
|
+
契约)、`./ui`(原语类型)、`./templates`(脚手架)、`./docs`(本指南
|
|
18
|
+
与 token 参考)。组件作者 devDependency 你的包,构建期拿类型,运行期
|
|
19
|
+
从种子表拿实例。
|
|
20
|
+
|
|
21
|
+
## 二、插槽词汇与占位模式
|
|
22
|
+
|
|
23
|
+
- 词汇用 `as const` 对象 + 字面量联合声明(新框架用
|
|
24
|
+
`@snap-rail/plugin-kit` 的 `defineSlotVocabulary`——定义即校验形状,
|
|
25
|
+
消费者得到自动补全与编译期防错;`custom:<publisher>.<name>` 是保留的
|
|
26
|
+
扩展命名空间)。
|
|
27
|
+
- 每个槽声明占位模式:`stack`(全部住户按 order 渲染——菜单、动作区)
|
|
28
|
+
或 `single`(按 `precedence` 选出唯一胜者——titlebar、main)。single
|
|
29
|
+
槽的内置默认注册 `precedence: 0`,三方覆盖者注册更高值,**卸载覆盖者
|
|
30
|
+
自动回退默认**(默认只是被遮蔽,不被销毁)。
|
|
31
|
+
- 禁止裸字符串槽 id——一律走词汇常量(doctor 会扫)。
|
|
32
|
+
|
|
33
|
+
## 三、作者模式规约(重要)
|
|
34
|
+
|
|
35
|
+
框架包只含:Shell + 槽注册表 + runtime + fallback 兜底。**一切具体住户
|
|
36
|
+
(默认菜单组件、默认 titlebar)都是独立组件插件**——于是「卸掉默认
|
|
37
|
+
titlebar、装一个三方新 titlebar」就是普通插件装卸。fallback 是降解地板
|
|
38
|
+
(零布局时兜底、其他 layout 注册即让位),不算住户。
|
|
39
|
+
|
|
40
|
+
## 四、生态三层
|
|
41
|
+
|
|
42
|
+
1. 框架作者:发 zip(feed)+ 发 npm(SDK 面)。
|
|
43
|
+
2. 组件/扩展库作者:`snap-rail-plugin init --template ui-view|ui-extension
|
|
44
|
+
--framework <你的包>`;manifest 声明 `requires: ['<你的包>@^0.1']`
|
|
45
|
+
(绑定即依赖,启用预检强制;主版本不匹配拒装)与 `client.externalSeeds`
|
|
46
|
+
(require 的贡献种子 id——打包器据此 externalize,运行期共享你的单
|
|
47
|
+
实例;提供方缺席时启用被拒绝)。
|
|
48
|
+
3. 终端用户:装你的框架 + 装任意绑定它的组件,即装即用;装了别的框架,
|
|
49
|
+
绑定不匹配的组件不挂载(fail-loud,双向保护)。
|
|
50
|
+
|
|
51
|
+
## 五、发布检查单
|
|
52
|
+
|
|
53
|
+
- [ ] `snapRail.exclusive: 'framework'`、`clientOnly: true`、
|
|
54
|
+
`client.seeds` 完整
|
|
55
|
+
- [ ] zip 内预编译 tailwind 输出(theme 令牌随 zip)
|
|
56
|
+
- [ ] npm 包 exports 齐全(slots/semantic/ui/templates/docs)
|
|
57
|
+
- [ ] 模板与指南随包(`snap-rail-plugin init --framework <你的包>` 可用)
|
|
58
|
+
- [ ] 版本 semver:种子 id 集或词汇变更 = minor+;破坏性 = major
|
|
59
|
+
(组件的 externalSeeds 声明会因 major 不满足而拒绝启用)
|
package/lib/fallback.js
ADDED
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import "@snap-rail/cordis";
|
|
3
|
+
import { clsx } from "clsx";
|
|
4
|
+
import { twMerge } from "tailwind-merge";
|
|
5
|
+
import { Fragment, useEffect, useState } from "react";
|
|
6
|
+
import { Slot } from "@radix-ui/react-slot";
|
|
7
|
+
import { cva } from "class-variance-authority";
|
|
8
|
+
import "@radix-ui/react-switch";
|
|
9
|
+
import "@radix-ui/react-tooltip";
|
|
10
|
+
import "@radix-ui/react-scroll-area";
|
|
11
|
+
import "@radix-ui/react-dialog";
|
|
12
|
+
import "@radix-ui/react-label";
|
|
13
|
+
import "@radix-ui/react-checkbox";
|
|
14
|
+
import "@radix-ui/react-progress";
|
|
15
|
+
import "@radix-ui/react-radio-group";
|
|
16
|
+
import "@radix-ui/react-tabs";
|
|
17
|
+
import "@radix-ui/react-collapsible";
|
|
18
|
+
import { z } from "zod";
|
|
19
|
+
//#region lib/types/slots.js
|
|
20
|
+
/** Stable semantic slot names shared by every layout implementation. */
|
|
21
|
+
const UI_SLOT_IDS = {
|
|
22
|
+
layout: "shell.layout",
|
|
23
|
+
titlebar: "shell.titlebar",
|
|
24
|
+
titlebarActions: "shell.titlebar.actions",
|
|
25
|
+
menu: "shell.menu",
|
|
26
|
+
sidebar: "shell.sidebar",
|
|
27
|
+
main: "shell.main",
|
|
28
|
+
view: "shell.view",
|
|
29
|
+
statusbar: "shell.statusbar",
|
|
30
|
+
overlay: "shell.overlay"
|
|
31
|
+
};
|
|
32
|
+
new Set(Object.values(UI_SLOT_IDS));
|
|
33
|
+
/**
|
|
34
|
+
* The mode table for the standard slots — the framework owns it.
|
|
35
|
+
*
|
|
36
|
+
* `titlebar` and `main` are `single`: exactly one tenant renders (a skin or
|
|
37
|
+
* replacement displaces the default and falls back on unload). `layout`
|
|
38
|
+
* stays `stack` on purpose — the settings dialog is a permanent overlay
|
|
39
|
+
* occupant beside the active layout, and the fallback shell yields by
|
|
40
|
+
* counting; every other slot collects contributions.
|
|
41
|
+
*/
|
|
42
|
+
const UI_SLOT_MODES = {
|
|
43
|
+
[UI_SLOT_IDS.layout]: "stack",
|
|
44
|
+
[UI_SLOT_IDS.titlebar]: "single",
|
|
45
|
+
[UI_SLOT_IDS.titlebarActions]: "stack",
|
|
46
|
+
[UI_SLOT_IDS.menu]: "stack",
|
|
47
|
+
[UI_SLOT_IDS.sidebar]: "stack",
|
|
48
|
+
[UI_SLOT_IDS.main]: "single",
|
|
49
|
+
[UI_SLOT_IDS.view]: "stack",
|
|
50
|
+
[UI_SLOT_IDS.statusbar]: "stack",
|
|
51
|
+
[UI_SLOT_IDS.overlay]: "stack"
|
|
52
|
+
};
|
|
53
|
+
new Map(Object.entries(UI_SLOT_MODES));
|
|
54
|
+
//#endregion
|
|
55
|
+
//#region lib/types/ui/utils.js
|
|
56
|
+
/**
|
|
57
|
+
* Class-name combiner: clsx joins, tailwind-merge resolves conflicting
|
|
58
|
+
* Tailwind utilities with the last one winning — the shadcn convention.
|
|
59
|
+
*
|
|
60
|
+
* @module @snap-rail/framework-shadcn/ui/utils
|
|
61
|
+
*/
|
|
62
|
+
/**
|
|
63
|
+
* Merge conditional class names into one Tailwind-aware string.
|
|
64
|
+
* @param inputs - class values (strings, arrays, conditional objects).
|
|
65
|
+
* @returns the merged class string.
|
|
66
|
+
*/
|
|
67
|
+
function cn(...inputs) {
|
|
68
|
+
return twMerge(clsx(inputs));
|
|
69
|
+
}
|
|
70
|
+
//#endregion
|
|
71
|
+
//#region lib/types/ui/hooks.js
|
|
72
|
+
/**
|
|
73
|
+
* Shared React recipes over the client runtime's event fabric.
|
|
74
|
+
*
|
|
75
|
+
* @module @snap-rail/framework-shadcn/ui/hooks
|
|
76
|
+
*/
|
|
77
|
+
/**
|
|
78
|
+
* Re-render the component whenever any of the named ctx events fires —
|
|
79
|
+
* the standard "surface follows the seam" tick. The cordis import is
|
|
80
|
+
* type-only: this module stays a pure React utility at runtime.
|
|
81
|
+
*
|
|
82
|
+
* @param ctx - the plugin context whose events drive the refresh.
|
|
83
|
+
* @param events - event names to listen on (identity is not part of the
|
|
84
|
+
* effect key; a freshly built array literal is fine).
|
|
85
|
+
*/
|
|
86
|
+
function useRefresh(ctx, events) {
|
|
87
|
+
const [, setTick] = useState(0);
|
|
88
|
+
const key = events.join("\n");
|
|
89
|
+
useEffect(() => {
|
|
90
|
+
const bump = () => setTick((value) => value + 1);
|
|
91
|
+
const detachers = (key === "" ? [] : key.split("\n")).map((event) => ctx.on(event, bump));
|
|
92
|
+
return () => {
|
|
93
|
+
for (const detach of detachers) detach();
|
|
94
|
+
};
|
|
95
|
+
}, [ctx, key]);
|
|
96
|
+
}
|
|
97
|
+
//#endregion
|
|
98
|
+
//#region lib/types/ui/button.js
|
|
99
|
+
/**
|
|
100
|
+
* Button: cva variants over a native button. Ghost/outline cover chrome
|
|
101
|
+
* surfaces (titlebar, panel toggles); `icon` size squares the padding for
|
|
102
|
+
* glyph-only controls.
|
|
103
|
+
*
|
|
104
|
+
* @module @snap-rail/framework-shadcn/ui/button
|
|
105
|
+
*/
|
|
106
|
+
const buttonVariants = cva("inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-base font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0", {
|
|
107
|
+
variants: {
|
|
108
|
+
variant: {
|
|
109
|
+
default: "bg-primary text-primary-foreground hover:bg-primary/90",
|
|
110
|
+
destructive: "bg-destructive/90 text-destructive-foreground hover:bg-destructive",
|
|
111
|
+
outline: "border border-border bg-transparent hover:bg-accent hover:text-accent-foreground",
|
|
112
|
+
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
|
113
|
+
ghost: "hover:bg-accent hover:text-accent-foreground",
|
|
114
|
+
link: "text-primary underline-offset-4 hover:underline"
|
|
115
|
+
},
|
|
116
|
+
size: {
|
|
117
|
+
xl: "h-16 px-8 text-lg",
|
|
118
|
+
lg: "h-14 px-6 text-lg",
|
|
119
|
+
default: "h-12 px-5 py-1.5",
|
|
120
|
+
sm: "h-10 px-4 text-sm",
|
|
121
|
+
icon: "h-12 w-12"
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
defaultVariants: {
|
|
125
|
+
variant: "default",
|
|
126
|
+
size: "default"
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
/** A themed button; `asChild` renders the child element instead. */
|
|
130
|
+
function Button({ className, variant, size, asChild = false, ...props }) {
|
|
131
|
+
return jsx(asChild ? Slot : "button", {
|
|
132
|
+
className: cn(buttonVariants({
|
|
133
|
+
variant,
|
|
134
|
+
size
|
|
135
|
+
}), className),
|
|
136
|
+
...props
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
cva("inline-flex items-center gap-1 rounded-md border px-2.5 py-1 text-sm font-medium whitespace-nowrap", {
|
|
140
|
+
variants: { variant: {
|
|
141
|
+
default: "border-transparent bg-primary text-primary-foreground",
|
|
142
|
+
secondary: "border-transparent bg-secondary text-secondary-foreground",
|
|
143
|
+
success: "border-transparent bg-success/15 text-success",
|
|
144
|
+
warning: "border-transparent bg-warning/15 text-warning",
|
|
145
|
+
destructive: "border-transparent bg-destructive/15 text-destructive",
|
|
146
|
+
outline: "text-muted-foreground"
|
|
147
|
+
} },
|
|
148
|
+
defaultVariants: { variant: "default" }
|
|
149
|
+
});
|
|
150
|
+
z.object({ mode: z.enum([
|
|
151
|
+
"system",
|
|
152
|
+
"light",
|
|
153
|
+
"dark"
|
|
154
|
+
]) });
|
|
155
|
+
//#endregion
|
|
156
|
+
//#region lib/types/fallback.js
|
|
157
|
+
/** The settings dialog's occupant id (a Dialog portal, not a layout). */
|
|
158
|
+
const SETTINGS_OCCUPANT_ID = "settings-dialog";
|
|
159
|
+
/** Window-control glyphs; inline so the shell needs no icon dependency. */
|
|
160
|
+
function ControlIcons(props) {
|
|
161
|
+
const base = "flex h-10 w-10 items-center justify-center text-muted-foreground transition-colors hover:bg-accent hover:text-accent-foreground";
|
|
162
|
+
return jsxs("div", {
|
|
163
|
+
className: "flex items-center",
|
|
164
|
+
children: [
|
|
165
|
+
jsx("button", {
|
|
166
|
+
type: "button",
|
|
167
|
+
"aria-label": "最小化",
|
|
168
|
+
className: base,
|
|
169
|
+
onClick: props.onMinimize,
|
|
170
|
+
children: jsx("svg", {
|
|
171
|
+
viewBox: "0 0 12 12",
|
|
172
|
+
className: "h-3 w-3",
|
|
173
|
+
fill: "none",
|
|
174
|
+
children: jsx("path", {
|
|
175
|
+
d: "M2 6h8",
|
|
176
|
+
stroke: "currentColor",
|
|
177
|
+
strokeWidth: "1.4"
|
|
178
|
+
})
|
|
179
|
+
})
|
|
180
|
+
}),
|
|
181
|
+
jsx("button", {
|
|
182
|
+
type: "button",
|
|
183
|
+
"aria-label": "最大化",
|
|
184
|
+
className: base,
|
|
185
|
+
onClick: props.onToggleMaximize,
|
|
186
|
+
children: jsx("svg", {
|
|
187
|
+
viewBox: "0 0 12 12",
|
|
188
|
+
className: "h-3 w-3",
|
|
189
|
+
fill: "none",
|
|
190
|
+
children: jsx("rect", {
|
|
191
|
+
x: "2.5",
|
|
192
|
+
y: "2.5",
|
|
193
|
+
width: "7",
|
|
194
|
+
height: "7",
|
|
195
|
+
stroke: "currentColor",
|
|
196
|
+
strokeWidth: "1.2"
|
|
197
|
+
})
|
|
198
|
+
})
|
|
199
|
+
}),
|
|
200
|
+
jsx("button", {
|
|
201
|
+
type: "button",
|
|
202
|
+
"aria-label": "关闭",
|
|
203
|
+
className: `${base} hover:bg-destructive hover:text-white`,
|
|
204
|
+
onClick: props.onClose,
|
|
205
|
+
children: jsx("svg", {
|
|
206
|
+
viewBox: "0 0 12 12",
|
|
207
|
+
className: "h-3 w-3",
|
|
208
|
+
fill: "none",
|
|
209
|
+
children: jsx("path", {
|
|
210
|
+
d: "m3 3 6 6M9 3l-6 6",
|
|
211
|
+
stroke: "currentColor",
|
|
212
|
+
strokeWidth: "1.4"
|
|
213
|
+
})
|
|
214
|
+
})
|
|
215
|
+
})
|
|
216
|
+
]
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
/** The fallback body: minimal titlebar + install guidance. */
|
|
220
|
+
function FallbackShell(props) {
|
|
221
|
+
useRefresh(props.ctx, ["ui/slot-changed", "settings/open-changed"]);
|
|
222
|
+
if (props.ctx.uiSlots.list(UI_SLOT_IDS.layout).filter((occupant) => occupant.id !== SETTINGS_OCCUPANT_ID).length > 1) return null;
|
|
223
|
+
const windowControl = (action) => {
|
|
224
|
+
props.link.call("window.control", { action }).catch(() => void 0);
|
|
225
|
+
};
|
|
226
|
+
return jsxs("div", {
|
|
227
|
+
className: "flex h-full flex-col",
|
|
228
|
+
"data-region": "fallback-shell",
|
|
229
|
+
children: [jsxs("div", {
|
|
230
|
+
className: "flex h-10 shrink-0 items-center justify-between border-b border-border pl-4 select-none",
|
|
231
|
+
style: { WebkitAppRegion: "drag" },
|
|
232
|
+
children: [jsx("span", {
|
|
233
|
+
className: "text-sm text-muted-foreground",
|
|
234
|
+
children: "snap-rail"
|
|
235
|
+
}), jsxs("div", {
|
|
236
|
+
className: "flex items-center",
|
|
237
|
+
style: { WebkitAppRegion: "no-drag" },
|
|
238
|
+
children: [
|
|
239
|
+
props.ctx.uiSlots.list(UI_SLOT_IDS.titlebarActions).map((occupant) => jsx(Fragment, { children: occupant.render() }, occupant.id)),
|
|
240
|
+
jsx("button", {
|
|
241
|
+
type: "button",
|
|
242
|
+
"aria-label": "设置",
|
|
243
|
+
className: "flex h-10 w-10 items-center justify-center text-muted-foreground transition-colors hover:bg-accent hover:text-accent-foreground",
|
|
244
|
+
onClick: () => props.ctx.settingsPages.open(),
|
|
245
|
+
children: jsxs("svg", {
|
|
246
|
+
viewBox: "0 0 24 24",
|
|
247
|
+
className: "h-5 w-5",
|
|
248
|
+
fill: "none",
|
|
249
|
+
stroke: "currentColor",
|
|
250
|
+
strokeWidth: "1.6",
|
|
251
|
+
children: [jsx("circle", {
|
|
252
|
+
cx: "12",
|
|
253
|
+
cy: "12",
|
|
254
|
+
r: "3.2"
|
|
255
|
+
}), jsx("path", { d: "M19.4 15a1.7 1.7 0 0 0 .34 1.87l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.7 1.7 0 0 0-1.87-.34 1.7 1.7 0 0 0-1 1.55V21a2 2 0 1 1-4 0v-.09a1.7 1.7 0 0 0-1-1.55 1.7 1.7 0 0 0-1.87.34l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06a1.7 1.7 0 0 0 .34-1.87 1.7 1.7 0 0 0-1.55-1H3a2 2 0 1 1 0-4h.09a1.7 1.7 0 0 0 1.55-1 1.7 1.7 0 0 0-.34-1.87l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06a1.7 1.7 0 0 0 1.87.34h0a1.7 1.7 0 0 0 1-1.55V3a2 2 0 1 1 4 0v.09a1.7 1.7 0 0 0 1 1.55h0a1.7 1.7 0 0 0 1.87-.34l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06a1.7 1.7 0 0 0-.34 1.87v0a1.7 1.7 0 0 0 1.55 1H21a2 2 0 1 1 0 4h-.09a1.7 1.7 0 0 0-1.55 1Z" })]
|
|
256
|
+
})
|
|
257
|
+
}),
|
|
258
|
+
jsx(ControlIcons, {
|
|
259
|
+
onMinimize: () => windowControl("minimize"),
|
|
260
|
+
onToggleMaximize: () => windowControl("toggle-maximize"),
|
|
261
|
+
onClose: () => windowControl("close")
|
|
262
|
+
})
|
|
263
|
+
]
|
|
264
|
+
})]
|
|
265
|
+
}), jsx("div", {
|
|
266
|
+
className: "flex min-h-0 flex-1 items-center justify-center p-6",
|
|
267
|
+
children: jsxs("div", {
|
|
268
|
+
className: "grid max-w-md gap-4 text-center",
|
|
269
|
+
children: [
|
|
270
|
+
jsx("div", {
|
|
271
|
+
className: "text-xl font-medium text-foreground",
|
|
272
|
+
children: "尚未激活业务套件"
|
|
273
|
+
}),
|
|
274
|
+
jsx("p", {
|
|
275
|
+
className: "text-sm text-muted-foreground",
|
|
276
|
+
children: "打开设置的「插件管理」页,安装并激活一个业务套件;通讯驱动也在同一处安装。"
|
|
277
|
+
}),
|
|
278
|
+
jsx("div", { children: jsx(Button, {
|
|
279
|
+
"data-fallback-open-settings": true,
|
|
280
|
+
onClick: () => props.ctx.settingsPages.open(),
|
|
281
|
+
children: "打开设置"
|
|
282
|
+
}) })
|
|
283
|
+
]
|
|
284
|
+
})
|
|
285
|
+
})]
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
/** The fallback shell occupant. */
|
|
289
|
+
const fallbackShellPlugin = {
|
|
290
|
+
name: "framework fallback",
|
|
291
|
+
inject: ["uiSlots", "settingsPages"],
|
|
292
|
+
apply(ctx) {
|
|
293
|
+
const client = ctx.get("client");
|
|
294
|
+
ctx.uiSlots.register(ctx, UI_SLOT_IDS.layout, {
|
|
295
|
+
id: "fallback-shell",
|
|
296
|
+
order: 200,
|
|
297
|
+
render() {
|
|
298
|
+
return jsx(FallbackShell, {
|
|
299
|
+
ctx,
|
|
300
|
+
link: client.link
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
};
|
|
306
|
+
//#endregion
|
|
307
|
+
export { fallbackShellPlugin as default };
|