@kerfjs/ui 4.4.1 → 4.5.0-beta.1
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 +425 -42
- 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 +102 -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-extension.schema.json +79 -0
- package/ai/component-catalog-v2-overrides.json +319 -0
- package/ai/component-catalog-v2.d.ts +67 -0
- package/ai/component-catalog-v2.json +4675 -0
- package/ai/component-catalog-v2.schema.json +246 -0
- package/ai/component-catalog.json +5826 -0
- package/ai/component-catalog.schema.json +95 -0
- package/ai/public-api-signatures-v1.md +1719 -0
- package/ai/skill.md +129 -21
- package/ai/webawesome-jsx-signatures-v1.md +106 -0
- package/analyzer/cli.mjs +46 -0
- package/analyzer/index.d.mts +45 -0
- package/analyzer/index.mjs +737 -0
- package/analyzer/report.schema.json +66 -0
- package/dist/app-tab.d.ts +14 -2
- package/dist/app-tab.js +3 -1
- package/dist/browser/app-tab.js +3 -2
- package/dist/browser/disclosure-arrow.js +4 -0
- package/dist/browser/empty-state.js +3 -3
- package/dist/browser/floating-toolbar.js +3 -0
- package/dist/browser/list-action-row.js +4 -0
- package/dist/browser/list-header.js +6 -0
- package/dist/browser/list-inset-control.js +3 -0
- package/dist/browser/list-inset-text.js +3 -0
- package/dist/browser/list-item.js +4 -0
- package/dist/browser/list.js +3 -0
- package/dist/browser/loading-spinner.js +2 -2
- package/dist/browser/lucide-icon.js +2 -2
- package/dist/browser/pane.js +3 -0
- package/dist/browser/panel-header.js +7 -0
- package/dist/browser/resizable-region.js +2 -2
- package/dist/browser/segmented-control.js +3 -2
- package/dist/browser/select.js +4 -3
- package/dist/browser/skeleton.js +3 -0
- package/dist/browser/state-banner.js +3 -2
- package/dist/browser/sunken-panel.js +3 -0
- package/dist/browser/tab-bar.js +2 -2
- package/dist/browser/token-search-field.js +4 -0
- package/dist/browser/toolbar-control-group.js +2 -2
- package/dist/browser/toolbar-text.js +3 -2
- package/dist/browser/toolbar.js +2 -2
- package/dist/browser/value-table.js +3 -2
- 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 +150 -0
- package/dist/catalog.js +377 -0
- package/dist/catalog.js.map +1 -0
- package/dist/chunk-5VTB2BKP.js +35 -0
- package/dist/chunk-5VTB2BKP.js.map +1 -0
- package/dist/chunk-6WTETUZ6.js +21 -0
- package/dist/chunk-6WTETUZ6.js.map +1 -0
- package/dist/chunk-7KEUJIIC.js +218 -0
- package/dist/chunk-7KEUJIIC.js.map +1 -0
- package/dist/chunk-7XRDUG4Y.js +58 -0
- package/dist/chunk-7XRDUG4Y.js.map +1 -0
- package/dist/chunk-B3A3IVO5.js +83 -0
- package/dist/chunk-B3A3IVO5.js.map +1 -0
- package/dist/chunk-DPDBVKNA.js +113 -0
- package/dist/chunk-DPDBVKNA.js.map +1 -0
- package/dist/chunk-FEAMR7S4.js +68 -0
- package/dist/chunk-FEAMR7S4.js.map +1 -0
- package/dist/chunk-FEY65TBV.js +15 -0
- package/dist/chunk-FEY65TBV.js.map +1 -0
- package/dist/{chunk-TRNBHPNW.js → chunk-IJBSZ4NX.js} +11 -8
- package/dist/chunk-IJBSZ4NX.js.map +1 -0
- package/dist/chunk-J5BFYY7Q.js +35 -0
- package/dist/chunk-J5BFYY7Q.js.map +1 -0
- package/dist/chunk-JRKSK2HX.js +79 -0
- package/dist/chunk-JRKSK2HX.js.map +1 -0
- package/dist/chunk-KWWR5VMS.js +45 -0
- package/dist/chunk-KWWR5VMS.js.map +1 -0
- package/dist/chunk-LPOMRC4C.js +25 -0
- package/dist/chunk-LPOMRC4C.js.map +1 -0
- package/dist/chunk-M7JBER5U.js +41 -0
- package/dist/chunk-M7JBER5U.js.map +1 -0
- package/dist/chunk-NENBUYX5.js +79 -0
- package/dist/chunk-NENBUYX5.js.map +1 -0
- package/dist/chunk-NWSEP5RT.js +117 -0
- package/dist/chunk-NWSEP5RT.js.map +1 -0
- package/dist/chunk-OJWWHFH3.js +426 -0
- package/dist/chunk-OJWWHFH3.js.map +1 -0
- package/dist/chunk-PSKN344N.js +114 -0
- package/dist/chunk-PSKN344N.js.map +1 -0
- package/dist/chunk-PX3YSS5C.js +32 -0
- package/dist/chunk-PX3YSS5C.js.map +1 -0
- package/dist/chunk-QO55FN2Y.js +20 -0
- package/dist/chunk-QO55FN2Y.js.map +1 -0
- package/dist/chunk-R7ZWUN64.js +38 -0
- package/dist/chunk-R7ZWUN64.js.map +1 -0
- package/dist/chunk-RKVQEH4J.js +61 -0
- package/dist/chunk-RKVQEH4J.js.map +1 -0
- package/dist/chunk-SRSJO5QE.js +40 -0
- package/dist/chunk-SRSJO5QE.js.map +1 -0
- package/dist/chunk-SYDMYBPG.js +31 -0
- package/dist/chunk-SYDMYBPG.js.map +1 -0
- package/dist/chunk-TB6DY7H5.js +25 -0
- package/dist/chunk-TB6DY7H5.js.map +1 -0
- package/dist/chunk-UFZKLBCF.js +108 -0
- package/dist/chunk-UFZKLBCF.js.map +1 -0
- package/dist/{chunk-MQIZAD4A.js → chunk-UKKSMZYT.js} +76 -22
- package/dist/chunk-UKKSMZYT.js.map +1 -0
- package/dist/chunk-W5L2JSBY.js +40 -0
- package/dist/chunk-W5L2JSBY.js.map +1 -0
- package/dist/chunk-Y45PPBDV.js +105 -0
- package/dist/chunk-Y45PPBDV.js.map +1 -0
- package/dist/chunk-Y6LYKFXY.js +43 -0
- package/dist/chunk-Y6LYKFXY.js.map +1 -0
- package/dist/collapsible-panel.d.ts +66 -0
- package/dist/collapsible-panel.js +63 -0
- package/dist/collapsible-panel.js.map +1 -0
- package/dist/device-class.d.ts +62 -0
- package/dist/device-class.js +103 -0
- package/dist/device-class.js.map +1 -0
- package/dist/disclosure-arrow.d.ts +14 -0
- package/dist/disclosure-arrow.js +4 -0
- package/dist/disclosure-arrow.js.map +1 -0
- package/dist/divider-sides-267FA7sY.d.ts +4 -0
- package/dist/empty-state.d.ts +1 -1
- package/dist/empty-state.js +1 -1
- package/dist/floating-toolbar.d.ts +28 -0
- package/dist/floating-toolbar.js +3 -0
- package/dist/floating-toolbar.js.map +1 -0
- package/dist/index.d.ts +20 -9
- package/dist/index.js +29 -18
- package/dist/list-action-row.d.ts +52 -0
- package/dist/list-action-row.js +5 -0
- package/dist/list-action-row.js.map +1 -0
- package/dist/list-header.d.ts +64 -0
- package/dist/list-header.js +7 -0
- package/dist/list-header.js.map +1 -0
- package/dist/list-inset-control.d.ts +16 -0
- package/dist/list-inset-control.js +3 -0
- package/dist/list-inset-control.js.map +1 -0
- package/dist/list-inset-text.d.ts +25 -0
- package/dist/list-inset-text.js +3 -0
- package/dist/list-inset-text.js.map +1 -0
- package/dist/list-item.d.ts +33 -0
- package/dist/list-item.js +5 -0
- package/dist/list-item.js.map +1 -0
- package/dist/list.d.ts +19 -0
- package/dist/list.js +3 -0
- package/dist/list.js.map +1 -0
- package/dist/loading-spinner.js +1 -1
- package/dist/nav-stack.d.ts +38 -0
- package/dist/nav-stack.js +5 -0
- package/dist/nav-stack.js.map +1 -0
- 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 +42 -0
- package/dist/panel-header.js +7 -0
- package/dist/panel-header.js.map +1 -0
- package/dist/resizable-region.d.ts +3 -1
- package/dist/resizable-region.js +1 -1
- package/dist/segmented-control.d.ts +8 -6
- package/dist/segmented-control.js +2 -1
- package/dist/select-register.d.ts +5 -1
- package/dist/select-register.js +3 -0
- package/dist/select-register.js.map +1 -1
- package/dist/select.d.ts +15 -6
- package/dist/select.js +2 -1
- package/dist/skeleton.d.ts +24 -0
- package/dist/skeleton.js +3 -0
- package/dist/skeleton.js.map +1 -0
- package/dist/split-view.d.ts +42 -0
- package/dist/split-view.js +81 -0
- package/dist/split-view.js.map +1 -0
- package/dist/state-banner.d.ts +8 -3
- package/dist/state-banner.js +2 -1
- package/dist/styles/app-tab.css +152 -0
- package/dist/styles/catalog.css +575 -0
- package/dist/styles/collapsible-panel.css +146 -0
- package/dist/styles/disclosure-arrow.css +23 -0
- package/dist/styles/empty-state.css +47 -0
- package/dist/styles/floating-toolbar.css +38 -0
- package/dist/styles/foundation.css +362 -0
- package/dist/styles/layout.css +55 -0
- package/dist/styles/list-action-row.css +160 -0
- package/dist/styles/list-header.css +160 -0
- package/dist/styles/list-inset-control.css +7 -0
- package/dist/styles/list-inset-text.css +14 -0
- package/dist/styles/list-item.css +123 -0
- package/dist/styles/list.css +43 -0
- package/dist/styles/loading-spinner.css +21 -0
- package/dist/styles/lucide-icon.css +6 -0
- package/dist/styles/nav-stack.css +111 -0
- package/dist/styles/pane.css +62 -0
- package/dist/styles/panel-header.css +64 -0
- package/dist/styles/resizable-region.css +111 -0
- package/{src → dist/styles}/segmented-control.css +58 -31
- package/dist/styles/select.css +140 -0
- package/dist/styles/skeleton.css +31 -0
- package/dist/styles/split-view.css +35 -0
- package/dist/styles/state-banner.css +198 -0
- package/dist/styles/styles.css +24 -0
- package/dist/styles/sunken-panel.css +22 -0
- package/dist/styles/tab-bar.css +45 -0
- package/dist/styles/tab-scaffold.css +84 -0
- package/dist/styles/token-search-field.css +289 -0
- package/dist/styles/toolbar-control-group.css +225 -0
- package/dist/styles/toolbar-text.css +92 -0
- package/dist/styles/toolbar.css +97 -0
- package/dist/styles/value-table.css +75 -0
- package/dist/styles/webawesome.css +399 -0
- package/dist/styles/workbench.css +94 -0
- package/dist/sunken-panel.d.ts +19 -0
- package/dist/sunken-panel.js +3 -0
- package/dist/sunken-panel.js.map +1 -0
- package/dist/tab-bar.d.ts +10 -2
- package/dist/tab-bar.js +1 -1
- package/dist/tab-scaffold.d.ts +30 -0
- package/dist/tab-scaffold.js +51 -0
- package/dist/tab-scaffold.js.map +1 -0
- package/dist/token-search-field.d.ts +59 -0
- package/dist/token-search-field.js +4 -0
- package/dist/token-search-field.js.map +1 -0
- package/dist/toolbar-control-group.d.ts +11 -5
- package/dist/toolbar-control-group.js +1 -1
- package/dist/toolbar-text.d.ts +31 -4
- package/dist/toolbar-text.js +2 -1
- package/dist/toolbar.d.ts +5 -3
- package/dist/toolbar.js +1 -1
- package/dist/value-table.d.ts +13 -3
- package/dist/value-table.js +2 -1
- package/dist/webawesome.d.ts +100 -0
- package/dist/webawesome.js +3 -0
- package/dist/webawesome.js.map +1 -0
- package/dist/wire-catalog.d.ts +57 -0
- package/dist/wire-catalog.js +302 -0
- package/dist/wire-catalog.js.map +1 -0
- package/dist/wire-nav-stack.d.ts +15 -0
- package/dist/wire-nav-stack.js +103 -0
- package/dist/wire-nav-stack.js.map +1 -0
- package/dist/wire-resizable-regions.d.ts +1 -1
- package/dist/wire-resizable-regions.js +1 -1
- package/dist/wire-sidebar.d.ts +43 -0
- package/dist/wire-sidebar.js +128 -0
- package/dist/wire-sidebar.js.map +1 -0
- package/dist/wire-tab-bars.d.ts +18 -2
- package/dist/wire-tab-bars.js +1 -1
- package/dist/wire-tab-scaffold.d.ts +11 -0
- package/dist/wire-tab-scaffold.js +21 -0
- package/dist/wire-tab-scaffold.js.map +1 -0
- package/dist/wire-token-search-fields.d.ts +111 -0
- package/dist/wire-token-search-fields.js +5 -0
- package/dist/wire-token-search-fields.js.map +1 -0
- package/dist/workbench.d.ts +33 -0
- package/dist/workbench.js +64 -0
- package/dist/workbench.js.map +1 -0
- package/docs/accessibility.md +124 -4
- package/docs/app-layouts.md +61 -0
- package/docs/catalog.md +364 -0
- package/docs/collapsible-panel.md +129 -0
- package/docs/component-contract.md +312 -5
- package/docs/component-selection.md +219 -0
- package/docs/design/templates/empty-state/filtered-dark.svg +1 -0
- package/docs/design/templates/empty-state/filtered.svg +1 -0
- package/docs/design/templates/empty-state/with-action-dark.svg +1 -0
- package/docs/design/templates/empty-state/with-action.svg +1 -0
- package/docs/design/templates/empty-state-dark.svg +1 -0
- package/docs/design/templates/empty-state.svg +1 -0
- package/docs/design/templates/list/compact-dark.svg +1 -0
- package/docs/design/templates/list/compact.svg +1 -0
- package/docs/design/templates/list/stack-dark.svg +1 -0
- package/docs/design/templates/list/stack.svg +1 -0
- package/docs/design/templates/list-action-row/default-dark.svg +1 -0
- package/docs/design/templates/list-action-row/default.svg +1 -0
- package/docs/design/templates/list-action-row/selected-dark.svg +1 -0
- package/docs/design/templates/list-action-row/selected.svg +1 -0
- package/docs/design/templates/list-action-row-dark.svg +1 -0
- package/docs/design/templates/list-action-row.svg +1 -0
- package/docs/design/templates/list-dark.svg +1 -0
- package/docs/design/templates/list-header/action-dark.svg +1 -0
- package/docs/design/templates/list-header/action.svg +1 -0
- package/docs/design/templates/list-header/count-dark.svg +1 -0
- package/docs/design/templates/list-header/count.svg +1 -0
- package/docs/design/templates/list-header/toggle-collapsed-dark.svg +1 -0
- package/docs/design/templates/list-header/toggle-collapsed.svg +1 -0
- package/docs/design/templates/list-header/toggle-expanded-dark.svg +1 -0
- package/docs/design/templates/list-header/toggle-expanded.svg +1 -0
- package/docs/design/templates/list-header-dark.svg +1 -0
- package/docs/design/templates/list-header.svg +1 -0
- package/docs/design/templates/list-item/default-dark.svg +1 -0
- package/docs/design/templates/list-item/default.svg +1 -0
- package/docs/design/templates/list-item/multiline-dark.svg +1 -0
- package/docs/design/templates/list-item/multiline.svg +1 -0
- package/docs/design/templates/list-item/selected-dark.svg +1 -0
- package/docs/design/templates/list-item/selected.svg +1 -0
- package/docs/design/templates/list-item/trailing-dark.svg +1 -0
- package/docs/design/templates/list-item/trailing.svg +1 -0
- package/docs/design/templates/list-item-dark.svg +1 -0
- package/docs/design/templates/list-item.svg +1 -0
- package/docs/design/templates/list.svg +1 -0
- package/docs/design/templates/panel-header/icon-actions-dark.svg +1 -0
- package/docs/design/templates/panel-header/icon-actions.svg +1 -0
- package/docs/design/templates/panel-header/icon-summary-actions-dark.svg +1 -0
- package/docs/design/templates/panel-header/icon-summary-actions.svg +1 -0
- package/docs/design/templates/panel-header/no-icon-dark.svg +1 -0
- package/docs/design/templates/panel-header/no-icon.svg +1 -0
- package/docs/design/templates/panel-header/page-heading-dark.svg +1 -0
- package/docs/design/templates/panel-header/page-heading.svg +1 -0
- package/docs/design/templates/panel-header/title-only-dark.svg +1 -0
- package/docs/design/templates/panel-header/title-only.svg +1 -0
- package/docs/design/templates/panel-header-dark.svg +1 -0
- package/docs/design/templates/panel-header.svg +1 -0
- package/docs/design/templates/segmented-control/equal-dark.svg +1 -0
- package/docs/design/templates/segmented-control/equal.svg +1 -0
- package/docs/design/templates/segmented-control/pill-small-dark.svg +1 -0
- package/docs/design/templates/segmented-control/pill-small.svg +1 -0
- package/docs/design/templates/segmented-control-dark.svg +1 -0
- package/docs/design/templates/segmented-control.svg +1 -0
- package/docs/design/templates/skeleton/avatar-dark.svg +1 -0
- package/docs/design/templates/skeleton/avatar.svg +1 -0
- package/docs/design/templates/skeleton/block-dark.svg +1 -0
- package/docs/design/templates/skeleton/block.svg +1 -0
- package/docs/design/templates/skeleton/lines-dark.svg +1 -0
- package/docs/design/templates/skeleton/lines.svg +1 -0
- package/docs/design/templates/skeleton-dark.svg +1 -0
- package/docs/design/templates/skeleton.svg +1 -0
- package/docs/design/templates/state-banner/danger-dark.svg +1 -0
- package/docs/design/templates/state-banner/danger.svg +1 -0
- package/docs/design/templates/state-banner/info-badge-dark.svg +1 -0
- package/docs/design/templates/state-banner/info-badge.svg +1 -0
- package/docs/design/templates/state-banner/info-dark.svg +1 -0
- package/docs/design/templates/state-banner/info.svg +1 -0
- package/docs/design/templates/state-banner/success-dark.svg +1 -0
- package/docs/design/templates/state-banner/success.svg +1 -0
- package/docs/design/templates/state-banner/warning-dark.svg +1 -0
- package/docs/design/templates/state-banner/warning.svg +1 -0
- package/docs/design/templates/state-banner-dark.svg +1 -0
- package/docs/design/templates/state-banner.svg +1 -0
- package/docs/design/templates/tab-bar/workspace-tabs-dark.svg +1 -0
- package/docs/design/templates/tab-bar/workspace-tabs.svg +1 -0
- package/docs/design/templates/tab-bar-dark.svg +1 -0
- package/docs/design/templates/tab-bar.svg +1 -0
- package/docs/design/templates/token-search-field/empty-dark.svg +1 -0
- package/docs/design/templates/token-search-field/empty.svg +1 -0
- package/docs/design/templates/token-search-field/tokens-dark.svg +1 -0
- package/docs/design/templates/token-search-field/tokens.svg +1 -0
- package/docs/design/templates/token-search-field-dark.svg +1 -0
- package/docs/design/templates/token-search-field.svg +1 -0
- package/docs/design/templates/toolbar/title-and-actions-dark.svg +1 -0
- package/docs/design/templates/toolbar/title-and-actions.svg +1 -0
- package/docs/design/templates/toolbar-control-group/borderless-single-dark.svg +1 -0
- package/docs/design/templates/toolbar-control-group/borderless-single.svg +1 -0
- package/docs/design/templates/toolbar-control-group/icon-buttons-dark.svg +1 -0
- package/docs/design/templates/toolbar-control-group/icon-buttons.svg +1 -0
- package/docs/design/templates/toolbar-control-group/push-buttons-dark.svg +1 -0
- package/docs/design/templates/toolbar-control-group/push-buttons.svg +1 -0
- package/docs/design/templates/toolbar-control-group/with-text-dark.svg +1 -0
- package/docs/design/templates/toolbar-control-group/with-text.svg +1 -0
- package/docs/design/templates/toolbar-control-group-dark.svg +1 -0
- package/docs/design/templates/toolbar-control-group.svg +1 -0
- package/docs/design/templates/toolbar-dark.svg +1 -0
- package/docs/design/templates/toolbar-text/default-dark.svg +1 -0
- package/docs/design/templates/toolbar-text/default.svg +1 -0
- package/docs/design/templates/toolbar-text/large-dark.svg +1 -0
- package/docs/design/templates/toolbar-text/large.svg +1 -0
- package/docs/design/templates/toolbar-text/small-dark.svg +1 -0
- package/docs/design/templates/toolbar-text/small.svg +1 -0
- package/docs/design/templates/toolbar-text-dark.svg +1 -0
- package/docs/design/templates/toolbar-text.svg +1 -0
- package/docs/design/templates/toolbar.svg +1 -0
- package/docs/design/templates/value-table/details-dark.svg +1 -0
- package/docs/design/templates/value-table/details.svg +1 -0
- package/docs/design/templates/value-table-dark.svg +1 -0
- package/docs/design/templates/value-table.svg +1 -0
- package/docs/design/templates.md +130 -0
- package/docs/design-philosophy.md +59 -1
- package/docs/device-class.md +54 -0
- package/docs/examples/application-ui-profile.json +60 -0
- package/docs/examples/command-palette-adapter.tsx +120 -0
- package/docs/examples/component-catalog-extension-v2.json +81 -0
- package/docs/examples/component-catalog-extension.json +45 -0
- package/docs/layout.md +150 -0
- package/docs/nav-stack.md +56 -0
- package/docs/recipes.md +167 -0
- package/docs/split-view.md +49 -0
- package/docs/sunken-panel.md +53 -0
- package/docs/tab-scaffold.md +50 -0
- package/docs/type-contracts.md +102 -0
- package/docs/ui-analyzer.md +86 -0
- package/docs/ui-doctor.md +84 -0
- package/docs/ui-evaluator.md +122 -0
- package/docs/ux-demo.md +23 -9
- package/docs/webawesome-theme.md +87 -7
- package/docs/workbench.md +73 -0
- package/doctor/cli.mjs +115 -0
- package/doctor/config.schema.json +68 -0
- package/doctor/eslint-diagnostics.d.mts +8 -0
- package/doctor/eslint-diagnostics.mjs +7 -0
- package/doctor/index.d.mts +116 -0
- package/doctor/index.mjs +1257 -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 +197 -14
- package/package.json +228 -46
- package/traversal-exclusions.mjs +27 -0
- package/ux-demo/recipes/app-shell.tsx +228 -0
- package/ux-demo/recipes/collapsible-sidebar.tsx +223 -0
- package/ux-demo/recipes/compact-toolbar.tsx +145 -0
- package/ux-demo/recipes/composer-form.tsx +151 -0
- package/ux-demo/recipes/list-detail-dialog.tsx +181 -0
- package/ux-demo/recipes/list-workspace-states.tsx +183 -0
- package/ux-demo/recipes/loaders.ts +36 -0
- package/ux-demo/recipes/loading-inspector.tsx +164 -0
- package/ux-demo/recipes/mount-recipe.ts +64 -0
- package/ux-demo/recipes/navigation-sidebar.tsx +137 -0
- package/ux-demo/recipes/navigation-stack.tsx +118 -0
- package/ux-demo/recipes/recipes.css +448 -0
- package/ux-demo/recipes/types.ts +19 -0
- package/ux-demo/recipes/workspace-header.tsx +67 -0
- package/dist/browser/dialog-header.js +0 -3
- package/dist/browser/menu-header.js +0 -3
- package/dist/browser/menu-item.js +0 -3
- package/dist/browser/page-header.js +0 -3
- package/dist/chunk-2PES33HS.js +0 -13
- package/dist/chunk-2PES33HS.js.map +0 -1
- package/dist/chunk-3O7ZEUW2.js +0 -27
- package/dist/chunk-3O7ZEUW2.js.map +0 -1
- package/dist/chunk-4HHCYSGT.js +0 -79
- package/dist/chunk-4HHCYSGT.js.map +0 -1
- package/dist/chunk-55DATV3A.js +0 -10
- package/dist/chunk-55DATV3A.js.map +0 -1
- package/dist/chunk-723YNJBH.js +0 -17
- package/dist/chunk-723YNJBH.js.map +0 -1
- package/dist/chunk-DAFFDO33.js +0 -14
- package/dist/chunk-DAFFDO33.js.map +0 -1
- package/dist/chunk-GY5WH7TO.js +0 -10
- package/dist/chunk-GY5WH7TO.js.map +0 -1
- package/dist/chunk-MBR3TYNL.js +0 -14
- package/dist/chunk-MBR3TYNL.js.map +0 -1
- package/dist/chunk-MFKMHK5W.js +0 -10
- package/dist/chunk-MFKMHK5W.js.map +0 -1
- package/dist/chunk-MQIZAD4A.js.map +0 -1
- package/dist/chunk-NHXX4JI5.js +0 -28
- package/dist/chunk-NHXX4JI5.js.map +0 -1
- package/dist/chunk-OPCJRCYO.js +0 -19
- package/dist/chunk-OPCJRCYO.js.map +0 -1
- package/dist/chunk-T5XNMVOI.js +0 -24
- package/dist/chunk-T5XNMVOI.js.map +0 -1
- package/dist/chunk-TRNBHPNW.js.map +0 -1
- package/dist/chunk-VHW5YNIB.js +0 -17
- package/dist/chunk-VHW5YNIB.js.map +0 -1
- package/dist/chunk-VKH2JLDZ.js +0 -10
- package/dist/chunk-VKH2JLDZ.js.map +0 -1
- package/dist/chunk-WVPYVDXY.js +0 -15
- package/dist/chunk-WVPYVDXY.js.map +0 -1
- package/dist/chunk-WY4CAEPG.js +0 -14
- package/dist/chunk-WY4CAEPG.js.map +0 -1
- package/dist/dialog-header.d.ts +0 -14
- package/dist/dialog-header.js +0 -3
- package/dist/dialog-header.js.map +0 -1
- package/dist/menu-header.d.ts +0 -15
- package/dist/menu-header.js +0 -3
- package/dist/menu-header.js.map +0 -1
- package/dist/menu-item.d.ts +0 -22
- package/dist/menu-item.js +0 -3
- package/dist/menu-item.js.map +0 -1
- package/dist/page-header.d.ts +0 -9
- package/dist/page-header.js +0 -3
- package/dist/page-header.js.map +0 -1
- package/src/app-tab.css +0 -20
- package/src/dialog-header.css +0 -9
- package/src/empty-state.css +0 -6
- package/src/foundation.css +0 -106
- package/src/loading-spinner.css +0 -4
- package/src/lucide-icon.css +0 -1
- package/src/menu-header.css +0 -13
- package/src/menu-item.css +0 -12
- package/src/page-header.css +0 -3
- package/src/resizable-region.css +0 -19
- package/src/select.css +0 -18
- package/src/state-banner.css +0 -31
- package/src/styles.css +0 -18
- package/src/tab-bar.css +0 -4
- package/src/toolbar-control-group.css +0 -25
- package/src/toolbar-text.css +0 -4
- package/src/toolbar.css +0 -9
- package/src/value-table.css +0 -5
- package/src/webawesome.css +0 -178
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# Static CSS and layout ownership analyzer
|
|
2
|
+
|
|
3
|
+
`kerf-ui-analyze` checks integration facts that TypeScript and an individual
|
|
4
|
+
ESLint file cannot see across component markup, stylesheets, the component
|
|
5
|
+
catalog, and the application UI profile.
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
npx kerf-ui-analyze --root . --format text src
|
|
9
|
+
npx kerf-ui-analyze --root . --format json --output artifacts/kerf-ui.json
|
|
10
|
+
npx kerf-ui-analyze --root . --format sarif --output artifacts/kerf-ui.sarif
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
The analyzer is opt-in. Add a project script such as
|
|
14
|
+
`"check:kerf-ui-layout": "kerf-ui-analyze --root . src"`; it never executes
|
|
15
|
+
application code. It discovers `.kerf-ui-profile.json`, joins every declared
|
|
16
|
+
composition catalog by `package:id`, parses literal TSX/JSX class usage and CSS,
|
|
17
|
+
and reports portable repository-relative locations.
|
|
18
|
+
|
|
19
|
+
Analysis is scoped per source file. Each TSX/JSX file resolves package,
|
|
20
|
+
workspace, and parent-to-child directory profiles from its own location, then
|
|
21
|
+
follows its relative CSS imports (including nested CSS `@import`s). Stylesheet
|
|
22
|
+
facts are never pooled across unrelated files or sibling packages, and a shared
|
|
23
|
+
stylesheet's diagnostics are evaluated against every importing source profile.
|
|
24
|
+
A violation stays active when any consumer has not narrowly excepted it;
|
|
25
|
+
duplicate source findings collapse to one result. Directly targeted or orphaned
|
|
26
|
+
CSS uses its own directory profile. A direct changed-file target still brings
|
|
27
|
+
its reachable project-local styles into the report, including quoted or
|
|
28
|
+
unquoted `url()` imports; external package styles remain outside the consumer
|
|
29
|
+
boundary.
|
|
30
|
+
|
|
31
|
+
Recursive discovery excludes generated and tool-owned trees, including any
|
|
32
|
+
nested `.claude/worktrees` checkout. Those checkouts are separate repositories,
|
|
33
|
+
not application source, and cannot contribute files, diagnostics, profile
|
|
34
|
+
policy, or analysis inputs to the containing application.
|
|
35
|
+
|
|
36
|
+
## Diagnostics and exit behavior
|
|
37
|
+
|
|
38
|
+
| Rule | Kind | Meaning |
|
|
39
|
+
| ---------- | ------ | ----------------------------------------------------------------- |
|
|
40
|
+
| `KUI-L001` | error | A stylesheet reaches into a private or unknown `.kui-*` selector. |
|
|
41
|
+
| `KUI-L002` | error | A stylesheet references an unknown or private `--kui-*` token. |
|
|
42
|
+
| `KUI-L003` | error | One element combines public classes that claim the same geometry. |
|
|
43
|
+
| `KUI-L004` | review | Nested literal classes both add a content inset. |
|
|
44
|
+
| `KUI-L005` | review | Consumer CSS forces a public component's dimensions. |
|
|
45
|
+
| `KUI-L006` | review | Literal spacing falls outside Kerf's approved scale. |
|
|
46
|
+
| `KUI-L007` | error | A declared scroll owner is nested inside another scroll owner. |
|
|
47
|
+
| `KUI-L008` | review | A dynamic class expression cannot be classified soundly. |
|
|
48
|
+
| `KUI-L009` | error | A stylesheet cannot be parsed. |
|
|
49
|
+
|
|
50
|
+
Errors are provable contract violations and make the command exit 1. Review
|
|
51
|
+
findings are deliberately heuristic and do not fail by default; pass
|
|
52
|
+
`--fail-on-review` when a project has reviewed its baseline and wants them to
|
|
53
|
+
gate CI. Text, versioned JSON, and SARIF carry the same stable rule ids,
|
|
54
|
+
source locations, evidence, and ownership chain.
|
|
55
|
+
|
|
56
|
+
The JSON report schema is exported as
|
|
57
|
+
`@kerfjs/ui/analyzer/report.schema.json`.
|
|
58
|
+
|
|
59
|
+
Profile discovery, parsing, catalog-loading, and validation diagnostics retain
|
|
60
|
+
their stable `KUI-P###` ids in every output format and count as errors. The
|
|
61
|
+
analyzer therefore cannot silently pass with stale or unreadable policy input.
|
|
62
|
+
|
|
63
|
+
The analyzer is conservative about dynamic class expressions: it emits one
|
|
64
|
+
review finding and does not guess which selectors, tokens, or owners the value
|
|
65
|
+
might contain. CSS values expressed through public variables or `calc()` are
|
|
66
|
+
also left to their owning token contract rather than reverse-engineered.
|
|
67
|
+
|
|
68
|
+
## Narrow exceptions
|
|
69
|
+
|
|
70
|
+
Use the application profile's `exceptions` only for an exact rule and exact
|
|
71
|
+
repository-relative file. Every exception requires an id and rationale; broad
|
|
72
|
+
directories, globs, absolute paths, and traversal are rejected by the profile
|
|
73
|
+
validator.
|
|
74
|
+
|
|
75
|
+
```json
|
|
76
|
+
{
|
|
77
|
+
"id": "legacy-inspector-spacing",
|
|
78
|
+
"rules": ["KUI-L006"],
|
|
79
|
+
"target": "src/legacy/inspector.css",
|
|
80
|
+
"rationale": "The legacy inspector retains its measured spacing until migration."
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Review findings remain visible until explicitly suppressed. Avoid suppressing
|
|
85
|
+
`KUI-L001`, `KUI-L002`, `KUI-L003`, `KUI-L007`, or `KUI-L009`: those indicate a
|
|
86
|
+
definite boundary or parsing failure rather than an aesthetic judgment.
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# Kerf UI doctor
|
|
2
|
+
|
|
3
|
+
`kerf-ui-doctor` is the supported repair-loop entry point for a Kerf application. It produces one versioned report from application-profile and catalog validation, TypeScript, the Kerf UI ESLint preset, static layout analysis, and an explicitly enabled browser evaluation.
|
|
4
|
+
|
|
5
|
+
```sh
|
|
6
|
+
npx kerf-ui-doctor --full
|
|
7
|
+
npx kerf-ui-doctor --changed
|
|
8
|
+
npx kerf-ui-doctor --package @acme/admin --changed
|
|
9
|
+
npx kerf-ui-doctor --full --browser-url http://127.0.0.1:4173
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
The default terminal output is short and repair-oriented. `--format json` emits the schema-version-1 report; `--output report.json` writes the same representation. The package exports its contract from `@kerfjs/ui/doctor`, its configuration schema from `@kerfjs/ui/doctor/config.schema.json`, and its report schema from `@kerfjs/ui/doctor/report.schema.json`.
|
|
13
|
+
|
|
14
|
+
## Stages and trust boundary
|
|
15
|
+
|
|
16
|
+
The catalog stage discovers the package-default application UI profile and workspace/root-to-leaf directory layers. It validates referenced selection/composition catalogs and, when a workspace declares `package.json#kerfComponentCatalog`, invokes the installed `create-kerf-component` catalog checker to validate metadata, source exports, schema conformance, and generated-output drift. This checker reads source text; it does not import application modules.
|
|
17
|
+
|
|
18
|
+
TypeScript uses the compiler API with `noEmit`. ESLint loads the installed `eslint-plugin-kerfjs` `recommended-ui` preset (`--eslint strict-ui` opts into advisory rules as errors). This is deliberately an isolated Kerf lint pass rather than the consumer's complete ESLint configuration. Inline directives for consumer-owned plugins, such as `@typescript-eslint`, therefore do not produce false "rule definition not found" diagnostics; the consumer's normal ESLint command remains authoritative for those rules. Unknown `kerfjs/*` directives still fail the doctor pass. Stable KUI identifiers come from the packaged `application-ui-diagnostic-ids-v1.json` registry; the doctor also reads installed ESLint message metadata for semantic conflict detection instead of duplicating rule definitions. The analyzer calls the public `@kerfjs/ui/analyzer` contract. None of these stages executes generated application code.
|
|
19
|
+
|
|
20
|
+
The browser evaluator is different: it runs the application and is disabled by default. It only runs when configuration supplies `browser.url` or the command receives `--browser-url`. Start and authorize the target application separately.
|
|
21
|
+
|
|
22
|
+
An unavailable or failed stage does not prevent independent stages from reporting. Its final exit is still a configuration failure, so a partial run cannot appear clean.
|
|
23
|
+
|
|
24
|
+
## Full and changed modes
|
|
25
|
+
|
|
26
|
+
`--full` is the default and analyzes the selected package. `--changed` reads tracked and untracked paths from Git unless one or more `--path` values are supplied. An empty changed set is a configuration error instead of a false-clean success. With `--package`, workspace-relative Git paths are converted to package-relative paths before TypeScript, ESLint, and analyzer selection; paths outside the selected package are ignored.
|
|
27
|
+
|
|
28
|
+
Full traversal treats generated/tool-owned directories (`dist`, `coverage`,
|
|
29
|
+
`node_modules`, `.git`, `.kerf-cache`, `kerf-ui-evidence`, and nested
|
|
30
|
+
`.claude/worktrees` checkouts) as outside application source. Their files are
|
|
31
|
+
excluded consistently from TypeScript, the isolated ESLint pass, analyzer
|
|
32
|
+
discovery, and cache inputs, even when one lives below another application
|
|
33
|
+
directory.
|
|
34
|
+
|
|
35
|
+
TypeScript constructs the selected package's program so compiler options retain their real meaning, while its root inputs are narrowed to changed source files. Use `--full` for release gates.
|
|
36
|
+
|
|
37
|
+
## Configuration and suppressions
|
|
38
|
+
|
|
39
|
+
Place `.kerf-ui-doctor.json` at the workspace root:
|
|
40
|
+
|
|
41
|
+
```json
|
|
42
|
+
{
|
|
43
|
+
"$schema": "./node_modules/@kerfjs/ui/doctor/config.schema.json",
|
|
44
|
+
"schemaVersion": 1,
|
|
45
|
+
"mode": "full",
|
|
46
|
+
"stages": { "browser": false },
|
|
47
|
+
"cache": true,
|
|
48
|
+
"suppressions": [
|
|
49
|
+
{
|
|
50
|
+
"id": "legacy-toolbar",
|
|
51
|
+
"rules": ["KUI-L006"],
|
|
52
|
+
"target": "src/legacy-toolbar.css",
|
|
53
|
+
"rationale": "Removed with the toolbar migration in the next release."
|
|
54
|
+
}
|
|
55
|
+
]
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
A suppression requires a stable id, one or more exact diagnostic ids, an exact portable source path or browser selector, and a substantive rationale. Wildcards, absolute paths, and parent traversal are rejected. Suppressed diagnostics remain in `report.suppressions` with their rationale and do not affect the exit code.
|
|
60
|
+
|
|
61
|
+
KUI suppression ids are checked against the analyzer, evaluator, doctor, installed ESLint-rule metadata, and diagnostics emitted by available tools. An unknown or stale id is a configuration error rather than a silent no-op. TypeScript `TS####` and namespaced `eslint:*` ids remain valid even when that particular run does not emit them.
|
|
62
|
+
|
|
63
|
+
The content-addressed cache lives at `.kerf-cache/ui-doctor-v1.json`. Its key covers selected source/config/catalog JSON, package manifests, root and package lockfiles, mode, paths, and configuration. Browser results are never cached. Use `--no-cache` when investigating tool installation changes not yet reflected in a lockfile.
|
|
64
|
+
|
|
65
|
+
Reports replace the workspace's absolute path with `<repo-root>` and express in-workspace files as portable relative paths. External paths are reduced to `<external>/<basename>`. URLs are excluded from the cache key. Tool messages and evidence receive the same root redaction before output or caching.
|
|
66
|
+
|
|
67
|
+
## Report and exit contract
|
|
68
|
+
|
|
69
|
+
Every diagnostic has a stable `id`, `severity`, `stage`, message, and—when applicable—an exact source location with JSON path or DOM context/selector. Analyzer evidence, catalog facts, documentation links, and safe next actions are preserved when the source tool provides them. Identical findings merge with their source stages; same identifiers with conflicting severities remain separate and add `KUI-D003`.
|
|
70
|
+
|
|
71
|
+
Exit codes are deterministic:
|
|
72
|
+
|
|
73
|
+
- `0`: no active error diagnostics;
|
|
74
|
+
- `1`: repairable application findings;
|
|
75
|
+
- `2`: malformed configuration, unavailable required tooling, or a failed stage;
|
|
76
|
+
- `130`: cancellation.
|
|
77
|
+
|
|
78
|
+
Warnings and review findings remain visible but do not fail unless their originating preset promotes them to errors. A cached report keeps its original exit code and labels previously run stages `cached`.
|
|
79
|
+
|
|
80
|
+
## Monorepos and repair loops
|
|
81
|
+
|
|
82
|
+
`--package` accepts either a workspace package name or a workspace-relative package path. The doctor uses the selected package for compiler, lint, profile-discovery start, and analyzer scope, while workspace profile precedence and root lockfiles remain authoritative.
|
|
83
|
+
|
|
84
|
+
A repair agent should run JSON mode, apply only source-located safe changes, and rerun until exit `0`. Do not treat exit `2`, a skipped required stage, or an empty changed selection as clean. Browser evidence is objective input; the evaluator's named subjective rubric still requires human review.
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# Browser UI quality evaluator
|
|
2
|
+
|
|
3
|
+
`kerf-ui-evaluate` is the runtime half of Kerf's UI integration checks. The
|
|
4
|
+
static [`kerf-ui-analyze`](./ui-analyzer.md) command finds source-level ownership
|
|
5
|
+
mistakes; this Playwright-backed evaluator loads a running application and
|
|
6
|
+
records what a browser can establish about the rendered result.
|
|
7
|
+
|
|
8
|
+
Install Playwright beside `@kerfjs/ui` and install its browsers in CI:
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npm install --save-dev playwright
|
|
12
|
+
npx playwright install --with-deps chromium firefox webkit
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Start the application separately, then evaluate its URL from the project root:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npx kerf-ui-evaluate \
|
|
19
|
+
--url http://127.0.0.1:4173 \
|
|
20
|
+
--root . \
|
|
21
|
+
--output artifacts/kerf-ui
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
The command discovers and resolves the same `.kerf-ui-profile.json` layers as
|
|
25
|
+
the static analyzer. By default it runs Chromium, Firefox, and WebKit through
|
|
26
|
+
six contexts: wide, intermediate, narrow, a half-width CSS viewport representing
|
|
27
|
+
200% browser zoom, dark color scheme, and reduced motion. Use
|
|
28
|
+
`--browser chromium` for a fast local loop; keep all three engines in CI.
|
|
29
|
+
|
|
30
|
+
## Objective checks
|
|
31
|
+
|
|
32
|
+
The versioned JSON report uses stable `KUI-B###` codes and includes the context,
|
|
33
|
+
selector, measured evidence, and a repair-oriented message for each finding.
|
|
34
|
+
|
|
35
|
+
| Code | Browser fact |
|
|
36
|
+
| ---------- | -------------------------------------------------------------------------------- |
|
|
37
|
+
| `KUI-B001` | Navigation, timeout, or browser-context failure |
|
|
38
|
+
| `KUI-B010` | Horizontal page overflow |
|
|
39
|
+
| `KUI-B011` | Interactive content clipped by a hidden/clip owner or entirely off viewport |
|
|
40
|
+
| `KUI-B012` | An in-viewport action covered at its center point |
|
|
41
|
+
| `KUI-B020` | Positive `tabindex` overriding DOM focus order |
|
|
42
|
+
| `KUI-B021` | Stable per-element focus reaches an invisible control or causes no visual change |
|
|
43
|
+
| `KUI-B022` | Any ARIA widget role is absent from sequential keyboard order |
|
|
44
|
+
| `KUI-B023` | A representative action does not activate with Enter |
|
|
45
|
+
| `KUI-B030` | An interactive control has no text alternative |
|
|
46
|
+
| `KUI-B040` | Alpha-composited interactive text is below its WCAG AA contrast threshold |
|
|
47
|
+
| `KUI-B050` | A button-like target is smaller than 44 × 44 CSS pixels |
|
|
48
|
+
| `KUI-B060` | A pane or declared scroll scope has multiple active scroll owners |
|
|
49
|
+
| `KUI-B070` | Elements in an explicitly declared alignment group drift by more than one pixel |
|
|
50
|
+
| `KUI-B080` | A cataloged root computes geometry assigned to `none`, `parent`, or `child` |
|
|
51
|
+
|
|
52
|
+
For a representative application action that must be operated in the generic
|
|
53
|
+
evaluation, add `data-kui-evaluator-action` to its owning native control or
|
|
54
|
+
keyboard-operable custom control. The evaluator focuses it, presses Enter, and
|
|
55
|
+
accepts a click event or controlled `aria-expanded`, `aria-pressed`,
|
|
56
|
+
`aria-checked`, or `value` transition as evidence. This attribute is test
|
|
57
|
+
instrumentation, not an application event hook.
|
|
58
|
+
|
|
59
|
+
Alignment checks are similarly explicit: put `data-kui-align-group` on the
|
|
60
|
+
shared container and `data-kui-align-edge` on two or more elements whose logical
|
|
61
|
+
start edges must align. Use `data-kui-scroll-scope` only for an application-owned
|
|
62
|
+
scope that is not a `.kui-pane`.
|
|
63
|
+
|
|
64
|
+
Accessible names follow ARIA/native naming inputs, including labels, referenced
|
|
65
|
+
content with `aria-hidden` descendants removed, and native input values.
|
|
66
|
+
Geometry checks use each catalog entry's explicit `boundaries.rootClass`; the
|
|
67
|
+
order of `publicClasses` has no runtime meaning.
|
|
68
|
+
|
|
69
|
+
Profile exceptions apply only when both the `KUI-B###` rule and `target` match
|
|
70
|
+
exactly. For the browser evaluator, `target` is the evaluated `startDirectory`
|
|
71
|
+
relative to `workspaceRoot` (for example `apps/settings`); wildcards and broad
|
|
72
|
+
workspace targets remain invalid.
|
|
73
|
+
|
|
74
|
+
## Report and artifacts
|
|
75
|
+
|
|
76
|
+
The public Node API is available at `@kerfjs/ui/evaluator`:
|
|
77
|
+
|
|
78
|
+
```js
|
|
79
|
+
import { evaluateUi } from '@kerfjs/ui/evaluator';
|
|
80
|
+
|
|
81
|
+
const abortController = new AbortController();
|
|
82
|
+
const report = await evaluateUi({
|
|
83
|
+
url: 'http://127.0.0.1:4173',
|
|
84
|
+
workspaceRoot: process.cwd(),
|
|
85
|
+
retention: 'on-failure',
|
|
86
|
+
signal: abortController.signal,
|
|
87
|
+
});
|
|
88
|
+
if (!report.summary.passed) process.exitCode = 1;
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
The default output is `kerf-ui-evidence/report.json`; its schema ships at
|
|
92
|
+
`@kerfjs/ui/evaluator/report.schema.json`. Every retained screenshot has a
|
|
93
|
+
SHA-256 digest. Retention is deterministic:
|
|
94
|
+
|
|
95
|
+
- `on-failure` (default) removes current and stale evaluator screenshots when
|
|
96
|
+
every objective check passes;
|
|
97
|
+
- `always` retains one full-page PNG per browser/context;
|
|
98
|
+
- `never` removes current and stale evaluator screenshots and writes only the
|
|
99
|
+
focused DOM/computed-style evidence in the report.
|
|
100
|
+
|
|
101
|
+
`--timeout` bounds navigation and browser operations, while `--settle` provides
|
|
102
|
+
one small explicit post-navigation delay for applications with asynchronous
|
|
103
|
+
startup. A timeout becomes `KUI-B001` evidence rather than an unstructured crash.
|
|
104
|
+
The Node API accepts an `AbortSignal`; cancellation closes the active browser,
|
|
105
|
+
removes screenshots from the interrupted run, does not write a partial report,
|
|
106
|
+
and rejects with `AbortError`.
|
|
107
|
+
The CLI exits `1` for objective failures, `2` for invocation/profile errors, and
|
|
108
|
+
`0` for a pass; `--no-fail` is available only for evidence-collection jobs.
|
|
109
|
+
|
|
110
|
+
## Human visual review stays human
|
|
111
|
+
|
|
112
|
+
Hierarchy, rhythm, density, aesthetic fit, and the perceived clarity of
|
|
113
|
+
alignment or scroll ownership are not inferred from computed pixels. Every
|
|
114
|
+
report carries the suite-v3 0–2 review rubric with `status: "not-recorded"` and
|
|
115
|
+
an empty rating list. An AI agent may use the retained screenshots as review
|
|
116
|
+
input, but it must record a named reviewer and rationale in the separate
|
|
117
|
+
human-visual evidence workflow; it must not turn a subjective rating into a
|
|
118
|
+
`KUI-B###` assertion.
|
|
119
|
+
|
|
120
|
+
The deterministic good/bad downstream fixtures and three-engine integration
|
|
121
|
+
suite live under `tests/fixtures/ui-evaluator/` and
|
|
122
|
+
`tests/integration/ui-evaluator-downstream.test.ts`.
|
package/docs/ux-demo.md
CHANGED
|
@@ -1,18 +1,32 @@
|
|
|
1
1
|
# UX catalog contract
|
|
2
2
|
|
|
3
|
-
Run `npm run dev` from `ui/`. The catalog is a development and review surface, not a second implementation.
|
|
3
|
+
Run `npm run dev` from `ui/`. The catalog is a development and review surface, not a second implementation. The development server allows repo-owned assets used by the catalog, and its production bundle uses relative asset URLs so the complete catalog remains intact below preview and proxy paths.
|
|
4
|
+
|
|
5
|
+
Seven lazy production-composition routes live under the `Recipes` category.
|
|
6
|
+
Each `?component=recipe-*` route uses public package primitives, real wiring,
|
|
7
|
+
deterministic state, and semantic layout owners from the [recipe guide](./recipes.md).
|
|
4
8
|
|
|
5
9
|
- It imports public component subpaths, exercising their browser-selected reachable CSS rather than a catalog-only style path.
|
|
10
|
+
- The `ListActionRow` route demonstrates sibling primary/trailing native controls, independent disabled states, controlled selection, first-line leading-icon alignment for wrapped labels, and an application-owned native popover relationship without nesting controls. The `ListItem` route demonstrates the same multiline leading-icon anchor alongside its single-control navigation contract.
|
|
11
|
+
- The `AppTab` route demonstrates a runtime-filtered domain metadata slot and a decorative replacement close icon without changing TabBar selection, close, reorder, or focus ownership. The `ResizableRegion` route likewise replaces only the decorative handle glyph while retaining separator semantics and shared wiring.
|
|
6
12
|
- Every first-party visual component and every free Web Awesome 3.12 component has its own stable `?component=<id>` URL and focused demo. Composite header, menu, feedback, and theme scenarios remain addressable alongside their subcomponents; the root opens the first Kerf component.
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
13
|
+
- `ai/component-catalog.json` is the canonical catalog metadata. A deterministic checked-in projection supplies the demo's unique id, category, kind (`component`, `composition`, or `recipe`), source (`kerf` or `webawesome`), name, description, recommendation, direct `uses` dependencies, existing documentation path, and demo-source path; first-party, recipe, and Web Awesome renderer maps are exhaustively typed against its generated id unions. Non-supported Web Awesome recommendations project to a visible `Discouraged` sidebar tag, with Popup as the deliberate conditional exception. Demo paths resolve to the matching `ui/ux-demo/demos/<id>.tsx` or `ui/ux-demo/recipes/*.tsx` file, or to `ui/ux-demo/webawesome-demos.tsx` for Web Awesome entries. `ui/ux-demo/main.tsx` owns only the catalog shell, shared state, routing, and interaction wiring. First-party component entries additionally derive `ui/src/<public-subpath>.tsx` from their canonical `delivery.browserImport` or opt-in `delivery.moduleImport`.
|
|
14
|
+
- Every detail footer groups compact `Demo source` and `Guidance` actions; first-party component details also expose `Component source`. Web Awesome details say `Integration guidance` to distinguish the local integration contract from the upstream component implementation. Each button retains its repository-relative path in the DOM for verification and uses a deploy-safe absolute GitHub blob URL with a contextual new-tab name; the demo does not depend on a local source server or add a package runtime export. At phone widths, the resource group and related-component selector stack as full-width rows, with any resource overflow contained locally so labels cannot overlap the selector or widen the document.
|
|
15
|
+
- The master/detail shell groups first-party routes by category with production `ListHeader` and `ListItem` components. Catalog entries can supply short reusable trailing tags; the Web Awesome projection uses them to label exactly the 15 discouraged choices. The 70 ecosystem components live in a separate `Web Awesome` disclosure with seven subgroups, so they remain discoverable without being presented as first-class `@kerfjs/ui` exports. Direct Web Awesome routes open the disclosure and reveal the selected row at wide sizes; narrow layouts keep the expanded list in document flow rather than introducing a competing nested scroller.
|
|
16
|
+
- The shell itself uses `@kerfjs/ui/layout.css`: sidebars, details, previews, dialogs, and resize specimens share unpadded panes plus self-contained content items. Its sidebar identity uses the Kerf logo beside a vertically centered title, with the subtitle in its own aligned row like `PanelHeader`; the current-view heading is also a toolbar. Collapsing the catalog removes the pane completely and moves its restore action to the main toolbar's leading edge instead of leaving an icon-only rail. The selected view description remains inset below the page toolbar. The checkerboard belongs to the complete scrollable preview region rather than a nested card. A lightly separated footer keeps live status above compact source, guidance, and relationship groups. The focused `ResizableRegion` specimen stretches its surface through the available stage height and reports committed width in that shared status footer; narrow layouts retain local horizontal access when the pane is resized wider than the stage, and the split remains readable at 200% root scaling. Geometry coverage checks wide, intermediate, narrow, and 200% zoom layouts.
|
|
17
|
+
- The composer-form recipe demonstrates one coherent visible form surface with `PanelHeader` title/summary hierarchy, fields and actions on the shared 8px control gutter, and 24px major rhythm. Its conditional error or success `StateBanner` is the only nested semantic surface. Reset synchronizes the upgraded Web Awesome fields' live value properties with their empty controlled attributes and announces `Draft reset`; browser coverage preserves behavior and verifies wide, narrow, dark, validation, success, reset, 200% zoom, and forced-colors states.
|
|
18
|
+
- The list-detail dialog recipe places its header directly on the dialog edge while preserving the header's internal inset, gives the selected project title the complete outer-plus-inner content gutter, lets the value table fill the available detail column between the shared outer margins, and keeps record actions on one outer gutter without an extra content-item inset. Browser geometry coverage protects those relationships at wide and narrow widths.
|
|
19
|
+
- Catalog-only brand marks, preview dimensions, checkerboard tiles, and the compound display-settings control retain local geometry because they are specimen chrome or intrinsic control anatomy rather than reusable application spacing roles.
|
|
20
|
+
- The menu composition route demonstrates toolbar/content/footer anatomy, 24px major gaps, 8/1/8 child geometry, the full-width `ListHeader` count-or-badge/action split, logical-end 44px action targets with 18px visuals, and 44px row/header/toolbar targets. Its one chevron is a production 18px `DisclosureArrow` inside a controlled Tools header that actually reveals and hides content; ordinary Projects navigation has no false disclosure affordance. Browser assertions cover stable naming, expanded/content transitions, visual rotation, node identity, reduced motion, RTL alignment and containment, and wide, narrow, dark, and 200% layouts. The focused ListHeader route separately shows the shared neutral count pill for Attachments, Notes (including zero), and Duplicates plus a non-count legacy badge, full-width logical-end actions, and no disclosure without content.
|
|
21
|
+
- The `DisclosureArrow` route renders the production 18px root-scaled default without a catalog-only size override. Its two native buttons independently control the default right-to-down chevron and a recognizable right-facing replacement arrow with left-to-up custom directions; both use shortest-path rotation and their names stay stable while `aria-expanded` communicates state. Browser coverage verifies the custom clockwise path, pointer and native keyboard activation, focus, narrow and 200%-zoom stacking with contained labels, consumer sizing, and the independent `.5` Web Awesome glyph scale retained by Kerf `Select`.
|
|
22
|
+
- A detail with relationships renders one grouped footer selector: `Uses` links to direct dependencies and `Used by` links to reverse dependents. Choosing either navigates to that component's demo. The relationship group is absent when both sets are empty; no duplicate summary is rendered.
|
|
10
23
|
- Demo-only CSS supplies only the catalog shell and stages; component appearance stays in package CSS. Decorative chrome and Web Awesome controls share the same semantic system palette instead of carrying a separate demo identity.
|
|
11
|
-
- Scenarios use deterministic data and cover real variants, long content, disabled state, selection, progress, feedback, overlays, media, helpers, and stateful interactions. The Web Awesome theme route remains the broad system view, while individual action, form, layout, navigation, feedback, media, and helper routes provide focused specimens; the Animation route exposes preset, easing, duration, playback-rate, and transport controls with live lifecycle status; observer routes provide user-driven intersection, mutation, and resize events with live results; ToolbarControlGroup exposes all eight Hot Sheet 2 variants; SegmentedControl covers toolbar, rounded-rectangle, pill, equal-width, and disabled presentations; StateBanner exposes every built-in tone plus a scoped override; TabBar demonstrates overflow, edge autoscroll, and controlled pointer/keyboard reordering.
|
|
12
|
-
-
|
|
13
|
-
- The action
|
|
24
|
+
- Scenarios use deterministic data and cover real variants, long content, disabled state, selection, progress, feedback, overlays, media, helpers, and stateful interactions. The Web Awesome theme route remains the broad system view, while individual action, form, layout, navigation, feedback, media, and helper routes provide focused specimens; ListItem and ListHeader demonstrate typed domain metadata, protected native button/disclosure semantics, and a native popover trigger relationship without product props; PanelHeader demonstrates its plain top-toolbar identity, extra-large title, optional subtitle row, and bordered brand-filled icon group; the Select route verifies that option icons survive unrelated Kerf rerenders and selected slot content follows value changes; the Animation route exposes preset, easing, duration, playback-rate, and transport controls with live lifecycle status; observer routes provide user-driven intersection, mutation, and resize events with live results; ToolbarControlGroup exposes all eight Hot Sheet 2 variants, loads the Web Awesome registrations needed by its popup specimen, and preserves the upgraded dropdown's managed light-DOM children across Kerf rerenders; the Toolbar route composes a collapsible TokenSearchField inside a group, animates between the empty iconic and complete field states, stays open while populated, and focuses the revealed editor; SegmentedControl covers toolbar, rounded-rectangle, pill, equal-width, and disabled presentations; TokenSearchField covers editable ordered chips, pointer and keyboard removal, caret-preserving controlled deletion, editing, clearing, free text, disabled state, and fixed first-line alignment while content wraps; StateBanner exposes every built-in tone with tone-tinted badges plus a scoped override; TabBar demonstrates overflow, edge autoscroll, and controlled pointer/keyboard reordering.
|
|
25
|
+
- Recipe ownership notes are hidden by default. A page-toolbar toggle reveals them as floating warning-toned notes with a folded corner, keeping instructional copy visually distinct and removable from the specimen flow.
|
|
26
|
+
- Settings exercise light/dark, increased contrast, and reduced motion without changing component code. The theme action starts from the operating system's effective color scheme, always names the appearance it will switch to, and installs an explicit light or dark override when activated; contrast and reduced motion expose their independent preferences through `aria-pressed`.
|
|
27
|
+
- The footer action log is an `aria-live` region, making interactions visible to both people and browser tests.
|
|
14
28
|
- The focused Toast route calls Web Awesome's documented programmatic stack API and notes that Hot Sheet 2 still uses its own app-level toast implementation; the catalog demonstrates ecosystem support without implying current product adoption.
|
|
15
|
-
- Browser coverage walks every Kerf, composition, and Web Awesome route, checks every listed Web Awesome custom element is registered in its focused specimen, exercises the ecosystem disclosure and both relationship directions, and covers tab selection/reordering/overflow, semantic palettes and scoped overrides, light/dark rendering, toolbar popup behavior, menu alignment, banner urgency, keyboard resizing, live custom-element values, and representative wide/narrow captures.
|
|
16
|
-
- The production build keeps the catalog shell and Kerf routes in the entry chunk, then dynamically imports the complete Web Awesome specimen and registration surface when an ecosystem route
|
|
29
|
+
- Browser coverage walks every Kerf, composition, and Web Awesome route, checks every listed Web Awesome custom element is registered in its focused specimen, exercises the ecosystem disclosure and both relationship directions, validates offline source/guidance hrefs and accessible wide/narrow/200%-zoom wrapping, and covers tab selection/reordering/overflow, semantic palettes and scoped overrides, light/dark rendering, toolbar popup and responsive find behavior, single/multiline search alignment, menu alignment, banner urgency, keyboard resizing, live custom-element values, and representative wide/narrow captures.
|
|
30
|
+
- The production build keeps the catalog shell and Kerf routes in the entry chunk, then dynamically imports the complete Web Awesome specimen and registration surface when an ecosystem route, the broad Web Awesome theme route, or the ToolbarControlGroup popup specimen is selected. Directly addressed routes pass through the same loader.
|
|
17
31
|
|
|
18
32
|
Before handoff, inspect the actual captures for readability, context, alignment, clipping, spacing, responsiveness, focus, and obvious defects. Fix and recapture rather than treating the screenshot as proof by itself.
|
package/docs/webawesome-theme.md
CHANGED
|
@@ -4,11 +4,18 @@
|
|
|
4
4
|
free components and the Kerf/Hot Sheet 2 design system.
|
|
5
5
|
|
|
6
6
|
```ts
|
|
7
|
-
import
|
|
8
|
-
import
|
|
9
|
-
import
|
|
7
|
+
import type {} from "@kerfjs/ui/webawesome";
|
|
8
|
+
import "@kerfjs/ui/webawesome.css";
|
|
9
|
+
import "@awesome.me/webawesome/dist/components/button/button.js";
|
|
10
|
+
import "@awesome.me/webawesome/dist/components/input/input.js";
|
|
10
11
|
```
|
|
11
12
|
|
|
13
|
+
The type-only package import augments `kerfjs/jsx-runtime` with the 70
|
|
14
|
+
catalog-supported `wa-*` elements. It has no runtime side effects. The catalog
|
|
15
|
+
gate keeps the declaration tags aligned with Web Awesome's installed custom
|
|
16
|
+
elements manifest, while individual component imports remain the only
|
|
17
|
+
registration boundary.
|
|
18
|
+
|
|
12
19
|
The CSS entry includes Web Awesome's native, utility, palette, and default-theme
|
|
13
20
|
styles, then overrides the public semantic contract in
|
|
14
21
|
`@layer wa-theme-overrides`. It does not import or register component
|
|
@@ -47,12 +54,82 @@ pointer targets. Override `--kui-wa-carousel-icon-size`,
|
|
|
47
54
|
`--kui-wa-carousel-dot-hit-size` on a carousel or containing scope when a
|
|
48
55
|
product needs different geometry.
|
|
49
56
|
|
|
57
|
+
Form fields carry the same content-item inset as the Kerf primitives: a 1px
|
|
58
|
+
border with 8px inside it (`--wa-form-control-border-width`,
|
|
59
|
+
`--wa-form-control-padding-block`, and `--wa-form-control-padding-inline` are set
|
|
60
|
+
to the `--kui-layout-item-border-width` / `--kui-layout-item-padding` values), so
|
|
61
|
+
a single-line control lands at the standard ~40px height and the value sits 9px
|
|
62
|
+
in from the field edge. Each field's top label is inset by that same border +
|
|
63
|
+
padding (9px) so it lines up with the value inside the field, and is styled
|
|
64
|
+
exactly like a `ListHeader` label — uppercase, `--kui-font-xs`, weight 650, quiet
|
|
65
|
+
foreground. This applies to every free field that exposes a top label
|
|
66
|
+
(`::part(form-control-label)` plus the Slider's `::part(label)`). OTP Input
|
|
67
|
+
exposes `label` instead of `form-control-label`; the theme gives it the same
|
|
68
|
+
uppercase 12px/650 treatment. Inline control labels (Checkbox, Switch) keep
|
|
69
|
+
their natural sentence case.
|
|
70
|
+
|
|
71
|
+
Known Date's field captions and bordered text-like field hints use the same 9px
|
|
72
|
+
inline inset, keeping secondary text aligned with the value rather than the
|
|
73
|
+
field's outer border. This covers Input, Known Date, Number Input, OTP Input,
|
|
74
|
+
Select, Textarea, and Time Input hints; unbordered group hints retain their own
|
|
75
|
+
natural geometry. OTP Input's label and hint both use the 9px field-text inset.
|
|
76
|
+
|
|
77
|
+
Checkbox Group and Radio Group option regions receive the shared 8px inline
|
|
78
|
+
outer inset through `::part(form-control-input)`. Unlike bordered fields, these
|
|
79
|
+
groups have no shell of their own, so the explicit margin places their controls
|
|
80
|
+
on the same content-item geometry without adding padding to each option. The
|
|
81
|
+
Color Picker trigger receives the same 8px inline outer inset because it also
|
|
82
|
+
has no bordered field shell. Slider's complete interactive region receives the
|
|
83
|
+
shared 8px logical inline outer inset through `::part(slider)`, keeping its
|
|
84
|
+
track, markers, references, and hint together without moving the label.
|
|
85
|
+
|
|
86
|
+
Non-field chrome follows explicit, overridable control, surface, and container
|
|
87
|
+
tiers:
|
|
88
|
+
|
|
89
|
+
- `--kui-wa-control-inset` defaults to the 8px content-item padding. It applies
|
|
90
|
+
to tabs, tree-item trailing content, tags, and dropdown items. Tree selection
|
|
91
|
+
also uses Kerf's 8px inline outer margin, so its background reads as an inset
|
|
92
|
+
list item instead of an edge-to-edge stripe. Buttons already consume the same
|
|
93
|
+
`--wa-form-control-padding-inline` 8px value at every size, so no extra part
|
|
94
|
+
override is needed.
|
|
95
|
+
- `--kui-wa-surface-margin` and `--kui-wa-surface-inset` default to 8px.
|
|
96
|
+
Accordion, Card, Details, Callout, and Include use the margin around their
|
|
97
|
+
complete surface and the inset within it. Accordion applies the outer margin
|
|
98
|
+
once to the group rather than separating connected items; Card's header,
|
|
99
|
+
body, and footer all receive the full inset.
|
|
100
|
+
- `--kui-wa-container-inset` defaults to the 16px homogeneous-group step. Tab
|
|
101
|
+
Panel retains this roomier tier for unframed panel content.
|
|
102
|
+
|
|
103
|
+
Badge remains intentionally compact at Web Awesome's intrinsic `0.375em` block /
|
|
104
|
+
`0.625em` inline padding (4.5px / 7.5px at its default 12px text size): it is a
|
|
105
|
+
short status or count, not a content item. Breadcrumb has no bordered or filled
|
|
106
|
+
container, and Scroller delegates item chrome to its slotted children, so both
|
|
107
|
+
retain their upstream geometry. Web Awesome 3.12's free catalog has
|
|
108
|
+
`wa-dropdown-item`, but no `wa-menu-item`; the theme therefore styles the
|
|
109
|
+
supported dropdown item rather than carrying a dead selector.
|
|
110
|
+
|
|
111
|
+
Override a tier for a product scope after importing the theme:
|
|
112
|
+
|
|
113
|
+
```css
|
|
114
|
+
.roomy-editor {
|
|
115
|
+
--kui-wa-control-inset: 0.75rem;
|
|
116
|
+
--kui-wa-surface-margin: 0.75rem;
|
|
117
|
+
--kui-wa-surface-inset: 0.75rem;
|
|
118
|
+
--kui-wa-container-inset: 1.5rem;
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
50
122
|
Accordion, Details, Breadcrumb, and Kerf Select share
|
|
51
123
|
`--kui-disclosure-icon-scale` (default `.5`) so disclosure and traversal
|
|
52
124
|
chevrons keep the same visual weight. The Accordion and Details components
|
|
53
125
|
retain their own rotation behavior because scaling uses `transform` while
|
|
54
126
|
their open state uses the independent `rotate` property.
|
|
55
127
|
|
|
128
|
+
That scale applies to each Web Awesome component's intrinsic glyph and is
|
|
129
|
+
independent of Kerf `DisclosureArrow`, whose default box is 18px relative to
|
|
130
|
+
the root font size and whose consumer override is
|
|
131
|
+
`--kui-disclosure-arrow-size`.
|
|
132
|
+
|
|
56
133
|
Tooltip and Popover use arrowless floating surfaces by default, matching Hot
|
|
57
134
|
Sheet 2. The theme sets Web Awesome's public `--wa-tooltip-arrow-size` token to
|
|
58
135
|
`0px` and maps each popover's public `--arrow-size` property from
|
|
@@ -61,12 +138,12 @@ subtree, or for one instance after the theme import:
|
|
|
61
138
|
|
|
62
139
|
```css
|
|
63
140
|
:root {
|
|
64
|
-
--wa-tooltip-arrow-size: .375rem;
|
|
65
|
-
--kui-wa-popover-arrow-size: .375rem;
|
|
141
|
+
--wa-tooltip-arrow-size: 0.375rem;
|
|
142
|
+
--kui-wa-popover-arrow-size: 0.375rem;
|
|
66
143
|
}
|
|
67
144
|
|
|
68
145
|
.pointed-popover {
|
|
69
|
-
--arrow-size: .5rem;
|
|
146
|
+
--arrow-size: 0.5rem;
|
|
70
147
|
}
|
|
71
148
|
```
|
|
72
149
|
|
|
@@ -129,6 +206,9 @@ static content and labels that constraint next to the rendered output.
|
|
|
129
206
|
|
|
130
207
|
The catalog lists every free Web Awesome component so support can be inspected;
|
|
131
208
|
listing does not make each component the preferred Kerf application pattern.
|
|
209
|
+
The [need-first decision matrix](./component-selection.md#web-awesome-overlap-policy)
|
|
210
|
+
is the exhaustive Kerf/Web Awesome overlap index; the rules below explain the
|
|
211
|
+
highest-risk choices.
|
|
132
212
|
|
|
133
213
|
- Consider `wa-popup` for low-level anchored positioning when its flip, shift,
|
|
134
214
|
or placement behavior avoids custom positioning code. Prefer the higher-level
|
|
@@ -164,7 +244,7 @@ smallest subtree that needs a different identity:
|
|
|
164
244
|
:root {
|
|
165
245
|
--wa-color-brand-fill-loud: #7540a8;
|
|
166
246
|
--wa-color-focus: #7540a8;
|
|
167
|
-
--wa-form-control-border-radius: .5rem;
|
|
247
|
+
--wa-form-control-border-radius: 0.5rem;
|
|
168
248
|
}
|
|
169
249
|
|
|
170
250
|
.billing-workspace {
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# Workbench
|
|
2
|
+
|
|
3
|
+
`@kerfjs/ui/workbench` is the Xcode-like multi-panel workspace: a collapsible
|
|
4
|
+
left rail, right rail, and bottom drawer around a central work area (any absent).
|
|
5
|
+
It generalizes the instant-width / sliding-content collapse used by the catalog
|
|
6
|
+
sidebar. One of the opt-in app layouts (see
|
|
7
|
+
[`../../docs/23-app-layouts.md`](../../docs/23-app-layouts.md)); best on
|
|
8
|
+
desktop-class devices.
|
|
9
|
+
|
|
10
|
+
```ts
|
|
11
|
+
import { Workbench } from "@kerfjs/ui/workbench";
|
|
12
|
+
import "@kerfjs/ui/workbench.css";
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## State lives in the app
|
|
16
|
+
|
|
17
|
+
`Workbench` is declarative and the collapse is **pure CSS** — no wire. The app
|
|
18
|
+
owns each panel's `collapsed` flag (usually a signal) and toggles it; the panel
|
|
19
|
+
animates itself.
|
|
20
|
+
|
|
21
|
+
```tsx
|
|
22
|
+
const navCollapsed = signal(false);
|
|
23
|
+
|
|
24
|
+
<Workbench
|
|
25
|
+
id="studio"
|
|
26
|
+
label="Studio"
|
|
27
|
+
main={<Editor />}
|
|
28
|
+
leftRail={{
|
|
29
|
+
content: <Navigator />,
|
|
30
|
+
label: "Navigator",
|
|
31
|
+
collapsed: navCollapsed.value,
|
|
32
|
+
size: 280,
|
|
33
|
+
}}
|
|
34
|
+
rightRail={{
|
|
35
|
+
content: <Inspector />,
|
|
36
|
+
label: "Inspector",
|
|
37
|
+
collapsed: inspectorCollapsed.value,
|
|
38
|
+
}}
|
|
39
|
+
bottomDrawer={{
|
|
40
|
+
content: <Console />,
|
|
41
|
+
label: "Console",
|
|
42
|
+
collapsed: consoleCollapsed.value,
|
|
43
|
+
}}
|
|
44
|
+
/>;
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Each `WorkbenchPanel` takes `content`, an optional `collapsed`, an optional
|
|
48
|
+
`size` (rail width or drawer height in px, overriding the CSS default —
|
|
49
|
+
`--kui-workbench-rail-width` 280px, `--kui-workbench-drawer-height` 220px), and
|
|
50
|
+
an optional `label`.
|
|
51
|
+
|
|
52
|
+
## Public styling boundary
|
|
53
|
+
|
|
54
|
+
Import `@kerfjs/ui/workbench.css` after the component subpath. Applications may
|
|
55
|
+
set `--kui-workbench-rail-width` and `--kui-workbench-drawer-height` on a
|
|
56
|
+
Workbench instance. The supported composition classes are `.kui-workbench`,
|
|
57
|
+
`.kui-workbench__rail`, `.kui-workbench__rail--left`,
|
|
58
|
+
`.kui-workbench__rail--right`, `.kui-workbench__center`,
|
|
59
|
+
`.kui-workbench__main`, `.kui-workbench__drawer`, and
|
|
60
|
+
`.kui-workbench__panel-content`; these exact hooks are cataloged for tools that
|
|
61
|
+
must classify public application selectors. Prefer the component props and two
|
|
62
|
+
size tokens before selecting internal anatomy, and do not target its data
|
|
63
|
+
attributes or descendant tags as styling contracts.
|
|
64
|
+
|
|
65
|
+
## How the collapse animates
|
|
66
|
+
|
|
67
|
+
Collapsing snaps the panel's flex track to zero in a single reflow (so the work
|
|
68
|
+
area relayouts once, not per frame) while the panel's fixed-size content slides
|
|
69
|
+
out via a composited `transform` — a rail slides horizontally, the drawer
|
|
70
|
+
vertically — clipped by the shell's overflow. It honors `prefers-reduced-motion`
|
|
71
|
+
(the slide collapses to instant). On smaller device classes, present the rails'
|
|
72
|
+
contents through a `NavStack` or overlay drawers rather than shrinking the
|
|
73
|
+
three-panel shell.
|