@dsh-workbench/shared 1.1.8 → 1.1.10
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 -1
- 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/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 +23 -24
- package/dist/i18n/index.d.ts +23 -24
- package/dist/i18n/runtime-terms.d.ts +15 -0
- package/dist/i18n/runtime-terms.js +22 -0
- package/dist/i18n/zh.d.ts +22 -23
- package/dist/i18n/zh.js +22 -23
- 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__/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/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 +23 -25
- package/src/i18n/runtime-terms.ts +26 -0
- package/src/i18n/zh.ts +22 -24
- 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
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { ADAPTER_SET_API_VERSION } from '../host/adapters.js';
|
|
2
|
+
import { readBundleManager } from '../host/bundle-manager.js';
|
|
3
|
+
import { declarationGaps, moduleDeclarationOf, profileDirOf, profileModules, readInstalledModule, readProfilePackage, } from '../host/installed-module.js';
|
|
4
|
+
import { isNewerVersion } from '../host/version.js';
|
|
5
|
+
import { refusalReason } from '../host/spec.js';
|
|
6
|
+
import { declarationRefusalOf, pathDeclarationOf } from '../host/local-declaration.js';
|
|
7
|
+
import { createRegistryLookup } from '../host/registry-declaration.js';
|
|
8
|
+
import { createPackageRouter } from '../host/package-route.js';
|
|
9
|
+
import { createReleaseProbe } from '../host/release-probe.js';
|
|
10
|
+
export const FAKE_ADAPTER_MARKER = { adapterSet: 'fake' };
|
|
11
|
+
function result() {
|
|
12
|
+
return { ok: true, data: FAKE_ADAPTER_MARKER };
|
|
13
|
+
}
|
|
14
|
+
const data = {
|
|
15
|
+
async read(_request) {
|
|
16
|
+
return result();
|
|
17
|
+
},
|
|
18
|
+
async request(_recipe) {
|
|
19
|
+
return result();
|
|
20
|
+
},
|
|
21
|
+
async requestMany(_recipe, _calls) {
|
|
22
|
+
return {};
|
|
23
|
+
},
|
|
24
|
+
async submit(_recipe) {
|
|
25
|
+
return result();
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
const supply = {
|
|
29
|
+
resolvePluginSource: () => ({ ok: false, problem: 'not-declared' }),
|
|
30
|
+
libInstallCommand: () => '',
|
|
31
|
+
libUninstallCommand: () => '',
|
|
32
|
+
pluginInstallPlan: () => ({ ok: false, problem: 'not-declared' }),
|
|
33
|
+
latestVersion: async () => undefined,
|
|
34
|
+
missingReleaseParts: async () => [],
|
|
35
|
+
probeRelease: async () => undefined,
|
|
36
|
+
splitNpmSpec: () => undefined,
|
|
37
|
+
};
|
|
38
|
+
const modules = {
|
|
39
|
+
isolatedRoot: () => '/tmp/dsh-workbench-fake',
|
|
40
|
+
isolatedHome: () => '/tmp/dsh-workbench-fake/home',
|
|
41
|
+
pluginPackagePrefix: () => 'fake-plugin-',
|
|
42
|
+
pluginManifestFile: () => 'fake-plugin.json',
|
|
43
|
+
cliCatalogPath: () => '/tmp/dsh-workbench-fake/catalog.json',
|
|
44
|
+
vendorPluginsDir: () => '/tmp/dsh-workbench-fake/plugins',
|
|
45
|
+
runtimeInstallCommand: () => 'npm i -g fake-runtime',
|
|
46
|
+
afterLibInstall: () => undefined,
|
|
47
|
+
profileDirOf: (ctx) => profileDirOf(ctx),
|
|
48
|
+
profileModules,
|
|
49
|
+
readInstalledModule,
|
|
50
|
+
readProfilePackage,
|
|
51
|
+
readBundleManager,
|
|
52
|
+
createRegistryNotebook: () => ({}),
|
|
53
|
+
declarationGaps,
|
|
54
|
+
moduleDeclarationOf,
|
|
55
|
+
registryDeclarationOf: async () => undefined,
|
|
56
|
+
pathDeclarationOf,
|
|
57
|
+
declarationRefusalOf,
|
|
58
|
+
refusalReason,
|
|
59
|
+
packageRouteOf: async () => undefined,
|
|
60
|
+
isNewerVersion,
|
|
61
|
+
startSelfUpdate: () => ({}),
|
|
62
|
+
};
|
|
63
|
+
const profile = {
|
|
64
|
+
dirOf: () => null,
|
|
65
|
+
dshPlugins: () => () => ({ ok: true, plugins: [] }),
|
|
66
|
+
selfUpdateView: () => undefined,
|
|
67
|
+
};
|
|
68
|
+
const probe = {
|
|
69
|
+
probeUnits: async () => [],
|
|
70
|
+
registerPreflight: () => undefined,
|
|
71
|
+
declaredPluginCommands: () => new Map(),
|
|
72
|
+
};
|
|
73
|
+
const runtimePreset = (capabilities) => ({
|
|
74
|
+
packageRoot: () => '/tmp/dsh-workbench-fake',
|
|
75
|
+
capabilities: () => capabilities,
|
|
76
|
+
profile: () => ({
|
|
77
|
+
label: 'fake',
|
|
78
|
+
terms: {
|
|
79
|
+
plugin: { zh: '插件', en: 'plugin' },
|
|
80
|
+
pluginOf: { zh: '的插件', en: 'a plugin of' },
|
|
81
|
+
pluginThing: { zh: '插件', en: 'a plugin' },
|
|
82
|
+
pluginArea: { zh: '插件区', en: 'the plugin area' },
|
|
83
|
+
},
|
|
84
|
+
installHint: { zh: '无', en: 'nothing to install' },
|
|
85
|
+
diagnose: {},
|
|
86
|
+
}),
|
|
87
|
+
commandSurface: () => ({
|
|
88
|
+
name: 'fake',
|
|
89
|
+
pluginPrefix: 'fake-plugin-',
|
|
90
|
+
invoke: (site, command) => (command === undefined ? `fake ${site}` : `fake ${site} ${command}`),
|
|
91
|
+
help: (site) => `fake ${site} --help`,
|
|
92
|
+
browser: (verb) => `fake browser ${verb.join(' ')}`,
|
|
93
|
+
}),
|
|
94
|
+
commands: () => ({}),
|
|
95
|
+
runners: () => ({}),
|
|
96
|
+
health: () => ({}),
|
|
97
|
+
seed: () => undefined,
|
|
98
|
+
warnUnwritableRoot: () => undefined,
|
|
99
|
+
});
|
|
100
|
+
const plugins = {
|
|
101
|
+
declarationLookup: () => () => undefined,
|
|
102
|
+
createUnits: () => ({
|
|
103
|
+
refresh: async () => ({ units: [] }),
|
|
104
|
+
touch: () => undefined,
|
|
105
|
+
report: () => ({ units: [] }),
|
|
106
|
+
dispose: () => undefined,
|
|
107
|
+
}),
|
|
108
|
+
};
|
|
109
|
+
const actions = {
|
|
110
|
+
configSyncHandler: () => (async () => ({ ok: true })),
|
|
111
|
+
profileCreateHandler: () => (async () => ({ ok: true })),
|
|
112
|
+
configSyncAction: () => ({}),
|
|
113
|
+
profileCreateAction: () => ({}),
|
|
114
|
+
};
|
|
115
|
+
export function fakeAdapterSet(overrides = {}) {
|
|
116
|
+
const release = createReleaseProbe();
|
|
117
|
+
const registry = createRegistryLookup();
|
|
118
|
+
const router = createPackageRouter({ manifestFile: 'fake-plugin.json', dshLookup: registry.registryDshOf });
|
|
119
|
+
const withRelease = {
|
|
120
|
+
...supply,
|
|
121
|
+
latestVersion: release.latestVersion,
|
|
122
|
+
missingReleaseParts: release.missingReleaseParts,
|
|
123
|
+
probeRelease: release.probeRelease,
|
|
124
|
+
};
|
|
125
|
+
return {
|
|
126
|
+
apiVersion: ADAPTER_SET_API_VERSION,
|
|
127
|
+
id: 'fake-adapter-set',
|
|
128
|
+
version: '0.0.0',
|
|
129
|
+
data,
|
|
130
|
+
supply: {
|
|
131
|
+
...withRelease,
|
|
132
|
+
...modules,
|
|
133
|
+
env: withRelease,
|
|
134
|
+
modules,
|
|
135
|
+
registryDeclarationOf: registry.registryDeclarationOf,
|
|
136
|
+
packageRouteOf: router.packageRouteOf,
|
|
137
|
+
},
|
|
138
|
+
modules,
|
|
139
|
+
profile,
|
|
140
|
+
probe,
|
|
141
|
+
runtime: runtimePreset({
|
|
142
|
+
console: false,
|
|
143
|
+
pluginMarket: false,
|
|
144
|
+
browserBridge: false,
|
|
145
|
+
}),
|
|
146
|
+
plugins,
|
|
147
|
+
actions,
|
|
148
|
+
...overrides,
|
|
149
|
+
};
|
|
150
|
+
}
|
package/dist/testing/needs.js
CHANGED
package/dist/testing.d.ts
CHANGED
package/dist/testing.js
CHANGED
package/package.json
CHANGED
|
@@ -313,3 +313,28 @@ describe('blockerOfCapability', () => {
|
|
|
313
313
|
expect(blocker?.actions.map((action) => action.kind)).toEqual(['retry']);
|
|
314
314
|
});
|
|
315
315
|
});
|
|
316
|
+
|
|
317
|
+
describe('执行类失败的话术由适配器给', () => {
|
|
318
|
+
it('适配器给了 diagnose.exec 就用它,不再说默认那句', () => {
|
|
319
|
+
const blocker = blockerOfError(
|
|
320
|
+
{ code: 'cli-exec-failed', message: '跑不起来' },
|
|
321
|
+
t,
|
|
322
|
+
{ diagnose: { exec: '确认 MCP server 起得来:npx -y chrome-devtools-mcp@latest' } },
|
|
323
|
+
);
|
|
324
|
+
expect(blocker.hint).toBe('确认 MCP server 起得来:npx -y chrome-devtools-mcp@latest');
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
it('适配器没给就回词表那句(opencli 那份)', () => {
|
|
328
|
+
const blocker = blockerOfError({ code: 'cli-exec-failed', message: '跑不起来' }, t);
|
|
329
|
+
expect(blocker.hint).toBe('blocker.exec.hint');
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
it('错误自己带的 hint 优先级最高', () => {
|
|
333
|
+
const blocker = blockerOfError(
|
|
334
|
+
{ code: 'cli-exec-failed', message: '跑不起来', hint: '端口被占了' },
|
|
335
|
+
t,
|
|
336
|
+
{ diagnose: { exec: '确认 MCP server 起得来' } },
|
|
337
|
+
);
|
|
338
|
+
expect(blocker.hint).toBe('端口被占了');
|
|
339
|
+
});
|
|
340
|
+
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
-
import { API_PREFIX } from '
|
|
3
|
-
import { apiGet } from '
|
|
2
|
+
import { API_PREFIX } from '../../config/constants.js';
|
|
3
|
+
import { apiGet } from '../workbench-client.js';
|
|
4
4
|
|
|
5
5
|
interface FetchRecord {
|
|
6
6
|
url: string;
|
package/src/api/blocker-table.ts
CHANGED
|
@@ -22,7 +22,11 @@ function copyAction(t: Translate, text: string): BlockerAction {
|
|
|
22
22
|
export function blockerOfError(
|
|
23
23
|
error: ApiError | undefined,
|
|
24
24
|
t: Translate,
|
|
25
|
-
context: {
|
|
25
|
+
context: {
|
|
26
|
+
path?: string | null;
|
|
27
|
+
platform?: string;
|
|
28
|
+
diagnose?: Readonly<Partial<Record<'exec' | 'login' | 'bridge', string>>>;
|
|
29
|
+
} = {},
|
|
26
30
|
): ModuleBlocker {
|
|
27
31
|
if (error === undefined) {
|
|
28
32
|
return {
|
|
@@ -93,7 +97,7 @@ export function blockerOfError(
|
|
|
93
97
|
id: 'cli-exec-failed',
|
|
94
98
|
severity: 'block',
|
|
95
99
|
title: error.message,
|
|
96
|
-
hint: error.hint ?? t('blocker.exec.hint'),
|
|
100
|
+
hint: error.hint ?? context.diagnose?.exec ?? t('blocker.exec.hint'),
|
|
97
101
|
...(error.detail === undefined ? {} : { detail: error.detail }),
|
|
98
102
|
actions: [retryAction(t)],
|
|
99
103
|
};
|
|
@@ -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;
|