@dsh-workbench/core 0.2.12 → 0.2.13

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.
@@ -435,84 +435,6 @@ function createPanelStateStore() {
435
435
  };
436
436
  }
437
437
  //#endregion
438
- //#region ../shared/dist/host/cli-error.js
439
- function unquote(raw) {
440
- const value = raw.trim();
441
- if (value.length >= 2 && (value.startsWith("\"") && value.endsWith("\"") || value.startsWith("'") && value.endsWith("'"))) return value.slice(1, -1);
442
- return value;
443
- }
444
- function joinBlock(previous, line, fold) {
445
- if (previous === "") return line;
446
- return fold ? `${previous} ${line}` : `${previous}\n${line}`;
447
- }
448
- function parseCliErrorEnvelope(text) {
449
- if (text === void 0 || text.trim() === "") return void 0;
450
- const lines = text.split("\n");
451
- const start = lines.findIndex((line) => /^error:\s*$/.test(line.trim()) || /^error:\s*\S/.test(line.trim()));
452
- if (start === -1) return void 0;
453
- const scalars = {};
454
- let current;
455
- let block;
456
- const errorIndent = lines[start]?.search(/\S/) ?? 0;
457
- for (const line of lines.slice(start + 1)) {
458
- if (line.trim() === "") continue;
459
- const indent = line.search(/\S/);
460
- if (block !== void 0 && indent > block.indent) {
461
- scalars[block.key] = joinBlock(scalars[block.key] ?? "", line.trim(), block.fold);
462
- continue;
463
- }
464
- block = void 0;
465
- if (indent <= errorIndent) break;
466
- const match = /^\s*(code|message|help|exitCode):\s*(.*)$/.exec(line);
467
- if (match === null) {
468
- if (current !== void 0) scalars[current] = `${scalars[current] ?? ""} ${line.trim()}`.trim();
469
- continue;
470
- }
471
- const [, key, value] = match;
472
- if (key === void 0) continue;
473
- const raw = (value ?? "").trim();
474
- const marker = /^([|>])[0-9]*([+-]?)$/.exec(raw);
475
- if (marker !== null) {
476
- scalars[key] = "";
477
- block = {
478
- key,
479
- fold: marker[1] === ">",
480
- indent
481
- };
482
- current = key;
483
- continue;
484
- }
485
- scalars[key] = unquote(raw);
486
- current = key;
487
- }
488
- const code = scalars["code"];
489
- const message = scalars["message"];
490
- if (code === void 0 || code === "") return void 0;
491
- const exitCode = scalars["exitCode"];
492
- return {
493
- code,
494
- message: message ?? code,
495
- ...scalars["help"] === void 0 || scalars["help"] === "" ? {} : { help: scalars["help"] },
496
- ...exitCode === void 0 || Number.isNaN(Number(exitCode)) ? {} : { exitCode: Number(exitCode) }
497
- };
498
- }
499
- function helpUrlOf(envelope) {
500
- return /https?:\/\/[^\s"'`]+/.exec(envelope.help ?? "")?.[0];
501
- }
502
- //#endregion
503
- //#region ../shared/dist/host/data-root.js
504
- function dshHome() {
505
- const configured = process.env.DSH_HOME?.trim();
506
- if (configured !== void 0 && configured !== "") return configured;
507
- return `${process.env.HOME ?? ""}/.dsh`;
508
- }
509
- function workbenchRoot() {
510
- return `${dshHome()}/workbench`;
511
- }
512
- function workspacesRoot() {
513
- return `${workbenchRoot()}/workspaces`;
514
- }
515
- //#endregion
516
438
  //#region ../shared/dist/host/opencli-window.js
517
439
  var OPENCLI_WINDOW_ENV = "OPENCLI_WINDOW=background";
518
440
  function backgroundPrefix() {
@@ -1023,6 +945,84 @@ async function browserFind(options) {
1023
945
  };
1024
946
  }
1025
947
  //#endregion
948
+ //#region ../shared/dist/host/cli-error.js
949
+ function unquote(raw) {
950
+ const value = raw.trim();
951
+ if (value.length >= 2 && (value.startsWith("\"") && value.endsWith("\"") || value.startsWith("'") && value.endsWith("'"))) return value.slice(1, -1);
952
+ return value;
953
+ }
954
+ function joinBlock(previous, line, fold) {
955
+ if (previous === "") return line;
956
+ return fold ? `${previous} ${line}` : `${previous}\n${line}`;
957
+ }
958
+ function parseCliErrorEnvelope(text) {
959
+ if (text === void 0 || text.trim() === "") return void 0;
960
+ const lines = text.split("\n");
961
+ const start = lines.findIndex((line) => /^error:\s*$/.test(line.trim()) || /^error:\s*\S/.test(line.trim()));
962
+ if (start === -1) return void 0;
963
+ const scalars = {};
964
+ let current;
965
+ let block;
966
+ const errorIndent = lines[start]?.search(/\S/) ?? 0;
967
+ for (const line of lines.slice(start + 1)) {
968
+ if (line.trim() === "") continue;
969
+ const indent = line.search(/\S/);
970
+ if (block !== void 0 && indent > block.indent) {
971
+ scalars[block.key] = joinBlock(scalars[block.key] ?? "", line.trim(), block.fold);
972
+ continue;
973
+ }
974
+ block = void 0;
975
+ if (indent <= errorIndent) break;
976
+ const match = /^\s*(code|message|help|exitCode):\s*(.*)$/.exec(line);
977
+ if (match === null) {
978
+ if (current !== void 0) scalars[current] = `${scalars[current] ?? ""} ${line.trim()}`.trim();
979
+ continue;
980
+ }
981
+ const [, key, value] = match;
982
+ if (key === void 0) continue;
983
+ const raw = (value ?? "").trim();
984
+ const marker = /^([|>])[0-9]*([+-]?)$/.exec(raw);
985
+ if (marker !== null) {
986
+ scalars[key] = "";
987
+ block = {
988
+ key,
989
+ fold: marker[1] === ">",
990
+ indent
991
+ };
992
+ current = key;
993
+ continue;
994
+ }
995
+ scalars[key] = unquote(raw);
996
+ current = key;
997
+ }
998
+ const code = scalars["code"];
999
+ const message = scalars["message"];
1000
+ if (code === void 0 || code === "") return void 0;
1001
+ const exitCode = scalars["exitCode"];
1002
+ return {
1003
+ code,
1004
+ message: message ?? code,
1005
+ ...scalars["help"] === void 0 || scalars["help"] === "" ? {} : { help: scalars["help"] },
1006
+ ...exitCode === void 0 || Number.isNaN(Number(exitCode)) ? {} : { exitCode: Number(exitCode) }
1007
+ };
1008
+ }
1009
+ function helpUrlOf(envelope) {
1010
+ return /https?:\/\/[^\s"'`]+/.exec(envelope.help ?? "")?.[0];
1011
+ }
1012
+ //#endregion
1013
+ //#region ../shared/dist/host/data-root.js
1014
+ function dshHome() {
1015
+ const configured = process.env.DSH_HOME?.trim();
1016
+ if (configured !== void 0 && configured !== "") return configured;
1017
+ return `${process.env.HOME ?? ""}/.dsh`;
1018
+ }
1019
+ function workbenchRoot() {
1020
+ return `${dshHome()}/workbench`;
1021
+ }
1022
+ function workspacesRoot() {
1023
+ return `${workbenchRoot()}/workspaces`;
1024
+ }
1025
+ //#endregion
1026
1026
  //#region ../shared/dist/host/facts.js
1027
1027
  function createFactLog(options = {}) {
1028
1028
  const epoch = options.epoch ?? `e${Date.now().toString(36)}${Math.random().toString(36).slice(2, 8)}`;
@@ -88,13 +88,13 @@ export declare const kit: {
88
88
  gateOf(capability: apiModule.CapabilityPayload | null, mode: apiModule.GateMode): apiModule.UnitGate;
89
89
  splitBlocker(candidates: readonly (apiModule.ModuleBlocker | undefined)[]): apiModule.UnitBlockers;
90
90
  probeKeyOf(error: apiModule.ApiError | undefined): string | null;
91
- hasEnvSupply(manifest: apiModule.UnitManifest): boolean;
92
91
  declarationProblemsOf(subject: string, raw: unknown): readonly string[];
93
92
  readDeclaration(subject: string, raw: unknown): apiModule.DeclarationRead;
94
93
  WORKBENCH_API_VERSION: 1;
95
94
  SUPPORTED_API_VERSIONS: readonly number[];
96
95
  DECLARATION_KEYS: readonly string[];
97
96
  DECLARATION_REQUIRED_KEYS: readonly string[];
97
+ hasEnvSupply(manifest: apiModule.UnitManifest): boolean;
98
98
  apiGet<T>(route: string, query?: apiModule.Query, options?: apiModule.ApiGetOptions): Promise<apiModule.ApiResponse<T>>;
99
99
  apiPost<T>(route: string, body: Record<string, unknown>): Promise<apiModule.ApiResponse<T>>;
100
100
  ActivityBlock({ head, children }: apiModule.ActivityBlockProps): JSX.Element;
@@ -109,6 +109,10 @@ export declare const kit: {
109
109
  CardBoundary: typeof apiModule.CardBoundary;
110
110
  CardHead({ t, title, meta, refreshing, onRefresh, gear, gearOpen, onToggleGear, platformHref, platform, platformTitle, }: apiModule.CardHeadProps): JSX.Element;
111
111
  ChoiceGroup(props: apiModule.ChoiceGroupProps): JSX.Element;
112
+ Field({ label, hint, children }: apiModule.FieldProps): JSX.Element;
113
+ FormRows({ children }: apiModule.FormRowsProps): JSX.Element;
114
+ FormRow({ label, hint, problem, layout, children }: apiModule.FormRowProps): JSX.Element;
115
+ Link({ href, children, title, external, className }: apiModule.LinkProps): JSX.Element;
112
116
  LongTaskRow(props: apiModule.LongTaskRowProps): JSX.Element;
113
117
  Note({ tone, as, role, children }: apiModule.NoteProps): JSX.Element;
114
118
  SkeletonRows({ rows }: {
@@ -123,7 +127,6 @@ export declare const kit: {
123
127
  Rows({ children }: apiModule.RowsProps): JSX.Element;
124
128
  Row({ label, value }: apiModule.RowProps): JSX.Element;
125
129
  SaveBar({ busy, disabled, labels, onCancel, onSave }: apiModule.SaveBarProps): JSX.Element;
126
- SettingsSection({ label, title, action, children }: apiModule.SettingsSectionProps): JSX.Element;
127
130
  searchTextOf(option: apiModule.SelectFieldOption): string;
128
131
  defaultSelectFilter(option: apiModule.SelectFieldOption, query: string): boolean;
129
132
  filterSelectOptions(options: readonly apiModule.SelectFieldOption[], query: string, filter?: (option: apiModule.SelectFieldOption, query: string) => boolean): readonly apiModule.SelectFieldOption[];
@@ -131,11 +134,11 @@ export declare const kit: {
131
134
  stepSelectCursor(shown: readonly apiModule.SelectFieldOption[], cursor: number | null, step: 1 | -1): number | null;
132
135
  selectEntries(shown: readonly apiModule.SelectFieldOption[], emptyText: string | undefined): readonly import("@deepseek-ai/dsh-client-ui-primitives").MenuEntry[];
133
136
  SelectField(props: apiModule.SelectFieldProps): apiModule.ReactNode;
137
+ SettingsSection({ label, title, action, children }: apiModule.SettingsSectionProps): JSX.Element;
138
+ Spinner({ size, label }: apiModule.SpinnerProps): JSX.Element;
134
139
  Stack({ children }: apiModule.StackProps): JSX.Element;
135
140
  StateBlock({ title, dot, hint, actions, resolving, children }: apiModule.StateBlockProps): JSX.Element;
136
141
  TextField({ invalid, className, ...rest }: apiModule.TextFieldProps): JSX.Element;
137
- Field({ label, hint, children }: apiModule.FieldProps): JSX.Element;
138
- Link({ href, children, title, external, className }: apiModule.LinkProps): JSX.Element;
139
142
  IconConversation({ size }: apiModule.IconProps): JSX.Element;
140
143
  IconBrandMark({ size }: apiModule.IconProps): JSX.Element;
141
144
  IconOpencli({ size }: apiModule.IconProps): JSX.Element;
@@ -989,6 +992,8 @@ export declare const kit: {
989
992
  blockerPending(busy: boolean, running: string | null): boolean;
990
993
  blockerActionLabel(action: apiModule.BlockerActionShape, state: apiModule.BlockerLabelState, t: apiModule.BlockerLabelTranslate): string;
991
994
  holdBusy(since: number | null, now: number, floorMs: number): apiModule.BusyHold;
995
+ firstProblemOf(root: ParentNode): Element | null;
996
+ revealFirstProblem(root: ParentNode): boolean;
992
997
  parseCardsReport(payload: unknown): apiModule.PanelParse<apiModule.PanelCardsReport>;
993
998
  parseCrashReport(payload: unknown): apiModule.PanelParse<apiModule.PanelCrashReport>;
994
999
  createPanelStateStore(): apiModule.PanelStateStore;
@@ -1061,13 +1066,13 @@ export declare const kit: {
1061
1066
  gateOf(capability: apiModule.CapabilityPayload | null, mode: apiModule.GateMode): apiModule.UnitGate;
1062
1067
  splitBlocker(candidates: readonly (apiModule.ModuleBlocker | undefined)[]): apiModule.UnitBlockers;
1063
1068
  probeKeyOf(error: apiModule.ApiError | undefined): string | null;
1064
- hasEnvSupply(manifest: apiModule.UnitManifest): boolean;
1065
1069
  declarationProblemsOf(subject: string, raw: unknown): readonly string[];
1066
1070
  readDeclaration(subject: string, raw: unknown): apiModule.DeclarationRead;
1067
1071
  WORKBENCH_API_VERSION: 1;
1068
1072
  SUPPORTED_API_VERSIONS: readonly number[];
1069
1073
  DECLARATION_KEYS: readonly string[];
1070
1074
  DECLARATION_REQUIRED_KEYS: readonly string[];
1075
+ hasEnvSupply(manifest: apiModule.UnitManifest): boolean;
1071
1076
  apiGet<T>(route: string, query?: apiModule.Query, options?: apiModule.ApiGetOptions): Promise<apiModule.ApiResponse<T>>;
1072
1077
  apiPost<T>(route: string, body: Record<string, unknown>): Promise<apiModule.ApiResponse<T>>;
1073
1078
  ActivityBlock({ head, children }: apiModule.ActivityBlockProps): JSX.Element;
@@ -1082,6 +1087,10 @@ export declare const kit: {
1082
1087
  CardBoundary: typeof apiModule.CardBoundary;
1083
1088
  CardHead({ t, title, meta, refreshing, onRefresh, gear, gearOpen, onToggleGear, platformHref, platform, platformTitle, }: apiModule.CardHeadProps): JSX.Element;
1084
1089
  ChoiceGroup(props: apiModule.ChoiceGroupProps): JSX.Element;
1090
+ Field({ label, hint, children }: apiModule.FieldProps): JSX.Element;
1091
+ FormRows({ children }: apiModule.FormRowsProps): JSX.Element;
1092
+ FormRow({ label, hint, problem, layout, children }: apiModule.FormRowProps): JSX.Element;
1093
+ Link({ href, children, title, external, className }: apiModule.LinkProps): JSX.Element;
1085
1094
  LongTaskRow(props: apiModule.LongTaskRowProps): JSX.Element;
1086
1095
  Note({ tone, as, role, children }: apiModule.NoteProps): JSX.Element;
1087
1096
  SkeletonRows({ rows }: {
@@ -1096,7 +1105,6 @@ export declare const kit: {
1096
1105
  Rows({ children }: apiModule.RowsProps): JSX.Element;
1097
1106
  Row({ label, value }: apiModule.RowProps): JSX.Element;
1098
1107
  SaveBar({ busy, disabled, labels, onCancel, onSave }: apiModule.SaveBarProps): JSX.Element;
1099
- SettingsSection({ label, title, action, children }: apiModule.SettingsSectionProps): JSX.Element;
1100
1108
  searchTextOf(option: apiModule.SelectFieldOption): string;
1101
1109
  defaultSelectFilter(option: apiModule.SelectFieldOption, query: string): boolean;
1102
1110
  filterSelectOptions(options: readonly apiModule.SelectFieldOption[], query: string, filter?: (option: apiModule.SelectFieldOption, query: string) => boolean): readonly apiModule.SelectFieldOption[];
@@ -1104,11 +1112,11 @@ export declare const kit: {
1104
1112
  stepSelectCursor(shown: readonly apiModule.SelectFieldOption[], cursor: number | null, step: 1 | -1): number | null;
1105
1113
  selectEntries(shown: readonly apiModule.SelectFieldOption[], emptyText: string | undefined): readonly import("@deepseek-ai/dsh-client-ui-primitives").MenuEntry[];
1106
1114
  SelectField(props: apiModule.SelectFieldProps): apiModule.ReactNode;
1115
+ SettingsSection({ label, title, action, children }: apiModule.SettingsSectionProps): JSX.Element;
1116
+ Spinner({ size, label }: apiModule.SpinnerProps): JSX.Element;
1107
1117
  Stack({ children }: apiModule.StackProps): JSX.Element;
1108
1118
  StateBlock({ title, dot, hint, actions, resolving, children }: apiModule.StateBlockProps): JSX.Element;
1109
1119
  TextField({ invalid, className, ...rest }: apiModule.TextFieldProps): JSX.Element;
1110
- Field({ label, hint, children }: apiModule.FieldProps): JSX.Element;
1111
- Link({ href, children, title, external, className }: apiModule.LinkProps): JSX.Element;
1112
1120
  IconConversation({ size }: apiModule.IconProps): JSX.Element;
1113
1121
  IconBrandMark({ size }: apiModule.IconProps): JSX.Element;
1114
1122
  IconOpencli({ size }: apiModule.IconProps): JSX.Element;
@@ -1962,6 +1970,8 @@ export declare const kit: {
1962
1970
  blockerPending(busy: boolean, running: string | null): boolean;
1963
1971
  blockerActionLabel(action: apiModule.BlockerActionShape, state: apiModule.BlockerLabelState, t: apiModule.BlockerLabelTranslate): string;
1964
1972
  holdBusy(since: number | null, now: number, floorMs: number): apiModule.BusyHold;
1973
+ firstProblemOf(root: ParentNode): Element | null;
1974
+ revealFirstProblem(root: ParentNode): boolean;
1965
1975
  parseCardsReport(payload: unknown): apiModule.PanelParse<apiModule.PanelCardsReport>;
1966
1976
  parseCrashReport(payload: unknown): apiModule.PanelParse<apiModule.PanelCrashReport>;
1967
1977
  createPanelStateStore(): apiModule.PanelStateStore;
@@ -2029,13 +2039,13 @@ export declare const kit: {
2029
2039
  gateOf(capability: apiModule.CapabilityPayload | null, mode: apiModule.GateMode): apiModule.UnitGate;
2030
2040
  splitBlocker(candidates: readonly (apiModule.ModuleBlocker | undefined)[]): apiModule.UnitBlockers;
2031
2041
  probeKeyOf(error: apiModule.ApiError | undefined): string | null;
2032
- hasEnvSupply(manifest: apiModule.UnitManifest): boolean;
2033
2042
  declarationProblemsOf(subject: string, raw: unknown): readonly string[];
2034
2043
  readDeclaration(subject: string, raw: unknown): apiModule.DeclarationRead;
2035
2044
  WORKBENCH_API_VERSION: 1;
2036
2045
  SUPPORTED_API_VERSIONS: readonly number[];
2037
2046
  DECLARATION_KEYS: readonly string[];
2038
2047
  DECLARATION_REQUIRED_KEYS: readonly string[];
2048
+ hasEnvSupply(manifest: apiModule.UnitManifest): boolean;
2039
2049
  apiGet<T>(route: string, query?: apiModule.Query, options?: apiModule.ApiGetOptions): Promise<apiModule.ApiResponse<T>>;
2040
2050
  apiPost<T>(route: string, body: Record<string, unknown>): Promise<apiModule.ApiResponse<T>>;
2041
2051
  ActivityBlock({ head, children }: apiModule.ActivityBlockProps): JSX.Element;
@@ -2050,6 +2060,10 @@ export declare const kit: {
2050
2060
  CardBoundary: typeof apiModule.CardBoundary;
2051
2061
  CardHead({ t, title, meta, refreshing, onRefresh, gear, gearOpen, onToggleGear, platformHref, platform, platformTitle, }: apiModule.CardHeadProps): JSX.Element;
2052
2062
  ChoiceGroup(props: apiModule.ChoiceGroupProps): JSX.Element;
2063
+ Field({ label, hint, children }: apiModule.FieldProps): JSX.Element;
2064
+ FormRows({ children }: apiModule.FormRowsProps): JSX.Element;
2065
+ FormRow({ label, hint, problem, layout, children }: apiModule.FormRowProps): JSX.Element;
2066
+ Link({ href, children, title, external, className }: apiModule.LinkProps): JSX.Element;
2053
2067
  LongTaskRow(props: apiModule.LongTaskRowProps): JSX.Element;
2054
2068
  Note({ tone, as, role, children }: apiModule.NoteProps): JSX.Element;
2055
2069
  SkeletonRows({ rows }: {
@@ -2064,7 +2078,6 @@ export declare const kit: {
2064
2078
  Rows({ children }: apiModule.RowsProps): JSX.Element;
2065
2079
  Row({ label, value }: apiModule.RowProps): JSX.Element;
2066
2080
  SaveBar({ busy, disabled, labels, onCancel, onSave }: apiModule.SaveBarProps): JSX.Element;
2067
- SettingsSection({ label, title, action, children }: apiModule.SettingsSectionProps): JSX.Element;
2068
2081
  searchTextOf(option: apiModule.SelectFieldOption): string;
2069
2082
  defaultSelectFilter(option: apiModule.SelectFieldOption, query: string): boolean;
2070
2083
  filterSelectOptions(options: readonly apiModule.SelectFieldOption[], query: string, filter?: (option: apiModule.SelectFieldOption, query: string) => boolean): readonly apiModule.SelectFieldOption[];
@@ -2072,11 +2085,11 @@ export declare const kit: {
2072
2085
  stepSelectCursor(shown: readonly apiModule.SelectFieldOption[], cursor: number | null, step: 1 | -1): number | null;
2073
2086
  selectEntries(shown: readonly apiModule.SelectFieldOption[], emptyText: string | undefined): readonly import("@deepseek-ai/dsh-client-ui-primitives").MenuEntry[];
2074
2087
  SelectField(props: apiModule.SelectFieldProps): apiModule.ReactNode;
2088
+ SettingsSection({ label, title, action, children }: apiModule.SettingsSectionProps): JSX.Element;
2089
+ Spinner({ size, label }: apiModule.SpinnerProps): JSX.Element;
2075
2090
  Stack({ children }: apiModule.StackProps): JSX.Element;
2076
2091
  StateBlock({ title, dot, hint, actions, resolving, children }: apiModule.StateBlockProps): JSX.Element;
2077
2092
  TextField({ invalid, className, ...rest }: apiModule.TextFieldProps): JSX.Element;
2078
- Field({ label, hint, children }: apiModule.FieldProps): JSX.Element;
2079
- Link({ href, children, title, external, className }: apiModule.LinkProps): JSX.Element;
2080
2093
  IconConversation({ size }: apiModule.IconProps): JSX.Element;
2081
2094
  IconBrandMark({ size }: apiModule.IconProps): JSX.Element;
2082
2095
  IconOpencli({ size }: apiModule.IconProps): JSX.Element;
@@ -2930,6 +2943,8 @@ export declare const kit: {
2930
2943
  blockerPending(busy: boolean, running: string | null): boolean;
2931
2944
  blockerActionLabel(action: apiModule.BlockerActionShape, state: apiModule.BlockerLabelState, t: apiModule.BlockerLabelTranslate): string;
2932
2945
  holdBusy(since: number | null, now: number, floorMs: number): apiModule.BusyHold;
2946
+ firstProblemOf(root: ParentNode): Element | null;
2947
+ revealFirstProblem(root: ParentNode): boolean;
2933
2948
  parseCardsReport(payload: unknown): apiModule.PanelParse<apiModule.PanelCardsReport>;
2934
2949
  parseCrashReport(payload: unknown): apiModule.PanelParse<apiModule.PanelCrashReport>;
2935
2950
  createPanelStateStore(): apiModule.PanelStateStore;
@@ -1,4 +1,8 @@
1
1
  import type { ReactNode } from '@dsh-workbench/shared';
2
+ export declare function KitNavProvider({ children }: {
3
+ readonly children?: ReactNode;
4
+ }): JSX.Element;
5
+ export declare function KitNav(): JSX.Element | null;
2
6
  export declare function Section({ title, note, children, }: {
3
7
  readonly title: string;
4
8
  readonly note?: string;
@@ -0,0 +1 @@
1
+ export declare function hashTargetsSection(anchor: string, hash: string): boolean;
@@ -1,4 +1,5 @@
1
1
  export declare function RowsSection(): JSX.Element;
2
+ export declare function FormSection(): JSX.Element;
2
3
  export declare function ActivitySection(): JSX.Element;
3
4
  export declare function LongTaskSection(): JSX.Element;
4
5
  export declare function IconSection(): JSX.Element;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dsh-workbench/core",
3
- "version": "0.2.12",
3
+ "version": "0.2.13",
4
4
  "description": "个人工作台:Agent 工作台外壳(左栏 + 常驻对话 + 自研右侧数据面板)。",
5
5
  "type": "module",
6
6
  "icon": "./icon.svg",
@@ -87,7 +87,7 @@
87
87
  "access": "public"
88
88
  },
89
89
  "dependencies": {
90
- "@dsh-workbench/shared": "0.2.12"
90
+ "@dsh-workbench/shared": "0.2.13"
91
91
  },
92
92
  "scripts": {
93
93
  "build": "node scripts/build.mjs && node scripts/build-types.mjs",
@@ -82,7 +82,7 @@ h(Field, { label: t('myunit.mode'), hint: t('myunit.modeHint') },
82
82
  ```
83
83
 
84
84
  **`TextField`** —— 单行输入(`invalid` 标红;其余是原生 input 的 props)。
85
- **`ChoiceGroup`** —— 单选 / 多选(`multi` 区分;`value` 或 `values` + `onChange`;选项 `{ value, label, hint?, disabled? }`)。
85
+ **`ChoiceGroup`** —— 单选 / 多选(`multi` 区分;`value` 或 `values` + `onChange`;选项 `{ value, label, description? }`;`disabled` 在整组上)。
86
86
  **`SelectField`** —— 下拉:默认是**常规下拉框**(跟 `TextField` 一套),无边框那种给 `variant: 'ghost'`;
87
87
  搜索、分组、禁用项、加载态、空态、页脚都是它的 prop。
88
88
  **`SaveBar`** —— 设置面底下那条"取消 / 保存"(`busy` / `disabled` / `labels` / `onCancel` / `onSave`)。
@@ -91,7 +91,57 @@ h(Field, { label: t('myunit.mode'), hint: t('myunit.modeHint') },
91
91
  kit 有上面这几件;手写的会拿到浏览器默认样式,而且**判据会红**(`check:style`)。
92
92
  `<textarea>` 例外(dsh 与 kit 都没有多行输入)——真要用就直接写,其余形态别开这个口子。
93
93
 
94
- ## 5 别的
94
+ 控件怎么排成一行(标签位置、错误态、尺寸)见 §5。
95
+
96
+ ## 5 表单这一层:面 → 行 → 控件
97
+
98
+ 表单三层:**面**(`SettingsSection` / `SaveBar`)→ **行**(`FormRows` + `FormRow`)→
99
+ **控件**(`TextField` / `SelectField` / `ChoiceGroup`)。**一行一个字段**,行这一层只有一个出处。
100
+
101
+ ```tsx
102
+ h(FormRows, null,
103
+ h(FormRow, { label: t('myunit.mode'), hint: t('myunit.modeHint') },
104
+ h(SelectField, { value, options, onChange: setMode })),
105
+ h(FormRow, { label: t('myunit.env'), problem: problemOfEnv },
106
+ h(ChoiceGroup, { label: t('myunit.env'), name: 'env', options, value: env, onChange: setEnv })))
107
+ ```
108
+
109
+ - **标签归 `FormRow`**(`label`):默认标签在左,`layout: 'stacked'` 才是上下堆叠(等于原来的 `Field`)。
110
+ **控件只给数据与语义 prop**,不写外观类名(§7 第 1 条)。进了 `FormRow` 就别再给控件自己的
111
+ `label` / `hint`(`SelectField` 那两件是没 `FormRow` 时的兜底,重复给会多一个标签、还退回上下堆叠)。
112
+ - **`hint` 与 `problem` 二选一**:给了 `problem` 就不画 `hint` —— 同一格不叠两段话,有错先显示错。
113
+
114
+ **错误态**:位置与长相由 `FormRow.problem` + 控件的 `invalid` 定(`SelectField` / `TextField` 同形,
115
+ `ChoiceGroup` 见它自己的档)。**校验逻辑留在模块**:只写"算错误"的纯函数与文案
116
+ (例:cmdb 的 `CmdbFormProblem` 那类),**别指望外壳给校验引擎**(不做 `rules` / `validate`、
117
+ 不做联动、不持有值)。提交失败后调 `kit.lib.revealFirstProblem(root)`(`root` = 表单容器)——
118
+ 把第一处错误带进视口、并聚焦它里面的控件。
119
+
120
+ **选择类用哪件**:候选多 / 要搜索 / 要分组 / 名单可能读不到 ⇒ `SelectField`(`search` 默认开、
121
+ `emptyText`、`loading` + `loadingText`、`fallback`、`variant: 'boxed' | 'ghost'`);
122
+ 候选少、要一眼看全 / 多选 ⇒ `ChoiceGroup`(`multi`、`variant`、`loading` + `loadingText`、
123
+ `emptyText`、`options[].description`)。两件的 `loading` / `loadingText` / `emptyText` / `variant`
124
+ 是一个意思;**loading 那颗一律是 `Spinner`**(`h(Spinner, { size: 14 })`)—— **别自己写转圈**。
125
+
126
+ **尺寸口径**:字段控件默认 **28**(dsh 紧凑档),**别自己调高**;行距**只有一档**、归 `FormRow(s)`,
127
+ 模块里不该出现管行距的 `gap` / `margin`(横向的、卡片/清单网格的不算)。
128
+
129
+ **反例(别这么写)**:
130
+
131
+ ```tsx
132
+ h('span', { className: styles.label }, t('myunit.mode')) // ← 紧邻控件的手写标签行:check:style 红
133
+ h('div', { className: styles.form }, …) // ← .form { gap: 8px }:自己管行距也红
134
+ ```
135
+
136
+ `<span className={styles.label}>` 紧邻控件、或给表单容器自己写 `gap` —— 这正是 `check:style` 那条
137
+ 棘轮(F1 手写行 / F2 自己管行距)盯的东西;`FORM_ROW_EXEMPT` 是**等外壳发版后要清空的历史账**
138
+ ("迁一处删一行"),**不是"可以继续这么写"的许可**。
139
+
140
+ **时点**:这一层随外壳**发版**后模块才能用 —— 模块的 `peerDependencies` 下限得先有它
141
+ (`check:kit` 拿 peer 下限那份外壳比,不抬必红);迁移步骤见
142
+ [ADR-0052](../../../../docs/adr/0052-the-form-row-is-one-component.md) §10。
143
+
144
+ ## 6 别的
95
145
 
96
146
  - **`Link`** —— **唯一**的外链写法:`h(Link, { href, children })`。自带新窗口 + `rel="noopener noreferrer"`
97
147
  + 正文色 + hover 下划线 + 那颗小箭头。**别去掉箭头、别在文案里自带 `→`、别手写 `<a>`**(判据红)。
@@ -103,7 +153,7 @@ kit 有上面这几件;手写的会拿到浏览器默认样式,而且**判
103
153
  - **图标** —— `IconPanelScreen`(面板/卡片那类)、`IconModuleMark`(我们的模块类型标)、
104
154
  `IconNpm` / `IconOpencli`(包类型);其余图形一律从 dsh 的 `Icon*Regular` 拿,**别自己画图标**。
105
155
 
106
- ## 6 三条纪律
156
+ ## 7 三条纪律
107
157
 
108
158
  1. **调用点只给数据与语义**:`h(Card, { title, refreshing, onRefresh }, …)`。别从外面塞外观类名
109
159
  —— 只有布局槽例外(通栏、外边距、网格位置)。
@@ -114,7 +164,7 @@ kit 有上面这几件;手写的会拿到浏览器默认样式,而且**判
114
164
  3. **自己写 CSS 只写布局**(flex / grid / gap / 对齐)。要一个新的"长得像控件"的东西时,
115
165
  先问一句:kit 里是不是已经有了?
116
166
 
117
- ## 7 kit 里没有、但你确定要标准控件
167
+ ## 8 kit 里没有、但你确定要标准控件
118
168
 
119
169
  见 [PANEL.md](PANEL.md) §9:可以引 dsh 的客户端原语,但**只在浏览器侧**、并在
120
170
  `package.json` 的 `dsh.client.external` 里声明 `@deepseek-ai/dsh-client-ui-primitives`
@@ -15,7 +15,7 @@
15
15
  | `standalone` | "只有工作台源码在场才编得过" ⇒ 发布出去就散架 | `build 在外壳源码不在场时失败` / `typecheck 在外壳源码不在场时失败` | 别引用工作台源码;`tsconfig` 里 `paths` 的值必须是**相对路径**(`"./src/*"`,不是 `"src/*"`) |
16
16
  | `markers` | 内网域名 / 企业内部名泄进要公开发布的包 | `命中内网 npm 源` / `命中内网域名` | 那些值不该写进包里;要配就落到模块自己的配置面,或走用户自己声明的来源 |
17
17
  | `alias` | `@/` 伸进工作台源码 ⇒ 装的人手上没有那份源码 | `…指向外壳源码` | `@/` 只许指自己的 `src/`;跨包的东西走包名 |
18
- | `style` | 模块定义 `.wb-*` 去覆盖外壳的类(全局类名,两份规则按注入顺序打架) | `定义了外壳前缀 wb-* 的 CSS 规则` / `借用了契约里没有的类` | 自己的样式走 `*.module.css`(类名**不带** `wb-` 前缀,构建时按文件路径哈希);外壳给的类只能用契约里列出的那些,要借就写 `:global(.wb-…)` |
18
+ | `style` | 模块定义 `.wb-*` 去覆盖外壳的类(全局类名,两份规则按注入顺序打架) | `定义了外壳前缀 wb-* 的 CSS 规则` / `借用了契约里没有的类` | 自己的样式走 `*.module.css`(类名**不带** `wb-` 前缀,构建时按文件路径哈希);外壳给的类只能用契约里列出的那些,要借就写 `:global(.wb-…)`;被 `样式归属` 判红的手写「标签+控件」行或模块自己管的行距 ⇒ 改用 `FormRow(s)`([KIT.md](KIT.md) §5) |
19
19
 
20
20
  ## 什么时候该怀疑门禁本身
21
21