diff --git a/app/assets/css/tailwind.css b/app/assets/css/tailwind.css index 23b7f0c..80b48d4 100644 --- a/app/assets/css/tailwind.css +++ b/app/assets/css/tailwind.css @@ -89,7 +89,7 @@ --secondary-foreground: oklch(0.864 0.033 305.939); --accent: oklch(0.372 0.055 283.423); --accent-foreground: oklch(0.91 0.014 286.109); - --destructive: oklch(0.649 0.226 31.646); + --destructive: oklch(75.56% 0.13 2.76); --destructive-foreground: oklch(1 0 180); --ring: oklch(0.787 0.119 304.446); --chart-1: oklch(0.787 0.119 304.446); diff --git a/app/components/ui/field/Field.vue b/app/components/ui/field/Field.vue new file mode 100644 index 0000000..5519d37 --- /dev/null +++ b/app/components/ui/field/Field.vue @@ -0,0 +1,25 @@ + + + diff --git a/app/components/ui/field/FieldContent.vue b/app/components/ui/field/FieldContent.vue new file mode 100644 index 0000000..d9a23fd --- /dev/null +++ b/app/components/ui/field/FieldContent.vue @@ -0,0 +1,20 @@ + + + diff --git a/app/components/ui/field/FieldDescription.vue b/app/components/ui/field/FieldDescription.vue new file mode 100644 index 0000000..7240a83 --- /dev/null +++ b/app/components/ui/field/FieldDescription.vue @@ -0,0 +1,22 @@ + + + diff --git a/app/components/ui/field/FieldError.vue b/app/components/ui/field/FieldError.vue new file mode 100644 index 0000000..8a0a63f --- /dev/null +++ b/app/components/ui/field/FieldError.vue @@ -0,0 +1,53 @@ + + + diff --git a/app/components/ui/field/FieldGroup.vue b/app/components/ui/field/FieldGroup.vue new file mode 100644 index 0000000..834d8ce --- /dev/null +++ b/app/components/ui/field/FieldGroup.vue @@ -0,0 +1,20 @@ + + + diff --git a/app/components/ui/field/FieldLabel.vue b/app/components/ui/field/FieldLabel.vue new file mode 100644 index 0000000..ce6c498 --- /dev/null +++ b/app/components/ui/field/FieldLabel.vue @@ -0,0 +1,23 @@ + + + diff --git a/app/components/ui/field/FieldLegend.vue b/app/components/ui/field/FieldLegend.vue new file mode 100644 index 0000000..c620fed --- /dev/null +++ b/app/components/ui/field/FieldLegend.vue @@ -0,0 +1,24 @@ + + + diff --git a/app/components/ui/field/FieldSeparator.vue b/app/components/ui/field/FieldSeparator.vue new file mode 100644 index 0000000..97d0efa --- /dev/null +++ b/app/components/ui/field/FieldSeparator.vue @@ -0,0 +1,29 @@ + + + diff --git a/app/components/ui/field/FieldSet.vue b/app/components/ui/field/FieldSet.vue new file mode 100644 index 0000000..7be4dc9 --- /dev/null +++ b/app/components/ui/field/FieldSet.vue @@ -0,0 +1,21 @@ + + + diff --git a/app/components/ui/field/FieldTitle.vue b/app/components/ui/field/FieldTitle.vue new file mode 100644 index 0000000..f564b8b --- /dev/null +++ b/app/components/ui/field/FieldTitle.vue @@ -0,0 +1,20 @@ + + + diff --git a/app/components/ui/field/index.ts b/app/components/ui/field/index.ts new file mode 100644 index 0000000..162ba14 --- /dev/null +++ b/app/components/ui/field/index.ts @@ -0,0 +1,39 @@ +import type { VariantProps } from "class-variance-authority" +import { cva } from "class-variance-authority" + +export const fieldVariants = cva( + "group/field flex w-full gap-3 data-[invalid=true]:text-destructive", + { + variants: { + orientation: { + vertical: ["flex-col [&>*]:w-full [&>.sr-only]:w-auto"], + horizontal: [ + "flex-row items-center", + "[&>[data-slot=field-label]]:flex-auto", + "has-[>[data-slot=field-content]]:items-start has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px", + ], + responsive: [ + "flex-col [&>*]:w-full [&>.sr-only]:w-auto @md/field-group:flex-row @md/field-group:items-center @md/field-group:[&>*]:w-auto", + "@md/field-group:[&>[data-slot=field-label]]:flex-auto", + "@md/field-group:has-[>[data-slot=field-content]]:items-start @md/field-group:has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px", + ], + }, + }, + defaultVariants: { + orientation: "vertical", + }, + }, +) + +export type FieldVariants = VariantProps + +export { default as Field } from "./Field.vue" +export { default as FieldContent } from "./FieldContent.vue" +export { default as FieldDescription } from "./FieldDescription.vue" +export { default as FieldError } from "./FieldError.vue" +export { default as FieldGroup } from "./FieldGroup.vue" +export { default as FieldLabel } from "./FieldLabel.vue" +export { default as FieldLegend } from "./FieldLegend.vue" +export { default as FieldSeparator } from "./FieldSeparator.vue" +export { default as FieldSet } from "./FieldSet.vue" +export { default as FieldTitle } from "./FieldTitle.vue" diff --git a/app/components/ui/input/Input.vue b/app/components/ui/input/Input.vue new file mode 100644 index 0000000..e5135c1 --- /dev/null +++ b/app/components/ui/input/Input.vue @@ -0,0 +1,33 @@ + + + diff --git a/app/components/ui/input/index.ts b/app/components/ui/input/index.ts new file mode 100644 index 0000000..9976b86 --- /dev/null +++ b/app/components/ui/input/index.ts @@ -0,0 +1 @@ +export { default as Input } from "./Input.vue" diff --git a/app/components/ui/label/Label.vue b/app/components/ui/label/Label.vue new file mode 100644 index 0000000..ee63970 --- /dev/null +++ b/app/components/ui/label/Label.vue @@ -0,0 +1,26 @@ + + + diff --git a/app/components/ui/label/index.ts b/app/components/ui/label/index.ts new file mode 100644 index 0000000..036e35c --- /dev/null +++ b/app/components/ui/label/index.ts @@ -0,0 +1 @@ +export { default as Label } from "./Label.vue" diff --git a/app/components/ui/separator/Separator.vue b/app/components/ui/separator/Separator.vue new file mode 100644 index 0000000..78d60ec --- /dev/null +++ b/app/components/ui/separator/Separator.vue @@ -0,0 +1,29 @@ + + + diff --git a/app/components/ui/separator/index.ts b/app/components/ui/separator/index.ts new file mode 100644 index 0000000..4407287 --- /dev/null +++ b/app/components/ui/separator/index.ts @@ -0,0 +1 @@ +export { default as Separator } from "./Separator.vue" diff --git a/app/pages/presets/new.vue b/app/pages/presets/new.vue index c6a2605..875750b 100644 --- a/app/pages/presets/new.vue +++ b/app/pages/presets/new.vue @@ -1,17 +1,67 @@ diff --git a/package.json b/package.json index 848c676..93b0d25 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "@neondatabase/serverless": "^1.0.2", "@pinia/nuxt": "0.11.2", "@tailwindcss/vite": "^4.1.16", + "@tanstack/vue-form": "^1.27.7", "@vueuse/core": "^14.0.0", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", @@ -34,7 +35,8 @@ "tw-animate-css": "^1.4.0", "vue": "^3.5.22", "vue-router": "^4.6.3", - "vue-sonner": "^2.0.9" + "vue-sonner": "^2.0.9", + "zod": "^4.3.5" }, "devDependencies": { "@iconify-json/radix-icons": "^1.2.5", @@ -46,12 +48,5 @@ "nuxt-cron": "^1.8.0", "tsx": "^4.21.0", "typescript": "^5.9.3" - }, - "pnpm": { - "overrides": { - "@vue/devtools-applet": "^8.0.3", - "@vue/devtools-core": "^8.0.3", - "@vue/devtools-kit": "^8.0.3" - } } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a06dcb9..0c95b81 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4,11 +4,6 @@ settings: autoInstallPeers: true excludeLinksFromLockfile: false -overrides: - '@vue/devtools-applet': ^8.0.3 - '@vue/devtools-core': ^8.0.3 - '@vue/devtools-kit': ^8.0.3 - importers: .: @@ -28,6 +23,9 @@ importers: '@tailwindcss/vite': specifier: ^4.1.16 version: 4.1.16(vite@7.3.1(@types/node@24.9.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.21.0)(yaml@2.8.2)) + '@tanstack/vue-form': + specifier: ^1.27.7 + version: 1.27.7(vue@3.5.22(typescript@5.9.3)) '@vueuse/core': specifier: ^14.0.0 version: 14.0.0(vue@3.5.22(typescript@5.9.3)) @@ -82,6 +80,9 @@ importers: vue-sonner: specifier: ^2.0.9 version: 2.0.9(@nuxt/kit@4.2.2(magicast@0.5.0))(@nuxt/schema@4.2.2)(nuxt@4.2.2(@parcel/watcher@2.5.1)(@types/node@24.9.2)(@vue/compiler-sfc@3.5.26)(cac@6.7.14)(db0@0.3.4(drizzle-orm@0.45.1(@neondatabase/serverless@1.0.2)(@types/pg@8.16.0)(kysely@0.28.9)))(drizzle-orm@0.45.1(@neondatabase/serverless@1.0.2)(@types/pg@8.16.0)(kysely@0.28.9))(ioredis@5.8.2)(lightningcss@1.30.2)(magicast@0.5.0)(rollup@4.52.5)(terser@5.44.0)(tsx@4.21.0)(typescript@5.9.3)(vite@7.3.1(@types/node@24.9.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.21.0)(yaml@2.8.2))(yaml@2.8.2)) + zod: + specifier: ^4.3.5 + version: 4.3.5 devDependencies: '@iconify-json/radix-icons': specifier: ^1.2.5 @@ -1710,9 +1711,37 @@ packages: peerDependencies: vite: ^5.2.0 || ^6 || ^7 + '@tanstack/devtools-event-client@0.4.0': + resolution: {integrity: sha512-RPfGuk2bDZgcu9bAJodvO2lnZeHuz4/71HjZ0bGb/SPg8+lyTA+RLSKQvo7fSmPSi8/vcH3aKQ8EM9ywf1olaw==} + engines: {node: '>=18'} + + '@tanstack/form-core@1.27.7': + resolution: {integrity: sha512-nvogpyE98fhb0NDw1Bf2YaCH+L7ZIUgEpqO9TkHucDn6zg3ni521boUpv0i8HKIrmmFwDYjWZoCnrgY4HYWTkw==} + + '@tanstack/pacer-lite@0.1.1': + resolution: {integrity: sha512-y/xtNPNt/YeyoVxE/JCx+T7yjEzpezmbb+toK8DDD1P4m7Kzs5YR956+7OKexG3f8aXgC3rLZl7b1V+yNUSy5w==} + engines: {node: '>=18'} + + '@tanstack/store@0.7.7': + resolution: {integrity: sha512-xa6pTan1bcaqYDS9BDpSiS63qa6EoDkPN9RsRaxHuDdVDNntzq3xNwR5YKTU/V3SkSyC9T4YVOPh2zRQN0nhIQ==} + '@tanstack/virtual-core@3.13.12': resolution: {integrity: sha512-1YBOJfRHV4sXUmWsFSf5rQor4Ss82G8dQWLRbnk3GA4jeP8hQt1hxXh0tmflpC0dz3VgEv/1+qwPyLeWkQuPFA==} + '@tanstack/vue-form@1.27.7': + resolution: {integrity: sha512-wET2d3R/9GYGgBHMwWnKlHuGczpyTNm7bfkJyZahVDLLm7tI/6HBT970lBN44obvD4Ayu/FX0jF0ARI5JGJgpA==} + peerDependencies: + vue: ^3.4.0 + + '@tanstack/vue-store@0.7.7': + resolution: {integrity: sha512-6iv1Odmreff6TgEjQN11xoddsCnpn+/ul7MZ2DadHT3/RSY1YdoFafK8lCa889MEFi/5K0zAhf8psIkgTrRa9A==} + peerDependencies: + '@vue/composition-api': ^1.2.1 + vue: ^2.5.0 || ^3.0.0 + peerDependenciesMeta: + '@vue/composition-api': + optional: true + '@tanstack/vue-virtual@3.13.12': resolution: {integrity: sha512-vhF7kEU9EXWXh+HdAwKJ2m3xaOnTTmgcdXcF2pim8g4GvI7eRrk2YRuV5nUlZnd/NbCIX4/Ja2OZu5EjJL06Ww==} peerDependencies: @@ -1839,9 +1868,15 @@ packages: peerDependencies: vue: ^3.0.0 + '@vue/devtools-kit@7.7.9': + resolution: {integrity: sha512-PyQ6odHSgiDVd4hnTP+aDk2X4gl2HmLDfiyEnn3/oV+ckFDuswRs4IbBT7vacMuGdwY/XemxBoh302ctbsptuA==} + '@vue/devtools-kit@8.0.5': resolution: {integrity: sha512-q2VV6x1U3KJMTQPUlRMyWEKVbcHuxhqJdSr6Jtjz5uAThAIrfJ6WVZdGZm5cuO63ZnSUz0RCsVwiUUb0mDV0Yg==} + '@vue/devtools-shared@7.7.9': + resolution: {integrity: sha512-iWAb0v2WYf0QWmxCGy0seZNDPdO3Sp5+u78ORnyeonS6MT4PC7VPrryX2BpMJrwlDeaZ6BD4vP4XKjK0SZqaeA==} + '@vue/devtools-shared@8.0.5': resolution: {integrity: sha512-bRLn6/spxpmgLk+iwOrR29KrYnJjG9DGpHGkDFG82UM21ZpJ39ztUT9OXX3g+usW7/b2z+h46I9ZiYyB07XMXg==} @@ -3312,6 +3347,9 @@ packages: pathe@2.0.3: resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + perfect-debounce@1.0.0: + resolution: {integrity: sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==} + perfect-debounce@2.0.0: resolution: {integrity: sha512-fkEH/OBiKrqqI/yIgjR92lMfs2K8105zt/VT6+7eTjNwisrsh47CeIED9z58zI7DfKdH3uHAn25ziRZn3kgAow==} @@ -4348,6 +4386,9 @@ packages: resolution: {integrity: sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA==} engines: {node: '>= 14'} + zod@4.3.5: + resolution: {integrity: sha512-k7Nwx6vuWx1IJ9Bjuf4Zt1PEllcwe7cls3VNzm4CQ1/hgtFUK2bRNG3rvnpPUhFjmqJKAKtjV576KnUkHocg/g==} + snapshots: '@babel/code-frame@7.27.1': @@ -5873,8 +5914,34 @@ snapshots: tailwindcss: 4.1.16 vite: 7.3.1(@types/node@24.9.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.21.0)(yaml@2.8.2) + '@tanstack/devtools-event-client@0.4.0': {} + + '@tanstack/form-core@1.27.7': + dependencies: + '@tanstack/devtools-event-client': 0.4.0 + '@tanstack/pacer-lite': 0.1.1 + '@tanstack/store': 0.7.7 + + '@tanstack/pacer-lite@0.1.1': {} + + '@tanstack/store@0.7.7': {} + '@tanstack/virtual-core@3.13.12': {} + '@tanstack/vue-form@1.27.7(vue@3.5.22(typescript@5.9.3))': + dependencies: + '@tanstack/form-core': 1.27.7 + '@tanstack/vue-store': 0.7.7(vue@3.5.22(typescript@5.9.3)) + vue: 3.5.22(typescript@5.9.3) + transitivePeerDependencies: + - '@vue/composition-api' + + '@tanstack/vue-store@0.7.7(vue@3.5.22(typescript@5.9.3))': + dependencies: + '@tanstack/store': 0.7.7 + vue: 3.5.22(typescript@5.9.3) + vue-demi: 0.14.10(vue@3.5.22(typescript@5.9.3)) + '@tanstack/vue-virtual@3.13.12(vue@3.5.22(typescript@5.9.3))': dependencies: '@tanstack/virtual-core': 3.13.12 @@ -6066,7 +6133,7 @@ snapshots: '@vue/devtools-api@7.7.7': dependencies: - '@vue/devtools-kit': 8.0.5 + '@vue/devtools-kit': 7.7.9 '@vue/devtools-core@8.0.5(vite@7.3.1(@types/node@24.9.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.21.0)(yaml@2.8.2))(vue@3.5.26(typescript@5.9.3))': dependencies: @@ -6080,6 +6147,16 @@ snapshots: transitivePeerDependencies: - vite + '@vue/devtools-kit@7.7.9': + dependencies: + '@vue/devtools-shared': 7.7.9 + birpc: 2.9.0 + hookable: 5.5.3 + mitt: 3.0.1 + perfect-debounce: 1.0.0 + speakingurl: 14.0.1 + superjson: 2.2.5 + '@vue/devtools-kit@8.0.5': dependencies: '@vue/devtools-shared': 8.0.5 @@ -6090,6 +6167,10 @@ snapshots: speakingurl: 14.0.1 superjson: 2.2.5 + '@vue/devtools-shared@7.7.9': + dependencies: + rfdc: 1.4.1 + '@vue/devtools-shared@8.0.5': dependencies: rfdc: 1.4.1 @@ -7760,6 +7841,8 @@ snapshots: pathe@2.0.3: {} + perfect-debounce@1.0.0: {} + perfect-debounce@2.0.0: {} pg-int8@1.0.1: {} @@ -8769,3 +8852,5 @@ snapshots: archiver-utils: 5.0.2 compress-commons: 6.0.2 readable-stream: 4.7.0 + + zod@4.3.5: {}