@kerfjs/ui 5.0.0-beta.18 → 5.0.0-beta.21
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/README.md +114 -3
- package/ai/application-ui-diagnostic-ids-v1.json +92 -0
- package/ai/application-ui-profile-sync.cjs +529 -0
- package/ai/application-ui-profile.d.mts +1 -0
- package/ai/application-ui-profile.d.ts +102 -0
- package/ai/application-ui-profile.defaults.json +41 -0
- package/ai/application-ui-profile.mjs +626 -0
- package/ai/application-ui-profile.schema.json +130 -0
- package/ai/catalog-authoring.json +25 -0
- package/ai/catalog-authoring.schema.json +61 -0
- package/ai/compile-time-contracts-v1.json +101 -0
- package/ai/compile-time-contracts-v1.schema.json +49 -0
- package/ai/component-catalog-extension-v2.d.ts +7 -0
- package/ai/component-catalog-extension-v2.schema.json +7 -0
- package/ai/component-catalog-v2-overrides.json +326 -0
- package/ai/component-catalog-v2.d.ts +67 -0
- package/ai/component-catalog-v2.json +4441 -0
- package/ai/component-catalog-v2.schema.json +246 -0
- package/ai/component-catalog.json +150 -6
- package/ai/public-api-signatures-v1.md +294 -64
- package/ai/skill.md +41 -5
- package/ai/webawesome-jsx-signatures-v1.md +1 -1
- package/analyzer/cli.mjs +46 -0
- package/analyzer/index.d.mts +45 -0
- package/analyzer/index.mjs +743 -0
- package/analyzer/report.schema.json +66 -0
- package/dist/browser/pane.js +3 -0
- package/dist/browser/sunken-panel.js +3 -0
- package/dist/catalog-resources.d.ts +34 -0
- package/dist/catalog-resources.js +22 -0
- package/dist/catalog-resources.js.map +1 -0
- package/dist/catalog.d.ts +21 -3
- package/dist/catalog.js +239 -210
- package/dist/catalog.js.map +1 -1
- package/dist/{chunk-ZK2G3VR5.js → chunk-5VTB2BKP.js} +1 -1
- package/dist/chunk-5VTB2BKP.js.map +1 -0
- package/dist/{chunk-S3HKPI7I.js → chunk-7KEUJIIC.js} +1 -1
- package/dist/chunk-7KEUJIIC.js.map +1 -0
- package/dist/{chunk-NVWXKU37.js → chunk-AASLYRGE.js} +1 -1
- package/dist/chunk-AASLYRGE.js.map +1 -0
- package/dist/{chunk-ENVTNLYC.js → chunk-CN2XNTAM.js} +6 -6
- package/dist/chunk-CN2XNTAM.js.map +1 -0
- package/dist/{chunk-QIRXYKCE.js → chunk-DSNIYOZ3.js} +3 -2
- package/dist/chunk-DSNIYOZ3.js.map +1 -0
- package/dist/chunk-JRKSK2HX.js +79 -0
- package/dist/chunk-JRKSK2HX.js.map +1 -0
- package/dist/{chunk-U5IFGQCD.js → chunk-K57KRAOV.js} +1 -1
- package/dist/chunk-K57KRAOV.js.map +1 -0
- package/dist/{chunk-ZVI5YST2.js → chunk-M7JBER5U.js} +1 -1
- package/dist/chunk-M7JBER5U.js.map +1 -0
- package/dist/{chunk-43FUQH35.js → chunk-OHU6CI22.js} +2 -2
- package/dist/{chunk-43FUQH35.js.map → chunk-OHU6CI22.js.map} +1 -1
- package/dist/{chunk-4TRDV6RL.js → chunk-OZWU5ZPZ.js} +1 -1
- package/dist/chunk-OZWU5ZPZ.js.map +1 -0
- package/dist/{chunk-JTHUXDN2.js → chunk-RYRIFZCT.js} +3 -3
- package/dist/chunk-RYRIFZCT.js.map +1 -0
- package/dist/{chunk-6ETZKKAE.js → chunk-VLYV65LK.js} +1 -1
- package/dist/chunk-VLYV65LK.js.map +1 -0
- package/dist/chunk-W4CXQYSG.js +23 -0
- package/dist/chunk-W4CXQYSG.js.map +1 -0
- package/dist/{chunk-S3PCZ7NV.js → chunk-W7YD6E4N.js} +1 -1
- package/dist/chunk-W7YD6E4N.js.map +1 -0
- package/dist/index.d.ts +6 -4
- package/dist/index.js +14 -12
- package/dist/list-header.d.ts +29 -8
- package/dist/list-header.js +1 -1
- package/dist/nav-stack.js +1 -1
- package/dist/pane.d.ts +49 -0
- package/dist/pane.js +4 -0
- package/dist/pane.js.map +1 -0
- package/dist/panel-header.d.ts +9 -3
- package/dist/panel-header.js +1 -1
- package/dist/segmented-control.d.ts +6 -6
- package/dist/segmented-control.js +1 -1
- package/dist/select.d.ts +10 -4
- package/dist/select.js +1 -1
- package/dist/split-view.js +2 -2
- package/dist/state-banner.d.ts +3 -2
- package/dist/state-banner.js +1 -1
- package/dist/styles/catalog.css +6 -6
- package/dist/styles/layout.css +2 -20
- package/dist/styles/pane.css +62 -0
- package/dist/styles/segmented-control.css +3 -7
- package/dist/styles/sunken-panel.css +18 -0
- package/dist/sunken-panel.d.ts +16 -0
- package/dist/sunken-panel.js +3 -0
- package/dist/sunken-panel.js.map +1 -0
- package/dist/tab-bar.d.ts +3 -2
- package/dist/tab-bar.js +1 -1
- package/dist/tab-scaffold.d.ts +6 -6
- package/dist/tab-scaffold.js.map +1 -1
- package/dist/token-search-field.d.ts +25 -9
- package/dist/token-search-field.js +1 -1
- package/dist/toolbar-control-group.d.ts +9 -5
- package/dist/toolbar-control-group.js +1 -1
- package/dist/toolbar-text.d.ts +10 -11
- package/dist/toolbar-text.js +1 -1
- package/dist/wire-tab-bars.d.ts +5 -2
- package/dist/wire-tab-bars.js +1 -1
- package/dist/wire-token-search-fields.d.ts +11 -6
- package/dist/wire-token-search-fields.js +1 -1
- package/docs/catalog.md +186 -67
- package/docs/component-contract.md +102 -5
- package/docs/component-selection.md +9 -5
- package/docs/design-philosophy.md +2 -4
- package/docs/examples/application-ui-profile.json +60 -0
- package/docs/examples/component-catalog-extension-v2.json +81 -0
- package/docs/layout.md +42 -23
- package/docs/sunken-panel.md +43 -0
- package/docs/type-contracts.md +99 -0
- package/docs/ui-analyzer.md +81 -0
- package/docs/ui-doctor.md +77 -0
- package/docs/ui-evaluator.md +122 -0
- package/doctor/cli.mjs +115 -0
- package/doctor/config.schema.json +68 -0
- package/doctor/index.d.mts +116 -0
- package/doctor/index.mjs +1256 -0
- package/doctor/report.schema.json +118 -0
- package/evaluator/cli.mjs +89 -0
- package/evaluator/index.d.mts +90 -0
- package/evaluator/index.mjs +1138 -0
- package/evaluator/report.schema.json +152 -0
- package/llms.txt +40 -3
- package/package.json +66 -8
- package/ux-demo/recipes/compact-toolbar.tsx +2 -2
- package/ux-demo/recipes/composer-form.tsx +1 -1
- package/dist/chunk-4TRDV6RL.js.map +0 -1
- package/dist/chunk-6ETZKKAE.js.map +0 -1
- package/dist/chunk-ENVTNLYC.js.map +0 -1
- package/dist/chunk-JTHUXDN2.js.map +0 -1
- package/dist/chunk-NVWXKU37.js.map +0 -1
- package/dist/chunk-QIRXYKCE.js.map +0 -1
- package/dist/chunk-S3HKPI7I.js.map +0 -1
- package/dist/chunk-S3PCZ7NV.js.map +0 -1
- package/dist/chunk-U5IFGQCD.js.map +0 -1
- package/dist/chunk-ZK2G3VR5.js.map +0 -1
- package/dist/chunk-ZVI5YST2.js.map +0 -1
package/doctor/cli.mjs
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import console from 'node:console';
|
|
4
|
+
import { execFile } from 'node:child_process';
|
|
5
|
+
import { writeFile } from 'node:fs/promises';
|
|
6
|
+
import { resolve } from 'node:path';
|
|
7
|
+
import process from 'node:process';
|
|
8
|
+
import { promisify } from 'node:util';
|
|
9
|
+
|
|
10
|
+
import { formatUiDoctorText, runUiDoctor, UI_DOCTOR_EXIT } from './index.mjs';
|
|
11
|
+
|
|
12
|
+
const execFileAsync = promisify(execFile);
|
|
13
|
+
const args = process.argv.slice(2);
|
|
14
|
+
const take = (name, repeat = false) => {
|
|
15
|
+
const values = [];
|
|
16
|
+
while (args.includes(name)) {
|
|
17
|
+
const index = args.indexOf(name);
|
|
18
|
+
const value = args[index + 1];
|
|
19
|
+
if (!value || value.startsWith('--'))
|
|
20
|
+
throw new Error(`${name} requires a value.`);
|
|
21
|
+
args.splice(index, 2);
|
|
22
|
+
values.push(value);
|
|
23
|
+
if (!repeat) break;
|
|
24
|
+
}
|
|
25
|
+
return repeat ? values : values[0];
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
async function changedPaths(root) {
|
|
29
|
+
const { stdout: tracked } = await execFileAsync(
|
|
30
|
+
'git',
|
|
31
|
+
['diff', '--name-only', '--diff-filter=ACMR', 'HEAD', '--'],
|
|
32
|
+
{ cwd: root },
|
|
33
|
+
);
|
|
34
|
+
const { stdout: untracked } = await execFileAsync(
|
|
35
|
+
'git',
|
|
36
|
+
['ls-files', '--others', '--exclude-standard'],
|
|
37
|
+
{ cwd: root },
|
|
38
|
+
);
|
|
39
|
+
return [
|
|
40
|
+
...new Set(`${tracked}\n${untracked}`.split(/\r?\n/).filter(Boolean)),
|
|
41
|
+
].sort();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
try {
|
|
45
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
46
|
+
console.log(`Usage: kerf-ui-doctor [--full | --changed] [options]
|
|
47
|
+
|
|
48
|
+
Options:
|
|
49
|
+
--root <path> Workspace root (default: current directory)
|
|
50
|
+
--package <name|path> Select one workspace package
|
|
51
|
+
--path <path> Restrict changed mode (repeatable)
|
|
52
|
+
--config <path> Doctor config (default: .kerf-ui-doctor.json)
|
|
53
|
+
--browser-url <url> Explicitly enable browser evaluation
|
|
54
|
+
--browser <list> Comma-separated chromium,firefox,webkit
|
|
55
|
+
--eslint <preset> recommended-ui or strict-ui
|
|
56
|
+
--format <format> text or json (default: text)
|
|
57
|
+
--output <path> Also write the merged report
|
|
58
|
+
--no-cache Disable the content-addressed cache
|
|
59
|
+
|
|
60
|
+
Exit codes: 0 clean, 1 findings, 2 configuration/tool failure, 130 cancelled.`);
|
|
61
|
+
process.exit(0);
|
|
62
|
+
}
|
|
63
|
+
const root = resolve(take('--root') ?? process.cwd());
|
|
64
|
+
const packageSelector = take('--package');
|
|
65
|
+
const explicitPaths = take('--path', true);
|
|
66
|
+
const configPath = take('--config');
|
|
67
|
+
const url = take('--browser-url');
|
|
68
|
+
const browserList = take('--browser');
|
|
69
|
+
const eslintConfig = take('--eslint') ?? 'recommended-ui';
|
|
70
|
+
const format = take('--format') ?? 'text';
|
|
71
|
+
const output = take('--output');
|
|
72
|
+
const full = args.includes('--full');
|
|
73
|
+
const changed = args.includes('--changed');
|
|
74
|
+
const noCache = args.includes('--no-cache');
|
|
75
|
+
const known = new Set(['--full', '--changed', '--no-cache']);
|
|
76
|
+
const unknown = args.filter((item) => !known.has(item));
|
|
77
|
+
if (unknown.length)
|
|
78
|
+
throw new Error(`Unknown arguments: ${unknown.join(' ')}`);
|
|
79
|
+
if (full && changed)
|
|
80
|
+
throw new Error('--full and --changed are mutually exclusive.');
|
|
81
|
+
if (!['text', 'json'].includes(format))
|
|
82
|
+
throw new Error('--format must be text or json.');
|
|
83
|
+
if (!['recommended-ui', 'strict-ui'].includes(eslintConfig))
|
|
84
|
+
throw new Error('--eslint must be recommended-ui or strict-ui.');
|
|
85
|
+
const abort = new globalThis.AbortController();
|
|
86
|
+
process.once('SIGINT', () => abort.abort());
|
|
87
|
+
const paths =
|
|
88
|
+
changed && !explicitPaths.length ? await changedPaths(root) : explicitPaths;
|
|
89
|
+
const report = await runUiDoctor({
|
|
90
|
+
root,
|
|
91
|
+
package: packageSelector,
|
|
92
|
+
mode: changed ? 'changed' : full ? 'full' : undefined,
|
|
93
|
+
paths,
|
|
94
|
+
configPath,
|
|
95
|
+
cache: !noCache,
|
|
96
|
+
signal: abort.signal,
|
|
97
|
+
eslintConfig,
|
|
98
|
+
browser: url
|
|
99
|
+
? { url, browsers: browserList?.split(',').map((item) => item.trim()) }
|
|
100
|
+
: undefined,
|
|
101
|
+
});
|
|
102
|
+
const rendered =
|
|
103
|
+
format === 'json'
|
|
104
|
+
? JSON.stringify(report, null, 2)
|
|
105
|
+
: formatUiDoctorText(report);
|
|
106
|
+
if (output) await writeFile(resolve(root, output), `${rendered}\n`);
|
|
107
|
+
else console.log(rendered);
|
|
108
|
+
process.exitCode = report.exitCode;
|
|
109
|
+
} catch (error) {
|
|
110
|
+
console.error(`kerf-ui-doctor: ${error.message}`);
|
|
111
|
+
process.exitCode =
|
|
112
|
+
process.exitCode === UI_DOCTOR_EXIT.cancelled
|
|
113
|
+
? process.exitCode
|
|
114
|
+
: UI_DOCTOR_EXIT.configuration;
|
|
115
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://raw.githubusercontent.com/brianwestphal/kerf/main/ui/doctor/config.schema.json",
|
|
4
|
+
"title": "Kerf UI doctor configuration",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"required": ["schemaVersion"],
|
|
7
|
+
"properties": {
|
|
8
|
+
"$schema": { "type": "string" },
|
|
9
|
+
"schemaVersion": { "const": 1 },
|
|
10
|
+
"mode": { "enum": ["full", "changed"] },
|
|
11
|
+
"package": { "type": "string", "minLength": 1 },
|
|
12
|
+
"stages": {
|
|
13
|
+
"type": "object",
|
|
14
|
+
"properties": {
|
|
15
|
+
"catalog": { "type": "boolean" },
|
|
16
|
+
"typescript": { "type": "boolean" },
|
|
17
|
+
"eslint": { "type": "boolean" },
|
|
18
|
+
"analyzer": { "type": "boolean" },
|
|
19
|
+
"browser": { "type": "boolean" }
|
|
20
|
+
},
|
|
21
|
+
"additionalProperties": false
|
|
22
|
+
},
|
|
23
|
+
"browser": {
|
|
24
|
+
"type": "object",
|
|
25
|
+
"required": ["url"],
|
|
26
|
+
"properties": {
|
|
27
|
+
"url": { "type": "string", "pattern": "^https?://" },
|
|
28
|
+
"browsers": {
|
|
29
|
+
"type": "array",
|
|
30
|
+
"items": { "enum": ["chromium", "firefox", "webkit"] },
|
|
31
|
+
"uniqueItems": true
|
|
32
|
+
},
|
|
33
|
+
"retention": { "enum": ["always", "on-failure", "never"] },
|
|
34
|
+
"outputDirectory": { "$ref": "#/$defs/path" },
|
|
35
|
+
"reportPath": { "$ref": "#/$defs/path" }
|
|
36
|
+
},
|
|
37
|
+
"additionalProperties": false
|
|
38
|
+
},
|
|
39
|
+
"cache": { "type": "boolean" },
|
|
40
|
+
"suppressions": {
|
|
41
|
+
"type": "array",
|
|
42
|
+
"items": {
|
|
43
|
+
"type": "object",
|
|
44
|
+
"required": ["id", "rules", "target", "rationale"],
|
|
45
|
+
"properties": {
|
|
46
|
+
"id": { "type": "string", "pattern": "^[a-z][a-z0-9-]*$" },
|
|
47
|
+
"rules": {
|
|
48
|
+
"type": "array",
|
|
49
|
+
"items": { "type": "string", "minLength": 1 },
|
|
50
|
+
"minItems": 1,
|
|
51
|
+
"uniqueItems": true
|
|
52
|
+
},
|
|
53
|
+
"target": { "$ref": "#/$defs/path" },
|
|
54
|
+
"rationale": { "type": "string", "minLength": 12 }
|
|
55
|
+
},
|
|
56
|
+
"additionalProperties": false
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
"additionalProperties": false,
|
|
61
|
+
"$defs": {
|
|
62
|
+
"path": {
|
|
63
|
+
"type": "string",
|
|
64
|
+
"minLength": 1,
|
|
65
|
+
"pattern": "^(?!/)(?!.*(^|/)\\.\\.(/|$))(?!.*[?*]).+"
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
export type UiDoctorSeverity = 'error' | 'warning' | 'review' | 'info';
|
|
2
|
+
export type UiDoctorStageId =
|
|
3
|
+
| 'catalog'
|
|
4
|
+
| 'typescript'
|
|
5
|
+
| 'eslint'
|
|
6
|
+
| 'analyzer'
|
|
7
|
+
| 'browser'
|
|
8
|
+
| 'doctor'
|
|
9
|
+
| 'merged';
|
|
10
|
+
export type UiDoctorStageStatus =
|
|
11
|
+
'ran' | 'cached' | 'skipped' | 'unavailable' | 'failed' | 'cancelled';
|
|
12
|
+
|
|
13
|
+
export interface UiDoctorSuppression {
|
|
14
|
+
id: string;
|
|
15
|
+
rules: string[];
|
|
16
|
+
target: string;
|
|
17
|
+
rationale: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface UiDoctorConfig {
|
|
21
|
+
$schema?: string;
|
|
22
|
+
schemaVersion: 1;
|
|
23
|
+
mode?: 'full' | 'changed';
|
|
24
|
+
package?: string;
|
|
25
|
+
stages?: Partial<
|
|
26
|
+
Record<
|
|
27
|
+
'catalog' | 'typescript' | 'eslint' | 'analyzer' | 'browser',
|
|
28
|
+
boolean
|
|
29
|
+
>
|
|
30
|
+
>;
|
|
31
|
+
browser?: {
|
|
32
|
+
url: string;
|
|
33
|
+
browsers?: Array<'chromium' | 'firefox' | 'webkit'>;
|
|
34
|
+
retention?: 'always' | 'on-failure' | 'never';
|
|
35
|
+
outputDirectory?: string;
|
|
36
|
+
reportPath?: string;
|
|
37
|
+
};
|
|
38
|
+
cache?: boolean;
|
|
39
|
+
suppressions?: UiDoctorSuppression[];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface UiDoctorDiagnostic {
|
|
43
|
+
id: string;
|
|
44
|
+
severity: UiDoctorSeverity;
|
|
45
|
+
stage: UiDoctorStageId | string;
|
|
46
|
+
message: string;
|
|
47
|
+
location?: { file: string; line: number; column: number; path?: string };
|
|
48
|
+
dom?: { context?: string; selector: string };
|
|
49
|
+
evidence?: unknown;
|
|
50
|
+
catalogFacts?: unknown;
|
|
51
|
+
documentation?: string;
|
|
52
|
+
action?: string;
|
|
53
|
+
sources?: string[];
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export interface UiDoctorReport {
|
|
57
|
+
schemaVersion: 1;
|
|
58
|
+
tool: { name: '@kerfjs/ui/doctor'; reportVersion: 1 };
|
|
59
|
+
mode: 'full' | 'changed';
|
|
60
|
+
root: '.';
|
|
61
|
+
package: string;
|
|
62
|
+
paths: string[];
|
|
63
|
+
stages: Array<{ id: string; status: UiDoctorStageStatus; detail?: string }>;
|
|
64
|
+
diagnostics: UiDoctorDiagnostic[];
|
|
65
|
+
suppressions: Array<
|
|
66
|
+
UiDoctorDiagnostic & { suppression: { id: string; rationale: string } }
|
|
67
|
+
>;
|
|
68
|
+
summary: {
|
|
69
|
+
errors: number;
|
|
70
|
+
warnings: number;
|
|
71
|
+
review: number;
|
|
72
|
+
suppressed: number;
|
|
73
|
+
};
|
|
74
|
+
exitCode: 0 | 1 | 2 | 130;
|
|
75
|
+
cache: { hit: boolean; key: string };
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export const UI_DOCTOR_SCHEMA_VERSION: 1;
|
|
79
|
+
export const UI_DOCTOR_EXIT: Readonly<{
|
|
80
|
+
clean: 0;
|
|
81
|
+
findings: 1;
|
|
82
|
+
configuration: 2;
|
|
83
|
+
cancelled: 130;
|
|
84
|
+
}>;
|
|
85
|
+
export const UI_DOCTOR_RULES: Readonly<
|
|
86
|
+
Record<string, { severity: UiDoctorSeverity; title: string }>
|
|
87
|
+
>;
|
|
88
|
+
export function validateUiDoctorConfig(
|
|
89
|
+
config: unknown,
|
|
90
|
+
source?: string,
|
|
91
|
+
): UiDoctorDiagnostic[];
|
|
92
|
+
export function readUiDoctorConfig(
|
|
93
|
+
root: string,
|
|
94
|
+
path?: string,
|
|
95
|
+
): Promise<{
|
|
96
|
+
path: string;
|
|
97
|
+
config: UiDoctorConfig;
|
|
98
|
+
diagnostics: UiDoctorDiagnostic[];
|
|
99
|
+
}>;
|
|
100
|
+
export function resolveUiDoctorPackage(
|
|
101
|
+
root: string,
|
|
102
|
+
selector?: string,
|
|
103
|
+
): Promise<string>;
|
|
104
|
+
export function runUiDoctor(options?: {
|
|
105
|
+
root?: string;
|
|
106
|
+
package?: string;
|
|
107
|
+
mode?: 'full' | 'changed';
|
|
108
|
+
paths?: string[];
|
|
109
|
+
config?: UiDoctorConfig;
|
|
110
|
+
configPath?: string;
|
|
111
|
+
cache?: boolean;
|
|
112
|
+
signal?: AbortSignal;
|
|
113
|
+
browser?: UiDoctorConfig['browser'];
|
|
114
|
+
eslintConfig?: 'recommended-ui' | 'strict-ui';
|
|
115
|
+
}): Promise<UiDoctorReport>;
|
|
116
|
+
export function formatUiDoctorText(report: UiDoctorReport): string;
|