@phosart/common 0.4.22

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.
Files changed (178) hide show
  1. package/README.md +58 -0
  2. package/dist/FullGallery.svelte +51 -0
  3. package/dist/FullGallery.svelte.d.ts +17 -0
  4. package/dist/FullGallery.svelte.d.ts.map +1 -0
  5. package/dist/Gallery.svelte +30 -0
  6. package/dist/Gallery.svelte.d.ts +14 -0
  7. package/dist/Gallery.svelte.d.ts.map +1 -0
  8. package/dist/GalleryPreview.svelte +60 -0
  9. package/dist/GalleryPreview.svelte.d.ts +9 -0
  10. package/dist/GalleryPreview.svelte.d.ts.map +1 -0
  11. package/dist/HighResContext.svelte +21 -0
  12. package/dist/HighResContext.svelte.d.ts +8 -0
  13. package/dist/HighResContext.svelte.d.ts.map +1 -0
  14. package/dist/Image.svelte +171 -0
  15. package/dist/Image.svelte.d.ts +14 -0
  16. package/dist/Image.svelte.d.ts.map +1 -0
  17. package/dist/Modal.svelte +87 -0
  18. package/dist/Modal.svelte.d.ts +9 -0
  19. package/dist/Modal.svelte.d.ts.map +1 -0
  20. package/dist/ModalGallery/Carousel.svelte +76 -0
  21. package/dist/ModalGallery/Carousel.svelte.d.ts +10 -0
  22. package/dist/ModalGallery/Carousel.svelte.d.ts.map +1 -0
  23. package/dist/ModalGallery/ImageSection.svelte +156 -0
  24. package/dist/ModalGallery/ImageSection.svelte.d.ts +11 -0
  25. package/dist/ModalGallery/ImageSection.svelte.d.ts.map +1 -0
  26. package/dist/ModalGallery/ImageView.svelte +92 -0
  27. package/dist/ModalGallery/ImageView.svelte.d.ts +9 -0
  28. package/dist/ModalGallery/ImageView.svelte.d.ts.map +1 -0
  29. package/dist/ModalGallery/Spinner.svelte +71 -0
  30. package/dist/ModalGallery/Spinner.svelte.d.ts +7 -0
  31. package/dist/ModalGallery/Spinner.svelte.d.ts.map +1 -0
  32. package/dist/ModalGallery.svelte +165 -0
  33. package/dist/ModalGallery.svelte.d.ts +16 -0
  34. package/dist/ModalGallery.svelte.d.ts.map +1 -0
  35. package/dist/OpengraphMeta.svelte +125 -0
  36. package/dist/OpengraphMeta.svelte.d.ts +15 -0
  37. package/dist/OpengraphMeta.svelte.d.ts.map +1 -0
  38. package/dist/Postcard/ArtistLink.svelte +46 -0
  39. package/dist/Postcard/ArtistLink.svelte.d.ts +9 -0
  40. package/dist/Postcard/ArtistLink.svelte.d.ts.map +1 -0
  41. package/dist/Postcard/Chip.svelte +100 -0
  42. package/dist/Postcard/Chip.svelte.d.ts +12 -0
  43. package/dist/Postcard/Chip.svelte.d.ts.map +1 -0
  44. package/dist/Postcard/Description/Character.svelte +79 -0
  45. package/dist/Postcard/Description/Character.svelte.d.ts +9 -0
  46. package/dist/Postcard/Description/Character.svelte.d.ts.map +1 -0
  47. package/dist/Postcard/Description.svelte +146 -0
  48. package/dist/Postcard/Description.svelte.d.ts +13 -0
  49. package/dist/Postcard/Description.svelte.d.ts.map +1 -0
  50. package/dist/Postcard/Headline.svelte +70 -0
  51. package/dist/Postcard/Headline.svelte.d.ts +10 -0
  52. package/dist/Postcard/Headline.svelte.d.ts.map +1 -0
  53. package/dist/index.d.ts +17 -0
  54. package/dist/index.d.ts.map +1 -0
  55. package/dist/index.js +17 -0
  56. package/dist/server/artist.d.ts +7 -0
  57. package/dist/server/artist.d.ts.map +1 -0
  58. package/dist/server/artist.js +40 -0
  59. package/dist/server/character.d.ts +9 -0
  60. package/dist/server/character.d.ts.map +1 -0
  61. package/dist/server/character.js +76 -0
  62. package/dist/server/directories.d.ts +4 -0
  63. package/dist/server/directories.d.ts.map +1 -0
  64. package/dist/server/directories.js +39 -0
  65. package/dist/server/fastcache.d.ts +11 -0
  66. package/dist/server/fastcache.d.ts.map +1 -0
  67. package/dist/server/fastcache.js +43 -0
  68. package/dist/server/filter.d.ts +6 -0
  69. package/dist/server/filter.d.ts.map +1 -0
  70. package/dist/server/filter.js +53 -0
  71. package/dist/server/gallery.d.ts +16 -0
  72. package/dist/server/gallery.d.ts.map +1 -0
  73. package/dist/server/gallery.js +162 -0
  74. package/dist/server/imageprocess.d.ts +18 -0
  75. package/dist/server/imageprocess.d.ts.map +1 -0
  76. package/dist/server/imageprocess.js +243 -0
  77. package/dist/server/index.d.ts +15 -0
  78. package/dist/server/index.d.ts.map +1 -0
  79. package/dist/server/index.js +15 -0
  80. package/dist/server/models/Artist.d.ts +7 -0
  81. package/dist/server/models/Artist.d.ts.map +1 -0
  82. package/dist/server/models/Artist.js +15 -0
  83. package/dist/server/models/Character.d.ts +108 -0
  84. package/dist/server/models/Character.d.ts.map +1 -0
  85. package/dist/server/models/Character.js +21 -0
  86. package/dist/server/models/Gallery.d.ts +373 -0
  87. package/dist/server/models/Gallery.d.ts.map +1 -0
  88. package/dist/server/models/Gallery.js +60 -0
  89. package/dist/server/models/image.d.ts +64 -0
  90. package/dist/server/models/image.d.ts.map +1 -0
  91. package/dist/server/models/image.js +17 -0
  92. package/dist/server/pack.d.ts +3 -0
  93. package/dist/server/pack.d.ts.map +1 -0
  94. package/dist/server/pack.js +26 -0
  95. package/dist/server/theme/schema.d.ts +57 -0
  96. package/dist/server/theme/schema.d.ts.map +1 -0
  97. package/dist/server/theme/schema.js +217 -0
  98. package/dist/server/util.d.ts +24 -0
  99. package/dist/server/util.d.ts.map +1 -0
  100. package/dist/server/util.js +71 -0
  101. package/dist/util/art.d.ts +52 -0
  102. package/dist/util/art.d.ts.map +1 -0
  103. package/dist/util/art.js +57 -0
  104. package/dist/util/artistcontext.svelte.d.ts +8 -0
  105. package/dist/util/artistcontext.svelte.d.ts.map +1 -0
  106. package/dist/util/artistcontext.svelte.js +18 -0
  107. package/dist/util/charactercontext.svelte.d.ts +4 -0
  108. package/dist/util/charactercontext.svelte.d.ts.map +1 -0
  109. package/dist/util/charactercontext.svelte.js +11 -0
  110. package/dist/util/date.d.ts +2 -0
  111. package/dist/util/date.d.ts.map +1 -0
  112. package/dist/util/date.js +6 -0
  113. package/dist/util/index.d.ts +13 -0
  114. package/dist/util/index.d.ts.map +1 -0
  115. package/dist/util/index.js +10 -0
  116. package/dist/util/markdown.d.ts +2 -0
  117. package/dist/util/markdown.d.ts.map +1 -0
  118. package/dist/util/markdown.js +16 -0
  119. package/dist/util/phosart_config.svelte.d.ts +44 -0
  120. package/dist/util/phosart_config.svelte.d.ts.map +1 -0
  121. package/dist/util/phosart_config.svelte.js +51 -0
  122. package/dist/util/search.d.ts +3 -0
  123. package/dist/util/search.d.ts.map +1 -0
  124. package/dist/util/search.js +24 -0
  125. package/dist/util/smoothscroll.d.ts +4 -0
  126. package/dist/util/smoothscroll.d.ts.map +1 -0
  127. package/dist/util/smoothscroll.js +21 -0
  128. package/dist/util/tree.d.ts +19 -0
  129. package/dist/util/tree.d.ts.map +1 -0
  130. package/dist/util/tree.js +58 -0
  131. package/dist/util/util.d.ts +4 -0
  132. package/dist/util/util.d.ts.map +1 -0
  133. package/dist/util/util.js +22 -0
  134. package/package.json +102 -0
  135. package/src/lib/FullGallery.svelte +51 -0
  136. package/src/lib/Gallery.svelte +30 -0
  137. package/src/lib/GalleryPreview.svelte +60 -0
  138. package/src/lib/HighResContext.svelte +21 -0
  139. package/src/lib/Image.svelte +171 -0
  140. package/src/lib/Modal.svelte +87 -0
  141. package/src/lib/ModalGallery/Carousel.svelte +76 -0
  142. package/src/lib/ModalGallery/ImageSection.svelte +156 -0
  143. package/src/lib/ModalGallery/ImageView.svelte +92 -0
  144. package/src/lib/ModalGallery/Spinner.svelte +71 -0
  145. package/src/lib/ModalGallery.svelte +165 -0
  146. package/src/lib/OpengraphMeta.svelte +125 -0
  147. package/src/lib/Postcard/ArtistLink.svelte +46 -0
  148. package/src/lib/Postcard/Chip.svelte +100 -0
  149. package/src/lib/Postcard/Description/Character.svelte +79 -0
  150. package/src/lib/Postcard/Description.svelte +146 -0
  151. package/src/lib/Postcard/Headline.svelte +70 -0
  152. package/src/lib/index.ts +20 -0
  153. package/src/lib/server/artist.ts +50 -0
  154. package/src/lib/server/character.ts +113 -0
  155. package/src/lib/server/directories.ts +45 -0
  156. package/src/lib/server/fastcache.ts +66 -0
  157. package/src/lib/server/filter.ts +71 -0
  158. package/src/lib/server/gallery.ts +259 -0
  159. package/src/lib/server/imageprocess.ts +382 -0
  160. package/src/lib/server/index.ts +57 -0
  161. package/src/lib/server/models/Artist.ts +19 -0
  162. package/src/lib/server/models/Character.ts +24 -0
  163. package/src/lib/server/models/Gallery.ts +70 -0
  164. package/src/lib/server/models/image.ts +20 -0
  165. package/src/lib/server/pack.ts +31 -0
  166. package/src/lib/server/theme/schema.ts +286 -0
  167. package/src/lib/server/util.ts +102 -0
  168. package/src/lib/util/art.ts +136 -0
  169. package/src/lib/util/artistcontext.svelte.ts +25 -0
  170. package/src/lib/util/charactercontext.svelte.ts +15 -0
  171. package/src/lib/util/date.ts +7 -0
  172. package/src/lib/util/index.ts +29 -0
  173. package/src/lib/util/markdown.ts +17 -0
  174. package/src/lib/util/phosart_config.svelte.ts +101 -0
  175. package/src/lib/util/search.ts +28 -0
  176. package/src/lib/util/smoothscroll.ts +21 -0
  177. package/src/lib/util/tree.ts +75 -0
  178. package/src/lib/util/util.ts +37 -0
@@ -0,0 +1,156 @@
1
+ <script lang="ts">
2
+ import '@fortawesome/fontawesome-free/css/all.min.css';
3
+
4
+ import type { ArtPiece } from '../util/art.js';
5
+ import ImageView from './ImageView.svelte';
6
+
7
+ interface Props {
8
+ piece: ArtPiece;
9
+ onnext: () => void;
10
+ onprev: () => void;
11
+ }
12
+
13
+ let { piece, onnext, onprev }: Props = $props();
14
+
15
+ function doOnNext(e: Event) {
16
+ e.stopPropagation();
17
+ onnext();
18
+ }
19
+ function doOnPrev(e: Event) {
20
+ e.stopPropagation();
21
+ onprev();
22
+ }
23
+
24
+ let containerWidth: number = $state(0);
25
+ let containerHeight: number = $state(0);
26
+ let containerHeightLessInfo = $derived(Math.max(0, containerHeight - 50));
27
+
28
+ let scaleByHeight = $derived(
29
+ containerWidth / containerHeightLessInfo >
30
+ piece.image.full.fallback.w / piece.image.full.fallback.h
31
+ );
32
+ let scalingFactor: number = $derived(
33
+ scaleByHeight
34
+ ? containerHeightLessInfo / piece.image.full.fallback.h
35
+ : containerWidth / piece.image.full.fallback.w
36
+ );
37
+
38
+ let w = $derived(piece.image.full.fallback.w * scalingFactor);
39
+ let h = $derived(piece.image.full.fallback.h * scalingFactor + 50);
40
+
41
+ let nameInHeader = $derived(w > 500);
42
+ </script>
43
+
44
+ <div class="image-section">
45
+ <div class="nav-container">
46
+ <div
47
+ class="fa-solid fa-arrow-left hoverable"
48
+ style="font-size: 32pt;"
49
+ onclick={doOnPrev}
50
+ onkeypress={doOnPrev}
51
+ role="button"
52
+ tabindex={-1}
53
+ ></div>
54
+ </div>
55
+
56
+ <div class="main-container">
57
+ <div class="bounding-div" bind:clientHeight={containerHeight} bind:clientWidth={containerWidth}>
58
+ <div
59
+ class="bounded-div"
60
+ onclick={(e) => e.stopPropagation()}
61
+ onkeypress={(e) => e.stopPropagation()}
62
+ role="button"
63
+ tabindex={-1}
64
+ style="width: {w}px; height: {h}px"
65
+ >
66
+ <ImageView {piece} {nameInHeader} />
67
+ </div>
68
+ </div>
69
+ </div>
70
+
71
+ <div class="nav-container">
72
+ <div
73
+ class="fa-solid fa-arrow-right hoverable"
74
+ style="font-size: 32pt;"
75
+ onclick={doOnNext}
76
+ onkeypress={doOnPrev}
77
+ role="button"
78
+ tabindex={-1}
79
+ ></div>
80
+ </div>
81
+ </div>
82
+
83
+ <style>
84
+ :global(:root) {
85
+ --info-height: 50px;
86
+ --carousel-height: 100px;
87
+ }
88
+
89
+ @media only screen and (max-width: 800px) {
90
+ :global(:root) {
91
+ --info-height: 50px;
92
+ --carousel-height: 75px;
93
+ }
94
+ .nav-container.nav-container {
95
+ width: 0;
96
+ }
97
+ }
98
+
99
+ .image-section {
100
+ display: flex;
101
+ flex-direction: row;
102
+ justify-content: center;
103
+ align-items: center;
104
+ overflow: visible;
105
+ max-height: calc(100% - 50px - var(--carousel-height));
106
+ position: relative;
107
+ height: 100%;
108
+ padding: 2rem;
109
+ }
110
+ .nav-container {
111
+ color: white;
112
+ height: min-content;
113
+ width: 50px;
114
+ display: flex;
115
+ flex-direction: column;
116
+ justify-content: center;
117
+ align-items: center;
118
+ transition: width 0.2s ease-in-out;
119
+ overflow: hidden;
120
+ }
121
+
122
+ .hoverable {
123
+ color: gray;
124
+ cursor: pointer;
125
+ }
126
+ .hoverable:hover {
127
+ color: white;
128
+ }
129
+
130
+ .main-container {
131
+ flex-grow: 1;
132
+ height: 100%;
133
+ display: flex;
134
+ flex-direction: row;
135
+ justify-content: center;
136
+ align-items: center;
137
+ overflow: hidden;
138
+ }
139
+
140
+ .bounding-div {
141
+ flex-grow: 1;
142
+ height: 100%;
143
+ position: relative;
144
+ overflow: hidden;
145
+ display: flex;
146
+ justify-content: center;
147
+ align-items: center;
148
+ }
149
+
150
+ .bounded-div {
151
+ overflow: hidden;
152
+ border-radius: 12px;
153
+ display: flex;
154
+ position: relative;
155
+ }
156
+ </style>
@@ -0,0 +1,11 @@
1
+ import '@fortawesome/fontawesome-free/css/all.min.css';
2
+ import type { ArtPiece } from '../util/art.ts';
3
+ interface Props {
4
+ piece: ArtPiece;
5
+ onnext: () => void;
6
+ onprev: () => void;
7
+ }
8
+ declare const ImageSection: import("svelte").Component<Props, {}, "">;
9
+ type ImageSection = ReturnType<typeof ImageSection>;
10
+ export default ImageSection;
11
+ //# sourceMappingURL=ImageSection.svelte.d.ts.map
@@ -0,0 +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;AAI9C,UAAU,KAAK;IACd,KAAK,EAAE,QAAQ,CAAC;IAChB,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,MAAM,EAAE,MAAM,IAAI,CAAC;CACnB;AA6DF,QAAA,MAAM,YAAY,2CAAwC,CAAC;AAC3D,KAAK,YAAY,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;AACpD,eAAe,YAAY,CAAC"}
@@ -0,0 +1,92 @@
1
+ <script lang="ts">
2
+ import { run } from 'svelte/legacy';
3
+
4
+ import Image from '../Image.svelte';
5
+
6
+ import Spinner from './Spinner.svelte';
7
+ import Headline from '../Postcard/Headline.svelte';
8
+ import Description from '../Postcard/Description.svelte';
9
+ import type { ArtPiece } from '../util/art.js';
10
+
11
+ interface Props {
12
+ piece: ArtPiece;
13
+ nameInHeader: boolean;
14
+ }
15
+
16
+ let { piece, nameInHeader }: Props = $props();
17
+
18
+ let loading = $state(true);
19
+ let showingDescription = $state(false);
20
+
21
+ let selectedAlt: number | null = $state(null);
22
+
23
+ let image = $derived(typeof selectedAlt === 'number' ? piece.alts![selectedAlt] : piece);
24
+
25
+ run(() => {
26
+ if (piece) loading = true;
27
+ });
28
+ </script>
29
+
30
+ <Spinner {loading} />
31
+
32
+ <div class="image-container">
33
+ <Image
34
+ video={image.video?.full}
35
+ controls
36
+ picture={image.image.full}
37
+ alt={image.alt}
38
+ onloaded={() => (loading = false)}
39
+ />
40
+ </div>
41
+
42
+ <div class="headline-container">
43
+ <Headline {piece} bind:showingDescription showName={nameInHeader} />
44
+ </div>
45
+ <div class="description-container">
46
+ <Description
47
+ {piece}
48
+ bind:visible={showingDescription}
49
+ showName={!nameInHeader}
50
+ {selectedAlt}
51
+ onselectalt={(i) => (selectedAlt = i ?? null)}
52
+ />
53
+ </div>
54
+
55
+ <style>
56
+ .image-container {
57
+ position: absolute;
58
+ top: var(--info-height);
59
+ bottom: 0;
60
+ left: 0;
61
+ right: 0;
62
+ }
63
+ .description-container {
64
+ position: absolute;
65
+ top: var(--info-height);
66
+ height: calc(100% - var(--info-height));
67
+ left: 0;
68
+ right: 0;
69
+ color: #ddd;
70
+ overflow-x: hidden;
71
+ overflow-y: auto;
72
+
73
+ & > :global(*) {
74
+ background-color: #000c;
75
+ }
76
+ :global(strong) {
77
+ color: white;
78
+ }
79
+ pointer-events: none;
80
+ }
81
+
82
+ .headline-container {
83
+ position: absolute;
84
+ top: 0;
85
+ left: 0;
86
+ right: 0;
87
+ color: white;
88
+ background-color: black;
89
+ height: var(--info-height);
90
+ z-index: 9;
91
+ }
92
+ </style>
@@ -0,0 +1,9 @@
1
+ import type { ArtPiece } from '../util/art.ts';
2
+ interface Props {
3
+ piece: ArtPiece;
4
+ nameInHeader: boolean;
5
+ }
6
+ declare const ImageView: import("svelte").Component<Props, {}, "">;
7
+ type ImageView = ReturnType<typeof ImageView>;
8
+ export default ImageView;
9
+ //# sourceMappingURL=ImageView.svelte.d.ts.map
@@ -0,0 +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;AAG9C,UAAU,KAAK;IACd,KAAK,EAAE,QAAQ,CAAC;IAChB,YAAY,EAAE,OAAO,CAAC;CACtB;AA2CF,QAAA,MAAM,SAAS,2CAAwC,CAAC;AACxD,KAAK,SAAS,GAAG,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC;AAC9C,eAAe,SAAS,CAAC"}
@@ -0,0 +1,71 @@
1
+ <script lang="ts">
2
+ interface Props {
3
+ loading: boolean;
4
+ }
5
+
6
+ let { loading }: Props = $props();
7
+ </script>
8
+
9
+ <svg class:loading class="loading-spinner" xmlns="http://www.w3.org/2000/svg">
10
+ <rect
11
+ width="100%"
12
+ height="100%"
13
+ fill="none"
14
+ stroke="#278965"
15
+ stroke-linecap="butt"
16
+ rx="12px"
17
+ ry="12px"
18
+ />
19
+ <rect
20
+ width="100%"
21
+ height="100%"
22
+ fill="none"
23
+ stroke="#3EB489"
24
+ stroke-dasharray="40, 100"
25
+ stroke-linecap="butt"
26
+ rx="12px"
27
+ ry="12px"
28
+ />
29
+ </svg>
30
+
31
+ <style>
32
+ .loading-spinner {
33
+ position: absolute;
34
+ left: 0;
35
+ top: 0;
36
+ width: 100%;
37
+ height: 100%;
38
+ border-radius: 12px;
39
+ z-index: 10;
40
+ opacity: 0;
41
+ transition: opacity 0.2s;
42
+ pointer-events: none;
43
+
44
+ rect {
45
+ transition: stroke-width 0.2s;
46
+ stroke-width: 0;
47
+ }
48
+
49
+ &.loading {
50
+ opacity: 1;
51
+
52
+ rect {
53
+ stroke-width: 4px;
54
+ }
55
+ rect:last-of-type {
56
+ stroke-dashoffset: 600;
57
+ stroke-dasharray: 200, 80;
58
+ animation: svgloader forwards linear infinite 0.5s;
59
+ }
60
+ }
61
+ }
62
+
63
+ @keyframes svgloader {
64
+ from {
65
+ stroke-dashoffset: 280;
66
+ }
67
+ to {
68
+ stroke-dashoffset: 0;
69
+ }
70
+ }
71
+ </style>
@@ -0,0 +1,7 @@
1
+ interface Props {
2
+ loading: boolean;
3
+ }
4
+ declare const Spinner: import("svelte").Component<Props, {}, "">;
5
+ type Spinner = ReturnType<typeof Spinner>;
6
+ export default Spinner;
7
+ //# sourceMappingURL=Spinner.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Spinner.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/ModalGallery/Spinner.svelte.ts"],"names":[],"mappings":"AAGC,UAAU,KAAK;IACd,OAAO,EAAE,OAAO,CAAC;CACjB;AAgBF,QAAA,MAAM,OAAO,2CAAwC,CAAC;AACtD,KAAK,OAAO,GAAG,UAAU,CAAC,OAAO,OAAO,CAAC,CAAC;AAC1C,eAAe,OAAO,CAAC"}
@@ -0,0 +1,165 @@
1
+ <script lang="ts" module>
2
+ import '@fortawesome/fontawesome-free/css/all.min.css';
3
+
4
+ const modelGalleryOpens: boolean[] = [];
5
+
6
+ export function useIsModelGalleryOpen() {
7
+ const idx = modelGalleryOpens.length;
8
+ modelGalleryOpens.push(false);
9
+ return {
10
+ set open(isOpen: boolean) {
11
+ modelGalleryOpens[idx] = isOpen;
12
+ },
13
+ get open() {
14
+ return modelGalleryOpens[idx];
15
+ }
16
+ };
17
+ }
18
+
19
+ export function isAnyModelGalleryOpen() {
20
+ return modelGalleryOpens.reduce((p, v) => p || v, false);
21
+ }
22
+ </script>
23
+
24
+ <script lang="ts">
25
+ import type { ArtPiece } from './util/art.js';
26
+ import Modal from './Modal.svelte';
27
+ import { onMount } from 'svelte';
28
+ import ImageSection from './ModalGallery/ImageSection.svelte';
29
+ import Carousel from './ModalGallery/Carousel.svelte';
30
+ import HighResContext from './HighResContext.svelte';
31
+
32
+ interface Props {
33
+ pieces: ArtPiece[];
34
+ selected?: number | null;
35
+ hashUpdateReady?: boolean;
36
+ browser: boolean;
37
+ }
38
+
39
+ let {
40
+ pieces,
41
+ selected = $bindable(null),
42
+ hashUpdateReady = $bindable(false),
43
+ browser
44
+ }: Props = $props();
45
+
46
+ const isGalleryOpen = $state(useIsModelGalleryOpen());
47
+
48
+ onMount(() => {
49
+ const handler = (e: KeyboardEvent) => {
50
+ if (e.code === 'ArrowLeft' && selected !== null) {
51
+ selected = (selected - 1 + pieces.length) % pieces.length;
52
+ } else if (e.code === 'ArrowRight' && selected !== null) {
53
+ selected = (selected + 1) % pieces.length;
54
+ }
55
+ };
56
+
57
+ const hashchange = (hce: { oldURL?: string; newURL: string }) => {
58
+ const hash = new URL(hce.newURL).hash.replace(/^#/, '');
59
+ if (browser) {
60
+ console.log('H ->', hash);
61
+ const foundSelected = pieces.findIndex((piece) => piece.slug === hash);
62
+ if (!isAnyModelGalleryOpen()) {
63
+ if (foundSelected !== -1) {
64
+ selected = foundSelected;
65
+ }
66
+ } else if (isGalleryOpen.open) {
67
+ if (!hash || hash == '#') {
68
+ selected = null;
69
+ } else if (foundSelected !== -1) {
70
+ selected = foundSelected;
71
+ }
72
+ }
73
+ }
74
+ };
75
+ hashchange({ newURL: window.location.href });
76
+
77
+ window.addEventListener('hashchange', hashchange);
78
+ document.body.addEventListener('keydown', handler);
79
+
80
+ hashUpdateReady = true;
81
+ return () => {
82
+ window.removeEventListener('hashchange', hashchange);
83
+ document.body.removeEventListener('keydown', handler);
84
+ };
85
+ });
86
+
87
+ function updateHash(selected: number | null) {
88
+ if (!browser) return;
89
+
90
+ if (isAnyModelGalleryOpen() === false && window.location.hash) {
91
+ window.location.hash = '##';
92
+ } else if (selected !== null && pieces[selected]) {
93
+ console.log('H <-', pieces[selected].slug);
94
+ window.location.hash = '#' + encodeURIComponent(pieces[selected].slug);
95
+ }
96
+ }
97
+
98
+ $effect.pre(() => {
99
+ isGalleryOpen.open = selected !== null;
100
+ });
101
+ $effect.pre(() => {
102
+ if (hashUpdateReady) {
103
+ updateHash(selected);
104
+ }
105
+ });
106
+ </script>
107
+
108
+ <Modal open={selected !== null} onclose={() => (selected = null)}>
109
+ <div class="gallery-container">
110
+ <div
111
+ role="button"
112
+ tabindex={-1}
113
+ class="fa-solid fa-close hoverable close-button"
114
+ onclick={(e) => {
115
+ e.stopPropagation();
116
+ selected = null;
117
+ }}
118
+ onkeypress={() => {
119
+ selected = null;
120
+ }}
121
+ ></div>
122
+ <div style="flex-grow: 1"></div>
123
+
124
+ {#if selected !== null}
125
+ <HighResContext>
126
+ <ImageSection
127
+ piece={pieces[selected]}
128
+ onprev={() =>
129
+ (selected = selected !== null ? (selected - 1 + pieces.length) % pieces.length : null)}
130
+ onnext={() => (selected = selected !== null ? (selected + 1) % pieces.length : null)}
131
+ />
132
+ </HighResContext>
133
+ {/if}
134
+
135
+ <div style="flex-grow: 1"></div>
136
+
137
+ <Carousel {pieces} {browser} bind:selected />
138
+ </div>
139
+ </Modal>
140
+
141
+ <style>
142
+ .gallery-container {
143
+ display: flex;
144
+ flex-direction: column;
145
+ width: 100%;
146
+ height: 100%;
147
+ user-select: none;
148
+ }
149
+
150
+ .close-button {
151
+ font-size: 20pt;
152
+ position: fixed;
153
+ top: 20px;
154
+ left: 20px;
155
+ z-index: 40;
156
+ }
157
+
158
+ .hoverable {
159
+ color: gray;
160
+ cursor: pointer;
161
+ }
162
+ .hoverable:hover {
163
+ color: white;
164
+ }
165
+ </style>
@@ -0,0 +1,16 @@
1
+ import '@fortawesome/fontawesome-free/css/all.min.css';
2
+ export declare function useIsModelGalleryOpen(): {
3
+ open: boolean;
4
+ };
5
+ export declare function isAnyModelGalleryOpen(): boolean;
6
+ import type { ArtPiece } from './util/art.ts';
7
+ interface Props {
8
+ pieces: ArtPiece[];
9
+ selected?: number | null;
10
+ hashUpdateReady?: boolean;
11
+ browser: boolean;
12
+ }
13
+ declare const ModalGallery: import("svelte").Component<Props, {}, "selected" | "hashUpdateReady">;
14
+ type ModalGallery = ReturnType<typeof ModalGallery>;
15
+ export default ModalGallery;
16
+ //# sourceMappingURL=ModalGallery.svelte.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,125 @@
1
+ <script lang="ts" module>
2
+ import { asString } from './Postcard/ArtistLink.svelte';
3
+ import { normalizeArtist, type ResourceRef } from './util/art.js';
4
+ import { useLibraryConfig } from './util/phosart_config.svelte.js';
5
+
6
+ interface OpengraphMeta {
7
+ title: string | null;
8
+ description: string | null;
9
+ siteName: string | null;
10
+ image: string | null;
11
+ width: number | null;
12
+ height: number | null;
13
+ }
14
+
15
+ function getMeta(resource: ResourceRef, siteName?: string | null): OpengraphMeta {
16
+ const base: OpengraphMeta = {
17
+ title: null,
18
+ description: null,
19
+ siteName: siteName ?? null,
20
+ image: null,
21
+ width: null,
22
+ height: null
23
+ };
24
+
25
+ if (!resource.type || !resource.resource) {
26
+ return base;
27
+ }
28
+
29
+ switch (resource.type) {
30
+ case 'artist':
31
+ base.title = `@${resource.resource.info?.name ?? resource.resource.name}`;
32
+ break;
33
+ case 'character': {
34
+ base.title = `${resource.resource.info?.name ?? resource.resource.name}`;
35
+ if (resource.resource.from) {
36
+ base.title += ` by @${resource.resource.from}`;
37
+ }
38
+ const image =
39
+ resource.resource.info?.thumbnail?.full.fallback ??
40
+ resource.resource.info?.picture.full.fallback ??
41
+ null;
42
+
43
+ base.image = image?.src ?? null;
44
+ base.width = image?.w ?? null;
45
+ base.height = image?.h ?? null;
46
+ base.description =
47
+ resource.resource.info?.short_description ?? resource.resource.info?.description ?? null;
48
+
49
+ break;
50
+ }
51
+ case 'piece': {
52
+ base.title = resource.resource.name;
53
+ const artists = normalizeArtist(resource.resource.artist);
54
+ if (artists.length > 0) {
55
+ base.title += ` by ${asString(artists)}`;
56
+ }
57
+ base.description = resource.resource.description || resource.resource.alt;
58
+ base.image = resource.resource.image.full.fallback.src;
59
+ base.width = resource.resource.image.full.fallback.w;
60
+ base.height = resource.resource.image.full.fallback.h;
61
+ break;
62
+ }
63
+ case 'tag':
64
+ base.title = `#${resource.resource}`;
65
+ break;
66
+ }
67
+
68
+ if (base.siteName) {
69
+ base.title += ` | ${base.siteName}`;
70
+ }
71
+ return base;
72
+ }
73
+ </script>
74
+
75
+ <script lang="ts">
76
+ type Props = ResourceRef &
77
+ // If unspecified, the following will be inferred from above
78
+ Partial<OpengraphMeta> & {
79
+ setPageTitle?: boolean;
80
+ };
81
+
82
+ const {
83
+ setPageTitle,
84
+ height: userHeight,
85
+ image: userImage,
86
+ siteName: userSiteName,
87
+ description: userDescription,
88
+ title: userTitle,
89
+ width: userWidth,
90
+ ...resourceRef
91
+ }: Props = $props();
92
+
93
+ const config = useLibraryConfig();
94
+
95
+ const inferred = getMeta(resourceRef, config.siteName);
96
+ const height = $derived(userHeight ?? inferred.height);
97
+ const image = $derived(userImage ?? inferred.image);
98
+ const siteName = $derived(userSiteName ?? inferred.siteName);
99
+ const description = $derived(userDescription ?? inferred.description);
100
+ const title = $derived(userTitle ?? inferred.title);
101
+ const width = $derived(userWidth ?? inferred.width);
102
+ </script>
103
+
104
+ <svelte:head>
105
+ {#if resourceRef.type && resourceRef.resource}
106
+ {#if siteName}
107
+ <meta property="og:site_name" content={siteName} />
108
+ {/if}
109
+ {#if title}
110
+ <meta property="og:title" content={title} />
111
+ {#if setPageTitle}
112
+ <title>{title}</title>
113
+ {/if}
114
+ {/if}
115
+ {#if description}
116
+ <meta property="og:description" content={description} />
117
+ {/if}
118
+ {#if image && width && height}
119
+ <meta property="og:image" content={image} />
120
+ <meta property="og:image:width" content={String(width)} />
121
+ <meta property="og:image:height" content={String(height)} />
122
+ <meta name="twitter:card" content="summary_large_image" />
123
+ {/if}
124
+ {/if}
125
+ </svelte:head>
@@ -0,0 +1,15 @@
1
+ import { type ResourceRef } from './util/art.ts';
2
+ interface OpengraphMeta {
3
+ title: string | null;
4
+ description: string | null;
5
+ siteName: string | null;
6
+ image: string | null;
7
+ width: number | null;
8
+ height: number | null;
9
+ }
10
+ type Props = ResourceRef & Partial<OpengraphMeta> & {
11
+ setPageTitle?: boolean;
12
+ };
13
+ declare const OpengraphMeta: import("svelte").Component<Props, {}, "">;
14
+ export default OpengraphMeta;
15
+ //# sourceMappingURL=OpengraphMeta.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"OpengraphMeta.svelte.d.ts","sourceRoot":"","sources":["../src/lib/OpengraphMeta.svelte.ts"],"names":[],"mappings":"AAIC,OAAO,EAAmB,KAAK,WAAW,EAAE,MAAM,eAAe,CAAC;AAGlE,UAAU,aAAa;IACtB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AA6DD,KAAK,KAAK,GAAG,WAAW,GAEvB,OAAO,CAAC,aAAa,CAAC,GAAG;IACxB,YAAY,CAAC,EAAE,OAAO,CAAC;CACvB,CAAC;AAqDJ,QAAA,MAAM,aAAa,2CAAwC,CAAC;AAE5D,eAAe,aAAa,CAAC"}