From b297f80f4f5d51ae00671e0fe4d4e814caa1a073 Mon Sep 17 00:00:00 2001 From: Sil Date: Sat, 17 Oct 2026 12:03:10 +0200 Subject: [PATCH 1/3] Publish universal Liquid registry assets and installation regression tests --- liquid/examples/accordion.liquid | 10 + liquid/examples/alert-dialog.liquid | 40 + liquid/examples/alert.liquid | 4 + liquid/examples/aspect-ratio.liquid | 1 + liquid/examples/attachment.liquid | 22 + liquid/examples/avatar.liquid | 1 + liquid/examples/badge.liquid | 5 + liquid/examples/banner.liquid | 3 + liquid/examples/breadcrumb.liquid | 7 + liquid/examples/button-group.liquid | 6 + liquid/examples/button.liquid | 8 + liquid/examples/card.liquid | 11 + liquid/examples/carousel.liquid | 12 + liquid/examples/checkbox.liquid | 4 + liquid/examples/collapsible.liquid | 29 + liquid/examples/combobox.liquid | 13 + liquid/examples/command.liquid | 10 + liquid/examples/dependencies.json | 60 + liquid/examples/dialog.liquid | 11 + liquid/examples/drawer.liquid | 18 + liquid/examples/dropdown-menu.liquid | 88 ++ liquid/examples/empty.liquid | 9 + liquid/examples/field.liquid | 105 ++ liquid/examples/form.liquid | 9 + liquid/examples/header.liquid | 8 + liquid/examples/hover-card.liquid | 16 + liquid/examples/icon.liquid | 3 + liquid/examples/input-group.liquid | 6 + liquid/examples/input.liquid | 4 + liquid/examples/item.liquid | 72 ++ liquid/examples/kbd.liquid | 1 + liquid/examples/label.liquid | 3 + liquid/examples/logo.liquid | 3 + liquid/examples/marquee.liquid | 5 + liquid/examples/native-select.liquid | 8 + liquid/examples/navigation-menu.liquid | 11 + liquid/examples/pagination.liquid | 11 + liquid/examples/popover.liquid | 14 + liquid/examples/price.liquid | 1 + liquid/examples/radio-group.liquid | 62 ++ liquid/examples/rating.liquid | 17 + liquid/examples/section.liquid | 9 + liquid/examples/select.liquid | 105 ++ liquid/examples/separator.liquid | 3 + liquid/examples/sheet.liquid | 11 + liquid/examples/skeleton.liquid | 5 + liquid/examples/slider.liquid | 46 + liquid/examples/spinner.liquid | 11 + liquid/examples/switch.liquid | 7 + liquid/examples/table.liquid | 37 + liquid/examples/tabs.liquid | 21 + liquid/examples/textarea.liquid | 8 + liquid/examples/toast.liquid | 47 + liquid/examples/toc.liquid | 11 + liquid/examples/toggle-group.liquid | 50 + liquid/examples/toggle.liquid | 21 + liquid/examples/tooltip.liquid | 33 + liquid/examples/typography.liquid | 9 + liquid/examples/video.liquid | 7 + package.json | 19 +- pnpm-lock.yaml | 3 + public/r/liquid/accordion.json | 25 + public/r/liquid/alert-dialog.json | 21 + public/r/liquid/alert.json | 18 + public/r/liquid/aspect-ratio.json | 18 + public/r/liquid/attachment.json | 20 + public/r/liquid/avatar.json | 18 + public/r/liquid/badge.json | 18 + public/r/liquid/banner.json | 18 + public/r/liquid/breadcrumb.json | 18 + public/r/liquid/button-group.json | 19 + public/r/liquid/button.json | 18 + public/r/liquid/card.json | 25 + public/r/liquid/carousel.json | 26 + public/r/liquid/checkbox.json | 19 + public/r/liquid/collapsible.json | 19 + public/r/liquid/combobox.json | 21 + public/r/liquid/command.json | 19 + public/r/liquid/components.json | 83 ++ public/r/liquid/dialog.json | 19 + public/r/liquid/drawer.json | 21 + public/r/liquid/dropdown-menu.json | 21 + public/r/liquid/empty.json | 18 + public/r/liquid/field.json | 25 + public/r/liquid/form.json | 25 + public/r/liquid/header.json | 19 + public/r/liquid/hover-card.json | 20 + public/r/liquid/icon.json | 24 + public/r/liquid/init.json | 34 + public/r/liquid/input-group.json | 20 + public/r/liquid/input.json | 18 + public/r/liquid/invalid-controls.json | 18 + public/r/liquid/item.json | 18 + public/r/liquid/kbd.json | 18 + public/r/liquid/label.json | 11 + public/r/liquid/logo.json | 18 + public/r/liquid/marquee.json | 25 + public/r/liquid/native-select.json | 11 + public/r/liquid/navigation-menu.json | 26 + public/r/liquid/pagination.json | 18 + public/r/liquid/popover.json | 21 + public/r/liquid/popup.json | 18 + public/r/liquid/price.json | 24 + public/r/liquid/radio-group.json | 20 + public/r/liquid/rating.json | 24 + public/r/liquid/registry.json | 1295 ++++++++++++++++++++++ public/r/liquid/runtime.json | 180 +++ public/r/liquid/section.json | 24 + public/r/liquid/select.json | 22 + public/r/liquid/separator.json | 18 + public/r/liquid/sheet.json | 20 + public/r/liquid/skeleton.json | 18 + public/r/liquid/slider.json | 19 + public/r/liquid/spinner.json | 18 + public/r/liquid/switch.json | 20 + public/r/liquid/table.json | 18 + public/r/liquid/tabs.json | 20 + public/r/liquid/textarea.json | 11 + public/r/liquid/toast.json | 27 + public/r/liquid/toc.json | 18 + public/r/liquid/toggle-group.json | 21 + public/r/liquid/toggle.json | 20 + public/r/liquid/tooltip.json | 20 + public/r/liquid/triggers.json | 18 + public/r/liquid/typography.json | 24 + public/r/liquid/video.json | 25 + scripts/registry-liquid.mjs | 91 ++ tests/browser/liquid.test.mjs | 311 ++++++ tests/fixtures/liquid/LICENSE-LUCIDE.md | 43 + tests/fixtures/liquid/en.json | 125 +++ tests/fixtures/liquid/icon.liquid | 145 +++ tests/fixtures/liquid/interactive.liquid | 97 ++ tests/fixtures/liquid/reference.liquid | 1156 +++++++++++++++++++ tests/helpers/liquid.mjs | 162 +++ tests/liquid.test.mjs | 139 +++ 135 files changed, 6269 insertions(+), 7 deletions(-) create mode 100644 liquid/examples/accordion.liquid create mode 100644 liquid/examples/alert-dialog.liquid create mode 100644 liquid/examples/alert.liquid create mode 100644 liquid/examples/aspect-ratio.liquid create mode 100644 liquid/examples/attachment.liquid create mode 100644 liquid/examples/avatar.liquid create mode 100644 liquid/examples/badge.liquid create mode 100644 liquid/examples/banner.liquid create mode 100644 liquid/examples/breadcrumb.liquid create mode 100644 liquid/examples/button-group.liquid create mode 100644 liquid/examples/button.liquid create mode 100644 liquid/examples/card.liquid create mode 100644 liquid/examples/carousel.liquid create mode 100644 liquid/examples/checkbox.liquid create mode 100644 liquid/examples/collapsible.liquid create mode 100644 liquid/examples/combobox.liquid create mode 100644 liquid/examples/command.liquid create mode 100644 liquid/examples/dependencies.json create mode 100644 liquid/examples/dialog.liquid create mode 100644 liquid/examples/drawer.liquid create mode 100644 liquid/examples/dropdown-menu.liquid create mode 100644 liquid/examples/empty.liquid create mode 100644 liquid/examples/field.liquid create mode 100644 liquid/examples/form.liquid create mode 100644 liquid/examples/header.liquid create mode 100644 liquid/examples/hover-card.liquid create mode 100644 liquid/examples/icon.liquid create mode 100644 liquid/examples/input-group.liquid create mode 100644 liquid/examples/input.liquid create mode 100644 liquid/examples/item.liquid create mode 100644 liquid/examples/kbd.liquid create mode 100644 liquid/examples/label.liquid create mode 100644 liquid/examples/logo.liquid create mode 100644 liquid/examples/marquee.liquid create mode 100644 liquid/examples/native-select.liquid create mode 100644 liquid/examples/navigation-menu.liquid create mode 100644 liquid/examples/pagination.liquid create mode 100644 liquid/examples/popover.liquid create mode 100644 liquid/examples/price.liquid create mode 100644 liquid/examples/radio-group.liquid create mode 100644 liquid/examples/rating.liquid create mode 100644 liquid/examples/section.liquid create mode 100644 liquid/examples/select.liquid create mode 100644 liquid/examples/separator.liquid create mode 100644 liquid/examples/sheet.liquid create mode 100644 liquid/examples/skeleton.liquid create mode 100644 liquid/examples/slider.liquid create mode 100644 liquid/examples/spinner.liquid create mode 100644 liquid/examples/switch.liquid create mode 100644 liquid/examples/table.liquid create mode 100644 liquid/examples/tabs.liquid create mode 100644 liquid/examples/textarea.liquid create mode 100644 liquid/examples/toast.liquid create mode 100644 liquid/examples/toc.liquid create mode 100644 liquid/examples/toggle-group.liquid create mode 100644 liquid/examples/toggle.liquid create mode 100644 liquid/examples/tooltip.liquid create mode 100644 liquid/examples/typography.liquid create mode 100644 liquid/examples/video.liquid create mode 100644 public/r/liquid/accordion.json create mode 100644 public/r/liquid/alert-dialog.json create mode 100644 public/r/liquid/alert.json create mode 100644 public/r/liquid/aspect-ratio.json create mode 100644 public/r/liquid/attachment.json create mode 100644 public/r/liquid/avatar.json create mode 100644 public/r/liquid/badge.json create mode 100644 public/r/liquid/banner.json create mode 100644 public/r/liquid/breadcrumb.json create mode 100644 public/r/liquid/button-group.json create mode 100644 public/r/liquid/button.json create mode 100644 public/r/liquid/card.json create mode 100644 public/r/liquid/carousel.json create mode 100644 public/r/liquid/checkbox.json create mode 100644 public/r/liquid/collapsible.json create mode 100644 public/r/liquid/combobox.json create mode 100644 public/r/liquid/command.json create mode 100644 public/r/liquid/components.json create mode 100644 public/r/liquid/dialog.json create mode 100644 public/r/liquid/drawer.json create mode 100644 public/r/liquid/dropdown-menu.json create mode 100644 public/r/liquid/empty.json create mode 100644 public/r/liquid/field.json create mode 100644 public/r/liquid/form.json create mode 100644 public/r/liquid/header.json create mode 100644 public/r/liquid/hover-card.json create mode 100644 public/r/liquid/icon.json create mode 100644 public/r/liquid/init.json create mode 100644 public/r/liquid/input-group.json create mode 100644 public/r/liquid/input.json create mode 100644 public/r/liquid/invalid-controls.json create mode 100644 public/r/liquid/item.json create mode 100644 public/r/liquid/kbd.json create mode 100644 public/r/liquid/label.json create mode 100644 public/r/liquid/logo.json create mode 100644 public/r/liquid/marquee.json create mode 100644 public/r/liquid/native-select.json create mode 100644 public/r/liquid/navigation-menu.json create mode 100644 public/r/liquid/pagination.json create mode 100644 public/r/liquid/popover.json create mode 100644 public/r/liquid/popup.json create mode 100644 public/r/liquid/price.json create mode 100644 public/r/liquid/radio-group.json create mode 100644 public/r/liquid/rating.json create mode 100644 public/r/liquid/registry.json create mode 100644 public/r/liquid/runtime.json create mode 100644 public/r/liquid/section.json create mode 100644 public/r/liquid/select.json create mode 100644 public/r/liquid/separator.json create mode 100644 public/r/liquid/sheet.json create mode 100644 public/r/liquid/skeleton.json create mode 100644 public/r/liquid/slider.json create mode 100644 public/r/liquid/spinner.json create mode 100644 public/r/liquid/switch.json create mode 100644 public/r/liquid/table.json create mode 100644 public/r/liquid/tabs.json create mode 100644 public/r/liquid/textarea.json create mode 100644 public/r/liquid/toast.json create mode 100644 public/r/liquid/toc.json create mode 100644 public/r/liquid/toggle-group.json create mode 100644 public/r/liquid/toggle.json create mode 100644 public/r/liquid/tooltip.json create mode 100644 public/r/liquid/triggers.json create mode 100644 public/r/liquid/typography.json create mode 100644 public/r/liquid/video.json create mode 100644 scripts/registry-liquid.mjs create mode 100644 tests/browser/liquid.test.mjs create mode 100644 tests/fixtures/liquid/LICENSE-LUCIDE.md create mode 100644 tests/fixtures/liquid/en.json create mode 100644 tests/fixtures/liquid/icon.liquid create mode 100644 tests/fixtures/liquid/interactive.liquid create mode 100644 tests/fixtures/liquid/reference.liquid create mode 100644 tests/helpers/liquid.mjs create mode 100644 tests/liquid.test.mjs diff --git a/liquid/examples/accordion.liquid b/liquid/examples/accordion.liquid new file mode 100644 index 000000000..c5305c1c1 --- /dev/null +++ b/liquid/examples/accordion.liquid @@ -0,0 +1,10 @@ +
+
+

+

Place your product details here.

+
+
+

+

Add care instructions for your product.

+
+
diff --git a/liquid/examples/alert-dialog.liquid b/liquid/examples/alert-dialog.liquid new file mode 100644 index 000000000..fae9b5ae8 --- /dev/null +++ b/liquid/examples/alert-dialog.liquid @@ -0,0 +1,40 @@ +
+ +
+ + +
+
diff --git a/liquid/examples/alert.liquid b/liquid/examples/alert.liquid new file mode 100644 index 000000000..b8f35596c --- /dev/null +++ b/liquid/examples/alert.liquid @@ -0,0 +1,4 @@ +
+

Example notice

+
Place a useful message for your customers here.
+
diff --git a/liquid/examples/aspect-ratio.liquid b/liquid/examples/aspect-ratio.liquid new file mode 100644 index 000000000..541c24bf9 --- /dev/null +++ b/liquid/examples/aspect-ratio.liquid @@ -0,0 +1 @@ +
16:9 media
diff --git a/liquid/examples/attachment.liquid b/liquid/examples/attachment.liquid new file mode 100644 index 000000000..4e2af2bb3 --- /dev/null +++ b/liquid/examples/attachment.liquid @@ -0,0 +1,22 @@ +
+
+ +
+
+ example-file.pdf + PDF +
+
diff --git a/liquid/examples/avatar.liquid b/liquid/examples/avatar.liquid new file mode 100644 index 000000000..f383f4e5d --- /dev/null +++ b/liquid/examples/avatar.liquid @@ -0,0 +1 @@ +FD diff --git a/liquid/examples/badge.liquid b/liquid/examples/badge.liquid new file mode 100644 index 000000000..db6679233 --- /dev/null +++ b/liquid/examples/badge.liquid @@ -0,0 +1,5 @@ +
+ DefaultSecondaryOutlineDestructive +
diff --git a/liquid/examples/banner.liquid b/liquid/examples/banner.liquid new file mode 100644 index 000000000..a61a6cb40 --- /dev/null +++ b/liquid/examples/banner.liquid @@ -0,0 +1,3 @@ + diff --git a/liquid/examples/breadcrumb.liquid b/liquid/examples/breadcrumb.liquid new file mode 100644 index 000000000..d07f37d8c --- /dev/null +++ b/liquid/examples/breadcrumb.liquid @@ -0,0 +1,7 @@ + diff --git a/liquid/examples/button-group.liquid b/liquid/examples/button-group.liquid new file mode 100644 index 000000000..642215eb3 --- /dev/null +++ b/liquid/examples/button-group.liquid @@ -0,0 +1,6 @@ +
+ Go to + Products + Search + Cart +
diff --git a/liquid/examples/button.liquid b/liquid/examples/button.liquid new file mode 100644 index 000000000..e4f69a66b --- /dev/null +++ b/liquid/examples/button.liquid @@ -0,0 +1,8 @@ +
+ {% render 'ui-button', label: 'Button' %} + {% render 'ui-button', label: 'Outline', variant: 'outline' %} + {% render 'ui-button', label: 'Secondary', variant: 'secondary' %} + {% render 'ui-button', label: 'Ghost', variant: 'ghost' %} + {% render 'ui-button', label: 'Destructive', variant: 'destructive' %} + {% render 'ui-button', label: 'View collection', href: routes.all_products_collection_url, variant: 'link' %} +
diff --git a/liquid/examples/card.liquid b/liquid/examples/card.liquid new file mode 100644 index 000000000..6357f1d1d --- /dev/null +++ b/liquid/examples/card.liquid @@ -0,0 +1,11 @@ +
+
+

Your collection

+

A complete card with native HTML parts.

+ New +
+
Place your own content here.
+ +
diff --git a/liquid/examples/carousel.liquid b/liquid/examples/carousel.liquid new file mode 100644 index 000000000..e6c54205a --- /dev/null +++ b/liquid/examples/carousel.liquid @@ -0,0 +1,12 @@ +
+
+
+
First slide
+
+
+
Second slide
+
+
+ +
diff --git a/liquid/examples/checkbox.liquid b/liquid/examples/checkbox.liquid new file mode 100644 index 000000000..d511357b7 --- /dev/null +++ b/liquid/examples/checkbox.liquid @@ -0,0 +1,4 @@ + diff --git a/liquid/examples/collapsible.liquid b/liquid/examples/collapsible.liquid new file mode 100644 index 000000000..eb23144cc --- /dev/null +++ b/liquid/examples/collapsible.liquid @@ -0,0 +1,29 @@ +
+ +
+

Collapsible content stays visible without JavaScript.

+
+
diff --git a/liquid/examples/combobox.liquid b/liquid/examples/combobox.liquid new file mode 100644 index 000000000..3dcd93474 --- /dev/null +++ b/liquid/examples/combobox.liquid @@ -0,0 +1,13 @@ +
+ + +
+ +
+
diff --git a/liquid/examples/command.liquid b/liquid/examples/command.liquid new file mode 100644 index 000000000..ec5a56ea3 --- /dev/null +++ b/liquid/examples/command.liquid @@ -0,0 +1,10 @@ +
+ +
+
+ Products + Cart +
+
No pages
+
+
diff --git a/liquid/examples/dependencies.json b/liquid/examples/dependencies.json new file mode 100644 index 000000000..cffa3c477 --- /dev/null +++ b/liquid/examples/dependencies.json @@ -0,0 +1,60 @@ +{ + "banner": ["banner"], + "tabs": ["tabs", "typography"], + "form": ["form", "button"], + "collapsible": ["collapsible", "icon", "typography", "button"], + "select": ["select", "field", "icon"], + "field": ["field", "icon", "select"], + "item": ["item", "button", "icon"], + "spinner": ["spinner"], + "video": ["video"], + "button": ["button"], + "toggle": ["toggle", "icon"], + "logo": ["logo"], + "kbd": ["kbd"], + "rating": ["rating"], + "tooltip": ["tooltip", "icon", "button"], + "avatar": ["avatar"], + "empty": ["empty", "button"], + "alert-dialog": ["alert-dialog", "icon"], + "navigation-menu": ["navigation-menu"], + "typography": ["typography"], + "toc": ["toc"], + "native-select": ["native-select", "field"], + "icon": ["icon"], + "textarea": ["textarea", "field"], + "combobox": ["combobox", "field"], + "toast": ["toast", "icon"], + "header": ["header", "button", "logo"], + "command": ["command"], + "accordion": ["accordion"], + "slider": ["slider", "field"], + "input": ["input", "field"], + "popover": ["popover", "button"], + "input-group": ["input-group", "button"], + "label": ["label"], + "toggle-group": ["toggle-group", "icon"], + "dialog": ["dialog", "button"], + "drawer": ["drawer"], + "badge": ["badge"], + "alert": ["alert"], + "table": ["table"], + "breadcrumb": ["breadcrumb"], + "aspect-ratio": ["aspect-ratio"], + "price": ["price"], + "radio-group": ["radio-group", "field"], + "checkbox": ["checkbox", "field"], + "switch": ["switch", "checkbox", "field"], + "skeleton": ["skeleton"], + "separator": ["separator"], + "card": ["card", "button"], + "pagination": ["pagination", "button"], + "sheet": ["sheet", "button"], + "carousel": ["carousel", "card"], + "attachment": ["attachment"], + "dropdown-menu": ["dropdown-menu", "icon", "button"], + "hover-card": ["hover-card", "typography", "button"], + "marquee": ["marquee"], + "section": ["section"], + "button-group": ["button-group"] +} diff --git a/liquid/examples/dialog.liquid b/liquid/examples/dialog.liquid new file mode 100644 index 000000000..50cd58e3f --- /dev/null +++ b/liquid/examples/dialog.liquid @@ -0,0 +1,11 @@ +
+ + + +
diff --git a/liquid/examples/drawer.liquid b/liquid/examples/drawer.liquid new file mode 100644 index 000000000..04d7b67a2 --- /dev/null +++ b/liquid/examples/drawer.liquid @@ -0,0 +1,18 @@ +
+ +
+ + +
+
diff --git a/liquid/examples/dropdown-menu.liquid b/liquid/examples/dropdown-menu.liquid new file mode 100644 index 000000000..69b065f82 --- /dev/null +++ b/liquid/examples/dropdown-menu.liquid @@ -0,0 +1,88 @@ +
+ +
+
+ +
+
+
diff --git a/liquid/examples/empty.liquid b/liquid/examples/empty.liquid new file mode 100644 index 000000000..15574c8a5 --- /dev/null +++ b/liquid/examples/empty.liquid @@ -0,0 +1,9 @@ +
+
+

No results

+

Try a different search.

+
+
+ Browse products +
+
diff --git a/liquid/examples/field.liquid b/liquid/examples/field.liquid new file mode 100644 index 000000000..39a6506d9 --- /dev/null +++ b/liquid/examples/field.liquid @@ -0,0 +1,105 @@ +
+ + + +
diff --git a/liquid/examples/form.liquid b/liquid/examples/form.liquid new file mode 100644 index 000000000..fb2435527 --- /dev/null +++ b/liquid/examples/form.liquid @@ -0,0 +1,9 @@ +
+
+
+ + +
+ +
+
diff --git a/liquid/examples/header.liquid b/liquid/examples/header.liquid new file mode 100644 index 000000000..3abef8c57 --- /dev/null +++ b/liquid/examples/header.liquid @@ -0,0 +1,8 @@ +
+
+ Your store + +
+
diff --git a/liquid/examples/hover-card.liquid b/liquid/examples/hover-card.liquid new file mode 100644 index 000000000..b8e956e17 --- /dev/null +++ b/liquid/examples/hover-card.liquid @@ -0,0 +1,16 @@ +
+ Products +
+
+ +
+
+
diff --git a/liquid/examples/icon.liquid b/liquid/examples/icon.liquid new file mode 100644 index 000000000..3e56d1aec --- /dev/null +++ b/liquid/examples/icon.liquid @@ -0,0 +1,3 @@ + diff --git a/liquid/examples/input-group.liquid b/liquid/examples/input-group.liquid new file mode 100644 index 000000000..5e864765b --- /dev/null +++ b/liquid/examples/input-group.liquid @@ -0,0 +1,6 @@ +
+ +
+ +
+
diff --git a/liquid/examples/input.liquid b/liquid/examples/input.liquid new file mode 100644 index 000000000..9f996f2db --- /dev/null +++ b/liquid/examples/input.liquid @@ -0,0 +1,4 @@ +
+ +
diff --git a/liquid/examples/item.liquid b/liquid/examples/item.liquid new file mode 100644 index 000000000..b5095aa9a --- /dev/null +++ b/liquid/examples/item.liquid @@ -0,0 +1,72 @@ + diff --git a/liquid/examples/kbd.liquid b/liquid/examples/kbd.liquid new file mode 100644 index 000000000..3942132c0 --- /dev/null +++ b/liquid/examples/kbd.liquid @@ -0,0 +1 @@ +Search ⌘ K diff --git a/liquid/examples/label.liquid b/liquid/examples/label.liquid new file mode 100644 index 000000000..4ae2129b5 --- /dev/null +++ b/liquid/examples/label.liquid @@ -0,0 +1,3 @@ +
+ +
diff --git a/liquid/examples/logo.liquid b/liquid/examples/logo.liquid new file mode 100644 index 000000000..c88d4e0ba --- /dev/null +++ b/liquid/examples/logo.liquid @@ -0,0 +1,3 @@ +Your store diff --git a/liquid/examples/marquee.liquid b/liquid/examples/marquee.liquid new file mode 100644 index 000000000..bc60ca569 --- /dev/null +++ b/liquid/examples/marquee.liquid @@ -0,0 +1,5 @@ +
+
+ Example brandAnother example +
+
diff --git a/liquid/examples/native-select.liquid b/liquid/examples/native-select.liquid new file mode 100644 index 000000000..85c67c524 --- /dev/null +++ b/liquid/examples/native-select.liquid @@ -0,0 +1,8 @@ +
+ +
diff --git a/liquid/examples/navigation-menu.liquid b/liquid/examples/navigation-menu.liquid new file mode 100644 index 000000000..e9da8a92d --- /dev/null +++ b/liquid/examples/navigation-menu.liquid @@ -0,0 +1,11 @@ + diff --git a/liquid/examples/pagination.liquid b/liquid/examples/pagination.liquid new file mode 100644 index 000000000..8f968143d --- /dev/null +++ b/liquid/examples/pagination.liquid @@ -0,0 +1,11 @@ + diff --git a/liquid/examples/popover.liquid b/liquid/examples/popover.liquid new file mode 100644 index 000000000..5d90132ee --- /dev/null +++ b/liquid/examples/popover.liquid @@ -0,0 +1,14 @@ +
+ +
+
+ +
+
+
diff --git a/liquid/examples/price.liquid b/liquid/examples/price.liquid new file mode 100644 index 000000000..7f9f08e1a --- /dev/null +++ b/liquid/examples/price.liquid @@ -0,0 +1 @@ +€29.00€39.00 diff --git a/liquid/examples/radio-group.liquid b/liquid/examples/radio-group.liquid new file mode 100644 index 000000000..996151074 --- /dev/null +++ b/liquid/examples/radio-group.liquid @@ -0,0 +1,62 @@ +
+ Delivery +
+ + + + + +
+ +
diff --git a/liquid/examples/rating.liquid b/liquid/examples/rating.liquid new file mode 100644 index 000000000..dfb1d5343 --- /dev/null +++ b/liquid/examples/rating.liquid @@ -0,0 +1,17 @@ +
+ {% for number in (1..5) %} + + {% endfor %} + Example rating +
diff --git a/liquid/examples/section.liquid b/liquid/examples/section.liquid new file mode 100644 index 000000000..7a1e06178 --- /dev/null +++ b/liquid/examples/section.liquid @@ -0,0 +1,9 @@ +
+
+
+

Your collection

+

Add a short introduction.

+
+
Place the section content here.
+
+
diff --git a/liquid/examples/select.liquid b/liquid/examples/select.liquid new file mode 100644 index 000000000..39a6506d9 --- /dev/null +++ b/liquid/examples/select.liquid @@ -0,0 +1,105 @@ +
+ + + +
diff --git a/liquid/examples/separator.liquid b/liquid/examples/separator.liquid new file mode 100644 index 000000000..fb73e94e0 --- /dev/null +++ b/liquid/examples/separator.liquid @@ -0,0 +1,3 @@ +

Collection details

+
+

Care instructions

diff --git a/liquid/examples/sheet.liquid b/liquid/examples/sheet.liquid new file mode 100644 index 000000000..c7a7a26c1 --- /dev/null +++ b/liquid/examples/sheet.liquid @@ -0,0 +1,11 @@ +
+ + + +
diff --git a/liquid/examples/skeleton.liquid b/liquid/examples/skeleton.liquid new file mode 100644 index 000000000..072730f4e --- /dev/null +++ b/liquid/examples/skeleton.liquid @@ -0,0 +1,5 @@ +
+
+
+
+
diff --git a/liquid/examples/slider.liquid b/liquid/examples/slider.liquid new file mode 100644 index 000000000..872957b00 --- /dev/null +++ b/liquid/examples/slider.liquid @@ -0,0 +1,46 @@ +
+ Range +
+ + +
+ +
diff --git a/liquid/examples/spinner.liquid b/liquid/examples/spinner.liquid new file mode 100644 index 000000000..2d1838cdf --- /dev/null +++ b/liquid/examples/spinner.liquid @@ -0,0 +1,11 @@ + + + diff --git a/liquid/examples/switch.liquid b/liquid/examples/switch.liquid new file mode 100644 index 000000000..275de6dda --- /dev/null +++ b/liquid/examples/switch.liquid @@ -0,0 +1,7 @@ + diff --git a/liquid/examples/table.liquid b/liquid/examples/table.liquid new file mode 100644 index 000000000..b425279b3 --- /dev/null +++ b/liquid/examples/table.liquid @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Example data +
ItemQuantityPrice
Example 111000
Example 222000
Example 333000
Total14000
diff --git a/liquid/examples/tabs.liquid b/liquid/examples/tabs.liquid new file mode 100644 index 000000000..36ff92512 --- /dev/null +++ b/liquid/examples/tabs.liquid @@ -0,0 +1,21 @@ +
+
+ + + + + +
+ +
+

Add the product description.

+
+ +
+

Add materials, dimensions or other details.

+
+ +
+

Add care instructions.

+
+
diff --git a/liquid/examples/textarea.liquid b/liquid/examples/textarea.liquid new file mode 100644 index 000000000..c3123c5ab --- /dev/null +++ b/liquid/examples/textarea.liquid @@ -0,0 +1,8 @@ +
+ > +
diff --git a/liquid/examples/toast.liquid b/liquid/examples/toast.liquid new file mode 100644 index 000000000..95eab3629 --- /dev/null +++ b/liquid/examples/toast.liquid @@ -0,0 +1,47 @@ +
+ + +
+
diff --git a/liquid/examples/toc.liquid b/liquid/examples/toc.liquid new file mode 100644 index 000000000..69098e67d --- /dev/null +++ b/liquid/examples/toc.liquid @@ -0,0 +1,11 @@ + +

Details

+

Add your details here.

+

Care

+

Add care instructions here.

diff --git a/liquid/examples/toggle-group.liquid b/liquid/examples/toggle-group.liquid new file mode 100644 index 000000000..b4001a7d7 --- /dev/null +++ b/liquid/examples/toggle-group.liquid @@ -0,0 +1,50 @@ +
+ + +
diff --git a/liquid/examples/toggle.liquid b/liquid/examples/toggle.liquid new file mode 100644 index 000000000..c0f636781 --- /dev/null +++ b/liquid/examples/toggle.liquid @@ -0,0 +1,21 @@ + diff --git a/liquid/examples/tooltip.liquid b/liquid/examples/tooltip.liquid new file mode 100644 index 000000000..0bec9281d --- /dev/null +++ b/liquid/examples/tooltip.liquid @@ -0,0 +1,33 @@ +
+ + + +
+
+ +
+
+
diff --git a/liquid/examples/typography.liquid b/liquid/examples/typography.liquid new file mode 100644 index 000000000..4d7ef3085 --- /dev/null +++ b/liquid/examples/typography.liquid @@ -0,0 +1,9 @@ +
+

Your collection

+

Readable text uses the same semantic tokens as the rest of your theme.

+ +
Add your own content here.
+
diff --git a/liquid/examples/video.liquid b/liquid/examples/video.liquid new file mode 100644 index 000000000..0e1a18e15 --- /dev/null +++ b/liquid/examples/video.liquid @@ -0,0 +1,7 @@ +
+ +
diff --git a/package.json b/package.json index 6af14988c..afcb07a75 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "fulldev-ui", "type": "module", "version": "0.22.3", - "description": "Open-source Astro UI components and blocks", + "description": "Open-source UI components for Astro, React and native Liquid", "homepage": "https://ui.full.dev", "bugs": "https://github.com/fulldotdev/ui/issues", "license": "MIT", @@ -40,7 +40,8 @@ "src/components/examples", "src/components/ui", "src/lib/utils.ts", - "src/styles/global.css" + "src/styles/global.css", + "liquid" ], "publishConfig": { "access": "public" @@ -50,7 +51,8 @@ }, "packageManager": "pnpm@12.11.2", "imports": { - "#styles": "./scripts/styles.mjs" + "#styles": "./scripts/styles.mjs", + "#liquid-fixture": "./tests/helpers/liquid.mjs" }, "scripts": { "astro": "astro", @@ -66,12 +68,14 @@ "ui:add": "node scripts/ui-add.ts", "build": "astro build && pnpm --dir react build && html-validate dist && linkinator dist", "a11y": "pnpm build && playwright install chromium --only-shell && node scripts/a11y.mjs", - "registry:build": "pnpm registry:meta && node scripts/registry-adapt.mjs && shadcn build --output node_modules/.cache/fulldev-registry && node scripts/registry-styles.mjs && shadcn build -c react -o ../node_modules/.cache/fulldev-registry-react && node scripts/registry-styles.mjs node_modules/.cache/fulldev-registry-react react/registry.json public/r/react && prettier --write registry.json react/registry.json public/r registry/styles/adapted --log-level warn", - "registry:check": "pnpm registry:validate && pnpm registry:build && git diff --exit-code -- registry.json react/registry.json public/r registry/styles/adapted", - "registry:validate": "node scripts/registry-validate.mjs && cd react && node ../scripts/registry-validate.mjs", + "registry:build": "pnpm registry:meta && node scripts/registry-adapt.mjs && shadcn build --output node_modules/.cache/fulldev-registry && node scripts/registry-styles.mjs && shadcn build -c react -o ../node_modules/.cache/fulldev-registry-react && node scripts/registry-styles.mjs node_modules/.cache/fulldev-registry-react react/registry.json public/r/react && pnpm registry:liquid && prettier --write registry.json react/registry.json public/r registry/styles/adapted --log-level warn", + "registry:check": "pnpm registry:validate && pnpm registry:build && git diff --exit-code -- registry.json react/registry.json liquid public/r registry/styles/adapted", + "registry:validate": "node scripts/registry-validate.mjs && cd react && node ../scripts/registry-validate.mjs && cd .. && node scripts/registry-liquid.mjs --check", "release": "changeset version && pnpm install --lockfile-only", "release:publish": "pnpm registry:check && changeset publish", - "registry:meta": "node scripts/registry-meta.mjs" + "registry:meta": "node scripts/registry-meta.mjs", + "registry:liquid": "node scripts/registry-liquid.mjs && shadcn build -c liquid -o ../public/r/liquid", + "test:liquid": "node --test tests/liquid.test.mjs" }, "dependencies": { "@data-slot/accordion": "^1.0.2", @@ -136,6 +140,7 @@ "html-validate": "^11.16.1", "knip": "^6.39.0", "linkinator": "^8.1.0", + "liquidjs": "10.25.1", "playwright": "1.64.0", "posthog-js": "1.434.15", "prettier": "^3.9.9", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 159d520cd..9dd451b82 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -340,6 +340,9 @@ importers: linkinator: specifier: ^8.1.0 version: 8.1.0 + liquidjs: + specifier: 10.25.1 + version: 10.25.1 playwright: specifier: 1.64.0 version: 1.64.0 diff --git a/public/r/liquid/accordion.json b/public/r/liquid/accordion.json new file mode 100644 index 000000000..faa5ca49e --- /dev/null +++ b/public/r/liquid/accordion.json @@ -0,0 +1,25 @@ +{ + "$schema": "https://ui.shadcn.com/schema/registry-item.json", + "name": "accordion", + "title": "Accordion", + "description": "Native Liquid accordion with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/runtime.json" + ], + "files": [ + { + "path": "components/accordion.liquid", + "content": "{% doc %}\n Fulldev UI accordion styles. Use native markup with its data-slot parts.\n{% enddoc %}\n\n{{ 'ui-accordion.css' | asset_url | stylesheet_tag }}\n", + "type": "registry:file", + "target": "~/snippets/ui-accordion.liquid" + }, + { + "path": "styles/accordion.css", + "content": "/* The data-slot accordion, and the same parts on native
and , which open\n without JavaScript. */\n[data-slot='accordion'] {\n display: flex;\n flex-direction: column;\n width: 100%;\n}\n[data-slot='accordion-item']:not(:last-child) {\n border-bottom: 1px solid var(--border);\n}\n.accordion__heading {\n font: inherit;\n letter-spacing: var(--tracking-normal);\n}\n[data-slot='accordion-trigger'] {\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n gap: calc(var(--spacing) * 4);\n width: 100%;\n padding-block: calc(var(--spacing) * 4);\n border: 1px solid transparent;\n border-radius: var(--radius-md);\n font-size: 0.875rem;\n font-weight: 500;\n line-height: 1.43;\n text-align: left;\n list-style: none;\n outline: none;\n}\nsummary[data-slot='accordion-trigger']::-webkit-details-marker {\n display: none;\n}\n[data-slot='accordion-trigger']:hover {\n text-decoration: underline;\n}\n[data-slot='accordion-trigger']:focus-visible {\n border-color: var(--ring);\n box-shadow: var(--focus-ring);\n}\n[data-slot='accordion-trigger'] > .icon {\n width: calc(var(--spacing) * 4);\n height: calc(var(--spacing) * 4);\n margin-left: auto;\n color: var(--muted-foreground);\n transition: rotate 200ms;\n}\n[data-slot='accordion-item'][data-state='open'] [data-slot='accordion-trigger'] > .icon,\ndetails[open] > [data-slot='accordion-trigger'] > .icon {\n rotate: 180deg;\n}\n[data-slot='accordion-content'] {\n overflow: hidden;\n font-size: 0.875rem;\n}\n[data-slot='accordion'] [data-slot='accordion-content'] {\n height: var(--accordion-panel-height);\n transition: height 200ms ease;\n}\n[data-slot='accordion-content'][data-starting-style],\n[data-slot='accordion-content'][data-ending-style] {\n height: 0;\n}\n[data-slot='accordion-content-inner'] {\n padding-bottom: calc(var(--spacing) * 4);\n}\n", + "type": "registry:file", + "target": "~/assets/ui-accordion.css" + } + ], + "type": "registry:item" +} \ No newline at end of file diff --git a/public/r/liquid/alert-dialog.json b/public/r/liquid/alert-dialog.json new file mode 100644 index 000000000..fca6f33c9 --- /dev/null +++ b/public/r/liquid/alert-dialog.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://ui.shadcn.com/schema/registry-item.json", + "name": "alert-dialog", + "title": "Alert Dialog", + "description": "Native Liquid alert-dialog with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/triggers.json", + "https://ui.full.dev/r/liquid/button.json", + "https://ui.full.dev/r/liquid/runtime.json" + ], + "files": [ + { + "path": "components/alert-dialog.liquid", + "content": "{% doc %}\n Fulldev UI alert-dialog styles. Use native markup with its data-slot parts.\n{% enddoc %}\n\n{% stylesheet %}\n /* A confirmation over the page, as Vega's alert dialog: a centered stack on phones and in the small\n size; from 640px the default size aligns text left with the media beside the title and description. */\n [data-slot='alert-dialog-overlay'] {\n position: fixed;\n inset: 0;\n z-index: calc(var(--alert-dialog-overlay-stack-index, 0) * 2 + 100);\n background: var(--scrim);\n transition: opacity 100ms;\n }\n @supports (backdrop-filter: blur(4px)) {\n [data-slot='alert-dialog-overlay'] {\n backdrop-filter: blur(4px);\n }\n }\n [data-slot='alert-dialog-content'] {\n position: fixed;\n top: 50%;\n left: 50%;\n z-index: calc(var(--alert-dialog-content-stack-index, 0) * 2 + 101);\n display: grid;\n gap: calc(var(--spacing) * 6);\n width: calc(100% - 2rem);\n max-width: 20rem;\n padding: calc(var(--spacing) * 6);\n border-radius: var(--radius-xl);\n background: var(--popover);\n color: var(--popover-foreground);\n font-size: 0.875rem;\n box-shadow: 0 0 0 1px color-mix(in oklch, var(--foreground) 10%, transparent);\n outline: none;\n translate: -50% -50%;\n transition:\n opacity 100ms,\n scale 100ms;\n }\n [data-slot='alert-dialog-overlay']:is([data-starting-style], [data-ending-style]) {\n opacity: 0;\n }\n [data-slot='alert-dialog-content']:is([data-starting-style], [data-ending-style]) {\n opacity: 0;\n scale: 0.95;\n }\n [data-slot='alert-dialog-header'] {\n display: grid;\n grid-template-rows: auto 1fr;\n place-items: center;\n gap: calc(var(--spacing) * 1.5);\n text-align: center;\n }\n [data-slot='alert-dialog-header']:has([data-slot='alert-dialog-media']) {\n grid-template-rows: auto auto 1fr;\n column-gap: calc(var(--spacing) * 6);\n }\n [data-slot='alert-dialog-media'] {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: calc(var(--spacing) * 16);\n height: calc(var(--spacing) * 16);\n margin-bottom: calc(var(--spacing) * 2);\n border-radius: var(--radius-md);\n background: var(--muted);\n }\n [data-slot='alert-dialog-media'] > .icon {\n width: calc(var(--spacing) * 8);\n height: calc(var(--spacing) * 8);\n }\n [data-slot='alert-dialog-title'] {\n font-size: 1.125rem;\n font-weight: 500;\n letter-spacing: var(--tracking-normal);\n }\n [data-slot='alert-dialog-description'] {\n color: var(--muted-foreground);\n text-wrap: balance;\n }\n [data-slot='alert-dialog-footer'] {\n display: flex;\n flex-direction: column-reverse;\n gap: calc(var(--spacing) * 2);\n }\n [data-slot='alert-dialog-content'][data-size='sm'] [data-slot='alert-dialog-footer'] {\n display: grid;\n grid-template-columns: 1fr 1fr;\n }\n @media (min-width: 640px) {\n [data-slot='alert-dialog-content']:not([data-size='sm']) {\n max-width: 32rem;\n }\n [data-slot='alert-dialog-content']:not([data-size='sm']) [data-slot='alert-dialog-header'] {\n place-items: start;\n text-align: left;\n }\n [data-slot='alert-dialog-content']:not([data-size='sm'])\n [data-slot='alert-dialog-header']:has([data-slot='alert-dialog-media']) {\n grid-template-rows: auto 1fr;\n }\n [data-slot='alert-dialog-content']:not([data-size='sm']) [data-slot='alert-dialog-media'] {\n grid-row: span 2;\n }\n [data-slot='alert-dialog-content']:not([data-size='sm'])\n [data-slot='alert-dialog-header']:has([data-slot='alert-dialog-media'])\n > [data-slot='alert-dialog-title'] {\n grid-column-start: 2;\n }\n [data-slot='alert-dialog-content']:not([data-size='sm']) [data-slot='alert-dialog-footer'] {\n flex-direction: row;\n justify-content: flex-end;\n }\n }\n{% endstylesheet %}\n", + "type": "registry:file", + "target": "~/snippets/ui-alert-dialog.liquid" + } + ], + "type": "registry:item" +} \ No newline at end of file diff --git a/public/r/liquid/alert.json b/public/r/liquid/alert.json new file mode 100644 index 000000000..d1f036b39 --- /dev/null +++ b/public/r/liquid/alert.json @@ -0,0 +1,18 @@ +{ + "$schema": "https://ui.shadcn.com/schema/registry-item.json", + "name": "alert", + "title": "Alert", + "description": "Native Liquid alert with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json" + ], + "files": [ + { + "path": "components/alert.liquid", + "content": "{% doc %}\n Fulldev UI alert styles. Use native markup with its data-slot parts.\n{% enddoc %}\n\n{% stylesheet %}\n [data-slot='alert'] {\n display: grid;\n gap: calc(var(--spacing) * 0.5);\n padding: calc(var(--spacing) * 3) calc(var(--spacing) * 4);\n border: 1px solid var(--border);\n border-radius: var(--radius-lg);\n background: var(--card);\n color: var(--card-foreground);\n font-size: 0.875rem;\n text-align: left;\n }\n [data-slot='alert']:has(> .icon) {\n grid-template-columns: auto 1fr;\n column-gap: calc(var(--spacing) * 2.5);\n }\n [data-slot='alert'] > .icon {\n grid-row: span 2;\n width: calc(var(--spacing) * 4);\n height: calc(var(--spacing) * 4);\n translate: 0 calc(var(--spacing) * 0.5);\n color: currentcolor;\n }\n [data-slot='alert'][data-variant='destructive'] {\n color: var(--destructive);\n }\n [data-slot='alert-title'] {\n font-weight: 500;\n }\n [data-slot='alert-description'] {\n color: var(--muted-foreground);\n text-wrap: balance;\n }\n [data-slot='alert'][data-variant='destructive'] [data-slot='alert-description'] {\n color: color-mix(in oklch, var(--destructive) 90%, transparent);\n }\n [data-slot='alert'] ul {\n margin: 0;\n padding-left: calc(var(--spacing) * 4.5);\n }\n{% endstylesheet %}\n", + "type": "registry:file", + "target": "~/snippets/ui-alert.liquid" + } + ], + "type": "registry:item" +} \ No newline at end of file diff --git a/public/r/liquid/aspect-ratio.json b/public/r/liquid/aspect-ratio.json new file mode 100644 index 000000000..b5e021608 --- /dev/null +++ b/public/r/liquid/aspect-ratio.json @@ -0,0 +1,18 @@ +{ + "$schema": "https://ui.shadcn.com/schema/registry-item.json", + "name": "aspect-ratio", + "title": "Aspect Ratio", + "description": "Native Liquid aspect-ratio with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json" + ], + "files": [ + { + "path": "components/aspect-ratio.liquid", + "content": "{% doc %}\n Fulldev UI aspect-ratio styles. Use native markup with its data-slot parts.\n{% enddoc %}\n\n{% stylesheet %}\n /* Media at a fixed ratio, covered by its image on a muted fill until it loads. The caller rounds it\n as its Fulldev UI block does; framed adds the rounded border of Fulldev UI's video. */\n [data-slot='aspect-ratio'] {\n position: relative;\n overflow: hidden;\n aspect-ratio: var(--ratio, 4 / 3);\n background: var(--muted);\n }\n [data-slot='aspect-ratio'][data-variant='framed'] {\n border: 1px solid var(--border);\n border-radius: var(--radius-xl);\n }\n [data-slot='aspect-ratio'] > :is(img, svg, video, iframe, picture) {\n position: absolute;\n inset: 0;\n width: 100%;\n height: 100%;\n object-fit: cover;\n }\n [data-slot='aspect-ratio'] > svg {\n fill: color-mix(in oklch, var(--muted-foreground) 35%, transparent);\n }\n{% endstylesheet %}\n", + "type": "registry:file", + "target": "~/snippets/ui-aspect-ratio.liquid" + } + ], + "type": "registry:item" +} \ No newline at end of file diff --git a/public/r/liquid/attachment.json b/public/r/liquid/attachment.json new file mode 100644 index 000000000..bf4e51652 --- /dev/null +++ b/public/r/liquid/attachment.json @@ -0,0 +1,20 @@ +{ + "$schema": "https://ui.shadcn.com/schema/registry-item.json", + "name": "attachment", + "title": "Attachment", + "description": "Native Liquid attachment with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/button.json", + "https://ui.full.dev/r/liquid/icon.json" + ], + "files": [ + { + "path": "components/attachment.liquid", + "content": "{% doc %}\n Fulldev UI attachment styles. Use native markup with its data-slot parts.\n{% enddoc %}\n\n{% stylesheet %}\n /* A file with its icon or preview, name and actions, as Fulldev UI's attachment. The caller sets\n data-state: idle (dashed), uploading or processing (pulsing name), error, or done. */\n [data-slot='attachment'] {\n position: relative;\n display: flex;\n flex-shrink: 0;\n flex-wrap: wrap;\n align-items: center;\n gap: calc(var(--spacing) * 2);\n width: fit-content;\n min-width: 10rem;\n max-width: 100%;\n border: 1px solid var(--border);\n border-radius: var(--radius-xl);\n background: var(--card);\n color: var(--card-foreground);\n font-size: 0.875rem;\n transition: background-color 150ms;\n }\n [data-slot='attachment']:has([data-slot='attachment-content']) {\n padding: calc(var(--spacing) * 2) calc(var(--spacing) * 2.5);\n }\n [data-slot='attachment']:focus-within {\n box-shadow: 0 0 0 1px color-mix(in oklch, var(--ring) 50%, transparent);\n }\n [data-slot='attachment']:has([data-slot='attachment-trigger']):hover {\n background: color-mix(in oklch, var(--muted) 50%, transparent);\n }\n [data-slot='attachment'][data-state='idle'] {\n border-style: dashed;\n }\n [data-slot='attachment'][data-state='error'] {\n border-color: color-mix(in oklch, var(--destructive) 30%, transparent);\n }\n [data-slot='attachment'][data-size='sm'] {\n gap: calc(var(--spacing) * 2.5);\n font-size: 0.75rem;\n }\n [data-slot='attachment'][data-size='sm']:has([data-slot='attachment-content']) {\n padding: calc(var(--spacing) * 1.5) calc(var(--spacing) * 2);\n }\n [data-slot='attachment-media'] {\n position: relative;\n display: flex;\n flex-shrink: 0;\n align-items: center;\n justify-content: center;\n width: calc(var(--spacing) * 10);\n aspect-ratio: 1;\n overflow: hidden;\n border-radius: var(--radius-lg);\n background: var(--muted);\n color: var(--foreground);\n }\n [data-slot='attachment-media'] > .icon {\n width: calc(var(--spacing) * 4);\n height: calc(var(--spacing) * 4);\n }\n [data-slot='attachment'][data-size='sm'] [data-slot='attachment-media'] {\n width: calc(var(--spacing) * 8);\n }\n [data-slot='attachment'][data-state='error'] [data-slot='attachment-media'] {\n background: color-mix(in oklch, var(--destructive) 10%, transparent);\n color: var(--destructive);\n }\n [data-slot='attachment-content'] {\n display: flex;\n flex: 1;\n flex-direction: column;\n min-width: 0;\n line-height: 1.25;\n }\n [data-slot='attachment-title'],\n [data-slot='attachment-description'] {\n display: block;\n max-width: 100%;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n [data-slot='attachment-title'] {\n font-weight: 500;\n }\n /* The skeleton's pulse reads at about 3.6:1 at 0.5; the name pulses to 0.65 to keep 4.5:1. */\n [data-slot='attachment'][data-state='uploading'] [data-slot='attachment-title'] {\n animation: attachment-pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;\n }\n @keyframes attachment-pulse {\n 50% {\n opacity: 0.65;\n }\n }\n [data-slot='attachment-description'] {\n margin-top: calc(var(--spacing) * 0.5);\n color: var(--muted-foreground);\n font-size: 0.75rem;\n }\n [data-slot='attachment'][data-state='error'] [data-slot='attachment-description'] {\n color: var(--destructive);\n }\n [data-slot='attachment-actions'] {\n position: relative;\n z-index: 2;\n display: flex;\n flex-shrink: 0;\n align-items: center;\n }\n /* A link or button that covers the whole attachment, under its actions. */\n [data-slot='attachment-trigger'] {\n position: absolute;\n inset: 0;\n z-index: 1;\n border-radius: inherit;\n outline: none;\n }\n [data-slot='attachment-group'] {\n display: flex;\n gap: calc(var(--spacing) * 3);\n min-width: 0;\n overflow-x: auto;\n overscroll-behavior-x: contain;\n padding-block: var(--spacing);\n scroll-snap-type: x mandatory;\n }\n [data-slot='attachment-group'] > [data-slot='attachment'] {\n flex: none;\n scroll-snap-align: start;\n }\n{% endstylesheet %}\n", + "type": "registry:file", + "target": "~/snippets/ui-attachment.liquid" + } + ], + "type": "registry:item" +} \ No newline at end of file diff --git a/public/r/liquid/avatar.json b/public/r/liquid/avatar.json new file mode 100644 index 000000000..373cd5eab --- /dev/null +++ b/public/r/liquid/avatar.json @@ -0,0 +1,18 @@ +{ + "$schema": "https://ui.shadcn.com/schema/registry-item.json", + "name": "avatar", + "title": "Avatar", + "description": "Native Liquid avatar with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json" + ], + "files": [ + { + "path": "components/avatar.liquid", + "content": "{% doc %}\n Fulldev UI avatar styles. Use native markup with its data-slot parts.\n{% enddoc %}\n\n{% stylesheet %}\n [data-slot='avatar'] {\n position: relative;\n display: grid;\n flex: none;\n place-items: center;\n width: calc(var(--spacing) * 8);\n height: calc(var(--spacing) * 8);\n overflow: hidden;\n border-radius: 50%;\n font-size: 0.75rem;\n font-weight: 500;\n }\n [data-slot='avatar'][data-size='sm'] {\n width: calc(var(--spacing) * 6);\n height: calc(var(--spacing) * 6);\n }\n [data-slot='avatar'][data-size='lg'] {\n width: calc(var(--spacing) * 10);\n height: calc(var(--spacing) * 10);\n font-size: 0.875rem;\n }\n [data-slot='avatar-image'] {\n width: 100%;\n height: 100%;\n border-radius: 50%;\n object-fit: cover;\n }\n /* Overlapping avatars, each ringed with the background. */\n [data-slot='avatar-group'] {\n display: flex;\n }\n [data-slot='avatar-group'] > [data-slot='avatar'] {\n box-shadow: 0 0 0 2px var(--background);\n }\n [data-slot='avatar-group'] > [data-slot='avatar'] + [data-slot='avatar'] {\n margin-inline-start: calc(var(--spacing) * -2);\n }\n [data-slot='avatar-fallback'] {\n display: grid;\n place-items: center;\n width: 100%;\n height: 100%;\n border-radius: 50%;\n background: var(--muted);\n color: var(--muted-foreground);\n }\n{% endstylesheet %}\n", + "type": "registry:file", + "target": "~/snippets/ui-avatar.liquid" + } + ], + "type": "registry:item" +} \ No newline at end of file diff --git a/public/r/liquid/badge.json b/public/r/liquid/badge.json new file mode 100644 index 000000000..7eef4c300 --- /dev/null +++ b/public/r/liquid/badge.json @@ -0,0 +1,18 @@ +{ + "$schema": "https://ui.shadcn.com/schema/registry-item.json", + "name": "badge", + "title": "Badge", + "description": "Native Liquid badge with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json" + ], + "files": [ + { + "path": "components/badge.liquid", + "content": "{% doc %}\n Fulldev UI badge styles. Use native markup with its data-slot parts.\n{% enddoc %}\n\n{% stylesheet %}\n /* A label wider than its container wraps, as a button does; one line keeps Vega's 20px. */\n [data-slot='badge'] {\n display: inline-flex;\n flex-shrink: 0;\n align-items: center;\n justify-content: center;\n gap: var(--spacing);\n width: fit-content;\n max-width: 100%;\n min-height: calc(var(--spacing) * 5);\n padding: calc(var(--spacing) * 0.5) calc(var(--spacing) * 2);\n overflow: hidden;\n border: 1px solid transparent;\n border-radius: var(--radius-4xl);\n background: var(--primary);\n color: var(--primary-foreground);\n font-size: 0.75rem;\n font-weight: 500;\n line-height: calc(var(--spacing) * 3.5);\n text-align: center;\n overflow-wrap: anywhere;\n text-underline-offset: 4px;\n transition:\n background-color 150ms,\n color 150ms;\n }\n [data-slot='badge'] > .icon {\n width: calc(var(--spacing) * 3);\n height: calc(var(--spacing) * 3);\n }\n a[data-slot='badge']:hover {\n background: color-mix(in oklch, var(--primary) 80%, transparent);\n }\n a[data-slot='badge']:focus-visible {\n border-color: var(--ring);\n outline: none;\n box-shadow: var(--focus-ring);\n }\n [data-slot='badge'][data-variant='secondary'] {\n background: var(--secondary);\n color: var(--secondary-foreground);\n }\n a[data-slot='badge'][data-variant='secondary']:hover {\n background: color-mix(in oklch, var(--secondary) 80%, transparent);\n }\n [data-slot='badge'][data-variant='outline'] {\n border-color: var(--border);\n background: transparent;\n color: var(--foreground);\n }\n a[data-slot='badge'][data-variant='outline']:hover {\n background: var(--muted);\n color: var(--muted-foreground);\n }\n [data-slot='badge'][data-variant='destructive'] {\n background: light-dark(\n color-mix(in oklch, var(--destructive) 10%, transparent),\n color-mix(in oklch, var(--destructive) 20%, transparent)\n );\n color: var(--destructive);\n }\n a[data-slot='badge'][data-variant='destructive']:hover {\n background: color-mix(in oklch, var(--destructive) 20%, transparent);\n }\n [data-slot='badge'][data-variant='ghost'] {\n background: transparent;\n color: var(--foreground);\n }\n a[data-slot='badge'][data-variant='ghost']:hover {\n background: light-dark(var(--muted), color-mix(in oklch, var(--muted) 50%, transparent));\n color: var(--muted-foreground);\n }\n [data-slot='badge'][data-variant='link'] {\n background: transparent;\n color: var(--primary);\n }\n a[data-slot='badge'][data-variant='link']:hover {\n background: transparent;\n text-decoration: underline;\n }\n{% endstylesheet %}\n", + "type": "registry:file", + "target": "~/snippets/ui-badge.liquid" + } + ], + "type": "registry:item" +} \ No newline at end of file diff --git a/public/r/liquid/banner.json b/public/r/liquid/banner.json new file mode 100644 index 000000000..e151a0f0a --- /dev/null +++ b/public/r/liquid/banner.json @@ -0,0 +1,18 @@ +{ + "$schema": "https://ui.shadcn.com/schema/registry-item.json", + "name": "banner", + "title": "Banner", + "description": "Native Liquid banner with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json" + ], + "files": [ + { + "path": "components/banner.liquid", + "content": "{% doc %}\n Fulldev UI banner styles. Use native markup with its data-slot parts.\n{% enddoc %}\n\n{% stylesheet %}\n /* Fulldev UI's banner: inverted across the page, or a floating framed panel inset by the gutter. */\n [data-slot='banner'] {\n position: relative;\n display: flex;\n align-items: center;\n width: 100%;\n padding-block: 8px;\n background: var(--foreground);\n color: var(--background);\n }\n [data-slot='banner'][data-variant='floating'] {\n width: calc(100% - 2 * var(--gutter));\n max-width: calc(var(--container) - 2 * var(--gutter));\n margin: 8px auto;\n border: 1px solid var(--border);\n border-radius: var(--radius-lg);\n background: var(--background);\n color: var(--foreground);\n box-shadow: var(--shadow-sm);\n }\n [data-slot='banner-container'] {\n position: relative;\n display: flex;\n gap: 16px;\n width: 100%;\n max-width: var(--container);\n margin-inline: auto;\n padding-inline: var(--gutter);\n }\n [data-slot='banner'] a:not([data-slot]) {\n font-weight: 500;\n text-decoration: underline;\n text-underline-offset: 4px;\n }\n{% endstylesheet %}\n", + "type": "registry:file", + "target": "~/snippets/ui-banner.liquid" + } + ], + "type": "registry:item" +} \ No newline at end of file diff --git a/public/r/liquid/breadcrumb.json b/public/r/liquid/breadcrumb.json new file mode 100644 index 000000000..8b0106bdd --- /dev/null +++ b/public/r/liquid/breadcrumb.json @@ -0,0 +1,18 @@ +{ + "$schema": "https://ui.shadcn.com/schema/registry-item.json", + "name": "breadcrumb", + "title": "Breadcrumb", + "description": "Native Liquid breadcrumb with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json" + ], + "files": [ + { + "path": "components/breadcrumb.liquid", + "content": "{% doc %}\n Fulldev UI breadcrumb styles. Use native markup with its data-slot parts.\n{% enddoc %}\n\n{% stylesheet %}\n /* The breadcrumb nav; long page titles wrap inside it. */\n [data-slot='breadcrumb'] {\n min-width: 0;\n overflow-wrap: anywhere;\n }\n [data-slot='breadcrumb-list'] {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: calc(var(--spacing) * 1.5);\n margin: 0;\n padding: 0;\n list-style: none;\n font-size: 0.875rem;\n color: var(--muted-foreground);\n overflow-wrap: anywhere;\n }\n @media (min-width: 640px) {\n [data-slot='breadcrumb-list'] {\n gap: calc(var(--spacing) * 2.5);\n }\n }\n [data-slot='breadcrumb-item'] {\n display: inline-flex;\n align-items: center;\n gap: calc(var(--spacing) * 1.5);\n }\n [data-slot='breadcrumb-link'] {\n transition: color 150ms;\n }\n [data-slot='breadcrumb-link']:hover {\n color: var(--foreground);\n }\n [data-slot='breadcrumb-page'] {\n color: var(--foreground);\n }\n [data-slot='breadcrumb-separator'] > .icon {\n width: calc(var(--spacing) * 3.5);\n height: calc(var(--spacing) * 3.5);\n }\n{% endstylesheet %}\n", + "type": "registry:file", + "target": "~/snippets/ui-breadcrumb.liquid" + } + ], + "type": "registry:item" +} \ No newline at end of file diff --git a/public/r/liquid/button-group.json b/public/r/liquid/button-group.json new file mode 100644 index 000000000..b639d4947 --- /dev/null +++ b/public/r/liquid/button-group.json @@ -0,0 +1,19 @@ +{ + "$schema": "https://ui.shadcn.com/schema/registry-item.json", + "name": "button-group", + "title": "Button Group", + "description": "Native Liquid button-group with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/button.json" + ], + "files": [ + { + "path": "components/button-group.liquid", + "content": "{% doc %}\n Fulldev UI button-group styles. Use native markup with its data-slot parts.\n{% enddoc %}\n\n{% stylesheet %}\n [data-slot='button-group'] {\n display: flex;\n align-items: stretch;\n width: fit-content;\n }\n [data-slot='button-group'] > :focus-visible {\n position: relative;\n z-index: 1;\n }\n [data-slot='button-group'] > input {\n flex: 1;\n }\n [data-slot='button-group']:not([data-orientation='vertical']) > [data-slot]:not(:last-child) {\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n }\n [data-slot='button-group']:not([data-orientation='vertical']) > [data-slot] ~ [data-slot] {\n border-left-width: 0;\n border-top-left-radius: 0;\n border-bottom-left-radius: 0;\n }\n [data-slot='button-group'][data-orientation='vertical'] {\n flex-direction: column;\n }\n [data-slot='button-group'][data-orientation='vertical'] > [data-slot]:not(:last-child) {\n border-bottom-right-radius: 0;\n border-bottom-left-radius: 0;\n }\n [data-slot='button-group'][data-orientation='vertical'] > [data-slot] ~ [data-slot] {\n border-top-width: 0;\n border-top-left-radius: 0;\n border-top-right-radius: 0;\n }\n [data-slot='button-group-text'] {\n display: flex;\n align-items: center;\n gap: calc(var(--spacing) * 2);\n padding-inline: calc(var(--spacing) * 2.5);\n border: 1px solid var(--border);\n border-radius: var(--radius-md);\n background: var(--muted);\n font-size: 0.875rem;\n font-weight: 500;\n box-shadow: var(--shadow-xs);\n }\n [data-slot='button-group-separator'] {\n align-self: stretch;\n width: 1px;\n background: var(--input);\n }\n{% endstylesheet %}\n", + "type": "registry:file", + "target": "~/snippets/ui-button-group.liquid" + } + ], + "type": "registry:item" +} \ No newline at end of file diff --git a/public/r/liquid/button.json b/public/r/liquid/button.json new file mode 100644 index 000000000..41e66de40 --- /dev/null +++ b/public/r/liquid/button.json @@ -0,0 +1,18 @@ +{ + "$schema": "https://ui.shadcn.com/schema/registry-item.json", + "name": "button", + "title": "Button", + "description": "Native Liquid button with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json" + ], + "files": [ + { + "path": "components/button.liquid", + "content": "{% doc %}\n A button or link. Omitting label registers its styles for native data-slot markup.\n\n @param {string} [label] - Plain text label, escaped.\n @param {string} [href] - Link destination.\n @param {string} [variant] - Visual variant.\n @param {string} [size] - Button size.\n @param {string} [type] - Native button type, defaults to button.\n @param {boolean} [disabled] - Disable the button.\n{% enddoc %}\n\n{% if label != blank %}\n {% if href != blank %}\n \n {{- label | escape -}}\n \n {% else %}\n \n {{ label | escape }}\n \n {% endif %}\n{% endif %}\n\n{% stylesheet %}\n /* A part that keeps a behavioral data-slot of its own, such as dialog-close or carousel-next,\n takes the button look with data-button and the same data-variant and data-size values. */\n /* Merchant labels can be long: a label wider than its container wraps inside the button, which\n then grows from its fixed height. One line keeps the Vega height in every size. */\n :is([data-slot='button'], [data-button]) {\n display: inline-flex;\n flex-shrink: 0;\n align-items: center;\n justify-content: center;\n gap: calc(var(--spacing) * 1.5);\n max-width: 100%;\n min-height: calc(var(--spacing) * 9);\n padding: calc(var(--spacing) * 2) calc(var(--spacing) * 2.5);\n border: 1px solid transparent;\n border-radius: var(--radius-md);\n background: var(--primary);\n background-clip: padding-box;\n color: var(--primary-foreground);\n font-size: 0.875rem;\n font-weight: 500;\n line-height: 1.25;\n text-align: center;\n overflow-wrap: anywhere;\n text-decoration: none;\n text-underline-offset: 4px;\n cursor: pointer;\n user-select: none;\n outline: none;\n transition:\n background-color 150ms,\n color 150ms,\n border-color 150ms,\n box-shadow 150ms,\n translate 150ms;\n }\n :is([data-slot='button'], [data-button]) > .icon {\n width: calc(var(--spacing) * 4);\n height: calc(var(--spacing) * 4);\n pointer-events: none;\n }\n :is([data-slot='button'], [data-button]):where(:has(> .icon:last-child:not(:first-child))) {\n padding-right: calc(var(--spacing) * 2);\n }\n :is([data-slot='button'], [data-button]):where(:has(> .icon:first-child:not(:last-child))) {\n padding-left: calc(var(--spacing) * 2);\n }\n :is([data-slot='button'], [data-button]):hover {\n background-color: color-mix(in oklch, var(--primary) 80%, transparent);\n }\n :is([data-slot='button'], [data-button]):focus-visible {\n border-color: var(--ring);\n box-shadow: var(--focus-ring);\n }\n :is([data-slot='button'], [data-button])[aria-invalid='true'] {\n border-color: light-dark(var(--destructive), color-mix(in oklch, var(--destructive) 50%, transparent));\n box-shadow: 0 0 0 3px\n light-dark(\n color-mix(in oklch, var(--destructive) 20%, transparent),\n color-mix(in oklch, var(--destructive) 40%, transparent)\n );\n }\n :is([data-slot='button'], [data-button]):active:not([aria-haspopup]) {\n translate: 0 1px;\n }\n :is([data-slot='button'], [data-button]):disabled,\n :is([data-slot='button'], [data-button])[aria-disabled='true'] {\n opacity: 0.5;\n pointer-events: none;\n }\n :is([data-slot='button'], [data-button])[data-variant='outline'] {\n border-color: light-dark(var(--border), var(--input));\n background-color: light-dark(var(--background), color-mix(in oklch, var(--input) 30%, transparent));\n color: var(--foreground);\n box-shadow: var(--shadow-xs);\n }\n :is([data-slot='button'], [data-button])[data-variant='outline']:hover {\n background-color: light-dark(var(--muted), color-mix(in oklch, var(--input) 50%, transparent));\n color: var(--foreground);\n }\n :is([data-slot='button'], [data-button])[data-variant='outline'][aria-expanded='true'] {\n background-color: var(--muted);\n }\n :is([data-slot='button'], [data-button])[data-variant='outline']:focus-visible {\n border-color: var(--ring);\n box-shadow: var(--focus-ring);\n }\n :is([data-slot='button'], [data-button])[data-variant='secondary'] {\n background-color: var(--secondary);\n color: var(--secondary-foreground);\n }\n :is([data-slot='button'], [data-button])[data-variant='secondary']:hover {\n background-color: color-mix(in oklch, var(--secondary), var(--foreground) 5%);\n }\n :is([data-slot='button'], [data-button])[data-variant='ghost'] {\n background-color: transparent;\n color: inherit;\n }\n :is([data-slot='button'], [data-button])[data-variant='ghost']:hover {\n background-color: light-dark(var(--muted), color-mix(in oklch, var(--muted) 50%, transparent));\n color: var(--foreground);\n }\n :is([data-slot='button'], [data-button])[data-variant='ghost'][aria-expanded='true'] {\n background-color: var(--muted);\n color: var(--foreground);\n }\n :is([data-slot='button'], [data-button])[data-variant='destructive'] {\n background-color: light-dark(\n color-mix(in oklch, var(--destructive) 10%, transparent),\n color-mix(in oklch, var(--destructive) 20%, transparent)\n );\n /* Vega's destructive text on its own tint is about 4:1; mixing in the foreground keeps 4.5:1. */\n color: color-mix(in oklch, var(--destructive) 65%, var(--foreground));\n }\n :is([data-slot='button'], [data-button])[data-variant='destructive']:hover {\n background-color: light-dark(\n color-mix(in oklch, var(--destructive) 20%, transparent),\n color-mix(in oklch, var(--destructive) 30%, transparent)\n );\n }\n :is([data-slot='button'], [data-button])[data-variant='destructive']:focus-visible {\n border-color: color-mix(in oklch, var(--destructive) 40%, transparent);\n box-shadow: 0 0 0 3px\n light-dark(\n color-mix(in oklch, var(--destructive) 20%, transparent),\n color-mix(in oklch, var(--destructive) 40%, transparent)\n );\n }\n :is([data-slot='button'], [data-button])[data-variant='link'] {\n background: none;\n color: var(--primary);\n }\n :is([data-slot='button'], [data-button])[data-variant='link']:hover {\n text-decoration: underline;\n }\n /* A fixed white button on photos and video. */\n :is([data-slot='button'], [data-button])[data-variant='media'] {\n background-color: var(--media-button);\n color: var(--media-button-foreground);\n }\n :is([data-slot='button'], [data-button])[data-variant='media']:hover {\n background-color: var(--media-button-hover);\n }\n /* The secondary button on photos: a translucent white fill. */\n :is([data-slot='button'], [data-button])[data-variant='media-secondary'] {\n background-color: var(--media-secondary);\n color: var(--on-media);\n }\n :is([data-slot='button'], [data-button])[data-variant='media-secondary']:hover {\n background-color: var(--media-secondary-hover);\n }\n :is([data-slot='button'], [data-button])[data-size='xs'] {\n gap: var(--spacing);\n min-height: calc(var(--spacing) * 6);\n padding: calc(var(--spacing) * 0.75) calc(var(--spacing) * 2);\n border-radius: min(var(--radius-md), 8px);\n font-size: 0.75rem;\n }\n :is([data-slot='button'], [data-button])[data-size='xs'] > .icon {\n width: calc(var(--spacing) * 3);\n height: calc(var(--spacing) * 3);\n }\n :is([data-slot='button'], [data-button])[data-size='sm'] {\n gap: var(--spacing);\n min-height: calc(var(--spacing) * 8);\n padding-block: calc(var(--spacing) * 1.5);\n border-radius: min(var(--radius-md), 10px);\n }\n :is([data-slot='button'], [data-button])[data-size='lg'] {\n min-height: calc(var(--spacing) * 10);\n }\n :is([data-slot='button'], [data-button])[data-size^='icon'] {\n width: calc(var(--spacing) * 9);\n height: calc(var(--spacing) * 9);\n min-height: 0;\n padding: 0;\n }\n :is([data-slot='button'], [data-button])[data-size='icon-xs'] {\n width: calc(var(--spacing) * 6);\n height: calc(var(--spacing) * 6);\n border-radius: min(var(--radius-md), 8px);\n }\n :is([data-slot='button'], [data-button])[data-size='icon-xs'] > .icon {\n width: calc(var(--spacing) * 3);\n height: calc(var(--spacing) * 3);\n }\n :is([data-slot='button'], [data-button])[data-size='icon-sm'] {\n width: calc(var(--spacing) * 8);\n height: calc(var(--spacing) * 8);\n border-radius: min(var(--radius-md), 10px);\n }\n :is([data-slot='button'], [data-button])[data-size='icon-lg'] {\n width: calc(var(--spacing) * 10);\n height: calc(var(--spacing) * 10);\n }\n{% endstylesheet %}\n", + "type": "registry:file", + "target": "~/snippets/ui-button.liquid" + } + ], + "type": "registry:item" +} \ No newline at end of file diff --git a/public/r/liquid/card.json b/public/r/liquid/card.json new file mode 100644 index 000000000..77caa96e5 --- /dev/null +++ b/public/r/liquid/card.json @@ -0,0 +1,25 @@ +{ + "$schema": "https://ui.shadcn.com/schema/registry-item.json", + "name": "card", + "title": "Card", + "description": "Native Liquid card with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/aspect-ratio.json" + ], + "files": [ + { + "path": "components/card.liquid", + "content": "{% doc %}\n Fulldev UI card styles. Use native markup with its data-slot parts.\n{% enddoc %}\n\n{{ 'ui-card.css' | asset_url | stylesheet_tag }}\n", + "type": "registry:file", + "target": "~/snippets/ui-card.liquid" + }, + { + "path": "styles/card.css", + "content": "/* The card surface and its parts, as in Vega. Header, content and footer carry the inline\n padding, so media placed first spans the full width. Fulldev UI's blocks restyle the card with\n classes; the theme names the two they use, outline (a border on the background) and muted (a\n muted fill), as variants. A card that is one link holds no buttons or forms. */\n[data-slot='card'] {\n --card-spacing: calc(var(--spacing) * 6);\n display: flex;\n flex-direction: column;\n gap: var(--card-spacing);\n min-width: 0;\n padding-block: var(--card-spacing);\n overflow: hidden;\n border-radius: var(--radius-xl);\n background: var(--card);\n color: var(--card-foreground);\n font-size: 0.875rem;\n text-decoration: none;\n box-shadow:\n var(--shadow-xs),\n 0 0 0 1px color-mix(in oklch, var(--foreground) 10%, transparent);\n outline: none;\n transition:\n background-color 150ms,\n box-shadow 150ms;\n}\n[data-slot='card'][data-size='sm'] {\n --card-spacing: calc(var(--spacing) * 4);\n}\n[data-slot='card']:has(> :is(img, [data-slot='aspect-ratio']):first-child) {\n padding-top: 0;\n}\n[data-slot='card-header'] {\n display: grid;\n grid-auto-rows: min-content;\n align-items: start;\n gap: var(--spacing);\n padding-inline: var(--card-spacing);\n}\n[data-slot='card-header']:has(> [data-slot='card-action']) {\n grid-template-columns: 1fr auto;\n}\n[data-slot='card-action'] {\n grid-row: 1 / span 2;\n grid-column: 2;\n place-self: start end;\n}\n[data-slot='card-title'] {\n font-family: inherit;\n font-size: 1rem;\n font-weight: 500;\n line-height: 1.5;\n letter-spacing: var(--tracking-normal);\n}\n[data-slot='card'][data-size='sm'] [data-slot='card-title'] {\n font-size: 0.875rem;\n}\n[data-slot='card-description'] {\n color: var(--muted-foreground);\n font-size: 0.875rem;\n line-height: 1.625;\n}\n[data-slot='card-content'] {\n padding-inline: var(--card-spacing);\n}\n[data-slot='card-footer'] {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: calc(var(--spacing) * 2);\n margin-top: auto;\n padding-inline: var(--card-spacing);\n}\n[data-slot='card'][data-variant='outline'] {\n border: 1px solid var(--border);\n background: var(--background);\n color: var(--foreground);\n box-shadow: none;\n}\n[data-slot='card'][data-variant='muted'] {\n background: color-mix(in oklch, var(--muted) 50%, transparent);\n color: var(--foreground);\n box-shadow: none;\n}\na[data-slot='card']:not([data-variant='muted']):hover {\n background-color: color-mix(in oklch, var(--muted) 40%, transparent);\n}\na[data-slot='card'][data-variant='muted']:hover {\n background-color: color-mix(in oklch, var(--muted) 70%, transparent);\n}\n/* After the variants, which drop the card's shadow, so every linked card shows the ring. */\n[data-slot='card']:focus-visible {\n box-shadow: var(--focus-ring);\n}\n:is([data-slot='card'][data-variant='outline'], [data-slot='card'][data-variant='muted']):focus-visible {\n border-color: var(--ring);\n}\n/* The arrow link at the foot of a content card; inside a linked card it is text. */\n.card-link {\n display: inline-flex;\n align-items: center;\n gap: var(--spacing);\n color: var(--primary);\n font-size: 0.875rem;\n font-weight: 500;\n text-underline-offset: 4px;\n}\n.card-link > .icon {\n width: calc(var(--spacing) * 3.5);\n height: calc(var(--spacing) * 3.5);\n}\na:hover .card-link,\na.card-link:hover {\n text-decoration: underline;\n}\n", + "type": "registry:file", + "target": "~/assets/ui-card.css" + } + ], + "type": "registry:item" +} \ No newline at end of file diff --git a/public/r/liquid/carousel.json b/public/r/liquid/carousel.json new file mode 100644 index 000000000..f350a40a0 --- /dev/null +++ b/public/r/liquid/carousel.json @@ -0,0 +1,26 @@ +{ + "$schema": "https://ui.shadcn.com/schema/registry-item.json", + "name": "carousel", + "title": "Carousel", + "description": "Native Liquid carousel with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/button.json", + "https://ui.full.dev/r/liquid/runtime.json" + ], + "files": [ + { + "path": "components/carousel.liquid", + "content": "{% doc %}\n Fulldev UI carousel styles. Use native markup with its data-slot parts.\n{% enddoc %}\n\n{{ 'ui-carousel.css' | asset_url | stylesheet_tag }}\n", + "type": "registry:file", + "target": "~/snippets/ui-carousel.liquid" + }, + { + "path": "styles/carousel.css", + "content": "[data-slot='carousel'] {\n position: relative;\n display: flex;\n flex-direction: column;\n gap: 24px;\n}\n/* The strip bleeds into the gutters. Each item's box starts at a scroll position and insets its\n content by the gutter, so the controller's positions and the snap points line up with the\n section's columns; the end padding keeps the last item off the edge. It is the containing block\n of absolutely positioned content in the cards, such as screen reader labels, so its overflow\n clips them and they cannot widen the page. */\n[data-slot='carousel-content'] {\n position: relative;\n display: flex;\n margin-inline: calc(var(--gutter) * -1);\n padding-inline-end: var(--gutter);\n overflow-x: auto;\n scroll-snap-type: x mandatory;\n scrollbar-width: none;\n}\n[data-slot='carousel-content']::-webkit-scrollbar {\n display: none;\n}\n[data-slot='carousel'][data-dragging='true'] [data-slot='carousel-content'] {\n cursor: grabbing;\n}\n[data-slot='carousel-item'] {\n flex: 0 0 calc(100% / var(--carousel-columns, 1));\n min-width: 0;\n padding-left: var(--gutter);\n scroll-snap-align: start;\n}\n.carousel__controls {\n display: flex;\n gap: 8px;\n}\n.carousel__controls[data-align='end'] {\n justify-content: flex-end;\n}\n.carousel__controls[data-align='center'] {\n justify-content: center;\n}\n.carousel__controls:not(:has(button:not([hidden]))) {\n display: none;\n}\n.carousel__button[data-button] {\n border-radius: 50%;\n}\n@media (max-width: 1023px) {\n [data-slot='carousel-item'] {\n flex-basis: calc(100% / var(--carousel-columns-tablet, 1));\n }\n}\n@media (max-width: 767px) {\n [data-slot='carousel-item'] {\n flex-basis: calc(100% / var(--carousel-columns-mobile, 1));\n }\n}\n", + "type": "registry:file", + "target": "~/assets/ui-carousel.css" + } + ], + "type": "registry:item" +} \ No newline at end of file diff --git a/public/r/liquid/checkbox.json b/public/r/liquid/checkbox.json new file mode 100644 index 000000000..791e1e3d3 --- /dev/null +++ b/public/r/liquid/checkbox.json @@ -0,0 +1,19 @@ +{ + "$schema": "https://ui.shadcn.com/schema/registry-item.json", + "name": "checkbox", + "title": "Checkbox", + "description": "Native Liquid checkbox with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/input.json" + ], + "files": [ + { + "path": "components/checkbox.liquid", + "content": "{% doc %}\n Fulldev UI checkbox styles. Use native markup with its data-slot parts.\n{% enddoc %}\n\n{% stylesheet %}\n /* Fulldev UI's checkbox: a native checkbox with Vega's box and a check mark. */\n [data-slot='checkbox'] {\n position: relative;\n flex-shrink: 0;\n appearance: none;\n border: 1px solid var(--input);\n border-radius: 4px;\n background-color: light-dark(transparent, color-mix(in oklch, var(--input) 30%, transparent));\n color: var(--primary-foreground);\n box-shadow: var(--shadow-xs);\n outline: none;\n transition: box-shadow 150ms;\n }\n [data-slot='checkbox']::before {\n content: '';\n position: absolute;\n inset: 0;\n margin: auto;\n width: 14px;\n height: 14px;\n background-color: currentcolor;\n opacity: 0;\n mask: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='black' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3.5 8.5l3 3 6-6'/%3E%3C/svg%3E\")\n center / 14px no-repeat;\n }\n [data-slot='checkbox']:checked {\n border-color: var(--primary);\n background-color: var(--primary);\n }\n [data-slot='checkbox']:checked::before {\n opacity: 1;\n }\n [data-slot='checkbox']:focus-visible {\n border-color: var(--ring);\n box-shadow: var(--focus-ring);\n }\n [data-slot='checkbox'][aria-invalid='true'] {\n border-color: light-dark(var(--destructive), color-mix(in oklch, var(--destructive) 50%, transparent));\n box-shadow: 0 0 0 3px\n light-dark(\n color-mix(in oklch, var(--destructive) 20%, transparent),\n color-mix(in oklch, var(--destructive) 40%, transparent)\n );\n }\n /* As Fulldev UI's compiled checkbox: checked keeps the primary border, except in dark mode. */\n [data-slot='checkbox'][aria-invalid='true']:checked {\n border-color: light-dark(var(--primary), color-mix(in oklch, var(--destructive) 50%, transparent));\n }\n{% endstylesheet %}\n", + "type": "registry:file", + "target": "~/snippets/ui-checkbox.liquid" + } + ], + "type": "registry:item" +} \ No newline at end of file diff --git a/public/r/liquid/collapsible.json b/public/r/liquid/collapsible.json new file mode 100644 index 000000000..cb0a06441 --- /dev/null +++ b/public/r/liquid/collapsible.json @@ -0,0 +1,19 @@ +{ + "$schema": "https://ui.shadcn.com/schema/registry-item.json", + "name": "collapsible", + "title": "Collapsible", + "description": "Native Liquid collapsible with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/runtime.json" + ], + "files": [ + { + "path": "components/collapsible.liquid", + "content": "{% doc %}\n collapsible uses its shared popup or trigger styles and native data-slot controller.\n{% enddoc %}\n", + "type": "registry:file", + "target": "~/snippets/ui-collapsible.liquid" + } + ], + "type": "registry:item" +} \ No newline at end of file diff --git a/public/r/liquid/combobox.json b/public/r/liquid/combobox.json new file mode 100644 index 000000000..86be8f6fd --- /dev/null +++ b/public/r/liquid/combobox.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://ui.shadcn.com/schema/registry-item.json", + "name": "combobox", + "title": "Combobox", + "description": "Native Liquid combobox with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/input.json", + "https://ui.full.dev/r/liquid/popup.json", + "https://ui.full.dev/r/liquid/runtime.json" + ], + "files": [ + { + "path": "components/combobox.liquid", + "content": "{% doc %}\n Fulldev UI combobox styles. Use native markup with its data-slot parts.\n{% enddoc %}\n\n{% stylesheet %}\n /* The list of a data-slot combobox, after Vega's combobox. The positioner and the content are\n portaled to the body, so the caller puts its scheme class on combobox-portal. */\n [data-slot='combobox-positioner'] {\n z-index: 1000;\n isolation: isolate;\n }\n [data-slot='combobox-content'] {\n width: var(--anchor-width);\n min-width: 9rem;\n max-width: var(--available-width);\n max-height: min(18rem, var(--available-height));\n overflow: hidden;\n border-radius: var(--radius-md);\n background: var(--popover);\n color: var(--popover-foreground);\n box-shadow:\n 0 0 0 1px color-mix(in oklch, var(--foreground) 10%, transparent),\n var(--shadow-md);\n opacity: 1;\n transform: scale(1);\n transform-origin: var(--transform-origin);\n transition:\n opacity 100ms,\n transform 100ms;\n }\n [data-slot='combobox-content'][data-starting-style],\n [data-slot='combobox-content'][data-ending-style] {\n opacity: 0;\n transform: scale(0.95);\n }\n [data-slot='combobox-list'] {\n max-height: min(18rem, var(--available-height));\n overflow-y: auto;\n overscroll-behavior: contain;\n padding: 4px;\n scroll-padding-block: 4px;\n scrollbar-width: none;\n }\n [data-slot='combobox-list'][data-empty] {\n padding: 0;\n }\n [data-slot='combobox-item'] {\n position: relative;\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 6px 32px 6px 8px;\n border-radius: var(--radius-sm);\n font-size: 0.875rem;\n cursor: default;\n user-select: none;\n outline: none;\n }\n [data-slot='combobox-item'][data-highlighted] {\n background: var(--accent);\n color: var(--accent-foreground);\n }\n [data-slot='combobox-item'][data-disabled] {\n opacity: 0.5;\n pointer-events: none;\n }\n [data-slot='combobox-item-indicator'] {\n position: absolute;\n right: 8px;\n display: flex;\n align-items: center;\n justify-content: center;\n width: 16px;\n height: 16px;\n pointer-events: none;\n }\n [data-slot='combobox-item-indicator'] .icon {\n width: 16px;\n height: 16px;\n }\n [data-slot='combobox-empty'] {\n padding: 8px;\n text-align: center;\n font-size: 0.875rem;\n color: var(--muted-foreground);\n }\n{% endstylesheet %}\n", + "type": "registry:file", + "target": "~/snippets/ui-combobox.liquid" + } + ], + "type": "registry:item" +} \ No newline at end of file diff --git a/public/r/liquid/command.json b/public/r/liquid/command.json new file mode 100644 index 000000000..7be990e4b --- /dev/null +++ b/public/r/liquid/command.json @@ -0,0 +1,19 @@ +{ + "$schema": "https://ui.shadcn.com/schema/registry-item.json", + "name": "command", + "title": "Command", + "description": "Native Liquid command with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/runtime.json" + ], + "files": [ + { + "path": "components/command.liquid", + "content": "{% doc %}\n Fulldev UI command styles. Use native markup with its data-slot parts.\n{% enddoc %}\n\n{% stylesheet %}\n [data-slot='command'] {\n display: flex;\n flex-direction: column;\n overflow: hidden;\n }\n\n [data-slot='command-input-wrapper'] {\n padding: 4px 4px 0;\n }\n\n [data-slot='command-input'] {\n flex: 1;\n height: 100%;\n padding: 0 4px;\n border: 0;\n border-radius: 0;\n background: transparent;\n box-shadow: none;\n font-size: 0.875rem;\n }\n\n [data-slot='command-input']:focus-visible {\n box-shadow: none;\n outline: 0;\n }\n\n [data-slot='command-input']::-webkit-search-cancel-button {\n appearance: none;\n }\n\n [data-slot='command-list'] {\n height: var(--search-results-height, auto);\n max-height: 18rem;\n overflow: hidden auto;\n scroll-padding-block: 4px;\n scrollbar-width: none;\n transition: height 150ms ease-out;\n }\n\n [data-slot='command-group'] {\n padding: 4px;\n }\n\n [data-slot='command-group'] + [data-slot='command-group'] {\n border-top: 1px solid var(--border);\n }\n\n [data-slot='command-group-heading'] {\n padding: 6px 8px;\n font-size: 0.75rem;\n font-weight: 500;\n color: var(--muted-foreground);\n }\n\n [data-slot='command-item'] {\n position: relative;\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n padding: 6px 8px;\n border-radius: var(--radius-lg);\n cursor: default;\n user-select: none;\n }\n\n [data-slot='command-item'][data-selected] {\n background: var(--muted);\n color: var(--foreground);\n }\n\n [data-slot='command-item'] > .icon {\n width: 16px;\n height: 16px;\n color: var(--muted-foreground);\n }\n\n [data-slot='command-item'][data-selected] > .icon {\n color: var(--foreground);\n }\n{% endstylesheet %}\n", + "type": "registry:file", + "target": "~/snippets/ui-command.liquid" + } + ], + "type": "registry:item" +} \ No newline at end of file diff --git a/public/r/liquid/components.json b/public/r/liquid/components.json new file mode 100644 index 000000000..2576b9d46 --- /dev/null +++ b/public/r/liquid/components.json @@ -0,0 +1,83 @@ +{ + "$schema": "https://ui.shadcn.com/schema/registry-item.json", + "name": "components", + "title": "Components", + "description": "All native Liquid styles, controllers and a single layout integration snippet.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/runtime.json", + "https://ui.full.dev/r/liquid/banner.json", + "https://ui.full.dev/r/liquid/tabs.json", + "https://ui.full.dev/r/liquid/form.json", + "https://ui.full.dev/r/liquid/select.json", + "https://ui.full.dev/r/liquid/field.json", + "https://ui.full.dev/r/liquid/item.json", + "https://ui.full.dev/r/liquid/spinner.json", + "https://ui.full.dev/r/liquid/video.json", + "https://ui.full.dev/r/liquid/button.json", + "https://ui.full.dev/r/liquid/toggle.json", + "https://ui.full.dev/r/liquid/logo.json", + "https://ui.full.dev/r/liquid/kbd.json", + "https://ui.full.dev/r/liquid/rating.json", + "https://ui.full.dev/r/liquid/tooltip.json", + "https://ui.full.dev/r/liquid/avatar.json", + "https://ui.full.dev/r/liquid/empty.json", + "https://ui.full.dev/r/liquid/alert-dialog.json", + "https://ui.full.dev/r/liquid/navigation-menu.json", + "https://ui.full.dev/r/liquid/typography.json", + "https://ui.full.dev/r/liquid/toc.json", + "https://ui.full.dev/r/liquid/icon.json", + "https://ui.full.dev/r/liquid/combobox.json", + "https://ui.full.dev/r/liquid/toast.json", + "https://ui.full.dev/r/liquid/header.json", + "https://ui.full.dev/r/liquid/command.json", + "https://ui.full.dev/r/liquid/accordion.json", + "https://ui.full.dev/r/liquid/slider.json", + "https://ui.full.dev/r/liquid/input.json", + "https://ui.full.dev/r/liquid/input-group.json", + "https://ui.full.dev/r/liquid/triggers.json", + "https://ui.full.dev/r/liquid/toggle-group.json", + "https://ui.full.dev/r/liquid/dialog.json", + "https://ui.full.dev/r/liquid/drawer.json", + "https://ui.full.dev/r/liquid/badge.json", + "https://ui.full.dev/r/liquid/alert.json", + "https://ui.full.dev/r/liquid/table.json", + "https://ui.full.dev/r/liquid/breadcrumb.json", + "https://ui.full.dev/r/liquid/aspect-ratio.json", + "https://ui.full.dev/r/liquid/price.json", + "https://ui.full.dev/r/liquid/radio-group.json", + "https://ui.full.dev/r/liquid/checkbox.json", + "https://ui.full.dev/r/liquid/switch.json", + "https://ui.full.dev/r/liquid/skeleton.json", + "https://ui.full.dev/r/liquid/separator.json", + "https://ui.full.dev/r/liquid/card.json", + "https://ui.full.dev/r/liquid/pagination.json", + "https://ui.full.dev/r/liquid/popup.json", + "https://ui.full.dev/r/liquid/sheet.json", + "https://ui.full.dev/r/liquid/carousel.json", + "https://ui.full.dev/r/liquid/attachment.json", + "https://ui.full.dev/r/liquid/marquee.json", + "https://ui.full.dev/r/liquid/invalid-controls.json", + "https://ui.full.dev/r/liquid/section.json", + "https://ui.full.dev/r/liquid/button-group.json", + "https://ui.full.dev/r/liquid/collapsible.json", + "https://ui.full.dev/r/liquid/dropdown-menu.json", + "https://ui.full.dev/r/liquid/popover.json", + "https://ui.full.dev/r/liquid/hover-card.json" + ], + "files": [ + { + "path": "snippets/ui.liquid", + "content": "{% doc %}\n All Fulldev UI styles. Render once in the head before theme styles.\n\n @param {boolean} [runtime] - False when the theme owns its import map and initialization.\n{% enddoc %}\n\n{% render 'ui-init' %}\n{% render 'ui-banner' %}\n{% render 'ui-tabs' %}\n{% render 'ui-form' %}\n{% render 'ui-select' %}\n{% render 'ui-field' %}\n{% render 'ui-item' %}\n{% render 'ui-spinner' %}\n{% render 'ui-video' %}\n{% render 'ui-button' %}\n{% render 'ui-toggle' %}\n{% render 'ui-logo' %}\n{% render 'ui-kbd' %}\n{% render 'ui-rating' %}\n{% render 'ui-tooltip' %}\n{% render 'ui-avatar' %}\n{% render 'ui-empty' %}\n{% render 'ui-alert-dialog' %}\n{% render 'ui-navigation-menu' %}\n{% render 'ui-typography' %}\n{% render 'ui-toc' %}\n{% render 'ui-icon' %}\n{% render 'ui-combobox' %}\n{% render 'ui-toast' %}\n{% render 'ui-header' %}\n{% render 'ui-command' %}\n{% render 'ui-accordion' %}\n{% render 'ui-slider' %}\n{% render 'ui-input' %}\n{% render 'ui-input-group' %}\n{% render 'ui-triggers' %}\n{% render 'ui-toggle-group' %}\n{% render 'ui-dialog' %}\n{% render 'ui-drawer' %}\n{% render 'ui-badge' %}\n{% render 'ui-alert' %}\n{% render 'ui-table' %}\n{% render 'ui-breadcrumb' %}\n{% render 'ui-aspect-ratio' %}\n{% render 'ui-price' %}\n{% render 'ui-radio-group' %}\n{% render 'ui-checkbox' %}\n{% render 'ui-switch' %}\n{% render 'ui-skeleton' %}\n{% render 'ui-separator' %}\n{% render 'ui-card' %}\n{% render 'ui-pagination' %}\n{% render 'ui-popup' %}\n{% render 'ui-sheet' %}\n{% render 'ui-carousel' %}\n{% render 'ui-attachment' %}\n{% render 'ui-marquee' %}\n{% render 'ui-invalid-controls' %}\n{% render 'ui-section' %}\n{% render 'ui-button-group' %}\n{% render 'ui-collapsible' %}\n{% render 'ui-dropdown-menu' %}\n{% render 'ui-popover' %}\n{% render 'ui-hover-card' %}\n{% if runtime != false %}\n {% render 'ui-runtime' %}\n{% endif %}\n", + "type": "registry:file", + "target": "~/snippets/ui.liquid" + }, + { + "path": "manifest.json", + "content": "{\n \"registry\": \"https://ui.full.dev/r/liquid/\",\n \"files\": {\n \"assets/ui-tokens.css\": \"55a8f2810ab4a1498ca3dd2f6e7dc89009c60e2402ddfcb95783b6eba996c374\",\n \"snippets/ui-base.liquid\": \"1a39ed17214f86fa6fa844cb1d26e7e4a386c2c8a9d4492b2fd03efa940b9955\",\n \"snippets/ui-init.liquid\": \"98f56067cf51e4cb2cd3e10ede7c00304c73ce57fe5b5a0270c0d8a9e448f7af\",\n \"assets/ui-licenses.json\": \"6eaca08e24468055e5471a9141a3453381b86ced4bdce85cb874f73b550baa09\",\n \"assets/ui-carousel.js\": \"95655cf60a4c1cacdad87295acb2126032dc8d59ca117c6d738cf534be648e12\",\n \"assets/ui-controls.js\": \"cf51f6d8b23c93e30d3355e993ff21a1aee79f38facfa413bed64e9815df255d\",\n \"assets/ui-lifecycle.js\": \"78c975473620171c01b1289bf5cdce56a5626c847437fa5168fb91a4300260d6\",\n \"assets/ui-start.js\": \"738d7e171b5d7993bfd0a169e4382b2db08340f52a87059b94d27e1fe3606500\",\n \"assets/data-slot-core.js\": \"d2879cc3a2f05a7867763b937bad3bd4a369f54670989ce535367cb690fb66e6\",\n \"assets/data-slot-dialog.js\": \"a5201f452700d04145b5156cc46880d2b5bdf7521b03ff9521c3f7693ee1b5d5\",\n \"assets/data-slot-command.js\": \"f7944120617676b14dfda2ea3f3e8a15767e0766d5312b4ed9519affe9b7444a\",\n \"assets/data-slot-navigation-menu.js\": \"f9b42a9bceca8e1103718d43aa5f0d141ab63d38278cb121e790a2c80e827b68\",\n \"assets/data-slot-accordion.js\": \"fe6b0ac2c87a2e31582e5e88c509e0b508f41d0065d0a420407999ad210e6a27\",\n \"assets/data-slot-tabs.js\": \"1f187134bba755cfda9d35cf9f16174c1a183862d32d100f270f4177033058c7\",\n \"assets/data-slot-combobox.js\": \"b5f2ed9d65a43d836c59c0d4dc33c16ab12ef9fa05d510078b5d62517d854db6\",\n \"assets/data-slot-carousel.js\": \"d83ef938d3b62dc3652b123428ca2261e03fb2ba1133992b0f5545a974238d03\",\n \"assets/data-slot-alert-dialog.js\": \"1caee9a86bb2a816ea32bc1a48b82f3cc588ec3c563ebc443f836ab1dae2f618\",\n \"assets/data-slot-collapsible.js\": \"c42f76e3dfeff4570eb2f5104434fbffd3ef55710833e888965c590ce59f4f9f\",\n \"assets/data-slot-drawer.js\": \"c9343b26ea27193cc1d2da3e41bf452005bc2e534151fa1a762b2b6535de28d0\",\n \"assets/data-slot-dropdown-menu.js\": \"543c5717c9ba8363bbab4c0a11005980d75d4baaed79cf7123c7bcfe3ec8720b\",\n \"assets/data-slot-hover-card.js\": \"86e1bfa5d4822f180f3501ca7cc51bf044d4e917a7ebdb7a10b70006dba6467f\",\n \"assets/data-slot-popover.js\": \"526e30d663381329a7a6394eae886224024034d0abf96f888c932bb4b8c1b1cb\",\n \"assets/data-slot-radio-group.js\": \"7a9b90166de80d79ee42aa2df73e4d8179e9d291cb30b3095c7d8332375b0499\",\n \"assets/data-slot-select.js\": \"13d1a3fbe90f4b3b830d987b6a28267897faa3589bf00e64edca4938acbf406b\",\n \"assets/data-slot-slider.js\": \"0d10131aa95450aa1269234103ead8d805d6abdf9e0683510f4fcf58ab0f1efa\",\n \"assets/data-slot-switch.js\": \"0d5b09a89429880f61aeb540be5f87383ba5c6beb261ad468e9a6cf18e45b6a0\",\n \"assets/data-slot-toast.js\": \"acfd86824d0ff37fbd62d0c48672e527d7391412780730e9a3a8d61268b3aaf2\",\n \"assets/data-slot-toggle.js\": \"7a651d578cb83be842c8e59fe1a2cd124594b1fd6c942faf0a33cf9e57326947\",\n \"assets/data-slot-toggle-group.js\": \"c1f2fc9712974e63e4e44c97ab2a1ec8bc3c3cb73c1afb4578b085fed11977f1\",\n \"assets/data-slot-tooltip.js\": \"5a9e51a54669153620eb3512e74487eac12ababe3331aff7dfed6c717b8b93c0\",\n \"snippets/ui-runtime.liquid\": \"62ae98f4ad16dc0ca406d96bbabb373cd30a63dd385e3614da0dcd2e63d0f17c\",\n \"assets/ui-motion.js\": \"2252a6794ddb162b73ee1640af212292b8c520e98ae09ea862b479078f81d3d1\",\n \"snippets/ui-banner.liquid\": \"9965b12e9a2036aca6e35f1327da8a240c894a6fd91d99f37e77107818d271a3\",\n \"snippets/ui-tabs.liquid\": \"5b57bb3d6400e44ef9343f7b45d9baf2d8581d678ccc64764be60164160687eb\",\n \"snippets/ui-form.liquid\": \"1c002d21c441e217a8f3d64268d429c97e4785af118eb7f7abb5ff1c3d10a3b6\",\n \"assets/ui-form.css\": \"ab0dd70553818fe0799e393f6811e949884eca9f44d9ceff4c2b333c0bf724b4\",\n \"snippets/ui-select.liquid\": \"2d8c99759161a024285d1b54c0ca166dc7b13c97596497351253e945b5e52ee2\",\n \"snippets/ui-field.liquid\": \"cf73aaaf3e27bc9d121dd089317610fbd08b9550a1d5fb0738337c6817879309\",\n \"assets/ui-field.css\": \"1436dff789c8811aae13025ca862ac1bfbf64c57e895c87474f45bfcd8b3f665\",\n \"snippets/ui-item.liquid\": \"fe03c1a3e6df09192c3ac7a08520b0fc1c629a597a029d193ff91655cb7ceac0\",\n \"snippets/ui-spinner.liquid\": \"84d48693f10cb8af4549cc0d71cd847a85da0e111f2ad64dae309c128c2a8c4a\",\n \"snippets/ui-video.liquid\": \"14acea7c523198269a9165270decac21a43a5d69bf12bd248a76aceb2e9b26ed\",\n \"assets/ui-video.css\": \"c1572853f48474f6291a70ccdb1085512700fc3dcbbdaebd71374b838ec67fee\",\n \"snippets/ui-button.liquid\": \"5dd981b8e5de94d5dac3fd367a3d31c0553a2ea2f72aefda6ee2e07fde876af8\",\n \"snippets/ui-toggle.liquid\": \"c86100cf7c163c0d5bc81976fd794788f68fab2ebc5c0887d8042958ce020912\",\n \"snippets/ui-logo.liquid\": \"5d6de7be3f7aab9e05c9533afef39ea0e9dce1b4ee12a13bf7b82780c973b126\",\n \"snippets/ui-kbd.liquid\": \"024f34ed2932d183671f7fa44ff724a70ec17b766cf7e0d3f565f46f33ab2cf2\",\n \"snippets/ui-rating.liquid\": \"aa57adc5a5508b368da78023b242694e6ce924b6e9e5407c141a5030a414bf1d\",\n \"assets/ui-rating.css\": \"e5efea2a9edaea78522f20ebd9e9d01abe8a2fcd310c193239adf80ec33b4975\",\n \"snippets/ui-tooltip.liquid\": \"d2863a0da3090c73dda10d8615b84fc50fab64373a466fbf4e4e743cfb083c96\",\n \"snippets/ui-avatar.liquid\": \"9bfb5d5f535857cc45f31afbc91c855e94972bab07ad3b4fe2dc0d241106385d\",\n \"snippets/ui-empty.liquid\": \"1a6b8a006d67700225a9f646fdb74308904319cc1cd8ea83a637e2d9d5fbc3d9\",\n \"snippets/ui-alert-dialog.liquid\": \"f739374035ffa0f3bca2c4b6d946710751c8268c8f1dde535251be9c2c7b0cd0\",\n \"snippets/ui-navigation-menu.liquid\": \"52aecbb36b0031810a85a99f5d4439e852dc1ba3a872b9525dd75c8df8b5a84e\",\n \"assets/ui-navigation-menu.css\": \"29a47ce0eb549e58cc8195dcb656bd2cef56360178c76acfc2d481a110463ca6\",\n \"snippets/ui-typography.liquid\": \"b03dc93c4de6636164a206c95979caa6fe9ab7cf8d1c24d75611416259da7979\",\n \"assets/ui-typography.css\": \"4363ea0774e3c76a0154b1c586a6270a4fbd31b7794fe9549977f41563f54d78\",\n \"snippets/ui-toc.liquid\": \"04038e0870112b2335b80b4092814915105b31d08cdd36ecf1f4de1761f66acf\",\n \"snippets/ui-icon.liquid\": \"ca75a9485eb33cdb31a3a4e2e5ca03ce74d4995dbd1696d964832a87602bf3ca\",\n \"assets/ui-icon.css\": \"cfb1a47905cbd9c25b635c96926a77f8eed983a4aca62fdce758345dcc792add\",\n \"snippets/ui-combobox.liquid\": \"bacd605b44237512a74d34119d01432440b76ae60218dae6a6f37f98b32e3e00\",\n \"snippets/ui-toast.liquid\": \"bb032d90d6084fb15e12da22777635b80d09a06402513c46c89e853e3ede0405\",\n \"assets/ui-toast.css\": \"c322fab210a202fa434695025550495dbdc93e688ed7ae39303fea9830864d0b\",\n \"snippets/ui-header.liquid\": \"cc14effb4e3b6c4ac397e306e4ebd270a31b3415cc12ffbeb6cd38f411eaba80\",\n \"snippets/ui-command.liquid\": \"4a37f59dc0039fa1505de764472212d6cd7c4e5c52b737a1700e928db286f233\",\n \"snippets/ui-accordion.liquid\": \"4b58c83ae13e14e03a3129a2dccbd02eeafa90c1e48edbbf9af2dc412bf3aabf\",\n \"assets/ui-accordion.css\": \"430924440bd0f1d49ceb72baffc77404c9411c31c24b940d75861daba2649e83\",\n \"snippets/ui-slider.liquid\": \"f2de1fb9099de77bf291455447bc86b2578781e7cf0efdf12117fb4575e0b0e6\",\n \"snippets/ui-input.liquid\": \"1f101e309dc15bae127766427ef619d190eb9e341fc34808b26f56ea354312e7\",\n \"snippets/ui-input-group.liquid\": \"b862a0c63e23dc7ae4ffe3a8e057a87f38fa8e6c8842ddf3c86b826de0283d70\",\n \"snippets/ui-triggers.liquid\": \"ba9c7b802fb0a2aba0e9cc145a9dd44134964afb53e5363cf9e0ae138b906cc9\",\n \"snippets/ui-toggle-group.liquid\": \"2d61e0b9bc746e05834157f5a75d23123f02bc2df737412e4f6099844afc4f49\",\n \"snippets/ui-dialog.liquid\": \"779bbcd609a69ed29a984df1f26f9ed80fd24d71c4ee3ead5f4daa055b28e9fb\",\n \"snippets/ui-drawer.liquid\": \"16120fdd70fac393470c3936223e78ad5f81164e2b7616fd075e8e82098a17d5\",\n \"snippets/ui-badge.liquid\": \"13d8b46cd5432783a9b986de76365c9fcc0e7fb30ad4f956ec386c1ae549ce99\",\n \"snippets/ui-alert.liquid\": \"7530d324ac1fb4645bba0a23780fbeacc67544f8bad3ed9e367debd88c0365b5\",\n \"snippets/ui-table.liquid\": \"6599d8b3bd193761571c2b71e499f78546ba5c2768d4e0a0a60cd0e348632e5b\",\n \"snippets/ui-breadcrumb.liquid\": \"dafdeaf03286b386c90f8e72777de1946fba4718ee568bd85ca7bc768483a3ca\",\n \"snippets/ui-aspect-ratio.liquid\": \"c8aae05fcd76af7ae768aedafd7e758533e2b52135b00ff72940896ad793f177\",\n \"snippets/ui-price.liquid\": \"96156754aee3a04aa3ae7084a14470000213e8be14468c4937e9f3a87e348234\",\n \"assets/ui-price.css\": \"95c947e219621102b704164e48a83e381e6e2b27b99311a294e076745b39a36f\",\n \"snippets/ui-radio-group.liquid\": \"af9978e18716cc86d03ed21dece8304992e74a4fab88e12069b0648cfd944492\",\n \"snippets/ui-checkbox.liquid\": \"0e4dd1738b355c5a93533e0b813e2e6c72f7491c289d65aa7b6435be1c599006\",\n \"snippets/ui-switch.liquid\": \"84ed60c777e77daec8b5fe6f2ee434877d51224235dc1bc2e17927cd182fe646\",\n \"snippets/ui-skeleton.liquid\": \"1e30245f6577f73d28f4badbc293934656882a29d5dec1d2f74ef95f3bcede2c\",\n \"snippets/ui-separator.liquid\": \"90b1d2795666b6c51f35645ca8d07659f2c4868fe9553e233cddb4e962095f6e\",\n \"snippets/ui-card.liquid\": \"581a46843f1280ebace0423ee84289b40fbd46142e6a9ab3940f63eca0e4c5f2\",\n \"assets/ui-card.css\": \"ee7e4192ec814066d0c9838f810ae4ca267b9f071b7fa945c43226d46b0f8dc9\",\n \"snippets/ui-pagination.liquid\": \"30943c00686e1a23d92865465b38c964e059b14ae3a20fb7fe151824a73773eb\",\n \"snippets/ui-popup.liquid\": \"840f938aa2a331e4e7d55c659f25e841dcfb1bf5c5cadace6263243352ff8359\",\n \"snippets/ui-sheet.liquid\": \"0de705bed685bf78fa3af4517c947b38d62a1aa56bf29356c0a201b64db17614\",\n \"snippets/ui-carousel.liquid\": \"59dc0c06b2a9f2f779ca22b3ff4b005071d4909aba052d6f8f0a1aa19e84c2ce\",\n \"assets/ui-carousel.css\": \"5317c251b3d3599ba330947fc9709ea50acfb858a5d7e08573b5d34ada2f3bf4\",\n \"snippets/ui-attachment.liquid\": \"6881df7cdb1b27462d8cce7c17c0b4ed4dd860d62535e1daa81378a57bb69ddf\",\n \"snippets/ui-marquee.liquid\": \"47d5df8f1ff46a2cd48a132986c2ba199c9e19b485e62fc3e0d19091d4baaab1\",\n \"assets/ui-marquee.css\": \"ef13b83f3dbcd6685fcee3dba14238386d0f34bc2266a5326c0b6f2d40a23fce\",\n \"snippets/ui-invalid-controls.liquid\": \"a169e8244cc86f5431f902f7a73b37188051ba8f2ef3e323830462dae5aed066\",\n \"snippets/ui-section.liquid\": \"da358cd87447457f3675b035b48556103d86cd11e0b2240ae2800e4c9ea54bdc\",\n \"assets/ui-section.css\": \"0d5df020f70d38b3c049430204d469d681f56f6c2c4905b925ca0c81e61ff16f\",\n \"snippets/ui-button-group.liquid\": \"a2790f8da0f8b37acc1ed558ba1548ea250a5b93e1b2dfbd11eb150f3f02783a\",\n \"snippets/ui-collapsible.liquid\": \"e6107a03d00b8cc75bb03206e64b9aa7f2989d982c0408b398b62067c6df450a\",\n \"snippets/ui-dropdown-menu.liquid\": \"25b31e09e3e7bfee9a397ca4de6e310754844c55e71639eadba917b0f4e6d2e5\",\n \"snippets/ui-popover.liquid\": \"44ff93d72eaad409f56f8a84ce25715e2c3633f2ac49ba7a414767c6dbe2ee47\",\n \"snippets/ui-hover-card.liquid\": \"ba4133d352fa14296743138c99f3694015bafe953751b8e42155f047c4beb9f0\",\n \"snippets/ui.liquid\": \"30679056b2eafefbaeef5ecd271604951676893a65a8eccb0f970c787a67b900\"\n }\n}\n", + "type": "registry:file", + "target": "~/assets/ui-manifest.json" + } + ], + "type": "registry:item" +} \ No newline at end of file diff --git a/public/r/liquid/dialog.json b/public/r/liquid/dialog.json new file mode 100644 index 000000000..de466e399 --- /dev/null +++ b/public/r/liquid/dialog.json @@ -0,0 +1,19 @@ +{ + "$schema": "https://ui.shadcn.com/schema/registry-item.json", + "name": "dialog", + "title": "Dialog", + "description": "Native Liquid dialog with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/runtime.json" + ], + "files": [ + { + "path": "components/dialog.liquid", + "content": "{% doc %}\n Fulldev UI dialog styles. Use native markup with its data-slot parts.\n{% enddoc %}\n\n{% stylesheet %}\n /* Dialogs, sheets and search, after shadcn/ui's base-vega dialog, sheet and command styles. */\n [data-slot='dialog-overlay'] {\n position: fixed;\n inset: 0;\n z-index: calc(var(--dialog-overlay-stack-index, 0) * 2 + 100);\n background: var(--scrim);\n opacity: 1;\n transition: opacity 150ms;\n }\n @supports (backdrop-filter: blur(4px)) {\n [data-slot='dialog-overlay'] {\n backdrop-filter: blur(4px);\n }\n }\n [data-slot='dialog-overlay'][data-starting-style],\n [data-slot='dialog-overlay'][data-ending-style] {\n opacity: 0;\n }\n\n /* A centered panel, as Vega's DialogContent. Sheets and the search dialog adjust it below. */\n [data-slot='dialog-content'] {\n position: fixed;\n top: 50%;\n left: 50%;\n z-index: calc(var(--dialog-content-stack-index, 0) * 2 + 101);\n display: grid;\n gap: calc(var(--spacing) * 6);\n width: 100%;\n max-width: calc(100% - 2rem);\n max-height: calc(100% - 2rem);\n overflow-y: auto;\n padding: calc(var(--spacing) * 6);\n border-radius: var(--radius-xl);\n background: var(--popover);\n color: var(--popover-foreground);\n font-size: 0.875rem;\n box-shadow: 0 0 0 1px color-mix(in oklch, var(--foreground) 10%, transparent);\n outline: none;\n opacity: 1;\n transform: translate(-50%, -50%) scale(1);\n transition:\n opacity 100ms,\n transform 100ms;\n }\n [data-slot='dialog-content'][data-starting-style],\n [data-slot='dialog-content'][data-ending-style] {\n opacity: 0;\n transform: translate(-50%, -50%) scale(0.95);\n }\n [data-slot='dialog-header'] {\n display: flex;\n flex-direction: column;\n gap: calc(var(--spacing) * 2);\n }\n [data-slot='dialog-title'] {\n font-size: 1rem;\n font-weight: 500;\n letter-spacing: var(--tracking-normal);\n line-height: 1;\n }\n [data-slot='dialog-description'] {\n color: var(--muted-foreground);\n }\n /* The close button in a panel's corner, as Vega's dialog. */\n [data-slot='dialog-content']:not([data-side]) > [data-slot='dialog-close'] {\n position: absolute;\n top: calc(var(--spacing) * 4);\n right: calc(var(--spacing) * 4);\n }\n @media (min-width: 640px) {\n [data-slot='dialog-content'] {\n max-width: 28rem;\n }\n }\n{% endstylesheet %}\n", + "type": "registry:file", + "target": "~/snippets/ui-dialog.liquid" + } + ], + "type": "registry:item" +} \ No newline at end of file diff --git a/public/r/liquid/drawer.json b/public/r/liquid/drawer.json new file mode 100644 index 000000000..39049186e --- /dev/null +++ b/public/r/liquid/drawer.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://ui.shadcn.com/schema/registry-item.json", + "name": "drawer", + "title": "Drawer", + "description": "Native Liquid drawer with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/triggers.json", + "https://ui.full.dev/r/liquid/button.json", + "https://ui.full.dev/r/liquid/runtime.json" + ], + "files": [ + { + "path": "components/drawer.liquid", + "content": "{% doc %}\n Fulldev UI drawer styles. Use native markup with its data-slot parts.\n{% enddoc %}\n\n{% stylesheet %}\n /* A bottom drawer, as Vega's drawer. The controller sets the snap and swipe offsets. */\n [data-slot='drawer-backdrop'] {\n position: fixed;\n inset: 0;\n z-index: calc(var(--drawer-overlay-stack-index, 0) * 2 + 100);\n background: var(--scrim);\n opacity: calc(1 - var(--drawer-swipe-progress, 0));\n transition: opacity 450ms cubic-bezier(0.32, 0.72, 0, 1);\n }\n @supports (backdrop-filter: blur(4px)) {\n [data-slot='drawer-backdrop'] {\n backdrop-filter: blur(4px);\n }\n }\n [data-slot='drawer-backdrop']:is([data-starting-style], [data-ending-style]) {\n opacity: 0;\n }\n [data-slot='drawer-backdrop'][data-swiping] {\n transition: none;\n }\n [data-slot='drawer-popup'] {\n position: fixed;\n inset-inline: 0;\n bottom: 0;\n z-index: calc(var(--drawer-content-stack-index, 0) * 2 + 101);\n display: flex;\n flex-direction: column;\n width: 100%;\n max-height: 80vh;\n border-top: 1px solid var(--border);\n border-radius: var(--radius-xl) var(--radius-xl) 0 0;\n background: var(--popover);\n color: var(--popover-foreground);\n font-size: 0.875rem;\n box-shadow: var(--shadow-lg);\n outline: none;\n transform: translateY(calc(var(--drawer-snap-point-offset, 0px) + var(--drawer-swipe-amount-y, 0px)));\n transition: transform 450ms cubic-bezier(0.32, 0.72, 0, 1);\n }\n [data-slot='drawer-popup']:is([data-starting-style], [data-ending-style]) {\n transform: translateY(100%);\n }\n [data-slot='drawer-popup'][data-swiping] {\n transition: none;\n }\n [data-slot='drawer-handle'] {\n flex-shrink: 0;\n width: 100px;\n height: 6px;\n margin: calc(var(--spacing) * 4) auto 0;\n border-radius: 9999px;\n background: var(--muted);\n }\n [data-slot='drawer-content'] {\n display: flex;\n flex-direction: column;\n width: 100%;\n max-width: 32rem;\n min-height: 0;\n margin-inline: auto;\n overflow-y: auto;\n }\n [data-slot='drawer-header'] {\n display: flex;\n flex-direction: column;\n gap: calc(var(--spacing) * 0.5);\n padding: calc(var(--spacing) * 4);\n text-align: center;\n }\n [data-slot='drawer-title'] {\n color: var(--foreground);\n font-size: 1rem;\n font-weight: 500;\n letter-spacing: var(--tracking-normal);\n }\n [data-slot='drawer-description'] {\n color: var(--muted-foreground);\n }\n [data-slot='drawer-footer'] {\n display: flex;\n flex-direction: column;\n gap: calc(var(--spacing) * 2);\n margin-top: auto;\n padding: calc(var(--spacing) * 4);\n }\n @media (min-width: 768px) {\n [data-slot='drawer-header'] {\n gap: calc(var(--spacing) * 1.5);\n text-align: left;\n }\n }\n{% endstylesheet %}\n", + "type": "registry:file", + "target": "~/snippets/ui-drawer.liquid" + } + ], + "type": "registry:item" +} \ No newline at end of file diff --git a/public/r/liquid/dropdown-menu.json b/public/r/liquid/dropdown-menu.json new file mode 100644 index 000000000..73ea236e7 --- /dev/null +++ b/public/r/liquid/dropdown-menu.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://ui.shadcn.com/schema/registry-item.json", + "name": "dropdown-menu", + "title": "Dropdown Menu", + "description": "Native Liquid dropdown-menu with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/popup.json", + "https://ui.full.dev/r/liquid/triggers.json", + "https://ui.full.dev/r/liquid/runtime.json" + ], + "files": [ + { + "path": "components/dropdown-menu.liquid", + "content": "{% doc %}\n dropdown-menu uses its shared popup or trigger styles and native data-slot controller.\n{% enddoc %}\n", + "type": "registry:file", + "target": "~/snippets/ui-dropdown-menu.liquid" + } + ], + "type": "registry:item" +} \ No newline at end of file diff --git a/public/r/liquid/empty.json b/public/r/liquid/empty.json new file mode 100644 index 000000000..fd7189601 --- /dev/null +++ b/public/r/liquid/empty.json @@ -0,0 +1,18 @@ +{ + "$schema": "https://ui.shadcn.com/schema/registry-item.json", + "name": "empty", + "title": "Empty", + "description": "Native Liquid empty with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json" + ], + "files": [ + { + "path": "components/empty.liquid", + "content": "{% doc %}\n Fulldev UI empty styles. Use native markup with its data-slot parts.\n{% enddoc %}\n\n{% stylesheet %}\n [data-slot='empty'] {\n display: flex;\n flex: 1;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: calc(var(--spacing) * 4);\n width: 100%;\n min-width: 0;\n padding: calc(var(--spacing) * 12);\n border-radius: var(--radius-lg);\n text-align: center;\n text-wrap: balance;\n }\n [data-slot='empty'][data-variant='outline'] {\n border: 1px dashed var(--border);\n }\n [data-slot='empty-header'] {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: calc(var(--spacing) * 2);\n max-width: 24rem;\n }\n [data-slot='empty-icon'] {\n display: flex;\n flex-shrink: 0;\n align-items: center;\n justify-content: center;\n margin-bottom: calc(var(--spacing) * 2);\n }\n [data-slot='empty-icon'][data-variant='icon'] {\n width: calc(var(--spacing) * 10);\n height: calc(var(--spacing) * 10);\n border-radius: var(--radius-lg);\n background: var(--muted);\n color: var(--foreground);\n }\n [data-slot='empty-icon'][data-variant='icon'] > .icon {\n width: calc(var(--spacing) * 6);\n height: calc(var(--spacing) * 6);\n }\n [data-slot='empty-title'] {\n font-size: 1.125rem;\n font-weight: 500;\n line-height: 1.75rem;\n letter-spacing: var(--tracking-tight);\n }\n [data-slot='empty-description'] {\n font-size: 0.875rem;\n line-height: 1.625;\n color: var(--muted-foreground);\n }\n [data-slot='empty-description'] a {\n text-decoration: underline;\n text-underline-offset: 4px;\n }\n [data-slot='empty-content'] {\n display: flex;\n flex-wrap: wrap;\n justify-content: center;\n gap: calc(var(--spacing) * 3);\n width: 100%;\n max-width: 24rem;\n font-size: 0.875rem;\n }\n{% endstylesheet %}\n", + "type": "registry:file", + "target": "~/snippets/ui-empty.liquid" + } + ], + "type": "registry:item" +} \ No newline at end of file diff --git a/public/r/liquid/field.json b/public/r/liquid/field.json new file mode 100644 index 000000000..04dd114c8 --- /dev/null +++ b/public/r/liquid/field.json @@ -0,0 +1,25 @@ +{ + "$schema": "https://ui.shadcn.com/schema/registry-item.json", + "name": "field", + "title": "Field", + "description": "Native Liquid field with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/input.json" + ], + "files": [ + { + "path": "components/field.liquid", + "content": "{% doc %}\n Fulldev UI field styles. Use native markup with its data-slot parts.\n{% enddoc %}\n\n{{ 'ui-field.css' | asset_url | stylesheet_tag }}\n", + "type": "registry:file", + "target": "~/snippets/ui-field.liquid" + }, + { + "path": "styles/field.css", + "content": "[data-slot='label'],\n[data-slot='field-label'] {\n display: flex;\n align-items: center;\n gap: calc(var(--spacing) * 2);\n font-size: 0.875rem;\n font-weight: 500;\n line-height: 1;\n}\n/* Fulldev UI's field set: a fieldset of fields or options under a legend. A legend sits outside its\n fieldset's flex layout, so it keeps the field gap as its margin. */\n[data-slot='field-set'] {\n display: flex;\n flex-direction: column;\n gap: calc(var(--spacing) * 6);\n min-width: 0;\n margin: 0;\n padding: 0;\n border: 0;\n}\n[data-slot='field-legend'] {\n display: flex;\n align-items: center;\n gap: calc(var(--spacing) * 2);\n margin-bottom: calc(var(--spacing) * 3);\n padding: 0;\n font-size: 1rem;\n font-weight: 500;\n line-height: 1;\n}\n[data-slot='field-legend'][data-variant='label'] {\n font-size: 0.875rem;\n}\n[data-slot='field'] {\n display: flex;\n flex-direction: column;\n gap: calc(var(--spacing) * 3);\n width: 100%;\n}\n/* An input and its button side by side, as in newsletter and search forms. */\n[data-slot='field'][data-orientation='horizontal'] {\n flex-direction: row;\n align-items: center;\n}\n[data-slot='field'][data-orientation='horizontal'] > :is(input, select, textarea) {\n flex: 1;\n}\n[data-slot='field-group'] {\n display: flex;\n flex-direction: column;\n gap: calc(var(--spacing) * 7);\n}\n/* Two fields side by side from 640px. */\n.field-row {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: calc(var(--spacing) * 4);\n}\n[data-slot='field-description'] {\n font-size: 0.875rem;\n color: var(--muted-foreground);\n}\n[data-slot='field-error'] {\n font-size: 0.875rem;\n color: var(--destructive);\n}\n", + "type": "registry:file", + "target": "~/assets/ui-field.css" + } + ], + "type": "registry:item" +} \ No newline at end of file diff --git a/public/r/liquid/form.json b/public/r/liquid/form.json new file mode 100644 index 000000000..4f2010d89 --- /dev/null +++ b/public/r/liquid/form.json @@ -0,0 +1,25 @@ +{ + "$schema": "https://ui.shadcn.com/schema/registry-item.json", + "name": "form", + "title": "Form", + "description": "Native Liquid form with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/field.json" + ], + "files": [ + { + "path": "components/form.liquid", + "content": "{% doc %}\n Fulldev UI form styles. Use native markup with its data-slot parts.\n{% enddoc %}\n\n{{ 'ui-form.css' | asset_url | stylesheet_tag }}\n", + "type": "registry:file", + "target": "~/snippets/ui-form.liquid" + }, + { + "path": "styles/form.css", + "content": "/* Fulldev UI's form. Shopify form tags take a class, so their forms use .form. */\n.form,\n[data-slot='form'] {\n display: flex;\n flex-direction: column;\n gap: calc(var(--spacing) * 6);\n}\n.form-error {\n font-size: 0.875rem;\n color: var(--destructive);\n}\n.form-error:empty {\n display: none;\n}\n", + "type": "registry:file", + "target": "~/assets/ui-form.css" + } + ], + "type": "registry:item" +} \ No newline at end of file diff --git a/public/r/liquid/header.json b/public/r/liquid/header.json new file mode 100644 index 000000000..6c81e7c2d --- /dev/null +++ b/public/r/liquid/header.json @@ -0,0 +1,19 @@ +{ + "$schema": "https://ui.shadcn.com/schema/registry-item.json", + "name": "header", + "title": "Header", + "description": "Native Liquid header with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/section.json" + ], + "files": [ + { + "path": "components/header.liquid", + "content": "{% doc %}\n Fulldev UI header styles. Use native markup with its data-slot parts.\n{% enddoc %}\n\n{% stylesheet %}\n /* Fulldev UI's header: a full-width bar, or a floating framed bar inset by the gutter. */\n [data-slot='header'] {\n position: relative;\n z-index: 50;\n display: flex;\n align-items: center;\n gap: 16px;\n width: 100%;\n padding-block: 8px;\n background: var(--background);\n color: var(--foreground);\n }\n [data-slot='header'][data-variant='floating'] {\n width: calc(100% - 2 * var(--gutter));\n max-width: calc(var(--container) - 2 * var(--gutter));\n margin: 8px auto;\n border: 1px solid var(--border);\n border-radius: var(--radius-lg);\n box-shadow: var(--shadow-sm);\n }\n\n [data-slot='header-group'] {\n display: flex;\n align-items: center;\n gap: 8px;\n min-width: 0;\n }\n{% endstylesheet %}\n", + "type": "registry:file", + "target": "~/snippets/ui-header.liquid" + } + ], + "type": "registry:item" +} \ No newline at end of file diff --git a/public/r/liquid/hover-card.json b/public/r/liquid/hover-card.json new file mode 100644 index 000000000..e1797c92d --- /dev/null +++ b/public/r/liquid/hover-card.json @@ -0,0 +1,20 @@ +{ + "$schema": "https://ui.shadcn.com/schema/registry-item.json", + "name": "hover-card", + "title": "Hover Card", + "description": "Native Liquid hover-card with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/popup.json", + "https://ui.full.dev/r/liquid/runtime.json" + ], + "files": [ + { + "path": "components/hover-card.liquid", + "content": "{% doc %}\n hover-card uses its shared popup or trigger styles and native data-slot controller.\n{% enddoc %}\n", + "type": "registry:file", + "target": "~/snippets/ui-hover-card.liquid" + } + ], + "type": "registry:item" +} \ No newline at end of file diff --git a/public/r/liquid/icon.json b/public/r/liquid/icon.json new file mode 100644 index 000000000..6b7c1ce64 --- /dev/null +++ b/public/r/liquid/icon.json @@ -0,0 +1,24 @@ +{ + "$schema": "https://ui.shadcn.com/schema/registry-item.json", + "name": "icon", + "title": "Icon", + "description": "Native Liquid icon with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json" + ], + "files": [ + { + "path": "components/icon.liquid", + "content": "{% doc %}\n Fulldev UI icon styles. Use native markup with its data-slot parts.\n{% enddoc %}\n\n{{ 'ui-icon.css' | asset_url | stylesheet_tag }}\n", + "type": "registry:file", + "target": "~/snippets/ui-icon.liquid" + }, + { + "path": "styles/icon.css", + "content": "/* snippets/icon.liquid: decorative Lucide icons, 20px unless a part sizes them. */\n[data-slot='icon'] {\n width: calc(var(--spacing) * 5);\n height: calc(var(--spacing) * 5);\n flex: none;\n}\n.icon-box {\n display: inline-flex;\n flex: none;\n align-items: center;\n justify-content: center;\n width: calc(var(--spacing) * 10);\n height: calc(var(--spacing) * 10);\n border-radius: var(--radius-lg);\n background: color-mix(in oklch, var(--primary) 10%, transparent);\n color: var(--primary);\n}\n.icon-box > .icon {\n width: calc(var(--spacing) * 5);\n height: calc(var(--spacing) * 5);\n}\n/* A 36px secondary tile with a primary icon, as Fulldev UI's contact details. */\n.icon-box--secondary {\n width: calc(var(--spacing) * 9);\n height: calc(var(--spacing) * 9);\n border-radius: var(--radius-md);\n background: var(--secondary);\n}\n.icon-box--secondary > .icon {\n width: calc(var(--spacing) * 4);\n height: calc(var(--spacing) * 4);\n}\n", + "type": "registry:file", + "target": "~/assets/ui-icon.css" + } + ], + "type": "registry:item" +} \ No newline at end of file diff --git a/public/r/liquid/init.json b/public/r/liquid/init.json new file mode 100644 index 000000000..6a613ef72 --- /dev/null +++ b/public/r/liquid/init.json @@ -0,0 +1,34 @@ +{ + "$schema": "https://ui.shadcn.com/schema/registry-item.json", + "name": "init", + "title": "Init", + "description": "Shared neutral tokens, native base styles and licenses. No framework or build required.", + "registryDependencies": [], + "files": [ + { + "path": "styles/tokens.css", + "content": "/* Generated from the shared neutral palette and Liquid primitives. */\n:root {\n color-scheme: light;\n\n --background: oklch(1 0 0);\n --foreground: oklch(0.145 0 0);\n --card: oklch(1 0 0);\n --card-foreground: oklch(0.145 0 0);\n --popover: oklch(1 0 0);\n --popover-foreground: oklch(0.145 0 0);\n --primary: oklch(0.205 0 0);\n --primary-foreground: oklch(0.985 0 0);\n --secondary: oklch(0.97 0 0);\n --secondary-foreground: oklch(0.205 0 0);\n --muted: oklch(0.97 0 0);\n --muted-foreground: oklch(0.556 0 0);\n --accent: oklch(0.97 0 0);\n --accent-foreground: oklch(0.205 0 0);\n --destructive: oklch(0.577 0.245 27.325);\n --border: oklch(0.922 0 0);\n --input: oklch(0.922 0 0);\n --ring: oklch(0.708 0 0);\n --chart-1: oklch(0.87 0 0);\n --chart-2: oklch(0.556 0 0);\n --chart-3: oklch(0.439 0 0);\n --chart-4: oklch(0.371 0 0);\n --chart-5: oklch(0.269 0 0);\n --radius: 0.625rem;\n --sidebar: oklch(0.985 0 0);\n --sidebar-foreground: oklch(0.145 0 0);\n --sidebar-primary: oklch(0.205 0 0);\n --sidebar-primary-foreground: oklch(0.985 0 0);\n --sidebar-accent: oklch(0.97 0 0);\n --sidebar-accent-foreground: oklch(0.205 0 0);\n --sidebar-border: oklch(0.922 0 0);\n --sidebar-ring: oklch(0.708 0 0);\n --heading-weight: 600;\n --container: 1280px;\n --section-gap: 64px;\n --header-height: 64px;\n --radius-sm: calc(var(--radius) * 0.6);\n --radius-md: calc(var(--radius) * 0.8);\n --radius-lg: var(--radius);\n --radius-xl: calc(var(--radius) * 1.4);\n --radius-2xl: calc(var(--radius) * 1.8);\n --radius-3xl: calc(var(--radius) * 2.2);\n --radius-4xl: calc(var(--radius) * 2.6);\n --shadow-x: 0;\n --shadow-y: 1px;\n --shadow-blur: 3px;\n --shadow-spread: 0;\n --shadow-opacity: 0.1;\n --shadow-color: oklch(0 0 0);\n --shadow-2xs: 0 1px 3px 0 hsl(0 0% 0% / 0.05);\n --shadow-xs: 0 1px 3px 0 hsl(0 0% 0% / 0.05);\n --shadow-sm: 0 1px 3px 0 hsl(0 0% 0% / 0.1), 0 1px 2px -1px hsl(0 0% 0% / 0.1);\n --shadow: 0 1px 3px 0 hsl(0 0% 0% / 0.1), 0 1px 2px -1px hsl(0 0% 0% / 0.1);\n --shadow-md: 0 1px 3px 0 hsl(0 0% 0% / 0.1), 0 2px 4px -1px hsl(0 0% 0% / 0.1);\n --shadow-lg: 0 1px 3px 0 hsl(0 0% 0% / 0.1), 0 4px 6px -1px hsl(0 0% 0% / 0.1);\n --shadow-xl: 0 1px 3px 0 hsl(0 0% 0% / 0.1), 0 8px 10px -1px hsl(0 0% 0% / 0.1);\n --shadow-2xl: 0 1px 3px 0 hsl(0 0% 0% / 0.25);\n --font-sans: 'Geist', ui-sans-serif, system-ui, sans-serif;\n --font-heading: var(--font-sans);\n --font-mono: ui-monospace, sfmono-regular, menlo, monaco, consolas, monospace;\n --tracking-tight: -0.025em;\n --tracking-normal: 0;\n --spacing: 0.25rem;\n --gutter: calc(var(--spacing) * 4);\n --section-padding: calc(var(--spacing) * 16);\n --on-media: #fff;\n --on-media-muted: rgb(255 255 255 / 0.85);\n --media-button: #fff;\n --media-button-foreground: #171717;\n --media-button-hover: #e5e5e5;\n --media-secondary: rgb(255 255 255 / 0.15);\n --media-secondary-hover: rgb(255 255 255 / 0.2);\n --media-overlay: rgb(0 0 0 / 0.7);\n --media-base: rgb(0 0 0);\n --media-fade: linear-gradient(to top, rgb(0 0 0 / 0.8), rgb(0 0 0 / 0.6));\n --scrim: rgb(0 0 0 / 0.1);\n --focus-ring: 0 0 0 3px color-mix(in oklch, var(--ring) 50%, transparent);\n}\n.dark {\n color-scheme: dark;\n\n --background: oklch(0.145 0 0);\n --foreground: oklch(0.985 0 0);\n --card: oklch(0.205 0 0);\n --card-foreground: oklch(0.985 0 0);\n --popover: oklch(0.205 0 0);\n --popover-foreground: oklch(0.985 0 0);\n --primary: oklch(0.922 0 0);\n --primary-foreground: oklch(0.205 0 0);\n --secondary: oklch(0.269 0 0);\n --secondary-foreground: oklch(0.985 0 0);\n --muted: oklch(0.269 0 0);\n --muted-foreground: oklch(0.708 0 0);\n --accent: oklch(0.269 0 0);\n --accent-foreground: oklch(0.985 0 0);\n --destructive: oklch(0.704 0.191 22.216);\n --border: oklch(1 0 0 / 10%);\n --input: oklch(1 0 0 / 15%);\n --ring: oklch(0.556 0 0);\n --chart-1: oklch(0.87 0 0);\n --chart-2: oklch(0.556 0 0);\n --chart-3: oklch(0.439 0 0);\n --chart-4: oklch(0.371 0 0);\n --chart-5: oklch(0.269 0 0);\n --sidebar: oklch(0.205 0 0);\n --sidebar-foreground: oklch(0.985 0 0);\n --sidebar-primary: oklch(0.488 0.243 264.376);\n --sidebar-primary-foreground: oklch(0.985 0 0);\n --sidebar-accent: oklch(0.269 0 0);\n --sidebar-accent-foreground: oklch(0.985 0 0);\n --sidebar-border: oklch(1 0 0 / 10%);\n --sidebar-ring: oklch(0.556 0 0);\n --focus-ring: 0 0 0 3px color-mix(in oklch, var(--ring) 50%, transparent);\n}\n", + "type": "registry:file", + "target": "~/assets/ui-tokens.css" + }, + { + "path": "components/base.liquid", + "content": "{% doc %}\n Fulldev UI base styles. Use native markup with its data-slot parts.\n{% enddoc %}\n\n{% stylesheet %}\n *,\n *::before,\n *::after {\n box-sizing: border-box;\n border-color: var(--border);\n }\n body {\n margin: 0;\n min-height: 100svh;\n display: flex;\n flex-direction: column;\n background: var(--background);\n color: var(--foreground);\n font: 16px/1.5 var(--font-sans);\n -webkit-font-smoothing: antialiased;\n text-rendering: optimizelegibility;\n }\n main {\n flex: 1;\n }\n img,\n video,\n svg,\n iframe {\n max-width: 100%;\n }\n img,\n video,\n svg {\n display: block;\n height: auto;\n }\n a {\n color: inherit;\n text-decoration: none;\n text-underline-offset: 4px;\n }\n button,\n input,\n select,\n textarea {\n font: inherit;\n color: inherit;\n }\n button,\n summary {\n cursor: pointer;\n }\n button {\n border: 0;\n background: none;\n padding: 0;\n border-radius: 0;\n }\n :focus-visible {\n outline: 2px solid color-mix(in oklch, var(--ring) 50%, transparent);\n outline-offset: 2px;\n }\n [hidden] {\n display: none !important;\n }\n h1,\n h2,\n h3,\n h4,\n p,\n figure,\n blockquote,\n fieldset,\n dl,\n dd {\n margin: 0;\n }\n fieldset {\n border: 0;\n padding: 0;\n min-width: 0;\n }\n /* Lists that a part or layout styles with a class; merchant rich text keeps its markers. */\n :where(ul, ol)[class] {\n margin: 0;\n padding: 0;\n list-style: none;\n }\n legend {\n padding: 0;\n }\n p,\n h1,\n h2,\n h3,\n h4,\n a,\n li {\n overflow-wrap: anywhere;\n }\n{% endstylesheet %}\n", + "type": "registry:file", + "target": "~/snippets/ui-base.liquid" + }, + { + "path": "snippets/init.liquid", + "content": "{% doc %}\n Fulldev UI tokens and native base. Render once in the head before theme overrides.\n{% enddoc %}\n\n{{ 'ui-tokens.css' | asset_url | stylesheet_tag: preload: true }}\n{% render 'ui-base' %}\n", + "type": "registry:file", + "target": "~/snippets/ui-init.liquid" + }, + { + "path": "licenses.json", + "content": "[\n {\n \"name\": \"Fulldev UI\",\n \"text\": \"MIT License\\n\\nCopyright (c) 2024 Fulldev, Sil Veltman\\n\\nPermission is hereby granted, free of charge, to any person obtaining a copy\\nof this software and associated documentation files (the \\\"Software\\\"), to deal\\nin the Software without restriction, including without limitation the rights\\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\\ncopies of the Software, and to permit persons to whom the Software is\\nfurnished to do so, subject to the following conditions:\\n\\nThe above copyright notice and this permission notice shall be included in all\\ncopies or substantial portions of the Software.\\n\\nTHE SOFTWARE IS PROVIDED \\\"AS IS\\\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\\nSOFTWARE.\"\n },\n {\n \"name\": \"shadcn/ui (derived Vega styles)\",\n \"text\": \"MIT License\\n\\nCopyright (c) 2023 shadcn\\n\\nPermission is hereby granted, free of charge, to any person obtaining a copy\\nof this software and associated documentation files (the \\\"Software\\\"), to deal\\nin the Software without restriction, including without limitation the rights\\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\\ncopies of the Software, and to permit persons to whom the Software is\\nfurnished to do so, subject to the following conditions:\\n\\nThe above copyright notice and this permission notice shall be included in all\\ncopies or substantial portions of the Software.\\n\\nTHE SOFTWARE IS PROVIDED \\\"AS IS\\\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\\nSOFTWARE.\"\n },\n {\n \"name\": \"data-slot 1.1.1\",\n \"text\": \"MIT License\\n\\nCopyright (c) 2026 Bejamas\\n\\nPermission is hereby granted, free of charge, to any person obtaining a copy\\nof this software and associated documentation files (the \\\"Software\\\"), to deal\\nin the Software without restriction, including without limitation the rights\\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\\ncopies of the Software, and to permit persons to whom the Software is\\nfurnished to do so, subject to the following conditions:\\n\\nThe above copyright notice and this permission notice shall be included in all\\ncopies or substantial portions of the Software.\\n\\nTHE SOFTWARE IS PROVIDED \\\"AS IS\\\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\\nSOFTWARE.\"\n }\n]\n", + "type": "registry:file", + "target": "~/assets/ui-licenses.json" + } + ], + "type": "registry:item" +} \ No newline at end of file diff --git a/public/r/liquid/input-group.json b/public/r/liquid/input-group.json new file mode 100644 index 000000000..9334e6d93 --- /dev/null +++ b/public/r/liquid/input-group.json @@ -0,0 +1,20 @@ +{ + "$schema": "https://ui.shadcn.com/schema/registry-item.json", + "name": "input-group", + "title": "Input Group", + "description": "Native Liquid input-group with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/input.json", + "https://ui.full.dev/r/liquid/icon.json" + ], + "files": [ + { + "path": "components/input-group.liquid", + "content": "{% doc %}\n Fulldev UI input-group styles. Use native markup with its data-slot parts.\n{% enddoc %}\n\n{% stylesheet %}\n /* An input with addons inside one border, as Fulldev UI's input group. The input keeps the slot of\n its own component, such as combobox-input. */\n [data-slot='input-group'] {\n display: flex;\n align-items: center;\n width: 100%;\n min-width: 0;\n height: calc(var(--spacing) * 9);\n border: 1px solid var(--input);\n border-radius: var(--radius-md);\n background-color: light-dark(transparent, color-mix(in oklch, var(--input) 30%, transparent));\n box-shadow: var(--shadow-xs);\n transition:\n border-color 150ms,\n box-shadow 150ms;\n }\n [data-slot='input-group'][data-size='sm'] {\n height: calc(var(--spacing) * 8);\n }\n [data-slot='input-group']:has(> input:focus-visible) {\n border-color: var(--ring);\n box-shadow: var(--focus-ring);\n }\n [data-slot='input-group'] > input {\n flex: 1;\n height: 100%;\n border: 0;\n border-radius: 0;\n background: transparent;\n box-shadow: none;\n }\n [data-slot='input-group'] > input:focus-visible {\n box-shadow: none;\n }\n [data-slot='input-group-addon'] {\n display: flex;\n align-items: center;\n gap: calc(var(--spacing) * 2);\n padding-right: calc(var(--spacing) * 2);\n color: var(--muted-foreground);\n font-size: 0.875rem;\n font-weight: 500;\n }\n [data-slot='input-group-addon'] > .icon {\n width: calc(var(--spacing) * 4);\n height: calc(var(--spacing) * 4);\n }\n{% endstylesheet %}\n", + "type": "registry:file", + "target": "~/snippets/ui-input-group.liquid" + } + ], + "type": "registry:item" +} \ No newline at end of file diff --git a/public/r/liquid/input.json b/public/r/liquid/input.json new file mode 100644 index 000000000..957ef12d0 --- /dev/null +++ b/public/r/liquid/input.json @@ -0,0 +1,18 @@ +{ + "$schema": "https://ui.shadcn.com/schema/registry-item.json", + "name": "input", + "title": "Input", + "description": "Native Liquid input with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json" + ], + "files": [ + { + "path": "components/input.liquid", + "content": "{% doc %}\n Fulldev UI input styles. Use native markup with its data-slot parts.\n{% enddoc %}\n\n{% stylesheet %}\n input:where(:not([type='hidden'], [type='checkbox'], [type='radio'], [type='range'], [type='submit'])),\n [data-slot='input'],\n [data-slot='native-select'],\n select,\n [data-slot='textarea'],\n textarea {\n width: 100%;\n min-width: 0;\n height: calc(var(--spacing) * 9);\n padding: var(--spacing) calc(var(--spacing) * 2.5);\n border: 1px solid var(--input);\n border-radius: var(--radius-md);\n background-color: light-dark(transparent, color-mix(in oklch, var(--input) 30%, transparent));\n font-size: 1rem;\n line-height: 1.5;\n box-shadow: var(--shadow-xs);\n outline: none;\n transition:\n color 150ms,\n border-color 150ms,\n box-shadow 150ms;\n }\n input::placeholder,\n textarea::placeholder {\n color: var(--muted-foreground);\n }\n input:where(:not([type='hidden'], [type='checkbox'], [type='radio'])):focus-visible,\n select:focus-visible,\n textarea:focus-visible {\n border-color: var(--ring);\n box-shadow: var(--focus-ring);\n }\n :is(input, select, textarea)[aria-invalid='true'] {\n border-color: light-dark(var(--destructive), color-mix(in oklch, var(--destructive) 50%, transparent));\n box-shadow: 0 0 0 3px\n light-dark(\n color-mix(in oklch, var(--destructive) 20%, transparent),\n color-mix(in oklch, var(--destructive) 40%, transparent)\n );\n }\n :is(input, select, textarea):disabled {\n cursor: not-allowed;\n opacity: 0.5;\n }\n textarea {\n height: auto;\n min-height: calc(var(--spacing) * 16);\n padding-block: calc(var(--spacing) * 2);\n resize: vertical;\n field-sizing: content;\n }\n select {\n appearance: none;\n padding-right: calc(var(--spacing) * 8);\n background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23737373' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E\");\n background-repeat: no-repeat;\n background-position: right 10px center;\n }\n :is(input, select)[data-size='sm'] {\n height: calc(var(--spacing) * 8);\n }\n input[type='checkbox'],\n input[type='radio'] {\n width: calc(var(--spacing) * 4);\n height: calc(var(--spacing) * 4);\n margin: 0;\n accent-color: var(--primary);\n }\n{% endstylesheet %}\n", + "type": "registry:file", + "target": "~/snippets/ui-input.liquid" + } + ], + "type": "registry:item" +} \ No newline at end of file diff --git a/public/r/liquid/invalid-controls.json b/public/r/liquid/invalid-controls.json new file mode 100644 index 000000000..14b7916b5 --- /dev/null +++ b/public/r/liquid/invalid-controls.json @@ -0,0 +1,18 @@ +{ + "$schema": "https://ui.shadcn.com/schema/registry-item.json", + "name": "invalid-controls", + "title": "Invalid Controls", + "description": "Native Liquid invalid-controls with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json" + ], + "files": [ + { + "path": "components/invalid-controls.liquid", + "content": "{% doc %}\n Fulldev UI invalid-controls styles. Use native markup with its data-slot parts.\n{% enddoc %}\n\n{% stylesheet %}\n /* Vega's invalid border and ring, after the focus rules so they win. The select controller sets\n aria-invalid on its trigger. The radio group and switch controllers validate the native input\n they add right after each item and switch, which matches :user-invalid after a submit attempt. */\n [data-slot='select-trigger'][aria-invalid='true'],\n :is([data-slot='radio-group-item'], [data-slot='switch']):is([aria-invalid='true'], :has(+ input:user-invalid)) {\n border-color: light-dark(var(--destructive), color-mix(in oklch, var(--destructive) 50%, transparent));\n box-shadow: 0 0 0 3px\n light-dark(\n color-mix(in oklch, var(--destructive) 20%, transparent),\n color-mix(in oklch, var(--destructive) 40%, transparent)\n );\n }\n{% endstylesheet %}\n", + "type": "registry:file", + "target": "~/snippets/ui-invalid-controls.liquid" + } + ], + "type": "registry:item" +} \ No newline at end of file diff --git a/public/r/liquid/item.json b/public/r/liquid/item.json new file mode 100644 index 000000000..3854ad542 --- /dev/null +++ b/public/r/liquid/item.json @@ -0,0 +1,18 @@ +{ + "$schema": "https://ui.shadcn.com/schema/registry-item.json", + "name": "item", + "title": "Item", + "description": "Native Liquid item with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json" + ], + "files": [ + { + "path": "components/item.liquid", + "content": "{% doc %}\n Fulldev UI item styles. Use native markup with its data-slot parts.\n{% enddoc %}\n\n{% stylesheet %}\n /* A row of media, content and actions, as Vega's item. */\n [data-slot='item'] {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: calc(var(--spacing) * 3.5);\n width: 100%;\n padding: calc(var(--spacing) * 3.5) calc(var(--spacing) * 4);\n border: 1px solid transparent;\n border-radius: var(--radius-md);\n font-size: 0.875rem;\n outline: none;\n transition: background-color 100ms;\n }\n a[data-slot='item']:hover {\n background: var(--muted);\n }\n [data-slot='item']:focus-visible {\n border-color: var(--ring);\n box-shadow: var(--focus-ring);\n }\n [data-slot='item'][data-variant='outline'] {\n border-color: var(--border);\n }\n [data-slot='item'][data-variant='muted'] {\n background: color-mix(in oklch, var(--muted) 50%, transparent);\n }\n [data-slot='item'][data-size='sm'] {\n gap: calc(var(--spacing) * 2.5);\n padding: calc(var(--spacing) * 2.5) calc(var(--spacing) * 3);\n }\n [data-slot='item'][data-size='xs'] {\n gap: calc(var(--spacing) * 2);\n padding: calc(var(--spacing) * 2) calc(var(--spacing) * 2.5);\n }\n [data-slot='item-media'] {\n display: flex;\n flex-shrink: 0;\n align-items: center;\n justify-content: center;\n gap: calc(var(--spacing) * 2);\n }\n [data-slot='item']:has([data-slot='item-description']) > [data-slot='item-media'] {\n align-self: flex-start;\n translate: 0 2px;\n }\n [data-slot='item-media'] > .icon {\n width: calc(var(--spacing) * 4);\n height: calc(var(--spacing) * 4);\n }\n [data-slot='item-media'][data-variant='image'] {\n width: calc(var(--spacing) * 10);\n height: calc(var(--spacing) * 10);\n overflow: hidden;\n border-radius: var(--radius-sm);\n }\n [data-slot='item-media'][data-variant='image'] img {\n width: 100%;\n height: 100%;\n object-fit: cover;\n }\n [data-slot='item-content'] {\n display: flex;\n flex: 1;\n flex-direction: column;\n gap: var(--spacing);\n min-width: 0;\n }\n [data-slot='item-title'] {\n display: flex;\n align-items: center;\n gap: calc(var(--spacing) * 2);\n font-weight: 500;\n line-height: 1.375;\n }\n [data-slot='item-description'] {\n color: var(--muted-foreground);\n line-height: 1.5;\n }\n [data-slot='item'][data-size='xs'] [data-slot='item-content'] {\n gap: 0;\n }\n [data-slot='item'][data-size='xs'] [data-slot='item-description'] {\n font-size: 0.75rem;\n }\n [data-slot='item-actions'],\n [data-slot='item-header'],\n [data-slot='item-footer'] {\n display: flex;\n align-items: center;\n gap: calc(var(--spacing) * 2);\n }\n [data-slot='item-header'],\n [data-slot='item-footer'] {\n flex-basis: 100%;\n justify-content: space-between;\n }\n [data-slot='item-group'] {\n display: flex;\n flex-direction: column;\n gap: calc(var(--spacing) * 4);\n margin: 0;\n padding: 0;\n list-style: none;\n }\n [data-slot='item-group']:has([data-slot='item'][data-size='sm']) {\n gap: calc(var(--spacing) * 2.5);\n }\n{% endstylesheet %}\n", + "type": "registry:file", + "target": "~/snippets/ui-item.liquid" + } + ], + "type": "registry:item" +} \ No newline at end of file diff --git a/public/r/liquid/kbd.json b/public/r/liquid/kbd.json new file mode 100644 index 000000000..c72f96100 --- /dev/null +++ b/public/r/liquid/kbd.json @@ -0,0 +1,18 @@ +{ + "$schema": "https://ui.shadcn.com/schema/registry-item.json", + "name": "kbd", + "title": "Kbd", + "description": "Native Liquid kbd with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json" + ], + "files": [ + { + "path": "components/kbd.liquid", + "content": "{% doc %}\n Fulldev UI kbd styles. Use native markup with its data-slot parts.\n{% enddoc %}\n\n{% stylesheet %}\n [data-slot='kbd'] {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: var(--spacing);\n width: fit-content;\n min-width: calc(var(--spacing) * 5);\n height: calc(var(--spacing) * 5);\n padding-inline: var(--spacing);\n border-radius: var(--radius-sm);\n background: var(--muted);\n color: var(--muted-foreground);\n font: 500 0.75rem/1 var(--font-sans);\n }\n{% endstylesheet %}\n", + "type": "registry:file", + "target": "~/snippets/ui-kbd.liquid" + } + ], + "type": "registry:item" +} \ No newline at end of file diff --git a/public/r/liquid/label.json b/public/r/liquid/label.json new file mode 100644 index 000000000..a3adb72a0 --- /dev/null +++ b/public/r/liquid/label.json @@ -0,0 +1,11 @@ +{ + "$schema": "https://ui.shadcn.com/schema/registry-item.json", + "name": "label", + "title": "Label", + "description": "Native Liquid label with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/field.json" + ], + "files": [], + "type": "registry:item" +} \ No newline at end of file diff --git a/public/r/liquid/logo.json b/public/r/liquid/logo.json new file mode 100644 index 000000000..92b2091fc --- /dev/null +++ b/public/r/liquid/logo.json @@ -0,0 +1,18 @@ +{ + "$schema": "https://ui.shadcn.com/schema/registry-item.json", + "name": "logo", + "title": "Logo", + "description": "Native Liquid logo with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json" + ], + "files": [ + { + "path": "components/logo.liquid", + "content": "{% doc %}\n Fulldev UI logo styles. Use native markup with its data-slot parts.\n{% enddoc %}\n\n{% stylesheet %}\n /* A wordmark stays on one line and ends in an ellipsis when it is wider than its container;\n the full name stays in the text. */\n [data-slot='logo'] {\n display: flex;\n align-items: center;\n gap: 8px;\n max-width: 100%;\n height: 40px;\n font-family: var(--font-heading);\n font-size: 1rem;\n font-weight: 600;\n letter-spacing: var(--tracking-tight);\n white-space: nowrap;\n }\n [data-slot='logo-text'] {\n overflow: hidden;\n font-size: 1.125rem;\n text-overflow: ellipsis;\n }\n [data-slot='logo-image'] {\n flex: none;\n width: var(--logo-width, auto);\n height: auto;\n max-height: 40px;\n object-fit: contain;\n object-position: left;\n }\n{% endstylesheet %}\n", + "type": "registry:file", + "target": "~/snippets/ui-logo.liquid" + } + ], + "type": "registry:item" +} \ No newline at end of file diff --git a/public/r/liquid/marquee.json b/public/r/liquid/marquee.json new file mode 100644 index 000000000..a53c44580 --- /dev/null +++ b/public/r/liquid/marquee.json @@ -0,0 +1,25 @@ +{ + "$schema": "https://ui.shadcn.com/schema/registry-item.json", + "name": "marquee", + "title": "Marquee", + "description": "Native Liquid marquee with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/runtime.json" + ], + "files": [ + { + "path": "components/marquee.liquid", + "content": "{% doc %}\n Fulldev UI marquee styles. Use native markup with its data-slot parts.\n{% enddoc %}\n\n{{ 'ui-marquee.css' | asset_url | stylesheet_tag }}\n", + "type": "registry:file", + "target": "~/snippets/ui-marquee.liquid" + }, + { + "path": "styles/marquee.css", + "content": "/* Fulldev UI's marquee parts on a CSS animation. Each marquee-content row holds its items once in a\n track and again in an inert duplicate. Rows stay static and wrapped without JavaScript and with\n reduced motion; motion.js sets data-motion-ready once the marquee-toggle pause button works.\n data-reverse runs a row the other way. Hover, focus and the toggle pause every row. */\n[data-slot='marquee'] {\n --marquee-gap: calc(var(--spacing) * 4);\n position: relative;\n}\n[data-slot='marquee-content'] {\n display: flex;\n overflow: hidden;\n}\n.marquee-track {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n justify-content: center;\n min-width: 100%;\n margin-inline-start: calc(var(--marquee-gap) * -1);\n}\n.marquee-track[aria-hidden='true'] {\n display: none;\n}\n[data-slot='marquee-item'] {\n flex: none;\n min-width: 0;\n padding-inline-start: var(--marquee-gap);\n}\n@media (prefers-reduced-motion: no-preference) {\n [data-slot='marquee'][data-motion-ready] .marquee-track {\n flex: none;\n flex-wrap: nowrap;\n justify-content: flex-start;\n margin-inline-start: 0;\n animation: marquee var(--marquee-duration, 40s) linear infinite;\n }\n [data-slot='marquee'][data-motion-ready] .marquee-track[aria-hidden='true'] {\n display: flex;\n }\n [data-slot='marquee'][data-motion-ready] [data-reverse] .marquee-track {\n animation-direction: reverse;\n }\n}\n[data-slot='marquee']:hover .marquee-track,\n[data-slot='marquee']:focus-within .marquee-track,\n[data-slot='marquee'][data-paused] .marquee-track {\n animation-play-state: paused;\n}\n@keyframes marquee {\n to {\n transform: translateX(-100%);\n }\n}\n/* The marquee's pause button, an outline icon-sm button. */\n[data-slot='marquee-toggle'] {\n z-index: 1;\n}\n/* The pause buttons of marquees swap their icon once pressed. */\n.motion-pause .icon--play,\n.motion-pause[aria-pressed='true'] .icon--pause {\n display: none;\n}\n.motion-pause[aria-pressed='true'] .icon--play {\n display: block;\n}\n", + "type": "registry:file", + "target": "~/assets/ui-marquee.css" + } + ], + "type": "registry:item" +} \ No newline at end of file diff --git a/public/r/liquid/native-select.json b/public/r/liquid/native-select.json new file mode 100644 index 000000000..8aa646bf6 --- /dev/null +++ b/public/r/liquid/native-select.json @@ -0,0 +1,11 @@ +{ + "$schema": "https://ui.shadcn.com/schema/registry-item.json", + "name": "native-select", + "title": "Native Select", + "description": "Native Liquid native-select with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/input.json" + ], + "files": [], + "type": "registry:item" +} \ No newline at end of file diff --git a/public/r/liquid/navigation-menu.json b/public/r/liquid/navigation-menu.json new file mode 100644 index 000000000..fe4a62038 --- /dev/null +++ b/public/r/liquid/navigation-menu.json @@ -0,0 +1,26 @@ +{ + "$schema": "https://ui.shadcn.com/schema/registry-item.json", + "name": "navigation-menu", + "title": "Navigation Menu", + "description": "Native Liquid navigation-menu with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/button.json", + "https://ui.full.dev/r/liquid/runtime.json" + ], + "files": [ + { + "path": "components/navigation-menu.liquid", + "content": "{% doc %}\n Fulldev UI navigation-menu styles. Use native markup with its data-slot parts.\n{% enddoc %}\n\n{{ 'ui-navigation-menu.css' | asset_url | stylesheet_tag }}\n", + "type": "registry:file", + "target": "~/snippets/ui-navigation-menu.liquid" + }, + { + "path": "styles/navigation-menu.css", + "content": ".navigation-menu {\n position: relative;\n justify-self: start;\n}\n.navigation-menu [data-slot='navigation-menu-list'] {\n display: flex;\n align-items: center;\n gap: 2px;\n margin: 0;\n padding: 0;\n list-style: none;\n}\n/* A long merchant label wraps and grows the link from its 36px height. */\n.navigation-menu [data-slot='navigation-menu-list'] > li > a,\n.navigation-menu [data-slot='navigation-menu-trigger'] {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n min-height: 36px;\n padding: 6px 16px;\n border-radius: var(--radius-md);\n background: transparent;\n font-size: 0.875rem;\n font-weight: 500;\n overflow-wrap: anywhere;\n text-decoration: none;\n transition:\n background-color 150ms,\n color 150ms;\n}\n.navigation-menu [data-slot='navigation-menu-trigger'] .icon {\n flex: none;\n width: 12px;\n height: 12px;\n margin-left: 4px;\n transition: rotate 300ms;\n}\n.navigation-menu [data-slot='navigation-menu-trigger']:hover,\n.navigation-menu [data-slot='navigation-menu-trigger']:focus-visible,\n.navigation-menu [data-slot='navigation-menu-list'] > li > a:hover {\n background: var(--muted);\n}\n.navigation-menu [data-slot='navigation-menu-list'] > li > a[aria-current] {\n background: color-mix(in oklch, var(--muted) 50%, transparent);\n}\n.navigation-menu [data-slot='navigation-menu-trigger'][data-open] {\n background: color-mix(in oklch, var(--muted) 50%, transparent);\n}\n.navigation-menu [data-slot='navigation-menu-trigger'][data-open] .icon {\n rotate: 180deg;\n}\n.navigation-menu [data-slot='navigation-menu-trigger']:focus-visible,\n.navigation-menu [data-slot='navigation-menu-list'] > li > a:focus-visible {\n outline: 1px solid var(--ring);\n outline-offset: 0;\n box-shadow: var(--focus-ring);\n}\n[data-slot='navigation-menu-positioner'] {\n z-index: 40;\n box-sizing: border-box;\n width: var(--positioner-width);\n height: var(--positioner-height);\n max-width: var(--available-width);\n top: 0;\n left: 0;\n transition:\n top 0.3s cubic-bezier(0.22, 1, 0.36, 1),\n left 0.3s cubic-bezier(0.22, 1, 0.36, 1);\n}\n[data-slot='navigation-menu-positioner'][data-instant] {\n transition: none;\n}\n[data-slot='navigation-menu-popup'] {\n position: relative;\n box-sizing: border-box;\n width: var(--popup-width);\n height: var(--popup-height);\n transform-origin: var(--transform-origin);\n overflow: hidden;\n border-radius: var(--radius-lg);\n background: var(--popover);\n color: var(--popover-foreground);\n box-shadow:\n 0 0 0 1px color-mix(in oklch, var(--foreground) 10%, transparent),\n var(--shadow-md);\n transition:\n transform 0.3s cubic-bezier(0.22, 1, 0.36, 1),\n width 0.3s cubic-bezier(0.22, 1, 0.36, 1),\n height 0.3s cubic-bezier(0.22, 1, 0.36, 1),\n opacity 0.3s cubic-bezier(0.22, 1, 0.36, 1);\n}\n[data-slot='navigation-menu-popup'][data-ending-style] {\n transition-duration: 150ms;\n transition-timing-function: ease;\n}\n[data-slot='navigation-menu-popup'][data-instant] {\n transition:\n transform 0.3s cubic-bezier(0.22, 1, 0.36, 1),\n opacity 0.3s cubic-bezier(0.22, 1, 0.36, 1);\n}\n[data-slot='navigation-menu-popup'][data-starting-style],\n[data-slot='navigation-menu-popup'][data-ending-style] {\n opacity: 0;\n transform: scale(0.9);\n}\n[data-slot='navigation-menu-popup'][data-closed]:not([data-ending-style]) {\n pointer-events: none;\n}\n[data-slot='navigation-menu-viewport'] {\n position: relative;\n overflow: hidden;\n width: 100%;\n height: 100%;\n}\n[data-slot='navigation-menu-content'] {\n position: relative;\n transform: translateX(0);\n transition:\n transform 0.3s cubic-bezier(0.22, 1, 0.36, 1),\n opacity 0.3s cubic-bezier(0.22, 1, 0.36, 1);\n will-change: transform, opacity;\n}\n[data-slot='navigation-menu-content'][data-closed]:not([data-ending-style]) {\n opacity: 0;\n pointer-events: none;\n}\n[data-slot='navigation-menu-content'][data-starting-style],\n[data-slot='navigation-menu-content'][data-ending-style] {\n opacity: 0;\n}\n[data-slot='navigation-menu-content'][data-starting-style][data-activation-direction='right'] {\n transform: translateX(50%);\n}\n[data-slot='navigation-menu-content'][data-starting-style][data-activation-direction='left'] {\n transform: translateX(-50%);\n}\n[data-slot='navigation-menu-content'][data-ending-style][data-activation-direction='right'] {\n transform: translateX(-50%);\n}\n[data-slot='navigation-menu-content'][data-ending-style][data-activation-direction='left'] {\n transform: translateX(50%);\n}\n/* Fulldev UI's mega panel: the overview card beside the child links. */\n.navigation-panel {\n display: grid;\n gap: 8px;\n width: min(46rem, calc(100vw - 32px));\n padding: 4px;\n}\n.navigation-panel[data-layout='wide'] {\n width: min(52rem, calc(100vw - 32px));\n padding: 8px;\n}\n[data-slot='navigation-menu-link'] {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 8px;\n border-radius: var(--radius-md);\n font-size: 0.875rem;\n outline: none;\n transition: background-color 150ms;\n}\n.navigation-panel [data-slot='navigation-menu-link']:is(:hover, :focus-visible) {\n background: var(--muted);\n}\n.navigation-panel [data-slot='navigation-menu-link']:focus-visible {\n box-shadow: var(--focus-ring);\n}\n.navigation-panel [data-slot='navigation-menu-link'][aria-current] {\n background: color-mix(in oklch, var(--muted) 50%, transparent);\n}\n.navigation-panel__overview[data-slot='navigation-menu-link'] {\n flex-direction: column;\n align-items: flex-start;\n justify-content: flex-end;\n gap: 12px;\n min-height: 160px;\n padding: 16px;\n background: color-mix(in oklch, var(--muted) 50%, transparent);\n}\n.navigation-panel[data-layout='wide'] .navigation-panel__overview[data-slot='navigation-menu-link'] {\n justify-content: flex-start;\n padding: 12px;\n}\n.navigation-panel__overview img {\n width: 100%;\n margin-bottom: auto;\n aspect-ratio: 16 / 9;\n border-radius: var(--radius-md);\n background: var(--muted);\n object-fit: cover;\n}\n.navigation-panel__overview-title {\n font-size: 1rem;\n font-weight: 500;\n}\n.navigation-panel__description {\n color: var(--muted-foreground);\n font-size: 0.875rem;\n line-height: 1.625;\n}\n.navigation-panel__links {\n display: grid;\n align-content: start;\n gap: 4px;\n}\n.navigation-panel__link {\n padding: 12px;\n font-weight: 500;\n}\n.navigation-panel__sublinks {\n display: grid;\n gap: 2px;\n margin-inline-start: 12px;\n}\n.navigation-panel__link--sub {\n padding-block: 6px;\n color: var(--muted-foreground);\n font-weight: 400;\n}\n.navigation-panel__link--sub:hover {\n color: var(--foreground);\n}\n@media (min-width: 1024px) {\n .navigation-panel {\n grid-template-columns: 1fr 1.1fr;\n }\n .navigation-panel[data-layout='wide'] {\n grid-template-columns: 1fr 1.2fr;\n }\n .navigation-panel[data-layout='wide'] .navigation-panel__links {\n grid-template-columns: repeat(2, minmax(0, 1fr));\n }\n}\n", + "type": "registry:file", + "target": "~/assets/ui-navigation-menu.css" + } + ], + "type": "registry:item" +} \ No newline at end of file diff --git a/public/r/liquid/pagination.json b/public/r/liquid/pagination.json new file mode 100644 index 000000000..f8fdded31 --- /dev/null +++ b/public/r/liquid/pagination.json @@ -0,0 +1,18 @@ +{ + "$schema": "https://ui.shadcn.com/schema/registry-item.json", + "name": "pagination", + "title": "Pagination", + "description": "Native Liquid pagination with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json" + ], + "files": [ + { + "path": "components/pagination.liquid", + "content": "{% doc %}\n Fulldev UI pagination styles. Use native markup with its data-slot parts.\n{% enddoc %}\n\n{% stylesheet %}\n /* Links take the button look: ghost, and outline for the current page. */\n [data-slot='pagination'] {\n display: flex;\n justify-content: center;\n width: 100%;\n }\n [data-slot='pagination-content'] {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--spacing);\n margin: 0;\n padding: 0;\n list-style: none;\n }\n [data-slot='pagination-link'] {\n min-width: calc(var(--spacing) * 9);\n }\n [data-slot='pagination-ellipsis'] {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: calc(var(--spacing) * 9);\n height: calc(var(--spacing) * 9);\n color: var(--muted-foreground);\n }\n{% endstylesheet %}\n", + "type": "registry:file", + "target": "~/snippets/ui-pagination.liquid" + } + ], + "type": "registry:item" +} \ No newline at end of file diff --git a/public/r/liquid/popover.json b/public/r/liquid/popover.json new file mode 100644 index 000000000..780c9a375 --- /dev/null +++ b/public/r/liquid/popover.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://ui.shadcn.com/schema/registry-item.json", + "name": "popover", + "title": "Popover", + "description": "Native Liquid popover with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/popup.json", + "https://ui.full.dev/r/liquid/triggers.json", + "https://ui.full.dev/r/liquid/runtime.json" + ], + "files": [ + { + "path": "components/popover.liquid", + "content": "{% doc %}\n popover uses its shared popup or trigger styles and native data-slot controller.\n{% enddoc %}\n", + "type": "registry:file", + "target": "~/snippets/ui-popover.liquid" + } + ], + "type": "registry:item" +} \ No newline at end of file diff --git a/public/r/liquid/popup.json b/public/r/liquid/popup.json new file mode 100644 index 000000000..cf0216c83 --- /dev/null +++ b/public/r/liquid/popup.json @@ -0,0 +1,18 @@ +{ + "$schema": "https://ui.shadcn.com/schema/registry-item.json", + "name": "popup", + "title": "Popup", + "description": "Native Liquid popup with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json" + ], + "files": [ + { + "path": "components/popup.liquid", + "content": "{% doc %}\n Fulldev UI popup styles. Use native markup with its data-slot parts.\n{% enddoc %}\n\n{% stylesheet %}\n /* Shared surface of the anchored popups, as Vega's popover, select, dropdown menu and hover card\n content: the popover colors, a hairline ring, a medium shadow and a 100ms fade and zoom. */\n :is(\n [data-slot='select-positioner'],\n [data-slot='popover-positioner'],\n [data-slot='hover-card-positioner'],\n [data-slot='dropdown-menu-positioner'],\n [data-slot='tooltip-positioner']\n ) {\n z-index: 1000;\n isolation: isolate;\n }\n [data-slot='select-content'],\n [data-slot='popover-content'],\n [data-slot='hover-card-content'],\n [data-slot='dropdown-menu-content'] {\n border-radius: var(--radius-md);\n background: var(--popover);\n color: var(--popover-foreground);\n font-size: 0.875rem;\n box-shadow:\n 0 0 0 1px color-mix(in oklch, var(--foreground) 10%, transparent),\n var(--shadow-md);\n outline: none;\n transform-origin: var(--transform-origin, center);\n transition:\n opacity 100ms,\n scale 100ms;\n }\n :is(\n [data-slot='select-content'],\n [data-slot='popover-content'],\n [data-slot='hover-card-content'],\n [data-slot='dropdown-menu-content'],\n [data-slot='tooltip-content']\n ):is([data-starting-style], [data-ending-style]) {\n opacity: 0;\n scale: 0.95;\n }\n :is([data-slot='hover-card-content'], [data-slot='tooltip-content'])[data-instant] {\n transition: none;\n }\n [data-slot='select-content'] {\n min-width: 9rem;\n max-height: var(--available-height, 24rem);\n overflow: hidden auto;\n }\n [data-slot='select-viewport'] {\n padding: var(--spacing);\n }\n [data-slot='select-label'],\n [data-slot='dropdown-menu-label'] {\n padding: calc(var(--spacing) * 1.5) calc(var(--spacing) * 2);\n color: var(--muted-foreground);\n font-size: 0.75rem;\n }\n [data-slot='dropdown-menu-label'] {\n font-weight: 500;\n }\n [data-slot='select-item'],\n [data-slot='dropdown-menu-item'],\n [data-slot='dropdown-menu-checkbox-item'],\n [data-slot='dropdown-menu-radio-item'] {\n position: relative;\n display: flex;\n align-items: center;\n gap: calc(var(--spacing) * 2);\n width: 100%;\n padding: calc(var(--spacing) * 1.5) calc(var(--spacing) * 2);\n border-radius: var(--radius-sm);\n font-size: 0.875rem;\n text-align: left;\n text-decoration: none;\n cursor: default;\n user-select: none;\n outline: none;\n }\n [data-slot='select-item'],\n [data-slot='dropdown-menu-checkbox-item'],\n [data-slot='dropdown-menu-radio-item'] {\n padding-right: calc(var(--spacing) * 8);\n }\n :is(\n [data-slot='select-item'],\n [data-slot='dropdown-menu-item'],\n [data-slot='dropdown-menu-checkbox-item'],\n [data-slot='dropdown-menu-radio-item']\n )[data-highlighted] {\n background: var(--accent);\n color: var(--accent-foreground);\n }\n :is(\n [data-slot='select-item'],\n [data-slot='dropdown-menu-item'],\n [data-slot='dropdown-menu-checkbox-item'],\n [data-slot='dropdown-menu-radio-item']\n )[data-disabled] {\n opacity: 0.5;\n pointer-events: none;\n }\n :is(\n [data-slot='select-item'],\n [data-slot='dropdown-menu-item'],\n [data-slot='dropdown-menu-checkbox-item'],\n [data-slot='dropdown-menu-radio-item']\n )\n > .icon {\n width: calc(var(--spacing) * 4);\n height: calc(var(--spacing) * 4);\n }\n [data-slot='dropdown-menu-item'][data-variant='destructive'] {\n color: var(--destructive);\n }\n [data-slot='dropdown-menu-item'][data-variant='destructive'][data-highlighted] {\n background: light-dark(\n color-mix(in oklch, var(--destructive) 10%, transparent),\n color-mix(in oklch, var(--destructive) 20%, transparent)\n );\n color: var(--destructive);\n }\n /* The check of a selected option sits at the end of its row. */\n [data-slot='select-item-indicator'],\n [data-slot='dropdown-menu-item-indicator'] {\n position: absolute;\n right: calc(var(--spacing) * 2);\n display: flex;\n align-items: center;\n justify-content: center;\n width: calc(var(--spacing) * 4);\n height: calc(var(--spacing) * 4);\n visibility: hidden;\n pointer-events: none;\n }\n [data-slot='select-item-indicator'] > .icon,\n [data-slot='dropdown-menu-item-indicator'] > .icon {\n width: calc(var(--spacing) * 4);\n height: calc(var(--spacing) * 4);\n }\n [data-selected] > [data-slot='select-item-indicator'],\n [data-checked] > [data-slot='dropdown-menu-item-indicator'] {\n visibility: visible;\n }\n [data-slot='select-separator'],\n [data-slot='dropdown-menu-separator'] {\n height: 1px;\n margin: var(--spacing) calc(var(--spacing) * -1);\n background: var(--border);\n }\n [data-slot='dropdown-menu-content'] {\n min-width: 8rem;\n padding: var(--spacing);\n }\n [data-slot='dropdown-menu-shortcut'] {\n margin-left: auto;\n color: var(--muted-foreground);\n font-size: 0.75rem;\n letter-spacing: 0.1em;\n }\n [data-slot='popover-content'] {\n display: flex;\n flex-direction: column;\n gap: calc(var(--spacing) * 4);\n width: min(18rem, var(--available-width, 18rem));\n padding: calc(var(--spacing) * 4);\n }\n [data-slot='popover-header'] {\n display: flex;\n flex-direction: column;\n gap: var(--spacing);\n }\n [data-slot='popover-title'] {\n font-size: 0.875rem;\n font-weight: 500;\n letter-spacing: var(--tracking-normal);\n }\n [data-slot='popover-description'] {\n color: var(--muted-foreground);\n }\n [data-slot='hover-card-content'] {\n width: min(16rem, var(--available-width, 16rem));\n padding: calc(var(--spacing) * 4);\n border-radius: var(--radius-lg);\n }\n{% endstylesheet %}\n", + "type": "registry:file", + "target": "~/snippets/ui-popup.liquid" + } + ], + "type": "registry:item" +} \ No newline at end of file diff --git a/public/r/liquid/price.json b/public/r/liquid/price.json new file mode 100644 index 000000000..0f8520ef5 --- /dev/null +++ b/public/r/liquid/price.json @@ -0,0 +1,24 @@ +{ + "$schema": "https://ui.shadcn.com/schema/registry-item.json", + "name": "price", + "title": "Price", + "description": "Native Liquid price with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json" + ], + "files": [ + { + "path": "components/price.liquid", + "content": "{% doc %}\n Fulldev UI price styles. Use native markup with its data-slot parts.\n{% enddoc %}\n\n{{ 'ui-price.css' | asset_url | stylesheet_tag }}\n", + "type": "registry:file", + "target": "~/snippets/ui-price.liquid" + }, + { + "path": "styles/price.css", + "content": "/* Fulldev UI's price: the current value, the higher compare-at price struck through, a savings\n badge, and the unit price on its own line. */\n[data-slot='price'] {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--spacing) calc(var(--spacing) * 2);\n font-size: 0.875rem;\n}\n[data-slot='price-value'] {\n display: inline-flex;\n color: var(--foreground);\n flex-wrap: wrap;\n align-items: center;\n gap: var(--spacing) calc(var(--spacing) * 2);\n line-height: 1.25;\n}\n[data-slot='price-value'] del {\n color: var(--muted-foreground);\n font-size: 0.875rem;\n}\n.price__badge {\n padding: calc(var(--spacing) * 0.5) calc(var(--spacing) * 2);\n border-radius: 9999px;\n background: var(--muted);\n color: var(--foreground);\n font-size: 0.75rem;\n font-weight: 500;\n line-height: 1rem;\n}\n[data-slot='price'] [data-slot='price-unit'] {\n width: 100%;\n}\n[data-slot='price-unit'] {\n font-size: 0.75rem;\n color: var(--muted-foreground);\n}\n.price--lg {\n font-size: 1.25rem;\n font-weight: 500;\n}\n", + "type": "registry:file", + "target": "~/assets/ui-price.css" + } + ], + "type": "registry:item" +} \ No newline at end of file diff --git a/public/r/liquid/radio-group.json b/public/r/liquid/radio-group.json new file mode 100644 index 000000000..90e1cca96 --- /dev/null +++ b/public/r/liquid/radio-group.json @@ -0,0 +1,20 @@ +{ + "$schema": "https://ui.shadcn.com/schema/registry-item.json", + "name": "radio-group", + "title": "Radio Group", + "description": "Native Liquid radio-group with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/invalid-controls.json", + "https://ui.full.dev/r/liquid/runtime.json" + ], + "files": [ + { + "path": "components/radio-group.liquid", + "content": "{% doc %}\n Fulldev UI radio-group styles. Use native markup with its data-slot parts.\n{% enddoc %}\n\n{% stylesheet %}\n /* The items are spans; the controller adds a hidden native radio for each, with the group's name. */\n [data-slot='radio-group'] {\n display: grid;\n gap: calc(var(--spacing) * 3);\n }\n [data-slot='radio-group-item'] {\n position: relative;\n display: flex;\n flex-shrink: 0;\n width: calc(var(--spacing) * 4);\n height: calc(var(--spacing) * 4);\n border: 1px solid var(--input);\n border-radius: 50%;\n background: light-dark(transparent, color-mix(in oklch, var(--input) 30%, transparent));\n box-shadow: var(--shadow-xs);\n outline: none;\n transition: box-shadow 150ms;\n }\n [data-slot='radio-group-item'][data-checked] {\n border-color: var(--primary);\n background: var(--primary);\n }\n [data-slot='radio-group-item']:focus-visible {\n border-color: var(--ring);\n box-shadow: var(--focus-ring);\n }\n [data-slot='radio-group-item'][data-disabled] {\n cursor: not-allowed;\n opacity: 0.5;\n }\n [data-slot='radio-group-indicator'] {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 100%;\n opacity: 0;\n }\n [data-slot='radio-group-indicator']::after {\n content: '';\n width: calc(var(--spacing) * 2);\n height: calc(var(--spacing) * 2);\n border-radius: 50%;\n background: var(--primary-foreground);\n }\n [data-slot='radio-group-indicator'][data-checked] {\n opacity: 1;\n }\n{% endstylesheet %}\n", + "type": "registry:file", + "target": "~/snippets/ui-radio-group.liquid" + } + ], + "type": "registry:item" +} \ No newline at end of file diff --git a/public/r/liquid/rating.json b/public/r/liquid/rating.json new file mode 100644 index 000000000..43ff409fd --- /dev/null +++ b/public/r/liquid/rating.json @@ -0,0 +1,24 @@ +{ + "$schema": "https://ui.shadcn.com/schema/registry-item.json", + "name": "rating", + "title": "Rating", + "description": "Native Liquid rating with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json" + ], + "files": [ + { + "path": "components/rating.liquid", + "content": "{% doc %}\n Fulldev UI rating styles. Use native markup with its data-slot parts.\n{% enddoc %}\n\n{{ 'ui-rating.css' | asset_url | stylesheet_tag }}\n", + "type": "registry:file", + "target": "~/snippets/ui-rating.liquid" + }, + { + "path": "styles/rating.css", + "content": "[data-slot='rating'] {\n display: inline-flex;\n align-items: center;\n gap: var(--spacing);\n color: var(--primary);\n}\n.rating__star {\n position: relative;\n display: inline-flex;\n width: calc(var(--spacing) * 4);\n height: calc(var(--spacing) * 4);\n color: color-mix(in oklch, var(--muted-foreground) 25%, transparent);\n}\n.rating__star svg {\n width: 100%;\n height: 100%;\n}\n.rating__fill {\n position: absolute;\n inset: 0;\n overflow: hidden;\n color: var(--primary);\n}\n", + "type": "registry:file", + "target": "~/assets/ui-rating.css" + } + ], + "type": "registry:item" +} \ No newline at end of file diff --git a/public/r/liquid/registry.json b/public/r/liquid/registry.json new file mode 100644 index 000000000..ed07bfa17 --- /dev/null +++ b/public/r/liquid/registry.json @@ -0,0 +1,1295 @@ +{ + "$schema": "https://ui.shadcn.com/schema/registry.json", + "name": "@fulldev/liquid", + "homepage": "https://ui.full.dev/liquid/", + "items": [ + { + "name": "init", + "type": "registry:item", + "title": "Init", + "description": "Shared neutral tokens, native base styles and licenses. No framework or build required.", + "registryDependencies": [], + "files": [ + { + "path": "styles/tokens.css", + "type": "registry:file", + "target": "~/assets/ui-tokens.css" + }, + { + "path": "components/base.liquid", + "type": "registry:file", + "target": "~/snippets/ui-base.liquid" + }, + { + "path": "snippets/init.liquid", + "type": "registry:file", + "target": "~/snippets/ui-init.liquid" + }, + { + "path": "licenses.json", + "type": "registry:file", + "target": "~/assets/ui-licenses.json" + } + ] + }, + { + "name": "runtime", + "type": "registry:item", + "title": "Runtime", + "description": "Native ES modules, lazy data-slot controllers and Shopify editor lifecycle. Render once before other modules, or extend your own import map and use the exported lifecycle.", + "registryDependencies": ["https://ui.full.dev/r/liquid/init.json"], + "files": [ + { + "path": "runtime/ui-carousel.js", + "type": "registry:file", + "target": "~/assets/ui-carousel.js" + }, + { + "path": "runtime/ui-controls.js", + "type": "registry:file", + "target": "~/assets/ui-controls.js" + }, + { + "path": "runtime/ui-lifecycle.js", + "type": "registry:file", + "target": "~/assets/ui-lifecycle.js" + }, + { + "path": "runtime/ui-start.js", + "type": "registry:file", + "target": "~/assets/ui-start.js" + }, + { + "path": "vendor/data-slot-core.js", + "type": "registry:file", + "target": "~/assets/data-slot-core.js" + }, + { + "path": "vendor/data-slot-dialog.js", + "type": "registry:file", + "target": "~/assets/data-slot-dialog.js" + }, + { + "path": "vendor/data-slot-command.js", + "type": "registry:file", + "target": "~/assets/data-slot-command.js" + }, + { + "path": "vendor/data-slot-navigation-menu.js", + "type": "registry:file", + "target": "~/assets/data-slot-navigation-menu.js" + }, + { + "path": "vendor/data-slot-accordion.js", + "type": "registry:file", + "target": "~/assets/data-slot-accordion.js" + }, + { + "path": "vendor/data-slot-tabs.js", + "type": "registry:file", + "target": "~/assets/data-slot-tabs.js" + }, + { + "path": "vendor/data-slot-combobox.js", + "type": "registry:file", + "target": "~/assets/data-slot-combobox.js" + }, + { + "path": "vendor/data-slot-carousel.js", + "type": "registry:file", + "target": "~/assets/data-slot-carousel.js" + }, + { + "path": "vendor/data-slot-alert-dialog.js", + "type": "registry:file", + "target": "~/assets/data-slot-alert-dialog.js" + }, + { + "path": "vendor/data-slot-collapsible.js", + "type": "registry:file", + "target": "~/assets/data-slot-collapsible.js" + }, + { + "path": "vendor/data-slot-drawer.js", + "type": "registry:file", + "target": "~/assets/data-slot-drawer.js" + }, + { + "path": "vendor/data-slot-dropdown-menu.js", + "type": "registry:file", + "target": "~/assets/data-slot-dropdown-menu.js" + }, + { + "path": "vendor/data-slot-hover-card.js", + "type": "registry:file", + "target": "~/assets/data-slot-hover-card.js" + }, + { + "path": "vendor/data-slot-popover.js", + "type": "registry:file", + "target": "~/assets/data-slot-popover.js" + }, + { + "path": "vendor/data-slot-radio-group.js", + "type": "registry:file", + "target": "~/assets/data-slot-radio-group.js" + }, + { + "path": "vendor/data-slot-select.js", + "type": "registry:file", + "target": "~/assets/data-slot-select.js" + }, + { + "path": "vendor/data-slot-slider.js", + "type": "registry:file", + "target": "~/assets/data-slot-slider.js" + }, + { + "path": "vendor/data-slot-switch.js", + "type": "registry:file", + "target": "~/assets/data-slot-switch.js" + }, + { + "path": "vendor/data-slot-toast.js", + "type": "registry:file", + "target": "~/assets/data-slot-toast.js" + }, + { + "path": "vendor/data-slot-toggle.js", + "type": "registry:file", + "target": "~/assets/data-slot-toggle.js" + }, + { + "path": "vendor/data-slot-toggle-group.js", + "type": "registry:file", + "target": "~/assets/data-slot-toggle-group.js" + }, + { + "path": "vendor/data-slot-tooltip.js", + "type": "registry:file", + "target": "~/assets/data-slot-tooltip.js" + }, + { + "path": "snippets/runtime.liquid", + "type": "registry:file", + "target": "~/snippets/ui-runtime.liquid" + }, + { + "path": "runtime/ui-motion.js", + "type": "registry:file", + "target": "~/assets/ui-motion.js" + } + ] + }, + { + "name": "banner", + "type": "registry:item", + "title": "Banner", + "description": "Native Liquid banner with readable CSS and data-slot parts.", + "registryDependencies": ["https://ui.full.dev/r/liquid/init.json"], + "files": [ + { + "path": "components/banner.liquid", + "type": "registry:file", + "target": "~/snippets/ui-banner.liquid" + } + ] + }, + { + "name": "tabs", + "type": "registry:item", + "title": "Tabs", + "description": "Native Liquid tabs with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/triggers.json", + "https://ui.full.dev/r/liquid/runtime.json" + ], + "files": [ + { + "path": "components/tabs.liquid", + "type": "registry:file", + "target": "~/snippets/ui-tabs.liquid" + } + ] + }, + { + "name": "form", + "type": "registry:item", + "title": "Form", + "description": "Native Liquid form with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/field.json" + ], + "files": [ + { + "path": "components/form.liquid", + "type": "registry:file", + "target": "~/snippets/ui-form.liquid" + }, + { + "path": "styles/form.css", + "type": "registry:file", + "target": "~/assets/ui-form.css" + } + ] + }, + { + "name": "select", + "type": "registry:item", + "title": "Select", + "description": "Native Liquid select with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/input.json", + "https://ui.full.dev/r/liquid/popup.json", + "https://ui.full.dev/r/liquid/invalid-controls.json", + "https://ui.full.dev/r/liquid/runtime.json" + ], + "files": [ + { + "path": "components/select.liquid", + "type": "registry:file", + "target": "~/snippets/ui-select.liquid" + } + ] + }, + { + "name": "field", + "type": "registry:item", + "title": "Field", + "description": "Native Liquid field with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/input.json" + ], + "files": [ + { + "path": "components/field.liquid", + "type": "registry:file", + "target": "~/snippets/ui-field.liquid" + }, + { + "path": "styles/field.css", + "type": "registry:file", + "target": "~/assets/ui-field.css" + } + ] + }, + { + "name": "item", + "type": "registry:item", + "title": "Item", + "description": "Native Liquid item with readable CSS and data-slot parts.", + "registryDependencies": ["https://ui.full.dev/r/liquid/init.json"], + "files": [ + { + "path": "components/item.liquid", + "type": "registry:file", + "target": "~/snippets/ui-item.liquid" + } + ] + }, + { + "name": "spinner", + "type": "registry:item", + "title": "Spinner", + "description": "Native Liquid spinner with readable CSS and data-slot parts.", + "registryDependencies": ["https://ui.full.dev/r/liquid/init.json"], + "files": [ + { + "path": "components/spinner.liquid", + "type": "registry:file", + "target": "~/snippets/ui-spinner.liquid" + } + ] + }, + { + "name": "video", + "type": "registry:item", + "title": "Video", + "description": "Native Liquid video with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/aspect-ratio.json" + ], + "files": [ + { + "path": "components/video.liquid", + "type": "registry:file", + "target": "~/snippets/ui-video.liquid" + }, + { + "path": "styles/video.css", + "type": "registry:file", + "target": "~/assets/ui-video.css" + } + ] + }, + { + "name": "button", + "type": "registry:item", + "title": "Button", + "description": "Native Liquid button with readable CSS and data-slot parts.", + "registryDependencies": ["https://ui.full.dev/r/liquid/init.json"], + "files": [ + { + "path": "components/button.liquid", + "type": "registry:file", + "target": "~/snippets/ui-button.liquid" + } + ] + }, + { + "name": "toggle", + "type": "registry:item", + "title": "Toggle", + "description": "Native Liquid toggle with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/triggers.json", + "https://ui.full.dev/r/liquid/runtime.json" + ], + "files": [ + { + "path": "components/toggle.liquid", + "type": "registry:file", + "target": "~/snippets/ui-toggle.liquid" + } + ] + }, + { + "name": "logo", + "type": "registry:item", + "title": "Logo", + "description": "Native Liquid logo with readable CSS and data-slot parts.", + "registryDependencies": ["https://ui.full.dev/r/liquid/init.json"], + "files": [ + { + "path": "components/logo.liquid", + "type": "registry:file", + "target": "~/snippets/ui-logo.liquid" + } + ] + }, + { + "name": "kbd", + "type": "registry:item", + "title": "Kbd", + "description": "Native Liquid kbd with readable CSS and data-slot parts.", + "registryDependencies": ["https://ui.full.dev/r/liquid/init.json"], + "files": [ + { + "path": "components/kbd.liquid", + "type": "registry:file", + "target": "~/snippets/ui-kbd.liquid" + } + ] + }, + { + "name": "rating", + "type": "registry:item", + "title": "Rating", + "description": "Native Liquid rating with readable CSS and data-slot parts.", + "registryDependencies": ["https://ui.full.dev/r/liquid/init.json"], + "files": [ + { + "path": "components/rating.liquid", + "type": "registry:file", + "target": "~/snippets/ui-rating.liquid" + }, + { + "path": "styles/rating.css", + "type": "registry:file", + "target": "~/assets/ui-rating.css" + } + ] + }, + { + "name": "tooltip", + "type": "registry:item", + "title": "Tooltip", + "description": "Native Liquid tooltip with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/popup.json", + "https://ui.full.dev/r/liquid/runtime.json" + ], + "files": [ + { + "path": "components/tooltip.liquid", + "type": "registry:file", + "target": "~/snippets/ui-tooltip.liquid" + } + ] + }, + { + "name": "avatar", + "type": "registry:item", + "title": "Avatar", + "description": "Native Liquid avatar with readable CSS and data-slot parts.", + "registryDependencies": ["https://ui.full.dev/r/liquid/init.json"], + "files": [ + { + "path": "components/avatar.liquid", + "type": "registry:file", + "target": "~/snippets/ui-avatar.liquid" + } + ] + }, + { + "name": "empty", + "type": "registry:item", + "title": "Empty", + "description": "Native Liquid empty with readable CSS and data-slot parts.", + "registryDependencies": ["https://ui.full.dev/r/liquid/init.json"], + "files": [ + { + "path": "components/empty.liquid", + "type": "registry:file", + "target": "~/snippets/ui-empty.liquid" + } + ] + }, + { + "name": "alert-dialog", + "type": "registry:item", + "title": "Alert Dialog", + "description": "Native Liquid alert-dialog with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/triggers.json", + "https://ui.full.dev/r/liquid/button.json", + "https://ui.full.dev/r/liquid/runtime.json" + ], + "files": [ + { + "path": "components/alert-dialog.liquid", + "type": "registry:file", + "target": "~/snippets/ui-alert-dialog.liquid" + } + ] + }, + { + "name": "navigation-menu", + "type": "registry:item", + "title": "Navigation Menu", + "description": "Native Liquid navigation-menu with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/button.json", + "https://ui.full.dev/r/liquid/runtime.json" + ], + "files": [ + { + "path": "components/navigation-menu.liquid", + "type": "registry:file", + "target": "~/snippets/ui-navigation-menu.liquid" + }, + { + "path": "styles/navigation-menu.css", + "type": "registry:file", + "target": "~/assets/ui-navigation-menu.css" + } + ] + }, + { + "name": "typography", + "type": "registry:item", + "title": "Typography", + "description": "Native Liquid typography with readable CSS and data-slot parts.", + "registryDependencies": ["https://ui.full.dev/r/liquid/init.json"], + "files": [ + { + "path": "components/typography.liquid", + "type": "registry:file", + "target": "~/snippets/ui-typography.liquid" + }, + { + "path": "styles/typography.css", + "type": "registry:file", + "target": "~/assets/ui-typography.css" + } + ] + }, + { + "name": "toc", + "type": "registry:item", + "title": "Toc", + "description": "Native Liquid toc with readable CSS and data-slot parts.", + "registryDependencies": ["https://ui.full.dev/r/liquid/init.json"], + "files": [ + { + "path": "components/toc.liquid", + "type": "registry:file", + "target": "~/snippets/ui-toc.liquid" + } + ] + }, + { + "name": "icon", + "type": "registry:item", + "title": "Icon", + "description": "Native Liquid icon with readable CSS and data-slot parts.", + "registryDependencies": ["https://ui.full.dev/r/liquid/init.json"], + "files": [ + { + "path": "components/icon.liquid", + "type": "registry:file", + "target": "~/snippets/ui-icon.liquid" + }, + { + "path": "styles/icon.css", + "type": "registry:file", + "target": "~/assets/ui-icon.css" + } + ] + }, + { + "name": "combobox", + "type": "registry:item", + "title": "Combobox", + "description": "Native Liquid combobox with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/input.json", + "https://ui.full.dev/r/liquid/popup.json", + "https://ui.full.dev/r/liquid/runtime.json" + ], + "files": [ + { + "path": "components/combobox.liquid", + "type": "registry:file", + "target": "~/snippets/ui-combobox.liquid" + } + ] + }, + { + "name": "toast", + "type": "registry:item", + "title": "Toast", + "description": "Native Liquid toast with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/triggers.json", + "https://ui.full.dev/r/liquid/button.json", + "https://ui.full.dev/r/liquid/runtime.json" + ], + "files": [ + { + "path": "components/toast.liquid", + "type": "registry:file", + "target": "~/snippets/ui-toast.liquid" + }, + { + "path": "styles/toast.css", + "type": "registry:file", + "target": "~/assets/ui-toast.css" + } + ] + }, + { + "name": "header", + "type": "registry:item", + "title": "Header", + "description": "Native Liquid header with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/section.json" + ], + "files": [ + { + "path": "components/header.liquid", + "type": "registry:file", + "target": "~/snippets/ui-header.liquid" + } + ] + }, + { + "name": "command", + "type": "registry:item", + "title": "Command", + "description": "Native Liquid command with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/runtime.json" + ], + "files": [ + { + "path": "components/command.liquid", + "type": "registry:file", + "target": "~/snippets/ui-command.liquid" + } + ] + }, + { + "name": "accordion", + "type": "registry:item", + "title": "Accordion", + "description": "Native Liquid accordion with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/runtime.json" + ], + "files": [ + { + "path": "components/accordion.liquid", + "type": "registry:file", + "target": "~/snippets/ui-accordion.liquid" + }, + { + "path": "styles/accordion.css", + "type": "registry:file", + "target": "~/assets/ui-accordion.css" + } + ] + }, + { + "name": "slider", + "type": "registry:item", + "title": "Slider", + "description": "Native Liquid slider with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/runtime.json" + ], + "files": [ + { + "path": "components/slider.liquid", + "type": "registry:file", + "target": "~/snippets/ui-slider.liquid" + } + ] + }, + { + "name": "input", + "type": "registry:item", + "title": "Input", + "description": "Native Liquid input with readable CSS and data-slot parts.", + "registryDependencies": ["https://ui.full.dev/r/liquid/init.json"], + "files": [ + { + "path": "components/input.liquid", + "type": "registry:file", + "target": "~/snippets/ui-input.liquid" + } + ] + }, + { + "name": "input-group", + "type": "registry:item", + "title": "Input Group", + "description": "Native Liquid input-group with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/input.json", + "https://ui.full.dev/r/liquid/icon.json" + ], + "files": [ + { + "path": "components/input-group.liquid", + "type": "registry:file", + "target": "~/snippets/ui-input-group.liquid" + } + ] + }, + { + "name": "triggers", + "type": "registry:item", + "title": "Triggers", + "description": "Native Liquid triggers with readable CSS and data-slot parts.", + "registryDependencies": ["https://ui.full.dev/r/liquid/init.json"], + "files": [ + { + "path": "components/triggers.liquid", + "type": "registry:file", + "target": "~/snippets/ui-triggers.liquid" + } + ] + }, + { + "name": "toggle-group", + "type": "registry:item", + "title": "Toggle Group", + "description": "Native Liquid toggle-group with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/toggle.json", + "https://ui.full.dev/r/liquid/triggers.json", + "https://ui.full.dev/r/liquid/runtime.json" + ], + "files": [ + { + "path": "components/toggle-group.liquid", + "type": "registry:file", + "target": "~/snippets/ui-toggle-group.liquid" + } + ] + }, + { + "name": "dialog", + "type": "registry:item", + "title": "Dialog", + "description": "Native Liquid dialog with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/runtime.json" + ], + "files": [ + { + "path": "components/dialog.liquid", + "type": "registry:file", + "target": "~/snippets/ui-dialog.liquid" + } + ] + }, + { + "name": "drawer", + "type": "registry:item", + "title": "Drawer", + "description": "Native Liquid drawer with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/triggers.json", + "https://ui.full.dev/r/liquid/button.json", + "https://ui.full.dev/r/liquid/runtime.json" + ], + "files": [ + { + "path": "components/drawer.liquid", + "type": "registry:file", + "target": "~/snippets/ui-drawer.liquid" + } + ] + }, + { + "name": "badge", + "type": "registry:item", + "title": "Badge", + "description": "Native Liquid badge with readable CSS and data-slot parts.", + "registryDependencies": ["https://ui.full.dev/r/liquid/init.json"], + "files": [ + { + "path": "components/badge.liquid", + "type": "registry:file", + "target": "~/snippets/ui-badge.liquid" + } + ] + }, + { + "name": "alert", + "type": "registry:item", + "title": "Alert", + "description": "Native Liquid alert with readable CSS and data-slot parts.", + "registryDependencies": ["https://ui.full.dev/r/liquid/init.json"], + "files": [ + { + "path": "components/alert.liquid", + "type": "registry:file", + "target": "~/snippets/ui-alert.liquid" + } + ] + }, + { + "name": "table", + "type": "registry:item", + "title": "Table", + "description": "Native Liquid table with readable CSS and data-slot parts.", + "registryDependencies": ["https://ui.full.dev/r/liquid/init.json"], + "files": [ + { + "path": "components/table.liquid", + "type": "registry:file", + "target": "~/snippets/ui-table.liquid" + } + ] + }, + { + "name": "breadcrumb", + "type": "registry:item", + "title": "Breadcrumb", + "description": "Native Liquid breadcrumb with readable CSS and data-slot parts.", + "registryDependencies": ["https://ui.full.dev/r/liquid/init.json"], + "files": [ + { + "path": "components/breadcrumb.liquid", + "type": "registry:file", + "target": "~/snippets/ui-breadcrumb.liquid" + } + ] + }, + { + "name": "aspect-ratio", + "type": "registry:item", + "title": "Aspect Ratio", + "description": "Native Liquid aspect-ratio with readable CSS and data-slot parts.", + "registryDependencies": ["https://ui.full.dev/r/liquid/init.json"], + "files": [ + { + "path": "components/aspect-ratio.liquid", + "type": "registry:file", + "target": "~/snippets/ui-aspect-ratio.liquid" + } + ] + }, + { + "name": "price", + "type": "registry:item", + "title": "Price", + "description": "Native Liquid price with readable CSS and data-slot parts.", + "registryDependencies": ["https://ui.full.dev/r/liquid/init.json"], + "files": [ + { + "path": "components/price.liquid", + "type": "registry:file", + "target": "~/snippets/ui-price.liquid" + }, + { + "path": "styles/price.css", + "type": "registry:file", + "target": "~/assets/ui-price.css" + } + ] + }, + { + "name": "radio-group", + "type": "registry:item", + "title": "Radio Group", + "description": "Native Liquid radio-group with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/invalid-controls.json", + "https://ui.full.dev/r/liquid/runtime.json" + ], + "files": [ + { + "path": "components/radio-group.liquid", + "type": "registry:file", + "target": "~/snippets/ui-radio-group.liquid" + } + ] + }, + { + "name": "checkbox", + "type": "registry:item", + "title": "Checkbox", + "description": "Native Liquid checkbox with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/input.json" + ], + "files": [ + { + "path": "components/checkbox.liquid", + "type": "registry:file", + "target": "~/snippets/ui-checkbox.liquid" + } + ] + }, + { + "name": "switch", + "type": "registry:item", + "title": "Switch", + "description": "Native Liquid switch with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/invalid-controls.json", + "https://ui.full.dev/r/liquid/runtime.json" + ], + "files": [ + { + "path": "components/switch.liquid", + "type": "registry:file", + "target": "~/snippets/ui-switch.liquid" + } + ] + }, + { + "name": "skeleton", + "type": "registry:item", + "title": "Skeleton", + "description": "Native Liquid skeleton with readable CSS and data-slot parts.", + "registryDependencies": ["https://ui.full.dev/r/liquid/init.json"], + "files": [ + { + "path": "components/skeleton.liquid", + "type": "registry:file", + "target": "~/snippets/ui-skeleton.liquid" + } + ] + }, + { + "name": "separator", + "type": "registry:item", + "title": "Separator", + "description": "Native Liquid separator with readable CSS and data-slot parts.", + "registryDependencies": ["https://ui.full.dev/r/liquid/init.json"], + "files": [ + { + "path": "components/separator.liquid", + "type": "registry:file", + "target": "~/snippets/ui-separator.liquid" + } + ] + }, + { + "name": "card", + "type": "registry:item", + "title": "Card", + "description": "Native Liquid card with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/aspect-ratio.json" + ], + "files": [ + { + "path": "components/card.liquid", + "type": "registry:file", + "target": "~/snippets/ui-card.liquid" + }, + { + "path": "styles/card.css", + "type": "registry:file", + "target": "~/assets/ui-card.css" + } + ] + }, + { + "name": "pagination", + "type": "registry:item", + "title": "Pagination", + "description": "Native Liquid pagination with readable CSS and data-slot parts.", + "registryDependencies": ["https://ui.full.dev/r/liquid/init.json"], + "files": [ + { + "path": "components/pagination.liquid", + "type": "registry:file", + "target": "~/snippets/ui-pagination.liquid" + } + ] + }, + { + "name": "popup", + "type": "registry:item", + "title": "Popup", + "description": "Native Liquid popup with readable CSS and data-slot parts.", + "registryDependencies": ["https://ui.full.dev/r/liquid/init.json"], + "files": [ + { + "path": "components/popup.liquid", + "type": "registry:file", + "target": "~/snippets/ui-popup.liquid" + } + ] + }, + { + "name": "sheet", + "type": "registry:item", + "title": "Sheet", + "description": "Native Liquid sheet with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/dialog.json", + "https://ui.full.dev/r/liquid/runtime.json" + ], + "files": [ + { + "path": "components/sheet.liquid", + "type": "registry:file", + "target": "~/snippets/ui-sheet.liquid" + } + ] + }, + { + "name": "carousel", + "type": "registry:item", + "title": "Carousel", + "description": "Native Liquid carousel with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/button.json", + "https://ui.full.dev/r/liquid/runtime.json" + ], + "files": [ + { + "path": "components/carousel.liquid", + "type": "registry:file", + "target": "~/snippets/ui-carousel.liquid" + }, + { + "path": "styles/carousel.css", + "type": "registry:file", + "target": "~/assets/ui-carousel.css" + } + ] + }, + { + "name": "attachment", + "type": "registry:item", + "title": "Attachment", + "description": "Native Liquid attachment with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/button.json", + "https://ui.full.dev/r/liquid/icon.json" + ], + "files": [ + { + "path": "components/attachment.liquid", + "type": "registry:file", + "target": "~/snippets/ui-attachment.liquid" + } + ] + }, + { + "name": "marquee", + "type": "registry:item", + "title": "Marquee", + "description": "Native Liquid marquee with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/runtime.json" + ], + "files": [ + { + "path": "components/marquee.liquid", + "type": "registry:file", + "target": "~/snippets/ui-marquee.liquid" + }, + { + "path": "styles/marquee.css", + "type": "registry:file", + "target": "~/assets/ui-marquee.css" + } + ] + }, + { + "name": "invalid-controls", + "type": "registry:item", + "title": "Invalid Controls", + "description": "Native Liquid invalid-controls with readable CSS and data-slot parts.", + "registryDependencies": ["https://ui.full.dev/r/liquid/init.json"], + "files": [ + { + "path": "components/invalid-controls.liquid", + "type": "registry:file", + "target": "~/snippets/ui-invalid-controls.liquid" + } + ] + }, + { + "name": "section", + "type": "registry:item", + "title": "Section", + "description": "Native Liquid section with readable CSS and data-slot parts.", + "registryDependencies": ["https://ui.full.dev/r/liquid/init.json"], + "files": [ + { + "path": "components/section.liquid", + "type": "registry:file", + "target": "~/snippets/ui-section.liquid" + }, + { + "path": "styles/section.css", + "type": "registry:file", + "target": "~/assets/ui-section.css" + } + ] + }, + { + "name": "button-group", + "type": "registry:item", + "title": "Button Group", + "description": "Native Liquid button-group with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/button.json" + ], + "files": [ + { + "path": "components/button-group.liquid", + "type": "registry:file", + "target": "~/snippets/ui-button-group.liquid" + } + ] + }, + { + "name": "collapsible", + "type": "registry:item", + "title": "Collapsible", + "description": "Native Liquid collapsible with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/runtime.json" + ], + "files": [ + { + "path": "components/collapsible.liquid", + "type": "registry:file", + "target": "~/snippets/ui-collapsible.liquid" + } + ] + }, + { + "name": "dropdown-menu", + "type": "registry:item", + "title": "Dropdown Menu", + "description": "Native Liquid dropdown-menu with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/popup.json", + "https://ui.full.dev/r/liquid/triggers.json", + "https://ui.full.dev/r/liquid/runtime.json" + ], + "files": [ + { + "path": "components/dropdown-menu.liquid", + "type": "registry:file", + "target": "~/snippets/ui-dropdown-menu.liquid" + } + ] + }, + { + "name": "popover", + "type": "registry:item", + "title": "Popover", + "description": "Native Liquid popover with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/popup.json", + "https://ui.full.dev/r/liquid/triggers.json", + "https://ui.full.dev/r/liquid/runtime.json" + ], + "files": [ + { + "path": "components/popover.liquid", + "type": "registry:file", + "target": "~/snippets/ui-popover.liquid" + } + ] + }, + { + "name": "hover-card", + "type": "registry:item", + "title": "Hover Card", + "description": "Native Liquid hover-card with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/popup.json", + "https://ui.full.dev/r/liquid/runtime.json" + ], + "files": [ + { + "path": "components/hover-card.liquid", + "type": "registry:file", + "target": "~/snippets/ui-hover-card.liquid" + } + ] + }, + { + "name": "textarea", + "type": "registry:item", + "title": "Textarea", + "description": "Native Liquid textarea with readable CSS and data-slot parts.", + "registryDependencies": ["https://ui.full.dev/r/liquid/input.json"], + "files": [] + }, + { + "name": "native-select", + "type": "registry:item", + "title": "Native Select", + "description": "Native Liquid native-select with readable CSS and data-slot parts.", + "registryDependencies": ["https://ui.full.dev/r/liquid/input.json"], + "files": [] + }, + { + "name": "label", + "type": "registry:item", + "title": "Label", + "description": "Native Liquid label with readable CSS and data-slot parts.", + "registryDependencies": ["https://ui.full.dev/r/liquid/field.json"], + "files": [] + }, + { + "name": "components", + "type": "registry:item", + "title": "Components", + "description": "All native Liquid styles, controllers and a single layout integration snippet.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/runtime.json", + "https://ui.full.dev/r/liquid/banner.json", + "https://ui.full.dev/r/liquid/tabs.json", + "https://ui.full.dev/r/liquid/form.json", + "https://ui.full.dev/r/liquid/select.json", + "https://ui.full.dev/r/liquid/field.json", + "https://ui.full.dev/r/liquid/item.json", + "https://ui.full.dev/r/liquid/spinner.json", + "https://ui.full.dev/r/liquid/video.json", + "https://ui.full.dev/r/liquid/button.json", + "https://ui.full.dev/r/liquid/toggle.json", + "https://ui.full.dev/r/liquid/logo.json", + "https://ui.full.dev/r/liquid/kbd.json", + "https://ui.full.dev/r/liquid/rating.json", + "https://ui.full.dev/r/liquid/tooltip.json", + "https://ui.full.dev/r/liquid/avatar.json", + "https://ui.full.dev/r/liquid/empty.json", + "https://ui.full.dev/r/liquid/alert-dialog.json", + "https://ui.full.dev/r/liquid/navigation-menu.json", + "https://ui.full.dev/r/liquid/typography.json", + "https://ui.full.dev/r/liquid/toc.json", + "https://ui.full.dev/r/liquid/icon.json", + "https://ui.full.dev/r/liquid/combobox.json", + "https://ui.full.dev/r/liquid/toast.json", + "https://ui.full.dev/r/liquid/header.json", + "https://ui.full.dev/r/liquid/command.json", + "https://ui.full.dev/r/liquid/accordion.json", + "https://ui.full.dev/r/liquid/slider.json", + "https://ui.full.dev/r/liquid/input.json", + "https://ui.full.dev/r/liquid/input-group.json", + "https://ui.full.dev/r/liquid/triggers.json", + "https://ui.full.dev/r/liquid/toggle-group.json", + "https://ui.full.dev/r/liquid/dialog.json", + "https://ui.full.dev/r/liquid/drawer.json", + "https://ui.full.dev/r/liquid/badge.json", + "https://ui.full.dev/r/liquid/alert.json", + "https://ui.full.dev/r/liquid/table.json", + "https://ui.full.dev/r/liquid/breadcrumb.json", + "https://ui.full.dev/r/liquid/aspect-ratio.json", + "https://ui.full.dev/r/liquid/price.json", + "https://ui.full.dev/r/liquid/radio-group.json", + "https://ui.full.dev/r/liquid/checkbox.json", + "https://ui.full.dev/r/liquid/switch.json", + "https://ui.full.dev/r/liquid/skeleton.json", + "https://ui.full.dev/r/liquid/separator.json", + "https://ui.full.dev/r/liquid/card.json", + "https://ui.full.dev/r/liquid/pagination.json", + "https://ui.full.dev/r/liquid/popup.json", + "https://ui.full.dev/r/liquid/sheet.json", + "https://ui.full.dev/r/liquid/carousel.json", + "https://ui.full.dev/r/liquid/attachment.json", + "https://ui.full.dev/r/liquid/marquee.json", + "https://ui.full.dev/r/liquid/invalid-controls.json", + "https://ui.full.dev/r/liquid/section.json", + "https://ui.full.dev/r/liquid/button-group.json", + "https://ui.full.dev/r/liquid/collapsible.json", + "https://ui.full.dev/r/liquid/dropdown-menu.json", + "https://ui.full.dev/r/liquid/popover.json", + "https://ui.full.dev/r/liquid/hover-card.json" + ], + "files": [ + { + "path": "snippets/ui.liquid", + "type": "registry:file", + "target": "~/snippets/ui.liquid" + }, + { + "path": "manifest.json", + "type": "registry:file", + "target": "~/assets/ui-manifest.json" + } + ] + } + ] +} diff --git a/public/r/liquid/runtime.json b/public/r/liquid/runtime.json new file mode 100644 index 000000000..26cb480c3 --- /dev/null +++ b/public/r/liquid/runtime.json @@ -0,0 +1,180 @@ +{ + "$schema": "https://ui.shadcn.com/schema/registry-item.json", + "name": "runtime", + "title": "Runtime", + "description": "Native ES modules, lazy data-slot controllers and Shopify editor lifecycle. Render once before other modules, or extend your own import map and use the exported lifecycle.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json" + ], + "files": [ + { + "path": "runtime/ui-carousel.js", + "content": "import { createCarousel } from '@data-slot/carousel';\n\n// @data-slot/carousel owns scrolling, snapping, dragging, the arrow keys and the previous and next\n// buttons, and keeps every visible card interactive. It writes English ARIA descriptions and slide\n// labels, so the translated ones from Liquid are put back after it binds.\nexport function initializeCarousel(root) {\n const content = root.querySelector('[data-slot=\"carousel-content\"]');\n const items = [...content.children].filter((item) => item.matches('[data-slot=\"carousel-item\"]'));\n const descriptions = [root, ...items].map((element) => [element, element.getAttribute('aria-roledescription')]);\n const carousel = createCarousel(root, { slides: 'multiple', drag: true, loop: false });\n for (const [element, description] of descriptions)\n if (description) element.setAttribute('aria-roledescription', description);\n // Markup without a translated label template keeps the controller's labels.\n const template = root.dataset.slideLabel;\n if (template)\n items.forEach((item, index) => {\n item.setAttribute('aria-label', template.replace('[index]', index + 1).replace('[total]', items.length));\n });\n for (const button of root.querySelectorAll('[data-slot=\"carousel-previous\"], [data-slot=\"carousel-next\"]'))\n button.hidden = false;\n return carousel;\n}\n", + "type": "registry:file", + "target": "~/assets/ui-carousel.js" + }, + { + "path": "runtime/ui-controls.js", + "content": "// The data-slot controllers that few pages use, imported when theme.js finds their markup. Each\n// entry starts one root and returns its controller with destroy(). Behavior is the vendored\n// controller's; the adapters here only connect it to native forms and theme state.\n\n// A composite input renders hidden after its native fallback, such as a select or radio inputs,\n// which the form submits until the controller runs. Then the fallback leaves the document, so\n// labels and the form see only the field the controller generates, and the fallback's id moves to\n// the labelled element, so a label[for] still reaches the control. start() creates the controller\n// with the fallback's reset default and the value a shopper chose while it loaded; its destroy()\n// writes the current value back, and the fallback returns with its own reset default.\nfunction replaceFallback(root, labelled, start) {\n const fallback = root.previousElementSibling?.matches('[data-fallback]') ? root.previousElementSibling : null;\n const attributes = ['id', 'aria-labelledby'].map((name) => [name, labelled.getAttribute(name)]);\n const restore = () => {\n root.hidden = true;\n for (const [name, value] of attributes) {\n if (value === null) labelled.removeAttribute(name);\n else labelled.setAttribute(name, value);\n }\n if (fallback) root.before(fallback);\n };\n fallback?.remove();\n if (fallback?.id) labelled.id = fallback.id;\n root.hidden = false;\n let controller;\n try {\n controller = start(fallback);\n } catch (error) {\n restore();\n throw error;\n }\n return {\n destroy() {\n controller.destroy();\n restore();\n },\n };\n}\n\nfunction initializeSelect({ createSelect }, root) {\n return replaceFallback(root, root.querySelector('[data-slot=\"select-trigger\"]'), (select) => {\n const options = [...(select?.options ?? [])];\n const initial = options.find((option) => option.defaultSelected) ?? options.find((option) => !option.disabled);\n const controller = createSelect(root, { mountStrategy: 'eager', defaultValue: initial?.value || undefined });\n const value = select?.value || null;\n if (select && value !== controller.value) root.dispatchEvent(new CustomEvent('select:set', { detail: { value } }));\n return {\n destroy() {\n if (select) select.value = controller.value ?? '';\n controller.destroy();\n },\n };\n });\n}\n\nfunction initializeRadioGroup({ createRadioGroup }, root) {\n return replaceFallback(root, root, (fallback) => {\n const inputs = [...(fallback?.querySelectorAll('input[type=\"radio\"]') ?? [])];\n const initial = inputs.find((input) => input.defaultChecked)?.value ?? '';\n const controller = createRadioGroup(root, fallback ? { defaultValue: initial } : {});\n const value = inputs.find((input) => input.checked)?.value ?? null;\n if (fallback && value !== controller.value) {\n if (value === null) controller.clear();\n else controller.select(value);\n }\n return {\n destroy() {\n for (const input of inputs) input.checked = input.value === controller.value;\n controller.destroy();\n },\n };\n });\n}\n\nfunction initializeSwitch({ createSwitch }, root) {\n return replaceFallback(root, root, (checkbox) => {\n const controller = createSwitch(root, checkbox ? { defaultChecked: checkbox.defaultChecked } : {});\n if (checkbox) controller.setChecked(checkbox.checked);\n return {\n destroy() {\n if (checkbox) checkbox.checked = controller.checked;\n controller.destroy();\n },\n };\n });\n}\n\n// The slider has no form field of its own, so its native range inputs stay in the form, hidden,\n// and follow its value. A form reset moves the slider back to the inputs' reset values.\nfunction initializeSlider({ createSlider }, root) {\n const fallback = root.previousElementSibling?.matches('[data-fallback]') ? root.previousElementSibling : null;\n const inputs = fallback ? [...fallback.querySelectorAll('input[type=\"range\"]')] : [];\n const read = () => (inputs.length > 1 ? inputs.map((input) => Number(input.value)) : Number(inputs[0]?.value));\n const write = (value) => {\n [value].flat().forEach((number, index) => {\n if (!inputs[index] || inputs[index].value === String(number)) return;\n inputs[index].value = String(number);\n inputs[index].dispatchEvent(new Event('input', { bubbles: true }));\n });\n };\n const slider = createSlider(root, {\n ...(inputs.length ? { defaultValue: read() } : {}),\n onValueChange: write,\n onValueCommit: () => inputs.forEach((input) => input.dispatchEvent(new Event('change', { bubbles: true }))),\n });\n const events = new AbortController();\n inputs[0]?.form?.addEventListener('reset', () => setTimeout(() => slider.setValue(read())), {\n signal: events.signal,\n });\n if (fallback) fallback.hidden = true;\n root.hidden = false;\n return {\n destroy() {\n events.abort();\n slider.destroy();\n root.hidden = true;\n if (fallback) fallback.hidden = false;\n },\n };\n}\n\n// The action of an alert dialog is a native link or submit or reset button, and the controller\n// leaves closing to the caller. A link closes the dialog on click; a submit or reset button once its\n// form submits or resets, so an invalid form keeps its native validation and the dialog stays open.\nfunction initializeAlertDialog({ createAlertDialog }, root) {\n const dialog = createAlertDialog(root);\n const events = new AbortController();\n for (const action of root.querySelectorAll('[data-slot=\"alert-dialog-action\"]')) {\n action.addEventListener(\n 'click',\n () => {\n const form = ['submit', 'reset'].includes(action.type) ? action.form : null;\n if (form) form.addEventListener(action.type, () => dialog.close(), { once: true, signal: events.signal });\n else dialog.close();\n },\n { signal: events.signal },\n );\n }\n return {\n destroy() {\n events.abort();\n dialog.destroy();\n },\n };\n}\n\n// Buttons inside a toaster show a toast with their translated title and description. Like other\n// triggers that only open a popup, they show once the component has started (data-enhanced).\nfunction initializeToaster({ createToast }, root) {\n const toaster = createToast(root);\n const events = new AbortController();\n for (const button of root.querySelectorAll('[data-toast-title]')) {\n button.addEventListener(\n 'click',\n () => toaster.show({ title: button.dataset.toastTitle, description: button.dataset.toastDescription }),\n { signal: events.signal },\n );\n }\n return {\n destroy() {\n events.abort();\n toaster.destroy();\n },\n };\n}\n\n// Each entry loads its data-slot package, then starts one root with it and returns a controller\n// with destroy(). theme.js starts the root only if it is still current once the package has\n// loaded. Select, tooltip and hover card keep their closed content connected (eager), so nested\n// components and the lifecycle in theme.js never see it leave the document.\nexport const controls = {\n select: [() => import('@data-slot/select'), initializeSelect],\n 'radio-group': [() => import('@data-slot/radio-group'), initializeRadioGroup],\n switch: [() => import('@data-slot/switch'), initializeSwitch],\n slider: [() => import('@data-slot/slider'), initializeSlider],\n toggle: [() => import('@data-slot/toggle'), ({ createToggle }, root) => createToggle(root)],\n 'toggle-group': [() => import('@data-slot/toggle-group'), ({ createToggleGroup }, root) => createToggleGroup(root)],\n collapsible: [() => import('@data-slot/collapsible'), ({ createCollapsible }, root) => createCollapsible(root)],\n popover: [() => import('@data-slot/popover'), ({ createPopover }, root) => createPopover(root)],\n 'hover-card': [\n () => import('@data-slot/hover-card'),\n ({ createHoverCard }, root) => createHoverCard(root, { mountStrategy: 'eager' }),\n ],\n tooltip: [\n () => import('@data-slot/tooltip'),\n ({ createTooltip }, root) => createTooltip(root, { mountStrategy: 'eager' }),\n ],\n 'dropdown-menu': [\n () => import('@data-slot/dropdown-menu'),\n ({ createDropdownMenu }, root) => createDropdownMenu(root),\n ],\n 'alert-dialog': [() => import('@data-slot/alert-dialog'), initializeAlertDialog],\n drawer: [() => import('@data-slot/drawer'), ({ createDrawer }, root) => createDrawer(root)],\n toast: [() => import('@data-slot/toast'), initializeToaster],\n};\n", + "type": "registry:file", + "target": "~/assets/ui-controls.js" + }, + { + "path": "runtime/ui-lifecycle.js", + "content": "// One owner per root, including asynchronous imports and Shopify editor replacements.\nexport function createLifecycle(factories, scope = document) {\n const components = new Map();\n const events = new AbortController();\n\n function initialize(parent) {\n for (const [selector, factory] of factories) {\n const roots = [...parent.querySelectorAll(selector)];\n if (parent.matches?.(selector)) roots.unshift(parent);\n for (const root of roots) {\n if (components.has(root) || !root.isConnected) continue;\n try {\n const controller = factory(root);\n components.set(root, controller);\n if (!controller.ready) root.dataset.enhanced = '';\n controller.ready?.then(\n () => {\n if (components.get(root) === controller) root.dataset.enhanced = '';\n },\n (error) => {\n console.error(error);\n if (components.get(root) === controller) components.delete(root);\n },\n );\n } catch (error) {\n console.error(error);\n }\n }\n }\n }\n\n function destroy(predicate) {\n for (const [root, controller] of components) {\n if (!predicate(root)) continue;\n // Drop ownership before a fallback is restored and the observer sees it.\n components.delete(root);\n controller.destroy?.();\n delete root.dataset.enhanced;\n }\n }\n\n function reveal(root, method, value) {\n const owner = components.get(root);\n const apply = (controller) => {\n if (owner && components.get(root) === owner) controller?.[method]?.(value);\n };\n // A selected block can arrive while its controller is still loading.\n if (owner?.ready)\n owner.ready.then(\n (controller) => apply(controller ?? owner),\n () => {},\n );\n else apply(owner);\n }\n\n initialize(scope);\n const observer = new MutationObserver((mutations) => {\n for (const mutation of mutations) {\n for (const node of mutation.addedNodes) {\n if (node.nodeType === Node.ELEMENT_NODE && node.isConnected) initialize(node);\n }\n }\n if (mutations.some((mutation) => mutation.removedNodes.length)) destroy((root) => !root.isConnected);\n });\n observer.observe(scope === document ? document.body : scope, { childList: true, subtree: true });\n document.addEventListener('shopify:section:load', (event) => initialize(event.target), { signal: events.signal });\n document.addEventListener('shopify:section:unload', (event) => destroy((root) => event.target.contains(root)), {\n signal: events.signal,\n });\n document.addEventListener(\n 'shopify:block:select',\n (event) => {\n const item = event.target.closest('[data-slot=\"accordion-item\"]');\n if (item) reveal(item.closest('[data-slot=\"accordion\"]'), 'expand', item.dataset.value);\n const slide = event.target.closest('[data-slot=\"carousel-item\"]');\n if (slide)\n reveal(slide.closest('[data-slot=\"carousel\"]'), 'goTo', [...slide.parentElement.children].indexOf(slide));\n },\n { signal: events.signal },\n );\n return {\n components,\n initialize,\n destroy() {\n observer.disconnect();\n events.abort();\n destroy(() => true);\n },\n };\n}\n\nexport function deferred(load, start) {\n return (root) => {\n let controller;\n let destroyed = false;\n const ready = load().then((module) => {\n if (!destroyed && root.isConnected) return (controller = start(module, root));\n });\n return {\n ready,\n destroy() {\n destroyed = true;\n controller?.destroy();\n },\n };\n };\n}\n", + "type": "registry:file", + "target": "~/assets/ui-lifecycle.js" + }, + { + "path": "runtime/ui-start.js", + "content": "import { controls } from '@fulldev/controls';\nimport { createLifecycle, deferred } from '@fulldev/lifecycle';\nimport { initializeAutoplayVideo, initializeMotionPause } from '@fulldev/motion';\n\nconst simple = [\n ['accordion', () => import('@data-slot/accordion'), 'createAccordion'],\n ['tabs', () => import('@data-slot/tabs'), 'createTabs'],\n ['dialog', () => import('@data-slot/dialog'), 'createDialog'],\n ['combobox', () => import('@data-slot/combobox'), 'createCombobox'],\n ['command', () => import('@data-slot/command'), 'createCommand'],\n ['navigation-menu', () => import('@data-slot/navigation-menu'), 'createNavigationMenu'],\n];\n\nexport const lifecycle = createLifecycle([\n ['[data-motion-pause]', initializeMotionPause],\n ['video[autoplay]', initializeAutoplayVideo],\n ...simple.map(([name, load, factory]) => [\n `[data-slot=\"${name}\"]`,\n deferred(load, (module, root) =>\n module[factory](root, {\n mountStrategy: 'eager',\n ...(name === 'accordion' ? { multiple: root.dataset.multiple !== 'false' } : {}),\n }),\n ),\n ]),\n [\n '[data-slot=\"carousel\"]',\n deferred(\n () => import('@fulldev/carousel'),\n (module, root) => module.initializeCarousel(root),\n ),\n ],\n ...Object.entries(controls).map(([name, [load, start]]) => [\n name === 'toggle' ? 'button[data-slot=\"toggle\"]' : `[data-slot=\"${name}\"]`,\n deferred(load, start),\n ]),\n]);\n", + "type": "registry:file", + "target": "~/assets/ui-start.js" + }, + { + "path": "vendor/data-slot-core.js", + "content": "const e=Symbol.for(`data-slot.retained-content`),t=`data-retained-content`,n=[];function r(t){return t[e]??n}function i(n){let{root:r,target:i}=n,a=r,o=(n.strategy??`lazy`)===`lazy`,s=o?r.ownerDocument.createComment(`data-slot content`):null,c=!1,l=!1;s&&i.before(s);let u=()=>{let n=a[e];n?.delete(i),!n?.size&&(delete a[e],a.removeAttribute(t))};return{mount:()=>{c||!l||(s?.parentNode?.insertBefore(i,s.nextSibling),u(),l=!1)},unmount(){c||!o||l||(i.remove(),l=!0,(a[e]??=new Set).add(i),a.setAttribute(t,``))},cleanup(){c||=(s?.parentNode&&s.parentNode.insertBefore(i,s.nextSibling),s?.remove(),u(),!0)}}}const a=(e,t,n)=>{let r=e.getAttribute(`data-slot`);return[...t.querySelectorAll(n)].filter(n=>{let i=r&&n.parentElement?.closest(`[data-slot=\"${r}\"]`);return!i||i===e||!t.contains(i)})},o=(e,t)=>s(e,t)[0]??null,s=(e,t)=>a(e,e,`[data-slot=\"${t}\"]`),c=(e,n)=>{let i=new Set,a=new Set,o=e=>{if(a.has(e))return;a.add(e);for(let t of e.querySelectorAll(`[data-slot=\"${n}\"]`))i.add(t);let s=e.querySelectorAll(`[${t}]`);for(let t of e instanceof Element?[e,...s]:s)for(let e of r(t))t.contains(e)||(e.getAttribute(`data-slot`)===n&&i.add(e),o(e))};return o(e),[...i]},l=Symbol.for(`data-slot.root-bindings`),u=Symbol.for(`data-slot.root-binding-warnings`),d=(e,t=!1)=>{let n=e,r=n[l];return!r&&t&&(r=new Map,n[l]=r),r},f=(e,t=!1)=>{let n=e,r=n[u];return!r&&t&&(r=new Set,n[u]=r),r};function p(e,t){return d(e)?.get(t)}function m(e,t){return d(e)?.has(t)??!1}function h(e,t,n){return d(e,!0).set(t,n),n}function g(e,t,n){let r=d(e);return!r?.has(t)||arguments.length>=3&&r.get(t)!==n?!1:(r.delete(t),r.size===0&&delete e[l],!0)}function _(e,t,n){if(typeof process<`u`&&process.env?.NODE_ENV===`production`)return;let r=f(e,!0);r.has(t)||(r.add(t),console.warn(n))}function v(e,t,n){let r=p(e,t);return r!==void 0&&_(e,t,n),r}const y=new WeakMap;function b(e,t,n){if(typeof process<`u`&&process.env?.NODE_ENV===`production`)return;let r=y.get(e);r||(r=new Set,y.set(e,r)),!r.has(t)&&(r.add(t),console.warn(`[@data-slot] ${n}`))}function x(e){let t=`data-${e.replace(/([A-Z])/g,`-$1`).toLowerCase()}`,n=`data-${e}`;return t===n?[t]:[t,n]}function S(e,t){for(let n of x(t))if(e.hasAttribute(n))return e.getAttribute(n);return null}function C(e,t){return x(t).some(t=>e.hasAttribute(t))}const ee=new Set([``,`true`,`1`,`yes`]),te=new Set([`false`,`0`,`no`]);function ne(e,t){if(!C(e,t))return;let n=S(e,t);if(n===null)return;let r=n.toLowerCase();if(ee.has(r))return!0;if(te.has(r))return!1;b(e,t,`Invalid boolean value \"${n}\" for data-${t}. Expected: true/false/1/0/yes/no or empty.`)}function re(e,t){let n=S(e,t);if(n===null||n===``)return;let r=Number(n);if(Number.isNaN(r)||!Number.isFinite(r)){b(e,t,`Invalid number value \"${n}\" for data-${t}.`);return}return r}function ie(e,t){if(C(e,t))return S(e,t)??void 0}function ae(e,t,n){let r=S(e,t);if(r!==null){if(n.includes(r))return r;b(e,t,`Invalid value \"${r}\" for data-${t}. Expected one of: ${n.join(`, `)}.`)}}const w=new WeakMap,T=Symbol.for(`data-slot.portal-owner`),oe=e=>e[T]??w.get(e),se=(e,t)=>{w.set(e,t),e[T]=t},ce=e=>{w.delete(e),delete e[T]};function E(e,t){return D(e,t,new Set)}function D(e,t,n){if(!t)return!1;let r=t instanceof Element?t:t.parentElement;if(!r)return!1;if(e.contains(r))return!0;let i=r;for(;i;){let t=oe(i);if(t&&!n.has(t)&&(n.add(t),D(e,t,n)))return!0;i=i.parentElement}return!1}function O(e,t,n){if(n.portaled)return;let r=(t.ownerDocument??document)?.body;r&&(n.originalParent=e.parentNode,n.originalNextSibling=e.nextSibling,se(e,t),r.appendChild(e),n.portaled=!0)}function k(e,t){if(!t.portaled)return;ce(e);let n=t.originalParent,r=t.originalNextSibling;n&&n.isConnected?r&&r.parentNode===n?n.insertBefore(e,r):n.appendChild(e):e.remove(),t.portaled=!1,t.originalParent=null,t.originalNextSibling=null}let le=0;const A=(e,t)=>e.id||=`${t}-${++le}`,ue=(e,t,n)=>{n===null?e.removeAttribute(`aria-${t}`):e.setAttribute(`aria-${t}`,String(n))},de=(e,t,n)=>{t&&e.setAttribute(`aria-labelledby`,A(t,`title`)),n&&e.setAttribute(`aria-describedby`,A(n,`desc`))};function j(e,t,n,r){return e.addEventListener(t,n,r),()=>e.removeEventListener(t,n,r)}const fe=(e,t,n)=>j(e,t,t=>{t.target===e&&n(t)}),pe=(e,t,n)=>e.dispatchEvent(new CustomEvent(t,{bubbles:!0,detail:n})),me=(...e)=>t=>{for(let n of e){if(t.defaultPrevented)break;n?.(t)}};function he(e,t,n,r){let i=e.ownerDocument.createElement(`input`);i.type=`hidden`,i.name=`data-slot-form-field-${crypto.getRandomValues(new Uint32Array(4)).join(`-`)}`;let a=n.getAttribute(`form`);a!==null&&i.setAttribute(`form`,a),n.after(i);let o=new Map,s=new WeakSet,c=e=>{if(e.target!==n.form||s.has(e))return;s.add(e),l();let a=e.formData,o=Array.from(a.entries()),c=o.findIndex(([e])=>e===i.name);if(c<0)return;let u=!n.disabled,d=r||t.matches(`:disabled`);n.disabled=d;let f=c-(u?1:0);o.splice(f,u?2:1,...d?[]:[[n.name,n.value]]);for(let e of new Set(a.keys()))a.delete(e);for(let[e,t]of o)a.append(e,t)},l=()=>{for(let t of[e.ownerDocument,e.getRootNode(),n.form])!t||o.has(t)||o.set(t,j(t,`formdata`,c,{capture:!0}))};return l(),{observe:l,destroy(){for(let e of o.values())e();o.clear(),i.remove()}}}function ge(e,t){e.required=!0,e.tabIndex=-1,e.setAttribute(`aria-hidden`,`true`),Object.assign(e.style,{position:`absolute`,width:`1px`,height:`1px`,padding:`0`,margin:`-1px`,overflow:`hidden`,clip:`rect(0, 0, 0, 0)`,whiteSpace:`nowrap`,border:`0`});let n=e.ownerDocument.defaultView,r,i=()=>{r!==void 0&&n?.cancelAnimationFrame(r),r=void 0},a=()=>{i(),e.willValidate&&!e.validity.valid?t?.setAttribute(`aria-invalid`,`true`):t?.removeAttribute(`aria-invalid`)},o=()=>!e.willValidate||e.validity.valid?!1:e.form?Array.from(e.form.elements).find(e=>{let t=e;return t.willValidate&&!t.validity.valid})===e:!0,s=j(e,`invalid`,i=>{a();let s=i.defaultPrevented;if(i.preventDefault(),s||!t||!o())return;t.focus();let c=e.form;r=n?.requestAnimationFrame(()=>{r=void 0,e.form===c&&o()&&t.focus()})});return a(),{sync:a,destroy(){i(),s(),t?.removeAttribute(`aria-invalid`)}}}function M({root:e,getForm:t,onReset:n,onResetDispatched:r}){let i=new Map,a=new Map,o=e=>{if(e.target!==t()||a.has(e))return;s(),r?.(e);let i=setTimeout(()=>{a.delete(e),!(e.defaultPrevented||e.target!==t())&&n(e)},0);a.set(e,i)},s=()=>{for(let n of[e.ownerDocument,e.getRootNode(),t()])!n||i.has(n)||i.set(n,j(n,`reset`,o,{capture:!0}))};return s(),{observe:s,destroy(){for(let e of a.values())clearTimeout(e);a.clear();for(let e of i.values())e();i.clear()}}}function _e({root:e,name:t,defaultValue:n,control:r,disabled:i=!1,required:a=!1,validationTarget:o,onReset:s}){let c=r?.getAttribute(`name`)??null,l=r?.getAttribute(`form`)??null,u=null,d=null,f=null,p=e=>{f&&(f.value=e??``),u&&(u.value=e??``)};if(t){if(r?.removeAttribute(`name`),a)d=e.ownerDocument.createElement(`select`),f=e.ownerDocument.createElement(`option`),f.defaultSelected=!0,d.appendChild(f),u=d;else{let t=e.ownerDocument.createElement(`input`);t.type=`hidden`,t.defaultValue=n??``,u=t}u.name=t,p(n),u.disabled=i||(r?.disabled??!1),l!==null&&u.setAttribute(`form`,l),u.setAttribute(`data-form-field-generated`,``),e.appendChild(u)}let m=d?ge(d,o):null,h=r&&u?he(e,r,u,i):null,g=()=>u?u.form:r?.hasAttribute(`form`)||r?.form?r.form:e.closest(`form`),_=n,v=0,y=new WeakMap,b=M({root:e,getForm:g,onResetDispatched:e=>{y.set(e,v)},onReset:e=>{if(y.get(e)!==v){p(_),m?.sync();return}p(n),m?.sync(),_=n,s(n)}});return{setValue(e){e!==_&&(_=e,v++),p(e),m?.sync(),h?.observe(),b.observe()},destroy(){b.destroy(),m?.destroy(),h?.destroy(),u?.remove(),r&&t&&(c===null?r.removeAttribute(`name`):r.setAttribute(`name`,c))}}}let N=0,P=``,F=``;function ve(){if(N===0){let e=document.documentElement;P=e.style.overflow,F=e.style.scrollbarGutter,e.style.overflow=`hidden`,e.style.scrollbarGutter=`stable`}N++}function ye(){if(N=Math.max(0,N-1),N===0){let e=document.documentElement;e.style.overflow=P,e.style.scrollbarGutter=F}}const I=[`a[href]`,`area[href]`,`button`,`input:not([type=\"hidden\"])`,`select`,`textarea`,`iframe`,`audio[controls]`,`video[controls]`].join(`, `),be=`${I}, summary, [contenteditable], [tabindex]`;function xe(e){for(let t=e;t;t=t.parentElement)if(t.hasAttribute(`inert`)||t.inert)return!0;return!1}function Se(e){let t=e.ownerDocument.defaultView?.getComputedStyle(e).visibility;if(t===`hidden`||t===`collapse`)return!1;for(let t=e;t;t=t.parentElement)if(t.hidden||t.ownerDocument.defaultView?.getComputedStyle(t)?.display===`none`)return!1;return!0}function Ce(e){for(let t=e.parentElement;t;t=t.parentElement)if(!(t.tagName!==`DETAILS`||t.hasAttribute(`open`))&&!Array.from(t.children).find(e=>e.tagName===`SUMMARY`)?.contains(e))return!0;return!1}function we(e){if(!e.matches(`button, input, select, textarea, option, optgroup`))return!1;let t=e.closest(`fieldset[disabled]`);return t?!Array.from(t.children).find(e=>e.tagName===`LEGEND`)?.contains(e):!1}function L(e){return!Te(e)||e.matches(`:disabled`)||we(e)?!1:Se(e)&&!xe(e)&&!Ce(e)}function R(e){for(let t=e;t;t=t.parentElement){let e=t.getAttribute(`contenteditable`)?.toLowerCase();if(e===`false`)return!1;if(e===``||e===`true`||e===`plaintext-only`)return!0}return!1}function Te(e){if(e.matches(`input[type=\"hidden\"]`))return!1;let t=e.getAttribute(`tabindex`);return t!==null&&!Number.isNaN(Number.parseInt(t,10))||e.matches(I)||e.tagName===`SUMMARY`&&e.parentElement?.tagName===`DETAILS`&&Array.from(e.parentElement.children).find(e=>e.tagName===`SUMMARY`)===e?!0:R(e)&&!R(e.parentElement)}function z(e){let t=e.getRootNode();if(t!==e)return t;let n=e;for(;n.parentNode;)n=n.parentNode;return n}function Ee(e){let t=new Map;for(let n of e.querySelectorAll(`input[type=\"radio\"]:checked`)){if(!n.name||z(n)!==e||!L(n))continue;let r=t.get(n.form);r||(r=new Map,t.set(n.form,r)),r.has(n.name)||r.set(n.name,n)}return t}function B(e){return Array.from(e.querySelectorAll(be)).filter(L)}function De(e){let t=B(e);return t.find(e=>e.hasAttribute(`autofocus`))??t[0]}function Oe(e){let t=new Map;return B(e).filter(e=>{let n=e.getAttribute(`tabindex`);if(n!==null&&Number.parseInt(n,10)<0)return!1;let r=e.ownerDocument.defaultView?.HTMLInputElement;if(!r||!(e instanceof r)||e.type!==`radio`||!e.name)return!0;let i=z(e),a=t.get(i);a||(a=Ee(i),t.set(i,a));let o=a.get(e.form)?.get(e.name);return!o||o===e})}const V=(e,t)=>e===`inline-start`?t===`rtl`?`right`:`left`:e===`inline-end`?t===`rtl`?`left`:`right`:e,ke=e=>{switch(e){case`top`:return[`top`,`right`,`bottom`,`left`];case`bottom`:return[`bottom`,`top`,`right`,`left`];case`left`:return[`left`,`top`,`right`,`bottom`];case`right`:return[`right`,`top`,`bottom`,`left`];case`inline-start`:return[`inline-start`,`inline-end`,`top`,`bottom`];case`inline-end`:return[`inline-end`,`inline-start`,`top`,`bottom`]}},Ae=e=>{let t=window.visualViewport,n=e.viewportWidth??t?.width,r=e.viewportHeight??t?.height;if(n==null||r==null)throw Error(`computeFloatingPosition requires window.visualViewport when viewport dimensions are not provided`);return{x:t?.offsetLeft??0,y:t?.offsetTop??0,width:n,height:r}},H=(e,t,n,r,i,a)=>{let o=0,s=0;return e===`top`?s=n.top-r.height-i:e===`bottom`?s=n.bottom+i:o=e===`left`?n.left-r.width-i:n.right+i,e===`top`||e===`bottom`?o=t===`start`?n.left+a:t===`center`?n.left+n.width/2-r.width/2+a:n.right-r.width-a:s=t===`start`?n.top+a:t===`center`?n.top+n.height/2-r.height/2+a:n.bottom-r.height-a,{x:o,y:s}},je=(e,t)=>t===`start`?{x:e.left,y:e.top}:t===`end`?{x:e.right,y:e.bottom}:{x:e.left+e.width/2,y:e.top+e.height/2},U=(e,t,n,r=`ltr`)=>{let i=V(e,r),a=je(n,t);return i===`top`?{x:a.x,y:n.top}:i===`bottom`?{x:a.x,y:n.bottom}:i===`left`?{x:n.left,y:a.y}:{x:n.right,y:a.y}},Me=e=>{let t=U(e.side,e.align,e.anchorRect,e.direction);return`${t.x-e.popupX}px ${t.y-e.popupY}px`};function Ne(e){let t=e.getBoundingClientRect(),n=e.offsetWidth>0?e.offsetWidth:t.width,r=e.offsetHeight>0?e.offsetHeight:t.height;return{top:t.top,left:t.left,right:t.left+n,bottom:t.top+r,width:n,height:r}}const Pe=e=>{if(e)try{e.focus({preventScroll:!0})}catch{e.focus()}},Fe=(e,t,n,r,i)=>{let a=r.x+i,o=r.x+r.width-i,s=r.y+i,c=r.y+r.height-i;return e===`top`?Math.max(0,s-t.y):e===`bottom`?Math.max(0,t.y+n.height-c):e===`left`?Math.max(0,a-t.x):Math.max(0,t.x+n.width-o)},W=(e,t,n)=>nt>n&&ee===`top`||e===`bottom`;function Le(e){let t=Ae(e),n=e.direction??`ltr`,r=e.allowedSides?.length?[...new Set(e.allowedSides)]:[...ke(e.side)],i=r.includes(e.side)?e.side:r[0],a=i,o=V(i,n),s=H(o,e.align,e.anchorRect,e.contentRect,e.sideOffset,e.alignOffset);if(e.avoidCollisions){let c=t.x+e.collisionPadding,l=t.y+e.collisionPadding,u=G(e.anchorRect.left,e.anchorRect.right,t.x,t.x+t.width),d=G(e.anchorRect.top,e.anchorRect.bottom,t.y,t.y+t.height);if(Ie(o)?d:u){let c=[i,...r.filter(e=>e!==i)],l=a,u=s,d=1/0,f=o;for(let r of c){let i=V(r,n),a=H(i,e.align,e.anchorRect,e.contentRect,e.sideOffset,e.alignOffset),o=Fe(i,a,e.contentRect,t,e.collisionPadding);if(o<=0){l=r,u=a,d=o,f=i;break}ol&&(u=s-t.clientHeight+n),u=Math.min(Math.max(u,0),r),u!==t.scrollTop&&(t.scrollTop=u)}const K=new WeakMap,ze=(e,t)=>{let n=String(t);e.overlay&&(e.overlay.setAttribute(`data-stack-index`,n),e.overlay.style.setProperty(`--${e.cssVarPrefix}-stack-index`,n),e.overlay.style.setProperty(`--${e.cssVarPrefix}-overlay-stack-index`,n)),e.content.setAttribute(`data-stack-index`,n),e.content.style.setProperty(`--${e.cssVarPrefix}-stack-index`,n),e.content.style.setProperty(`--${e.cssVarPrefix}-content-stack-index`,n)},Be=e=>{e.overlay&&(e.overlay.removeAttribute(`data-stack-index`),e.overlay.style.removeProperty(`--${e.cssVarPrefix}-stack-index`),e.overlay.style.removeProperty(`--${e.cssVarPrefix}-overlay-stack-index`)),e.content.removeAttribute(`data-stack-index`),e.content.style.removeProperty(`--${e.cssVarPrefix}-stack-index`),e.content.style.removeProperty(`--${e.cssVarPrefix}-content-stack-index`)},q=e=>{e.entries.forEach((e,t)=>ze(e,t)),e.updateIsolation()},Ve=e=>{let t=e.defaultView,n=new Map,r,i=()=>{for(let[e,t]of n)e.toggleAttribute(`inert`,t.inert),t.aria===null?e.removeAttribute(`aria-hidden`):e.setAttribute(`aria-hidden`,t.aria);n.clear()},a={entries:[],cleanup:()=>{},updateIsolation(){i();let o=a.entries.at(-1);if(!o||!a.entries.some(e=>e.isolateOutside)){r?.disconnect(),r=void 0;return}r||(r=new t.MutationObserver(()=>a.updateIsolation()),r.observe(e.body,{childList:!0,subtree:!0}));let s=new Set([o.content]);o.overlay&&s.add(o.overlay);for(let t of e.body.querySelectorAll(`*`))E(o.content,t)&&s.add(t);let c=new Set;for(let e of s)for(let t=e.parentElement;t&&!c.has(t);t=t.parentElement)c.add(t);let l=e=>{for(let t of e.children)if(!s.has(t)){if(c.has(t)){l(t);continue}n.set(t,{inert:t.hasAttribute(`inert`),aria:t.getAttribute(`aria-hidden`)}),t.setAttribute(`inert`,``),t.setAttribute(`aria-hidden`,`true`)}};l(e.body)}},o=j(e,`keydown`,e=>{if(e.key!==`Tab`)return;let t=a.entries[a.entries.length-1];t&&t.onTabKeydown?.(e)});return a.cleanup=()=>{o(),r?.disconnect(),i(),a.entries.length=0},a},He=e=>{let t=K.get(e);if(t)return t;let n=Ve(e);return K.set(e,n),n};function Ue(e){let t=e.content.ownerDocument??document,n={content:e.content,overlay:e.overlay??null,onTabKeydown:e.onTabKeydown,cssVarPrefix:e.cssVarPrefix,isolateOutside:e.isolateOutside},r=!1,i=()=>{let e=K.get(t);if(!e)return;let r=e.entries.indexOf(n);r!==-1&&(e.entries.splice(r,1),Be(n),q(e),e.entries.length===0&&(e.cleanup(),K.delete(t)))};return{open:()=>{if(r)return;let e=He(t);e.entries.includes(n)||(e.entries.push(n),q(e))},close:i,get isTopmost(){return K.get(t)?.entries.at(-1)===n},destroy:()=>{r||(r=!0,i())}}}const J=e=>({x:e.x,y:e.y,width:e.width,height:e.height}),Y=(e,t)=>e.x===t.x&&e.y===t.y&&e.width===t.width&&e.height===t.height,We=e=>{let t=getComputedStyle(e),n=`${t.overflow}${t.overflowX}${t.overflowY}`;return/(auto|scroll|overlay)/.test(n)},Ge=(e,t)=>{let n=new Set([e]),r=e.parentNode;for(;r;){if(r instanceof Element){We(r)&&n.add(r),r=r.parentNode;continue}if(r instanceof Document){r.scrollingElement&&n.add(r.scrollingElement),n.add(r),r.defaultView&&(n.add(r.defaultView),r.defaultView.visualViewport&&n.add(r.defaultView.visualViewport));break}r=null}return n.add(t),t.visualViewport&&n.add(t.visualViewport),[...n]},Ke=(e,t,n)=>{let r=null,i=null,a=()=>{i!==null&&(n.clearTimeout(i),i=null),r?.disconnect(),r=null},o=(s=!1,c=1)=>{a();let l=e.getBoundingClientRect();if(s||t(),!l.width||!l.height)return;let u=e.ownerDocument.documentElement,d=Math.floor(l.top),f=Math.floor(u.clientWidth-(l.left+l.width)),p=Math.floor(u.clientHeight-(l.top+l.height)),m=Math.floor(l.left),h=`${-d}px ${-f}px ${-p}px ${-m}px`,g=Math.max(0,Math.min(1,c))||1,_=!0;r=new IntersectionObserver(t=>{let r=t[0]?.intersectionRatio??1;if(r!==g){if(!_){o();return}r?o(!1,r):i=n.setTimeout(()=>{o(!1,1e-7)},1e3)}if(r===1&&!Y(J(l),J(e.getBoundingClientRect()))){o();return}_=!1},{rootMargin:h,threshold:g}),r.observe(e)};return o(!0),a};function qe(e){let t=e.win??window,n=e.isActive??(()=>!0),r=e.observedElements??[],i=e.ancestorScroll??!0,a=e.syncOnScroll??!1,o=e.ancestorResize??!0,s=e.elementResize??typeof ResizeObserver<`u`,c=e.layoutShift??!1,l=e.animationFrame??!1,u=null,d=null,f=!1,p=null,m=null,h=[],g=()=>{u===null&&(u=t.requestAnimationFrame(()=>{u=null,n()&&e.onUpdate()}))},_=()=>{u!==null&&(t.cancelAnimationFrame(u),u=null)},v=()=>g(),y=t=>{if(!e.ignoreScrollTarget?.(t.target)){if(a){_(),n()&&e.onUpdate();return}g()}},b=()=>{let e=new Set([t]);t.visualViewport&&e.add(t.visualViewport);for(let n of r)for(let r of Ge(n,t))e.add(r);return[...e]};return{start:()=>{if(f)return;if(f=!0,i)for(let e of b())e.addEventListener(`scroll`,y,{passive:!0}),h.push(()=>e.removeEventListener(`scroll`,y));if(o){let e=[t];t.visualViewport&&e.push(t.visualViewport);for(let t of e)t.addEventListener(`resize`,v),h.push(()=>t.removeEventListener(`resize`,v))}if(s&&typeof ResizeObserver<`u`){p=new ResizeObserver(v);for(let e of r)p.observe(e)}let e=r[0]??null;if(c&&e&&typeof IntersectionObserver<`u`&&(m=Ke(e,g,t)),l&&e){let n=J(e.getBoundingClientRect()),r=()=>{if(!f)return;let i=J(e.getBoundingClientRect());Y(n,i)||g(),n=i,d=t.requestAnimationFrame(r)};d=t.requestAnimationFrame(r)}},stop:()=>{f&&(f=!1,u!==null&&_(),d!==null&&(t.cancelAnimationFrame(d),d=null),h.forEach(e=>e()),h=[],p?.disconnect(),p=null,m?.(),m=null)},update:g}}const X=new WeakMap,Je=(e,t)=>{let n=t.isOpen();return n&&!t.wasOpen&&(e.openSequence+=1,t.openOrder=e.openSequence),t.wasOpen=n,n},Z=(e,t)=>{let n=null;for(let r of e.layers)Je(e,r)&&(t&&!t(r)||(!n||r.openOrder>n.openOrder)&&(n=r));return n},Ye=e=>{let t=e.defaultView??window,n={layers:[],openSequence:0,pendingTouchOutside:!1,pendingIframeBlur:null,cleanup:()=>{}},r=()=>{n.pendingIframeBlur!=null&&(t.clearTimeout(n.pendingIframeBlur),n.pendingIframeBlur=null)},i=j(e,`pointerdown`,e=>{let t=e.target,r=Z(n);if(!r||!r.closeOnClickOutside||r.isInside(t)){n.pendingTouchOutside=!1;return}if(e.pointerType===`touch`){n.pendingTouchOutside=!0;return}n.pendingTouchOutside=!1,r.onDismiss({reason:`outside-press`,originalEvent:e})}),a=j(e,`click`,e=>{if(!n.pendingTouchOutside)return;n.pendingTouchOutside=!1;let t=e.target,r=Z(n);!r||!r.closeOnClickOutside||r.isInside(t)||r.onDismiss({reason:`outside-press`,originalEvent:e})}),o=j(e,`pointercancel`,()=>{n.pendingTouchOutside=!1}),s=j(t,`blur`,i=>{Z(n,e=>e.closeOnClickOutside)&&(n.pendingTouchOutside=!1,r(),n.pendingIframeBlur=t.setTimeout(()=>{n.pendingIframeBlur=null;let r=Z(n,e=>e.closeOnClickOutside);if(!r)return;let a=t.HTMLIFrameElement,o=e.activeElement;!a||!(o instanceof a)||r.isInside(o)||r.onDismiss({reason:`focus-out`,originalEvent:i})},0))}),c=j(e,`keydown`,e=>{if(e.key!==`Escape`||e.defaultPrevented)return;let t=e.target,r=Z(n,e=>e.closeOnEscape&&e.isInside(t))??Z(n,e=>e.closeOnEscape);r&&(r.preventEscapeDefault&&e.preventDefault(),r.onDismiss({reason:`escape-key`,originalEvent:e}))});return n.cleanup=()=>{i(),a(),o(),s(),c(),r(),n.pendingTouchOutside=!1,n.layers.length=0},n},Xe=e=>{let t=X.get(e);if(t)return t;let n=Ye(e);return X.set(e,n),n};function Ze(e){let t=e.root.ownerDocument??document,n=Xe(t),r={isOpen:e.isOpen,onDismiss:e.onDismiss,isInside:e.isInside??(t=>E(e.root,t)),closeOnClickOutside:e.closeOnClickOutside??!0,closeOnEscape:e.closeOnEscape??!0,preventEscapeDefault:e.preventEscapeDefault??!0,wasOpen:!1,openOrder:0};return Je(n,r)&&(r.wasOpen=!0),n.layers.push(r),()=>{let e=n.layers.indexOf(r);e!==-1&&n.layers.splice(e,1),n.layers.length===0&&(n.cleanup(),X.delete(t))}}function Qe(){let e=!1,t=!1,n=[],r=[],i=new Set,a=new Set;return{get isDestroyed(){return e},onBeforeDestroy:r=>{e||(t?r():n.push(r))},onDestroy:t=>{e?t():r.push(t)},trackRaf:n=>{if(e||t)return null;let r=0;return r=requestAnimationFrame(t=>{i.delete(r),e||n(t)}),i.add(r),r},cancelRaf:e=>{e===null||!i.delete(e)||cancelAnimationFrame(e)},cancelTimeout:e=>{e===null||!a.delete(e)||clearTimeout(e)},trackFinalRaf:n=>!t||e?null:requestAnimationFrame(n),trackTimeout:(n,r)=>{if(e||t)return null;let i=setTimeout(()=>{a.delete(i),e||n()},r);return a.add(i),i},destroy(){if(e||t)return!1;t=!0;for(let e of i)cancelAnimationFrame(e);i.clear();for(let e of a)clearTimeout(e);a.clear();for(let e of n.splice(0))e();e=!0,t=!1;for(let e of r.splice(0))e();return!0}}}function $e(e,t){e.onDestroy(()=>{t.positionSync.stop(),t.presence.cleanup(),t.portal.cleanup(),Q(t.cleanups),t.unbind()})}function et(e,t){t.beforeDestroy&&e.onBeforeDestroy(t.beforeDestroy),e.onDestroy(()=>{t.modalStack.destroy();for(let e of t.presence)e.cleanup();t.reset(),t.releaseScrollLock(),t.cleanup(),t.portal?.cleanup(),Q(t.cleanups),t.unbind()})}function Q(e){for(let t of e.splice(0))t()}function tt(e){let t=e.enabled??!0,n=e.wrapperSlot,r=e.container,i=e.mountTarget??r??e.content,a=e.state??{originalParent:null,originalNextSibling:null,portaled:!1},o=e.root.ownerDocument??document,s=null,c=()=>{if(s)return s;let e=o.createElement(`div`);return n&&(e.setAttribute(`data-slot`,n),e.style.isolation=`isolate`,e.style.zIndex=`50`),s=e,e},l=()=>{if(a.portaled)return;let t=e.content.parentNode;if(!t)return;let n=c();t.insertBefore(n,e.content),n.appendChild(e.content),O(n,e.root,a)},u=()=>{a.portaled||i.isConnected&&O(i,e.root,a)},d=()=>{if(!a.portaled)return;let t=s;if(!t)return;k(t,a);let n=t.parentNode;n&&n.isConnected?(n.insertBefore(e.content,t),t.remove()):e.content.remove()},f=()=>{a.portaled&&k(i,a)};return{state:a,get container(){return r||(t&&n&&a.portaled&&s?s:e.content)},mount:()=>{if(t){if(r||e.mountTarget){u();return}n?l():O(e.content,e.root,a)}},restore:()=>{if(t){if(r||e.mountTarget){f();return}n?d():k(e.content,a)}},cleanup:()=>{if(t){if(r||e.mountTarget){f();return}n?d():k(e.content,a)}}}}const nt=e=>{let t=e.trim().toLowerCase();if(!t)return 0;if(t.endsWith(`ms`)){let e=Number(t.slice(0,-2).trim());return Number.isFinite(e)?e:0}if(t.endsWith(`s`)){let e=Number(t.slice(0,-1).trim());return Number.isFinite(e)?e*1e3:0}let n=Number(t);return Number.isFinite(n)?n:0},$=(e,t)=>{let n=e.split(`,`),r=t.split(`,`),i=Math.max(n.length,r.length),a=0;for(let e=0;e{let t=getComputedStyle(e),n=$(t.transitionDuration,t.transitionDelay),r=$(t.animationDuration,t.animationDelay);return Math.max(n,r)};function it(e){let t=e.win??window,n=!1,r=null,i=null,a=null,o=null,s=[],c=()=>{a!==null&&(t.cancelAnimationFrame(a),a=null),o!==null&&(t.clearTimeout(o),o=null),s.forEach(e=>e()),s=[]},l=()=>{c(),n=!1,e.element.removeAttribute(`data-ending-style`)},u=()=>{r!==null&&(t.cancelAnimationFrame(r),r=null),i!==null&&(t.cancelAnimationFrame(i),i=null),e.element.removeAttribute(`data-starting-style`)},d=()=>{n&&(c(),n=!1,e.element.removeAttribute(`data-ending-style`),e.onExitComplete())};return{get isExiting(){return n},enter:()=>{l(),u(),e.element.setAttribute(`data-starting-style`,``),r=t.requestAnimationFrame(()=>{r=null,i=t.requestAnimationFrame(()=>{i=null,e.element.removeAttribute(`data-starting-style`)})})},exit:()=>{l(),u(),n=!0,e.element.setAttribute(`data-ending-style`,``);let r=t.performance.now(),i=rt(e.element);if(i>0){let n=n=>{if(n.target===e.element){if(typeof e.element.getAnimations==`function`){if(e.element.getAnimations().some(e=>e.playState===`running`||e.pending))return}else if(t.performance.now()-re.element.removeEventListener(`transitionend`,n)),s.push(()=>e.element.removeEventListener(`animationend`,n)),o=t.setTimeout(()=>{o=null,d()},Math.ceil(i)+50)}else a=t.requestAnimationFrame(()=>{a=null,d()})},cleanup:()=>{l(),u()}}}function at(e){let{element:t,axes:n}=e,r=t.ownerDocument,i=e.lockThreshold??8,a=null,o=!1,s,c=()=>{let e=a;if(a=null,e?.captured)try{e.captured.releasePointerCapture?.(e.pointerId)}catch{}return e},l=t=>{let n=c();n?.axis&&e.reset(n.target,t)},u=t=>!e.active||e.active(t.target),d=t=>{if(t.defaultPrevented||t.button!==0)return;let n=t.target;if(!(n instanceof Element))return;let r=e.start(t,n);r!==null&&(l(t),a={target:r,pressTarget:n,pointerId:t.pointerId,startX:t.clientX,startY:t.clientY,lastX:t.clientX,lastY:t.clientY,timeStamp:t.timeStamp,axis:null,captured:null})},f=(r,o=r)=>{if(!a||a.pointerId!==r.pointerId)return;if(!u(a)){l(o);return}a.lastX=r.clientX,a.lastY=r.clientY;let s=r.clientX-a.startX,c=r.clientY-a.startY;if(!a.axis){let l=Math.abs(s),u=Math.abs(c);if(Math.max(l,u)u?`x`:`y`,f={axis:d,deltaX:s,deltaY:c,event:o};if(!n.includes(d)||e.lock&&!e.lock(a.target,f,a.pressTarget)){a=null;return}a.axis=d,a.captured=e.capture?.(a.target)??t;try{a.captured.setPointerCapture?.(r.pointerId)}catch{}}o.cancelable&&o.preventDefault(),e.move(a.target,{axis:a.axis,deltaX:s,deltaY:c,event:o})},p=(t,n=t)=>{if(!a||a.pointerId!==t.pointerId)return;if(!u(a)){l(n);return}let r=c();r.axis&&(o=!0,clearTimeout(s),s=setTimeout(()=>{o=!1},400),e.release(r.target,{axis:r.axis,deltaX:r.lastX-r.startX,deltaY:r.lastY-r.startY,event:n,duration:t.timeStamp-r.timeStamp}))},m=()=>{o=!1,clearTimeout(s)},h=(e,t)=>({target:e.target,pointerId:t.identifier,clientX:t.clientX,clientY:t.clientY,button:0,timeStamp:e.timeStamp,defaultPrevented:e.defaultPrevented,cancelable:e.cancelable,preventDefault:()=>e.preventDefault()}),g=e=>Array.from(e).find(e=>e.identifier===a?.pointerId),_=[j(r,`pointerdown`,m,{capture:!0}),j(r,`touchstart`,m,{capture:!0,passive:!0}),j(t,`pointerdown`,e=>{e.pointerType!==`touch`&&d(e)}),j(r,`pointermove`,e=>{e.pointerType!==`touch`&&f(e)},{passive:!1}),j(r,`pointerup`,e=>{e.pointerType!==`touch`&&p(e)}),j(r,`pointercancel`,e=>{e.pointerType!==`touch`&&a?.pointerId===e.pointerId&&l(e)}),j(t,`touchstart`,e=>{if(e.touches.length!==1){l(e);return}d(h(e,e.touches[0]))},{passive:!0}),j(r,`touchmove`,e=>{let t=g(e.touches);t&&f(h(e,t),e)},{passive:!1}),j(r,`touchend`,e=>{let t=g(e.changedTouches);t&&p(h(e,t),e)}),j(r,`touchcancel`,e=>l(e)),j(r,`click`,e=>{!o||e.detail===0||(o=!1,e.preventDefault(),e.stopImmediatePropagation())},{capture:!0})];return{cancel(e){a&&(e===void 0||a.target===e)&&l()},destroy(){_.forEach(e=>e()),clearTimeout(s),l()}}}function ot(e={}){let t=e.resetDelayMs??500,n=e.win??window,r=``,i=null,a=()=>{i!==null&&(n.clearTimeout(i),i=null),r=``};return{match:(e,a,o)=>{i!==null&&n.clearTimeout(i),i=n.setTimeout(()=>{r=``,i=null},t);let s=e.toLowerCase();r+=s;let c=a.map(e=>e.toLowerCase()),l=c.findIndex(e=>e.startsWith(r));if(l===-1&&r.length===1){let e=o+1;for(let t=0;t{A.trackFinalRaf(()=>{j&&(document.contains(j)?a(j):T&&document.contains(T)&&a(T)),j=null})};ne(O,`dialog-content`),O.setAttribute(`role`,w?`alertdialog`:`dialog`),h(O,`modal`,!0),f(O,se,ce),D.setAttribute(`role`,`presentation`),D.setAttribute(`aria-hidden`,`true`),D.tabIndex=-1,T&&(T.setAttribute(`aria-haspopup`,`dialog`),T.setAttribute(`aria-controls`,O.id),h(T,`expanded`,!1));let I=!1,L=()=>{O.hasAttribute(`tabindex`)||(O.tabIndex=-1,I=!0)},R=()=>{I&&=(O.removeAttribute(`tabindex`),!1)},z=()=>{let e=o(O);if(e)return e.focus();L(),O.focus()},B=()=>{N?.mount()},V=()=>{A.trackRaf(()=>{j&&document.contains(j)&&typeof j.focus==`function`?a(j):T&&document.contains(T)&&a(T),j=null})},H=e=>{if(l.setAttribute(`data-state`,e),E&&E.setAttribute(`data-state`,e),D.setAttribute(`data-state`,e),O.setAttribute(`data-state`,e),e===`open`){l.setAttribute(`data-open`,``),E?.setAttribute(`data-open`,``),D.setAttribute(`data-open`,``),O.setAttribute(`data-open`,``),l.removeAttribute(`data-closed`),E?.removeAttribute(`data-closed`),D.removeAttribute(`data-closed`),O.removeAttribute(`data-closed`);return}l.setAttribute(`data-closed`,``),E?.setAttribute(`data-closed`,``),D.setAttribute(`data-closed`,``),O.setAttribute(`data-closed`,``),l.removeAttribute(`data-open`),E?.removeAttribute(`data-open`),D.removeAttribute(`data-open`),O.removeAttribute(`data-open`)},U=0,W=0,G=0,K=0,q=(e,t)=>{A.isDestroyed||k||t!==U||(e.hidden=!0,W=Math.max(0,W-1),W===0&&(R(),V()))},J=i({element:D,onExitComplete:()=>q(D,G)}),Y=i({element:O,onExitComplete:()=>q(O,K)}),X=(e,t=!1)=>{A.isDestroyed||k===e&&!t||(e?(U+=1,W=0,B(),j=document.activeElement,Q.open(),C&&!P&&(p(),P=!0)):(U+=1,W=2,G=U,K=U,Q.close(),P&&=(g(),!1)),k=e,T&&h(T,`expanded`,k),e?(D.hidden=!1,O.hidden=!1,H(`open`),J.enter(),Y.enter()):(H(`closed`),J.exit(),Y.exit()),te(l,`dialog:change`,{open:k}),b?.(k),e&&A.trackRaf(()=>z()))},Z=e=>{if(e.key!==`Tab`)return;let t=u(O);if(t.length===0){e.preventDefault(),L(),O.focus();return}let n=t[0],r=t[t.length-1],i=O.ownerDocument.activeElement;if(!t.includes(i)){e.preventDefault(),(e.shiftKey?r:n).focus();return}if(n===r){e.preventDefault();return}e.shiftKey?i===n&&(e.preventDefault(),r.focus()):i===r&&(e.preventDefault(),n.focus())},Q=n({content:O,overlay:D,onTabKeydown:Z,cssVarPrefix:`dialog`});O.hidden=!0,D.hidden=!0,H(`closed`),T&&M.push(m(T,`click`,()=>X(!k))),M.push(m(O,`click`,e=>{let t=e.target;if(!t)return;let n=t.closest?.(`[data-slot=\"dialog-close\"]`);n&&O.contains(n)&&X(!1)})),x&&M.push(m(D,`click`,e=>{e.button===0&&e.target===D&&k&&X(!1)})),M.push(t({root:l,isOpen:()=>k,onDismiss:()=>X(!1),closeOnClickOutside:!1,closeOnEscape:S}));let $={open:()=>{A.isDestroyed||X(!0)},close:()=>{A.isDestroyed||X(!1)},toggle:()=>{A.isDestroyed||X(!k)},get isOpen(){return k},destroy:()=>{A.destroy()},_handleKeydown:Z,_content:O,_overlay:D};return ie(A,{cleanups:M,modalStack:Q,presence:[J,Y],portal:N,beforeDestroy:F,reset:()=>{U+=1,W=0,k=!1,H(`closed`),D.hidden=!0,O.hidden=!0,T&&h(T,`expanded`,!1)},releaseScrollLock:()=>{P&&=(g(),!1)},cleanup:R,unbind:()=>e(l,_,$)}),M.push(re(l,`dialog:set`,e=>{let t=e.detail,n;t?.open===void 0?t?.value!==void 0&&(n=t.value):n=t.open,typeof n==`boolean`&&X(n)})),oe(l,_,$),y&&X(!0),$}function y(e=document){let t=[];for(let n of l(e,`dialog`))d(n,_)||t.push(v(n));return t}export{y as create,v as createDialog};", + "type": "registry:file", + "target": "~/assets/data-slot-dialog.js" + }, + { + "path": "vendor/data-slot-command.js", + "content": "import{clearRootBinding as e,emit as t,ensureId as n,ensureItemVisibleInContainer as r,getDataBool as i,getDataString as a,getOwnedElements as o,getPart as s,getRoots as c,hasRootBinding as l,on as u,onRoot as d,reuseRootBinding as ee,setAria as f,setRootBinding as te}from\"@data-slot/core\";const p=.1,m=.999,h=/[\\\\/_+.#\"@\\[\\(\\{&]/,g=/[\\\\/_+.#\"@\\[\\(\\{&]/g,_=/[\\s-]/,v=/[\\s-]/g;function y(e,t,n,r,i,a,o){if(a===t.length)return i===e.length?1:.99;let s=`${i},${a}`,c=o[s];if(c!==void 0)return c;let l=r.charAt(a),u=n.indexOf(l,i),d=0;for(;u>=0;){let s=y(e,t,n,r,u+1,a+1,o);if(s>d){if(u===i)s*=1;else if(h.test(e.charAt(u-1))){s*=.8;let t=e.slice(i,u-1).match(g);t&&i>0&&(s*=m**+t.length)}else if(_.test(e.charAt(u-1))){s*=.9;let t=e.slice(i,u-1).match(v);t&&i>0&&(s*=m**+t.length)}else s*=.17,i>0&&(s*=m**+(u-i));e.charAt(u)!==t.charAt(a)&&(s*=.9999)}if(ss&&(s=i*p)}s>d&&(d=s),u=n.indexOf(l,u+1)}return o[s]=d,d}function b(e){return e.toLowerCase().replace(v,` `)}function ne(e,t,n=[]){let r=n.length>0?`${e} ${n.join(` `)}`:e;return y(r,t,b(r),b(t),0,0,{})}const x=Symbol(`data-slot.command.authored-value`),S=Symbol(`data-slot.command.inferred-value`),C=e=>e==null?null:e.trim(),re=e=>e?e.split(/[,\\n]/).map(e=>e.trim()).filter(Boolean):[],ie=(e,t)=>Array.from(e.children).filter(e=>e instanceof HTMLElement&&e.getAttribute(`data-slot`)===t),ae=e=>{let t=e=>e.nodeType===Node.TEXT_NODE?e.textContent??``:!(e instanceof HTMLElement)||e.getAttribute(`data-slot`)===`command-shortcut`?``:Array.from(e.childNodes,t).join(``);return t(e).replace(/\\s+/g,` `).trim()},oe=e=>e.hasAttribute(`disabled`)||e.hasAttribute(`data-disabled`)||e.getAttribute(`aria-disabled`)===`true`,se=(e,t)=>{let n=e,r=e.hasAttribute(`data-value`);if(n[x]===void 0&&(n[x]=r),r?n[x]||(n[x]=C(e.getAttribute(`data-value`))!==n[S]):n[x]=!1,n[x])return{authored:!0,value:C(e.getAttribute(`data-value`))};let i=t();return n[S]=i,i===null?e.removeAttribute(`data-value`):e.setAttribute(`data-value`,i),{authored:!1,value:i}},w=`@data-slot/command`,T=`[data-slot=\"command-item\"]`,E=`[data-slot=\"command-group\"]`,ce=`[data-slot=\"command-separator\"]`,le=[`input:not([type=\"hidden\"])`,`textarea`,`select`,`button`,`a[href]`,`summary`,`audio[controls]`,`video[controls]`,`[contenteditable=\"\"]`,`[contenteditable=\"true\"]`,`[contenteditable=\"plaintext-only\"]`,`[tabindex]:not([tabindex=\"-1\"])`].join(`, `),ue=(e,t,n)=>t.trim()?ne(e,t,n??[]):1,de=(e,t)=>{let n=e;for(;n&&n.parentElement&&n.parentElement!==t;)n=n.parentElement;return n?.parentElement===t?n:null},D=e=>Array.from(e.children).filter(e=>e instanceof HTMLElement);function O(c,l={}){let p=ee(c,w,`[@data-slot/command] createCommand() called more than once for the same root. Returning the existing controller. Destroy it before rebinding with new options.`);if(p)return p;let m=c,h=s(c,`command-input`),g=s(c,`command-list`);if(!h||!g)throw Error(`Command requires command-input and command-list slots`);let _=o(c,g,`[data-slot=\"command-empty\"]`)[0]??null,v=l.label??a(m,`label`)??`Command Menu`,y=l.shouldFilter??i(m,`shouldFilter`)??!0,b=l.loop??i(m,`loop`)??!1,ne=l.disablePointerSelection??i(m,`disablePointerSelection`)??!1,x=l.vimBindings??i(m,`vimBindings`)??!0,S=l.filter??ue,O=l.onValueChange,fe=l.onSearchChange,pe=l.onSelect,k=C(l.defaultValue??a(m,`defaultValue`)),A=l.defaultSearch??a(m,`defaultSearch`)??``,j=[],M=[],N=[],P=new Map,F=new Map,I=0,L=[],R=null,z=null,B=!1,V=!1,me=!1,H=[],U=new Map,W=!1,he=n(h,`command-input`),ge=n(g,`command-list`);m.hasAttribute(`tabindex`)||(m.tabIndex=-1,me=!0),h.setAttribute(`role`,`combobox`),h.setAttribute(`aria-autocomplete`,`list`),h.setAttribute(`aria-expanded`,`true`),h.setAttribute(`aria-controls`,ge),h.setAttribute(`autocomplete`,`off`),h.setAttribute(`autocorrect`,`off`),h.spellcheck=!1,g.setAttribute(`role`,`listbox`),g.tabIndex=-1;let _e=document.querySelector(`label[for=\"${CSS.escape(he)}\"]`);if(_e){let e=n(_e,`command-label`),t=h.getAttribute(`aria-labelledby`);h.setAttribute(`aria-labelledby`,t?`${t} ${e}`:e),g.setAttribute(`aria-labelledby`,e)}else !h.hasAttribute(`aria-label`)&&!h.hasAttribute(`aria-labelledby`)&&(h.setAttribute(`aria-label`,v),g.setAttribute(`aria-label`,`Suggestions`));let ve=()=>{document.activeElement!==h&&h.focus({preventScroll:!0})},ye=e=>{if(!(e instanceof HTMLElement)||!m.contains(e))return null;let t=e.closest(le);return t instanceof HTMLElement&&m.contains(t)?t:null},be=e=>{R?.disconnect();try{return e()}finally{He()}},xe=()=>{g.style.setProperty(`--command-list-height`,`${g.scrollHeight.toFixed(1)}px`)},Se=(e,t)=>{let n=e.filter(e=>t.includes(e));for(let e of t){if(n.includes(e))continue;let r=t.indexOf(e),i=!1;for(let a=r-1;a>=0;--a){let r=t[a];if(!r)continue;let o=n.indexOf(r);if(o!==-1){n.splice(o+1,0,e),i=!0;break}}if(!i){for(let a=r+1;a{H=D(g),U=new Map(M.map(e=>[e.el,D(e.el)]))},we=()=>{H=Se(H,D(g)),U=new Map(M.map(e=>[e.el,Se(U.get(e.el)??[],D(e.el))]))},G=(e,t)=>{let n=new Set(t.filter(t=>t.parentElement===e)),r=e.firstElementChild;for(let t of[...n,...D(e).filter(e=>!n.has(e))])t===r?r=r.nextElementSibling:e.insertBefore(t,r)},Te=e=>new Map(e.map((e,t)=>[e,t])),Ee=()=>{for(let e of M)G(e.el,U.get(e.el)??[]);G(g,H)},K=()=>k===null?null:j.find(e=>e.value===k&&!e.el.hidden&&!(e.group?.el.hidden??!1))?.el??null,De=()=>{for(let e of j){let t=e.value===k&&k!==null;e.el.hasAttribute(`data-selected`)!==t&&(f(e.el,`selected`,t),t?e.el.setAttribute(`data-selected`,`true`):e.el.removeAttribute(`data-selected`))}let e=K();e?(h.setAttribute(`aria-activedescendant`,e.id),g.setAttribute(`aria-activedescendant`,e.id)):(h.removeAttribute(`aria-activedescendant`),g.removeAttribute(`aria-activedescendant`))},Oe=()=>{k===null?m.removeAttribute(`data-value`):m.setAttribute(`data-value`,k),A?m.setAttribute(`data-search`,A):m.removeAttribute(`data-search`)},ke=()=>[...g.querySelectorAll(T)].map(e=>P.get(e)??null).filter(e=>e!==null&&!e.el.hidden&&!(e.group?.el.hidden??!1)),q=()=>ke().filter(e=>!e.disabled),Ae=(e,n,r)=>{!r||e===n||(t(m,`command:change`,{value:n}),O?.(n))},je=()=>{let e=K();e&&r(e,g)},J=(e,t={})=>{let n=C(e),r=k,i=t.emit??!0,a=t.ensureVisible??!1;return k=n,De(),Oe(),a&&je(),(document.activeElement===h||document.activeElement===m)&&ve(),Ae(r,k,i),r!==k},Me=()=>{j=[],M=[],P=new Map,F=new Map;for(let e of o(m,g,E)){let t=ie(e,`command-group-heading`)[0]??null,r={el:e,heading:t,value:se(e,()=>C(t?.textContent)??n(e,`command-group`)).value??n(e,`command-group`),forceMount:i(e,`forceMount`)??!1,maxRank:0};if(F.set(e,r),M.push(r),e.setAttribute(`role`,`group`),t){let r=n(t,`command-group-heading`);e.setAttribute(`aria-labelledby`,r)}else e.removeAttribute(`aria-labelledby`)}for(let e of o(m,g,T)){let t=e.closest(E),r=t instanceof HTMLElement&&t.closest(`[data-slot=\"command\"]`)===m?F.get(t)??null:null,a={el:e,value:se(e,()=>C(e.getAttribute(`data-label`))??C(ae(e))).value,keywords:re(e.getAttribute(`data-keywords`)??void 0),disabled:oe(e),forceMount:i(e,`forceMount`)??!1,rank:0,group:r};P.set(e,a),j.push(a),n(e,`command-item`),e.setAttribute(`role`,`option`),f(e,`selected`,!1),f(e,`disabled`,a.disabled||null),e.removeAttribute(`data-selected`)}N=o(m,g,ce);for(let e of N)e.setAttribute(`role`,`separator`)},Ne=()=>{let e=A.length>0,t=y&&e;I=0;for(let e of M)e.maxRank=0;for(let e of j)e.rank=t&&e.value!==null?S(e.value,A,e.keywords):1,e.rank>0&&(I+=1),e.el.hidden=!(e.forceMount||e.group?.forceMount||e.rank>0),e.group&&e.rank>e.group.maxRank&&(e.group.maxRank=e.rank);for(let e of M)e.el.hidden=t&&!e.forceMount&&e.maxRank<=0;for(let t of N)t.hidden=e&&!(i(t,`alwaysRender`)??!1);_&&(_.hidden=I>0)},Pe=()=>{if(!y||!A)return;let e=H.filter(e=>e.parentElement===g),t=Te(e),n=new Map,r=new Map;for(let e of j){if(e.group!==null)continue;let t=de(e.el,g);t&&(n.set(t,Math.max(n.get(t)??0,e.rank)),e.el.hidden||r.set(t,Math.max(r.get(t)??0,e.rank)))}let i=new Set([...n.keys(),...M.map(e=>e.el)]),a=[...Array.from(r,([e,t])=>({el:e,rank:t})),...M.filter(e=>!e.el.hidden).map(e=>({el:e.el,rank:e.maxRank}))].sort((e,n)=>n.rank===e.rank?(t.get(e.el)??2**53-1)-(t.get(n.el)??2**53-1):n.rank-e.rank),o=new Set(a.map(e=>e.el)),s=D(g).filter(e=>i.has(e)&&!o.has(e)),c=[...a.map(e=>e.el),...s],l=0;G(g,e.map(e=>i.has(e)?c[l++]??e:e));for(let e of M){let t=U.get(e.el)?.filter(t=>t.parentElement===e.el)??[],n=Te(t),r=new Set,i=new Map;for(let t of j){if(t.group!==e)continue;let n=de(t.el,e.el);n&&(r.add(n),t.el.hidden||i.set(n,Math.max(i.get(n)??0,t.rank)))}let a=Array.from(i,([e,t])=>({el:e,rank:t})).sort((e,t)=>t.rank===e.rank?(n.get(e.el)??2**53-1)-(n.get(t.el)??2**53-1):t.rank-e.rank),o=new Set(a.map(e=>e.el)),s=D(e.el).filter(e=>r.has(e)&&!o.has(e)),c=[...a.map(e=>e.el),...s],l=0,u=t.map(e=>r.has(e)?c[l++]??e:e);G(e.el,u)}},Fe=()=>{be(()=>{y&&A?(Ne(),Pe(),W=!0):(W&&Ee(),Ne(),W=!1),De(),Oe()}),z||xe()},Y=()=>{if(be(Me),W?we():Ce(),Fe(),!(typeof ResizeObserver>`u`)){z??=new ResizeObserver(xe),z.disconnect();for(let e of[g,...o(m,g,`${T}, ${E}, ${ce}, [data-slot=\"command-empty\"]`)])z.observe(e)}},Ie=(e=!0,t=!1)=>{J(q()[0]?.value??null,{emit:e,ensureVisible:t})},X=(e,n=!0,r=!0)=>{A!==e&&(A=e,h.value=e,Fe(),r&&Ie(n,!0),n&&(t(m,`command:search-change`,{search:A}),fe?.(A)))},Le=e=>{e.value===null||e.disabled||(J(e.value,{emit:!0}),t(m,`command:select`,{value:e.value}),pe?.(e.value))},Re=e=>{let t=P.get(e)??null;return t||(Y(),t=P.get(e)??null,t)},Z=e=>{let t=q()[e];t&&J(t.value,{emit:!0,ensureVisible:!0})},Q=e=>{let t=q();if(t.length===0)return;let n=t.findIndex(e=>e.value===k),r=t[n+e];b&&(r=n+e<0?t[t.length-1]:n+e===t.length?t[0]:t[n+e]),r&&J(r.value,{emit:!0,ensureVisible:!0})},ze=e=>{let t=K()?.closest(E);for(;t;){let n=e>0?t.nextElementSibling:t.previousElementSibling,r=null;for(;n;){if(n instanceof HTMLElement&&n.getAttribute(`data-slot`)===`command-group`&&!n.hidden){r=n;break}n=e>0?n.nextElementSibling:n.previousElementSibling}if(!r)break;let i=[...r.querySelectorAll(T)].map(e=>P.get(e)??null).find(e=>e!==null&&!e.disabled&&!e.el.hidden);if(i){J(i.value,{emit:!0,ensureVisible:!0});return}t=r}Q(e)},Be=e=>{if(e.defaultPrevented)return;let t=ye(e.target);if(t&&t!==h)return;let n=e.key.toLowerCase();if(e.isComposing||e.keyCode===229)return;let r=()=>{e.preventDefault(),e.metaKey?Z(q().length-1):e.altKey?ze(1):Q(1)},i=()=>{e.preventDefault(),e.metaKey?Z(0):e.altKey?ze(-1):Q(-1)};switch(n){case`arrowdown`:r();break;case`arrowup`:i();break;case`home`:e.preventDefault(),Z(0);break;case`end`:e.preventDefault(),Z(q().length-1);break;case`enter`:{let t=K(),n=t?P.get(t):null;if(!n||n.value===null)return;e.preventDefault(),Le(n);break}case`j`:case`n`:x&&e.ctrlKey&&r();break;case`k`:case`p`:x&&e.ctrlKey&&i();break}},Ve=()=>{B||V||(B=!0,queueMicrotask(()=>{if(B=!1,V)return;let e=K();Y();let t=e?P.get(e)??null:null;if(t&&t.value!==null&&!t.disabled&&!t.el.hidden&&!(t.group?.el.hidden??!1)){J(t.value,{emit:!0});return}if(k!==null){let e=q().find(e=>e.value===k)??null;if(e){J(e.value,{emit:!0});return}}J(q()[0]?.value??null,{emit:!0})}))},He=()=>{R&&R.observe(g,{subtree:!0,childList:!0,characterData:!0,attributes:!0,attributeFilter:[`data-slot`,`data-value`,`data-label`,`data-keywords`,`data-disabled`,`disabled`,`aria-disabled`,`data-force-mount`,`data-always-render`]})};typeof MutationObserver<`u`&&(R=new MutationObserver(()=>{Ve()}),He()),h.value=A,Y(),(A||k===null)&&Ie(!1),L.push(u(h,`input`,()=>{X(h.value,!0,!0)})),L.push(u(m,`keydown`,e=>{Be(e)})),L.push(u(g,`pointermove`,e=>{if(ne)return;let t=e.target,n=t instanceof HTMLElement?t.closest(T):null;if(!(n instanceof HTMLElement))return;let r=Re(n);!r||r.disabled||n.hidden||r.group?.el.hidden||J(r.value,{emit:!0})})),L.push(u(g,`click`,e=>{let t=e.target,n=ye(t),r=t instanceof HTMLElement?t.closest(T):null;if(n&&n!==r||!(r instanceof HTMLElement))return;let i=Re(r);!i||i.disabled||i.value===null||r.hidden||i.group?.el.hidden||Le(i)})),L.push(d(m,`command:set`,e=>{let t=e.detail;t&&(t.search!==void 0&&X(String(t.search),!0,t.value===void 0),t.value!==void 0&&J(t.value,{emit:!0,ensureVisible:!0}))}));let $={get value(){return k},get search(){return A},select(e){J(e,{emit:!0,ensureVisible:!0})},setSearch(e){X(e,!0,!0)},destroy(){V=!0,R?.disconnect(),z?.disconnect(),L.forEach(e=>e()),L=[],me&&m.removeAttribute(`tabindex`),e(m,w,$)}};return te(m,w,$),$}function fe(e=document){let t=[];for(let n of c(e,`command`))l(n,w)||t.push(O(n));return t}export{fe as create,O as createCommand};", + "type": "registry:file", + "target": "~/assets/data-slot-command.js" + }, + { + "path": "vendor/data-slot-navigation-menu.js", + "content": "import{clearRootBinding as e,computeFloatingPosition as t,containsWithPortals as n,createContentMount as r,createDismissLayer as i,createPortalLifecycle as a,createPositionSync as o,createPresenceLifecycle as s,createTerminalLifecycle as c,drainCleanups as l,emit as u,ensureId as d,getDataBool as f,getDataEnum as p,getDataNumber as m,getOwnedElements as h,getPart as g,getParts as ee,getRoots as _,getTabbables as v,hasRootBinding as te,on as y,onRoot as b,reuseRootBinding as x,setAria as S,setRootBinding as C}from\"@data-slot/core\";function w(e,t,n,r){let i=c(),a=null,o=null,s=()=>{o!==null&&(i.cancelRaf(o),o=null)},l=r=>{if(!e)return;if(a=r,!r){s(),e.removeAttribute(`data-instant`),e.setAttribute(`data-state`,`hidden`);return}e.getAttribute(`data-state`)!==`visible`&&(s(),e.setAttribute(`data-instant`,``),o=i.trackRaf(()=>{o=i.trackRaf(()=>{e?.removeAttribute(`data-instant`),o=null})}));let c=t.getBoundingClientRect(),l=r.getBoundingClientRect();e.style.setProperty(`--indicator-left`,`${l.left-c.left}px`),e.style.setProperty(`--indicator-width`,`${l.width}px`),e.style.setProperty(`--indicator-top`,`${l.top-c.top}px`);let u=n&&parseFloat(getComputedStyle(n).marginTop)||0;e.style.setProperty(`--indicator-height`,`${l.height-(u<1?1:0)}px`),e.setAttribute(`data-state`,`visible`)};return{show:l,sync:(e=null)=>l(r()??e),get hovered(){return a},destroy(){i.destroy(),s(),a=null}}}const T=`a[href], button:not([disabled]), [role=\"link\"], [role=\"button\"], [tabindex]:not([tabindex=\"-1\"])`,E=e=>e.replace(/[^a-z0-9\\-_:.]/gi,`-`);function ne(e,t,n){let r=new Map,i=[],a=0;for(let t of n){let n=t.dataset.value;if(!n)continue;let o=h(e,t,`[data-slot=\"navigation-menu-trigger\"]`)[0]??null,s=h(e,t,`[data-slot=\"navigation-menu-content\"]`)[0]??null;if(!o||!s)continue;let c={value:n,item:t,trigger:o,content:s,index:a++};i.push(c),r.set(n,c);let l=E(n),u=d(o,`nav-menu-trigger-${l}`);d(s,`nav-menu-content-${l}`),o.setAttribute(`aria-haspopup`,`true`),o.removeAttribute(`aria-controls`),s.setAttribute(`aria-labelledby`,u)}let o=e=>{let t=e?.closest(`[data-slot=\"navigation-menu-item\"]`);if(!t)return null;let n=t.dataset.value,i=n?r.get(n):null;return i?.item===t?i:null},s=t=>{if(t.matches(T))return t;for(let n of h(e,t,T))if(!n.closest(`[data-slot=\"navigation-menu-content\"]`)&&!n.hidden&&!n.closest(`[hidden]`))return n;return null},c=[],l=new Map;for(let e of n){let t=o(e),n=t?{kind:`submenu`,element:t.trigger,item:t.item,value:t.value,trigger:t.trigger}:(()=>{let t=s(e);return t?{kind:`plain`,element:t}:null})();n&&(c.push(n),l.set(n.element,n),n.kind===`submenu`&&l.set(n.item,n))}return{allItems:i,itemMap:r,navigables:c,navigableByTarget:e=>{let n=e instanceof HTMLElement?e:null;for(;n&&n!==t;){let e=l.get(n);if(e)return e;n=n.parentElement}return null},isNonSubmenuListTarget(e){if(!(e instanceof Node)||!t.contains(e))return!1;let n=e instanceof HTMLElement?e:e.parentElement;return!n||n.closest(`[data-slot=\"navigation-menu-indicator\"]`)||o(n)?!1:!!n.closest(`[data-slot=\"navigation-menu-item\"], a[href], button, [role=\"link\"], [role=\"button\"]`)}}}const D=e=>{let t=getComputedStyle(e),n=parseFloat(t.width)||0,r=parseFloat(t.height)||0;if(e instanceof HTMLElement){let t=e.offsetWidth||n,i=e.offsetHeight||r;(Math.round(n)!==t||Math.round(r)!==i)&&(n=t,r=i)}return{width:n,height:r}};function O(e){let t=new WeakMap,n=null,r=null,i={committed:{width:0,height:0},live:{width:0,height:0}},o=()=>n?.popup??x().popup,c=()=>n?.positioner??x().positioner,l=()=>{let{viewport:t,root:r}=e;if(!t)return!1;if(n)return!0;let i=x(),o=i.popup,c=i.positioner,l=i.portal,u=!1,d=!1,f=!1;(!o||!o.contains(t))&&(o=S(t,`navigation-menu-popup`),u=!0),(!c||!c.contains(o))&&(c=S(o,`navigation-menu-positioner`),d=!0),(!l||!l.contains(c))&&(l=S(c,`navigation-menu-portal`),f=!0);let p=a({content:o,root:r,enabled:!0,container:c,mountTarget:l}),m,h=s({element:t,onExitComplete:()=>{m.viewportExitComplete=!0,t.setAttribute(`data-ending-style`,``),y(m)}}),g=s({element:o,onExitComplete:()=>{m.popupExitComplete=!0,o.setAttribute(`data-ending-style`,``),y(m)}});return m={popup:o,positioner:c,portal:l,popupPortal:p,popupPresence:g,viewportPresence:h,generatedPopup:u,generatedPositioner:d,generatedPortal:f,isClosing:!1,popupExitComplete:!1,viewportExitComplete:!1},n=m,w(o),!0},u=()=>{if(!l())return;let e=n;e.popupPortal.mount(),e.isClosing=!1,e.popupExitComplete=!1,e.viewportExitComplete=!1},d=t=>{let r=n,i=e.viewport;!r||!i||(r.popup.hidden=!1,r.popup.style.pointerEvents=`auto`,i.hidden=!1,t&&(r.popupPresence.enter(),r.viewportPresence.enter()))},f=e=>{let t=n;t&&(e.width>0&&e.height>0&&(ne(t.popup,e),_(t.positioner,e),ee(e.width,e.height)),t.isClosing=!0,t.popupExitComplete=!1,t.viewportExitComplete=!1,t.popupPresence.exit(),t.viewportPresence.exit())},p=()=>{let e=o();if(!e)return{...i.committed};let t=D(e),n={width:e.offsetWidth||t.width||i.live.width||i.committed.width,height:e.offsetHeight||t.height||i.live.height||i.committed.height};return E(n),n},m=()=>{let e=i.committed;if(e.width<=0||e.height<=0)return!1;let t=p();return Math.abs(t.width-e.width)>1||Math.abs(t.height-e.height)>1},h=(e,t)=>{if(!l())return t;let r=n,a={...i.committed},o=e===`sync-current`?p():O(r),s={width:e===`sync-current`?a.width||o.width||t.width:o.width||t.width,height:e===`sync-current`?a.height||o.height||t.height:o.height||t.height};return E(o.width>0&&o.height>0?o:s),e!==`sync-current`&&T(s),s},g=(e,t)=>{let n=o();if(n){if(e!==`sync-current`){ne(n,t);return}(!n.style.getPropertyValue(`--popup-width`).trim()||!n.style.getPropertyValue(`--popup-height`).trim())&&ne(n,{width:i.committed.width||t.width,height:i.committed.height||t.height})}},ee=(t,n)=>{let r=e.viewport;r&&(r.style.setProperty(`--viewport-width`,`${t}px`),r.style.setProperty(`--viewport-height`,`${n}px`))},_=(e,t)=>{e.style.setProperty(`--positioner-width`,t===`max-content`?`max-content`:`${t.width}px`),e.style.setProperty(`--positioner-height`,t===`max-content`?`max-content`:`${t.height}px`)},v=e=>{let n=o();if(!n)return;if(e===null){k(n);return}let r=t.get(n)??{applied:!1,bottom:``,left:``,position:``,right:``,top:``};r.applied||Object.assign(r,{position:n.style.position,top:n.style.top,right:n.style.right,bottom:n.style.bottom,left:n.style.left}),r.applied=!0,t.set(n,r),n.style.position=`absolute`,e===`top`?Object.assign(n.style,{top:``,right:``,bottom:`0px`,left:`0px`}):Object.assign(n.style,{top:`0px`,right:`0px`,bottom:``,left:``})},te=()=>{b(!0)},y=t=>{e.isDestroyed()||n!==t||!t.isClosing||!t.popupExitComplete||!t.viewportExitComplete||e.hasExitingContent?.()||b(!1)},b=t=>{let a=n,o=e.viewport;if(!a||!o||!t&&e.isDestroyed())return;r?.disconnect(),r=null,e.beforeRestore(),A(a),i={committed:{width:0,height:0},live:{width:0,height:0}},o.hidden=!0,o.style.pointerEvents=`none`,a.popup.hidden=!0,a.popup.style.pointerEvents=`none`;let{originalParent:s,originalNextSibling:c}=a.popupPortal.state;a.popupPortal.restore(),s&&!s.isConnected&&s.insertBefore(a.portal,c?.parentNode===s?c:null),a.generatedPopup&&C(a.popup),a.generatedPositioner&&C(a.positioner),a.generatedPortal&&C(a.portal),a.popupPresence.cleanup(),a.viewportPresence.cleanup(),n=null},x=()=>{let t=e.viewport,n=e.root;if(!t)return{popup:null,positioner:null,portal:null};let r=null,i=null,a=null,o=t.parentElement,s=`popup`;for(;o&&o!==n;){let e=o.getAttribute(`data-slot`);if(e===`navigation-menu-portal`){a=o;break}if(s===`popup`&&e===`navigation-menu-popup`){r=o,s=`positioner`,o=o.parentElement;continue}if((s===`popup`||s===`positioner`)&&(e===`navigation-menu-positioner`||e===`navigation-menu-viewport-positioner`)){i=o,s=`portal`,o=o.parentElement;continue}o=o.parentElement}return{popup:r,positioner:i,portal:a}},S=(t,n)=>{let r=(e.root.ownerDocument??document).createElement(`div`);r.setAttribute(`data-slot`,n),n===`navigation-menu-positioner`&&(r.style.isolation=`isolate`,r.style.zIndex=`50`);let i=t.parentNode;if(!i)throw Error(`NavigationMenu expected popup stack child to have a parent node`);return i.insertBefore(r,t),r.appendChild(t),r},C=e=>{let t=e.parentNode;if(t){for(;e.firstChild;)t.insertBefore(e.firstChild,e);e.remove()}},w=e=>{r?.disconnect(),r=null,typeof ResizeObserver==`function`&&(r=new ResizeObserver(t=>{let n=t[0],r=Array.isArray(n?.borderBoxSize)?n.borderBoxSize[0]:void 0;E({width:Math.ceil(r?.inlineSize??e.offsetWidth),height:Math.ceil(r?.blockSize??e.offsetHeight)})}),r.observe(e))},T=e=>{e.width>0&&(i.committed.width=e.width),e.height>0&&(i.committed.height=e.height)},E=e=>{e.width>0&&(i.live.width=e.width),e.height>0&&(i.live.height=e.height)},ne=(e,t)=>{e.style.setProperty(`--popup-width`,`${t.width}px`),e.style.setProperty(`--popup-height`,`${t.height}px`)},O=e=>{let t=(e,t)=>{let n=new Map;for(let[r,i]of Object.entries(t))n.set(r,e.style.getPropertyValue(r)),e.style.setProperty(r,i);return()=>{for(let[t,r]of n)r?e.style.setProperty(t,r):e.style.removeProperty(t)}},n=t(e.popup,{\"--popup-width\":`auto`,\"--popup-height\":`auto`,position:`static`,transform:`none`,scale:`1`,top:``,right:``,bottom:``,left:``}),r=t(e.positioner,{\"--positioner-width\":`max-content`,\"--positioner-height\":`max-content`,\"--available-width\":`none`,\"--available-height\":`none`}),i=D(e.popup);return r(),n(),i},k=e=>{let n=t.get(e);if(n?.applied){for(let t of[`position`,`top`,`right`,`bottom`,`left`])n[t]?e.style.setProperty(t,n[t]):e.style.removeProperty(t);n.applied=!1}},A=t=>{let n=t.positioner;Object.assign(n.style,{position:``,top:``,left:``,margin:``,willChange:``,pointerEvents:``,transform:``});for(let e of[`--transform-origin`,`--positioner-width`,`--positioner-height`,`--available-width`,`--available-height`])n.style.removeProperty(e);let r=t.popup;Object.assign(r.style,{willChange:``,pointerEvents:``}),k(r),r.style.removeProperty(`scale`),r.style.removeProperty(`--transform-origin`),r.style.removeProperty(`--popup-width`),r.style.removeProperty(`--popup-height`);let i=e.viewport;if(i){Object.assign(i.style,{top:``,left:``,willChange:``});for(let e of[`--transform-origin`,`--viewport-width`,`--viewport-height`])i.style.removeProperty(e)}};return{get popup(){return o()},get positioner(){return c()},get closing(){return!!n?.isClosing},ensure:l,prepareOpen:u,reveal:d,close:f,checkExitComplete:()=>{n&&y(n)},baseline:p,isSizeTransitioning:m,measure:h,commitSize:g,setViewportSize:ee,setPositionerSize:_,setRuntimePosition:v,destroy:te}}function k(e){let{root:t,viewport:n,popup:r,activePanel:i,onBridgeEnter:a,onBridgeLeave:o}=e,s=e.safeTriangle??f(t,`safeTriangle`)??!1,c=e.debugSafeTriangle??f(t,`debugSafeTriangle`)??!1,l=s||c,u=[],d=null,p=null,m=()=>n?r.positioner?r.positioner:r.popup?r.popup:n.parentElement instanceof HTMLElement?n.parentElement:n:null,h=()=>{let e=m();return d||(d=t.ownerDocument.createElement(`div`),d.setAttribute(`data-slot`,`navigation-menu-bridge`),d.style.cssText=`position: absolute; pointer-events: auto; z-index: 0; display: none;`,u.push(y(d,`pointerenter`,a),y(d,`pointerleave`,e=>o(e.relatedTarget)))),e&&d.parentElement!==e&&e.insertBefore(d,e.firstChild),d},g=()=>{d&&Object.assign(d.style,{height:`0`,width:`0`,top:`0px`,left:`0px`,right:`0px`,bottom:`auto`,transform:`none`,clipPath:`none`,display:`none`})},ee=()=>!c||!t.ownerDocument.body?null:(p||(p=t.ownerDocument.createElement(`div`),p.setAttribute(`data-slot`,`navigation-menu-safe-triangle`),p.style.cssText=`position: fixed; pointer-events: none; display: none; z-index: 2147483647; background: rgba(255, 0, 0, 0.18); border: 1px solid rgba(255, 0, 0, 0.45)`),p.parentElement!==t.ownerDocument.body&&t.ownerDocument.body.appendChild(p),p),_=()=>{p&&Object.assign(p.style,{width:`0`,height:`0`,clipPath:`none`,display:`none`})},v=()=>{c||_()},te=e=>{let t=ee();if(!t)return;let{apex:n,edgeA:r,edgeB:i}=e,a=Math.min(n.x,r.x,i.x),o=Math.min(n.y,r.y,i.y),s=Math.max(1,Math.max(n.x,r.x,i.x)-a),c=Math.max(1,Math.max(n.y,r.y,i.y)-o),l=e=>`${(e.x-a)/s*100}% ${(e.y-o)/c*100}%`;Object.assign(t.style,{display:`block`,left:`${a}px`,top:`${o}px`,width:`${s}px`,height:`${c}px`,clipPath:`polygon(${l(n)}, ${l(r)}, ${l(i)})`})},b=(e,t,n)=>(e.x-n.x)*(t.y-n.y)-(t.x-n.x)*(e.y-n.y),x=(e,t,n,r)=>{let i=b(e,t,n),a=b(e,n,r),o=b(e,r,t);return!((i<0||a<0||o<0)&&(i>0||a>0||o>0))},S=(e,t,n)=>{let r=.5;return n.top>=t.bottom-r?[{x:n.left,y:n.top},{x:n.right,y:n.top}]:n.bottom<=t.top+r?[{x:n.left,y:n.bottom},{x:n.right,y:n.bottom}]:n.left>=t.right-r?[{x:n.left,y:n.top},{x:n.left,y:n.bottom}]:n.right<=t.left+r?[{x:n.right,y:n.top},{x:n.right,y:n.bottom}]:[[{x:n.left,y:n.top},{x:n.right,y:n.top}],[{x:n.right,y:n.top},{x:n.right,y:n.bottom}],[{x:n.left,y:n.bottom},{x:n.right,y:n.bottom}],[{x:n.left,y:n.top},{x:n.left,y:n.bottom}]].reduce((t,n)=>Math.abs(n[0].y===n[1].y?e.y-n[0].y:e.x-n[0].x){if(n.width<=0||n.height<=0)return null;let r={x:e.left+e.width/2,y:e.top+e.height*.62},[i,a]=S(r,t,n),o=i.x===a.x?Math.abs(a.y-i.y):Math.abs(a.x-i.x),s=o<=28?0:Math.min(10,(o-28)/2);if(i.x===a.x){let e=Math.min(i.y,a.y),t=Math.max(i.y,a.y);i={x:i.x,y:e+s},a={x:a.x,y:t-s}}else{let e=Math.min(i.x,a.x),t=Math.max(i.x,a.x);i={x:e+s,y:i.y},a={x:t-s,y:a.y}}return{apex:r,edgeA:i,edgeB:a}},w=()=>{let e=i();if(!l||!n||!e)return null;let r=n.getBoundingClientRect(),a=e.content.getBoundingClientRect();return C(e.trigger.getBoundingClientRect(),t.getBoundingClientRect(),r.width>0&&r.height>0?r:a)},T=e=>{let t=w();return!!t&&x({x:e.clientX,y:e.clientY},t.apex,t.edgeA,t.edgeB)},E=e=>{let t=Array.from(new Map(e.map(e=>[`${e.x.toFixed(3)}:${e.y.toFixed(3)}`,e])).values()).sort((e,t)=>e.x===t.x?e.y-t.y:e.x-t.x);if(t.length<=2)return t;let n=(e,t,n)=>(t.x-e.x)*(n.y-e.y)-(t.y-e.y)*(n.x-e.x),r=e=>{let t=[];for(let r of e){for(;t.length>=2&&n(t[t.length-2],t[t.length-1],r)<=0;)t.pop();t.push(r)}return t},i=r(t),a=r([...t].reverse());return i.pop(),a.pop(),i.concat(a)},ne=(e,t)=>{if(e.length<3)return g();let n=h(),i=E(n.parentElement===r.positioner?e.map(e=>({x:e.x-t.x,y:e.y-t.y})):e);if(i.length<3)return g();let a=Math.min(...i.map(e=>e.x)),o=Math.min(...i.map(e=>e.y)),s=Math.max(...i.map(e=>e.x)),c=Math.max(...i.map(e=>e.y)),l=Math.max(1,s-a),u=Math.max(1,c-o);Object.assign(n.style,{display:`block`,transform:`none`,bottom:`auto`,right:`auto`,left:`${a}px`,top:`${o}px`,width:`${l}px`,height:`${u}px`,clipPath:`polygon(${i.map(e=>`${(e.x-a)/l*100}% ${(e.y-o)/u*100}%`).join(`, `)})`})},D=e=>{let t=Math.max(0,e.viewportRect.top-e.rootRect.bottom),n=Math.max(0,e.rootRect.top-e.viewportRect.bottom),r=Math.max(0,e.viewportRect.left-e.rootRect.right),i=Math.max(0,e.rootRect.left-e.viewportRect.right),a=Math.max(t,n,e.viewportMarginTop),o=Math.max(r,i),s=l?C(e.triggerRect,e.rootRect,e.viewportRect):null,c=[];if(a>=o&&a>0){let r=Math.max(t,n,e.viewportMarginTop),i=e.offset.x,a=e.contentSize.width,o=n>t&&n>=e.viewportMarginTop?e.offset.y+e.contentSize.height:e.offset.y-r;if(s){let t=Math.min(s.edgeA.x,s.edgeB.x)-e.rootRect.left,n=Math.max(s.edgeA.x,s.edgeB.x)-e.rootRect.left;i=t-8,a=n-t+16}c.push({x:i,y:o},{x:i,y:o},{x:i+a,y:o},{x:i+a,y:o+r},{x:i,y:o+r})}else if(o>0){let t=Math.max(r,i),n=e.offset.y,a=e.contentSize.height,o=i>r?e.offset.x+e.contentSize.width:e.offset.x-t;if(s){let t=Math.min(s.edgeA.y,s.edgeB.y)-e.rootRect.top,r=Math.max(s.edgeA.y,s.edgeB.y)-e.rootRect.top;n=t-8,a=r-t+16}c.push({x:o,y:n},{x:o+t,y:n},{x:o+t,y:n+a},{x:o,y:n+a})}s&&c.push({x:s.apex.x-e.rootRect.left,y:s.apex.y-e.rootRect.top},{x:s.edgeA.x-e.rootRect.left,y:s.edgeA.y-e.rootRect.top},{x:s.edgeB.x-e.rootRect.left,y:s.edgeB.y-e.rootRect.top}),ne(c,e.offset),O()},O=()=>{if(!c)return;let e=w();e?te(e):_()};return{update:D,contains:e=>!!e&&(!!i()?.content.contains(e)||!!n?.contains(e)||!!r.popup?.contains(e)||!!d?.contains(e)||!!r.positioner?.contains(e)),inCorridor:T,clear:v,hideBridge:g,refreshDebug:O,destroy:()=>{g(),_(),p?.remove(),p=null,u.splice(0).forEach(e=>e())}}}const A=[`start`,`center`,`end`],j=[`top`,`right`,`bottom`,`left`],M=(e,t)=>t===`start`?{x:e.left,y:e.top}:t===`end`?{x:e.right,y:e.bottom}:{x:e.left+e.width/2,y:e.top+e.height/2},N=(e,t,n)=>{let r=M(n,t);return e===`top`?{x:r.x,y:n.top}:e===`bottom`?{x:r.x,y:n.bottom}:e===`left`?{x:n.left,y:r.y}:{x:n.right,y:r.y}};function re(e){let{root:n,viewport:r,triggers:i,popup:a,placement:s,readSelection:l,onLayout:u}=e,d=s.side??p(n,`side`,j)??`bottom`,f=s.align??p(n,`align`,A)??`start`,h=s.sideOffset??m(n,`sideOffset`)??0,g=s.alignOffset??m(n,`alignOffset`)??0,ee=s.positionMethod??p(n,`positionMethod`,[`absolute`,`fixed`])??`absolute`,_=new Map,v=new Map,te={x:0,y:0},y=!1,b=0,x=!1,S=null,C=null,w=null,T=null,E=c(),ne=e=>({side:s.side??p(e.content,`side`,j)??p(e.item,`side`,j)??d,align:s.align??p(e.content,`align`,A)??p(e.item,`align`,A)??f,sideOffset:s.sideOffset??m(e.content,`sideOffset`)??m(e.item,`sideOffset`)??h,alignOffset:s.alignOffset??m(e.content,`alignOffset`)??m(e.item,`alignOffset`)??g}),D=()=>{C!==null&&E.cancelRaf(C),C=null},O=()=>{S!==null&&E.cancelRaf(S),S=null},k=()=>{let e=y||x;for(let t of[r,a.popup,a.positioner])t&&(e?t.setAttribute(`data-instant`,``):t.removeAttribute(`data-instant`))},M=()=>{O(),S=E.trackRaf(()=>{if(S=null,y&&b>0){--b,M();return}y=!1,x=!1,k()})},re=(e,t)=>{let n=_.get(e)??{applied:!1,left:``,position:``,top:``};if(t){n.applied||Object.assign(n,{position:e.style.position,top:e.style.top,left:e.style.left}),n.applied=!0,_.set(e,n),Object.assign(e.style,{position:`absolute`,top:`0px`,left:`0px`});return}if(n.applied){for(let t of[`position`,`top`,`left`])n[t]?e.style.setProperty(t,n[t]):e.style.removeProperty(t);n.applied=!1}},ie=e=>{if(!r)return;let t=v.get(e)??{originalParent:null,originalNextSibling:null,mountedInViewport:!1};t.mountedInViewport||(t.originalParent=e.parentNode,t.originalNextSibling=e.nextSibling,t.mountedInViewport=!0,v.set(e,t)),e.parentNode!==r&&r.appendChild(e)},P=e=>{let t=v.get(e);t?.mountedInViewport&&(t.originalParent?t.originalNextSibling?.parentNode===t.originalParent?t.originalParent.insertBefore(e,t.originalNextSibling):t.originalParent.appendChild(e):e.remove(),t.mountedInViewport=!1,t.originalParent=null,t.originalNextSibling=null)},ae=(e,i)=>{if(!r||!a.ensure()||a.closing||r.getAttribute(`data-state`)!==`open`||e.content.getAttribute(`data-state`)!==`active`)return;let o=a.popup,s=a.positioner;if(!o||!s)return;re(e.content,!1);let c=e.content.firstElementChild,l=e.content.lastElementChild,d=c&&parseFloat(getComputedStyle(c).marginTop)||0,f=l&&parseFloat(getComputedStyle(l).marginBottom)||0,p=e.content.getBoundingClientRect(),m={width:Math.max(p.width,e.content.scrollWidth,e.content.offsetWidth,e.content.clientWidth),height:Math.max(p.height,e.content.scrollHeight,e.content.offsetHeight,e.content.clientHeight)+d+f},h=a.measure(i,m);if(h.width<=0||h.height<=0)return;let g=ne(e),_=n.getBoundingClientRect(),v=e.trigger.getBoundingClientRect(),y=t({anchorRect:v,contentRect:{top:p.top,left:p.left,width:h.width,height:h.height,right:p.left+h.width,bottom:p.top+h.height},side:g.side,align:g.align,sideOffset:g.sideOffset,alignOffset:g.alignOffset,avoidCollisions:!1,collisionPadding:0,allowedSides:j});te={x:y.x-_.left,y:y.y-_.top};let b=y.side,x=y.align,S=N(b,x,v),C=`${S.x-y.x}px ${S.y-y.y}px`,w=`${S.x-_.left}px ${S.y-_.top}px`,T=n.ownerDocument.defaultView??window,E=ee===`fixed`,D=T.visualViewport,O=D?.offsetLeft??0,k=D?.offsetTop??0,A=D?.width??T.innerWidth,M=D?.height??T.innerHeight,ie=b===`left`?Math.max(0,v.left-O-g.sideOffset):b===`right`?Math.max(0,O+A-v.right-g.sideOffset):Math.max(0,A),P=b===`top`?Math.max(0,v.top-k-g.sideOffset):b===`bottom`?Math.max(0,k+M-v.bottom-g.sideOffset):Math.max(0,M);Object.assign(s.style,{position:ee,top:`${y.y+(E?0:T.scrollY)}px`,left:`${y.x+(E?0:T.scrollX)}px`,margin:`0`,willChange:`top,left`,pointerEvents:`none`}),o.style.willChange=`width,height`,o.style.pointerEvents=`auto`,a.setRuntimePosition(b===`top`?`top`:b===`left`?`left`:null),o.style.setProperty(`--transform-origin`,C),Object.assign(r.style,{top:`0px`,left:`0px`,willChange:`transform,width,height`}),r.style.setProperty(`--transform-origin`,C),e.content.style.setProperty(`--transform-origin`,C);for(let t of[o,r,e.content,s])t.setAttribute(`data-side`,b),t.setAttribute(`data-align`,x);s.style.setProperty(`--transform-origin`,w),a.setPositionerSize(s,h),s.style.setProperty(`--available-width`,`${ie}px`),s.style.setProperty(`--available-height`,`${P}px`),a.setViewportSize(h.width,h.height),a.commitSize(i,h),u({rootRect:_,triggerRect:v,viewportRect:new DOMRect(y.x,y.y,h.width,h.height),offset:{...te},contentSize:h,viewportMarginTop:parseFloat(getComputedStyle(r).marginTop)||0})},F=(e=l().panel,t={})=>{if(!e)return;let n=()=>ae(e,t.mode??`measure-target`);t.defer===!1?n():E.trackRaf(()=>{E.isDestroyed||n()})},oe=()=>{D(),C=E.trackRaf(()=>{C=null;let e=l();!e.panel||!a.ensure()||a.closing||(y&&R(),F(e.panel,{defer:!1,mode:a.popup?.hasAttribute(`data-starting-style`)||a.isSizeTransitioning()?`sync-current`:`measure-target`}))})},I=e=>{w?.disconnect(),w=null,T?.disconnect(),T=null,D(),!(!r||!e)&&(typeof ResizeObserver==`function`&&(w=new ResizeObserver(oe),w.observe(e.content)),typeof MutationObserver==`function`&&(T=new MutationObserver(oe),T.observe(e.content,{childList:!0,subtree:!0,characterData:!0})))},L=o({observedElements:[n,...i],isActive:()=>l().open,ancestorScroll:!0,syncOnScroll:!0,ancestorResize:!0,elementResize:!0,layoutShift:!0,onUpdate:()=>{x=!0,k(),F(void 0,{defer:!1,mode:`sync-current`}),M()}}),R=()=>{y=!0,b=Math.max(b,4),k(),M()},z=()=>{O(),y=!1,b=0,x=!1,k()},B=({open:e,initial:t,switching:n})=>{(!e||n)&&(O(),x=!1),y=t,b=t?Math.max(b,4):0,k()},V=()=>{D(),z(),te={x:0,y:0}},H=()=>{L.stop(),I(null),V()};return{mount:ie,setAbsolute:re,restore:P,sync:F,observe:I,start:()=>L.start(),stop:()=>L.stop(),beginInitialOpen:R,clearInstant:z,commitInstant:B,reset:V,dispose:H,destroy:()=>{if(E.destroy()){H();for(let e of v.keys())P(e),re(e,!1),e.hidden=!0,e.style.pointerEvents=`none`;v.clear(),_.clear()}}}}const ie=`@data-slot/navigation-menu`;function P(t,a={}){let o=x(t,ie,`[@data-slot/navigation-menu] createNavigationMenu() called more than once for the same root. Returning the existing controller. Destroy it before rebinding with new options.`);if(o)return o;let d=a.delayOpen??m(t,`delayOpen`)??0,h=a.delayClose??m(t,`delayClose`)??0,_=a.openOnFocus??f(t,`openOnFocus`)??!1,te=a.onValueChange,T=a.mountStrategy??p(t,`mountStrategy`,[`lazy`,`eager`])??`lazy`,E=(e,t)=>{`inert`in e&&(e.inert=t)},D=g(t,`navigation-menu-list`),A=ee(t,`navigation-menu-item`),j=g(t,`navigation-menu-viewport`),M=g(t,`navigation-menu-indicator`);if(!D||A.length===0)throw Error(`NavigationMenu requires navigation-menu-list and at least one navigation-menu-item`);let N=null,P=null,ae=-1,F=null,oe=null,I=null,L=!1,R=null,z=null,B=!1,V=c();V.onDestroy(()=>l(U));let H=null,se=()=>{H!==null&&(V.cancelRaf(H),H=null)},U=[],ce=new Map,le=(e,t)=>{e&&(t?(e.setAttribute(`data-open`,``),e.removeAttribute(`data-closed`)):(e.setAttribute(`data-closed`,``),e.removeAttribute(`data-open`)))},W=(e,t)=>{e&&(e.setAttribute(`data-state`,t?`open`:`closed`),le(e,t))},ue=(e,t)=>{e.setAttribute(`data-state`,t?`active`:`inactive`),le(e,t)},de=(e,t)=>{t?e.setAttribute(`data-activation-direction`,t):e.removeAttribute(`data-activation-direction`)},fe=()=>{},G=O({root:t,viewport:j,isDestroyed:()=>V.isDestroyed,beforeRestore:()=>fe(),hasExitingContent:()=>[...ce.values()].some(e=>e.isExiting)}),pe=()=>G.popup,me=()=>G.positioner,he=ne(t,D,A),{allItems:ge,itemMap:K,navigables:q,navigableByTarget:_e}=he,ve=new Map;for(let{content:e}of ge)ve.set(e,r({root:t,target:e,strategy:T})),ce.set(e,s({element:e,onExitComplete:()=>{V.isDestroyed||(ue(e,!1),de(e,null),e.removeAttribute(`data-motion`),Z.setAbsolute(e,!1),Z.restore(e),e.hidden=!0,e.style.pointerEvents=`none`,ve.get(e)?.unmount(),G.checkExitComplete())}}));let ye=Array.from(K.values()).map(e=>e.trigger),be=_e,xe=e=>{let t=be(e);return t?.kind===`plain`?t:null},Se=e=>{let t=be(e);return t?.kind===`submenu`?t:null},Ce=()=>{let e=t.ownerDocument.defaultView;if(!e?.matchMedia)return!0;try{return e.matchMedia(`(any-hover: hover)`).matches}catch{return!0}},we=e=>{try{return e.matches(`:hover`)}catch{return!1}},Te=()=>{for(let e of q){if(e.kind===`submenu`){if(we(e.item)||we(e.trigger))return e;continue}if(we(e.element))return e}return null},Ee=(e,n={})=>{let r=t.ownerDocument,i=n.preserveOpenOnPlain??!1,a=e.kind===`submenu`?e.trigger:e.element;e.kind===`plain`&&N!==null&&!i&&$();let o=r.activeElement===a;return a.focus(),r.activeElement===a?(o&&Ve(a),!0):!1},De=(e,t,n={})=>{for(let r=e+t;r>=0&&r{let r=q.indexOf(e);return r===-1?!1:De(r,t,n)},ke=(e,t,n={})=>{let r=q.findIndex(t=>t.kind===`submenu`&&t.trigger===e);return r===-1?!1:De(r,t,n)},Ae=e=>he.isNonSubmenuListTarget(e),je=v,Me=e=>{if(t.contains(e)||j?.contains(e)||pe()?.contains(e)||me()?.contains(e))return!0;for(let{content:t}of K.values())if(t.contains(e))return!0;return!1},Ne=()=>{let e=t.ownerDocument,n=v(e);for(let r of n)if(!Me(r)&&(t.compareDocumentPosition(r)&Node.DOCUMENT_POSITION_FOLLOWING)!==0&&(r.focus(),e.activeElement===r))return!0;return!1},Pe=e=>{V.trackRaf(()=>{if(N!==e)return;let t=K.get(e);if(!t)return;let n=je(t.content)[0];n?n.focus():t.content.focus()})},J=()=>{F&&=(V.cancelTimeout(F),null),oe&&=(V.cancelTimeout(oe),null)},Fe=()=>{J(),P=null},Ie=()=>{R=null,z=null},Le=()=>N?K.get(N)??null:null,Y=k({root:t,viewport:j,popup:G,safeTriangle:a.safeTriangle,debugSafeTriangle:a.debugSafeTriangle,activePanel:Le,onBridgeEnter:J,onBridgeLeave:e=>{L||N===null||Y.contains(e)||e&&n(t,e)||(Q(null),X(null))}});U.push(()=>Y.destroy());let Re=e=>Y.update(e),ze=e=>ae===-1||e>ae?`right`:`left`,Be=w(M,D,j,()=>N?K.get(N)?.trigger??null:null);U.push(()=>Be.destroy());let X=e=>{e||se(),I=e,Be.show(e)},Ve=(e=null)=>{Be.sync(e),I=Be.hovered},Z=re({root:t,viewport:j,triggers:ye,popup:G,placement:a,readSelection:()=>({open:N!==null,panel:Le()}),onLayout:Re});fe=Z.reset,U.push(()=>{Z.dispose(),ce.forEach(e=>e.cleanup()),ce.clear(),Z.destroy(),G.destroy(),ve.forEach(e=>e.cleanup()),ve.clear()});let Q=(e,r=!1)=>{if(V.isDestroyed)return;if(Y.clear(),e===N){e===null?Fe():J();return}if(!r&&e!==null&&e===P)return;J(),P=e===null?null:e;let i=()=>{let r=N,i=e?K.get(e):null,a=e!==null,o=i?null:G.baseline(),s=r===null&&a,c=r!==null&&e!==null&&r!==e,l=c&&i?ze(i.index):null,d=document.activeElement;if(e===null&&d&&r){let e=K.get(r);e&&n(e.content,d)&&e.trigger.focus()}let f=r?K.get(r):null;if(f){let{trigger:e,content:t,item:n}=f;S(e,`expanded`,!1),e.removeAttribute(`aria-controls`),e.setAttribute(`data-state`,`closed`),n.setAttribute(`data-state`,`closed`),ue(t,!1),t.setAttribute(`aria-hidden`,`true`),E(t,!0),t.style.pointerEvents=`none`,de(t,l),l?t.setAttribute(`data-motion`,l===`right`?`to-left`:`to-right`):t.removeAttribute(`data-motion`),Z.setAbsolute(t,!0),ce.get(t)?.exit()}if(i){if(i.trigger.setAttribute(`data-state`,`open`),i.item.setAttribute(`data-state`,`open`),ve.get(i.content)?.mount(),G.prepareOpen(),j&&s&&(W(t,!0),W(me(),!0),W(pe(),!0),W(j,!0),j.style.pointerEvents=`auto`,Z.beginInitialOpen()),G.reveal(r===null),Z.mount(i.content),i.trigger.setAttribute(`aria-controls`,i.content.id),S(i.trigger,`expanded`,!0),ce.get(i.content)?.enter(),Z.start(),l){de(i.content,l);let e=l===`right`?`from-right`:`from-left`;i.content.setAttribute(`data-motion`,e)}else de(i.content,null),i.content.removeAttribute(`data-motion`);ue(i.content,!0),Z.setAbsolute(i.content,!1),i.content.removeAttribute(`aria-hidden`),E(i.content,!1),i.content.hidden=!1,i.content.style.pointerEvents=`auto`,ae=i.index,c&&Z.clearInstant(),Z.sync(i,{defer:s||c?!1:void 0,mode:s?`initial-open`:`measure-target`}),Z.observe(i),X(i.trigger)}else se(),X(null),Z.stop(),Y.hideBridge(),Y.clear(),G.close(o),Z.observe(null);if(W(t,a),l?t.setAttribute(`data-motion`,l===`right`?`from-right`:`from-left`):t.removeAttribute(`data-motion`),j){let e=pe();W(me(),a),W(e,a),W(j,a),j.style.pointerEvents=a?`auto`:`none`,Z.commitInstant({open:a,initial:s,switching:c}),l?j.style.setProperty(`--motion-direction`,l===`right`?`1`:`-1`):j.style.removeProperty(`--motion-direction`)}N=e,P=null,e===null&&X(null),Y.refreshDebug(),u(t,`navigation-menu:change`,{value:e}),te?.(e)};r?i():e!==null&&N===null?F=V.trackTimeout(i,d):e!==null&&N!==null?i():oe=V.trackTimeout(i,h)},He={popup:G.popup,positioner:G.positioner};W(t,!1),j&&(W(He.positioner,!1),W(He.popup,!1),W(j,!1),j.hidden=!0,j.style.pointerEvents=`none`,He.popup&&(He.popup.hidden=!0,He.popup.style.pointerEvents=`none`)),M&&M.setAttribute(`data-state`,`hidden`),K.forEach(({trigger:e,content:t,item:n})=>{e.tagName===`BUTTON`&&!e.hasAttribute(`type`)&&(e.type=`button`),S(e,`expanded`,!1),e.setAttribute(`data-state`,`closed`),e.tabIndex=0,n.setAttribute(`data-state`,`closed`),ue(t,!1),t.setAttribute(`aria-hidden`,`true`),t.tabIndex=-1,E(t,!0),t.hidden=!0,t.style.pointerEvents=`none`,ve.get(t)?.unmount()}),K.forEach(({item:e,trigger:r},i)=>{U.push(y(r,`pointerenter`,e=>{if(!L){if(N!==i&&Y.inCorridor(e))return;X(r)}})),U.push(y(e,`pointerenter`,e=>{if(!L){if(N!==i&&Y.inCorridor(e))return;Q(i)}})),U.push(y(e,`pointerleave`,e=>{if(P===i&&N===null&&Fe(),N===i&&!L){let r=e.relatedTarget;if(Y.contains(r))return;(!r||!n(t,r))&&(Q(null),X(null))}})),U.push(y(r,`focus`,()=>{if(R===r){R=null;return}_&&Q(i,!0),Ve(r)})),U.push(y(r,`pointerdown`,()=>{R=r,z=r}),y(r,`keydown`,()=>{z=null})),U.push(y(r,`click`,()=>{let e=z===r;z=null,R=null,Fe(),N===i&&L?(L=!1,Q(null,!0),X(null)):N===i&&!L?(L=!0,X(r),e||Pe(i)):(L=!0,Q(i,!0),e||Pe(i))}))}),U.push(y(D,`pointerover`,e=>{let t=e;if(t.pointerType===`touch`)return;let n=Se(t.target);if(n){if(B=!0,L||N!==n.value&&Y.inCorridor(t))return;X(n.trigger),Q(n.value);return}let r=xe(t.target);if(r){if(B=!0,N!==null){if(L)return;$()}X(r.element);return}N!==null&&(L||Ae(t.target)&&$())}),y(D,`click`,e=>{N!==null&&Ae(e.target)&&$()})),U.push(y(D,`focusin`,e=>{let t=xe(e.target);if(t){if(N!==null){Ve();return}X(t.element)}}),y(t,`pointerenter`,()=>{B=!0}),y(t,`pointerleave`,e=>{let t=e.relatedTarget;if(!Y.contains(t)&&(B=!1,!L)){if(Y.inCorridor(e)){J();return}Q(null),X(null)}}),y(t,`pointerdown`,()=>{Y.clear(),Fe()}));let Ue=()=>{if(!Ce()||N!==null||P!==null||L)return;let e=Te();if(B=e!==null||t instanceof HTMLElement&&we(t),e){if(e.kind===`submenu`){Q(e.value,!0);return}X(e.element)}};j&&U.push(y(j,`pointerenter`,()=>{Y.clear(),J()}),y(j,`transitionend`,e=>{if(e.target!==j)return;let t=N?K.get(N):null;t&&Z.sync(t,{defer:!1,mode:`sync-current`})})),K.forEach(({content:e})=>{U.push(y(e,`pointerenter`,()=>{Y.clear(),J()}),y(e,`pointerleave`,e=>{if(L)return;let r=e.relatedTarget;Y.contains(r)||n(t,r)||(Q(null),X(null))}))}),U.push(y(D,`keydown`,e=>{let t=be(e.target);if(!t)return;let n=q.indexOf(t);if(n===-1)return;let r=n;switch(e.key){case`Tab`:if(e.shiftKey||N===null)return;(Oe(t,1,{preserveOpenOnPlain:!0})||Ne())&&e.preventDefault();return;case`ArrowLeft`:r=n-1,r<0&&(r=q.length-1);break;case`ArrowRight`:r=n+1,r>=q.length&&(r=0);break;case`ArrowDown`:if(t.kind===`submenu`){e.preventDefault();let n=t.value;L=!0,Q(n,!0),Pe(n)}return;case`Home`:r=0;break;case`End`:r=q.length-1;break;case`Escape`:L=!1,Q(null,!0),X(null);return;default:return}e.preventDefault();let i=q[r];i&&Ee(i,{preserveOpenOnPlain:!0})})),K.forEach(({content:e,trigger:t})=>{U.push(y(e,`keydown`,n=>{if(!/^(Tab|Arrow(Up|Down|Left|Right)|Escape)$/.test(n.key))return;let r=n.target,i=je(e),a=i.indexOf(r);if(a!==-1)switch(n.key){case`Tab`:if(!n.shiftKey&&a===i.length-1){(ke(t,1,{preserveOpenOnPlain:!0})||Ne())&&n.preventDefault();return}if(n.shiftKey&&a===0){n.preventDefault(),t.focus();return}return;case`ArrowDown`:case`ArrowRight`:{n.preventDefault();let e=a+1;en(t,document.activeElement)||B||L,$=()=>{Y.clear(),Fe(),Ie(),L=!1,Q(null,!0),X(null),Y.refreshDebug()};U.push(y(document,`focusin`,e=>{let r=e.target;if(!n(t,r)){if(N!==null){$();return}X(null)}})),U.push(i({root:t,isOpen:()=>N!==null&&We(),onDismiss:$,closeOnClickOutside:!0,closeOnEscape:!0,preventEscapeDefault:!1,isInside:e=>!!e&&n(t,e)})),U.push(y(window,`resize`,()=>{(N||I)&&(se(),H=V.trackRaf(()=>{H=null,V.isDestroyed||Ve(I)}))}),y(D,`scroll`,()=>{(N||I)&&(se(),H=V.trackRaf(()=>{H=null,V.isDestroyed||Ve(I)}))})),U.push(b(t,`navigation-menu:set`,e=>{let t=e.detail;if(t?.value!==void 0){if(t.value===null)$();else if(K.has(t.value)){L=!0,Q(t.value,!0);let e=K.get(t.value);e&&X(e.trigger)}}}));let Ge={get value(){return N},open:e=>{V.isDestroyed||Q(e,!0)},close:()=>{V.isDestroyed||$()},destroy:()=>{V.destroy()&&(Fe(),Ie(),se(),X(null),K.forEach(({trigger:e,content:t,item:n})=>{S(e,`expanded`,!1),e.removeAttribute(`aria-controls`),e.setAttribute(`data-state`,`closed`),n.setAttribute(`data-state`,`closed`),ue(t,!1),t.setAttribute(`aria-hidden`,`true`),E(t,!0),t.hidden=!0,t.style.pointerEvents=`none`}),N=null,e(t,ie,Ge))}};return Ue(),C(t,ie,Ge),Ge}function ae(e=document){let t=[];for(let n of _(e,`navigation-menu`))te(n,ie)||t.push(P(n));return t}function F(e,t={}){return P(e,t)}export{ae as create,F as createNavigationMenu};", + "type": "registry:file", + "target": "~/assets/data-slot-navigation-menu.js" + }, + { + "path": "vendor/data-slot-accordion.js", + "content": "import{clearRootBinding as e,createPresenceLifecycle as t,emit as n,ensureId as r,getDataBool as i,getDataEnum as a,getDataString as o,getOwnedElements as s,getParts as c,getRoots as l,hasRootBinding as u,on as d,onRoot as f,reuseRootBinding as p,setAria as m,setRootBinding as ee}from\"@data-slot/core\";const te=[`horizontal`,`vertical`],h=new Set([`all`,`height`,`width`,`block-size`,`inline-size`]),ne=new Set([`Enter`,` `]),g=e=>!!e&&(e.hasAttribute(`disabled`)||e.hasAttribute(`data-disabled`)||e.getAttribute(`aria-disabled`)===`true`),_=(e,t,n)=>{n?e.setAttribute(t,``):e.removeAttribute(t)},v=(e,t)=>{e.setAttribute(`data-state`,t?`open`:`closed`),t?(e.setAttribute(`data-open`,``),e.removeAttribute(`data-closed`)):(e.setAttribute(`data-closed`,``),e.removeAttribute(`data-open`))},y=e=>{let t=e.trim();return t?t.endsWith(`ms`)?Number.parseFloat(t.slice(0,-2))||0:t.endsWith(`s`)?(Number.parseFloat(t.slice(0,-1))||0)*1e3:Number.parseFloat(t)||0:0},b=(e,t)=>{let n=e.split(`,`),r=t.split(`,`),i=Math.max(n.length,r.length),a=0;for(let e=0;e{let t=b(e.transitionDuration,e.transitionDelay),n=b(e.animationDuration,e.animationDelay);return Math.max(t,n)},ie=e=>b(e.animationDuration,e.animationDelay)<=0?!1:e.animationName.split(`,`).map(e=>e.trim()).some(e=>e!==``&&e!==`none`),x=(e,t)=>{let n=e.transitionProperty.split(`,`).map(e=>e.trim()),r=e.transitionDuration.split(`,`),i=e.transitionDelay.split(`,`),a=Math.max(n.length,r.length,i.length),o=0;for(let e=0;ex(e,e=>h.has(e)),ae=e=>{if(e===void 0)return;let t=e.trim();if(!t.startsWith(`[`)||!t.endsWith(`]`))return e;try{let n=JSON.parse(t);return Array.isArray(n)?n.filter(e=>typeof e==`string`):e}catch{return e}},C=`@data-slot/accordion`;function w(l,u={}){let y=p(l,C,`[@data-slot/accordion] createAccordion() called more than once for the same root. Returning the existing controller. Destroy it before rebinding with new options.`);if(y)return y;let b=l,x=c(l,`accordion-item`);if(x.length===0)throw Error(`Accordion requires at least one accordion-item`);let w=l.ownerDocument?.defaultView??window,T=u.multiple??i(l,`multiple`)??!1,oe=u.onValueChange,E=u.disabled??i(l,`disabled`)??g(l),D=u.orientation??a(l,`orientation`,te)??`vertical`,O=u.loopFocus??i(l,`loopFocus`)??!0,k=u.hiddenUntilFound??i(l,`hiddenUntilFound`)??!1,se=u.collapsible??i(l,`collapsible`)??!0,A=[],j=[],M=(e,t,n)=>{e.content.style.setProperty(`--accordion-panel-height`,t),e.content.style.setProperty(`--accordion-panel-width`,n),e.content.style.setProperty(`--radix-accordion-content-height`,t),e.content.style.setProperty(`--radix-accordion-content-width`,n)},N=(e,t,n)=>{M(e,`${t}px`,`${n}px`)},P=e=>{M(e,`auto`,`auto`)},F=e=>{N(e,0,0)},I=e=>{N(e,e.content.scrollHeight,e.content.scrollWidth)},L=e=>{let t=e.content.style.getPropertyValue(`--accordion-panel-height`).trim(),n=e.content.style.getPropertyValue(`--accordion-panel-width`).trim();return t===`auto`&&n===`auto`},R=e=>{e.suppressClick=!1,e.suppressClickTimeoutId!==null&&(w.clearTimeout(e.suppressClickTimeoutId),e.suppressClickTimeoutId=null)},z=e=>{e.openSettleRafId!==null&&(w.cancelAnimationFrame(e.openSettleRafId),e.openSettleRafId=null),e.openSettleTimeoutId!==null&&(w.clearTimeout(e.openSettleTimeoutId),e.openSettleTimeoutId=null),e.openSettleCleanups.forEach(e=>e()),e.openSettleCleanups=[]},B=e=>{e.closeZeroRafId!==null&&(w.cancelAnimationFrame(e.closeZeroRafId),e.closeZeroRafId=null)},V=e=>{z(e),B(e)},H=e=>S(e)>0?`css-transition`:ie(e)?`css-animation`:`none`,U=(e,t)=>{let n=e.content.style.getPropertyValue(`animation-name`);e.content.style.setProperty(`animation-name`,`none`);try{P(e),I(e),t?.()}finally{n?e.content.style.setProperty(`animation-name`,n):e.content.style.removeProperty(`animation-name`)}},ce=e=>{e.idleAnimationSuppressed||(e.idleAnimationName=e.content.style.getPropertyValue(`animation-name`)||null,e.idleAnimationSuppressed=!0,e.content.style.setProperty(`animation-name`,`none`))},le=e=>{e.idleAnimationSuppressed&&=(e.idleAnimationName?e.content.style.setProperty(`animation-name`,e.idleAnimationName):e.content.style.removeProperty(`animation-name`),e.idleAnimationName=null,!1)},W=e=>{e.content.removeAttribute(`hidden`)},G=e=>{k?e.content.setAttribute(`hidden`,`until-found`):e.content.hidden=!0,F(e)},K=new Set,q=(e,t)=>{z(e),!(!K.has(e.value)||e.presence.isExiting)&&(P(e),t===`css-animation`&&ce(e))},J=(e,t,n)=>{z(e);let r=re(n),i=S(n),a=i||r;if(a>0){let n=typeof w.performance?.now==`function`?w.performance.now():Date.now(),r=e=>(typeof w.performance?.now==`function`?w.performance.now():Date.now())-n>=Math.max(0,e-5),o=n=>{if(n.target!==e.content)return;let o=`propertyName`in n?String(n.propertyName):``;if(i>0){if(!h.has(o)||!r(i))return}else if(!r(a))return;q(e,t)},s=n=>{n.target===e.content&&(i>0||r(a)&&q(e,t))};e.content.addEventListener(`transitionend`,o),e.content.addEventListener(`animationend`,s),e.openSettleCleanups.push(()=>e.content.removeEventListener(`transitionend`,o)),e.openSettleCleanups.push(()=>e.content.removeEventListener(`animationend`,s)),e.openSettleTimeoutId=w.setTimeout(()=>{e.openSettleTimeoutId=null,q(e,t)},Math.ceil(a)+50);return}e.openSettleRafId=w.requestAnimationFrame(()=>{e.openSettleRafId=null,q(e,t)})},ue=e=>{B(e),e.closeZeroRafId=w.requestAnimationFrame(()=>{e.closeZeroRafId=null,!K.has(e.value)&&e.presence.isExiting&&F(e)})},de=e=>{e.el.setAttribute(`data-index`,String(e.index)),e.content.setAttribute(`data-index`,String(e.index)),e.content.setAttribute(`data-orientation`,D),_(e.el,`data-disabled`,e.disabled),_(e.trigger,`data-disabled`,e.disabled),_(e.content,`data-disabled`,e.disabled),e.disabled?(e.trigger.setAttribute(`aria-disabled`,`true`),e.trigger instanceof HTMLButtonElement&&(e.trigger.disabled=!0)):(e.trigger.removeAttribute(`aria-disabled`),e.trigger instanceof HTMLButtonElement&&(e.trigger.disabled=!1))},Y=(e,t)=>{e.trigger.setAttribute(`data-state`,t?`open`:`closed`),_(e.trigger,`data-panel-open`,t)},fe=e=>{let t=K.has(e.value);if(de(e),m(e.trigger,`expanded`,t),v(e.el,t),v(e.content,t),Y(e,t),e.content.removeAttribute(`data-starting-style`),e.content.removeAttribute(`data-ending-style`),t){W(e);let t=getComputedStyle(e.content),n=H(t);n===`css-animation`?U(e):I(e),J(e,n,t)}else G(e)},pe=(e,t)=>{let n=K.has(e.value),r=e.trigger.getAttribute(`aria-expanded`)===`true`;if(!(n===r&&(n?L(e):!e.presence.isExiting))){if(m(e.trigger,`expanded`,n),v(e.el,n),v(e.content,n),Y(e,n),n){B(e),W(e);let t=getComputedStyle(e.content),n=H(t);n===`css-animation`?U(e,()=>{r||e.presence.enter()}):(I(e),r||e.presence.enter()),J(e,n,t);return}if(r){z(e),H(getComputedStyle(e.content))===`css-animation`?U(e,()=>e.presence.exit()):(N(e,...t),e.presence.exit(),ue(e));return}V(e),e.content.removeAttribute(`data-starting-style`),e.content.removeAttribute(`data-ending-style`),G(e)}},me=e=>{let t=[],n=new Set;for(let r of e)if(!(n.has(r)||!x.some(e=>e.dataset.value===r))&&(n.add(r),t.push(r),!T&&t.length===1))break;return t},he=e=>e.size===K.size?[...e].some(e=>!K.has(e)):!0,ge=()=>{let e=[...K];n(l,`accordion:change`,{value:e}),oe?.(e)},X=e=>{let t=me(e);if(!T&&!se&&t.length===0&&K.size>0)return!1;let n=new Set(t);if(!he(n))return!1;let r=j.filter(e=>K.has(e.value)!==n.has(e.value)),i=new Map;for(let e of r)K.has(e.value)&&i.set(e,[e.content.scrollHeight,e.content.scrollWidth]);return r.forEach(le),K=n,j.forEach(e=>pe(e,i.get(e))),ge(),!0},_e=e=>(e.getAttribute(`dir`)??b.getAttribute(`dir`))===`rtl`||(getComputedStyle(e).direction||getComputedStyle(b).direction||l.ownerDocument?.documentElement.getAttribute(`dir`)||``)===`rtl`?`rtl`:`ltr`;b.setAttribute(`data-orientation`,D),_(b,`data-disabled`,!!E),x.forEach((e,n)=>{let i=e.dataset.value;if(!i)return;let a=s(l,e,`[data-slot=\"accordion-trigger\"]`)[0]??null,o=s(l,e,`[data-slot=\"accordion-content\"]`)[0]??null;if(!a||!o)return;let c=r(o,`accordion-content`),u=r(a,`accordion-trigger`);a.setAttribute(`aria-controls`,c),o.setAttribute(`aria-labelledby`,u),o.setAttribute(`role`,`region`);let f=!!E||g(e)||g(a),p;p={el:e,value:i,index:n,disabled:f,trigger:a,content:o,presence:t({element:o,onExitComplete:()=>{B(p),G(p)}}),sizeObserver:null,idleAnimationName:null,idleAnimationSuppressed:!1,openSettleRafId:null,openSettleTimeoutId:null,closeZeroRafId:null,openSettleCleanups:[],suppressClick:!1,suppressClickTimeoutId:null},typeof ResizeObserver<`u`&&(p.sizeObserver=new ResizeObserver(()=>{!K.has(p.value)||p.presence.isExiting||L(p)||I(p)}),p.sizeObserver.observe(o)),A.push(d(a,`click`,()=>{if(p.suppressClick){R(p);return}p.disabled||(K.has(p.value)?X([...K].filter(e=>e!==p.value)):X(T?[...K,p.value]:[p.value]))})),A.push(d(a,`keydown`,e=>{if(ne.has(e.key)){if(p.disabled){e.preventDefault();return}e.preventDefault(),R(p),p.suppressClick=!0,p.suppressClickTimeoutId=w.setTimeout(()=>{p.suppressClick=!1,p.suppressClickTimeoutId=null},0),K.has(p.value)?X([...K].filter(e=>e!==p.value)):X(T?[...K,p.value]:[p.value])}})),k&&A.push(d(o,`beforematch`,()=>{X(T?[...K,p.value]:[p.value])})),j.push(p)});let Z=new Set(j.map(e=>e.value)),Q=u.defaultValue??ae(o(l,`defaultValue`)),ve=me((Q?Array.isArray(Q)?Q:[Q]:[]).filter(e=>Z.has(e)));K=new Set(ve),j.forEach(fe),A.push(d(b,`keydown`,e=>{let t=e.target;if(!t)return;let n=j.find(e=>e.trigger===t);if(!n)return;let r=j.filter(e=>!e.disabled),i=r.findIndex(e=>e.trigger===t);if(i===-1)return;let a=r.length-1,o=-1,s=()=>{o=O?i+1>a?0:i+1:Math.min(i+1,a)},c=()=>{o=O?i===0?a:i-1:Math.max(i-1,0)};switch(e.key){case`ArrowDown`:D===`vertical`&&s();break;case`ArrowUp`:D===`vertical`&&c();break;case`ArrowRight`:D===`horizontal`&&(_e(n.trigger)===`rtl`?c():s());break;case`ArrowLeft`:D===`horizontal`&&(_e(n.trigger)===`rtl`?s():c());break;case`Home`:o=0;break;case`End`:o=a;break;default:return}o<0||(e.preventDefault(),r[o]?.trigger.focus())})),A.push(f(l,`accordion:set`,e=>{let t=e.detail?.value;t!==void 0&&X(Array.isArray(t)?t:[t])}));let $={expand:e=>{!Z.has(e)||K.has(e)||X(T?[...K,e]:[e])},collapse:e=>{!Z.has(e)||!K.has(e)||X([...K].filter(t=>t!==e))},toggle:e=>{Z.has(e)&&(K.has(e)?$.collapse(e):$.expand(e))},get value(){return[...K]},destroy:()=>{j.forEach(e=>{R(e),e.presence.cleanup(),V(e),e.sizeObserver?.disconnect(),e.sizeObserver=null}),A.forEach(e=>e()),A.length=0,e(l,C,$)}};return ee(l,C,$),$}function T(e=document){let t=[];for(let n of l(e,`accordion`))u(n,C)||t.push(w(n));return t}export{T as create,w as createAccordion};", + "type": "registry:file", + "target": "~/assets/data-slot-accordion.js" + }, + { + "path": "vendor/data-slot-tabs.js", + "content": "import{clearRootBinding as e,emit as t,ensureId as n,getDataEnum as r,getDataString as i,getFocusable as a,getPart as o,getParts as s,getRoots as c,hasRootBinding as l,on as u,onRoot as d,reuseRootBinding as f,setAria as p,setRootBinding as m}from\"@data-slot/core\";const h=[`horizontal`,`vertical`],g=[`auto`,`manual`],_=`@data-slot/tabs`;function v(c,l={}){let v=f(c,_,`[@data-slot/tabs] createTabs() called more than once for the same root. Returning the existing controller. Destroy it before rebinding with new options.`);if(v)return v;let y=o(c,`tabs-list`),b=s(c,`tabs-trigger`),x=s(c,`tabs-content`),S=o(c,`tabs-indicator`);if(!y||b.length===0)throw Error(`Tabs requires tabs-list and at least one tabs-trigger`);let C=l.onValueChange,w=l.orientation??r(c,`orientation`,h)??`horizontal`,T=l.activationMode??r(c,`activationMode`,g)??`auto`,E=new Map;for(let e of x){let t=(e.dataset.value||``).trim();t&&E.set(t,e)}let D=[],O=new Map,k=new Map;for(let e of b){let t=(e.dataset.value||``).trim();if(!t)continue;let n={el:e,value:t,disabled:e.hasAttribute(`disabled`)||e.dataset.disabled!==void 0||e.getAttribute(`aria-disabled`)===`true`,panel:E.get(t)};D.push(n),O.set(t,n),k.set(e,n)}let A=D.filter(e=>!e.disabled),j=new Map;A.forEach((e,t)=>j.set(e.value,t));let M=A[0]?.value||``,N=(l.defaultValue??i(c,`defaultValue`)??``).trim(),P=O.get(N),F=P&&!P.disabled?N:M,I=[],L=null,R=null;y.setAttribute(`role`,`tablist`),w===`vertical`&&p(y,`orientation`,`vertical`);for(let e of D){let{el:t,disabled:r,panel:i}=e;t.setAttribute(`role`,`tab`);let a=n(t,`tab`);if(t.tagName===`BUTTON`&&!t.hasAttribute(`type`)&&(t.type=`button`),r&&(t.setAttribute(`aria-disabled`,`true`),t.tagName===`BUTTON`&&(t.disabled=!0)),i){i.setAttribute(`role`,`tabpanel`),i.tabIndex=-1;let e=n(i,`tabpanel`);t.setAttribute(`aria-controls`,e),i.setAttribute(`aria-labelledby`,a)}}let z=e=>{for(let[t,n]of Object.entries(e))S.style.setProperty(`--active-tab-${t}`,`${n}px`)},B=e=>{let t=0,n=0,r=e;for(;r&&r!==y;){t+=r.offsetLeft,n+=r.offsetTop;let e=r.offsetParent;if(!(e instanceof HTMLElement))return null;e!==y&&(t-=e.scrollLeft,n-=e.scrollTop),r=e}if(r!==y)return null;let i={left:t-y.clientLeft,top:n-y.clientTop,width:e.offsetWidth,height:e.offsetHeight},a=V(e);return a&&(w===`horizontal`?i.top=Math.floor(a.top):i.left=Math.floor(a.left)),i},V=e=>{let t=y.getBoundingClientRect(),n=e.getBoundingClientRect();if(t.width===0&&t.height===0||n.width===0&&n.height===0)return null;let r=(e,t)=>{if(t<=0)return 1;let n=e/t;return Math.abs(n-1)<.05?1:n},i=r(n.width,e.offsetWidth),a=r(n.height,e.offsetHeight);return{left:(n.left-t.left-y.clientLeft+y.scrollLeft)/i,top:(n.top-t.top-y.clientTop+y.scrollTop)/a,width:e.offsetWidth,height:e.offsetHeight}},H=()=>{if(!S)return;let e=O.get(F);z(e&&(B(e.el)??V(e.el))||{left:0,top:0,width:0,height:0})},U=()=>{!S||L!==null||(L=requestAnimationFrame(()=>{L=null,H()}))},W=(e,t)=>{let n=D.findIndex(t=>t.value===e),r=D.findIndex(e=>e.value===t);return n<0||r<0||n===r?null:w===`vertical`?r>n?`down`:`up`:r>n?`right`:`left`},G=(e,n=!1)=>{if(e=e.trim(),F===e&&!n)return;let r=O.get(e);if(!r||r.disabled)if(n){if(e=M,!e)return}else return;let i=F,a=n?null:W(i,e),o=n||a!==R;R=a,F=e;for(let t of D){let r=t.value===e;!n&&!r&&t.value!==i||(p(t.el,`selected`,r),t.el.tabIndex=r&&!t.disabled?0:-1,t.el.dataset.state=r?`active`:`inactive`)}for(let t of x){let r=(t.dataset.value||``).trim();if(!r)continue;let s=r===e;(n||s||r===i)&&(t.hidden=!s,t.dataset.state=s?`active`:`inactive`),o&&(a?t.dataset.activationDirection=a:delete t.dataset.activationDirection)}c.setAttribute(`data-value`,e),U(),n||(t(c,`tabs:change`,{value:e}),C?.(e))};if(G(F,!0),S){let e=()=>U();I.push(u(window,`resize`,e)),I.push(u(y,`scroll`,e));let t=new ResizeObserver(e);t.observe(y),I.push(()=>t.disconnect()),I.push(()=>{L!==null&&(cancelAnimationFrame(L),L=null)})}I.push(u(y,`click`,e=>{let t=e.target.closest?.(`[data-slot=\"tabs-trigger\"]`);if(!t)return;let n=k.get(t);n&&!n.disabled&&G(n.value)}));let K=w===`horizontal`,q=K?`ArrowLeft`:`ArrowUp`,J=K?`ArrowRight`:`ArrowDown`;I.push(u(y,`keydown`,e=>{let t=e.target.closest?.(`[data-slot=\"tabs-trigger\"]`);if(!t)return;let n=k.get(t);if(!n||A.length===0)return;if(e.key===`Enter`||e.key===` `){e.preventDefault(),n.disabled||G(n.value);return}if(K&&e.key===`ArrowDown`&&n.value===F){let t=n.panel;if(t){e.preventDefault(),(a(t)[0]||t).focus();return}}let r=j.get(n.value)??-1;r===-1&&(r=j.get(F)??0);let i=r;switch(e.key){case q:i=r-1,i<0&&(i=A.length-1);break;case J:i=r+1,i>=A.length&&(i=0);break;case`Home`:i=0;break;case`End`:i=A.length-1;break;default:return}e.preventDefault();let o=A[i];o&&(o.el.focus(),T===`auto`&&G(o.value))}));let Y=e=>{let t=e,n=e.currentTarget,r=t.detail,i=(typeof r==`string`?r:r?.value??n?.dataset?.value)?.trim();i&&G(i)};I.push(d(c,`tabs:set`,Y)),I.push(d(c,`tabs:select`,Y));let X={select:e=>G(e),get value(){return F},updateIndicator:H,destroy:()=>{I.forEach(e=>e()),I.length=0,e(c,_,X)}};return m(c,_,X),X}function y(e=document){let t=[];for(let n of c(e,`tabs`))l(n,_)||t.push(v(n));return t}export{y as create,v as createTabs};", + "type": "registry:file", + "target": "~/assets/data-slot-tabs.js" + }, + { + "path": "vendor/data-slot-combobox.js", + "content": "import{clearRootBinding as e,computeFloatingPosition as t,computeFloatingTransformOrigin as n,containsWithPortals as r,createDismissLayer as i,createFormFieldAdapter as a,createPortalLifecycle as o,createPositionSync as s,createPresenceLifecycle as c,createTerminalLifecycle as l,emit as u,ensureId as d,ensureItemVisibleInContainer as f,getDataBool as p,getDataEnum as m,getDataNumber as h,getDataString as g,getOwnedElements as _,getPart as v,getRoots as y,hasRootBinding as b,measurePopupContentRect as ee,on as x,onRoot as te,registerFloatingTerminalResources as ne,reuseRootBinding as S,setAria as C,setRootBinding as w}from\"@data-slot/core\";const T=[`top`,`bottom`],E=[`start`,`center`,`end`];function re(e,t,n,r,i){let a=(t,i)=>m(n,t,i)??(r?m(r,t,i):void 0)??m(e,t,i),o=t=>h(n,t)??(r?h(r,t):void 0)??h(e,t);return{defaultValue:i.defaultValue??g(e,`defaultValue`)??null,defaultOpen:i.defaultOpen??p(e,`defaultOpen`)??!1,placeholder:i.placeholder??g(e,`placeholder`)??``,disabled:i.disabled??p(e,`disabled`)??!1,required:i.required??p(e,`required`)??!1,name:i.name??g(e,`name`)??t.getAttribute(`name`)??null,openOnFocus:i.openOnFocus??p(e,`openOnFocus`)??!0,autoHighlight:i.autoHighlight??p(e,`autoHighlight`)??!1,customFilter:i.filter??null,onValueChange:i.onValueChange,onOpenChange:i.onOpenChange,onInputValueChange:i.onInputValueChange,preferredSide:i.side??a(`side`,T)??`bottom`,preferredAlign:i.align??a(`align`,E)??`start`,sideOffset:i.sideOffset??o(`sideOffset`)??4,alignOffset:i.alignOffset??o(`alignOffset`)??0,avoidCollisions:i.avoidCollisions??(t=>p(n,t)??(r?p(r,t):void 0)??p(e,t))(`avoidCollisions`)??!0,collisionPadding:i.collisionPadding??o(`collisionPadding`)??8}}function ie({root:e,container:t,input:n,emptySlot:r,filter:i,itemToStringValue:a}){let o=v(e,`combobox-content`)??t,s=[],c=[],l=[],u=[],p=[],m=new Map,h=-1,g=a,y=(e,t)=>{e.hidden!==t&&(e.hidden=t)},b=e=>e.hasAttribute(`disabled`)||e.hasAttribute(`data-disabled`)||e.getAttribute(`aria-disabled`)===`true`,ee=e=>{if(e.dataset.label)return e.dataset.label;let t=``;for(let n of e.childNodes)n.nodeType===Node.TEXT_NODE&&(t+=n.textContent);return t.trim()||e.textContent?.trim()||``},x=e=>e.hasAttribute(`data-value`)?e.getAttribute(`data-value`):void 0,te=n=>n===null?null:_(e,t,`[data-slot=\"combobox-item\"]`).find(e=>x(e)===n)??null,ne=e=>{let t=te(e);return g?g(t,e):t?ee(t):``},S=(n,r)=>{for(let e of n){let t=x(e)===r;C(e,`selected`,t),e.toggleAttribute(`data-selected`,t)}for(let n of _(e,t,`[data-slot=\"combobox-item-indicator\"]`)){let e=n.closest(`[data-slot=\"combobox-item\"]`);e&&(n.hidden=!e.hasAttribute(`data-selected`))}},w=()=>{p[h]?.removeAttribute(`data-highlighted`),h=-1,n.removeAttribute(`aria-activedescendant`)},T=()=>{w(),p=s.filter(e=>!e.hidden&&!b(e)),m=new Map(p.map((e,t)=>[e,t]))},E=()=>{if(u.length===0)return;for(let e of u)e.hidden=!0;let e=Array.from(t.children).filter(e=>e instanceof HTMLElement);for(let t=0;t{let r=n===``||i(n,x(e)??``,c[t]);y(e,!r),r&&a++});for(let t of l)y(t,_(e,t,`[data-slot=\"combobox-item\"]`).every(e=>e.hidden));E(),r&&y(r,a>0),o.toggleAttribute(`data-empty`,a===0),T()},select(n){S(s.length>0?s:_(e,t,`[data-slot=\"combobox-item\"]`),n)},highlight(e){let r=p[e];w(),r&&(r.setAttribute(`data-highlighted`,``),n.setAttribute(`aria-activedescendant`,r.id),f(r,t.contains(r)&&t.scrollHeight>t.clientHeight?t:o),h=e)},clearHighlight:w,setItemToStringValue(e){g=e},get enabled(){return p},get highlightedIndex(){return h},indexOf(e){return m.get(e)},isDisabled:b,valueOf:x,labelFor:ne}}function ae(e,t){return Array.from(y(e,`combobox`)).filter(e=>!b(e,`@data-slot/combobox`)).map(t)}const oe=`@data-slot/combobox`,se=[`top`,`bottom`];function D(f,p={}){let m=S(f,oe,`[@data-slot/combobox] createCombobox() called more than once for the same root. Returning the existing controller. Destroy it before rebinding with new options.`);if(m)return m;let h=v(f,`combobox-input`),g=v(f,`combobox-content`),y=v(f,`combobox-list`)??_(f,g??f,`[data-slot=\"combobox-list\"]`)[0]??null,b=v(f,`combobox-trigger`),T=v(f,`combobox-clear`),E=v(f,`combobox-value`),ae=_(f,y??g??f,`[data-slot=\"combobox-empty\"]`)[0]??null,D=v(f,`combobox-positioner`),O=D&&g&&D.contains(g)?D:null,ce=v(f,`combobox-portal`),le=ce&&O&&ce.contains(O)?ce:null;if(!h||!g)throw Error(`Combobox requires combobox-input and combobox-content slots`);let k=g.contains(h),ue=E?.textContent?.trim()??``,{defaultValue:de,defaultOpen:fe,placeholder:A,disabled:j,required:pe,name:me,openOnFocus:he,autoHighlight:ge,customFilter:_e,onValueChange:ve,onOpenChange:ye,onInputValueChange:be,preferredSide:xe,preferredAlign:Se,sideOffset:M,alignOffset:Ce,avoidCollisions:we,collisionPadding:N}=re(f,h,g,O,p),P=p.itemToStringValue??null,F=!1,I=de,L=!1,R=null,z=[],B=f.ownerDocument??document,V=B.defaultView??window,Te=f,Ee=-1/0,H=!1,U=!1,De=null,W=o({content:g,root:f,wrapperSlot:O?void 0:`combobox-positioner`,container:O??void 0,mountTarget:O?le??O:void 0}),G=l(),Oe=e=>!!V.matchMedia?.(e).matches,K=Oe(`(pointer: coarse)`)||V.navigator.maxTouchPoints>0&&Oe(`(hover: none)`),ke=d(h,`combobox-input`),Ae=y??g,je=d(Ae,`combobox-list`);h.setAttribute(`role`,`combobox`),h.setAttribute(`aria-autocomplete`,`list`),h.setAttribute(`autocomplete`,`off`),h.setAttribute(`aria-controls`,je),Ae.setAttribute(`role`,`listbox`),b&&(b.hasAttribute(`type`)||b.setAttribute(`type`,`button`),b.hasAttribute(`tabindex`)||(b.tabIndex=-1),b.setAttribute(`aria-label`,`Toggle`)),T instanceof HTMLButtonElement&&!T.hasAttribute(`type`)&&T.setAttribute(`type`,`button`),T&&!T.hasAttribute(`tabindex`)&&(T.tabIndex=-1);let Me=document.querySelector(`label[for=\"${CSS.escape(ke)}\"]`);if(Me){let e=d(Me,`combobox-label`),t=h.getAttribute(`aria-labelledby`);h.setAttribute(`aria-labelledby`,t?`${t} ${e}`:e),Ae.setAttribute(`aria-labelledby`,e)}j&&(h.setAttribute(`aria-disabled`,`true`),h.disabled=!0,b&&(b.setAttribute(`aria-disabled`,`true`),b.setAttribute(`data-disabled`,``))),pe&&(h.setAttribute(`aria-required`,`true`),h.required=!0);let Ne=()=>{pe&&h.setCustomValidity(I===null?`Please select a value`:``)};A&&(h.placeholder=A),E&&(E.textContent=ue||A,E.textContent.trim().length>0&&(E.setAttribute(`data-placeholder`,``),b?.setAttribute(`data-placeholder`,``)));let q=ie({root:f,container:y??g,input:h,emptySlot:ae,filter:_e??((e,t,n)=>n.toLowerCase().includes(e.toLowerCase())),itemToStringValue:P}),Pe=()=>{let e=Te.getBoundingClientRect();return g.style.minWidth=`${e.width}px`,e},J=(e=Pe())=>{let r=W.container,i=K?`bottom`:R??xe,a=K?!1:we,o=t({anchorRect:e,contentRect:ee(g),side:i,align:Se,sideOffset:M,alignOffset:Ce,avoidCollisions:a,collisionPadding:N,allowedSides:se}),s=n({side:o.side,align:o.align,anchorRect:e,popupX:o.x,popupY:o.y}),c=`translate3d(${o.x+V.scrollX}px, ${o.y+V.scrollY}px, 0)`,l=V.visualViewport,u=l?.offsetTop??0,d=l?.width??V.innerWidth,f=l?.height??V.innerHeight,p=Math.max(0,d-N*2),m=o.side===`top`?Math.max(0,e.top-u-N-M):Math.max(0,u+f-e.bottom-N-M),h=V.devicePixelRatio||1,_=(Math.round((e.x+e.width)*h)-Math.round(e.x*h))/h,v=(Math.round((e.y+e.height)*h)-Math.round(e.y*h))/h;r.style.transform=c,r.style.setProperty(`--transform-origin`,s);for(let e of r===g?[g]:[g,r])e.style.setProperty(`--available-width`,`${p}px`),e.style.setProperty(`--available-height`,`${m}px`),e.style.setProperty(`--anchor-width`,`${_}px`),e.style.setProperty(`--anchor-height`,`${v}px`),e.setAttribute(`data-side`,o.side),e.setAttribute(`data-align`,o.align);!K&&a&&(R=o.side)},Y=s({observedElements:[f,g],isActive:()=>F,ancestorScroll:!0,onUpdate:J,ignoreScrollTarget:e=>e instanceof Node&&g.contains(e)}),X=e=>{for(let t of[f,g,b])t&&(t.setAttribute(`data-state`,e),t.toggleAttribute(`data-open`,e===`open`),t.toggleAttribute(`data-closed`,e===`closed`))},Fe=c({element:g,onExitComplete:()=>{G.isDestroyed||(W.restore(),g.hidden=!0)}}),Ie=()=>{L=!1,k&&(h.value=``),q.filter(h.value),q.highlight(q.enabled.findIndex(e=>q.valueOf(e)===I))},Z=e=>{if(!G.isDestroyed&&F!==e&&!(j&&e)){if(e){let e=Pe();F=!0,R=null,C(h,`expanded`,!0),W.mount();let t=W.container.style;t.position=`absolute`,t.top=`0px`,t.left=`0px`,t.willChange=`transform`,t.margin=`0`,g.hidden=!1,X(`open`),Fe.enter(),q.cache(I),Ie(),Y.start(),J(e),G.trackRaf(()=>{G.isDestroyed||!F||J()})}else F=!1,R=null,C(h,`expanded`,!1),X(`closed`),q.clearHighlight(),L=!1,Y.stop(),Fe.exit(),k?h.value=``:h.value=q.labelFor(I);u(f,`combobox:open-change`,{open:F}),ye?.(F)}},Q=(e,t=!1)=>{if(I===e&&!t)return;let n=I;I=e,Ne(),De?.setValue(e),e===null?f.removeAttribute(`data-value`):f.setAttribute(`data-value`,e),q.select(e);let r=q.labelFor(e);k||(h.value=r),E?e===null?(E.textContent=ue||A,(E.textContent??``).trim().length>0?(E.setAttribute(`data-placeholder`,``),b?.setAttribute(`data-placeholder`,``)):(E.removeAttribute(`data-placeholder`),b?.removeAttribute(`data-placeholder`))):(E.textContent=r,E.removeAttribute(`data-placeholder`),b?.removeAttribute(`data-placeholder`)):b&&(e===null?b.setAttribute(`data-placeholder`,``):b.removeAttribute(`data-placeholder`)),!t&&n!==e&&(u(f,`combobox:change`,{value:e}),ve?.(e))},Le=e=>{if(q.isDisabled(e))return;let t=q.valueOf(e);t!==void 0&&(Q(t),Z(!1))};C(h,`expanded`,!1),g.hidden=!0,X(`closed`),Q(I,!0),De=a({root:f,name:me,defaultValue:de,control:h,disabled:j,onReset:e=>{Q(e,!0),F&&(Ie(),Y.update())}}),z.push(x(B,`keydown`,e=>{e.key===`Tab`&&(Ee=Date.now())},{capture:!0}),x(h,`pointerdown`,()=>{H=!0}),x(h,`input`,()=>{let e=h.value,t=e.trim()!==``;u(f,`combobox:input-change`,{inputValue:e}),be?.(e),F?(q.filter(e),Y.update()):Z(!0),q.highlight(ge&&t?0:-1)}),x(h,`keydown`,e=>{if(!j)switch(e.key){case`ArrowDown`:{if(e.preventDefault(),!F){Z(!0),ge&&q.highlight(0);return}L=!0;let t=q.enabled.length;if(t===0)return;q.highlight(q.highlightedIndex===-1?0:(q.highlightedIndex+1)%t);break}case`ArrowUp`:{if(e.preventDefault(),!F){Z(!0),ge&&q.highlight(q.enabled.length-1);return}L=!0;let t=q.enabled.length;if(t===0)return;q.highlight(q.highlightedIndex===-1?t-1:(q.highlightedIndex-1+t)%t);break}case`Home`:if(!F)return;e.preventDefault(),L=!0,q.highlight(0);break;case`End`:if(!F)return;e.preventDefault(),L=!0,q.highlight(q.enabled.length-1);break;case`Enter`:{if(!F)return;e.preventDefault();let t=q.enabled[q.highlightedIndex];t&&Le(t);break}case`Escape`:F?(e.preventDefault(),Z(!1)):I!==null&&(e.preventDefault(),Q(null));break;case`Tab`:F&&Z(!1);break}}),x(h,`focus`,()=>{if(j)return;if(U){U=!1,H=!1;return}K||h.select();let e=H||Date.now()-Ee<=750;H=!1,he&&!F&&e&&Z(!0)})),b&&z.push(x(b,`click`,()=>{j||(F?Z(!1):(Z(!0),h.focus()))})),T&&z.push(x(T,`mousedown`,e=>{e.preventDefault()}),x(T,`click`,()=>{if(j||h.readOnly||T&&(T.hasAttribute(`disabled`)||T.getAttribute(`aria-disabled`)===`true`))return;Q(null),h.value=``,q.clearHighlight(),F&&(q.filter(h.value),Y.update());let e=B.activeElement!==h;U=e,h.focus(),e||(U=!1)})),z.push(x(g,`click`,e=>{let t=e.target.closest?.(`[data-slot=\"combobox-item\"]`);t&&!t.hidden&&_(f,y??g,`[data-slot=\"combobox-item\"]`).includes(t)&&Le(t)}),x(g,`pointermove`,e=>{let t=e.target.closest?.(`[data-slot=\"combobox-item\"]`);if(!(L&&(L=!1,t&&q.indexOf(t)===q.highlightedIndex)))if(t&&!t.hidden){let e=q.indexOf(t);e!==void 0&&e!==q.highlightedIndex&&q.highlight(e)}else q.clearHighlight()}),x(g,`pointerleave`,()=>{L||q.clearHighlight()}),x(g,`mousedown`,e=>{e.preventDefault()})),z.push(i({root:f,isOpen:()=>F,onDismiss:()=>Z(!1),closeOnClickOutside:!K,closeOnEscape:!1})),K&&z.push(x(B,`click`,e=>{if(!F)return;let t=e.target;r(f,t)||Z(!1)},{capture:!0})),z.push(te(f,`combobox:set`,e=>{let t=e.detail;t?.value!==void 0&&Q(t.value),t?.open!==void 0&&Z(t.open),t?.inputValue!==void 0&&(h.value=t.inputValue),t?.itemToStringValue!==void 0&&(P=t.itemToStringValue,q.setItemToStringValue(P),Q(I,!0))}));let $={get value(){return I},get inputValue(){return h.value},get isOpen(){return F},select:e=>{G.isDestroyed||Q(e)},clear:()=>{G.isDestroyed||Q(null)},open:()=>{G.isDestroyed||Z(!0)},close:()=>{G.isDestroyed||Z(!1)},setItemToStringValue:e=>{G.isDestroyed||(P=e,q.setItemToStringValue(P),Q(I,!0))},destroy:()=>{G.destroy()&&(F=!1,C(h,`expanded`,!1),X(`closed`),g.hidden=!0,De?.destroy())}};return ne(G,{cleanups:z,positionSync:Y,presence:Fe,portal:W,unbind:()=>e(f,oe,$)}),w(f,oe,$),fe&&Z(!0),$}function O(e=document){return ae(e,D)}export{O as create,D as createCombobox};", + "type": "registry:file", + "target": "~/assets/data-slot-combobox.js" + }, + { + "path": "vendor/data-slot-carousel.js", + "content": "import{clearRootBinding as e,createSwipeGesture as t,drainCleanups as n,emit as r,ensureId as i,getDataBool as a,getDataEnum as o,getDataNumber as s,getPart as ee,getParts as c,getRoots as l,hasRootBinding as u,on as d,reuseRootBinding as te,setAria as f,setRootBinding as ne}from\"@data-slot/core\";const re=[`single`,`multiple`],ie=[`horizontal`,`vertical`],ae={horizontal:{scroll:`scrollLeft`,edge:`left`,extent:`scrollWidth`,viewport:`clientWidth`,prevKey:`ArrowLeft`,nextKey:`ArrowRight`,touchAction:`pan-y`,swipe:`x`},vertical:{scroll:`scrollTop`,edge:`top`,extent:`scrollHeight`,viewport:`clientHeight`,prevKey:`ArrowUp`,nextKey:`ArrowDown`,touchAction:`pan-x`,swipe:`y`}},oe=`Carousel requires carousel-content and at least one carousel-item`,p=`@data-slot/carousel`,m=(e,t,n)=>{if(t<=0)return 0;let r=Number.isFinite(e)?Math.trunc(e):0;return n?(r%t+t)%t:Math.min(t-1,Math.max(0,r))},se=e=>e instanceof Element&&e.closest(`input, textarea, select, [contenteditable]:not([contenteditable=\"false\"])`)!==null,h=(e,t)=>{`disabled`in e&&(e.disabled=t),f(e,`disabled`,t)};function g(l,u={}){let g=te(l,p,`[@data-slot/carousel] createCarousel() was called on a root that is already bound. Returning the existing controller.`);if(g)return g;let _=ee(l,`carousel-content`);if(!_)throw Error(oe);let ce=()=>Array.from(_.children).filter(e=>e instanceof HTMLElement&&e.getAttribute(`data-slot`)===`carousel-item`),v=ce();if(v.length===0)throw Error(oe);let y=u.orientation??o(l,`orientation`,ie)??`horizontal`,b=u.loop??a(l,`loop`)??!1,x=u.slides??o(l,`slides`,re)??`single`,S=u.snap??(l.getAttribute(`data-snap`)===`none`?!1:void 0),le=u.drag??a(l,`drag`)??!1,C=u.defaultIndex??s(l,`defaultIndex`)??0,ue=u.onIndexChange,w=ae[y],T=c(l,`carousel-previous`),E=c(l,`carousel-next`),D=[],O=l.ownerDocument?.defaultView??window,de=O.matchMedia?.(`(prefers-reduced-motion: reduce)`).matches??!1?`auto`:`smooth`,k=m(C,v.length,b),A=[],j=[],M,N=!1,P,F=()=>{P&&=(_.style.scrollSnapType=P.style,void 0)},fe=_.style.scrollSnapType;S!==void 0&&(_.style.scrollSnapType=S?`${w.swipe} mandatory`:`none`);let I=()=>P?.enabled??S??O.getComputedStyle(_).scrollSnapType!==`none`,pe=()=>{P??={style:_.style.scrollSnapType,enabled:I()},_.style.scrollSnapType=`none`},L=null,R=null,me=e=>e.getBoundingClientRect()[w.edge]-_.getBoundingClientRect()[w.edge]+_[w.scroll],z=e=>{let t=0,n=1/0;for(let r=0;rA.length<=1?!1:b||(I()?k>0:_[w.scroll]>(A[0]??0)+1),V=()=>A.length<=1?!1:b||(I()?k{l.setAttribute(`role`,`region`),l.setAttribute(`aria-roledescription`,`carousel`),l.setAttribute(`data-orientation`,y);for(let e=0;e{let e=!B(),t=!V();for(let t of T)h(t,e);for(let e of E)h(e,t)},W=()=>{let e=_.getBoundingClientRect(),t=e.left+_.clientLeft,n=e.top+_.clientTop,r=t+_.clientWidth,i=n+_.clientHeight;for(let e=0;et&&o.leftn&&o.top{_.getAttribute(`tabindex`)===`-1`&&_.removeAttribute(`tabindex`)})),_.focus({preventScroll:!0})),a.setAttribute(`data-state`,s?`active`:`inactive`),x===`multiple`&&s?a.removeAttribute(`aria-hidden`):f(a,`hidden`,!s),a.toggleAttribute(`inert`,!s)}},he=()=>{for(let e of c(l,`carousel-indicators`)){for(;e.children.length>A.length;)e.lastElementChild?.remove();for(;e.children.lengthe.setAttribute(`data-index`,String(t)))}for(let e of c(l,`carousel-indicator`)){let t=s(e,`index`),n=t!==void 0&&Number.isInteger(t)&&t>=0&&t{l.setAttribute(`data-index`,String(k)),W(),U(),he(),e&&(r(l,`carousel:change`,{index:k}),ue?.(k))},K=(e,t=`auto`)=>{let n={behavior:t};n[w.edge]=A[e]??0,_.scrollTo(n)},q=e=>{let t=e!==k;k=e,G(t)},J=()=>{let e=Math.max(0,_[w.extent]-_[w.viewport]);A=[],j=[];for(let t of v){let n=Math.min(e,Math.max(0,me(t)));A.some(e=>Math.abs(e-n)<1)||(A.push(n),j.push(t))}},Y=e=>{let t=_[w.scroll];if(J(),v.length===0){k=0,G(!1);return}let n=I(),r=n?e(t):z(t);n&&!N&&K(r),q(r)},ge=()=>{if(!(typeof ResizeObserver>`u`)){L?.disconnect(),L=new ResizeObserver(()=>Y(z)),L.observe(_);for(let e of v)L.observe(e)}},_e=()=>{let e=j[k]??null;v=ce(),H(),Y(()=>{let t=e?j.indexOf(e):-1;return t>=0?t:m(k,A.length,b)}),ge()},X=(e,t=de)=>{let n=m(e,A.length,b);K(n,t),q(n)},Z=()=>{B()&&X(I()?k-1:A.findLastIndex(e=>e<_[w.scroll]-1))},Q=()=>{if(!V())return;let e=I()?k+1:A.findIndex(e=>e>_[w.scroll]+1);X(e<0?A.length:e)},ve=()=>{O.clearTimeout(M),M=void 0,!N&&(F(),q(z(_[w.scroll])))},ye=()=>{x===`multiple`&&W(),I()||U(),!N&&(O.clearTimeout(M),M=O.setTimeout(ve,150))};J(),k=m(C,A.length,b),H(),K(k),q(k),D.push(d(_,`scroll`,ye)),D.push(d(_,`scrollend`,ve)),D.push(d(l,`keydown`,e=>{if(!(e.defaultPrevented||se(e.target))){switch(e.key){case w.prevKey:Z();break;case w.nextKey:Q();break;case`Home`:X(0);break;case`End`:X(A.length-1);break;default:return}e.preventDefault()}})),D.push(d(l,`carousel:set`,e=>{let t=e.detail;if(!(!t||typeof t!=`object`)){if(typeof t.index==`number`){X(t.index);return}t.action===`next`?Q():t.action===`prev`&&Z()}})),D.push(d(l,`click`,e=>{let t=e.target instanceof Element?e.target.closest(`[data-slot=\"carousel-indicator\"]`):null;if(!t||t.closest(`[data-slot=\"carousel\"]`)!==l)return;let n=s(t,`index`);n===void 0||!Number.isInteger(n)||n<0||n>=A.length||X(n)})),le&&D.push((()=>{let e=_.style.touchAction,n=0;_.style.touchAction=w.touchAction;let r=()=>{N&&(N=!1,l.removeAttribute(`data-dragging`))},i=()=>{if(!I()){F(),q(z(_[w.scroll]));return}let e=z(_[w.scroll]);X(e),Math.abs(_[w.scroll]-(A[e]??0))<1?F():ye()},a=t({element:_,axes:[w.swipe],lockThreshold:12,start:(e,t)=>t.closest(`a[href], button, input, select, textarea, summary, [contenteditable]:not([contenteditable=\"false\"]), [role=\"button\"], [role=\"link\"], [role=\"tab\"], [role=\"checkbox\"], [role=\"radio\"], [role=\"switch\"], [role=\"textbox\"]`)?null:_,lock(){return n=_[w.scroll],N=!0,l.setAttribute(`data-dragging`,`true`),O.clearTimeout(M),pe(),_.scrollTo({[w.edge]:n,behavior:`instant`}),!0},move(e,{deltaX:t,deltaY:r}){_[w.scroll]=n-(w.swipe===`x`?t:r)},release(){r(),i()},reset(e,t){r(),t&&i()}});return()=>{a.destroy(),F(),_.style.touchAction=e}})());for(let[e,t]of[[T,Z],[E,Q]])for(let n of e)n.tagName===`BUTTON`&&!n.hasAttribute(`type`)&&(n.type=`button`),D.push(d(n,`click`,()=>t()));ge(),typeof MutationObserver<`u`&&(R=new MutationObserver(_e),R.observe(_,{childList:!0}));let $={prev:Z,next:Q,goTo(e){X(e)},get index(){return k},get count(){return A.length},get canScrollPrev(){return B()},get canScrollNext(){return V()},destroy(){O.clearTimeout(M),L?.disconnect(),R?.disconnect(),n(D),S!==void 0&&(_.style.scrollSnapType=fe),e(l,p,$)}};return ne(l,p,$),$}function _(e=document){let t=[];for(let n of l(e,`carousel`))u(n,p)||t.push(g(n));return t}export{_ as create,g as createCarousel};", + "type": "registry:file", + "target": "~/assets/data-slot-carousel.js" + }, + { + "path": "vendor/data-slot-alert-dialog.js", + "content": "import{clearRootBinding as e,createDismissLayer as t,createModalStackItem as n,createPortalLifecycle as r,createPresenceLifecycle as i,createTerminalLifecycle as a,emit as ee,ensureId as te,focusElement as o,getAutofocusOrFirstFocusable as s,getDataBool as c,getPart as l,getRoots as u,getTabbables as ne,hasRootBinding as d,linkLabelledBy as f,lockScroll as p,on as m,onRoot as h,registerModalTerminalResources as re,reuseRootBinding as ie,setAria as g,setRootBinding as _,unlockScroll as v}from\"@data-slot/core\";const y=`@data-slot/alert-dialog`;function b(u,d={}){let b=ie(u,y,`[@data-slot/alert-dialog] createAlertDialog() called more than once for the same root. Returning the existing controller. Destroy it before rebinding with new options.`);if(b)return b;let x=d.defaultOpen??c(u,`defaultOpen`)??!1,S=d.onOpenChange,C=d.closeOnClickOutside??c(u,`closeOnClickOutside`)??!1,w=d.closeOnEscape??c(u,`closeOnEscape`)??!0,T=d.lockScroll??c(u,`lockScroll`)??!0,E=l(u,`alert-dialog-trigger`),D=l(u,`alert-dialog-portal`),O=l(u,`alert-dialog-overlay`),k=l(u,`alert-dialog-content`),A=l(u,`alert-dialog-title`),ae=l(u,`alert-dialog-description`);if(!k)throw Error(`Alert dialog requires alert-dialog-content slot`);if(!O)throw Error(`Alert dialog requires alert-dialog-overlay slot`);let j=!1,M=a(),N=null,P=[],F=D?r({content:D,root:u}):null,I=!1,L=()=>{M.trackFinalRaf(()=>{N&&(document.contains(N)?o(N):E&&document.contains(E)&&o(E)),N=null})};te(k,`alert-dialog-content`),k.setAttribute(`role`,`alertdialog`),g(k,`modal`,!0),f(k,A,ae),O.setAttribute(`role`,`presentation`),O.setAttribute(`aria-hidden`,`true`),O.tabIndex=-1,E&&(E.setAttribute(`aria-haspopup`,`dialog`),E.setAttribute(`aria-controls`,k.id),g(E,`expanded`,!1));let R=!1,z=()=>{k.hasAttribute(`tabindex`)||(k.tabIndex=-1,R=!0)},B=()=>{R&&=(k.removeAttribute(`tabindex`),!1)},V=()=>{let e=s(k);if(e)return e.focus();z(),k.focus()},H=()=>{M.trackRaf(()=>{N&&document.contains(N)&&typeof N.focus==`function`?o(N):E&&document.contains(E)&&o(E),N=null})},U=e=>{if(u.setAttribute(`data-state`,e),D?.setAttribute(`data-state`,e),O.setAttribute(`data-state`,e),k.setAttribute(`data-state`,e),e===`open`){u.setAttribute(`data-open`,``),D?.setAttribute(`data-open`,``),O.setAttribute(`data-open`,``),k.setAttribute(`data-open`,``),u.removeAttribute(`data-closed`),D?.removeAttribute(`data-closed`),O.removeAttribute(`data-closed`),k.removeAttribute(`data-closed`);return}u.setAttribute(`data-closed`,``),D?.setAttribute(`data-closed`,``),O.setAttribute(`data-closed`,``),k.setAttribute(`data-closed`,``),u.removeAttribute(`data-open`),D?.removeAttribute(`data-open`),O.removeAttribute(`data-open`),k.removeAttribute(`data-open`)},W=0,G=0,K=0,q=0,J=(e,t)=>{M.isDestroyed||j||t!==W||(e.hidden=!0,G=Math.max(0,G-1),G===0&&(B(),H()))},Y=i({element:O,onExitComplete:()=>J(O,K)}),X=i({element:k,onExitComplete:()=>J(k,q)}),Z=n({content:k,overlay:O,onTabKeydown:e=>{if(e.key!==`Tab`)return;let t=ne(k);if(t.length===0){e.preventDefault(),z(),k.focus();return}let n=t[0],r=t[t.length-1],i=k.ownerDocument.activeElement;if(!t.includes(i)){e.preventDefault(),(e.shiftKey?r:n).focus();return}if(n===r){e.preventDefault();return}e.shiftKey?i===n&&(e.preventDefault(),r.focus()):i===r&&(e.preventDefault(),n.focus())},cssVarPrefix:`alert-dialog`}),Q=(e,t=!1)=>{M.isDestroyed||j===e&&!t||(e?(W+=1,G=0,F?.mount(),N=document.activeElement,Z.open(),T&&!I&&(p(),I=!0)):(W+=1,G=2,K=W,q=W,Z.close(),I&&=(v(),!1)),j=e,E&&g(E,`expanded`,j),e?(O.hidden=!1,k.hidden=!1,U(`open`),Y.enter(),X.enter()):(U(`closed`),Y.exit(),X.exit()),ee(u,`alert-dialog:change`,{open:j}),S?.(j),e&&M.trackRaf(()=>V()))};k.hidden=!0,O.hidden=!0,U(`closed`),E&&P.push(m(E,`click`,()=>Q(!j))),P.push(m(k,`click`,e=>{let t=e.target;if(!t)return;let n=t.closest?.(`[data-slot=\"alert-dialog-cancel\"]`);n&&k.contains(n)&&Q(!1)})),C&&P.push(m(O,`click`,e=>{e.button===0&&e.target===O&&j&&Q(!1)})),P.push(t({root:u,isOpen:()=>j,onDismiss:()=>Q(!1),closeOnClickOutside:!1,closeOnEscape:w}));let $={open:()=>{M.isDestroyed||Q(!0)},close:()=>{M.isDestroyed||Q(!1)},toggle:()=>{M.isDestroyed||Q(!j)},get isOpen(){return j},destroy:()=>{M.destroy()}};return re(M,{cleanups:P,modalStack:Z,presence:[Y,X],portal:F,beforeDestroy:L,reset:()=>{W+=1,G=0,j=!1,U(`closed`),O.hidden=!0,k.hidden=!0,E&&g(E,`expanded`,!1)},releaseScrollLock:()=>{I&&=(v(),!1)},cleanup:B,unbind:()=>e(u,y,$)}),P.push(h(u,`alert-dialog:set`,e=>{let t=e.detail;typeof t?.open==`boolean`&&Q(t.open)})),_(u,y,$),x&&Q(!0),$}function x(e=document){let t=[];for(let n of u(e,`alert-dialog`))d(n,y)||t.push(b(n));return t}export{x as create,b as createAlertDialog};", + "type": "registry:file", + "target": "~/assets/data-slot-alert-dialog.js" + }, + { + "path": "vendor/data-slot-collapsible.js", + "content": "import{clearRootBinding as e,containsWithPortals as t,createDismissLayer as n,createPresenceLifecycle as r,emit as i,ensureId as a,focusElement as o,getDataBool as s,getPart as c,getRoots as l,hasRootBinding as u,on as d,onRoot as f,reuseRootBinding as p,setAria as m,setRootBinding as h}from\"@data-slot/core\";const g=`@data-slot/collapsible`,_=e=>{let t=e.trim();return t?t.endsWith(`ms`)?Number.parseFloat(t.slice(0,-2))||0:t.endsWith(`s`)?(Number.parseFloat(t.slice(0,-1))||0)*1e3:Number.parseFloat(t)||0:0},v=(e,t)=>{let n=e.split(`,`),r=t.split(`,`),i=Math.max(n.length,r.length),a=0;for(let e=0;e{let t=getComputedStyle(e),n=v(t.transitionDuration,t.transitionDelay),r=v(t.animationDuration,t.animationDelay);return Math.max(n,r)};function b(l,u={}){let _=p(l,g,`[@data-slot/collapsible] createCollapsible() called more than once for the same root. Returning the existing controller. Destroy it before rebinding with new options.`);if(_)return _;let v=u.defaultOpen??s(l,`defaultOpen`)??!1,b=u.hiddenUntilFound??s(l,`hiddenUntilFound`)??!1,x=u.closeOnEscape??s(l,`closeOnEscape`)??!1,S=u.closeOnClickOutside??s(l,`closeOnClickOutside`)??!1,C=u.onOpenChange,w=c(l,`collapsible-trigger`),T=c(l,`collapsible-content`);if(!w||!T)throw Error(`Collapsible requires trigger and content slots`);let E=l.ownerDocument?.defaultView??window,D=v,O=[],k=null,A=null,j=null,M=null,N=[],P=a(T,`collapsible-content`),F=a(w,`collapsible-trigger`);w.setAttribute(`aria-controls`,P),T.setAttribute(`role`,`region`),T.setAttribute(`aria-labelledby`,F);let I=e=>{l.setAttribute(`data-state`,e),T.setAttribute(`data-state`,e)},L=(e,t)=>{T.style.setProperty(`--collapsible-panel-height`,e),T.style.setProperty(`--collapsible-panel-width`,t)},R=(e,t)=>{L(`${e}px`,`${t}px`)},z=()=>{R(0,0)},B=()=>{R(T.scrollHeight,T.scrollWidth)},V=()=>{let e=T.style.getPropertyValue(`--collapsible-panel-height`).trim(),t=T.style.getPropertyValue(`--collapsible-panel-width`).trim();return e===`auto`&&t===`auto`},H=()=>{A!==null&&(E.cancelAnimationFrame(A),A=null),j!==null&&(E.clearTimeout(j),j=null),N.forEach(e=>e()),N=[]},U=()=>{M!==null&&(E.cancelAnimationFrame(M),M=null)},W=()=>{T.removeAttribute(`hidden`)},G=()=>{b?T.setAttribute(`hidden`,`until-found`):T.hidden=!0,z()},K=()=>{H(),!(!D||Y.isExiting)&&L(`auto`,`auto`)},q=()=>{H();let e=y(T);if(e>0){let t=e=>{e.target===T&&K()};T.addEventListener(`transitionend`,t),T.addEventListener(`animationend`,t),N.push(()=>T.removeEventListener(`transitionend`,t)),N.push(()=>T.removeEventListener(`animationend`,t)),j=E.setTimeout(()=>{j=null,K()},Math.ceil(e)+50);return}A=E.requestAnimationFrame(()=>{A=null,K()})},J=()=>{U(),M=E.requestAnimationFrame(()=>{M=null,!D&&Y.isExiting&&z()})},Y=r({element:T,onExitComplete:()=>{U(),G()}}),X=e=>{D!==e&&(D=e,D||B(),m(w,`expanded`,D),I(D?`open`:`closed`),D?(U(),W(),B(),Y.enter(),q()):(H(),Y.exit(),J()),i(l,`collapsible:change`,{open:D}),C?.(D))};m(w,`expanded`,D),D?(W(),B(),q()):G(),I(D?`open`:`closed`),typeof ResizeObserver<`u`&&(k=new ResizeObserver(()=>{!D||Y.isExiting||V()||B()}),k.observe(T));let Z=()=>w.hasAttribute(`disabled`)||w.getAttribute(`aria-disabled`)===`true`;O.push(d(w,`click`,()=>{Z()||X(!D)})),(x||S)&&O.push(n({root:l,isOpen:()=>D,onDismiss:({reason:e})=>{if(Z())return;let n=e===`escape-key`&&t(l,l.ownerDocument.activeElement);X(!1),n&&o(w)},closeOnEscape:x,closeOnClickOutside:S})),b&&O.push(d(T,`beforematch`,()=>{D||X(!0)})),O.push(f(l,`collapsible:set`,e=>{if(Z())return;let t=e.detail,n;t?.open===void 0?t?.value!==void 0&&(n=t.value):n=t.open,typeof n==`boolean`&&X(n)}));let Q={open:()=>X(!0),close:()=>X(!1),toggle:()=>X(!D),get isOpen(){return D},destroy:()=>{Y.cleanup(),H(),U(),k?.disconnect(),k=null,O.forEach(e=>e()),O.length=0,e(l,g,Q)}};return h(l,g,Q),Q}function x(e=document){let t=[];for(let n of l(e,`collapsible`))u(n,g)||t.push(b(n));return t}export{x as create,b as createCollapsible};", + "type": "registry:file", + "target": "~/assets/data-slot-collapsible.js" + }, + { + "path": "vendor/data-slot-drawer.js", + "content": "import{clearRootBinding as e,containsWithPortals as t,createDismissLayer as n,createModalStackItem as r,createPortalLifecycle as i,createPresenceLifecycle as a,createSwipeGesture as o,emit as s,ensureId as c,focusElement as l,getAutofocusOrFirstFocusable as u,getDataBool as d,getDataString as f,getRoots as p,getTabbables as ee,hasRootBinding as m,linkLabelledBy as te,lockScroll as ne,on as h,reuseRootBinding as re,setAria as g,setRootBinding as ie,unlockScroll as ae}from\"@data-slot/core\";function oe(e){if(typeof e==`number`)return Number.isFinite(e)&&e>=0?e:null;if(typeof e!=`string`||!e.trim())return null;let t=e.trim();if(/^\\d*\\.?\\d+(px|rem)$/.test(t))return t;let n=Number(t);return Number.isFinite(n)&&n>=0?n:null}function se(e,t,n){return typeof e==`number`?e<=1?e*t:e:parseFloat(e)*(e.endsWith(`rem`)?n:1)}function _(e,t,n,r){for(let i=e;i&&t.contains(i);i=i.parentElement){let e=i,t=e.ownerDocument.defaultView.getComputedStyle(e),a=n?t.overflowX:t.overflowY;if(!/(auto|scroll)/.test(a))continue;let o=n?e.scrollLeft:e.scrollTop,s=n?e.scrollWidth-e.clientWidth:e.scrollHeight-e.clientHeight;if(s>1&&(r>0?o>0:or?e.deltaX:e.deltaY;return o({element:t,axes:[r?`x`:`y`],start:(n,r)=>!e.enabled()||r.closest(`[data-swipe-ignore],input,textarea,select,button,a,[contenteditable=\"true\"]`)?null:r.closest(`[data-slot=\"drawer-popup\"]`)===t?t:null,lock:(e,n,i)=>!_(i,t,r,a(n)),active:e.enabled,move:(t,n)=>e.move(a(n)*i,n.event),release:(t,n)=>{let r=a(n)*i;e.release(r,r/Math.max(1,n.duration),n.event),e.reset()},reset:(t,n)=>e.reset(n)}).destroy}function le(e){let t=e.ownerDocument,n=t.defaultView,r=n.visualViewport,i=e.style.getPropertyValue(`--drawer-keyboard-inset`),a=e.hasAttribute(`data-keyboard-open`),o=0,s=null,c=()=>{if(!s)return;let{element:e,paddingBottom:t,scrollPaddingBottom:n,overflowAnchor:r}=s;Object.assign(e.style,{paddingBottom:t,scrollPaddingBottom:n,overflowAnchor:r}),s=null},l=()=>{o=0;let i=t.activeElement,a=i?.matches(`textarea, input:not([type]), input[type=\"text\"], input[type=\"email\"], input[type=\"number\"], input[type=\"password\"], input[type=\"search\"], input[type=\"tel\"], input[type=\"url\"], [contenteditable]:not([contenteditable=\"false\"])`),l=!!r&&(r.scale===void 0||r.scale===1)&&n.innerHeight-r.height>60&&!!a&&e.contains(i),u=r?Math.min(n.innerHeight,r.height+r.offsetTop):n.innerHeight;if(e.style.setProperty(`--drawer-keyboard-inset`,`${l?Math.max(0,n.innerHeight-u):0}px`),e.toggleAttribute(`data-keyboard-open`,l),!l||!i){c();return}let d=i.parentElement;for(;d&&e.contains(d)&&!/(auto|scroll)/.test(n.getComputedStyle(d).overflowY);)d=d.parentElement;if(!d||!e.contains(d)){c();return}s?.element!==d&&c();let f=i.getBoundingClientRect(),p=d.getBoundingClientRect(),ee=Math.min(u,p.bottom)-16,m=Math.max(r?.offsetTop??0,p.top)+16,te=f.bottom-ee;te>0?(s||={element:d,paddingBottom:d.style.paddingBottom,scrollPaddingBottom:d.style.scrollPaddingBottom,overflowAnchor:d.style.overflowAnchor,basePadding:parseFloat(n.getComputedStyle(d).paddingBottom)||0},d.style.overflowAnchor=`none`,d.style.paddingBottom=`${s.basePadding+Math.max(0,p.bottom-u)+f.height+48}px`,d.style.scrollPaddingBottom=`16px`,d.scrollTop+=te):f.top{o||=n.requestAnimationFrame(l)};l();let d=[h(t,`focusin`,u),h(t,`focusout`,u),h(n,`resize`,u)];return r&&d.push(h(r,`resize`,u),h(r,`scroll`,u)),()=>{d.forEach(e=>e()),n.cancelAnimationFrame(o),c(),i?e.style.setProperty(`--drawer-keyboard-inset`,i):e.style.removeProperty(`--drawer-keyboard-inset`),e.toggleAttribute(`data-keyboard-open`,a)}}const v=new WeakMap;function y(e){let t=null;for(let n of e)n.open&&(!t||n.order>t.order)&&(t=n);return t}function b(e){let{popup:t,swipe:n}=e,r=e.front??e;t.toggleAttribute(`data-nested-swiping`,e.descendants.some(e=>e.open&&e.swipe!==null));for(let i of[t,e.parts.backdrop,e.parts.viewport])i&&(i.toggleAttribute(`data-swiping`,n!==null),i.style.setProperty(`--drawer-swipe-progress`,String((i===t?r.swipe:n)?.progress??0)));t.style.setProperty(`--drawer-swipe-amount-x`,`${n?.x??0}px`),t.style.setProperty(`--drawer-swipe-amount-y`,`${n?.y??0}px`)}function x(e){e.popup.toggleAttribute(`data-nested`,!!e.parent),e.popup.toggleAttribute(`data-nested-drawer-open`,e.front!==null),e.popup.style.setProperty(`--nested-drawers`,String(e.descendants.filter(e=>e.open).length)),e.popup.style.setProperty(`--drawer-front-height`,`${(e.front??e).height}px`),b(e)}function S(e){let{front:t}=e;for(let n of e.indents)n.toggleAttribute(`data-active`,!!t),n.toggleAttribute(`data-inactive`,!t),n.style.setProperty(`--drawer-swipe-progress`,String(t?.swipe?.progress??0)),n.style.setProperty(`--drawer-height`,`${t?.height??0}px`)}function ue(e){e.front=y(e.entries),e.indents=Array.from(e.element.querySelectorAll(`[data-slot=\"drawer-indent\"],[data-slot=\"drawer-indent-background\"]`)).filter(t=>t.closest(`[data-slot=\"drawer-provider\"]`)===e.element),S(e)}function de(e){for(let t of e.entries.values())t.ancestors=[],t.descendants=[];for(let t of e.entries.values())for(let n=t.parent&&e.entries.get(t.parent);n;n=n.parent&&e.entries.get(n.parent))t.ancestors.push(n),n.descendants.push(t);for(let t of e.entries.values())t.front=y(t.descendants),x(t);for(let[t,n]of e.providers)ue(n),n.entries.size||e.providers.delete(t)}function fe(e,n,r={}){let i=e.ownerDocument,a=v.get(i);a||(a={entries:new Map,providers:new Map,openOrder:0},v.set(i,a));let o=null;for(let n of a.entries.values())t(n.popup,e)&&(!o||t(o.popup,n.root))&&(o=n);let s=e.closest(`[data-slot=\"drawer-provider\"]`)??o?.provider?.element,c=null;s&&(c=a.providers.get(s)??{element:s,entries:new Set,indents:[],front:null},a.providers.set(s,c));let l={root:e,popup:n,parts:r,parent:e.parentElement?.closest(`[data-slot=\"drawer\"]`)??o?.root??null,ancestors:[],descendants:[],provider:c,open:!1,swipe:null,order:0,height:n.getBoundingClientRect().height,front:null};a.entries.set(e,l),c?.entries.add(l),de(a);let u=()=>{x(l),l.ancestors.forEach(x)},d=!1;return{get parent(){return l.parent},setOpen(e){if(!(d||e===l.open)){e&&(l.order=++a.openOrder),l.open=e,e||(l.swipe=null);for(let e of l.ancestors)e.front=y(e.descendants);u(),c&&ue(c)}},setSwipe(e){if(!d&&(l.swipe=e,b(l),l.ancestors.forEach(b),c?.front===l))for(let t of c.indents)t.style.setProperty(`--drawer-swipe-progress`,String(e?.progress??0))},refresh(e){d||l.height===e||(l.height=e,u(),c?.front===l&&S(c))},destroy(){d||(d=!0,a.entries.delete(e),c?.entries.delete(l),de(a),a.entries.size||v.delete(i))}}}const C=`@data-slot/drawer`,pe=e=>e.matches(`:disabled`)||d(e,`disabled`)===!0||e.getAttribute(`aria-disabled`)===`true`,w=(e,t)=>Array.from(e.querySelectorAll(`[data-slot=\"drawer-${t}\"]`)).filter(t=>t.closest(`[data-slot=\"drawer\"]`)===e),me=(e,t)=>{try{return e.querySelector(t)}catch{return null}},he=(e,t)=>{let n=f(e,t);return n===`false`?!1:n===`true`||n===``?!0:n},ge=e=>{let t=e?.getAttribute(`data-payload`);if(t!=null)try{return JSON.parse(t)}catch{return t}};function T(o,p={}){let m=re(o,C,`[@data-slot/drawer] Drawer already initialized; returning its controller.`);if(m)return m;let _=w(o,`popup`)[0];if(!_)throw Error(`Drawer requires drawer-popup slot`);let v=o.ownerDocument,y=v.defaultView,b=w(o,`portal`)[0],x=w(o,`backdrop`)[0],S=w(o,`viewport`)[0],ue=w(o,`title`)[0],de=w(o,`description`)[0],T=w(o,`trigger`);if(o.id)for(let e of v.querySelectorAll(`[data-drawer-target]`))e.getAttribute(`data-drawer-target`)?.replace(/^#/,``)===o.id&&e.dataset.slot===`drawer-trigger`&&!T.includes(e)&&T.push(e);let _e=f(o,`modal`),E=p.modal??(_e===`trap-focus`?`trap-focus`:_e!==`false`),ve=!(p.disablePointerDismissal??d(o,`disablePointerDismissal`)??!1),ye=p.closeOnEscape??d(o,`closeOnEscape`)??!0,be=p.swipeDirection??f(o,`swipeDirection`),D=[`up`,`down`,`left`,`right`].includes(be??``)?be:`down`,O=D===`left`||D===`right`,xe=D===`left`||D===`up`?-1:1,k=oe(p.snapPoint===void 0?p.defaultSnapPoint===void 0?f(o,`snapPoint`)??f(o,`defaultSnapPoint`):p.defaultSnapPoint:p.snapPoint),Se=p.initialFocus??he(_,`initialFocus`),A=p.finalFocus??he(_,`finalFocus`),Ce=p.keepMounted??(b?d(b,`keepMounted`):void 0)??!1,j=p.container??(b?f(b,`container`):void 0),we=typeof j==`string`?me(v,j):j,Te=b??S??_,M=i({content:Te,root:o}),N=[],P=!1,F=!1,Ee=!1,De=!1,I=!1,L,Oe=S&&(o.closest(`[data-slot=\"drawer-virtual-keyboard-provider\"]`)||w(o,`virtual-keyboard-provider`).length),R=null,ke=p.triggerId??p.defaultTriggerId??f(o,`triggerId`)??f(o,`defaultTriggerId`),z=T.find(e=>e.id===ke)??null,B=0,Ae=-1,je=0,V=new Set,Me=e=>{let t=y.requestAnimationFrame(()=>{V.delete(t),F||e()});V.add(t)},Ne=[o,b,x,S,_].filter(e=>!!e),Pe=_.getAttribute(`tabindex`);Pe===null&&(_.tabIndex=-1),_.setAttribute(`role`,`dialog`),E===!0?g(_,`modal`,!0):_.removeAttribute(`aria-modal`),c(_,`drawer-popup`),te(_,ue??null,de??null),x&&(x.setAttribute(`aria-hidden`,`true`),x.setAttribute(`role`,`presentation`));for(let e of[...T,...w(o,`close`)])e.tagName===`BUTTON`&&!e.hasAttribute(`type`)&&e.setAttribute(`type`,`button`);for(let e of T)c(e,`drawer-trigger`),e.setAttribute(`aria-haspopup`,`dialog`),e.setAttribute(`aria-controls`,_.id),g(e,`expanded`,!1);let H=fe(o,_,{backdrop:x,viewport:S}),U=()=>{for(let e of Ne)e.setAttribute(`data-state`,P?`open`:`closed`),e.toggleAttribute(`data-open`,P),e.toggleAttribute(`data-closed`,!P),e.setAttribute(`data-swipe-direction`,D);for(let e of T)g(e,`expanded`,P&&e===z),e.toggleAttribute(`data-popup-open`,P&&e===z);H.setOpen(P)},Fe=()=>O?_.getBoundingClientRect().width||y.innerWidth:_.getBoundingClientRect().height||y.innerHeight,W=()=>k===null?Fe():Math.min(Fe(),se(k,O?S?.clientWidth||v.documentElement.clientWidth||y.innerWidth:S?.clientHeight||v.documentElement.clientHeight||y.innerHeight,parseFloat(y.getComputedStyle(v.documentElement).fontSize)||16)),Ie=()=>Math.max(0,Fe()-W()),G=()=>{let e=_.getBoundingClientRect();_.style.setProperty(`--drawer-height`,`${e.height}px`),_.style.setProperty(`--drawer-width`,`${e.width}px`),_.style.setProperty(`--drawer-snap-point-offset`,`${Ie()*xe}px`),k===null?o.removeAttribute(`data-snap-point`):o.setAttribute(`data-snap-point`,String(k)),H.refresh(e.height)},K=e=>t(_,e),Le=e=>typeof e==`string`?me(v,e):typeof e==`object`?e:null,Re=()=>{!P||!Y.isTopmost||Se===!1||l(Le(Se)??(Se===!0?u(_):null)??_)},ze=()=>{let e=Le(A)??z??R;R=null,A!==!1&&e?.isConnected&&l(e)},Be=e=>{s(o,`drawer:change-complete`,{open:e}),p.onOpenChangeComplete?.(e)},Ve=e=>{if(!P||B!==e||Ae===e)return;let t=Ue.flatMap(e=>typeof e.getAnimations==`function`?e.getAnimations():[]),n=()=>{!F&&P&&B===e&&Ae!==e&&(Ae=e,Be(!0))};t.length?Promise.allSettled(t.map(e=>e.finished)).then(n):n()},q=()=>{if(!(P||I||F)){_.hidden=!0,x&&(x.hidden=!0),S&&(S.hidden=!0),b&&(b.hidden=!0);for(let e of Ue)e.removeAttribute(`data-ending-style`);Ce||(M.restore(),Ee=!1)}},He=()=>{P||F||--je>0||(q(),Be(!1))},Ue=[_,x].filter(e=>!!e),J=Ue.map(e=>a({element:e,win:y,onExitComplete:()=>{e.setAttribute(`data-ending-style`,``),He()}})),We=(e,t=`imperative-action`,n)=>{let r=oe(e);if(F||e!==null&&r===null||r===k)return;let i=!1,a={snapPoint:r,reason:t,originalEvent:n,cancel:()=>{i=!0}},c=new CustomEvent(`drawer:beforesnapchange`,{bubbles:!0,cancelable:!0,detail:a});o.dispatchEvent(c),!(c.defaultPrevented||i||F)&&(p.onSnapPointChange?.(r,a),!(i||F)&&(k=r,G(),s(o,`drawer:snapchange`,a)))},Y=r({content:_,overlay:x,cssVarPrefix:`drawer`,isolateOutside:E===!0,onTabKeydown:e=>{if(E===!1||!P)return;let t=ee(_),n=v.activeElement;if(K(n)&&!_.contains(n))return;if(!t.length){e.preventDefault(),l(_);return}let r=t[0],i=t.at(-1);(!t.includes(n)||!e.shiftKey&&n===i||e.shiftKey&&n===r)&&(e.preventDefault(),l(e.shiftKey?i:r))}}),Ge=({next:e,reason:t,originalEvent:n,trigger:r})=>{if(F)return;let i=P&&e&&r!==z;if(P===e&&!i)return;let a=!1,c=!1,l={open:e,reason:t,trigger:r,payload:ge(r),originalEvent:n,cancel:()=>{a=!0},preventUnmountOnClose:()=>{c=!0}},u=new CustomEvent(`drawer:beforechange`,{bubbles:!0,cancelable:!0,detail:l});if(o.dispatchEvent(u),u.defaultPrevented||a||F||(p.onOpenChange?.(e,l),a||F))return;if(i){z=r,U(),s(o,`drawer:change`,l);return}let d=++B;if(z=r,P=e,I=!e&&c,e){R=v.activeElement,J.forEach(e=>e.enter()),Ee||=(M.mount(),we&&we.appendChild(Te),!0);for(let e of[b,S,x,_])e&&(e.hidden=!1);Y.open(),E===!0&&(ne(),De=!0),Oe&&S&&(L=le(S)),U(),G(),Me(Re),Me(()=>Me(()=>Ve(d)))}else{Y.close(),L?.(),L=void 0,De&&=(ae(),!1),U(),je=J.length,J.forEach(e=>e.exit());let e=n?.target;A===void 0&&(t===`focus-out`||t===`outside-press`&&E===!1&&e?.closest?.(`button,input,textarea,select,a[href],[tabindex]`))?R=null:ze()}s(o,`drawer:change`,l)},X=null,Z=null,Q=(e,t=`imperative-action`,n,r=z)=>{if(!F){if(X?.next===e&&X.trigger===r){Z=null;return}if(Z={next:e,reason:t,originalEvent:n,trigger:r},!X)try{for(;Z&&!F;)X=Z,Z=null,Ge(X)}finally{X=null,Z=null}}};N.push(n({root:o,isOpen:()=>P,closeOnEscape:ye,closeOnClickOutside:ve,isInside:e=>K(e)||T.some(t=>t.contains(e)),onDismiss:({reason:e,originalEvent:t})=>{e===`outside-press`&&t.button!==0||Q(!1,e,t)}}));for(let e of T)N.push(h(e,`click`,t=>{t.defaultPrevented||pe(e)||Q(!P||z!==e,`trigger-press`,t,e)}));for(let e of w(o,`close`))N.push(h(e,`click`,t=>{!t.defaultPrevented&&!pe(e)&&Q(!1,`close-press`,t)}));N.push(h(v,`focusin`,e=>{!P||!Y.isTopmost||K(e.target)||(E===!1?ve&&!T.includes(e.target)&&Q(!1,`focus-out`,e):l(u(_)??_))}));let Ke=()=>H.setSwipe(null);if(N.push(ce({popup:_,direction:D,enabled:()=>P&&Y.isTopmost,move:e=>{let t=Math.max(-Ie(),Math.min(W(),e)),n=Math.min(1,Math.max(0,t/Math.max(1,W())));H.setSwipe({progress:n,x:O?t*xe:0,y:O?0:t*xe})},release:(e,t,n)=>{_.style.setProperty(`--drawer-swipe-strength`,String(Math.max(.1,Math.min(1,1/Math.max(1,Math.abs(t)))))),(e>W()*.35||t>.5&&e>12)&&Q(!1,`swipe`,n)},reset:Ke})),N.push(h(y,`resize`,G)),y.ResizeObserver!==void 0){let e=new y.ResizeObserver(G);e.observe(_),N.push(()=>e.disconnect())}let $=(e,t)=>N.push(h(o,e,e=>{e.target===o&&t(e)}));$(`drawer:set`,e=>{let t=e.detail;if(!t||typeof t!=`object`)return;let n=typeof t.triggerId==`string`?T.find(e=>e.id===t.triggerId)??z:z;`snapPoint`in t&&We(t.snapPoint,`imperative-action`,e),typeof t.open==`boolean`&&Q(t.open,`imperative-action`,e,n)}),$(`drawer:open`,e=>Q(!0,`imperative-action`,e)),$(`drawer:close`,e=>Q(!1,`imperative-action`,e)),$(`drawer:toggle`,e=>Q(!P,`imperative-action`,e)),$(`drawer:unmount`,()=>{I=!1,q()}),_.style.setProperty(`--drawer-swipe-strength`,`1`),U(),G(),Ke(),q();let qe={open:e=>Q(!0,`imperative-action`,void 0,T.find(t=>t.id===e)??z),close:()=>Q(!1),toggle:()=>Q(!P),setSnapPoint:e=>We(e),unmount:()=>{I=!1,q()},get isOpen(){return P},get snapPoint(){return k},get triggerId(){return z?.id??null},destroy(){if(F)return;let t=P&&K(v.activeElement);F=!0,P=!1,B++,V.forEach(e=>y.cancelAnimationFrame(e)),V.clear(),J.forEach(e=>e.cleanup()),Y.destroy(),L?.(),De&&=(ae(),!1),N.forEach(e=>e()),U(),H.destroy();for(let e of[_,x,S,b])e&&(e.hidden=!0);M.cleanup(),Pe===null&&_.removeAttribute(`tabindex`),t&&ze(),e(o,C,qe)}};return ie(o,C,qe),(p.open??p.defaultOpen??d(o,`defaultOpen`)??!1)&&Q(!0,`none`),qe}function _e(e=document){return p(e,`drawer`).filter(e=>!m(e,C)).map(e=>T(e))}export{_e as create,T as createDrawer};", + "type": "registry:file", + "target": "~/assets/data-slot-drawer.js" + }, + { + "path": "vendor/data-slot-dropdown-menu.js", + "content": "import{clearRootBinding as e,computeFloatingPosition as t,computeFloatingTransformOrigin as n,createDismissLayer as r,createPortalLifecycle as i,createPositionSync as a,createPresenceLifecycle as o,createTerminalLifecycle as s,createTypeahead as c,emit as l,ensureId as u,ensureItemVisibleInContainer as d,focusElement as f,getDataBool as p,getDataEnum as m,getDataNumber as h,getDataString as g,getOwnedElements as _,getPart as v,getRoots as y,hasRootBinding as b,lockScroll as ee,measurePopupContentRect as te,on as x,onRoot as ne,registerFloatingTerminalResources as re,reuseRootBinding as ie,setAria as S,setRootBinding as ae,unlockScroll as oe}from\"@data-slot/core\";const C=[`top`,`right`,`bottom`,`left`],w=[`start`,`center`,`end`],T=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),se=e=>{try{let t=JSON.parse(e?.trim()||`[]`);return Array.isArray(t)?t.filter(e=>typeof e==`string`).map(e=>e.trim()).filter(Boolean):[]}catch{return[]}},ce=(e,t,n,r)=>{let i=(r,i)=>m(t,r,i)??(n?m(n,r,i):void 0)??m(e,r,i),a=r=>h(t,r)??(n?h(n,r):void 0)??h(e,r),o=r=>p(t,r)??(n?p(n,r):void 0)??p(e,r),s=T(r,`defaultValue`),c=T(r,`defaultValues`),l=e.hasAttribute(`data-default-value`),u=e.hasAttribute(`data-default-values`);return{defaultOpen:r.defaultOpen??p(e,`defaultOpen`)??!1,closeOnClickOutside:r.closeOnClickOutside??p(e,`closeOnClickOutside`)??!0,closeOnEscape:r.closeOnEscape??p(e,`closeOnEscape`)??!0,closeOnSelect:r.closeOnSelect??p(e,`closeOnSelect`)??!0,preferredSide:r.side??i(`side`,C)??`bottom`,preferredAlign:r.align??i(`align`,w)??`start`,sideOffset:r.sideOffset??a(`sideOffset`)??4,alignOffset:r.alignOffset??a(`alignOffset`)??0,avoidCollisions:r.avoidCollisions??o(`avoidCollisions`)??!0,collisionPadding:r.collisionPadding??a(`collisionPadding`)??8,lockScroll:r.lockScroll??p(e,`lockScroll`)??!0,highlightItemOnHover:r.highlightItemOnHover??p(e,`highlightItemOnHover`)??!0,optionsHasDefaultValue:s,optionsHasDefaultValues:c,rootHasDefaultValue:l,rootHasDefaultValues:u,requestedDefaultValue:s?r.defaultValue??null:l?g(e,`defaultValue`)??null:null,requestedDefaultValues:c?r.defaultValues??[]:u?se(g(e,`defaultValues`)):[]}},E=`[data-slot=\"dropdown-menu-item\"], [data-slot=\"dropdown-menu-radio-item\"], [data-slot=\"dropdown-menu-checkbox-item\"]`,le=e=>{let t=e.getAttribute(`data-slot`);return t===`dropdown-menu-radio-item`?`radio`:t===`dropdown-menu-checkbox-item`?`checkbox`:`item`},D=e=>e.el.hasAttribute(`disabled`)||e.el.hasAttribute(`data-disabled`)||e.el.getAttribute(`aria-disabled`)===`true`||e.type!==`item`&&e.value===null,ue=(e,t)=>{let n=[],r=[],i=new Map,a=new WeakSet,o=()=>n.filter(e=>e.type===`radio`),s=()=>n.filter(e=>e.type===`checkbox`),c=e=>n.find(t=>t.el===e)??null,l=e=>c(e instanceof Element?e.closest(E):null),u=(e,t=n)=>t.find(t=>t.type===`radio`&&t.value===e)??null,d=e=>e?e.type===`item`?e.value||e.el.textContent?.trim()||``:e.value:null,f=e=>r.find(t=>d(t)===e)??null,p=(e,t)=>{let n=s().filter(e=>e.value!==null);if(n.length===0)return null;if(e.length===0)return[];let r=new Set(e.filter(e=>typeof e==`string`).map(e=>e.trim()).filter(Boolean));if(r.size===0)return t===`init`?[]:null;let i=n.flatMap(e=>e.value&&r.has(e.value)?[e.value]:[]);return i.length>0||t===`init`?i:null},m=(e,t,r=n)=>{let i=new Set(e),a=new Set(t),o=null,s=null,c=null;for(let e of r){if(e.type!==`checkbox`||!e.value)continue;let t=i.has(e.value),n=a.has(e.value);if(t!==n){if(o!==null)return{changedValue:null,checked:null,item:null};o=e.value,s=n,c=e.el}}return{changedValue:o,checked:s,item:c}},h=(t,r)=>{for(let e of n){if(e.type===`item`)continue;let n=e.value!==null&&(e.type===`radio`?t===e.value:r.includes(e.value));e.el.hasAttribute(`data-checked`)!==n&&(e.el.toggleAttribute(`data-checked`,n),S(e.el,`checked`,n))}t!==null&&o().length>0?e.setAttribute(`data-value`,t):e.removeAttribute(`data-value`)},g=(e,t)=>{e!==t&&(e?.removeAttribute(`data-highlighted`),t?.setAttribute(`data-highlighted`,``))};return{refresh:o=>{let s=n;n=_(e,t,E).map(e=>({el:e,type:le(e),value:e.dataset.value?.trim()||null})),r=[],i=new Map;for(let e of n){a.has(e.el)||(a.add(e.el),e.el.setAttribute(`role`,e.type===`radio`?`menuitemradio`:e.type===`checkbox`?`menuitemcheckbox`:`menuitem`),e.el.tabIndex=-1,e.el.removeAttribute(`data-checked`),S(e.el,`checked`,e.type!==`item`&&e.value!==null?!1:null));let t=D(e);S(e.el,`disabled`,t||null),t||i.set(e.el,r.push(e)-1)}let c=o.value!==null&&u(o.value)?o.value:null,l=o.values.length?p(o.values,`init`)??[]:[],d=o.highlightedItem&&i.has(o.highlightedItem)?o.highlightedItem:null;return h(c,l),g(o.highlightedItem,d),{value:c,values:l,highlightedItem:d,previousItems:s}},synchronizeSelection:h,highlight:g,recordFor:c,fromTarget:l,radios:o,checkboxes:s,radioFor:u,valueFor:d,itemForValue:f,checkboxValues:p,checkboxDiff:m,isDisabled:D,get enabled(){return r},enabledIndex:e=>i.get(e),isEnabled:e=>i.has(e)}},O=`@data-slot/dropdown-menu`,de=(e,t)=>e.length===t.length&&e.every((e,n)=>e===t[n]),fe=(e,t,n,r=!1)=>e.dispatchEvent(new CustomEvent(t,{bubbles:!0,cancelable:r,detail:n}));function k(m,h={}){let g=ie(m,O,`[@data-slot/dropdown-menu] createDropdownMenu() called more than once for the same root. Returning the existing controller. Destroy it before rebinding with new options.`);if(g)return g;let _=v(m,`dropdown-menu-trigger`),y=v(m,`dropdown-menu-content`),b=v(m,`dropdown-menu-positioner`),C=b&&y&&b.contains(y)?b:null,w=v(m,`dropdown-menu-portal`),T=w&&C&&w.contains(C)?w:null;if(!_||!y)throw Error(`DropdownMenu requires trigger and content slots`);let{defaultOpen:se,closeOnClickOutside:E,closeOnEscape:le,closeOnSelect:D,preferredSide:k,preferredAlign:pe,sideOffset:me,alignOffset:he,avoidCollisions:ge,collisionPadding:_e,lockScroll:A,highlightItemOnHover:j,optionsHasDefaultValue:ve,optionsHasDefaultValues:ye,rootHasDefaultValue:be,rootHasDefaultValues:xe,requestedDefaultValue:Se,requestedDefaultValues:Ce}=ce(m,y,C,h),we=h.onOpenChange,Te=h.onSelect,Ee=h.onValueChange,De=h.onValuesChange,M=!1,N=null,P=[],F=null,I=null,L=c(),R=!1,z=!1,B=s();B.onDestroy(()=>{z&&=(oe(),!1)});let V=[],H=i({content:y,root:m,wrapperSlot:C?void 0:`dropdown-menu-positioner`,container:C??void 0,mountTarget:C?T??C:void 0}),U=ue(m,y),Oe=e=>e.pointerType!==`touch`,W=()=>U.synchronizeSelection(N,P),G=({source:e=`programmatic`,emitSelectionInvalidation:t=!1}={})=>{let n=N,r=[...P],i=U.refresh({value:n,values:r,highlightedItem:F}),{previousItems:a}=i;if(N=i.value,P=i.values,F=i.highlightedItem,t&&(n!==N&&Ae({value:N,previousValue:n,item:N===null?null:U.radioFor(N)?.el??null,previousItem:n===null?null:U.radioFor(n,a)?.el??null,source:e}),!de(r,P))){let t=U.checkboxDiff(r,P,a);je({values:[...P],previousValues:r,changedValue:t.changedValue,checked:t.checked,item:t.item,source:e})}},ke=e=>{l(m,`dropdown-menu:open-change`,e),l(m,`dropdown-menu:change`,e),we?.(e.open)},Ae=e=>{l(m,`dropdown-menu:value-change`,e),Ee?.(e.value)},je=e=>{l(m,`dropdown-menu:values-change`,e),De?.([...e.values])},Me=()=>{let e=H.container,r=m.ownerDocument.defaultView??window,i=_.getBoundingClientRect(),a=t({anchorRect:i,contentRect:te(y),side:k,align:pe,sideOffset:me,alignOffset:he,avoidCollisions:ge,collisionPadding:_e}),o=n({side:a.side,align:a.align,anchorRect:i,popupX:a.x,popupY:a.y});e.style.transform=A?`translate3d(${a.x}px, ${a.y}px, 0)`:`translate3d(${a.x+r.scrollX}px, ${a.y+r.scrollY}px, 0)`,e.style.position=A?`fixed`:`absolute`,e.style.top=`0px`,e.style.left=`0px`,e.style.setProperty(`--transform-origin`,o),e.style.willChange=`transform`,e.style.margin=`0`,y.setAttribute(`data-side`,a.side),y.setAttribute(`data-align`,a.align),e!==y&&(e.setAttribute(`data-side`,a.side),e.setAttribute(`data-align`,a.align))},K=a({observedElements:[_,y],isActive:()=>M,ancestorScroll:A,onUpdate:Me}),q=!1,Ne=()=>{q=!1,I&&document.contains(I)?f(I):document.contains(_)&&f(_),I=null},Pe=()=>{q=!0,B.trackRaf(Ne)};B.onBeforeDestroy(()=>{q&&B.trackFinalRaf(Ne)});let J=o({element:y,onExitComplete:()=>{B.isDestroyed||(H.restore(),y.hidden=!0,Pe())}}),Y=e=>{for(let t of[m,y])t.setAttribute(`data-state`,e),t.toggleAttribute(`data-open`,e===`open`),t.toggleAttribute(`data-closed`,e===`closed`)},X=(e,{source:t,focus:n=!0,focusContentOnClear:r=!1})=>{if(e&&!U.isEnabled(e))return!1;let i=F;return i===e?(e&&n?(d(e,y),f(e)):!e&&r&&f(y),!1):(F=e,U.highlight(i,e),e?(d(e,y),n&&f(e)):r&&f(y),l(m,`dropdown-menu:highlight-change`,{value:U.valueFor(U.recordFor(e)),previousValue:U.valueFor(U.recordFor(i)),item:e,previousItem:i,source:t}),!0)},Z=(e,t,n=!0)=>{if(G({source:t,emitSelectionInvalidation:n}),U.radios().length===0)return!1;let r=e===null?null:U.radioFor(e);if(e!==null&&!r||N===e)return!1;let i=N,a=i===null?null:U.radioFor(i);return N=e,W(),n&&Ae({value:N,previousValue:i,item:r?.el??null,previousItem:a?.el??null,source:t}),!0},Fe=(e,t,n=!0)=>{G({source:t,emitSelectionInvalidation:n});let r=U.checkboxValues(e,n?`set`:`init`);if(r===null||de(P,r))return!1;let i=[...P],a=U.checkboxDiff(i,r);return P=r,W(),n&&je({values:[...P],previousValues:i,changedValue:a.changedValue,checked:a.checked,item:a.item,source:t}),!0},Ie=()=>{if(G(),ve||be)Se===null?N=null:Z(Se,`programmatic`,!1);else for(let e of U.radios())if(e.value!==null&&p(e.el,`defaultChecked`)){N=e.value;break}if(ye||xe)P=U.checkboxValues(Ce,`init`)??[];else{let e=U.checkboxes().filter(e=>e.value!==null&&p(e.el,`defaultChecked`)).map(e=>e.value);P=U.checkboxValues(e,`init`)??[]}W()},Q=(e,{source:t,reason:n})=>{if(B.isDestroyed||M===e)return;let r=M;e?(I=document.activeElement,M=!0,S(_,`expanded`,!0),H.mount(),y.hidden=!1,Y(`open`),J.enter(),A&&!z&&(ee(),z=!0),G({source:t===`restore`?`restore`:`programmatic`,emitSelectionInvalidation:t!==`init`}),R=!1,L.reset(),K.start(),Me(),f(y)):(M=!1,S(_,`expanded`,!1),Y(`closed`),X(null,{source:t===`init`?`programmatic`:t,focus:!1}),L.reset(),R=!1,z&&=(oe(),!1),K.stop(),J.exit()),ke({open:M,previousOpen:r,source:t,reason:n})},Le=(e,t)=>{if(U.isDisabled(e))return;let n=U.valueFor(e);if(n===null)return;let r;if(e.type===`radio`?r=!0:e.type===`checkbox`&&e.value!==null&&(r=!P.includes(e.value)),fe(m,`dropdown-menu:select`,{value:n,item:e.el,itemType:e.type,source:t,checked:r},!0)){if(Te?.(n),e.type===`radio`)Z(e.value,t,!0);else if(e.type===`checkbox`&&e.value!==null){let n=new Set(P);n.has(e.value)?n.delete(e.value):n.add(e.value),Fe([...n],t,!0)}D&&Q(!1,{source:t,reason:`select`})}},Re=e=>{let t=F?U.enabledIndex(F)??-1:-1,n=L.match(e,U.enabled.map(e=>e.el.textContent?.trim()??``),t);n!==-1&&(R=!0,X(U.enabled[n]?.el??null,{source:`keyboard`,focus:!0}))},ze=e=>{let t=e.source??`programmatic`;if(e.value!==void 0&&Z(e.value,t,!0),e.values!==void 0&&Fe(e.values,t,!0),e.open!==void 0&&Q(e.open,{source:t,reason:`programmatic`}),e.highlightedValue!==void 0){if(!M)return;if(e.highlightedValue===null)X(null,{source:t,focus:!1,focusContentOnClear:!0});else{let n=U.itemForValue(e.highlightedValue);n&&X(n.el,{source:t,focus:!0})}}},Be=u(_,`dropdown-menu-trigger`),Ve=u(y,`dropdown-menu-content`);_.setAttribute(`aria-haspopup`,`menu`),_.setAttribute(`aria-controls`,Ve),y.setAttribute(`role`,`menu`),y.setAttribute(`aria-labelledby`,Be),y.tabIndex=-1,S(_,`expanded`,!1),y.hidden=!0,Y(`closed`),Ie(),V.push(x(_,`click`,()=>{Q(!M,{source:`pointer`,reason:`trigger`})}),x(_,`keydown`,e=>{(e.key===`Enter`||e.key===` `||e.key===`ArrowDown`)&&!M&&(e.preventDefault(),Q(!0,{source:`keyboard`,reason:`trigger`}))})),V.push(x(y,`keydown`,e=>{if(e.key===`Tab`){Q(!1,{source:`keyboard`,reason:`tab`});return}let t=U.enabled.length;if(t!==0)switch(e.key){case`ArrowDown`:e.preventDefault(),R=!0,X(U.enabled[F?((U.enabledIndex(F)??-1)+1)%t:0]?.el??null,{source:`keyboard`,focus:!0});break;case`ArrowUp`:e.preventDefault(),R=!0,X(U.enabled[F?(U.enabledIndex(F)-1+t)%t:t-1]?.el??null,{source:`keyboard`,focus:!0});break;case`Home`:e.preventDefault(),R=!0,X(U.enabled[0]?.el??null,{source:`keyboard`,focus:!0});break;case`End`:e.preventDefault(),R=!0,X(U.enabled[t-1]?.el??null,{source:`keyboard`,focus:!0});break;case`Enter`:case` `:if(e.preventDefault(),F){let e=U.recordFor(F);e&&Le(e,`keyboard`)}break;default:e.key.length===1&&!e.ctrlKey&&!e.metaKey&&!e.altKey&&(e.preventDefault(),Re(e.key.toLowerCase()))}}),x(y,`click`,e=>{let t=U.fromTarget(e.target);t&&Le(t,`pointer`)}),x(y,`pointermove`,e=>{if(!j||!Oe(e))return;let t=U.fromTarget(e.target)?.el??null;R&&(R=!1,t&&t===F)||(t&&U.isEnabled(t)?X(t,{source:`pointer`,focus:!0}):F&&X(null,{source:`pointer`,focus:!1,focusContentOnClear:!0}))}),x(y,`pointerleave`,e=>{!j||!Oe(e)||R||!F||X(null,{source:`pointer`,focus:!1,focusContentOnClear:!0})})),V.push(r({root:m,isOpen:()=>M,onDismiss:({reason:e})=>{Q(!1,e===`escape-key`?{source:`keyboard`,reason:`escape`}:{source:`pointer`,reason:`outside`})},closeOnClickOutside:E,closeOnEscape:le})),V.push(ne(m,`dropdown-menu:set`,e=>{let t=e.detail;if(!t||typeof t!=`object`)return;let n={source:t.source===`restore`||t.source===`programmatic`?t.source:void 0};t.open!==void 0&&(n.open=t.open),t.values!==void 0&&(n.values=Array.isArray(t.values)?t.values.filter(e=>typeof e==`string`):void 0),t.highlightedValue!==void 0&&(n.highlightedValue=t.highlightedValue===null||typeof t.highlightedValue==`string`?t.highlightedValue:void 0),t.value!==void 0&&(typeof t.value==`boolean`&&t.open===void 0?n.open=t.value:(t.value===null||typeof t.value==`string`)&&(n.value=t.value)),ze(n)}));let $={open:()=>!B.isDestroyed&&Q(!0,{source:`programmatic`,reason:`programmatic`}),close:()=>!B.isDestroyed&&Q(!1,{source:`programmatic`,reason:`programmatic`}),toggle:()=>!B.isDestroyed&&Q(!M,{source:`programmatic`,reason:`programmatic`}),set:e=>{B.isDestroyed||ze(e)},get isOpen(){return M},get value(){return N},get values(){return[...P]},get highlightedValue(){return U.valueFor(U.recordFor(F))},destroy:()=>{B.destroy()&&(L.destroy(),M=!1,S(_,`expanded`,!1),Y(`closed`),y.hidden=!0)}};return re(B,{cleanups:V,positionSync:K,presence:J,portal:H,unbind:()=>e(m,O,$)}),ae(m,O,$),se&&Q(!0,{source:`init`,reason:`init`}),$}function pe(e=document){let t=[];for(let n of y(e,`dropdown-menu`))b(n,O)||t.push(k(n));return t}export{pe as create,k as createDropdownMenu};", + "type": "registry:file", + "target": "~/assets/data-slot-dropdown-menu.js" + }, + { + "path": "vendor/data-slot-hover-card.js", + "content": "import{clearRootBinding as e,computeFloatingPosition as t,computeFloatingTransformOrigin as n,createContentMount as r,createDismissLayer as i,createPortalLifecycle as a,createPositionSync as ee,createPresenceLifecycle as te,createTerminalLifecycle as ne,emit as re,ensureId as ie,getDataBool as o,getDataEnum as s,getDataNumber as c,getPart as l,getRoots as u,hasRootBinding as d,measurePopupContentRect as ae,on as f,onRoot as oe,registerFloatingTerminalResources as se,reuseRootBinding as ce,setAria as p,setRootBinding as le}from\"@data-slot/core\";const ue=[`top`,`right`,`bottom`,`left`],de=[`start`,`center`,`end`];let fe=0;const m=new Set,h=new Set,g=(e,t)=>{if(!e)return!1;for(let n of m)if(n!==t&&n.contains(e))return!0;return!1},_=(e,t)=>{for(let n of h)n(e,t)},v=`@data-slot/hover-card`;function y(u,d={}){let y=ce(u,v,`[@data-slot/hover-card] createHoverCard() called more than once for the same root. Returning the existing controller. Destroy it before rebinding with new options.`);if(y)return y;let b=l(u,`hover-card-trigger`),x=l(u,`hover-card-content`),S=l(u,`hover-card-positioner`),C=S&&x&&S.contains(x)?S:null,pe=l(u,`hover-card-portal`),me=pe&&C&&pe.contains(C)?pe:null;if(!b||!x)throw Error(`Hover-card requires trigger and content slots`);let he=d.open!==void 0,ge=d.defaultOpen??o(u,`defaultOpen`)??!1,_e=d.delay??c(u,`delay`)??700,w=d.skipDelayDuration??c(u,`skipDelayDuration`)??300,ve=d.closeDelay??c(u,`closeDelay`)??300,ye=d.onOpenChange,be=d.closeOnClickOutside??o(u,`closeOnClickOutside`)??!0,xe=d.closeOnEscape??o(u,`closeOnEscape`)??!0,Se=d.portal??o(x,`portal`)??o(u,`portal`)??!0,Ce=(e,t)=>s(x,e,t)??(C?s(C,e,t):void 0)??s(u,e,t),T=e=>c(x,e)??(C?c(C,e):void 0)??c(u,e),we=e=>o(x,e)??(C?o(C,e):void 0)??o(u,e),Te=d.side??Ce(`side`,ue)??`bottom`,Ee=d.align??Ce(`align`,de)??`center`,De=d.sideOffset??T(`sideOffset`)??4,Oe=d.alignOffset??T(`alignOffset`)??0,ke=d.avoidCollisions??we(`avoidCollisions`)??!0,Ae=d.collisionPadding??T(`collisionPadding`)??8,E=d.open??ge,D=!1,O=ne(),k=!1,A=!1,j=!1,M=null,N=null,P=-1/0,F=-1/0,I=[],L=a({content:x,root:u,enabled:Se,wrapperSlot:C?void 0:`hover-card-positioner`,container:C??void 0,mountTarget:C?me??C:void 0}),R=r({root:u,target:me??C??x,strategy:d.mountStrategy??s(u,`mountStrategy`,[`lazy`,`eager`])??`lazy`});I.push(()=>R.cleanup());let je=ie(x,`hover-card-content`);b.setAttribute(`aria-haspopup`,`dialog`),E?b.setAttribute(`aria-controls`,je):b.removeAttribute(`aria-controls`),x.setAttribute(`data-side`,Te),x.setAttribute(`data-align`,Ee);let z=()=>b.hasAttribute(`disabled`)||b.getAttribute(`aria-disabled`)===`true`,B=()=>{M&&=(O.cancelTimeout(M),null)},V=()=>{N&&=(O.cancelTimeout(N),null)},H=()=>{B(),V()},U=()=>{H(),k=!1,A=!1,j=!1},Me=(e,t)=>{re(u,`hover-card:change`,{open:e,reason:t,trigger:b,content:x}),ye?.(e)},W=()=>{let e=L.container,r=u.ownerDocument.defaultView??window,i=b.getBoundingClientRect(),a=t({anchorRect:i,contentRect:ae(x),side:Te,align:Ee,sideOffset:De,alignOffset:Oe,avoidCollisions:ke,collisionPadding:Ae}),ee=n({side:a.side,align:a.align,anchorRect:i,popupX:a.x,popupY:a.y});e.style.position=`absolute`,e.style.top=`0px`,e.style.left=`0px`,e.style.transform=`translate3d(${a.x+r.scrollX}px, ${a.y+r.scrollY}px, 0)`,e.style.setProperty(`--transform-origin`,ee),e.style.willChange=`transform`,e.style.margin=`0`,x.setAttribute(`data-side`,a.side),x.setAttribute(`data-align`,a.align),e!==x&&(e.setAttribute(`data-side`,a.side),e.setAttribute(`data-align`,a.align))},G=e=>{let t=L.container;if(u.setAttribute(`data-state`,e),x.setAttribute(`data-state`,e),t!==x&&t.setAttribute(`data-state`,e),e===`open`){u.setAttribute(`data-open`,``),x.setAttribute(`data-open`,``),t!==x&&t.setAttribute(`data-open`,``),D?(u.setAttribute(`data-instant`,``),x.setAttribute(`data-instant`,``),t!==x&&t.setAttribute(`data-instant`,``)):(u.removeAttribute(`data-instant`),x.removeAttribute(`data-instant`),t!==x&&t.removeAttribute(`data-instant`)),u.removeAttribute(`data-closed`),x.removeAttribute(`data-closed`),t!==x&&t.removeAttribute(`data-closed`);return}u.setAttribute(`data-closed`,``),x.setAttribute(`data-closed`,``),t!==x&&t.setAttribute(`data-closed`,``),D?(u.setAttribute(`data-instant`,``),x.setAttribute(`data-instant`,``),t!==x&&t.setAttribute(`data-instant`,``)):(u.removeAttribute(`data-instant`),x.removeAttribute(`data-instant`),t!==x&&t.removeAttribute(`data-instant`)),u.removeAttribute(`data-open`),x.removeAttribute(`data-open`),t!==x&&t.removeAttribute(`data-open`)},K=te({element:x,onExitComplete:()=>{O.isDestroyed||(x.contains(u.ownerDocument.activeElement)&&(P=-1/0,b.focus()),L.restore(),x.hidden=!0,R.unmount())}}),q=ee({observedElements:[b,x],isActive:()=>E,ancestorScroll:!1,onUpdate:W}),Ne=(e,t,n=!1)=>{O.isDestroyed||E!==e&&(!e&&E&&w>0&&(fe=Date.now()+w),D=n,E=e,p(b,`expanded`,E),e?(R.mount(),L.mount(),b.setAttribute(`aria-controls`,je),x.hidden=!1,G(`open`),K.enter(),W(),q.start()):(b.removeAttribute(`aria-controls`),G(`closed`),K.exit(),q.stop()),Me(E,t))},J=(e,t,n=!1)=>{if(E!==e){if(he){Me(e,t);return}Ne(e,t,n)}},Y=(e,t,n=!1)=>{Ne(e,t,n)},X=(e,t=!1)=>{U(),J(!1,e,t)},Pe=(e,t=!1)=>{U(),Y(!1,e,t)},Fe=(e,t)=>{e===b||!E||X(t,!0)};m.add(b),h.add(Fe),I.push(()=>{h.delete(Fe),m.delete(b)});let Z=e=>{if(V(),B(),w>0&&Date.now(){M=null,!O.isDestroyed&&J(!0,e)},_e)},Ie=e=>{if(B(),V(),ve<=0){J(!1,e);return}N=O.trackTimeout(()=>{N=null,!O.isDestroyed&&J(!1,e)},ve)},Q=e=>{k||A||j||Ie(e)};p(b,`expanded`,E),G(E?`open`:`closed`),x.hidden=!E,E&&(L.mount(),K.enter(),x.hidden=!1,W(),q.start()),I.push(f(u.ownerDocument,`keydown`,e=>{e.key===`Tab`&&(P=Date.now())},{capture:!0}),f(u.ownerDocument,`pointerdown`,()=>{P=-1/0,F=-1/0},{capture:!0}),f(u.ownerDocument,`pointermove`,e=>{e.pointerType!==`touch`&&(F=Date.now())},{capture:!0}),f(b,`pointerenter`,e=>{e.pointerType!==`touch`&&(k=!0,!z()&&(Date.now()-F>250||(_(b,`pointer`),Z(`pointer`))))}),f(b,`pointermove`,e=>{e.pointerType!==`touch`&&(!k||z()||E||M||(_(b,`pointer`),Z(`pointer`)))}),f(b,`pointerleave`,e=>{if(e.pointerType===`touch`)return;k=!1;let t=e.relatedTarget;if(!(t&&x.contains(t))){if(g(t,b)){X(`pointer`,!0);return}Q(`pointer`)}})),I.push(f(x,`pointerenter`,e=>{e.pointerType!==`touch`&&(A=!0,V())}),f(x,`pointerleave`,e=>{if(e.pointerType===`touch`)return;A=!1;let t=e.relatedTarget;if(!(t&&b.contains(t))){if(g(t,b)){X(`pointer`,!0);return}Q(`pointer`)}})),I.push(f(b,`focusin`,()=>{z()||Date.now()-P>750||(j=!0,_(b,`focus`),Z(`focus`))}),f(b,`focusout`,e=>{let t=e.relatedTarget;if(!(t&&(b.contains(t)||x.contains(t)))){if(j=!1,g(t,b)){X(`blur`,!0);return}Q(`blur`)}}),f(x,`focusin`,()=>{j=!0,V()}),f(x,`focusout`,e=>{let t=e.relatedTarget;if(!(t&&(b.contains(t)||x.contains(t)))){if(j=!1,g(t,b)){X(`blur`,!0);return}Q(`blur`)}})),I.push(i({root:u,isOpen:()=>E,onDismiss:()=>X(`dismiss`),closeOnClickOutside:be,closeOnEscape:xe})),I.push(oe(u,`hover-card:set`,e=>{let t=e.detail,n;t?.open===void 0?t?.value!==void 0&&(n=t.value):n=t.open,typeof n==`boolean`&&(n?(H(),Y(!0,`api`)):Pe(`api`))}));let $={open:()=>{O.isDestroyed||z()||(H(),J(!0,`api`))},close:()=>{O.isDestroyed||X(`api`)},toggle:()=>{O.isDestroyed||!E&&z()||(E?X(`api`):(H(),J(!0,`api`)))},setOpen:e=>{O.isDestroyed||(e?(H(),Y(!0,`api`)):Pe(`api`))},get isOpen(){return E},destroy:()=>{O.destroy()&&(U(),E=!1,p(b,`expanded`,!1),b.removeAttribute(`aria-controls`),G(`closed`),x.hidden=!0)}};return se(O,{cleanups:I,positionSync:q,presence:K,portal:L,unbind:()=>e(u,v,$)}),E||R.unmount(),le(u,v,$),$}function b(e=document){let t=[];for(let n of u(e,`hover-card`))d(n,v)||t.push(y(n));return t}export{b as create,y as createHoverCard};", + "type": "registry:file", + "target": "~/assets/data-slot-hover-card.js" + }, + { + "path": "vendor/data-slot-popover.js", + "content": "import{clearRootBinding as e,computeFloatingPosition as t,computeFloatingTransformOrigin as n,createDismissLayer as r,createPortalLifecycle as i,createPositionSync as a,createPresenceLifecycle as o,createTerminalLifecycle as ee,emit as te,ensureId as ne,focusElement as s,getAutofocusOrFirstFocusable as re,getDataBool as c,getDataEnum as l,getDataNumber as u,getPart as d,getRoots as f,hasRootBinding as p,measurePopupContentRect as ie,on as m,onRoot as ae,registerFloatingTerminalResources as oe,reuseRootBinding as se,setAria as h,setRootBinding as g}from\"@data-slot/core\";const _=[`top`,`right`,`bottom`,`left`],ce=[`start`,`center`,`end`],v=`@data-slot/popover`;function y(f,p={}){let y=se(f,v,`[@data-slot/popover] createPopover() called more than once for the same root. Returning the existing controller. Destroy it before rebinding with new options.`);if(y)return y;let b=d(f,`popover-trigger`),x=d(f,`popover-content`),S=d(f,`popover-close`),C=d(f,`popover-positioner`),w=C&&x&&C.contains(x)?C:null,T=d(f,`popover-portal`),le=T&&w&&T.contains(w)?T:null;if(!b||!x)throw Error(`Popover requires trigger and content slots`);let E=p.defaultOpen??c(f,`defaultOpen`)??!1,D=p.onOpenChange,ue=p.closeOnClickOutside??c(f,`closeOnClickOutside`)??!0,O=p.closeOnEscape??c(f,`closeOnEscape`)??!0,k=p.portal??c(x,`portal`)??c(f,`portal`)??!0,A=(e,t)=>l(x,e,t)??(w?l(w,e,t):void 0)??l(f,e,t),j=e=>u(x,e)??(w?u(w,e):void 0)??u(f,e),de=e=>c(x,e)??(w?c(w,e):void 0)??c(f,e),fe=p.position??A(`position`,_),M=p.side??A(`side`,_)??fe??`bottom`,N=p.align??A(`align`,ce)??`center`,pe=p.sideOffset??j(`sideOffset`)??4,P=p.alignOffset??j(`alignOffset`)??0,F=p.avoidCollisions??de(`avoidCollisions`)??!0,I=p.collisionPadding??j(`collisionPadding`)??8,L=E,R=[],z=i({content:x,root:f,enabled:k,wrapperSlot:w?void 0:`popover-positioner`,container:w??void 0,mountTarget:w?le??w:void 0}),B=ee(),V=null,H=!1,U=()=>{H&&=(x.removeAttribute(`tabindex`),!1)},W=()=>{if(B.isDestroyed)return;let e=re(x);if(e)return e.focus();x.getAttribute(`tabindex`)||(x.setAttribute(`tabindex`,`-1`),H=!0),x.focus()},me=ne(x,`popover-content`);b.setAttribute(`aria-haspopup`,`dialog`),b.setAttribute(`aria-controls`,me),x.setAttribute(`data-side`,M),x.setAttribute(`data-align`,N),x.setAttribute(`data-position`,M);let G=()=>{let e=z.container,r=f.ownerDocument.defaultView??window,i=b.getBoundingClientRect(),a=t({anchorRect:i,contentRect:ie(x),side:M,align:N,sideOffset:pe,alignOffset:P,avoidCollisions:F,collisionPadding:I}),o=n({side:a.side,align:a.align,anchorRect:i,popupX:a.x,popupY:a.y});e.style.position=`absolute`,e.style.top=`0px`,e.style.left=`0px`,e.style.transform=`translate3d(${a.x+r.scrollX}px, ${a.y+r.scrollY}px, 0)`,e.style.setProperty(`--transform-origin`,o),e.style.willChange=`transform`,e.style.margin=`0`,x.setAttribute(`data-side`,a.side),x.setAttribute(`data-align`,a.align),e!==x&&(e.setAttribute(`data-side`,a.side),e.setAttribute(`data-align`,a.align)),x.setAttribute(`data-position`,a.side)},K=e=>{let t=z.container;f.setAttribute(`data-state`,e),x.setAttribute(`data-state`,e),t!==x&&t.setAttribute(`data-state`,e),e===`open`?(f.setAttribute(`data-open`,``),x.setAttribute(`data-open`,``),t!==x&&t.setAttribute(`data-open`,``),f.removeAttribute(`data-closed`),x.removeAttribute(`data-closed`),t!==x&&t.removeAttribute(`data-closed`)):(f.setAttribute(`data-closed`,``),x.setAttribute(`data-closed`,``),t!==x&&t.setAttribute(`data-closed`,``),f.removeAttribute(`data-open`),x.removeAttribute(`data-open`),t!==x&&t.removeAttribute(`data-open`))},q=!1,J=()=>{q=!1,V&&V.isConnected?s(V):s(b),V=null},Y=()=>{q=!0,B.trackRaf(J)};B.onBeforeDestroy(()=>{q&&B.trackFinalRaf(J)});let X=o({element:x,onExitComplete:()=>{B.isDestroyed||(z.restore(),x.hidden=!0,U(),Y())}}),Z=a({observedElements:[b,x],isActive:()=>L,ancestorScroll:!1,onUpdate:G}),Q=e=>{B.isDestroyed||L!==e&&(e&&(V=document.activeElement),L=e,h(b,`expanded`,L),e?(z.mount(),x.hidden=!1,K(`open`),X.enter(),G(),Z.start(),B.trackRaf(W)):(K(`closed`),X.exit(),Z.stop()),te(f,`popover:change`,{open:L}),D?.(L))};h(b,`expanded`,L),K(L?`open`:`closed`),x.hidden=!L,E&&(z.mount(),X.enter(),x.hidden=!1,G(),Z.start(),B.trackRaf(W)),R.push(m(b,`click`,()=>Q(!L))),S&&R.push(m(S,`click`,()=>Q(!1))),R.push(r({root:f,isOpen:()=>L,onDismiss:()=>Q(!1),closeOnClickOutside:ue,closeOnEscape:O})),R.push(ae(f,`popover:set`,e=>{let t=e.detail,n;t?.open===void 0?t?.value!==void 0&&(n=t.value):n=t.open,typeof n==`boolean`&&Q(n)}));let $={open:()=>{B.isDestroyed||Q(!0)},close:()=>{B.isDestroyed||Q(!1)},toggle:()=>{B.isDestroyed||Q(!L)},get isOpen(){return L},destroy:()=>{B.destroy()&&(L=!1,h(b,`expanded`,!1),K(`closed`),x.hidden=!0,U())}};return oe(B,{cleanups:R,positionSync:Z,presence:X,portal:z,unbind:()=>e(f,v,$)}),g(f,v,$),$}function b(e=document){let t=[];for(let n of f(e,`popover`))p(n,v)||t.push(y(n));return t}export{b as create,y as createPopover};", + "type": "registry:file", + "target": "~/assets/data-slot-popover.js" + }, + { + "path": "vendor/data-slot-radio-group.js", + "content": "import{clearRootBinding as e,emit as t,ensureId as n,getDataBool as r,getDataString as i,getOwnedElements as a,getParts as o,getRoots as s,hasRootBinding as c,observeFormReset as l,on as u,onRoot as d,reuseRootBinding as f,setAria as p,setRootBinding as m}from\"@data-slot/core\";const h=`@data-slot/radio-group`,g=[`position:absolute`,`width:1px`,`height:1px`,`padding:0`,`margin:-1px`,`overflow:hidden`,`clip:rect(0, 0, 0, 0)`,`white-space:nowrap`,`border:0`,`pointer-events:none`].join(`;`);function _(e,t,n){n?e.setAttribute(t,``):e.removeAttribute(t)}function v(e,t){_(e,`data-checked`,t),_(e,`data-unchecked`,!t)}function y(e,t,n,r){_(e,`data-disabled`,t),_(e,`data-readonly`,n),_(e,`data-required`,r)}function b(e,t){let n=new Set;if(e)for(let t of e.split(/\\s+/))t&&n.add(t);for(let e of t)e&&n.add(e);return n.size>0?[...n].join(` `):null}function x(e,t){let n=e.parentNode;if(!n){e.appendChild(t);return}n.insertBefore(t,e.nextSibling)}function S(e){return e.tagName===`BUTTON`}function C(e){let t=[],n=e.closest(`label`);if(n instanceof HTMLLabelElement&&t.push(n),!e.id)return t;let r=e.ownerDocument??document,i=`label[for=\"${CSS.escape(e.id)}\"]`;for(let e of r.querySelectorAll(i))t.includes(e)||t.push(e);return t}function w(s,c={}){let _=f(s,h,`[@data-slot/radio-group] createRadioGroup() called more than once for the same root. Returning the existing controller. Destroy it before rebinding with new options.`);if(_)return _;let w=s,T=o(w,`radio-group-item`);if(T.length===0)throw Error(`RadioGroup requires at least one radio-group-item`);let E=c.disabled??r(w,`disabled`)??(w.hasAttribute(`disabled`)||w.getAttribute(`aria-disabled`)===`true`),D=c.readOnly??r(w,`readOnly`)??w.getAttribute(`aria-readonly`)===`true`,O=c.required??r(w,`required`)??w.getAttribute(`aria-required`)===`true`,k=c.name??i(w,`name`),A=c.onValueChange,j=[],M=[],N=new Map,P=[];for(let e of T){let t=(e.dataset.value||``).trim();if(!t){j.push(e);continue}let n=(w.ownerDocument??document).createElement(`input`);n.type=`radio`,n.tabIndex=-1,n.setAttribute(`aria-hidden`,`true`),n.setAttribute(`data-radio-group-generated`,`input`),n.style.cssText=g,x(e,n);let r={el:e,value:t,authoredDisabled:e.hasAttribute(`disabled`)||e.hasAttribute(`data-disabled`)||e.getAttribute(`aria-disabled`)===`true`,indicators:a(w,e,`[data-slot=\"radio-group-indicator\"]`),hiddenInput:n};M.push(r),N.has(t)||N.set(t,r)}for(let e of j)e.tabIndex=-1,e.setAttribute(`aria-disabled`,`true`);if(M.length===0)throw Error(`RadioGroup requires at least one radio-group-item with a data-value attribute`);let F=(c.defaultValue??i(w,`defaultValue`)??``).trim(),I=N.get(F)??null,L=null,R=e=>E||e.authoredDisabled,z=()=>M.filter(e=>!R(e)),B=()=>I?.value??null,V=(e,t)=>{e.hiddenInput.checked=t,p(e.el,`checked`,t),v(e.el,t);for(let n of e.indicators)v(n,t)},H=()=>{let e=(I&&!R(I)?I:null)??z()[0]??null;e!==L&&(L&&(L.el.tabIndex=-1),e&&(e.el.tabIndex=0),L=e)},U=()=>{H();let e=B();e?w.setAttribute(`data-value`,e):w.removeAttribute(`data-value`)},W=(e,n=!0)=>{I!==e&&(I&&V(I,!1),I=e,e&&V(e,!0),U(),n&&(t(w,`radio-group:change`,{value:B()}),A?.(B())))},G=e=>{e&&e.el.focus()},K=(e,t)=>{let n=z();if(n.length===0)return null;let r=n.findIndex(t=>t===e);return r===-1?n[0]??null:n[(r+t+n.length)%n.length]??null},q=e=>{let t=z();return t.length===0?null:e===`first`?t[0]??null:t[t.length-1]??null};w.setAttribute(`role`,`radiogroup`),p(w,`disabled`,E?!0:null),p(w,`readonly`,D?!0:null),p(w,`required`,O?!0:null);for(let e of M){let t=R(e),r=e===I;S(e.el)&&(e.el.hasAttribute(`type`)||(e.el.type=`button`),e.el.disabled=t),e.el.setAttribute(`role`,`radio`),e.el.tabIndex=-1,p(e.el,`disabled`,t?!0:null),p(e.el,`readonly`,D?!0:null),p(e.el,`required`,O?!0:null),y(e.el,t,D,O);for(let n of e.indicators)y(n,t,D,O);e.hiddenInput.disabled=t,e.hiddenInput.required=!!(O&&k),e.hiddenInput.value=e.value,k&&(e.hiddenInput.name=k),e.hiddenInput.defaultChecked=r,V(e,r);let i=C(e.el);if(i.length>0){let t=i.map(e=>n(e,`radio-group-label`)),r=b(e.el.getAttribute(`aria-labelledby`),t);r&&e.el.setAttribute(`aria-labelledby`,r)}P.push(u(e.hiddenInput,`focus`,()=>{document.activeElement!==e.el&&e.el.focus()})),P.push(u(e.hiddenInput,`click`,t=>{(E||D||e.authoredDisabled)&&t.preventDefault()})),P.push(u(e.hiddenInput,`change`,()=>{E||D||e.authoredDisabled||W(e)})),P.push(u(e.el,`click`,t=>{if(t.target!==e.hiddenInput){if(E||D||e.authoredDisabled){t.preventDefault();return}if(I===e){t.preventDefault();return}t.preventDefault(),G(e),e.hiddenInput.click()}})),P.push(u(e.el,`keydown`,t=>{if(E||D||e.authoredDisabled)return;let n=t;if(!n.repeat)switch(n.key){case`ArrowRight`:case`ArrowDown`:{n.preventDefault();let t=K(e,1);G(t),t&&W(t);return}case`ArrowLeft`:case`ArrowUp`:{n.preventDefault();let t=K(e,-1);G(t),t&&W(t);return}case`Home`:{n.preventDefault();let e=q(`first`);G(e),e&&W(e);return}case`End`:{n.preventDefault();let e=q(`last`);G(e),e&&W(e);return}case` `:case`Spacebar`:case`Enter`:n.preventDefault(),I!==e&&e.hiddenInput.click();return;default:return}}));for(let t of i)t.contains(e.el)||P.push(u(t,`click`,t=>{t.preventDefault(),!(E||D||e.authoredDisabled)&&(G(e),I!==e&&e.hiddenInput.click())}))}U();let J=l({root:w,getForm:()=>M.find(e=>e.hiddenInput.form)?.hiddenInput.form??w.closest(`form`),onReset:()=>{W(M.find(e=>e.hiddenInput.checked)??null,!1)}});P.push(()=>J.destroy()),P.push(d(w,`radio-group:set`,e=>{if(E||D)return;let t=e.detail,n=t?.value===null||typeof t?.value==`string`?t.value:void 0;n!==void 0&&W(n===null?null:N.get(n)??null)}));let Y={get value(){return B()},select:e=>{let t=N.get(e);t&&W(t)},clear:()=>W(null),destroy:()=>{P.forEach(e=>e()),P.length=0;for(let e of M)e.hiddenInput.remove();e(w,h,Y)}};return m(w,h,Y),Y}function T(e=document){let t=[];for(let n of s(e,`radio-group`))c(n,h)||t.push(w(n));return t}export{T as create,w as createRadioGroup};", + "type": "registry:file", + "target": "~/assets/data-slot-radio-group.js" + }, + { + "path": "vendor/data-slot-select.js", + "content": "import{clearRootBinding as e,computeFloatingPosition as t,computeFloatingTransformOrigin as n,createContentMount as r,createDismissLayer as i,createFormFieldAdapter as a,createPortalLifecycle as o,createPositionSync as s,createPresenceLifecycle as c,createTerminalLifecycle as l,createTypeahead as u,emit as d,ensureId as f,ensureItemVisibleInContainer as p,focusElement as m,getDataBool as h,getDataEnum as g,getDataNumber as _,getDataString as v,getOwnedElements as y,getPart as b,getRoots as x,hasRootBinding as S,lockScroll as ee,measurePopupContentRect as te,on as C,onRoot as ne,registerFloatingTerminalResources as re,reuseRootBinding as ie,setAria as w,setRootBinding as ae,unlockScroll as T}from\"@data-slot/core\";const E=[`top`,`bottom`],oe=[`start`,`center`,`end`],se=[`item-aligned`,`popper`];function ce(e,t,n,r,i){let a=(n,i)=>g(t,n,i)??(r?g(r,n,i):void 0)??g(e,n,i),o=n=>_(t,n)??(r?_(r,n):void 0)??_(e,n);return{defaultValue:i.defaultValue??v(e,`defaultValue`)??null,defaultOpen:i.defaultOpen??h(e,`defaultOpen`)??!1,mountStrategy:i.mountStrategy??g(e,`mountStrategy`,[`lazy`,`eager`])??`lazy`,placeholder:i.placeholder??v(e,`placeholder`)??(n?v(n,`placeholder`):void 0)??``,disabled:i.disabled??h(e,`disabled`)??!1,required:i.required??h(e,`required`)??!1,name:i.name??v(e,`name`)??null,onValueChange:i.onValueChange,onOpenChange:i.onOpenChange,position:i.position??a(`position`,se)??`item-aligned`,preferredSide:i.side??a(`side`,E)??`bottom`,preferredAlign:i.align??a(`align`,oe)??`start`,sideOffset:i.sideOffset??o(`sideOffset`)??4,alignOffset:i.alignOffset??o(`alignOffset`)??0,avoidCollisions:i.avoidCollisions??(n=>h(t,n)??(r?h(r,n):void 0)??h(e,n))(`avoidCollisions`)??!0,collisionPadding:i.collisionPadding??o(`collisionPadding`)??8,lockScrollOption:i.lockScroll??h(e,`lockScroll`)??!0,highlightItemOnHover:i.highlightItemOnHover??h(e,`highlightItemOnHover`)??!0}}function D(e,t){return Array.from(x(e,`select`)).filter(e=>!S(e,`@data-slot/select`)).map(t)}const O=[`top`,`bottom`];function le(e){let{root:r,trigger:i,content:a,valueSlot:o,getPositioner:c,viewport:l,isOpen:u,getCollection:d,position:f,preferredSide:p,preferredAlign:m,sideOffset:h,alignOffset:g,avoidCollisions:_,collisionPadding:v,lockScroll:y}=e,b=l??a,x=e=>e.querySelector(`[data-slot=\"select-item-text\"]`),S=e=>{if(!e)return null;let t=e.getBoundingClientRect();return t.width>0||t.height>0?t:null},ee=(e=f===`item-aligned`)=>{a.setAttribute(`data-position`,f),a.setAttribute(`data-align-trigger`,e?`true`:`false`),l?.setAttribute(`data-position`,f)},C=()=>{let e=i.getBoundingClientRect();return a.style.minWidth=`${e.width}px`,e},ne=e=>S(o)??e,re=(e,t,n,r,i)=>{let a=i===`top`?`offsetTop`:`offsetLeft`,o=i===`top`?`clientTop`:`clientLeft`,s=i===`top`?`top`:`left`,c=0,l=e;for(;l&&l!==t;){c+=l[a];let e=l.offsetParent;if(!(e instanceof HTMLElement)){c=NaN;break}e!==t&&(c+=e[o]),l=e}return l===t&&Number.isFinite(c)?c:e.getBoundingClientRect()[s]-n[s]-t[o]+r},ie=(e,t)=>re(e,a,t,b.scrollTop,`top`)+a.clientTop,w=e=>{let t=x(e);return S(t)?t:e},ae=(e,t)=>{let{enabledItems:n,highlightedIndex:r,selectedItem:i}=d(),a=i??n[r]??n[0],s=ne(e),c=S(o),l=e.left,u,f=0,p=s.height;if(a){let e=x(a),n=!!(c&&S(e)),r=n&&e?e:w(a),i=S(r)??r.getBoundingClientRect();f=ie(r,t),p=i.height||r.offsetHeight||a.getBoundingClientRect().height||a.offsetHeight||s.height,n&&c&&(l=c.left-(i.left-t.left)),u=s.top+s.height/2-f-p/2}else u=e.top;return{x:l,y:u,alignItem:a,anchorTopInContent:f,anchorHeight:p,triggerAlignmentRect:s}},T=()=>{let e=c(),i=r.ownerDocument.defaultView??window,o=C(),s=te(a),l,u=`bottom`,d,x=f===`item-aligned`;if(f===`item-aligned`){let e=Number.parseFloat(i.getComputedStyle(a).minHeight)||0,r=Math.max(0,i.innerHeight-v*2),c=(o.width>0||o.height>0)&&(o.topi.innerHeight-v-20),f=s.height>0&&(b.scrollHeight<=b.clientHeight&&s.height>r+.5||e>0&&r+.5_?n0){let t=t=>Math.min(Math.max(e.anchorTopInContent+e.anchorHeight/2-(n-t),0),c),i=t=>r(n-(e.anchorTopInContent-t+e.anchorHeight/2),v,a),o=t(i(t(r(n-s.height/2,v,a))));b.scrollTop=o,l.y=i(o)}else b.scrollTop=0,l.y=r(e.y,v,a);u=l.ye instanceof Node&&a.contains(e)});return{update:T,measureTrigger:C,start:()=>{let e=c().style;e.position=y?`fixed`:`absolute`,e.top=`0px`,e.left=`0px`,e.willChange=`transform`,e.margin=`0`,E.start()},stop:()=>E.stop(),syncResolvedPositionAttributes:ee}}const ue=`@data-slot/select`;function de(t,n={}){let s=ie(t,ue,`[@data-slot/select] createSelect() called more than once for the same root. Returning the existing controller. Destroy it before rebinding with new options.`);if(s)return s;let h=b(t,`select-trigger`),g=b(t,`select-content`),_=b(t,`select-value`),v=b(t,`select-positioner`),x=v&&g&&v.contains(g)?v:null,S=b(t,`select-portal`),te=S&&x&&S.contains(x)?S:null;if(!h||!g)throw Error(`Select requires trigger and content slots`);let{defaultValue:E,defaultOpen:oe,placeholder:se,disabled:D,required:O,name:de,onValueChange:fe,onOpenChange:pe,position:me,preferredSide:he,preferredAlign:ge,sideOffset:_e,alignOffset:ve,avoidCollisions:ye,collisionPadding:be,lockScrollOption:xe,highlightItemOnHover:Se,mountStrategy:Ce}=ce(t,g,_,x,n),k=!1,A=E,j=null,M=-1,N=u(),P=!1,F=0,I=0,we=``,L=!1,R=[],z=[],B=[],V=new Map,Te=null,H=!1,U=o({content:g,root:t,wrapperSlot:x?void 0:`select-positioner`,container:x??void 0,mountTarget:x?te??x:void 0}),W=r({root:t,target:te??x??g,strategy:Ce});R.push(()=>W.cleanup());let G=l();G.onDestroy(()=>{H&&=(T(),!1)});let Ee=!0,K=e=>e.hasAttribute(`disabled`)||e.hasAttribute(`data-disabled`)||e.getAttribute(`aria-disabled`)===`true`,De=e=>e.pointerType!==`touch`,Oe=f(h,`select-trigger`),ke=f(g,`select-content`);h.setAttribute(`role`,`combobox`),h.setAttribute(`aria-haspopup`,`listbox`),h.removeAttribute(`aria-controls`),h.hasAttribute(`type`)||h.setAttribute(`type`,`button`),g.setAttribute(`role`,`listbox`),g.setAttribute(`aria-labelledby`,Oe),g.tabIndex=-1;let Ae=document.querySelector(`label[for=\"${CSS.escape(Oe)}\"]`);if(Ae){let e=f(Ae,`select-label`),t=h.getAttribute(`aria-labelledby`);h.setAttribute(`aria-labelledby`,t?`${t} ${e}`:e),R.push(C(Ae,`click`,e=>{e.preventDefault(),D||Q(!k)}))}D&&(h.setAttribute(`aria-disabled`,`true`),h.setAttribute(`data-disabled`,``),h instanceof HTMLButtonElement&&(h.disabled=!0)),O&&h.setAttribute(`aria-required`,`true`);let q=null,je=()=>{let e=z.find(e=>e.dataset.value===A)??null;e!==q&&(q&&(w(q,`selected`,!1),q.removeAttribute(`data-selected`)),e&&(w(e,`selected`,!0),e.setAttribute(`data-selected`,``)),q=e)},Me=()=>{let e=new Set(z);z=y(t,g,`[data-slot=\"select-item\"]`);for(let t of z)t.hasAttribute(`data-disabled`)||t.hasAttribute(`disabled`)?t.setAttribute(`aria-disabled`,`true`):t.removeAttribute(`aria-disabled`),!e.has(t)&&(t.setAttribute(`role`,`option`),t.tabIndex=-1,w(t,`selected`,!1),t.removeAttribute(`data-selected`));B=z.filter(e=>!K(e)),V=new Map(B.map((e,t)=>[e,t])),je();let n=y(t,g,`[data-slot=\"select-group\"]`);for(let e of n){e.setAttribute(`role`,`group`);let n=y(t,e,`[data-slot=\"select-label\"]`)[0]??null;if(n){let t=f(n,`select-label`);e.setAttribute(`aria-labelledby`,t)}}},Ne=y(t,g,`[data-slot=\"select-viewport\"]`)[0]??null,Pe=Ne??g,Fe=e=>y(t,e,`[data-slot=\"select-item-text\"]`)[0]??null,Ie=e=>e?.textContent?.trim()||void 0,Le=(e,t=``)=>e?(e.dataset.label?.trim()||void 0)??Ie(Fe(e))??Ie(e)??t:t,J=le({root:t,trigger:h,content:g,valueSlot:_,getPositioner:()=>U.container,viewport:Ne,isOpen:()=>k,getCollection:()=>({enabledItems:B,highlightedIndex:M,selectedItem:q}),position:me,preferredSide:he,preferredAlign:ge,sideOffset:_e,alignOffset:ve,avoidCollisions:ye,collisionPadding:be,lockScroll:xe}),Y=(e,t=!0,n=!0)=>{e!==M&&(B[M]?.removeAttribute(`data-highlighted`),B[e]?.setAttribute(`data-highlighted`,``),M=e);let r=B[e];r&&(n&&p(r,Pe),t&&r.focus())},Re=()=>Y(-1),ze=()=>Y(q?B.indexOf(q):-1,!1,!1),Be=()=>{Re(),m(g)},Ve=e=>{t.setAttribute(`data-state`,e),h.setAttribute(`data-state`,e),g.setAttribute(`data-state`,e),e===`open`?(t.setAttribute(`data-open`,``),h.setAttribute(`data-open`,``),g.setAttribute(`data-open`,``),t.removeAttribute(`data-closed`),h.removeAttribute(`data-closed`),g.removeAttribute(`data-closed`)):(t.setAttribute(`data-closed`,``),h.setAttribute(`data-closed`,``),g.setAttribute(`data-closed`,``),t.removeAttribute(`data-open`),h.removeAttribute(`data-open`),g.removeAttribute(`data-open`))},X=!1,He=null,Ue=()=>{X=!1,He=null,j&&document.contains(j)?m(j):h&&document.contains(h)&&m(h),j=null},We=()=>{X=!0,He=G.trackRaf(Ue)};G.onBeforeDestroy(()=>{X&&G.trackFinalRaf(Ue)});let Ge=()=>{G.isDestroyed||(U.restore(),g.hidden=!0,W.unmount(),Ee?We():j=null)},Z=c({element:g,onExitComplete:Ge}),Ke=()=>{_&&(A===null?(_.textContent=se,h.setAttribute(`data-placeholder`,``)):(_.textContent=Le(q,A),h.removeAttribute(`data-placeholder`)))},Q=(e,n={})=>{if(G.isDestroyed)return;let{skipFocusRestore:r=!1,immediate:i=!1}=n;if(k!==e&&!(D&&e)){if(e){let e=L;L=!1,Ee=!0;let t=document.activeElement;(!X||t!==document.body&&!g.contains(t))&&(j=t),G.cancelRaf(He),He=null,X=!1,k=!0,J.measureTrigger(),W.mount(),U.mount(),h.setAttribute(`aria-controls`,ke),w(h,`expanded`,!0),g.hidden=!1,Ve(`open`),Z.enter(),xe&&!H&&(ee(),H=!0),Me(),P=!1,ze(),J.start(),J.update(),G.trackRaf(()=>{if(!(G.isDestroyed||!k)&&(J.update(),e&&Se&&we!==`touch`&&(F!==0||I!==0))){let e=document.elementFromPoint(F,I)?.closest?.(`[data-slot=\"select-item\"]`);if(e&&!K(e)&&g.contains(e)){let t=V.get(e);t!==void 0&&Y(t,!0,!1)}}}),m(g)}else k=!1,L=!1,F=0,I=0,we=``,w(h,`expanded`,!1),h.removeAttribute(`aria-controls`),Ve(`closed`),Re(),N.reset(),P=!1,Ee=!r,H&&=(T(),!1),J.stop(),i?(Z.cleanup(),Ge()):Z.exit();d(t,`select:open-change`,{open:k}),pe?.(k)}},$=(e,n=!1)=>{if(G.isDestroyed||A===e&&!n)return;let r=A;A=e,Te?.setValue(e),e===null?t.removeAttribute(`data-value`):t.setAttribute(`data-value`,e),je(),Ke(),!n&&r!==e&&(d(t,`select:change`,{value:e}),fe?.(e))},qe=e=>{if(K(e))return;let t=e.dataset.value;t!==void 0&&($(t),Q(!1,{immediate:!0}))},Je=e=>{let t=B.length;if(t!==0)switch(e.key){case`ArrowDown`:e.preventDefault(),P=!0,Y(M===-1?0:Math.min(M+1,t-1));break;case`ArrowUp`:e.preventDefault(),P=!0,Y(M===-1?t-1:Math.max(M-1,0));break;case`Home`:e.preventDefault(),P=!0,Y(0);break;case`End`:e.preventDefault(),P=!0,Y(t-1);break;case`Enter`:case` `:e.preventDefault(),M>=0&&qe(B[M]);break;case`Tab`:Q(!1,{skipFocusRestore:!0});break;case`Escape`:e.preventDefault(),Q(!1);break;default:e.key.length===1&&!e.ctrlKey&&!e.metaKey&&!e.altKey&&(e.preventDefault(),Ye(e.key.toLowerCase()))}},Ye=e=>{let t=N.match(e,B.map(e=>Le(e)),M);t!==-1&&(P=!0,Y(t))};w(h,`expanded`,!1),g.hidden=!0,J.syncResolvedPositionAttributes(),Ve(`closed`),Me(),$(A,!0),Te=a({root:t,name:de,defaultValue:E,disabled:D,required:O,validationTarget:h,onReset:e=>{$(e,!0),k&&(N.reset(),ze())}}),R.push(C(h,`pointerdown`,e=>{F=e.clientX,I=e.clientY,we=e.pointerType,L=!0}),C(h,`click`,()=>{D||Q(!k)}),C(h,`keydown`,e=>{if(!D)switch(e.key){case`Enter`:case` `:case`ArrowDown`:case`ArrowUp`:e.preventDefault(),L=!1,Q(!0);break}})),R.push(C(g,`keydown`,Je),C(g,`click`,e=>{let n=e.target.closest?.(`[data-slot=\"select-item\"]`);n&&y(t,g,`[data-slot=\"select-item\"]`).includes(n)&&qe(n)}),C(g,`pointermove`,e=>{if(!Se||!De(e))return;let t=e.target.closest?.(`[data-slot=\"select-item\"]`);if(!(P&&(P=!1,t&&V.get(t)===M)))if(t&&!K(t)){let e=V.get(t);e!==void 0&&e!==M&&Y(e,!0)}else Be()}),C(g,`pointerleave`,e=>{!Se||!De(e)||P||Be()})),R.push(i({root:t,isOpen:()=>k,onDismiss:()=>Q(!1),closeOnClickOutside:!0,closeOnEscape:!1})),R.push(ne(t,`select:set`,e=>{let t=e.detail;t?.value!==void 0&&$(t.value),t?.open!==void 0&&Q(t.open)}));let Xe={get value(){return A},get isOpen(){return k},select:e=>{G.isDestroyed||$(e)},open:()=>{G.isDestroyed||Q(!0)},close:()=>{G.isDestroyed||Q(!1)},destroy:()=>{G.destroy()&&(N.destroy(),k=!1,w(h,`expanded`,!1),h.removeAttribute(`aria-controls`),Ve(`closed`),g.hidden=!0,Te?.destroy())}};return re(G,{cleanups:R,positionSync:J,presence:Z,portal:U,unbind:()=>e(t,ue,Xe)}),ae(t,ue,Xe),oe&&Q(!0),k||W.unmount(),Xe}function fe(e=document){return D(e,de)}export{fe as create,de as createSelect};", + "type": "registry:file", + "target": "~/assets/data-slot-select.js" + }, + { + "path": "vendor/data-slot-slider.js", + "content": "import{clearRootBinding as e,emit as t,ensureId as n,getDataBool as r,getDataEnum as i,getDataNumber as a,getDataString as o,getPart as s,getParts as c,getRoots as l,hasRootBinding as u,on as d,onRoot as f,reuseRootBinding as p,setAria as m,setRootBinding as ee}from\"@data-slot/core\";const te=[`horizontal`,`vertical`],ne=[`center`,`edge`,`edge-client-only`];function re(e){if(!e)return;let t=e.split(`,`).map(e=>parseFloat(e.trim()));if(!t.some(e=>isNaN(e))){if(t.length===2)return[t[0],t[1]];if(t.length===1)return t[0]}}function h(e){return Array.isArray(e)}function g(e,t,n,r){let i=Math.round((e-t)/r)*r+t,a=r.toString().split(`.`)[1]?.length??0,o=parseFloat(i.toFixed(a));return Math.min(n,Math.max(t,o))}function _(e,t,n){return n===t?0:(e-t)/(n-t)*100}function ie(e,t,n){return e/100*(n-t)+t}function v(e){return Math.max(0,Math.min(100,e))}const y=`@data-slot/slider`;function b(l,u={}){let b=p(l,y,`[@data-slot/slider] createSlider() called more than once for the same root. Returning the existing controller. Destroy it before rebinding with new options.`);if(b)return b;let x=l,S=s(l,`slider-track`),C=c(l,`slider-thumb`),w=s(l,`slider-range`),ae=s(l,`slider-control`);if(!S||C.length===0)throw Error(`Slider requires slider-track and at least one slider-thumb`);let T=ae??(S.parentElement instanceof HTMLElement?S.parentElement:null)??x,E=u.min??a(l,`min`)??0,D=u.max??a(l,`max`)??100;E>D&&([E,D]=[D,E]);let O=u.step??a(l,`step`)??1;O<=0&&(O=1);let k=u.largeStep??a(l,`largeStep`)??O*10,A=u.orientation??i(l,`orientation`,te)??`horizontal`,oe=u.thumbAlignment??i(l,`thumbAlignment`,ne)??`center`,j=u.disabled??r(l,`disabled`)??!1,se=u.onValueChange,M=u.onValueCommit,ce=re(o(l,`defaultValue`)),N=u.defaultValue??ce??E,P=C.length>=2;P&&!h(N)?N=[E,N]:!P&&h(N)&&(N=N[1]);let F=h(N)?[g(N[0],E,D,O),g(N[1],E,D,O)]:g(N,E,D,O),I=[],L=null,R=0,z=null,B=null,V=0,H=null,le=Array.from(new Set([x,T,S,w,...C].filter(e=>e instanceof HTMLElement)));(()=>{for(let e of le)e.setAttribute(`data-orientation`,A)})(),(e=>{for(let t of le)e?t.setAttribute(`data-disabled`,``):t.removeAttribute(`data-disabled`);for(let t of C)m(t,`disabled`,e),t.tabIndex=e?-1:0})(j);let ue=(e,t)=>{e.setAttribute(`role`,`slider`),e.tabIndex=j?-1:0,n(e,`slider-thumb`),m(e,`orientation`,A),P?e.setAttribute(`data-index`,String(t)):e.removeAttribute(`data-index`);let r=e.hasAttribute(`aria-label`)||e.hasAttribute(`aria-labelledby`),i=e.dataset.label;i?m(e,`label`,i):!r&&P&&m(e,`label`,t===0?`Minimum`:`Maximum`)};for(let e=0;ee instanceof HTMLElement))),fe=()=>{S.style.position=`relative`},G=(e,t)=>{e.style.position=`absolute`,e.style.setProperty(`--position`,`${t}%`),U?(e.style.setProperty(`inset-inline-start`,`var(--position)`),e.style.top=`50%`,e.style.bottom=``,e.style.left=``,e.style.setProperty(`translate`,`-50% -50%`)):(e.style.removeProperty(`inset-inline-start`),e.style.bottom=`var(--position)`,e.style.left=`50%`,e.style.top=``,e.style.setProperty(`translate`,`-50% 50%`))},pe=(e,t,n)=>{if(!w)return;let r=n?e:t;w.style.setProperty(`--start-position`,`${r}%`),w.style.position=U?`relative`:`absolute`,U?(w.style.setProperty(`inset-inline-start`,n?`var(--start-position)`:`0%`),w.style.width=n?`var(--relative-size)`:`var(--start-position)`,w.style.height=`inherit`,w.style.bottom=``,w.style.left=``):(w.style.removeProperty(`inset-inline-start`),w.style.bottom=n?`var(--start-position)`:`0%`,w.style.height=n?`var(--relative-size)`:`var(--start-position)`,w.style.width=`inherit`,w.style.left=``),n?w.style.setProperty(`--relative-size`,`${t}%`):w.style.removeProperty(`--relative-size`)};fe();let K=e=>U?e.width:e.height,me=(e,t)=>U?t.left-e.left:e.bottom-t.bottom,he=e=>e.offsetParent instanceof HTMLElement?e.offsetParent:T,q=(e,t,n)=>{if(!Number.isFinite(t)||!Number.isFinite(n)||t<=0)return;let r=Math.min(t,n),i=Math.max(0,t-r),a=v((r/2+i*e/100)/t*100);return Number.isFinite(a)?a:void 0},J=(e,t)=>{let n={thumbPercent:t,trackPercent:t};if(!e||!W)return n;let r=S.getBoundingClientRect(),i=e.getBoundingClientRect(),a=he(e).getBoundingClientRect(),o=K(r),s=K(i),c=K(a);if(!Number.isFinite(o)||!Number.isFinite(s)||!Number.isFinite(c)||o<=0||c<=0)return n;let l=q(t,o,s);if(l===void 0)return n;let u=Math.min(o,s),d=Math.max(0,o-u),f=u/2+d*t/100,p=v((me(a,r)+f)/c*100);return!Number.isFinite(l)||!Number.isFinite(p)?n:{thumbPercent:p,trackPercent:l}},ge=(e,t)=>{let n={startPercent:e,sizePercent:Math.max(0,t-e)};if(!W)return n;let r=K(S.getBoundingClientRect());if(!Number.isFinite(r)||r<=0)return n;let i=C.slice(0,2).map(e=>e instanceof HTMLElement?K(e.getBoundingClientRect()):NaN);if(i.length<2||i.some(e=>!Number.isFinite(e)||e<=0))return n;let a=Math.max(...i),o=q(e,r,a),s=q(t,r,a);return o===void 0||s===void 0?n:{startPercent:o,sizePercent:Math.max(0,s-o)}},Y=()=>{if(h(F)){let[e,t]=F,n=_(e,E,D),r=_(t,E,D),i=J(C[0],n),a=J(C[1],r),o=ge(n,r);C[0]&&(m(C[0],`valuenow`,String(e)),m(C[0],`valuemin`,String(E)),m(C[0],`valuemax`,String(t)),G(C[0],i.thumbPercent)),C[1]&&(m(C[1],`valuenow`,String(t)),m(C[1],`valuemin`,String(e)),m(C[1],`valuemax`,String(D)),G(C[1],a.thumbPercent)),pe(o.startPercent,o.sizePercent,!0)}else{let e=_(F,E,D),t=J(C[0],e);C[0]&&(m(C[0],`valuenow`,String(F)),m(C[0],`valuemin`,String(E)),m(C[0],`valuemax`,String(D)),G(C[0],t.thumbPercent)),pe(0,t.trackPercent,!1)}h(F)?l.setAttribute(`data-value`,`${F[0]},${F[1]}`):l.setAttribute(`data-value`,String(F))},_e=(e,t)=>h(e)&&h(t)?e[0]===t[0]&&e[1]===t[1]:e===t,X=(e,n=!0)=>{let r;if(h(e)){let[t,n]=e;t=g(t,E,D,O),n=g(n,E,D,O),t>n&&([t,n]=[n,t]),r=[t,n]}else r=g(e,E,D,O);return _e(r,F)?!1:(F=r,Y(),n&&(t(l,`slider:change`,{value:F}),se?.(F)),!0)};Y(),(()=>{if(H?.disconnect(),H=null,!(!W||typeof ResizeObserver!=`function`)){H=new ResizeObserver(()=>{Y()});for(let e of de)H.observe(e)}})(),I.push(()=>{H?.disconnect(),H=null});let Z=(e,t=null)=>{let n=S.getBoundingClientRect(),r=K(n);if(r===0)return null;let i=(U?e.clientX:e.clientY)-V,a=U?i-n.left:n.bottom-i,o=a/r*100;if(W&&t!==null){let e=C[t]?.getBoundingClientRect(),n=e?K(e):0,i=r-n;Number.isFinite(n)&&n>0&&i>0&&(o=(a-n/2)/i*100)}return o=v(o),ie(o,E,D)},ve=(e,t)=>{if(!W||e===null)return 0;let n=C[e],r=n?.getBoundingClientRect(),i=r?K(r):0;if(!n||!r||!Number.isFinite(i)||i<=0)return 0;let a=U?r.left+r.width/2:r.top+r.height/2;return(U?t.clientX:t.clientY)-a},ye=e=>{if(!e||!(e instanceof HTMLElement))return null;let t=C.indexOf(e);if(t!==-1)return t;for(let t=0;t{if(!h(F))return 0;let[t,n]=F,r=Math.abs(e-t),i=Math.abs(e-n);return r===i?R:r{if(h(F)){let[n,r]=F;return X(e===0?[g(t,E,r,O),r]:[n,g(t,n,D,O)])}else return X(t)},xe=e=>{if(j)return;e.preventDefault();let t=ye(e.target);V=ve(t,e);let n=Z(e,t);if(n===null){V=0;return}if(t===null){t=be(n);let r=Z(e,t);r!==null&&(n=r)}L=t,R=t,l.setAttribute(`data-dragging`,``),C[t]?.setAttribute(`data-dragging`,``),C[t]?.focus(),B=T.style.touchAction,T.style.touchAction=`none`,Q(t,n),T.setPointerCapture(e.pointerId)},Se=e=>{if(L===null||j)return;e.preventDefault();let t=Z(e,L);t!==null&&Q(L,t)},Ce=e=>{if(L!==null){l.removeAttribute(`data-dragging`);for(let e of C)e.removeAttribute(`data-dragging`);T.style.touchAction=B??``,B=null,t(l,`slider:commit`,{value:F}),M?.(F),L=null,V=0;try{T.releasePointerCapture(e.pointerId)}catch{}}};I.push(d(T,`pointerdown`,xe)),I.push(d(T,`pointermove`,Se)),I.push(d(T,`pointerup`,Ce)),I.push(d(T,`pointercancel`,Ce));let we=e=>{if(j)return;let t=e.target,n=C.indexOf(t);if(n===-1)return;let r=0,i=null;switch(e.key){case`ArrowRight`:if(!U)return;r=O;break;case`ArrowLeft`:if(!U)return;r=-O;break;case`ArrowUp`:if(U)return;r=O;break;case`ArrowDown`:if(U)return;r=-O;break;case`PageUp`:r=k;break;case`PageDown`:r=-k;break;case`Home`:i=E;break;case`End`:i=D;break;default:return}e.shiftKey&&e.key.startsWith(`Arrow`)&&(r=r>0?k:r<0?-k:0),e.preventDefault(),R=n,z===null&&(z=h(F)?[F[0],F[1]]:F);let a=h(F)?F[n]??F[0]:F;Q(n,i===null?a+r:i)},Te=()=>{z!==null&&(_e(z,F)||(t(l,`slider:commit`,{value:F}),M?.(F)),z=null)};for(let e of C)I.push(d(e,`keydown`,we)),I.push(d(e,`blur`,Te));I.push(f(l,`slider:set`,e=>{if(j)return;let n=e.detail,r;typeof n==`number`||Array.isArray(n)?r=n:n&&typeof n==`object`&&`value`in n&&(r=n.value),r!==void 0&&X(r)&&(t(l,`slider:commit`,{value:F}),M?.(F))}));let $={setValue:e=>{X(e)},get value(){return F},get min(){return E},get max(){return D},get disabled(){return j},destroy:()=>{V=0,I.forEach(e=>e()),I.length=0,e(l,y,$)}};return ee(l,y,$),$}function x(e=document){let t=[];for(let n of l(e,`slider`))u(n,y)||t.push(b(n));return t}export{x as create,b as createSlider};", + "type": "registry:file", + "target": "~/assets/data-slot-slider.js" + }, + { + "path": "vendor/data-slot-switch.js", + "content": "import{clearRootBinding as e,emit as t,ensureId as n,getDataBool as r,getDataString as i,getParts as a,getRoots as o,hasRootBinding as s,observeFormReset as c,on as l,onRoot as u,reuseRootBinding as d,setAria as f,setRootBinding as p}from\"@data-slot/core\";const m=`@data-slot/switch`,h=[`position:absolute`,`width:1px`,`height:1px`,`padding:0`,`margin:-1px`,`overflow:hidden`,`clip:rect(0, 0, 0, 0)`,`white-space:nowrap`,`border:0`,`pointer-events:none`].join(`;`);function g(e,t,n){n?e.setAttribute(t,``):e.removeAttribute(t)}function _(e,t){g(e,`data-checked`,t),g(e,`data-unchecked`,!t)}function v(e,t,n,r){g(e,`data-disabled`,t),g(e,`data-readonly`,n),g(e,`data-required`,r)}function y(e,t){let n=new Set;if(e)for(let t of e.split(/\\s+/))t&&n.add(t);for(let e of t)e&&n.add(e);return n.size>0?[...n].join(` `):null}function b(e,t){let n=e.parentNode;if(!n){e.appendChild(t);return}n.insertBefore(t,e.nextSibling)}function x(e){return e.tagName===`BUTTON`}function S(e){let t=e.tagName;return t===`BUTTON`||t===`INPUT`||t===`SELECT`||t===`TEXTAREA`?!0:t===`A`?e.hasAttribute(`href`):!1}function C(e){let t=[],n=e.closest(`label`);if(n instanceof HTMLLabelElement&&t.push(n),!e.id)return t;let r=e.ownerDocument??document,i=`label[for=\"${CSS.escape(e.id)}\"]`;for(let e of r.querySelectorAll(i))t.includes(e)||t.push(e);return t}function w(o,s={}){let g=d(o,m,`[@data-slot/switch] createSwitch() called more than once for the same root. Returning the existing controller. Destroy it before rebinding with new options.`);if(g)return g;let w=o,T=s.disabled??r(w,`disabled`)??(w.hasAttribute(`disabled`)||w.getAttribute(`aria-disabled`)===`true`),E=s.readOnly??r(w,`readOnly`)??w.getAttribute(`aria-readonly`)===`true`,D=s.required??r(w,`required`)??w.getAttribute(`aria-required`)===`true`,O=s.defaultChecked??r(w,`defaultChecked`)??w.getAttribute(`aria-checked`)===`true`,k=s.name??i(w,`name`),A=s.value??i(w,`value`),j=s.uncheckedValue??i(w,`uncheckedValue`),M=s.onCheckedChange,N=[],P=o.ownerDocument??document,F=P.createElement(`input`);F.type=`checkbox`,F.tabIndex=-1,F.setAttribute(`aria-hidden`,`true`),F.setAttribute(`data-switch-generated`,`input`),F.style.cssText=h,b(w,F);let I=null,L=!!O;F.defaultChecked=L;let R=a(w,`switch-thumb`),z=()=>{F.checked=L,f(w,`checked`,L),_(w,L);for(let e of R)_(e,L);!T&&!L&&k!==void 0&&j!==void 0?I||(I=P.createElement(`input`),I.type=`hidden`,I.setAttribute(`data-switch-generated`,`unchecked`),I.name=k,I.value=j,b(F,I)):(I?.remove(),I=null)},B=(e,n=!0)=>{L!==e&&(L=e,z(),n&&(t(w,`switch:change`,{checked:L}),M?.(L)))},V=()=>{T||E||F.click()},H=C(w);if(H.length>0){let e=H.map(e=>n(e,`switch-label`)),t=y(w.getAttribute(`aria-labelledby`),e);t&&w.setAttribute(`aria-labelledby`,t)}x(w)?(w.hasAttribute(`type`)||w.setAttribute(`type`,`button`),w.disabled=T):S(w)||(T?w.tabIndex=-1:w.hasAttribute(`tabindex`)||(w.tabIndex=0)),w.setAttribute(`role`,`switch`),f(w,`disabled`,T?!0:null),f(w,`readonly`,E?!0:null),f(w,`required`,D?!0:null),v(w,T,E,D);for(let e of R)v(e,T,E,D);F.disabled=T,F.required=D,k&&(F.name=k),A!==void 0&&(F.value=A),z();let U=c({root:w,getForm:()=>F.form??w.closest(`form`),onReset:()=>B(F.checked,!1)});N.push(()=>U.destroy()),N.push(l(F,`click`,e=>{(T||E)&&e.preventDefault()})),N.push(l(F,`change`,()=>{B(F.checked)})),N.push(l(w,`click`,e=>{if(!(e.target===F||e.target===I)){if(T||E){e.preventDefault();return}e.preventDefault(),F.click()}})),x(w)||N.push(l(w,`keydown`,e=>{let t=e;t.repeat||t.key!==`Enter`&&t.key!==` `&&t.key!==`Spacebar`||(t.preventDefault(),V())}));for(let e of H)e.contains(w)||N.push(l(e,`click`,e=>{e.preventDefault(),V()}));N.push(u(w,`switch:set`,e=>{let t=e.detail,n=typeof t==`boolean`?t:typeof t?.checked==`boolean`?t.checked:typeof t?.value==`boolean`?t.value:void 0;typeof n==`boolean`&&B(n)}));let W={get checked(){return L},toggle:()=>B(!L),check:()=>B(!0),uncheck:()=>B(!1),setChecked:e=>B(!!e),destroy:()=>{N.forEach(e=>e()),N.length=0,F.remove(),I?.remove(),e(w,m,W)}};return p(w,m,W),W}function T(e=document){let t=[];for(let n of o(e,`switch`))s(n,m)||t.push(w(n));return t}export{T as create,w as createSwitch};", + "type": "registry:file", + "target": "~/assets/data-slot-switch.js" + }, + { + "path": "vendor/data-slot-toast.js", + "content": "import{clearRootBinding as e,createPortalLifecycle as t,createPresenceLifecycle as n,createSwipeGesture as r,createTerminalLifecycle as i,emit as a,focusElement as o,getDataBool as s,getDataEnum as c,getDataNumber as l,getFocusable as u,getPart as d,getRoots as f,hasRootBinding as p,isFocusable as m,on as h,reuseRootBinding as g,setRootBinding as _}from\"@data-slot/core\";const v=[`top-left`,`top-center`,`top-right`,`bottom-left`,`bottom-center`,`bottom-right`],y=[`default`,`success`,`error`,`warning`,`info`,`loading`],b=e=>!!e&&typeof e==`object`,x=e=>typeof e==`string`,S=e=>typeof e==`string`&&e.trim()!==``,C=e=>typeof e==`boolean`,w=e=>typeof e==`number`&&Number.isFinite(e),T=e=>typeof e==`string`&&y.includes(e),E={title:S,description:x,type:T,duration:w,action:e=>b(e)&&S(e.label)&&(e.onClick===void 0||typeof e.onClick==`function`)&&(e.value===void 0||x(e.value)),dismissible:C,closeButtonAriaLabel:x,testId:x},ee=Object.keys(E),te=new Set([`description`,`action`,`closeButtonAriaLabel`,`testId`]),ne=e=>w(e)?Math.max(1,Math.trunc(e)):3,re=(e,t)=>w(e)?e<=0?0:Math.max(1,Math.trunc(e)):t,ie=(e,t)=>({id:e,title:``,type:`default`,duration:t,dismissible:!0}),D=(e,t)=>t===void 0?e:t??void 0,ae=(e,t)=>{let n=t.title??e.title;if(!S(n))throw Error(`Toast requires a non-empty title`);return{id:e.id,title:n,description:D(e.description,t.description),type:T(t.type)?t.type:e.type,duration:t.duration===void 0?e.duration:re(t.duration,e.duration),action:D(e.action,t.action),dismissible:t.dismissible??e.dismissible,closeButtonAriaLabel:D(e.closeButtonAriaLabel,t.closeButtonAriaLabel),testId:D(e.testId,t.testId)}},O=e=>{let t={};for(let n of ee){let r=e[n];(E[n](r)||r===null&&te.has(n))&&Object.assign(t,{[n]:r})}return t},oe=e=>{if(!b(e))return null;let t=O(e);return t.title?{id:S(e.id)?e.id:void 0,patch:t}:null},se=e=>{if(!b(e)||!S(e.id))return null;let t=O(e);return Object.keys(t).length>0?{id:e.id,patch:t}:null},ce=e=>S(e)?e:b(e)&&S(e.id)?e.id:null,le=e=>typeof e==`string`?{title:e}:e??{},ue=(e,t)=>le(typeof e==`function`?e(t):e),k=(e,t)=>{let{message:n,...r}=e;return{...r,title:S(e.title)?e.title:S(n)?n:t.title,description:e.description===void 0?t.description:e.description,type:e.type??t.type,duration:e.duration??t.duration}},de=e=>S(e)?e:e instanceof Error&&S(e.message)?e.message:`Error`,A=e=>e instanceof HTMLTemplateElement,j=e=>!!e.content.querySelector(`[data-slot=\"toast-item\"]`),M=e=>{let t=e.createElement(`template`);return t.innerHTML=`\n
  • \n \n \n \n \n
  • \n `,t},N=(e,t)=>{e.setAttribute(`data-state`,t),t===`open`?(e.setAttribute(`data-open`,``),e.removeAttribute(`data-closed`)):(e.setAttribute(`data-closed`,``),e.removeAttribute(`data-open`))},P=(e,t)=>{t===`error`||t===`warning`?(e.setAttribute(`role`,`alert`),e.setAttribute(`aria-live`,`assertive`)):(e.setAttribute(`role`,`status`),e.setAttribute(`aria-live`,`polite`)),e.setAttribute(`aria-atomic`,`true`)};function fe(e,t){let{viewport:r}=t,a=(A(t.template)&&j(t.template)?t.template:M(r.ownerDocument)).content.cloneNode(!0),o=a.querySelector(`[data-slot=\"toast-item\"]`),s=new WeakMap,c=i(),l=e,u=!1,d=t.paused,f=null,p=l.duration,m=0,h=(e,t)=>{let n=e.querySelector(`[data-slot=\"toast-title\"]`),r=e.querySelector(`[data-slot=\"toast-description\"]`),i=e.querySelector(`[data-slot=\"toast-action\"]`),a=e.querySelector(`[data-slot=\"toast-close\"]`);if(n&&(n.textContent=t.title,n.hidden=!1),r&&(t.description&&t.description.trim()!==``?(r.textContent=t.description,r.hidden=!1):(r.textContent=``,r.hidden=!0)),i&&(t.action?.label&&t.action.label.trim()!==``?(i.textContent=t.action.label,i.hidden=!1):(i.textContent=``,i.hidden=!0)),e.setAttribute(`data-type`,t.type),t.dismissible?e.removeAttribute(`data-dismissible`):e.setAttribute(`data-dismissible`,`false`),t.testId&&t.testId.trim()!==``?e.setAttribute(`data-testid`,t.testId):e.removeAttribute(`data-testid`),a){if(s.has(a)||s.set(a,a.getAttribute(`aria-label`)),t.closeButtonAriaLabel&&t.closeButtonAriaLabel.trim()!==``)a.setAttribute(`aria-label`,t.closeButtonAriaLabel);else{let e=s.get(a);e&&e.trim()!==``?a.setAttribute(`aria-label`,e):a.setAttribute(`aria-label`,`Close`)}a.getAttribute(`aria-label`)||a.setAttribute(`aria-label`,`Close`)}P(e,t.type)},g=()=>{c.cancelTimeout(f),f=null},_=()=>{if(!(!y.active||d||l.duration<=0||f!==null)){if(p<=0){t.onTimeout(y);return}m=Date.now(),f=c.trackTimeout(()=>{f=null,t.onTimeout(y)},p)}},v=n({element:o,win:r.ownerDocument.defaultView??window,onExitComplete:()=>{c.isDestroyed||t.onExitComplete(y)}});c.onDestroy(()=>{v.cleanup(),o.remove()});let y={get id(){return l.id},element:o,get toast(){return l},get active(){return!u&&!c.isDestroyed},get exiting(){return u},mount(){r.appendChild(a),c.trackRaf(()=>{c.trackRaf(()=>{y.active&&o.setAttribute(`data-mounted`,`true`)})}),_()},update(e){y.active&&(l=ae(l,e),h(o,l),e.duration!==void 0&&(g(),p=l.duration,_()))},setPaused(e){d===e||!y.active||(d=e,d?(f!==null&&(p=Math.max(0,p-(Date.now()-m))),g()):_())},dismiss(){y.active&&(u=!0,g(),o.setAttribute(`data-removed`,`true`),o.style.zIndex=`0`,o.style.pointerEvents=`none`,N(o,`closed`),v.exit())},destroy(){c.destroy()}};o.setAttribute(`data-id`,l.id),h(o,l);for(let e of[`mounted`,`removed`,`front`,`visible`,`swiping`,`swipe-out`])o.setAttribute(`data-${e}`,`false`);o.setAttribute(`data-expanded`,String(t.expanded));for(let e of[`amount-x`,`amount-y`,`end-x`,`end-y`])o.style.setProperty(`--toast-swipe-${e}`,`0px`);return N(o,`open`),y}const F=`data-toast-prev-tabindex`,I=`__none__`,pe=e=>{let t=getComputedStyle(e).getPropertyValue(`--toast-gap`).trim();if(!t)return 8;let n=Number.parseFloat(t);return Number.isFinite(n)?n:8},L=e=>{let t=getComputedStyle(e).getPropertyValue(`--toast-collapsed-peek`).trim();if(!t)return 14;let n=Number.parseFloat(t);return Number.isFinite(n)?Math.max(0,n):14},R=e=>{let t=e.cloneNode(!0);t.setAttribute(`aria-hidden`,`true`),t.setAttribute(`inert`,``),t.style.width=getComputedStyle(e).width,t.style.height=`auto`,t.style.maxHeight=`none`,t.style.position=`absolute`,t.style.inset=`0 auto auto 0`,t.style.visibility=`hidden`,t.style.pointerEvents=`none`,t.style.transform=`none`,t.style.transition=`none`,t.style.animation=`none`,e.parentElement.append(t);try{return t.offsetHeight||t.getBoundingClientRect().height||e.offsetHeight||e.getBoundingClientRect().height}finally{t.remove()}},me=e=>{let t=u(e);return m(e)&&t.unshift(e),t.filter(e=>e.getAttribute(`aria-hidden`)!==`true`)},z=e=>{let t=[...e.querySelectorAll(`[${F}]`)];return e.hasAttribute(F)&&t.unshift(e),t},B=(e,t)=>{if(t){e.removeAttribute(`aria-hidden`),e.removeAttribute(`inert`);for(let t of z(e)){let e=t.getAttribute(F);t.removeAttribute(F),!e||e===I?t.removeAttribute(`tabindex`):t.setAttribute(`tabindex`,e)}return}for(let t of me(e)){if(!t.hasAttribute(F)){let e=t.getAttribute(`tabindex`);t.setAttribute(F,e===null?I:e)}t.setAttribute(`tabindex`,`-1`)}e.setAttribute(`aria-hidden`,`true`),e.setAttribute(`inert`,``)};function he({viewport:e,limit:t,stackDirection:n,getItems:r,onLayout:i}){let a=e.ownerDocument,o=new Set,s=new WeakMap,c=!1;e.style.setProperty(`--toast-lift`,String(n));let l=t=>{t?e.setAttribute(`data-expanded`,``):e.removeAttribute(`data-expanded`);let n=t?e.style.getPropertyValue(`--toast-expanded-stack-size`):e.style.getPropertyValue(`--toast-collapsed-stack-size`);e.style.setProperty(`--toast-stack-size`,n.trim()===``?`0px`:n);for(let e of o)e.setAttribute(`data-expanded`,String(t))},u=()=>{if(c)return;let n=r(),o=pe(e),l=L(e),u=n.length,d=0,f=0,p=0,m=0;for(let e=0;e0&&s.set(r,h),e===0&&(m=h);let g=e*l;if(r.style.setProperty(`--toast-index`,String(e)),r.style.setProperty(`--toast-initial-height`,`${h}px`),r.style.setProperty(`--toast-offset`,`${d}px`),r.style.setProperty(`--toast-collapsed-offset-y`,`${g}px`),r.setAttribute(`data-front`,String(e===0)),r.setAttribute(`data-visible`,String(i)),r.setAttribute(`data-removed`,`false`),r.style.zIndex=String(u-e+1),r.style.pointerEvents=i?``:`none`,!i&&c){let e=a.activeElement;e instanceof HTMLElement&&r.contains(e)&&e.blur()}B(r,i),d+=h+o,i&&(p+=1,f+=h+o)}let h=p>0?Math.max(0,f-o):0,g=p>0?Math.max(0,m+l*(p-1)):0;e.style.setProperty(`--toast-count`,String(u)),e.style.setProperty(`--toast-front-height`,`${m}px`),e.style.setProperty(`--toast-expanded-stack-size`,`${h}px`),e.style.setProperty(`--toast-collapsed-stack-size`,`${g}px`),i()},d=typeof ResizeObserver<`u`?new ResizeObserver(u):null;return{update:u,setExpanded:l,observe(e){o.add(e),d?.observe(e)},unobserve(e){o.delete(e),d?.unobserve(e)},destroy(){c=!0,d?.disconnect(),o.clear(),e.removeAttribute(`data-expanded`)}}}const V=e=>{e.element.setAttribute(`data-swiping`,`false`),e.element.setAttribute(`data-swipe-out`,`false`);for(let t of[`amount-x`,`amount-y`,`end-x`,`end-y`])e.element.style.removeProperty(`--toast-swipe-${t}`)};function ge({viewport:e,position:t,getEntry:n,dismiss:i}){let a={x:t.endsWith(`left`)?-1:t.endsWith(`right`)?1:0,y:t.startsWith(`top`)?-1:1},o=a.x===0?[`y`]:[`x`,`y`],s=(e,t)=>e*t<0?e*.2:e,c=e=>e.active&&e.toast.dismissible;return r({element:e,axes:o,lockThreshold:12,capture:e=>e.element,start(e,t){if(t.closest(`[data-slot=\"toast-action\"], [data-slot=\"toast-close\"]`))return null;let r=n(t);return r&&c(r)?r:null},active:c,move(e,{axis:t,deltaX:n,deltaY:r}){let i=t===`x`?s(n,a.x):0,o=t===`y`?s(r,a.y):0;e.element.setAttribute(`data-swiping`,`true`),e.element.style.setProperty(`--toast-swipe-amount-x`,`${i}px`),e.element.style.setProperty(`--toast-swipe-amount-y`,`${o}px`)},release(e,{axis:t,deltaX:n,deltaY:r}){let o=t===`x`?n:r;if(o*a[t]<40){V(e);return}let s=t===`x`?e.element.offsetWidth:e.element.offsetHeight,c=a[t]*Math.max(Math.abs(o),s+16);e.element.setAttribute(`data-swiping`,`false`),e.element.setAttribute(`data-swipe-out`,`true`),e.element.style.setProperty(`--toast-swipe-end-x`,`${t===`x`?c:0}px`),e.element.style.setProperty(`--toast-swipe-end-y`,`${t===`y`?c:0}px`),i(e)},reset:V})}const H=new Set([`hover`,`focus`]);function _e({hasExitingEntries:e,setExpanded:t,setPaused:n}){let r=new Set,i=!1,a=!1,o=()=>[...r].some(e=>H.has(e)),s=()=>{i&&(o()||!e())&&(i=!1),t(o()||i);let s=r.size>0;s!==a&&(a=s,n(a))};return{get expanded(){return o()||i},get paused(){return a},set(t,n){n?r.add(t):r.delete(t)&&H.has(t)&&!o()&&e()&&(i=!0),s()},sync:s}}const U=`@data-slot/toast`;function W(n,r={}){let u=g(n,U,`[@data-slot/toast] createToast() called more than once for the same root. Returning the existing controller. Destroy it before rebinding with new options.`);if(u)return u;let f=d(n,`toast-viewport`);if(!f)throw Error(`Toast requires a toast-viewport slot`);let p=n.ownerDocument??document,m=d(n,`toast-template`),y=ne(r.limit??l(n,`limit`)),b=re(r.duration??l(n,`duration`),5e3),x=r.position??c(n,`position`,v)??`bottom-right`,C=r.pauseOnHover??s(n,`pauseOnHover`)??!0,w=r.pauseOnFocus??s(n,`pauseOnFocus`)??!0,T=r.portal??s(n,`portal`)??!1,E=r.onShow,ee=r.onDismiss,te=r.onAction,D=x.startsWith(`top`)?1:-1;n.setAttribute(`data-position`,x),f.setAttribute(`data-position`,x),f.hasAttribute(`role`)||f.setAttribute(`role`,`region`),f.hasAttribute(`aria-label`)||f.setAttribute(`aria-label`,`Notifications`);let O=t({content:f,root:n,enabled:T});T&&O.mount();let A=new Map,j=i(),M=[],N=0,P=null,F=()=>[...A.values()].filter(e=>e.active),I=e=>A.get(e.id)===e,pe=()=>[...A.values()].some(e=>e.exiting),L=he({viewport:f,limit:y,stackDirection:D,getItems:()=>F().reverse().map(e=>e.element),onLayout:()=>R.set(`focus`,w&&B())}),R=_e({hasExitingEntries:pe,setExpanded:e=>L.setExpanded(e),setPaused:e=>{for(let t of F())t.setPaused(e)}});p.visibilityState===`hidden`&&R.set(`document`,!0);let z=e=>{if(!(e instanceof Node)||!f.contains(e))return!1;if(!(e instanceof Element))return!0;let t=e.closest(`[data-slot=\"toast-item\"]`);return!t||!f.contains(t)?!0:t.getAttribute(`data-visible`)===`true`&&t.getAttribute(`data-state`)===`open`},B=()=>z(p.activeElement),V=()=>{for(let e of F().reverse()){if(e.element.getAttribute(`data-visible`)!==`true`)continue;let t=me(e.element)[0];if(t)return o(t),!0}return!1},H=()=>P&&P.isConnected?(o(P),!0):!1,W=e=>{let t=p.activeElement;!(t instanceof HTMLElement)||!f.contains(t)||e&&!e.contains(t)||V()||H()||t.blur()},G=e=>{a(n,`toast:change`,{id:e,action:`dismiss`}),ee?.(e)},K=e=>{I(e)&&(Q.cancel(e),L.unobserve(e.element),e.destroy(),A.delete(e.id),L.update())},q=(e,t=!0)=>{!I(e)||!e.active||(Q.cancel(e),e.dismiss(),L.update(),t&&W(e.element),G(e.id))},ve=e=>{let t=A.get(e);t&&q(t)},J=()=>{let e=``;do N+=1,e=`toast-${N}`;while(A.has(e));return e},Y=(e,t)=>{let r=S(e)?e:J(),i=ae(ie(r,b),t),o=A.get(r),s=o?.active;o&&K(o),s&&G(r);let c=fe(i,{viewport:f,template:m,expanded:R.expanded,paused:R.paused,onTimeout:q,onExitComplete:K});if(j.isDestroyed)return c.destroy(),c;let l=A.get(r);return l&&K(l),A.set(r,c),c.mount(),L.observe(c.element),L.update(),I(c)&&c.active&&(a(n,`toast:change`,{id:r,action:`show`}),E?.(r)),c},ye=e=>j.isDestroyed?e.id??J():Y(e.id,e).id,X=(e,t)=>{!I(e)||!e.active||(e.update(t),e.toast.dismissible||Q.cancel(e),L.update())},be=(e,t)=>{let n=A.get(e);n&&X(n,t)},xe=(e,t)=>{let n=k(le(t.loading),{title:`Loading...`,type:`loading`,duration:0,description:t.description}),r=j.isDestroyed?void 0:Y(void 0,n),i=r?.id??J(),a=Promise.resolve().then(()=>typeof e==`function`?e():e).then(e=>{let n=k(ue(t.success,e),{title:`Success`,type:`success`,duration:b,description:t.description});return r&&X(r,n),e}).catch(e=>{let n=k(ue(t.error,e),{title:de(e),type:`error`,duration:b,description:t.description});throw r&&X(r,n),e});return a.catch(()=>{}),{id:i,unwrap:()=>a}},Se=()=>{let e=p.activeElement instanceof HTMLElement&&f.contains(p.activeElement);for(let e of F())q(e,!1);e&&W()},Ce=()=>{let e=!1;return{get defaultPrevented(){return e},preventDefault:()=>{e=!0}}},we=e=>{let t=A.get(e);if(!t||t.exiting)return;let r=t.toast.action?.value,i=Ce();t.toast.action?.onClick?.(i),a(n,`toast:action`,{id:e,value:r}),te?.(e,r),i.defaultPrevented||q(t)},Z=e=>{if(!(e instanceof Element))return;let t=e.closest(`[data-slot=\"toast-item\"]`);if(!t||!f.contains(t))return;let n=A.get(t.getAttribute(`data-id`)??``);return n?.element===t?n:void 0},Q=ge({viewport:f,position:x,getEntry:Z,dismiss:e=>q(e)});M.push(h(f,`click`,e=>{let t=e.target;if(!t)return;let n=t.closest(`[data-slot=\"toast-close\"]`);if(n){let e=Z(n);e&&q(e);return}let r=t.closest(`[data-slot=\"toast-action\"]`);if(r){let e=Z(r);e&&we(e.id)}}));let Te=e=>z(e)||B();M.push(h(f,`pointerenter`,()=>{C&&R.set(`hover`,!0)}),h(f,`pointerleave`,()=>{C&&R.set(`hover`,!1)}),h(f,`focusin`,e=>{if(!w)return;let{relatedTarget:t,target:n}=e;t instanceof HTMLElement&&!f.contains(t)&&(P=t),R.set(`focus`,Te(n))}),h(f,`focusout`,e=>{w&&R.set(`focus`,Te(e.relatedTarget))}));let Ee=p.defaultView??window;M.push(h(Ee,`blur`,()=>R.set(`window`,!0)),h(Ee,`focus`,()=>R.set(`window`,!1)),h(p,`visibilitychange`,()=>R.set(`document`,p.visibilityState===`hidden`))),M.push(h(n,`toast:show`,e=>{let t=oe(e.detail);!t||j.isDestroyed||Y(t.id,t.patch)}),h(n,`toast:update`,e=>{let t=se(e.detail);t&&be(t.id,t.patch)}),h(n,`toast:dismiss`,e=>{let t=ce(e.detail);t&&ve(t)}),h(n,`toast:clear`,()=>{Se()})),j.onDestroy(()=>{M.forEach(e=>e()),Q.destroy(),L.destroy();for(let e of A.values())e.destroy();A.clear(),P=null,O.cleanup(),e(n,U,$)}),L.update();let $={show:ye,update:be,promise:xe,dismiss:ve,dismissAll:Se,get count(){return F().length},destroy:()=>{j.destroy()}};return _(n,U,$),$}function G(e=document){let t=[];for(let n of f(e,`toast`))p(n,U)||t.push(W(n));return t}export{G as create,W as createToast};", + "type": "registry:file", + "target": "~/assets/data-slot-toast.js" + }, + { + "path": "vendor/data-slot-toggle.js", + "content": "import{clearRootBinding as e,emit as t,getDataBool as n,getRoots as r,hasRootBinding as i,on as a,onRoot as o,reuseRootBinding as s,setAria as c,setRootBinding as l}from\"@data-slot/core\";const u=`@data-slot/toggle`;function d(r,i={}){let d=s(r,u,`[@data-slot/toggle] createToggle() called more than once for the same root. Returning the existing controller. Destroy it before rebinding with new options.`);if(d)return d;let f=i.defaultPressed??n(r,`defaultPressed`)??!1,p=i.disabled??n(r,`disabled`)??!1,m=i.onPressedChange,h=f,g=[],_=()=>r.hasAttribute(`disabled`)||r.getAttribute(`aria-disabled`)===`true`,v=(e,n=!1)=>{h===e&&!n||(h=e,c(r,`pressed`,h),r.dataset.state=h?`on`:`off`,n||(t(r,`toggle:change`,{pressed:h}),m?.(h)))};p&&(r.tagName===`BUTTON`&&r.setAttribute(`disabled`,``),r.setAttribute(`aria-disabled`,`true`)),r.tagName===`BUTTON`&&!r.hasAttribute(`type`)&&r.setAttribute(`type`,`button`),v(h,!0),g.push(a(r,`click`,()=>{_()||v(!h)})),g.push(o(r,`toggle:set`,e=>{if(_())return;let t=e.detail,n;typeof t==`boolean`?n=t:t?.value===void 0?t?.pressed!==void 0&&(n=t.pressed):n=t.value,typeof n==`boolean`&&v(n)}));let y={toggle:()=>v(!h),press:()=>v(!0),release:()=>v(!1),get pressed(){return h},destroy:()=>{g.forEach(e=>e()),g.length=0,e(r,u,y)}};return l(r,u,y),y}function f(e=document){let t=[];for(let n of r(e,`toggle`))i(n,u)||t.push(d(n));return t}export{f as create,d as createToggle};", + "type": "registry:file", + "target": "~/assets/data-slot-toggle.js" + }, + { + "path": "vendor/data-slot-toggle-group.js", + "content": "import{clearRootBinding as e,emit as t,ensureId as n,getDataBool as r,getDataEnum as i,getDataString as a,getParts as o,getRoots as s,hasRootBinding as c,on as l,onRoot as u,reuseRootBinding as d,setAria as f,setRootBinding as p}from\"@data-slot/core\";const m=[`horizontal`,`vertical`],h=`@data-slot/toggle-group`;function g(s,c={}){let g=d(s,h,`[@data-slot/toggle-group] createToggleGroup() called more than once for the same root. Returning the existing controller. Destroy it before rebinding with new options.`);if(g)return g;let _=o(s,`toggle-group-item`);if(_.length===0)throw Error(`ToggleGroup requires at least one toggle-group-item`);let v=c.multiple??r(s,`multiple`)??!1,y=c.orientation??i(s,`orientation`,m)??`horizontal`,b=c.loop??r(s,`loop`)??!0,x=c.disabled??r(s,`disabled`)??!1,S=c.onValueChange,C=(()=>{if(c.defaultValue!==void 0)return Array.isArray(c.defaultValue)?c.defaultValue:c.defaultValue.split(/\\s+/).filter(Boolean);let e=a(s,`defaultValue`);return e?e.split(/\\s+/).filter(Boolean):[]})(),w=[],T=new Map,E=new Map,D=[];for(let e of _){let t=(e.dataset.value||``).trim();if(!t){D.push(e);continue}let n={el:e,value:t,disabled:e.hasAttribute(`disabled`)||e.hasAttribute(`data-disabled`)||e.getAttribute(`aria-disabled`)===`true`};w.push(n),T.set(t,n),E.set(e,n)}for(let e of D)e.tabIndex=-1,e.setAttribute(`aria-disabled`,`true`);if(w.length===0)throw Error(`ToggleGroup requires at least one toggle-group-item with a data-value attribute`);let O=new Set;for(let e of C)if(T.has(e)&&(O.add(e),!v))break;let k=[],A=()=>s.hasAttribute(`disabled`)||s.hasAttribute(`data-disabled`)||s.getAttribute(`aria-disabled`)===`true`,j=e=>e.el.hasAttribute(`disabled`)||e.el.hasAttribute(`data-disabled`)||e.el.getAttribute(`aria-disabled`)===`true`,M=()=>w.filter(e=>!j(e));s.setAttribute(`role`,`group`),x&&s.setAttribute(`aria-disabled`,`true`),y===`vertical`&&f(s,`orientation`,`vertical`),v&&(s.dataset.multiple=``);for(let e of w){let{el:t,disabled:r}=e;n(t,`toggle-group-item`),t.tabIndex=-1,t.tagName===`BUTTON`&&!t.hasAttribute(`type`)&&(t.type=`button`),r&&(t.setAttribute(`aria-disabled`,`true`),t.tagName===`BUTTON`&&(t.disabled=!0))}let N=(e,n=!1)=>{let r=!1;for(let t of w){let i=e.has(t.value);!n&&i===O.has(t.value)||(r=!0,f(t.el,`pressed`,i),t.el.dataset.state=i?`on`:`off`)}O=e,I();let i=[...O];s.setAttribute(`data-value`,i.join(` `)),r&&!n&&(t(s,`toggle-group:change`,{value:i}),S?.(i))},P,F=e=>{e!==P&&(P&&(P.el.tabIndex=-1),e&&(e.el.tabIndex=0),P=e)},I=()=>{let e=M();F(e.find(e=>O.has(e.value))??e[0])};N(O,!0);let L=e=>{let t=new Set(O);v?t.has(e)?t.delete(e):t.add(e):t.has(e)?t.clear():(t.clear(),t.add(e)),N(t)},R=e=>{let t=Array.isArray(e)?e:e.split(/\\s+/).filter(Boolean),n=new Set;for(let e of t)if(T.has(e)&&(n.add(e),!v))break;N(n)};k.push(l(s,`click`,e=>{if(A())return;let t=e.target.closest?.(`[data-slot=\"toggle-group-item\"]`);if(!t)return;let n=E.get(t);!n||j(n)||L(n.value)}));let z=y===`horizontal`,B=z?`ArrowLeft`:`ArrowUp`,V=z?`ArrowRight`:`ArrowDown`;k.push(l(s,`keydown`,e=>{if(A())return;let t=e.target.closest?.(`[data-slot=\"toggle-group-item\"]`);if(!t)return;let n=E.get(t);if(!n)return;if(e.key===`Enter`||e.key===` `){j(n)&&e.preventDefault();return}let r=M();if(r.length===0)return;let i=r.findIndex(e=>e.el===t);i===-1&&(i=0);let a=i;switch(e.key){case B:a=i-1,a<0&&(a=b?r.length-1:0);break;case V:a=i+1,a>=r.length&&(a=b?0:r.length-1);break;case`Home`:a=0;break;case`End`:a=r.length-1;break;default:return}e.preventDefault();let o=r[a];o&&(F(o),o.el.focus())})),k.push(u(s,`toggle-group:set`,e=>{if(A())return;let t=e.detail,n;typeof t==`string`||Array.isArray(t)?n=t:t&&typeof t==`object`&&`value`in t&&(n=t.value),n!==void 0&&R(n)}));let H={setValue:e=>R(e),toggle:e=>L(e),get value(){return[...O]},destroy:()=>{k.forEach(e=>e()),k.length=0,e(s,h,H)}};return p(s,h,H),H}function _(e=document){let t=[];for(let n of s(e,`toggle-group`))c(n,h)||t.push(g(n));return t}export{_ as create,g as createToggleGroup};", + "type": "registry:file", + "target": "~/assets/data-slot-toggle-group.js" + }, + { + "path": "vendor/data-slot-tooltip.js", + "content": "import{clearRootBinding as e,computeFloatingPosition as t,computeFloatingTransformOrigin as n,createContentMount as r,createDismissLayer as i,createPortalLifecycle as a,createPositionSync as o,createPresenceLifecycle as s,createTerminalLifecycle as c,emit as l,ensureId as u,getDataBool as d,getDataEnum as f,getDataNumber as p,getOwnedElements as ee,getPart as m,getRoots as h,hasRootBinding as g,measurePopupContentRect as te,on as _,onRoot as ne,registerFloatingTerminalResources as re,reuseRootBinding as ie,setRootBinding as ae}from\"@data-slot/core\";const v=`@data-slot/tooltip`;let y=0;const b=new Set,x=new Set,S=(e,t)=>{if(!e)return!1;for(let n of b)if(n!==t&&n.contains(e))return!0;return!1},oe=(e,t)=>{for(let n of x)n(e,t)},se=[`top`,`right`,`bottom`,`left`,`inline-start`,`inline-end`],ce=[`start`,`center`,`end`],le=(e,t)=>e===`inline-start`?t===`rtl`?`right`:`left`:e===`inline-end`?t===`rtl`?`left`:`right`:e,C=(...e)=>[...new Set(e.filter(e=>e!=null))];function w(h,g={}){let w=ie(h,v,`[@data-slot/tooltip] createTooltip() called more than once for the same root. Returning the existing controller. Destroy it before rebinding with new options.`);if(w)return w;let T=m(h,`tooltip-trigger`),E=m(h,`tooltip-content`),D=E?ee(h,E,`[data-slot=\"tooltip-arrow\"]`)[0]??null:null,O=m(h,`tooltip-positioner`),k=O&&E&&O.contains(E)?O:null,A=m(h,`tooltip-portal`),j=A&&k&&A.contains(k)?A:null;if(!T||!E)throw Error(`Tooltip requires trigger and content slots`);D&&(D.setAttribute(`aria-hidden`,`true`),D.style.position=`absolute`);let ue=g.delay??p(h,`delay`)??300,de=g.skipDelayDuration??p(h,`skipDelayDuration`)??300,fe=g.onOpenChange,pe=g.portal??d(E,`portal`)??d(h,`portal`)??!0,me=(e,t)=>f(E,e,t)??(k?f(k,e,t):void 0)??f(h,e,t),M=e=>p(E,e)??(k?p(k,e):void 0)??p(h,e),he=e=>d(E,e)??(k?d(k,e):void 0)??d(h,e),N=g.side??me(`side`,se)??`top`,P=g.align??me(`align`,ce)??`center`,ge=g.sideOffset??M(`sideOffset`)??4,_e=g.alignOffset??M(`alignOffset`)??0,ve=g.avoidCollisions??he(`avoidCollisions`)??!0,ye=g.collisionPadding??M(`collisionPadding`)??8,F=!1,I=null,L=!1,R=c(),z=null,B=[],V=a({content:E,root:h,enabled:pe,wrapperSlot:k?void 0:`tooltip-positioner`,container:k??void 0,mountTarget:k?j??k:void 0}),be=g.mountStrategy??f(h,`mountStrategy`,[`lazy`,`eager`])??`lazy`,H=r({root:h,target:j??k??E,strategy:be});B.push(()=>H.cleanup());let U=u(E,`tooltip-content`),W=!1,xe=!1,G=()=>T.getAttribute(`aria-describedby`)?.split(/\\s+/).filter(Boolean)??[],Se=()=>{let e=G();e.includes(U)||(T.setAttribute(`aria-describedby`,[...e,U].join(` `)),W=!0)},K=()=>{if(!W)return;let e=G().filter(e=>e!==U);e.length?T.setAttribute(`aria-describedby`,e.join(` `)):T.removeAttribute(`aria-describedby`),W=!1},Ce=()=>{be===`lazy`&&G().includes(U)&&(xe||=!W,W=!0,K()),H.unmount()};E.setAttribute(`role`,`tooltip`);let we=()=>{let e=h instanceof HTMLElement?h:null;return(e?.getAttribute(`dir`)??T.getAttribute(`dir`))===`rtl`||(getComputedStyle(T).direction||(e?getComputedStyle(e).direction:``)||h.ownerDocument.documentElement.getAttribute(`dir`)||``)===`rtl`?`rtl`:`ltr`},Te=(e,t)=>{let n=V.container;for(let r of C(E,n,D))r.setAttribute(`data-side`,e),r.setAttribute(`data-align`,t)},Ee=e=>{let t=V.container;for(let n of C(h,E,t,D))e?n.setAttribute(`data-instant`,e):n.removeAttribute(`data-instant`)},De=(e,t,n,r)=>{if(!D)return;D.style.position=`absolute`;let i=D.getBoundingClientRect(),a=D.offsetWidth>0?D.offsetWidth:i.width,o=D.offsetHeight>0?D.offsetHeight:i.height;if(a<=0||o<=0){D.style.removeProperty(`left`),D.style.removeProperty(`top`),D.removeAttribute(`data-uncentered`);return}let s=le(e,t);if(s===`top`||s===`bottom`){let e=n.left+n.width/2-r.left-a/2,t=Math.max(5,r.width-a-5),i=Math.min(Math.max(e,5),t);D.style.left=`${i}px`,D.style.removeProperty(`top`),Math.abs(i-e)>.5?D.setAttribute(`data-uncentered`,``):D.removeAttribute(`data-uncentered`);return}let c=n.top+n.height/2-r.top-o/2,l=Math.max(5,r.height-o-5),u=Math.min(Math.max(c,5),l);D.style.top=`${u}px`,D.style.removeProperty(`left`),Math.abs(u-c)>.5?D.setAttribute(`data-uncentered`,``):D.removeAttribute(`data-uncentered`)},q=e=>{let t=V.container;if(h.setAttribute(`data-state`,e),E.setAttribute(`data-state`,e),Ee(I),e===`open`){for(let e of C(h,E,t,D))e.setAttribute(`data-open`,``),e.removeAttribute(`data-closed`);return}for(let e of C(h,E,t,D))e.setAttribute(`data-closed`,``),e.removeAttribute(`data-open`)},Oe=()=>{let e=V.container,r=h.ownerDocument.defaultView??window,i=we(),a=T.getBoundingClientRect(),o=te(E),s=t({anchorRect:a,contentRect:o,side:N,align:P,sideOffset:ge,alignOffset:_e,avoidCollisions:ve,collisionPadding:ye,direction:i}),c=n({side:s.side,align:s.align,anchorRect:a,popupX:s.x,popupY:s.y,direction:i});e.style.position=`absolute`,e.style.top=`0px`,e.style.left=`0px`,e.style.transform=`translate3d(${s.x+r.scrollX}px, ${s.y+r.scrollY}px, 0)`,e.style.setProperty(`--transform-origin`,c),e.style.willChange=`transform`,e.style.margin=`0`,Te(s.side,s.align),De(s.side,i,a,{top:s.y,left:s.x,right:s.x+o.width,bottom:s.y+o.height,width:o.width,height:o.height})},J=s({element:E,onExitComplete:()=>{R.isDestroyed||(V.restore(),E.hidden=!0,Ce())}}),Y=o({observedElements:[T,E],isActive:()=>F,ancestorScroll:!1,onUpdate:Oe}),X=()=>T.hasAttribute(`disabled`)||T.getAttribute(`aria-disabled`)===`true`,Z=(e,t,n=null)=>{R.isDestroyed||F!==e&&(!e&&F&&de>0&&(y=Date.now()+de),I=n,F=e,F?(H.mount(),V.mount(),Se(),E.setAttribute(`aria-hidden`,`false`),E.hidden=!1,q(`open`),J.enter(),Oe(),Y.start()):(q(`closed`),K(),E.setAttribute(`aria-hidden`,`true`),J.exit(),Y.stop()),l(h,`tooltip:change`,{open:F,trigger:T,content:E,reason:t}),fe?.(F))},ke=e=>{if(z&&=(R.cancelTimeout(z),null),Date.now(){R.isDestroyed||(Z(!0,e,e===`focus`?`focus`:null),z=null)},ue)},Q=(e,t=null)=>{z&&=(R.cancelTimeout(z),null),Z(!1,e,t)},Ae=(e,t)=>{e===T||!F||(L=!1,Q(t,`delay`))};b.add(T),x.add(Ae),B.push(()=>{x.delete(Ae),b.delete(T)}),E.hidden=!0,E.setAttribute(`aria-hidden`,`true`),Te(N,P),q(`closed`),B.push(_(T,`pointerenter`,e=>{e.pointerType!==`touch`&&(X()||(oe(T,`pointer`),ke(`pointer`)))}),_(T,`pointerleave`,e=>{if(e.pointerType===`touch`||L)return;let t=e.relatedTarget;if(!(t&&E.contains(t))){if(S(t,T)){Q(`pointer`,`delay`);return}Q(`pointer`)}}),_(T,`click`,()=>{if(!X()){if(z){R.cancelTimeout(z),z=null;return}F&&Q(`pointer`,`dismiss`)}}),_(T,`focus`,()=>{L=!0,!X()&&(oe(T,`focus`),ke(`focus`))}),_(T,`blur`,e=>{L=!1;let t=e.relatedTarget;if(S(t,T)){Q(`blur`,`delay`);return}Q(`blur`)})),B.push(_(E,`pointerleave`,e=>{if(e.pointerType===`touch`||L)return;let t=e.relatedTarget;if(!(t&&T.contains(t))){if(S(t,T)){Q(`pointer`,`delay`);return}Q(`pointer`)}})),B.push(ne(h,`tooltip:set`,e=>{let t=e.detail,n;if(t?.open===void 0?t?.value!==void 0&&(n=t.value):n=t.open,typeof n==`boolean`)if(n){if(X())return;z&&=(R.cancelTimeout(z),null),Z(!0,`api`)}else Q(`api`)})),B.push(i({root:h,isOpen:()=>F,onDismiss:()=>Q(`escape`,`dismiss`),closeOnClickOutside:!1,closeOnEscape:!0,preventEscapeDefault:!1}));let $={show:()=>{R.isDestroyed||X()||(z&&=(R.cancelTimeout(z),null),Z(!0,`api`))},hide:()=>{R.isDestroyed||Q(`api`)},get isOpen(){return F},destroy:()=>{R.destroy()&&(z&&R.cancelTimeout(z),z=null,F=!1,q(`closed`),K(),xe&&Se(),E.setAttribute(`aria-hidden`,`true`),E.hidden=!0)}};return re(R,{cleanups:B,positionSync:Y,presence:J,portal:V,unbind:()=>e(h,v,$)}),Ce(),ae(h,v,$),$}function T(e=document){let t=[];for(let n of h(e,`tooltip`))g(n,v)||t.push(w(n));return t}export{T as create,w as createTooltip};", + "type": "registry:file", + "target": "~/assets/data-slot-tooltip.js" + }, + { + "path": "snippets/runtime.liquid", + "content": "{% doc %}\n Default Fulldev UI runtime. Render once in the head, before other module scripts.\n Use your own import map and createLifecycle when the theme supplies Shopify adapters.\n{% enddoc %}\n\n\n\n", + "type": "registry:file", + "target": "~/snippets/ui-runtime.liquid" + }, + { + "path": "runtime/ui-motion.js", + "content": "const reducedMotion = matchMedia('(prefers-reduced-motion: reduce)');\n\n// A pause button for a CSS animation that runs on its own, such as a marquee. Its label stays Pause\n// and aria-pressed carries the state: pressed while the animation is paused.\nexport function initializeMotionPause(button) {\n const root = button.closest('[data-motion]');\n const events = new AbortController();\n // CSS animates the root only from here on, so it never moves without a working pause button.\n root.dataset.motionReady = '';\n const sync = () => {\n button.hidden = reducedMotion.matches;\n };\n button.addEventListener(\n 'click',\n () => {\n const paused = button.getAttribute('aria-pressed') !== 'true';\n button.setAttribute('aria-pressed', String(paused));\n root.toggleAttribute('data-paused', paused);\n },\n { signal: events.signal },\n );\n reducedMotion.addEventListener('change', sync, { signal: events.signal });\n sync();\n return {\n destroy() {\n events.abort();\n delete root.dataset.motionReady;\n },\n };\n}\n\n// Muted autoplay videos stay still for visitors who prefer reduced motion; they keep their controls.\nexport function initializeAutoplayVideo(video) {\n const events = new AbortController();\n const sync = () => {\n if (reducedMotion.matches) video.pause();\n };\n reducedMotion.addEventListener('change', sync, { signal: events.signal });\n sync();\n return {\n destroy() {\n events.abort();\n },\n };\n}\n", + "type": "registry:file", + "target": "~/assets/ui-motion.js" + } + ], + "type": "registry:item" +} \ No newline at end of file diff --git a/public/r/liquid/section.json b/public/r/liquid/section.json new file mode 100644 index 000000000..a49b0cb0d --- /dev/null +++ b/public/r/liquid/section.json @@ -0,0 +1,24 @@ +{ + "$schema": "https://ui.shadcn.com/schema/registry-item.json", + "name": "section", + "title": "Section", + "description": "Native Liquid section with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json" + ], + "files": [ + { + "path": "components/section.liquid", + "content": "{% doc %}\n Fulldev UI section styles. Use native markup with its data-slot parts.\n{% enddoc %}\n\n{{ 'ui-section.css' | asset_url | stylesheet_tag }}\n", + "type": "registry:file", + "target": "~/snippets/ui-section.liquid" + }, + { + "path": "styles/section.css", + "content": "[data-slot='section'] {\n position: relative;\n display: flex;\n flex-direction: column;\n gap: var(--section-gap);\n padding-block: var(--section-padding);\n background: var(--background);\n color: var(--foreground);\n scroll-margin-top: 48px;\n}\n[data-slot='section'][data-variant='bordered'] {\n padding-block: 32px;\n border-block: 1px solid var(--border);\n}\n/* The first section of a template page, below the header. */\n[data-slot='section'][data-variant='page'] {\n padding-top: 32px;\n}\n/* A framed panel inset by the gutter, as Fulldev UI's floating section. */\n[data-slot='section'][data-variant='floating'] {\n width: calc(100% - 2 * var(--gutter));\n max-width: calc(var(--container) - 2 * var(--gutter));\n margin: 64px auto;\n overflow: hidden;\n border: 1px solid var(--border);\n border-radius: var(--radius-xl);\n box-shadow: var(--shadow-md);\n}\n/* The container only sets the width and gutters; each section lays out its own children. */\n[data-slot='section-container'],\n[data-slot='header-container'] {\n position: relative;\n width: 100%;\n min-width: 0;\n max-width: var(--container);\n margin-inline: auto;\n padding-inline: var(--gutter);\n}\n[data-slot='header-container'] {\n display: flex;\n gap: 16px;\n}\n[data-slot='section-title'] {\n font-size: 1.875rem;\n line-height: 2.25rem;\n text-wrap: balance;\n}\n[data-slot='section-description'] {\n font-size: 1.125rem;\n line-height: 1.625;\n color: var(--muted-foreground);\n text-wrap: balance;\n}\n/* Display headline of centered heroes and banners. */\n.display-title {\n max-width: 56rem;\n font-size: 2.25rem;\n line-height: 1.1;\n text-wrap: balance;\n}\n\n/* The actions of a section intro, wrapping when they do not fit beside each other. */\n.section-actions {\n display: flex;\n flex-wrap: wrap;\n gap: 12px;\n}\n/* A section title set tight, as in Fulldev UI's call to action and content blocks. */\n[data-slot='section-title'].tight-title {\n line-height: 1.1;\n}\n/* The display headline of split heroes: 36px, 48px from 640px. */\n.hero-title {\n font-size: 2.25rem;\n line-height: 1.1;\n text-wrap: balance;\n}\n", + "type": "registry:file", + "target": "~/assets/ui-section.css" + } + ], + "type": "registry:item" +} \ No newline at end of file diff --git a/public/r/liquid/select.json b/public/r/liquid/select.json new file mode 100644 index 000000000..b60f7d88c --- /dev/null +++ b/public/r/liquid/select.json @@ -0,0 +1,22 @@ +{ + "$schema": "https://ui.shadcn.com/schema/registry-item.json", + "name": "select", + "title": "Select", + "description": "Native Liquid select with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/input.json", + "https://ui.full.dev/r/liquid/popup.json", + "https://ui.full.dev/r/liquid/invalid-controls.json", + "https://ui.full.dev/r/liquid/runtime.json" + ], + "files": [ + { + "path": "components/select.liquid", + "content": "{% doc %}\n Fulldev UI select styles. Use native markup with its data-slot parts.\n{% enddoc %}\n\n{% stylesheet %}\n /* The data-slot select: a trigger like the native select and a listbox popup, as Vega's select. */\n [data-slot='select-trigger'] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: calc(var(--spacing) * 1.5);\n width: 100%;\n height: calc(var(--spacing) * 9);\n padding: calc(var(--spacing) * 2) calc(var(--spacing) * 2) calc(var(--spacing) * 2) calc(var(--spacing) * 2.5);\n border: 1px solid var(--input);\n border-radius: var(--radius-md);\n background: light-dark(transparent, color-mix(in oklch, var(--input) 30%, transparent));\n font-size: 0.875rem;\n white-space: nowrap;\n box-shadow: var(--shadow-xs);\n outline: none;\n transition:\n color 150ms,\n box-shadow 150ms;\n }\n [data-slot='select-trigger'][data-placeholder] {\n color: var(--muted-foreground);\n }\n [data-slot='select-trigger']:focus-visible {\n border-color: var(--ring);\n box-shadow: var(--focus-ring);\n }\n [data-slot='select-trigger']:disabled {\n cursor: not-allowed;\n opacity: 0.5;\n }\n [data-slot='select-trigger'] > .icon {\n width: calc(var(--spacing) * 4);\n height: calc(var(--spacing) * 4);\n color: var(--muted-foreground);\n }\n [data-slot='select'][data-size='sm'] [data-slot='select-trigger'] {\n height: calc(var(--spacing) * 8);\n }\n [data-slot='select-value'] {\n flex: 1;\n overflow: hidden;\n text-align: left;\n text-overflow: ellipsis;\n }\n{% endstylesheet %}\n", + "type": "registry:file", + "target": "~/snippets/ui-select.liquid" + } + ], + "type": "registry:item" +} \ No newline at end of file diff --git a/public/r/liquid/separator.json b/public/r/liquid/separator.json new file mode 100644 index 000000000..56f7f2323 --- /dev/null +++ b/public/r/liquid/separator.json @@ -0,0 +1,18 @@ +{ + "$schema": "https://ui.shadcn.com/schema/registry-item.json", + "name": "separator", + "title": "Separator", + "description": "Native Liquid separator with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json" + ], + "files": [ + { + "path": "components/separator.liquid", + "content": "{% doc %}\n Fulldev UI separator styles. Use native markup with its data-slot parts.\n{% enddoc %}\n\n{% stylesheet %}\n [data-slot='separator'] {\n flex-shrink: 0;\n width: 100%;\n height: 1px;\n margin: 0;\n border: 0;\n background: var(--border);\n }\n [data-slot='separator'][data-orientation='vertical'] {\n width: 1px;\n height: auto;\n align-self: stretch;\n }\n{% endstylesheet %}\n", + "type": "registry:file", + "target": "~/snippets/ui-separator.liquid" + } + ], + "type": "registry:item" +} \ No newline at end of file diff --git a/public/r/liquid/sheet.json b/public/r/liquid/sheet.json new file mode 100644 index 000000000..cc5e389de --- /dev/null +++ b/public/r/liquid/sheet.json @@ -0,0 +1,20 @@ +{ + "$schema": "https://ui.shadcn.com/schema/registry-item.json", + "name": "sheet", + "title": "Sheet", + "description": "Native Liquid sheet with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/dialog.json", + "https://ui.full.dev/r/liquid/runtime.json" + ], + "files": [ + { + "path": "components/sheet.liquid", + "content": "{% doc %}\n Fulldev UI sheet styles. Use native markup with its data-slot parts.\n{% enddoc %}\n\n{% stylesheet %}\n /* Dialog content with data-side='right' is a sheet, as shadcn's SheetContent. */\n [data-slot='dialog-content'][data-side='right'] {\n position: fixed;\n inset: 0 0 0 auto;\n z-index: calc(var(--dialog-content-stack-index, 0) * 2 + 101);\n display: flex;\n flex-direction: column;\n gap: 0;\n width: 75%;\n max-width: 24rem;\n height: 100%;\n max-height: none;\n overflow-y: auto;\n padding: 0;\n border-left: 1px solid var(--border);\n border-radius: 0;\n background: var(--popover);\n color: var(--popover-foreground);\n font-size: 0.875rem;\n box-shadow: var(--shadow-lg);\n opacity: 1;\n transform: translateX(0);\n transition:\n opacity 200ms ease-in-out,\n transform 200ms ease-in-out;\n }\n [data-slot='dialog-content'][data-side='right'][data-starting-style],\n [data-slot='dialog-content'][data-side='right'][data-ending-style] {\n opacity: 0;\n transform: translateX(2.5rem);\n }\n [data-slot='sheet-header'] {\n position: sticky;\n top: 0;\n z-index: 1;\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: calc(var(--spacing) * 3);\n padding: calc(var(--spacing) * 4);\n background: var(--popover);\n }\n [data-slot='sheet-header'] [data-slot='dialog-title'] {\n font-size: 1rem;\n font-weight: 500;\n letter-spacing: var(--tracking-normal);\n line-height: 1.5;\n }\n [data-slot='sheet-header'] [data-slot='dialog-close'] {\n color: var(--muted-foreground);\n }\n [data-slot='sheet-footer'] {\n position: sticky;\n bottom: 0;\n display: flex;\n flex-direction: column;\n gap: calc(var(--spacing) * 2);\n margin-top: auto;\n padding: calc(var(--spacing) * 4);\n border-top: 1px solid var(--border);\n background: var(--popover);\n }\n{% endstylesheet %}\n", + "type": "registry:file", + "target": "~/snippets/ui-sheet.liquid" + } + ], + "type": "registry:item" +} \ No newline at end of file diff --git a/public/r/liquid/skeleton.json b/public/r/liquid/skeleton.json new file mode 100644 index 000000000..55b008aef --- /dev/null +++ b/public/r/liquid/skeleton.json @@ -0,0 +1,18 @@ +{ + "$schema": "https://ui.shadcn.com/schema/registry-item.json", + "name": "skeleton", + "title": "Skeleton", + "description": "Native Liquid skeleton with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json" + ], + "files": [ + { + "path": "components/skeleton.liquid", + "content": "{% doc %}\n Fulldev UI skeleton styles. Use native markup with its data-slot parts.\n{% enddoc %}\n\n{% stylesheet %}\n /* A placeholder while content loads, as Fulldev UI's skeleton. */\n [data-slot='skeleton'] {\n border-radius: var(--radius-md);\n background: var(--muted);\n animation: skeleton-pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;\n }\n @keyframes skeleton-pulse {\n 50% {\n opacity: 0.5;\n }\n }\n{% endstylesheet %}\n", + "type": "registry:file", + "target": "~/snippets/ui-skeleton.liquid" + } + ], + "type": "registry:item" +} \ No newline at end of file diff --git a/public/r/liquid/slider.json b/public/r/liquid/slider.json new file mode 100644 index 000000000..e782f85c3 --- /dev/null +++ b/public/r/liquid/slider.json @@ -0,0 +1,19 @@ +{ + "$schema": "https://ui.shadcn.com/schema/registry-item.json", + "name": "slider", + "title": "Slider", + "description": "Native Liquid slider with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/runtime.json" + ], + "files": [ + { + "path": "components/slider.liquid", + "content": "{% doc %}\n Fulldev UI slider styles. Use native markup with its data-slot parts.\n{% enddoc %}\n\n{% stylesheet %}\n /* The controller positions the range and thumbs; these rules only draw them. */\n [data-slot='slider'] {\n width: 100%;\n }\n [data-slot='slider-control'] {\n position: relative;\n display: flex;\n align-items: center;\n width: 100%;\n min-height: calc(var(--spacing) * 4);\n touch-action: none;\n user-select: none;\n }\n [data-slot='slider-control'][data-disabled] {\n opacity: 0.5;\n }\n [data-slot='slider-track'] {\n position: relative;\n flex-grow: 1;\n height: 6px;\n overflow: hidden;\n border-radius: 9999px;\n background: var(--muted);\n }\n [data-slot='slider-range'] {\n height: 100%;\n background: var(--primary);\n }\n [data-slot='slider-thumb'] {\n display: block;\n flex-shrink: 0;\n width: calc(var(--spacing) * 4);\n height: calc(var(--spacing) * 4);\n border: 1px solid var(--primary);\n border-radius: 50%;\n background: var(--background);\n box-shadow: var(--shadow-sm);\n outline: none;\n transition: box-shadow 150ms;\n }\n [data-slot='slider-thumb']:hover,\n [data-slot='slider-thumb']:focus-visible {\n box-shadow: 0 0 0 4px color-mix(in oklch, var(--ring) 50%, transparent);\n }\n{% endstylesheet %}\n", + "type": "registry:file", + "target": "~/snippets/ui-slider.liquid" + } + ], + "type": "registry:item" +} \ No newline at end of file diff --git a/public/r/liquid/spinner.json b/public/r/liquid/spinner.json new file mode 100644 index 000000000..0c49ca1d9 --- /dev/null +++ b/public/r/liquid/spinner.json @@ -0,0 +1,18 @@ +{ + "$schema": "https://ui.shadcn.com/schema/registry-item.json", + "name": "spinner", + "title": "Spinner", + "description": "Native Liquid spinner with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json" + ], + "files": [ + { + "path": "components/spinner.liquid", + "content": "{% doc %}\n Fulldev UI spinner styles. Use native markup with its data-slot parts.\n{% enddoc %}\n\n{% stylesheet %}\n /* Fulldev UI's spinner: a turning loader icon in a role=status element with a translated label. */\n [data-slot='spinner'] {\n display: inline-flex;\n flex-shrink: 0;\n width: calc(var(--spacing) * 4);\n height: calc(var(--spacing) * 4);\n animation: spinner-turn 1s linear infinite;\n }\n [data-slot='spinner'] > .icon {\n width: 100%;\n height: 100%;\n }\n @keyframes spinner-turn {\n to {\n rotate: 360deg;\n }\n }\n @media (prefers-reduced-motion: reduce) {\n [data-slot='spinner'] {\n animation-duration: 3s;\n }\n }\n{% endstylesheet %}\n", + "type": "registry:file", + "target": "~/snippets/ui-spinner.liquid" + } + ], + "type": "registry:item" +} \ No newline at end of file diff --git a/public/r/liquid/switch.json b/public/r/liquid/switch.json new file mode 100644 index 000000000..3611b1ab6 --- /dev/null +++ b/public/r/liquid/switch.json @@ -0,0 +1,20 @@ +{ + "$schema": "https://ui.shadcn.com/schema/registry-item.json", + "name": "switch", + "title": "Switch", + "description": "Native Liquid switch with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/invalid-controls.json", + "https://ui.full.dev/r/liquid/runtime.json" + ], + "files": [ + { + "path": "components/switch.liquid", + "content": "{% doc %}\n Fulldev UI switch styles. Use native markup with its data-slot parts.\n{% enddoc %}\n\n{% stylesheet %}\n [data-slot='switch'] {\n display: inline-flex;\n flex-shrink: 0;\n align-items: center;\n width: 32px;\n height: 18.4px;\n border: 1px solid transparent;\n border-radius: 9999px;\n background: light-dark(var(--input), color-mix(in oklch, var(--input) 80%, transparent));\n box-shadow: var(--shadow-xs);\n cursor: pointer;\n outline: none;\n transition:\n background-color 150ms,\n box-shadow 150ms;\n }\n [data-slot='switch'][data-checked] {\n background: var(--primary);\n }\n [data-slot='switch']:focus-visible {\n border-color: var(--ring);\n box-shadow: var(--focus-ring);\n }\n [data-slot='switch'][data-disabled] {\n cursor: not-allowed;\n opacity: 0.5;\n }\n [data-slot='switch-thumb'] {\n display: block;\n width: calc(var(--spacing) * 4);\n height: calc(var(--spacing) * 4);\n border-radius: 50%;\n background: light-dark(var(--background), var(--foreground));\n pointer-events: none;\n transition: translate 150ms;\n }\n [data-slot='switch-thumb'][data-checked] {\n background: light-dark(var(--background), var(--primary-foreground));\n translate: calc(100% - 2px) 0;\n }\n [data-slot='switch'][data-size='sm'] {\n width: 24px;\n height: 14px;\n }\n [data-slot='switch'][data-size='sm'] [data-slot='switch-thumb'] {\n width: calc(var(--spacing) * 3);\n height: calc(var(--spacing) * 3);\n }\n{% endstylesheet %}\n", + "type": "registry:file", + "target": "~/snippets/ui-switch.liquid" + } + ], + "type": "registry:item" +} \ No newline at end of file diff --git a/public/r/liquid/table.json b/public/r/liquid/table.json new file mode 100644 index 000000000..ea8f96b1d --- /dev/null +++ b/public/r/liquid/table.json @@ -0,0 +1,18 @@ +{ + "$schema": "https://ui.shadcn.com/schema/registry-item.json", + "name": "table", + "title": "Table", + "description": "Native Liquid table with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json" + ], + "files": [ + { + "path": "components/table.liquid", + "content": "{% doc %}\n Fulldev UI table styles. Use native markup with its data-slot parts.\n{% enddoc %}\n\n{% stylesheet %}\n /* Vega's table: rows between rules, a muted footer and the caption below. */\n [data-slot='table-container'] {\n position: relative;\n width: 100%;\n overflow-x: auto;\n }\n [data-slot='table'] {\n width: 100%;\n border-collapse: collapse;\n caption-side: bottom;\n font-size: 0.875rem;\n }\n [data-slot='table-header'] [data-slot='table-row'] {\n border-bottom: 1px solid var(--border);\n }\n [data-slot='table-row'] {\n border-bottom: 1px solid var(--border);\n transition: background-color 150ms;\n }\n [data-slot='table-body'] [data-slot='table-row']:last-child {\n border-bottom: 0;\n }\n [data-slot='table-body'] [data-slot='table-row']:hover {\n background: color-mix(in oklch, var(--muted) 50%, transparent);\n }\n [data-slot='table-row'][data-state='selected'] {\n background: var(--muted);\n }\n [data-slot='table-footer'] {\n border-top: 1px solid var(--border);\n background: color-mix(in oklch, var(--muted) 50%, transparent);\n font-weight: 500;\n }\n [data-slot='table-footer'] [data-slot='table-row']:last-child {\n border-bottom: 0;\n }\n [data-slot='table-head'] {\n height: calc(var(--spacing) * 10);\n padding-inline: calc(var(--spacing) * 2);\n color: var(--foreground);\n font-weight: 500;\n text-align: left;\n vertical-align: middle;\n white-space: nowrap;\n }\n [data-slot='table-cell'] {\n padding: calc(var(--spacing) * 2);\n vertical-align: middle;\n white-space: nowrap;\n }\n :is([data-slot='table-head'], [data-slot='table-cell']):has([data-slot='checkbox']) {\n padding-right: 0;\n }\n [data-slot='table-caption'] {\n margin-top: calc(var(--spacing) * 4);\n color: var(--muted-foreground);\n font-size: 0.875rem;\n }\n{% endstylesheet %}\n", + "type": "registry:file", + "target": "~/snippets/ui-table.liquid" + } + ], + "type": "registry:item" +} \ No newline at end of file diff --git a/public/r/liquid/tabs.json b/public/r/liquid/tabs.json new file mode 100644 index 000000000..71aedb065 --- /dev/null +++ b/public/r/liquid/tabs.json @@ -0,0 +1,20 @@ +{ + "$schema": "https://ui.shadcn.com/schema/registry-item.json", + "name": "tabs", + "title": "Tabs", + "description": "Native Liquid tabs with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/triggers.json", + "https://ui.full.dev/r/liquid/runtime.json" + ], + "files": [ + { + "path": "components/tabs.liquid", + "content": "{% doc %}\n Fulldev UI tabs styles. Use native markup with its data-slot parts.\n{% enddoc %}\n\n{% stylesheet %}\n /* Vega's tabs. The controller sets data-state='active' on the selected trigger and its content. */\n [data-slot='tabs'] {\n display: flex;\n flex-direction: column;\n gap: calc(var(--spacing) * 2);\n }\n [data-slot='tabs-list'] {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: fit-content;\n height: calc(var(--spacing) * 9);\n padding: 3px;\n border-radius: var(--radius-lg);\n background: var(--muted);\n color: var(--muted-foreground);\n }\n [data-slot='tabs-trigger'] {\n position: relative;\n display: inline-flex;\n flex: 1;\n align-items: center;\n justify-content: center;\n gap: calc(var(--spacing) * 1.5);\n height: calc(100% - 1px);\n padding: var(--spacing) calc(var(--spacing) * 2);\n border: 1px solid transparent;\n border-radius: var(--radius-md);\n color: color-mix(in oklch, var(--foreground) 60%, transparent);\n font-size: 0.875rem;\n font-weight: 500;\n white-space: nowrap;\n outline: none;\n transition:\n color 150ms,\n background-color 150ms,\n box-shadow 150ms;\n }\n [data-slot='tabs-trigger']:hover {\n color: var(--foreground);\n }\n [data-slot='tabs-trigger']:focus-visible {\n border-color: var(--ring);\n box-shadow: var(--focus-ring);\n }\n [data-slot='tabs-trigger'][data-state='active'] {\n border-color: light-dark(transparent, var(--input));\n background: light-dark(var(--background), color-mix(in oklch, var(--input) 30%, transparent));\n color: var(--foreground);\n box-shadow: var(--shadow-sm);\n }\n [data-slot='tabs-trigger']:disabled {\n opacity: 0.5;\n pointer-events: none;\n }\n [data-slot='tabs-trigger'] > .icon {\n width: calc(var(--spacing) * 4);\n height: calc(var(--spacing) * 4);\n }\n [data-slot='tabs-list'][data-variant='line'] {\n gap: var(--spacing);\n border-radius: 0;\n background: transparent;\n }\n [data-slot='tabs-list'][data-variant='line'] [data-slot='tabs-trigger'][data-state='active'] {\n border-color: transparent;\n background: transparent;\n box-shadow: none;\n }\n [data-slot='tabs-list'][data-variant='line'] [data-slot='tabs-trigger'][data-state='active']::after {\n content: '';\n position: absolute;\n inset-inline: 0;\n bottom: -5px;\n height: 2px;\n background: var(--foreground);\n }\n [data-slot='tabs-content'] {\n flex: 1;\n font-size: 0.875rem;\n outline: none;\n }\n{% endstylesheet %}\n", + "type": "registry:file", + "target": "~/snippets/ui-tabs.liquid" + } + ], + "type": "registry:item" +} \ No newline at end of file diff --git a/public/r/liquid/textarea.json b/public/r/liquid/textarea.json new file mode 100644 index 000000000..7ad1c06de --- /dev/null +++ b/public/r/liquid/textarea.json @@ -0,0 +1,11 @@ +{ + "$schema": "https://ui.shadcn.com/schema/registry-item.json", + "name": "textarea", + "title": "Textarea", + "description": "Native Liquid textarea with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/input.json" + ], + "files": [], + "type": "registry:item" +} \ No newline at end of file diff --git a/public/r/liquid/toast.json b/public/r/liquid/toast.json new file mode 100644 index 000000000..755f3366f --- /dev/null +++ b/public/r/liquid/toast.json @@ -0,0 +1,27 @@ +{ + "$schema": "https://ui.shadcn.com/schema/registry-item.json", + "name": "toast", + "title": "Toast", + "description": "Native Liquid toast with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/triggers.json", + "https://ui.full.dev/r/liquid/button.json", + "https://ui.full.dev/r/liquid/runtime.json" + ], + "files": [ + { + "path": "components/toast.liquid", + "content": "{% doc %}\n Fulldev UI toast styles. Use native markup with its data-slot parts.\n{% enddoc %}\n\n{{ 'ui-toast.css' | asset_url | stylesheet_tag }}\n", + "type": "registry:file", + "target": "~/snippets/ui-toast.liquid" + }, + { + "path": "styles/toast.css", + "content": "/* Stacked notifications, after Fulldev UI's toaster. The controller measures the stack and writes\n the offsets; collapsed toasts behind the front one show only their edge. */\n[data-slot='toast-viewport'] {\n --toast-gap: 0.75rem;\n --toast-collapsed-peek: 0.75rem;\n\n position: fixed;\n right: 1rem;\n bottom: 1rem;\n z-index: 1100;\n width: min(24rem, calc(100vw - 2rem));\n height: var(--toast-stack-size, 0);\n pointer-events: none;\n outline: none;\n}\n[data-slot='toast-item'] {\n position: absolute;\n inset-inline: 0;\n bottom: 0;\n z-index: calc(1000 - var(--toast-index, 0));\n border-radius: var(--radius-2xl);\n background: var(--popover);\n color: var(--popover-foreground);\n box-shadow:\n 0 0 0 1px color-mix(in oklch, var(--foreground) 10%, transparent),\n var(--shadow-lg);\n opacity: 0;\n transform: translate3d(0, calc(var(--toast-lift, -1) * -100%), 0);\n transform-origin: bottom;\n pointer-events: auto;\n outline: none;\n user-select: none;\n transition:\n transform 300ms cubic-bezier(0.22, 1, 0.36, 1),\n opacity 300ms cubic-bezier(0.22, 1, 0.36, 1),\n height 300ms cubic-bezier(0.22, 1, 0.36, 1);\n}\n/* Keeps the pointer on the stack between toasts, so hovering it keeps it expanded. */\n[data-slot='toast-item']::after {\n content: '';\n position: absolute;\n bottom: 100%;\n left: 0;\n width: 100%;\n height: calc(var(--toast-gap, 0px) + 1px);\n}\n[data-slot='toast-item']:focus-visible {\n box-shadow: var(--focus-ring);\n}\n[data-slot='toast-item'][data-mounted='true'] {\n opacity: 1;\n transform: translate3d(0, 0, 0);\n}\n[data-slot='toast-item'][data-mounted='true'][data-expanded='true'] {\n height: var(--toast-initial-height);\n transform: translate3d(0, calc(var(--toast-offset, 0px) * var(--toast-lift, -1)), 0);\n}\n[data-slot='toast-item'][data-mounted='true'][data-expanded='false'][data-front='false'] {\n height: var(--toast-front-height);\n transform: translate3d(0, calc(var(--toast-collapsed-offset-y, 0px) * var(--toast-lift, -1)), 0)\n scale(calc(1 - var(--toast-index, 0) * 0.05));\n}\n[data-slot='toast-item'][data-expanded='false'][data-front='false'][data-open] > * {\n opacity: 0;\n}\n[data-slot='toast-item'][data-visible='false'] {\n opacity: 0;\n pointer-events: none;\n}\n[data-slot='toast-item'][data-removed='true'] {\n opacity: 0;\n}\n[data-slot='toast-item'][data-removed='true'][data-front='true'] {\n transform: translate3d(0, calc(var(--toast-lift, -1) * -100%), 0);\n}\n[data-slot='toast-item'][data-swiping='true'] {\n transition: none;\n transform: translate3d(var(--toast-swipe-amount-x, 0), var(--toast-swipe-amount-y, 0), 0);\n}\n[data-slot='toast-item'][data-removed='true'][data-swipe-out='true'] {\n transform: translate3d(var(--toast-swipe-end-x, 0), var(--toast-swipe-end-y, 0), 0);\n}\n[data-slot='toast-content'] {\n display: flex;\n align-items: center;\n gap: calc(var(--spacing) * 3);\n min-height: calc(var(--spacing) * 16);\n padding: calc(var(--spacing) * 4);\n overflow: hidden;\n transition: opacity 200ms;\n}\n/* The title and description beside the toast's icon. */\n.toast-text {\n display: flex;\n flex: 1;\n flex-direction: column;\n gap: var(--spacing);\n min-width: 0;\n}\n[data-slot='toast-title'] {\n font-size: 0.875rem;\n font-weight: 500;\n}\n[data-slot='toast-description'] {\n color: var(--muted-foreground);\n font-size: 0.875rem;\n}\n[data-slot='toast-description']:empty,\n[data-slot='toast-action']:empty {\n display: none;\n}\n[data-slot='toast-close'] {\n flex-shrink: 0;\n color: var(--muted-foreground);\n}\n[data-slot='toast-close']:hover {\n color: var(--foreground);\n}\n@media (prefers-reduced-motion: reduce) {\n [data-slot='toast-item'],\n [data-slot='drawer-popup'],\n [data-slot='drawer-backdrop'] {\n transition-duration: 0ms;\n }\n}\n", + "type": "registry:file", + "target": "~/assets/ui-toast.css" + } + ], + "type": "registry:item" +} \ No newline at end of file diff --git a/public/r/liquid/toc.json b/public/r/liquid/toc.json new file mode 100644 index 000000000..f4bd97ae1 --- /dev/null +++ b/public/r/liquid/toc.json @@ -0,0 +1,18 @@ +{ + "$schema": "https://ui.shadcn.com/schema/registry-item.json", + "name": "toc", + "title": "Toc", + "description": "Native Liquid toc with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json" + ], + "files": [ + { + "path": "components/toc.liquid", + "content": "{% doc %}\n Fulldev UI toc styles. Use native markup with its data-slot parts.\n{% enddoc %}\n\n{% stylesheet %}\n /* Fulldev UI's toc: links to the headings of a page, the current one in the foreground. */\n [data-slot='toc'] {\n display: flex;\n flex-direction: column;\n gap: calc(var(--spacing) * 3);\n }\n [data-slot='toc-title'] {\n font-size: 0.875rem;\n font-weight: 500;\n letter-spacing: var(--tracking-tight);\n }\n [data-slot='toc-menu'] {\n display: flex;\n flex-direction: column;\n gap: var(--spacing);\n margin: 0;\n padding: 0;\n list-style: none;\n }\n [data-slot='toc-menu-item'] {\n position: relative;\n }\n [data-slot='toc-menu-link'] {\n display: block;\n color: var(--muted-foreground);\n font-size: 0.875rem;\n line-height: 1.5rem;\n transition: color 150ms;\n }\n [data-slot='toc-menu-link']:hover,\n [data-slot='toc-menu-link'][aria-current] {\n color: var(--foreground);\n }\n [data-slot='toc-menu-link'][data-depth='3'] {\n padding-inline-start: calc(var(--spacing) * 4);\n }\n [data-slot='toc-menu-link'][data-depth='4'] {\n padding-inline-start: calc(var(--spacing) * 8);\n }\n{% endstylesheet %}\n", + "type": "registry:file", + "target": "~/snippets/ui-toc.liquid" + } + ], + "type": "registry:item" +} \ No newline at end of file diff --git a/public/r/liquid/toggle-group.json b/public/r/liquid/toggle-group.json new file mode 100644 index 000000000..b8c3f747b --- /dev/null +++ b/public/r/liquid/toggle-group.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://ui.shadcn.com/schema/registry-item.json", + "name": "toggle-group", + "title": "Toggle Group", + "description": "Native Liquid toggle-group with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/toggle.json", + "https://ui.full.dev/r/liquid/triggers.json", + "https://ui.full.dev/r/liquid/runtime.json" + ], + "files": [ + { + "path": "components/toggle-group.liquid", + "content": "{% doc %}\n Fulldev UI toggle-group styles. Use native markup with its data-slot parts.\n{% enddoc %}\n\n{% stylesheet %}\n /* Joined toggles, as Vega's toggle group without spacing. The group sets the variant and size. */\n [data-slot='toggle-group'] {\n display: flex;\n align-items: center;\n width: fit-content;\n border-radius: var(--radius-md);\n }\n [data-slot='toggle-group'][data-variant='outline'] {\n box-shadow: var(--shadow-xs);\n }\n [data-slot='toggle-group-item'] {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: var(--spacing);\n min-width: calc(var(--spacing) * 9);\n height: calc(var(--spacing) * 9);\n padding-inline: calc(var(--spacing) * 2);\n border: 1px solid transparent;\n border-radius: 0;\n background: transparent;\n font-size: 0.875rem;\n font-weight: 500;\n white-space: nowrap;\n outline: none;\n transition:\n background-color 150ms,\n color 150ms,\n box-shadow 150ms;\n }\n [data-slot='toggle-group-item']:first-child {\n border-top-left-radius: var(--radius-md);\n border-bottom-left-radius: var(--radius-md);\n }\n [data-slot='toggle-group-item']:last-child {\n border-top-right-radius: var(--radius-md);\n border-bottom-right-radius: var(--radius-md);\n }\n [data-slot='toggle-group-item']:hover,\n [data-slot='toggle-group-item'][data-state='on'] {\n background: var(--muted);\n color: var(--foreground);\n }\n [data-slot='toggle-group-item']:focus-visible {\n position: relative;\n z-index: 1;\n border-color: var(--ring);\n box-shadow: var(--focus-ring);\n }\n [data-slot='toggle-group-item']:disabled {\n opacity: 0.5;\n pointer-events: none;\n }\n [data-slot='toggle-group-item'] > .icon {\n width: calc(var(--spacing) * 4);\n height: calc(var(--spacing) * 4);\n }\n [data-slot='toggle-group'][data-variant='outline'] [data-slot='toggle-group-item'] {\n border-color: var(--input);\n }\n [data-slot='toggle-group'][data-variant='outline'] [data-slot='toggle-group-item'] + [data-slot='toggle-group-item'] {\n border-left-width: 0;\n }\n [data-slot='toggle-group'][data-size='sm'] [data-slot='toggle-group-item'] {\n min-width: calc(var(--spacing) * 8);\n height: calc(var(--spacing) * 8);\n }\n [data-slot='toggle-group'][data-size='lg'] [data-slot='toggle-group-item'] {\n min-width: calc(var(--spacing) * 10);\n height: calc(var(--spacing) * 10);\n }\n{% endstylesheet %}\n", + "type": "registry:file", + "target": "~/snippets/ui-toggle-group.liquid" + } + ], + "type": "registry:item" +} \ No newline at end of file diff --git a/public/r/liquid/toggle.json b/public/r/liquid/toggle.json new file mode 100644 index 000000000..360da9174 --- /dev/null +++ b/public/r/liquid/toggle.json @@ -0,0 +1,20 @@ +{ + "$schema": "https://ui.shadcn.com/schema/registry-item.json", + "name": "toggle", + "title": "Toggle", + "description": "Native Liquid toggle with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/triggers.json", + "https://ui.full.dev/r/liquid/runtime.json" + ], + "files": [ + { + "path": "components/toggle.liquid", + "content": "{% doc %}\n Fulldev UI toggle styles. Use native markup with its data-slot parts.\n{% enddoc %}\n\n{% stylesheet %}\n /* Option values and filter values: links or labels with the toggle look. The selected value\n also takes a primary border, so it stands out beyond the fill. */\n [data-slot='toggle'] {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: var(--spacing);\n min-width: calc(var(--spacing) * 9);\n height: calc(var(--spacing) * 9);\n padding-inline: calc(var(--spacing) * 2.5);\n border: 1px solid transparent;\n border-radius: var(--radius-md);\n background: transparent;\n font-size: 0.875rem;\n font-weight: 500;\n white-space: nowrap;\n text-decoration: none;\n outline: none;\n transition:\n background-color 150ms,\n border-color 150ms,\n color 150ms,\n box-shadow 150ms;\n }\n [data-slot='toggle'] > .icon {\n width: calc(var(--spacing) * 4);\n height: calc(var(--spacing) * 4);\n }\n [data-slot='toggle']:hover {\n background: var(--muted);\n color: var(--foreground);\n }\n [data-slot='toggle']:focus-visible,\n [data-slot='toggle']:has(:focus-visible) {\n border-color: var(--ring);\n box-shadow: var(--focus-ring);\n }\n [data-slot='toggle']:is([aria-current], [aria-pressed='true'], :has(:checked)) {\n background: var(--muted);\n }\n [data-slot='toggle'][data-variant='outline'] {\n border-color: var(--input);\n box-shadow: var(--shadow-xs);\n }\n [data-slot='toggle'][data-variant='outline']:is([aria-current], [aria-pressed='true'], :has(:checked)) {\n border-color: var(--primary);\n }\n [data-slot='toggle'][data-size='sm'] {\n min-width: calc(var(--spacing) * 8);\n height: calc(var(--spacing) * 8);\n }\n [data-slot='toggle'][data-size='lg'] {\n min-width: calc(var(--spacing) * 10);\n height: calc(var(--spacing) * 10);\n }\n{% endstylesheet %}\n", + "type": "registry:file", + "target": "~/snippets/ui-toggle.liquid" + } + ], + "type": "registry:item" +} \ No newline at end of file diff --git a/public/r/liquid/tooltip.json b/public/r/liquid/tooltip.json new file mode 100644 index 000000000..893f44249 --- /dev/null +++ b/public/r/liquid/tooltip.json @@ -0,0 +1,20 @@ +{ + "$schema": "https://ui.shadcn.com/schema/registry-item.json", + "name": "tooltip", + "title": "Tooltip", + "description": "Native Liquid tooltip with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/popup.json", + "https://ui.full.dev/r/liquid/runtime.json" + ], + "files": [ + { + "path": "components/tooltip.liquid", + "content": "{% doc %}\n Fulldev UI tooltip styles. Use native markup with its data-slot parts.\n{% enddoc %}\n\n{% stylesheet %}\n /* Inverted colors: the scheme's foreground as the fill, as Fulldev UI's tooltip. */\n [data-slot='tooltip-content'] {\n position: relative;\n display: inline-flex;\n align-items: center;\n gap: calc(var(--spacing) * 1.5);\n width: fit-content;\n max-width: 20rem;\n padding: calc(var(--spacing) * 1.5) calc(var(--spacing) * 3);\n border-radius: var(--radius-md);\n background: var(--foreground);\n color: var(--background);\n font-size: 0.75rem;\n line-height: 1.33;\n transform-origin: var(--transform-origin, center);\n transition:\n opacity 100ms,\n scale 100ms;\n }\n [data-slot='tooltip-arrow'] {\n position: absolute;\n width: 10px;\n height: 10px;\n border-radius: 2px;\n background: var(--foreground);\n rotate: 45deg;\n }\n [data-slot='tooltip-arrow'][data-side='bottom'] {\n top: -4px;\n }\n [data-slot='tooltip-arrow'][data-side='top'] {\n bottom: -4px;\n }\n [data-slot='tooltip-arrow'][data-side='left'] {\n right: -4px;\n }\n [data-slot='tooltip-arrow'][data-side='right'] {\n left: -4px;\n }\n{% endstylesheet %}\n", + "type": "registry:file", + "target": "~/snippets/ui-tooltip.liquid" + } + ], + "type": "registry:item" +} \ No newline at end of file diff --git a/public/r/liquid/triggers.json b/public/r/liquid/triggers.json new file mode 100644 index 000000000..c97ff543b --- /dev/null +++ b/public/r/liquid/triggers.json @@ -0,0 +1,18 @@ +{ + "$schema": "https://ui.shadcn.com/schema/registry-item.json", + "name": "triggers", + "title": "Triggers", + "description": "Native Liquid triggers with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json" + ], + "files": [ + { + "path": "components/triggers.liquid", + "content": "{% doc %}\n Fulldev UI triggers styles. Use native markup with its data-slot parts.\n{% enddoc %}\n\n{% stylesheet %}\n /* A control that only works with its controller, such as a popup trigger, a toggle or a tab list,\n shows once the controller has started, which theme.js marks with data-enhanced. Without it, the\n page keeps the content available: collapsible and tab panels stay open. */\n [data-slot='popover']:not([data-enhanced]) > [data-slot='popover-trigger'],\n [data-slot='dropdown-menu']:not([data-enhanced]) > [data-slot='dropdown-menu-trigger'],\n [data-slot='alert-dialog']:not([data-enhanced]) > [data-slot='alert-dialog-trigger'],\n [data-slot='drawer']:not([data-enhanced]) > [data-slot='drawer-trigger'],\n [data-slot='toast']:not([data-enhanced]) [data-toast-title],\n [data-slot='collapsible']:not([data-enhanced]) > [data-slot='collapsible-trigger'],\n [data-slot='tabs']:not([data-enhanced]) > [data-slot='tabs-list'],\n [data-slot='toggle-group']:not([data-enhanced]),\n button[data-slot='toggle']:not([data-enhanced]) {\n display: none;\n }\n{% endstylesheet %}\n", + "type": "registry:file", + "target": "~/snippets/ui-triggers.liquid" + } + ], + "type": "registry:item" +} \ No newline at end of file diff --git a/public/r/liquid/typography.json b/public/r/liquid/typography.json new file mode 100644 index 000000000..ffd2d725b --- /dev/null +++ b/public/r/liquid/typography.json @@ -0,0 +1,24 @@ +{ + "$schema": "https://ui.shadcn.com/schema/registry-item.json", + "name": "typography", + "title": "Typography", + "description": "Native Liquid typography with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json" + ], + "files": [ + { + "path": "components/typography.liquid", + "content": "{% doc %}\n Fulldev UI typography styles. Use native markup with its data-slot parts.\n{% enddoc %}\n\n{{ 'ui-typography.css' | asset_url | stylesheet_tag }}\n", + "type": "registry:file", + "target": "~/snippets/ui-typography.liquid" + }, + { + "path": "styles/typography.css", + "content": "h1,\nh2,\nh3,\nh4 {\n font-family: var(--font-heading);\n font-weight: var(--heading-weight);\n letter-spacing: var(--tracking-tight);\n scroll-margin-top: 80px;\n}\nh1 {\n font-size: 2.25rem;\n line-height: 2.5rem;\n text-wrap: balance;\n}\nh2 {\n font-size: 1.875rem;\n line-height: 2.25rem;\n}\nh3 {\n font-size: 1.5rem;\n line-height: 2rem;\n}\nh4 {\n font-size: 1.25rem;\n line-height: 1.75rem;\n}\n[data-size='sm']:is(h1) {\n font-size: 1.875rem;\n line-height: 2.25rem;\n}\n[data-size='sm']:is(h2) {\n font-size: 1.5rem;\n line-height: 2rem;\n}\n[data-size='sm']:is(h3) {\n font-size: 1.25rem;\n line-height: 1.75rem;\n}\n[data-size='sm']:is(h4) {\n font-size: 1.125rem;\n line-height: 1.75rem;\n}\n[data-size='lg']:is(h1) {\n font-size: 3rem;\n line-height: 1;\n}\n.muted {\n color: var(--muted-foreground);\n}\n.link {\n font-weight: 500;\n text-decoration: underline;\n}\n.link:hover {\n text-decoration-thickness: 2px;\n}\n\n/* Rich text from the editor or Shopify content: Fulldev UI's typography, shadcn/ui's typeset.\n Text is 18px below 768px and 16px from there (sm 14px, lg 18px), headings scale in em, and the\n flow sets the space between blocks. Theme tokens set the colors and fonts, images and videos\n keep square corners, and a wide table scrolls. [data-not-typeset] keeps a part out.\n The typeset class gives merchant rich text inside another part, such as a section or card\n description, the same flow, headings, lists and links at that part's size, leading and color. */\n:is([data-slot='typography'], .typeset) {\n --typeset-font-heading: var(--font-heading);\n --typeset-font-mono: var(--font-mono);\n --typeset-flow: 1.25em;\n --typeset-heading: currentcolor;\n --typeset-muted: currentcolor;\n --typeset-rule: var(--border);\n\n overflow-wrap: break-word;\n}\n[data-slot='typography'] {\n --typeset-font-body: inherit;\n --typeset-size: 1rem;\n --typeset-leading: 1.75;\n --typeset-heading: var(--foreground);\n --typeset-muted: var(--muted-foreground);\n\n font-family: var(--typeset-font-body);\n font-size: calc(var(--typeset-size) * 1.125);\n line-height: var(--typeset-leading);\n color: var(--foreground);\n}\n@media (min-width: 768px), print {\n [data-slot='typography'] {\n font-size: var(--typeset-size);\n }\n}\n[data-slot='typography'][data-size='sm'] {\n --typeset-size: 0.875rem;\n --typeset-leading: 1.7;\n --typeset-flow: 1.125em;\n}\n[data-slot='typography'][data-size='lg'] {\n --typeset-size: 1.125rem;\n --typeset-leading: 1.8;\n --typeset-flow: 1.5em;\n}\n[data-slot='typography'][data-variant='muted'] {\n color: var(--muted-foreground);\n}\n:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(p) {\n margin-block: var(--typeset-flow) 0;\n}\n:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(h1),\n:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(h2),\n:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(h3),\n:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(h4),\n:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(h5),\n:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(h6) {\n color: var(--typeset-heading);\n font-family: var(--typeset-font-heading);\n font-weight: var(--heading-weight);\n letter-spacing: var(--tracking-normal);\n margin-block-end: 0;\n}\n:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(h1) {\n font-size: 1.75em;\n line-height: 1.3;\n margin-block-start: var(--typeset-flow);\n}\n:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(h2) {\n font-size: 1.25em;\n line-height: 1.4;\n margin-block-start: calc(var(--typeset-flow) * 1.4);\n}\n:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(h3) {\n font-size: 1.125em;\n line-height: 1.45;\n margin-block-start: var(--typeset-flow);\n}\n:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(h4) {\n font-size: 1em;\n line-height: 1.5;\n margin-block-start: var(--typeset-flow);\n}\n:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(h5) {\n font-size: 0.875em;\n line-height: 1.5;\n font-weight: 500;\n color: var(--typeset-muted);\n margin-block-start: calc(var(--typeset-flow) / 0.875);\n}\n:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(h6) {\n font-size: 0.8125em;\n line-height: 1.5;\n font-weight: 500;\n letter-spacing: 0.08em;\n text-transform: uppercase;\n color: var(--typeset-muted);\n margin-block-start: calc(var(--typeset-flow) / 0.8125);\n}\n:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where([id]) {\n scroll-margin-block-start: var(--typeset-flow);\n}\n:is([data-slot='typography'], .typeset)\n :not(:where([data-not-typeset], [data-not-typeset] *)):where(h1 + *, h2 + *, h3 + *, h4 + *, h5 + *, h6 + *) {\n margin-block-start: 1em;\n}\n:is([data-slot='typography'], .typeset)\n :not(:where([data-not-typeset], [data-not-typeset] *)):where(:is(h1, h2, h3, h4) :is(code)) {\n font-size: 0.9em;\n}\n:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(a) {\n color: inherit;\n font-weight: 500;\n text-decoration-line: underline;\n}\n:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(a:hover) {\n text-decoration-thickness: 2px;\n}\n:is([data-slot='typography'], .typeset)\n :not(:where([data-not-typeset], [data-not-typeset] *)):where(:is(h1, h2, h3, h4, h5, h6) :is(a)) {\n font-weight: inherit;\n}\n:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(strong, b) {\n font-weight: 600;\n}\n:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(mark) {\n padding: 0.05em 0.15em;\n border-radius: 0.2em;\n background-color: color-mix(in oklab, var(--primary) 15%, transparent);\n color: inherit;\n}\n:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(abbr[title]) {\n text-decoration-line: underline;\n text-decoration-style: dotted;\n text-underline-offset: 0.2em;\n cursor: help;\n}\n:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(del, s) {\n color: var(--typeset-muted);\n text-decoration-line: line-through;\n}\n:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(sup, sub) {\n position: relative;\n font-size: 0.75em;\n line-height: 0;\n vertical-align: baseline;\n}\n:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(sup) {\n top: -0.5em;\n}\n:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(sub) {\n bottom: -0.25em;\n}\n:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(sup a) {\n text-decoration-line: none;\n font-weight: 500;\n}\n:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(ul) {\n list-style-type: disc;\n}\n:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(ol) {\n list-style-type: decimal;\n}\n:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(ul ul) {\n list-style-type: circle;\n}\n:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(ul ul ul) {\n list-style-type: square;\n}\n:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(ol[type='a' i]) {\n list-style-type: lower-alpha;\n}\n:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(ol[type='i' i]) {\n list-style-type: lower-roman;\n}\n:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(ol[type='A' s]) {\n list-style-type: upper-alpha;\n}\n:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(ol[type='I' s]) {\n list-style-type: upper-roman;\n}\n:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(ul, ol) {\n margin-block: var(--typeset-flow) 0;\n padding-inline-start: 1.5em;\n}\n:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(li) {\n margin-block-start: 0.5em;\n padding-inline-start: 0.4em;\n}\n:is([data-slot='typography'], .typeset)\n :not(:where([data-not-typeset], [data-not-typeset] *)):where(li > p, li > ul, li > ol) {\n margin-block-start: 0.5em;\n}\n:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(ul > li)::marker,\n:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(ol > li)::marker {\n color: var(--typeset-muted);\n}\n:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(details) {\n margin-block: var(--typeset-flow) 0;\n}\n:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(summary) {\n cursor: pointer;\n font-weight: 500;\n}\n:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(summary)::marker {\n color: var(--typeset-muted);\n}\n:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(kbd) {\n padding: 0.0625em 0.35em;\n border: 1px solid var(--typeset-rule);\n border-block-end-width: 2px;\n border-radius: min(calc(var(--radius) * 0.6), 0.35em);\n font-family: inherit;\n font-size: 0.85em;\n font-weight: 500;\n}\n:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(dl) {\n margin-block: var(--typeset-flow) 0;\n}\n:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(dt) {\n margin-block-start: 1em;\n font-weight: 500;\n}\n:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(dt + dt) {\n margin-block-start: 0.25em;\n}\n:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(dd) {\n margin-block-start: 0.25em;\n margin-inline-start: 0;\n padding-inline-start: 1em;\n color: var(--typeset-muted);\n}\n:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(:not(pre) > code) {\n padding: 0.125em 0.3em;\n border-radius: min(calc(var(--radius) * 0.6), 0.35em);\n background-color: var(--muted);\n font-family: var(--typeset-font-mono);\n font-size: 0.85em;\n}\n:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(pre) {\n margin-block: calc(var(--typeset-flow) / 0.875) 0;\n overflow-x: auto;\n padding: 0.75em 1em;\n border-radius: var(--radius);\n background-color: var(--muted);\n font-family: var(--typeset-font-mono);\n font-size: 0.875em;\n line-height: 1.5;\n tab-size: 2;\n direction: ltr;\n}\n:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(pre code) {\n padding: 0;\n border-radius: 0;\n background-color: transparent;\n font-family: inherit;\n font-size: inherit;\n}\n:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(blockquote) {\n margin-block: var(--typeset-flow) 0;\n margin-inline: 0;\n padding-inline-start: 1em;\n border-inline-start: 2px solid var(--typeset-rule);\n}\n:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(hr) {\n margin-block: calc(var(--typeset-flow) * 2.4) 0;\n border: 0;\n border-block-start: 1px solid var(--typeset-rule);\n}\n:is([data-slot='typography'], .typeset)\n :not(:where([data-not-typeset], [data-not-typeset] *)):where(hr + h1, hr + h2, hr + h3, hr + h4) {\n margin-block-start: var(--typeset-flow);\n}\n:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(img, video) {\n max-width: 100%;\n height: auto;\n margin-block: var(--typeset-flow) 0;\n}\n:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(iframe) {\n max-width: 100%;\n margin-block: var(--typeset-flow) 0;\n}\n:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(p img) {\n margin-block-start: 0;\n}\n:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(figure) {\n margin-block: var(--typeset-flow) 0;\n margin-inline: 0;\n}\n:is([data-slot='typography'], .typeset)\n :not(:where([data-not-typeset], [data-not-typeset] *)):where(figcaption, caption) {\n margin-block-start: calc(0.75em / 0.875);\n color: var(--typeset-muted);\n font-size: 0.875em;\n text-align: center;\n}\n:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(caption) {\n caption-side: bottom;\n}\n:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(table) {\n display: block;\n width: max-content;\n max-width: 100%;\n margin-block: var(--typeset-flow) 0;\n overflow-x: auto;\n border-block-end: 1px solid var(--typeset-rule);\n border-collapse: separate;\n border-spacing: 0;\n font-size: 1em;\n line-height: 1.5;\n font-variant-numeric: tabular-nums;\n}\n:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(thead th) {\n padding: 0.65em 1em;\n font-weight: 500;\n text-align: start;\n white-space: nowrap;\n}\n:is([data-slot='typography'], .typeset)\n :not(:where([data-not-typeset], [data-not-typeset] *)):where(:is(tbody, tfoot) :is(td, th)) {\n padding: 0.75em 1em;\n border-block-start: 1px solid var(--typeset-rule);\n text-align: start;\n vertical-align: top;\n}\n:is([data-slot='typography'], .typeset)\n :not(:where([data-not-typeset], [data-not-typeset] *)):where(tbody th, tfoot th) {\n font-weight: 500;\n}\n:is([data-slot='typography'], .typeset)\n :not(:where([data-not-typeset], [data-not-typeset] *)):where(th:first-child, td:first-child) {\n padding-inline-start: 0;\n}\n:is([data-slot='typography'], .typeset)\n :not(:where([data-not-typeset], [data-not-typeset] *)):where(th[align='center'], td[align='center']) {\n text-align: center;\n}\n:is([data-slot='typography'], .typeset)\n :not(:where([data-not-typeset], [data-not-typeset] *)):where(th[align='right'], td[align='right']) {\n text-align: end;\n}\n:is([data-slot='typography'], .typeset)\n :not(:where([data-not-typeset], [data-not-typeset] *)):where(li > blockquote, li > table, li > figure) {\n margin-block-start: 0.5em;\n}\n:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(li > pre) {\n margin-block-start: calc(0.5em / 0.875);\n}\n@media print {\n :is([data-slot='typography'], .typeset)\n :not(:where([data-not-typeset], [data-not-typeset] *)):where(pre, table, blockquote, figure) {\n break-inside: avoid;\n }\n :is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(h1, h2, h3, h4) {\n break-after: avoid;\n }\n}\n@media (forced-colors: active) {\n :is([data-slot='typography'], .typeset)\n :not(:where([data-not-typeset], [data-not-typeset] *)):where(:not(pre) > code) {\n border: 1px solid;\n }\n}\n:is([data-slot='typography'], .typeset) > :where(:first-child):not(:where([data-not-typeset], [data-not-typeset] *)),\n:is([data-slot='typography'], .typeset)\n > :where(:first-child)\n > :where(:first-child):not(:where([data-not-typeset], [data-not-typeset] *)),\n:is([data-slot='typography'], .typeset)\n :where(\n li > :first-child,\n blockquote > :first-child,\n td > :first-child,\n th > :first-child,\n dd > :first-child,\n figure > :first-child,\n figure > picture > img\n ):not(:where([data-not-typeset], [data-not-typeset] *)) {\n margin-block-start: 0;\n}\n\n/* Typography parts, as Fulldev UI's typography components: the same roles outside rich text,\n each with the sm and lg sizes. Large, small and muted are shadcn/ui's typography roles. */\n[data-slot='typography-h1'],\n[data-slot='typography-h2'],\n[data-slot='typography-h3'],\n[data-slot='typography-h4'] {\n font-family: var(--font-heading);\n font-weight: var(--heading-weight);\n letter-spacing: var(--tracking-tight);\n scroll-margin-top: 80px;\n}\n[data-slot='typography-h1'] {\n text-wrap: balance;\n font-size: 2.25rem;\n line-height: 2.5rem;\n}\n[data-slot='typography-h1'][data-size='sm'] {\n font-size: 1.875rem;\n line-height: 2.25rem;\n}\n[data-slot='typography-h1'][data-size='lg'] {\n font-size: 3rem;\n line-height: 1;\n}\n[data-slot='typography-h2'] {\n font-size: 1.875rem;\n line-height: 2.25rem;\n}\n[data-slot='typography-h2'][data-size='sm'] {\n font-size: 1.5rem;\n line-height: 2rem;\n}\n[data-slot='typography-h2'][data-size='lg'] {\n font-size: 2.25rem;\n line-height: 2.5rem;\n}\n[data-slot='typography-h3'] {\n font-size: 1.5rem;\n line-height: 2rem;\n}\n[data-slot='typography-h3'][data-size='sm'] {\n font-size: 1.25rem;\n line-height: 1.75rem;\n}\n[data-slot='typography-h3'][data-size='lg'] {\n font-size: 1.875rem;\n line-height: 2.25rem;\n}\n[data-slot='typography-h4'] {\n font-size: 1.25rem;\n line-height: 1.75rem;\n}\n[data-slot='typography-h4'][data-size='sm'] {\n font-size: 1.125rem;\n line-height: 1.75rem;\n}\n[data-slot='typography-h4'][data-size='lg'] {\n font-size: 1.5rem;\n line-height: 2rem;\n}\n[data-slot='typography-p'],\n[data-slot='typography-blockquote'],\n[data-slot='typography-list'] {\n font-size: 1rem;\n line-height: 1.75rem;\n}\n[data-slot='typography-p']:not(:first-child) {\n margin-top: 1.5rem;\n}\n[data-slot='typography-lead'] {\n font-size: 1.125rem;\n line-height: 1.75rem;\n color: var(--muted-foreground);\n}\n[data-slot='typography-lead'][data-size='sm'] {\n font-size: 1rem;\n line-height: 1.5rem;\n}\n[data-slot='typography-lead'][data-size='lg'] {\n font-size: 1.25rem;\n line-height: 1.75rem;\n}\n[data-slot='typography-large'] {\n font-size: 1.125rem;\n font-weight: 600;\n line-height: 1.75rem;\n}\n[data-slot='typography-small'] {\n font-size: 0.875rem;\n font-weight: 500;\n line-height: 1;\n}\n[data-slot='typography-muted'] {\n font-size: 0.875rem;\n line-height: 1.25rem;\n color: var(--muted-foreground);\n}\n[data-slot='typography-blockquote'] {\n margin-top: 1.5rem;\n padding-inline-start: 1.5rem;\n border-inline-start: 2px solid var(--border);\n font-style: italic;\n}\n[data-slot='typography-list'] {\n margin-block: 1.5rem;\n padding-inline-start: 1.5rem;\n}\n[data-slot='typography-list'] > li + li {\n margin-top: 0.5rem;\n}\n[data-slot='typography-p'][data-size='sm'] {\n font-size: 0.875rem;\n line-height: 1.5rem;\n}\n[data-slot='typography-p'][data-size='lg'] {\n font-size: 1.125rem;\n line-height: 1.875rem;\n}\n[data-slot='typography-blockquote'][data-size='sm'] {\n font-size: 0.875rem;\n line-height: 1.5rem;\n}\n[data-slot='typography-blockquote'][data-size='lg'] {\n font-size: 1.125rem;\n line-height: 1.875rem;\n}\n[data-slot='typography-list'][data-size='sm'] {\n font-size: 0.875rem;\n line-height: 1.5rem;\n}\n[data-slot='typography-list'][data-size='lg'] {\n font-size: 1.125rem;\n line-height: 1.875rem;\n}\n[data-slot='typography-inline-code'] {\n padding: 0.2rem 0.3rem;\n border-radius: var(--radius-sm);\n background: var(--muted);\n font-family: var(--font-mono);\n font-size: 0.875rem;\n font-weight: 600;\n}\n[data-slot='typography-inline-code'][data-size='sm'] {\n font-size: 0.75rem;\n}\n[data-slot='typography-inline-code'][data-size='lg'] {\n font-size: 1rem;\n}\n[data-slot='typography-a'] {\n color: var(--primary);\n font-size: 1rem;\n font-weight: 500;\n line-height: 1.5rem;\n text-decoration: underline;\n text-underline-offset: 4px;\n}\n[data-slot='typography-a'][data-size='sm'] {\n font-size: 0.875rem;\n line-height: 1.25rem;\n}\n[data-slot='typography-a'][data-size='lg'] {\n font-size: 1.125rem;\n line-height: 1.75rem;\n}\n[data-slot='typography-a']:hover {\n text-decoration-thickness: 2px;\n}\n\n/* A table in running text, as Fulldev UI's typography table: framed, with striped rows. */\n[data-slot='typography-table-container'] {\n width: 100%;\n margin-block: 1.5rem;\n overflow-x: auto;\n border: 1px solid var(--border);\n border-radius: var(--radius-xl);\n}\n[data-slot='typography-table'] {\n width: 100%;\n border-collapse: collapse;\n font-size: 1rem;\n line-height: 1.5rem;\n}\n[data-slot='typography-table'][data-size='sm'] {\n font-size: 0.875rem;\n line-height: 1.25rem;\n}\n[data-slot='typography-table'][data-size='lg'] {\n font-size: 1.125rem;\n line-height: 1.75rem;\n}\n[data-slot='typography-table-row']:nth-child(even) {\n background: var(--muted);\n}\n[data-slot='typography-table-head'],\n[data-slot='typography-table-cell'] {\n padding: 0.75rem 1rem;\n text-align: start;\n}\n[data-slot='typography-table-head'] {\n border-bottom: 1px solid var(--border);\n font-weight: 600;\n}\n[data-slot='typography-table-cell'] {\n border-top: 1px solid var(--border);\n}\n:is([data-slot='typography-table-head'], [data-slot='typography-table-cell'])[align='left'] {\n text-align: left;\n}\n:is([data-slot='typography-table-head'], [data-slot='typography-table-cell'])[align='center'] {\n text-align: center;\n}\n:is([data-slot='typography-table-head'], [data-slot='typography-table-cell'])[align='right'] {\n text-align: right;\n}\n\n.check-list {\n display: flex;\n flex-direction: column;\n gap: 12px;\n margin: 0;\n padding: 0;\n list-style: none;\n font-size: 0.875rem;\n}\n.check-list > li {\n display: flex;\n align-items: flex-start;\n gap: 12px;\n}\n.check-list .icon {\n width: 16px;\n height: 16px;\n margin-top: 2px;\n color: var(--primary);\n}\n", + "type": "registry:file", + "target": "~/assets/ui-typography.css" + } + ], + "type": "registry:item" +} \ No newline at end of file diff --git a/public/r/liquid/video.json b/public/r/liquid/video.json new file mode 100644 index 000000000..ca51c7d73 --- /dev/null +++ b/public/r/liquid/video.json @@ -0,0 +1,25 @@ +{ + "$schema": "https://ui.shadcn.com/schema/registry-item.json", + "name": "video", + "title": "Video", + "description": "Native Liquid video with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/aspect-ratio.json" + ], + "files": [ + { + "path": "components/video.liquid", + "content": "{% doc %}\n Fulldev UI video styles. Use native markup with its data-slot parts.\n{% enddoc %}\n\n{{ 'ui-video.css' | asset_url | stylesheet_tag }}\n", + "type": "registry:file", + "target": "~/snippets/ui-video.liquid" + }, + { + "path": "styles/video.css", + "content": "/* The playing media of a video: a Shopify video, an embed, or the click-to-load cover that becomes\n one. It fills its aspect-ratio frame, which carries Fulldev UI's border and radius. */\n[data-slot='video'] {\n display: block;\n width: 100%;\n height: 100%;\n border: 0;\n background: var(--muted);\n object-fit: cover;\n}\n", + "type": "registry:file", + "target": "~/assets/ui-video.css" + } + ], + "type": "registry:item" +} \ No newline at end of file diff --git a/scripts/registry-liquid.mjs b/scripts/registry-liquid.mjs new file mode 100644 index 000000000..eead3bd34 --- /dev/null +++ b/scripts/registry-liquid.mjs @@ -0,0 +1,91 @@ +// Only tokens, contracts and provenance are generated. Component CSS is handwritten. +import assert from "node:assert/strict" +import { createHash } from "node:crypto" +import { readFileSync, writeFileSync } from "node:fs" +import { resolve } from "node:path" +import { format } from "prettier" + +const root = resolve("liquid") +const read = (path) => readFileSync(resolve(root, path), "utf8") +const write = (path, value) => + process.argv.includes("--check") + ? assert.equal( + read(path), + value, + `liquid/${path}: rebuild the Liquid registry` + ) + : writeFileSync(resolve(root, path), value) +const registry = JSON.parse(read("registry.json")) +const names = new Set(registry.items.map((item) => item.name)) +assert.equal(names.size, registry.items.length, "Duplicate Liquid item names") +const prefix = "https://ui.full.dev/r/liquid/" +const palette = JSON.parse( + readFileSync("registry/styles/shadcn/themes.json", "utf8") +).find((theme) => theme.name === "neutral").cssVars +const declarations = (values) => + Object.entries(values) + .map(([name, value]) => ` --${name}: ${value};`) + .join("\n") +const primitives = Object.fromEntries( + [...read("styles/primitives.css").matchAll(/--([\w-]+):\s*([^;]+);/g)].map( + ([, name, value]) => [name, value] + ) +) +const focus = { + "focus-ring": "0 0 0 3px color-mix(in oklch, var(--ring) 50%, transparent)", +} +write( + "styles/tokens.css", + await format( + `/* Generated from the shared neutral palette and Liquid primitives. */\n:root {\n color-scheme: light;\n\n${declarations({ ...palette.light, ...primitives, ...focus })}\n}\n.dark {\n color-scheme: dark;\n\n${declarations({ ...palette.dark, ...focus })}\n}\n`, + { parser: "css", singleQuote: true, printWidth: 120 } + ) +) +const licenses = [ + ["Fulldev UI", "LICENSE-FULLDEV.txt"], + ["shadcn/ui (derived Vega styles)", "LICENSE-SHADCN.md"], + ["data-slot 1.1.1", "LICENSE-DATA-SLOT.md"], +].map(([name, file]) => ({ name, text: read(file).trim() })) +write("licenses.json", `${JSON.stringify(licenses, null, 2)}\n`) + +const files = new Map() +const contract = {} +for (const item of registry.items) { + assert.equal(item.type, "registry:item", item.name) + assert(!item.dependencies && !item.css && !item.cssVars && !item.config) + for (const dependency of item.registryDependencies ?? []) { + assert(dependency.startsWith(prefix), dependency) + assert(names.has(dependency.slice(prefix.length, -5)), dependency) + } + const slots = new Set() + for (const file of item.files ?? []) { + assert.equal(file.type, "registry:file") + assert.match(file.target, /^~\/(assets|snippets)\/[a-z0-9.-]+$/) + assert(!file.path.includes("..")) + if (file.path === "manifest.json") continue + const content = read(file.path) + assert(!/--tw-|@tailwind|@apply\b/.test(content), file.path) + for (const match of content.matchAll(/data-slot=['"]([^'"]+)['"]/g)) + slots.add(match[1]) + const hash = createHash("sha256").update(content).digest("hex") + const target = file.target.slice(2) + if (files.has(target)) assert.equal(files.get(target), hash, target) + files.set(target, hash) + } + if (slots.size) contract[item.name] = [...slots].sort() +} +const vendor = JSON.parse(read("vendor.json")) +for (const entry of vendor.files) { + const hash = createHash("sha256") + .update(read(`vendor/${entry.path.split("/").at(-1)}`)) + .digest("hex") + assert.equal(hash, entry.sha256, entry.path) +} +write("contracts.json", `${JSON.stringify(contract, null, 2)}\n`) +write( + "manifest.json", + `${JSON.stringify({ registry: prefix, files: Object.fromEntries(files) }, null, 2)}\n` +) +console.log( + `Liquid: ${registry.items.length} universal items, ${files.size} files` +) diff --git a/tests/browser/liquid.test.mjs b/tests/browser/liquid.test.mjs new file mode 100644 index 000000000..0249364b0 --- /dev/null +++ b/tests/browser/liquid.test.mjs @@ -0,0 +1,311 @@ +import assert from "node:assert/strict" +import { mkdtemp, rm } from "node:fs/promises" +import { tmpdir } from "node:os" +import { resolve } from "node:path" +import { after, before, test } from "node:test" +import { fixtureServer, install, registryServer } from "#liquid-fixture" +import { chromium, firefox, webkit } from "playwright" + +let theme, server +before(async () => { + theme = await mkdtemp(resolve(tmpdir(), "fulldev-liquid-browser-")) + const registry = await registryServer() + try { + await install(registry.origin, theme) + } finally { + await registry.close() + } + server = await fixtureServer(theme) +}) +after(async () => { + await server?.close() + if (theme) await rm(theme, { recursive: true, force: true }) +}) +const slot = (name) => `[data-slot="${name}"]` +const controllers = [ + "accordion", + "select", + "radio-group", + "switch", + "slider", + "alert-dialog", + "toggle", + "toggle-group", + "tabs", + "collapsible", + "popover", + "hover-card", + "tooltip", + "dropdown-menu", + "drawer", + "toast", + "dialog", + "combobox", + "command", + "carousel", + "navigation-menu", +] + +for (const [name, type] of Object.entries({ chromium, firefox, webkit })) { + test(`Liquid ${name}: all controllers, forms, overlays and editor lifecycle`, async () => { + const browser = await type.launch() + const page = await browser.newPage() + const errors = [] + page.on("pageerror", (error) => errors.push(error.message)) + page.on("console", (message) => { + if (message.type() === "error") errors.push(message.text()) + }) + try { + await page.goto(server.origin) + for (const component of controllers) + await page + .locator(`${slot(component)}[data-enhanced]`) + .first() + .waitFor({ state: "attached" }) + assert.deepEqual(errors, []) + assert.equal( + await page + .getByRole("button", { name: "", exact: true }) + .count(), + 1 + ) + const button = page.getByRole("button", { + name: "", + exact: true, + }) + assert.equal(await button.evaluate((el) => el.children.length), 0) + for (const width of [390, 768, 1440]) { + await page.setViewportSize({ width, height: 900 }) + assert( + await page.evaluate( + () => document.documentElement.scrollWidth <= innerWidth + ), + `${name}: overflow at ${width}` + ) + } + await page.locator(slot("tabs-trigger")).nth(1).click() + assert.equal( + await page + .locator(slot("tabs-trigger")) + .nth(1) + .getAttribute("aria-selected"), + "true" + ) + await page.locator(slot("accordion-trigger")).first().click() + assert.equal( + await page + .locator(slot("accordion-trigger")) + .first() + .getAttribute("aria-expanded"), + "false" + ) + await page + .locator(slot("accordion-item")) + .first() + .evaluate((item) => { + item.dispatchEvent( + new CustomEvent("shopify:block:select", { bubbles: true }) + ) + }) + await page.waitForFunction( + () => + document + .querySelector('[data-slot="accordion-trigger"]') + .getAttribute("aria-expanded") === "true" + ) + await page.locator(slot("toggle")).first().click() + assert.equal( + await page.locator(slot("toggle")).first().getAttribute("aria-pressed"), + "true" + ) + await page.locator(slot("select-trigger")).click() + await page.locator(`${slot("select-item")}[data-value="medium"]`).click() + await page + .locator(`${slot("radio-group-item")}[data-value="express"]`) + .click() + await page.locator(slot("switch")).click() + await page.locator(slot("toggle-group-item")).last().click() + assert.equal( + await page + .locator(slot("toggle-group-item")) + .last() + .getAttribute("aria-pressed"), + "true" + ) + await page.locator(slot("slider-thumb")).first().focus() + await page.keyboard.press("ArrowRight") + assert.equal( + await page.locator('input[name="range_min"]').inputValue(), + "25" + ) + const values = await page + .locator("#ui-reference-form") + .evaluate((form) => Object.fromEntries(new FormData(form))) + assert.equal(values.size, "medium") + assert.equal(values.delivery, "express") + assert.equal(values.updates, "yes") + await page.locator(slot("dialog-trigger")).focus() + await page.keyboard.press("Enter") + await page.locator(slot("dialog-content")).waitFor({ state: "visible" }) + await page.keyboard.press("Escape") + await page.locator(slot("dialog-content")).waitFor({ state: "hidden" }) + await page.waitForFunction( + () => + document.querySelector('[data-slot="dialog-trigger"]') === + document.activeElement + ) + for (const component of ["popover", "dropdown-menu", "drawer"]) { + await page.locator(slot(`${component}-trigger`)).click() + const part = + component === "drawer" ? "drawer-popup" : `${component}-content` + await page.locator(slot(part)).waitFor({ state: "visible" }) + await page.keyboard.press("Escape") + await page.locator(slot(part)).waitFor({ state: "hidden" }) + } + await page.locator("[data-toast-title]").click() + await page.locator(slot("toast-item")).waitFor({ state: "visible" }) + await page.locator(slot("toast-close")).click() + await page.locator(slot("toast-item")).waitFor({ state: "detached" }) + await page.locator(slot("combobox-input")).fill("Belg") + await page.locator(`${slot("combobox-item")}[data-value="be"]`).click() + assert.equal( + await page.locator(slot("combobox-input")).inputValue(), + "Belgium" + ) + await page.locator(slot("command-input")).fill("Second") + await page + .locator('[data-slot="command-item"][href="/one"]') + .waitFor({ state: "hidden" }) + await page.locator(slot("carousel-next")).click() + await page.waitForFunction( + () => + document.querySelector('[data-slot="carousel-content"]').scrollLeft > + 0 + ) + await page + .locator(slot("carousel-item")) + .first() + .evaluate((slide) => { + slide.dispatchEvent( + new CustomEvent("shopify:block:select", { bubbles: true }) + ) + }) + await page.waitForFunction( + () => + document.querySelector('[data-slot="carousel-content"]') + .scrollLeft === 0 + ) + await page.locator(slot("navigation-menu-trigger")).click() + assert.equal( + await page + .locator(slot("navigation-menu-trigger")) + .getAttribute("aria-expanded"), + "true" + ) + await page.keyboard.press("Escape") + for (const component of ["hover-card", "tooltip"]) { + await page.locator(slot(`${component}-trigger`)).hover() + await page + .locator(slot(`${component}-content`)) + .waitFor({ state: "visible" }) + await page.locator("h1").hover() + await page + .locator(slot(`${component}-content`)) + .waitFor({ state: "hidden" }) + } + await page.locator("[data-motion-pause]").click() + assert.equal( + await page.locator("[data-motion-pause]").getAttribute("aria-pressed"), + "true" + ) + await page.emulateMedia({ reducedMotion: "reduce" }) + await page.locator("[data-motion-pause]").waitFor({ state: "hidden" }) + await page.emulateMedia({ reducedMotion: "no-preference" }) + await page.locator(slot("alert-dialog-trigger")).click() + await page.locator(slot("alert-dialog-action")).click() + await page + .locator(slot("alert-dialog-content")) + .waitFor({ state: "hidden" }) + await page.waitForFunction( + () => + new FormData(document.querySelector("#ui-reference-form")).get( + "delivery" + ) === "standard" + ) + // Unload restores native fields with their labels and current state. Load starts once. + await page.evaluate(() => { + const section = document + .querySelector("#ui-reference-form") + .closest("section") + section.dispatchEvent( + new CustomEvent("shopify:section:unload", { bubbles: true }) + ) + }) + assert(await page.locator("select[data-fallback]").isVisible()) + assert.equal( + await page.locator(slot("select")).getAttribute("hidden"), + "" + ) + await page.evaluate(() => { + const section = document + .querySelector("#ui-reference-form") + .closest("section") + section.dispatchEvent( + new CustomEvent("shopify:section:load", { bubbles: true }) + ) + section.dispatchEvent( + new CustomEvent("shopify:section:load", { bubbles: true }) + ) + }) + await page + .locator(`${slot("select")}[data-enhanced]`) + .waitFor({ state: "attached" }) + assert.equal(await page.locator("#ui-reference-size").count(), 1) + await page.evaluate(() => { + const root = document.createElement("div") + root.innerHTML = + '' + root.id = "inserted" + document.body.append(root) + }) + await page + .locator("#inserted [data-enhanced]") + .waitFor({ state: "attached" }) + await page.getByRole("button", { name: "Inserted", exact: true }).click() + assert.equal( + await page + .getByRole("button", { name: "Inserted", exact: true }) + .getAttribute("aria-pressed"), + "true" + ) + await page.evaluate(() => document.querySelector("#inserted").remove()) + assert.deepEqual(errors, []) + } finally { + await browser.close() + } + }) +} + +test("Liquid without JavaScript keeps native controls and readable content", async () => { + const browser = await chromium.launch() + const page = await browser.newPage({ + javaScriptEnabled: false, + viewport: { width: 390, height: 900 }, + }) + try { + await page.goto(server.origin) + assert(await page.locator("select[data-fallback]").isVisible()) + assert(await page.locator(slot("select")).isHidden()) + assert(await page.locator(slot("tabs-content")).nth(1).isVisible()) + assert(await page.locator(slot("collapsible-content")).isVisible()) + await page.locator("select[data-fallback]").selectOption("large") + assert.equal( + await page + .locator("#ui-reference-form") + .evaluate((form) => new FormData(form).get("size")), + "large" + ) + } finally { + await browser.close() + } +}) diff --git a/tests/fixtures/liquid/LICENSE-LUCIDE.md b/tests/fixtures/liquid/LICENSE-LUCIDE.md new file mode 100644 index 000000000..718bb3f0e --- /dev/null +++ b/tests/fixtures/liquid/LICENSE-LUCIDE.md @@ -0,0 +1,43 @@ +ISC License + +Copyright (c) 2026 Lucide Icons and Contributors + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +--- + +The following Lucide icons are derived from the Feather project: + +airplay, alert-circle, alert-octagon, alert-triangle, aperture, arrow-down-circle, arrow-down-left, arrow-down-right, arrow-down, arrow-left-circle, arrow-left, arrow-right-circle, arrow-right, arrow-up-circle, arrow-up-left, arrow-up-right, arrow-up, at-sign, calendar, cast, check, chevron-down, chevron-left, chevron-right, chevron-up, chevrons-down, chevrons-left, chevrons-right, chevrons-up, circle, clipboard, clock, code, columns, command, compass, corner-down-left, corner-down-right, corner-left-down, corner-left-up, corner-right-down, corner-right-up, corner-up-left, corner-up-right, crosshair, database, divide-circle, divide-square, dollar-sign, download, external-link, feather, frown, hash, headphones, help-circle, info, italic, key, layout, life-buoy, link-2, link, loader, lock, log-in, log-out, maximize, meh, minimize, minimize-2, minus-circle, minus-square, minus, monitor, moon, more-horizontal, more-vertical, move, music, navigation-2, navigation, octagon, pause-circle, percent, plus-circle, plus-square, plus, power, radio, rss, search, server, share, shopping-bag, sidebar, smartphone, smile, square, table-2, tablet, target, terminal, trash-2, trash, triangle, tv, type, upload, x-circle, x-octagon, x-square, x, zoom-in, zoom-out + +The MIT License (MIT) (for the icons listed above) + +Copyright (c) 2013-present Cole Bemis + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/tests/fixtures/liquid/en.json b/tests/fixtures/liquid/en.json new file mode 100644 index 000000000..ca0aeeb17 --- /dev/null +++ b/tests/fixtures/liquid/en.json @@ -0,0 +1,125 @@ +{ + "general": { + "skip": "Skip to content", + "navigation": "Main navigation", + "announcements": "Announcements", + "menu": "Menu", + "open_menu": "Open menu", + "close_menu": "Close menu", + "account": "Account", + "shop": "Shop all", + "view_all": "View all", + "learn_more": "Learn more", + "legal": "Legal", + "view_all_named": "View all {{ title }}", + "continue_shopping": "Continue shopping", + "apply": "Apply", + "pagination": "Pagination", + "breadcrumb": "Breadcrumb", + "close": "Close", + "previous": "Previous", + "next": "Next", + "country": "Country or region", + "language": "Language", + "payment_methods": "Payment methods", + "play_video": "Play video", + "placeholder_heading": "Heading", + "step": "Step {{ number }}", + "pause_scrolling": "Pause scrolling", + "resume_scrolling": "Resume scrolling", + "page": "Page {{ number }}", + "search_countries": "Search countries", + "no_countries": "No countries found", + "dark_mode": "Dark mode" + }, + "component_reference": { + "title": "Components", + "intro": "The shared parts of this theme, for checking them in a preview. This page is only reachable with ?view=components.", + "contents": "On this page", + "typography": "Typography", + "rich_text": "Rich text", + "rich_text_text": "Pages, articles and product descriptions set merchant content like this, in a default, small and large size.", + "rich_inline_html": "Bold, italic, highlighted and removed text, an SKU, a note1, Esc and code.", + "rich_nested": "A list item can hold its own paragraphs and a nested list.", + "rich_paragraph": "Running text follows the size of its rich text block and reads slightly larger on phones.", + "rich_opt_out": "This part keeps the theme's own styles through data-not-typeset.", + "rich_accordion": "Rich text in an accordion", + "forms": "Forms", + "actions": "Actions", + "content": "Content", + "overlays": "Overlays", + "heading": "Heading {{ level }}", + "lead": "Lead text introduces a page or section.", + "paragraph": "Body text reads at 16 pixels with a relaxed line height.", + "link": "A text link", + "quote": "A blockquote sets a quotation apart from the text around it.", + "list_item": "List item {{ number }}", + "large": "Large text", + "small": "Small text", + "muted": "Muted text adds secondary detail.", + "role": "Role", + "size": "Size", + "forms_text": "Every control works without JavaScript through its native fallback, and submits its value with the form.", + "choose": "Choose a size", + "delivery": "Delivery", + "standard": "Standard", + "express": "Express", + "pickup": "Pickup", + "gift_wrap": "Gift wrap", + "updates": "Email me updates", + "range": "Range", + "minimum": "Minimum", + "maximum": "Maximum", + "submit": "Submit", + "reset": "Reset", + "reset_title": "Reset the form?", + "reset_text": "This clears the choices in the form above.", + "cancel": "Cancel", + "shortcuts": "Shortcuts", + "go_to": "Go to", + "products": "Products", + "favorite": "Favorite", + "view": "View", + "grid": "Grid", + "list": "List", + "loading": "Loading", + "please_wait": "Please wait", + "item_title": "Item title", + "item_text": "Add a short description of this item.", + "view_products": "View products", + "table_caption": "Example data", + "item": "Item", + "example": "Example {{ number }}", + "total": "Total", + "file": "example-file.pdf", + "uploading": "Uploading", + "failed": "Upload failed", + "product_details": "Product details", + "description": "Description", + "details": "Details", + "care": "Care", + "description_text": "Add the product description.", + "details_text": "Add materials, dimensions or other details.", + "care_text": "Add care instructions.", + "more": "Show more", + "more_text": "Collapsible content stays visible without JavaScript.", + "overlays_text": "Popups open from a trigger that shows once its controller has started. Keep essential information on the page itself.", + "popover": "Popover", + "popover_text": "A popover holds short, nonessential content and links.", + "hover_card_text": "A hover card previews a link on hover or keyboard focus.", + "menu": "Menu", + "sort": "Sort by", + "featured": "Featured", + "newest": "Newest", + "in_stock": "In stock only", + "drawer": "Drawer", + "drawer_text": "Swipe down or press Escape to close.", + "toast": "Show toast", + "toast_title": "Toast", + "toast_text": "A toast confirms an action and closes by itself.", + "notifications": "Notifications", + "size_small": "Small", + "size_medium": "Medium", + "size_large": "Large" + } +} diff --git a/tests/fixtures/liquid/icon.liquid b/tests/fixtures/liquid/icon.liquid new file mode 100644 index 000000000..1adca8715 --- /dev/null +++ b/tests/fixtures/liquid/icon.liquid @@ -0,0 +1,145 @@ +{% doc %} + Renders a decorative interface icon. Paths are derived from Lucide; see LICENSE-LUCIDE.md. + + @param {string} name - Icon name + @param {string} [class] - Extra class names +{% enddoc %} + diff --git a/tests/fixtures/liquid/interactive.liquid b/tests/fixtures/liquid/interactive.liquid new file mode 100644 index 000000000..71a3da137 --- /dev/null +++ b/tests/fixtures/liquid/interactive.liquid @@ -0,0 +1,97 @@ +
    +
    + {% render 'ui-button', label: '', variant: 'outline' %} +
    +
    +

    Native Liquid card

    +

    Parts stay native HTML.

    + Action +
    +
    Content
    +
    Footer
    +
    +
    + + + +
    +
    + + +
    + +
    +
    +
    + +
    + +
    No pages
    +
    +
    +
    +
    +
    First slide
    +
    Second slide
    +
    Third slide
    +
    + + +
    + +
    +
    + +
    +
    +
    + Example logo +
    +
    + +
    diff --git a/tests/fixtures/liquid/reference.liquid b/tests/fixtures/liquid/reference.liquid new file mode 100644 index 000000000..d570ca1da --- /dev/null +++ b/tests/fixtures/liquid/reference.liquid @@ -0,0 +1,1156 @@ +{% comment %} + The template's shared parts in one place, for checking them in a store preview: open any page + with ?view=components. Only templates/page.components.json uses this section; it has no preset, + so it stays out of the editor's section list and the storefront's navigation. +{% endcomment %} +{% liquid + assign id = section.id + assign scheme = 'scheme-' | append: section.settings.scheme + assign groups = 'typography,rich_text,forms,actions,content,overlays' | split: ',' + assign sizes = 'small,medium,large' | split: ',' + assign deliveries = 'standard,express,pickup' | split: ',' + assign typeset_sizes = 'sm,lg' | split: ',' + assign tabs = 'description,details,care' | split: ',' + assign sorts = 'featured,newest' | split: ',' +%} +
    +
    + +
    +
    +

    {{ 'component_reference.title' | t }}

    +

    + {{ 'component_reference.intro' | t }} +

    +
    + +
    +

    + {{ 'component_reference.typography' | t }} +

    +
    +

    + {{ 'component_reference.heading' | t: level: 1 }} +

    +

    + {{ 'component_reference.heading' | t: level: 2 }} +

    +

    + {{ 'component_reference.heading' | t: level: 3 }} +

    +

    + {{ 'component_reference.heading' | t: level: 4 }} +

    +

    + {{ 'component_reference.lead' | t }} +

    +

    + {{ 'component_reference.paragraph' | t }} + + {{- 'component_reference.link' | t -}} + + data-slot +

    +
    + {{ 'component_reference.quote' | t }} +
    +
      +
    • {{ 'component_reference.list_item' | t: number: 1 }}
    • +
    • {{ 'component_reference.list_item' | t: number: 2 }}
    • +
    • {{ 'component_reference.list_item' | t: number: 3 }}
    • +
    +

    + {{ 'component_reference.large' | t }} +

    +

    + {{ 'component_reference.small' | t }} +

    +

    + {{ 'component_reference.muted' | t }} +

    +
    + + + + + + + + + + + + + + + + + + + + + +
    + {{ 'component_reference.role' | t }} + + {{ 'component_reference.size' | t }} +
    + {{ 'component_reference.lead' | t }} + 18px
    + {{ 'component_reference.paragraph' | t }} + 16px
    + {{ 'component_reference.muted' | t }} + 14px
    +
    +
    +
    + +
    +

    + {{ 'component_reference.rich_text' | t }} +

    +

    + {{ 'component_reference.rich_text_text' | t }} +

    + {% comment %} Shopify rich text as the editor, articles and product descriptions output it. {% endcomment %} +
    +
    +

    {{ 'component_reference.heading' | t: level: 2 }}

    +

    + {{ 'component_reference.rich_inline_html' | t }} + + {{- 'component_reference.link' | t -}} + +

    +

    {{ 'component_reference.heading' | t: level: 3 }}

    +
      +
    • +

      {{ 'component_reference.list_item' | t: number: 1 }}

      +

      {{ 'component_reference.rich_nested' | t }}

      +
        +
      • + {{ 'component_reference.list_item' | t: number: 2 }} +
          +
        • + {{ 'component_reference.list_item' | t: number: 3 }} +
        • +
        +
      • +
      +
    • +
    • {{ 'component_reference.list_item' | t: number: 4 }}
    • +
    +
      +
    1. {{ 'component_reference.list_item' | t: number: 1 }}
    2. +
    3. {{ 'component_reference.list_item' | t: number: 2 }}
    4. +
    +
    +

    {{ 'component_reference.quote' | t }}

    +
    +

    {{ 'component_reference.heading' | t: level: 4 }}

    + + + + + + + + + + + + + + + + + + + + + + +
    + {{ 'component_reference.table_caption' | t }} +
    {{ 'component_reference.role' | t }}{{ 'component_reference.size' | t }}
    + {{ 'component_reference.heading' | t: level: 2 }} + 1.25em
    + {{ 'component_reference.heading' | t: level: 3 }} + 1.125em
    + {{ 'component_reference.heading' | t: level: 5 }} + 0.875em
    +
    {{ 'component_reference.heading' | t: level: 5 }}
    +

    {{ 'component_reference.rich_paragraph' | t }}

    +
    {{ 'component_reference.heading' | t: level: 6 }}
    +
    +
    {{ 'component_reference.delivery' | t }}
    +
    {{ 'component_reference.standard' | t }}
    +
    +
    +
    +

    {{ 'component_reference.rich_opt_out' | t }}

    +
      +
    • {{ 'component_reference.list_item' | t: number: 1 }}
    • +
    +
    +
    +
    + {% for size in typeset_sizes %} + {% if size == 'sm' %} + {% assign key = 'component_reference.size_small' %} + {% else %} + {% assign key = 'component_reference.size_large' %} + {% endif %} +
    +

    {{ key | t }}

    +
    +

    {{ 'component_reference.heading' | t: level: 3 }}

    +

    {{ 'component_reference.rich_paragraph' | t }}

    +
      +
    • {{ 'component_reference.list_item' | t: number: 1 }}
    • +
    • {{ 'component_reference.list_item' | t: number: 2 }}
    • +
    +
    +
    + {% endfor %} + {% comment %} Rich text in an accordion, as in product descriptions, policies and FAQ answers. {% endcomment %} +
    +
    +
    +

    + +

    +
    +
    +
    +

    {{ 'component_reference.rich_paragraph' | t }}

    +
      +
    • +

      + {{ 'component_reference.list_item' | t: number: 1 }} +

      +

      {{ 'component_reference.rich_nested' | t }}

      +
        +
      • + {{ 'component_reference.list_item' | t: number: 2 }} +
      • +
      +
    • +
    +
      +
    1. + {{ 'component_reference.list_item' | t: number: 1 }} +
    2. +
    +

    + {{ 'component_reference.rich_paragraph' | t }} + + {{- 'component_reference.link' | t -}} + +

    +
    +

    {{ 'component_reference.rich_opt_out' | t }}

    +
      +
    • + {{ 'component_reference.list_item' | t: number: 1 }} +
    • +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +

    + {{ 'component_reference.forms' | t }} +

    +

    + {{ 'component_reference.forms_text' | t }} +

    + {% comment %} A GET form back to this page, so the submitted values show in the address. {% endcomment %} +
    + +
    + + + +
    + +
    + + {{ 'component_reference.delivery' | t }} + +
    + {% for option in deliveries %} + {% assign key = 'component_reference.' | append: option %} + + {% endfor %} +
    + +
    + +
    + + +
    + +
    + + {{- 'component_reference.range' | t -}} + +
    + + +
    + +
    + +
    + +
    + +
    + + +
    +
    +
    +
    +
    + +
    +

    + {{ 'component_reference.actions' | t }} +

    +
    + +
    + +
    + + +
    + +
    +
    +
    + +
    +

    + {{ 'component_reference.content' | t }} +

    +
    + + +
    + + + + + + + + + + + {% for number in (1..3) %} + + + + + + {% endfor %} + + + + + + + +
    + {{ 'component_reference.table_caption' | t }} +
    + {{ 'component_reference.item' | t }} + + {{ 'products.quantity' | t }} + + {{ 'products.price' | t }} +
    + {{ 'component_reference.example' | t: number: number }} + {{ number }} + {{ number | times: 1000 | money }} +
    + {{ 'component_reference.total' | t }} + + {{ 14000 | money }} +
    +
    + +
    +
    +
    + {% render 'icon', name: 'file' %} +
    +
    + + {{- 'component_reference.file' | t -}} + + PDF +
    +
    +
    +
    + + {%- render 'icon', name: 'loader' -%} + +
    +
    + + {{- 'component_reference.file' | t -}} + + + {{- 'component_reference.uploading' | t -}} + +
    +
    +
    +
    + {% render 'icon', name: 'file' %} +
    +
    + + {{- 'component_reference.file' | t -}} + + + {{- 'component_reference.failed' | t -}} + +
    +
    +
    + +
    +
    + {% for tab in tabs %} + {% assign key = 'component_reference.' | append: tab %} + + {% endfor %} +
    + {% for tab in tabs %} + {% assign key = 'component_reference.' + | append: tab + | append: '_text' + %} +
    +

    {{ key | t }}

    +
    + {% endfor %} +
    + +
    + +
    +

    + {{ 'component_reference.more_text' | t }} +

    +
    +
    +
    +
    + +
    +

    + {{ 'component_reference.overlays' | t }} +

    +

    + {{ 'component_reference.overlays_text' | t }} +

    +
    +
    + +
    +
    + +
    +
    +
    + +
    + + {{- 'component_reference.products' | t -}} + +
    +
    + +
    +
    +
    + +
    + + {% render 'icon', name: 'bag' %} + +
    +
    + +
    +
    +
    + +
    + +
    +
    + +
    +
    +
    + +
    + +
    + + +
    +
    + +
    + + +
    +
    +
    +
    +
    +
    +
    + +{% stylesheet %} + .component-reference__layout { + display: grid; + grid-template-columns: 12rem minmax(0, 1fr); + align-items: start; + gap: 48px; + } + .component-reference__toc { + position: sticky; + top: calc(var(--header-height) + 24px); + } + .component-reference__content { + display: flex; + flex-direction: column; + gap: 64px; + min-width: 0; + max-width: 48rem; + } + .component-reference__intro { + display: flex; + flex-direction: column; + gap: 16px; + } + .component-reference__group { + display: flex; + flex-direction: column; + gap: 16px; + scroll-margin-top: calc(var(--header-height) + 24px); + } + .component-reference__demo { + display: flex; + flex-direction: column; + gap: 24px; + padding: 24px; + border: 1px solid var(--border); + border-radius: var(--radius-xl); + } + .component-reference__row { + display: flex; + flex-flow: row wrap; + align-items: center; + gap: 12px; + } + .component-reference__options { + display: grid; + gap: 12px; + } + .component-reference__number { + text-align: right; + } + .component-reference__collapsible { + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 8px; + } + .component-reference__collapsible[data-state='open'] .icon { + rotate: 180deg; + } + @media (max-width: 767px) { + .component-reference__layout { + grid-template-columns: minmax(0, 1fr); + } + .component-reference__toc { + position: static; + } + } +{% endstylesheet %} + +{% schema %} +{ + "name": "Component reference", + "settings": [ + { + "type": "select", + "id": "scheme", + "label": "Color scheme", + "options": [ + { + "value": "default", + "label": "Default" + }, + { + "value": "muted", + "label": "Light gray" + }, + { + "value": "dark", + "label": "Dark" + } + ], + "default": "default" + } + ] +} +{% endschema %} diff --git a/tests/helpers/liquid.mjs b/tests/helpers/liquid.mjs new file mode 100644 index 000000000..64ad87f9f --- /dev/null +++ b/tests/helpers/liquid.mjs @@ -0,0 +1,162 @@ +import { spawn } from "node:child_process" +import { readFile } from "node:fs/promises" +import { createServer } from "node:http" +import { resolve } from "node:path" + +export async function registryServer() { + let origin + const requests = [] + const server = createServer(async (request, response) => { + try { + const name = request.url.split("/").at(-1) + if (!/^[a-z-]+\.json$/.test(name)) throw new Error("Invalid item") + requests.push(name) + const body = JSON.parse( + await readFile(resolve("public/r/liquid", name), "utf8") + ) + body.registryDependencies = body.registryDependencies?.map((dependency) => + dependency.replace("https://ui.full.dev/r/liquid", origin) + ) + response.setHeader("content-type", "application/json") + response.end(JSON.stringify(body)) + } catch { + response.writeHead(404).end() + } + }) + await new Promise((done) => server.listen(0, "127.0.0.1", done)) + origin = `http://127.0.0.1:${server.address().port}` + return { + origin, + requests, + close: () => new Promise((done) => server.close(done)), + } +} + +export async function install(origin, theme, name = "components", flags = []) { + const child = spawn( + process.execPath, + [ + resolve("node_modules/shadcn/dist/index.js"), + "add", + `${origin}/${name}.json`, + "--cwd", + theme, + "--yes", + ...flags, + ], + { + env: { ...process.env, NO_COLOR: "1" }, + stdio: ["ignore", "pipe", "pipe"], + } + ) + let output = "" + child.stdout.on("data", (data) => (output += data)) + child.stderr.on("data", (data) => (output += data)) + const code = await new Promise((done) => child.on("close", done)) + if (code !== 0) throw new Error(`shadcn exited ${code}:\n${output}`) + return output +} + +// LiquidJS exercises our markup, not Shopify's commerce objects or asset pipeline. +export async function renderFixture( + theme, + { legacy = false, themeRuntime = false } = {} +) { + const { Liquid } = await import("liquidjs") + const { mkdir, readdir, writeFile } = await import("node:fs/promises") + const renderRoot = resolve(theme, ".dev/liquid-fixture") + await mkdir(renderRoot, { recursive: true }) + const strip = (source) => + source + .replace(/{%-? (doc|schema) -?%}[\s\S]*?{%-? end\1 -?%}/g, "") + .replace( + /{%-? stylesheet -?%}([\s\S]*?){%-? endstylesheet -?%}/g, + "" + ) + for (const file of await readdir(resolve(theme, "snippets"))) { + await writeFile( + resolve(renderRoot, file), + strip(await readFile(resolve(theme, "snippets", file), "utf8")) + ) + } + await writeFile( + resolve(renderRoot, "icon.liquid"), + strip(await readFile("tests/fixtures/liquid/icon.liquid", "utf8")) + ) + const locale = JSON.parse( + await readFile("tests/fixtures/liquid/en.json", "utf8") + ) + const engine = new Liquid({ root: renderRoot, extname: ".liquid" }) + engine.registerFilter("t", (key, ...args) => { + let value = + key.split(".").reduce((value, part) => value?.[part], locale) ?? key + for (const [name, replacement] of args) + value = value.replaceAll(`{{ ${name} }}`, replacement) + return value + }) + engine.registerFilter("asset_url", (asset) => `/assets/${asset}`) + engine.registerFilter( + "stylesheet_tag", + (url) => `` + ) + engine.registerFilter("json", JSON.stringify) + const reference = strip( + await readFile("tests/fixtures/liquid/reference.liquid", "utf8") + ) + const extra = themeRuntime + ? "" + : await readFile("tests/fixtures/liquid/interactive.liquid", "utf8") + let head = legacy + ? "" + : `{% render 'ui'${themeRuntime ? ", runtime: false" : ""} %}` + if (themeRuntime) { + head += ["critical.css", "overlays.css", "components.css"] + .map((name) => ``) + .join("") + const layout = await readFile(resolve(theme, "layout/theme.liquid"), "utf8") + head += layout.match(/' + } + const styles = new Set() + const html = await engine.parseAndRender( + `Fulldev Liquid components${head}${reference}${extra}`, + { + section: { id: "ui-reference", settings: { scheme: "default" } }, + routes: { all_products_collection_url: "/collections/all" }, + } + ) + // Model Shopify's stylesheet collection: shared snippet styles after the theme assets. + return html + .replace(/`) +} + +export async function fixtureServer(theme, options) { + const html = await renderFixture(theme, options) + const server = createServer(async (request, response) => { + try { + if (request.url === "/") { + response.setHeader("content-type", "text/html") + response.end(html) + } else { + if (!/^\/assets\/[a-z0-9.-]+$/.test(request.url)) + throw new Error("Invalid asset") + response.setHeader( + "content-type", + request.url.endsWith(".js") ? "text/javascript" : "text/css" + ) + response.end(await readFile(resolve(theme, request.url.slice(1)))) + } + } catch { + response.writeHead(404).end() + } + }) + await new Promise((done) => server.listen(0, "127.0.0.1", done)) + return { + origin: `http://127.0.0.1:${server.address().port}`, + close: () => new Promise((done) => server.close(done)), + } +} diff --git a/tests/liquid.test.mjs b/tests/liquid.test.mjs new file mode 100644 index 000000000..6b4376aad --- /dev/null +++ b/tests/liquid.test.mjs @@ -0,0 +1,139 @@ +import assert from "node:assert/strict" +import { createHash } from "node:crypto" +import { + mkdir, + mkdtemp, + readdir, + readFile, + rm, + writeFile, +} from "node:fs/promises" +import { tmpdir } from "node:os" +import { resolve } from "node:path" +import { test } from "node:test" +import { install, registryServer } from "#liquid-fixture" + +const json = async (path) => JSON.parse(await readFile(path, "utf8")) +const digest = (source) => createHash("sha256").update(source).digest("hex") + +test("Liquid retains the shared semantic palette and Card/Button parts", async () => { + const themes = await json("registry/styles/shadcn/themes.json") + const neutral = themes.find((theme) => theme.name === "neutral").cssVars + const css = await readFile("liquid/styles/tokens.css", "utf8") + for (const [key, value] of Object.entries(neutral.light)) { + assert(css.includes(`--${key}: ${value};`), key) + } + const contract = await json("liquid/contracts.json") + const astroCardFiles = await readdir("src/components/ui/card") + const astroCard = ( + await Promise.all( + astroCardFiles + .filter((name) => name.endsWith(".astro")) + .map((name) => + readFile(resolve("src/components/ui/card", name), "utf8") + ) + ) + ).join("\n") + for (const [, part] of astroCard.matchAll(/data-slot="([^"]+)"/g)) + assert(contract.card.includes(part), part) + const button = await readFile("liquid/components/button.liquid", "utf8") + const astroButton = await readFile( + "src/components/ui/button/button-variants.ts", + "utf8" + ) + for (const [, kind, value] of astroButton.matchAll( + /cn-button-(variant|size)-([a-z-]+)/g + )) { + if (value === "default") continue + assert( + button.includes(`data-${kind}='${value}'`) || + (value === "icon" && button.includes("data-size^='icon'")), + value + ) + } +}) + +test("every individual universal item installs its complete dependency closure", async () => { + const registry = await json("liquid/registry.json") + const items = new Map(registry.items.map((item) => [item.name, item])) + const collect = (name, seen = new Set()) => { + if (seen.has(name)) return seen + seen.add(name) + for (const dependency of items.get(name).registryDependencies ?? []) + collect(dependency.split("/").at(-1).slice(0, -5), seen) + return seen + } + for (const item of registry.items) { + const provided = new Map( + [...collect(item.name)].flatMap((name) => + items.get(name).files.map((file) => [file.target.slice(2), file]) + ) + ) + for (const file of provided.values()) { + if (!file.path.endsWith(".liquid")) continue + const source = await readFile(resolve("liquid", file.path), "utf8") + for (const [, snippet] of source.matchAll(/{%[-\s]*render\s+'([^']+)'/g)) + assert( + provided.has(`snippets/${snippet}.liquid`), + `${item.name}: ${snippet}` + ) + for (const [, asset] of source.matchAll( + /'([^']+\.(?:css|js))' \| asset_url/g + )) + assert(provided.has(`assets/${asset}`), `${item.name}: ${asset}`) + } + } +}) + +test("real shadcn CLI: clean theme, single items, repeat install and preservation", async () => { + const server = await registryServer() + const theme = await mkdtemp(resolve(tmpdir(), "fulldev-liquid-test-")) + try { + await mkdir(resolve(theme, "layout")) + await mkdir(resolve(theme, "config")) + const merchant = { + "layout/theme.liquid": "{{ content_for_header }}", + "config/settings_data.json": '{"merchant":"keep"}', + } + for (const [file, source] of Object.entries(merchant)) + await writeFile(resolve(theme, file), source) + await install(server.origin, theme, "button") + assert( + (await readdir(resolve(theme, "snippets"))).includes("ui-button.liquid") + ) + assert( + !(await readdir(resolve(theme, "assets"))).some((name) => + name.endsWith(".js") + ) + ) + await install(server.origin, theme, "select") + await install(server.origin, theme) + const manifest = await json(resolve(theme, "assets/ui-manifest.json")) + for (const [path, expected] of Object.entries(manifest.files)) + assert.equal(digest(await readFile(resolve(theme, path))), expected, path) + const before = await readFile( + resolve(theme, "assets/ui-manifest.json"), + "utf8" + ) + await install(server.origin, theme) + assert.equal( + await readFile(resolve(theme, "assets/ui-manifest.json"), "utf8"), + before + ) + for (const [file, source] of Object.entries(merchant)) + assert.equal(await readFile(resolve(theme, file), "utf8"), source) + assert(!(await readdir(theme)).includes("components.json")) + assert(!(await readdir(theme)).includes("package.json")) + assert(server.requests.includes("runtime.json")) + const custom = "{% comment %}Store customization{% endcomment %}" + await writeFile(resolve(theme, "snippets/ui-button.liquid"), custom) + await install(server.origin, theme) + assert.equal( + await readFile(resolve(theme, "snippets/ui-button.liquid"), "utf8"), + custom + ) + } finally { + await server.close() + await rm(theme, { recursive: true, force: true }) + } +}) From 246bc2106728063f658f4689261c08b6fd5ac04f Mon Sep 17 00:00:00 2001 From: Sil Date: Sat, 17 Oct 2026 12:20:52 +0200 Subject: [PATCH 2/3] test(liquid): cover portable breakpoints and dynamic slide translations --- liquid/README.md | 4 +- public/r/liquid/components.json | 2 +- public/r/liquid/empty.json | 2 +- public/r/liquid/field.json | 2 +- public/r/liquid/init.json | 2 +- public/r/liquid/input.json | 2 +- public/r/liquid/runtime.json | 2 +- public/r/liquid/section.json | 2 +- tests/browser/liquid-regressions.test.mjs | 159 ++++++++++++++++++++++ 9 files changed, 168 insertions(+), 9 deletions(-) create mode 100644 tests/browser/liquid-regressions.test.mjs diff --git a/liquid/README.md b/liquid/README.md index fa5b58990..5cc3362ac 100644 --- a/liquid/README.md +++ b/liquid/README.md @@ -8,9 +8,9 @@ Native Shopify Liquid, readable CSS and ES modules. The consumer needs no Tailwi `registry.json` is the install contract. Every item is universal, every file has an explicit `~/assets/` or `~/snippets/` target, and dependencies use full Liquid registry URLs. Individual items install their prerequisites, including shared popup styles and runtime where needed. `components` is the complete set. `init` is tokens and base styles only. -CSS and Liquid are maintained by hand. The shared neutral palette comes from `registry/styles/shadcn/themes.json`; `styles/primitives.css` holds native sizes, radii, shadows and media roles. Theme colors, settings, content, Shopify forms and layouts remain theme-owned. A consumer loads its scheme overrides after `ui`, and `custom.css` after `content_for_header`. +Only shared neutral palette tokens, the part inventory, licenses and file hashes are generated by `scripts/registry-liquid.mjs`. CSS and Liquid are maintained by hand. The palette comes from `registry/styles/shadcn/themes.json`; `styles/primitives.css` holds native sizes, radii, shadows and media roles. Theme colors, settings, content, Shopify forms and layouts remain theme-owned. A consumer loads its scheme overrides after `ui`, and `custom.css` after `content_for_header`. -Registry generation and installation tests are added separately from this source layer. Local LiquidJS fixtures do not verify a store's commerce objects or Shopify's stylesheet subsetting. +Run `pnpm registry:liquid` after a source change. Commit `public/r/liquid`, `contracts.json`, `styles/tokens.css`, `licenses.json` and `manifest.json`. `pnpm test:liquid` verifies the real CLI, the dependency closures, token parity and repeat installs. `pnpm test:browser` verifies a CLI-installed copy in Chromium, Firefox and WebKit, including native forms and editor lifecycle. The local renderer is LiquidJS with explicit Shopify filter substitutes. It does not verify a store's commerce objects or Shopify's stylesheet subsetting. This first port follows Vega and the complete reusable storefront layer from `fulldotdev/liquid-template` at `e13fc25`. Sidebar, Resizable, Menubar, Scroll Area and React-only app components are outside this native storefront set. Shopify blocks, commerce adapters, translation keys and settings are not installed by the UI registry. The existing storefront media and card variants are additive to the shared component contract. Other Tailwind presets are not translated automatically. diff --git a/public/r/liquid/components.json b/public/r/liquid/components.json index 2576b9d46..ffd6cf620 100644 --- a/public/r/liquid/components.json +++ b/public/r/liquid/components.json @@ -74,7 +74,7 @@ }, { "path": "manifest.json", - "content": "{\n \"registry\": \"https://ui.full.dev/r/liquid/\",\n \"files\": {\n \"assets/ui-tokens.css\": \"55a8f2810ab4a1498ca3dd2f6e7dc89009c60e2402ddfcb95783b6eba996c374\",\n \"snippets/ui-base.liquid\": \"1a39ed17214f86fa6fa844cb1d26e7e4a386c2c8a9d4492b2fd03efa940b9955\",\n \"snippets/ui-init.liquid\": \"98f56067cf51e4cb2cd3e10ede7c00304c73ce57fe5b5a0270c0d8a9e448f7af\",\n \"assets/ui-licenses.json\": \"6eaca08e24468055e5471a9141a3453381b86ced4bdce85cb874f73b550baa09\",\n \"assets/ui-carousel.js\": \"95655cf60a4c1cacdad87295acb2126032dc8d59ca117c6d738cf534be648e12\",\n \"assets/ui-controls.js\": \"cf51f6d8b23c93e30d3355e993ff21a1aee79f38facfa413bed64e9815df255d\",\n \"assets/ui-lifecycle.js\": \"78c975473620171c01b1289bf5cdce56a5626c847437fa5168fb91a4300260d6\",\n \"assets/ui-start.js\": \"738d7e171b5d7993bfd0a169e4382b2db08340f52a87059b94d27e1fe3606500\",\n \"assets/data-slot-core.js\": \"d2879cc3a2f05a7867763b937bad3bd4a369f54670989ce535367cb690fb66e6\",\n \"assets/data-slot-dialog.js\": \"a5201f452700d04145b5156cc46880d2b5bdf7521b03ff9521c3f7693ee1b5d5\",\n \"assets/data-slot-command.js\": \"f7944120617676b14dfda2ea3f3e8a15767e0766d5312b4ed9519affe9b7444a\",\n \"assets/data-slot-navigation-menu.js\": \"f9b42a9bceca8e1103718d43aa5f0d141ab63d38278cb121e790a2c80e827b68\",\n \"assets/data-slot-accordion.js\": \"fe6b0ac2c87a2e31582e5e88c509e0b508f41d0065d0a420407999ad210e6a27\",\n \"assets/data-slot-tabs.js\": \"1f187134bba755cfda9d35cf9f16174c1a183862d32d100f270f4177033058c7\",\n \"assets/data-slot-combobox.js\": \"b5f2ed9d65a43d836c59c0d4dc33c16ab12ef9fa05d510078b5d62517d854db6\",\n \"assets/data-slot-carousel.js\": \"d83ef938d3b62dc3652b123428ca2261e03fb2ba1133992b0f5545a974238d03\",\n \"assets/data-slot-alert-dialog.js\": \"1caee9a86bb2a816ea32bc1a48b82f3cc588ec3c563ebc443f836ab1dae2f618\",\n \"assets/data-slot-collapsible.js\": \"c42f76e3dfeff4570eb2f5104434fbffd3ef55710833e888965c590ce59f4f9f\",\n \"assets/data-slot-drawer.js\": \"c9343b26ea27193cc1d2da3e41bf452005bc2e534151fa1a762b2b6535de28d0\",\n \"assets/data-slot-dropdown-menu.js\": \"543c5717c9ba8363bbab4c0a11005980d75d4baaed79cf7123c7bcfe3ec8720b\",\n \"assets/data-slot-hover-card.js\": \"86e1bfa5d4822f180f3501ca7cc51bf044d4e917a7ebdb7a10b70006dba6467f\",\n \"assets/data-slot-popover.js\": \"526e30d663381329a7a6394eae886224024034d0abf96f888c932bb4b8c1b1cb\",\n \"assets/data-slot-radio-group.js\": \"7a9b90166de80d79ee42aa2df73e4d8179e9d291cb30b3095c7d8332375b0499\",\n \"assets/data-slot-select.js\": \"13d1a3fbe90f4b3b830d987b6a28267897faa3589bf00e64edca4938acbf406b\",\n \"assets/data-slot-slider.js\": \"0d10131aa95450aa1269234103ead8d805d6abdf9e0683510f4fcf58ab0f1efa\",\n \"assets/data-slot-switch.js\": \"0d5b09a89429880f61aeb540be5f87383ba5c6beb261ad468e9a6cf18e45b6a0\",\n \"assets/data-slot-toast.js\": \"acfd86824d0ff37fbd62d0c48672e527d7391412780730e9a3a8d61268b3aaf2\",\n \"assets/data-slot-toggle.js\": \"7a651d578cb83be842c8e59fe1a2cd124594b1fd6c942faf0a33cf9e57326947\",\n \"assets/data-slot-toggle-group.js\": \"c1f2fc9712974e63e4e44c97ab2a1ec8bc3c3cb73c1afb4578b085fed11977f1\",\n \"assets/data-slot-tooltip.js\": \"5a9e51a54669153620eb3512e74487eac12ababe3331aff7dfed6c717b8b93c0\",\n \"snippets/ui-runtime.liquid\": \"62ae98f4ad16dc0ca406d96bbabb373cd30a63dd385e3614da0dcd2e63d0f17c\",\n \"assets/ui-motion.js\": \"2252a6794ddb162b73ee1640af212292b8c520e98ae09ea862b479078f81d3d1\",\n \"snippets/ui-banner.liquid\": \"9965b12e9a2036aca6e35f1327da8a240c894a6fd91d99f37e77107818d271a3\",\n \"snippets/ui-tabs.liquid\": \"5b57bb3d6400e44ef9343f7b45d9baf2d8581d678ccc64764be60164160687eb\",\n \"snippets/ui-form.liquid\": \"1c002d21c441e217a8f3d64268d429c97e4785af118eb7f7abb5ff1c3d10a3b6\",\n \"assets/ui-form.css\": \"ab0dd70553818fe0799e393f6811e949884eca9f44d9ceff4c2b333c0bf724b4\",\n \"snippets/ui-select.liquid\": \"2d8c99759161a024285d1b54c0ca166dc7b13c97596497351253e945b5e52ee2\",\n \"snippets/ui-field.liquid\": \"cf73aaaf3e27bc9d121dd089317610fbd08b9550a1d5fb0738337c6817879309\",\n \"assets/ui-field.css\": \"1436dff789c8811aae13025ca862ac1bfbf64c57e895c87474f45bfcd8b3f665\",\n \"snippets/ui-item.liquid\": \"fe03c1a3e6df09192c3ac7a08520b0fc1c629a597a029d193ff91655cb7ceac0\",\n \"snippets/ui-spinner.liquid\": \"84d48693f10cb8af4549cc0d71cd847a85da0e111f2ad64dae309c128c2a8c4a\",\n \"snippets/ui-video.liquid\": \"14acea7c523198269a9165270decac21a43a5d69bf12bd248a76aceb2e9b26ed\",\n \"assets/ui-video.css\": \"c1572853f48474f6291a70ccdb1085512700fc3dcbbdaebd71374b838ec67fee\",\n \"snippets/ui-button.liquid\": \"5dd981b8e5de94d5dac3fd367a3d31c0553a2ea2f72aefda6ee2e07fde876af8\",\n \"snippets/ui-toggle.liquid\": \"c86100cf7c163c0d5bc81976fd794788f68fab2ebc5c0887d8042958ce020912\",\n \"snippets/ui-logo.liquid\": \"5d6de7be3f7aab9e05c9533afef39ea0e9dce1b4ee12a13bf7b82780c973b126\",\n \"snippets/ui-kbd.liquid\": \"024f34ed2932d183671f7fa44ff724a70ec17b766cf7e0d3f565f46f33ab2cf2\",\n \"snippets/ui-rating.liquid\": \"aa57adc5a5508b368da78023b242694e6ce924b6e9e5407c141a5030a414bf1d\",\n \"assets/ui-rating.css\": \"e5efea2a9edaea78522f20ebd9e9d01abe8a2fcd310c193239adf80ec33b4975\",\n \"snippets/ui-tooltip.liquid\": \"d2863a0da3090c73dda10d8615b84fc50fab64373a466fbf4e4e743cfb083c96\",\n \"snippets/ui-avatar.liquid\": \"9bfb5d5f535857cc45f31afbc91c855e94972bab07ad3b4fe2dc0d241106385d\",\n \"snippets/ui-empty.liquid\": \"1a6b8a006d67700225a9f646fdb74308904319cc1cd8ea83a637e2d9d5fbc3d9\",\n \"snippets/ui-alert-dialog.liquid\": \"f739374035ffa0f3bca2c4b6d946710751c8268c8f1dde535251be9c2c7b0cd0\",\n \"snippets/ui-navigation-menu.liquid\": \"52aecbb36b0031810a85a99f5d4439e852dc1ba3a872b9525dd75c8df8b5a84e\",\n \"assets/ui-navigation-menu.css\": \"29a47ce0eb549e58cc8195dcb656bd2cef56360178c76acfc2d481a110463ca6\",\n \"snippets/ui-typography.liquid\": \"b03dc93c4de6636164a206c95979caa6fe9ab7cf8d1c24d75611416259da7979\",\n \"assets/ui-typography.css\": \"4363ea0774e3c76a0154b1c586a6270a4fbd31b7794fe9549977f41563f54d78\",\n \"snippets/ui-toc.liquid\": \"04038e0870112b2335b80b4092814915105b31d08cdd36ecf1f4de1761f66acf\",\n \"snippets/ui-icon.liquid\": \"ca75a9485eb33cdb31a3a4e2e5ca03ce74d4995dbd1696d964832a87602bf3ca\",\n \"assets/ui-icon.css\": \"cfb1a47905cbd9c25b635c96926a77f8eed983a4aca62fdce758345dcc792add\",\n \"snippets/ui-combobox.liquid\": \"bacd605b44237512a74d34119d01432440b76ae60218dae6a6f37f98b32e3e00\",\n \"snippets/ui-toast.liquid\": \"bb032d90d6084fb15e12da22777635b80d09a06402513c46c89e853e3ede0405\",\n \"assets/ui-toast.css\": \"c322fab210a202fa434695025550495dbdc93e688ed7ae39303fea9830864d0b\",\n \"snippets/ui-header.liquid\": \"cc14effb4e3b6c4ac397e306e4ebd270a31b3415cc12ffbeb6cd38f411eaba80\",\n \"snippets/ui-command.liquid\": \"4a37f59dc0039fa1505de764472212d6cd7c4e5c52b737a1700e928db286f233\",\n \"snippets/ui-accordion.liquid\": \"4b58c83ae13e14e03a3129a2dccbd02eeafa90c1e48edbbf9af2dc412bf3aabf\",\n \"assets/ui-accordion.css\": \"430924440bd0f1d49ceb72baffc77404c9411c31c24b940d75861daba2649e83\",\n \"snippets/ui-slider.liquid\": \"f2de1fb9099de77bf291455447bc86b2578781e7cf0efdf12117fb4575e0b0e6\",\n \"snippets/ui-input.liquid\": \"1f101e309dc15bae127766427ef619d190eb9e341fc34808b26f56ea354312e7\",\n \"snippets/ui-input-group.liquid\": \"b862a0c63e23dc7ae4ffe3a8e057a87f38fa8e6c8842ddf3c86b826de0283d70\",\n \"snippets/ui-triggers.liquid\": \"ba9c7b802fb0a2aba0e9cc145a9dd44134964afb53e5363cf9e0ae138b906cc9\",\n \"snippets/ui-toggle-group.liquid\": \"2d61e0b9bc746e05834157f5a75d23123f02bc2df737412e4f6099844afc4f49\",\n \"snippets/ui-dialog.liquid\": \"779bbcd609a69ed29a984df1f26f9ed80fd24d71c4ee3ead5f4daa055b28e9fb\",\n \"snippets/ui-drawer.liquid\": \"16120fdd70fac393470c3936223e78ad5f81164e2b7616fd075e8e82098a17d5\",\n \"snippets/ui-badge.liquid\": \"13d8b46cd5432783a9b986de76365c9fcc0e7fb30ad4f956ec386c1ae549ce99\",\n \"snippets/ui-alert.liquid\": \"7530d324ac1fb4645bba0a23780fbeacc67544f8bad3ed9e367debd88c0365b5\",\n \"snippets/ui-table.liquid\": \"6599d8b3bd193761571c2b71e499f78546ba5c2768d4e0a0a60cd0e348632e5b\",\n \"snippets/ui-breadcrumb.liquid\": \"dafdeaf03286b386c90f8e72777de1946fba4718ee568bd85ca7bc768483a3ca\",\n \"snippets/ui-aspect-ratio.liquid\": \"c8aae05fcd76af7ae768aedafd7e758533e2b52135b00ff72940896ad793f177\",\n \"snippets/ui-price.liquid\": \"96156754aee3a04aa3ae7084a14470000213e8be14468c4937e9f3a87e348234\",\n \"assets/ui-price.css\": \"95c947e219621102b704164e48a83e381e6e2b27b99311a294e076745b39a36f\",\n \"snippets/ui-radio-group.liquid\": \"af9978e18716cc86d03ed21dece8304992e74a4fab88e12069b0648cfd944492\",\n \"snippets/ui-checkbox.liquid\": \"0e4dd1738b355c5a93533e0b813e2e6c72f7491c289d65aa7b6435be1c599006\",\n \"snippets/ui-switch.liquid\": \"84ed60c777e77daec8b5fe6f2ee434877d51224235dc1bc2e17927cd182fe646\",\n \"snippets/ui-skeleton.liquid\": \"1e30245f6577f73d28f4badbc293934656882a29d5dec1d2f74ef95f3bcede2c\",\n \"snippets/ui-separator.liquid\": \"90b1d2795666b6c51f35645ca8d07659f2c4868fe9553e233cddb4e962095f6e\",\n \"snippets/ui-card.liquid\": \"581a46843f1280ebace0423ee84289b40fbd46142e6a9ab3940f63eca0e4c5f2\",\n \"assets/ui-card.css\": \"ee7e4192ec814066d0c9838f810ae4ca267b9f071b7fa945c43226d46b0f8dc9\",\n \"snippets/ui-pagination.liquid\": \"30943c00686e1a23d92865465b38c964e059b14ae3a20fb7fe151824a73773eb\",\n \"snippets/ui-popup.liquid\": \"840f938aa2a331e4e7d55c659f25e841dcfb1bf5c5cadace6263243352ff8359\",\n \"snippets/ui-sheet.liquid\": \"0de705bed685bf78fa3af4517c947b38d62a1aa56bf29356c0a201b64db17614\",\n \"snippets/ui-carousel.liquid\": \"59dc0c06b2a9f2f779ca22b3ff4b005071d4909aba052d6f8f0a1aa19e84c2ce\",\n \"assets/ui-carousel.css\": \"5317c251b3d3599ba330947fc9709ea50acfb858a5d7e08573b5d34ada2f3bf4\",\n \"snippets/ui-attachment.liquid\": \"6881df7cdb1b27462d8cce7c17c0b4ed4dd860d62535e1daa81378a57bb69ddf\",\n \"snippets/ui-marquee.liquid\": \"47d5df8f1ff46a2cd48a132986c2ba199c9e19b485e62fc3e0d19091d4baaab1\",\n \"assets/ui-marquee.css\": \"ef13b83f3dbcd6685fcee3dba14238386d0f34bc2266a5326c0b6f2d40a23fce\",\n \"snippets/ui-invalid-controls.liquid\": \"a169e8244cc86f5431f902f7a73b37188051ba8f2ef3e323830462dae5aed066\",\n \"snippets/ui-section.liquid\": \"da358cd87447457f3675b035b48556103d86cd11e0b2240ae2800e4c9ea54bdc\",\n \"assets/ui-section.css\": \"0d5df020f70d38b3c049430204d469d681f56f6c2c4905b925ca0c81e61ff16f\",\n \"snippets/ui-button-group.liquid\": \"a2790f8da0f8b37acc1ed558ba1548ea250a5b93e1b2dfbd11eb150f3f02783a\",\n \"snippets/ui-collapsible.liquid\": \"e6107a03d00b8cc75bb03206e64b9aa7f2989d982c0408b398b62067c6df450a\",\n \"snippets/ui-dropdown-menu.liquid\": \"25b31e09e3e7bfee9a397ca4de6e310754844c55e71639eadba917b0f4e6d2e5\",\n \"snippets/ui-popover.liquid\": \"44ff93d72eaad409f56f8a84ce25715e2c3633f2ac49ba7a414767c6dbe2ee47\",\n \"snippets/ui-hover-card.liquid\": \"ba4133d352fa14296743138c99f3694015bafe953751b8e42155f047c4beb9f0\",\n \"snippets/ui.liquid\": \"30679056b2eafefbaeef5ecd271604951676893a65a8eccb0f970c787a67b900\"\n }\n}\n", + "content": "{\n \"registry\": \"https://ui.full.dev/r/liquid/\",\n \"files\": {\n \"assets/ui-tokens.css\": \"55a8f2810ab4a1498ca3dd2f6e7dc89009c60e2402ddfcb95783b6eba996c374\",\n \"snippets/ui-base.liquid\": \"bc55195b5b6c82e63deeeed1255fd19b434c47866815f82b95b9202421256d34\",\n \"snippets/ui-init.liquid\": \"98f56067cf51e4cb2cd3e10ede7c00304c73ce57fe5b5a0270c0d8a9e448f7af\",\n \"assets/ui-licenses.json\": \"6eaca08e24468055e5471a9141a3453381b86ced4bdce85cb874f73b550baa09\",\n \"assets/ui-carousel.js\": \"3337447a9e0051cebbc1726bca7c7966de1035819fb8e0b676383b85f1917632\",\n \"assets/ui-controls.js\": \"cf51f6d8b23c93e30d3355e993ff21a1aee79f38facfa413bed64e9815df255d\",\n \"assets/ui-lifecycle.js\": \"78c975473620171c01b1289bf5cdce56a5626c847437fa5168fb91a4300260d6\",\n \"assets/ui-start.js\": \"738d7e171b5d7993bfd0a169e4382b2db08340f52a87059b94d27e1fe3606500\",\n \"assets/data-slot-core.js\": \"d2879cc3a2f05a7867763b937bad3bd4a369f54670989ce535367cb690fb66e6\",\n \"assets/data-slot-dialog.js\": \"a5201f452700d04145b5156cc46880d2b5bdf7521b03ff9521c3f7693ee1b5d5\",\n \"assets/data-slot-command.js\": \"f7944120617676b14dfda2ea3f3e8a15767e0766d5312b4ed9519affe9b7444a\",\n \"assets/data-slot-navigation-menu.js\": \"f9b42a9bceca8e1103718d43aa5f0d141ab63d38278cb121e790a2c80e827b68\",\n \"assets/data-slot-accordion.js\": \"fe6b0ac2c87a2e31582e5e88c509e0b508f41d0065d0a420407999ad210e6a27\",\n \"assets/data-slot-tabs.js\": \"1f187134bba755cfda9d35cf9f16174c1a183862d32d100f270f4177033058c7\",\n \"assets/data-slot-combobox.js\": \"b5f2ed9d65a43d836c59c0d4dc33c16ab12ef9fa05d510078b5d62517d854db6\",\n \"assets/data-slot-carousel.js\": \"d83ef938d3b62dc3652b123428ca2261e03fb2ba1133992b0f5545a974238d03\",\n \"assets/data-slot-alert-dialog.js\": \"1caee9a86bb2a816ea32bc1a48b82f3cc588ec3c563ebc443f836ab1dae2f618\",\n \"assets/data-slot-collapsible.js\": \"c42f76e3dfeff4570eb2f5104434fbffd3ef55710833e888965c590ce59f4f9f\",\n \"assets/data-slot-drawer.js\": \"c9343b26ea27193cc1d2da3e41bf452005bc2e534151fa1a762b2b6535de28d0\",\n \"assets/data-slot-dropdown-menu.js\": \"543c5717c9ba8363bbab4c0a11005980d75d4baaed79cf7123c7bcfe3ec8720b\",\n \"assets/data-slot-hover-card.js\": \"86e1bfa5d4822f180f3501ca7cc51bf044d4e917a7ebdb7a10b70006dba6467f\",\n \"assets/data-slot-popover.js\": \"526e30d663381329a7a6394eae886224024034d0abf96f888c932bb4b8c1b1cb\",\n \"assets/data-slot-radio-group.js\": \"7a9b90166de80d79ee42aa2df73e4d8179e9d291cb30b3095c7d8332375b0499\",\n \"assets/data-slot-select.js\": \"13d1a3fbe90f4b3b830d987b6a28267897faa3589bf00e64edca4938acbf406b\",\n \"assets/data-slot-slider.js\": \"0d10131aa95450aa1269234103ead8d805d6abdf9e0683510f4fcf58ab0f1efa\",\n \"assets/data-slot-switch.js\": \"0d5b09a89429880f61aeb540be5f87383ba5c6beb261ad468e9a6cf18e45b6a0\",\n \"assets/data-slot-toast.js\": \"acfd86824d0ff37fbd62d0c48672e527d7391412780730e9a3a8d61268b3aaf2\",\n \"assets/data-slot-toggle.js\": \"7a651d578cb83be842c8e59fe1a2cd124594b1fd6c942faf0a33cf9e57326947\",\n \"assets/data-slot-toggle-group.js\": \"c1f2fc9712974e63e4e44c97ab2a1ec8bc3c3cb73c1afb4578b085fed11977f1\",\n \"assets/data-slot-tooltip.js\": \"5a9e51a54669153620eb3512e74487eac12ababe3331aff7dfed6c717b8b93c0\",\n \"snippets/ui-runtime.liquid\": \"62ae98f4ad16dc0ca406d96bbabb373cd30a63dd385e3614da0dcd2e63d0f17c\",\n \"assets/ui-motion.js\": \"2252a6794ddb162b73ee1640af212292b8c520e98ae09ea862b479078f81d3d1\",\n \"snippets/ui-banner.liquid\": \"9965b12e9a2036aca6e35f1327da8a240c894a6fd91d99f37e77107818d271a3\",\n \"snippets/ui-tabs.liquid\": \"5b57bb3d6400e44ef9343f7b45d9baf2d8581d678ccc64764be60164160687eb\",\n \"snippets/ui-form.liquid\": \"1c002d21c441e217a8f3d64268d429c97e4785af118eb7f7abb5ff1c3d10a3b6\",\n \"assets/ui-form.css\": \"ab0dd70553818fe0799e393f6811e949884eca9f44d9ceff4c2b333c0bf724b4\",\n \"snippets/ui-select.liquid\": \"2d8c99759161a024285d1b54c0ca166dc7b13c97596497351253e945b5e52ee2\",\n \"snippets/ui-field.liquid\": \"cf73aaaf3e27bc9d121dd089317610fbd08b9550a1d5fb0738337c6817879309\",\n \"assets/ui-field.css\": \"96f846a52ab0b832a9eca70ef430e5fd14664a3367d04842de320a82e622722b\",\n \"snippets/ui-item.liquid\": \"fe03c1a3e6df09192c3ac7a08520b0fc1c629a597a029d193ff91655cb7ceac0\",\n \"snippets/ui-spinner.liquid\": \"84d48693f10cb8af4549cc0d71cd847a85da0e111f2ad64dae309c128c2a8c4a\",\n \"snippets/ui-video.liquid\": \"14acea7c523198269a9165270decac21a43a5d69bf12bd248a76aceb2e9b26ed\",\n \"assets/ui-video.css\": \"c1572853f48474f6291a70ccdb1085512700fc3dcbbdaebd71374b838ec67fee\",\n \"snippets/ui-button.liquid\": \"5dd981b8e5de94d5dac3fd367a3d31c0553a2ea2f72aefda6ee2e07fde876af8\",\n \"snippets/ui-toggle.liquid\": \"c86100cf7c163c0d5bc81976fd794788f68fab2ebc5c0887d8042958ce020912\",\n \"snippets/ui-logo.liquid\": \"5d6de7be3f7aab9e05c9533afef39ea0e9dce1b4ee12a13bf7b82780c973b126\",\n \"snippets/ui-kbd.liquid\": \"024f34ed2932d183671f7fa44ff724a70ec17b766cf7e0d3f565f46f33ab2cf2\",\n \"snippets/ui-rating.liquid\": \"aa57adc5a5508b368da78023b242694e6ce924b6e9e5407c141a5030a414bf1d\",\n \"assets/ui-rating.css\": \"e5efea2a9edaea78522f20ebd9e9d01abe8a2fcd310c193239adf80ec33b4975\",\n \"snippets/ui-tooltip.liquid\": \"d2863a0da3090c73dda10d8615b84fc50fab64373a466fbf4e4e743cfb083c96\",\n \"snippets/ui-avatar.liquid\": \"9bfb5d5f535857cc45f31afbc91c855e94972bab07ad3b4fe2dc0d241106385d\",\n \"snippets/ui-empty.liquid\": \"0e7fb5b68d50abcf7213304034762b4dd535c791dd6f4dc517100145e04b0cde\",\n \"snippets/ui-alert-dialog.liquid\": \"f739374035ffa0f3bca2c4b6d946710751c8268c8f1dde535251be9c2c7b0cd0\",\n \"snippets/ui-navigation-menu.liquid\": \"52aecbb36b0031810a85a99f5d4439e852dc1ba3a872b9525dd75c8df8b5a84e\",\n \"assets/ui-navigation-menu.css\": \"29a47ce0eb549e58cc8195dcb656bd2cef56360178c76acfc2d481a110463ca6\",\n \"snippets/ui-typography.liquid\": \"b03dc93c4de6636164a206c95979caa6fe9ab7cf8d1c24d75611416259da7979\",\n \"assets/ui-typography.css\": \"4363ea0774e3c76a0154b1c586a6270a4fbd31b7794fe9549977f41563f54d78\",\n \"snippets/ui-toc.liquid\": \"04038e0870112b2335b80b4092814915105b31d08cdd36ecf1f4de1761f66acf\",\n \"snippets/ui-icon.liquid\": \"ca75a9485eb33cdb31a3a4e2e5ca03ce74d4995dbd1696d964832a87602bf3ca\",\n \"assets/ui-icon.css\": \"cfb1a47905cbd9c25b635c96926a77f8eed983a4aca62fdce758345dcc792add\",\n \"snippets/ui-combobox.liquid\": \"bacd605b44237512a74d34119d01432440b76ae60218dae6a6f37f98b32e3e00\",\n \"snippets/ui-toast.liquid\": \"bb032d90d6084fb15e12da22777635b80d09a06402513c46c89e853e3ede0405\",\n \"assets/ui-toast.css\": \"c322fab210a202fa434695025550495dbdc93e688ed7ae39303fea9830864d0b\",\n \"snippets/ui-header.liquid\": \"cc14effb4e3b6c4ac397e306e4ebd270a31b3415cc12ffbeb6cd38f411eaba80\",\n \"snippets/ui-command.liquid\": \"4a37f59dc0039fa1505de764472212d6cd7c4e5c52b737a1700e928db286f233\",\n \"snippets/ui-accordion.liquid\": \"4b58c83ae13e14e03a3129a2dccbd02eeafa90c1e48edbbf9af2dc412bf3aabf\",\n \"assets/ui-accordion.css\": \"430924440bd0f1d49ceb72baffc77404c9411c31c24b940d75861daba2649e83\",\n \"snippets/ui-slider.liquid\": \"f2de1fb9099de77bf291455447bc86b2578781e7cf0efdf12117fb4575e0b0e6\",\n \"snippets/ui-input.liquid\": \"d29e280ce1d08c7450902e69a73a0b7aa125f1cb662c50dbfc80a4a4fef0a85e\",\n \"snippets/ui-input-group.liquid\": \"b862a0c63e23dc7ae4ffe3a8e057a87f38fa8e6c8842ddf3c86b826de0283d70\",\n \"snippets/ui-triggers.liquid\": \"ba9c7b802fb0a2aba0e9cc145a9dd44134964afb53e5363cf9e0ae138b906cc9\",\n \"snippets/ui-toggle-group.liquid\": \"2d61e0b9bc746e05834157f5a75d23123f02bc2df737412e4f6099844afc4f49\",\n \"snippets/ui-dialog.liquid\": \"779bbcd609a69ed29a984df1f26f9ed80fd24d71c4ee3ead5f4daa055b28e9fb\",\n \"snippets/ui-drawer.liquid\": \"16120fdd70fac393470c3936223e78ad5f81164e2b7616fd075e8e82098a17d5\",\n \"snippets/ui-badge.liquid\": \"13d8b46cd5432783a9b986de76365c9fcc0e7fb30ad4f956ec386c1ae549ce99\",\n \"snippets/ui-alert.liquid\": \"7530d324ac1fb4645bba0a23780fbeacc67544f8bad3ed9e367debd88c0365b5\",\n \"snippets/ui-table.liquid\": \"6599d8b3bd193761571c2b71e499f78546ba5c2768d4e0a0a60cd0e348632e5b\",\n \"snippets/ui-breadcrumb.liquid\": \"dafdeaf03286b386c90f8e72777de1946fba4718ee568bd85ca7bc768483a3ca\",\n \"snippets/ui-aspect-ratio.liquid\": \"c8aae05fcd76af7ae768aedafd7e758533e2b52135b00ff72940896ad793f177\",\n \"snippets/ui-price.liquid\": \"96156754aee3a04aa3ae7084a14470000213e8be14468c4937e9f3a87e348234\",\n \"assets/ui-price.css\": \"95c947e219621102b704164e48a83e381e6e2b27b99311a294e076745b39a36f\",\n \"snippets/ui-radio-group.liquid\": \"af9978e18716cc86d03ed21dece8304992e74a4fab88e12069b0648cfd944492\",\n \"snippets/ui-checkbox.liquid\": \"0e4dd1738b355c5a93533e0b813e2e6c72f7491c289d65aa7b6435be1c599006\",\n \"snippets/ui-switch.liquid\": \"84ed60c777e77daec8b5fe6f2ee434877d51224235dc1bc2e17927cd182fe646\",\n \"snippets/ui-skeleton.liquid\": \"1e30245f6577f73d28f4badbc293934656882a29d5dec1d2f74ef95f3bcede2c\",\n \"snippets/ui-separator.liquid\": \"90b1d2795666b6c51f35645ca8d07659f2c4868fe9553e233cddb4e962095f6e\",\n \"snippets/ui-card.liquid\": \"581a46843f1280ebace0423ee84289b40fbd46142e6a9ab3940f63eca0e4c5f2\",\n \"assets/ui-card.css\": \"ee7e4192ec814066d0c9838f810ae4ca267b9f071b7fa945c43226d46b0f8dc9\",\n \"snippets/ui-pagination.liquid\": \"30943c00686e1a23d92865465b38c964e059b14ae3a20fb7fe151824a73773eb\",\n \"snippets/ui-popup.liquid\": \"840f938aa2a331e4e7d55c659f25e841dcfb1bf5c5cadace6263243352ff8359\",\n \"snippets/ui-sheet.liquid\": \"0de705bed685bf78fa3af4517c947b38d62a1aa56bf29356c0a201b64db17614\",\n \"snippets/ui-carousel.liquid\": \"59dc0c06b2a9f2f779ca22b3ff4b005071d4909aba052d6f8f0a1aa19e84c2ce\",\n \"assets/ui-carousel.css\": \"5317c251b3d3599ba330947fc9709ea50acfb858a5d7e08573b5d34ada2f3bf4\",\n \"snippets/ui-attachment.liquid\": \"6881df7cdb1b27462d8cce7c17c0b4ed4dd860d62535e1daa81378a57bb69ddf\",\n \"snippets/ui-marquee.liquid\": \"47d5df8f1ff46a2cd48a132986c2ba199c9e19b485e62fc3e0d19091d4baaab1\",\n \"assets/ui-marquee.css\": \"ef13b83f3dbcd6685fcee3dba14238386d0f34bc2266a5326c0b6f2d40a23fce\",\n \"snippets/ui-invalid-controls.liquid\": \"a169e8244cc86f5431f902f7a73b37188051ba8f2ef3e323830462dae5aed066\",\n \"snippets/ui-section.liquid\": \"da358cd87447457f3675b035b48556103d86cd11e0b2240ae2800e4c9ea54bdc\",\n \"assets/ui-section.css\": \"f4936a02aaf1927ccced898886857855e4b5e95fafe5c2a52748ecbbe87b6621\",\n \"snippets/ui-button-group.liquid\": \"a2790f8da0f8b37acc1ed558ba1548ea250a5b93e1b2dfbd11eb150f3f02783a\",\n \"snippets/ui-collapsible.liquid\": \"e6107a03d00b8cc75bb03206e64b9aa7f2989d982c0408b398b62067c6df450a\",\n \"snippets/ui-dropdown-menu.liquid\": \"25b31e09e3e7bfee9a397ca4de6e310754844c55e71639eadba917b0f4e6d2e5\",\n \"snippets/ui-popover.liquid\": \"44ff93d72eaad409f56f8a84ce25715e2c3633f2ac49ba7a414767c6dbe2ee47\",\n \"snippets/ui-hover-card.liquid\": \"ba4133d352fa14296743138c99f3694015bafe953751b8e42155f047c4beb9f0\",\n \"snippets/ui.liquid\": \"30679056b2eafefbaeef5ecd271604951676893a65a8eccb0f970c787a67b900\"\n }\n}\n", "type": "registry:file", "target": "~/assets/ui-manifest.json" } diff --git a/public/r/liquid/empty.json b/public/r/liquid/empty.json index fd7189601..59290172c 100644 --- a/public/r/liquid/empty.json +++ b/public/r/liquid/empty.json @@ -9,7 +9,7 @@ "files": [ { "path": "components/empty.liquid", - "content": "{% doc %}\n Fulldev UI empty styles. Use native markup with its data-slot parts.\n{% enddoc %}\n\n{% stylesheet %}\n [data-slot='empty'] {\n display: flex;\n flex: 1;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: calc(var(--spacing) * 4);\n width: 100%;\n min-width: 0;\n padding: calc(var(--spacing) * 12);\n border-radius: var(--radius-lg);\n text-align: center;\n text-wrap: balance;\n }\n [data-slot='empty'][data-variant='outline'] {\n border: 1px dashed var(--border);\n }\n [data-slot='empty-header'] {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: calc(var(--spacing) * 2);\n max-width: 24rem;\n }\n [data-slot='empty-icon'] {\n display: flex;\n flex-shrink: 0;\n align-items: center;\n justify-content: center;\n margin-bottom: calc(var(--spacing) * 2);\n }\n [data-slot='empty-icon'][data-variant='icon'] {\n width: calc(var(--spacing) * 10);\n height: calc(var(--spacing) * 10);\n border-radius: var(--radius-lg);\n background: var(--muted);\n color: var(--foreground);\n }\n [data-slot='empty-icon'][data-variant='icon'] > .icon {\n width: calc(var(--spacing) * 6);\n height: calc(var(--spacing) * 6);\n }\n [data-slot='empty-title'] {\n font-size: 1.125rem;\n font-weight: 500;\n line-height: 1.75rem;\n letter-spacing: var(--tracking-tight);\n }\n [data-slot='empty-description'] {\n font-size: 0.875rem;\n line-height: 1.625;\n color: var(--muted-foreground);\n }\n [data-slot='empty-description'] a {\n text-decoration: underline;\n text-underline-offset: 4px;\n }\n [data-slot='empty-content'] {\n display: flex;\n flex-wrap: wrap;\n justify-content: center;\n gap: calc(var(--spacing) * 3);\n width: 100%;\n max-width: 24rem;\n font-size: 0.875rem;\n }\n{% endstylesheet %}\n", + "content": "{% doc %}\n Fulldev UI empty styles. Use native markup with its data-slot parts.\n{% enddoc %}\n\n{% stylesheet %}\n [data-slot='empty'] {\n display: flex;\n flex: 1;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: calc(var(--spacing) * 4);\n width: 100%;\n min-width: 0;\n padding: calc(var(--spacing) * 12);\n border-radius: var(--radius-lg);\n text-align: center;\n text-wrap: balance;\n }\n [data-slot='empty'][data-variant='outline'] {\n border: 1px dashed var(--border);\n }\n [data-slot='empty-header'] {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: calc(var(--spacing) * 2);\n max-width: 24rem;\n }\n [data-slot='empty-icon'] {\n display: flex;\n flex-shrink: 0;\n align-items: center;\n justify-content: center;\n margin-bottom: calc(var(--spacing) * 2);\n }\n [data-slot='empty-icon'][data-variant='icon'] {\n width: calc(var(--spacing) * 10);\n height: calc(var(--spacing) * 10);\n border-radius: var(--radius-lg);\n background: var(--muted);\n color: var(--foreground);\n }\n [data-slot='empty-icon'][data-variant='icon'] > .icon {\n width: calc(var(--spacing) * 6);\n height: calc(var(--spacing) * 6);\n }\n [data-slot='empty-title'] {\n font-size: 1.125rem;\n font-weight: 500;\n line-height: 1.75rem;\n letter-spacing: var(--tracking-tight);\n }\n [data-slot='empty-description'] {\n font-size: 0.875rem;\n line-height: 1.625;\n color: var(--muted-foreground);\n }\n [data-slot='empty-description'] a {\n text-decoration: underline;\n text-underline-offset: 4px;\n }\n [data-slot='empty-content'] {\n display: flex;\n flex-wrap: wrap;\n justify-content: center;\n gap: calc(var(--spacing) * 3);\n width: 100%;\n max-width: 24rem;\n font-size: 0.875rem;\n }\n @media (max-width: 639px) {\n [data-slot='empty'] {\n padding: 32px 16px;\n }\n }\n{% endstylesheet %}\n", "type": "registry:file", "target": "~/snippets/ui-empty.liquid" } diff --git a/public/r/liquid/field.json b/public/r/liquid/field.json index 04dd114c8..f6f12363c 100644 --- a/public/r/liquid/field.json +++ b/public/r/liquid/field.json @@ -16,7 +16,7 @@ }, { "path": "styles/field.css", - "content": "[data-slot='label'],\n[data-slot='field-label'] {\n display: flex;\n align-items: center;\n gap: calc(var(--spacing) * 2);\n font-size: 0.875rem;\n font-weight: 500;\n line-height: 1;\n}\n/* Fulldev UI's field set: a fieldset of fields or options under a legend. A legend sits outside its\n fieldset's flex layout, so it keeps the field gap as its margin. */\n[data-slot='field-set'] {\n display: flex;\n flex-direction: column;\n gap: calc(var(--spacing) * 6);\n min-width: 0;\n margin: 0;\n padding: 0;\n border: 0;\n}\n[data-slot='field-legend'] {\n display: flex;\n align-items: center;\n gap: calc(var(--spacing) * 2);\n margin-bottom: calc(var(--spacing) * 3);\n padding: 0;\n font-size: 1rem;\n font-weight: 500;\n line-height: 1;\n}\n[data-slot='field-legend'][data-variant='label'] {\n font-size: 0.875rem;\n}\n[data-slot='field'] {\n display: flex;\n flex-direction: column;\n gap: calc(var(--spacing) * 3);\n width: 100%;\n}\n/* An input and its button side by side, as in newsletter and search forms. */\n[data-slot='field'][data-orientation='horizontal'] {\n flex-direction: row;\n align-items: center;\n}\n[data-slot='field'][data-orientation='horizontal'] > :is(input, select, textarea) {\n flex: 1;\n}\n[data-slot='field-group'] {\n display: flex;\n flex-direction: column;\n gap: calc(var(--spacing) * 7);\n}\n/* Two fields side by side from 640px. */\n.field-row {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: calc(var(--spacing) * 4);\n}\n[data-slot='field-description'] {\n font-size: 0.875rem;\n color: var(--muted-foreground);\n}\n[data-slot='field-error'] {\n font-size: 0.875rem;\n color: var(--destructive);\n}\n", + "content": "[data-slot='label'],\n[data-slot='field-label'] {\n display: flex;\n align-items: center;\n gap: calc(var(--spacing) * 2);\n font-size: 0.875rem;\n font-weight: 500;\n line-height: 1;\n}\n/* Fulldev UI's field set: a fieldset of fields or options under a legend. A legend sits outside its\n fieldset's flex layout, so it keeps the field gap as its margin. */\n[data-slot='field-set'] {\n display: flex;\n flex-direction: column;\n gap: calc(var(--spacing) * 6);\n min-width: 0;\n margin: 0;\n padding: 0;\n border: 0;\n}\n[data-slot='field-legend'] {\n display: flex;\n align-items: center;\n gap: calc(var(--spacing) * 2);\n margin-bottom: calc(var(--spacing) * 3);\n padding: 0;\n font-size: 1rem;\n font-weight: 500;\n line-height: 1;\n}\n[data-slot='field-legend'][data-variant='label'] {\n font-size: 0.875rem;\n}\n[data-slot='field'] {\n display: flex;\n flex-direction: column;\n gap: calc(var(--spacing) * 3);\n width: 100%;\n}\n/* An input and its button side by side, as in newsletter and search forms. */\n[data-slot='field'][data-orientation='horizontal'] {\n flex-direction: row;\n align-items: center;\n}\n[data-slot='field'][data-orientation='horizontal'] > :is(input, select, textarea) {\n flex: 1;\n}\n[data-slot='field-group'] {\n display: flex;\n flex-direction: column;\n gap: calc(var(--spacing) * 7);\n}\n/* Two fields side by side from 640px. */\n.field-row {\n display: grid;\n gap: calc(var(--spacing) * 4);\n}\n@media (min-width: 640px) {\n .field-row {\n grid-template-columns: 1fr 1fr;\n }\n}\n[data-slot='field-description'] {\n font-size: 0.875rem;\n color: var(--muted-foreground);\n}\n[data-slot='field-error'] {\n font-size: 0.875rem;\n color: var(--destructive);\n}\n", "type": "registry:file", "target": "~/assets/ui-field.css" } diff --git a/public/r/liquid/init.json b/public/r/liquid/init.json index 6a613ef72..cd3ea61bd 100644 --- a/public/r/liquid/init.json +++ b/public/r/liquid/init.json @@ -13,7 +13,7 @@ }, { "path": "components/base.liquid", - "content": "{% doc %}\n Fulldev UI base styles. Use native markup with its data-slot parts.\n{% enddoc %}\n\n{% stylesheet %}\n *,\n *::before,\n *::after {\n box-sizing: border-box;\n border-color: var(--border);\n }\n body {\n margin: 0;\n min-height: 100svh;\n display: flex;\n flex-direction: column;\n background: var(--background);\n color: var(--foreground);\n font: 16px/1.5 var(--font-sans);\n -webkit-font-smoothing: antialiased;\n text-rendering: optimizelegibility;\n }\n main {\n flex: 1;\n }\n img,\n video,\n svg,\n iframe {\n max-width: 100%;\n }\n img,\n video,\n svg {\n display: block;\n height: auto;\n }\n a {\n color: inherit;\n text-decoration: none;\n text-underline-offset: 4px;\n }\n button,\n input,\n select,\n textarea {\n font: inherit;\n color: inherit;\n }\n button,\n summary {\n cursor: pointer;\n }\n button {\n border: 0;\n background: none;\n padding: 0;\n border-radius: 0;\n }\n :focus-visible {\n outline: 2px solid color-mix(in oklch, var(--ring) 50%, transparent);\n outline-offset: 2px;\n }\n [hidden] {\n display: none !important;\n }\n h1,\n h2,\n h3,\n h4,\n p,\n figure,\n blockquote,\n fieldset,\n dl,\n dd {\n margin: 0;\n }\n fieldset {\n border: 0;\n padding: 0;\n min-width: 0;\n }\n /* Lists that a part or layout styles with a class; merchant rich text keeps its markers. */\n :where(ul, ol)[class] {\n margin: 0;\n padding: 0;\n list-style: none;\n }\n legend {\n padding: 0;\n }\n p,\n h1,\n h2,\n h3,\n h4,\n a,\n li {\n overflow-wrap: anywhere;\n }\n{% endstylesheet %}\n", + "content": "{% doc %}\n Fulldev UI base styles. Use native markup with its data-slot parts.\n{% enddoc %}\n\n{% stylesheet %}\n *,\n *::before,\n *::after {\n box-sizing: border-box;\n border-color: var(--border);\n }\n body {\n margin: 0;\n min-height: 100svh;\n display: flex;\n flex-direction: column;\n background: var(--background);\n color: var(--foreground);\n font: 16px/1.5 var(--font-sans);\n -webkit-font-smoothing: antialiased;\n text-rendering: optimizelegibility;\n }\n main {\n flex: 1;\n }\n img,\n video,\n svg,\n iframe {\n max-width: 100%;\n }\n img,\n video,\n svg {\n display: block;\n height: auto;\n }\n a {\n color: inherit;\n text-decoration: none;\n text-underline-offset: 4px;\n }\n button,\n input,\n select,\n textarea {\n font: inherit;\n color: inherit;\n }\n button,\n summary {\n cursor: pointer;\n }\n button {\n border: 0;\n background: none;\n padding: 0;\n border-radius: 0;\n }\n :focus-visible {\n outline: 2px solid color-mix(in oklch, var(--ring) 50%, transparent);\n outline-offset: 2px;\n }\n [hidden] {\n display: none !important;\n }\n h1,\n h2,\n h3,\n h4,\n p,\n figure,\n blockquote,\n fieldset,\n dl,\n dd {\n margin: 0;\n }\n fieldset {\n border: 0;\n padding: 0;\n min-width: 0;\n }\n /* Lists that a part or layout styles with a class; merchant rich text keeps its markers. */\n :where(ul, ol)[class] {\n margin: 0;\n padding: 0;\n list-style: none;\n }\n legend {\n padding: 0;\n }\n p,\n h1,\n h2,\n h3,\n h4,\n a,\n li {\n overflow-wrap: anywhere;\n }\n @media (prefers-reduced-motion: reduce) {\n *,\n *::before,\n *::after {\n scroll-behavior: auto !important;\n animation-duration: 0.01ms !important;\n animation-iteration-count: 1 !important;\n transition-duration: 0.01ms !important;\n }\n }\n{% endstylesheet %}\n", "type": "registry:file", "target": "~/snippets/ui-base.liquid" }, diff --git a/public/r/liquid/input.json b/public/r/liquid/input.json index 957ef12d0..309ec091b 100644 --- a/public/r/liquid/input.json +++ b/public/r/liquid/input.json @@ -9,7 +9,7 @@ "files": [ { "path": "components/input.liquid", - "content": "{% doc %}\n Fulldev UI input styles. Use native markup with its data-slot parts.\n{% enddoc %}\n\n{% stylesheet %}\n input:where(:not([type='hidden'], [type='checkbox'], [type='radio'], [type='range'], [type='submit'])),\n [data-slot='input'],\n [data-slot='native-select'],\n select,\n [data-slot='textarea'],\n textarea {\n width: 100%;\n min-width: 0;\n height: calc(var(--spacing) * 9);\n padding: var(--spacing) calc(var(--spacing) * 2.5);\n border: 1px solid var(--input);\n border-radius: var(--radius-md);\n background-color: light-dark(transparent, color-mix(in oklch, var(--input) 30%, transparent));\n font-size: 1rem;\n line-height: 1.5;\n box-shadow: var(--shadow-xs);\n outline: none;\n transition:\n color 150ms,\n border-color 150ms,\n box-shadow 150ms;\n }\n input::placeholder,\n textarea::placeholder {\n color: var(--muted-foreground);\n }\n input:where(:not([type='hidden'], [type='checkbox'], [type='radio'])):focus-visible,\n select:focus-visible,\n textarea:focus-visible {\n border-color: var(--ring);\n box-shadow: var(--focus-ring);\n }\n :is(input, select, textarea)[aria-invalid='true'] {\n border-color: light-dark(var(--destructive), color-mix(in oklch, var(--destructive) 50%, transparent));\n box-shadow: 0 0 0 3px\n light-dark(\n color-mix(in oklch, var(--destructive) 20%, transparent),\n color-mix(in oklch, var(--destructive) 40%, transparent)\n );\n }\n :is(input, select, textarea):disabled {\n cursor: not-allowed;\n opacity: 0.5;\n }\n textarea {\n height: auto;\n min-height: calc(var(--spacing) * 16);\n padding-block: calc(var(--spacing) * 2);\n resize: vertical;\n field-sizing: content;\n }\n select {\n appearance: none;\n padding-right: calc(var(--spacing) * 8);\n background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23737373' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E\");\n background-repeat: no-repeat;\n background-position: right 10px center;\n }\n :is(input, select)[data-size='sm'] {\n height: calc(var(--spacing) * 8);\n }\n input[type='checkbox'],\n input[type='radio'] {\n width: calc(var(--spacing) * 4);\n height: calc(var(--spacing) * 4);\n margin: 0;\n accent-color: var(--primary);\n }\n{% endstylesheet %}\n", + "content": "{% doc %}\n Fulldev UI input styles. Use native markup with its data-slot parts.\n{% enddoc %}\n\n{% stylesheet %}\n input:where(:not([type='hidden'], [type='checkbox'], [type='radio'], [type='range'], [type='submit'])),\n [data-slot='input'],\n [data-slot='native-select'],\n select,\n [data-slot='textarea'],\n textarea {\n width: 100%;\n min-width: 0;\n height: calc(var(--spacing) * 9);\n padding: var(--spacing) calc(var(--spacing) * 2.5);\n border: 1px solid var(--input);\n border-radius: var(--radius-md);\n background-color: light-dark(transparent, color-mix(in oklch, var(--input) 30%, transparent));\n font-size: 1rem;\n line-height: 1.5;\n box-shadow: var(--shadow-xs);\n outline: none;\n transition:\n color 150ms,\n border-color 150ms,\n box-shadow 150ms;\n }\n input::placeholder,\n textarea::placeholder {\n color: var(--muted-foreground);\n }\n input:where(:not([type='hidden'], [type='checkbox'], [type='radio'])):focus-visible,\n select:focus-visible,\n textarea:focus-visible {\n border-color: var(--ring);\n box-shadow: var(--focus-ring);\n }\n :is(input, select, textarea)[aria-invalid='true'] {\n border-color: light-dark(var(--destructive), color-mix(in oklch, var(--destructive) 50%, transparent));\n box-shadow: 0 0 0 3px\n light-dark(\n color-mix(in oklch, var(--destructive) 20%, transparent),\n color-mix(in oklch, var(--destructive) 40%, transparent)\n );\n }\n :is(input, select, textarea):disabled {\n cursor: not-allowed;\n opacity: 0.5;\n }\n textarea {\n height: auto;\n min-height: calc(var(--spacing) * 16);\n padding-block: calc(var(--spacing) * 2);\n resize: vertical;\n field-sizing: content;\n }\n select {\n appearance: none;\n padding-right: calc(var(--spacing) * 8);\n background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23737373' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E\");\n background-repeat: no-repeat;\n background-position: right 10px center;\n }\n :is(input, select)[data-size='sm'] {\n height: calc(var(--spacing) * 8);\n }\n input[type='checkbox'],\n input[type='radio'] {\n width: calc(var(--spacing) * 4);\n height: calc(var(--spacing) * 4);\n margin: 0;\n accent-color: var(--primary);\n }\n @media (min-width: 768px) {\n input:where(:not([type='hidden'], [type='checkbox'], [type='radio'], [type='range'], [type='submit'])),\n [data-slot='input'],\n [data-slot='native-select'],\n select,\n [data-slot='textarea'],\n textarea {\n font-size: 0.875rem;\n }\n }\n{% endstylesheet %}\n", "type": "registry:file", "target": "~/snippets/ui-input.liquid" } diff --git a/public/r/liquid/runtime.json b/public/r/liquid/runtime.json index 26cb480c3..ceacdc80d 100644 --- a/public/r/liquid/runtime.json +++ b/public/r/liquid/runtime.json @@ -9,7 +9,7 @@ "files": [ { "path": "runtime/ui-carousel.js", - "content": "import { createCarousel } from '@data-slot/carousel';\n\n// @data-slot/carousel owns scrolling, snapping, dragging, the arrow keys and the previous and next\n// buttons, and keeps every visible card interactive. It writes English ARIA descriptions and slide\n// labels, so the translated ones from Liquid are put back after it binds.\nexport function initializeCarousel(root) {\n const content = root.querySelector('[data-slot=\"carousel-content\"]');\n const items = [...content.children].filter((item) => item.matches('[data-slot=\"carousel-item\"]'));\n const descriptions = [root, ...items].map((element) => [element, element.getAttribute('aria-roledescription')]);\n const carousel = createCarousel(root, { slides: 'multiple', drag: true, loop: false });\n for (const [element, description] of descriptions)\n if (description) element.setAttribute('aria-roledescription', description);\n // Markup without a translated label template keeps the controller's labels.\n const template = root.dataset.slideLabel;\n if (template)\n items.forEach((item, index) => {\n item.setAttribute('aria-label', template.replace('[index]', index + 1).replace('[total]', items.length));\n });\n for (const button of root.querySelectorAll('[data-slot=\"carousel-previous\"], [data-slot=\"carousel-next\"]'))\n button.hidden = false;\n return carousel;\n}\n", + "content": "import { createCarousel } from '@data-slot/carousel';\n\n// @data-slot/carousel owns scrolling, snapping, dragging, the arrow keys and the previous and next\n// buttons, and keeps every visible card interactive. It writes English ARIA descriptions and slide\n// labels, so the translated ones from Liquid are put back after binding and slide updates.\nexport function initializeCarousel(root) {\n const content = root.querySelector('[data-slot=\"carousel-content\"]');\n const items = () => [...content.children].filter((item) => item.matches('[data-slot=\"carousel-item\"]'));\n const rootDescription = root.getAttribute('aria-roledescription');\n const descriptions = new WeakMap(items().map((item) => [item, item.getAttribute('aria-roledescription')]));\n let destroyed = false;\n const translate = () => {\n if (destroyed) return;\n if (rootDescription) root.setAttribute('aria-roledescription', rootDescription);\n const slides = items();\n const template = root.dataset.slideLabel;\n slides.forEach((item, index) => {\n const description = descriptions.get(item);\n if (description) item.setAttribute('aria-roledescription', description);\n if (template)\n item.setAttribute('aria-label', template.replace('[index]', index + 1).replace('[total]', slides.length));\n });\n };\n // Observe first to capture authored descriptions before data-slot rewrites them.\n const observer = new MutationObserver((mutations) => {\n for (const mutation of mutations)\n for (const node of mutation.addedNodes)\n if (node instanceof Element && node.matches('[data-slot=\"carousel-item\"]'))\n descriptions.set(node, node.getAttribute('aria-roledescription'));\n queueMicrotask(translate);\n });\n observer.observe(content, { childList: true });\n const carousel = createCarousel(root, { slides: 'multiple', drag: true, loop: false });\n translate();\n for (const button of root.querySelectorAll('[data-slot=\"carousel-previous\"], [data-slot=\"carousel-next\"]'))\n button.hidden = false;\n const destroy = carousel.destroy.bind(carousel);\n carousel.destroy = () => {\n destroyed = true;\n observer.disconnect();\n destroy();\n };\n return carousel;\n}\n", "type": "registry:file", "target": "~/assets/ui-carousel.js" }, diff --git a/public/r/liquid/section.json b/public/r/liquid/section.json index a49b0cb0d..4aa5b3979 100644 --- a/public/r/liquid/section.json +++ b/public/r/liquid/section.json @@ -15,7 +15,7 @@ }, { "path": "styles/section.css", - "content": "[data-slot='section'] {\n position: relative;\n display: flex;\n flex-direction: column;\n gap: var(--section-gap);\n padding-block: var(--section-padding);\n background: var(--background);\n color: var(--foreground);\n scroll-margin-top: 48px;\n}\n[data-slot='section'][data-variant='bordered'] {\n padding-block: 32px;\n border-block: 1px solid var(--border);\n}\n/* The first section of a template page, below the header. */\n[data-slot='section'][data-variant='page'] {\n padding-top: 32px;\n}\n/* A framed panel inset by the gutter, as Fulldev UI's floating section. */\n[data-slot='section'][data-variant='floating'] {\n width: calc(100% - 2 * var(--gutter));\n max-width: calc(var(--container) - 2 * var(--gutter));\n margin: 64px auto;\n overflow: hidden;\n border: 1px solid var(--border);\n border-radius: var(--radius-xl);\n box-shadow: var(--shadow-md);\n}\n/* The container only sets the width and gutters; each section lays out its own children. */\n[data-slot='section-container'],\n[data-slot='header-container'] {\n position: relative;\n width: 100%;\n min-width: 0;\n max-width: var(--container);\n margin-inline: auto;\n padding-inline: var(--gutter);\n}\n[data-slot='header-container'] {\n display: flex;\n gap: 16px;\n}\n[data-slot='section-title'] {\n font-size: 1.875rem;\n line-height: 2.25rem;\n text-wrap: balance;\n}\n[data-slot='section-description'] {\n font-size: 1.125rem;\n line-height: 1.625;\n color: var(--muted-foreground);\n text-wrap: balance;\n}\n/* Display headline of centered heroes and banners. */\n.display-title {\n max-width: 56rem;\n font-size: 2.25rem;\n line-height: 1.1;\n text-wrap: balance;\n}\n\n/* The actions of a section intro, wrapping when they do not fit beside each other. */\n.section-actions {\n display: flex;\n flex-wrap: wrap;\n gap: 12px;\n}\n/* A section title set tight, as in Fulldev UI's call to action and content blocks. */\n[data-slot='section-title'].tight-title {\n line-height: 1.1;\n}\n/* The display headline of split heroes: 36px, 48px from 640px. */\n.hero-title {\n font-size: 2.25rem;\n line-height: 1.1;\n text-wrap: balance;\n}\n", + "content": "[data-slot='section'] {\n position: relative;\n display: flex;\n flex-direction: column;\n gap: var(--section-gap);\n padding-block: var(--section-padding);\n background: var(--background);\n color: var(--foreground);\n scroll-margin-top: 48px;\n}\n[data-slot='section'][data-variant='bordered'] {\n padding-block: 32px;\n border-block: 1px solid var(--border);\n}\n/* The first section of a template page, below the header. */\n[data-slot='section'][data-variant='page'] {\n padding-top: 32px;\n}\n/* A framed panel inset by the gutter, as Fulldev UI's floating section. */\n[data-slot='section'][data-variant='floating'] {\n width: calc(100% - 2 * var(--gutter));\n max-width: calc(var(--container) - 2 * var(--gutter));\n margin: 64px auto;\n overflow: hidden;\n border: 1px solid var(--border);\n border-radius: var(--radius-xl);\n box-shadow: var(--shadow-md);\n}\n/* The container only sets the width and gutters; each section lays out its own children. */\n[data-slot='section-container'],\n[data-slot='header-container'] {\n position: relative;\n width: 100%;\n min-width: 0;\n max-width: var(--container);\n margin-inline: auto;\n padding-inline: var(--gutter);\n}\n[data-slot='header-container'] {\n display: flex;\n gap: 16px;\n}\n[data-slot='section-title'] {\n font-size: 1.875rem;\n line-height: 2.25rem;\n text-wrap: balance;\n}\n[data-slot='section-description'] {\n font-size: 1.125rem;\n line-height: 1.625;\n color: var(--muted-foreground);\n text-wrap: balance;\n}\n/* Display headline of centered heroes and banners. */\n.display-title {\n max-width: 56rem;\n font-size: 2.25rem;\n line-height: 1.1;\n text-wrap: balance;\n}\n\n/* The actions of a section intro, wrapping when they do not fit beside each other. */\n.section-actions {\n display: flex;\n flex-wrap: wrap;\n gap: 12px;\n}\n/* A section title set tight, as in Fulldev UI's call to action and content blocks. */\n[data-slot='section-title'].tight-title {\n line-height: 1.1;\n}\n/* The display headline of split heroes: 36px, 48px from 640px. */\n.hero-title {\n font-size: 2.25rem;\n line-height: 1.1;\n text-wrap: balance;\n}\n@media (min-width: 640px) {\n [data-slot='section-title'] {\n font-size: 2.25rem;\n line-height: 2.5rem;\n }\n .display-title,\n .hero-title {\n font-size: 3rem;\n }\n}\n@media (min-width: 1024px) {\n .display-title {\n font-size: 3.75rem;\n }\n}\n", "type": "registry:file", "target": "~/assets/ui-section.css" } diff --git a/tests/browser/liquid-regressions.test.mjs b/tests/browser/liquid-regressions.test.mjs new file mode 100644 index 000000000..527685761 --- /dev/null +++ b/tests/browser/liquid-regressions.test.mjs @@ -0,0 +1,159 @@ +import assert from "node:assert/strict" +import { mkdtemp, rm } from "node:fs/promises" +import { tmpdir } from "node:os" +import { resolve } from "node:path" +import { after, before, test } from "node:test" +import { fixtureServer, install, registryServer } from "#liquid-fixture" +import { chromium, firefox, webkit } from "playwright" + +let theme, server +before(async () => { + theme = await mkdtemp(resolve(tmpdir(), "fulldev-liquid-regressions-")) + const registry = await registryServer() + try { + await install(registry.origin, theme) + } finally { + await registry.close() + } + server = await fixtureServer(theme) +}) +after(async () => { + await server?.close() + if (theme) await rm(theme, { recursive: true, force: true }) +}) + +for (const [name, type] of Object.entries({ chromium, firefox, webkit })) { + test(`Liquid ${name}: portable breakpoints and translated dynamic slides`, async () => { + const browser = await type.launch() + try { + const page = await browser.newPage() + await page.goto(server.origin) + await page.locator('[data-slot="carousel"][data-enhanced]').waitFor() + await page.evaluate(() => { + const container = document.createElement("div") + container.id = "responsive-regression" + container.innerHTML = ` +

    Split headline

    +

    Centered headline

    +

    Section headline

    +
    Name
    Email
    + +
    Empty state
    ` + document.body.append(container) + }) + for (const width of [390, 639, 640, 768, 1024, 1440]) { + await page.setViewportSize({ width, height: 900 }) + const styles = await page + .locator("#responsive-regression") + .evaluate((root) => { + const style = (selector) => + getComputedStyle(root.querySelector(selector)) + return { + hero: style(".hero-title").fontSize, + display: style(".display-title").fontSize, + title: style('[data-slot="section-title"]').fontSize, + columns: + style(".field-row").gridTemplateColumns.split(" ").length, + input: style("input").fontSize, + empty: style('[data-slot="empty"]').padding, + } + }) + assert.equal(styles.hero, width < 640 ? "36px" : "48px") + assert.equal( + styles.display, + width < 640 ? "36px" : width < 1024 ? "48px" : "60px" + ) + assert.equal(styles.title, width < 640 ? "30px" : "36px") + assert.equal(styles.columns, width < 640 ? 1 : 2) + assert.equal(styles.input, width < 768 ? "16px" : "14px") + assert.equal(styles.empty, width < 640 ? "32px 16px" : "48px") + } + const carousel = page.locator('[data-slot="carousel"]') + // Exercise the adapter directly on an isolated root; the lifecycle does not observe shadow trees. + await carousel.evaluate(async (source, moduleURL) => { + const host = document.createElement("div") + host.id = "carousel-regression" + document.body.append(host) + const shadow = host.attachShadow({ mode: "open" }) + const root = source.cloneNode(true) + root.removeAttribute("data-enhanced") + root.setAttribute("aria-roledescription", "Carrousel") + root.dataset.slideLabel = "Dia [index] van [total]" + const content = root.querySelector('[data-slot="carousel-content"]') + content.style.cssText = "display:flex;width:300px;overflow:auto" + for (const item of content.children) { + item.style.cssText = "flex:0 0 300px" + item.setAttribute("aria-roledescription", "Dia") + } + shadow.append(root) + const { initializeCarousel } = await import(moduleURL) + window.carouselRegression = { + root, + controller: initializeCarousel(root), + } + }, "/assets/ui-carousel.js") + const readSlides = () => + page.evaluate(() => { + const { root, controller } = window.carouselRegression + return { + description: root.getAttribute("aria-roledescription"), + count: controller.count, + slides: [ + ...root.querySelector('[data-slot="carousel-content"]').children, + ].map((item) => ({ + description: item.getAttribute("aria-roledescription"), + label: item.getAttribute("aria-label"), + })), + } + }) + assert.equal((await readSlides()).count, 3) + await page.evaluate(() => { + const { root } = window.carouselRegression + const item = document.createElement("div") + item.dataset.slot = "carousel-item" + item.style.cssText = "flex:0 0 300px" + item.setAttribute("aria-roledescription", "Paneel") + root.querySelector('[data-slot="carousel-content"]').append(item) + }) + assert.deepEqual(await readSlides(), { + description: "Carrousel", + count: 4, + slides: ["Dia", "Dia", "Dia", "Paneel"].map((description, index) => ({ + description, + label: `Dia ${index + 1} van 4`, + })), + }) + await page.evaluate(() => + window.carouselRegression.root + .querySelector('[data-slot="carousel-content"]') + .firstElementChild.remove() + ) + assert.deepEqual( + (await readSlides()).slides.map((slide) => slide.label), + ["Dia 1 van 3", "Dia 2 van 3", "Dia 3 van 3"] + ) + await page.evaluate(() => { + const { root, controller } = window.carouselRegression + const content = root.querySelector('[data-slot="carousel-content"]') + const item = content.firstElementChild.cloneNode(true) + content.append(item) + queueMicrotask(() => { + controller.destroy() + item.setAttribute("aria-label", "After destroy") + }) + }) + assert.equal((await readSlides()).slides.at(-1).label, "After destroy") + await page.evaluate(() => { + const content = window.carouselRegression.root.querySelector( + '[data-slot="carousel-content"]' + ) + const item = document.createElement("div") + item.dataset.slot = "carousel-item" + content.append(item) + }) + assert.equal((await readSlides()).slides.at(-1).label, null) + } finally { + await browser.close() + } + }) +} From e05bd45dae32ec97b15ff3e17fbee50e4f7fa035 Mon Sep 17 00:00:00 2001 From: Sil Date: Sat, 17 Oct 2026 12:44:07 +0200 Subject: [PATCH 3/3] Fix Liquid examples and complete browser fixture context --- liquid/examples/textarea.liquid | 3 ++- liquid/examples/video.liquid | 4 ++-- tests/browser/liquid.test.mjs | 9 +++++++++ tests/fixtures/liquid/en.json | 5 ++++- tests/helpers/liquid.mjs | 11 ++++++++--- 5 files changed, 25 insertions(+), 7 deletions(-) diff --git a/liquid/examples/textarea.liquid b/liquid/examples/textarea.liquid index c3123c5ab..de19f36f5 100644 --- a/liquid/examples/textarea.liquid +++ b/liquid/examples/textarea.liquid @@ -1,5 +1,6 @@
    - >