@phosart/common 0.4.40 → 0.4.42

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,9 +1,10 @@
1
1
  <script lang="ts">
2
2
  import '@fortawesome/fontawesome-free/css/all.min.css';
3
3
 
4
- import type { ArtPiece } from '../util/art.js';
4
+ import type { ArtPiece, Picture } from '../util/art.js';
5
5
  import ImageView from './ImageView.svelte';
6
6
  import { useLibraryConfig } from '../util/phosart_config.svelte.js';
7
+ import Image from '../Image.svelte';
7
8
 
8
9
  interface Props {
9
10
  piece: ArtPiece;
@@ -22,22 +23,41 @@
22
23
  onprev();
23
24
  }
24
25
 
26
+ let infoHeight = $derived(
27
+ parseInt(getComputedStyle(document.documentElement).getPropertyValue('--info-height') ?? '100')
28
+ );
29
+
25
30
  let containerWidth: number = $state(0);
26
31
  let containerHeight: number = $state(0);
27
- let containerHeightLessInfo = $derived(Math.max(0, containerHeight - 50));
32
+ let containerHeightLessInfo = $derived(Math.max(0, containerHeight - infoHeight));
33
+ let isComic = $derived(piece.alts_display === 'comic_panels');
34
+ let bounded: HTMLDivElement | null = $state(null);
35
+
36
+ function scale(image: Picture) {
37
+ const scaleByHeight =
38
+ !isComic &&
39
+ containerWidth / containerHeightLessInfo > image.full.fallback.w / image.full.fallback.h;
40
+ const scalingFactor: number = scaleByHeight
41
+ ? containerHeightLessInfo / image.full.fallback.h
42
+ : containerWidth / image.full.fallback.w;
43
+
44
+ return scalingFactor;
45
+ }
28
46
 
29
- let scaleByHeight = $derived(
30
- containerWidth / containerHeightLessInfo >
31
- piece.image.full.fallback.w / piece.image.full.fallback.h
32
- );
33
- let scalingFactor: number = $derived(
34
- scaleByHeight
35
- ? containerHeightLessInfo / piece.image.full.fallback.h
36
- : containerWidth / piece.image.full.fallback.w
37
- );
47
+ function width(image: Picture) {
48
+ return image.full.fallback.w * scale(image);
49
+ }
50
+
51
+ function height(image: Picture) {
52
+ return image.full.fallback.h * scale(image);
53
+ }
38
54
 
39
- let w = $derived(piece.image.full.fallback.w * scalingFactor);
40
- let h = $derived(piece.image.full.fallback.h * scalingFactor + 50);
55
+ function scrollDown() {
56
+ bounded?.scrollBy({ behavior: 'smooth', top: containerHeight });
57
+ }
58
+
59
+ let w = $derived(width(piece.image));
60
+ let h = $derived(height(piece.image) + 50);
41
61
 
42
62
  let config = useLibraryConfig();
43
63
 
@@ -56,17 +76,65 @@
56
76
  ></div>
57
77
  </div>
58
78
 
59
- <div class="main-container">
60
- <div class="bounding-div" bind:clientHeight={containerHeight} bind:clientWidth={containerWidth}>
61
- <div
62
- class="bounded-div"
63
- onclick={(e) => e.stopPropagation()}
64
- onkeypress={(e) => e.stopPropagation()}
65
- role="button"
66
- tabindex={-1}
67
- style="width: {w}px; height: {h}px"
68
- >
69
- <ImageView {piece} {nameInHeader} />
79
+ <div class="main-container" style={isComic ? 'overflow: visible' : ''}>
80
+ <div
81
+ class="bounding-div"
82
+ style={isComic ? 'overflow-y: scroll; align-items: flex-start; z-index: 100' : ''}
83
+ bind:clientHeight={containerHeight}
84
+ bind:clientWidth={containerWidth}
85
+ bind:this={bounded}
86
+ >
87
+ <div class="flex flex-col">
88
+ <div
89
+ class="bounded-div"
90
+ onclick={(e) => {
91
+ e.stopPropagation();
92
+ scrollDown();
93
+ }}
94
+ onkeypress={(e) => {
95
+ e.stopPropagation();
96
+ scrollDown();
97
+ }}
98
+ role="button"
99
+ tabindex={-1}
100
+ style="width: {w}px; height: {h}px"
101
+ >
102
+ <ImageView {piece} {nameInHeader}>
103
+ {#snippet display(image, onloaded)}
104
+ <div class="image-container">
105
+ <Image
106
+ video={image.video?.full}
107
+ controls
108
+ picture={image.image.full}
109
+ alt={image.alt}
110
+ {onloaded}
111
+ />
112
+ </div>
113
+ {/snippet}
114
+ </ImageView>
115
+ </div>
116
+ {#if isComic && piece.alts}
117
+ {#each piece.alts as alt (JSON.stringify(alt))}
118
+ <div
119
+ class="bounded-div"
120
+ onclick={(e) => {
121
+ e.stopPropagation();
122
+ scrollDown();
123
+ }}
124
+ onkeypress={(e) => {
125
+ e.stopPropagation();
126
+ scrollDown();
127
+ }}
128
+ role="button"
129
+ tabindex={-1}
130
+ style="width: {width(alt.image)}px; height: {height(alt.image)}px"
131
+ >
132
+ <div class="image-container">
133
+ <Image video={alt.video?.full} controls picture={alt.image.full} alt={alt.alt} />
134
+ </div>
135
+ </div>
136
+ {/each}
137
+ {/if}
70
138
  </div>
71
139
  </div>
72
140
  </div>
@@ -84,16 +152,7 @@
84
152
  </div>
85
153
 
86
154
  <style>
87
- :global(:root) {
88
- --info-height: 50px;
89
- --carousel-height: 100px;
90
- }
91
-
92
155
  @media only screen and (max-width: 800px) {
93
- :global(:root) {
94
- --info-height: 50px;
95
- --carousel-height: 75px;
96
- }
97
156
  .nav-container.nav-container {
98
157
  width: 0;
99
158
  }
@@ -105,10 +164,10 @@
105
164
  justify-content: center;
106
165
  align-items: center;
107
166
  overflow: visible;
108
- max-height: calc(100% - 50px - var(--carousel-height));
167
+ max-height: calc(100% - var(--carousel-height));
109
168
  position: relative;
110
169
  height: 100%;
111
- padding: 2rem;
170
+ padding: 1rem;
112
171
  }
113
172
  .nav-container {
114
173
  color: white;
@@ -156,4 +215,12 @@
156
215
  display: flex;
157
216
  position: relative;
158
217
  }
218
+
219
+ .image-container {
220
+ position: absolute;
221
+ top: var(--info-height);
222
+ bottom: 0;
223
+ left: 0;
224
+ right: 0;
225
+ }
159
226
  </style>
@@ -1 +1 @@
1
- {"version":3,"file":"ImageSection.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/ModalGallery/ImageSection.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,+CAA+C,CAAC;AAEvD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAK9C,UAAU,KAAK;IACd,KAAK,EAAE,QAAQ,CAAC;IAChB,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,MAAM,EAAE,MAAM,IAAI,CAAC;CACnB;AAgEF,QAAA,MAAM,YAAY,2CAAwC,CAAC;AAC3D,KAAK,YAAY,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;AACpD,eAAe,YAAY,CAAC"}
1
+ {"version":3,"file":"ImageSection.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/ModalGallery/ImageSection.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,+CAA+C,CAAC;AAEvD,OAAO,KAAK,EAAE,QAAQ,EAAW,MAAM,gBAAgB,CAAC;AAMvD,UAAU,KAAK;IACd,KAAK,EAAE,QAAQ,CAAC;IAChB,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,MAAM,EAAE,MAAM,IAAI,CAAC;CACnB;AAiHF,QAAA,MAAM,YAAY,2CAAwC,CAAC;AAC3D,KAAK,YAAY,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;AACpD,eAAe,YAAY,CAAC"}
@@ -1,20 +1,22 @@
1
1
  <script lang="ts">
2
2
  import { run } from 'svelte/legacy';
3
3
 
4
- import Image from '../Image.svelte';
5
-
6
4
  import Spinner from './Spinner.svelte';
7
5
  import Headline from '../Postcard/Headline.svelte';
8
6
  import Description from '../Postcard/Description.svelte';
9
7
  import type { ArtPiece } from '../util/art.js';
10
8
  import { useLibraryConfig } from '../util/phosart_config.svelte.js';
9
+ import type { Snippet } from 'svelte';
11
10
 
12
11
  interface Props {
13
12
  piece: ArtPiece;
14
13
  nameInHeader: boolean;
14
+ display: Snippet<
15
+ [image: ArtPiece | NonNullable<ArtPiece['alts']>[number], onloaded: () => void]
16
+ >;
15
17
  }
16
18
 
17
- let { piece, nameInHeader }: Props = $props();
19
+ let { piece, nameInHeader, display }: Props = $props();
18
20
 
19
21
  let config = useLibraryConfig();
20
22
 
@@ -32,15 +34,7 @@
32
34
 
33
35
  <Spinner {loading} />
34
36
 
35
- <div class="image-container">
36
- <Image
37
- video={image.video?.full}
38
- controls
39
- picture={image.image.full}
40
- alt={image.alt}
41
- onloaded={() => (loading = false)}
42
- />
43
- </div>
37
+ {@render display(image, () => (loading = false))}
44
38
 
45
39
  <div class="headline-container">
46
40
  <Headline {piece} bind:showingDescription showName={!config.modal?.hideNames && nameInHeader} />
@@ -56,13 +50,6 @@
56
50
  </div>
57
51
 
58
52
  <style>
59
- .image-container {
60
- position: absolute;
61
- top: var(--info-height);
62
- bottom: 0;
63
- left: 0;
64
- right: 0;
65
- }
66
53
  .description-container {
67
54
  position: absolute;
68
55
  top: var(--info-height);
@@ -1,7 +1,12 @@
1
1
  import type { ArtPiece } from '../util/art.ts';
2
+ import type { Snippet } from 'svelte';
2
3
  interface Props {
3
4
  piece: ArtPiece;
4
5
  nameInHeader: boolean;
6
+ display: Snippet<[
7
+ image: ArtPiece | NonNullable<ArtPiece['alts']>[number],
8
+ onloaded: () => void
9
+ ]>;
5
10
  }
6
11
  declare const ImageView: import("svelte").Component<Props, {}, "">;
7
12
  type ImageView = ReturnType<typeof ImageView>;
@@ -1 +1 @@
1
- {"version":3,"file":"ImageView.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/ModalGallery/ImageView.svelte.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAI9C,UAAU,KAAK;IACd,KAAK,EAAE,QAAQ,CAAC;IAChB,YAAY,EAAE,OAAO,CAAC;CACtB;AA8CF,QAAA,MAAM,SAAS,2CAAwC,CAAC;AACxD,KAAK,SAAS,GAAG,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC;AAC9C,eAAe,SAAS,CAAC"}
1
+ {"version":3,"file":"ImageView.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/ModalGallery/ImageView.svelte.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE/C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAGrC,UAAU,KAAK;IACd,KAAK,EAAE,QAAQ,CAAC;IAChB,YAAY,EAAE,OAAO,CAAC;IACtB,OAAO,EAAE,OAAO,CACf;QAAC,KAAK,EAAE,QAAQ,GAAG,WAAW,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC;QAAE,QAAQ,EAAE,MAAM,IAAI;KAAC,CAC/E,CAAC;CACF;AA2CF,QAAA,MAAM,SAAS,2CAAwC,CAAC;AACxD,KAAK,SAAS,GAAG,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC;AAC9C,eAAe,SAAS,CAAC"}
@@ -45,6 +45,10 @@
45
45
 
46
46
  const isGalleryOpen = $state(useIsModelGalleryOpen());
47
47
 
48
+ const currentIsComic = $derived(
49
+ typeof selected === 'number' ? pieces[selected]?.alts_display === 'comic_panels' : false
50
+ );
51
+
48
52
  onMount(() => {
49
53
  const handler = (e: KeyboardEvent) => {
50
54
  if (e.code === 'ArrowLeft' && selected !== null) {
@@ -57,16 +61,15 @@
57
61
  const hashchange = (hce: { oldURL?: string; newURL: string }) => {
58
62
  const hash = new URL(hce.newURL).hash.replace(/^#/, '');
59
63
  if (browser) {
60
- console.log('H ->', hash);
61
- const foundSelected = pieces.findIndex((piece) => piece.slug === hash);
64
+ const foundSelected = pieces.findIndex((piece) => piece.slug === decodeURIComponent(hash));
62
65
  if (!isAnyModelGalleryOpen()) {
63
66
  if (foundSelected !== -1) {
64
67
  selected = foundSelected;
65
68
  }
66
69
  } else if (isGalleryOpen.open) {
67
- if (!hash || hash == '#') {
70
+ if (!hash || hash == '#' || foundSelected === -1) {
68
71
  selected = null;
69
- } else if (foundSelected !== -1) {
72
+ } else {
70
73
  selected = foundSelected;
71
74
  }
72
75
  }
@@ -90,13 +93,12 @@
90
93
  if (isAnyModelGalleryOpen() === false && window.location.hash) {
91
94
  window.location.hash = '##';
92
95
  } else if (selected !== null && pieces[selected]) {
93
- console.log('H <-', pieces[selected].slug);
94
96
  window.location.hash = '#' + encodeURIComponent(pieces[selected].slug);
95
97
  }
96
98
  }
97
99
 
98
100
  $effect.pre(() => {
99
- isGalleryOpen.open = selected !== null;
101
+ isGalleryOpen.open = selected !== null && !!pieces[selected];
100
102
  });
101
103
  $effect.pre(() => {
102
104
  if (hashUpdateReady) {
@@ -105,8 +107,8 @@
105
107
  });
106
108
  </script>
107
109
 
108
- <Modal open={selected !== null} onclose={() => (selected = null)}>
109
- <div class="gallery-container">
110
+ <Modal open={selected !== null && !!pieces[selected]} onclose={() => (selected = null)}>
111
+ <div class="gallery-container" style={currentIsComic ? '--carousel-height: 25px' : ''}>
110
112
  <div
111
113
  role="button"
112
114
  tabindex={-1}
@@ -121,7 +123,7 @@
121
123
  ></div>
122
124
  <div style="flex-grow: 1"></div>
123
125
 
124
- {#if selected !== null}
126
+ {#if selected !== null && !!pieces[selected]}
125
127
  <HighResContext>
126
128
  <ImageSection
127
129
  piece={pieces[selected]}
@@ -139,6 +141,18 @@
139
141
  </Modal>
140
142
 
141
143
  <style>
144
+ @media only screen and (max-width: 800px) {
145
+ :global(:root) {
146
+ --info-height: 50px;
147
+ --carousel-height: 75px;
148
+ }
149
+ }
150
+
151
+ :global(:root) {
152
+ --info-height: 50px;
153
+ --carousel-height: 50px;
154
+ }
155
+
142
156
  .gallery-container {
143
157
  display: flex;
144
158
  flex-direction: column;
@@ -1 +1 @@
1
- {"version":3,"file":"ModalGallery.svelte.d.ts","sourceRoot":"","sources":["../src/lib/ModalGallery.svelte.ts"],"names":[],"mappings":"AAGC,OAAO,+CAA+C,CAAC;AAIvD,wBAAgB,qBAAqB;UAIlB,OAAO;EAOzB;AAED,wBAAgB,qBAAqB,YAEpC;AAGF,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAQ7C,UAAU,KAAK;IACd,MAAM,EAAE,QAAQ,EAAE,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,OAAO,EAAE,OAAO,CAAC;CACjB;AA4GF,QAAA,MAAM,YAAY,uEAAwC,CAAC;AAC3D,KAAK,YAAY,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;AACpD,eAAe,YAAY,CAAC"}
1
+ {"version":3,"file":"ModalGallery.svelte.d.ts","sourceRoot":"","sources":["../src/lib/ModalGallery.svelte.ts"],"names":[],"mappings":"AAGC,OAAO,+CAA+C,CAAC;AAIvD,wBAAgB,qBAAqB;UAIlB,OAAO;EAOzB;AAED,wBAAgB,qBAAqB,YAEpC;AAGF,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAQ7C,UAAU,KAAK;IACd,MAAM,EAAE,QAAQ,EAAE,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,OAAO,EAAE,OAAO,CAAC;CACjB;AA8GF,QAAA,MAAM,YAAY,uEAAwC,CAAC;AAC3D,KAAK,YAAY,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;AACpD,eAAe,YAAY,CAAC"}
@@ -15,6 +15,9 @@ declare const ZMultiSelectionOption: z.ZodObject<{
15
15
  declare const ZStringOption: z.ZodObject<{
16
16
  type: z.ZodLiteral<"string">;
17
17
  }, z.z.core.$strip>;
18
+ declare const ZBoolOption: z.ZodObject<{
19
+ type: z.ZodLiteral<"boolean">;
20
+ }, z.z.core.$strip>;
18
21
  declare const ZTagsOption: z.ZodObject<{
19
22
  type: z.ZodLiteral<"tag-list">;
20
23
  }, z.z.core.$strip>;
@@ -37,6 +40,8 @@ export declare const ZThemeSettingsSchema: z.ZodRecord<z.ZodString, z.ZodUnion<r
37
40
  type: z.ZodLiteral<"string-list">;
38
41
  }, z.z.core.$strip>, z.ZodObject<{
39
42
  type: z.ZodLiteral<"string">;
43
+ }, z.z.core.$strip>, z.ZodObject<{
44
+ type: z.ZodLiteral<"boolean">;
40
45
  }, z.z.core.$strip>]>>;
41
46
  export type ThemeSettingsSchema = z.infer<typeof ZThemeSettingsSchema>;
42
47
  export declare const builtinSettings: {
@@ -49,7 +54,7 @@ export declare const builtinSettings: {
49
54
  };
50
55
  };
51
56
  export type BuiltinSettings = typeof builtinSettings;
52
- type MaterializedOptionFor<T extends ThemeSettingsSchema[string]> = T extends z.infer<typeof ZColorOption> ? `#${string}` : T extends z.infer<typeof ZSelectionOption> ? [...T['options']] : T extends z.infer<typeof ZMultiSelectionOption> ? Array<[...T['options']]> : T extends z.infer<typeof ZStringList> ? Array<string> : T extends z.infer<typeof ZTagsOption> ? Array<string> : T extends z.infer<typeof ZStringOption> ? string : never;
57
+ type MaterializedOptionFor<T extends ThemeSettingsSchema[string]> = T extends z.infer<typeof ZColorOption> ? `#${string}` : T extends z.infer<typeof ZSelectionOption> ? [...T['options']] : T extends z.infer<typeof ZMultiSelectionOption> ? Array<[...T['options']]> : T extends z.infer<typeof ZStringList> ? Array<string> : T extends z.infer<typeof ZTagsOption> ? Array<string> : T extends z.infer<typeof ZStringOption> ? string : T extends z.infer<typeof ZBoolOption> ? boolean | undefined : never;
53
58
  export type SettingsFor<T extends ThemeSettingsSchema> = {
54
59
  [K in keyof T]: MaterializedOptionFor<T[K]>;
55
60
  };
@@ -1 +1 @@
1
- {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../src/lib/server/theme/schema.ts"],"names":[],"mappings":"AAGA,OAAO,CAAC,MAAM,KAAK,CAAC;AAcpB,QAAA,MAAM,YAAY;;mBAAyC,CAAC;AAC5D,QAAA,MAAM,gBAAgB;;;;mBAIpB,CAAC;AACH,QAAA,MAAM,qBAAqB;;;;mBAIzB,CAAC;AACH,QAAA,MAAM,aAAa;;mBAA0C,CAAC;AAC9D,QAAA,MAAM,WAAW;;mBAA4C,CAAC;AAC9D,QAAA,MAAM,WAAW;;mBAA+C,CAAC;AACjE,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;sBAUhC,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAEvE,eAAO,MAAM,eAAe;;;;;;;;CAGY,CAAC;AACzC,MAAM,MAAM,eAAe,GAAG,OAAO,eAAe,CAAC;AAErD,KAAK,qBAAqB,CAAC,CAAC,SAAS,mBAAmB,CAAC,MAAM,CAAC,IAC/D,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,GACnC,IAAI,MAAM,EAAE,GACZ,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,GACzC,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,CAAC,GACjB,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,GAC9C,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,GACxB,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,GACpC,KAAK,CAAC,MAAM,CAAC,GACb,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,GACpC,KAAK,CAAC,MAAM,CAAC,GACb,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,GACtC,MAAM,GACN,KAAK,CAAC;AAEf,MAAM,MAAM,WAAW,CAAC,CAAC,SAAS,mBAAmB,IAAI;KACvD,CAAC,IAAI,MAAM,CAAC,GAAG,qBAAqB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;CAC3C,CAAC;AAEF,wBAAsB,eAAe,CAAC,CAAC,SAAS,mBAAmB,KAAK,OAAO,CAAC,CAAC,CAAC,CAqBjF;AAyCD,wBAAgB,cAAc,CAAC,CAAC,SAAS,mBAAmB,EAC3D,MAAM,EAAE,CAAC,EACT,GAAG,EAAE,OAAO,GACV,GAAG,IAAI,WAAW,CAAC,CAAC,GAAG,eAAe,CAAC,CAEzC;AAoGD,wBAAsB,eAAe,CAAC,CAAC,SAAS,mBAAmB,EAClE,MAAM,EAAE,CAAC,GACP,OAAO,CAAC,WAAW,CAAC,CAAC,GAAG,eAAe,CAAC,CAAC,CAoB3C;AAED,wBAAsB,gBAAgB,CAAC,CAAC,SAAS,mBAAmB,EACnE,MAAM,EAAE,CAAC,EACT,MAAM,EAAE,WAAW,CAAC,CAAC,GAAG,eAAe,CAAC,GACtC,OAAO,CAAC,IAAI,CAAC,CAIf"}
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../src/lib/server/theme/schema.ts"],"names":[],"mappings":"AAGA,OAAO,CAAC,MAAM,KAAK,CAAC;AAcpB,QAAA,MAAM,YAAY;;mBAAyC,CAAC;AAC5D,QAAA,MAAM,gBAAgB;;;;mBAIpB,CAAC;AACH,QAAA,MAAM,qBAAqB;;;;mBAIzB,CAAC;AACH,QAAA,MAAM,aAAa;;mBAA0C,CAAC;AAC9D,QAAA,MAAM,WAAW;;mBAA2C,CAAC;AAC7D,QAAA,MAAM,WAAW;;mBAA4C,CAAC;AAC9D,QAAA,MAAM,WAAW;;mBAA+C,CAAC;AACjE,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;sBAWhC,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAEvE,eAAO,MAAM,eAAe;;;;;;;;CAGY,CAAC;AACzC,MAAM,MAAM,eAAe,GAAG,OAAO,eAAe,CAAC;AAErD,KAAK,qBAAqB,CAAC,CAAC,SAAS,mBAAmB,CAAC,MAAM,CAAC,IAC/D,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,GACnC,IAAI,MAAM,EAAE,GACZ,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,GACzC,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,CAAC,GACjB,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,GAC9C,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,GACxB,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,GACpC,KAAK,CAAC,MAAM,CAAC,GACb,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,GACpC,KAAK,CAAC,MAAM,CAAC,GACb,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,GACtC,MAAM,GACN,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,GACpC,OAAO,GAAG,SAAS,GACnB,KAAK,CAAC;AAEhB,MAAM,MAAM,WAAW,CAAC,CAAC,SAAS,mBAAmB,IAAI;KACvD,CAAC,IAAI,MAAM,CAAC,GAAG,qBAAqB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;CAC3C,CAAC;AAEF,wBAAsB,eAAe,CAAC,CAAC,SAAS,mBAAmB,KAAK,OAAO,CAAC,CAAC,CAAC,CAqBjF;AAyCD,wBAAgB,cAAc,CAAC,CAAC,SAAS,mBAAmB,EAC3D,MAAM,EAAE,CAAC,EACT,GAAG,EAAE,OAAO,GACV,GAAG,IAAI,WAAW,CAAC,CAAC,GAAG,eAAe,CAAC,CAEzC;AAoGD,wBAAsB,eAAe,CAAC,CAAC,SAAS,mBAAmB,EAClE,MAAM,EAAE,CAAC,GACP,OAAO,CAAC,WAAW,CAAC,CAAC,GAAG,eAAe,CAAC,CAAC,CAoB3C;AAED,wBAAsB,gBAAgB,CAAC,CAAC,SAAS,mBAAmB,EACnE,MAAM,EAAE,CAAC,EACT,MAAM,EAAE,WAAW,CAAC,CAAC,GAAG,eAAe,CAAC,GACtC,OAAO,CAAC,IAAI,CAAC,CAIf"}
@@ -25,6 +25,7 @@ const ZMultiSelectionOption = z.object({
25
25
  multi: z.literal(true)
26
26
  });
27
27
  const ZStringOption = z.object({ type: z.literal('string') });
28
+ const ZBoolOption = z.object({ type: z.literal('boolean') });
28
29
  const ZTagsOption = z.object({ type: z.literal('tag-list') });
29
30
  const ZStringList = z.object({ type: z.literal('string-list') });
30
31
  export const ZThemeSettingsSchema = z.record(z.string(), z.union([
@@ -33,7 +34,8 @@ export const ZThemeSettingsSchema = z.record(z.string(), z.union([
33
34
  ZMultiSelectionOption,
34
35
  ZTagsOption,
35
36
  ZStringList,
36
- ZStringOption
37
+ ZStringOption,
38
+ ZBoolOption
37
39
  ]));
38
40
  export const builtinSettings = {
39
41
  defaultArtist: { type: 'string' },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@phosart/common",
3
- "version": "0.4.40",
3
+ "version": "0.4.42",
4
4
  "files": [
5
5
  "dist",
6
6
  "!dist/**/*.test.*",
@@ -1,9 +1,10 @@
1
1
  <script lang="ts">
2
2
  import '@fortawesome/fontawesome-free/css/all.min.css';
3
3
 
4
- import type { ArtPiece } from '../util/art.ts';
4
+ import type { ArtPiece, Picture } from '../util/art.ts';
5
5
  import ImageView from './ImageView.svelte';
6
6
  import { useLibraryConfig } from '../util/phosart_config.svelte.ts';
7
+ import Image from '$lib/Image.svelte';
7
8
 
8
9
  interface Props {
9
10
  piece: ArtPiece;
@@ -22,22 +23,41 @@
22
23
  onprev();
23
24
  }
24
25
 
26
+ let infoHeight = $derived(
27
+ parseInt(getComputedStyle(document.documentElement).getPropertyValue('--info-height') ?? '100')
28
+ );
29
+
25
30
  let containerWidth: number = $state(0);
26
31
  let containerHeight: number = $state(0);
27
- let containerHeightLessInfo = $derived(Math.max(0, containerHeight - 50));
32
+ let containerHeightLessInfo = $derived(Math.max(0, containerHeight - infoHeight));
33
+ let isComic = $derived(piece.alts_display === 'comic_panels');
34
+ let bounded: HTMLDivElement | null = $state(null);
35
+
36
+ function scale(image: Picture) {
37
+ const scaleByHeight =
38
+ !isComic &&
39
+ containerWidth / containerHeightLessInfo > image.full.fallback.w / image.full.fallback.h;
40
+ const scalingFactor: number = scaleByHeight
41
+ ? containerHeightLessInfo / image.full.fallback.h
42
+ : containerWidth / image.full.fallback.w;
43
+
44
+ return scalingFactor;
45
+ }
28
46
 
29
- let scaleByHeight = $derived(
30
- containerWidth / containerHeightLessInfo >
31
- piece.image.full.fallback.w / piece.image.full.fallback.h
32
- );
33
- let scalingFactor: number = $derived(
34
- scaleByHeight
35
- ? containerHeightLessInfo / piece.image.full.fallback.h
36
- : containerWidth / piece.image.full.fallback.w
37
- );
47
+ function width(image: Picture) {
48
+ return image.full.fallback.w * scale(image);
49
+ }
50
+
51
+ function height(image: Picture) {
52
+ return image.full.fallback.h * scale(image);
53
+ }
38
54
 
39
- let w = $derived(piece.image.full.fallback.w * scalingFactor);
40
- let h = $derived(piece.image.full.fallback.h * scalingFactor + 50);
55
+ function scrollDown() {
56
+ bounded?.scrollBy({ behavior: 'smooth', top: containerHeight });
57
+ }
58
+
59
+ let w = $derived(width(piece.image));
60
+ let h = $derived(height(piece.image) + 50);
41
61
 
42
62
  let config = useLibraryConfig();
43
63
 
@@ -56,17 +76,65 @@
56
76
  ></div>
57
77
  </div>
58
78
 
59
- <div class="main-container">
60
- <div class="bounding-div" bind:clientHeight={containerHeight} bind:clientWidth={containerWidth}>
61
- <div
62
- class="bounded-div"
63
- onclick={(e) => e.stopPropagation()}
64
- onkeypress={(e) => e.stopPropagation()}
65
- role="button"
66
- tabindex={-1}
67
- style="width: {w}px; height: {h}px"
68
- >
69
- <ImageView {piece} {nameInHeader} />
79
+ <div class="main-container" style={isComic ? 'overflow: visible' : ''}>
80
+ <div
81
+ class="bounding-div"
82
+ style={isComic ? 'overflow-y: scroll; align-items: flex-start; z-index: 100' : ''}
83
+ bind:clientHeight={containerHeight}
84
+ bind:clientWidth={containerWidth}
85
+ bind:this={bounded}
86
+ >
87
+ <div class="flex flex-col">
88
+ <div
89
+ class="bounded-div"
90
+ onclick={(e) => {
91
+ e.stopPropagation();
92
+ scrollDown();
93
+ }}
94
+ onkeypress={(e) => {
95
+ e.stopPropagation();
96
+ scrollDown();
97
+ }}
98
+ role="button"
99
+ tabindex={-1}
100
+ style="width: {w}px; height: {h}px"
101
+ >
102
+ <ImageView {piece} {nameInHeader}>
103
+ {#snippet display(image, onloaded)}
104
+ <div class="image-container">
105
+ <Image
106
+ video={image.video?.full}
107
+ controls
108
+ picture={image.image.full}
109
+ alt={image.alt}
110
+ {onloaded}
111
+ />
112
+ </div>
113
+ {/snippet}
114
+ </ImageView>
115
+ </div>
116
+ {#if isComic && piece.alts}
117
+ {#each piece.alts as alt (JSON.stringify(alt))}
118
+ <div
119
+ class="bounded-div"
120
+ onclick={(e) => {
121
+ e.stopPropagation();
122
+ scrollDown();
123
+ }}
124
+ onkeypress={(e) => {
125
+ e.stopPropagation();
126
+ scrollDown();
127
+ }}
128
+ role="button"
129
+ tabindex={-1}
130
+ style="width: {width(alt.image)}px; height: {height(alt.image)}px"
131
+ >
132
+ <div class="image-container">
133
+ <Image video={alt.video?.full} controls picture={alt.image.full} alt={alt.alt} />
134
+ </div>
135
+ </div>
136
+ {/each}
137
+ {/if}
70
138
  </div>
71
139
  </div>
72
140
  </div>
@@ -84,16 +152,7 @@
84
152
  </div>
85
153
 
86
154
  <style>
87
- :global(:root) {
88
- --info-height: 50px;
89
- --carousel-height: 100px;
90
- }
91
-
92
155
  @media only screen and (max-width: 800px) {
93
- :global(:root) {
94
- --info-height: 50px;
95
- --carousel-height: 75px;
96
- }
97
156
  .nav-container.nav-container {
98
157
  width: 0;
99
158
  }
@@ -105,10 +164,10 @@
105
164
  justify-content: center;
106
165
  align-items: center;
107
166
  overflow: visible;
108
- max-height: calc(100% - 50px - var(--carousel-height));
167
+ max-height: calc(100% - var(--carousel-height));
109
168
  position: relative;
110
169
  height: 100%;
111
- padding: 2rem;
170
+ padding: 1rem;
112
171
  }
113
172
  .nav-container {
114
173
  color: white;
@@ -156,4 +215,12 @@
156
215
  display: flex;
157
216
  position: relative;
158
217
  }
218
+
219
+ .image-container {
220
+ position: absolute;
221
+ top: var(--info-height);
222
+ bottom: 0;
223
+ left: 0;
224
+ right: 0;
225
+ }
159
226
  </style>
@@ -1,20 +1,22 @@
1
1
  <script lang="ts">
2
2
  import { run } from 'svelte/legacy';
3
3
 
4
- import Image from '../Image.svelte';
5
-
6
4
  import Spinner from './Spinner.svelte';
7
5
  import Headline from '../Postcard/Headline.svelte';
8
6
  import Description from '../Postcard/Description.svelte';
9
7
  import type { ArtPiece } from '../util/art.ts';
10
8
  import { useLibraryConfig } from '../util/phosart_config.svelte.ts';
9
+ import type { Snippet } from 'svelte';
11
10
 
12
11
  interface Props {
13
12
  piece: ArtPiece;
14
13
  nameInHeader: boolean;
14
+ display: Snippet<
15
+ [image: ArtPiece | NonNullable<ArtPiece['alts']>[number], onloaded: () => void]
16
+ >;
15
17
  }
16
18
 
17
- let { piece, nameInHeader }: Props = $props();
19
+ let { piece, nameInHeader, display }: Props = $props();
18
20
 
19
21
  let config = useLibraryConfig();
20
22
 
@@ -32,15 +34,7 @@
32
34
 
33
35
  <Spinner {loading} />
34
36
 
35
- <div class="image-container">
36
- <Image
37
- video={image.video?.full}
38
- controls
39
- picture={image.image.full}
40
- alt={image.alt}
41
- onloaded={() => (loading = false)}
42
- />
43
- </div>
37
+ {@render display(image, () => (loading = false))}
44
38
 
45
39
  <div class="headline-container">
46
40
  <Headline {piece} bind:showingDescription showName={!config.modal?.hideNames && nameInHeader} />
@@ -56,13 +50,6 @@
56
50
  </div>
57
51
 
58
52
  <style lang="postcss">
59
- .image-container {
60
- position: absolute;
61
- top: var(--info-height);
62
- bottom: 0;
63
- left: 0;
64
- right: 0;
65
- }
66
53
  .description-container {
67
54
  position: absolute;
68
55
  top: var(--info-height);
@@ -45,6 +45,10 @@
45
45
 
46
46
  const isGalleryOpen = $state(useIsModelGalleryOpen());
47
47
 
48
+ const currentIsComic = $derived(
49
+ typeof selected === 'number' ? pieces[selected]?.alts_display === 'comic_panels' : false
50
+ );
51
+
48
52
  onMount(() => {
49
53
  const handler = (e: KeyboardEvent) => {
50
54
  if (e.code === 'ArrowLeft' && selected !== null) {
@@ -57,16 +61,15 @@
57
61
  const hashchange = (hce: { oldURL?: string; newURL: string }) => {
58
62
  const hash = new URL(hce.newURL).hash.replace(/^#/, '');
59
63
  if (browser) {
60
- console.log('H ->', hash);
61
- const foundSelected = pieces.findIndex((piece) => piece.slug === hash);
64
+ const foundSelected = pieces.findIndex((piece) => piece.slug === decodeURIComponent(hash));
62
65
  if (!isAnyModelGalleryOpen()) {
63
66
  if (foundSelected !== -1) {
64
67
  selected = foundSelected;
65
68
  }
66
69
  } else if (isGalleryOpen.open) {
67
- if (!hash || hash == '#') {
70
+ if (!hash || hash == '#' || foundSelected === -1) {
68
71
  selected = null;
69
- } else if (foundSelected !== -1) {
72
+ } else {
70
73
  selected = foundSelected;
71
74
  }
72
75
  }
@@ -90,13 +93,12 @@
90
93
  if (isAnyModelGalleryOpen() === false && window.location.hash) {
91
94
  window.location.hash = '##';
92
95
  } else if (selected !== null && pieces[selected]) {
93
- console.log('H <-', pieces[selected].slug);
94
96
  window.location.hash = '#' + encodeURIComponent(pieces[selected].slug);
95
97
  }
96
98
  }
97
99
 
98
100
  $effect.pre(() => {
99
- isGalleryOpen.open = selected !== null;
101
+ isGalleryOpen.open = selected !== null && !!pieces[selected];
100
102
  });
101
103
  $effect.pre(() => {
102
104
  if (hashUpdateReady) {
@@ -105,8 +107,8 @@
105
107
  });
106
108
  </script>
107
109
 
108
- <Modal open={selected !== null} onclose={() => (selected = null)}>
109
- <div class="gallery-container">
110
+ <Modal open={selected !== null && !!pieces[selected]} onclose={() => (selected = null)}>
111
+ <div class="gallery-container" style={currentIsComic ? '--carousel-height: 25px' : ''}>
110
112
  <div
111
113
  role="button"
112
114
  tabindex={-1}
@@ -121,7 +123,7 @@
121
123
  ></div>
122
124
  <div style="flex-grow: 1"></div>
123
125
 
124
- {#if selected !== null}
126
+ {#if selected !== null && !!pieces[selected]}
125
127
  <HighResContext>
126
128
  <ImageSection
127
129
  piece={pieces[selected]}
@@ -139,6 +141,18 @@
139
141
  </Modal>
140
142
 
141
143
  <style>
144
+ @media only screen and (max-width: 800px) {
145
+ :global(:root) {
146
+ --info-height: 50px;
147
+ --carousel-height: 75px;
148
+ }
149
+ }
150
+
151
+ :global(:root) {
152
+ --info-height: 50px;
153
+ --carousel-height: 50px;
154
+ }
155
+
142
156
  .gallery-container {
143
157
  display: flex;
144
158
  flex-direction: column;
@@ -27,6 +27,7 @@ const ZMultiSelectionOption = z.object({
27
27
  multi: z.literal(true)
28
28
  });
29
29
  const ZStringOption = z.object({ type: z.literal('string') });
30
+ const ZBoolOption = z.object({ type: z.literal('boolean') });
30
31
  const ZTagsOption = z.object({ type: z.literal('tag-list') });
31
32
  const ZStringList = z.object({ type: z.literal('string-list') });
32
33
  export const ZThemeSettingsSchema = z.record(
@@ -37,7 +38,8 @@ export const ZThemeSettingsSchema = z.record(
37
38
  ZMultiSelectionOption,
38
39
  ZTagsOption,
39
40
  ZStringList,
40
- ZStringOption
41
+ ZStringOption,
42
+ ZBoolOption
41
43
  ])
42
44
  );
43
45
 
@@ -62,7 +64,9 @@ type MaterializedOptionFor<T extends ThemeSettingsSchema[string]> =
62
64
  ? Array<string>
63
65
  : T extends z.infer<typeof ZStringOption>
64
66
  ? string
65
- : never;
67
+ : T extends z.infer<typeof ZBoolOption>
68
+ ? boolean | undefined
69
+ : never;
66
70
 
67
71
  export type SettingsFor<T extends ThemeSettingsSchema> = {
68
72
  [K in keyof T]: MaterializedOptionFor<T[K]>;