@kanyun-ai-infra/band-plugin-react 0.1.1 → 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.
@@ -334,6 +334,358 @@ function LogoLockup() {
334
334
  ] });
335
335
  }
336
336
 
337
+ // src/settings/kit.tsx
338
+ import {
339
+ useEffect,
340
+ useId,
341
+ useRef
342
+ } from "react";
343
+ import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
344
+ function settingsLinkHandler(go, target) {
345
+ return (e) => {
346
+ if (!go) return;
347
+ if (e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return;
348
+ e.preventDefault();
349
+ go(target);
350
+ };
351
+ }
352
+ function SettingsLayout({
353
+ navLabel,
354
+ title,
355
+ obj,
356
+ crumb,
357
+ groups,
358
+ current,
359
+ view = "page",
360
+ back,
361
+ backMobile,
362
+ onNavigate,
363
+ mainRef,
364
+ children
365
+ }) {
366
+ return /* @__PURE__ */ jsxs2("div", { className: "band-sf", "data-view": view, children: [
367
+ /* @__PURE__ */ jsxs2("nav", { className: "band-sf-nav", "aria-label": navLabel, children: [
368
+ /* @__PURE__ */ jsxs2("div", { className: "band-sf-head", children: [
369
+ crumb ? /* @__PURE__ */ jsx2("div", { className: "band-sf-crumb", children: crumb }) : null,
370
+ /* @__PURE__ */ jsx2("span", { className: "band-sf-title", children: title }),
371
+ !crumb && obj ? /* @__PURE__ */ jsx2("span", { className: "band-sf-obj", children: obj }) : null
372
+ ] }),
373
+ groups.map((g) => /* @__PURE__ */ jsxs2("div", { className: "band-sf-grp", children: [
374
+ /* @__PURE__ */ jsx2("span", { children: g.group }),
375
+ g.sections.map((s) => /* @__PURE__ */ jsxs2(
376
+ "a",
377
+ {
378
+ href: s.href,
379
+ "aria-current": s.slug === current ? "page" : void 0,
380
+ onClick: settingsLinkHandler(onNavigate, s),
381
+ children: [
382
+ s.title,
383
+ s.glyph ?? null,
384
+ /* @__PURE__ */ jsx2("span", { className: "band-sf-chev", "aria-hidden": "true", children: "\u203A" })
385
+ ]
386
+ },
387
+ s.slug
388
+ ))
389
+ ] }, g.group))
390
+ ] }),
391
+ /* @__PURE__ */ jsx2("div", { className: "band-sf-scroll", children: /* @__PURE__ */ jsxs2("main", { className: "band-sf-main", "data-view": view, ref: mainRef, children: [
392
+ view === "page" && back ? /* @__PURE__ */ jsx2(SettingsBack, { target: back, onNavigate }) : null,
393
+ view === "page" && backMobile ? /* @__PURE__ */ jsx2(SettingsBack, { target: backMobile, onNavigate, mobileOnly: true }) : null,
394
+ children
395
+ ] }) })
396
+ ] });
397
+ }
398
+ function SettingsBack({
399
+ target,
400
+ onNavigate,
401
+ mobileOnly = false
402
+ }) {
403
+ return /* @__PURE__ */ jsxs2(
404
+ "a",
405
+ {
406
+ className: mobileOnly ? "band-sf-back band-sf-back-m" : "band-sf-back",
407
+ href: target.href,
408
+ onClick: settingsLinkHandler(onNavigate, target),
409
+ children: [
410
+ /* @__PURE__ */ jsx2("span", { "aria-hidden": "true", children: "\u2039" }),
411
+ " ",
412
+ target.label
413
+ ]
414
+ }
415
+ );
416
+ }
417
+ function SettingsHead({
418
+ title,
419
+ desc,
420
+ action
421
+ }) {
422
+ return /* @__PURE__ */ jsxs2("div", { className: "band-sf-h", children: [
423
+ /* @__PURE__ */ jsxs2("div", { children: [
424
+ /* @__PURE__ */ jsx2("h1", { children: title }),
425
+ desc ? /* @__PURE__ */ jsx2("p", { children: desc }) : null
426
+ ] }),
427
+ action ? /* @__PURE__ */ jsx2("div", { className: "band-sf-h-a", children: action }) : null
428
+ ] });
429
+ }
430
+ function SettingsRows({ label, children }) {
431
+ return /* @__PURE__ */ jsx2("ul", { className: "band-sf-rows", "aria-label": label, children });
432
+ }
433
+ var warnedExtra = false;
434
+ function warnExtraWithOpen() {
435
+ const env = globalThis.process?.env?.NODE_ENV;
436
+ if (warnedExtra || env === void 0 || env === "production") return;
437
+ warnedExtra = true;
438
+ console.warn("SettingsRow\uFF1Aextra \u4E0E onOpen \u4E0D\u8981\u540C\u65F6\u7ED9\uFF0C\u6574\u884C\u94FE\u63A5\u4F1A\u76D6\u4F4F\u5C55\u5F00\u7684\u90A3\u4E00\u5757");
439
+ }
440
+ function SettingsRow({
441
+ title,
442
+ tags,
443
+ meta,
444
+ actions,
445
+ leading,
446
+ href,
447
+ onOpen,
448
+ mono = false,
449
+ isNew = false,
450
+ extra
451
+ }) {
452
+ if (extra && onOpen) warnExtraWithOpen();
453
+ const name = /* @__PURE__ */ jsx2("b", { className: mono ? "band-sf-mono" : void 0, children: title });
454
+ const cls = ["band-sf-row", leading ? "has-lead" : "", isNew ? "is-new" : ""].filter(Boolean).join(" ");
455
+ return /* @__PURE__ */ jsxs2("li", { className: cls, children: [
456
+ leading ? /* @__PURE__ */ jsx2("div", { className: "band-sf-row-l", children: leading }) : null,
457
+ /* @__PURE__ */ jsxs2("div", { className: "band-sf-row-t", children: [
458
+ onOpen ? /* @__PURE__ */ jsx2(
459
+ "a",
460
+ {
461
+ className: "band-sf-row-link",
462
+ href: href ?? "#",
463
+ onClick: (e) => {
464
+ if (e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return;
465
+ e.preventDefault();
466
+ onOpen();
467
+ },
468
+ children: name
469
+ }
470
+ ) : name,
471
+ tags
472
+ ] }),
473
+ /* @__PURE__ */ jsxs2("div", { className: "band-sf-row-a", children: [
474
+ actions,
475
+ onOpen ? /* @__PURE__ */ jsx2("span", { className: "band-sf-chev", "aria-hidden": "true", children: "\u203A" }) : null
476
+ ] }),
477
+ meta && meta.length > 0 ? /* @__PURE__ */ jsx2("div", { className: "band-sf-row-m", children: meta.map((m, i) => (
478
+ // biome-ignore lint/suspicious/noArrayIndexKey: 元信息是一串短句,没有稳定 id
479
+ /* @__PURE__ */ jsx2("span", { children: m }, i)
480
+ )) }) : null,
481
+ extra ? /* @__PURE__ */ jsx2("div", { className: "band-sf-row-x", children: extra }) : null
482
+ ] });
483
+ }
484
+ function SettingsFacts({ items }) {
485
+ return /* @__PURE__ */ jsx2("dl", { className: "band-sf-facts", children: items.map((f) => /* @__PURE__ */ jsxs2("div", { className: "band-sf-fact", children: [
486
+ /* @__PURE__ */ jsx2("dt", { children: f.key }),
487
+ /* @__PURE__ */ jsx2("dd", { children: f.value })
488
+ ] }, f.key)) });
489
+ }
490
+ function SettingsSection({
491
+ title,
492
+ desc,
493
+ children
494
+ }) {
495
+ return /* @__PURE__ */ jsxs2("section", { className: "band-sf-sec", "aria-label": title, children: [
496
+ /* @__PURE__ */ jsx2("h2", { children: title }),
497
+ desc ? /* @__PURE__ */ jsx2("p", { children: desc }) : null,
498
+ children
499
+ ] });
500
+ }
501
+ function SettingsSingle({ label, children }) {
502
+ return /* @__PURE__ */ jsx2("section", { className: "band-sf-one", "aria-label": label, children });
503
+ }
504
+ function SettingsChoices({ children }) {
505
+ return /* @__PURE__ */ jsx2("div", { className: "band-sf-choices", children });
506
+ }
507
+ function SettingsDanger({ children }) {
508
+ return /* @__PURE__ */ jsxs2("section", { className: "band-sf-dz", "aria-label": "\u5371\u9669\u64CD\u4F5C", children: [
509
+ /* @__PURE__ */ jsx2("h2", { children: "\u5371\u9669\u64CD\u4F5C" }),
510
+ children
511
+ ] });
512
+ }
513
+ function SettingsDangerRow({
514
+ title,
515
+ note,
516
+ action
517
+ }) {
518
+ return /* @__PURE__ */ jsxs2("div", { className: "band-sf-dz-r", children: [
519
+ /* @__PURE__ */ jsxs2("div", { children: [
520
+ /* @__PURE__ */ jsx2("b", { children: title }),
521
+ /* @__PURE__ */ jsx2("span", { children: note })
522
+ ] }),
523
+ action
524
+ ] });
525
+ }
526
+ function SettingsLoading({ text }) {
527
+ return /* @__PURE__ */ jsx2("output", { className: "band-sf-load", children: /* @__PURE__ */ jsxs2("span", { className: "tuning", children: [
528
+ /* @__PURE__ */ jsxs2("svg", { viewBox: "0 0 80 55", "aria-hidden": "true", children: [
529
+ /* @__PURE__ */ jsx2("line", { x1: "0", y1: "12", x2: "80", y2: "12" }),
530
+ /* @__PURE__ */ jsx2("line", { x1: "0", y1: "22", x2: "80", y2: "22" }),
531
+ /* @__PURE__ */ jsx2("line", { x1: "0", y1: "32", x2: "80", y2: "32" }),
532
+ /* @__PURE__ */ jsx2("line", { x1: "0", y1: "42", x2: "80", y2: "42" }),
533
+ /* @__PURE__ */ jsx2("line", { x1: "0", y1: "52", x2: "80", y2: "52" }),
534
+ /* @__PURE__ */ jsx2("circle", { cx: "20", cy: "37", r: "4.6", fill: "var(--ink)" }),
535
+ /* @__PURE__ */ jsx2("circle", { cx: "40", cy: "27", r: "4.6", fill: "var(--accent)" }),
536
+ /* @__PURE__ */ jsx2("circle", { cx: "60", cy: "17", r: "4.6", fill: "var(--ink)" })
537
+ ] }),
538
+ /* @__PURE__ */ jsx2("span", { children: text })
539
+ ] }) });
540
+ }
541
+ function SettingsEmpty({ children, action }) {
542
+ return /* @__PURE__ */ jsxs2("div", { className: "band-sf-empty rest", children: [
543
+ /* @__PURE__ */ jsx2("i", {}),
544
+ /* @__PURE__ */ jsx2("p", { children }),
545
+ action ?? null
546
+ ] });
547
+ }
548
+ function SettingsReadError({ children, retry }) {
549
+ return /* @__PURE__ */ jsx2("div", { className: "band-sf-error", children: /* @__PURE__ */ jsx2(ErrorNote, { retry, children }) });
550
+ }
551
+ function SettingsNoAccess({
552
+ title,
553
+ children,
554
+ action
555
+ }) {
556
+ const id = useId();
557
+ return /* @__PURE__ */ jsxs2("section", { className: "band-settings-no-access", "aria-labelledby": id, children: [
558
+ /* @__PURE__ */ jsx2("h2", { id, children: title }),
559
+ /* @__PURE__ */ jsx2("p", { children }),
560
+ action ? /* @__PURE__ */ jsx2("button", { type: "button", className: "btn", onClick: action.onClick, children: action.label }) : null
561
+ ] });
562
+ }
563
+ function SettingsNotice({ text }) {
564
+ return /* @__PURE__ */ jsx2("output", { className: "sr-only", "aria-live": "polite", children: text });
565
+ }
566
+ function SettingsSaved({ text = "\u5DF2\u4FDD\u5B58" }) {
567
+ return /* @__PURE__ */ jsx2("output", { className: "band-sf-ok", children: text });
568
+ }
569
+ function SettingsDialog({
570
+ title,
571
+ children,
572
+ actions,
573
+ onClose
574
+ }) {
575
+ const ref = useRef(null);
576
+ const titleId = useId();
577
+ useEffect(() => {
578
+ const el = ref.current;
579
+ const opener = document.activeElement instanceof HTMLElement ? document.activeElement : null;
580
+ if (el && !el.open) {
581
+ if (typeof el.showModal === "function") el.showModal();
582
+ else el.setAttribute("open", "");
583
+ }
584
+ return () => {
585
+ if (opener?.isConnected) opener.focus();
586
+ };
587
+ }, []);
588
+ return /* @__PURE__ */ jsxs2(
589
+ "dialog",
590
+ {
591
+ ref,
592
+ className: "band-sf-dialog",
593
+ "aria-labelledby": titleId,
594
+ onCancel: (e) => {
595
+ e.preventDefault();
596
+ onClose();
597
+ },
598
+ children: [
599
+ /* @__PURE__ */ jsx2("h2", { id: titleId, children: title }),
600
+ children,
601
+ /* @__PURE__ */ jsx2("div", { className: "band-sf-acts band-sf-acts-end", children: actions })
602
+ ]
603
+ }
604
+ );
605
+ }
606
+ function SettingsForm({
607
+ label,
608
+ onSubmit,
609
+ children
610
+ }) {
611
+ return /* @__PURE__ */ jsx2(
612
+ "form",
613
+ {
614
+ className: "band-sf-form",
615
+ "aria-label": label,
616
+ onSubmit: (e) => {
617
+ e.preventDefault();
618
+ onSubmit?.();
619
+ },
620
+ children
621
+ }
622
+ );
623
+ }
624
+ function SettingsField({
625
+ label,
626
+ required = false,
627
+ optional = false,
628
+ hint,
629
+ error,
630
+ count,
631
+ over = false,
632
+ children
633
+ }) {
634
+ const id = useId();
635
+ const noteId = `${id}-note`;
636
+ const note = error ?? hint;
637
+ const props = { id };
638
+ if (note) props["aria-describedby"] = `${noteId}-t`;
639
+ if (error) props["aria-invalid"] = true;
640
+ return /* @__PURE__ */ jsxs2("div", { className: "band-sf-fld", children: [
641
+ /* @__PURE__ */ jsxs2("label", { htmlFor: id, children: [
642
+ label,
643
+ required ? /* @__PURE__ */ jsxs2(Fragment, { children: [
644
+ " ",
645
+ /* @__PURE__ */ jsx2("span", { className: "band-sf-req", children: "\u5FC5\u586B" })
646
+ ] }) : optional ? /* @__PURE__ */ jsxs2(Fragment, { children: [
647
+ " ",
648
+ /* @__PURE__ */ jsx2("span", { className: "band-sf-optional", children: "\u9009\u586B" })
649
+ ] }) : null
650
+ ] }),
651
+ children(props),
652
+ note || count ? (
653
+ // 出错的话当场播报:校验被拦时焦点不动,读屏不会自己走到这一行
654
+ /* @__PURE__ */ jsxs2(
655
+ "p",
656
+ {
657
+ className: error ? "field-note err" : "field-note",
658
+ id: noteId,
659
+ role: error ? "alert" : void 0,
660
+ children: [
661
+ note ? /* @__PURE__ */ jsx2("span", { id: `${noteId}-t`, children: note }) : null,
662
+ count ? /* @__PURE__ */ jsx2("span", { className: over ? "field-count over" : "field-count", children: count }) : null
663
+ ]
664
+ }
665
+ )
666
+ ) : null
667
+ ] });
668
+ }
669
+ function SettingsActions({
670
+ children,
671
+ end = false
672
+ }) {
673
+ return /* @__PURE__ */ jsx2("div", { className: end ? "band-sf-acts band-sf-acts-end" : "band-sf-acts", children });
674
+ }
675
+ function SettingsSaveState({
676
+ saving,
677
+ saved,
678
+ failed,
679
+ missing
680
+ }) {
681
+ if (failed)
682
+ return /* @__PURE__ */ jsx2("p", { className: "band-sf-err", role: "alert", children: failed });
683
+ if (saved) return /* @__PURE__ */ jsx2(SettingsSaved, { text: saved === true ? "\u5DF2\u4FDD\u5B58" : saved });
684
+ const text = saving ?? missing;
685
+ if (!text) return /* @__PURE__ */ jsx2("span", { className: "band-sf-state", "aria-live": "polite" });
686
+ return /* @__PURE__ */ jsx2("span", { className: "band-sf-state", "aria-live": "polite", children: text });
687
+ }
688
+
337
689
  export {
338
690
  ErrorNote,
339
691
  InfoNote,
@@ -350,5 +702,28 @@ export {
350
702
  WorkshopIcon,
351
703
  WorkshopEmblem,
352
704
  BandMark,
353
- LogoLockup
705
+ LogoLockup,
706
+ settingsLinkHandler,
707
+ SettingsLayout,
708
+ SettingsBack,
709
+ SettingsHead,
710
+ SettingsRows,
711
+ SettingsRow,
712
+ SettingsFacts,
713
+ SettingsSection,
714
+ SettingsSingle,
715
+ SettingsChoices,
716
+ SettingsDanger,
717
+ SettingsDangerRow,
718
+ SettingsLoading,
719
+ SettingsEmpty,
720
+ SettingsReadError,
721
+ SettingsNoAccess,
722
+ SettingsNotice,
723
+ SettingsSaved,
724
+ SettingsDialog,
725
+ SettingsForm,
726
+ SettingsField,
727
+ SettingsActions,
728
+ SettingsSaveState
354
729
  };
@@ -1,5 +1,5 @@
1
1
  import * as react from 'react';
2
- import { ReactNode } from 'react';
2
+ import { ReactNode, Ref, MouseEvent } from 'react';
3
3
  import { MemberView } from '@kanyun-ai-infra/band-plugin-sdk';
4
4
 
5
5
  /**
@@ -96,4 +96,218 @@ declare function BandMark({ size, className }: {
96
96
  /** Band 字标(五线谱 + 三个音符 + 「Band」,DESIGN.md「标志」)。样式在 Band Design 的 `.logo`。 */
97
97
  declare function LogoLockup(): react.JSX.Element;
98
98
 
99
- export { AgentAvatar, Avatar, BandMark, ErrorNote, Glyph, type GlyphKind, INSTRUMENTS, InfoNote, type Instrument, LogoLockup, PaneHeader, WorkshopEmblem, WorkshopIcon, agentInstrument, avatarInitial, avatarTone, instrumentOf, memberOrder };
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 };