@qijenchen/design-system 0.1.0-beta.83 → 0.1.0-beta.84
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/CLAUDE.md +8 -8
- package/README.md +3 -2
- package/dist/components/Alert/alert.js.map +1 -1
- package/dist/components/AppShell/_demo-helpers.d.ts.map +1 -1
- package/dist/components/AppShell/app-shell.d.ts +1 -1
- package/dist/components/AppShell/app-shell.d.ts.map +1 -1
- package/dist/components/AppShell/app-shell.js +22 -2
- package/dist/components/AppShell/app-shell.js.map +1 -1
- package/dist/components/Avatar/avatar.d.ts.map +1 -1
- package/dist/components/Avatar/avatar.js +4 -4
- package/dist/components/Avatar/avatar.js.map +1 -1
- package/dist/components/Breadcrumb/breadcrumb.d.ts.map +1 -1
- package/dist/components/Breadcrumb/breadcrumb.js +1 -1
- package/dist/components/Breadcrumb/breadcrumb.js.map +1 -1
- package/dist/components/Button/button.d.ts +2 -2
- package/dist/components/Button/button.d.ts.map +1 -1
- package/dist/components/Button/button.js +12 -3
- package/dist/components/Button/button.js.map +1 -1
- package/dist/components/Calendar/calendar.d.ts +13 -5
- package/dist/components/Calendar/calendar.d.ts.map +1 -1
- package/dist/components/Calendar/calendar.js +7 -1
- package/dist/components/Calendar/calendar.js.map +1 -1
- package/dist/components/Carousel/carousel.d.ts.map +1 -1
- package/dist/components/Carousel/carousel.js +8 -3
- package/dist/components/Carousel/carousel.js.map +1 -1
- package/dist/components/Chart/chart.d.ts.map +1 -1
- package/dist/components/Chart/chart.js +1 -0
- package/dist/components/Chart/chart.js.map +1 -1
- package/dist/components/Checkbox/checkbox.d.ts +1 -1
- package/dist/components/Checkbox/checkbox.d.ts.map +1 -1
- package/dist/components/Checkbox/checkbox.js +8 -4
- package/dist/components/Checkbox/checkbox.js.map +1 -1
- package/dist/components/Chip/chip.d.ts +1 -1
- package/dist/components/Chip/chip.d.ts.map +1 -1
- package/dist/components/Chip/chip.js.map +1 -1
- package/dist/components/Coachmark/coachmark.d.ts +10 -3
- package/dist/components/Coachmark/coachmark.d.ts.map +1 -1
- package/dist/components/Coachmark/coachmark.js.map +1 -1
- package/dist/components/Combobox/combobox.d.ts +5 -1
- package/dist/components/Combobox/combobox.d.ts.map +1 -1
- package/dist/components/Combobox/combobox.js +30 -13
- package/dist/components/Combobox/combobox.js.map +1 -1
- package/dist/components/DataTable/cell-registry.d.ts.map +1 -1
- package/dist/components/DataTable/cell-registry.js +53 -62
- package/dist/components/DataTable/cell-registry.js.map +1 -1
- package/dist/components/DataTable/column-types.d.ts +10 -4
- package/dist/components/DataTable/column-types.d.ts.map +1 -1
- package/dist/components/DataTable/column-types.js.map +1 -1
- package/dist/components/DataTable/data-table-column-visibility-panel.d.ts +1 -1
- package/dist/components/DataTable/data-table-column-visibility-panel.js +183 -0
- package/dist/components/DataTable/data-table-column-visibility-panel.js.map +1 -0
- package/dist/components/DataTable/data-table-filter-panel.d.ts.map +1 -1
- package/dist/components/DataTable/data-table-filter-panel.js +607 -0
- package/dist/components/DataTable/data-table-filter-panel.js.map +1 -0
- package/dist/components/DataTable/data-table-interaction-layer.d.ts +11 -15
- package/dist/components/DataTable/data-table-interaction-layer.d.ts.map +1 -1
- package/dist/components/DataTable/data-table-interaction-layer.js +1 -3
- package/dist/components/DataTable/data-table-interaction-layer.js.map +1 -1
- package/dist/components/DataTable/data-table-sort-manager.js +140 -0
- package/dist/components/DataTable/data-table-sort-manager.js.map +1 -0
- package/dist/components/DataTable/data-table.d.ts +5 -6
- package/dist/components/DataTable/data-table.d.ts.map +1 -1
- package/dist/components/DataTable/data-table.js +36 -15
- package/dist/components/DataTable/data-table.js.map +1 -1
- package/dist/components/DataTable/filter-operators.js +140 -0
- package/dist/components/DataTable/filter-operators.js.map +1 -0
- package/dist/components/DataTable/filter-tree.js +312 -0
- package/dist/components/DataTable/filter-tree.js.map +1 -0
- package/dist/components/DataTable/index.d.ts +3 -0
- package/dist/components/DataTable/index.d.ts.map +1 -1
- package/dist/components/DataTable/index.js +12 -1
- package/dist/components/DataTable/index.js.map +1 -1
- package/dist/components/DataTable/lib/column-meta.js +25 -0
- package/dist/components/DataTable/lib/column-meta.js.map +1 -0
- package/dist/components/DateGrid/date-grid.d.ts.map +1 -1
- package/dist/components/DateGrid/date-grid.js +3 -1
- package/dist/components/DateGrid/date-grid.js.map +1 -1
- package/dist/components/DatePicker/date-picker.d.ts +7 -1
- package/dist/components/DatePicker/date-picker.d.ts.map +1 -1
- package/dist/components/DatePicker/date-picker.js +105 -74
- package/dist/components/DatePicker/date-picker.js.map +1 -1
- package/dist/components/Dialog/dialog.d.ts +20 -3
- package/dist/components/Dialog/dialog.d.ts.map +1 -1
- package/dist/components/Dialog/dialog.js +38 -20
- package/dist/components/Dialog/dialog.js.map +1 -1
- package/dist/components/DropdownMenu/dropdown-menu.d.ts.map +1 -1
- package/dist/components/DropdownMenu/dropdown-menu.js +3 -1
- package/dist/components/DropdownMenu/dropdown-menu.js.map +1 -1
- package/dist/components/Field/field-context.d.ts +45 -5
- package/dist/components/Field/field-context.d.ts.map +1 -1
- package/dist/components/Field/field-context.js +19 -0
- package/dist/components/Field/field-context.js.map +1 -1
- package/dist/components/Field/field-edit-keys.d.ts +21 -0
- package/dist/components/Field/field-edit-keys.d.ts.map +1 -0
- package/dist/components/Field/field-edit-keys.js +19 -0
- package/dist/components/Field/field-edit-keys.js.map +1 -0
- package/dist/components/Field/field-types.d.ts +20 -1
- package/dist/components/Field/field-types.d.ts.map +1 -1
- package/dist/components/Field/field-types.js.map +1 -1
- package/dist/components/Field/field-wrapper.d.ts +3 -2
- package/dist/components/Field/field-wrapper.d.ts.map +1 -1
- package/dist/components/Field/field-wrapper.js +38 -39
- package/dist/components/Field/field-wrapper.js.map +1 -1
- package/dist/components/Field/field.d.ts +2 -1
- package/dist/components/Field/field.d.ts.map +1 -1
- package/dist/components/Field/field.js.map +1 -1
- package/dist/components/FieldControlGroup/field-control-group.d.ts +1 -1
- package/dist/components/FieldControlGroup/field-control-group.js.map +1 -1
- package/dist/components/FileItem/file-item.d.ts.map +1 -1
- package/dist/components/FileItem/file-item.js +1 -1
- package/dist/components/FileItem/file-item.js.map +1 -1
- package/dist/components/FileUpload/file-upload.d.ts +1 -1
- package/dist/components/FileUpload/file-upload.js.map +1 -1
- package/dist/components/FileViewer/file-viewer.d.ts.map +1 -1
- package/dist/components/FileViewer/file-viewer.js +7 -2
- package/dist/components/FileViewer/file-viewer.js.map +1 -1
- package/dist/components/FileViewer/image-renderer.d.ts.map +1 -1
- package/dist/components/FileViewer/image-renderer.js +22 -1
- package/dist/components/FileViewer/image-renderer.js.map +1 -1
- package/dist/components/HoverCard/hover-card.d.ts.map +1 -1
- package/dist/components/HoverCard/hover-card.js +5 -3
- package/dist/components/HoverCard/hover-card.js.map +1 -1
- package/dist/components/InlineEdit/index.d.ts +2 -0
- package/dist/components/InlineEdit/index.d.ts.map +1 -0
- package/dist/components/InlineEdit/index.js +5 -0
- package/dist/components/InlineEdit/index.js.map +1 -0
- package/dist/components/InlineEdit/inline-edit.d.ts +59 -0
- package/dist/components/InlineEdit/inline-edit.d.ts.map +1 -0
- package/dist/components/InlineEdit/inline-edit.js +164 -0
- package/dist/components/InlineEdit/inline-edit.js.map +1 -0
- package/dist/components/Input/input.d.ts +6 -11
- package/dist/components/Input/input.d.ts.map +1 -1
- package/dist/components/Input/input.js +43 -13
- package/dist/components/Input/input.js.map +1 -1
- package/dist/components/LinkInput/link-input.d.ts +2 -2
- package/dist/components/LinkInput/link-input.d.ts.map +1 -1
- package/dist/components/LinkInput/link-input.js +6 -5
- package/dist/components/LinkInput/link-input.js.map +1 -1
- package/dist/components/Menu/menu-item.d.ts +14 -2
- package/dist/components/Menu/menu-item.d.ts.map +1 -1
- package/dist/components/Menu/menu-item.js +25 -3
- package/dist/components/Menu/menu-item.js.map +1 -1
- package/dist/components/NumberInput/number-input.d.ts +3 -3
- package/dist/components/NumberInput/number-input.d.ts.map +1 -1
- package/dist/components/NumberInput/number-input.js +6 -5
- package/dist/components/NumberInput/number-input.js.map +1 -1
- package/dist/components/OverflowIndicator/overflow-indicator.d.ts.map +1 -1
- package/dist/components/OverflowIndicator/overflow-indicator.js +2 -6
- package/dist/components/OverflowIndicator/overflow-indicator.js.map +1 -1
- package/dist/components/Pagination/pagination.js +1 -1
- package/dist/components/Pagination/pagination.js.map +1 -1
- package/dist/components/PeoplePicker/index.js +7 -1
- package/dist/components/PeoplePicker/index.js.map +1 -1
- package/dist/components/PeoplePicker/people-picker.d.ts +9 -2
- package/dist/components/PeoplePicker/people-picker.d.ts.map +1 -1
- package/dist/components/PeoplePicker/people-picker.js +23 -10
- package/dist/components/PeoplePicker/people-picker.js.map +1 -1
- package/dist/components/PeoplePicker/person-display.d.ts +6 -3
- package/dist/components/PeoplePicker/person-display.d.ts.map +1 -1
- package/dist/components/PeoplePicker/person-display.js +19 -14
- package/dist/components/PeoplePicker/person-display.js.map +1 -1
- package/dist/components/Popover/popover.d.ts.map +1 -1
- package/dist/components/Popover/popover.js +3 -1
- package/dist/components/Popover/popover.js.map +1 -1
- package/dist/components/ProfileCard/profile-card.d.ts +2 -2
- package/dist/components/ProfileCard/profile-card.js +3 -3
- package/dist/components/ProfileCard/profile-card.js.map +1 -1
- package/dist/components/RadioGroup/radio-group.d.ts.map +1 -1
- package/dist/components/RadioGroup/radio-group.js +15 -9
- package/dist/components/RadioGroup/radio-group.js.map +1 -1
- package/dist/components/ScrollArea/scroll-area.d.ts +11 -1
- package/dist/components/ScrollArea/scroll-area.d.ts.map +1 -1
- package/dist/components/ScrollArea/scroll-area.js +6 -3
- package/dist/components/ScrollArea/scroll-area.js.map +1 -1
- package/dist/components/Select/select.d.ts +46 -5
- package/dist/components/Select/select.d.ts.map +1 -1
- package/dist/components/Select/select.js +109 -28
- package/dist/components/Select/select.js.map +1 -1
- package/dist/components/SelectMenu/select-menu.d.ts +3 -1
- package/dist/components/SelectMenu/select-menu.d.ts.map +1 -1
- package/dist/components/SelectMenu/select-menu.js +8 -1
- package/dist/components/SelectMenu/select-menu.js.map +1 -1
- package/dist/components/SelectionControl/boolean-value.d.ts +11 -0
- package/dist/components/SelectionControl/boolean-value.d.ts.map +1 -0
- package/dist/components/SelectionControl/boolean-value.js +23 -0
- package/dist/components/SelectionControl/boolean-value.js.map +1 -0
- package/dist/components/SelectionControl/index.d.ts +1 -0
- package/dist/components/SelectionControl/index.d.ts.map +1 -1
- package/dist/components/SelectionControl/index.js +2 -0
- package/dist/components/SelectionControl/index.js.map +1 -1
- package/dist/components/Sheet/index.js +2 -1
- package/dist/components/Sheet/sheet.d.ts +7 -1
- package/dist/components/Sheet/sheet.d.ts.map +1 -1
- package/dist/components/Sheet/sheet.js +5 -2
- package/dist/components/Sheet/sheet.js.map +1 -1
- package/dist/components/Sidebar/sidebar.d.ts +9 -4
- package/dist/components/Sidebar/sidebar.d.ts.map +1 -1
- package/dist/components/Sidebar/sidebar.js +8 -10
- package/dist/components/Sidebar/sidebar.js.map +1 -1
- package/dist/components/Slider/slider.d.ts.map +1 -1
- package/dist/components/Slider/slider.js.map +1 -1
- package/dist/components/Switch/switch.d.ts +6 -6
- package/dist/components/Switch/switch.d.ts.map +1 -1
- package/dist/components/Switch/switch.js +3 -2
- package/dist/components/Switch/switch.js.map +1 -1
- package/dist/components/Tabs/tabs.d.ts.map +1 -1
- package/dist/components/Tabs/tabs.js +15 -1
- package/dist/components/Tabs/tabs.js.map +1 -1
- package/dist/components/Textarea/textarea.d.ts +2 -2
- package/dist/components/Textarea/textarea.d.ts.map +1 -1
- package/dist/components/Textarea/textarea.js +50 -42
- package/dist/components/Textarea/textarea.js.map +1 -1
- package/dist/components/TimePicker/time-columns.d.ts.map +1 -1
- package/dist/components/TimePicker/time-columns.js +42 -38
- package/dist/components/TimePicker/time-columns.js.map +1 -1
- package/dist/components/TimePicker/time-picker.d.ts.map +1 -1
- package/dist/components/TimePicker/time-picker.js +8 -6
- package/dist/components/TimePicker/time-picker.js.map +1 -1
- package/dist/components/Tooltip/tooltip.d.ts.map +1 -1
- package/dist/components/Tooltip/tooltip.js +7 -5
- package/dist/components/Tooltip/tooltip.js.map +1 -1
- package/dist/components/TreeView/tree-view.d.ts +56 -1
- package/dist/components/TreeView/tree-view.d.ts.map +1 -1
- package/dist/components/TreeView/tree-view.js +211 -12
- package/dist/components/TreeView/tree-view.js.map +1 -1
- package/dist/index.d.ts +115 -58
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +93 -108
- package/dist/index.js.map +1 -1
- package/dist/patterns/element-anatomy/item-anatomy.d.ts +7 -6
- package/dist/patterns/element-anatomy/item-anatomy.d.ts.map +1 -1
- package/dist/patterns/element-anatomy/item-anatomy.js.map +1 -1
- package/dist/tokens/categorical-color.js +15 -0
- package/dist/tokens/categorical-color.js.map +1 -1
- package/dist/tokens/motion/motion.d.ts +7 -6
- package/dist/tokens/motion/motion.d.ts.map +1 -1
- package/dist/tokens/motion/motion.js +6 -6
- package/dist/tokens/motion/motion.js.map +1 -1
- package/dist/tokens/motion/overlay-motion.d.ts +19 -0
- package/dist/tokens/motion/overlay-motion.d.ts.map +1 -0
- package/dist/tokens/motion/overlay-motion.js +9 -0
- package/dist/tokens/motion/overlay-motion.js.map +1 -0
- package/dist/tokens/uiSize/icon-size.js +5 -0
- package/dist/tokens/uiSize/icon-size.js.map +1 -0
- package/ds-canonical/fork/governance.lock +31 -19
- package/ds-canonical/fork/hooks/_log-fire.sh +46 -0
- package/ds-canonical/fork/hooks/block_prototype_imports.py +15 -4
- package/ds-canonical/fork/hooks/check_consumer_app_invariants.sh +145 -13
- package/ds-canonical/fork/hooks/check_ds_anchor_preflight.sh +2 -1
- package/ds-canonical/fork/hooks/check_dynamic_tailwind_class.sh +47 -0
- package/ds-canonical/fork/hooks/check_escape_marker_abuse.sh +27 -0
- package/ds-canonical/fork/hooks/check_field_family_invariants.sh +61 -1
- package/ds-canonical/fork/hooks/check_layout_space_magic_numbers.sh +28 -4
- package/ds-canonical/fork/hooks/check_opacity_token_usage.sh +28 -0
- package/ds-canonical/fork/hooks/check_story_determinism.sh +2 -0
- package/ds-canonical/fork/hooks/check_story_invariants.sh +119 -0
- package/ds-canonical/fork/hooks/check_tabs_content_chrome_body_double_gap.sh +29 -0
- package/ds-canonical/fork/manifest.json +15 -0
- package/ds-canonical/fork/preamble.md +20 -18
- package/ds-canonical/fork/skills/bug-fix-rhythm/SKILL.md +3 -3
- package/ds-canonical/fork/skills/code-quality-audit/SKILL.md +3 -3
- package/ds-canonical/fork/skills/delivery-handoff/SKILL.md +1 -1
- package/ds-canonical/fork/skills/product-ui-audit/SKILL.md +3 -3
- package/ds-canonical/fork/skills/propose-options/SKILL.md +3 -3
- package/ds-canonical/fork/skills/prototype/SKILL.md +1 -1
- package/ds-canonical/fork/skills/scan-similar-bugs/SKILL.md +2 -2
- package/ds-canonical/fork/skills/visual-audit/SKILL.md +2 -2
- package/ds-canonical/hooks/README.md +1 -1
- package/ds-canonical/hooks/block_prototype_imports.py +15 -4
- package/ds-canonical/hooks/check_audit_post_report_validator.sh +65 -7
- package/ds-canonical/hooks/check_codex_brief_invariants.sh +28 -1
- package/ds-canonical/hooks/check_consumer_app_invariants.sh +145 -13
- package/ds-canonical/hooks/check_ds_anchor_preflight.sh +2 -1
- package/ds-canonical/hooks/check_dynamic_tailwind_class.sh +47 -0
- package/ds-canonical/hooks/check_escape_marker_abuse.sh +27 -0
- package/ds-canonical/hooks/check_field_family_invariants.sh +61 -1
- package/ds-canonical/hooks/check_layout_space_magic_numbers.sh +28 -4
- package/ds-canonical/hooks/check_naming_and_abstraction.sh +1 -1
- package/ds-canonical/hooks/check_opacity_token_usage.sh +28 -0
- package/ds-canonical/hooks/check_pixel_quantified_audit.sh +3 -2
- package/ds-canonical/hooks/check_post_main_ssot_propagate.sh +6 -1
- package/ds-canonical/hooks/check_ssot_header_declaration.sh +3 -1
- package/ds-canonical/hooks/check_story_determinism.sh +2 -0
- package/ds-canonical/hooks/check_substantive_edit_approval_preflight.sh +8 -7
- package/ds-canonical/hooks/check_tabs_content_chrome_body_double_gap.sh +29 -0
- package/ds-canonical/hooks/lib/_story_compile_drift.sh +1 -1
- package/ds-canonical/hooks/lib/_token_hygiene.sh +10 -1
- package/ds-canonical/hooks/stop_self_audit.sh +31 -0
- package/ds-canonical/hooks/tests/test_block_prototype_imports.sh +24 -0
- package/ds-canonical/hooks/tests/test_check_audit_post_report_validator.sh +44 -0
- package/ds-canonical/hooks/tests/test_check_codex_brief_invariants.sh +43 -2
- package/ds-canonical/hooks/tests/test_check_consumer_ds_primitive_misuse.sh +112 -0
- package/ds-canonical/hooks/tests/test_check_consumer_story_baseline.sh +9 -2
- package/ds-canonical/hooks/tests/test_check_dynamic_tailwind_class.sh +76 -0
- package/ds-canonical/hooks/tests/test_check_escape_marker_abuse.sh +24 -0
- package/ds-canonical/hooks/tests/test_check_field_family_invariants.sh +27 -2
- package/ds-canonical/hooks/tests/test_check_opacity_token_usage.sh +39 -0
- package/ds-canonical/hooks/tests/test_check_tabs_content_chrome_body_double_gap.sh +76 -0
- package/ds-canonical/references/audit-coverage-vs-24-checklist.md +1 -1
- package/ds-canonical/references/build-ui-canonicals.md +2 -1
- package/ds-canonical/references/failure-class-registry.json +145 -0
- package/ds-canonical/references/governance-audit-coverage.md +33 -0
- package/ds-canonical/references/item-anatomy-recipe.md +2 -2
- package/ds-canonical/references/scenario-definition.md +6 -9
- package/ds-canonical/references/ssot-consultation.md +2 -2
- package/ds-canonical/references/ssot-index.md +5 -4
- package/ds-canonical/references/story-baseline-registry.json +8 -2
- package/ds-canonical/references/structural-token-retention.md +6 -9
- package/ds-canonical/rules/meta-patterns.md +8 -8
- package/ds-canonical/rules/self-verify.md +4 -3
- package/ds-canonical/rules/story-rules.md +2 -2
- package/ds-canonical/rules/ui-development.md +2 -2
- package/ds-canonical/skills/bug-fix-rhythm/SKILL.md +3 -3
- package/ds-canonical/skills/code-quality-audit/SKILL.md +3 -3
- package/ds-canonical/skills/codex-collab/SKILL.md +9 -7
- package/ds-canonical/skills/codex-collab/references/brief-template.md +1 -1
- package/ds-canonical/skills/codify-principle/SKILL.md +2 -2
- package/ds-canonical/skills/component-quality-gate/SKILL.md +1 -1
- package/ds-canonical/skills/component-quality-gate/references/checklist.md +2 -2
- package/ds-canonical/skills/deep-audit-cross-codex/SKILL.md +51 -58
- package/ds-canonical/skills/deep-audit-cross-codex/references/phase-a-workflow.md +29 -0
- package/ds-canonical/skills/deep-audit-cross-codex/references/phase-b-codex-brief.md +29 -10
- package/ds-canonical/skills/deep-audit-cross-codex/references/triage-rubric.md +21 -0
- package/ds-canonical/skills/delivery-handoff/SKILL.md +1 -1
- package/ds-canonical/skills/design-system-audit/SKILL.md +26 -25
- package/ds-canonical/skills/design-system-audit/references/audit-prompts.md +13 -1
- package/ds-canonical/skills/design-system-audit/references/rule-placement.md +1 -1
- package/ds-canonical/skills/governance-health/SKILL.md +1 -1
- package/ds-canonical/skills/knowledge-prune/SKILL.md +3 -0
- package/ds-canonical/skills/knowledge-prune/references/prune-dimensions-d5-d10.md +1 -0
- package/ds-canonical/skills/new-component/SKILL.md +5 -5
- package/ds-canonical/skills/new-component/references/new-component-checklist.md +1 -1
- package/ds-canonical/skills/product-ui-audit/SKILL.md +3 -3
- package/ds-canonical/skills/propose-options/SKILL.md +3 -3
- package/ds-canonical/skills/prototype/SKILL.md +1 -1
- package/ds-canonical/skills/scan-similar-bugs/SKILL.md +2 -2
- package/ds-canonical/skills/story-auto-compile-migrate/SKILL.md +3 -3
- package/ds-canonical/skills/story-writing/SKILL.md +3 -3
- package/ds-canonical/skills/visual-audit/SKILL.md +2 -2
- package/ds-story-manifest.json +29 -20
- package/llms-full.txt +22 -1
- package/llms.txt +5 -4
- package/package.json +7 -3
- package/src/components/Accordion/accordion.principles.stories.tsx +1 -1
- package/src/components/Accordion/accordion.stories.tsx +16 -1
- package/src/components/Alert/alert.principles.stories.tsx +1 -1
- package/src/components/Alert/alert.spec.md +8 -10
- package/src/components/Alert/alert.stories.tsx +28 -28
- package/src/components/Alert/alert.tsx +1 -1
- package/src/components/AppShell/_demo-helpers.tsx +6 -3
- package/src/components/AppShell/app-shell.spec.md +4 -4
- package/src/components/AppShell/app-shell.tsx +28 -2
- package/src/components/AspectRatio/aspect-ratio.spec.md +1 -1
- package/src/components/AspectRatio/aspect-ratio.stories.tsx +7 -89
- package/src/components/Avatar/avatar.anatomy.stories.tsx +7 -3
- package/src/components/Avatar/avatar.principles.stories.tsx +19 -15
- package/src/components/Avatar/avatar.spec.md +2 -2
- package/src/components/Avatar/avatar.stories.tsx +8 -6
- package/src/components/Avatar/avatar.tsx +13 -5
- package/src/components/Badge/badge.principles.stories.tsx +1 -1
- package/src/components/Badge/badge.spec.md +2 -0
- package/src/components/Badge/badge.stories.tsx +4 -2
- package/src/components/Breadcrumb/breadcrumb.principles.stories.tsx +6 -4
- package/src/components/Breadcrumb/breadcrumb.stories.tsx +6 -5
- package/src/components/Breadcrumb/breadcrumb.tsx +3 -2
- package/src/components/BulkActionBar/bulk-action-bar.spec.md +4 -2
- package/src/components/Button/button.principles.stories.tsx +1 -1
- package/src/components/Button/button.spec.md +2 -2
- package/src/components/Button/button.tsx +18 -3
- package/src/components/Calendar/calendar.anatomy.stories.tsx +12 -10
- package/src/components/Calendar/calendar.principles.stories.tsx +14 -6
- package/src/components/Calendar/calendar.spec.md +5 -4
- package/src/components/Calendar/calendar.stories.tsx +7 -4
- package/src/components/Calendar/calendar.tsx +26 -7
- package/src/components/Carousel/carousel.anatomy.stories.tsx +5 -4
- package/src/components/Carousel/carousel.spec.md +2 -2
- package/src/components/Carousel/carousel.tsx +11 -3
- package/src/components/Chart/chart.anatomy.stories.tsx +7 -4
- package/src/components/Chart/chart.principles.stories.tsx +1 -1
- package/src/components/Chart/chart.stories.tsx +2 -8
- package/src/components/Chart/chart.tsx +3 -0
- package/src/components/Checkbox/checkbox.anatomy.stories.tsx +28 -49
- package/src/components/Checkbox/checkbox.principles.stories.tsx +1 -1
- package/src/components/Checkbox/checkbox.spec.md +11 -26
- package/src/components/Checkbox/checkbox.stories.tsx +11 -8
- package/src/components/Checkbox/checkbox.tsx +18 -13
- package/src/components/Chip/chip.anatomy.stories.tsx +44 -24
- package/src/components/Chip/chip.principles.stories.tsx +1 -1
- package/src/components/Chip/chip.spec.md +5 -4
- package/src/components/Chip/chip.tsx +5 -1
- package/src/components/CircularProgress/circular-progress.principles.stories.tsx +7 -4
- package/src/components/CircularProgress/circular-progress.spec.md +3 -3
- package/src/components/CircularProgress/circular-progress.stories.tsx +2 -2
- package/src/components/Coachmark/coachmark.anatomy.stories.tsx +2 -2
- package/src/components/Coachmark/coachmark.principles.stories.tsx +2 -1
- package/src/components/Coachmark/coachmark.spec.md +5 -5
- package/src/components/Coachmark/coachmark.stories.tsx +3 -1
- package/src/components/Coachmark/coachmark.tsx +10 -3
- package/src/components/Combobox/combobox.anatomy.stories.tsx +2 -2
- package/src/components/Combobox/combobox.tsx +53 -19
- package/src/components/Command/command.anatomy.stories.tsx +2 -2
- package/src/components/Command/command.principles.stories.tsx +1 -1
- package/src/components/Command/command.stories.tsx +42 -10
- package/src/components/DataTable/cell-registry.tsx +86 -63
- package/src/components/DataTable/column-types.ts +10 -1
- package/src/components/DataTable/data-table-column-visibility-panel.tsx +1 -1
- package/src/components/DataTable/data-table-filter-panel.tsx +4 -1
- package/src/components/DataTable/data-table-interaction-layer.tsx +15 -19
- package/src/components/DataTable/data-table.anatomy.stories.tsx +15 -13
- package/src/components/DataTable/data-table.principles.stories.tsx +9 -9
- package/src/components/DataTable/data-table.spec.md +32 -12
- package/src/components/DataTable/data-table.stories.tsx +16 -17
- package/src/components/DataTable/data-table.tsx +103 -60
- package/src/components/DataTable/index.ts +3 -0
- package/src/components/DateGrid/date-grid.anatomy.stories.tsx +4 -4
- package/src/components/DateGrid/date-grid.principles.stories.tsx +1 -1
- package/src/components/DateGrid/date-grid.spec.md +1 -1
- package/src/components/DateGrid/date-grid.tsx +3 -1
- package/src/components/DatePicker/date-picker.anatomy.stories.tsx +8 -8
- package/src/components/DatePicker/date-picker.principles.stories.tsx +3 -3
- package/src/components/DatePicker/date-picker.spec.md +3 -3
- package/src/components/DatePicker/date-picker.stories.tsx +4 -5
- package/src/components/DatePicker/date-picker.tsx +99 -45
- package/src/components/DescriptionList/description-list.anatomy.stories.tsx +12 -10
- package/src/components/DescriptionList/description-list.principles.stories.tsx +2 -2
- package/src/components/DescriptionList/description-list.spec.md +3 -3
- package/src/components/DescriptionList/description-list.stories.tsx +7 -6
- package/src/components/Dialog/dialog.anatomy.stories.tsx +1 -0
- package/src/components/Dialog/dialog.spec.md +9 -0
- package/src/components/Dialog/dialog.stories.tsx +66 -5
- package/src/components/Dialog/dialog.tsx +60 -25
- package/src/components/DropdownMenu/dropdown-menu.anatomy.stories.tsx +1 -1
- package/src/components/DropdownMenu/dropdown-menu.principles.stories.tsx +8 -6
- package/src/components/DropdownMenu/dropdown-menu.spec.md +5 -5
- package/src/components/DropdownMenu/dropdown-menu.tsx +18 -9
- package/src/components/Empty/empty.anatomy.stories.tsx +1 -1
- package/src/components/Empty/empty.spec.md +2 -2
- package/src/components/Field/field-context.ts +69 -5
- package/src/components/Field/field-controls.spec.md +62 -8
- package/src/components/Field/field-edit-keys.ts +66 -0
- package/src/components/Field/field-types.ts +58 -9
- package/src/components/Field/field-wrapper.tsx +50 -42
- package/src/components/Field/field.anatomy.stories.tsx +9 -6
- package/src/components/Field/field.spec.md +1 -1
- package/src/components/Field/field.stories.tsx +1 -1
- package/src/components/Field/field.tsx +2 -1
- package/src/components/Field/form-validation.spec.md +3 -3
- package/src/components/FieldControlGroup/field-control-group.principles.stories.tsx +6 -6
- package/src/components/FieldControlGroup/field-control-group.spec.md +9 -4
- package/src/components/FieldControlGroup/field-control-group.tsx +1 -1
- package/src/components/FileItem/file-item.anatomy.stories.tsx +1 -1
- package/src/components/FileItem/file-item.spec.md +5 -8
- package/src/components/FileItem/file-item.stories.tsx +36 -25
- package/src/components/FileItem/file-item.tsx +8 -6
- package/src/components/FileUpload/file-upload.anatomy.stories.tsx +6 -6
- package/src/components/FileUpload/file-upload.principles.stories.tsx +5 -5
- package/src/components/FileUpload/file-upload.spec.md +1 -1
- package/src/components/FileUpload/file-upload.tsx +1 -1
- package/src/components/FileViewer/file-viewer.anatomy.stories.tsx +4 -3
- package/src/components/FileViewer/file-viewer.principles.stories.tsx +3 -3
- package/src/components/FileViewer/file-viewer.spec.md +30 -12
- package/src/components/FileViewer/file-viewer.stories.tsx +1 -2
- package/src/components/FileViewer/file-viewer.tsx +12 -4
- package/src/components/FileViewer/image-renderer.tsx +35 -7
- package/src/components/HoverCard/hover-card.anatomy.stories.tsx +6 -2
- package/src/components/HoverCard/hover-card.principles.stories.tsx +6 -3
- package/src/components/HoverCard/hover-card.spec.md +5 -5
- package/src/components/HoverCard/hover-card.stories.tsx +16 -16
- package/src/components/HoverCard/hover-card.tsx +5 -3
- package/src/components/InlineEdit/index.ts +5 -0
- package/src/components/InlineEdit/inline-edit.spec.md +149 -0
- package/src/components/InlineEdit/inline-edit.stories.tsx +246 -0
- package/src/components/InlineEdit/inline-edit.tsx +287 -0
- package/src/components/Input/input.anatomy.stories.tsx +8 -7
- package/src/components/Input/input.principles.stories.tsx +2 -12
- package/src/components/Input/input.spec.md +13 -18
- package/src/components/Input/input.tsx +69 -22
- package/src/components/LinkInput/link-input.anatomy.stories.tsx +1 -1
- package/src/components/LinkInput/link-input.principles.stories.tsx +2 -2
- package/src/components/LinkInput/link-input.tsx +11 -10
- package/src/components/Menu/menu-item.anatomy.stories.tsx +3 -3
- package/src/components/Menu/menu-item.principles.stories.tsx +1 -1
- package/src/components/Menu/menu-item.spec.md +22 -15
- package/src/components/Menu/menu-item.stories.tsx +39 -11
- package/src/components/Menu/menu-item.tsx +38 -4
- package/src/components/Notice/notice.principles.stories.tsx +1 -1
- package/src/components/Notice/notice.spec.md +16 -3
- package/src/components/NumberInput/number-input.anatomy.stories.tsx +3 -3
- package/src/components/NumberInput/number-input.principles.stories.tsx +1 -1
- package/src/components/NumberInput/number-input.spec.md +4 -4
- package/src/components/NumberInput/number-input.tsx +19 -12
- package/src/components/OverflowIndicator/overflow-indicator.anatomy.stories.tsx +10 -8
- package/src/components/OverflowIndicator/overflow-indicator.principles.stories.tsx +3 -3
- package/src/components/OverflowIndicator/overflow-indicator.spec.md +4 -2
- package/src/components/OverflowIndicator/overflow-indicator.stories.tsx +71 -63
- package/src/components/OverflowIndicator/overflow-indicator.tsx +9 -8
- package/src/components/Pagination/pagination.spec.md +1 -1
- package/src/components/Pagination/pagination.stories.tsx +2 -6
- package/src/components/Pagination/pagination.tsx +1 -1
- package/src/components/PeoplePicker/people-picker.anatomy.stories.tsx +4 -4
- package/src/components/PeoplePicker/people-picker.principles.stories.tsx +2 -2
- package/src/components/PeoplePicker/people-picker.spec.md +6 -6
- package/src/components/PeoplePicker/people-picker.tsx +56 -16
- package/src/components/PeoplePicker/person-display.tsx +32 -16
- package/src/components/Popover/popover.anatomy.stories.tsx +2 -2
- package/src/components/Popover/popover.tsx +3 -1
- package/src/components/ProfileCard/profile-card.anatomy.stories.tsx +12 -23
- package/src/components/ProfileCard/profile-card.spec.md +3 -3
- package/src/components/ProfileCard/profile-card.tsx +6 -6
- package/src/components/ProgressBar/progress-bar.anatomy.stories.tsx +3 -1
- package/src/components/ProgressBar/progress-bar.principles.stories.tsx +1 -1
- package/src/components/ProgressBar/progress-bar.stories.tsx +50 -42
- package/src/components/RadioGroup/radio-group.anatomy.stories.tsx +10 -28
- package/src/components/RadioGroup/radio-group.stories.tsx +29 -32
- package/src/components/RadioGroup/radio-group.tsx +41 -15
- package/src/components/Rating/rating.spec.md +11 -3
- package/src/components/ScrollArea/scroll-area.anatomy.stories.tsx +1 -1
- package/src/components/ScrollArea/scroll-area.principles.stories.tsx +1 -1
- package/src/components/ScrollArea/scroll-area.spec.md +7 -3
- package/src/components/ScrollArea/scroll-area.stories.tsx +1 -1
- package/src/components/ScrollArea/scroll-area.tsx +17 -4
- package/src/components/Select/select.anatomy.stories.tsx +3 -3
- package/src/components/Select/select.spec.md +27 -17
- package/src/components/Select/select.stories.tsx +33 -0
- package/src/components/Select/select.tsx +190 -36
- package/src/components/SelectMenu/select-menu.anatomy.stories.tsx +4 -4
- package/src/components/SelectMenu/select-menu.spec.md +15 -7
- package/src/components/SelectMenu/select-menu.tsx +16 -3
- package/src/components/SelectionControl/boolean-value.tsx +39 -0
- package/src/components/SelectionControl/index.ts +1 -0
- package/src/components/SelectionControl/selection-item.anatomy.stories.tsx +2 -2
- package/src/components/SelectionControl/selection-item.principles.stories.tsx +2 -2
- package/src/components/SelectionControl/selection-item.spec.md +3 -3
- package/src/components/SelectionControl/selection-item.stories.tsx +3 -3
- package/src/components/Separator/separator.spec.md +2 -2
- package/src/components/Separator/separator.stories.tsx +6 -27
- package/src/components/Sheet/sheet.anatomy.stories.tsx +1 -1
- package/src/components/Sheet/sheet.principles.stories.tsx +1 -1
- package/src/components/Sheet/sheet.spec.md +2 -2
- package/src/components/Sheet/sheet.tsx +9 -5
- package/src/components/Sidebar/sidebar.anatomy.stories.tsx +16 -12
- package/src/components/Sidebar/sidebar.principles.stories.tsx +10 -8
- package/src/components/Sidebar/sidebar.spec.md +13 -15
- package/src/components/Sidebar/sidebar.stories.tsx +8 -7
- package/src/components/Sidebar/sidebar.tsx +37 -25
- package/src/components/Skeleton/skeleton.stories.tsx +4 -11
- package/src/components/Slider/slider.anatomy.stories.tsx +3 -3
- package/src/components/Slider/slider.principles.stories.tsx +2 -2
- package/src/components/Slider/slider.spec.md +5 -5
- package/src/components/Slider/slider.tsx +3 -1
- package/src/components/Steps/steps.anatomy.stories.tsx +4 -4
- package/src/components/Steps/steps.principles.stories.tsx +1 -1
- package/src/components/Steps/steps.spec.md +1 -1
- package/src/components/Switch/switch.anatomy.stories.tsx +7 -7
- package/src/components/Switch/switch.principles.stories.tsx +4 -4
- package/src/components/Switch/switch.spec.md +8 -8
- package/src/components/Switch/switch.stories.tsx +9 -24
- package/src/components/Switch/switch.tsx +17 -13
- package/src/components/Tabs/tabs.anatomy.stories.tsx +10 -7
- package/src/components/Tabs/tabs.spec.md +1 -1
- package/src/components/Tabs/tabs.tsx +21 -1
- package/src/components/Textarea/textarea.anatomy.stories.tsx +1 -1
- package/src/components/Textarea/textarea.principles.stories.tsx +1 -1
- package/src/components/Textarea/textarea.spec.md +4 -4
- package/src/components/Textarea/textarea.tsx +72 -46
- package/src/components/TimePicker/time-columns.tsx +6 -2
- package/src/components/TimePicker/time-picker.principles.stories.tsx +1 -1
- package/src/components/TimePicker/time-picker.spec.md +3 -3
- package/src/components/TimePicker/time-picker.tsx +16 -10
- package/src/components/Toast/toast.stories.tsx +7 -4
- package/src/components/Tooltip/tooltip.anatomy.stories.tsx +4 -4
- package/src/components/Tooltip/tooltip.principles.stories.tsx +3 -3
- package/src/components/Tooltip/tooltip.spec.md +1 -1
- package/src/components/Tooltip/tooltip.stories.tsx +4 -39
- package/src/components/Tooltip/tooltip.tsx +8 -6
- package/src/components/TreeView/tree-view.anatomy.stories.tsx +6 -3
- package/src/components/TreeView/tree-view.spec.md +42 -11
- package/src/components/TreeView/tree-view.stories.tsx +1 -0
- package/src/components/TreeView/tree-view.tsx +322 -17
- package/src/index.ts +584 -60
- package/src/patterns/action-bar/action-bar.spec.md +9 -0
- package/src/patterns/action-bar/action-bar.stories.tsx +6 -3
- package/src/patterns/element-anatomy/inline-action.spec.md +2 -0
- package/src/patterns/element-anatomy/item-anatomy.spec.md +14 -5
- package/src/patterns/element-anatomy/item-anatomy.stories.tsx +6 -4
- package/src/patterns/element-anatomy/item-anatomy.tsx +7 -6
- package/src/patterns/header-canonical/header-canonical.spec.md +5 -4
- package/src/patterns/overlay-surface/overlay-surface.spec.md +22 -1
- package/src/styles/tokens.css +3 -0
- package/src/tokens/README.md +1 -1
- package/src/tokens/color/color.spec.md +3 -3
- package/src/tokens/color/color.stories.tsx +3 -3
- package/src/tokens/density/density.stories.tsx +16 -3
- package/src/tokens/layoutSpace/layoutSpace.spec.md +3 -1
- package/src/tokens/motion/motion.css +33 -21
- package/src/tokens/motion/motion.spec.md +32 -15
- package/src/tokens/motion/motion.stories.tsx +58 -5
- package/src/tokens/motion/motion.ts +7 -6
- package/src/tokens/motion/overlay-motion.ts +26 -0
- package/src/tokens/orphan-tokens.spec.md +4 -4
- package/src/tokens/token-system.spec.md +1 -0
- package/src/tokens/uiSize/uiSize.spec.md +1 -1
- package/src/tokens/uiSize/uiSize.stories.tsx +4 -2
- package/src/tokens/utility-registry.json +160 -23
|
@@ -14,6 +14,31 @@ source "$(dirname "$0")/_log-fire.sh" 2>/dev/null && log_hook_fire
|
|
|
14
14
|
set -uo pipefail
|
|
15
15
|
INPUT=$(cat 2>/dev/null || echo "{}")
|
|
16
16
|
|
|
17
|
+
# ── Shared: 中和 consumer 自有 local 元件的裸 tag(named-import 除鏽的反向保護,2026-07-10 N6 根治)──
|
|
18
|
+
# 多數 r2/r3 pattern 用 `<(DS\.)?X`(named-import 除鏽,抓 fork 用 named import `<Dialog>` 繞過
|
|
19
|
+
# `<DS.>` 前綴)→ 但會誤殺 consumer 自有的同名 local 元件。判準:裸 <X> 只在「明確 named-import
|
|
20
|
+
# 自非 DS 模組」(local './x' 或他 package)時才確定是 consumer 自有 → 中和成 <LOCAL_X;
|
|
21
|
+
# 無 import(fixture 慣例)或 DS-package import → 視為 DS,保留(維持除鏽 intent)。
|
|
22
|
+
# namespace <DS.X>(capture 到 "DS" token → skip)/ raw 小寫 HTML tag 不受影響。
|
|
23
|
+
neutralize_local_tags() {
|
|
24
|
+
local content="$1"
|
|
25
|
+
# LOCAL_NAMED extraction 用 flattened 副本(抓跨行 import block);但**回傳保留原換行**
|
|
26
|
+
# (r2 的空 @story-baseline: marker 偵測是逐行的,flatten 會讓下一行內容誤填空 marker → P4)。
|
|
27
|
+
local local_named
|
|
28
|
+
local_named=$(printf '%s' "$content" | tr '\n' ' ' \
|
|
29
|
+
| grep -oE "import[[:space:]]*\{[^}]*\}[[:space:]]*from[[:space:]]*['\"][^'\"]+['\"]" \
|
|
30
|
+
| grep -vE "from[[:space:]]*['\"]@qijenchen/design-system" \
|
|
31
|
+
| grep -oE '\{[^}]*\}' | tr -d '{}' | tr ',' '\n' \
|
|
32
|
+
| sed -E 's/.*[[:space:]]as[[:space:]]+([A-Za-z0-9_]+).*/\1/; s/[[:space:]]//g' \
|
|
33
|
+
| grep -E '^[A-Z]' | sort -u)
|
|
34
|
+
local T
|
|
35
|
+
for T in $local_named; do
|
|
36
|
+
[ "$T" = "DS" ] && continue
|
|
37
|
+
content=$(printf '%s' "$content" | sed -E "s#<(/?)$T([[:space:]/>])#<\\1LOCAL_$T\\2#g")
|
|
38
|
+
done
|
|
39
|
+
printf '%s' "$content"
|
|
40
|
+
}
|
|
41
|
+
|
|
17
42
|
r1_no_ds_catalog() {
|
|
18
43
|
set -uo pipefail
|
|
19
44
|
|
|
@@ -117,11 +142,15 @@ CONTENT=$(echo "$INPUT" | jq -r '.tool_input.new_string // .tool_input.content /
|
|
|
117
142
|
# Escape clauses
|
|
118
143
|
if echo "$CONTENT" | grep -qE '@story-baseline-allow:|@consumer-catalog-allow:'; then exit 0; fi
|
|
119
144
|
|
|
145
|
+
# 中和 consumer 自有 local 元件的裸 tag(N6 over-broad guard;shared helper SSOT)
|
|
146
|
+
CONTENT=$(neutralize_local_tags "$CONTENT")
|
|
147
|
+
|
|
120
148
|
# High-risk DS primitives requiring baseline marker
|
|
121
149
|
HIGH_RISK_PRIMITIVES='DataTable|Dialog|Sheet|Popover|DropdownMenu|Tooltip|HoverCard|LinkInput|RadioGroup|CircularProgress|AppShell|Sidebar'
|
|
122
150
|
|
|
123
|
-
# Detect usage
|
|
124
|
-
|
|
151
|
+
# Detect usage(named-import 除鏽:namespace <DS.X> + DS-named-import 的裸 <X> 皆計入;
|
|
152
|
+
# consumer 自有 local 元件已由 neutralize_local_tags 中和成 <LOCAL_X 不會誤匹配)
|
|
153
|
+
USED=$(echo "$CONTENT" | grep -oE "<(DS\.)?($HIGH_RISK_PRIMITIVES)\\b" | sort -u | head -10)
|
|
125
154
|
|
|
126
155
|
if [ -z "$USED" ]; then exit 0; fi
|
|
127
156
|
|
|
@@ -179,33 +208,36 @@ CONTENT=$(echo "$CONTENT" | tr '\n' ' ')
|
|
|
179
208
|
# Global escape — file-wide allowlist
|
|
180
209
|
if echo "$CONTENT" | grep -q '@ds-misuse-allow:'; then exit 0; fi
|
|
181
210
|
|
|
211
|
+
# 中和 consumer 自有 local 元件的裸 tag(N6 over-broad guard;shared helper SSOT,對齊 r2_story_baseline)
|
|
212
|
+
CONTENT=$(neutralize_local_tags "$CONTENT")
|
|
213
|
+
|
|
182
214
|
VIOLATIONS=""
|
|
183
215
|
|
|
184
216
|
# Pattern 1: <CircularProgress size={N}> with literal number (override default 24)
|
|
185
|
-
if echo "$CONTENT" | grep -qE '<DS\.CircularProgress[^>]+size=\{[0-9]+\}'; then
|
|
217
|
+
if echo "$CONTENT" | grep -qE '<(DS\.)?CircularProgress[^>]+size=\{[0-9]+\}'; then
|
|
186
218
|
VIOLATIONS="${VIOLATIONS} - <CircularProgress size={N}> hardcoded number override default 24 (per circular-progress.spec.md:101)\n"
|
|
187
219
|
fi
|
|
188
220
|
|
|
189
221
|
# Pattern 2: <RadioGroupItem> NOT wrapped in <SelectionItem control={...}>
|
|
190
222
|
# Approximation: file uses RadioGroupItem but doesn't reference SelectionItem
|
|
191
|
-
if echo "$CONTENT" | grep -qE '<DS\.RadioGroupItem\b' && ! echo "$CONTENT" | grep -qE 'SelectionItem|<DS\.RadioGroupItem[^>]+label='; then
|
|
223
|
+
if echo "$CONTENT" | grep -qE '<(DS\.)?RadioGroupItem\b' && ! echo "$CONTENT" | grep -qE 'SelectionItem|<(DS\.)?RadioGroupItem[^>]+label='; then
|
|
192
224
|
VIOLATIONS="${VIOLATIONS} - <RadioGroupItem> 沒 wrap <SelectionItem control={<RadioGroupItem>}> (per selection-item.spec.md:23 SSOT spacing/padding)\n"
|
|
193
225
|
fi
|
|
194
226
|
|
|
195
227
|
# Pattern 3: <DataTable columns={[…]}> with literal single column
|
|
196
|
-
if echo "$CONTENT" | grep -qE '<DS\.DataTable[^>]+columns=\{\[\s*\{[^}]+\}\s*\]\}' && ! echo "$CONTENT" | grep -qE 'columns=\{[^}]*\},\s*\{'; then
|
|
228
|
+
if echo "$CONTENT" | grep -qE '<(DS\.)?DataTable[^>]+columns=\{\[\s*\{[^}]+\}\s*\]\}' && ! echo "$CONTENT" | grep -qE 'columns=\{[^}]*\},\s*\{'; then
|
|
197
229
|
VIOLATIONS="${VIOLATIONS} - <DataTable columns={[single-col]}> minimal one-column = 違反 data-table.spec.md canonical(min 2 cols for meaningful render)\n"
|
|
198
230
|
fi
|
|
199
231
|
|
|
200
232
|
# Pattern 4: <LinkInput placeholder=...> without value prop
|
|
201
|
-
if echo "$CONTENT" | grep -qE '<DS\.LinkInput[^>]+placeholder=' && ! echo "$CONTENT" | grep -qE '<DS\.LinkInput[^>]+(value|defaultValue)='; then
|
|
233
|
+
if echo "$CONTENT" | grep -qE '<(DS\.)?LinkInput[^>]+placeholder=' && ! echo "$CONTENT" | grep -qE '<(DS\.)?LinkInput[^>]+(value|defaultValue)='; then
|
|
202
234
|
VIOLATIONS="${VIOLATIONS} - <LinkInput placeholder=...> 沒 value prop = placeholder-only mode 抹平 link/edit canonical (per link-input.spec.md:18,48-58)\n"
|
|
203
235
|
fi
|
|
204
236
|
|
|
205
237
|
# Pattern 5: <Empty title=...> without icon and without description
|
|
206
|
-
if echo "$CONTENT" | grep -qE '<DS\.Empty[^>]+title=' && \
|
|
207
|
-
! echo "$CONTENT" | grep -qE '<DS\.Empty[^>]+icon=' && \
|
|
208
|
-
! echo "$CONTENT" | grep -qE '<DS\.Empty[^>]+description='; then
|
|
238
|
+
if echo "$CONTENT" | grep -qE '<(DS\.)?Empty[^>]+title=' && \
|
|
239
|
+
! echo "$CONTENT" | grep -qE '<(DS\.)?Empty[^>]+icon=' && \
|
|
240
|
+
! echo "$CONTENT" | grep -qE '<(DS\.)?Empty[^>]+description='; then
|
|
209
241
|
VIOLATIONS="${VIOLATIONS} - <Empty title=...> 無 icon 無 description = 違反 Empty.tsx:11「預設只需 description」minimal mock looks weird\n"
|
|
210
242
|
fi
|
|
211
243
|
|
|
@@ -219,12 +251,12 @@ if echo "$CONTENT" | grep -qE '\b[a-z][a-z-]*-\[(#[0-9a-fA-F]{3,8}|rgb|rgba|hsl|
|
|
|
219
251
|
fi
|
|
220
252
|
|
|
221
253
|
# Pattern 9(2026-06-12,user 抓 fork「四不像」G4 補洞):AppShell slot 餵 raw HTML element。
|
|
222
|
-
# app-shell.spec.md
|
|
254
|
+
# app-shell.spec.md「Consumer 紀律」段明文禁 sidebar={<div>}/header={<header>},原註「靠 audit 把關」
|
|
223
255
|
# 無機械閘 → 兌現成 P0(per memory feedback_ssot_mechanical_p0_not_p1_warn)。
|
|
224
256
|
# 零誤判:雙條件 = 同檔有 <DS.AppShell> + slot 屬性直接餵 raw tag(div/header/nav/aside/section)。
|
|
225
|
-
if echo "$CONTENT" | grep -qE '<DS\.AppShell\b' && \
|
|
257
|
+
if echo "$CONTENT" | grep -qE '<(DS\.)?AppShell\b' && \
|
|
226
258
|
echo "$CONTENT" | grep -qE '\b(header|sidebar|aside)=\{ *<(div|header|nav|aside|section)\b'; then
|
|
227
|
-
VIOLATIONS="${VIOLATIONS} - <AppShell header/sidebar/aside={<raw element>}> — slot 必餵 DS 元件(ChromeHeader / Sidebar / AppShellAside),raw div/header 漏接 border/scroll/responsive canonical(per app-shell.spec.md
|
|
259
|
+
VIOLATIONS="${VIOLATIONS} - <AppShell header/sidebar/aside={<raw element>}> — slot 必餵 DS 元件(ChromeHeader / Sidebar / AppShellAside),raw div/header 漏接 border/scroll/responsive canonical(per app-shell.spec.md「Consumer 紀律」段)\n"
|
|
228
260
|
fi
|
|
229
261
|
|
|
230
262
|
# Pattern 10(2026-06-16,user 抓 fork prototype 手刻 table 不用 DataTable):RAW PRIMITIVE 手刻
|
|
@@ -236,21 +268,121 @@ fi
|
|
|
236
268
|
if echo "$CONTENT" | grep -qE '<table\b' && echo "$CONTENT" | grep -qE '<thead\b|<tbody\b|<th\b'; then
|
|
237
269
|
VIOLATIONS="${VIOLATIONS} - 手刻 raw <table><thead>/<tbody> 資料表 → 必用 <DataTable columns={...} data={...} />(build-ui-canonicals.md:18 ❌→✅ SSOT;這是「優先消費既有元件」大原則,無理由不得手刻)\n"
|
|
238
270
|
fi
|
|
271
|
+
# 2026-07-08 WM 戰役 R4:偽表格視覺簽名(div-grid 表格繞過字面 <table> 偵測 — WM 5 檔
|
|
272
|
+
# MINI_TABLE grid-cols + header bg + row 常數重複宣告實證,spec broad / hook narrow M34 gap)。
|
|
273
|
+
# 簽名 = 同段 content 有 grid-cols-[ 且(bg-muted|bg-neutral-selected 當 header 底 或 自創
|
|
274
|
+
# ROW_PAD 類常數)。escape:@handcraft-table-ok:(真非表格的 grid 佈局)。
|
|
275
|
+
if echo "$CONTENT" | grep -qE 'grid-cols-\[' && echo "$CONTENT" | grep -qE '(bg-muted|bg-neutral-selected)[^a-z-]' && echo "$CONTENT" | grep -qiE 'ROW_PAD|MINI_TABLE|<div[^>]*grid[^>]*font-medium' && ! echo "$CONTENT" | grep -q '@handcraft-table-ok:'; then
|
|
276
|
+
VIOLATIONS="${VIOLATIONS} - 偽表格簽名(div grid-cols + header bg + row 常數):div-grid 手刻資料表 → 必用 <DataTable size=\"sm\" height=\"auto\">(data-table.spec「嵌入式表格」canonical;WM MembersTab 等 5 檔錨例)。真非表格 grid 佈局 → 行內 @handcraft-table-ok: <rationale>\n"
|
|
277
|
+
fi
|
|
239
278
|
if echo "$CONTENT" | grep -qE '<img\b[^>]*rounded-full'; then
|
|
240
279
|
VIOLATIONS="${VIOLATIONS} - 手刻 <img ... rounded-full> 頭像 → 必用 <Avatar>(build-ui-canonicals.md:25)\n"
|
|
241
280
|
fi
|
|
242
281
|
if echo "$CONTENT" | grep -qE '<select\b'; then
|
|
243
282
|
VIOLATIONS="${VIOLATIONS} - native <select> 手刻下拉 → 必用 <Select> / <DropdownMenu>(build-ui-canonicals.md:15)\n"
|
|
244
283
|
fi
|
|
284
|
+
# 2026-07-08 WM 戰役 R4:手刻表單 validation 簽名(WM CreateWorkItemDialog 註解引 canonical
|
|
285
|
+
# 卻手刻 showErrors state = M23(d)「cite 存在 ≠ consume」;useFormValidation hook 零消費偵測缺口)。
|
|
286
|
+
# 簽名 = 檔內有 showErrors/submitAttempt 類 state + FieldError 渲染,但無 useFormValidation import。
|
|
287
|
+
if echo "$CONTENT" | grep -q 'useState' && echo "$CONTENT" | grep -qE '\b(showErrors|showError|submitAttempt|attemptedSubmit)\b' && echo "$CONTENT" | grep -q 'FieldError' && ! echo "$CONTENT" | grep -q 'useFormValidation' && ! echo "$CONTENT" | grep -q '@form-validation-ok:'; then
|
|
288
|
+
VIOLATIONS="${VIOLATIONS} - 手刻表單 validation state(showErrors/submitAttempt + FieldError)→ 必消費 useFormValidation hook(form-validation.spec 規則 1 blur/7 全驗/8 DOM 序 anchor 隨 hook 自帶;root barrel 有 export)。特殊表單 → 行內 @form-validation-ok: <rationale>\n"
|
|
289
|
+
fi
|
|
245
290
|
if echo "$CONTENT" | grep -qE '<hr\b'; then
|
|
246
291
|
VIOLATIONS="${VIOLATIONS} - 手刻 <hr> 分隔線 → 用 <Separator>(或 separator.spec 允許的 CSS border)(build-ui-canonicals.md:23)\n"
|
|
247
292
|
fi
|
|
293
|
+
# 2026-07-10 WM 戰役收官:手刻 menu-item 可點列簽名(WM TypeSettingsDialog 左 rail 錨例 —
|
|
294
|
+
# 手刻 <button hover:bg-neutral-hover + bg-neutral-selected> nav row,未消費 MenuItem)。
|
|
295
|
+
# 前置:DS API 根因已修(MenuItem startContent slot,beta.84)→ 有合法替代才上機械攔(避免
|
|
296
|
+
# fork 無路可走 brick)。簽名 = hover:bg-neutral-hover 與 bg-neutral-selected 成對出現
|
|
297
|
+
# (= menu-item family 狀態語義)+ 可點訊號(<button/<a/onClick),且沒在消費 <MenuItem。
|
|
298
|
+
# 注意:不 tag-anchor className(WM 錨例把 class 抽 const 再 className={cls} 引用,
|
|
299
|
+
# tag-anchored regex 會漏 — test 27 抓到的真實 shape)。
|
|
300
|
+
if echo "$CONTENT" | grep -q 'hover:bg-neutral-hover' && \
|
|
301
|
+
echo "$CONTENT" | grep -q 'bg-neutral-selected' && \
|
|
302
|
+
echo "$CONTENT" | grep -qE '<button\b|<a\b|onClick=' && \
|
|
303
|
+
! echo "$CONTENT" | grep -q '<MenuItem' && \
|
|
304
|
+
! echo "$CONTENT" | grep -q '@nav-row-handcraft-ok:'; then
|
|
305
|
+
VIOLATIONS="${VIOLATIONS} - 手刻 menu-item 可點列(hover:bg-neutral-hover + bg-neutral-selected 成對)→ 必包 <MenuItem>(subpath @qijenchen/design-system/components/Menu;selected prop + startIcon/avatar/startContent slot;menu-item.spec.md「結構」)。非 menu-item 語義 → 行內 @nav-row-handcraft-ok: <rationale>\n"
|
|
306
|
+
fi
|
|
307
|
+
|
|
308
|
+
# ── 2026-07-10 批次 A(治理覆蓋 matrix 收官;來源 .claude/planning/2026-07-10-consumer-coverage-remediation.md)──
|
|
309
|
+
# C5 對比配對:深字桶 hue(yellow/amber/orange/lime step-6)配 text-white — 各自合法、組合對比違規
|
|
310
|
+
# (tag.spec.md 深字桶 = text-on-emphasis-dark)。修 → 消費 CAT_SOLID token pair。
|
|
311
|
+
if echo "$CONTENT" | grep -q 'text-white' && \
|
|
312
|
+
echo "$CONTENT" | grep -qE 'bg-\[var\(--color-(yellow|amber|orange|lime)-[0-9]' && \
|
|
313
|
+
! echo "$CONTENT" | grep -q '@cat-solid-ok:'; then
|
|
314
|
+
VIOLATIONS="${VIOLATIONS} - 亮色底(yellow/amber/orange/lime step-6)配 text-white = 對比違規 → 消費 CAT_SOLID / CAT_SOLID_TOKENS pair(深字桶自帶 text-on-emphasis-dark;tokens/categorical-color.ts)。特例 → @cat-solid-ok: <rationale>\n"
|
|
315
|
+
fi
|
|
316
|
+
# C18 計數串接:element children 出現 {label} ({count/length/total})。attr(aria-label)字串層合法
|
|
317
|
+
# — regex 錨 children 的 > 前綴,attribute 不中(item-anatomy.spec.md:176 禁括號串接)。
|
|
318
|
+
if echo "$CONTENT" | grep -qE '>[^<]*\{[a-zA-Z0-9_.]+\} *\(\{[a-zA-Z0-9_.]*(count|Count|length|total)[a-zA-Z0-9_.]*\}\)' && \
|
|
319
|
+
! echo "$CONTENT" | grep -q '@count-in-label-ok:'; then
|
|
320
|
+
VIOLATIONS="${VIOLATIONS} - 計數括號串接 {label} ({count}) → row header 用「標題左、純數字右 space-between + tabular-nums」(item-anatomy.spec.md:176);互動元素(tab/chip/button)用 badge slot。aria/純文字匯出層 → @count-in-label-ok: <rationale>\n"
|
|
321
|
+
fi
|
|
322
|
+
# C17 分隔線幾何:vertical Separator 無 mx- = 黏按鈕(action-bar.spec.md「分隔線幾何」h-6 mx-1;
|
|
323
|
+
# ButtonGroup 內用 <ButtonDivider/>(mx built-in))。
|
|
324
|
+
SEP_TAGS=$(echo "$CONTENT" | grep -oE '<(DS\.)?Separator[^>]*orientation=["'"'"']vertical["'"'"'][^>]*' || true)
|
|
325
|
+
if [ -n "$SEP_TAGS" ] && echo "$SEP_TAGS" | grep -qv 'mx-' && \
|
|
326
|
+
! echo "$CONTENT" | grep -q '@separator-geometry-ok:'; then
|
|
327
|
+
VIOLATIONS="${VIOLATIONS} - vertical <Separator> 無 mx- = 分隔線黏按鈕 → toolbar 用 className=\"h-6 mx-1\"(dense h-5 mx-1);ButtonGroup 內用 <ButtonDivider/>(action-bar.spec.md「分隔線幾何」)。版面切分 divider → @separator-geometry-ok: <rationale>\n"
|
|
328
|
+
fi
|
|
329
|
+
# C20 連結字手刻 underline:color.spec.md「Action — Primary」= hover 換色不用底線;
|
|
330
|
+
# dropzone 連結走 FileUpload children slot。no-underline / hover:underline 皆不中此 regex 形。
|
|
331
|
+
if echo "$CONTENT" | grep -qE '<(span|a|button|div)[^>]*className="([^"]* )?underline( [^"]*)?"'; then
|
|
332
|
+
VIOLATIONS="${VIOLATIONS} - 手刻 underline 連結字 → Action—Primary canonical = text-primary + hover 換色(text-primary-hover),不用底線(color.spec.md「Action — Primary」);dropzone 連結走 <FileUpload> children slot\n"
|
|
333
|
+
fi
|
|
334
|
+
# C10 業務 search 被推最右:spacer(flex-1 自閉合 / ml-auto)緊接 Search Input。
|
|
335
|
+
# 過濾當前檢視的 search = 業務層,與其他操作同區(action-bar.spec.md:69/76);無標題 toolbar 靠左(:109)。
|
|
336
|
+
if echo "$CONTENT" | grep -qE '(flex-1[^>]*/>|ml-auto)[^<]{0,80}<(DS\.)?Input[^>]{0,200}placeholder="(Search|搜尋)' && \
|
|
337
|
+
! echo "$CONTENT" | grep -q '@search-right-ok:'; then
|
|
338
|
+
VIOLATIONS="${VIOLATIONS} - 業務 search(過濾當前檢視)被 spacer 推到最右 → 歸業務層與其他操作同區(action-bar.spec.md:69/76;無標題 toolbar 業務層靠左 :109)。工具型跨頁 search 置最右合法 → @search-right-ok: <rationale>\n"
|
|
339
|
+
fi
|
|
340
|
+
# C11 dialog chrome 雙 X:DialogHeader 之後 250 字窗內手放 startIcon={X} 且窗內無 actions=
|
|
341
|
+
# (DialogHeader 自帶 Close;header 操作走 actions slot — action-bar.spec.md:281)。
|
|
342
|
+
DH_WIN=$(echo "$CONTENT" | grep -oE '<(DS\.)?DialogHeader.{0,250}' || true) # BSD grep bound 上限 255
|
|
343
|
+
if [ -n "$DH_WIN" ] && echo "$DH_WIN" | grep -qE 'startIcon=\{X\}' && ! echo "$DH_WIN" | grep -q 'actions=' && \
|
|
344
|
+
! echo "$CONTENT" | grep -q '@dialog-chrome-ok:'; then
|
|
345
|
+
VIOLATIONS="${VIOLATIONS} - DialogHeader 內手放 X 關閉鈕(DialogHeader 已自帶 Close = 雙 X)→ header 操作走 actions slot(自動 ButtonDivider 分隔,action-bar.spec.md:281)。特例 → @dialog-chrome-ok: <rationale>\n"
|
|
346
|
+
fi
|
|
347
|
+
# C7 SelectMenu internal 直用(select-menu.spec.md:53 明文禁 consumer 直用 — 它是 Select/
|
|
348
|
+
# Combobox 的內部選單面,consumer 用 <Select>/<Combobox>;此為「internal 且 spec 明文禁直用」
|
|
349
|
+
# 特例,非一般 internal 包裝即可)。
|
|
350
|
+
if echo "$CONTENT" | grep -qE '<SelectMenu\b' && \
|
|
351
|
+
! echo "$CONTENT" | grep -q '@selectmenu-direct-ok:'; then
|
|
352
|
+
VIOLATIONS="${VIOLATIONS} - <SelectMenu> 直用(internal 選單面,select-menu.spec.md:53 明文禁)→ 用 <Select> / <Combobox>(自帶 trigger + 選單接線)。極特例 → @selectmenu-direct-ok: <rationale>\n"
|
|
353
|
+
fi
|
|
354
|
+
# C13 手刻可收合 section header(2026-07-10 收官:R3-7 拍板 SectionHeader 留產品客製、但 layout
|
|
355
|
+
# 全消費 canonical — 組合鐵律 codify 於 item-anatomy.spec.md「可收合 section header」;簽名 =
|
|
356
|
+
# Chevron toggle + justify-between + 可點,且檔內無 SectionHeader 共用元件)。
|
|
357
|
+
if echo "$CONTENT" | grep -qE 'Chevron(Down|Right|Up)' && \
|
|
358
|
+
echo "$CONTENT" | grep -q 'justify-between' && \
|
|
359
|
+
echo "$CONTENT" | grep -qE '<button\b|onClick=' && \
|
|
360
|
+
! echo "$CONTENT" | grep -q 'SectionHeader' && \
|
|
361
|
+
! echo "$CONTENT" | grep -q '@section-header-ok:'; then
|
|
362
|
+
VIOLATIONS="${VIOLATIONS} - 手刻可收合 section 標題列(Chevron + justify-between + 可點)→ 抽共用 SectionHeader 元件並照 item-anatomy.spec.md「可收合 section header 組合 canonical」(chevron = title 後 suffix inline action / desc 用 item-gap token / endSlot 垂直置中;WM SectionHeader 錨例)。非 section 標題語義 → @section-header-ok: <rationale>\n"
|
|
363
|
+
fi
|
|
364
|
+
# C19 兩欄 dialog 共用捲軸(2026-07-10 user 拍板:兩欄是組合非元件;組合鐵律 = 各欄自帶
|
|
365
|
+
# ScrollArea,overlay-surface.spec.md「兩欄 dialog 組合 canonical」)。簽名 = Dialog body +
|
|
366
|
+
# 欄分隔 border-l + ScrollArea 少於 2 = 兩欄共用單一捲軸。
|
|
367
|
+
if echo "$CONTENT" | grep -qE '<(DS\.)?Dialog(Body|Content)\b' && \
|
|
368
|
+
echo "$CONTENT" | grep -q 'border-l' && \
|
|
369
|
+
echo "$CONTENT" | grep -qE 'className="[^"]*flex[^"]*"' && \
|
|
370
|
+
[ "$(echo "$CONTENT" | grep -o '<ScrollArea' | wc -l | tr -d ' ')" -lt 2 ] && \
|
|
371
|
+
! echo "$CONTENT" | grep -q '@two-pane-dialog-ok:'; then
|
|
372
|
+
VIOLATIONS="${VIOLATIONS} - 兩欄 dialog(border-l 欄分隔)但 ScrollArea < 2 = 共用單一捲軸 → 各欄自帶 <ScrollArea> 獨立捲(overlay-surface.spec.md「兩欄 dialog 組合 canonical」;archetype = WM WorkItemDetailDialog / FileViewer InfoPanel)。非兩欄語義 → @two-pane-dialog-ok: <rationale>\n"
|
|
373
|
+
fi
|
|
374
|
+
# C12 Field family 硬寬:Select/Combobox/DatePicker/PeoplePicker 帶 w-N / w-[Npx] 硬寬
|
|
375
|
+
# → width="hug"/"fill" 語義軸(field-controls.spec.md「寬度」)。
|
|
376
|
+
if echo "$CONTENT" | grep -qE '<(DS\.)?(Select|Combobox|DatePicker|PeoplePicker)\b[^>]*className="[^"]*\bw-([0-9]+|\[[0-9]+(px|rem)\])' && \
|
|
377
|
+
! echo "$CONTENT" | grep -q '@field-width-ok:'; then
|
|
378
|
+
VIOLATIONS="${VIOLATIONS} - Field 家族控件 className 硬寬(w-N / w-[Npx])→ 用 width=\"hug\"(內容自適)或預設 fill(field-controls.spec.md「寬度」;WM 詳情 metadata 全 hug 拍板錨例)。特例 → @field-width-ok: <rationale>\n"
|
|
379
|
+
fi
|
|
248
380
|
|
|
249
381
|
# Pattern 6: Overlay trigger without defaultOpen state for visual demo
|
|
250
382
|
# (Skip in production .tsx; only enforce in .stories.tsx where visual snapshot matters)
|
|
251
383
|
if echo "$FILE" | grep -qE '\.stories\.tsx$'; then
|
|
252
384
|
for overlay in Tooltip Popover Dialog Sheet DropdownMenu; do
|
|
253
|
-
if echo "$CONTENT" | grep -qE "<DS
|
|
385
|
+
if echo "$CONTENT" | grep -qE "<(DS\.)?${overlay}\b" && \
|
|
254
386
|
! echo "$CONTENT" | grep -qE "(defaultOpen|open=\{(true|isOpen)\})"; then
|
|
255
387
|
VIOLATIONS="${VIOLATIONS} - Story uses <${overlay}> without defaultOpen — visual audit can't see overlay content\n"
|
|
256
388
|
fi
|
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
#
|
|
5
5
|
# Purpose: PreToolUse Edit/Write/MultiEdit 偵測 production tsx wrap DS primitive
|
|
6
6
|
# (`<Sidebar>` / `<AppShell>` / `<DataTable>` / `<Dialog>` / `<Field>` 等)
|
|
7
|
-
# 沒同 turn 跑過 Grep / Read DS canonical baseline →
|
|
7
|
+
# 沒同 turn 跑過 Grep / Read DS canonical baseline → P0 BLOCKER(exit 2,2026-07-07 治理進化方向 2 升級;
|
|
8
|
+
# escape CLAUDE_BYPASS_DS_ANCHOR=1 audit-logged),propose 前必出 3-column owner table。
|
|
8
9
|
#
|
|
9
10
|
# Scope:同 check_substantive_edit_approval_preflight.sh extended scope
|
|
10
11
|
# - node_modules/@qijenchen/design-system/src/**.tsx (DS internal)
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# check_dynamic_tailwind_class — 攔「動態 Tailwind utility class」脆弱寫法(2026-07-09 root-cause 防線)
|
|
3
|
+
#
|
|
4
|
+
# 病根(user 以 GitHub Pages 對比抓出的 DataTable row-height regression):
|
|
5
|
+
# `h-table-row-${size}` 這種「class 名本身含 ${...} 插值」的模板字串,Tailwind **靜態掃描看不到**
|
|
6
|
+
# 完整 class → 只能靠專案別處剛好有 literal(如 spec.md)被 content-detection 掃到才生成。
|
|
7
|
+
# 一旦那個脆弱 literal 消失 / 掃描樹變動(本 case 是 .gitignore 加 reference/)→ 整組 `.h-table-row-*`
|
|
8
|
+
# 規則不生成 → 元件靜默塌成內容高度(non-editable 33px / editable 44px 而非 token 40px)+ 選取控件走位。
|
|
9
|
+
# tsc / build 全綠、CI 不紅 = 沉默陷阱。
|
|
10
|
+
#
|
|
11
|
+
# 正解(對齊 fieldWrapperStyles 的 `h-field-md` cva-literal 慣例):class 名必須是**完整 literal**;
|
|
12
|
+
# 要隨變數選 class 用 literal map:`{ sm:'h-table-row-sm', md:'h-table-row-md', lg:'h-table-row-lg' }[size]`。
|
|
13
|
+
#
|
|
14
|
+
# Scope:production tsx/ts(packages/design-system/src + apps)。CSS var 插值(`var(--x-${y})` / arbitrary
|
|
15
|
+
# value `[...${...}]`)不在此列(那是值,不是 class 名;且 CSS var 真存在,fail-loud 不同機制)。
|
|
16
|
+
# Escape:`// @dynamic-tailwind-allow: <理由>` per-line。
|
|
17
|
+
source "$(dirname "$0")/_log-fire.sh" 2>/dev/null && log_hook_fire
|
|
18
|
+
set -euo pipefail
|
|
19
|
+
INPUT=$(cat)
|
|
20
|
+
fp=$(printf '%s' "$INPUT" | python3 -c "import sys,json;print(json.load(sys.stdin).get('tool_input',{}).get('file_path',''))" 2>/dev/null || true)
|
|
21
|
+
content=$(printf '%s' "$INPUT" | python3 -c "import sys,json;d=json.load(sys.stdin).get('tool_input',{});print(d.get('content') or d.get('new_string') or '')" 2>/dev/null || true)
|
|
22
|
+
[ -z "$fp" ] && exit 0
|
|
23
|
+
case "$fp" in
|
|
24
|
+
*packages/design-system/src/*.tsx|*packages/design-system/src/*.ts|*apps/*/src/*.tsx|*apps/*/src/*.ts) ;;
|
|
25
|
+
*) exit 0 ;;
|
|
26
|
+
esac
|
|
27
|
+
[ -z "$content" ] && exit 0
|
|
28
|
+
|
|
29
|
+
# 抓:backtick 字串內、Tailwind 尺寸/間距 utility 前綴 + 之後含 `${` 讓 class 名不完整。
|
|
30
|
+
# e.g. `h-table-row-${size}` / `h-${x}` / `min-h-${y}` / `w-col-${i}` / `gap-${g}` / `py-${p}`
|
|
31
|
+
# 排除:`[` 之後(arbitrary value,如 `h-[var(--x-${y})]`)、`var(` 內。
|
|
32
|
+
hits=$(printf '%s' "$content" | grep -nE '`[^`]*\b(h|w|min-h|max-h|min-w|max-w|size|gap|space-[xy]|p[xytblr]?|m[xytblr]?)-[a-z0-9-]*\$\{' 2>/dev/null \
|
|
33
|
+
| grep -vE '@dynamic-tailwind-allow' \
|
|
34
|
+
| grep -vE '\[[^]]*\$\{|var\(' || true)
|
|
35
|
+
|
|
36
|
+
if [ -n "$hits" ]; then
|
|
37
|
+
echo "🚨 BLOCKER: 動態 Tailwind class(class 名含 \${...} 插值)— Tailwind 靜態掃描看不到 → 靜默不生成規則" >&2
|
|
38
|
+
echo " file: $fp" >&2
|
|
39
|
+
echo "$hits" | sed 's/^/ /' >&2
|
|
40
|
+
echo "" >&2
|
|
41
|
+
echo " 病根:2026-07-09 DataTable row-height regression(h-table-row-\${size} → utility 消失 → row 塌 + 選取走位)" >&2
|
|
42
|
+
echo " 正解:改 literal map,class 名寫完整字面 —" >&2
|
|
43
|
+
echo " const cls = { sm: 'h-table-row-sm', md: 'h-table-row-md', lg: 'h-table-row-lg' }[size]" >&2
|
|
44
|
+
echo " 逃生:確實非 class(CSS var 值等)→ 行尾加 // @dynamic-tailwind-allow: <理由>" >&2
|
|
45
|
+
exit 2
|
|
46
|
+
fi
|
|
47
|
+
exit 0
|
|
@@ -135,6 +135,33 @@ $(echo "$MARKERS_FOUND" | sed 's/^/ /')
|
|
|
135
135
|
EOF
|
|
136
136
|
exit 2
|
|
137
137
|
fi
|
|
138
|
+
|
|
139
|
+
# ── Repo 級累計 ratchet(2026-07-08 WM 戰役 R4)──────────────────────────────
|
|
140
|
+
# per-file gate(≥3 distinct/≥5 total)可被「每檔 1-2 個、散多檔」繞過 → repo 級 ceiling。
|
|
141
|
+
# Ratchet 精神(Polaris stylelint migrator):存量 ≥ cap 後只擋「本次 edit 再新增 marker」,
|
|
142
|
+
# 不因存量 brick 無關 edit(本次內容無 marker → 直接放行)。
|
|
143
|
+
EDIT_ADDS_MARKER=$(echo "$CONTENT" | grep -cE "$MARKER_RE" || true)
|
|
144
|
+
if [ "${EDIT_ADDS_MARKER:-0}" -gt 0 ]; then
|
|
145
|
+
APPS_ROOT="${CLAUDE_PROJECT_DIR:-.}/apps"
|
|
146
|
+
REPO_CAP=10
|
|
147
|
+
if [ -d "$APPS_ROOT" ]; then
|
|
148
|
+
REPO_TOTAL=$(grep -rhoE "$MARKER_RE" "$APPS_ROOT" --include='*.tsx' --include='*.ts' 2>/dev/null | wc -l | tr -d ' ')
|
|
149
|
+
if [ "${REPO_TOTAL:-0}" -ge "$REPO_CAP" ]; then
|
|
150
|
+
cat >&2 << EOF
|
|
151
|
+
🚨 ESCAPE-MARKER-REPO-RATCHET BLOCKER(P0,2026-07-08 WM 戰役 R4)
|
|
152
|
+
|
|
153
|
+
apps/** 全 repo escape marker 存量已 $REPO_TOTAL 個(ceiling $REPO_CAP),本次 edit 又要新增 $EDIT_ADDS_MARKER 個。
|
|
154
|
+
每檔 1-2 個散多檔 = 繞 per-file gate 的同型濫用 — repo 級 ratchet 攔。
|
|
155
|
+
|
|
156
|
+
修法 2 選 1:
|
|
157
|
+
(a) 先清既有 escape 的根因(重構走 DS canonical),存量 < $REPO_CAP 後再加真必要的
|
|
158
|
+
(b) Override env(極罕見,commit msg 必記 rationale):
|
|
159
|
+
CLAUDE_BYPASS_ESCAPE_MARKER_AUDIT=1
|
|
160
|
+
EOF
|
|
161
|
+
exit 2
|
|
162
|
+
fi
|
|
163
|
+
fi
|
|
164
|
+
fi
|
|
138
165
|
fi
|
|
139
166
|
|
|
140
167
|
exit 0
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
2
|
# Field family unified invariant hook(2026-05-08 cluster A consolidation)
|
|
3
3
|
#
|
|
4
|
-
# Merges PreToolUse hooks(原各檔已 retire,合併入此)— 共
|
|
4
|
+
# Merges PreToolUse hooks(原各檔已 retire,合併入此)— 共 6 條 sub-rules:
|
|
5
5
|
# A.1 naked row-mode propagation(原 check_naked_row_mode_propagation,P0 BLOCKER)
|
|
6
6
|
# A.2 FieldControlGroup wrapper direct child(原 check_field_control_group_direct_child,P1 WARN)
|
|
7
7
|
# A.3 Field state ring SSOT(原 check_field_state_token_consume 3 sub-rules,P0 BLOCKER)
|
|
8
8
|
# A.4 disabled placeholder color(原 check_disabled_placeholder_color,P1 stderr only)
|
|
9
9
|
# A.5 _Group child fieldCtx.id 隔離(2026-05-31 折入,M4 AR34 regression detector,P0 BLOCKER)
|
|
10
|
+
# A.6 空值顯示 SSOT(2026-07-08 user 拍板半形 hyphen,P0 BLOCKER)
|
|
10
11
|
#
|
|
11
12
|
# Why merge:皆 Field 家族 invariant,共用 INPUT parsing + Edit/Write filter pattern,
|
|
12
13
|
# 分散在 4 個 hook 是「散裝 SSOT」(M17 + Anthropic ≤ 15 hook best practice 違反)。
|
|
@@ -19,6 +20,7 @@
|
|
|
19
20
|
# A.3: `// @field-state-ring-allow: <reason>`
|
|
20
21
|
# A.4: `// @disabled-color-allow: <reason>`
|
|
21
22
|
# A.5: `// @group-fieldctx-allow: <reason>`
|
|
23
|
+
# A.6: `// @empty-display-allow: <reason>`
|
|
22
24
|
|
|
23
25
|
source "$(dirname "$0")/_log-fire.sh" 2>/dev/null && log_hook_fire
|
|
24
26
|
|
|
@@ -247,4 +249,62 @@ EOF
|
|
|
247
249
|
;;
|
|
248
250
|
esac
|
|
249
251
|
|
|
252
|
+
# ── A.6 空值顯示 SSOT(P0 BLOCKER,2026-07-08 user 拍板半形 hyphen)────────────────
|
|
253
|
+
# user verbatim「我從頭到尾哪裡有說要用全形的」+ 五情境空值框架。SSOT:符號常數 =
|
|
254
|
+
# field-wrapper.tsx `EMPTY_DISPLAY = '-'`(U+002D 半形);分流 hook = field-context.ts
|
|
255
|
+
# `useFieldEmptyDisplay()`。禁 2 shape:
|
|
256
|
+
# A.6.1 非 owner 檔直接 import `EMPTY_DISPLAY` 常數(應消費 hook 分流,否則 table-cell
|
|
257
|
+
# editable→'' 分流會漏);
|
|
258
|
+
# A.6.2 components/ tsx display 路徑 hardcode 全形 em dash `'—'`/`"—"`(U+2014)字面。
|
|
259
|
+
# M7 broad-vs-narrow:spec wording 廣(「禁 hardcode 空值符號」);hook 取「quoted em dash 字面」
|
|
260
|
+
# 高信號 subset + owner skip + comment 剝離 + `@empty-display-allow` escape,零誤判導向。
|
|
261
|
+
if ! echo "$NEW_CONTENT" | grep -q '@empty-display-allow'; then
|
|
262
|
+
# A.6.1 非 owner 檔 import EMPTY_DISPLAY 常數(剝離註解行)
|
|
263
|
+
case "$FILE_PATH" in
|
|
264
|
+
*/field-context.ts|*/field-wrapper.tsx) ;; # SSOT owner(定義 + 分流 hook)skip
|
|
265
|
+
*)
|
|
266
|
+
A61_CONTENT=$(echo "$NEW_CONTENT" | grep -vE '^[[:space:]]*(//|\*|/\*)')
|
|
267
|
+
if printf '%s' "$A61_CONTENT" | grep -E "import.*EMPTY_DISPLAY|EMPTY_DISPLAY.*from" >/dev/null; then
|
|
268
|
+
cat >&2 <<EOF
|
|
269
|
+
|
|
270
|
+
┄┄┄ A.6.1 check_field_family_invariants — EMPTY_DISPLAY 直引 BLOCKER ┄┄┄
|
|
271
|
+
|
|
272
|
+
[P0] ${FILE_PATH}
|
|
273
|
+
偵測到直接 import \`EMPTY_DISPLAY\` 常數(非 field-context.ts owner)。
|
|
274
|
+
空值顯示必經 \`useFieldEmptyDisplay()\` hook 分流(table-cell 可編輯→空白 / 其餘→半形 '-'),
|
|
275
|
+
直引常數會漏 surface × editable 分流。
|
|
276
|
+
|
|
277
|
+
修法:import { useFieldEmptyDisplay } from '@/design-system/components/Field/field-context'
|
|
278
|
+
const emptyDisplay = useFieldEmptyDisplay()
|
|
279
|
+
例外:行尾 \`// @empty-display-allow: <reason>\`
|
|
280
|
+
|
|
281
|
+
EOF
|
|
282
|
+
record_worst 2
|
|
283
|
+
fi
|
|
284
|
+
;;
|
|
285
|
+
esac
|
|
286
|
+
# A.6.2 components/ tsx hardcode 全形 em dash 空值字面(剝離註解行 + owner/story skip)
|
|
287
|
+
case "$FILE_PATH" in
|
|
288
|
+
*.stories.tsx|*/field-context.ts|*/field-wrapper.tsx) ;; # doc N/A marker + SSOT owner skip
|
|
289
|
+
*components/*.tsx)
|
|
290
|
+
A62_CONTENT=$(echo "$NEW_CONTENT" | grep -vE '^[[:space:]]*(//|\*|/\*)')
|
|
291
|
+
if printf '%s' "$A62_CONTENT" | grep -E "['\"]—['\"]" >/dev/null; then
|
|
292
|
+
cat >&2 <<EOF
|
|
293
|
+
|
|
294
|
+
┄┄┄ A.6.2 check_field_family_invariants — 全形 em dash 空值 BLOCKER ┄┄┄
|
|
295
|
+
|
|
296
|
+
[P0] ${FILE_PATH}
|
|
297
|
+
偵測到 components/ 實作檔 hardcode 全形 em dash \`'—'\`(U+2014)字面。
|
|
298
|
+
user 2026-07-08 拍板:空值符號 = **半形 hyphen '-'(U+002D)**,禁全形。
|
|
299
|
+
|
|
300
|
+
修法:空值一律 \`{value ?? emptyDisplay}\`(emptyDisplay = useFieldEmptyDisplay()),不 hardcode 符號。
|
|
301
|
+
例外:行尾 \`// @empty-display-allow: <reason>\`
|
|
302
|
+
|
|
303
|
+
EOF
|
|
304
|
+
record_worst 2
|
|
305
|
+
fi
|
|
306
|
+
;;
|
|
307
|
+
esac
|
|
308
|
+
fi
|
|
309
|
+
|
|
250
310
|
exit $WORST
|
|
@@ -43,7 +43,31 @@ ESCAPE_MARKER='@layout-space-magic-ok:'
|
|
|
43
43
|
|
|
44
44
|
# Detect magic spacing classes (Tailwind class strings only — NOT JSX props like size={24})
|
|
45
45
|
# Match per line so we can check per-line escape comments
|
|
46
|
-
|
|
46
|
+
# 2026-07-08 WM 戰役 R4 擴 scope(broad-vs-narrow gap 實證修補):
|
|
47
|
+
# (a) logical properties ps-/pe-/ms-/me-(WM 6 筆 ps-4 漏網實證)
|
|
48
|
+
# (b) arbitrary bracket 固定 px:gap-[7px] / p-[13px] 類(spacing 家族)
|
|
49
|
+
# (c) 固定寬度 w-[Npx](WM w-[640px] 丟失 min() viewport clamp 實證;w-[min(...)]/var()/% 不攔)
|
|
50
|
+
# (d) grid-template 固定 px 欄寬:grid-cols-[...Npx...](WM mini-table 88px/72px 黏死實證)
|
|
51
|
+
MAGIC_LINES=$(echo "$NEW_CONTENT" | grep -nE '\b(p|px|py|pt|pb|pl|pr|ps|pe|gap|space-x|space-y|m|mx|my|mt|mb|ml|mr|ms|me)-(0\.5|[1-9][0-9]?(\.[0-9])?)\b|\b(p|px|py|pt|pb|pl|pr|ps|pe|gap|m|mx|my|mt|mb|ml|mr|ms|me)-\[[0-9]+(\.[0-9]+)?px\]|\bw-\[[0-9]+(\.[0-9]+)?px\]|\bgrid-(cols|rows)-\[[^]]*[0-9]+px[^]]*\]')
|
|
52
|
+
|
|
53
|
+
# ── 幻覺 token 攔截(2026-07-08 WM 戰役:app 自創 var(--layout-space-distant) → CSS 未定義
|
|
54
|
+
# silent 解析 0 → 區塊全黏死,PDF「全部擠在一起」根因之一。白名單 = loose|tight|bottom
|
|
55
|
+
# (layoutSpace.css SSOT 僅此三顆;新增 tier 走 DS token 流程,不在 consumer 端發明)──
|
|
56
|
+
PHANTOM=$(echo "$NEW_CONTENT" | grep -nE 'var\(--layout-space-[a-z-]+\)' | grep -vE 'var\(--layout-space-(loose|tight|bottom)\)' || true)
|
|
57
|
+
if [ -n "$PHANTOM" ]; then
|
|
58
|
+
cat >&2 <<EOF_PH
|
|
59
|
+
🚨 [P0 BLOCKER] 幻覺 layout-space token(CSS 未定義 = silent 0 = 佈局黏死):
|
|
60
|
+
$PHANTOM
|
|
61
|
+
→ 白名單只有 --layout-space-{loose,tight,bottom}(layoutSpace.css SSOT)。
|
|
62
|
+
要更大一級間距 → 用 loose;真需新 tier → 走 DS token 新增流程(layoutSpace.spec),禁 consumer 端自創。
|
|
63
|
+
Anchor:2026-07-08 WM --layout-space-distant ×4 處,整個 detail modal 區塊黏死。
|
|
64
|
+
EOF_PH
|
|
65
|
+
exit 2
|
|
66
|
+
fi
|
|
67
|
+
|
|
68
|
+
# 2026-07-10 批次 A:divider 幾何 canonical(h-6 mx-1 / h-5 mx-1,action-bar.spec.md「分隔線幾何」)
|
|
69
|
+
# 是**正解**,不該被 magic-number 攔(原本懲罰 canonical 修法、放行違規 = 治理自我打架)。
|
|
70
|
+
MAGIC_LINES=$(echo "$MAGIC_LINES" | grep -vE '(Separator|ButtonDivider)' || true)
|
|
47
71
|
|
|
48
72
|
if [ -z "$MAGIC_LINES" ]; then
|
|
49
73
|
exit 0
|
|
@@ -82,9 +106,9 @@ $(echo -e "$UNJUSTIFIED" | sed 's/^/ /' | head -10)
|
|
|
82
106
|
per app-shell.spec.md L205-219 + layoutSpace.spec.md SSOT:consumer content 必遵循
|
|
83
107
|
layoutSpace 6 條規則 + 親疏 3 級,**禁** 硬寫 Tailwind magic numbers。改用:
|
|
84
108
|
p-[var(--layout-space-loose)] /* 16px 規則 1A/1B chrome / wrap */
|
|
85
|
-
|
|
86
|
-
gap-[var(--layout-space-
|
|
87
|
-
|
|
109
|
+
gap-[var(--layout-space-tight)] /* 12px 規則 3 親 gap(跨範疇相關) */
|
|
110
|
+
gap-[var(--layout-space-loose)] /* 16px 規則 3 疏 gap(跨範疇 parallel;lg 密度自動 24px) */
|
|
111
|
+
(token 全集 = loose/tight/bottom,無其他級 — 自創 --layout-space-distant 類 = 幻覺 token,本 hook 會攔)
|
|
88
112
|
|
|
89
113
|
修法 2 選 1:
|
|
90
114
|
(a) 改 token:換成 var(--layout-space-N) N∈{loose,tight} family per 6 規則 + 親疏 3 級
|
|
@@ -135,6 +135,24 @@ if [ -n "$HITS_PRIMITIVE" ]; then
|
|
|
135
135
|
VIOLATIONS="${VIOLATIONS}\n [primitive color as utility] $(echo "$HITS_PRIMITIVE" | tr '\n' ' ')"
|
|
136
136
|
fi
|
|
137
137
|
|
|
138
|
+
# 10) 幻覺 semantic state 尾綴(2026-07-10 批次 A C1:WM hover:bg-neutral-hovered ×3 檔 silent no-op 實證。
|
|
139
|
+
# 真尾綴只有 -hover/-active/-selected(+ selected-hover/selected-active);過去式/-pressed 全幻覺,
|
|
140
|
+
# Tailwind 靜默不生成 → hover 無效果、build 不報錯)
|
|
141
|
+
HITS_PHANTOM_STATE=$(echo "$NEW_CONTENT" | grep -oE "\b(hover:|active:|focus:)?(bg|text|border|ring)-[a-z][a-z-]*-(hovered|actived|focussed|pressed)\b" | sort -u || true)
|
|
142
|
+
if [ -n "$HITS_PHANTOM_STATE" ]; then
|
|
143
|
+
VIOLATIONS="${VIOLATIONS}\n [phantom state suffix(silent no-op)] $(echo "$HITS_PHANTOM_STATE" | tr '\n' ' ')"
|
|
144
|
+
fi
|
|
145
|
+
|
|
146
|
+
# 11) micro gap(<4px)+ registry fraction width(#71 2026-07-11 接線)——
|
|
147
|
+
# registry micro_gap / fraction 的 rationale 明寫「flag for human review」(非硬禁)→ 走
|
|
148
|
+
# SOFT WARN 非 block(避免誤殺 Calendar/Chart/FileViewer/Carousel 等 4 個既有合法 micro-gap 用途)。
|
|
149
|
+
# fraction 用 registry 明列 14 條(w-1/3.. 不含 w-1/2 = 合法留白),避免廣捕 w-N/M 誤殺。
|
|
150
|
+
HITS_MICROGAP=$(echo "$NEW_CONTENT" | grep -oE "\bgap-(px|0\.5|1\.5)\b" | sort -u || true)
|
|
151
|
+
HITS_FRACTION=$(echo "$NEW_CONTENT" | grep -oE "\bw-(1/3|2/3|1/4|3/4|1/5|2/5|3/5|4/5|1/6|5/6|1/12|5/12|7/12|11/12)\b" | sort -u || true)
|
|
152
|
+
SOFT_NOTES=""
|
|
153
|
+
[ -n "$HITS_MICROGAP" ] && SOFT_NOTES="${SOFT_NOTES}\n [micro gap <4px] $(echo "$HITS_MICROGAP" | tr '\n' ' ')"
|
|
154
|
+
[ -n "$HITS_FRACTION" ] && SOFT_NOTES="${SOFT_NOTES}\n [fraction width] $(echo "$HITS_FRACTION" | tr '\n' ' ')"
|
|
155
|
+
|
|
138
156
|
if [ -n "$VIOLATIONS" ]; then
|
|
139
157
|
cat >&2 <<'EOF_HEAD'
|
|
140
158
|
|
|
@@ -160,7 +178,17 @@ EOF_HEAD
|
|
|
160
178
|
EOF_BODY
|
|
161
179
|
# P0 BLOCKER(2026-07-07 治理進化方向 2:對齊 user 2026-05-27「SSOT canonical 必 P0」doctrine;
|
|
162
180
|
# 檔級豁免(anatomy/principles stories / token 自家)+ 行級 escape 已備,升級不增誤殺)
|
|
181
|
+
# micro-gap/fraction soft note 附在硬 block 訊息尾(一起讓 AI 看到,但不影響 exit code 判定)
|
|
182
|
+
[ -n "$SOFT_NOTES" ] && { echo "" >&2; echo " (另有 soft flag,非 block 原因):" >&2; echo -e "$SOFT_NOTES" >&2; }
|
|
163
183
|
exit 2
|
|
164
184
|
fi
|
|
165
185
|
|
|
186
|
+
# 只有 micro-gap / fraction soft note(無硬 violation)→ WARN 非 block(registry rationale「flag for human review」)
|
|
187
|
+
if [ -n "$SOFT_NOTES" ]; then
|
|
188
|
+
echo "" >&2
|
|
189
|
+
echo "⚠️ Dim 47 soft flag(registry micro_gap / fraction「flag for human review」— 非 block,#71 接線):" >&2
|
|
190
|
+
echo -e "$SOFT_NOTES" >&2
|
|
191
|
+
echo " <4px gap / registry fraction width 常違反 item-anatomy slot 幾何 / layout SSOT → 人工 review。合理則行尾 @token-registry-ok: <rationale>。" >&2
|
|
192
|
+
fi
|
|
193
|
+
|
|
166
194
|
exit 0
|
|
@@ -11,6 +11,8 @@
|
|
|
11
11
|
# 第一期 WARN(exit 0):存量 10 檔(dry-run 2026-07-07,DateGrid 家族為主)未清,
|
|
12
12
|
# 誤殺率驗證期後升 P0(對齊 handcraft 偵測同節奏;roadmap 方向 1 KPI 隨頻回顧)。
|
|
13
13
|
|
|
14
|
+
source "$(dirname "$0")/_log-fire.sh" 2>/dev/null && log_hook_fire
|
|
15
|
+
|
|
14
16
|
set -euo pipefail
|
|
15
17
|
INPUT=$(cat)
|
|
16
18
|
TOOL=$(echo "$INPUT" | jq -r '.tool_name // ""')
|