@gluhar/app 6.0.0-next.1 → 6.0.0-next.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,30 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ # [6.0.0-next.4](https://github.com/gluharco/gluhar/compare/v6.0.0-next.3...v6.0.0-next.4) (2026-10-21)
7
+
8
+ **📝 Note:** Version bump only for package @gluhar/app
9
+
10
+
11
+
12
+
13
+
14
+ # [6.0.0-next.3](https://github.com/gluharco/gluhar/compare/v6.0.0-next.2...v6.0.0-next.3) (2026-10-21)
15
+
16
+ **📝 Note:** Version bump only for package @gluhar/app
17
+
18
+
19
+
20
+
21
+
22
+ # [6.0.0-next.2](https://github.com/gluharco/gluhar/compare/v6.0.0-next.1...v6.0.0-next.2) (2026-10-21)
23
+
24
+ **📝 Note:** Version bump only for package @gluhar/app
25
+
26
+
27
+
28
+
29
+
6
30
  # [6.0.0-next.1](https://github.com/gluharco/gluhar/compare/v6.0.0-next.0...v6.0.0-next.1) (2026-10-20)
7
31
 
8
32
 
@@ -0,0 +1,22 @@
1
+ import * as _nuxt_schema from '@nuxt/schema';
2
+ import { GluharI18nOptions } from '@gluhar/i18n';
3
+ import { GluharThemeOptions } from '@gluhar/theme';
4
+ import { GluharSanityOptions } from '@gluhar/sanity';
5
+ import { ModuleOptions } from 'v-gsap-nuxt';
6
+ import { ModuleOptions as ModuleOptions$1 } from '@nuxt/image';
7
+ import { ModuleOptions as ModuleOptions$2 } from 'dayjs-nuxt';
8
+
9
+ interface GluharAppOptions {
10
+ theme?: GluharThemeOptions | false;
11
+ i18n?: GluharI18nOptions | false;
12
+ sanity?: GluharSanityOptions | false;
13
+ vgsap?: (ModuleOptions & {
14
+ dev?: boolean;
15
+ }) | false;
16
+ image?: ModuleOptions$1 | false;
17
+ dayjs?: ModuleOptions$2 | false;
18
+ }
19
+ declare const _default: _nuxt_schema.NuxtModule<GluharAppOptions, GluharAppOptions, false>;
20
+
21
+ export { _default as default };
22
+ export type { GluharAppOptions };
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "@gluhar/app",
3
+ "configKey": "gluhar",
4
+ "version": "6.0.0-next.4",
5
+ "builder": {
6
+ "@nuxt/module-builder": "1.0.3",
7
+ "unbuild": "3.6.1"
8
+ }
9
+ }
@@ -0,0 +1,54 @@
1
+ import { defineNuxtModule, createResolver, installModule, addImportsDir, addComponentsDir } from '@nuxt/kit';
2
+ import defu from 'defu';
3
+
4
+ const module$1 = defineNuxtModule({
5
+ meta: {
6
+ name: "@gluhar/app",
7
+ configKey: "gluhar"
8
+ },
9
+ // Default configuration options of the Nuxt module
10
+ defaults: {},
11
+ async setup(options, nuxt) {
12
+ const { resolve } = createResolver(import.meta.url);
13
+ const { theme, i18n, sanity, vgsap, image, dayjs } = options;
14
+ await installModule("@gluhar/core");
15
+ await installModule("@pinia/nuxt");
16
+ if (theme !== false) {
17
+ await installModule("@gluhar/theme", theme);
18
+ }
19
+ if (i18n !== false) {
20
+ await installModule("@gluhar/i18n", i18n);
21
+ }
22
+ if (sanity !== false && i18n !== false) {
23
+ await installModule("@gluhar/sanity", sanity);
24
+ }
25
+ if (vgsap !== false) {
26
+ if (vgsap?.dev) {
27
+ await installModule("@gluhar/v-gsap-nuxt", vgsap);
28
+ } else {
29
+ await installModule("v-gsap-nuxt", vgsap);
30
+ }
31
+ }
32
+ if (image !== false) {
33
+ await installModule("@nuxt/image", image);
34
+ }
35
+ if (dayjs !== false) {
36
+ await installModule("dayjs-nuxt", dayjs);
37
+ }
38
+ addImportsDir(resolve("runtime/composables"));
39
+ addComponentsDir({ path: resolve("runtime/components") });
40
+ addImportsDir(resolve("runtime/stores"));
41
+ addImportsDir(resolve("runtime/utils"));
42
+ nuxt.options.vite = defu(nuxt.options.vite, {
43
+ build: {
44
+ rolldownOptions: {
45
+ output: { comments: false }
46
+ }
47
+ }
48
+ });
49
+ nuxt.options.experimental.watcher = "parcel";
50
+ nuxt.options.devtools.enabled = false;
51
+ }
52
+ });
53
+
54
+ export { module$1 as default };
@@ -0,0 +1,40 @@
1
+ <script setup>
2
+ import { useResponsive, computed, ref } from "#imports";
3
+ const props = defineProps({
4
+ phone: { type: String, required: false },
5
+ email: { type: String, required: false },
6
+ onCopy: { type: Function, required: false }
7
+ });
8
+ const defaultCursorText = "Copy to clipboard";
9
+ const cursorText = ref(defaultCursorText);
10
+ const { isMobile } = useResponsive();
11
+ const href = computed(() => {
12
+ if (!isMobile.value) return void 0;
13
+ return props.phone ? `tel:${props.phone}` : `mailto:${props.email}`;
14
+ });
15
+ const handleClick = async (isPhone) => {
16
+ const value = isPhone ? props.phone : props.email;
17
+ if (isMobile.value) return;
18
+ cursorText.value = "Copied!";
19
+ await navigator.clipboard.writeText(value);
20
+ props.onCopy?.();
21
+ };
22
+ const onMouseLeave = () => {
23
+ cursorText.value = defaultCursorText;
24
+ };
25
+ </script>
26
+
27
+ <template>
28
+ <a :href v-bind="$attrs" v-if="isMobile">
29
+ <slot> {{ phone || email }} </slot>
30
+ </a>
31
+ <div
32
+ :data-cursor-text="cursorText"
33
+ @mousedown="handleClick(!!phone)"
34
+ @mouseleave="onMouseLeave"
35
+ v-bind="$attrs"
36
+ v-else
37
+ >
38
+ <slot> {{ phone || email }} </slot>
39
+ </div>
40
+ </template>
@@ -0,0 +1,2 @@
1
+ declare const _default: {};
2
+ export default _default;
@@ -0,0 +1,32 @@
1
+ <script setup>
2
+ import { useResizeObserver } from "@vueuse/core";
3
+ import { ref } from "vue";
4
+ import { useLenis } from "#imports";
5
+ defineProps({
6
+ contentClass: { type: String, required: false },
7
+ contentStyle: { type: Object, required: false }
8
+ });
9
+ const scrollerRef = ref();
10
+ const contentRef = ref();
11
+ const { lenis } = useLenis(scrollerRef);
12
+ const prevHeight = ref(0);
13
+ useResizeObserver(contentRef, (entries) => {
14
+ const entry = entries[0];
15
+ const { height } = entry.contentRect;
16
+ if (height === prevHeight.value) return;
17
+ prevHeight.value = height;
18
+ lenis.value?.resize();
19
+ });
20
+ defineExpose({
21
+ lenis,
22
+ scrollerRef
23
+ });
24
+ </script>
25
+
26
+ <template>
27
+ <div class="lenis-scroller overflow-y-auto" ref="scrollerRef">
28
+ <div :class="contentClass" :style="contentStyle" ref="contentRef">
29
+ <slot />
30
+ </div>
31
+ </div>
32
+ </template>
@@ -0,0 +1,2 @@
1
+ declare const _default: {};
2
+ export default _default;
@@ -0,0 +1,46 @@
1
+ <script setup lang="ts"></script>
2
+
3
+ <template>
4
+ <svg viewBox="0 0 262.1 89" class="fill-secondary-base">
5
+ <path
6
+ d="M229.5,28l-15.8-14.6l4.7-4.7l12.2,13.2L229.8,0h6.6l-0.9,21.9L249,6.8l4.7,4.7l-17,16.5l13.9,12.8l-4.7,4.7
7
+ l-12.8-13.9c-6.4,6.6-10.6,12.6-13.3,18.1c-2.9,6-4.2,12.8-4.2,23.3H209c0-11.1,1.3-19,4.9-26.2C217,40.3,222.9,34.7,229.5,28z
8
+ M262.1,31.3l-14.9-0.9v-4.8l14.9-0.9V31.3z"
9
+ />
10
+ <path
11
+ d="M181.4,72.6V38.4h6.2l0.6,5c0.9-1.9,2.4-3.3,4.4-4.2c2-1,4.1-1.4,6.2-1.4c3.7,0,6.9,1.1,9.6,3.2l-2.9,5.6
12
+ c-1.9-1.6-4.1-2.4-6.7-2.4c-1.7,0-3.4,0.4-4.9,1.2s-2.7,1.9-3.7,3.4s-1.5,3.2-1.5,5.2v18.7L181.4,72.6L181.4,72.6z"
13
+ />
14
+ <path
15
+ d="M153.5,73.5c-4,0-7.4-0.9-10.3-2.7c-2.8-1.9-4.2-4.7-4.2-8.4s1.3-6.4,4-8c2.7-1.7,6.1-2.5,10.1-2.5h13.5
16
+ c-0.2-2.3-1.3-4.2-3.3-5.7c-2-1.4-4.4-2.2-7.2-2.2c-2.1,0-4,0.4-6,1.2c-1.9,0.8-3.4,2-4.4,3.4l-5.3-2.9c1.6-2.7,3.8-4.7,6.5-6.1
17
+ c2.8-1.4,5.9-2.2,9.1-2.2c3.1,0,6,0.6,8.7,1.7c2.8,1.1,5,2.8,6.7,5s2.6,4.8,2.6,7.8v14l0.3,6.5h-6.9l-0.6-5.2
18
+ c-1.1,2-2.9,3.5-5.2,4.6C159.4,73,156.7,73.5,153.5,73.5z M154.2,67c3.3,0,6.3-0.6,8.8-1.8c2.5-1.2,3.8-3.4,3.8-6.5v-1.1h-13.5
19
+ c-1.9,0-3.5,0.4-4.9,1.1c-1.3,0.8-2,1.9-2,3.5c0,1.7,0.8,2.9,2.4,3.7C150.4,66.7,152.2,67,154.2,67z"
20
+ />
21
+ <path
22
+ d="M98.8,72.6V22.3h7.4v21.1c1-1.9,2.5-3.3,4.4-4.2c1.9-1,4.1-1.4,6.5-1.4c4.7,0,8.8,1.4,12.1,4.2s5,7.2,5,13.2v17.4h-7.5V55
23
+ c0-3.8-0.9-6.7-2.7-8.5c-1.9-1.8-4.2-2.8-7-2.8c-1.8,0-3.5,0.4-5.2,1.3c-1.6,0.9-3,2.2-4,3.9s-1.5,3.9-1.5,6.4v17.2L98.8,72.6
24
+ L98.8,72.6z"
25
+ />
26
+ <path
27
+ d="M73.1,73.2c-4.7,0-8.8-1.4-12.1-4.2c-3.3-2.8-5-7.2-5-13.2V38.4h7.5v17.5c0,3.8,0.9,6.7,2.7,8.5c1.9,1.8,4.2,2.8,7,2.8
28
+ c1.8,0,3.5-0.4,5.2-1.3c1.6-0.9,2.9-2.2,3.9-3.9c1.1-1.8,1.6-3.9,1.6-6.5V38.4h7.4v34.2h-6.6l-0.6-5c-1,1.9-2.5,3.3-4.5,4.2
29
+ S75.5,73.2,73.1,73.2z"
30
+ />
31
+ <path d="M50,72.6h-7.4V22.3H50V72.6z" />
32
+ <path
33
+ d="M19.8,89c-4.1,0-7.7-0.4-10.6-1.3c-3-0.9-5.3-2.6-6.9-5.2c-1-1.6-1.5-3.4-1.5-5.3c0-1.5,0.3-2.8,0.9-4.1
34
+ c0.6-1.2,1.5-2.2,2.7-2.9c-1.4-0.5-2.5-1.3-3.3-2.4C0.4,66.7,0,65.4,0,63.9c0-1.6,0.5-3,1.4-4.2c1-1.2,2.3-2,3.9-2.4
35
+ c-1-0.9-1.7-1.9-2.2-3.2c-0.5-1.2-0.8-2.5-0.8-3.7c0-2.8,0.8-5.2,2.5-7.2c1.6-1.9,3.8-3.3,6.5-4.1c2.7-0.9,5.6-1.2,8.5-1.1L37,38.3
36
+ v0.8v3.7v0.9l-3.4-0.1l-1.9-0.1c1.3,0.8,2.3,1.8,3.1,3S36,49,36,50.4c0,2.8-0.8,5.1-2.4,7c-1.6,1.9-3.7,3.3-6.2,4.3
37
+ c-2.5,0.9-5.2,1.4-8,1.4c-2.4,0-4.8-0.3-7.2-1C11,61.8,9.9,61.6,9,61.6c-0.5,0-1,0.1-1.4,0.2c-0.5,0.2-0.8,0.5-1.1,0.9
38
+ c-0.2,0.3-0.4,0.7-0.4,1.1c0,0.6,0.3,1.2,0.9,1.7c0.7,0.5,1.6,0.7,2.8,0.7H19c5.5,0,10,0.7,13.6,2.1c3.7,1.3,5.5,4.1,5.5,8.3
39
+ c0,3-0.9,5.4-2.8,7.2c-1.9,1.9-4.2,3.2-7,4C25.6,88.6,22.7,89,19.8,89z M19.3,57.2c2.3,0,4.5-0.6,6.5-1.7c2-1.1,2.9-2.8,2.9-5
40
+ c0-2.1-0.9-3.7-2.8-4.9c-1.9-1.1-4.1-1.7-6.7-1.7s-4.9,0.6-6.7,1.7c-1.9,1.1-2.8,2.8-2.8,4.9c0,2.3,1,4,2.9,5.1
41
+ C14.6,56.6,16.9,57.2,19.3,57.2z M19.7,82.7c3.1,0,5.7-0.6,7.8-1.7c2.2-1.1,3.2-2.7,3.2-4.8c0-1.3-0.5-2.3-1.6-2.8s-2.4-0.9-4.1-1.1
42
+ c-0.8-0.1-2-0.1-3.5-0.1h-2.4h-9c-0.9,0.4-1.5,1-2,1.7s-0.7,1.5-0.7,2.3c0,1.1,0.4,2.1,1.1,3c0.9,1.1,2.4,2,4.5,2.6
43
+ C15.1,82.4,17.3,82.7,19.7,82.7z"
44
+ />
45
+ </svg>
46
+ </template>
@@ -0,0 +1,2 @@
1
+ declare const _default: {};
2
+ export default _default;
@@ -0,0 +1,51 @@
1
+ <script setup>
2
+ import { computed, ref, useMessages } from "#imports";
3
+ import { createImageUrlBuilder } from "@sanity/image-url";
4
+ import sanityConfig from "~/config/sanity.config";
5
+ const props = defineProps({
6
+ image: { type: Object, required: false },
7
+ alt: { type: null, required: false },
8
+ by: { type: String, required: false },
9
+ width: { type: Number, required: false },
10
+ height: { type: Number, required: false },
11
+ format: { type: [String, null], required: false },
12
+ crop: { type: String, required: false },
13
+ dpr: { type: Number, required: false }
14
+ });
15
+ const { l } = useMessages();
16
+ const builder = createImageUrlBuilder(sanityConfig);
17
+ const urlFor = (source) => builder.image(source);
18
+ const imgRef = ref(null);
19
+ const src = computed(() => {
20
+ const { width, height } = imgRef.value?.getBoundingClientRect() || {
21
+ width: void 0,
22
+ height: void 0
23
+ };
24
+ if (!props.image?.asset || !width && !height) return void 0;
25
+ let url = urlFor(props.image);
26
+ if (props.width) url = url.width(props.width);
27
+ else if (width && (props.by === "width" || !props.by))
28
+ url = url.width(Math.ceil(width));
29
+ if (props.height) url = url.height(props.height);
30
+ else if (height && (props.by === "height" || !props.by))
31
+ url = url.height(Math.ceil(height));
32
+ if (props.crop) url = url.fit("crop").crop(props.crop);
33
+ url = url.format(props.format ?? "webp");
34
+ url = url.quality(100);
35
+ url = url.dpr(
36
+ props.dpr || typeof window !== "undefined" ? window.devicePixelRatio : 1
37
+ );
38
+ return url.url();
39
+ });
40
+ </script>
41
+
42
+ <template>
43
+ <img
44
+ class="object-cover"
45
+ :style="{ aspectRatio: image?.asset?.metadata?.dimensions?.aspectRatio }"
46
+ :src
47
+ :alt="l(alt) || l(image.caption) || l(image.alt)"
48
+ ref="imgRef"
49
+ v-if="image"
50
+ />
51
+ </template>
@@ -0,0 +1,2 @@
1
+ declare const _default: {};
2
+ export default _default;
@@ -0,0 +1,394 @@
1
+ import Lenis from "lenis";
2
+ import { type Ref } from "vue";
3
+ interface LenisOptions {
4
+ wrapper?: HTMLElement | string;
5
+ content?: HTMLElement;
6
+ lerp?: number;
7
+ duration?: number;
8
+ orientation?: "vertical" | "horizontal";
9
+ gestureOrientation?: "vertical" | "horizontal" | "both";
10
+ smoothWheel?: boolean;
11
+ smoothTouch?: boolean;
12
+ wheelMultiplier?: number;
13
+ touchMultiplier?: number;
14
+ normalizeWheel?: boolean;
15
+ easing?: (t: number) => number;
16
+ }
17
+ export declare const useLenis: (wrapperOrOptions?: string | HTMLElement | Ref<HTMLElement | undefined> | LenisOptions) => {
18
+ lenis: Ref<{
19
+ isTouching?: boolean | undefined;
20
+ isIos: boolean;
21
+ time: number;
22
+ userData: import("lenis").UserData;
23
+ lastVelocity: number;
24
+ velocity: number;
25
+ direction: 1 | -1 | 0;
26
+ options: {
27
+ wrapper: Window | HTMLElement | Element;
28
+ content: HTMLElement | Element;
29
+ eventsTarget: Window | HTMLElement | Element;
30
+ smoothWheel: boolean;
31
+ syncTouch: boolean;
32
+ syncTouchLerp: number;
33
+ touchInertiaExponent: number;
34
+ lerp: number;
35
+ infinite: boolean;
36
+ orientation: import("lenis").Orientation;
37
+ gestureOrientation: import("lenis").GestureOrientation;
38
+ touchMultiplier: number;
39
+ wheelMultiplier: number;
40
+ autoResize: boolean;
41
+ overscroll: boolean;
42
+ autoRaf: boolean;
43
+ anchors: boolean | {
44
+ offset?: number | undefined;
45
+ immediate?: boolean | undefined;
46
+ lock?: boolean | undefined;
47
+ duration?: number | undefined;
48
+ easing?: import("lenis").EasingFunction | undefined;
49
+ lerp?: number | undefined;
50
+ onStart?: ((lenis: Lenis) => void) | undefined;
51
+ onComplete?: ((lenis: Lenis) => void) | undefined;
52
+ force?: boolean | undefined;
53
+ programmatic?: boolean | undefined;
54
+ userData?: import("lenis").UserData | undefined;
55
+ };
56
+ autoToggle: boolean;
57
+ allowNestedScroll: boolean;
58
+ naiveDimensions: boolean;
59
+ stopInertiaOnNavigate: boolean;
60
+ respectReducedMotion: boolean;
61
+ virtualScroll?: ((data: import("lenis").VirtualScrollData) => boolean) | undefined;
62
+ duration?: number | undefined;
63
+ easing?: import("lenis").EasingFunction | undefined;
64
+ prevent?: ((node: HTMLElement) => boolean) | undefined;
65
+ __experimental__naiveDimensions?: boolean | undefined;
66
+ };
67
+ targetScroll: number;
68
+ animatedScroll: number;
69
+ readonly dimensions: {
70
+ width: number;
71
+ height: number;
72
+ scrollHeight: number;
73
+ scrollWidth: number;
74
+ destroy: () => void;
75
+ resize: () => void;
76
+ onWrapperResize: () => void;
77
+ onContentResize: () => void;
78
+ readonly limit: {
79
+ x: number;
80
+ y: number;
81
+ };
82
+ };
83
+ destroy: () => void;
84
+ on: {
85
+ (event: "scroll", callback: import("lenis").ScrollCallback): () => void;
86
+ (event: "virtual-scroll", callback: import("lenis").VirtualScrollCallback): () => void;
87
+ };
88
+ off: {
89
+ (event: "scroll", callback: import("lenis").ScrollCallback): void;
90
+ (event: "virtual-scroll", callback: import("lenis").VirtualScrollCallback): void;
91
+ };
92
+ readonly overflow: string;
93
+ resize: () => void;
94
+ start: () => void;
95
+ stop: () => void;
96
+ raf: (time: number) => void;
97
+ scrollTo: (_target: number | string | Element, { offset, immediate, lock, programmatic, lerp, duration, easing, onStart, onComplete, force, userData }?: import("lenis").ScrollToOptions) => void;
98
+ readonly rootElement: HTMLElement;
99
+ readonly limit: number;
100
+ readonly isHorizontal: boolean;
101
+ readonly actualScroll: number;
102
+ readonly scroll: number;
103
+ readonly progress: number;
104
+ isScrolling: import("lenis").Scrolling;
105
+ isStopped: boolean;
106
+ isLocked: boolean;
107
+ readonly isSmooth: boolean;
108
+ readonly prefersReducedMotion: boolean;
109
+ readonly className: string;
110
+ } | null, Lenis | {
111
+ isTouching?: boolean | undefined;
112
+ isIos: boolean;
113
+ time: number;
114
+ userData: import("lenis").UserData;
115
+ lastVelocity: number;
116
+ velocity: number;
117
+ direction: 1 | -1 | 0;
118
+ options: {
119
+ wrapper: Window | HTMLElement | Element;
120
+ content: HTMLElement | Element;
121
+ eventsTarget: Window | HTMLElement | Element;
122
+ smoothWheel: boolean;
123
+ syncTouch: boolean;
124
+ syncTouchLerp: number;
125
+ touchInertiaExponent: number;
126
+ lerp: number;
127
+ infinite: boolean;
128
+ orientation: import("lenis").Orientation;
129
+ gestureOrientation: import("lenis").GestureOrientation;
130
+ touchMultiplier: number;
131
+ wheelMultiplier: number;
132
+ autoResize: boolean;
133
+ overscroll: boolean;
134
+ autoRaf: boolean;
135
+ anchors: boolean | {
136
+ offset?: number | undefined;
137
+ immediate?: boolean | undefined;
138
+ lock?: boolean | undefined;
139
+ duration?: number | undefined;
140
+ easing?: import("lenis").EasingFunction | undefined;
141
+ lerp?: number | undefined;
142
+ onStart?: ((lenis: Lenis) => void) | undefined;
143
+ onComplete?: ((lenis: Lenis) => void) | undefined;
144
+ force?: boolean | undefined;
145
+ programmatic?: boolean | undefined;
146
+ userData?: import("lenis").UserData | undefined;
147
+ };
148
+ autoToggle: boolean;
149
+ allowNestedScroll: boolean;
150
+ naiveDimensions: boolean;
151
+ stopInertiaOnNavigate: boolean;
152
+ respectReducedMotion: boolean;
153
+ virtualScroll?: ((data: import("lenis").VirtualScrollData) => boolean) | undefined;
154
+ duration?: number | undefined;
155
+ easing?: import("lenis").EasingFunction | undefined;
156
+ prevent?: ((node: HTMLElement) => boolean) | undefined;
157
+ __experimental__naiveDimensions?: boolean | undefined;
158
+ };
159
+ targetScroll: number;
160
+ animatedScroll: number;
161
+ readonly dimensions: {
162
+ width: number;
163
+ height: number;
164
+ scrollHeight: number;
165
+ scrollWidth: number;
166
+ destroy: () => void;
167
+ resize: () => void;
168
+ onWrapperResize: () => void;
169
+ onContentResize: () => void;
170
+ readonly limit: {
171
+ x: number;
172
+ y: number;
173
+ };
174
+ };
175
+ destroy: () => void;
176
+ on: {
177
+ (event: "scroll", callback: import("lenis").ScrollCallback): () => void;
178
+ (event: "virtual-scroll", callback: import("lenis").VirtualScrollCallback): () => void;
179
+ };
180
+ off: {
181
+ (event: "scroll", callback: import("lenis").ScrollCallback): void;
182
+ (event: "virtual-scroll", callback: import("lenis").VirtualScrollCallback): void;
183
+ };
184
+ readonly overflow: string;
185
+ resize: () => void;
186
+ start: () => void;
187
+ stop: () => void;
188
+ raf: (time: number) => void;
189
+ scrollTo: (_target: number | string | Element, { offset, immediate, lock, programmatic, lerp, duration, easing, onStart, onComplete, force, userData }?: import("lenis").ScrollToOptions) => void;
190
+ readonly rootElement: HTMLElement;
191
+ readonly limit: number;
192
+ readonly isHorizontal: boolean;
193
+ readonly actualScroll: number;
194
+ readonly scroll: number;
195
+ readonly progress: number;
196
+ isScrolling: import("lenis").Scrolling;
197
+ isStopped: boolean;
198
+ isLocked: boolean;
199
+ readonly isSmooth: boolean;
200
+ readonly prefersReducedMotion: boolean;
201
+ readonly className: string;
202
+ } | null>;
203
+ destroy: () => void;
204
+ createInstance?: undefined;
205
+ } | {
206
+ lenis: Ref<{
207
+ isTouching?: boolean | undefined;
208
+ isIos: boolean;
209
+ time: number;
210
+ userData: import("lenis").UserData;
211
+ lastVelocity: number;
212
+ velocity: number;
213
+ direction: 1 | -1 | 0;
214
+ options: {
215
+ wrapper: Window | HTMLElement | Element;
216
+ content: HTMLElement | Element;
217
+ eventsTarget: Window | HTMLElement | Element;
218
+ smoothWheel: boolean;
219
+ syncTouch: boolean;
220
+ syncTouchLerp: number;
221
+ touchInertiaExponent: number;
222
+ lerp: number;
223
+ infinite: boolean;
224
+ orientation: import("lenis").Orientation;
225
+ gestureOrientation: import("lenis").GestureOrientation;
226
+ touchMultiplier: number;
227
+ wheelMultiplier: number;
228
+ autoResize: boolean;
229
+ overscroll: boolean;
230
+ autoRaf: boolean;
231
+ anchors: boolean | {
232
+ offset?: number | undefined;
233
+ immediate?: boolean | undefined;
234
+ lock?: boolean | undefined;
235
+ duration?: number | undefined;
236
+ easing?: import("lenis").EasingFunction | undefined;
237
+ lerp?: number | undefined;
238
+ onStart?: ((lenis: Lenis) => void) | undefined;
239
+ onComplete?: ((lenis: Lenis) => void) | undefined;
240
+ force?: boolean | undefined;
241
+ programmatic?: boolean | undefined;
242
+ userData?: import("lenis").UserData | undefined;
243
+ };
244
+ autoToggle: boolean;
245
+ allowNestedScroll: boolean;
246
+ naiveDimensions: boolean;
247
+ stopInertiaOnNavigate: boolean;
248
+ respectReducedMotion: boolean;
249
+ virtualScroll?: ((data: import("lenis").VirtualScrollData) => boolean) | undefined;
250
+ duration?: number | undefined;
251
+ easing?: import("lenis").EasingFunction | undefined;
252
+ prevent?: ((node: HTMLElement) => boolean) | undefined;
253
+ __experimental__naiveDimensions?: boolean | undefined;
254
+ };
255
+ targetScroll: number;
256
+ animatedScroll: number;
257
+ readonly dimensions: {
258
+ width: number;
259
+ height: number;
260
+ scrollHeight: number;
261
+ scrollWidth: number;
262
+ destroy: () => void;
263
+ resize: () => void;
264
+ onWrapperResize: () => void;
265
+ onContentResize: () => void;
266
+ readonly limit: {
267
+ x: number;
268
+ y: number;
269
+ };
270
+ };
271
+ destroy: () => void;
272
+ on: {
273
+ (event: "scroll", callback: import("lenis").ScrollCallback): () => void;
274
+ (event: "virtual-scroll", callback: import("lenis").VirtualScrollCallback): () => void;
275
+ };
276
+ off: {
277
+ (event: "scroll", callback: import("lenis").ScrollCallback): void;
278
+ (event: "virtual-scroll", callback: import("lenis").VirtualScrollCallback): void;
279
+ };
280
+ readonly overflow: string;
281
+ resize: () => void;
282
+ start: () => void;
283
+ stop: () => void;
284
+ raf: (time: number) => void;
285
+ scrollTo: (_target: number | string | Element, { offset, immediate, lock, programmatic, lerp, duration, easing, onStart, onComplete, force, userData }?: import("lenis").ScrollToOptions) => void;
286
+ readonly rootElement: HTMLElement;
287
+ readonly limit: number;
288
+ readonly isHorizontal: boolean;
289
+ readonly actualScroll: number;
290
+ readonly scroll: number;
291
+ readonly progress: number;
292
+ isScrolling: import("lenis").Scrolling;
293
+ isStopped: boolean;
294
+ isLocked: boolean;
295
+ readonly isSmooth: boolean;
296
+ readonly prefersReducedMotion: boolean;
297
+ readonly className: string;
298
+ } | null, Lenis | {
299
+ isTouching?: boolean | undefined;
300
+ isIos: boolean;
301
+ time: number;
302
+ userData: import("lenis").UserData;
303
+ lastVelocity: number;
304
+ velocity: number;
305
+ direction: 1 | -1 | 0;
306
+ options: {
307
+ wrapper: Window | HTMLElement | Element;
308
+ content: HTMLElement | Element;
309
+ eventsTarget: Window | HTMLElement | Element;
310
+ smoothWheel: boolean;
311
+ syncTouch: boolean;
312
+ syncTouchLerp: number;
313
+ touchInertiaExponent: number;
314
+ lerp: number;
315
+ infinite: boolean;
316
+ orientation: import("lenis").Orientation;
317
+ gestureOrientation: import("lenis").GestureOrientation;
318
+ touchMultiplier: number;
319
+ wheelMultiplier: number;
320
+ autoResize: boolean;
321
+ overscroll: boolean;
322
+ autoRaf: boolean;
323
+ anchors: boolean | {
324
+ offset?: number | undefined;
325
+ immediate?: boolean | undefined;
326
+ lock?: boolean | undefined;
327
+ duration?: number | undefined;
328
+ easing?: import("lenis").EasingFunction | undefined;
329
+ lerp?: number | undefined;
330
+ onStart?: ((lenis: Lenis) => void) | undefined;
331
+ onComplete?: ((lenis: Lenis) => void) | undefined;
332
+ force?: boolean | undefined;
333
+ programmatic?: boolean | undefined;
334
+ userData?: import("lenis").UserData | undefined;
335
+ };
336
+ autoToggle: boolean;
337
+ allowNestedScroll: boolean;
338
+ naiveDimensions: boolean;
339
+ stopInertiaOnNavigate: boolean;
340
+ respectReducedMotion: boolean;
341
+ virtualScroll?: ((data: import("lenis").VirtualScrollData) => boolean) | undefined;
342
+ duration?: number | undefined;
343
+ easing?: import("lenis").EasingFunction | undefined;
344
+ prevent?: ((node: HTMLElement) => boolean) | undefined;
345
+ __experimental__naiveDimensions?: boolean | undefined;
346
+ };
347
+ targetScroll: number;
348
+ animatedScroll: number;
349
+ readonly dimensions: {
350
+ width: number;
351
+ height: number;
352
+ scrollHeight: number;
353
+ scrollWidth: number;
354
+ destroy: () => void;
355
+ resize: () => void;
356
+ onWrapperResize: () => void;
357
+ onContentResize: () => void;
358
+ readonly limit: {
359
+ x: number;
360
+ y: number;
361
+ };
362
+ };
363
+ destroy: () => void;
364
+ on: {
365
+ (event: "scroll", callback: import("lenis").ScrollCallback): () => void;
366
+ (event: "virtual-scroll", callback: import("lenis").VirtualScrollCallback): () => void;
367
+ };
368
+ off: {
369
+ (event: "scroll", callback: import("lenis").ScrollCallback): void;
370
+ (event: "virtual-scroll", callback: import("lenis").VirtualScrollCallback): void;
371
+ };
372
+ readonly overflow: string;
373
+ resize: () => void;
374
+ start: () => void;
375
+ stop: () => void;
376
+ raf: (time: number) => void;
377
+ scrollTo: (_target: number | string | Element, { offset, immediate, lock, programmatic, lerp, duration, easing, onStart, onComplete, force, userData }?: import("lenis").ScrollToOptions) => void;
378
+ readonly rootElement: HTMLElement;
379
+ readonly limit: number;
380
+ readonly isHorizontal: boolean;
381
+ readonly actualScroll: number;
382
+ readonly scroll: number;
383
+ readonly progress: number;
384
+ isScrolling: import("lenis").Scrolling;
385
+ isStopped: boolean;
386
+ isLocked: boolean;
387
+ readonly isSmooth: boolean;
388
+ readonly prefersReducedMotion: boolean;
389
+ readonly className: string;
390
+ } | null>;
391
+ createInstance: (customOptions?: LenisOptions) => Lenis | null;
392
+ destroy: () => void;
393
+ };
394
+ export {};
@@ -0,0 +1,101 @@
1
+ import Lenis from "lenis";
2
+ import { ScrollTrigger } from "gsap/ScrollTrigger";
3
+ import { computed, onMounted, onUnmounted, isRef, ref } from "vue";
4
+ import { useClientOnly, useGSAP, watch } from "#imports";
5
+ const globalLenis = ref(null);
6
+ const globalDestroy = () => {
7
+ if (globalLenis.value) {
8
+ globalLenis.value.destroy();
9
+ globalLenis.value = null;
10
+ }
11
+ };
12
+ export const useLenis = (wrapperOrOptions) => {
13
+ const { isClient } = useClientOnly();
14
+ const gsap = useGSAP();
15
+ if (!wrapperOrOptions) {
16
+ return {
17
+ lenis: globalLenis,
18
+ destroy: globalDestroy
19
+ };
20
+ }
21
+ const lenis = ref(null);
22
+ const options = computed(() => {
23
+ if (!isClient.value) return {};
24
+ if (typeof wrapperOrOptions === "string" || wrapperOrOptions instanceof HTMLElement) {
25
+ return { wrapper: wrapperOrOptions };
26
+ }
27
+ if (isRef(wrapperOrOptions)) {
28
+ return { wrapper: wrapperOrOptions.value };
29
+ }
30
+ return wrapperOrOptions || {};
31
+ });
32
+ const initLenis = (customOptions = {}) => {
33
+ if (!isClient.value) return null;
34
+ const currentOptions = options.value;
35
+ if (currentOptions.wrapper === void 0) {
36
+ return null;
37
+ }
38
+ const lenisOptions = {
39
+ wrapper: window,
40
+ // Default to window/body instead of #scroller
41
+ ...currentOptions,
42
+ ...customOptions
43
+ };
44
+ let wrapper = lenisOptions.wrapper;
45
+ if (typeof wrapper === "string") {
46
+ wrapper = document.querySelector(wrapper) || window;
47
+ }
48
+ const lenisInstance = new Lenis({
49
+ ...lenisOptions,
50
+ wrapper
51
+ });
52
+ lenisInstance.on("scroll", ScrollTrigger.update);
53
+ gsap.ticker.add((time) => {
54
+ lenisInstance.raf(time * 1e3);
55
+ });
56
+ gsap.ticker.lagSmoothing(0);
57
+ return lenisInstance;
58
+ };
59
+ const createInstance = (customOptions = {}) => {
60
+ const instance = initLenis(customOptions);
61
+ if (!lenis.value) {
62
+ lenis.value = instance;
63
+ }
64
+ return instance;
65
+ };
66
+ const destroy = () => {
67
+ if (lenis.value) {
68
+ lenis.value.destroy();
69
+ lenis.value = null;
70
+ }
71
+ };
72
+ watch(
73
+ () => options.value.wrapper,
74
+ (newWrapper) => {
75
+ if (newWrapper && !lenis.value) {
76
+ lenis.value = initLenis();
77
+ if (newWrapper === "#scroller" || typeof newWrapper === "object" && newWrapper?.id === "scroller") {
78
+ globalLenis.value = lenis.value;
79
+ }
80
+ }
81
+ },
82
+ { immediate: true }
83
+ );
84
+ onMounted(() => {
85
+ if (!lenis.value) {
86
+ lenis.value = initLenis();
87
+ const currentWrapper = options.value.wrapper;
88
+ if (currentWrapper === "#scroller" || typeof currentWrapper === "object" && currentWrapper?.id === "scroller") {
89
+ globalLenis.value = lenis.value;
90
+ }
91
+ }
92
+ });
93
+ onUnmounted(() => {
94
+ destroy();
95
+ });
96
+ return {
97
+ lenis,
98
+ createInstance,
99
+ destroy
100
+ };
101
+ };
@@ -0,0 +1,22 @@
1
+ import type { AsyncDataRequestStatus } from "#app";
2
+ import type { SanityPage } from "@gluhar/sanity/types";
3
+ interface Page extends SanityPage {
4
+ _type: "page";
5
+ }
6
+ type PageTypeKeys = "page" | string;
7
+ type PageInterfaces = Record<PageTypeKeys, Page | SanityPage>;
8
+ type PageTypes<PI extends PageInterfaces> = Record<keyof PI, {
9
+ query: string;
10
+ slug: string;
11
+ type?: string;
12
+ }>;
13
+ export declare const useSanityPage: <PI extends PageInterfaces = {
14
+ page: Page;
15
+ }, PT extends PageTypes<PI> = PageTypes<PI>>(types?: PT, slug?: string | string[]) => Promise<{
16
+ page: import("vue").Ref<PI[Extract<keyof PT, string>] | undefined, PI[Extract<keyof PT, string>] | undefined>;
17
+ status: import("vue").Ref<AsyncDataRequestStatus, AsyncDataRequestStatus>;
18
+ } | {
19
+ page: import("vue").Ref<Page | undefined, Page | undefined>;
20
+ status: import("vue").Ref<AsyncDataRequestStatus, AsyncDataRequestStatus>;
21
+ }>;
22
+ export {};
@@ -0,0 +1,60 @@
1
+ import {
2
+ getCanonicalUrl,
3
+ groq,
4
+ useHead,
5
+ useRoute,
6
+ useSanityQueryWithPreview
7
+ } from "#imports";
8
+ import { imageWithAsset, linkWithPage } from "@gluhar/sanity/query";
9
+ export const useSanityPage = async (types, slug) => {
10
+ const route = useRoute();
11
+ const s = slug ? slug : route.params.slug;
12
+ const sl = Array.isArray(s) ? s : [s];
13
+ if (types && sl.length > 1) {
14
+ for (const type in types) {
15
+ const options = types[type];
16
+ if (sl[0] === options.slug) {
17
+ const query2 = groq`
18
+ *[_type == "${type}" && slug.current == "${sl[1]}"]{
19
+ ${options.query}
20
+ }[0]
21
+ `;
22
+ const { data: page2, status: status2 } = await useSanityQueryWithPreview(query2);
23
+ useHead({
24
+ link: [
25
+ {
26
+ rel: "canonical",
27
+ href: getCanonicalUrl(page2?.value)
28
+ }
29
+ ]
30
+ });
31
+ return { page: page2, status: status2 };
32
+ }
33
+ }
34
+ }
35
+ const pageQuery = groq`
36
+ ...,
37
+ content[]{
38
+ ...,
39
+ _ref != null => @-> {
40
+ ...,
41
+ button{ ${linkWithPage} },
42
+ thumbnail{ ${imageWithAsset} },
43
+ image{ ${imageWithAsset} },
44
+ images[]{ ${imageWithAsset} },
45
+ bckg{ ${imageWithAsset} },
46
+ }
47
+ }
48
+ `;
49
+ const query = groq`
50
+ *[_type == "page" && slug.current == "${sl[0] ? sl?.join("/") : "/"}"]{
51
+ ${types?.page?.query ?? pageQuery}
52
+ }[0]
53
+ `;
54
+ const { data: page, status } = await useSanityQueryWithPreview(query);
55
+ if (page.value) page.value._type = "page";
56
+ useHead({
57
+ link: [{ rel: "canonical", href: getCanonicalUrl(page.value) }]
58
+ });
59
+ return { page, status };
60
+ };
@@ -0,0 +1,2 @@
1
+ import type { Seo } from "@gluhar/sanity/types";
2
+ export declare const useSeo: (seo?: Seo) => void;
@@ -0,0 +1,34 @@
1
+ import { useGStore } from "../stores/useGStore.js";
2
+ import { useMessages } from "#imports";
3
+ import {
4
+ useSeoMeta,
5
+ useServerSeoMeta
6
+ } from "@unhead/vue";
7
+ export const useSeo = (seo) => {
8
+ const store = useGStore();
9
+ const { l } = useMessages();
10
+ const meta = {
11
+ title: l(store.defaultSeo?.title),
12
+ ogTitle: l(store.defaultSeo?.title),
13
+ description: l(store.defaultSeo?.description),
14
+ ogDescription: l(store.defaultSeo?.description),
15
+ ogImage: store.defaultSeo?.ogImage?.asset?.url,
16
+ twitterCard: "summary_large_image"
17
+ };
18
+ if (seo) {
19
+ const { title, description, ogImage } = seo;
20
+ if (title && l(title)) {
21
+ meta.title = l(title);
22
+ meta.ogTitle = l(title);
23
+ }
24
+ if (description && l(description)) {
25
+ meta.description = l(description);
26
+ meta.ogDescription = l(description);
27
+ }
28
+ if (ogImage && ogImage.asset?.url) {
29
+ meta.ogImage = ogImage.asset?.url;
30
+ }
31
+ }
32
+ useServerSeoMeta(meta);
33
+ useSeoMeta(meta);
34
+ };
@@ -0,0 +1,28 @@
1
+ import type { Seo } from "@gluhar/sanity/types";
2
+ export declare const useGStore: import("pinia").SetupStoreDefinition<"gluhar", {
3
+ defaultSeo: import("vue").Ref<{
4
+ title?: import("@gluhar/i18n/types").LocaleString | undefined;
5
+ description?: import("@gluhar/i18n/types").LocaleString | undefined;
6
+ ogImage?: {
7
+ asset?: {
8
+ url: string;
9
+ } | undefined;
10
+ caption?: import("@gluhar/i18n/types").LocaleString | undefined;
11
+ alt?: import("@gluhar/i18n/types").LocaleString | undefined;
12
+ } | undefined;
13
+ }, Seo | {
14
+ title?: import("@gluhar/i18n/types").LocaleString | undefined;
15
+ description?: import("@gluhar/i18n/types").LocaleString | undefined;
16
+ ogImage?: {
17
+ asset?: {
18
+ url: string;
19
+ } | undefined;
20
+ caption?: import("@gluhar/i18n/types").LocaleString | undefined;
21
+ alt?: import("@gluhar/i18n/types").LocaleString | undefined;
22
+ } | undefined;
23
+ }>;
24
+ setDefaultSeo: (seo: Seo) => void;
25
+ mobileMenuActive: import("vue").Ref<boolean, boolean>;
26
+ toggleMobileMenu: () => void;
27
+ closeMobileMenu: () => void;
28
+ }>;
@@ -0,0 +1,21 @@
1
+ import { defineStore, ref } from "#imports";
2
+ export const useGStore = defineStore("gluhar", () => {
3
+ const defaultSeo = ref({});
4
+ const setDefaultSeo = (seo) => {
5
+ defaultSeo.value = seo;
6
+ };
7
+ const mobileMenuActive = ref(false);
8
+ const toggleMobileMenu = () => {
9
+ mobileMenuActive.value = !mobileMenuActive.value;
10
+ };
11
+ const closeMobileMenu = () => {
12
+ mobileMenuActive.value = false;
13
+ };
14
+ return {
15
+ defaultSeo,
16
+ setDefaultSeo,
17
+ mobileMenuActive,
18
+ toggleMobileMenu,
19
+ closeMobileMenu
20
+ };
21
+ });
@@ -0,0 +1 @@
1
+ export {};
File without changes
@@ -0,0 +1,3 @@
1
+ import type { SanityPage } from "@gluhar/sanity/types";
2
+ export declare const getCanonicalUrl: (page: SanityPage | null) => string;
3
+ export declare const defaultSeoQuery = "\n *[_id == \"defaultSeo\"]{\n ...,\n ogImage{\n ...,\n asset->\n }\n }[0]\n";
@@ -0,0 +1,22 @@
1
+ import { useRuntimeConfig } from "#imports";
2
+ import { envMode } from "@gluhar/core/utils/common";
3
+ export const getCanonicalUrl = (page) => {
4
+ const prod = envMode() === "prod";
5
+ const config = useRuntimeConfig().public;
6
+ const websiteUrl = (prod ? config.baseUrl?.toString() : config.devUrl?.toString()) || "http://localhost:3000";
7
+ if (!page?.slug) return websiteUrl;
8
+ const { _type } = page;
9
+ const slug = page.slug.current;
10
+ const path = _type === "page" ? slug : `${_type}s/${slug}`;
11
+ const url = new URL(path, websiteUrl);
12
+ return url.toString();
13
+ };
14
+ export const defaultSeoQuery = `
15
+ *[_id == "defaultSeo"]{
16
+ ...,
17
+ ogImage{
18
+ ...,
19
+ asset->
20
+ }
21
+ }[0]
22
+ `;
@@ -0,0 +1,9 @@
1
+ import type { NuxtModule } from '@nuxt/schema'
2
+
3
+ import type { default as Module } from './module.mjs'
4
+
5
+ export type ModuleOptions = typeof Module extends NuxtModule<infer O> ? Partial<O> : Record<string, any>
6
+
7
+ export { default } from './module.mjs'
8
+
9
+ export { type GluharAppOptions } from './module.mjs'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gluhar/app",
3
- "version": "6.0.0-next.1",
3
+ "version": "6.0.0-next.4",
4
4
  "description": "Gluhar main package. This combines and configures @gluhar/theme, @gluhar/i18n, @gluhar/sanity and @gluhar/core into one main package. Provides numerous components, composables, utils and types.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -41,35 +41,34 @@
41
41
  "test:watch": "vitest watch"
42
42
  },
43
43
  "dependencies": {
44
- "@gluhar/core": "^6.0.0-next.1",
45
- "@gluhar/i18n": "^6.0.0-next.1",
46
- "@gluhar/sanity": "^6.0.0-next.1",
47
- "@gluhar/theme": "^6.0.0-next.1",
48
- "@gluhar/v-gsap-nuxt": "^1.5.1",
49
- "@nuxt/image": "^1.10.0",
50
- "@nuxt/kit": "^4.5.0",
51
- "@pinia/nuxt": "^0.11.2",
52
- "@sanity/image-url": "^1.1.0",
44
+ "@gluhar/core": "^6.0.0-next.4",
45
+ "@gluhar/i18n": "^6.0.0-next.4",
46
+ "@gluhar/sanity": "^6.0.0-next.4",
47
+ "@gluhar/theme": "^6.0.0-next.4",
48
+ "@gluhar/v-gsap-nuxt": "^1.6.4",
49
+ "@nuxt/image": "^2.2.0",
50
+ "@nuxt/kit": "^4.6.2",
51
+ "@pinia/nuxt": "^1.0.3",
52
+ "@sanity/image-url": "^2.1.1",
53
53
  "dayjs-nuxt": "^2.1.11",
54
54
  "defu": "^6.1.4",
55
- "lenis": "^1.3.11",
56
- "pinia": "^3.0.3",
57
- "v-gsap-nuxt": "^1.4.2"
55
+ "lenis": "^1.3.27",
56
+ "pinia": "^4.1.0",
57
+ "v-gsap-nuxt": "^1.5.12"
58
58
  },
59
59
  "devDependencies": {
60
- "@gluhar/fluid": "^6.0.0-next.1",
61
- "@nuxt/devtools": "latest",
60
+ "@gluhar/fluid": "^6.0.0-next.4",
62
61
  "@nuxt/eslint-config": "^1.9.0",
63
62
  "@nuxt/module-builder": "^1.0.3",
64
- "@nuxt/schema": "^4.5.0",
65
- "@nuxt/test-utils": "^3.19.2",
63
+ "@nuxt/schema": "^4.6.2",
64
+ "@nuxt/test-utils": "^4.3.4",
66
65
  "@types/node": "^22.5.4",
67
66
  "changelogen": "^0.5.5",
68
67
  "eslint": "^9.10.0",
69
- "nuxt": "^4.5.0",
70
- "vitest": "^3.2.4"
68
+ "nuxt": "^4.6.2",
69
+ "vitest": "^5.0.5"
71
70
  },
72
71
  "peerDependencies": {
73
- "nuxt": "^4.5.0"
72
+ "nuxt": "^4.6.2"
74
73
  }
75
74
  }