@kanyun-ai-infra/band-plugin-react 0.0.0-stage → 0.1.2

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.
@@ -0,0 +1,56 @@
1
+ // src/first-delivery.ts
2
+ var FIRST_DELIVERY_LABEL = "\u624B\u52A8\u4EA4\u7B2C\u4E00\u7248";
3
+ var FIRST_DELIVERY_SUBMIT = "\u63D0\u4EA4\u7B2C\u4E00\u7248";
4
+ function firstDeliveryNotStoreNote(typeLabel) {
5
+ return `\u300C${typeLabel}\u300D\u5B58\u653E\u5728\u76EE\u6807\u4ED3\u5E93\u91CC\uFF0C\u4E0D\u80FD\u5728\u8FD9\u91CC\u624B\u52A8\u4EA4\uFF0C\u8BF7\u91CD\u8BD5`;
6
+ }
7
+ function firstDeliveryEntry(failure, can) {
8
+ const fd = failure?.firstDelivery;
9
+ if (!fd) return { draw: false };
10
+ if (fd.state === "not_store")
11
+ return { draw: false, note: firstDeliveryNotStoreNote(fd.nonStoreType ?? "") };
12
+ if (!can.submitChange) return { draw: false };
13
+ if (fd.state === "blocked")
14
+ return { draw: true, disabled: true, title: fd.blocked ?? "\u4E0A\u6E38\u8FD8\u6CA1\u6279\u51C6\uFF0C\u4E0D\u80FD\u624B\u52A8\u4EA4\u7B2C\u4E00\u7248" };
15
+ return { draw: true, disabled: false, title: "\u81EA\u5DF1\u4EA4\u4E00\u4EFD\u8FD9\u4E2A\u9636\u6BB5\u7684\u4EA7\u7269\uFF0C\u4E0D\u518D\u7B49\u5B83\u8DD1\u5B8C" };
16
+ }
17
+ function firstDeliverySlots(fd) {
18
+ return fd.expected.flatMap(
19
+ (x) => x.keys && x.keys.length > 0 ? x.keys.map((key) => ({
20
+ id: `${x.type}\0${key}`,
21
+ type: x.type,
22
+ key,
23
+ label: `${x.label} \xB7 ${key}`
24
+ })) : [{ id: x.type, type: x.type, label: x.label }]
25
+ );
26
+ }
27
+ function firstDeliveryMissing(slots, filled) {
28
+ return slots.filter((s) => !filled.has(s.id));
29
+ }
30
+ function firstDeliveryMissingText(missing) {
31
+ return missing.length === 0 ? "" : `\u8FD8\u7F3A\uFF1A${missing.map((m) => m.label).join("\u3001")}`;
32
+ }
33
+ var FIRST_DELIVERY_PICK = "\u9009\u62E9\u6587\u4EF6";
34
+ var FIRST_DELIVERY_REMOVE = "\u79FB\u9664";
35
+ var FIRST_DELIVERY_REPLACED = "\u5DF2\u66FF\u6362";
36
+ var firstDeliveryFilesText = (n) => `${n} \u4E2A\u6587\u4EF6`;
37
+ function fileSizeText(bytes) {
38
+ if (bytes < 1024) return `${bytes} B`;
39
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
40
+ return `${(bytes / 1024 / 1024).toFixed(1)} MB`;
41
+ }
42
+
43
+ export {
44
+ FIRST_DELIVERY_LABEL,
45
+ FIRST_DELIVERY_SUBMIT,
46
+ firstDeliveryNotStoreNote,
47
+ firstDeliveryEntry,
48
+ firstDeliverySlots,
49
+ firstDeliveryMissing,
50
+ firstDeliveryMissingText,
51
+ FIRST_DELIVERY_PICK,
52
+ FIRST_DELIVERY_REMOVE,
53
+ FIRST_DELIVERY_REPLACED,
54
+ firstDeliveryFilesText,
55
+ fileSizeText
56
+ };
@@ -0,0 +1,46 @@
1
+ // src/scroll.ts
2
+ function scrollIntoPanel(el, {
3
+ block = "nearest",
4
+ behavior = "auto",
5
+ page = false
6
+ } = {}) {
7
+ const doc = el.ownerDocument;
8
+ const view = doc.defaultView;
9
+ const r = el.getBoundingClientRect();
10
+ if (r.width === 0 && r.height === 0) return;
11
+ let moved = 0;
12
+ for (const box of scrollersOf(el, page)) {
13
+ const rTop = r.top - moved;
14
+ const whole = box === (doc.scrollingElement ?? doc.documentElement);
15
+ const top = whole ? 0 : box.getBoundingClientRect().top + box.clientTop;
16
+ const height = whole ? view?.innerHeight ?? box.clientHeight : box.clientHeight;
17
+ let delta = 0;
18
+ if (block === "start") delta = rTop - top;
19
+ else if (rTop < top) delta = rTop - top;
20
+ else if (rTop + r.height > top + height)
21
+ delta = Math.min(rTop + r.height - (top + height), rTop - top);
22
+ const max = box.scrollHeight - box.clientHeight;
23
+ const to = Math.max(0, Math.min(box.scrollTop + delta, max));
24
+ if (Math.abs(to - box.scrollTop) >= 1) {
25
+ moved += to - box.scrollTop;
26
+ if (typeof box.scrollTo === "function") box.scrollTo({ top: to, behavior });
27
+ else box.scrollTop = to;
28
+ }
29
+ if (!page) return;
30
+ }
31
+ }
32
+ function scrollersOf(el, page) {
33
+ const doc = el.ownerDocument;
34
+ const out = [];
35
+ for (let p = el.parentElement; p && p !== doc.body && p !== doc.documentElement; p = p.parentElement) {
36
+ const y = getComputedStyle(p).overflowY;
37
+ if ((y === "auto" || y === "scroll") && p.scrollHeight > p.clientHeight) out.push(p);
38
+ }
39
+ const whole = doc.scrollingElement ?? doc.documentElement;
40
+ if (page && whole && whole.scrollHeight > whole.clientHeight) out.push(whole);
41
+ return out;
42
+ }
43
+
44
+ export {
45
+ scrollIntoPanel
46
+ };
@@ -0,0 +1,107 @@
1
+ import {
2
+ failedBecause,
3
+ isForbidden
4
+ } from "./chunk-6VMUZ4PQ.js";
5
+
6
+ // src/upgrade-text.ts
7
+ import { LOCKED_ROLE_LABELS } from "@kanyun-ai-infra/band-contracts";
8
+ var labels = LOCKED_ROLE_LABELS;
9
+ function upgradeRoleLabel(role) {
10
+ return Object.hasOwn(labels, role) ? labels[role] : role;
11
+ }
12
+ function roleList(roles) {
13
+ return roles.map(upgradeRoleLabel).join(" / ");
14
+ }
15
+ function projectName(p, index) {
16
+ return p.name ?? `\u9879\u76EE ${index + 1}`;
17
+ }
18
+ function namedProjects(projects, total = projects.length) {
19
+ if (total === 0 || projects.length !== total || projects.some((p) => p.name === void 0))
20
+ return "";
21
+ const shown = projects.slice(0, 3).map((p, i) => projectName(p, i)).join("\u3001");
22
+ return total > 3 ? `\uFF1A${shown} \u7B49 ${total} \u4E2A` : `\uFF1A${shown}`;
23
+ }
24
+ var UPGRADE_TEXT = {
25
+ /** 落后但兼容:只提示。 */
26
+ // 不写「升级后生效」:升完页面自动刷新,用户不用再点一次(note 决定第 6 条)。值保持字符串,钉着旧版的插件还按字符串读它。
27
+ outdated: "\u9879\u76EE\u6709\u65B0\u7248\u672C\u53EF\u7528\u3002\u5347\u7EA7\u540E\u9875\u9762\u4F1A\u81EA\u52A8\u5237\u65B0\u3002",
28
+ /** 不兼容:常显,写被挡。 */
29
+ incompatible: (roles) => `\u9700\u8981\u5347\u7EA7\u624D\u80FD\u7EE7\u7EED\u7528${roleList(roles)}`,
30
+ /** 部署回滚后,钉住的比本部署新。 */
31
+ rolledBack: "\u672C\u90E8\u7F72\u5DF2\u56DE\u9000\uFF0C\u5347\u7EA7\u4F1A\u6362\u56DE\u672C\u90E8\u7F72\u7684\u7248\u672C",
32
+ askAdmin: "\u8BF7\u9879\u76EE\u7BA1\u7406\u5458\u5347\u7EA7",
33
+ button: "\u5347\u7EA7",
34
+ busy: "\u6B63\u5728\u5347\u7EA7\uFF0C\u5B8C\u6210\u540E\u9875\u9762\u4F1A\u81EA\u52A8\u5237\u65B0",
35
+ /** 按钮上的短词(`busy` 是整句,给横幅 / 读屏)。 */
36
+ busyShort: "\u6B63\u5728\u5347\u7EA7\u2026",
37
+ upgraded: "\u5347\u7EA7\u6210\u529F\uFF0C\u6B63\u5728\u5237\u65B0\u9875\u9762\u2026",
38
+ /** 别人升级了、打开着的页面先落盘草稿再刷新。 */
39
+ refreshingOthers: "\u8FD9\u4E2A\u9879\u76EE\u521A\u5347\u7EA7\u4E86\uFF0C\u6B63\u5728\u4FDD\u5B58\u4F60\u7684\u8349\u7A3F\u5E76\u5237\u65B0\u9875\u9762\u2026",
40
+ /** 已登记的草稿或未保存内容阻止刷新时显示;旧项目首次升级前的旧组件可能尚未登记。`own` 是自己点的升级。 */
41
+ refreshStuck: (own) => `${own ? "\u5347\u7EA7\u6210\u529F\u4E86" : "\u8FD9\u4E2A\u9879\u76EE\u521A\u5347\u7EA7\u4E86"}\uFF0C\u4F46\u4F60\u6709\u5185\u5BB9\u6CA1\u80FD\u4FDD\u5B58\uFF0C\u6240\u4EE5\u9875\u9762\u6CA1\u6709\u5237\u65B0\u3002\u8BF7\u5148\u590D\u5236\u6216\u63D0\u4EA4\u5185\u5BB9\uFF0C\u518D\u70B9\u300C\u5237\u65B0\u300D\u3002`,
42
+ alreadyLatest: "\u5DF2\u7ECF\u662F\u6700\u65B0",
43
+ /** 实例钉点的阶段结构与当前版本不同,留在原版本;`n` 是几个实例。 */
44
+ keptInstances: (n) => `${n} \u4E2A\u6D41\u7A0B\u5B9E\u4F8B\u7684\u9636\u6BB5\u7ED3\u6784\u4E0E\u5F53\u524D\u7248\u672C\u4E0D\u540C\uFF0C\u7559\u5728\u539F\u7248\u672C`,
45
+ /** 升级检查没过:哪个钉点、什么原因。 */
46
+ checkFailed: (failures) => `\u5347\u7EA7\u6CA1\u505A\u6210\uFF1A${failures.map((f) => `${f.pin}\uFF08${f.problems.length > 0 ? f.problems.join("\uFF1B") : "\u68C0\u67E5\u6CA1\u8FC7"}\uFF09`).join("\uFF1B")}`,
47
+ noPermission: "\u6CA1\u6709\u5347\u7EA7\u7684\u6743\u9650",
48
+ /** 工坊页。 */
49
+ workshop: {
50
+ /** 落后的项目数:协作功能或本工坊自带的页面功能(工坊重新发布换了自己的插件)有新版本。 */
51
+ outdatedProjects: (n) => `${n} \u4E2A\u9879\u76EE\u6709\u65B0\u7248\u672C\u53EF\u7528`,
52
+ incompatibleProjects: (n) => `${n} \u4E2A\u9879\u76EE\u8981\u66F4\u65B0\u534F\u4F5C\u529F\u80FD\uFF0C\u624D\u80FD\u7EE7\u7EED\u63D0\u610F\u89C1\u3001\u6279\u51C6`,
53
+ button: "\u66F4\u65B0\u672C\u5DE5\u574A\u5168\u90E8\u9879\u76EE",
54
+ currentOutdated: "\u5E73\u53F0\u66F4\u65B0\u4E86\u534F\u4F5C\u529F\u80FD\uFF0C\u91CD\u65B0\u53D1\u5E03\u540E\uFF0C\u65B0\u9879\u76EE\u624D\u4F1A\u7528\u4E0A",
55
+ /** 副本里自带的插件和原版某个共享版本内容一致:可以改成引用,之后跟着原版升级。只提示,不挡任何操作。 */
56
+ referenceHint: {
57
+ title: (n) => `${n} \u4E2A\u63D2\u4EF6\u53EF\u4EE5\u6539\u4E3A\u5F15\u7528`,
58
+ /** `spec` 是该写进 `views.plugins` 的引用,如 `@teaching/wall@^1.0.0`。 */
59
+ row: (name, version) => `${name} \u548C\u539F\u7248\u7684 ${version} \u5185\u5BB9\u4E00\u81F4`,
60
+ how: (specs) => `\u628A\u5DE5\u574A\u4ED3\u5E93 band/workshop.yaml \u91CC views.plugins \u7684\u5BF9\u5E94\u6761\u76EE\u6362\u6210 ${specs.join("\u3001")}\uFF0C\u518D\u53D1\u5E03\u65B0\u7248\u3002\u4E4B\u540E\u6BCF\u6B21\u53D1\u5E03\uFF0C\u90FD\u4F1A\u53D6\u5230\u539F\u7248\u533A\u95F4\u5185\u6700\u65B0\u7684\u517C\u5BB9\u7248\u672C\uFF1B\u4E0D\u60F3\u6539\u5C31\u4FDD\u6301\u73B0\u72B6\u3002`
61
+ },
62
+ /** 点按钮后的确认弹窗:改哪些项目、什么不变、哪些不动、能不能撤回,都说清楚。 */
63
+ confirm: {
64
+ title: "\u66F4\u65B0\u672C\u5DE5\u574A\u5168\u90E8\u9879\u76EE",
65
+ /** 会有变化的项目;名字都有时点名。 */
66
+ willUpdate: (projects) => `\u5C06\u66F4\u65B0 ${projects.length} \u4E2A\u9879\u76EE${namedProjects(projects)}\u3002`,
67
+ unchanged: "\u5B83\u4EEC\u7528\u7684\u8BC4\u8BBA\u3001\u6279\u51C6\u3001\u961F\u5217\u3001\u7248\u672C\u5207\u6362\u548C\u672C\u5DE5\u574A\u81EA\u5E26\u7684\u9875\u9762\u529F\u80FD\u4F1A\u6362\u6210\u6700\u65B0\u7248\uFF1B\u9879\u76EE\u91CC\u7684\u4EA7\u7269\u3001\u610F\u89C1\u548C\u6210\u5458\u90FD\u4E0D\u53D8\u3002",
68
+ // 键名沿用(钉着旧版的插件还读它),内容换成现在的事实:升级不再逐个检查,换完正在看的页面自动刷新
69
+ checks: "\u5347\u7EA7\u540E\u9875\u9762\u4F1A\u81EA\u52A8\u5237\u65B0\uFF0C\u804A\u5929\u6846\u8349\u7A3F\u5148\u4FDD\u5B58\u3002\u5DF2\u5347\u7EA7\u5230\u542B\u672A\u4FDD\u5B58\u5185\u5BB9\u4FDD\u62A4\u7248\u672C\u7684\u9879\u76EE\uFF0C\u5199\u7740\u6CA1\u6CD5\u4FDD\u5B58\u7684\u5185\u5BB9\u65F6\u9875\u9762\u4E0D\u4F1A\u5237\u65B0\uFF0C\u4F1A\u63D0\u793A\u5148\u590D\u5236\u6216\u63D0\u4EA4\uFF0C\u518D\u70B9\u300C\u5237\u65B0\u300D\u3002\u65E7\u9879\u76EE\u9996\u6B21\u5347\u7EA7\u5230\u8BE5\u7248\u672C\u524D\uFF0C\u8BF7\u5148\u590D\u5236\u6216\u63D0\u4EA4\u8BC4\u8BBA\u3001\u610F\u89C1\u6D6E\u5C42\u548C\u9636\u6BB5\u53C2\u6570\u8868\u5355\u91CC\u7684\u5185\u5BB9\u3002",
70
+ archived: (n) => `\u5DF2\u5F52\u6863\u7684 ${n} \u4E2A\u9879\u76EE\u4E0D\u52A8\u3002`,
71
+ /** 工坊里已经是最新、这次不会有变化的那些:写出来,免得「将更新 3 个」和这个工坊的项目数对不上。 */
72
+ alreadyLatest: (n) => `\u5176\u4F59 ${n} \u4E2A\u9879\u76EE\u672C\u6765\u5C31\u662F\u6700\u65B0\u3002`,
73
+ noUndo: "\u66F4\u65B0\u540E\u4E0D\u80FD\u64A4\u56DE\u3002",
74
+ submit: "\u66F4\u65B0",
75
+ busy: "\u6B63\u5728\u66F4\u65B0\u2026"
76
+ },
77
+ /** 结果四类,逐类一句;这一类为零时界面不写。 */
78
+ result: {
79
+ /** `n` 是这一类有几个,`projects` 是有名字的那些(条数对不上时不点名)。 */
80
+ upgraded: (n, projects) => `\u5DF2\u66F4\u65B0 ${n} \u4E2A\u9879\u76EE${namedProjects(projects, n)}\u3002`,
81
+ current: (n) => `${n} \u4E2A\u9879\u76EE\u672C\u6765\u5C31\u662F\u6700\u65B0\u3002`,
82
+ archived: (n) => `${n} \u4E2A\u9879\u76EE\u5DF2\u5F52\u6863\uFF0C\u6CA1\u6709\u66F4\u65B0\u3002`,
83
+ /** 项目钉点换了,但有流程实例的阶段结构与当前版本不同、留在原版本;`n` 是实例数。 */
84
+ keptInstances: (n) => `${n} \u4E2A\u6D41\u7A0B\u5B9E\u4F8B\u7684\u9636\u6BB5\u7ED3\u6784\u4E0E\u5F53\u524D\u7248\u672C\u4E0D\u540C\uFF0C\u7559\u5728\u539F\u7248\u672C\u3002`,
85
+ /** 没更新成的项目逐个点名带原因:检查没过的是哪些问题。 */
86
+ failed: (projects) => `\u6CA1\u66F4\u65B0\u6210 ${projects.length} \u4E2A\u9879\u76EE\uFF1A${projects.map(
87
+ (p, i) => `${projectName(p, i)}\uFF08${p.problems.length > 0 ? p.problems.join("\uFF1B") : "\u68C0\u67E5\u6CA1\u8FC7"}\uFF09`
88
+ ).join("\uFF1B")}\u3002`
89
+ }
90
+ }
91
+ };
92
+ function upgradeErrorText(e) {
93
+ if (isForbidden(e)) return UPGRADE_TEXT.askAdmin;
94
+ const details = e?.details;
95
+ if (details?.reason === "UPGRADE_CHECK_FAILED" && Array.isArray(details.failures)) {
96
+ const failures = details.failures.filter(
97
+ (f) => typeof f === "object" && f !== null && typeof f.pin === "string" && Array.isArray(f.problems)
98
+ );
99
+ return UPGRADE_TEXT.checkFailed(failures);
100
+ }
101
+ return failedBecause(e, "\u7A0D\u540E\u518D\u8BD5");
102
+ }
103
+
104
+ export {
105
+ UPGRADE_TEXT,
106
+ upgradeErrorText
107
+ };
@@ -0,0 +1,313 @@
1
+ import * as react from 'react';
2
+ import { ReactNode, Ref, MouseEvent } from 'react';
3
+ import { MemberView } from '@kanyun-ai-infra/band-plugin-sdk';
4
+
5
+ /**
6
+ * 一行错误提示:操作失败时显示在面板里。`children` 为空(null、undefined、空串、false)时什么也不渲染。
7
+ * 给了 `retry` 是「读失败」:整行是 alert,提示后面跟「重试」。全站读数据失败只走这一个出口,各页不自己拼;
8
+ * 原因里断网那句由宿主统一写(`errors` 的 `NETWORK_LOST`)。
9
+ */
10
+ declare function ErrorNote({ children, retry }: {
11
+ children: ReactNode;
12
+ retry?: () => void;
13
+ }): react.JSX.Element | null;
14
+ /** 不是错误的一句说明(比如「已经交给 Rondo 了」):不用红色,不抢眼。 */
15
+ declare function InfoNote({ children }: {
16
+ children: ReactNode;
17
+ }): react.JSX.Element | null;
18
+ /** 面板头:标题(`h2`)在左,`children`(筛选、按钮、说明)在右。 */
19
+ declare function PaneHeader({ title, children }: {
20
+ title: ReactNode;
21
+ children?: ReactNode;
22
+ }): react.JSX.Element;
23
+ /**
24
+ * 头像的底色(Band Design `--av1`…`--av5`)。给了 `order`(项目里人的 id,按成员列表的顺序)就按位置轮着取,
25
+ * 五人以内互不相同,同一页上对话、成员、@ 候选看到的是同一种颜色;不在里面的按 id 取一个固定的。
26
+ */
27
+ declare function avatarTone(id: string, order?: readonly string[]): number;
28
+ /** 项目里人的 id,按成员列表的顺序(Agent 不占底色):传给 `Avatar` 的 `order`。 */
29
+ declare function memberOrder(members: readonly MemberView[]): string[];
30
+ /** 头像上的字:名字的第一个字(「王敏」写「王」),拉丁字母大写;没有名字写「?」。 */
31
+ declare function avatarInitial(name: string): string;
32
+ /**
33
+ * 成员头像(Band Design `.av`):人是带底色的名字首字;Agent 是标志里的蓝色符头(DESIGN.md · Rondo)。
34
+ * 旁边已经写了名字时 `decorative` 让读屏跳过它;单独出现时读出名字。
35
+ */
36
+ declare function Avatar({ id, name, agent, instrument, size, decorative, order, }: {
37
+ id: string;
38
+ name: string;
39
+ agent?: boolean;
40
+ /** 工坊 Agent 的乐器(`instrumentOf`);不给就画 Rondo 的符头。只对 `agent` 有效。 */
41
+ instrument?: Instrument;
42
+ size?: "xs" | "sm" | "md";
43
+ decorative?: boolean;
44
+ /** 项目里人的 id(`memberOrder(members)`),决定底色;见 `avatarTone`。 */
45
+ order?: readonly string[];
46
+ }): react.JSX.Element;
47
+ /** 状态符号:每行最右边一个 16px 符号,含义全站一致(DESIGN.md「状态符号」,样式在 Band Design)。 */
48
+ type GlyphKind = "working" | "needs" | "new" | "done" | "seen" | "waiting" | "queued" | "failed" | "";
49
+ /** `bg`:后台还有排着的回合(符号底下垫一层)。装饰性,含义由同一行的文字给出。 */
50
+ declare function Glyph({ kind, bg }: {
51
+ kind: GlyphKind;
52
+ bg?: boolean;
53
+ }): react.JSX.Element;
54
+ /** 项目里某个 Agent 的乐器(`instrumentOf`,同伴是项目里全部 Agent);Rondo 没有。 */
55
+ declare function agentInstrument(id: string, members: readonly MemberView[]): Instrument | undefined;
56
+ /** 项目里某个 Agent 的头像:名字取成员显示名,工坊 Agent 画乐器,Rondo 画符头。 */
57
+ declare function AgentAvatar({ id, members, size, decorative, }: {
58
+ id: string;
59
+ members: readonly MemberView[] | undefined;
60
+ size?: "xs" | "sm" | "md";
61
+ decorative?: boolean;
62
+ }): react.JSX.Element;
63
+ /** 工坊 Agent 的乐器(DESIGN.md「乐器:谁在演」),顺序就是 tokens `instruments/index.json` 的顺序。 */
64
+ declare const INSTRUMENTS: readonly ["sax", "keys", "guitar", "drum", "trumpet", "violin"];
65
+ type Instrument = (typeof INSTRUMENTS)[number];
66
+ /**
67
+ * 一个工坊里各 Agent 的缺省乐器:名字的 FNV-1a 对乐器数取模,同工坊里撞了顺延到下一件空闲的;
68
+ * `peers` 是这个工坊的全部 Agent(顺序无关,按名字排序后依次分配,结果不随阶段顺序变)。
69
+ * Rondo 不拿乐器(返回 undefined,画符头)。超过六个时乐器会重复。
70
+ */
71
+ declare function instrumentOf(name: string, peers: readonly string[]): Instrument | undefined;
72
+ /**
73
+ * 工坊 icon(v8 §7「工坊 icon」):`icon` 是工坊卡片上的字段,`genre:<曲风>` 画内置曲风;仓库内图片还读不到、
74
+ * 没写或认不出时画标志简版。画的是只有乐器的简版(见 `GENRE_COMPACT_SRC`),48px 及以上的完整版用
75
+ * `WorkshopEmblem`。装饰性,名称由旁边的文字给出。
76
+ */
77
+ declare function WorkshopIcon({ icon, size, tile, }: {
78
+ icon?: string;
79
+ size?: number;
80
+ /** 画在方底板上(DESIGN.md「工坊 icon」:底板归宿主)。 */
81
+ tile?: boolean;
82
+ }): react.JSX.Element;
83
+ /**
84
+ * 工坊 icon 的完整版(带英文流派名),画在方底板上,给 48px 及以上的大卡与工坊页头用(DESIGN.md「工坊 icon」)。
85
+ * 和 `WorkshopIcon` 分开,是为了让只画小图标的插件打包时摇掉完整版的图。
86
+ */
87
+ declare function WorkshopEmblem({ icon, size }: {
88
+ icon?: string;
89
+ size?: number;
90
+ }): react.JSX.Element;
91
+ /** Band 标志简版(三个连杠音符,中间那个蓝色的代表 Agent)。装饰性。 */
92
+ declare function BandMark({ size, className }: {
93
+ size?: number;
94
+ className?: string;
95
+ }): react.JSX.Element;
96
+ /** Band 字标(五线谱 + 三个音符 + 「Band」,DESIGN.md「标志」)。样式在 Band Design 的 `.logo`。 */
97
+ declare function LogoLockup(): react.JSX.Element;
98
+
99
+ /** 一次页内跳转的目标:页键与地址参数,由调用方交给宿主。 */
100
+ interface SettingsTarget {
101
+ page: string;
102
+ params?: Record<string, string>;
103
+ }
104
+ /** 导航里的一项(分区导航的链接)。 */
105
+ interface SettingsNavItem extends SettingsTarget {
106
+ slug: string;
107
+ title: string;
108
+ href: string;
109
+ /** 右侧 16px 状态符号(同侧栏),可选。 */
110
+ glyph?: ReactNode;
111
+ }
112
+ interface SettingsGroup {
113
+ group: string;
114
+ sections: readonly SettingsNavItem[];
115
+ }
116
+ /** 页头上方那行返回(`‹ 设置`、`‹ 列表名`)。 */
117
+ interface SettingsBackTarget extends SettingsTarget {
118
+ label: string;
119
+ href: string;
120
+ }
121
+ type Navigate = (target: SettingsTarget) => void;
122
+ /** 只在主键上点(没按修饰键)时走页内跳转;其余交给浏览器(新标签页)。 */
123
+ declare function settingsLinkHandler(go: Navigate | undefined, target: SettingsTarget): (e: MouseEvent<HTMLAnchorElement>) => void;
124
+ /**
125
+ * 四个作用域共用的骨架(稿 §3.1、§3.7):左边分区导航、右边内容。作用域只决定导航头与分区名单;
126
+ * 摆法与手机宽度(600px)下的两页切换不变。导航是链接列表(`aria-current="page"`),不是 tablist。
127
+ */
128
+ declare function SettingsLayout({ navLabel, title, obj, crumb, groups, current, view, back, backMobile, onNavigate, mainRef, children, }: {
129
+ /** 分区导航的 aria-label(「设置分区」「项目设置分区」)。 */
130
+ navLabel: string;
131
+ title: string;
132
+ /** 导航头第二行(「只有你看得到这里」)。 */
133
+ obj?: ReactNode;
134
+ /** 导航头上方的面包屑;项目、工坊作用域的对象头沿用页面顶栏,一般不传。 */
135
+ crumb?: ReactNode;
136
+ groups: readonly SettingsGroup[];
137
+ /** 当前分区的 slug;目录页不传。 */
138
+ current?: string | undefined;
139
+ view?: "page" | "index";
140
+ /** 详情 / 新增页头上的「‹ 列表名」:宽窄都画(稿 §3.2)。 */
141
+ back?: SettingsBackTarget | undefined;
142
+ /** 分区页给手机宽度的「‹ 设置」:只在窄屏画(稿 §3.7)。 */
143
+ backMobile?: SettingsBackTarget | undefined;
144
+ /** 页内跳转;不给就按普通链接走。 */
145
+ onNavigate?: Navigate;
146
+ /** 内容区的 ref(接线层在上面装未保存离开的守卫)。 */
147
+ mainRef?: Ref<HTMLElement>;
148
+ children: ReactNode;
149
+ }): react.JSX.Element;
150
+ /** 「‹ 列表名」:详情、新增页头上方;`mobileOnly` 是只在手机宽度画的「‹ 设置」。 */
151
+ declare function SettingsBack({ target, onNavigate, mobileOnly, }: {
152
+ target: SettingsBackTarget;
153
+ onNavigate?: Navigate | undefined;
154
+ mobileOnly?: boolean;
155
+ }): react.JSX.Element;
156
+ /** 页头:标题(分区名)+ 一句说明这里管什么、谁看得到 + 主操作(靠右,一页最多一个)。 */
157
+ declare function SettingsHead({ title, desc, action, }: {
158
+ title: ReactNode;
159
+ desc?: ReactNode;
160
+ action?: ReactNode;
161
+ }): react.JSX.Element;
162
+ /** 列表:行不画框不画线,悬停垫浅底;整行进详情。 */
163
+ declare function SettingsRows({ label, children }: {
164
+ label: string;
165
+ children: ReactNode;
166
+ }): react.JSX.Element;
167
+ /**
168
+ * 一行:(可选的头像 / 图标位)名称 + 标记 / 一行元信息 / 右端最多一个快捷操作 + 进详情的箭头。
169
+ * 给了 `onOpen` 时整行可点。
170
+ */
171
+ declare function SettingsRow({ title, tags, meta, actions, leading, href, onOpen, mono, isNew, extra, }: {
172
+ title: ReactNode;
173
+ tags?: ReactNode;
174
+ meta?: readonly ReactNode[];
175
+ actions?: ReactNode;
176
+ /** 名称左边的头像或图标(成员行用 `Avatar`),占两行高。 */
177
+ leading?: ReactNode;
178
+ href?: string;
179
+ onOpen?: () => void;
180
+ mono?: boolean;
181
+ /** 刚保存的那一行垫 `--ok-soft`(回列表时标出来)。 */
182
+ isNew?: boolean;
183
+ /** 行内展开的一块,占满整行宽。画它时别给 `onOpen`:整行链接会盖住它。 */
184
+ extra?: ReactNode;
185
+ }): react.JSX.Element;
186
+ interface SettingsFact {
187
+ key: string;
188
+ value: ReactNode;
189
+ }
190
+ /** 详情概况:定义列表,不画框。 */
191
+ declare function SettingsFacts({ items }: {
192
+ items: readonly SettingsFact[];
193
+ }): react.JSX.Element;
194
+ /** 一节:`h2` + 可选的一句说明 + 内容;节与节之间留白,不画线、不套卡(稿 §3.1)。 */
195
+ declare function SettingsSection({ title, desc, children, }: {
196
+ title: string;
197
+ desc?: ReactNode;
198
+ children: ReactNode;
199
+ }): react.JSX.Element;
200
+ /**
201
+ * 只有一份设置的分区(提醒、批准):不分节、不画 h2(稿 §3.2),控件、说明与反馈竖排。
202
+ * 名字给 `aria-label`;里面要成组的控件自己放 `fieldset`(组件抹掉它的默认框)。
203
+ */
204
+ declare function SettingsSingle({ label, children }: {
205
+ label: string;
206
+ children: ReactNode;
207
+ }): react.JSX.Element;
208
+ /** 一排勾选(`.choice`):自动折行。 */
209
+ declare function SettingsChoices({ children }: {
210
+ children: ReactNode;
211
+ }): react.JSX.Element;
212
+ /** 危险操作区:只在详情页最下面,每项一行(动作 + 后果一句 + 按钮),按钮是 `.btn.danger`(稿 §3.6)。 */
213
+ declare function SettingsDanger({ children }: {
214
+ children: ReactNode;
215
+ }): react.JSX.Element;
216
+ declare function SettingsDangerRow({ title, note, action, }: {
217
+ title: ReactNode;
218
+ note: ReactNode;
219
+ action: ReactNode;
220
+ }): react.JSX.Element;
221
+ /** 加载:页头照常画,内容区用 Band Design 的 `.tuning`(五线谱)配「正在读取<什么>…」;不画骨架条(稿 §3.3)。 */
222
+ declare function SettingsLoading({ text }: {
223
+ text: string;
224
+ }): react.JSX.Element;
225
+ /** 空:休止符插画 + 一句「还没有什么、有了能做什么」+ 主操作(没有新增权限时不画按钮)。 */
226
+ declare function SettingsEmpty({ children, action }: {
227
+ children: ReactNode;
228
+ action?: ReactNode;
229
+ }): react.JSX.Element;
230
+ /** 读失败:一行 `ErrorNote`(「<什么>没读出来:<原因>」+「重试」)。 */
231
+ declare function SettingsReadError({ children, retry }: {
232
+ children: ReactNode;
233
+ retry: () => void;
234
+ }): react.JSX.Element;
235
+ /**
236
+ * 没有权限(读 403)与环境没开通:标题、谁能看 / 缺什么、去找谁,一个离开的按钮(可不给);不画重试(稿 §3.3)。
237
+ */
238
+ declare function SettingsNoAccess({ title, children, action, }: {
239
+ title: string;
240
+ children: ReactNode;
241
+ action?: {
242
+ label: string;
243
+ onClick: () => void;
244
+ } | undefined;
245
+ }): react.JSX.Element;
246
+ /** 保存后的读屏播报(「已保存」「已删除」,不抢眼、不占版面)。 */
247
+ declare function SettingsNotice({ text }: {
248
+ text: string;
249
+ }): react.JSX.Element;
250
+ /** 「已保存」那一句(控件旁或按钮旁,停四拍由调用方的 `useSavedNote` 管)。 */
251
+ declare function SettingsSaved({ text }: {
252
+ text?: ReactNode;
253
+ }): react.JSX.Element;
254
+ /**
255
+ * 确认弹窗(稿 §3.6):`--scrim`、`--shadow-pop`、`--r-xl`;标题问动作,正文列后果,末尾写能不能恢复。
256
+ * 不用输入名字确认:弹窗已列后果,多打一遍字不增加信息。
257
+ */
258
+ declare function SettingsDialog({ title, children, actions, onClose, }: {
259
+ title: string;
260
+ children: ReactNode;
261
+ actions: ReactNode;
262
+ onClose: () => void;
263
+ }): react.JSX.Element;
264
+ /** 设置里的表单:字段竖排,最宽 560。提交走 `onSubmit`(已 `preventDefault`)。 */
265
+ declare function SettingsForm({ label, onSubmit, children, }: {
266
+ label?: string;
267
+ onSubmit?: () => void;
268
+ children: ReactNode;
269
+ }): react.JSX.Element;
270
+ /** 字段的输入要挂的属性:id、读屏说明、出错标记。 */
271
+ interface SettingsFieldProps {
272
+ id: string;
273
+ "aria-describedby"?: string;
274
+ "aria-invalid"?: true;
275
+ }
276
+ /**
277
+ * 字段行:标签(必填写「必填」)、输入、下一行的提示或出错、计数。提示 / 出错 / 计数用 Band Design 的
278
+ * `.field-note`(出错加 `.err`)与 `.field-count`(DESIGN.md「控件」),出错时输入挂 `aria-invalid`。
279
+ * 输入本身由调用方用 Band Design 的 `.field` 画,组件只摆位置、接好 id 与读屏说明。
280
+ */
281
+ declare function SettingsField({ label, required, optional, hint, error, count, over, children, }: {
282
+ label: ReactNode;
283
+ required?: boolean;
284
+ /** 选填:标签旁写「选填」(稿 §3.5 的样张两种都标);与 `required` 不同时给。 */
285
+ optional?: boolean;
286
+ hint?: ReactNode;
287
+ /** 出错的话;有它时不画提示。 */
288
+ error?: ReactNode;
289
+ /** 计数(「12 / 200」),靠右。 */
290
+ count?: ReactNode;
291
+ /** 计数超了:计数变红。 */
292
+ over?: boolean;
293
+ children: (props: SettingsFieldProps) => ReactNode;
294
+ }): react.JSX.Element;
295
+ /** 表单末尾的操作行:按钮 + 保存反馈 / 还缺什么。 */
296
+ declare function SettingsActions({ children, end, }: {
297
+ children: ReactNode;
298
+ end?: boolean;
299
+ }): react.JSX.Element;
300
+ /**
301
+ * 保存反馈(稿 §3.5):`saving` 时写在做什么(「正在验证…」那一档的旁注),`saved` 停四拍的「已保存」,
302
+ * `failed` 是贴在按钮旁的整体错误(「没保存:」开头),`missing` 是按钮置灰时缺什么(「还差 key 没填」)。
303
+ * 不用吐司;读屏走 `aria-live`。
304
+ */
305
+ declare function SettingsSaveState({ saving, saved, failed, missing, }: {
306
+ saving?: ReactNode;
307
+ /** `true` 写「已保存」;给文字或节点就照它写(「已保存,ModelGate 验证通过」)。 */
308
+ saved?: ReactNode;
309
+ failed?: ReactNode;
310
+ missing?: ReactNode;
311
+ }): react.JSX.Element;
312
+
313
+ export { AgentAvatar, Avatar, BandMark, ErrorNote, Glyph, type GlyphKind, INSTRUMENTS, InfoNote, type Instrument, LogoLockup, PaneHeader, SettingsActions, SettingsBack, type SettingsBackTarget, SettingsChoices, SettingsDanger, SettingsDangerRow, SettingsDialog, SettingsEmpty, type SettingsFact, SettingsFacts, SettingsField, type SettingsFieldProps, SettingsForm, type SettingsGroup, SettingsHead, SettingsLayout, SettingsLoading, type SettingsNavItem, SettingsNoAccess, SettingsNotice, SettingsReadError, SettingsRow, SettingsRows, SettingsSaveState, SettingsSaved, SettingsSection, SettingsSingle, type SettingsTarget, WorkshopEmblem, WorkshopIcon, agentInstrument, avatarInitial, avatarTone, instrumentOf, memberOrder, settingsLinkHandler };
@@ -0,0 +1,94 @@
1
+ /* band:inject-css */ /* @kanyun-ai-infra/band-plugin-react/components 的样式(scripts/tsup-inject-css.ts) */
2
+ if (typeof document !== "undefined" && document.head) {
3
+ const css = "/* src/ui.css */\n.band-plugin-react-error {\n font-size: var(--fs-ui);\n padding: 7px 10px;\n border-radius: 8px;\n background: var(--bad-soft);\n color: var(--bad);\n font-family: var(--mono);\n}\n.band-plugin-react-info {\n font-size: var(--fs-ui);\n padding: 7px 10px;\n border-radius: 8px;\n background: var(--panel);\n color: var(--muted);\n}\n.band-plugin-react-retry {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 10px;\n}\n.band-plugin-react-failed {\n display: grid;\n gap: 6px;\n justify-items: start;\n margin: auto;\n padding: 8px 10px;\n border-radius: var(--r);\n background: var(--bad-soft);\n color: var(--bad);\n font-size: var(--fs-ui);\n}\n.band-plugin-react-failed p {\n margin: 0;\n}\n.band-plugin-react-failed-title {\n font-weight: 600;\n}\n.band-plugin-react-failed-reason {\n overflow-wrap: anywhere;\n}\n.band-plugin-react-pane-head {\n padding: 6px 14px 6px 16px;\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n min-height: 36px;\n}\n.band-plugin-react-pane-title {\n margin: 0;\n font-size: var(--fs-dense);\n color: var(--muted);\n font-weight: 500;\n letter-spacing: 0.02em;\n}\n.band-plugin-react-wsicon {\n flex: none;\n display: block;\n}\n.band-plugin-react-wstile {\n --band-wstile: 16px;\n width: var(--band-wstile);\n height: var(--band-wstile);\n display: inline-grid;\n place-items: center;\n flex: none;\n border-radius: calc(var(--band-wstile) * 0.26);\n background: var(--panel);\n box-shadow: var(--shadow-card);\n}\n.band-plugin-react-wstile > :where(.band-plugin-react-wsicon) {\n width: 82%;\n height: 82%;\n}\n.band-plugin-react-wstile:where(.is-large) {\n border-radius: var(--r-xl);\n}\n.band-plugin-react-wstile:where(.is-large) > :where(.band-plugin-react-wsicon) {\n width: 76%;\n height: 76%;\n}\n.band-plugin-react-instrument {\n width: 72%;\n height: 72%;\n background: currentColor;\n -webkit-mask: var(--band-instrument) center / contain no-repeat;\n mask: var(--band-instrument) center / contain no-repeat;\n}\n.band-plugin-react-model-hint {\n margin: 0;\n}\n.band-plugin-react-target-repo {\n display: flex;\n flex-direction: column;\n gap: 2px;\n margin: 0;\n font-size: var(--fs-meta);\n line-height: 1.5;\n color: var(--muted);\n}\n.band-plugin-react-target-repo code {\n font-family: var(--mono);\n color: var(--ink);\n}\n.band-plugin-react-target-repo a {\n color: var(--accent-text);\n}\n.band-plugin-react-target-repo-request {\n align-self: flex-start;\n}\n.band-plugin-react-target-repo-err {\n color: var(--bad);\n}\n.band-plugin-react-first-delivery {\n display: flex;\n flex-direction: column;\n gap: 8px;\n flex-basis: 100%;\n padding: 12px;\n border: 1px solid var(--rule);\n border-radius: var(--r-lg);\n background: var(--panel);\n margin: 0;\n min-width: 0;\n}\n.band-plugin-react-first-delivery-slot {\n display: flex;\n flex-direction: column;\n gap: 4px;\n font-size: var(--fs-meta);\n}\n.band-plugin-react-first-delivery-pick,\n.band-plugin-react-first-delivery-file {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 8px;\n}\n.band-plugin-react-first-delivery-list {\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n.band-plugin-react-first-delivery-name {\n min-width: 0;\n overflow-wrap: anywhere;\n}\n.band-plugin-react-first-delivery-size {\n color: var(--muted);\n}\n.band-plugin-react-first-delivery-missing {\n margin: 0;\n font-size: var(--fs-meta);\n color: var(--muted);\n}\n.band-plugin-react-first-delivery .btn {\n align-self: flex-start;\n}\n.band-plugin-react-no-key {\n margin: 0;\n font-size: var(--fs-ui);\n color: var(--bad);\n}\n\n/* src/settings/settings.css */\n.band-sf {\n display: grid;\n grid-template-columns: 216px minmax(0, 1fr);\n min-height: 0;\n height: 100%;\n}\n.band-sf-nav {\n min-height: 0;\n overflow: auto;\n overscroll-behavior: contain;\n padding: 24px 12px 24px 20px;\n display: grid;\n gap: 16px;\n align-content: start;\n}\n.band-sf-head {\n display: grid;\n gap: 2px;\n min-width: 0;\n padding: 0 8px;\n}\n.band-sf-title {\n font-size: var(--fs-title);\n font-weight: 600;\n line-height: 1.3;\n}\n.band-sf-obj {\n font-size: var(--fs-ui);\n color: var(--muted);\n}\n.band-sf-grp {\n display: grid;\n gap: 1px;\n}\n.band-sf-grp > span {\n padding: 2px 8px;\n color: var(--muted);\n font-size: var(--fs-dense);\n}\n.band-sf-grp a {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n padding: 6px 8px;\n border-radius: var(--r);\n color: var(--ink);\n text-decoration: none;\n font-size: var(--fs-row);\n}\n.band-sf-grp a:hover {\n background: var(--fill);\n}\n.band-sf-grp a[aria-current=page] {\n background: var(--fill-2);\n font-weight: 500;\n}\n.band-sf-grp a:focus-visible {\n outline: none;\n box-shadow: 0 0 0 2px var(--panel), 0 0 0 3.5px var(--accent);\n}\n.band-sf-grp a .band-sf-chev {\n display: none;\n color: var(--muted);\n}\n.band-sf-scroll {\n min-height: 0;\n overflow: auto;\n overscroll-behavior: contain;\n}\n.band-sf-main {\n box-sizing: border-box;\n width: 100%;\n max-width: 824px;\n padding: 32px 32px 64px;\n display: grid;\n gap: 20px;\n align-content: start;\n}\n.band-sf-crumb {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 4px;\n min-width: 0;\n color: var(--muted);\n font-size: var(--fs-dense);\n}\n.band-sf-crumb-in {\n display: inline-flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 4px;\n min-width: 0;\n}\n.band-sf-crumb-ws {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n min-width: 0;\n white-space: nowrap;\n}\n.band-sf-crumb-ws > span {\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.band-sf-crumb a,\n.band-sf-crumb button {\n min-width: 0;\n border: 0;\n padding: 0;\n background: none;\n color: var(--muted);\n font: inherit;\n text-decoration: none;\n border-radius: 4px;\n overflow-wrap: anywhere;\n cursor: pointer;\n}\n.band-sf-crumb button {\n text-align: start;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.band-sf-crumb a:hover,\n.band-sf-crumb button:hover {\n color: var(--ink);\n}\n.band-sf-crumb a:focus-visible,\n.band-sf-crumb button:focus-visible {\n outline: none;\n box-shadow: 0 0 0 2px var(--accent);\n}\n.band-sf-back {\n display: inline-flex;\n align-self: start;\n justify-self: start;\n gap: 4px;\n color: var(--muted);\n text-decoration: none;\n font-size: var(--fs-ui);\n border-radius: 4px;\n}\n.band-sf-back-m {\n display: none;\n}\n.band-sf-back:hover {\n color: var(--ink);\n}\n.band-sf-back:focus-visible {\n outline: none;\n box-shadow: 0 0 0 2px var(--panel), 0 0 0 3.5px var(--accent);\n}\n.band-sf-h {\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n gap: 16px;\n}\n.band-sf-h > div {\n display: grid;\n gap: 4px;\n min-width: 0;\n}\n.band-sf-h h1 {\n margin: 0;\n font-size: var(--fs-h2);\n font-weight: 600;\n line-height: 1.3;\n}\n.band-sf-h p {\n margin: 0;\n color: var(--muted);\n font-size: var(--fs-ui);\n max-width: 560px;\n}\n.band-sf-h-a {\n flex: none;\n}\n.band-sf-sec {\n display: grid;\n gap: 10px;\n}\n.band-sf-sec h2 {\n margin: 0;\n font-size: var(--fs-lead);\n font-weight: 600;\n}\n.band-sf-sec > p {\n margin: 0;\n color: var(--muted);\n font-size: var(--fs-ui);\n}\n.band-sf-rows {\n display: grid;\n gap: 2px;\n margin: 0;\n padding: 0;\n list-style: none;\n}\n.band-sf-row {\n position: relative;\n display: grid;\n grid-template-columns: minmax(0, 1fr) auto;\n gap: 2px 12px;\n align-items: center;\n padding: 10px 12px;\n margin: 0 -12px;\n border-radius: var(--r-lg);\n}\n.band-sf-row:hover {\n background: var(--fill);\n}\n.band-sf-row.is-new,\n.band-sf-row.is-new:hover {\n background: var(--ok-soft);\n}\n.band-sf-row-t {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 4px 8px;\n min-width: 0;\n}\n.band-sf-row-t b {\n font-weight: 600;\n overflow-wrap: anywhere;\n}\n.band-sf-row-link {\n color: inherit;\n text-decoration: none;\n border-radius: 4px;\n}\n.band-sf-row-link::after {\n content: \"\";\n position: absolute;\n inset: 0;\n border-radius: var(--r-lg);\n}\n.band-sf-row-a {\n position: relative;\n z-index: 1;\n}\n.band-sf-row-link:focus-visible {\n outline: none;\n box-shadow: 0 0 0 2px var(--panel), 0 0 0 3.5px var(--accent);\n}\n.band-sf-row-m {\n grid-column: 1;\n display: flex;\n flex-wrap: wrap;\n gap: 2px 14px;\n color: var(--muted);\n font-size: var(--fs-dense);\n min-width: 0;\n}\n.band-sf-row-m > span {\n min-width: 0;\n overflow-wrap: anywhere;\n}\n.band-sf-row-a {\n grid-column: 2;\n grid-row: 1 / span 2;\n display: flex;\n align-items: center;\n gap: 8px;\n}\n.band-sf-row-a .btn {\n position: relative;\n z-index: 1;\n}\n.band-sf-chev {\n color: var(--muted);\n}\n.band-sf-mono {\n font-family: var(--mono);\n font-size: 0.92em;\n}\n.band-sf-facts {\n display: grid;\n gap: 6px 16px;\n margin: 0;\n font-size: var(--fs-ui);\n}\n.band-sf-fact {\n display: grid;\n grid-template-columns: 112px minmax(0, 1fr);\n gap: 16px;\n}\n.band-sf-facts dt {\n color: var(--muted);\n}\n.band-sf-facts dd {\n margin: 0;\n min-width: 0;\n overflow-wrap: anywhere;\n}\n.band-sf-dz {\n display: grid;\n gap: 10px;\n padding-top: 8px;\n}\n.band-sf-dz h2 {\n margin: 0;\n font-size: var(--fs-lead);\n font-weight: 600;\n}\n.band-sf-dz-r {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 16px;\n}\n.band-sf-dz-r > div {\n display: grid;\n gap: 2px;\n min-width: 0;\n}\n.band-sf-dz-r span {\n color: var(--muted);\n font-size: var(--fs-dense);\n}\n.band-sf-dz-r .btn {\n flex: none;\n}\n.band-sf-empty,\n.band-sf .band-sf-empty.rest {\n display: grid;\n justify-items: start;\n text-align: start;\n gap: 10px;\n padding: 24px 0;\n max-width: 480px;\n}\n.band-sf-empty p {\n margin: 0;\n color: var(--muted);\n}\n.band-sf-error {\n min-width: 0;\n}\n.band-sf-load {\n padding: 40px 0;\n display: grid;\n justify-items: start;\n}\n.band-sf-ok {\n margin: 0;\n color: var(--ok);\n font-size: var(--fs-ui);\n}\n.band-sf-muted {\n margin: 0;\n color: var(--muted);\n font-size: var(--fs-ui);\n}\n.band-sf-banner {\n margin: 0;\n padding: 10px 12px;\n border-radius: var(--r-lg);\n background: var(--fill);\n font-size: var(--fs-ui);\n}\n.band-sf-banner.is-warn {\n display: flex;\n align-items: center;\n gap: 8px;\n background: var(--warn-soft);\n}\n.band-sf-form {\n display: grid;\n gap: 16px;\n max-width: 560px;\n min-width: 0;\n}\n.band-sf-form code {\n overflow-wrap: anywhere;\n}\n.band-sf-fld {\n display: grid;\n gap: 6px;\n min-width: 0;\n}\n.band-sf-fld > label {\n font-size: var(--fs-ui);\n font-weight: 500;\n}\n.band-sf-fld .field {\n box-sizing: border-box;\n width: 100%;\n min-width: 0;\n}\n.band-sf-fld textarea.field {\n resize: vertical;\n line-height: 1.5;\n}\n.band-sf-req {\n margin-left: 6px;\n color: var(--muted);\n font-weight: 400;\n}\n.band-sf-optional {\n margin-left: 6px;\n color: var(--muted);\n font-weight: 400;\n}\n.band-sf-one {\n display: grid;\n gap: 10px;\n min-width: 0;\n}\n.band-sf-one fieldset {\n margin: 0;\n padding: 0;\n border: 0;\n min-width: 0;\n display: grid;\n gap: 10px;\n}\n.band-sf-choices {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 8px 12px;\n}\n.band-sf-hint {\n color: var(--muted);\n font-size: var(--fs-dense);\n}\n.band-sf-hint a,\n.band-sf-fld .field-note a,\n.band-sf-muted a {\n color: var(--accent-text);\n text-decoration: underline;\n text-underline-offset: 2px;\n}\n.band-sf-err {\n margin: 0;\n color: var(--bad);\n font-size: var(--fs-ui);\n}\n.band-sf-fail {\n grid-column: 1 / -1;\n display: flex;\n align-items: flex-start;\n gap: 8px;\n overflow-wrap: anywhere;\n}\n.band-sf-given {\n margin: 0;\n padding: 10px 12px;\n border-radius: var(--r-lg);\n background: var(--fill);\n font-size: var(--fs-ui);\n}\ndl.band-sf-given {\n display: grid;\n grid-template-columns: 56px minmax(0, 1fr);\n gap: 2px 12px;\n}\ndl.band-sf-given dt {\n color: var(--muted);\n}\ndl.band-sf-given dd {\n margin: 0;\n min-width: 0;\n}\ndl.band-sf-given p {\n grid-column: 1 / -1;\n margin: 0;\n color: var(--muted);\n font-size: var(--fs-dense);\n}\n.band-sf-stepback {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n align-self: start;\n justify-self: start;\n padding: 0;\n border: 0;\n background: none;\n color: var(--muted);\n font: inherit;\n font-size: var(--fs-ui);\n cursor: pointer;\n border-radius: 4px;\n}\n.band-sf-stepback:hover {\n color: var(--ink);\n}\n.band-sf-stepback:focus-visible {\n outline: none;\n box-shadow: 0 0 0 2px var(--panel), 0 0 0 3.5px var(--accent);\n}\n.band-sf-acts {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 8px 12px;\n}\n.band-sf-acts-end {\n justify-content: flex-end;\n}\n.band-sf-state {\n display: flex;\n align-items: center;\n gap: 8px;\n color: var(--muted);\n font-size: var(--fs-ui);\n}\n.band-settings-no-access {\n display: grid;\n gap: 10px;\n justify-items: start;\n max-width: 560px;\n padding-top: 8px;\n}\n.band-settings-no-access h2 {\n margin: 0;\n font-size: var(--fs-title);\n}\n.band-settings-no-access p {\n margin: 0;\n color: var(--muted);\n}\n.band-sf-dialog {\n box-sizing: border-box;\n width: min(460px, calc(100vw - 32px));\n max-height: calc(100dvh - 32px);\n overflow: auto;\n overscroll-behavior: contain;\n border: 0;\n border-radius: var(--r-xl);\n box-shadow: var(--shadow-pop);\n background: var(--panel);\n color: var(--ink);\n padding: 22px 24px;\n}\n.band-sf-dialog[open] {\n display: grid;\n gap: 14px;\n}\n.band-sf-dialog::backdrop {\n background: var(--scrim);\n}\n.band-sf-dialog h2 {\n margin: 0;\n font-size: var(--fs-title);\n overflow-wrap: anywhere;\n}\n.band-sf-dialog ul {\n margin: 0;\n padding-left: 18px;\n display: grid;\n gap: 4px;\n}\n.band-sf-row-x {\n grid-column: 1 / -1;\n margin-top: 8px;\n min-width: 0;\n}\n.band-sf-row.has-lead {\n grid-template-columns: auto minmax(0, 1fr) auto;\n}\n.band-sf-row-l {\n grid-column: 1;\n grid-row: 1 / span 2;\n display: flex;\n align-items: center;\n}\n.band-sf-row.has-lead .band-sf-row-t,\n.band-sf-row.has-lead .band-sf-row-m {\n grid-column: 2;\n}\n.band-sf-row.has-lead .band-sf-row-a {\n grid-column: 3;\n}\n@media (max-width: 600px) {\n .band-sf {\n grid-template-columns: minmax(0, 1fr);\n }\n .band-sf[data-view=page] .band-sf-nav {\n display: none;\n }\n .band-sf[data-view=index] .band-sf-scroll {\n display: none;\n }\n .band-sf-nav {\n padding: 16px;\n }\n .band-sf-grp a {\n padding: 10px 8px;\n font-size: var(--fs-body);\n }\n .band-sf-grp a .band-sf-chev {\n display: inline;\n }\n .band-sf-grp a[aria-current=page] {\n background: none;\n font-weight: 400;\n }\n .band-sf-main {\n padding: 12px 16px 48px;\n gap: 16px;\n }\n .band-sf-back-m {\n display: inline-flex;\n }\n .band-sf-h {\n display: grid;\n gap: 10px;\n }\n .band-sf-h-a .btn {\n justify-self: start;\n }\n .band-sf-row-a {\n grid-column: 1 / -1;\n grid-row: auto;\n justify-content: flex-start;\n margin-top: 4px;\n }\n .band-sf-row-a .band-sf-chev {\n margin-left: auto;\n }\n .band-sf-row-a:has(.band-sf-chev:only-child) {\n grid-column: 2;\n grid-row: 1 / span 2;\n justify-content: flex-end;\n margin-top: 0;\n }\n .band-sf-row > .band-sf-acts {\n grid-column: 1 / -1;\n justify-content: flex-start;\n margin-top: 4px;\n }\n .band-sf-fact {\n grid-template-columns: 88px minmax(0, 1fr);\n gap: 12px;\n }\n .band-sf-dz-r {\n display: grid;\n gap: 8px;\n }\n .band-sf-dz-r .btn {\n justify-self: start;\n }\n .band-sf-dialog {\n padding: 18px 16px;\n }\n .band-sf-row.has-lead .band-sf-row-a {\n grid-column: 2 / -1;\n }\n}\n";
4
+ let el = document.head.querySelector("style[data-band-style=\"@kanyun-ai-infra/band-plugin-react/components\"]");
5
+ if (!el) {
6
+ el = document.createElement("style");
7
+ el.setAttribute("data-band-style", "@kanyun-ai-infra/band-plugin-react/components");
8
+ document.head.prepend(el);
9
+ }
10
+ el.textContent = css;
11
+ }
12
+ /* band:inject-css */
13
+ import {
14
+ AgentAvatar,
15
+ Avatar,
16
+ BandMark,
17
+ ErrorNote,
18
+ Glyph,
19
+ INSTRUMENTS,
20
+ InfoNote,
21
+ LogoLockup,
22
+ PaneHeader,
23
+ SettingsActions,
24
+ SettingsBack,
25
+ SettingsChoices,
26
+ SettingsDanger,
27
+ SettingsDangerRow,
28
+ SettingsDialog,
29
+ SettingsEmpty,
30
+ SettingsFacts,
31
+ SettingsField,
32
+ SettingsForm,
33
+ SettingsHead,
34
+ SettingsLayout,
35
+ SettingsLoading,
36
+ SettingsNoAccess,
37
+ SettingsNotice,
38
+ SettingsReadError,
39
+ SettingsRow,
40
+ SettingsRows,
41
+ SettingsSaveState,
42
+ SettingsSaved,
43
+ SettingsSection,
44
+ SettingsSingle,
45
+ WorkshopEmblem,
46
+ WorkshopIcon,
47
+ agentInstrument,
48
+ avatarInitial,
49
+ avatarTone,
50
+ instrumentOf,
51
+ memberOrder,
52
+ settingsLinkHandler
53
+ } from "./chunk-HX653UQN.js";
54
+ export {
55
+ AgentAvatar,
56
+ Avatar,
57
+ BandMark,
58
+ ErrorNote,
59
+ Glyph,
60
+ INSTRUMENTS,
61
+ InfoNote,
62
+ LogoLockup,
63
+ PaneHeader,
64
+ SettingsActions,
65
+ SettingsBack,
66
+ SettingsChoices,
67
+ SettingsDanger,
68
+ SettingsDangerRow,
69
+ SettingsDialog,
70
+ SettingsEmpty,
71
+ SettingsFacts,
72
+ SettingsField,
73
+ SettingsForm,
74
+ SettingsHead,
75
+ SettingsLayout,
76
+ SettingsLoading,
77
+ SettingsNoAccess,
78
+ SettingsNotice,
79
+ SettingsReadError,
80
+ SettingsRow,
81
+ SettingsRows,
82
+ SettingsSaveState,
83
+ SettingsSaved,
84
+ SettingsSection,
85
+ SettingsSingle,
86
+ WorkshopEmblem,
87
+ WorkshopIcon,
88
+ agentInstrument,
89
+ avatarInitial,
90
+ avatarTone,
91
+ instrumentOf,
92
+ memberOrder,
93
+ settingsLinkHandler
94
+ };