zone5 1.1.0 → 1.2.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.
@@ -1,5 +1,5 @@
1
1
  <script lang="ts">
2
- import { onMount } from 'svelte';
2
+ import { onMount, untrack } from 'svelte';
3
3
 
4
4
  import Img from './Zone5Img.svelte';
5
5
  import { useImageRegistry } from './Zone5Provider.svelte';
@@ -29,9 +29,15 @@
29
29
 
30
30
  // Register images with the global image registry
31
31
  $effect(() => {
32
- if (imageStore) {
33
- imageStore.register(componentId, images);
34
- }
32
+ // Track images to re-run when they change
33
+ const imagesToRegister = images;
34
+
35
+ // Use untrack to prevent the store update from creating a circular dependency
36
+ untrack(() => {
37
+ if (imageStore) {
38
+ imageStore.register(componentId, imagesToRegister);
39
+ }
40
+ });
35
41
  });
36
42
 
37
43
  // Cleanup on component unmount
@@ -1,4 +1,5 @@
1
1
  <script lang="ts">
2
+ import { untrack } from 'svelte';
2
3
  import type { Action } from 'svelte/action';
3
4
 
4
5
  import type { ImageData } from './types';
@@ -26,13 +27,30 @@
26
27
  });
27
28
 
28
29
  let img: HTMLImageElement;
29
- let loaded = $state(false);
30
+ let loaded = $state(true);
31
+
30
32
  $effect(() => {
31
- loaded = img.complete;
33
+ // Track `sizes` to rerun on change of `image` prop
34
+ void sizes;
35
+ // Track `img` element, in case effect runs before img is set
36
+ const imgEl = img;
37
+
38
+ if (!imgEl) return;
39
+ if (imgEl.complete) {
40
+ untrack(() => {
41
+ loaded = true;
42
+ });
43
+ return;
44
+ }
45
+
46
+ untrack(() => {
47
+ loaded = false;
48
+ });
32
49
  const handleLoad = () => (loaded = true);
33
- img.addEventListener('load', handleLoad);
50
+ imgEl.addEventListener('load', handleLoad);
51
+
34
52
  return () => {
35
- img.removeEventListener('load', handleLoad);
53
+ imgEl.removeEventListener('load', handleLoad);
36
54
  };
37
55
  });
38
56
 
@@ -1,15 +1,17 @@
1
1
  <script lang="ts" module>
2
2
  import { getContext, setContext } from 'svelte';
3
3
 
4
- const key = Symbol('Zone5 provider');
4
+ const key = 'Zone5 provider';
5
5
  export const useImageRegistry = () => getContext<Registry>(key);
6
6
  </script>
7
7
 
8
8
  <script lang="ts">
9
9
  import type { Snippet } from 'svelte';
10
- import { queryParameters } from 'sveltekit-search-params';
10
+ import { untrack } from 'svelte';
11
11
 
12
- import { beforeNavigate } from '$app/navigation';
12
+ import { browser } from '$app/environment';
13
+ import { beforeNavigate, goto } from '$app/navigation';
14
+ import { page } from '$app/state';
13
15
 
14
16
  import { type Registry, registry } from '../stores';
15
17
  import Zone5Lightbox from './Zone5Lightbox.svelte';
@@ -18,39 +20,86 @@
18
20
 
19
21
  setContext<Registry>(key, registry);
20
22
 
21
- // URL state store
22
- const params = queryParameters({
23
- z5: true,
24
- });
25
- let optimisticForce = $state($params.z5 !== null);
23
+ const getZ5FromUrl = () => (browser ? page.url.searchParams.get('z5') : null);
24
+ const setZ5InUrl = (value: string | null) => {
25
+ if (!browser) return;
26
+ const url = new URL(page.url);
27
+ if (value) {
28
+ url.searchParams.set('z5', value);
29
+ } else {
30
+ url.searchParams.delete('z5');
31
+ }
32
+
33
+ // eslint-disable-next-line svelte/no-navigation-without-resolve -- staying on same page, just updating query params
34
+ goto(`${url.pathname}${url.search}`, { replaceState: true, noScroll: true, keepFocus: true });
35
+ };
36
+
37
+ let optimisticForce = $state(getZ5FromUrl() !== null);
38
+
39
+ let isUpdatingFromUrl = false;
40
+ let isUpdatingUrl = false;
41
+ let initialSyncComplete = false;
26
42
 
27
- // clear registry when navigating away
28
43
  beforeNavigate((navigation) => {
29
- if (navigation.from?.route.id == navigation.to?.route.id) return;
44
+ if (navigation.from?.route.id === navigation.to?.route.id) return;
30
45
  registry.clear();
31
46
  });
32
47
 
33
- // update URL state, once Svelte's router has settled
34
- let routerSettled = false;
35
- $effect(function updateZ5UrlState() {
36
- const current = $registry.current?.id ?? null;
37
- if (routerSettled || current) {
38
- routerSettled = true;
39
- if (current !== $params.z5) {
40
- $params.z5 = current;
48
+ // registry URL
49
+ $effect(() => {
50
+ const current = $registry.current;
51
+ const images = $registry.images;
52
+
53
+ if (isUpdatingFromUrl) return;
54
+
55
+ const currentZ5 = untrack(() => getZ5FromUrl());
56
+ const newZ5 = current?.id ?? null;
57
+
58
+ if (currentZ5 !== newZ5) {
59
+ // Don't clear the URL z5 param during initial load (waiting for all images to register)
60
+ if (newZ5 === null && currentZ5 !== null && !initialSyncComplete) {
61
+ const urlImageExists = images.some((img) => img.id === currentZ5);
62
+ if (urlImageExists) {
63
+ // Don't clear URL yet, let URL→registry sync handle it
64
+ return;
65
+ }
41
66
  }
67
+
68
+ isUpdatingUrl = true;
69
+ setZ5InUrl(newZ5);
70
+ queueMicrotask(() => {
71
+ isUpdatingUrl = false;
72
+ });
42
73
  }
43
74
  });
44
75
 
45
- // update from URL state
46
- $effect(function updateZ5fromUrl() {
47
- const z5 = $params.z5;
48
- const current = $registry.current?.id ?? null;
49
- if (z5 && z5 !== current) {
50
- if (!registry.findCurrent(z5)) {
76
+ // URL registry
77
+ $effect(() => {
78
+ const z5 = getZ5FromUrl();
79
+ const images = $registry.images;
80
+
81
+ if (isUpdatingUrl || images.length === 0) return;
82
+
83
+ if (z5) {
84
+ isUpdatingFromUrl = true;
85
+ const found = registry.findCurrent(z5);
86
+ if (found) {
51
87
  optimisticForce = false;
52
- $params.z5 = null;
53
88
  }
89
+ initialSyncComplete = true;
90
+ queueMicrotask(() => {
91
+ isUpdatingFromUrl = false;
92
+ });
93
+ } else {
94
+ const currentId = untrack(() => $registry.current?.id);
95
+ if (currentId) {
96
+ isUpdatingFromUrl = true;
97
+ registry.clearCurrent();
98
+ queueMicrotask(() => {
99
+ isUpdatingFromUrl = false;
100
+ });
101
+ }
102
+ initialSyncComplete = true;
54
103
  }
55
104
  });
56
105
  </script>
package/dist/config.d.ts CHANGED
@@ -15,7 +15,7 @@ declare const ConfigSchema: z.ZodObject<{
15
15
  resize_kernel: z.ZodDefault<z.ZodEnum<{
16
16
  [x: string]: any;
17
17
  }>>;
18
- resize_gamma: z.ZodDefault<z.ZodNumber>;
18
+ resize_gamma: z.ZodOptional<z.ZodNumber>;
19
19
  variants: z.ZodDefault<z.ZodArray<z.ZodNumber>>;
20
20
  }, z.core.$strip>>>;
21
21
  }, z.core.$strip>;
@@ -29,8 +29,8 @@ export declare const load: (configDir?: string | undefined) => Promise<{
29
29
  };
30
30
  processor: {
31
31
  resize_kernel: any;
32
- resize_gamma: number;
33
32
  variants: number[];
33
+ resize_gamma?: number | undefined;
34
34
  };
35
35
  } | {
36
36
  src: string;
@@ -41,8 +41,8 @@ export declare const load: (configDir?: string | undefined) => Promise<{
41
41
  };
42
42
  processor: {
43
43
  resize_kernel: any;
44
- resize_gamma: number;
45
44
  variants: number[];
45
+ resize_gamma?: number | undefined;
46
46
  };
47
47
  }>;
48
48
  export declare const toToml: (config: ConfigType & {
@@ -3,7 +3,7 @@ export declare const ProcessorConfigSchema: z.ZodPrefault<z.ZodObject<{
3
3
  resize_kernel: z.ZodDefault<z.ZodEnum<{
4
4
  [x: string]: any;
5
5
  }>>;
6
- resize_gamma: z.ZodDefault<z.ZodNumber>;
6
+ resize_gamma: z.ZodOptional<z.ZodNumber>;
7
7
  variants: z.ZodDefault<z.ZodArray<z.ZodNumber>>;
8
8
  }, z.core.$strip>>;
9
9
  export type ProcessorConfig = z.infer<typeof ProcessorConfigSchema>;
@@ -4,7 +4,7 @@ import z from 'zod';
4
4
  export const ProcessorConfigSchema = z
5
5
  .object({
6
6
  resize_kernel: z.enum(Object.values(sharp.kernel)).default(sharp.kernel.mks2021),
7
- resize_gamma: z.number().min(1.0).max(3.0).default(2.2),
7
+ resize_gamma: z.number().min(1.0).max(3.0).optional(),
8
8
  variants: z.array(z.number().int().min(1)).default([640, 768, 1280, 1920, 2560]),
9
9
  })
10
10
  .prefault({});
@@ -43,7 +43,11 @@ export async function generateImageVariants(options) {
43
43
  // Check if variant already exists and should be overwritten
44
44
  const variantExists = await fileExists(variantPath);
45
45
  if (!variantExists || forceOverwrite) {
46
- let img = sharp(sourceFile).gamma(processor.resize_gamma).resize(width, null, {
46
+ let img = sharp(sourceFile);
47
+ if (processor.resize_gamma) {
48
+ img = img.gamma(processor.resize_gamma);
49
+ }
50
+ img = img.resize(width, null, {
47
51
  fit: 'inside',
48
52
  kernel: processor.resize_kernel,
49
53
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zone5",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "repository": {
5
5
  "url": "https://github.com/cwygoda/zone5"
6
6
  },