kmcom-nuxt-layers 2.2.12 → 2.2.13
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/FEEDS.md +1 -2
- package/layers/animations/app/composables/useMagneticElement.ts +11 -9
- package/layers/animations/app/composables/useTiltEffect.ts +11 -9
- package/layers/animations/app/utils/pointerMotion.ts +31 -0
- package/layers/canvas/app/components/ShaderCanvas.vue +2 -2
- package/layers/content/app/composables/useCollectionItems.ts +28 -0
- package/layers/content/app/composables/useGalleryItems.ts +8 -14
- package/layers/content/app/composables/usePortfolioItems.ts +10 -18
- package/layers/core/app/composables/useBrowser.ts +9 -82
- package/layers/core/app/composables/useFeatures.ts +3 -27
- package/layers/core/app/plugins/init.ts +157 -135
- package/layers/core/app/utils/browserInfo.ts +115 -0
- package/layers/core/app/utils/featureClasses.ts +40 -0
- package/layers/core/app/utils/helpers.test.ts +51 -0
- package/layers/feeds/app/components/Feeds/Index.vue +1 -1
- package/layers/feeds/app/components/Feeds/RouteCard.vue +3 -9
- package/layers/feeds/app/utils/feed-catalog.ts +9 -4
- package/layers/feeds/nuxt.config.ts +0 -1
- package/layers/feeds/server/utils/content-adapter.test.ts +68 -0
- package/layers/feeds/server/utils/content-adapter.ts +2 -22
- package/layers/feeds/server/utils/feed-author.ts +32 -0
- package/layers/feeds/server/utils/feed-config.ts +88 -0
- package/layers/feeds/server/utils/feed-service.ts +11 -30
- package/layers/feeds/server/utils/feed-xml.ts +26 -0
- package/layers/feeds/server/utils/formats/rss.ts +10 -15
- package/layers/feeds/server/utils/formats.test.ts +71 -0
- package/layers/forms/app/components/Form/Field.vue +42 -30
- package/layers/forms/app/utils/fieldProps.ts +65 -0
- package/layers/layout/app/components/Layout/Grid/Item.vue +29 -146
- package/layers/layout/app/utils/gridPlacementStyle.ts +195 -0
- package/layers/mailer/app/types/mailer.ts +7 -25
- package/layers/mailer/server/utils/email.ts +28 -13
- package/layers/mailer/server/utils/hooks.ts +1 -20
- package/layers/navigation/app/composables/useSite.ts +2 -9
- package/layers/navigation/app/utils/site.ts +26 -0
- package/layers/routing/app/utils/resolveRoute.test.ts +47 -0
- package/layers/routing/app/utils/resolveRoute.ts +19 -10
- package/layers/scripts/app/composables/useAnalytics.ts +8 -41
- package/layers/scripts/app/composables/useGtm.ts +6 -13
- package/layers/scripts/app/utils/scriptClients.ts +70 -0
- package/layers/scroll/app/composables/useSmoothScroll.ts +9 -43
- package/layers/scroll/app/utils/scroll.ts +103 -0
- package/layers/seo/app/composables/useSeoConfig.ts +3 -9
- package/layers/seo/app/utils/seoConfig.ts +38 -0
- package/layers/shader/app/components/Material/AmbientAurora.client.vue +11 -33
- package/layers/shader/app/components/Material/AmbientFlow.client.vue +10 -37
- package/layers/shader/app/components/Material/AmbientGradientMesh.client.vue +10 -37
- package/layers/shader/app/components/Material/AmbientNebula.client.vue +12 -37
- package/layers/shader/app/components/Material/AmbientOcean.client.vue +9 -33
- package/layers/shader/app/components/Material/Gradient.client.vue +25 -46
- package/layers/shader/app/components/Material/Image.client.vue +10 -55
- package/layers/shader/app/components/Material/Node.client.vue +18 -5
- package/layers/shader/app/components/Material/Noise.client.vue +9 -43
- package/layers/shader/app/components/Preset/ThemeBubble.client.vue +2 -1
- package/layers/shader/app/components/Preset/ThemeFlow.client.vue +2 -1
- package/layers/shader/app/components/Preset/ThemeGradient.client.vue +2 -1
- package/layers/shader/app/components/Preset/ThemeLavaLamp.client.vue +2 -1
- package/layers/shader/app/components/Preset/ThemePlasma.client.vue +2 -1
- package/layers/shader/app/components/Preset/ThemeWave.client.vue +2 -1
- package/layers/shader/app/components/Shader/Background.client.vue +44 -24
- package/layers/shader/app/composables/useAmbientMaterials.ts +5 -1
- package/layers/shader/app/composables/useShader.ts +38 -23
- package/layers/shader/app/composables/useShaderGraph.ts +11 -6
- package/layers/shader/app/composables/useShaderMixBlend.ts +4 -4
- package/layers/shader/app/composables/useShaderRuntime.ts +0 -1
- package/layers/shader/app/composables/useShaderVec2.ts +2 -4
- package/layers/shader/app/composables/useThemePreset.ts +34 -8
- package/layers/shader/app/composables/useUniformWatchers.ts +15 -0
- package/layers/shader/app/composables/useUniforms.ts +0 -1
- package/layers/shader/app/shaders/common/blend.ts +4 -4
- package/layers/shader/app/shaders/common/effects.ts +38 -21
- package/layers/shader/app/shaders/common/grain.ts +46 -49
- package/layers/shader/app/shaders/common/lighting.ts +17 -15
- package/layers/shader/app/shaders/common/math.ts +2 -4
- package/layers/shader/app/shaders/common/nodes.ts +17 -0
- package/layers/shader/app/shaders/common/palette.ts +21 -11
- package/layers/shader/app/shaders/common/patterns.ts +25 -14
- package/layers/shader/app/shaders/common/shapes.ts +97 -88
- package/layers/shader/app/shaders/common/uv.ts +33 -34
- package/layers/shader/app/shaders/createMaterial.ts +92 -78
- package/layers/shader/app/shaders/layers/paperShading.ts +22 -10
- package/layers/shader/app/shaders/layers/shaderGradient.ts +46 -21
- package/layers/shader/app/utils/tsl/tween.ts +2 -4
- package/layers/shader/package.json +5 -1
- package/layers/theme/app/components/ThemePicker/Menu.vue +3 -25
- package/layers/theme/app/composables/useThemePreferenceModels.ts +39 -0
- package/layers/theme/server/plugins/theme-fouc.ts +1 -92
- package/layers/theme/server/utils/accent-css.ts +75 -0
- package/layers/typography/app/composables/typography.ts +3 -7
- package/layers/visual/app/composables/accent.ts +2 -9
- package/layers/visual/app/composables/gradient.ts +33 -46
- package/layers/visual/app/composables/picture.ts +2 -79
- package/layers/visual/app/utils/colorTokens.ts +23 -0
- package/layers/visual/app/utils/gradientStyle.ts +41 -0
- package/layers/visual/app/utils/responsiveSizes.ts +49 -0
- package/package.json +15 -4
- package/layers/feeds/app/utils/feed-catalog.test.ts +0 -71
- package/layers/feeds/server/routes/feed/discovery.get.ts +0 -31
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { ComputedRef, CSSProperties, MaybeRefOrGetter } from 'vue'
|
|
2
2
|
|
|
3
|
-
import type { GradientConfig
|
|
3
|
+
import type { GradientConfig } from '../types/gradient'
|
|
4
|
+
import { buildGradientStyle } from '../utils/gradientStyle'
|
|
4
5
|
|
|
5
6
|
const DEFAULT_CONFIG: GradientConfig = {
|
|
6
7
|
shape: 'linear',
|
|
@@ -9,44 +10,41 @@ const DEFAULT_CONFIG: GradientConfig = {
|
|
|
9
10
|
to: { color: 'secondary', shade: 500 },
|
|
10
11
|
}
|
|
11
12
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
'to-bl': 'to bottom left',
|
|
19
|
-
'to-l': 'to left',
|
|
20
|
-
'to-tl': 'to top left',
|
|
13
|
+
function resolveGradientConfig(
|
|
14
|
+
raw: GradientConfig | string,
|
|
15
|
+
override: Partial<GradientConfig> | undefined,
|
|
16
|
+
appConfig: ReturnType<typeof useAppConfig>
|
|
17
|
+
): GradientConfig {
|
|
18
|
+
return mergeGradientOverride(resolveGradientPreset(raw, appConfig), override)
|
|
21
19
|
}
|
|
22
20
|
|
|
23
|
-
function
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
return
|
|
21
|
+
function resolveGradientPreset(
|
|
22
|
+
raw: GradientConfig | string,
|
|
23
|
+
appConfig: ReturnType<typeof useAppConfig>
|
|
24
|
+
): GradientConfig {
|
|
25
|
+
if (typeof raw !== 'string') return raw
|
|
26
|
+
|
|
27
|
+
return resolveGradientPresetByName(raw, getGradientPresetMap(appConfig))
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function getGradientPresetMap(appConfig: ReturnType<typeof useAppConfig>) {
|
|
31
|
+
return (appConfig.uiLayer as Record<string, unknown> | undefined)?.['gradients'] as
|
|
32
|
+
| Record<string, GradientConfig>
|
|
33
|
+
| undefined
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
function
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
36
|
+
function resolveGradientPresetByName(
|
|
37
|
+
name: string,
|
|
38
|
+
presets: Record<string, GradientConfig> | undefined
|
|
39
|
+
) {
|
|
40
|
+
return presets?.[name] ?? DEFAULT_CONFIG
|
|
41
|
+
}
|
|
41
42
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
}
|
|
48
|
-
const dir = DIRECTION_MAP[cfg.direction ?? 'to-br'] ?? 'to bottom right'
|
|
49
|
-
return { backgroundImage: `linear-gradient(${dir}, ${stops})` }
|
|
43
|
+
function mergeGradientOverride(
|
|
44
|
+
resolved: GradientConfig,
|
|
45
|
+
override: Partial<GradientConfig> | undefined
|
|
46
|
+
): GradientConfig {
|
|
47
|
+
return override ? { ...resolved, ...override } : resolved
|
|
50
48
|
}
|
|
51
49
|
|
|
52
50
|
export function useGradient(
|
|
@@ -58,18 +56,7 @@ export function useGradient(
|
|
|
58
56
|
const style = computed((): CSSProperties => {
|
|
59
57
|
const raw = toValue(config)
|
|
60
58
|
const override = overrides ? toValue(overrides) : undefined
|
|
61
|
-
|
|
62
|
-
const presets = (appConfig.uiLayer as Record<string, unknown> | undefined)?.['gradients'] as
|
|
63
|
-
| Record<string, GradientConfig>
|
|
64
|
-
| undefined
|
|
65
|
-
let resolved: GradientConfig =
|
|
66
|
-
typeof raw === 'string' ? (presets?.[raw] ?? DEFAULT_CONFIG) : raw
|
|
67
|
-
|
|
68
|
-
if (override) {
|
|
69
|
-
resolved = { ...resolved, ...override }
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
return buildStyle(resolved)
|
|
59
|
+
return buildGradientStyle(resolveGradientConfig(raw, override, appConfig))
|
|
73
60
|
})
|
|
74
61
|
|
|
75
62
|
return { style }
|
|
@@ -1,12 +1,7 @@
|
|
|
1
1
|
import type { MaybeRefOrGetter } from 'vue'
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
BREAKPOINT_VALUES,
|
|
5
|
-
DEVICE_BREAKPOINT_VALUES,
|
|
6
|
-
PHONE_BREAKPOINT_VALUES,
|
|
7
|
-
TABLET_BREAKPOINT_VALUES,
|
|
8
|
-
} from '../types/breakpoints'
|
|
9
3
|
import type { PictureProps, ResponsiveSizes, UsePictureReturn } from '../types/media'
|
|
4
|
+
import { buildResponsiveSizesQueries } from '../utils/responsiveSizes'
|
|
10
5
|
|
|
11
6
|
/**
|
|
12
7
|
* Convert ResponsiveSizes object to CSS sizes attribute string
|
|
@@ -32,79 +27,7 @@ import type { PictureProps, ResponsiveSizes, UsePictureReturn } from '../types/m
|
|
|
32
27
|
* ```
|
|
33
28
|
*/
|
|
34
29
|
function responsiveSizesToString(sizes: ResponsiveSizes): string {
|
|
35
|
-
|
|
36
|
-
const breakpointEntries: Array<{ key: keyof ResponsiveSizes; value: number }> = []
|
|
37
|
-
|
|
38
|
-
// Add Tailwind breakpoints
|
|
39
|
-
const tailwindBreakpoints: Array<keyof typeof BREAKPOINT_VALUES> = ['2xl', 'xl', 'lg', 'md', 'sm']
|
|
40
|
-
for (const bp of tailwindBreakpoints) {
|
|
41
|
-
if (sizes[bp]) {
|
|
42
|
-
breakpointEntries.push({ key: bp, value: BREAKPOINT_VALUES[bp] })
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
// Add device breakpoints (skip mobile as it's 0px - handled by default)
|
|
47
|
-
const deviceBreakpoints: Array<keyof typeof DEVICE_BREAKPOINT_VALUES> = [
|
|
48
|
-
'wide',
|
|
49
|
-
'desktop',
|
|
50
|
-
'tablet',
|
|
51
|
-
]
|
|
52
|
-
for (const bp of deviceBreakpoints) {
|
|
53
|
-
if (sizes[bp]) {
|
|
54
|
-
breakpointEntries.push({ key: bp, value: DEVICE_BREAKPOINT_VALUES[bp] })
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// Add phone breakpoints
|
|
59
|
-
const phoneBreakpoints: Array<keyof typeof PHONE_BREAKPOINT_VALUES> = [
|
|
60
|
-
'phone-lg',
|
|
61
|
-
'phone-md',
|
|
62
|
-
'phone-sm',
|
|
63
|
-
]
|
|
64
|
-
for (const bp of phoneBreakpoints) {
|
|
65
|
-
if (sizes[bp]) {
|
|
66
|
-
breakpointEntries.push({ key: bp, value: PHONE_BREAKPOINT_VALUES[bp] })
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// Add tablet breakpoints
|
|
71
|
-
const tabletBreakpoints: Array<keyof typeof TABLET_BREAKPOINT_VALUES> = [
|
|
72
|
-
'tablet-lg',
|
|
73
|
-
'tablet-md',
|
|
74
|
-
'tablet-sm',
|
|
75
|
-
]
|
|
76
|
-
for (const bp of tabletBreakpoints) {
|
|
77
|
-
if (sizes[bp]) {
|
|
78
|
-
breakpointEntries.push({ key: bp, value: TABLET_BREAKPOINT_VALUES[bp] })
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// Sort by value descending (largest to smallest)
|
|
83
|
-
breakpointEntries.sort((a, b) => b.value - a.value)
|
|
84
|
-
|
|
85
|
-
// Build media queries
|
|
86
|
-
const mediaQueries: string[] = []
|
|
87
|
-
|
|
88
|
-
// Add orientation breakpoints first (these are feature queries, not min-width)
|
|
89
|
-
if (sizes.landscape) {
|
|
90
|
-
mediaQueries.push(`(orientation: landscape) ${sizes.landscape}`)
|
|
91
|
-
}
|
|
92
|
-
if (sizes.portrait) {
|
|
93
|
-
mediaQueries.push(`(orientation: portrait) ${sizes.portrait}`)
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
// Add min-width breakpoints
|
|
97
|
-
for (const entry of breakpointEntries) {
|
|
98
|
-
const size = sizes[entry.key]
|
|
99
|
-
if (size) {
|
|
100
|
-
mediaQueries.push(`(min-width: ${entry.value}px) ${size}`)
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// Add default size at the end (no media query)
|
|
105
|
-
mediaQueries.push(sizes.default)
|
|
106
|
-
|
|
107
|
-
return mediaQueries.join(', ')
|
|
30
|
+
return buildResponsiveSizesQueries(sizes).join(', ')
|
|
108
31
|
}
|
|
109
32
|
|
|
110
33
|
/**
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
type ResolvableColorToken = {
|
|
2
|
+
color?: string | undefined
|
|
3
|
+
shade?: number | undefined
|
|
4
|
+
opacity?: number | undefined
|
|
5
|
+
customColor?: string | undefined
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
// fallow-ignore-next-line complexity
|
|
9
|
+
export function resolveUiColorToken(token: ResolvableColorToken): string {
|
|
10
|
+
const { color = 'primary', shade = 500, opacity, customColor } = token
|
|
11
|
+
|
|
12
|
+
if (color === 'custom') return customColor ?? 'transparent'
|
|
13
|
+
if (color === 'transparent') return 'transparent'
|
|
14
|
+
if (color === 'white') {
|
|
15
|
+
return opacity !== undefined ? `rgb(255 255 255 / ${opacity / 100})` : '#ffffff'
|
|
16
|
+
}
|
|
17
|
+
if (color === 'black') {
|
|
18
|
+
return opacity !== undefined ? `rgb(0 0 0 / ${opacity / 100})` : '#000000'
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const value = `var(--ui-color-${color}-${shade})`
|
|
22
|
+
return opacity !== undefined ? `color-mix(in srgb, ${value} ${opacity}%, transparent)` : value
|
|
23
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { CSSProperties } from 'vue'
|
|
2
|
+
|
|
3
|
+
import type { GradientConfig, GradientStop } from '../types/gradient'
|
|
4
|
+
import { resolveUiColorToken } from './colorTokens'
|
|
5
|
+
|
|
6
|
+
const DIRECTION_MAP: Record<string, string> = {
|
|
7
|
+
'to-t': 'to top',
|
|
8
|
+
'to-tr': 'to top right',
|
|
9
|
+
'to-r': 'to right',
|
|
10
|
+
'to-br': 'to bottom right',
|
|
11
|
+
'to-b': 'to bottom',
|
|
12
|
+
'to-bl': 'to bottom left',
|
|
13
|
+
'to-l': 'to left',
|
|
14
|
+
'to-tl': 'to top left',
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function resolveGradientColor(stop: GradientStop): string {
|
|
18
|
+
return resolveUiColorToken(stop)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function resolveGradientStops(cfg: GradientConfig) {
|
|
22
|
+
const from = resolveGradientColor(cfg.from)
|
|
23
|
+
const to = resolveGradientColor(cfg.to)
|
|
24
|
+
const via = cfg.via ? resolveGradientColor(cfg.via) : undefined
|
|
25
|
+
return via ? `${from}, ${via}, ${to}` : `${from}, ${to}`
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// fallow-ignore-next-line complexity
|
|
29
|
+
export function buildGradientStyle(cfg: GradientConfig): CSSProperties {
|
|
30
|
+
const stops = resolveGradientStops(cfg)
|
|
31
|
+
|
|
32
|
+
if (cfg.shape === 'radial') {
|
|
33
|
+
return { backgroundImage: `radial-gradient(circle, ${stops})` }
|
|
34
|
+
}
|
|
35
|
+
if (cfg.shape === 'conic') {
|
|
36
|
+
return { backgroundImage: `conic-gradient(${stops})` }
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const direction = DIRECTION_MAP[cfg.direction ?? 'to-br'] ?? 'to bottom right'
|
|
40
|
+
return { backgroundImage: `linear-gradient(${direction}, ${stops})` }
|
|
41
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import {
|
|
2
|
+
BREAKPOINT_VALUES,
|
|
3
|
+
DEVICE_BREAKPOINT_VALUES,
|
|
4
|
+
PHONE_BREAKPOINT_VALUES,
|
|
5
|
+
TABLET_BREAKPOINT_VALUES,
|
|
6
|
+
} from '../types/breakpoints'
|
|
7
|
+
|
|
8
|
+
import type { ResponsiveSizes } from '../types/media'
|
|
9
|
+
|
|
10
|
+
type BreakpointGroup = readonly [keyof ResponsiveSizes, number]
|
|
11
|
+
|
|
12
|
+
const BREAKPOINT_GROUPS = [
|
|
13
|
+
['2xl', BREAKPOINT_VALUES['2xl']],
|
|
14
|
+
['xl', BREAKPOINT_VALUES.xl],
|
|
15
|
+
['lg', BREAKPOINT_VALUES.lg],
|
|
16
|
+
['md', BREAKPOINT_VALUES.md],
|
|
17
|
+
['sm', BREAKPOINT_VALUES.sm],
|
|
18
|
+
['wide', DEVICE_BREAKPOINT_VALUES.wide],
|
|
19
|
+
['desktop', DEVICE_BREAKPOINT_VALUES.desktop],
|
|
20
|
+
['tablet', DEVICE_BREAKPOINT_VALUES.tablet],
|
|
21
|
+
['phone-lg', PHONE_BREAKPOINT_VALUES['phone-lg']],
|
|
22
|
+
['phone-md', PHONE_BREAKPOINT_VALUES['phone-md']],
|
|
23
|
+
['phone-sm', PHONE_BREAKPOINT_VALUES['phone-sm']],
|
|
24
|
+
['tablet-lg', TABLET_BREAKPOINT_VALUES['tablet-lg']],
|
|
25
|
+
['tablet-md', TABLET_BREAKPOINT_VALUES['tablet-md']],
|
|
26
|
+
['tablet-sm', TABLET_BREAKPOINT_VALUES['tablet-sm']],
|
|
27
|
+
] as const satisfies readonly BreakpointGroup[]
|
|
28
|
+
|
|
29
|
+
// fallow-ignore-next-line complexity
|
|
30
|
+
export function buildResponsiveSizesQueries(sizes: ResponsiveSizes): string[] {
|
|
31
|
+
const breakpoints = BREAKPOINT_GROUPS.filter(([key]) => sizes[key])
|
|
32
|
+
.map(([key, value]) => ({ key, value }))
|
|
33
|
+
.sort((a, b) => b.value - a.value)
|
|
34
|
+
|
|
35
|
+
const queries: string[] = []
|
|
36
|
+
|
|
37
|
+
if (sizes.landscape) queries.push(`(orientation: landscape) ${sizes.landscape}`)
|
|
38
|
+
if (sizes.portrait) queries.push(`(orientation: portrait) ${sizes.portrait}`)
|
|
39
|
+
|
|
40
|
+
for (const breakpoint of breakpoints) {
|
|
41
|
+
const size = sizes[breakpoint.key]
|
|
42
|
+
if (size) {
|
|
43
|
+
queries.push(`(min-width: ${breakpoint.value}px) ${size}`)
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
queries.push(sizes.default)
|
|
48
|
+
return queries
|
|
49
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kmcom-nuxt-layers",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "2.2.
|
|
4
|
+
"version": "2.2.13",
|
|
5
5
|
"description": "Composable Nuxt 4 layers for building scalable Vue applications",
|
|
6
6
|
"exports": {
|
|
7
7
|
"./layers/core": "./layers/core/nuxt.config.ts",
|
|
@@ -139,16 +139,20 @@
|
|
|
139
139
|
"@nuxt/eslint": "^1.15.2",
|
|
140
140
|
"@nuxt/fonts": "^0.14.0",
|
|
141
141
|
"@nuxt/image": "^2.0.0",
|
|
142
|
+
"@nuxt/test-utils": "^4.0.3",
|
|
143
|
+
"@playwright/test": "1.61.0",
|
|
142
144
|
"@nuxt/ui": "latest",
|
|
143
145
|
"@nuxtjs/device": "^4.0.0",
|
|
144
146
|
"@oxc-parser/binding-darwin-arm64": "^0.134.0",
|
|
145
147
|
"@perplex-digital/stylelint-config": "^17.4.0",
|
|
146
148
|
"@pinia/nuxt": "^0.11.3",
|
|
149
|
+
"@vitejs/plugin-vue": "^6.0.7",
|
|
147
150
|
"@types/node": "^25.9.1",
|
|
148
151
|
"@types/three": "^0.184.1",
|
|
149
152
|
"@typescript-eslint/eslint-plugin": "^8.60.1",
|
|
150
153
|
"@typescript-eslint/parser": "^8.60.1",
|
|
151
154
|
"@vue/eslint-config-typescript": "^14.7.0",
|
|
155
|
+
"@vue/test-utils": "^2.4.10",
|
|
152
156
|
"@vueuse/core": "^14.3.0",
|
|
153
157
|
"@vueuse/nuxt": "^14.3.0",
|
|
154
158
|
"@webgpu/glslang": "^0.0.15",
|
|
@@ -167,11 +171,11 @@
|
|
|
167
171
|
"eslint-plugin-unused-imports": "^4.4.1",
|
|
168
172
|
"eslint-plugin-vue": "^10.9.2",
|
|
169
173
|
"fallow": "^2.92.1",
|
|
174
|
+
"happy-dom": "^20.10.3",
|
|
170
175
|
"netlify-cli": "^26.1.0",
|
|
171
176
|
"npm-check-updates": "^22.2.2",
|
|
172
177
|
"nuxt": "^4.4.7",
|
|
173
178
|
"pinia": "^3.0.4",
|
|
174
|
-
"playwright": "^1.60.0",
|
|
175
179
|
"postcss-html": "^1.8.1",
|
|
176
180
|
"prettier": "^3.8.3",
|
|
177
181
|
"prettier-plugin-css-order": "^2.2.0",
|
|
@@ -206,7 +210,6 @@
|
|
|
206
210
|
],
|
|
207
211
|
"dependencies": {
|
|
208
212
|
"node-gyp": "^12.3.0",
|
|
209
|
-
"pnpm": "11.5.3",
|
|
210
213
|
"skills": "^1.5.10"
|
|
211
214
|
},
|
|
212
215
|
"engines": {
|
|
@@ -214,7 +217,7 @@
|
|
|
214
217
|
},
|
|
215
218
|
"volta": {
|
|
216
219
|
"node": "24.16.0",
|
|
217
|
-
"pnpm": "11.5.
|
|
220
|
+
"pnpm": "11.5.3"
|
|
218
221
|
},
|
|
219
222
|
"scripts": {
|
|
220
223
|
"dev": "pnpm -F playground dev",
|
|
@@ -233,6 +236,14 @@
|
|
|
233
236
|
"fix:layer": "f(){ pnpm turbo run lint --filter \"./layers/$1\" -- --fix && prettier --write \"layers/$1\"; }; f",
|
|
234
237
|
"fix:app": "f(){ pnpm turbo run lint --filter \"./apps/$1\" -- --fix && prettier --write \"apps/$1\"; }; f",
|
|
235
238
|
"typecheck": "vue-tsc --noEmit -p tsconfig.typecheck.json",
|
|
239
|
+
"test": "vitest run",
|
|
240
|
+
"test:watch": "vitest",
|
|
241
|
+
"test:unit": "vitest run --project unit",
|
|
242
|
+
"test:vitest": "vitest run --project vitest",
|
|
243
|
+
"test:vue": "vitest run --project vue",
|
|
244
|
+
"test:nuxt": "vitest run --project nuxt",
|
|
245
|
+
"test:playwright": "playwright test",
|
|
246
|
+
"test:e2e": "playwright test",
|
|
236
247
|
"clean": "pnpm -r exec rm -rf node_modules .nuxt .output .data && pnpm store prune && pnpm install",
|
|
237
248
|
"cleancache": "pnpm store prune --force",
|
|
238
249
|
"update": "ncu -i",
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest'
|
|
2
|
-
|
|
3
|
-
import { createFeedCatalog } from './feed-catalog'
|
|
4
|
-
|
|
5
|
-
describe('createFeedCatalog', () => {
|
|
6
|
-
it('builds the feed manifest from app config and content collections', () => {
|
|
7
|
-
const catalog = createFeedCatalog({
|
|
8
|
-
site: {
|
|
9
|
-
title: 'Nuxt Layers Playground',
|
|
10
|
-
description: 'Demo and development playground for nuxt-layers.',
|
|
11
|
-
url: 'https://nuxtlayers.netlify.app/',
|
|
12
|
-
},
|
|
13
|
-
feed: {
|
|
14
|
-
collections: ['blog', 'portfolio', 'gallery'],
|
|
15
|
-
defaultCollection: 'blog',
|
|
16
|
-
limit: 30,
|
|
17
|
-
},
|
|
18
|
-
manifest: {
|
|
19
|
-
content: { type: 'page' },
|
|
20
|
-
blog: { type: 'page' },
|
|
21
|
-
portfolio: { type: 'page' },
|
|
22
|
-
gallery: { type: 'page' },
|
|
23
|
-
info: { type: 'data' },
|
|
24
|
-
},
|
|
25
|
-
})
|
|
26
|
-
|
|
27
|
-
expect(catalog.site.title).toBe('Nuxt Layers Playground')
|
|
28
|
-
expect(catalog.feed.defaultCollection).toBe('blog')
|
|
29
|
-
expect(catalog.feed.limit).toBe(30)
|
|
30
|
-
expect(catalog.feed.collections).toEqual(['blog', 'portfolio', 'gallery'])
|
|
31
|
-
expect(catalog.feed.availableCollections).toEqual(['content', 'blog', 'portfolio', 'gallery'])
|
|
32
|
-
expect(catalog.feed.missingCollections).toEqual([])
|
|
33
|
-
expect(catalog.siteRoutes.map((route) => route.path)).toEqual([
|
|
34
|
-
'/feed',
|
|
35
|
-
'/feed/discovery',
|
|
36
|
-
'/feed/rss',
|
|
37
|
-
'/feed/atom',
|
|
38
|
-
'/feed/json',
|
|
39
|
-
])
|
|
40
|
-
expect(catalog.collectionGroups).toHaveLength(3)
|
|
41
|
-
expect(catalog.collectionGroups[1]).toMatchObject({
|
|
42
|
-
collection: 'portfolio',
|
|
43
|
-
label: 'Portfolio',
|
|
44
|
-
})
|
|
45
|
-
expect(catalog.collectionGroups[1]?.routes.map((route) => route.path)).toEqual([
|
|
46
|
-
'/feed/portfolio/rss',
|
|
47
|
-
'/feed/portfolio/atom',
|
|
48
|
-
'/feed/portfolio/json',
|
|
49
|
-
])
|
|
50
|
-
})
|
|
51
|
-
|
|
52
|
-
it('surfaces configured collections that do not exist in content', () => {
|
|
53
|
-
const catalog = createFeedCatalog({
|
|
54
|
-
feed: {
|
|
55
|
-
collections: ['blog', 'missing'],
|
|
56
|
-
defaultCollection: 'missing',
|
|
57
|
-
},
|
|
58
|
-
manifest: {
|
|
59
|
-
blog: { type: 'page' },
|
|
60
|
-
gallery: { type: 'page' },
|
|
61
|
-
},
|
|
62
|
-
})
|
|
63
|
-
|
|
64
|
-
expect(catalog.feed.missingCollections).toEqual(['missing'])
|
|
65
|
-
expect(catalog.collectionGroups).toHaveLength(1)
|
|
66
|
-
expect(catalog.collectionGroups[0]).toMatchObject({
|
|
67
|
-
collection: 'blog',
|
|
68
|
-
label: 'Blog',
|
|
69
|
-
})
|
|
70
|
-
})
|
|
71
|
-
})
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import contentManifest from '#content/manifest'
|
|
2
|
-
|
|
3
|
-
import { createFeedCatalog } from '../../../app/utils/feed-catalog'
|
|
4
|
-
|
|
5
|
-
export default defineEventHandler((event) => {
|
|
6
|
-
const appConfig = useAppConfig()
|
|
7
|
-
|
|
8
|
-
const requestUrl = getRequestURL(event)
|
|
9
|
-
// Always use the request origin so discovery URLs are reachable from wherever
|
|
10
|
-
// the request came from, not the configured canonical site URL.
|
|
11
|
-
const baseUrl = `${requestUrl.protocol}//${requestUrl.host}`
|
|
12
|
-
|
|
13
|
-
const catalog = createFeedCatalog({
|
|
14
|
-
site: appConfig.site,
|
|
15
|
-
feed: appConfig.feedsLayer?.feed,
|
|
16
|
-
manifest: contentManifest,
|
|
17
|
-
})
|
|
18
|
-
|
|
19
|
-
setHeader(event, 'Cache-Control', 'public, max-age=300, s-maxage=3600')
|
|
20
|
-
|
|
21
|
-
return {
|
|
22
|
-
feeds: catalog.collectionGroups.flatMap((group) =>
|
|
23
|
-
group.routes.map((route) => ({
|
|
24
|
-
collection: group.collection,
|
|
25
|
-
format: route.label,
|
|
26
|
-
url: `${baseUrl}${route.path}`,
|
|
27
|
-
contentType: route.contentType ?? 'application/octet-stream',
|
|
28
|
-
}))
|
|
29
|
-
),
|
|
30
|
-
}
|
|
31
|
-
})
|