@dsh-workbench/shared 1.1.7 → 1.1.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/blocker-table.d.ts +1 -0
- package/dist/api/blocker-table.js +1 -1
- package/dist/components/SelectField.css +0 -6
- package/dist/components/SelectField.d.ts +2 -3
- package/dist/components/SelectField.js +9 -30
- package/dist/components/SelectFieldNodes.d.ts +3 -1
- package/dist/components/SelectFieldNodes.js +2 -1
- package/dist/components/SelectFieldWheel.d.ts +9 -0
- package/dist/components/SelectFieldWheel.js +35 -0
- package/dist/config/constants.d.ts +4 -4
- package/dist/config/constants.js +4 -4
- package/dist/contract/capabilities.d.ts +29 -0
- package/dist/contract/capabilities.js +1 -0
- package/dist/contract/needs.d.ts +3 -3
- package/dist/contract/needs.js +1 -1
- package/dist/contract/plugin-source.d.ts +19 -0
- package/dist/contract/protocol/bundle-install.d.ts +3 -1
- package/dist/contract/protocol/bundle-install.js +3 -0
- package/dist/contract/protocol/market-index.d.ts +1 -0
- package/dist/contract/protocol/platform.d.ts +1 -1
- package/dist/contract/runner.d.ts +6 -0
- package/dist/contract/runner.js +1 -0
- package/dist/contract/unit.d.ts +4 -4
- package/dist/host/adapter-contract.d.ts +3 -0
- package/dist/host/adapter-contract.js +93 -0
- package/dist/host/adapter-set.js +6 -4
- package/dist/host/adapter-types.d.ts +1 -1
- package/dist/host/bundle-manager.d.ts +3 -0
- package/dist/host/bundle-manager.js +181 -0
- package/dist/host/cli.js +3 -1
- package/dist/host/config-sync.d.ts +48 -0
- package/dist/host/config-sync.js +342 -0
- package/dist/host/dsh-plugins.d.ts +5 -0
- package/dist/host/dsh-plugins.js +51 -0
- package/dist/host/extension.js +1 -1
- package/dist/host/flow/check.d.ts +9 -0
- package/dist/host/flow/check.js +179 -0
- package/dist/host/flow/expr.d.ts +30 -0
- package/dist/host/flow/expr.js +99 -0
- package/dist/host/flow/kit.d.ts +14 -0
- package/dist/host/flow/kit.js +13 -0
- package/dist/host/flow/run.d.ts +13 -0
- package/dist/host/flow/run.js +287 -0
- package/dist/host/flow/types.d.ts +188 -0
- package/dist/host/flow/types.js +1 -0
- package/dist/host/host-actions.d.ts +2 -0
- package/dist/host/host-actions.js +5 -0
- package/dist/host/host-kit.d.ts +19 -0
- package/dist/host/host-kit.js +14 -0
- package/dist/host/http.d.ts +2 -6
- package/dist/host/http.js +2 -2
- package/dist/host/installed-module.d.ts +23 -0
- package/dist/host/installed-module.js +264 -0
- package/dist/host/lib-install.d.ts +7 -9
- package/dist/host/lib-install.js +9 -12
- package/dist/host/lib-runners.d.ts +3 -5
- package/dist/host/lib-runners.js +2 -2
- package/dist/host/local-declaration.d.ts +7 -0
- package/dist/host/local-declaration.js +33 -0
- package/dist/host/market-index-claim.d.ts +0 -1
- package/dist/host/market-index-claim.js +0 -3
- package/dist/host/market-index-doc.d.ts +1 -45
- package/dist/host/market-index-parse.d.ts +9 -4
- package/dist/host/market-index-parse.js +17 -92
- package/dist/host/market-index.d.ts +5 -6
- package/dist/host/market-index.js +29 -87
- package/dist/host/module-root.d.ts +1 -0
- package/dist/host/module-root.js +5 -0
- package/dist/host/npm-registry.d.ts +10 -0
- package/dist/host/npm-registry.js +16 -0
- package/dist/host/opencli-runner.d.ts +2 -1
- package/dist/host/package-content.d.ts +7 -0
- package/dist/host/package-content.js +69 -0
- package/dist/host/package-kind.d.ts +13 -0
- package/dist/host/package-kind.js +51 -0
- package/dist/host/package-route.d.ts +31 -0
- package/dist/host/package-route.js +57 -0
- package/dist/host/paths.d.ts +11 -19
- package/dist/host/paths.js +16 -43
- package/dist/host/platform.js +1 -1
- package/dist/host/plugin-ledger.d.ts +8 -8
- package/dist/host/plugin-ledger.js +23 -16
- package/dist/host/plugin.d.ts +1 -2
- package/dist/host/plugin.js +4 -5
- package/dist/host/port-types.d.ts +42 -9
- package/dist/host/preflight.d.ts +3 -2
- package/dist/host/profile-create.d.ts +18 -0
- package/dist/host/profile-create.js +118 -0
- package/dist/host/profile-registries.d.ts +10 -0
- package/dist/host/profile-registries.js +150 -0
- package/dist/host/registry-declaration.d.ts +21 -0
- package/dist/host/registry-declaration.js +33 -0
- package/dist/host/release-probe.d.ts +34 -0
- package/dist/host/release-probe.js +88 -0
- package/dist/host/runner.d.ts +26 -0
- package/dist/host/runner.js +50 -0
- package/dist/host/shell.d.ts +0 -2
- package/dist/host/shell.js +0 -7
- package/dist/host/spec.js +1 -1
- package/dist/host/supply-index.d.ts +1 -0
- package/dist/host/supply-index.js +8 -7
- package/dist/host/supply-port.js +13 -3
- package/dist/host/unit-face.d.ts +2 -1
- package/dist/host/version.js +11 -6
- package/dist/host.d.ts +24 -6
- package/dist/host.js +23 -6
- package/dist/i18n/en.js +19 -20
- package/dist/i18n/index.d.ts +20 -21
- package/dist/i18n/runtime-terms.d.ts +15 -0
- package/dist/i18n/runtime-terms.js +22 -0
- package/dist/i18n/zh.d.ts +19 -20
- package/dist/i18n/zh.js +19 -20
- package/dist/index.d.ts +4 -0
- package/dist/index.js +4 -0
- package/dist/testing/fake-adapter-set.d.ts +5 -0
- package/dist/testing/fake-adapter-set.js +150 -0
- package/dist/testing/needs.js +1 -1
- package/dist/testing.d.ts +1 -0
- package/dist/testing.js +1 -0
- package/package.json +1 -1
- package/src/api/__tests__/blocker-table.test.ts +25 -0
- package/src/api/{workbench-client.test.ts → __tests__/workbench-client.test.ts} +2 -2
- package/src/api/blocker-table.ts +6 -2
- package/src/components/SelectField.css +0 -6
- package/src/components/SelectField.tsx +7 -36
- package/src/components/SelectFieldNodes.tsx +4 -1
- package/src/components/{Card.test.tsx → __tests__/Card.test.tsx} +2 -2
- package/src/components/{CardAnchor.test.tsx → __tests__/CardAnchor.test.tsx} +3 -3
- package/src/components/{CardHead.test.tsx → __tests__/CardHead.test.tsx} +1 -1
- package/src/components/{ChoiceGroup.test.tsx → __tests__/ChoiceGroup.test.tsx} +2 -2
- package/src/components/{ConfirmBar.test.tsx → __tests__/ConfirmBar.test.tsx} +5 -5
- package/src/components/{Field.test.tsx → __tests__/Field.test.tsx} +1 -1
- package/src/components/{Form.test.tsx → __tests__/Form.test.tsx} +5 -5
- package/src/components/{FormActions.test.tsx → __tests__/FormActions.test.tsx} +3 -3
- package/src/components/{FormRows.test.tsx → __tests__/FormRows.test.tsx} +2 -2
- package/src/components/{FormTypes.test.ts → __tests__/FormTypes.test.ts} +1 -1
- package/src/components/{FormWiring.test.tsx → __tests__/FormWiring.test.tsx} +4 -4
- package/src/components/{Link.test.tsx → __tests__/Link.test.tsx} +1 -1
- package/src/components/{Rows.test.tsx → __tests__/Rows.test.tsx} +2 -2
- package/src/components/{SelectField.test.tsx → __tests__/SelectField.test.tsx} +7 -37
- package/src/components/{Spinner.test.tsx → __tests__/Spinner.test.tsx} +4 -4
- package/src/config/{constants.test.ts → __tests__/constants.test.ts} +1 -1
- package/src/config/{storage.test.ts → __tests__/storage.test.ts} +1 -1
- package/src/config/constants.ts +4 -4
- package/src/contract/capabilities.ts +30 -0
- package/src/contract/needs.ts +4 -4
- package/src/contract/plugin-source.ts +24 -0
- package/src/contract/protocol/bundle-install.ts +7 -1
- package/src/contract/protocol/market-index.ts +1 -0
- package/src/contract/protocol/platform.ts +1 -1
- package/src/contract/runner.ts +6 -0
- package/src/contract/unit.ts +4 -4
- package/src/host/{extension.test.ts → __tests__/extension.test.ts} +4 -4
- package/src/host/{install-failure.test.ts → __tests__/install-failure.test.ts} +1 -1
- package/src/host/{installed-sources.test.ts → __tests__/installed-sources.test.ts} +1 -1
- package/src/host/__tests__/lib-runners.test.ts +11 -2
- package/src/host/__tests__/market-index-assemble.test.ts +26 -0
- package/src/host/__tests__/market-index.test.ts +41 -51
- package/src/host/__tests__/plugin-ledger.test.ts +19 -6
- package/src/host/{recipe.test.ts → __tests__/recipe.test.ts} +3 -3
- package/src/host/__tests__/runtime-layout.test.ts +14 -0
- package/src/host/__tests__/supply-index.test.ts +2 -2
- package/src/host/__tests__/supply-port.test.ts +32 -0
- package/src/host/adapter-contract.ts +93 -0
- package/src/host/adapter-set.ts +6 -4
- package/src/host/adapter-types.ts +1 -1
- package/src/host/bundle-manager.ts +195 -0
- package/src/host/cli.ts +3 -1
- package/src/host/config-sync.ts +386 -0
- package/src/host/dsh-plugins.ts +60 -0
- package/src/host/extension.ts +1 -1
- package/src/host/flow/__tests__/advanced.test.ts +302 -0
- package/src/host/flow/__tests__/check.test.ts +169 -0
- package/src/host/flow/__tests__/run.test.ts +301 -0
- package/src/host/flow/check.ts +216 -0
- package/src/host/flow/expr.ts +94 -0
- package/src/host/flow/kit.ts +30 -0
- package/src/host/flow/run.ts +325 -0
- package/src/host/flow/types.ts +199 -0
- package/src/host/host-actions.ts +7 -0
- package/src/host/host-kit.ts +26 -0
- package/src/host/http.ts +6 -8
- package/src/host/installed-module.ts +272 -0
- package/src/host/lib-install.ts +14 -12
- package/src/host/lib-runners.ts +6 -8
- package/src/host/local-declaration.ts +37 -0
- package/src/host/market-index-claim.ts +0 -4
- package/src/host/market-index-doc.ts +1 -53
- package/src/host/market-index-parse.ts +21 -85
- package/src/host/market-index.ts +33 -127
- package/src/host/module-root.ts +6 -0
- package/src/host/npm-registry.ts +21 -0
- package/src/host/package-content.ts +76 -0
- package/src/host/package-kind.ts +63 -0
- package/src/host/package-route.ts +93 -0
- package/src/host/paths.ts +27 -52
- package/src/host/platform.ts +1 -1
- package/src/host/plugin-ledger.ts +25 -18
- package/src/host/plugin.ts +4 -5
- package/src/host/port-types.ts +43 -9
- package/src/host/preflight.ts +3 -2
- package/src/host/profile-create.ts +133 -0
- package/src/host/profile-registries.ts +145 -0
- package/src/host/registry-declaration.ts +70 -0
- package/src/host/release-probe.ts +202 -0
- package/src/host/shell.ts +0 -9
- package/src/host/spec.ts +1 -1
- package/src/host/supply-index.ts +9 -7
- package/src/host/supply-port.ts +13 -3
- package/src/host/unit-face.ts +2 -1
- package/src/host/version.ts +12 -7
- package/src/host.ts +34 -6
- package/src/i18n/{index.test.ts → __tests__/index.test.ts} +3 -3
- package/src/i18n/{merge.test.ts → __tests__/merge.test.ts} +1 -1
- package/src/i18n/{zh.test.ts → __tests__/zh.test.ts} +2 -2
- package/src/i18n/en.ts +19 -21
- package/src/i18n/runtime-terms.ts +26 -0
- package/src/i18n/zh.ts +19 -21
- package/src/index.ts +4 -0
- package/src/lib/{board-fact.test.ts → __tests__/board-fact.test.ts} +1 -1
- package/src/lib/{busy.test.ts → __tests__/busy.test.ts} +1 -1
- package/src/lib/{form-problem.test.ts → __tests__/form-problem.test.ts} +1 -1
- package/src/lib/{git-address.test.ts → __tests__/git-address.test.ts} +1 -1
- package/src/lib/{react.test.ts → __tests__/react.test.ts} +1 -1
- package/src/lib/{text.test.ts → __tests__/text.test.ts} +1 -1
- package/src/lib/{ui.test.ts → __tests__/ui.test.ts} +1 -1
- package/src/testing/__tests__/fake-adapter-set.test.ts +28 -0
- package/src/testing/fake-adapter-set.ts +173 -0
- package/src/testing/needs.ts +1 -1
- package/src/testing.ts +1 -0
- package/src/host/__tests__/command-name.test.ts +0 -16
- package/src/host/__tests__/opencli-runner.test.ts +0 -120
- package/src/host/channel-plugin.ts +0 -55
- package/src/host/command-name.ts +0 -15
- package/src/host/opencli-runner.ts +0 -76
- package/src/host/paths.test.ts +0 -26
- package/src/host/plugin-source.ts +0 -137
- package/src/host/runtime-links.test.ts +0 -31
- package/src/host/runtime-links.ts +0 -23
|
@@ -56,7 +56,6 @@ export interface SelectFieldProps {
|
|
|
56
56
|
readonly selection?: 'check' | 'fill' | undefined;
|
|
57
57
|
readonly variant?: 'ghost' | 'boxed' | undefined;
|
|
58
58
|
readonly iconOnly?: boolean | undefined;
|
|
59
|
-
readonly revealId?: string | undefined;
|
|
60
59
|
readonly ariaLabel?: string | undefined;
|
|
61
60
|
readonly title?: string | undefined;
|
|
62
61
|
readonly id?: string | undefined;
|
|
@@ -76,41 +75,15 @@ function triggerClassOf(variantClass: string | undefined, iconOnly: boolean): st
|
|
|
76
75
|
return mergeClass('wb-selectTrigger', variantClass, iconOnly ? 'wb-selectIconOnly' : undefined);
|
|
77
76
|
}
|
|
78
77
|
|
|
79
|
-
function itemOf(option: SelectFieldOption
|
|
80
|
-
const mark = option.value === revealId;
|
|
81
|
-
if (!mark) {
|
|
82
|
-
return {
|
|
83
|
-
id: option.value,
|
|
84
|
-
label: option.label,
|
|
85
|
-
...(option.icon === undefined ? {} : { icon: option.icon }),
|
|
86
|
-
...(option.disabled === true ? { disabled: true } : {}),
|
|
87
|
-
};
|
|
88
|
-
}
|
|
89
|
-
const revealed = (node: ReactNode): ReactNode => (
|
|
90
|
-
<span className="wb-selectReveal" data-wb-select-reveal="">
|
|
91
|
-
{node}
|
|
92
|
-
</span>
|
|
93
|
-
);
|
|
78
|
+
function itemOf(option: SelectFieldOption): MenuItem {
|
|
94
79
|
return {
|
|
95
80
|
id: option.value,
|
|
96
|
-
label: option.
|
|
97
|
-
...(option.icon === undefined ? {} : { icon:
|
|
81
|
+
label: option.label,
|
|
82
|
+
...(option.icon === undefined ? {} : { icon: option.icon }),
|
|
98
83
|
...(option.disabled === true ? { disabled: true } : {}),
|
|
99
84
|
};
|
|
100
85
|
}
|
|
101
86
|
|
|
102
|
-
function revealNode(): HTMLElement | null {
|
|
103
|
-
const found = document.querySelector('[data-wb-select-reveal]');
|
|
104
|
-
return found instanceof HTMLElement ? found : null;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
function useRevealOnOpen(open: boolean, revealId: string | undefined): void {
|
|
108
|
-
useEffect(() => {
|
|
109
|
-
if (!open || revealId === undefined) return;
|
|
110
|
-
revealNode()?.scrollIntoView({ block: 'nearest' });
|
|
111
|
-
}, [open, revealId]);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
87
|
function selectValueNode(
|
|
115
88
|
current: SelectFieldOption | undefined,
|
|
116
89
|
renderValue: SelectFieldProps['renderValue'],
|
|
@@ -251,11 +224,10 @@ export function stepSelectCursor(
|
|
|
251
224
|
return (cursor + step + shown.length) % shown.length;
|
|
252
225
|
}
|
|
253
226
|
|
|
254
|
-
/** 把候选排成 `Menu` 要的条目表:按 `group` 插分组标题;一项都没有时退回 `emptyText`
|
|
227
|
+
/** 把候选排成 `Menu` 要的条目表:按 `group` 插分组标题;一项都没有时退回 `emptyText` 那一行(不给就是空表)。 */
|
|
255
228
|
export function selectEntries(
|
|
256
229
|
shown: readonly SelectFieldOption[],
|
|
257
230
|
emptyText: string | undefined,
|
|
258
|
-
revealId?: string | undefined,
|
|
259
231
|
): readonly MenuEntry[] {
|
|
260
232
|
if (shown.length === 0) {
|
|
261
233
|
return emptyText === undefined ? [] : [{ type: 'label', id: EMPTY_ID, text: emptyText }];
|
|
@@ -267,7 +239,7 @@ export function selectEntries(
|
|
|
267
239
|
out.push({ type: 'label', id: `__wb-select-group-${String(out.length)}`, text: option.group });
|
|
268
240
|
group = option.group;
|
|
269
241
|
}
|
|
270
|
-
out.push(itemOf(option
|
|
242
|
+
out.push(itemOf(option));
|
|
271
243
|
}
|
|
272
244
|
return out;
|
|
273
245
|
}
|
|
@@ -372,7 +344,6 @@ export function SelectField(props: SelectFieldProps) {
|
|
|
372
344
|
selection = 'fill',
|
|
373
345
|
variant = 'boxed',
|
|
374
346
|
iconOnly = false,
|
|
375
|
-
revealId,
|
|
376
347
|
ariaLabel,
|
|
377
348
|
title,
|
|
378
349
|
id,
|
|
@@ -383,7 +354,6 @@ export function SelectField(props: SelectFieldProps) {
|
|
|
383
354
|
const state = useSelectState(props);
|
|
384
355
|
const current = options.find((option) => option.value === value);
|
|
385
356
|
|
|
386
|
-
useRevealOnOpen(state.open, revealId);
|
|
387
357
|
const variantClass = variant === 'ghost' ? 'wb-selectGhost' : undefined;
|
|
388
358
|
const triggerClass = triggerClassOf(variantClass, iconOnly);
|
|
389
359
|
|
|
@@ -425,6 +395,7 @@ export function SelectField(props: SelectFieldProps) {
|
|
|
425
395
|
value: selectValueNode(current, renderValue, placeholder),
|
|
426
396
|
iconOnly,
|
|
427
397
|
onOpen: () => state.setOpen(true),
|
|
398
|
+
ref: state.triggerRef,
|
|
428
399
|
});
|
|
429
400
|
|
|
430
401
|
const anchor =
|
|
@@ -470,7 +441,7 @@ export function SelectField(props: SelectFieldProps) {
|
|
|
470
441
|
anchor,
|
|
471
442
|
selection,
|
|
472
443
|
selectedId: selectCursorValue(state.shown, state.cursor) ?? undefined,
|
|
473
|
-
items: selectEntries(state.shown, state.empty
|
|
444
|
+
items: selectEntries(state.shown, state.empty),
|
|
474
445
|
footer:
|
|
475
446
|
footer === undefined
|
|
476
447
|
? undefined
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ReactNode } from 'react';
|
|
1
|
+
import type { ReactNode, Ref } from 'react';
|
|
2
2
|
import { Fragment, h } from '../lib/react.js';
|
|
3
3
|
import {
|
|
4
4
|
IconChevronDownOutlineRegular,
|
|
@@ -19,6 +19,7 @@ import { Spinner } from './Spinner.js';
|
|
|
19
19
|
* @param args.value - 已渲染好的当前值节点(文本或占位)。
|
|
20
20
|
* @param args.iconOnly - 只画图标(窄栏那颗切换钮),值与箭头都不画。
|
|
21
21
|
* @param args.onOpen - 点它做什么,通常是把列表打开。
|
|
22
|
+
* @param args.ref - 挂到这颗 `<button>` 上,取消后回焦点、菜单开着头条滚轮要用它。
|
|
22
23
|
* @returns 一颗 `<button>`。
|
|
23
24
|
*/
|
|
24
25
|
export function selectTriggerNode(args: {
|
|
@@ -33,10 +34,12 @@ export function selectTriggerNode(args: {
|
|
|
33
34
|
readonly value: ReactNode;
|
|
34
35
|
readonly iconOnly: boolean;
|
|
35
36
|
readonly onOpen: () => void;
|
|
37
|
+
readonly ref: Ref<HTMLButtonElement> | undefined;
|
|
36
38
|
}): ReactNode {
|
|
37
39
|
return (
|
|
38
40
|
<button
|
|
39
41
|
type="button"
|
|
42
|
+
ref={args.ref}
|
|
40
43
|
id={args.id}
|
|
41
44
|
className={args.className}
|
|
42
45
|
disabled={args.disabled}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { describe, expect, it, vi } from 'vitest';
|
|
2
2
|
|
|
3
|
-
vi.mock('
|
|
3
|
+
vi.mock('../../lib/react.js', () => ({
|
|
4
4
|
h: (type: unknown, props: unknown, ...children: unknown[]) => ({ type, props, children }),
|
|
5
5
|
}));
|
|
6
6
|
|
|
7
|
-
import { Card, type CardProps } from '
|
|
7
|
+
import { Card, type CardProps } from '../Card.js';
|
|
8
8
|
|
|
9
9
|
const t = (key: string): string => key;
|
|
10
10
|
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { describe, expect, it, vi } from 'vitest';
|
|
2
2
|
|
|
3
|
-
vi.mock('
|
|
3
|
+
vi.mock('../../lib/react.js', () => ({
|
|
4
4
|
h: (type: unknown, props: unknown, ...children: unknown[]) => ({ type, props, children }),
|
|
5
5
|
}));
|
|
6
6
|
|
|
7
|
-
vi.mock('
|
|
7
|
+
vi.mock('../CardAnchor.module.css', () => ({ default: { anchor: 'wb-anchor' } }));
|
|
8
8
|
|
|
9
|
-
import { CardAnchor, cardIdOf } from '
|
|
9
|
+
import { CardAnchor, cardIdOf } from '../CardAnchor.js';
|
|
10
10
|
|
|
11
11
|
interface AnyElement {
|
|
12
12
|
readonly type: unknown;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import { Fragment, h } from '
|
|
3
|
-
import { ChoiceGroup } from '
|
|
2
|
+
import { Fragment, h } from '../../lib/react.js';
|
|
3
|
+
import { ChoiceGroup } from '../ChoiceGroup.js';
|
|
4
4
|
|
|
5
5
|
interface AnyElement {
|
|
6
6
|
readonly type: unknown;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { readFileSync } from 'node:fs';
|
|
2
2
|
import { describe, expect, it } from 'vitest';
|
|
3
|
-
import { ConfirmBar, type ConfirmRow } from '
|
|
4
|
-
import { FormActions, type FormAction } from '
|
|
5
|
-
import { Note } from '
|
|
6
|
-
import { Rows } from '
|
|
3
|
+
import { ConfirmBar, type ConfirmRow } from '../ConfirmBar.js';
|
|
4
|
+
import { FormActions, type FormAction } from '../FormActions.js';
|
|
5
|
+
import { Note } from '../Note.js';
|
|
6
|
+
import { Rows } from '../Rows.js';
|
|
7
7
|
|
|
8
8
|
interface AnyElement {
|
|
9
9
|
readonly type: unknown;
|
|
@@ -84,7 +84,7 @@ describe('确认条(契约类 wb-confirmBar)', () => {
|
|
|
84
84
|
});
|
|
85
85
|
});
|
|
86
86
|
|
|
87
|
-
const CSS = readFileSync(new URL('
|
|
87
|
+
const CSS = readFileSync(new URL('../ConfirmBar.css', import.meta.url), 'utf8');
|
|
88
88
|
|
|
89
89
|
function declarations(selector: string): string[] {
|
|
90
90
|
for (const block of CSS.split('}')) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { readFileSync } from 'node:fs';
|
|
2
2
|
import { describe, expect, it } from 'vitest';
|
|
3
|
-
import type { FormField, FormValue } from '
|
|
3
|
+
import type { FormField, FormValue } from '../FormTypes.js';
|
|
4
4
|
import {
|
|
5
5
|
duplicateKeysOf,
|
|
6
6
|
formActionsOf,
|
|
@@ -11,9 +11,9 @@ import {
|
|
|
11
11
|
formValuesOf,
|
|
12
12
|
reportDuplicateKeys,
|
|
13
13
|
visibleFieldsOf,
|
|
14
|
-
} from '
|
|
15
|
-
import { FormRow } from '
|
|
16
|
-
import type { FormAction } from '
|
|
14
|
+
} from '../FormNodes.js';
|
|
15
|
+
import { FormRow } from '../FormRows.js';
|
|
16
|
+
import type { FormAction } from '../FormActions.js';
|
|
17
17
|
|
|
18
18
|
function field(key: string, value: FormValue, validate?: FormField['validate']): FormField {
|
|
19
19
|
return {
|
|
@@ -444,7 +444,7 @@ describe('formRowsOf', () => {
|
|
|
444
444
|
});
|
|
445
445
|
});
|
|
446
446
|
|
|
447
|
-
const CSS = readFileSync(new URL('
|
|
447
|
+
const CSS = readFileSync(new URL('../Form.css', import.meta.url), 'utf8');
|
|
448
448
|
|
|
449
449
|
function declarations(selector: string): string[] {
|
|
450
450
|
for (const block of CSS.split('}')) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { readFileSync } from 'node:fs';
|
|
2
2
|
import { describe, expect, it, vi } from 'vitest';
|
|
3
|
-
import { FormActions, type FormAction, type FormActionKind } from '
|
|
4
|
-
import { SaveBar } from '
|
|
3
|
+
import { FormActions, type FormAction, type FormActionKind } from '../FormActions.js';
|
|
4
|
+
import { SaveBar } from '../SaveBar.js';
|
|
5
5
|
|
|
6
6
|
function action(key: string, kind?: FormActionKind): FormAction {
|
|
7
7
|
return { key, label: key, ...(kind === undefined ? {} : { kind }), onClick: () => {} };
|
|
@@ -90,7 +90,7 @@ describe('FormActions', () => {
|
|
|
90
90
|
});
|
|
91
91
|
});
|
|
92
92
|
|
|
93
|
-
const CSS = readFileSync(new URL('
|
|
93
|
+
const CSS = readFileSync(new URL('../FormActions.css', import.meta.url), 'utf8');
|
|
94
94
|
|
|
95
95
|
function declarations(selector: string): string[] {
|
|
96
96
|
for (const block of CSS.split('}')) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { readFileSync } from 'node:fs';
|
|
2
2
|
import { describe, expect, it } from 'vitest';
|
|
3
|
-
import { FormRow, FormRows } from '
|
|
3
|
+
import { FormRow, FormRows } from '../FormRows.js';
|
|
4
4
|
|
|
5
5
|
function texts(node: unknown): string[] {
|
|
6
6
|
if (node === null || node === undefined || typeof node === 'boolean') return [];
|
|
@@ -111,7 +111,7 @@ describe('FormRow', () => {
|
|
|
111
111
|
});
|
|
112
112
|
});
|
|
113
113
|
|
|
114
|
-
const FORM_CSS = readFileSync(new URL('
|
|
114
|
+
const FORM_CSS = readFileSync(new URL('../FormRows.css', import.meta.url), 'utf8');
|
|
115
115
|
|
|
116
116
|
function normalize(selector: string): string {
|
|
117
117
|
return selector
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { describe, expect, it, vi } from 'vitest';
|
|
2
|
-
import type { FormField, FormProps, FormValue } from '
|
|
3
|
-
import { Form } from '
|
|
2
|
+
import type { FormField, FormProps, FormValue } from '../FormTypes.js';
|
|
3
|
+
import { Form } from '../Form.js';
|
|
4
4
|
|
|
5
5
|
function field(key: string, value: FormValue, validate?: FormField['validate']): FormField {
|
|
6
6
|
return {
|
|
@@ -13,8 +13,8 @@ function field(key: string, value: FormValue, validate?: FormField['validate']):
|
|
|
13
13
|
};
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
vi.mock('
|
|
17
|
-
const actual = await vi.importActual<Record<string, unknown>>('
|
|
16
|
+
vi.mock('../../lib/react.js', async () => {
|
|
17
|
+
const actual = await vi.importActual<Record<string, unknown>>('../../lib/react.js');
|
|
18
18
|
return {
|
|
19
19
|
...actual,
|
|
20
20
|
useState: (initial: unknown) => [typeof initial === 'function' ? (initial as () => unknown)() : initial, () => {}],
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { readFileSync } from 'node:fs';
|
|
2
2
|
import { describe, expect, it } from 'vitest';
|
|
3
|
-
import { Row, Rows } from '
|
|
3
|
+
import { Row, Rows } from '../Rows.js';
|
|
4
4
|
|
|
5
5
|
function texts(node: unknown): string[] {
|
|
6
6
|
if (node === null || node === undefined || typeof node === 'boolean') return [];
|
|
@@ -35,7 +35,7 @@ describe('Row', () => {
|
|
|
35
35
|
});
|
|
36
36
|
});
|
|
37
37
|
|
|
38
|
-
const ROWS_CSS = readFileSync(new URL('
|
|
38
|
+
const ROWS_CSS = readFileSync(new URL('../Rows.css', import.meta.url), 'utf8');
|
|
39
39
|
const FLEX_CONTAINER = `.wb-rows {
|
|
40
40
|
display: flex;
|
|
41
41
|
flex-direction: column;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { describe, expect, it, vi } from 'vitest';
|
|
2
2
|
|
|
3
3
|
let states: unknown[] = [];
|
|
4
|
-
vi.mock('
|
|
4
|
+
vi.mock('../../lib/react.js', () => ({
|
|
5
5
|
Fragment: Symbol.for('react.fragment'),
|
|
6
6
|
h: (type: unknown, props: unknown, ...children: unknown[]) => ({ type, props, children }),
|
|
7
7
|
useEffect: () => {},
|
|
@@ -10,21 +10,21 @@ vi.mock('../lib/react.js', () => ({
|
|
|
10
10
|
useState: (initial: unknown) => [states.length > 0 ? states.shift() : initial, () => {}],
|
|
11
11
|
}));
|
|
12
12
|
|
|
13
|
-
vi.mock('
|
|
13
|
+
vi.mock('../SelectField.css', () => ({ default: {} }));
|
|
14
14
|
|
|
15
15
|
import { Menu, TextShimmer } from '@deepseek-ai/dsh-client-ui-primitives';
|
|
16
|
-
import { Spinner } from '
|
|
16
|
+
import { Spinner } from '../Spinner.js';
|
|
17
17
|
import type { MenuEntry } from '@deepseek-ai/dsh-client-ui-primitives';
|
|
18
|
-
import { h } from '
|
|
19
|
-
import { Field } from '
|
|
20
|
-
import { TextField } from '
|
|
18
|
+
import { h } from '../../lib/react.js';
|
|
19
|
+
import { Field } from '../Field.js';
|
|
20
|
+
import { TextField } from '../TextField.js';
|
|
21
21
|
import {
|
|
22
22
|
SelectField,
|
|
23
23
|
defaultSelectFilter,
|
|
24
24
|
selectCursorValue,
|
|
25
25
|
selectEntries,
|
|
26
26
|
stepSelectCursor,
|
|
27
|
-
} from '
|
|
27
|
+
} from '../SelectField.js';
|
|
28
28
|
|
|
29
29
|
interface AnyElement {
|
|
30
30
|
readonly type: unknown;
|
|
@@ -54,10 +54,6 @@ function elementsOf(root: unknown): readonly AnyElement[] {
|
|
|
54
54
|
return collect(root);
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
-
function marked(nodes: readonly AnyElement[]): boolean {
|
|
58
|
-
return nodes.some((one) => one.props?.['data-wb-select-reveal'] === '');
|
|
59
|
-
}
|
|
60
|
-
|
|
61
57
|
function menuOf(root: unknown): AnyElement {
|
|
62
58
|
const menu = elementsOf(root).find((one) => one.type === Menu);
|
|
63
59
|
if (menu === undefined) throw new Error('没有渲染出 Menu');
|
|
@@ -276,32 +272,6 @@ describe('SelectField:工作区那套下拉的公共形状', () => {
|
|
|
276
272
|
expect(defaultSelectFilter(option, 'nope')).toBe(false);
|
|
277
273
|
});
|
|
278
274
|
|
|
279
|
-
it('★ revealId:只给那一行的图标裹上标记(开列表时靠它滚进可视区),别行一个字节不变', () => {
|
|
280
|
-
const items = selectEntries(
|
|
281
|
-
[
|
|
282
|
-
{ value: 'a', label: '甲', icon: <span>甲</span> },
|
|
283
|
-
{ value: 'b', label: '乙', icon: <span>乙</span> },
|
|
284
|
-
],
|
|
285
|
-
undefined,
|
|
286
|
-
'b',
|
|
287
|
-
);
|
|
288
|
-
const iconOf = (id: string): unknown =>
|
|
289
|
-
(items.find((one) => (one as { id?: string }).id === id) as { icon?: unknown } | undefined)?.icon;
|
|
290
|
-
|
|
291
|
-
expect(marked(elementsOf(iconOf('a')))).toBe(false);
|
|
292
|
-
expect(marked(elementsOf(iconOf('b')))).toBe(true);
|
|
293
|
-
});
|
|
294
|
-
|
|
295
|
-
it('★ revealId 撞上没有图标的行:退而标记它的文字(标记只落一处)', () => {
|
|
296
|
-
const items = selectEntries([{ value: 'a', label: '甲' }], undefined, 'a');
|
|
297
|
-
const row = items.find((one) => (one as { id?: string }).id === 'a') as
|
|
298
|
-
| { label?: unknown; icon?: unknown }
|
|
299
|
-
| undefined;
|
|
300
|
-
|
|
301
|
-
expect(row?.icon).toBeUndefined();
|
|
302
|
-
expect(marked(elementsOf(row?.label))).toBe(true);
|
|
303
|
-
});
|
|
304
|
-
|
|
305
275
|
it('条目构造:图标与禁用照传,分组标题只在换组时出现一次', () => {
|
|
306
276
|
const items = selectEntries(
|
|
307
277
|
[
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { describe, expect, it, vi } from 'vitest';
|
|
2
2
|
|
|
3
|
-
vi.mock('
|
|
4
|
-
const actual = await vi.importActual<typeof import('
|
|
3
|
+
vi.mock('../../lib/react.js', async () => {
|
|
4
|
+
const actual = await vi.importActual<typeof import('../../lib/react.js')>('../../lib/react.js');
|
|
5
5
|
return { ...actual, h: (type: unknown, props?: unknown, ...children: unknown[]) => ({ type, props: props ?? {}, children }) };
|
|
6
6
|
});
|
|
7
|
-
vi.mock('
|
|
7
|
+
vi.mock('../Spinner.css', () => ({ default: {} }));
|
|
8
8
|
|
|
9
9
|
import { StateDot } from '@deepseek-ai/dsh-client-ui-primitives';
|
|
10
|
-
import { Spinner } from '
|
|
10
|
+
import { Spinner } from '../Spinner.js';
|
|
11
11
|
|
|
12
12
|
describe('Spinner(loading 那颗)', () => {
|
|
13
13
|
it('画的是 dsh 的 ongoing 状态点(视觉只有这一处实现)', () => {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
-
import { readText, writeText } from '
|
|
2
|
+
import { readText, writeText } from '../storage.js';
|
|
3
3
|
|
|
4
4
|
function fakeStorage(options: { throwOnWrite?: boolean } = {}) {
|
|
5
5
|
const data = new Map<string, string>();
|
package/src/config/constants.ts
CHANGED
|
@@ -31,7 +31,7 @@ export const DIY_RECORD_ACT_PATH = `${DIY_RECORD_PATH}/act`;
|
|
|
31
31
|
/** 面板运行态的路由前缀,挂在外壳 API 下:上报口用它,拒报回 report-refused。 */
|
|
32
32
|
export const PANEL_STATE_PREFIX = `${API_PREFIX}/${PANEL_STATE_PATH}`;
|
|
33
33
|
|
|
34
|
-
/**
|
|
34
|
+
/** 钉住的运行时版本:隔离运行时按它校验、取那一份。 */
|
|
35
35
|
export const OPENCLI_PIN = "1.8.8";
|
|
36
36
|
|
|
37
37
|
/** 缺陷看板套件的 dsh 服务名(`workbenchIssues`):套件照它登记自己那份服务。 */
|
|
@@ -55,13 +55,13 @@ export const DIY_RECORD_SERVICE = "workbenchDiyRecord";
|
|
|
55
55
|
/** 会话投影服务的 dsh 服务名(`sessionProjections`):宿主经 `stateOf(session, 'agentPreset')` 读那一档。 */
|
|
56
56
|
export const SESSION_PROJECTIONS_SERVICE = "sessionProjections";
|
|
57
57
|
|
|
58
|
-
/**
|
|
58
|
+
/** 面板清单一次取回的行数上限(条):外部模块拿它当分页参数。 */
|
|
59
59
|
export const PANEL_LIST_LIMIT = 50;
|
|
60
60
|
|
|
61
|
-
/**
|
|
61
|
+
/** 单次运行时调用的默认超时(毫秒);调用点没传 `timeoutMs` 时按它算。 */
|
|
62
62
|
export const OPENCLI_TIMEOUT_MS = 120_000;
|
|
63
63
|
|
|
64
|
-
/**
|
|
64
|
+
/** 同时最多几条运行时调用在跑(条):超出就排队。 */
|
|
65
65
|
export const OPENCLI_MAX_CONCURRENT = 4;
|
|
66
66
|
|
|
67
67
|
/** 启动预热站点那一跳的超时(毫秒);超时按未热对待,不挡面板。 */
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export interface RuntimeCapabilities {
|
|
2
|
+
readonly console: boolean;
|
|
3
|
+
readonly pluginMarket: boolean;
|
|
4
|
+
readonly browserBridge: boolean;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export interface RuntimeText {
|
|
8
|
+
readonly zh: string;
|
|
9
|
+
readonly en: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface RuntimeTerms {
|
|
13
|
+
readonly plugin: RuntimeText;
|
|
14
|
+
readonly pluginOf: RuntimeText;
|
|
15
|
+
readonly pluginThing: RuntimeText;
|
|
16
|
+
readonly pluginArea: RuntimeText;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface RuntimeBridgeFix {
|
|
20
|
+
readonly text: RuntimeText;
|
|
21
|
+
readonly link?: { readonly label: RuntimeText; readonly href: string };
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface RuntimeProfile {
|
|
25
|
+
readonly label: string;
|
|
26
|
+
readonly terms: RuntimeTerms;
|
|
27
|
+
readonly installHint: RuntimeText;
|
|
28
|
+
readonly diagnose: Readonly<Partial<Record<'exec' | 'login' | 'bridge', RuntimeText>>>;
|
|
29
|
+
readonly bridge?: Readonly<Partial<Record<string, RuntimeBridgeFix>>>;
|
|
30
|
+
}
|
package/src/contract/needs.ts
CHANGED
|
@@ -69,7 +69,7 @@ export interface BinNeed extends NeedCommon {
|
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
/**
|
|
72
|
-
*
|
|
72
|
+
* 某个运行时上要有一个扩展(运行时自己的插件就是这种),并且要提供这些命令。
|
|
73
73
|
*/
|
|
74
74
|
export interface ExtensionNeed extends NeedCommon {
|
|
75
75
|
readonly kind: 'extension';
|
|
@@ -82,8 +82,8 @@ export interface ExtensionNeed extends NeedCommon {
|
|
|
82
82
|
|
|
83
83
|
/**
|
|
84
84
|
* 要在隔离运行时里有一个共用依赖包。
|
|
85
|
-
* `runs`
|
|
86
|
-
* (库侧约定 `
|
|
85
|
+
* `runs` 写它自己会起哪个运行时的命令 —— 写了外壳就把那份执行器注入进去
|
|
86
|
+
* (库侧约定 `set<Runtime>Runner`);不写就按它自己在 PATH 上找。
|
|
87
87
|
*/
|
|
88
88
|
export interface LibNeed extends NeedCommon {
|
|
89
89
|
readonly kind: 'lib';
|
|
@@ -294,7 +294,7 @@ function perKindProblemsOf(
|
|
|
294
294
|
if (raw['docs'] !== undefined && text(raw['docs']) === '') errors.push(`${at}.docs 是空串`);
|
|
295
295
|
if (kind === 'lib') errors.push(...libRunsProblemsOf(raw['runs'], at));
|
|
296
296
|
if (kind === 'extension') {
|
|
297
|
-
if (text(raw['runtime']) === '') errors.push(`${at}.runtime 没写 ——
|
|
297
|
+
if (text(raw['runtime']) === '') errors.push(`${at}.runtime 没写 —— 扩展属于哪个运行时`);
|
|
298
298
|
if (text(raw['name']) === '') errors.push(`${at}.name 没写`);
|
|
299
299
|
if (raw['commands'] !== undefined) {
|
|
300
300
|
if (!Array.isArray(raw['commands'])) errors.push(`${at}.commands 要是数组`);
|
|
@@ -1 +1,25 @@
|
|
|
1
1
|
export type ProvisionProblem = 'not-declared' | 'env-missing' | 'malformed';
|
|
2
|
+
|
|
3
|
+
export interface PluginSourceCommand {
|
|
4
|
+
readonly line: string;
|
|
5
|
+
readonly optional?: true;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export type ResolvedPluginSource =
|
|
9
|
+
| {
|
|
10
|
+
readonly ok: true;
|
|
11
|
+
readonly commands: readonly PluginSourceCommand[];
|
|
12
|
+
readonly line: string;
|
|
13
|
+
}
|
|
14
|
+
| {
|
|
15
|
+
readonly ok: false;
|
|
16
|
+
readonly problem: ProvisionProblem;
|
|
17
|
+
readonly env?: string;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export interface PluginSourceOptions {
|
|
21
|
+
readonly command?: string;
|
|
22
|
+
readonly plugin?: string;
|
|
23
|
+
readonly version?: string;
|
|
24
|
+
readonly mode?: 'install' | 'update';
|
|
25
|
+
}
|
|
@@ -155,8 +155,14 @@ export interface BundleSupplyPayload {
|
|
|
155
155
|
readonly reason?: string;
|
|
156
156
|
}
|
|
157
157
|
|
|
158
|
+
export type PluginPackageKind = 'runtime-plugin';
|
|
159
|
+
|
|
160
|
+
export function isPluginPackageKind(kind: unknown): kind is PluginPackageKind {
|
|
161
|
+
return kind === 'runtime-plugin';
|
|
162
|
+
}
|
|
163
|
+
|
|
158
164
|
export interface BundleInstalledPlugin {
|
|
159
|
-
readonly kind:
|
|
165
|
+
readonly kind: PluginPackageKind;
|
|
160
166
|
readonly pkg: string;
|
|
161
167
|
readonly name?: string;
|
|
162
168
|
}
|
|
@@ -263,6 +263,7 @@ export interface PublishPreview {
|
|
|
263
263
|
readonly name: string;
|
|
264
264
|
readonly version: string;
|
|
265
265
|
readonly author?: string;
|
|
266
|
+
readonly labels?: readonly string[];
|
|
266
267
|
readonly registry?: string;
|
|
267
268
|
readonly market?: MarketPublishDestination;
|
|
268
269
|
readonly claim?: MarketClaim;
|
|
@@ -21,7 +21,7 @@ export interface HostCapabilities {
|
|
|
21
21
|
|
|
22
22
|
export interface ToolchainPlan {
|
|
23
23
|
readonly node: { readonly source: 'desktop-shim' | 'path' };
|
|
24
|
-
readonly npm: { readonly neededFor: '
|
|
24
|
+
readonly npm: { readonly neededFor: 'runtime-install' };
|
|
25
25
|
readonly pnpm: { readonly neededFor: 'workshop'; readonly expectedMajor: 10 };
|
|
26
26
|
}
|
|
27
27
|
|
package/src/contract/unit.ts
CHANGED
|
@@ -25,9 +25,9 @@ export interface UnitCard {
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
/**
|
|
28
|
-
*
|
|
28
|
+
* 合成工具背后那条运行时命令:插件名与命令名都是运行时那边的原名,`args` 是命令自己声明的参数名(按声明顺序)。
|
|
29
29
|
*/
|
|
30
|
-
export interface
|
|
30
|
+
export interface UnitToolRuntime {
|
|
31
31
|
readonly plugin: string;
|
|
32
32
|
readonly command: string;
|
|
33
33
|
readonly args: readonly string[];
|
|
@@ -35,12 +35,12 @@ export interface UnitToolOpencli {
|
|
|
35
35
|
|
|
36
36
|
/**
|
|
37
37
|
* 一条工具在清单里的样子:全名加会不会写;审批按 writes 认领。
|
|
38
|
-
* `
|
|
38
|
+
* `runtime` 只有「按已装插件合成」的单元写,手写模块的工具没有这一格。
|
|
39
39
|
*/
|
|
40
40
|
export interface UnitTool {
|
|
41
41
|
readonly name: string;
|
|
42
42
|
readonly writes: boolean;
|
|
43
|
-
readonly
|
|
43
|
+
readonly runtime?: UnitToolRuntime;
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
/**
|