From a8b8e3d9bf83a4d8c18896bf29735e19ed2e1224 Mon Sep 17 00:00:00 2001 From: Jeton Korenica Date: Fri, 17 Jul 2026 10:53:56 +0200 Subject: [PATCH 1/2] feat: add TagInput, PinInput, RippleButton, Marquee Four micro-interaction components, each with a registry entry and a gallery card: - TagInput: Enter pops a pill in on a back-eased overshoot, x fades it out, Backspace clears the last tag - PinInput: per-digit spring pop with auto-advance and backspace step-back - RippleButton: click-point material ripple in the accent green - Marquee: seamless duplicate-track loop, hover-pause, edge masks --- public/r/marquee.json | 14 ++++ public/r/pin-input.json | 16 +++++ public/r/registry.json | 28 ++++++++ public/r/ripple-button.json | 16 +++++ public/r/tag-input.json | 16 +++++ registry.json | 34 +++++++++ src/App.vue | 46 +++++++++++- src/components/Marquee.vue | 77 ++++++++++++++++++++ src/components/PinInput.vue | 117 ++++++++++++++++++++++++++++++ src/components/RippleButton.vue | 69 ++++++++++++++++++ src/components/TagInput.vue | 122 ++++++++++++++++++++++++++++++++ src/components/index.ts | 4 ++ 12 files changed, 558 insertions(+), 1 deletion(-) create mode 100644 public/r/marquee.json create mode 100644 public/r/pin-input.json create mode 100644 public/r/ripple-button.json create mode 100644 public/r/tag-input.json create mode 100644 src/components/Marquee.vue create mode 100644 src/components/PinInput.vue create mode 100644 src/components/RippleButton.vue create mode 100644 src/components/TagInput.vue diff --git a/public/r/marquee.json b/public/r/marquee.json new file mode 100644 index 0000000..4b6ae9a --- /dev/null +++ b/public/r/marquee.json @@ -0,0 +1,14 @@ +{ + "$schema": "https://shadcn-vue.com/schema/registry-item.json", + "name": "marquee", + "type": "registry:ui", + "title": "Marquee", + "description": "An infinite horizontal chip loop that pauses on hover, with edge mask fades. The list is duplicated and slid translateX 0 to -50% for a seamless loop. Pure CSS.", + "files": [ + { + "path": "ui/Marquee.vue", + "content": "\n\n\n\n\n", + "type": "registry:ui" + } + ] +} diff --git a/public/r/pin-input.json b/public/r/pin-input.json new file mode 100644 index 0000000..ed902e4 --- /dev/null +++ b/public/r/pin-input.json @@ -0,0 +1,16 @@ +{ + "$schema": "https://shadcn-vue.com/schema/registry-item.json", + "name": "pin-input", + "type": "registry:ui", + "title": "PinInput", + "description": "A one-time-code field of single-character boxes. Each digit pops in with a back-eased scale and advances focus; Backspace in an empty box steps back. Emits a complete event.", + "dependencies": ["motion-v@^2.3.0"], + "registryDependencies": ["https://verve.jettonn.dev/r/use-reduced-motion.json"], + "files": [ + { + "path": "ui/PinInput.vue", + "content": "\n\n\n", + "type": "registry:ui" + } + ] +} diff --git a/public/r/registry.json b/public/r/registry.json index 1949733..a5ebe7f 100644 --- a/public/r/registry.json +++ b/public/r/registry.json @@ -86,6 +86,34 @@ "title": "Accordion", "description": "Collapsible panels that expand to their content height on a smooth ease, with a chevron that rotates with a springy overshoot. Pure CSS.", "url": "https://verve.jettonn.dev/r/accordion.json" + }, + { + "name": "tag-input", + "type": "registry:ui", + "title": "TagInput", + "description": "A tag field: Enter pops a pill in with a back-eased overshoot, × removes it with a scale-down fade, and Backspace clears the last. Highlights on focus-within.", + "url": "https://verve.jettonn.dev/r/tag-input.json" + }, + { + "name": "pin-input", + "type": "registry:ui", + "title": "PinInput", + "description": "A one-time-code field of single-character boxes. Each digit pops in with a back-eased scale and advances focus; Backspace in an empty box steps back. Emits a complete event.", + "url": "https://verve.jettonn.dev/r/pin-input.json" + }, + { + "name": "ripple-button", + "type": "registry:ui", + "title": "RippleButton", + "description": "A button with a material-style ripple that starts at the exact click point, scaling out and fading over 0.6s inside an overflow-hidden face.", + "url": "https://verve.jettonn.dev/r/ripple-button.json" + }, + { + "name": "marquee", + "type": "registry:ui", + "title": "Marquee", + "description": "An infinite horizontal chip loop that pauses on hover, with edge mask fades. The list is duplicated and slid translateX 0 to -50% for a seamless loop. Pure CSS.", + "url": "https://verve.jettonn.dev/r/marquee.json" } ] } diff --git a/public/r/ripple-button.json b/public/r/ripple-button.json new file mode 100644 index 0000000..cd9176d --- /dev/null +++ b/public/r/ripple-button.json @@ -0,0 +1,16 @@ +{ + "$schema": "https://shadcn-vue.com/schema/registry-item.json", + "name": "ripple-button", + "type": "registry:ui", + "title": "RippleButton", + "description": "A button with a material-style ripple that starts at the exact click point, scaling out and fading over 0.6s inside an overflow-hidden face.", + "dependencies": ["motion-v@^2.3.0"], + "registryDependencies": ["https://verve.jettonn.dev/r/use-reduced-motion.json"], + "files": [ + { + "path": "ui/RippleButton.vue", + "content": "\n\n\n", + "type": "registry:ui" + } + ] +} diff --git a/public/r/tag-input.json b/public/r/tag-input.json new file mode 100644 index 0000000..7d658d6 --- /dev/null +++ b/public/r/tag-input.json @@ -0,0 +1,16 @@ +{ + "$schema": "https://shadcn-vue.com/schema/registry-item.json", + "name": "tag-input", + "type": "registry:ui", + "title": "TagInput", + "description": "A tag field: Enter pops a pill in with a back-eased overshoot, × removes it with a scale-down fade, and Backspace clears the last. Highlights on focus-within.", + "dependencies": ["motion-v@^2.3.0", "lucide-vue-next"], + "registryDependencies": ["https://verve.jettonn.dev/r/use-reduced-motion.json"], + "files": [ + { + "path": "ui/TagInput.vue", + "content": "\n\n\n", + "type": "registry:ui" + } + ] +} diff --git a/registry.json b/registry.json index 5f7c957..2a13bf5 100644 --- a/registry.json +++ b/registry.json @@ -107,6 +107,40 @@ "description": "Collapsible panels that expand to their content height on a smooth ease, with a chevron that rotates with a springy overshoot. Pure CSS.", "dependencies": ["lucide-vue-next"], "files": [{ "path": "src/components/Accordion.vue", "type": "registry:ui" }] + }, + { + "name": "tag-input", + "type": "registry:ui", + "title": "TagInput", + "description": "A tag field: Enter pops a pill in with a back-eased overshoot, × removes it with a scale-down fade, and Backspace clears the last. Highlights on focus-within.", + "dependencies": ["motion-v@^2.3.0", "lucide-vue-next"], + "registryDependencies": ["use-reduced-motion"], + "files": [{ "path": "src/components/TagInput.vue", "type": "registry:ui" }] + }, + { + "name": "pin-input", + "type": "registry:ui", + "title": "PinInput", + "description": "A one-time-code field of single-character boxes. Each digit pops in with a back-eased scale and advances focus; Backspace in an empty box steps back. Emits a complete event.", + "dependencies": ["motion-v@^2.3.0"], + "registryDependencies": ["use-reduced-motion"], + "files": [{ "path": "src/components/PinInput.vue", "type": "registry:ui" }] + }, + { + "name": "ripple-button", + "type": "registry:ui", + "title": "RippleButton", + "description": "A button with a material-style ripple that starts at the exact click point, scaling out and fading over 0.6s inside an overflow-hidden face.", + "dependencies": ["motion-v@^2.3.0"], + "registryDependencies": ["use-reduced-motion"], + "files": [{ "path": "src/components/RippleButton.vue", "type": "registry:ui" }] + }, + { + "name": "marquee", + "type": "registry:ui", + "title": "Marquee", + "description": "An infinite horizontal chip loop that pauses on hover, with edge mask fades. The list is duplicated and slid translateX 0 to -50% for a seamless loop. Pure CSS.", + "files": [{ "path": "src/components/Marquee.vue", "type": "registry:ui" }] } ] } diff --git a/src/App.vue b/src/App.vue index 102874c..a5e49a2 100644 --- a/src/App.vue +++ b/src/App.vue @@ -16,7 +16,11 @@ import { AnimatedCounter, StaggeredList, ErrorShake, - Accordion + Accordion, + TagInput, + PinInput, + RippleButton, + Marquee } from './components' // Raw sources for the copy-to-clipboard buttons. @@ -31,6 +35,10 @@ import CounterSrc from './components/AnimatedCounter.vue?raw' import StaggeredSrc from './components/StaggeredList.vue?raw' import ErrorShakeSrc from './components/ErrorShake.vue?raw' import AccordionSrc from './components/Accordion.vue?raw' +import TagInputSrc from './components/TagInput.vue?raw' +import PinInputSrc from './components/PinInput.vue?raw' +import RippleButtonSrc from './components/RippleButton.vue?raw' +import MarqueeSrc from './components/Marquee.vue?raw' // Will resolve once the repo is pushed under the active account. const repoUrl = 'https://github.com/Jettonn/verve' @@ -86,6 +94,18 @@ const accordionItems = [ } ] const shakeRef = ref | null>(null) +const tags = ref(['Vue', 'Motion']) +const pin = ref('') +const marqueeChips = [ + 'Spring', + 'Stagger', + 'Ripple', + 'Tooltip', + 'Magnetic', + 'Tween', + 'Blur', + 'Overshoot' +] const demoBtn = 'interactive rounded-full border border-foreground/10 bg-foreground/[0.04] px-3 py-1.5 text-xs font-medium tracking-tight text-foreground hover:bg-foreground/[0.07]' @@ -295,6 +315,30 @@ const demoBtn = > + + + + + + + + + + + Press anywhere + + + + + diff --git a/src/components/Marquee.vue b/src/components/Marquee.vue new file mode 100644 index 0000000..112b6db --- /dev/null +++ b/src/components/Marquee.vue @@ -0,0 +1,77 @@ + + + + + diff --git a/src/components/PinInput.vue b/src/components/PinInput.vue new file mode 100644 index 0000000..44adb99 --- /dev/null +++ b/src/components/PinInput.vue @@ -0,0 +1,117 @@ + + + diff --git a/src/components/RippleButton.vue b/src/components/RippleButton.vue new file mode 100644 index 0000000..97b441a --- /dev/null +++ b/src/components/RippleButton.vue @@ -0,0 +1,69 @@ + + + diff --git a/src/components/TagInput.vue b/src/components/TagInput.vue new file mode 100644 index 0000000..41ddfe3 --- /dev/null +++ b/src/components/TagInput.vue @@ -0,0 +1,122 @@ + + + diff --git a/src/components/index.ts b/src/components/index.ts index 9c63552..5215adb 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -9,3 +9,7 @@ export { default as Tooltip } from './Tooltip.vue' export { default as SegmentedControl } from './SegmentedControl.vue' export { default as SpringSwitch } from './SpringSwitch.vue' export { default as Accordion } from './Accordion.vue' +export { default as TagInput } from './TagInput.vue' +export { default as PinInput } from './PinInput.vue' +export { default as RippleButton } from './RippleButton.vue' +export { default as Marquee } from './Marquee.vue' From 467b5123c79c47c4ccc752bc74be1027888ac9fd Mon Sep 17 00:00:00 2001 From: Jeton Korenica Date: Fri, 17 Jul 2026 10:54:15 +0200 Subject: [PATCH 2/2] build: add husky pre-commit and lint/format CI gates - Add husky + lint-staged; pre-commit runs eslint --fix and prettier on staged files - Gate CI on lint:check and format:check ahead of type-check and build - Reformat ThemeToggle to satisfy the new format:check --- .github/workflows/ci.yml | 2 + .husky/pre-commit | 1 + package.json | 14 +- pnpm-lock.yaml | 242 ++++++++++++++++++++++++++++++++- public/r/theme-toggle.json | 11 +- src/components/ThemeToggle.vue | 5 +- 6 files changed, 260 insertions(+), 15 deletions(-) create mode 100644 .husky/pre-commit diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3174035..84553fa 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,5 +17,7 @@ jobs: node-version: 22 cache: pnpm - run: pnpm install --frozen-lockfile + - run: pnpm lint:check + - run: pnpm format:check - run: pnpm type-check - run: pnpm build-only diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100644 index 0000000..cb2c84d --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1 @@ +pnpm lint-staged diff --git a/package.json b/package.json index 37293f2..1d4e687 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,17 @@ "preview": "vite preview", "type-check": "vue-tsc --build", "lint": "eslint . --fix", - "format": "prettier --write src/" + "lint:check": "eslint .", + "format": "prettier --write src/", + "format:check": "prettier --check src/", + "prepare": "husky" + }, + "lint-staged": { + "*.{ts,vue,js,mjs}": [ + "eslint --fix", + "prettier --write" + ], + "*.{json,css,md,html}": "prettier --write" }, "dependencies": { "@fontsource/uncut-sans": "^5.2.5", @@ -35,6 +45,8 @@ "@vue/tsconfig": "^0.5.1", "eslint": "^9.39.5", "eslint-plugin-vue": "^9.33.0", + "husky": "^9.1.7", + "lint-staged": "^17.0.8", "npm-run-all2": "^7.0.2", "postcss": "^8.5.19", "prettier": "^3.9.5", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0e968ac..26c6a29 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -32,7 +32,7 @@ importers: version: 22.20.1 '@vitejs/plugin-vue': specifier: ^6.0.7 - version: 6.0.7(vite@8.1.4(@types/node@22.20.1)(jiti@2.7.0))(vue@3.5.39(typescript@5.9.3)) + version: 6.0.7(vite@8.1.4(@types/node@22.20.1)(jiti@2.7.0)(yaml@2.9.0))(vue@3.5.39(typescript@5.9.3)) '@vue/eslint-config-prettier': specifier: ^10.2.0 version: 10.2.0(eslint@9.39.5(jiti@2.7.0))(prettier@3.9.5) @@ -48,6 +48,12 @@ importers: eslint-plugin-vue: specifier: ^9.33.0 version: 9.33.0(eslint@9.39.5(jiti@2.7.0)) + husky: + specifier: ^9.1.7 + version: 9.1.7 + lint-staged: + specifier: ^17.0.8 + version: 17.0.8 npm-run-all2: specifier: ^7.0.2 version: 7.0.2 @@ -65,7 +71,7 @@ importers: version: 5.9.3 vite: specifier: ^8.1.4 - version: 8.1.4(@types/node@22.20.1)(jiti@2.7.0) + version: 8.1.4(@types/node@22.20.1)(jiti@2.7.0)(yaml@2.9.0) vue-tsc: specifier: ^3.3.7 version: 3.3.7(typescript@5.9.3) @@ -572,6 +578,14 @@ packages: alien-signals@3.2.1: resolution: {integrity: sha512-I8FjmltrfnDFoZedi5CG8DghVYNhzb/Ijluz7tCSJH0xpd0484Kowhbb1XDYOxfJpU1p5wnM2X54dA+IfGyD1g==} + ansi-escapes@7.3.0: + resolution: {integrity: sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg==} + engines: {node: '>=18'} + + ansi-regex@6.2.2: + resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==} + engines: {node: '>=12'} + ansi-styles@4.3.0: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} @@ -615,6 +629,14 @@ packages: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} + cli-cursor@5.0.0: + resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==} + engines: {node: '>=18'} + + cli-truncate@5.2.0: + resolution: {integrity: sha512-xRwvIOMGrfOAnM1JYtqQImuaNtDEv9v6oIYAs4LIHwTiKee8uwvIi363igssOC0O5U04i4AlENs79LQLu9tEMw==} + engines: {node: '>=20'} + color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} @@ -653,6 +675,9 @@ packages: resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} engines: {node: '>=8'} + emoji-regex@10.6.0: + resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==} + enhanced-resolve@5.21.6: resolution: {integrity: sha512-aNnGCvbJ/RIyWo1IuhNdVjnNF+EjH9wpzpNHt+ci/m9He9LJvUN8wrCcXjp9cWsGNAuvSpVFTx/vraAFQ8qGjQ==} engines: {node: '>=10.13.0'} @@ -661,6 +686,10 @@ packages: resolution: {integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==} engines: {node: '>=0.12'} + environment@1.1.0: + resolution: {integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==} + engines: {node: '>=18'} + escape-string-regexp@4.0.0: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} @@ -756,6 +785,9 @@ packages: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} + eventemitter3@5.0.4: + resolution: {integrity: sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==} + fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} @@ -822,6 +854,10 @@ packages: engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] + get-east-asian-width@1.6.0: + resolution: {integrity: sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==} + engines: {node: '>=18'} + glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} engines: {node: '>= 6'} @@ -848,6 +884,11 @@ packages: hey-listen@1.0.8: resolution: {integrity: sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q==} + husky@9.1.7: + resolution: {integrity: sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==} + engines: {node: '>=18'} + hasBin: true + ignore@5.3.2: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} @@ -868,6 +909,10 @@ packages: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} + is-fullwidth-code-point@5.1.0: + resolution: {integrity: sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==} + engines: {node: '>=18'} + is-glob@4.0.3: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} @@ -985,6 +1030,15 @@ packages: resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==} engines: {node: '>= 12.0.0'} + lint-staged@17.0.8: + resolution: {integrity: sha512-B2P/d+jVW0UXOQ0MVMLrB/9ydA1P+zz6jYfdrbbEd9ur3S2rcbduFWKiUCC02Sm5hbC8nrm7y24WuYMG54HfxA==} + engines: {node: '>=22.22.1'} + hasBin: true + + listr2@10.2.2: + resolution: {integrity: sha512-JtNtbZj8q5BnDMR7trpwvwk3RIrANtIVzEUm8w7amp6xelLgyuq+4WZoTH913XaQAoH/cNdYhaNzBPA2U3xbDw==} + engines: {node: '>=22.13.0'} + locate-path@6.0.0: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} @@ -995,6 +1049,10 @@ packages: lodash@4.18.1: resolution: {integrity: sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==} + log-update@6.1.0: + resolution: {integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==} + engines: {node: '>=18'} + lucide-vue-next@0.454.0: resolution: {integrity: sha512-UeThgLHm5sZyVTBIE1pcFn2jJsZTKe7AWo94lY2OMQAgYTKgPnrj1QLlSco2lU+kE7iFWl/stNLSsbxfoWCd6A==} deprecated: Package deprecated. Please use @lucide/vue instead. @@ -1016,6 +1074,10 @@ packages: resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} engines: {node: '>=8.6'} + mimic-function@5.0.1: + resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==} + engines: {node: '>=18'} + minimatch@10.2.5: resolution: {integrity: sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==} engines: {node: 18 || 20 || >=22} @@ -1065,6 +1127,10 @@ packages: nth-check@2.1.1: resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} + onetime@7.0.0: + resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==} + engines: {node: '>=18'} + optionator@0.9.4: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} @@ -1144,10 +1210,17 @@ packages: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} + restore-cursor@5.1.0: + resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==} + engines: {node: '>=18'} + reusify@1.1.0: resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + rfdc@1.4.1: + resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} + rolldown@1.1.5: resolution: {integrity: sha512-t9z29cJjXf/vxQ8dyhCSpt6H6aSwHTk8cT5I3iy6SMXuFpk5mB6PL6XfC8PCwrPTx93udwKUm9HRteAlTGBLiA==} engines: {node: ^20.19.0 || >=22.12.0} @@ -1173,10 +1246,38 @@ packages: resolution: {integrity: sha512-w1aiOKwKuRgtwAReIIj89puqg+I7GvX4IbLrvmhXbzQsj1+Zwi4VO3+fa6ZF91TWSjIxoEkKnMeHcLEODK5ZXA==} engines: {node: '>= 0.4'} + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + + slice-ansi@7.1.2: + resolution: {integrity: sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==} + engines: {node: '>=18'} + + slice-ansi@8.0.0: + resolution: {integrity: sha512-stxByr12oeeOyY2BlviTNQlYV5xOj47GirPr4yA1hE9JCtxfQN0+tVbkxwCtYDQWhEKWFHsEK48ORg5jrouCAg==} + engines: {node: '>=20'} + source-map-js@1.2.1: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} + string-argv@0.3.2: + resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==} + engines: {node: '>=0.6.19'} + + string-width@7.2.0: + resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} + engines: {node: '>=18'} + + string-width@8.2.2: + resolution: {integrity: sha512-GaPUh5gfdrYzqeVNZvUfT23vYYxXzKYidUcnMtJg/3rxRV63EFZy3k6xfKlmfeJD0176lnUV/Usr3XcwSvFzpg==} + engines: {node: '>=20'} + + strip-ansi@7.2.0: + resolution: {integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==} + engines: {node: '>=12'} + strip-json-comments@3.1.1: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} @@ -1196,6 +1297,10 @@ packages: resolution: {integrity: sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==} engines: {node: '>=6'} + tinyexec@1.2.4: + resolution: {integrity: sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==} + engines: {node: '>=18'} + tinyglobby@0.2.17: resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} engines: {node: '>=12.0.0'} @@ -1328,10 +1433,23 @@ packages: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} + wrap-ansi@10.0.0: + resolution: {integrity: sha512-SGcvg80f0wUy2/fXES19feHMz8E0JoXv2uNgHOu4Dgi2OrCy1lqwFYEJz1BLbDI0exjPMe/ZdzZ/YpGECBG/aQ==} + engines: {node: '>=20'} + + wrap-ansi@9.0.2: + resolution: {integrity: sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==} + engines: {node: '>=18'} + xml-name-validator@4.0.0: resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==} engines: {node: '>=12'} + yaml@2.9.0: + resolution: {integrity: sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==} + engines: {node: '>= 14.6'} + hasBin: true + yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} @@ -1705,10 +1823,10 @@ snapshots: '@typescript-eslint/types': 8.64.0 eslint-visitor-keys: 5.0.1 - '@vitejs/plugin-vue@6.0.7(vite@8.1.4(@types/node@22.20.1)(jiti@2.7.0))(vue@3.5.39(typescript@5.9.3))': + '@vitejs/plugin-vue@6.0.7(vite@8.1.4(@types/node@22.20.1)(jiti@2.7.0)(yaml@2.9.0))(vue@3.5.39(typescript@5.9.3))': dependencies: '@rolldown/pluginutils': 1.0.1 - vite: 8.1.4(@types/node@22.20.1)(jiti@2.7.0) + vite: 8.1.4(@types/node@22.20.1)(jiti@2.7.0)(yaml@2.9.0) vue: 3.5.39(typescript@5.9.3) '@volar/language-core@2.4.28': @@ -1839,6 +1957,12 @@ snapshots: alien-signals@3.2.1: {} + ansi-escapes@7.3.0: + dependencies: + environment: 1.1.0 + + ansi-regex@6.2.2: {} + ansi-styles@4.3.0: dependencies: color-convert: 2.0.1 @@ -1877,6 +2001,15 @@ snapshots: ansi-styles: 4.3.0 supports-color: 7.2.0 + cli-cursor@5.0.0: + dependencies: + restore-cursor: 5.1.0 + + cli-truncate@5.2.0: + dependencies: + slice-ansi: 8.0.0 + string-width: 8.2.2 + color-convert@2.0.1: dependencies: color-name: 1.1.4 @@ -1903,6 +2036,8 @@ snapshots: detect-libc@2.1.2: {} + emoji-regex@10.6.0: {} + enhanced-resolve@5.21.6: dependencies: graceful-fs: 4.2.11 @@ -1910,6 +2045,8 @@ snapshots: entities@7.0.1: {} + environment@1.1.0: {} + escape-string-regexp@4.0.0: {} eslint-config-prettier@10.1.8(eslint@9.39.5(jiti@2.7.0)): @@ -2035,6 +2172,8 @@ snapshots: esutils@2.0.3: {} + eventemitter3@5.0.4: {} + fast-deep-equal@3.1.3: {} fast-diff@1.3.0: {} @@ -2088,6 +2227,8 @@ snapshots: fsevents@2.3.3: optional: true + get-east-asian-width@1.6.0: {} + glob-parent@5.1.2: dependencies: is-glob: 4.0.3 @@ -2108,6 +2249,8 @@ snapshots: hey-listen@1.0.8: {} + husky@9.1.7: {} + ignore@5.3.2: {} ignore@7.0.6: {} @@ -2121,6 +2264,10 @@ snapshots: is-extglob@2.1.1: {} + is-fullwidth-code-point@5.1.0: + dependencies: + get-east-asian-width: 1.6.0 + is-glob@4.0.3: dependencies: is-extglob: 2.1.1 @@ -2203,6 +2350,23 @@ snapshots: lightningcss-win32-arm64-msvc: 1.32.0 lightningcss-win32-x64-msvc: 1.32.0 + lint-staged@17.0.8: + dependencies: + listr2: 10.2.2 + picomatch: 4.0.5 + string-argv: 0.3.2 + tinyexec: 1.2.4 + optionalDependencies: + yaml: 2.9.0 + + listr2@10.2.2: + dependencies: + cli-truncate: 5.2.0 + eventemitter3: 5.0.4 + log-update: 6.1.0 + rfdc: 1.4.1 + wrap-ansi: 10.0.0 + locate-path@6.0.0: dependencies: p-locate: 5.0.0 @@ -2211,6 +2375,14 @@ snapshots: lodash@4.18.1: {} + log-update@6.1.0: + dependencies: + ansi-escapes: 7.3.0 + cli-cursor: 5.0.0 + slice-ansi: 7.1.2 + strip-ansi: 7.2.0 + wrap-ansi: 9.0.2 + lucide-vue-next@0.454.0(vue@3.5.39(typescript@5.9.3)): dependencies: vue: 3.5.39(typescript@5.9.3) @@ -2228,6 +2400,8 @@ snapshots: braces: 3.0.3 picomatch: 2.3.2 + mimic-function@5.0.1: {} + minimatch@10.2.5: dependencies: brace-expansion: 5.0.7 @@ -2284,6 +2458,10 @@ snapshots: dependencies: boolbase: 1.0.0 + onetime@7.0.0: + dependencies: + mimic-function: 5.0.1 + optionator@0.9.4: dependencies: deep-is: 0.1.4 @@ -2349,8 +2527,15 @@ snapshots: resolve-from@4.0.0: {} + restore-cursor@5.1.0: + dependencies: + onetime: 7.0.0 + signal-exit: 4.1.0 + reusify@1.1.0: {} + rfdc@1.4.1: {} + rolldown@1.1.5: dependencies: '@oxc-project/types': 0.139.0 @@ -2386,8 +2571,37 @@ snapshots: shell-quote@1.10.0: {} + signal-exit@4.1.0: {} + + slice-ansi@7.1.2: + dependencies: + ansi-styles: 6.2.3 + is-fullwidth-code-point: 5.1.0 + + slice-ansi@8.0.0: + dependencies: + ansi-styles: 6.2.3 + is-fullwidth-code-point: 5.1.0 + source-map-js@1.2.1: {} + string-argv@0.3.2: {} + + string-width@7.2.0: + dependencies: + emoji-regex: 10.6.0 + get-east-asian-width: 1.6.0 + strip-ansi: 7.2.0 + + string-width@8.2.2: + dependencies: + get-east-asian-width: 1.6.0 + strip-ansi: 7.2.0 + + strip-ansi@7.2.0: + dependencies: + ansi-regex: 6.2.2 + strip-json-comments@3.1.1: {} supports-color@7.2.0: @@ -2402,6 +2616,8 @@ snapshots: tapable@2.3.3: {} + tinyexec@1.2.4: {} + tinyglobby@0.2.17: dependencies: fdir: 6.5.0(picomatch@4.0.5) @@ -2444,7 +2660,7 @@ snapshots: util-deprecate@1.0.2: {} - vite@8.1.4(@types/node@22.20.1)(jiti@2.7.0): + vite@8.1.4(@types/node@22.20.1)(jiti@2.7.0)(yaml@2.9.0): dependencies: lightningcss: 1.32.0 picomatch: 4.0.5 @@ -2455,6 +2671,7 @@ snapshots: '@types/node': 22.20.1 fsevents: 2.3.3 jiti: 2.7.0 + yaml: 2.9.0 vscode-uri@3.1.0: {} @@ -2509,6 +2726,21 @@ snapshots: word-wrap@1.2.5: {} + wrap-ansi@10.0.0: + dependencies: + ansi-styles: 6.2.3 + string-width: 8.2.2 + strip-ansi: 7.2.0 + + wrap-ansi@9.0.2: + dependencies: + ansi-styles: 6.2.3 + string-width: 7.2.0 + strip-ansi: 7.2.0 + xml-name-validator@4.0.0: {} + yaml@2.9.0: + optional: true + yocto-queue@0.1.0: {} diff --git a/public/r/theme-toggle.json b/public/r/theme-toggle.json index dab4faa..892f86a 100644 --- a/public/r/theme-toggle.json +++ b/public/r/theme-toggle.json @@ -4,17 +4,12 @@ "type": "registry:ui", "title": "ThemeToggle", "description": "Day/night switch: the knob springs across while sun and moon cross-fade with a blurred scale.", - "dependencies": [ - "motion-v@^2.3.0", - "lucide-vue-next" - ], - "registryDependencies": [ - "https://verve.jettonn.dev/r/use-reduced-motion.json" - ], + "dependencies": ["motion-v@^2.3.0", "lucide-vue-next"], + "registryDependencies": ["https://verve.jettonn.dev/r/use-reduced-motion.json"], "files": [ { "path": "ui/ThemeToggle.vue", - "content": "\n\n\n", + "content": "\n\n\n", "type": "registry:ui" } ] diff --git a/src/components/ThemeToggle.vue b/src/components/ThemeToggle.vue index 4ed77ca..5f4ac86 100644 --- a/src/components/ThemeToggle.vue +++ b/src/components/ThemeToggle.vue @@ -54,7 +54,10 @@ const iconSize = computed(() => (props.size === 'sm' ? 'size-3.5' : 'size-4'))