luxen-ui 0.5.0 → 0.6.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 +91 -0
- package/bin/cli.mjs +527 -0
- package/cdn/chunks/lit.js.map +1 -1
- package/cdn/config.d.ts +82 -0
- package/cdn/config.d.ts.map +1 -0
- package/cdn/config.js +0 -0
- package/cdn/custom-elements.json +3139 -3038
- package/cdn/define.d.ts +1 -1
- package/cdn/define.d.ts.map +1 -1
- package/cdn/define.js.map +1 -1
- package/cdn/elements/avatar/avatar.d.ts +1 -1
- package/cdn/elements/avatar/avatar.d.ts.map +1 -1
- package/cdn/elements/avatar/avatar.js.map +1 -1
- package/cdn/elements/avatar/index.d.ts +1 -7
- package/cdn/elements/avatar/index.d.ts.map +1 -1
- package/cdn/elements/avatar/index.js.map +1 -1
- package/cdn/elements/badge/badge.d.ts +7 -4
- package/cdn/elements/badge/badge.d.ts.map +1 -1
- package/cdn/elements/badge/badge.js.map +1 -1
- package/cdn/elements/badge/index.d.ts +1 -7
- package/cdn/elements/badge/index.d.ts.map +1 -1
- package/cdn/elements/badge/index.js.map +1 -1
- package/cdn/elements/carousel/carousel.d.ts +1 -1
- package/cdn/elements/carousel/carousel.d.ts.map +1 -1
- package/cdn/elements/carousel/carousel.js.map +1 -1
- package/cdn/elements/carousel/index.d.ts +1 -7
- package/cdn/elements/carousel/index.d.ts.map +1 -1
- package/cdn/elements/carousel/index.js.map +1 -1
- package/cdn/elements/carousel-item/carousel-item.d.ts +1 -1
- package/cdn/elements/carousel-item/carousel-item.d.ts.map +1 -1
- package/cdn/elements/carousel-item/carousel-item.js.map +1 -1
- package/cdn/elements/carousel-item/index.d.ts +1 -7
- package/cdn/elements/carousel-item/index.d.ts.map +1 -1
- package/cdn/elements/carousel-item/index.js.map +1 -1
- package/cdn/elements/dialog/dialog.d.ts +1 -1
- package/cdn/elements/dialog/dialog.d.ts.map +1 -1
- package/cdn/elements/dialog/dialog.js.map +1 -1
- package/cdn/elements/dialog/index.d.ts +1 -7
- package/cdn/elements/dialog/index.d.ts.map +1 -1
- package/cdn/elements/dialog/index.js.map +1 -1
- package/cdn/elements/divider/divider.d.ts +1 -1
- package/cdn/elements/divider/divider.d.ts.map +1 -1
- package/cdn/elements/divider/divider.js.map +1 -1
- package/cdn/elements/divider/index.d.ts +1 -7
- package/cdn/elements/divider/index.d.ts.map +1 -1
- package/cdn/elements/divider/index.js.map +1 -1
- package/cdn/elements/drawer/drawer.d.ts +1 -1
- package/cdn/elements/drawer/drawer.d.ts.map +1 -1
- package/cdn/elements/drawer/drawer.js.map +1 -1
- package/cdn/elements/drawer/index.d.ts +1 -7
- package/cdn/elements/drawer/index.d.ts.map +1 -1
- package/cdn/elements/drawer/index.js.map +1 -1
- package/cdn/elements/dropdown/dropdown.d.ts +1 -1
- package/cdn/elements/dropdown/dropdown.d.ts.map +1 -1
- package/cdn/elements/dropdown/dropdown.js.map +1 -1
- package/cdn/elements/dropdown/index.d.ts +1 -7
- package/cdn/elements/dropdown/index.d.ts.map +1 -1
- package/cdn/elements/dropdown/index.js.map +1 -1
- package/cdn/elements/dropdown-item/dropdown-item.d.ts +1 -1
- package/cdn/elements/dropdown-item/dropdown-item.d.ts.map +1 -1
- package/cdn/elements/dropdown-item/dropdown-item.js.map +1 -1
- package/cdn/elements/dropdown-item/index.d.ts +1 -7
- package/cdn/elements/dropdown-item/index.d.ts.map +1 -1
- package/cdn/elements/dropdown-item/index.js.map +1 -1
- package/cdn/elements/icon/icon.d.ts +1 -1
- package/cdn/elements/icon/icon.d.ts.map +1 -1
- package/cdn/elements/icon/icon.js.map +1 -1
- package/cdn/elements/icon/index.d.ts +1 -7
- package/cdn/elements/icon/index.d.ts.map +1 -1
- package/cdn/elements/icon/index.js.map +1 -1
- package/cdn/elements/input-otp/index.d.ts +1 -7
- package/cdn/elements/input-otp/index.d.ts.map +1 -1
- package/cdn/elements/input-otp/index.js.map +1 -1
- package/cdn/elements/input-otp/input-otp.d.ts +1 -1
- package/cdn/elements/input-otp/input-otp.d.ts.map +1 -1
- package/cdn/elements/input-otp/input-otp.js.map +1 -1
- package/cdn/elements/input-stepper/index.d.ts +1 -7
- package/cdn/elements/input-stepper/index.d.ts.map +1 -1
- package/cdn/elements/input-stepper/index.js.map +1 -1
- package/cdn/elements/input-stepper/input-stepper.d.ts +1 -1
- package/cdn/elements/input-stepper/input-stepper.d.ts.map +1 -1
- package/cdn/elements/input-stepper/input-stepper.js.map +1 -1
- package/cdn/elements/popover/index.d.ts +1 -7
- package/cdn/elements/popover/index.d.ts.map +1 -1
- package/cdn/elements/popover/index.js.map +1 -1
- package/cdn/elements/popover/popover.d.ts +1 -1
- package/cdn/elements/popover/popover.d.ts.map +1 -1
- package/cdn/elements/popover/popover.js.map +1 -1
- package/cdn/elements/rating/index.d.ts +1 -7
- package/cdn/elements/rating/index.d.ts.map +1 -1
- package/cdn/elements/rating/index.js.map +1 -1
- package/cdn/elements/rating/rating.d.ts +1 -1
- package/cdn/elements/rating/rating.d.ts.map +1 -1
- package/cdn/elements/rating/rating.js.map +1 -1
- package/cdn/elements/skeleton/index.d.ts +1 -7
- package/cdn/elements/skeleton/index.d.ts.map +1 -1
- package/cdn/elements/skeleton/index.js.map +1 -1
- package/cdn/elements/skeleton/skeleton.d.ts +1 -1
- package/cdn/elements/skeleton/skeleton.d.ts.map +1 -1
- package/cdn/elements/skeleton/skeleton.js.map +1 -1
- package/cdn/elements/spinner/index.d.ts +1 -7
- package/cdn/elements/spinner/index.d.ts.map +1 -1
- package/cdn/elements/spinner/index.js.map +1 -1
- package/cdn/elements/spinner/spinner.d.ts +1 -1
- package/cdn/elements/spinner/spinner.d.ts.map +1 -1
- package/cdn/elements/spinner/spinner.js.map +1 -1
- package/cdn/elements/sticky-bar/index.d.ts +1 -7
- package/cdn/elements/sticky-bar/index.d.ts.map +1 -1
- package/cdn/elements/sticky-bar/index.js.map +1 -1
- package/cdn/elements/sticky-bar/sticky-bar.d.ts +1 -1
- package/cdn/elements/sticky-bar/sticky-bar.d.ts.map +1 -1
- package/cdn/elements/sticky-bar/sticky-bar.js.map +1 -1
- package/cdn/elements/stories/index.d.ts +1 -7
- package/cdn/elements/stories/index.d.ts.map +1 -1
- package/cdn/elements/stories/index.js.map +1 -1
- package/cdn/elements/stories/stories.d.ts +2 -7
- package/cdn/elements/stories/stories.d.ts.map +1 -1
- package/cdn/elements/stories/stories.js.map +1 -1
- package/cdn/elements/stories-viewer/index.d.ts +1 -7
- package/cdn/elements/stories-viewer/index.d.ts.map +1 -1
- package/cdn/elements/stories-viewer/index.js.map +1 -1
- package/cdn/elements/stories-viewer/stories-viewer.d.ts +3 -8
- package/cdn/elements/stories-viewer/stories-viewer.d.ts.map +1 -1
- package/cdn/elements/stories-viewer/stories-viewer.js.map +1 -1
- package/cdn/elements/story/index.d.ts +1 -7
- package/cdn/elements/story/index.d.ts.map +1 -1
- package/cdn/elements/story/index.js.map +1 -1
- package/cdn/elements/story/story.d.ts +2 -7
- package/cdn/elements/story/story.d.ts.map +1 -1
- package/cdn/elements/story/story.js.map +1 -1
- package/cdn/elements/tabs/index.d.ts +1 -7
- package/cdn/elements/tabs/index.d.ts.map +1 -1
- package/cdn/elements/tabs/index.js.map +1 -1
- package/cdn/elements/tabs/tabs.d.ts +1 -1
- package/cdn/elements/tabs/tabs.d.ts.map +1 -1
- package/cdn/elements/tabs/tabs.js.map +1 -1
- package/cdn/elements/toast/index.d.ts +1 -8
- package/cdn/elements/toast/index.d.ts.map +1 -1
- package/cdn/elements/toast/index.js.map +1 -1
- package/cdn/elements/toast/toast.d.ts +7 -5
- package/cdn/elements/toast/toast.d.ts.map +1 -1
- package/cdn/elements/toast/toast.js +1 -1
- package/cdn/elements/toast/toast.js.map +1 -1
- package/cdn/elements/tooltip/index.d.ts +1 -7
- package/cdn/elements/tooltip/index.d.ts.map +1 -1
- package/cdn/elements/tooltip/index.js.map +1 -1
- package/cdn/elements/tooltip/tooltip.d.ts +1 -1
- package/cdn/elements/tooltip/tooltip.d.ts.map +1 -1
- package/cdn/elements/tooltip/tooltip.js.map +1 -1
- package/cdn/elements/tree/index.d.ts +1 -7
- package/cdn/elements/tree/index.d.ts.map +1 -1
- package/cdn/elements/tree/index.js.map +1 -1
- package/cdn/elements/tree/tree.d.ts +2 -2
- package/cdn/elements/tree/tree.d.ts.map +1 -1
- package/cdn/elements/tree/tree.js.map +1 -1
- package/cdn/elements/tree-item/index.d.ts +1 -7
- package/cdn/elements/tree-item/index.d.ts.map +1 -1
- package/cdn/elements/tree-item/index.js.map +1 -1
- package/cdn/elements/tree-item/tree-item.d.ts +1 -1
- package/cdn/elements/tree-item/tree-item.d.ts.map +1 -1
- package/cdn/elements/tree-item/tree-item.js.map +1 -1
- package/cdn/index.d.ts +7 -5
- package/cdn/index.d.ts.map +1 -1
- package/cdn/index.js +1 -1
- package/cdn/shared/controllers/popover.js.map +1 -1
- package/cdn/shared/luxen-form-associated-element.d.ts +1 -1
- package/cdn/shared/luxen-form-associated-element.d.ts.map +1 -1
- package/cdn/shared/luxen-form-associated-element.js.map +1 -1
- package/cdn/standalone.css +3581 -0
- package/cdn/standalone.js +9301 -0
- package/cdn/standalone.js.map +1 -0
- package/cdn/styles/base.css +728 -0
- package/cdn/styles/elements/badge.css +6 -6
- package/cdn/styles/elements/button.css +9 -9
- package/cdn/styles/elements/close-button/circle.css +3 -3
- package/cdn/styles/elements/close-button/ring.css +1 -1
- package/cdn/styles/elements/close-button/square.css +3 -3
- package/cdn/styles/elements/disclosure.css +2 -2
- package/cdn/styles/elements/divider.css +3 -3
- package/cdn/styles/elements/input-otp.css +1 -1
- package/cdn/styles/elements/input-stepper/default.css +1 -1
- package/cdn/styles/elements/input-stepper/rounded.css +2 -2
- package/cdn/styles/elements/kbd.css +3 -3
- package/cdn/styles/elements/select.css +2 -2
- package/cdn/styles/elements/skeleton.css +4 -4
- package/cdn/styles/elements/stories.css +1 -1
- package/cdn/styles/elements/story.css +37 -37
- package/cdn/styles/elements/tabs/enclosed.css +6 -6
- package/cdn/styles/elements/tabs/line.css +4 -4
- package/cdn/styles/{index.css → preset.css} +223 -145
- package/cdn/styles/tailwind/preset.css +575 -0
- package/cdn/styles/tokens/aliases.css +103 -0
- package/cdn/styles/tokens/palette.css +250 -0
- package/cdn/styles/tokens/primitives.css +141 -0
- package/cdn/styles/tokens.css +239 -0
- package/dist/config.d.ts +82 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +28 -0
- package/dist/css/base.css +728 -0
- package/dist/css/elements/badge.css +6 -6
- package/dist/css/elements/button.css +9 -9
- package/dist/css/elements/close-button/circle.css +3 -3
- package/dist/css/elements/close-button/ring.css +1 -1
- package/dist/css/elements/close-button/square.css +3 -3
- package/dist/css/elements/disclosure.css +2 -2
- package/dist/css/elements/divider.css +3 -3
- package/dist/css/elements/input-otp.css +1 -1
- package/dist/css/elements/input-stepper/default.css +1 -1
- package/dist/css/elements/input-stepper/rounded.css +2 -2
- package/dist/css/elements/kbd.css +3 -3
- package/dist/css/elements/select.css +2 -2
- package/dist/css/elements/skeleton.css +4 -4
- package/dist/css/elements/stories.css +1 -1
- package/dist/css/elements/story.css +37 -37
- package/dist/css/elements/tabs/enclosed.css +6 -6
- package/dist/css/elements/tabs/line.css +4 -4
- package/dist/css/{index.css → preset.css} +223 -145
- package/dist/css/tailwind/preset.css +575 -0
- package/dist/css/tokens/aliases.css +103 -0
- package/dist/css/tokens/palette.css +250 -0
- package/dist/css/tokens/primitives.css +141 -0
- package/dist/css/tokens.css +239 -0
- package/dist/custom-elements.json +3139 -3038
- package/dist/define.d.ts +1 -1
- package/dist/define.d.ts.map +1 -1
- package/dist/define.js +1 -1
- package/dist/elements/avatar/avatar.d.ts +1 -1
- package/dist/elements/avatar/avatar.d.ts.map +1 -1
- package/dist/elements/avatar/avatar.js +2 -2
- package/dist/elements/avatar/index.d.ts +1 -7
- package/dist/elements/avatar/index.d.ts.map +1 -1
- package/dist/elements/avatar/index.js +3 -3
- package/dist/elements/badge/badge.d.ts +7 -4
- package/dist/elements/badge/badge.d.ts.map +1 -1
- package/dist/elements/badge/badge.js +1 -1
- package/dist/elements/badge/index.d.ts +1 -7
- package/dist/elements/badge/index.d.ts.map +1 -1
- package/dist/elements/badge/index.js +3 -3
- package/dist/elements/carousel/carousel.d.ts +1 -1
- package/dist/elements/carousel/carousel.d.ts.map +1 -1
- package/dist/elements/carousel/carousel.js +2 -2
- package/dist/elements/carousel/index.d.ts +1 -7
- package/dist/elements/carousel/index.d.ts.map +1 -1
- package/dist/elements/carousel/index.js +3 -3
- package/dist/elements/carousel-item/carousel-item.d.ts +1 -1
- package/dist/elements/carousel-item/carousel-item.d.ts.map +1 -1
- package/dist/elements/carousel-item/carousel-item.js +2 -2
- package/dist/elements/carousel-item/index.d.ts +1 -7
- package/dist/elements/carousel-item/index.d.ts.map +1 -1
- package/dist/elements/carousel-item/index.js +3 -3
- package/dist/elements/dialog/dialog.d.ts +1 -1
- package/dist/elements/dialog/dialog.d.ts.map +1 -1
- package/dist/elements/dialog/dialog.js +3 -3
- package/dist/elements/dialog/index.d.ts +1 -7
- package/dist/elements/dialog/index.d.ts.map +1 -1
- package/dist/elements/dialog/index.js +3 -3
- package/dist/elements/divider/divider.d.ts +1 -1
- package/dist/elements/divider/divider.d.ts.map +1 -1
- package/dist/elements/divider/divider.js +1 -1
- package/dist/elements/divider/index.d.ts +1 -7
- package/dist/elements/divider/index.d.ts.map +1 -1
- package/dist/elements/divider/index.js +3 -3
- package/dist/elements/drawer/drawer.d.ts +1 -1
- package/dist/elements/drawer/drawer.d.ts.map +1 -1
- package/dist/elements/drawer/drawer.js +3 -3
- package/dist/elements/drawer/index.d.ts +1 -7
- package/dist/elements/drawer/index.d.ts.map +1 -1
- package/dist/elements/drawer/index.js +3 -3
- package/dist/elements/dropdown/dropdown.d.ts +1 -1
- package/dist/elements/dropdown/dropdown.d.ts.map +1 -1
- package/dist/elements/dropdown/dropdown.js +4 -4
- package/dist/elements/dropdown/index.d.ts +1 -7
- package/dist/elements/dropdown/index.d.ts.map +1 -1
- package/dist/elements/dropdown/index.js +3 -3
- package/dist/elements/dropdown-item/dropdown-item.d.ts +1 -1
- package/dist/elements/dropdown-item/dropdown-item.d.ts.map +1 -1
- package/dist/elements/dropdown-item/dropdown-item.js +2 -2
- package/dist/elements/dropdown-item/index.d.ts +1 -7
- package/dist/elements/dropdown-item/index.d.ts.map +1 -1
- package/dist/elements/dropdown-item/index.js +3 -3
- package/dist/elements/icon/icon.d.ts +1 -1
- package/dist/elements/icon/icon.d.ts.map +1 -1
- package/dist/elements/icon/icon.js +2 -2
- package/dist/elements/icon/index.d.ts +1 -7
- package/dist/elements/icon/index.d.ts.map +1 -1
- package/dist/elements/icon/index.js +3 -3
- package/dist/elements/input-otp/index.d.ts +1 -7
- package/dist/elements/input-otp/index.d.ts.map +1 -1
- package/dist/elements/input-otp/index.js +3 -3
- package/dist/elements/input-otp/input-otp.d.ts +1 -1
- package/dist/elements/input-otp/input-otp.d.ts.map +1 -1
- package/dist/elements/input-otp/input-otp.js +1 -1
- package/dist/elements/input-stepper/index.d.ts +1 -7
- package/dist/elements/input-stepper/index.d.ts.map +1 -1
- package/dist/elements/input-stepper/index.js +3 -3
- package/dist/elements/input-stepper/input-stepper.d.ts +1 -1
- package/dist/elements/input-stepper/input-stepper.d.ts.map +1 -1
- package/dist/elements/input-stepper/input-stepper.js +1 -1
- package/dist/elements/popover/index.d.ts +1 -7
- package/dist/elements/popover/index.d.ts.map +1 -1
- package/dist/elements/popover/index.js +3 -3
- package/dist/elements/popover/popover.d.ts +1 -1
- package/dist/elements/popover/popover.d.ts.map +1 -1
- package/dist/elements/popover/popover.js +4 -4
- package/dist/elements/rating/index.d.ts +1 -7
- package/dist/elements/rating/index.d.ts.map +1 -1
- package/dist/elements/rating/index.js +3 -3
- package/dist/elements/rating/rating.d.ts +1 -1
- package/dist/elements/rating/rating.d.ts.map +1 -1
- package/dist/elements/rating/rating.js +2 -2
- package/dist/elements/skeleton/index.d.ts +1 -7
- package/dist/elements/skeleton/index.d.ts.map +1 -1
- package/dist/elements/skeleton/index.js +3 -3
- package/dist/elements/skeleton/skeleton.d.ts +1 -1
- package/dist/elements/skeleton/skeleton.d.ts.map +1 -1
- package/dist/elements/skeleton/skeleton.js +1 -1
- package/dist/elements/spinner/index.d.ts +1 -7
- package/dist/elements/spinner/index.d.ts.map +1 -1
- package/dist/elements/spinner/index.js +3 -3
- package/dist/elements/spinner/spinner.d.ts +1 -1
- package/dist/elements/spinner/spinner.d.ts.map +1 -1
- package/dist/elements/spinner/spinner.js +2 -2
- package/dist/elements/sticky-bar/index.d.ts +1 -7
- package/dist/elements/sticky-bar/index.d.ts.map +1 -1
- package/dist/elements/sticky-bar/index.js +3 -3
- package/dist/elements/sticky-bar/sticky-bar.d.ts +1 -1
- package/dist/elements/sticky-bar/sticky-bar.d.ts.map +1 -1
- package/dist/elements/sticky-bar/sticky-bar.js +2 -2
- package/dist/elements/stories/index.d.ts +1 -7
- package/dist/elements/stories/index.d.ts.map +1 -1
- package/dist/elements/stories/index.js +3 -3
- package/dist/elements/stories/stories.d.ts +2 -7
- package/dist/elements/stories/stories.d.ts.map +1 -1
- package/dist/elements/stories/stories.js +1 -1
- package/dist/elements/stories-viewer/index.d.ts +1 -7
- package/dist/elements/stories-viewer/index.d.ts.map +1 -1
- package/dist/elements/stories-viewer/index.js +3 -3
- package/dist/elements/stories-viewer/stories-viewer.d.ts +3 -8
- package/dist/elements/stories-viewer/stories-viewer.d.ts.map +1 -1
- package/dist/elements/stories-viewer/stories-viewer.js +2 -2
- package/dist/elements/story/index.d.ts +1 -7
- package/dist/elements/story/index.d.ts.map +1 -1
- package/dist/elements/story/index.js +3 -3
- package/dist/elements/story/story.d.ts +2 -7
- package/dist/elements/story/story.d.ts.map +1 -1
- package/dist/elements/story/story.js +2 -2
- package/dist/elements/tabs/index.d.ts +1 -7
- package/dist/elements/tabs/index.d.ts.map +1 -1
- package/dist/elements/tabs/index.js +3 -3
- package/dist/elements/tabs/tabs.d.ts +1 -1
- package/dist/elements/tabs/tabs.d.ts.map +1 -1
- package/dist/elements/tabs/tabs.js +2 -2
- package/dist/elements/toast/index.d.ts +1 -8
- package/dist/elements/toast/index.d.ts.map +1 -1
- package/dist/elements/toast/index.js +3 -3
- package/dist/elements/toast/toast.d.ts +7 -5
- package/dist/elements/toast/toast.d.ts.map +1 -1
- package/dist/elements/toast/toast.js +2 -4
- package/dist/elements/tooltip/index.d.ts +1 -7
- package/dist/elements/tooltip/index.d.ts.map +1 -1
- package/dist/elements/tooltip/index.js +3 -3
- package/dist/elements/tooltip/tooltip.d.ts +1 -1
- package/dist/elements/tooltip/tooltip.d.ts.map +1 -1
- package/dist/elements/tooltip/tooltip.js +4 -4
- package/dist/elements/tree/index.d.ts +1 -7
- package/dist/elements/tree/index.d.ts.map +1 -1
- package/dist/elements/tree/index.js +3 -3
- package/dist/elements/tree/tree.d.ts +2 -2
- package/dist/elements/tree/tree.d.ts.map +1 -1
- package/dist/elements/tree/tree.js +3 -3
- package/dist/elements/tree-item/index.d.ts +1 -7
- package/dist/elements/tree-item/index.d.ts.map +1 -1
- package/dist/elements/tree-item/index.js +3 -3
- package/dist/elements/tree-item/tree-item.d.ts +1 -1
- package/dist/elements/tree-item/tree-item.d.ts.map +1 -1
- package/dist/elements/tree-item/tree-item.js +3 -3
- package/dist/index.d.ts +7 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -4
- package/dist/shared/luxen-form-associated-element.d.ts +1 -1
- package/dist/shared/luxen-form-associated-element.d.ts.map +1 -1
- package/dist/shared/luxen-form-associated-element.js +1 -1
- package/dist/{skills/luxen-ui/references → templates/elements}/badge.md +8 -8
- package/dist/{skills/luxen-ui/references → templates/elements}/button.md +1 -1
- package/elements.json +229 -0
- package/package.json +45 -16
- package/postcss-plugin-prefix.d.ts +9 -0
- package/templates/SKILL.md.tpl +34 -0
- package/templates/claude-design.md.tpl +42 -0
- package/templates/integration.md.tpl +53 -0
- package/templates/mockups.md.tpl +64 -0
- package/vite-plugin.ts +265 -3
- package/dist/skills/luxen-ui/SKILL.md +0 -83
- /package/dist/{skills/luxen-ui/references → templates/elements}/avatar.md +0 -0
- /package/dist/{skills/luxen-ui/references → templates/elements}/close-button.md +0 -0
- /package/dist/{skills/luxen-ui/references → templates/elements}/dialog.md +0 -0
- /package/dist/{skills/luxen-ui/references → templates/elements}/drawer.md +0 -0
- /package/dist/{skills/luxen-ui/references → templates/elements}/progress.md +0 -0
- /package/dist/{skills/luxen-ui/references → templates/elements}/select.md +0 -0
- /package/dist/{skills/luxen-ui/references → templates/elements}/sticky-bar.md +0 -0
- /package/dist/{skills/luxen-ui/references → templates/elements}/toast.md +0 -0
- /package/dist/{skills/luxen-ui/references → templates/elements}/tree.md +0 -0
package/README.md
CHANGED
|
@@ -77,6 +77,97 @@ import 'luxen-ui/css/button';
|
|
|
77
77
|
<l-tooltip for="my-button">Hello world</l-tooltip>
|
|
78
78
|
```
|
|
79
79
|
|
|
80
|
+
## TypeScript (prefix-aware)
|
|
81
|
+
|
|
82
|
+
Element classes are exported under `luxen-ui/<name>/element` as side-effect-free
|
|
83
|
+
type entries:
|
|
84
|
+
|
|
85
|
+
```ts
|
|
86
|
+
import type { Badge, BadgeVariant } from 'luxen-ui/badge/element';
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
The package does **not** ship a `HTMLElementTagNameMap` augmentation by
|
|
90
|
+
default — consumers own that file so it always reflects the prefix they
|
|
91
|
+
actually use (default `l-*` or rebranded). The Vite plugin can write it for
|
|
92
|
+
you:
|
|
93
|
+
|
|
94
|
+
```ts
|
|
95
|
+
// vite.config.ts (or nuxt.config.ts)
|
|
96
|
+
import luxen from 'luxen-ui/vite-plugin';
|
|
97
|
+
|
|
98
|
+
export default defineConfig({
|
|
99
|
+
plugins: [
|
|
100
|
+
luxen({
|
|
101
|
+
elementPrefix: 'pulse',
|
|
102
|
+
cssPrefix: 'pulse',
|
|
103
|
+
emitTypes: 'types/luxen.d.ts', // ← writes the file on first build
|
|
104
|
+
}),
|
|
105
|
+
],
|
|
106
|
+
});
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
The generated `types/luxen.d.ts` imports the class types from the
|
|
110
|
+
`*/element` subpaths and augments `HTMLElementTagNameMap` for every Luxen
|
|
111
|
+
element under your prefix. Once written:
|
|
112
|
+
|
|
113
|
+
- **You own the file.** Edit it freely — drop elements you don't use, or
|
|
114
|
+
add ones from your own custom element set.
|
|
115
|
+
- **The plugin never overwrites** an existing file silently. Pass
|
|
116
|
+
`emitTypes: { path: 'types/luxen.d.ts', force: true }` to regenerate.
|
|
117
|
+
- **Drift detection.** If your `elementPrefix` later changes, the plugin
|
|
118
|
+
logs a warning so you can regenerate.
|
|
119
|
+
|
|
120
|
+
Need a subset only?
|
|
121
|
+
|
|
122
|
+
```ts
|
|
123
|
+
luxen({
|
|
124
|
+
elementPrefix: 'pulse',
|
|
125
|
+
emitTypes: { path: 'types/luxen.d.ts', elements: ['badge', 'dropdown', 'popover'] },
|
|
126
|
+
});
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### Vue / Nuxt — strict template checking
|
|
130
|
+
|
|
131
|
+
`HTMLElementTagNameMap` types the DOM side (`document.querySelector`,
|
|
132
|
+
`el.variant = …`) but Vue's template checker treats custom elements as a
|
|
133
|
+
permissive surface — typos and bad prop values are **not** flagged. Use
|
|
134
|
+
`target: 'vue'` to additionally augment Vue's `GlobalComponents`:
|
|
135
|
+
|
|
136
|
+
```ts
|
|
137
|
+
luxen({
|
|
138
|
+
elementPrefix: 'pulse',
|
|
139
|
+
cssPrefix: 'pulse',
|
|
140
|
+
emitTypes: { path: 'types/luxen.d.ts', target: 'vue' },
|
|
141
|
+
});
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Then enable strict templates in your tsconfig (for Nuxt, set it via
|
|
145
|
+
`nuxt.config.ts` → `typescript.tsConfig.vueCompilerOptions`):
|
|
146
|
+
|
|
147
|
+
```jsonc
|
|
148
|
+
{
|
|
149
|
+
"vueCompilerOptions": { "strictTemplates": true },
|
|
150
|
+
}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Now `<pulse-badge variant="bogus">` and `<pulse-badge typo="x">` are errors
|
|
154
|
+
in the editor and in `vue-tsc`, while autocomplete stays scoped to each
|
|
155
|
+
element's real props. The generated file also re-allows `data-*` and `slot`
|
|
156
|
+
on native elements (which `strictTemplates` would otherwise reject) and keeps
|
|
157
|
+
`@event` listeners permissive.
|
|
158
|
+
|
|
159
|
+
Installed under an npm alias? Pass `packageName` so the emitted imports
|
|
160
|
+
resolve:
|
|
161
|
+
|
|
162
|
+
```jsonc
|
|
163
|
+
// package.json
|
|
164
|
+
{ "dependencies": { "pulse-ui": "npm:luxen-ui@^0.5.0" } }
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
```ts
|
|
168
|
+
emitTypes: { path: 'types/luxen.d.ts', target: 'vue', packageName: 'pulse-ui' }
|
|
169
|
+
```
|
|
170
|
+
|
|
80
171
|
## Local Development
|
|
81
172
|
|
|
82
173
|
Requires **Node.js >= 24** and **pnpm**.
|
package/bin/cli.mjs
ADDED
|
@@ -0,0 +1,527 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/*
|
|
3
|
+
* luxen-ui CLI — two subcommands:
|
|
4
|
+
*
|
|
5
|
+
* import <noun> Copy a CSS preset / token file into the project for
|
|
6
|
+
* customization (see IMPORTABLES below).
|
|
7
|
+
*
|
|
8
|
+
* generate-skill Produce an Agent Skill folder named after your design
|
|
9
|
+
* system, with prefix and brand tokens already applied.
|
|
10
|
+
* Output is meant to be committed to your repo (e.g.
|
|
11
|
+
* `.claude/skills/<name>/`).
|
|
12
|
+
*
|
|
13
|
+
* Run `luxen-ui --help` for usage.
|
|
14
|
+
*/
|
|
15
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
16
|
+
import { dirname, join, relative, resolve } from 'node:path';
|
|
17
|
+
import { pathToFileURL, fileURLToPath } from 'node:url';
|
|
18
|
+
|
|
19
|
+
const PKG_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..');
|
|
20
|
+
const PKG_VERSION = JSON.parse(readFileSync(resolve(PKG_ROOT, 'package.json'), 'utf-8')).version;
|
|
21
|
+
|
|
22
|
+
// =============================================================================
|
|
23
|
+
// `import <noun>` — copies a Luxen preset / piece into your project.
|
|
24
|
+
// =============================================================================
|
|
25
|
+
|
|
26
|
+
const IMPORTABLES = {
|
|
27
|
+
preset: {
|
|
28
|
+
source: resolve(PKG_ROOT, 'dist/css/preset.css'),
|
|
29
|
+
defaultDest: './luxen-preset.css',
|
|
30
|
+
next: (importPath) => [
|
|
31
|
+
'Next steps:',
|
|
32
|
+
' 1. In your CSS entry, replace',
|
|
33
|
+
" @import 'luxen-ui/css/preset';",
|
|
34
|
+
' with',
|
|
35
|
+
` @import '${importPath}';`,
|
|
36
|
+
'',
|
|
37
|
+
' 2. Edit the file freely — swap any layer for your own',
|
|
38
|
+
' implementation, or import atomic pieces directly:',
|
|
39
|
+
" @import 'luxen-ui/css/base'; /* runtime only */",
|
|
40
|
+
" @import '@my-ds/colors'; /* your palette */",
|
|
41
|
+
" @import 'luxen-ui/css/tokens/aliases'; /* semantic aliases */",
|
|
42
|
+
],
|
|
43
|
+
},
|
|
44
|
+
tailwind: {
|
|
45
|
+
source: resolve(PKG_ROOT, 'dist/css/tailwind/preset.css'),
|
|
46
|
+
defaultDest: './luxen-tailwind.css',
|
|
47
|
+
next: (importPath) => [
|
|
48
|
+
'Next steps:',
|
|
49
|
+
' 1. In your CSS entry, replace',
|
|
50
|
+
" @import 'luxen-ui/tailwind/preset';",
|
|
51
|
+
' with',
|
|
52
|
+
` @import '${importPath}';`,
|
|
53
|
+
'',
|
|
54
|
+
' 2. Uncomment any extended Tailwind palette families you want to use.',
|
|
55
|
+
'',
|
|
56
|
+
' 3. Add project-specific tokens (brand colors, custom fonts, etc.)',
|
|
57
|
+
' in the same file — they coexist with Luxen tokens.',
|
|
58
|
+
],
|
|
59
|
+
},
|
|
60
|
+
'design-tokens': {
|
|
61
|
+
source: resolve(PKG_ROOT, 'dist/css/tokens/aliases.css'),
|
|
62
|
+
defaultDest: './luxen-design-tokens.css',
|
|
63
|
+
next: (importPath) => [
|
|
64
|
+
'Next steps:',
|
|
65
|
+
' 1. In your CSS entry, replace',
|
|
66
|
+
" @import 'luxen-ui/css/tokens/aliases';",
|
|
67
|
+
" or @import 'luxen-ui/css/preset'; (if you used the preset)",
|
|
68
|
+
' with the new ordering:',
|
|
69
|
+
" @import 'luxen-ui/css/base';",
|
|
70
|
+
" @import 'luxen-ui/css/tokens/primitives'; /* or your own */",
|
|
71
|
+
` @import '${importPath}';`,
|
|
72
|
+
'',
|
|
73
|
+
' 2. Edit ./luxen-design-tokens.css — every semantic alias is yours to',
|
|
74
|
+
' repurpose (text-primary, bg-fill-brand, focus-ring, …).',
|
|
75
|
+
'',
|
|
76
|
+
' 3. Tokens reference primitives via `var(--l-color-*)`. Either keep the',
|
|
77
|
+
' Luxen primitives, or remap them (e.g. to Radix UI colors) above this',
|
|
78
|
+
' import so the aliases pick up your palette.',
|
|
79
|
+
],
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
function cmdImport([noun, destArg]) {
|
|
84
|
+
if (!noun) {
|
|
85
|
+
console.error('✗ Missing noun after `import`. Try: luxen-ui import preset');
|
|
86
|
+
usageImport(1);
|
|
87
|
+
}
|
|
88
|
+
const recipe = IMPORTABLES[noun];
|
|
89
|
+
if (!recipe) {
|
|
90
|
+
console.error(`✗ Don't know how to import "${noun}".`);
|
|
91
|
+
console.error(` Available: ${Object.keys(IMPORTABLES).join(', ')}`);
|
|
92
|
+
process.exit(1);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const dest = destArg ?? recipe.defaultDest;
|
|
96
|
+
const destPath = resolve(process.cwd(), dest);
|
|
97
|
+
|
|
98
|
+
if (existsSync(destPath)) {
|
|
99
|
+
console.error(`✗ ${dest} already exists. Move it aside or pass a different path.`);
|
|
100
|
+
process.exit(1);
|
|
101
|
+
}
|
|
102
|
+
if (!existsSync(recipe.source)) {
|
|
103
|
+
console.error(`✗ Source not found at ${recipe.source}.`);
|
|
104
|
+
console.error(' Did luxen-ui finish installing? Try re-running install.');
|
|
105
|
+
process.exit(1);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const content = readFileSync(recipe.source, 'utf8');
|
|
109
|
+
writeFileSync(destPath, content, 'utf8');
|
|
110
|
+
|
|
111
|
+
const importPath = dest.startsWith('.') || dest.startsWith('/') ? dest : `./${dest}`;
|
|
112
|
+
console.log(`✓ Created ${relative(process.cwd(), destPath)}`);
|
|
113
|
+
console.log('');
|
|
114
|
+
for (const line of recipe.next(importPath)) console.log(line);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// =============================================================================
|
|
118
|
+
// `generate-skill` — produce a brand-aware Agent Skill folder.
|
|
119
|
+
// =============================================================================
|
|
120
|
+
|
|
121
|
+
const DEFAULT_CONFIG = {
|
|
122
|
+
name: 'luxen-ui',
|
|
123
|
+
displayName: 'Luxen UI',
|
|
124
|
+
description:
|
|
125
|
+
'Generate UI with Luxen UI, a CSS-first web component library. Provides CSS classes for native HTML elements and custom elements (l-badge, l-dialog, l-toast). Use when building interfaces with Luxen UI.',
|
|
126
|
+
// Prefixes use the Vite plugin convention: bare identifier without trailing
|
|
127
|
+
// dash. `elementPrefix: 'pulse'` means tags are `<pulse-badge>`, type selectors
|
|
128
|
+
// are `pulse-badge`. `cssPrefix: 'pulse'` controls `.pulse-button` classes,
|
|
129
|
+
// `--pulse-*` custom properties, and `@keyframes pulse-*`.
|
|
130
|
+
elementPrefix: 'l',
|
|
131
|
+
cssPrefix: 'l',
|
|
132
|
+
cssImportPath: 'luxen-ui/css',
|
|
133
|
+
jsImportPath: 'luxen-ui',
|
|
134
|
+
themeCss: null,
|
|
135
|
+
out: null,
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
async function cmdGenerateSkill(args) {
|
|
139
|
+
const opts = parseFlags(args);
|
|
140
|
+
const config = await loadConfig(opts.config);
|
|
141
|
+
const ctx = buildContext(config, opts);
|
|
142
|
+
|
|
143
|
+
await runGenerateSkill(ctx);
|
|
144
|
+
|
|
145
|
+
console.log(`✓ Skill generated at ${relative(process.cwd(), ctx.outDir)}`);
|
|
146
|
+
console.log('');
|
|
147
|
+
console.log('Next steps:');
|
|
148
|
+
console.log(` • Commit ${ctx.outDir} so AI agents (and the rest of your team) can read it.`);
|
|
149
|
+
console.log(` • Re-run \`luxen-ui generate-skill\` whenever luxen-ui or your tokens change.`);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function parseFlags(argv) {
|
|
153
|
+
const opts = {};
|
|
154
|
+
for (let i = 0; i < argv.length; i++) {
|
|
155
|
+
const arg = argv[i];
|
|
156
|
+
if (arg === '--config') opts.config = argv[++i];
|
|
157
|
+
else if (arg === '--out') opts.out = argv[++i];
|
|
158
|
+
else if (arg === '--element-prefix') opts.elementPrefix = argv[++i];
|
|
159
|
+
else if (arg === '--css-prefix') opts.cssPrefix = argv[++i];
|
|
160
|
+
else if (arg === '--name') opts.name = argv[++i];
|
|
161
|
+
else if (arg === '--theme-css') opts.themeCss = argv[++i];
|
|
162
|
+
else if (arg === '-h' || arg === '--help') usageGenerateSkill(0);
|
|
163
|
+
else {
|
|
164
|
+
console.error(`✗ Unknown flag: ${arg}`);
|
|
165
|
+
usageGenerateSkill(1);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return opts;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
async function loadConfig(configPath) {
|
|
172
|
+
const candidates = configPath
|
|
173
|
+
? [resolve(process.cwd(), configPath)]
|
|
174
|
+
: [resolve(process.cwd(), 'luxen.config.mjs'), resolve(process.cwd(), 'luxen.config.js')];
|
|
175
|
+
for (const p of candidates) {
|
|
176
|
+
if (existsSync(p)) {
|
|
177
|
+
// eslint-disable-next-line no-await-in-loop -- early-return on first match, max 2 candidates
|
|
178
|
+
const mod = await import(pathToFileURL(p).href);
|
|
179
|
+
return mod.default ?? mod;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
if (configPath) {
|
|
183
|
+
console.error(`✗ Config file not found: ${configPath}`);
|
|
184
|
+
process.exit(1);
|
|
185
|
+
}
|
|
186
|
+
return {};
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function buildContext(config, opts) {
|
|
190
|
+
const merged = { ...DEFAULT_CONFIG, ...config, ...stripUndefined(opts) };
|
|
191
|
+
// Sanity: prefixes must NOT end with a dash (matches Vite plugin convention).
|
|
192
|
+
for (const k of ['elementPrefix', 'cssPrefix']) {
|
|
193
|
+
if (merged[k].endsWith('-')) {
|
|
194
|
+
console.error(
|
|
195
|
+
`✗ Invalid ${k} "${merged[k]}" — drop the trailing dash (e.g. "pulse", not "pulse-").`,
|
|
196
|
+
);
|
|
197
|
+
process.exit(1);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
const outDir = resolve(process.cwd(), merged.out ?? `.claude/skills/${merged.name}`);
|
|
201
|
+
return {
|
|
202
|
+
...merged,
|
|
203
|
+
outDir,
|
|
204
|
+
pkgRoot: PKG_ROOT,
|
|
205
|
+
sourceVersion: PKG_VERSION,
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function stripUndefined(obj) {
|
|
210
|
+
return Object.fromEntries(Object.entries(obj).filter(([, v]) => v !== undefined));
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// --- Pipeline -----------------------------------------------------------------
|
|
214
|
+
|
|
215
|
+
async function runGenerateSkill(ctx) {
|
|
216
|
+
ensureDir(ctx.outDir);
|
|
217
|
+
ensureDir(join(ctx.outDir, 'references'));
|
|
218
|
+
ensureDir(join(ctx.outDir, 'assets'));
|
|
219
|
+
ensureDir(join(ctx.outDir, 'assets', 'css'));
|
|
220
|
+
|
|
221
|
+
// 1. Read templates (hand-written) and pre-transformed element docs (build).
|
|
222
|
+
const tplDir = resolve(ctx.pkgRoot, 'templates');
|
|
223
|
+
const distTplDir = resolve(ctx.pkgRoot, 'dist/templates');
|
|
224
|
+
|
|
225
|
+
if (!existsSync(distTplDir)) {
|
|
226
|
+
console.error(`✗ Missing ${distTplDir}.`);
|
|
227
|
+
console.error(" This directory is generated by luxen-ui's build. Reinstall the package.");
|
|
228
|
+
process.exit(1);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
const elements = JSON.parse(readFileSync(resolve(ctx.pkgRoot, 'elements.json'), 'utf-8'));
|
|
232
|
+
const skillElements = elements.elements.filter((e) => e.inSkill);
|
|
233
|
+
|
|
234
|
+
// 2. Compute template variables.
|
|
235
|
+
const vars = {
|
|
236
|
+
name: ctx.name,
|
|
237
|
+
displayName: ctx.displayName,
|
|
238
|
+
description: ctx.description.trim().replace(/\s+/g, ' '),
|
|
239
|
+
elementPrefix: ctx.elementPrefix,
|
|
240
|
+
cssPrefix: ctx.cssPrefix,
|
|
241
|
+
sourceVersion: ctx.sourceVersion,
|
|
242
|
+
cdnVersion: ctx.sourceVersion.split('.').slice(0, 2).join('.'),
|
|
243
|
+
cssImportPath: ctx.cssImportPath,
|
|
244
|
+
jsImportPath: ctx.jsImportPath,
|
|
245
|
+
elementsTable: buildElementsTable(skillElements, ctx),
|
|
246
|
+
elementsList: buildElementsList(skillElements),
|
|
247
|
+
tagsList: buildTagsList(elements.elements),
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
// 3. Render SKILL.md, integration.md, claude-design.md
|
|
251
|
+
await renderTemplate(join(tplDir, 'SKILL.md.tpl'), join(ctx.outDir, 'SKILL.md'), vars, ctx);
|
|
252
|
+
await renderTemplate(
|
|
253
|
+
join(tplDir, 'integration.md.tpl'),
|
|
254
|
+
join(ctx.outDir, 'references', 'integration.md'),
|
|
255
|
+
vars,
|
|
256
|
+
ctx,
|
|
257
|
+
);
|
|
258
|
+
await renderTemplate(
|
|
259
|
+
join(tplDir, 'claude-design.md.tpl'),
|
|
260
|
+
join(ctx.outDir, 'assets', 'claude-design.md'),
|
|
261
|
+
vars,
|
|
262
|
+
ctx,
|
|
263
|
+
);
|
|
264
|
+
|
|
265
|
+
// 4. Per-element references (with prefix + package-name substitution).
|
|
266
|
+
for (const el of skillElements) {
|
|
267
|
+
const src = join(distTplDir, 'elements', `${el.name}.md`);
|
|
268
|
+
const dst = join(ctx.outDir, 'references', `${el.name}.md`);
|
|
269
|
+
const content = readFileSync(src, 'utf-8');
|
|
270
|
+
const out = applyPackageName(applyPrefix(content, ctx), ctx);
|
|
271
|
+
writeFileSync(dst, out, 'utf-8');
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// 5. mockups.md — rendered from template (local-assets paths, prefix-aware
|
|
275
|
+
// since assets/cdn/ has been substituted to the consumer's prefix).
|
|
276
|
+
await renderTemplate(
|
|
277
|
+
join(tplDir, 'mockups.md.tpl'),
|
|
278
|
+
join(ctx.outDir, 'references', 'mockups.md'),
|
|
279
|
+
vars,
|
|
280
|
+
ctx,
|
|
281
|
+
);
|
|
282
|
+
|
|
283
|
+
// 6. Standalone runtime bundle for mockup mode (assets/<name>-standalone.{js,css}).
|
|
284
|
+
// Two files instead of a 150-file tree: one <link> + one <script> in any
|
|
285
|
+
// mockup HTML loads the full library with the consumer's prefix and tokens.
|
|
286
|
+
const standaloneJsSrc = resolve(ctx.pkgRoot, 'cdn/standalone.js');
|
|
287
|
+
const standaloneCssSrc = resolve(ctx.pkgRoot, 'cdn/standalone.css');
|
|
288
|
+
if (!existsSync(standaloneJsSrc) || !existsSync(standaloneCssSrc)) {
|
|
289
|
+
console.error(
|
|
290
|
+
`✗ cdn/standalone.{js,css} not found in ${ctx.pkgRoot}. Run \`pnpm build:standalone\` in luxen-ui first.`,
|
|
291
|
+
);
|
|
292
|
+
process.exit(1);
|
|
293
|
+
}
|
|
294
|
+
const jsDst = join(ctx.outDir, 'assets', `${ctx.name}-standalone.js`);
|
|
295
|
+
const cssDst = join(ctx.outDir, 'assets', `${ctx.name}-standalone.css`);
|
|
296
|
+
writeFileSync(
|
|
297
|
+
jsDst,
|
|
298
|
+
applyPackageName(applyPrefixJs(readFileSync(standaloneJsSrc, 'utf-8'), ctx), ctx),
|
|
299
|
+
'utf-8',
|
|
300
|
+
);
|
|
301
|
+
writeFileSync(
|
|
302
|
+
cssDst,
|
|
303
|
+
applyPackageName(applyPrefixCss(readFileSync(standaloneCssSrc, 'utf-8'), ctx), ctx),
|
|
304
|
+
'utf-8',
|
|
305
|
+
);
|
|
306
|
+
|
|
307
|
+
// 7. Optional theme override CSS — appended to the standalone CSS so it
|
|
308
|
+
// overrides Luxen's default tokens. The consumer authored the file, so we
|
|
309
|
+
// copy verbatim without prefix substitution.
|
|
310
|
+
if (ctx.themeCss) {
|
|
311
|
+
const themePath = resolve(process.cwd(), ctx.themeCss);
|
|
312
|
+
if (!existsSync(themePath)) {
|
|
313
|
+
console.error(`✗ themeCss not found: ${ctx.themeCss}`);
|
|
314
|
+
process.exit(1);
|
|
315
|
+
}
|
|
316
|
+
const themeContent = readFileSync(themePath, 'utf-8');
|
|
317
|
+
writeFileSync(
|
|
318
|
+
cssDst,
|
|
319
|
+
readFileSync(cssDst, 'utf-8') + '\n\n/* Theme overrides */\n' + themeContent,
|
|
320
|
+
'utf-8',
|
|
321
|
+
);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// --- Helpers ------------------------------------------------------------------
|
|
326
|
+
|
|
327
|
+
function ensureDir(p) {
|
|
328
|
+
mkdirSync(p, { recursive: true });
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
async function renderTemplate(srcPath, dstPath, vars, ctx) {
|
|
332
|
+
const raw = readFileSync(srcPath, 'utf-8');
|
|
333
|
+
const withVars = applyMustache(raw, vars);
|
|
334
|
+
const withPrefix = applyPrefix(withVars, ctx);
|
|
335
|
+
const final = applyPackageName(withPrefix, ctx);
|
|
336
|
+
writeFileSync(dstPath, final, 'utf-8');
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
function applyMustache(content, vars) {
|
|
340
|
+
return content.replace(/\{\{(\w+)\}\}/g, (m, key) => (key in vars ? String(vars[key]) : m));
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// Substitutes the npm package name `luxen-ui` with the consumer's name in
|
|
344
|
+
// quoted import paths — covers both `'luxen-ui'` (bare) and `'luxen-ui/...'`
|
|
345
|
+
// (sub-paths like `'luxen-ui/css/badge'`). The strict patterns (quote + exact
|
|
346
|
+
// name) avoid false positives on unrelated identifiers like
|
|
347
|
+
// `Symbol.for('luxen-dialog-scroll-lock')` (no closing quote on `luxen-ui`,
|
|
348
|
+
// continues with other text).
|
|
349
|
+
function applyPackageName(content, ctx) {
|
|
350
|
+
const pkg = ctx.jsImportPath;
|
|
351
|
+
if (pkg === 'luxen-ui') return content;
|
|
352
|
+
return content
|
|
353
|
+
.replace(/'luxen-ui'/g, `'${pkg}'`)
|
|
354
|
+
.replace(/"luxen-ui"/g, `"${pkg}"`)
|
|
355
|
+
.replace(/'luxen-ui\//g, `'${pkg}/`)
|
|
356
|
+
.replace(/"luxen-ui\//g, `"${pkg}/`);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
// Prefix substitution for markdown / generic text. Patterns are split by
|
|
360
|
+
// surface: tag-shaped patterns use elementPrefix (matching the Vite plugin's
|
|
361
|
+
// elementPrefix and registry.js), CSS-shaped patterns use cssPrefix (matching
|
|
362
|
+
// postcss-plugin-prefix). Quoted forms (e.g. `'l-'`) default to elementPrefix
|
|
363
|
+
// because they overwhelmingly refer to tag names in JS strings / JSON. When
|
|
364
|
+
// elementPrefix === 'l' and cssPrefix === 'l', this is a no-op.
|
|
365
|
+
function applyPrefix(content, ctx) {
|
|
366
|
+
const { elementPrefix: e, cssPrefix: c } = ctx;
|
|
367
|
+
if (e === 'l' && c === 'l') return content;
|
|
368
|
+
return content
|
|
369
|
+
.replace(/<l-/g, `<${e}-`)
|
|
370
|
+
.replace(/<\/l-/g, `</${e}-`)
|
|
371
|
+
.replace(/--l-/g, `--${c}-`)
|
|
372
|
+
.replace(/\.l-/g, `.${c}-`)
|
|
373
|
+
.replace(/"l-/g, `"${e}-`)
|
|
374
|
+
.replace(/'l-/g, `'${e}-`)
|
|
375
|
+
.replace(/`l-/g, `\`${e}-`)
|
|
376
|
+
.replace(/ l-/g, ` ${e}-`)
|
|
377
|
+
.replace(/\(l-/g, `(${e}-`)
|
|
378
|
+
.replace(/\[l-/g, `[${e}-`)
|
|
379
|
+
.replace(/,l-/g, `,${e}-`)
|
|
380
|
+
.replace(/=l-/g, `=${e}-`)
|
|
381
|
+
.replace(/\nl-/g, `\n${e}-`);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// Prefix substitution for CSS files. Three surfaces, two prefixes (mirrors
|
|
385
|
+
// postcss-plugin-prefix):
|
|
386
|
+
// - CSS variables `--l-*` → cssPrefix
|
|
387
|
+
// - Class selectors `.l-*` → cssPrefix
|
|
388
|
+
// - Type selectors `l-foo { ... }`, `l-foo:hover`, `, l-foo` → elementPrefix
|
|
389
|
+
// The type-selector pattern matches `l-` only at positions where a CSS selector
|
|
390
|
+
// can start (line start, whitespace, comma, combinators), to avoid touching the
|
|
391
|
+
// `l-` inside `--l-`, `.l-`, `--prefix-l-something`, etc.
|
|
392
|
+
function applyPrefixCss(content, ctx) {
|
|
393
|
+
const { elementPrefix: e, cssPrefix: c } = ctx;
|
|
394
|
+
if (e === 'l' && c === 'l') return content;
|
|
395
|
+
return content
|
|
396
|
+
.replace(/--l-/g, `--${c}-`)
|
|
397
|
+
.replace(/\.l-/g, `.${c}-`)
|
|
398
|
+
.replace(/(^|[\s,>+~(])l-/gm, `$1${e}-`);
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
// Prefix substitution for the standalone JS bundle: registry initialisers,
|
|
402
|
+
// tag-name string literals, shadow-DOM CSS inlined as JS strings, and (if
|
|
403
|
+
// included) custom-elements.json. The bundle is built unminified so these
|
|
404
|
+
// patterns are stable.
|
|
405
|
+
function applyPrefixJs(content, ctx) {
|
|
406
|
+
const { elementPrefix: e, cssPrefix: c } = ctx;
|
|
407
|
+
if (e === 'l' && c === 'l') return content;
|
|
408
|
+
return (
|
|
409
|
+
content
|
|
410
|
+
.replace(/'l-/g, `'${e}-`)
|
|
411
|
+
.replace(/"l-/g, `"${e}-`)
|
|
412
|
+
.replace(/--l-/g, `--${c}-`)
|
|
413
|
+
// Registry initialisers — match the standalone bundle's unminified form:
|
|
414
|
+
// var _elementPrefix = "l"; → var _elementPrefix = "pulse";
|
|
415
|
+
// var _cssPrefix = "l"; → var _cssPrefix = "p";
|
|
416
|
+
.replace(/_elementPrefix = "l"/g, `_elementPrefix = "${e}"`)
|
|
417
|
+
.replace(/_cssPrefix = "l"/g, `_cssPrefix = "${c}"`)
|
|
418
|
+
);
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
function buildElementsTable(elements, ctx) {
|
|
422
|
+
const lines = [
|
|
423
|
+
'| Element | Type | Selector | Reference |',
|
|
424
|
+
'|---------|------|----------|-----------|',
|
|
425
|
+
];
|
|
426
|
+
for (const e of elements) {
|
|
427
|
+
const type = e.kind === 'native' ? 'CSS class' : 'Custom element';
|
|
428
|
+
// CSS classes use cssPrefix; custom elements use elementPrefix.
|
|
429
|
+
const prefix = e.kind === 'native' ? ctx.cssPrefix : ctx.elementPrefix;
|
|
430
|
+
const sel =
|
|
431
|
+
e.selector ?? (e.kind === 'native' ? `.${prefix}-${e.name}` : `<${prefix}-${e.name}>`);
|
|
432
|
+
// Selector overrides (e.g. `.l-close`) need manual prefix swap. Choose the
|
|
433
|
+
// right prefix based on the leading character (`<` = tag, `.` = class).
|
|
434
|
+
const finalSel = e.selector
|
|
435
|
+
? e.selector.replace(/^([<.])l-/, (_, ch) =>
|
|
436
|
+
ch === '<' ? `<${ctx.elementPrefix}-` : `.${ctx.cssPrefix}-`,
|
|
437
|
+
)
|
|
438
|
+
: sel;
|
|
439
|
+
lines.push(`| ${e.displayName} | ${type} | \`${finalSel}\` | [${e.name}.md](${e.name}.md) |`);
|
|
440
|
+
}
|
|
441
|
+
return lines.join('\n');
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
function buildElementsList(elements) {
|
|
445
|
+
return elements.map((e) => e.displayName).join(', ') + '.';
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
// Builds the comma-separated `l-foo` tag list for mockups.md (from manifest
|
|
449
|
+
// entries flagged `inMockups`, excluding native CSS-only elements). The result
|
|
450
|
+
// is processed by applyPrefix at render time, so the tags reflect the consumer's
|
|
451
|
+
// rebrand if any.
|
|
452
|
+
function buildTagsList(allElements) {
|
|
453
|
+
return (
|
|
454
|
+
allElements
|
|
455
|
+
.filter((e) => e.inMockups && e.kind !== 'native')
|
|
456
|
+
.map((e) => `\`l-${e.name}\``)
|
|
457
|
+
.join(', ') + '.'
|
|
458
|
+
);
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
// =============================================================================
|
|
462
|
+
// Command dispatcher
|
|
463
|
+
// =============================================================================
|
|
464
|
+
|
|
465
|
+
function usageImport(code = 1) {
|
|
466
|
+
console.error('Usage: luxen-ui import <noun> [path]');
|
|
467
|
+
console.error('');
|
|
468
|
+
console.error('Available nouns:');
|
|
469
|
+
console.error(' preset Import the CSS preset (base + tokens).');
|
|
470
|
+
console.error(' Default: ./luxen-preset.css');
|
|
471
|
+
console.error(' tailwind Import the Tailwind theme preset (bridge).');
|
|
472
|
+
console.error(' Default: ./luxen-tailwind.css');
|
|
473
|
+
console.error(' design-tokens Import the semantic aliases for customization.');
|
|
474
|
+
console.error(' Default: ./luxen-design-tokens.css');
|
|
475
|
+
process.exit(code);
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
function usageGenerateSkill(code = 1) {
|
|
479
|
+
console.error('Usage: luxen-ui generate-skill [flags]');
|
|
480
|
+
console.error('');
|
|
481
|
+
console.error('Flags:');
|
|
482
|
+
console.error(' --config <path> Config file (default: ./luxen.config.mjs)');
|
|
483
|
+
console.error(' --name <name> Skill name (default: luxen-ui)');
|
|
484
|
+
console.error(' --element-prefix <p> Tag prefix without trailing dash (default: l)');
|
|
485
|
+
console.error(
|
|
486
|
+
' --css-prefix <p> CSS class/var prefix without trailing dash (default: l)',
|
|
487
|
+
);
|
|
488
|
+
console.error(' --theme-css <path> Optional CSS file with token overrides');
|
|
489
|
+
console.error(' --out <path> Output dir (default: .claude/skills/<name>)');
|
|
490
|
+
console.error('');
|
|
491
|
+
console.error('Config file (luxen.config.mjs) takes the same keys, plus identity:');
|
|
492
|
+
console.error(' export default {');
|
|
493
|
+
console.error(" name: 'pulse',");
|
|
494
|
+
console.error(" displayName: 'Pulse',");
|
|
495
|
+
console.error(" description: '...',");
|
|
496
|
+
console.error(" elementPrefix: 'pulse',");
|
|
497
|
+
console.error(" cssPrefix: 'pulse',");
|
|
498
|
+
console.error(" themeCss: 'src/styles/tokens.css',");
|
|
499
|
+
console.error(" out: '.claude/skills/pulse',");
|
|
500
|
+
console.error(' }');
|
|
501
|
+
console.error('');
|
|
502
|
+
console.error('The Vite plugin (luxen-ui/vite-plugin) reads the same file —');
|
|
503
|
+
console.error('keep elementPrefix/cssPrefix in sync between dev and skill generation.');
|
|
504
|
+
process.exit(code);
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
function usage(code = 1) {
|
|
508
|
+
console.error('Usage: luxen-ui <command> [args]');
|
|
509
|
+
console.error('');
|
|
510
|
+
console.error('Commands:');
|
|
511
|
+
console.error(' import <noun> [path] Copy a CSS preset / token file into the project.');
|
|
512
|
+
console.error(' generate-skill Produce a brand-aware Agent Skill folder.');
|
|
513
|
+
console.error('');
|
|
514
|
+
console.error('Run `luxen-ui <command> --help` for command-specific flags.');
|
|
515
|
+
process.exit(code);
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
const [, , command, ...rest] = process.argv;
|
|
519
|
+
|
|
520
|
+
if (!command || command === '-h' || command === '--help') usage(0);
|
|
521
|
+
|
|
522
|
+
if (command === 'import') cmdImport(rest);
|
|
523
|
+
else if (command === 'generate-skill') await cmdGenerateSkill(rest);
|
|
524
|
+
else {
|
|
525
|
+
console.error(`✗ Unknown command: ${command}`);
|
|
526
|
+
usage(1);
|
|
527
|
+
}
|