kmcom-nuxt-layers 1.7.4 → 1.7.5
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/layers/content/app/components/Blog/Article.vue +1 -1
- package/layers/core/app/components/DiagnosticsPage.vue +6 -0
- package/layers/motion/app/components/Motion/ScrollProgress.vue +4 -1
- package/layers/shader/app/components/Shader/Pipeline.client.vue +8 -10
- package/layers/theme/app/composables/useAccentColor.ts +9 -3
- package/layers/theme/app/composables/useThemeContrast.ts +5 -2
- package/layers/theme/app/composables/useThemeMotion.ts +5 -2
- package/layers/theme/app/composables/useThemeTransparency.ts +4 -2
- package/layers/theme/app/plugins/theme.client.ts +21 -4
- package/layers/ui/app/components/Mast/Footer.vue +5 -1
- package/layers/ui/app/components/Progress/Circular.vue +1 -1
- package/package.json +1 -1
|
@@ -12,7 +12,7 @@ const asBlog = (item: unknown) => item as BlogCollectionItem
|
|
|
12
12
|
<NuxtContentDetail collection="blog" :slug not-found-message="Blog post not found">
|
|
13
13
|
<template #headline="{ item }">
|
|
14
14
|
<div class="flex items-center gap-3 text-sm text-muted">
|
|
15
|
-
<time v-if="asBlog(item).date">{{ new Date(asBlog(item).date).toLocaleDateString() }}</time>
|
|
15
|
+
<time v-if="asBlog(item).date">{{ new Date(asBlog(item).date).toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' }) }}</time>
|
|
16
16
|
<UBadge v-if="asBlog(item).badge" color="primary" variant="subtle">
|
|
17
17
|
{{ asBlog(item).badge }}
|
|
18
18
|
</UBadge>
|
|
@@ -27,6 +27,7 @@ useSeoMeta({
|
|
|
27
27
|
<template>
|
|
28
28
|
<div class="min-h-screen p-8">
|
|
29
29
|
<UContainer>
|
|
30
|
+
<ClientOnly>
|
|
30
31
|
<div class="space-y-8">
|
|
31
32
|
<div class="text-center">
|
|
32
33
|
<h1 class="text-4xl font-bold mb-2">Core Layer Diagnostics</h1>
|
|
@@ -215,6 +216,11 @@ useSeoMeta({
|
|
|
215
216
|
</div>
|
|
216
217
|
</UCard>
|
|
217
218
|
</div>
|
|
219
|
+
|
|
220
|
+
<template #fallback>
|
|
221
|
+
<div class="text-center text-muted py-16">Loading diagnostics…</div>
|
|
222
|
+
</template>
|
|
223
|
+
</ClientOnly>
|
|
218
224
|
</UContainer>
|
|
219
225
|
</div>
|
|
220
226
|
</template>
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
+
defineOptions({ inheritAttrs: false })
|
|
3
|
+
|
|
2
4
|
const props = withDefaults(
|
|
3
5
|
defineProps<{
|
|
4
6
|
/**
|
|
@@ -48,7 +50,7 @@ const percentage = computed(() => Math.round(progress.value * 100))
|
|
|
48
50
|
|
|
49
51
|
<template>
|
|
50
52
|
<!-- Linear Progress -->
|
|
51
|
-
<div v-if="type === 'linear'" class="motion-scroll-progress-linear">
|
|
53
|
+
<div v-if="type === 'linear'" class="motion-scroll-progress-linear" v-bind="$attrs">
|
|
52
54
|
<div
|
|
53
55
|
class="w-full rounded-full overflow-hidden"
|
|
54
56
|
:style="{ height: `${height}px`, backgroundColor: bgColor }"
|
|
@@ -69,6 +71,7 @@ const percentage = computed(() => Math.round(progress.value * 100))
|
|
|
69
71
|
<!-- Circular Progress -->
|
|
70
72
|
<ProgressCircular
|
|
71
73
|
v-else
|
|
74
|
+
v-bind="$attrs"
|
|
72
75
|
:progress="progress"
|
|
73
76
|
:size
|
|
74
77
|
:stroke-width="strokeWidth"
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// @ts-nocheck - TSL types are complex
|
|
3
3
|
import { DoubleSide } from 'three'
|
|
4
4
|
import { MeshBasicNodeMaterial } from 'three/webgpu'
|
|
5
|
-
import { float, positionLocal, uv, vec3, vec4 } from 'three/tsl'
|
|
5
|
+
import { float, Fn, positionLocal, uv, vec3, vec4 } from 'three/tsl'
|
|
6
6
|
|
|
7
7
|
const { transparent = false } = defineProps<{
|
|
8
8
|
transparent?: boolean
|
|
@@ -28,18 +28,16 @@ watch(
|
|
|
28
28
|
const vertex = pipeline.stagesFor('vertex')
|
|
29
29
|
const ray = pipeline.stagesFor('ray')
|
|
30
30
|
|
|
31
|
-
//
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
pipeline.rayNode.value = ray.reduce((node, { fn }) => fn(node), screenRayNode)
|
|
36
|
-
|
|
37
|
-
// Fragment chain
|
|
38
|
-
material.colorNode = fragment.reduce((node, { fn }) => fn(node), vec4(0, 0, 0, 1))
|
|
31
|
+
// Wrap each chain in Fn() so TSL .toVar()/.assign() ops (used by noise functions
|
|
32
|
+
// like simplexNoise3d) have a valid stack context. Without this, TSL throws
|
|
33
|
+
// "No stack defined for assign operation".
|
|
34
|
+
pipeline.uvNode.value = Fn(() => uvStages.reduce((node, { fn }) => fn(node), uv()))()
|
|
35
|
+
pipeline.rayNode.value = Fn(() => ray.reduce((node, { fn }) => fn(node), screenRayNode))()
|
|
36
|
+
material.colorNode = Fn(() => fragment.reduce((node, { fn }) => fn(node), vec4(0, 0, 0, 1)))()
|
|
39
37
|
material.needsUpdate = true
|
|
40
38
|
|
|
41
39
|
if (vertex.length > 0) {
|
|
42
|
-
material.positionNode = vertex.reduce((node, { fn }) => fn(node), positionLocal)
|
|
40
|
+
material.positionNode = Fn(() => vertex.reduce((node, { fn }) => fn(node), positionLocal))()
|
|
43
41
|
material.needsUpdate = true
|
|
44
42
|
}
|
|
45
43
|
},
|
|
@@ -1,17 +1,23 @@
|
|
|
1
|
-
import { createSharedComposable
|
|
1
|
+
import { createSharedComposable } from '@vueuse/core'
|
|
2
2
|
import type { AccentColor } from '#layers/theme/app/types/theme'
|
|
3
3
|
|
|
4
4
|
export const useAccentColor = createSharedComposable(() => {
|
|
5
5
|
const appConfig = useAppConfig()
|
|
6
|
-
|
|
7
6
|
const defaultAccent = (appConfig.themeLayer?.defaultAccent ?? 'blue') as AccentColor
|
|
8
|
-
|
|
7
|
+
|
|
8
|
+
// useState ensures server value is serialized into the Nuxt payload and reused
|
|
9
|
+
// during hydration, avoiding a mismatch with localStorage (client-only).
|
|
10
|
+
// theme.client.ts reads localStorage after hydration and calls setAccent.
|
|
11
|
+
const accent = useState<AccentColor>('theme-accent', () => defaultAccent)
|
|
9
12
|
|
|
10
13
|
const pageAccent = ref<AccentColor | null>(null)
|
|
11
14
|
const activeAccent = computed<AccentColor>(() => pageAccent.value ?? accent.value)
|
|
12
15
|
|
|
13
16
|
function setAccent(color: AccentColor) {
|
|
14
17
|
accent.value = color
|
|
18
|
+
if (import.meta.client) {
|
|
19
|
+
localStorage.setItem('theme-colour', color)
|
|
20
|
+
}
|
|
15
21
|
}
|
|
16
22
|
|
|
17
23
|
function setPageAccent(color: AccentColor | null) {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { createSharedComposable,
|
|
1
|
+
import { createSharedComposable, usePreferredContrast } from '@vueuse/core'
|
|
2
2
|
import type { PreferenceOverride } from '#layers/theme/app/types/theme'
|
|
3
3
|
|
|
4
4
|
export const useThemeContrast = createSharedComposable(() => {
|
|
5
|
-
const contrastOverride =
|
|
5
|
+
const contrastOverride = useState<PreferenceOverride>('theme-contrast', () => 'system')
|
|
6
6
|
const systemContrast = usePreferredContrast()
|
|
7
7
|
|
|
8
8
|
const effectiveHighContrast = computed(() => {
|
|
@@ -13,6 +13,9 @@ export const useThemeContrast = createSharedComposable(() => {
|
|
|
13
13
|
|
|
14
14
|
function setContrastOverride(value: PreferenceOverride) {
|
|
15
15
|
contrastOverride.value = value
|
|
16
|
+
if (import.meta.client) {
|
|
17
|
+
localStorage.setItem('theme-contrast', value)
|
|
18
|
+
}
|
|
16
19
|
}
|
|
17
20
|
|
|
18
21
|
return {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { createSharedComposable,
|
|
1
|
+
import { createSharedComposable, usePreferredReducedMotion } from '@vueuse/core'
|
|
2
2
|
import type { PreferenceOverride } from '#layers/theme/app/types/theme'
|
|
3
3
|
|
|
4
4
|
export const useThemeMotion = createSharedComposable(() => {
|
|
5
|
-
const motionOverride =
|
|
5
|
+
const motionOverride = useState<PreferenceOverride>('theme-motion', () => 'system')
|
|
6
6
|
const systemMotion = usePreferredReducedMotion()
|
|
7
7
|
|
|
8
8
|
const effectiveReducedMotion = computed(() => {
|
|
@@ -13,6 +13,9 @@ export const useThemeMotion = createSharedComposable(() => {
|
|
|
13
13
|
|
|
14
14
|
function setMotionOverride(value: PreferenceOverride) {
|
|
15
15
|
motionOverride.value = value
|
|
16
|
+
if (import.meta.client) {
|
|
17
|
+
localStorage.setItem('theme-motion', value)
|
|
18
|
+
}
|
|
16
19
|
}
|
|
17
20
|
|
|
18
21
|
return {
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createSharedComposable,
|
|
3
|
-
useLocalStorage,
|
|
4
3
|
usePreferredReducedTransparency,
|
|
5
4
|
} from '@vueuse/core'
|
|
6
5
|
import type { PreferenceOverride } from '#layers/theme/app/types/theme'
|
|
7
6
|
|
|
8
7
|
export const useThemeTransparency = createSharedComposable(() => {
|
|
9
|
-
const transparencyOverride =
|
|
8
|
+
const transparencyOverride = useState<PreferenceOverride>('theme-transparency', () => 'system')
|
|
10
9
|
const systemTransparency = usePreferredReducedTransparency()
|
|
11
10
|
|
|
12
11
|
const effectiveReducedTransparency = computed(() => {
|
|
@@ -17,6 +16,9 @@ export const useThemeTransparency = createSharedComposable(() => {
|
|
|
17
16
|
|
|
18
17
|
function setTransparencyOverride(value: PreferenceOverride) {
|
|
19
18
|
transparencyOverride.value = value
|
|
19
|
+
if (import.meta.client) {
|
|
20
|
+
localStorage.setItem('theme-transparency', value)
|
|
21
|
+
}
|
|
20
22
|
}
|
|
21
23
|
|
|
22
24
|
return {
|
|
@@ -1,8 +1,25 @@
|
|
|
1
|
+
import type { AccentColor, PreferenceOverride } from '#layers/theme/app/types/theme'
|
|
2
|
+
|
|
1
3
|
export default defineNuxtPlugin(() => {
|
|
2
|
-
const { activeAccent } = useAccentColor()
|
|
3
|
-
const { effectiveHighContrast } = useThemeContrast()
|
|
4
|
-
const { effectiveReducedMotion } = useThemeMotion()
|
|
5
|
-
const { effectiveReducedTransparency } = useThemeTransparency()
|
|
4
|
+
const { setAccent, activeAccent } = useAccentColor()
|
|
5
|
+
const { setContrastOverride, effectiveHighContrast } = useThemeContrast()
|
|
6
|
+
const { setMotionOverride, effectiveReducedMotion } = useThemeMotion()
|
|
7
|
+
const { setTransparencyOverride, effectiveReducedTransparency } = useThemeTransparency()
|
|
8
|
+
|
|
9
|
+
// Restore persisted preferences. useState defaults were serialized from the
|
|
10
|
+
// server, so hydration matches. We update to the stored values here, after
|
|
11
|
+
// hydration completes, to avoid any mismatch.
|
|
12
|
+
const storedAccent = localStorage.getItem('theme-colour')
|
|
13
|
+
if (storedAccent) setAccent(storedAccent as AccentColor)
|
|
14
|
+
|
|
15
|
+
const storedContrast = localStorage.getItem('theme-contrast')
|
|
16
|
+
if (storedContrast) setContrastOverride(storedContrast as PreferenceOverride)
|
|
17
|
+
|
|
18
|
+
const storedMotion = localStorage.getItem('theme-motion')
|
|
19
|
+
if (storedMotion) setMotionOverride(storedMotion as PreferenceOverride)
|
|
20
|
+
|
|
21
|
+
const storedTransparency = localStorage.getItem('theme-transparency')
|
|
22
|
+
if (storedTransparency) setTransparencyOverride(storedTransparency as PreferenceOverride)
|
|
6
23
|
|
|
7
24
|
watch(activeAccent, (color) => {
|
|
8
25
|
document.documentElement.setAttribute('data-theme-colour', color)
|
|
@@ -1,9 +1,13 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
const year = useState('footer-year', () => new Date().getFullYear())
|
|
3
|
+
</script>
|
|
4
|
+
|
|
1
5
|
<template>
|
|
2
6
|
<UFooter>
|
|
3
7
|
<template #bottom>
|
|
4
8
|
<div class="pt-10">
|
|
5
9
|
<p class="text-center text-sm text-neutral-300">
|
|
6
|
-
Copyright © {{
|
|
10
|
+
Copyright © {{ year }}
|
|
7
11
|
</p>
|
|
8
12
|
</div>
|
|
9
13
|
</template>
|
|
@@ -20,7 +20,7 @@ const radius = computed(() => (size - strokeWidth) / 2)
|
|
|
20
20
|
const circumference = computed(() => 2 * Math.PI * radius.value)
|
|
21
21
|
const strokeDasharray = computed(() => `${progress * circumference.value} ${circumference.value}`)
|
|
22
22
|
|
|
23
|
-
const gradientId = `progress-circular-gradient-${
|
|
23
|
+
const gradientId = `progress-circular-gradient-${useId()}`
|
|
24
24
|
</script>
|
|
25
25
|
|
|
26
26
|
<template>
|
package/package.json
CHANGED