@gluhar/core 4.2.0 → 6.0.0-next.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -20,39 +20,33 @@ My new Nuxt module for doing amazing things.
20
20
  <!-- - [🏀 Online playground](https://stackblitz.com/github/your-org/my-module?file=playground%2Fapp.vue) -->
21
21
  <!-- - [📖 &nbsp;Documentation](https://example.com) -->
22
22
 
23
- ## Features
23
+ Shared composables and utils, installed by `@gluhar/app`. Installs `@nuxtjs/device`.
24
24
 
25
- <!-- Highlight some of the features your module provide here -->
26
- - ⛰ &nbsp;Foo
27
- - 🚠 &nbsp;Bar
28
- - 🌲 &nbsp;Baz
25
+ ## `useResponsive()`
29
26
 
30
- ## Quick Setup
27
+ The one breakpoint composable for every gluhar project. It replaces the project-level `useMobile` copies.
31
28
 
32
- 1. Add `my-module` dependency to your project
29
+ **For behaviour only** — event handlers, GSAP/ScrollTrigger setup, measuring. Responsive layout is Tailwind variants in the template; never put a width flag in `v-if` / `:class`.
33
30
 
34
- ```bash
35
- # Using pnpm
36
- pnpm add -D my-module
37
-
38
- # Using yarn
39
- yarn add --dev my-module
31
+ ```ts
32
+ const { isMobileDevice, mounted, mobile, tablet, desktop, touch, up, down, between } =
33
+ useResponsive();
40
34
 
41
- # Using npm
42
- npm install --save-dev my-module
35
+ const nav = up("nav"); // any --breakpoint-* the project declares
36
+ onMounted(() => {
37
+ if (desktop.value) initParallax(); // already correct here
38
+ });
43
39
  ```
44
40
 
45
- 2. Add `my-module` to the `modules` section of `nuxt.config.ts`
46
-
47
- ```js
48
- export default defineNuxtConfig({
49
- modules: [
50
- 'my-module'
51
- ]
52
- })
53
- ```
41
+ | | Source | On the server | Safe in markup |
42
+ |---|---|---|---|
43
+ | `isMobileDevice` | user agent | real value | yes — but it's a device guess, not a width |
44
+ | `mobile` `tablet` `desktop` `wide` `touch` `tabletExact` `desktopExact` `up()` `down()` `between()` | viewport width | `false` | no |
54
45
 
55
- That's it! You can now use My Module in your Nuxt app ✨
46
+ - Width flags stay `false` through the first hydration so server and client renders agree, then switch on in the composable's own `onMounted` — before any `onMounted` you register after calling it. Components created after hydration get live values immediately.
47
+ - Breakpoints are the project's `--breakpoint-*` CSS values (read by `@gluhar/theme`), defaulting to `tablet` 768 / `desktop` 1200 / `wide` 2000.
48
+ - `>=` comparisons, like CSS `min-width`: `tablet` and `tablet:` agree at every width, including exactly 768.
49
+ - `mobile` = below `tablet`; `touch` = below `desktop` (a width, not touch capability).
56
50
 
57
51
  ## Development
58
52
 
package/dist/module.d.mts CHANGED
@@ -4,4 +4,5 @@ interface GluharCoreOptions {
4
4
  }
5
5
  declare const _default: _nuxt_schema.NuxtModule<GluharCoreOptions, GluharCoreOptions, false>;
6
6
 
7
- export { type GluharCoreOptions, _default as default };
7
+ export { _default as default };
8
+ export type { GluharCoreOptions };
package/dist/module.d.ts CHANGED
@@ -4,4 +4,5 @@ interface GluharCoreOptions {
4
4
  }
5
5
  declare const _default: _nuxt_schema.NuxtModule<GluharCoreOptions, GluharCoreOptions, false>;
6
6
 
7
- export { type GluharCoreOptions, _default as default };
7
+ export { _default as default };
8
+ export type { GluharCoreOptions };
package/dist/module.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@gluhar/core",
3
3
  "configKey": "gluharCore",
4
- "version": "4.2.0",
4
+ "version": "6.0.0-next.0",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "0.8.3",
7
7
  "unbuild": "2.0.0"
package/dist/module.mjs CHANGED
@@ -7,9 +7,9 @@ const module = defineNuxtModule({
7
7
  },
8
8
  // Default configuration options of the Nuxt module
9
9
  defaults: {},
10
- setup(options, nuxt) {
10
+ async setup() {
11
11
  const { resolve } = createResolver(import.meta.url);
12
- installModule("@nuxtjs/device");
12
+ await installModule("@nuxtjs/device");
13
13
  addImportsDir(resolve("runtime/composables"));
14
14
  addImportsDir(resolve("runtime/utils"));
15
15
  }
@@ -1,9 +1,42 @@
1
+ import { type ComputedRef } from "vue";
2
+ import type { GluharBreakpoints } from "../types.js";
3
+ /**
4
+ * Viewport state for BEHAVIOUR — event handlers, GSAP/ScrollTrigger setup,
5
+ * imperative measurement. Never for layout: responsive layout is Tailwind
6
+ * variants in the template (`tablet:`, `desktop:`), which the server renders
7
+ * for every width with no JS and no hydration risk.
8
+ *
9
+ * Two kinds of signal, kept apart on purpose:
10
+ *
11
+ * - `isMobileDevice` — user-agent based, identical on server and client. The
12
+ * only one safe in `v-if` / `:class` / props. A device guess, not a width.
13
+ *
14
+ * - Width flags (`mobile`, `tablet`, `desktop`, `up("nav")`, …) — `false` on
15
+ * the server and through the first hydration, so the server render and the
16
+ * first client render agree by construction. They switch on in this
17
+ * composable's own `onMounted`, which Vue runs before any `onMounted` you
18
+ * register later in the same setup. Components created after hydration
19
+ * (client-side navigation, SPA mode) get live values from the first render.
20
+ *
21
+ * Breakpoints come from the project's CSS (`--breakpoint-*` in the main
22
+ * stylesheet's `@theme`, read at build time by @gluhar/theme), so JS and CSS
23
+ * cannot drift. Comparisons are `>=`, matching CSS `min-width`: at exactly the
24
+ * breakpoint width, `tablet:` applies and `tablet` is true.
25
+ */
1
26
  export declare const useResponsive: () => {
2
- mobile: import("vue").ComputedRef<boolean>;
3
- tablet: import("vue").Ref<boolean, boolean>;
4
- tabletExact: import("vue").ComputedRef<boolean>;
5
- desktop: import("vue").Ref<boolean, boolean>;
6
- desktopExact: import("vue").ComputedRef<boolean>;
7
- wide: import("vue").Ref<boolean, boolean>;
8
- touch: import("vue").ComputedRef<boolean>;
27
+ isMobileDevice: boolean;
28
+ /** True once hydrated — for guarding client-only behaviour explicitly. */
29
+ mounted: any;
30
+ breakpoints: GluharBreakpoints;
31
+ up: (name: string) => ComputedRef<any>;
32
+ down: (name: string) => ComputedRef<any>;
33
+ between: (from: string, to: string) => ComputedRef<any>;
34
+ mobile: ComputedRef<any>;
35
+ tablet: ComputedRef<any>;
36
+ tabletExact: ComputedRef<any>;
37
+ desktop: ComputedRef<any>;
38
+ desktopExact: ComputedRef<any>;
39
+ wide: ComputedRef<any>;
40
+ /** Below desktop width. A width, not touch capability. */
41
+ touch: ComputedRef<any>;
9
42
  };
@@ -1,26 +1,36 @@
1
- import { computed } from "#imports";
2
- import { useBreakpoints, breakpointsTailwind } from "@vueuse/core";
3
- import { useDevice } from "#imports";
1
+ import { computed, onMounted, ref } from "vue";
2
+ import { useBreakpoints } from "@vueuse/core";
3
+ import { useAppConfig, useDevice, useNuxtApp } from "#imports";
4
+ const DEFAULT_BREAKPOINTS = { tablet: 768, desktop: 1200, wide: 2e3 };
4
5
  export const useResponsive = () => {
5
- const breakpoints = useBreakpoints({
6
- ...breakpointsTailwind,
7
- tablet: 768,
8
- desktop: 1200,
9
- wide: 2e3
10
- });
11
- const { isMobile } = useDevice();
12
- const mobile = computed(
13
- () => breakpoints.smaller("tablet").value || isMobile
14
- );
15
- const tablet = breakpoints.greater("tablet");
16
- const tabletExact = computed(
17
- () => breakpoints.isInBetween("tablet", "desktop")
18
- );
19
- const desktop = breakpoints.greater("desktop");
20
- const desktopExact = computed(
21
- () => breakpoints.isInBetween("desktop", "wide")
22
- );
23
- const touch = computed(() => !desktop.value);
24
- const wide = breakpoints.greater("wide");
25
- return { mobile, tablet, tabletExact, desktop, desktopExact, wide, touch };
6
+ const nuxtApp = useNuxtApp();
7
+ const breakpoints = {
8
+ ...DEFAULT_BREAKPOINTS,
9
+ ...useAppConfig().gluhar?.breakpoints
10
+ };
11
+ const viewport = useBreakpoints(breakpoints);
12
+ const { isMobile: isMobileDevice } = useDevice();
13
+ const mounted = ref(import.meta.client && !nuxtApp.isHydrating);
14
+ if (!mounted.value) onMounted(() => mounted.value = true);
15
+ const gate = (source) => computed(() => mounted.value && source.value);
16
+ const up = (name) => gate(viewport.greaterOrEqual(name));
17
+ const down = (name) => gate(viewport.smaller(name));
18
+ const between = (from, to) => gate(viewport.between(from, to));
19
+ return {
20
+ isMobileDevice,
21
+ /** True once hydrated — for guarding client-only behaviour explicitly. */
22
+ mounted,
23
+ breakpoints,
24
+ up,
25
+ down,
26
+ between,
27
+ mobile: down("tablet"),
28
+ tablet: up("tablet"),
29
+ tabletExact: between("tablet", "desktop"),
30
+ desktop: up("desktop"),
31
+ desktopExact: between("desktop", "wide"),
32
+ wide: up("wide"),
33
+ /** Below desktop width. A width, not touch capability. */
34
+ touch: down("desktop")
35
+ };
26
36
  };
@@ -1,3 +1,18 @@
1
1
  import { breakpointsTailwind } from "@vueuse/core";
2
2
  export type NonNegativeInteger<T extends number> = number extends T ? never : `${T}` extends `-${string}` | `${string}.${string}` ? never : T;
3
+ /** Tailwind's default screens (sm…2xl), still present in v4. */
3
4
  export type Breakpoint = keyof typeof breakpointsTailwind;
5
+ /** Breakpoint name -> min-width in px, as declared in the project's CSS. */
6
+ export type GluharBreakpoints = Record<string, number>;
7
+ declare module "nuxt/schema" {
8
+ interface AppConfigInput {
9
+ gluhar?: {
10
+ breakpoints?: GluharBreakpoints;
11
+ };
12
+ }
13
+ interface AppConfig {
14
+ gluhar?: {
15
+ breakpoints?: GluharBreakpoints;
16
+ };
17
+ }
18
+ }
@@ -1,4 +1,11 @@
1
+ /**
2
+ * Evaluates a project config file (e.g. `config/i18n.config.ts`) at build time.
3
+ *
4
+ * Relative paths resolve against `rootDir` — pass `nuxt.options.rootDir`.
5
+ * `process.cwd()` is only the fallback: it is wrong whenever Nuxt is started
6
+ * from another directory (`nuxi dev playground`, monorepos, `--cwd`).
7
+ */
1
8
  export declare const readModule: <T extends Record<string, any>>(path: string | undefined, options?: {
2
- cwd?: boolean;
9
+ rootDir?: string;
3
10
  default?: boolean;
4
11
  }) => Promise<T>;
@@ -1,13 +1,13 @@
1
1
  import { readFileSync } from "fs";
2
- import { join } from "path";
2
+ import { isAbsolute, join } from "path";
3
3
  import { evalModule } from "mlly";
4
- export const readModule = async (path, options = { cwd: true }) => {
4
+ export const readModule = async (path, options = {}) => {
5
5
  if (!path) throw new Error("[@gluhar/core] readModule | No path provided.");
6
- const p = options?.cwd ? join(process.cwd(), path) : path;
6
+ const p = isAbsolute(path) ? path : join(options.rootDir ?? process.cwd(), path);
7
7
  const file = readFileSync(p)?.toString();
8
8
  if (!file)
9
9
  throw new Error("[@gluhar/core] readModule | File not found on path: " + p);
10
10
  const sanitizedFile = file.replaceAll(" as const", "");
11
11
  const module = await evalModule(sanitizedFile);
12
- return options?.default ? module.default : module;
12
+ return options.default ? module.default : module;
13
13
  };
package/dist/types.d.mts CHANGED
@@ -4,4 +4,4 @@ import type { default as Module } from './module.js'
4
4
 
5
5
  export type ModuleOptions = typeof Module extends NuxtModule<infer O> ? Partial<O> : Record<string, any>
6
6
 
7
- export { type GluharCoreOptions, default } from './module.js'
7
+ export { default } from './module.js'
package/dist/types.d.ts CHANGED
@@ -4,4 +4,4 @@ import type { default as Module } from './module'
4
4
 
5
5
  export type ModuleOptions = typeof Module extends NuxtModule<infer O> ? Partial<O> : Record<string, any>
6
6
 
7
- export { type GluharCoreOptions, default } from './module'
7
+ export { default } from './module'
package/package.json CHANGED
@@ -1,13 +1,17 @@
1
1
  {
2
2
  "name": "@gluhar/core",
3
- "version": "4.2.0",
3
+ "version": "6.0.0-next.0",
4
4
  "description": "Gluhar Core packages.",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "git+https://github.com/gluharco/gluhar.git",
8
+ "directory": "packages/core"
9
+ },
5
10
  "type": "module",
6
11
  "exports": {
7
12
  ".": {
8
- "types": "./dist/types.d.ts",
9
- "import": "./dist/module.mjs",
10
- "require": "./dist/module.cjs"
13
+ "types": "./dist/types.d.mts",
14
+ "import": "./dist/module.mjs"
11
15
  },
12
16
  "./utils/*": {
13
17
  "types": "./dist/runtime/utils/*.d.ts",
@@ -17,8 +21,8 @@
17
21
  "types": "./dist/runtime/types/index.d.ts"
18
22
  }
19
23
  },
20
- "main": "./dist/module.cjs",
21
- "types": "./dist/types.d.ts",
24
+ "main": "./dist/module.mjs",
25
+ "types": "./dist/types.d.mts",
22
26
  "files": [
23
27
  "dist",
24
28
  "CHANGELOG.md"
@@ -26,6 +30,7 @@
26
30
  "scripts": {
27
31
  "gluhar": "npm run gluhar --prefix ../.. -- --cwd=$PWD",
28
32
  "build": "npm run gluhar build -- -t module",
33
+ "prepack": "npm run build",
29
34
  "dev": "nuxi dev playground",
30
35
  "dev:build": "nuxi build playground",
31
36
  "dev:prepare": "nuxt-module-build build && nuxt-module-build prepare && nuxi prepare playground",
@@ -34,23 +39,26 @@
34
39
  "test:watch": "vitest watch"
35
40
  },
36
41
  "dependencies": {
37
- "@nuxt/kit": "^3.13.1",
42
+ "@nuxt/kit": "^4.5.0",
38
43
  "@nuxtjs/device": "^3.2.2",
39
- "@vueuse/core": "^11.0.3",
44
+ "@vueuse/core": "^14.1.0",
40
45
  "mlly": "^1.7.1"
41
46
  },
42
47
  "devDependencies": {
43
48
  "@nuxt/devtools": "latest",
44
- "@nuxt/eslint-config": "^0.5.6",
45
- "@nuxt/module-builder": "^0.8.3",
46
- "@nuxt/schema": "^3.13.1",
47
- "@nuxt/test-utils": "^3.14.1",
49
+ "@nuxt/eslint-config": "^1.9.0",
50
+ "@nuxt/module-builder": "^1.0.3",
51
+ "@nuxt/schema": "^4.5.0",
52
+ "@nuxt/test-utils": "^3.19.2",
48
53
  "@types/node": "^22.5.4",
49
54
  "changelogen": "^0.5.5",
50
55
  "copyfiles": "^2.4.1",
51
56
  "eslint": "^9.10.0",
52
- "nuxt": "^3.13.1",
53
- "vitest": "^2.0.5"
57
+ "nuxt": "^4.5.0",
58
+ "vitest": "^3.2.4"
59
+ },
60
+ "peerDependencies": {
61
+ "nuxt": "^4.5.0"
54
62
  },
55
- "gitHead": "10ebf6500c71e1beeceece4de90467b26aa67a21"
63
+ "gitHead": "8773cd99c43cbe6a7dc415e50af8b13a7b8d4daf"
56
64
  }