kmcom-nuxt-layers 6.3.0 â 6.5.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/docs/MIGRATION-v6.4.0.md +215 -0
- package/docs/MIGRATION-v6.5.0.md +87 -0
- package/docs/MIGRATION.md +3 -1
- package/docs/USAGE.md +35 -10
- package/docs/adr/0003-modular-step-system-replaces-fluid-clamps.md +16 -0
- package/docs/component-levels.md +1 -0
- package/docs/layout-shift-causes.md +18 -0
- package/layers/content/app/components/content/Figure.vue +1 -1
- package/layers/content/app/composables/useContentSection.ts +1 -1
- package/layers/core/app/app.config.ts +9 -5
- package/layers/core/app/app.vue +2 -1
- package/layers/core/app/components/LoadingScreen.vue +35 -21
- package/layers/core/app/composables/useCache.ts +2 -2
- package/layers/core/app/composables/useErrorLog.ts +5 -5
- package/layers/core/app/composables/useLoading.ts +19 -10
- package/layers/core/app/plugins/loading.client.ts +11 -3
- package/layers/core/app/types/loading.ts +10 -3
- package/layers/core/app/utils/loading.ts +7 -16
- package/layers/core/nuxt.config.ts +14 -14
- package/layers/core/server/utils/app-config.ts +17 -0
- package/layers/data/auth/app/middleware/auth.ts +1 -1
- package/layers/data/forms/app/components/Form/Contact.vue +1 -1
- package/layers/data/forms/server/api/contact.post.ts +16 -8
- package/layers/data/forms/server/api/forms/status.get.ts +3 -0
- package/layers/data/forms/{app â shared}/utils/contact-schema.ts +2 -1
- package/layers/data/metadata/server/api/metadata/search.get.ts +27 -7
- package/layers/data/metadata/server/api/metadata/status.get.ts +4 -1
- package/layers/data/metadata/server/api/metadata/sync.post.ts +13 -3
- package/layers/data/metadata/server/utils/metadata/resolve-record.ts +3 -3
- package/layers/delivery/feeds/nuxt.config.ts +6 -1
- package/layers/delivery/feeds/package.json +2 -2
- package/layers/delivery/feeds/server/routes/feed/demo.get.ts +2 -2
- package/layers/delivery/feeds/server/utils/content-adapter.ts +1 -1
- package/layers/delivery/feeds/server/utils/feed-service.ts +2 -2
- package/layers/design-system/theming/app/plugins/theme.client.ts +7 -0
- package/layers/design-system/theming/nuxt.config.ts +0 -2
- package/layers/design-system/typography/app/assets/css/typography.css +41 -5
- package/layers/design-system/visual/app/components/HoverDimGroup.vue +11 -3
- package/layers/motion/scroll/app/plugins/locomotive-scroll.client.ts +3 -2
- package/layers/render/canvas/nuxt.config.ts +0 -4
- package/layers/render/shader/nuxt.config.ts +0 -4
- package/layers/structure/navigation/app/app.config.ts +37 -14
- package/layers/structure/navigation/app/components/Mast/Nav.vue +14 -13
- package/layers/structure/navigation/app/components/Mast/NavModal.vue +83 -28
- package/layers/structure/navigation/app/components/Site/Logo.vue +35 -0
- package/layers/structure/navigation/app/components/Site/Title.vue +3 -3
- package/layers/structure/navigation/app/composables/mastNav.ts +38 -13
- package/layers/structure/navigation/app/types/nav.ts +34 -0
- package/layers/structure/routing/app/middleware/01.maintenance.global.ts +4 -1
- package/layers/structure/routing/app/middleware/02.governance.global.ts +1 -1
- package/layers/structure/routing/nuxt.config.ts +19 -12
- package/layers/structure/routing/server/api/feature-flags.get.ts +4 -2
- package/package.json +35 -5
- package/docs/FALLOW-COMPLEXITY-DUPLICATION-AUDIT.md +0 -64
- package/docs/IMPROVE-AUDIT-README.md +0 -29
- package/docs/IMPROVE-AUDIT-RESULTS.md +0 -51
- package/docs/IMPROVE-DEEP-AUDIT-RESULTS.md +0 -81
- package/docs/fallow-refactor/apps-debug.md +0 -27
- package/docs/fallow-refactor/apps-playground.md +0 -46
- package/docs/fallow-refactor/apps-visual-identity.md +0 -41
- package/docs/fallow-refactor/layers-animations.md +0 -34
- package/docs/fallow-refactor/layers-canvas.md +0 -32
- package/docs/fallow-refactor/layers-content.md +0 -33
- package/docs/fallow-refactor/layers-core.md +0 -39
- package/docs/fallow-refactor/layers-feeds.md +0 -39
- package/docs/fallow-refactor/layers-forms.md +0 -30
- package/docs/fallow-refactor/layers-layout.md +0 -42
- package/docs/fallow-refactor/layers-mailer.md +0 -32
- package/docs/fallow-refactor/layers-motion.md +0 -27
- package/docs/fallow-refactor/layers-navigation.md +0 -31
- package/docs/fallow-refactor/layers-page-transitions.md +0 -30
- package/docs/fallow-refactor/layers-routing.md +0 -33
- package/docs/fallow-refactor/layers-scripts.md +0 -35
- package/docs/fallow-refactor/layers-scroll.md +0 -38
- package/docs/fallow-refactor/layers-seo.md +0 -32
- package/docs/fallow-refactor/layers-shader.md +0 -53
- package/docs/fallow-refactor/layers-theme.md +0 -33
- package/docs/fallow-refactor/layers-transitions.md +0 -27
- package/docs/fallow-refactor/layers-typography.md +0 -29
- package/docs/fallow-refactor/layers-visual.md +0 -34
- package/docs/superpowers/plans/2026-09-03-layout-layer-fluid-rebuild.md +0 -1850
- package/docs/superpowers/plans/2026-09-03-vue-element-system.md +0 -1019
- package/docs/superpowers/plans/2026-09-04-grid-layer.md +0 -977
- package/docs/superpowers/specs/2026-08-30-layer-taxonomy-tier-0-1-design.md +0 -533
- package/docs/superpowers/specs/2026-08-30-layer-taxonomy-tier-2-design.md +0 -154
- package/docs/superpowers/specs/2026-08-30-layer-taxonomy-tier-3-design.md +0 -109
- package/docs/superpowers/specs/2026-08-30-layer-taxonomy-tier-4-design.md +0 -124
- package/docs/superpowers/specs/2026-08-30-layer-taxonomy-tier-5-design.md +0 -121
- package/docs/superpowers/specs/2026-08-30-layer-taxonomy-tier-6-design.md +0 -77
- package/docs/superpowers/specs/2026-08-30-layer-taxonomy-tier-7-design.md +0 -73
- package/docs/superpowers/specs/2026-08-31-swiss-grid-layer-design.md +0 -190
- package/docs/superpowers/specs/2026-09-03-layout-layer-fluid-rebuild-design.md +0 -312
- package/docs/superpowers/specs/2026-09-03-vue-element-system-design.md +0 -342
- package/docs/superpowers/specs/2026-09-04-grid-layer-design.md +0 -236
- package/layers/content/app/utils/galleryImageMeta.test.ts +0 -30
- package/layers/core/app/components/Element.test.ts +0 -90
- package/layers/core/app/components/ElementTw.test.ts +0 -64
- package/layers/core/app/composables/useLayoutAttrs.test.ts +0 -77
- package/layers/core/app/utils/browserInfo.test.ts +0 -81
- package/layers/core/app/utils/featureClasses.test.ts +0 -101
- package/layers/core/app/utils/loading.test.ts +0 -48
- package/layers/core/app/utils/resolveElementStyle.test.ts +0 -99
- package/layers/core/app/utils/scrollGuardClamp.test.ts +0 -36
- package/layers/core/app/utils/styleToTailwindClasses.test.ts +0 -31
- package/layers/data/forms/app/utils/contact-schema.test.ts +0 -75
- package/layers/data/forms/app/utils/fieldProps.test.ts +0 -141
- package/layers/data/metadata/providers/comicvine/server/utils/metadata-comicvine/normalise.test.ts +0 -201
- package/layers/data/metadata/providers/google-books/server/utils/metadata-google-books/client.test.ts +0 -76
- package/layers/data/metadata/providers/google-books/server/utils/metadata-google-books/normalise.test.ts +0 -144
- package/layers/data/metadata/providers/openlibrary/server/utils/metadata-openlibrary/client.test.ts +0 -25
- package/layers/data/metadata/providers/openlibrary/server/utils/metadata-openlibrary/normalise.test.ts +0 -144
- package/layers/data/metadata/providers/themoviedb/server/utils/metadata-tmdb/client.test.ts +0 -29
- package/layers/data/metadata/providers/themoviedb/server/utils/metadata-tmdb/normalise.test.ts +0 -162
- package/layers/data/metadata/server/utils/metadata/cache.test.ts +0 -39
- package/layers/data/metadata/server/utils/metadata/provider-fetch.test.ts +0 -27
- package/layers/data/metadata/server/utils/metadata/provider-registry.test.ts +0 -54
- package/layers/data/metadata/server/utils/metadata/resolve-record.test.ts +0 -87
- package/layers/data/metadata/server/utils/metadata/search.test.ts +0 -204
- package/layers/delivery/feeds/server/utils/content-adapter.test.ts +0 -67
- package/layers/delivery/feeds/server/utils/feed-author.test.ts +0 -71
- package/layers/delivery/feeds/server/utils/feed-config.test.ts +0 -184
- package/layers/delivery/feeds/server/utils/formats.test.ts +0 -71
- package/layers/delivery/seo/app/utils/seoConfig.test.ts +0 -58
- package/layers/design-system/theming/server/utils/accent-css.test.ts +0 -71
- package/layers/design-system/theming/server/utils/fouc-config.test.ts +0 -78
- package/layers/design-system/typography/app/components/Typography/Headline.test.ts +0 -36
- package/layers/design-system/typography/app/components/Typography/index.test.ts +0 -31
- package/layers/design-system/typography/app/composables/color.test.ts +0 -30
- package/layers/design-system/typography/app/composables/typography.test.ts +0 -53
- package/layers/design-system/visual/app/composables/tint.test.ts +0 -22
- package/layers/design-system/visual/app/utils/colorTokens.test.ts +0 -51
- package/layers/design-system/visual/app/utils/gradientStyle.test.ts +0 -75
- package/layers/design-system/visual/app/utils/responsiveSizes.test.ts +0 -71
- package/layers/motion/animations/app/components/Motion/Staggered.test.ts +0 -40
- package/layers/motion/scroll/app/composables/useReducedMotion.test.ts +0 -23
- package/layers/motion/scroll/app/utils/parallax.test.ts +0 -24
- package/layers/structure/grid/app/composables/useGridTracks.test.ts +0 -94
- package/layers/structure/grid/app/types/tracks.test.ts +0 -32
- package/layers/structure/grid/app/utils/placement.test.ts +0 -19
- package/layers/structure/layout/app/utils/gridPlacementStyle.test.ts +0 -269
- package/layers/structure/navigation/app/utils/site.test.ts +0 -39
- package/layers/structure/routing/app/utils/resolveRoute.test.ts +0 -47
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
# Migration Guide â v6.3.x â v6.4.0
|
|
2
|
+
|
|
3
|
+
**Audience:** an AI coding agent (or developer) updating a downstream Nuxt app that consumes these
|
|
4
|
+
layers.
|
|
5
|
+
**What changed:** the duplicate overlay provider is gone, the nav is plain shared state with a
|
|
6
|
+
configurable trigger and modal, a new `SiteLogo` wordmark, and a per-element display type range.
|
|
7
|
+
Background: [`NAV-LOGO-OVERLAY-PLAN.md`](../NAV-LOGO-OVERLAY-PLAN.md).
|
|
8
|
+
|
|
9
|
+
Work top to bottom. Each change has a **Detect** step and an **Apply** step; skip anything Detect
|
|
10
|
+
doesn't find. An app component with the same name as a layer component overrides it, so a change
|
|
11
|
+
to a layer component you've overridden doesn't reach you until you delete the override.
|
|
12
|
+
|
|
13
|
+
## 0. Preconditions
|
|
14
|
+
|
|
15
|
+
- Update the package to `^6.4.0`, reinstall, run `npx nuxt prepare`.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Required changes
|
|
20
|
+
|
|
21
|
+
### 1. Remove your own `<UOverlayProvider />`
|
|
22
|
+
|
|
23
|
+
`UApp` already renders one. A second one made every `useOverlay()`/`createModal` overlay render
|
|
24
|
+
twice (doubled modals, layout shift). `core`'s `app.vue` no longer adds one.
|
|
25
|
+
|
|
26
|
+
**Detect:** grep your `app.vue` and layouts for `<UOverlayProvider`.
|
|
27
|
+
|
|
28
|
+
**Apply:** delete it. Keep `<UApp>`.
|
|
29
|
+
|
|
30
|
+
### 2. Render `<MastNavModal />` once in your layout
|
|
31
|
+
|
|
32
|
+
`<MastNav>` is only a trigger now. The modal is rendered once by the layout, and every trigger on
|
|
33
|
+
the page opens that one modal.
|
|
34
|
+
|
|
35
|
+
**Detect:** a layout or page renders `<MastNav` but not `<MastNavModal`.
|
|
36
|
+
|
|
37
|
+
**Apply:**
|
|
38
|
+
|
|
39
|
+
```vue
|
|
40
|
+
<!-- app/layouts/default.vue -->
|
|
41
|
+
<template>
|
|
42
|
+
<MastNav class="fixed top-0 right-0 z-(--z-header)" />
|
|
43
|
+
<MastNavModal />
|
|
44
|
+
<slot />
|
|
45
|
+
</template>
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### 3. `useMastNav()` returns shared state
|
|
49
|
+
|
|
50
|
+
| Before (v6.3) | After (v6.4) |
|
|
51
|
+
| --------------------------------- | --------------------------------------------------- |
|
|
52
|
+
| `{ open(props?), close, patch }` | `{ isOpen, open, close, toggle }` |
|
|
53
|
+
| An overlay created per app | `useState('mast-nav:open')`; `isOpen` is read-only |
|
|
54
|
+
| `open()` called `lockScrolling()` | UModal locks body scroll; `open()` only stops Lenis |
|
|
55
|
+
|
|
56
|
+
**Detect:** grep for `useMastNav().patch`, `patch(` on the nav, or `open({` with props.
|
|
57
|
+
|
|
58
|
+
**Apply:** drop the props (the modal reads `mastNav` config and its own props instead), and read
|
|
59
|
+
`isOpen` where you used to track open state yourself.
|
|
60
|
+
|
|
61
|
+
### 4. `MastNav` is a `UButton`
|
|
62
|
+
|
|
63
|
+
The trigger was a bare `<button>` with a hamburger icon, an `#icon` slot and an inline
|
|
64
|
+
`z-index` (dropdown layer). It's now a `UButton` whose props come from `mastNav.trigger`, and
|
|
65
|
+
attrs on the component win.
|
|
66
|
+
|
|
67
|
+
**Detect:** `<MastNav>` with an `#icon` slot, or a fixed trigger that relied on the old inline
|
|
68
|
+
z-index.
|
|
69
|
+
|
|
70
|
+
**Apply:**
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
// app/app.config.ts
|
|
74
|
+
mastNav: {
|
|
75
|
+
trigger: { label: 'Menu', icon: undefined, variant: 'ghost', size: 'xl' },
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
- Replace `#icon` with `trigger.icon` (or `icon="âĻ"` on the component). The default slot now fills
|
|
80
|
+
the button label.
|
|
81
|
+
- Add `z-(--z-header)` (or your own) to a fixed trigger that has to sit over page content.
|
|
82
|
+
- The default look moved from `p-4` + 28px icon to `UButton` neutral/ghost/xl. Restate `class` if
|
|
83
|
+
you need the old size.
|
|
84
|
+
|
|
85
|
+
### 5. Loading screen is opt-in, and `core` renders it
|
|
86
|
+
|
|
87
|
+
`core`'s `app.vue` now renders `<LoadingScreen />`, so you can delete an `app.vue` that existed only
|
|
88
|
+
to add it. So that every other app doesn't suddenly get a loading screen,
|
|
89
|
+
`coreLayer.loading.enabled` now defaults to **`false`**.
|
|
90
|
+
|
|
91
|
+
**Detect:** your app shows the loading screen (it renders `<LoadingScreen />` or relied on the
|
|
92
|
+
default being on).
|
|
93
|
+
|
|
94
|
+
**Apply:**
|
|
95
|
+
|
|
96
|
+
```ts
|
|
97
|
+
// app/app.config.ts
|
|
98
|
+
coreLayer: { loading: { enabled: true } },
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Then delete your `app.vue` if `<LoadingScreen />` was its only difference from `core`'s.
|
|
102
|
+
|
|
103
|
+
### 6. `mastNav.links` is typed as `MastNavLink[]`
|
|
104
|
+
|
|
105
|
+
Route `params`/`query` values are `string | number` (the app.config type used to allow
|
|
106
|
+
`unknown`).
|
|
107
|
+
|
|
108
|
+
**Detect:** typecheck errors in `app.config.ts` under `mastNav.links`.
|
|
109
|
+
|
|
110
|
+
**Apply:** stringify the offending values.
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## Behaviour changes â check, usually no code
|
|
115
|
+
|
|
116
|
+
### 7. The current page's link is active
|
|
117
|
+
|
|
118
|
+
A link used to be active only when `useState('activeSection')` matched its `id`. It's now also
|
|
119
|
+
active when its `to` resolves to the current route, except on the smooth-scroll index, where every
|
|
120
|
+
link points at `index`. Active links get `aria-current="page"` plus `mastNav.ui.linkActive`. The
|
|
121
|
+
default weight (light â normal) keys off `aria-current`.
|
|
122
|
+
|
|
123
|
+
### 8. No more scrollbar jump when the nav opens
|
|
124
|
+
|
|
125
|
+
With classic (non-overlay) scrollbars the page used to widen by the scrollbar on open, because the
|
|
126
|
+
nav hid `html` overflow before the dialog measured the scrollbar. UModal now owns the lock and pads
|
|
127
|
+
`body`, and Lenis stops after it. `useSmoothScroll().lockScrolling()` is unchanged for other
|
|
128
|
+
callers.
|
|
129
|
+
|
|
130
|
+
### 9. `HoverDimGroup` renders nested fragments
|
|
131
|
+
|
|
132
|
+
Children inside `<template v-for>` around a `<slot>` (fragments two deep) used to be dropped
|
|
133
|
+
silently. They render and dim now. Check any `HoverDimGroup` that rendered fewer children than
|
|
134
|
+
you passed.
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
## New â optional
|
|
139
|
+
|
|
140
|
+
### `MastNavModal` styling and slots
|
|
141
|
+
|
|
142
|
+
The layer keeps the behaviour (dialog, focus trap, Escape, scroll lock, smooth-scroll vs router,
|
|
143
|
+
active link). The look comes from config and slots:
|
|
144
|
+
|
|
145
|
+
```ts
|
|
146
|
+
// app/app.config.ts: each key you set replaces that default string (no class merging)
|
|
147
|
+
mastNav: {
|
|
148
|
+
ui: {
|
|
149
|
+
content: 'bg-default text-default',
|
|
150
|
+
close: 'absolute right-[5vw] top-[3vh]',
|
|
151
|
+
nav: 'flex w-full flex-col items-start gap-2 px-12',
|
|
152
|
+
link: 'cursor-pointer py-3 text-step-4 tracking-wider transition-all',
|
|
153
|
+
linkActive: 'underline',
|
|
154
|
+
},
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
| API | Purpose |
|
|
159
|
+
| ------------------------------------ | --------------------------------------------------------------- |
|
|
160
|
+
| `ui` prop | Per-instance override, merged per key over `mastNav.ui` |
|
|
161
|
+
| `links` prop | Replace `mastNav.links`, e.g. filtered by your page flags |
|
|
162
|
+
| `open` prop / `update:open` | Manual control; omit to follow `useMastNav().isOpen` |
|
|
163
|
+
| `#link="{ link, active, navigate }"` | Replace a link's markup; `navigate()` scrolls/routes and closes |
|
|
164
|
+
| `#close="{ close }"` | Replace the close button |
|
|
165
|
+
| `#header`, `#footer` | Content above/below the links |
|
|
166
|
+
| `hidden: true` on a link | Skip it without a custom `links` prop |
|
|
167
|
+
|
|
168
|
+
### `SiteLogo`
|
|
169
|
+
|
|
170
|
+
`site.title` as a wordmark on the step scale. Replaces the deprecated `SiteTitle`.
|
|
171
|
+
|
|
172
|
+
```vue
|
|
173
|
+
<SiteLogo />
|
|
174
|
+
<!-- stacked, text-step-1 -->
|
|
175
|
+
<SiteLogo size="display" :stacked="false" />
|
|
176
|
+
<!-- one line -->
|
|
177
|
+
<SiteLogo v-slot="{ words }">âĻ</SiteLogo>
|
|
178
|
+
<!-- your own markup -->
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
```ts
|
|
182
|
+
// app/app.config.ts: sizes merge per key
|
|
183
|
+
siteLogo: {
|
|
184
|
+
stacked: true,
|
|
185
|
+
ui: { root: 'font-display leading-none tracking-tight', word: '' },
|
|
186
|
+
sizes: { display: 'text-step-display [--display-from:4] [--display-to:9]' },
|
|
187
|
+
}
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### Per-element display range
|
|
191
|
+
|
|
192
|
+
`text-step-display` and plain `h1` now read `--display-from` (step at â¤360px, default 5) and
|
|
193
|
+
`--display-to` (step at âĨ1280px, default 7) on the element itself:
|
|
194
|
+
|
|
195
|
+
```html
|
|
196
|
+
<span class="text-step-display [--display-from:4] [--display-to:9]">âĻ</span>
|
|
197
|
+
<!-- â42px at 390px, 154px at 1280px -->
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
Defaults are unchanged in Chromium, Firefox and WebKit. If you override `--step-display` on an
|
|
201
|
+
element with your own unlayered CSS, that still works; the two numbers are the simpler way.
|
|
202
|
+
|
|
203
|
+
## Deprecated
|
|
204
|
+
|
|
205
|
+
`SiteTitle` â `<SiteLogo :stacked="false">`. Removal in the next major, alongside `SiteSubtitle`,
|
|
206
|
+
`LinksNamed` and `LinksGroup`.
|
|
207
|
+
|
|
208
|
+
## Verify
|
|
209
|
+
|
|
210
|
+
- [ ] `npx nuxt prepare && npx nuxi typecheck` is clean.
|
|
211
|
+
- [ ] Opening any modal puts exactly one `[role="dialog"]` in the DOM (§1).
|
|
212
|
+
- [ ] The nav trigger opens one modal. Escape and the close button close it, focus returns to the
|
|
213
|
+
trigger, and the page stays where it was (§2â4, §8).
|
|
214
|
+
- [ ] The current page's link is highlighted (§7).
|
|
215
|
+
- [ ] The loading screen shows only where you enabled it (§5).
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# Migration Guide â v6.4.x â v6.5.0
|
|
2
|
+
|
|
3
|
+
**Audience:** an AI coding agent (or developer) updating a downstream Nuxt app that consumes these
|
|
4
|
+
layers.
|
|
5
|
+
**What changed:** layers stop making app-wide choices for you, the published package declares the
|
|
6
|
+
dependencies its layers import, and two diagnostic API routes are now dev-only.
|
|
7
|
+
|
|
8
|
+
Work top to bottom. Each change has a **Detect** step and an **Apply** step; skip anything Detect
|
|
9
|
+
doesn't find.
|
|
10
|
+
|
|
11
|
+
## 0. Preconditions
|
|
12
|
+
|
|
13
|
+
- Update the package to `^6.5.0`, reinstall, run `npx nuxt prepare`.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Required changes
|
|
18
|
+
|
|
19
|
+
### 1. `core` no longer sets `future.compatibilityVersion: 5` or `devtools`
|
|
20
|
+
|
|
21
|
+
Layer config merges into the app, so `core` was opting every consumer into Nuxt 5 behaviour and
|
|
22
|
+
forcing devtools on. Both are now the app's call.
|
|
23
|
+
|
|
24
|
+
**Detect:** your app's `nuxt.config.ts` has no `future.compatibilityVersion`.
|
|
25
|
+
|
|
26
|
+
**Apply:** to keep the behaviour you had on v6.4, add:
|
|
27
|
+
|
|
28
|
+
```ts
|
|
29
|
+
export default defineNuxtConfig({
|
|
30
|
+
future: { compatibilityVersion: 5 },
|
|
31
|
+
devtools: { enabled: true },
|
|
32
|
+
})
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### 2. Install the peers for the layers you use
|
|
36
|
+
|
|
37
|
+
The root package now lists these as optional peer dependencies. They were always imported at
|
|
38
|
+
runtime; previously the app only worked if something else happened to install them.
|
|
39
|
+
|
|
40
|
+
**Detect:** you extend `mailer`/`forms`, `database`, `auth` or `feeds`.
|
|
41
|
+
|
|
42
|
+
**Apply:**
|
|
43
|
+
|
|
44
|
+
| Layer | Install |
|
|
45
|
+
| ------------------ | ------------------------------------------ |
|
|
46
|
+
| `mailer` / `forms` | `resend hookable` (plus `zod` for `forms`) |
|
|
47
|
+
| `database` | `drizzle-orm @neondatabase/serverless` |
|
|
48
|
+
| `auth` | `nuxt-auth-utils` |
|
|
49
|
+
| `feeds` | `feed` |
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## Behaviour changes (no action unless you rely on them)
|
|
54
|
+
|
|
55
|
+
- **`GET /api/forms/status`** returns only `{ configured }` outside dev. `emailFrom`/`emailTo` are
|
|
56
|
+
dev-only because `emailTo` is the private inbox every contact submission goes to.
|
|
57
|
+
- **`GET /api/metadata/status`** returns 404 outside dev. Each call ran a live search on every
|
|
58
|
+
provider, spending real API quota.
|
|
59
|
+
- **`GET /api/metadata/search`** rejects an unknown `mediaType` or a non-positive `limit` with a
|
|
60
|
+
400, and caps `limit` at 50.
|
|
61
|
+
- **`feeds`** now always extends `core` and `content`. The `FEEDS_STANDALONE` env var is gone.
|
|
62
|
+
- **`routing`**: `layerDefaultDeny` now works in npm consumers. Layer pages were never tagged
|
|
63
|
+
before, so a layer page without a `feature` meta will now be denied if you enabled
|
|
64
|
+
`layerDefaultDeny`.
|
|
65
|
+
- **`auth`** middleware redirects to `/auth/github` with `external: true`, so client-side
|
|
66
|
+
navigation reaches the OAuth route instead of the 404 page.
|
|
67
|
+
- **`forms`**: `contactSchema` moved from `app/utils/` to `shared/utils/`. Import it from
|
|
68
|
+
`#layers/forms/shared/utils/contact-schema` if you imported it by path.
|
|
69
|
+
- **`@nuxt/eslint`** loads only when installed, so apps without it no longer fail outside
|
|
70
|
+
production.
|
|
71
|
+
- **`feeds`** and **`routing`** server routes now read your `app.config.ts` when Nitro
|
|
72
|
+
auto-imports are on. They imported `useAppConfig` from `nitropack/runtime`, which never sees
|
|
73
|
+
it, so feeds were titled "My Site", linked to the request origin (localhost when prerendered)
|
|
74
|
+
and ignored `feedsLayer`, and `/api/feature-flags` returned `{}`. They now use `site`,
|
|
75
|
+
`feedsLayer` and `routingLayer.features`. With `experimental.nitroAutoImports: false` (the
|
|
76
|
+
Nuxt 5 default) Nuxt can't evaluate app.config.ts in Nitro, so they keep the old defaults;
|
|
77
|
+
set `nitroAutoImports: true` to get your values. Layer server code reads app config through
|
|
78
|
+
core's `readServerAppConfig()`.
|
|
79
|
+
- **`core` loading screen** stops being a timer. `minDuration` defaults to `0` (was `1500`) and
|
|
80
|
+
`maxDuration` to `1500` (was `3000`). A new `showAfter` (default `400` ms) keeps the screen
|
|
81
|
+
hidden on fast loads. The percentage moves at real milestones (hydrated, fonts ready, page
|
|
82
|
+
loaded) instead of a simulated trickle; `trickleProgress` is gone. Under
|
|
83
|
+
`prefers-reduced-motion` the screen is hidden and loading ends on mount. `useLoading()` adds
|
|
84
|
+
`finishNow()` and `skipped`. Set `minDuration: 1500` to keep the old pacing.
|
|
85
|
+
- **`theming`**: the Reduce Motion switch also ends the boot loading screen.
|
|
86
|
+
- **`forms`**: `contactSchema` accepts any non-blank name (was 3+ characters), so short names
|
|
87
|
+
like Li or Ng get through.
|
package/docs/MIGRATION.md
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
> **Upgrading
|
|
1
|
+
> **Upgrading?** v6.4 â v6.5: [`MIGRATION-v6.5.0.md`](./MIGRATION-v6.5.0.md) ¡ v6.3 â v6.4:
|
|
2
|
+
> [`MIGRATION-v6.4.0.md`](./MIGRATION-v6.4.0.md) ¡ v6.2 â v6.3:
|
|
3
|
+
> [`MIGRATION-v6.3.0.md`](./MIGRATION-v6.3.0.md). The guides below cover older versions.
|
|
2
4
|
|
|
3
5
|
# Agent Migration Guide â Update an app to nuxt-layers v2.2.x
|
|
4
6
|
|
package/docs/USAGE.md
CHANGED
|
@@ -93,12 +93,21 @@ npm install gsap locomotive-scroll
|
|
|
93
93
|
# canvas / shader
|
|
94
94
|
npm install three @tresjs/nuxt @tresjs/core @tresjs/cientos @tresjs/post-processing
|
|
95
95
|
|
|
96
|
-
# forms
|
|
97
|
-
npm install zod
|
|
96
|
+
# forms / mailer
|
|
97
|
+
npm install zod resend hookable
|
|
98
|
+
|
|
99
|
+
# database
|
|
100
|
+
npm install drizzle-orm @neondatabase/serverless
|
|
101
|
+
|
|
102
|
+
# auth
|
|
103
|
+
npm install nuxt-auth-utils
|
|
98
104
|
|
|
99
105
|
# content
|
|
100
106
|
npm install @nuxt/content better-sqlite3
|
|
101
107
|
|
|
108
|
+
# feeds
|
|
109
|
+
npm install feed
|
|
110
|
+
|
|
102
111
|
# scripts (baseline-status badge)
|
|
103
112
|
npm install @nuxt/scripts baseline-status
|
|
104
113
|
```
|
|
@@ -188,14 +197,30 @@ export default defineAppConfig({
|
|
|
188
197
|
|
|
189
198
|
### Available Config Namespaces
|
|
190
199
|
|
|
191
|
-
| Namespace | Layer | Key Options
|
|
192
|
-
| ----------------------- | ------- |
|
|
193
|
-
| `coreLayer.loading` | core | `enabled`, `minDuration`, `maxDuration`, `label`, `background`, `textColor` |
|
|
194
|
-
| `coreLayer.scrollGuard` | core | `enabled`, `strict`, `excludeSelectors`, `debug`
|
|
195
|
-
| `coreLayer.errors` | core | `logToConsole`, `logToExternal`, `externalUrl`
|
|
196
|
-
| `coreLayer.notFound` | core | `icon`, `title`, `description`, `showHomeButton`, `actions`, `suggestions`
|
|
197
|
-
| `motion` | motion | `gsapScrollTrigger`, `lenis.*` (duration, smoothWheel, smoothTouch, etc.)
|
|
198
|
-
| `themeLayer` | theming | `accents`, `defaultAccent`
|
|
200
|
+
| Namespace | Layer | Key Options |
|
|
201
|
+
| ----------------------- | ------- | ---------------------------------------------------------------------------------------- |
|
|
202
|
+
| `coreLayer.loading` | core | `enabled`, `minDuration`, `maxDuration`, `showAfter`, `label`, `background`, `textColor` |
|
|
203
|
+
| `coreLayer.scrollGuard` | core | `enabled`, `strict`, `excludeSelectors`, `debug` |
|
|
204
|
+
| `coreLayer.errors` | core | `logToConsole`, `logToExternal`, `externalUrl` |
|
|
205
|
+
| `coreLayer.notFound` | core | `icon`, `title`, `description`, `showHomeButton`, `actions`, `suggestions` |
|
|
206
|
+
| `motion` | motion | `gsapScrollTrigger`, `lenis.*` (duration, smoothWheel, smoothTouch, etc.) |
|
|
207
|
+
| `themeLayer` | theming | `accents`, `defaultAccent` |
|
|
208
|
+
|
|
209
|
+
### Overriding Layer Components
|
|
210
|
+
|
|
211
|
+
Layer components are auto-imported by file name, and the app always wins: a component in your
|
|
212
|
+
app with the same name replaces the layer's copy everywhere, including where other layers render
|
|
213
|
+
it. These unprefixed names are the ones most likely to collide by accident:
|
|
214
|
+
|
|
215
|
+
| Component | Layer | Notes |
|
|
216
|
+
| -------------------------------------- | ------ | -------------------------------------------------------------- |
|
|
217
|
+
| `LoadingScreen` | core | Meant to be overridable (custom boot screen) |
|
|
218
|
+
| `ElementTw` | core | Rendered by typography and visual â shadowing changes them too |
|
|
219
|
+
| `Element`, `AppContainer` | core | Primitives; pick another name for app components |
|
|
220
|
+
| `ErrorBoundary` | core | Pick another name for app boundaries |
|
|
221
|
+
| `HStack`, `VStack`, `ZStack`, `Spacer` | layout | Primitives; pick another name for app components |
|
|
222
|
+
|
|
223
|
+
Override one on purpose by creating the same-named file in your `app/components/`.
|
|
199
224
|
|
|
200
225
|
## Optional Modules
|
|
201
226
|
|
|
@@ -56,3 +56,19 @@ the element using it.
|
|
|
56
56
|
steps (h1 4, h2 3, h3 2, h4 1) and `p`/`li` keep the body's 1.7 leading. Otherwise the
|
|
57
57
|
24px copy sat at or above Nuxt UI's fixed `text-2xl`/`text-xl` prose headings. This needs
|
|
58
58
|
`text-step-*` registered as a font-size group in tailwind-merge (`ui.tv.twMergeConfig`).
|
|
59
|
+
|
|
60
|
+
## Amendment (v6.4.0): per-element display range
|
|
61
|
+
|
|
62
|
+
The display step was computed once at `:root`, so retuning it for one element (a hero wordmark)
|
|
63
|
+
meant restating the whole `tan(atan2(clamp(âĻ)))` expression. Its two ends are now plain numbers,
|
|
64
|
+
`--display-from: 5` and `--display-to: 7`, and `text-step-display` and the `h1` base rule
|
|
65
|
+
recompute `--step-display` from them on the element. `class="[--display-from:4]
|
|
66
|
+
[--display-to:9]"` retunes one element without touching the formula.
|
|
67
|
+
|
|
68
|
+
- At the defaults the middle term reduces to the old `4.2176rem + 3.4784vw¡k`; computed sizes
|
|
69
|
+
are identical (Chromium, Firefox and WebKit, 360â1600px).
|
|
70
|
+
- The accessibility rules above are unchanged: the `tan(atan2(1rem, 16px))` factor keeps the
|
|
71
|
+
exponent independent of root font size, and the clamp keeps its rem terms.
|
|
72
|
+
- `--step-display` still exists at `:root` (TypographyHeadline levels and theming read it). An
|
|
73
|
+
unlayered `--step-display` override on an element still beats the utility's, so existing
|
|
74
|
+
overrides keep working.
|
package/docs/component-levels.md
CHANGED
|
@@ -112,5 +112,6 @@ left out.
|
|
|
112
112
|
| `layers/structure/navigation/app/components/Mast/Header.vue` | organism |
|
|
113
113
|
| `layers/structure/navigation/app/components/Mast/Nav.vue` | organism |
|
|
114
114
|
| `layers/structure/navigation/app/components/Mast/NavModal.vue` | organism |
|
|
115
|
+
| `layers/structure/navigation/app/components/Site/Logo.vue` | atom |
|
|
115
116
|
| `layers/structure/navigation/app/components/Site/Subtitle.vue` | atom |
|
|
116
117
|
| `layers/structure/navigation/app/components/Site/Title.vue` | atom |
|
|
@@ -86,9 +86,27 @@ Catalogue of layout shift (CLS) issues found and fixed in this monorepo, with ro
|
|
|
86
86
|
|
|
87
87
|
---
|
|
88
88
|
|
|
89
|
+
## 11. Duplicate `UOverlayProvider` â `core/app.vue`
|
|
90
|
+
|
|
91
|
+
**Cause:** `core`'s `app.vue` rendered `<UOverlayProvider />` inside `<UApp>`, which already renders one. Both read the same `useOverlay()` state, so every `useOverlay().create()` overlay (including the old `createModal`-based nav) rendered twice: a doubled modal and a layout shift on open.
|
|
92
|
+
|
|
93
|
+
**Fix:** Delete the extra provider (v6.4.0). `UApp` owns overlays and toasts.
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## 12. Nav modal hid the scrollbar before Reka measured it â `useMastNav`
|
|
98
|
+
|
|
99
|
+
**Cause:** `useMastNav().open()` called `lockScrolling()` (`html { overflow: hidden }`) before the modal opened. Reka's body scroll lock then measured a 0px scrollbar and added no padding, so with classic (non-overlay) scrollbars the page widened by the scrollbar width on open.
|
|
100
|
+
|
|
101
|
+
**Fix:** Let UModal/Reka own the overflow lock (it pads `body` for the scrollbar) and stop Lenis on the next tick, after Reka has measured (v6.4.0). A stopped Lenis sets `overflow: clip` on `html`, so the order matters.
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
89
105
|
## Key Rules Going Forward
|
|
90
106
|
|
|
91
107
|
- Never use `@layer` / `@utility` in CSS files loaded via `css: []` â use plain selectors instead.
|
|
92
108
|
- Never use runtime `resolveComponent` to conditionally render components in Nuxt 4 â components are compile-time, not runtime-registered.
|
|
93
109
|
- Use `useState` (not `useLocalStorage`) for any value rendered during SSR that might differ between server and client.
|
|
94
110
|
- Keep accessibility-only elements (`aria-live` regions, announcers) outside CSS grid containers to prevent them participating in grid flow.
|
|
111
|
+
- Never add a `<UOverlayProvider />` next to `<UApp>` â `UApp` renders one.
|
|
112
|
+
- When a dialog locks scroll, don't hide `html` overflow first: the lock has to measure the scrollbar to compensate for it.
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
|
|
17
17
|
<template>
|
|
18
18
|
<figure class="my-6">
|
|
19
|
-
<img :src :alt :width :height class="w-full rounded-lg" />
|
|
19
|
+
<img :src :alt :width :height loading="lazy" decoding="async" class="w-full rounded-lg" />
|
|
20
20
|
<figcaption v-if="caption" class="text-muted mt-2 text-center text-sm italic">
|
|
21
21
|
{{ caption }}
|
|
22
22
|
</figcaption>
|
|
@@ -4,6 +4,6 @@ import type { ContentLayerConfig } from '../types/content'
|
|
|
4
4
|
export function useContentSection(section: keyof NonNullable<ContentLayerConfig['sections']>) {
|
|
5
5
|
const contentLayer = useAppConfig().contentLayer as ContentLayerConfig | undefined
|
|
6
6
|
if (contentLayer?.sections?.[section] === false) {
|
|
7
|
-
throw createError({
|
|
7
|
+
throw createError({ status: 404, statusText: 'Not Found' })
|
|
8
8
|
}
|
|
9
9
|
}
|
|
@@ -31,12 +31,15 @@ export default defineAppConfig({
|
|
|
31
31
|
|
|
32
32
|
// Loading screen configuration (initial app load only)
|
|
33
33
|
loading: {
|
|
34
|
-
//
|
|
35
|
-
enabled:
|
|
36
|
-
// Minimum time (ms, from navigation start) the count
|
|
37
|
-
|
|
34
|
+
// Opt in: app.vue renders LoadingScreen for every app, so it stays off unless enabled
|
|
35
|
+
enabled: false,
|
|
36
|
+
// Minimum time (ms, from navigation start) before the count may reach 100 - 0 finishes
|
|
37
|
+
// as soon as the work does; raise it only for a deliberate brand moment
|
|
38
|
+
minDuration: 0,
|
|
38
39
|
// Longest wait (ms, after mount) for fonts and images before finishing anyway
|
|
39
|
-
maxDuration:
|
|
40
|
+
maxDuration: 1500,
|
|
41
|
+
// Delay (ms, from first paint) before the screen fades in - faster loads never see it
|
|
42
|
+
showAfter: 400,
|
|
40
43
|
// Small caption above the counter - empty string hides it
|
|
41
44
|
label: 'Loading',
|
|
42
45
|
// Background color
|
|
@@ -76,6 +79,7 @@ declare module '@nuxt/schema' {
|
|
|
76
79
|
enabled?: boolean
|
|
77
80
|
minDuration?: number
|
|
78
81
|
maxDuration?: number
|
|
82
|
+
showAfter?: number
|
|
79
83
|
label?: string
|
|
80
84
|
background?: string
|
|
81
85
|
textColor?: string
|
package/layers/core/app/app.vue
CHANGED
|
@@ -5,16 +5,18 @@
|
|
|
5
5
|
// Beat on "100" so the finished count registers before the exit starts
|
|
6
6
|
const HOLD_MS = 350
|
|
7
7
|
|
|
8
|
-
const { enabled, label, background, textColor, zIndex } = resolveLoadingConfig(
|
|
8
|
+
const { enabled, label, background, textColor, zIndex, showAfter } = resolveLoadingConfig(
|
|
9
9
|
useAppConfig().coreLayer
|
|
10
10
|
)
|
|
11
11
|
|
|
12
|
-
const { isLoading, progress } = useLoading()
|
|
12
|
+
const { isLoading, progress, skipped } = useLoading()
|
|
13
13
|
// Written only here; everything else reads it through useLoading().screen
|
|
14
14
|
const screen = useState<LoadingScreenState>('core:loading:screen', () => 'hidden')
|
|
15
15
|
|
|
16
16
|
const visible = ref(enabled)
|
|
17
17
|
if (enabled) screen.value = 'visible'
|
|
18
|
+
// Leave without the beat or the exit animation: skipped, or done before it ever faded in
|
|
19
|
+
const instant = ref(false)
|
|
18
20
|
|
|
19
21
|
const displayProgress = computed(() => Math.floor(progress.value))
|
|
20
22
|
|
|
@@ -35,22 +37,31 @@
|
|
|
35
37
|
isLoading,
|
|
36
38
|
(loading) => {
|
|
37
39
|
if (loading || !visible.value) return
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
instant.value = skipped.value || performance.now() < showAfter
|
|
41
|
+
setTimeout(
|
|
42
|
+
() => {
|
|
43
|
+
visible.value = false
|
|
44
|
+
screen.value = 'leaving'
|
|
45
|
+
},
|
|
46
|
+
instant.value ? 0 : HOLD_MS
|
|
47
|
+
)
|
|
42
48
|
},
|
|
43
49
|
{ immediate: true }
|
|
44
50
|
)
|
|
45
51
|
</script>
|
|
46
52
|
|
|
47
53
|
<template>
|
|
48
|
-
<Transition name="loading-screen" @after-leave="screen = 'hidden'">
|
|
54
|
+
<Transition name="loading-screen" :css="!instant" @after-leave="screen = 'hidden'">
|
|
49
55
|
<div
|
|
50
56
|
v-if="visible"
|
|
51
57
|
id="loading-screen"
|
|
52
58
|
class="loading-screen"
|
|
53
|
-
:style="{
|
|
59
|
+
:style="{
|
|
60
|
+
backgroundColor: background,
|
|
61
|
+
color: textColor,
|
|
62
|
+
zIndex,
|
|
63
|
+
'--loading-show-after': `${showAfter}ms`,
|
|
64
|
+
}"
|
|
54
65
|
>
|
|
55
66
|
<div
|
|
56
67
|
class="loading-content"
|
|
@@ -76,6 +87,10 @@
|
|
|
76
87
|
position: fixed;
|
|
77
88
|
inset: 0;
|
|
78
89
|
container-type: size;
|
|
90
|
+
|
|
91
|
+
/* Fast loads never see it: hidden and click-through until showAfter has passed.
|
|
92
|
+
`backwards`, not `both` - a held end state would block the exit transition below */
|
|
93
|
+
animation: loading-show 0.2s ease var(--loading-show-after, 0ms) backwards;
|
|
79
94
|
}
|
|
80
95
|
|
|
81
96
|
.loading-content {
|
|
@@ -94,7 +109,8 @@
|
|
|
94
109
|
sans-serif;
|
|
95
110
|
|
|
96
111
|
/* `backwards`, not `both` - a held end state would block the exit transition below */
|
|
97
|
-
animation: loading-enter 0.9s cubic-bezier(0.16, 1, 0.3, 1)
|
|
112
|
+
animation: loading-enter 0.9s cubic-bezier(0.16, 1, 0.3, 1)
|
|
113
|
+
calc(var(--loading-show-after, 0ms) + 0.1s) backwards;
|
|
98
114
|
}
|
|
99
115
|
|
|
100
116
|
.loading-label {
|
|
@@ -141,6 +157,13 @@
|
|
|
141
157
|
}
|
|
142
158
|
}
|
|
143
159
|
|
|
160
|
+
@keyframes loading-show {
|
|
161
|
+
from {
|
|
162
|
+
visibility: hidden;
|
|
163
|
+
opacity: 0;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
144
167
|
@keyframes loading-enter {
|
|
145
168
|
from {
|
|
146
169
|
transform: translateY(0.75rem);
|
|
@@ -173,19 +196,10 @@
|
|
|
173
196
|
opacity: 0;
|
|
174
197
|
}
|
|
175
198
|
|
|
199
|
+
/* No screen under reduced motion - the plugin ends loading on mount (finishNow) */
|
|
176
200
|
@media (prefers-reduced-motion: reduce) {
|
|
177
|
-
.loading-
|
|
178
|
-
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
.loading-screen-leave-to .loading-content {
|
|
182
|
-
transform: none;
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
@keyframes loading-fade-in {
|
|
187
|
-
from {
|
|
188
|
-
opacity: 0;
|
|
201
|
+
.loading-screen {
|
|
202
|
+
display: none;
|
|
189
203
|
}
|
|
190
204
|
}
|
|
191
205
|
</style>
|
|
@@ -78,11 +78,11 @@ export function useCache() {
|
|
|
78
78
|
try {
|
|
79
79
|
const cacheNames = await caches.keys()
|
|
80
80
|
await Promise.all(cacheNames.map((name) => caches.delete(name)))
|
|
81
|
-
if (
|
|
81
|
+
if (import.meta.dev) {
|
|
82
82
|
console.log('[useCache] All caches cleared')
|
|
83
83
|
}
|
|
84
84
|
} catch (error) {
|
|
85
|
-
if (
|
|
85
|
+
if (import.meta.dev) {
|
|
86
86
|
console.error('[useCache] Failed to clear cache:', error)
|
|
87
87
|
}
|
|
88
88
|
}
|
|
@@ -73,7 +73,7 @@ export function useErrorLog() {
|
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
// Console logging (development only by default)
|
|
76
|
-
if (config.value.logToConsole && import.meta.dev
|
|
76
|
+
if (config.value.logToConsole && import.meta.dev) {
|
|
77
77
|
console.group('đ´ Error Logged')
|
|
78
78
|
|
|
79
79
|
console.error('Error:', errorObj)
|
|
@@ -96,7 +96,7 @@ export function useErrorLog() {
|
|
|
96
96
|
const { externalUrl, externalToken } = config.value
|
|
97
97
|
|
|
98
98
|
if (!externalUrl) {
|
|
99
|
-
if (
|
|
99
|
+
if (import.meta.dev) {
|
|
100
100
|
console.warn('Error logging enabled but no externalUrl configured')
|
|
101
101
|
}
|
|
102
102
|
return
|
|
@@ -120,7 +120,7 @@ export function useErrorLog() {
|
|
|
120
120
|
})
|
|
121
121
|
} catch (loggingError) {
|
|
122
122
|
// Don't let logging errors crash the app
|
|
123
|
-
if (import.meta.dev
|
|
123
|
+
if (import.meta.dev) {
|
|
124
124
|
console.error('Failed to send error to external service:', loggingError)
|
|
125
125
|
}
|
|
126
126
|
}
|
|
@@ -130,7 +130,7 @@ export function useErrorLog() {
|
|
|
130
130
|
* Log a warning (non-critical error)
|
|
131
131
|
*/
|
|
132
132
|
const logWarning = (message: string, context?: ErrorContext) => {
|
|
133
|
-
if (config.value.logToConsole && import.meta.dev
|
|
133
|
+
if (config.value.logToConsole && import.meta.dev) {
|
|
134
134
|
console.warn('â ī¸ Warning:', message, context)
|
|
135
135
|
}
|
|
136
136
|
}
|
|
@@ -139,7 +139,7 @@ export function useErrorLog() {
|
|
|
139
139
|
* Log info (for debugging)
|
|
140
140
|
*/
|
|
141
141
|
const logInfo = (message: string, data?: unknown) => {
|
|
142
|
-
if (config.value.logToConsole && import.meta.dev
|
|
142
|
+
if (config.value.logToConsole && import.meta.dev) {
|
|
143
143
|
console.log('âšī¸ Info:', message, data)
|
|
144
144
|
}
|
|
145
145
|
}
|