@projektemacher/storymapjs 0.10.8 → 0.11.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.
Files changed (195) hide show
  1. package/CHANGELOG +81 -0
  2. package/README.md +129 -12
  3. package/dist/assets/{demo-DBpeF31K.js → demo-bWnOyfUw.js} +1 -1
  4. package/dist/assets/harness-C3JUv5Ii.js +1 -0
  5. package/dist/assets/{harness-multi-CYHgI0a9.js → harness-multi-XZNex3tb.js} +1 -1
  6. package/dist/assets/{harness-support-Do7sIp2j.js → harness-support-CjfZIVeb.js} +1 -1
  7. package/dist/assets/main-DQ32hWBM.js +17 -0
  8. package/dist/context.json +13 -2
  9. package/dist/css/storymap.css +1 -1
  10. package/dist/demo.html +3 -2
  11. package/dist/docs/api/assets/navigation.js +1 -1
  12. package/dist/docs/api/assets/search.js +1 -1
  13. package/dist/docs/api/classes/StoryMap.html +777 -269
  14. package/dist/docs/api/functions/MediaType.html +1 -1
  15. package/dist/docs/api/functions/isPresentation3Collection.html +1 -1
  16. package/dist/docs/api/functions/isPresentation3Manifest.html +1 -1
  17. package/dist/docs/api/functions/isSlideshowCollection.html +51 -0
  18. package/dist/docs/api/functions/loadCSS.html +1 -1
  19. package/dist/docs/api/functions/manifestToStorymapData.html +1 -1
  20. package/dist/docs/api/functions/setLanguage.html +1 -1
  21. package/dist/docs/api/functions/slideshowToStorymapData.html +51 -0
  22. package/dist/docs/api/functions/slideshowVersion.html +48 -0
  23. package/dist/docs/api/functions/storymapToManifest.html +1 -1
  24. package/dist/docs/api/functions/validateStorymap.html +1 -1
  25. package/dist/docs/api/functions/validateStorymapAndReport.html +1 -1
  26. package/dist/docs/api/index.html +115 -12
  27. package/dist/docs/api/interfaces/AnimationHandle.html +2 -2
  28. package/dist/docs/api/interfaces/ImagereadyPayload.html +4 -4
  29. package/dist/docs/api/interfaces/LanguageEntry.html +6 -6
  30. package/dist/docs/api/interfaces/LatLngLiteral.html +4 -4
  31. package/dist/docs/api/interfaces/LoadOptions.html +2 -2
  32. package/dist/docs/api/interfaces/MapEvents.html +116 -0
  33. package/dist/docs/api/interfaces/MapMarkerEvents.html +66 -0
  34. package/dist/docs/api/interfaces/MarkerEventPayload.html +57 -0
  35. package/dist/docs/api/interfaces/MediaEvents.html +78 -0
  36. package/dist/docs/api/interfaces/MenuBarEvents.html +114 -0
  37. package/dist/docs/api/interfaces/MessageEvents.html +72 -0
  38. package/dist/docs/api/interfaces/OlLayer.html +1 -1
  39. package/dist/docs/api/interfaces/OlMap.html +1 -1
  40. package/dist/docs/api/interfaces/OlSource.html +1 -1
  41. package/dist/docs/api/interfaces/OlTileLayer.html +1 -1
  42. package/dist/docs/api/interfaces/OlVectorLayer.html +1 -1
  43. package/dist/docs/api/interfaces/OlView.html +1 -1
  44. package/dist/docs/api/interfaces/SlideBackgroundState.html +66 -0
  45. package/dist/docs/api/interfaces/SlideEvents.html +78 -0
  46. package/dist/docs/api/interfaces/SlideNavEvents.html +72 -0
  47. package/dist/docs/api/interfaces/SlideshowPlayerSettings.html +182 -0
  48. package/dist/docs/api/interfaces/SlideshowToStorymapResult.html +60 -0
  49. package/dist/docs/api/interfaces/SlideshowTranslatorOptions.html +65 -0
  50. package/dist/docs/api/interfaces/SlideshowWarning.html +72 -0
  51. package/dist/docs/api/interfaces/StoryMapEvents.html +146 -0
  52. package/dist/docs/api/interfaces/StoryMapMarker.html +763 -0
  53. package/dist/docs/api/interfaces/StorySliderEvents.html +102 -0
  54. package/dist/docs/api/interfaces/StorymapBasemap.html +69 -0
  55. package/dist/docs/api/interfaces/StorymapData.html +12 -6
  56. package/dist/docs/api/interfaces/StorymapDataWrapper.html +2 -2
  57. package/dist/docs/api/interfaces/StorymapDocument.html +2 -2
  58. package/dist/docs/api/interfaces/StorymapError.html +3 -3
  59. package/dist/docs/api/interfaces/StorymapGeoreference.html +5 -5
  60. package/dist/docs/api/interfaces/StorymapGeoreferenceBody.html +4 -4
  61. package/dist/docs/api/interfaces/StorymapGroundControlPoint.html +4 -4
  62. package/dist/docs/api/interfaces/StorymapLanguageMap.html +2 -2
  63. package/dist/docs/api/interfaces/StorymapManifest.html +12 -12
  64. package/dist/docs/api/interfaces/StorymapManifestAnnotation.html +43 -11
  65. package/dist/docs/api/interfaces/StorymapManifestBackground.html +6 -6
  66. package/dist/docs/api/interfaces/StorymapManifestCanvas.html +65 -17
  67. package/dist/docs/api/interfaces/StorymapManifestContentResource.html +8 -8
  68. package/dist/docs/api/interfaces/StorymapManifestFeatureCollection.html +4 -4
  69. package/dist/docs/api/interfaces/StorymapManifestGeoreferencing.html +7 -7
  70. package/dist/docs/api/interfaces/StorymapManifestImageService.html +4 -4
  71. package/dist/docs/api/interfaces/StorymapManifestRange.html +5 -5
  72. package/dist/docs/api/interfaces/StorymapManifestService.html +4 -4
  73. package/dist/docs/api/interfaces/StorymapManifestSpecificResource.html +4 -4
  74. package/dist/docs/api/interfaces/StorymapManifestStatement.html +3 -3
  75. package/dist/docs/api/interfaces/StorymapMapOptions.html +5 -5
  76. package/dist/docs/api/interfaces/StorymapOptions.html +277 -75
  77. package/dist/docs/api/interfaces/StorymapOverlayLayer.html +42 -11
  78. package/dist/docs/api/interfaces/StorymapSlide.html +70 -16
  79. package/dist/docs/api/interfaces/StorymapSlideBackground.html +4 -4
  80. package/dist/docs/api/interfaces/StorymapSlideLocation.html +58 -14
  81. package/dist/docs/api/interfaces/StorymapSlideMarker.html +8 -9
  82. package/dist/docs/api/interfaces/StorymapSlideMedia.html +49 -10
  83. package/dist/docs/api/interfaces/StorymapSlideNarration.html +61 -9
  84. package/dist/docs/api/interfaces/StorymapSlideText.html +4 -4
  85. package/dist/docs/api/interfaces/StorymapTilejson.html +8 -8
  86. package/dist/docs/api/interfaces/SwipableEvents.html +102 -0
  87. package/dist/docs/api/interfaces/TextEvents.html +66 -0
  88. package/dist/docs/api/media/events.md +66 -0
  89. package/dist/docs/api/media/migration-from-knightlab.md +89 -49
  90. package/dist/docs/api/media/slideshow.md +109 -0
  91. package/dist/docs/api/media/storymap-as-iiif-manifest.md +1007 -0
  92. package/dist/docs/api/modules.html +6 -6
  93. package/dist/docs/api/types/ContentState.html +3 -3
  94. package/dist/docs/api/types/FiredEvent.html +43 -0
  95. package/dist/docs/api/types/ImagereadyKind.html +1 -1
  96. package/dist/docs/api/types/MediaTypeMatch.html +5 -5
  97. package/dist/docs/api/types/SwipeDirection.html +36 -0
  98. package/dist/docs/api/types/TileSourceFactory.html +1 -1
  99. package/dist/docs/authoring.html +20 -7
  100. package/dist/docs/events.html +263 -0
  101. package/dist/docs/iiif.html +136 -22
  102. package/dist/docs/migration.html +66 -19
  103. package/dist/docs/readme.html +113 -12
  104. package/dist/embed/index.html +7 -0
  105. package/dist/examples/font-css-false.json +33 -0
  106. package/dist/examples/issue-image-region.json +1 -1
  107. package/dist/examples/layers-control.json +60 -0
  108. package/dist/examples/map-template-provider.json +36 -0
  109. package/dist/examples/multi-media.json +71 -0
  110. package/dist/examples-iiif/annotated-image.json +16 -0
  111. package/dist/examples-iiif/font-css-false.json +114 -0
  112. package/dist/examples-iiif/issue-image-region.json +1 -1
  113. package/dist/examples-iiif/layers-control.json +226 -0
  114. package/dist/examples-iiif/map-template-provider.json +133 -0
  115. package/dist/examples-iiif/multi-media.json +237 -0
  116. package/dist/examples-slideshow/single.json +79 -0
  117. package/dist/examples-slideshow/static/pixel.png +0 -0
  118. package/dist/examples-slideshow/static.json +44 -0
  119. package/dist/harness-multi.html +3 -3
  120. package/dist/harness.html +4 -3
  121. package/dist/index.html +3 -2
  122. package/dist/js/storymap.d.ts +1326 -289
  123. package/dist/js/storymap.js +6690 -4784
  124. package/dist/js/storymap.js.map +1 -1
  125. package/dist/navplace-properties.json +14 -0
  126. package/dist/site.css +54 -36
  127. package/docs/migration-from-knightlab.md +89 -49
  128. package/docs/storymap-as-iiif-manifest.md +199 -126
  129. package/package.json +4 -2
  130. package/schema/storymap.schema.json +334 -34
  131. package/src/core/Util.ts +3 -1
  132. package/src/core/mixins.ts +42 -28
  133. package/src/language/Language.ts +7 -0
  134. package/src/language/locale/.expected-gaps.json +287 -27
  135. package/src/language/locale/de.json +11 -1
  136. package/src/language/locale/en.json +11 -1
  137. package/src/main.ts +51 -0
  138. package/src/map/Map.ts +44 -6
  139. package/src/map/MapMarker.ts +16 -3
  140. package/src/map/openlayers/Map.OpenLayers.ts +787 -62
  141. package/src/map/openlayers/MapMarker.OpenLayers.ts +113 -13
  142. package/src/map/slideView.ts +149 -0
  143. package/src/map/types.ts +2 -0
  144. package/src/media/EmbedUtil.ts +27 -2
  145. package/src/media/Media.ts +27 -4
  146. package/src/media/embedId.ts +6 -0
  147. package/src/media/types/Flickr.ts +14 -3
  148. package/src/media/types/HtmlMedia.ts +66 -0
  149. package/src/media/types/Image.ts +15 -3
  150. package/src/media/types/SoundCloud.ts +1 -1
  151. package/src/media/types/Text.ts +27 -8
  152. package/src/media/types/Twitter.ts +9 -7
  153. package/src/media/types/Vimeo.ts +1 -1
  154. package/src/media/types/YouTube.ts +2 -2
  155. package/src/scss/Typography.scss +15 -15
  156. package/src/scss/VCO.StoryMap.scss +173 -8
  157. package/src/scss/core/Reset.scss +5 -18
  158. package/src/scss/map/VCO.Map.scss +1 -1
  159. package/src/scss/map/VCO.MapMarker.scss +20 -9
  160. package/src/scss/map/openlayers/VCO.Map.OpenLayers.scss +7 -4
  161. package/src/scss/media/VCO.Media.scss +7 -7
  162. package/src/scss/media/types/VCO.Media.Blockquote.scss +1 -1
  163. package/src/scss/media/types/VCO.Media.Text.scss +5 -5
  164. package/src/scss/media/types/VCO.Media.Twitter.scss +4 -4
  165. package/src/scss/media/types/VCO.Media.Wikipedia.scss +2 -19
  166. package/src/scss/site/site.scss +55 -36
  167. package/src/scss/slider/VCO.Slide.scss +49 -11
  168. package/src/scss/slider/VCO.SlideNav.scss +23 -7
  169. package/src/scss/slider/VCO.StorySlider.scss +50 -0
  170. package/src/scss/snapmap/VCO.SnapMap.scss +3 -3
  171. package/src/scss/ui/VCO.Consent.scss +20 -14
  172. package/src/scss/ui/VCO.Layers.scss +119 -0
  173. package/src/scss/ui/VCO.MenuBar.Button.scss +140 -19
  174. package/src/scss/ui/VCO.MenuBar.scss +9 -2
  175. package/src/scss/ui/VCO.Message.scss +14 -0
  176. package/src/slider/Slide.ts +232 -74
  177. package/src/slider/SlideNav.ts +15 -4
  178. package/src/slider/StorySlider.ts +203 -22
  179. package/src/storymap/Consent.ts +22 -0
  180. package/src/storymap/StoryMap.ts +944 -131
  181. package/src/storymap/content-state.ts +3 -8
  182. package/src/storymap/font-css.ts +72 -0
  183. package/src/storymap/from-slideshow.ts +785 -0
  184. package/src/storymap/iiif-shared.ts +228 -0
  185. package/src/storymap/iiif.ts +289 -100
  186. package/src/storymap/to-iiif.ts +131 -44
  187. package/src/types.ts +382 -17
  188. package/src/ui/LayersControl.ts +302 -0
  189. package/src/ui/MenuBar.ts +232 -21
  190. package/src/ui/Message.ts +36 -3
  191. package/src/ui/Swipable.ts +25 -7
  192. package/dist/assets/harness-Cr0rYpvt.js +0 -1
  193. package/dist/assets/main-DXalctuR.js +0 -17
  194. package/src/scss/VCO.StoryMap.Dark.scss +0 -150
  195. package/src/scss/Variables.Dark.scss +0 -57
package/src/types.ts CHANGED
@@ -17,10 +17,38 @@ export interface StorymapSlideLocation {
17
17
  zoom?: number;
18
18
  /**
19
19
  * Image region `[x, y, w, h]` in image pixels (IIIF xywh convention,
20
- * StrollView-style image stops): the view fits the region on
20
+ * slideshow-style image stops): the view fits the region on
21
21
  * navigation; image maps (`map_type: "iiif"` + `map_as_image`) only.
22
22
  */
23
23
  region?: [number, number, number, number];
24
+ /**
25
+ * View rotation in degrees clockwise (slideshow `rotation`). Applied to
26
+ * the map view on navigation; absent means north-up (0). Image and geo
27
+ * maps alike; purely presentational. Honored only with
28
+ * `slideshow_source`.
29
+ */
30
+ rotation?: number;
31
+ /**
32
+ * Per-slide image grading (slideshow `filters`): applied as a CSS
33
+ * `filter()` on the map viewport. All fields optional; absent means
34
+ * unfiltered. `hueRotate` is degrees (-180..180), `blur` is CSS pixels.
35
+ * Honored only with `slideshow_source`.
36
+ */
37
+ filter?: StorymapSlideFilter;
38
+ /**
39
+ * Spotlight mask (slideshow `passepartout`): normalized viewport
40
+ * fractions `{x, y, w, h}` (0..1) left visible while the rest is dimmed
41
+ * with `color`. `invert` dims the inside instead of the outside.
42
+ * Absent means no mask. Honored only with `slideshow_source`.
43
+ */
44
+ mask?: StorymapSlideMask;
45
+ /**
46
+ * Per-slide basemap override (multi-manifest slideshow tours): any
47
+ * `map_type` value or IIIF `info.json` URL. Absent keeps the story
48
+ * basemap. The layer is cached, so returning to a slide is free.
49
+ * Honored only with `slideshow_source`.
50
+ */
51
+ basemap?: string;
24
52
  line?: boolean;
25
53
  icon?: string;
26
54
  iconSize?: number[];
@@ -29,6 +57,35 @@ export interface StorymapSlideLocation {
29
57
  [key: string]: unknown;
30
58
  }
31
59
 
60
+ /**
61
+ * Declarative CSS filter grading for one slide (slideshow `filters`).
62
+ * Rendered by `buildSlideFilter()`; unknown keys never reach CSS.
63
+ */
64
+ export interface StorymapSlideFilter {
65
+ brightness?: number;
66
+ contrast?: number;
67
+ saturate?: number;
68
+ hueRotate?: number;
69
+ sepia?: number;
70
+ blur?: number;
71
+ [key: string]: unknown;
72
+ }
73
+
74
+ /**
75
+ * Spotlight mask geometry (slideshow `passepartout`). Fractions of the
76
+ * visible map area; `color` is a `#rgb`/`#rrggbb`/`#rrggbbaa`/`rgb(a)` string.
77
+ */
78
+ export interface StorymapSlideMask {
79
+ x: number;
80
+ y: number;
81
+ w: number;
82
+ h: number;
83
+ color?: string;
84
+ invert?: boolean;
85
+ opacity?: number;
86
+ [key: string]: unknown;
87
+ }
88
+
32
89
  export type MediaTypeMatch = {
33
90
  type: string;
34
91
  name: string;
@@ -56,20 +113,35 @@ export interface StorymapSlideMedia {
56
113
  * `TextualBody` + `format: text/vtt` shape we also accept.
57
114
  */
58
115
  subtitles?: string | null;
116
+ /**
117
+ * Start offset in seconds for audio/video media (slideshow `audio.offset`).
118
+ * Applied once the media metadata is available; absent or negative means 0.
119
+ * Honored only with `slideshow_source`.
120
+ */
121
+ offset?: number;
122
+ /**
123
+ * Loop audio/video media (slideshow `audio.loop`). Absent means no loop.
124
+ * Honored only with `slideshow_source`.
125
+ */
126
+ loop?: boolean;
127
+ /**
128
+ * `auto` (default) plays on activation; `click` arms the media paused and
129
+ * lets the visitor start it (slideshow `audio.play`). Absent means `auto`.
130
+ * Honored only with `slideshow_source`.
131
+ */
132
+ play?: "auto" | "click";
133
+ /**
134
+ * Stop this media when leaving the slide (default true). `false` lets an
135
+ * audio bed continue across slides (slideshow ambient audio). Honored
136
+ * only with `slideshow_source`.
137
+ */
138
+ stopOnExit?: boolean;
59
139
  [key: string]: unknown;
60
140
  }
61
141
 
62
- /**
63
- * Optional spoken narration for one slide, played through a dedicated audio
64
- * element rather than the slide's media frame — a tour stop usually has a
65
- * picture and a recording, not one media item. Storymap JSON only: the
66
- * closest manifest spelling is a `Sound` body, which currently lands in
67
- * `media.url` rather than here.
68
- */
69
142
  /**
70
143
  * Per-slide marker presentation, consolidated. `location.*` keeps working as
71
- * the legacy spelling and `marker.*` wins when present — see
72
- * docs/plans/iiif-media-tours.md §2.
144
+ * the legacy spelling and `marker.*` wins when present.
73
145
  *
74
146
  * In a IIIF manifest none of this is a `storymap:` term: it all arrives in the
75
147
  * `navPlace` Feature's `properties` bag (`icon`, `iconSize`, `image`, `name`,
@@ -89,8 +161,46 @@ export interface StorymapSlideMarker {
89
161
  [key: string]: unknown;
90
162
  }
91
163
 
164
+ /**
165
+ * Optional spoken narration for one slide, played through a dedicated audio
166
+ * element rather than the slide's media frame — a tour stop usually has a
167
+ * picture and a recording, not one media item. In a IIIF manifest this is a
168
+ * `motivation: "supplementing"` Sound/Video body, read and written alongside
169
+ * the painting annotation.
170
+ */
92
171
  export interface StorymapSlideNarration {
93
172
  url: string;
173
+ /**
174
+ * Loop the narration (slideshow `audio.loop`). Absent means no loop.
175
+ * Note: a looping narration never fires `ended`, so `autoplay_media`
176
+ * falls back to its millisecond timer for that slide. Honored only with
177
+ * `slideshow_source`.
178
+ */
179
+ loop?: boolean;
180
+ /**
181
+ * Start offset in seconds (slideshow `audio.offset`), applied once the
182
+ * audio metadata is available. Absent or negative means 0. Honored only
183
+ * with `slideshow_source`.
184
+ */
185
+ offset?: number;
186
+ /**
187
+ * `auto` (default) plays on navigation; `click` arms the narration
188
+ * paused and lets the visitor start it (slideshow `audio.play`).
189
+ * Honored only with `slideshow_source`.
190
+ */
191
+ play?: "auto" | "click";
192
+ /**
193
+ * Stop the narration when leaving the slide (default true). `false`
194
+ * keeps an audio bed playing across slides while the URL is unchanged
195
+ * (slideshow ambient audio). Honored only with `slideshow_source`.
196
+ */
197
+ stopOnExit?: boolean;
198
+ /**
199
+ * Stop any other playing narration/ambient track before starting this
200
+ * one (default true). `false` allows the ambient overlap of two tracks.
201
+ * Honored only with `slideshow_source`.
202
+ */
203
+ stopAllPrevious?: boolean;
94
204
  [key: string]: unknown;
95
205
  }
96
206
 
@@ -112,8 +222,7 @@ export interface StorymapSlide {
112
222
  date?: string | Record<string, unknown> | null;
113
223
  group?: string;
114
224
  /**
115
- * The language tag this slide's text was read in (§3.4 of
116
- * docs/plans/iiif-interop.md). A IIIF manifest states its text as a
225
+ * The language tag this slide's text was read in. A IIIF manifest states its text as a
117
226
  * language map; the reader picks the viewer's configured language and
118
227
  * reports which one it used, so a host can offer a language switch.
119
228
  * Absent for language-neutral (`none`) text, and for a single-language
@@ -122,17 +231,90 @@ export interface StorymapSlide {
122
231
  language?: string;
123
232
  location?: StorymapSlideLocation | null;
124
233
  media?: StorymapSlideMedia | null;
234
+ /**
235
+ * Additional visible media for this slide (#358), rendered after `media`
236
+ * in order. Items without a non-empty `url` are skipped, so a document
237
+ * without this field renders identically. In a IIIF manifest these are
238
+ * further `painting` annotations on the canvas; the slide layout
239
+ * (`media_layout`) has no IIIF term and stays viewer-side.
240
+ */
241
+ media_extra?: StorymapSlideMedia[] | null;
242
+ /**
243
+ * Layout of a multi-media slide (#358): `stack` piles the items
244
+ * vertically (the panel scrolls), `row` puts two items side by side.
245
+ * Defaults to `stack`; `row` with anything but two items falls back to
246
+ * `stack`. Ignored for a single media item.
247
+ */
248
+ media_layout?: "stack" | "row";
125
249
  marker?: StorymapSlideMarker | null;
126
250
  narration?: StorymapSlideNarration | null;
127
251
  text?: StorymapSlideText | null;
128
252
  background?: StorymapSlideBackground | string | null;
129
253
  uniqueid?: string | null;
254
+ /**
255
+ * Provenance identifiers for the slide's source (slideshow
256
+ * `manifest_id`/`canvas_id`/`image_id`): which manifest, canvas and
257
+ * image the stop was translated from. Data-only (deep links, export);
258
+ * never rendered.
259
+ */
260
+ provenance?: StorymapSlideProvenance | null;
261
+ /**
262
+ * Per-slide autoplay dwell in milliseconds (slideshow `slidetimeout`):
263
+ * overrides the global `autoplay` interval for this slide; `0` holds on
264
+ * this slide. Absent means the global interval. Honored only with
265
+ * `slideshow_source`.
266
+ */
267
+ slidetimeout?: number;
268
+ /**
269
+ * Per-slide image overlay on the map (slideshow `imgoverlay`): a placed
270
+ * image shown while this slide is active. Absent means no overlay.
271
+ * Honored only with `slideshow_source`.
272
+ */
273
+ imgoverlay?: StorymapImageOverlay | null;
274
+ [key: string]: unknown;
275
+ }
276
+
277
+ /**
278
+ * A per-slide image overlay (slideshow `imgoverlay`). `url` is a full-size
279
+ * image; `extent` pins it to `[west, south, east, north]` (lon/lat, or raw
280
+ * image pixels on image-space maps) and defaults to the current view;
281
+ * `size` scales it as a fraction of the viewport width; `opacity` is 0..1.
282
+ */
283
+ export interface StorymapImageOverlay {
284
+ url: string;
285
+ size?: number;
286
+ opacity?: number;
287
+ extent?: [number, number, number, number];
288
+ [key: string]: unknown;
289
+ }
290
+
291
+ /**
292
+ * Provenance identifiers for one slide's source: the manifest, canvas and
293
+ * image a stop was translated from. Data-only.
294
+ */
295
+ export interface StorymapSlideProvenance {
296
+ manifest?: string;
297
+ canvas?: string;
298
+ image?: string;
299
+ [key: string]: unknown;
300
+ }
301
+
302
+ /**
303
+ * Authorship and rights for the story as a whole (slideshow
304
+ * `creator`/`rights`). Rendered into the map attribution line for
305
+ * slideshow tours; the license URL stays data-only for hosts.
306
+ */
307
+ export interface StorymapCredit {
308
+ creator?: string;
309
+ rights?: string;
310
+ licenseUrl?: string;
130
311
  [key: string]: unknown;
131
312
  }
132
313
 
133
314
  export interface StorymapData {
134
315
  uniqueid?: string;
135
316
  slides: StorymapSlide[];
317
+ credit?: StorymapCredit | null;
136
318
  [key: string]: unknown;
137
319
  }
138
320
 
@@ -157,6 +339,14 @@ export type TileSourceFactory = (
157
339
  context: { options: StorymapOptions; createDefault: () => TileLayer },
158
340
  ) => Layer | TileLayer | Source | null | undefined;
159
341
 
342
+ /**
343
+ * Named progress indicator styles (slideshow `progressbar`), shared by the
344
+ * translator (which emits them) and the menubar (which renders them) so the
345
+ * set cannot drift.
346
+ */
347
+ export const PROGRESSBAR_STYLES = ["bar", "dots", "squares", "block", "thinblock"] as const;
348
+ export type ProgressbarStyle = (typeof PROGRESSBAR_STYLES)[number];
349
+
160
350
  export interface StorymapOptions {
161
351
  width: number;
162
352
  height: number;
@@ -198,10 +388,40 @@ export interface StorymapOptions {
198
388
  autoplay_media: boolean;
199
389
  /** Show a progress bar in the menubar (issue #247) */
200
390
  show_progress: boolean;
201
- /** Show the slide headline as a label on the active map marker (issue #243) */
391
+ /**
392
+ * Progress indicator style (slideshow `progressbar`): `false`/`"off"`
393
+ * hides it (same as `show_progress: false`), `true`/`"bar"` is the
394
+ * classic fill bar, `dots`/`squares` render one step per slide,
395
+ * `block`/`thinblock` are fill-bar height variants. Absent means the
396
+ * `show_progress` boolean decides and the style is `"bar"`. Honored
397
+ * only with `slideshow_source`.
398
+ */
399
+ progressbar?:
400
+ | boolean
401
+ | "bar"
402
+ | "dots"
403
+ | "squares"
404
+ | "block"
405
+ | "thinblock"
406
+ | "off"; /** Show the slide headline as a label on the active map marker (issue #243) */
202
407
  marker_labels: boolean;
203
408
  /** Default text alignment for slide text: left, center or right (issue #244) */
204
409
  text_align: "left" | "center" | "right";
410
+ /**
411
+ * Slide panel dock (slideshow `textmode`): which side the text panel
412
+ * occupies in landscape. `"right"` (default) is the current layout;
413
+ * `"left"` mirrors it; `"bottom"` docks the panel below the map.
414
+ * Portrait layouts always behave like `"bottom"`. Honored only with
415
+ * `slideshow_source`.
416
+ */
417
+ textmode?: "left" | "right" | "bottom";
418
+ /**
419
+ * Slide panel size as a percentage (slideshow `textsize`): percent of
420
+ * the width for side docks, of the height for the bottom dock.
421
+ * Range 10..80; absent keeps the built-in 50/50 split. Honored only
422
+ * with `slideshow_source`.
423
+ */
424
+ textsize?: number;
205
425
  /** Override the overview fit center (issues #107, #271) */
206
426
  map_overview_center: { lat: number; lon: number } | null;
207
427
  map_type: string;
@@ -222,6 +442,15 @@ export interface StorymapOptions {
222
442
  * (default) means base map only.
223
443
  */
224
444
  overlays: StorymapOverlayLayer[];
445
+ /** Show the basemap/overlay switcher in the menubar (default false) */
446
+ show_layers_control: boolean;
447
+ /**
448
+ * Selectable base maps for the layer switcher. Entry 0 is the initial
449
+ * basemap unless `map_type` says otherwise; omit for an overlays-only
450
+ * control. Constructor- and runtime-only when labels are functions
451
+ * (functions cannot ride storymap JSON — see `label`).
452
+ */
453
+ basemaps?: StorymapBasemap[];
225
454
  map_mini: boolean;
226
455
  map_as_image: boolean;
227
456
  map_access_token: string;
@@ -274,6 +503,88 @@ export interface StorymapOptions {
274
503
  text_background_color: string;
275
504
  /** Show the great-circle route distance in the menubar (issue #341) */
276
505
  show_distance: boolean;
506
+ /**
507
+ * Slide transition effect (slideshow `fxmode`): `"slide"` (default) is
508
+ * the current glide, `"fade"` cross-fades slides, `"none"` jumps without
509
+ * animation. `prefers-reduced-motion` still forces `"none"`. Honored
510
+ * only with `slideshow_source`.
511
+ */
512
+ fxmode?: "slide" | "fade" | "none";
513
+ /**
514
+ * Reading mode (slideshow `mode`): `"standard"` (default) is the
515
+ * slider; `"static"` stacks all slides in a scrollable list and follows
516
+ * the map to the slide in view. Honored only with `slideshow_source`.
517
+ */
518
+ mode?: "standard" | "static";
519
+ /**
520
+ * HUD foreground color override (slideshow `hudcolor`): sets the
521
+ * `--vco-hud-fg` CSS variable. Absent follows the theme. Honored only
522
+ * with `slideshow_source`.
523
+ */
524
+ hudcolor?: string;
525
+ /**
526
+ * HUD background color override (slideshow `hudbgcolor`): sets the
527
+ * `--vco-hud-bg` CSS variable. Absent follows the theme. Honored only
528
+ * with `slideshow_source`.
529
+ */
530
+ hudbgcolor?: string;
531
+ /**
532
+ * HUD opacity percent 0..100 (slideshow `hudopacity`): applied to the
533
+ * HUD background. Absent means fully opaque theme background. Honored
534
+ * only with `slideshow_source`.
535
+ */
536
+ hudopacity?: number;
537
+ /**
538
+ * Show the previous/next slide navigation (slideshow `shownav`).
539
+ * Default true; `false` hides the nav chrome (keyboard/AT navigation
540
+ * still works, like `show_progress: false` keeps the story usable).
541
+ * Honored only with `slideshow_source`.
542
+ */
543
+ shownav?: boolean;
544
+ /**
545
+ * Render slide headlines (slideshow `showheadings`). Default true;
546
+ * `false` hides headline elements via CSS. Honored only with
547
+ * `slideshow_source`.
548
+ */
549
+ show_headings?: boolean;
550
+ /**
551
+ * Show scrollbars in the slide panel (slideshow `showscrollbars`).
552
+ * Default true; `false` hides them via CSS (content still scrolls).
553
+ * Honored only with `slideshow_source`.
554
+ */
555
+ show_scrollbars?: boolean;
556
+ /**
557
+ * Fixed viewer height (slideshow `viewerheight`), e.g. `"400px"`:
558
+ * applied as a container height override when set. Absent keeps the
559
+ * measured-container behavior. Only `px`, `%` and `vh` units accepted.
560
+ * Honored only with `slideshow_source`.
561
+ */
562
+ viewerheight?: string;
563
+ /**
564
+ * Show media captions and credits (slideshow `showinfo`). Default true;
565
+ * `false` hides the caption/credit block. Honored only with
566
+ * `slideshow_source`.
567
+ */
568
+ show_info?: boolean;
569
+ /**
570
+ * Detect slideshow tours automatically (default `true`): an
571
+ * `AnnotationCollection` shaped like one loads through the translator.
572
+ * Set `false` to disable detection (such documents then fail validation
573
+ * as before).
574
+ */
575
+ slideshow?: boolean;
576
+ /**
577
+ * @internal Source marker: true when the loaded document came from a
578
+ * slideshow tour (set programmatically by the viewer, never from data).
579
+ * The slideshow-motivated presentation fields (location
580
+ * rotation/filter/mask/basemap, narration/media playback bags,
581
+ * slidetimeout, imgoverlay, and the player-chrome options above) render
582
+ * only with this set — internal documents keep the long-standing
583
+ * behavior. Absent from constructor defaults and schema, so storymap
584
+ * data cannot inject it; it travels via options copies. Hosts may pass
585
+ * it explicitly as an escape hatch.
586
+ */
587
+ slideshow_source?: boolean;
277
588
  /**
278
589
  * Raw OpenLayers map configuration. `controls` replaces the StoryMapJS
279
590
  * defaults, `interactions` are *added to* the viewer's own pan/zoom
@@ -293,11 +604,24 @@ export interface StorymapOptions {
293
604
  show_lines: boolean;
294
605
  show_history_line: boolean;
295
606
  use_custom_markers: boolean;
296
- iiif: { url: string; attribution: string };
607
+ /**
608
+ * IIIF image service for image maps. `url` is the `info.json`; `width`
609
+ * and `height` record the canvas size so region clamping and the static
610
+ * fallback work without another fetch; `fallbackUrl` is a full-size
611
+ * image used when `info.json` cannot be loaded (slideshow
612
+ * `image_static`).
613
+ */
614
+ iiif: {
615
+ url: string;
616
+ attribution: string;
617
+ width?: number;
618
+ height?: number;
619
+ fallbackUrl?: string;
620
+ };
297
621
  tilejson?: StorymapTilejson;
298
622
  /**
299
623
  * The `seeAlso` targets a IIIF manifest points at, recorded but not
300
- * fetched — see `loadSeeAlso()` (§5.2 of docs/plans/iiif-interop.md).
624
+ * fetched — see `loadSeeAlso()`.
301
625
  */
302
626
  see_also?: { id: string; type: string }[];
303
627
  map_height: number;
@@ -306,8 +630,18 @@ export interface StorymapOptions {
306
630
  slide_default_fade: string;
307
631
  menubar_default_y: number;
308
632
  script_path: string;
309
- font_css: string;
633
+ /**
634
+ * Font theme stylesheet: `stock:<name>`, a URL/path, or `false` for no
635
+ * injected stylesheet (the host bundles the theme itself).
636
+ */
637
+ font_css: string | false;
310
638
  language: string;
639
+ /**
640
+ * Colour theme: `"dark"` forces the dark palette, `"light"` pins the
641
+ * light one (overriding an OS dark preference), unset follows
642
+ * `prefers-color-scheme`. Construction-time only, like `map_type`.
643
+ */
644
+ theme?: "dark" | "light";
311
645
  api_key_flickr: string;
312
646
  [key: string]: unknown;
313
647
  }
@@ -330,7 +664,7 @@ export interface AnimationHandle {
330
664
 
331
665
  /**
332
666
  * TileJSON 2.1 tile source metadata, from a manifest's map configuration
333
- * service (§2.9 of docs/plans/iiif-interop.md).
667
+ * service.
334
668
  *
335
669
  * This is how a manifest states a tile source: `tiles` is the URL template and
336
670
  * the rest is the standard metadata around it, where a keyword basemap
@@ -353,6 +687,20 @@ export interface StorymapTilejson {
353
687
  center?: [number, number, number];
354
688
  }
355
689
 
690
+ /**
691
+ * A selectable base map for the layer switcher (`StorymapOptions.basemaps`).
692
+ */
693
+ export interface StorymapBasemap {
694
+ /** Any `map_type` value. */
695
+ map_type: string;
696
+ /**
697
+ * Control row label. Defaults to `map_type`. A function is re-invoked
698
+ * on every `refreshLabels()` for host i18n (constructor options only —
699
+ * see `StorymapOverlayLayer.label`).
700
+ */
701
+ label?: string | (() => string);
702
+ }
703
+
356
704
  /**
357
705
  * A stacked raster overlay above the base map (StorymapOptions.overlays).
358
706
  * Presentation is declarative: hosts no longer need to reach into the
@@ -381,6 +729,23 @@ export interface StorymapOverlayLayer {
381
729
  className?: string;
382
730
  /** CSS mix-blend-mode for the layer container (needs className) */
383
731
  blendMode?: string;
732
+ /**
733
+ * Control row label for the layer switcher (`show_layers_control`).
734
+ * Defaults to `map_type`. A function is re-invoked on every
735
+ * `refreshLabels()` for host i18n — but only when the entry comes from
736
+ * constructor options: `mergeData()` overwrites option keys with the
737
+ * storymap JSON, so a document carrying `overlays` replaces function
738
+ * labels with its own strings.
739
+ */
740
+ label?: string | (() => string);
741
+ /**
742
+ * Render the row checked and disabled: an overlay the visitor may add
743
+ * back but not remove (e.g. a georeferenced scan the story is built on).
744
+ * The control never toggles a locked entry.
745
+ */
746
+ locked?: boolean;
747
+ /** false hides the row from the control without removing the overlay. */
748
+ control?: boolean;
384
749
  /** Clip box `[west, south, east, north]` in lon/lat (mercator maps) */
385
750
  extent?: [number, number, number, number];
386
751
  /**