@manifest-editor/creators 1.2.0 → 1.3.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 (41) hide show
  1. package/dist/iiif-browser-form.lazy-3EZNJN4M.js +21 -0
  2. package/dist/iiif-browser-form.lazy-3YUQOIEL.js +21 -0
  3. package/dist/iiif-browser-form.lazy-4CDWA6JD.js +47 -0
  4. package/dist/iiif-browser-form.lazy-B2RXVKR5.js +21 -0
  5. package/dist/iiif-browser-form.lazy-DCCYGD42.js +35 -0
  6. package/dist/iiif-browser-form.lazy-DU2NJUCE.js +46 -0
  7. package/dist/iiif-browser-form.lazy-DUTUHLUT.js +50 -0
  8. package/dist/iiif-browser-form.lazy-EJZ754MG.js +42 -0
  9. package/dist/iiif-browser-form.lazy-ENQRBLTJ.js +21 -0
  10. package/dist/iiif-browser-form.lazy-FMKQQWDE.js +33 -0
  11. package/dist/iiif-browser-form.lazy-FOIWPPI7.js +47 -0
  12. package/dist/iiif-browser-form.lazy-G7XAQYYG.js +51 -0
  13. package/dist/iiif-browser-form.lazy-GO6U6NE5.js +32 -0
  14. package/dist/iiif-browser-form.lazy-GVEEZX37.js +33 -0
  15. package/dist/iiif-browser-form.lazy-HBWB7QGF.js +31 -0
  16. package/dist/iiif-browser-form.lazy-I7ZNZHID.js +48 -0
  17. package/dist/iiif-browser-form.lazy-IX5DFVDG.js +47 -0
  18. package/dist/iiif-browser-form.lazy-KPB57B25.js +21 -0
  19. package/dist/iiif-browser-form.lazy-KYOKLDRV.js +47 -0
  20. package/dist/iiif-browser-form.lazy-NDPLPCQQ.js +43 -0
  21. package/dist/iiif-browser-form.lazy-OQLU6NDJ.js +51 -0
  22. package/dist/iiif-browser-form.lazy-OZNQO2OO.js +21 -0
  23. package/dist/iiif-browser-form.lazy-QR55LXI4.js +21 -0
  24. package/dist/iiif-browser-form.lazy-R5TMXUBV.js +19 -0
  25. package/dist/iiif-browser-form.lazy-RWEJWTIW.js +21 -0
  26. package/dist/iiif-browser-form.lazy-T6LT6BJD.js +48 -0
  27. package/dist/iiif-browser-form.lazy-TWQYATEG.js +21 -0
  28. package/dist/iiif-browser-form.lazy-UPZXU6JB.js +51 -0
  29. package/dist/iiif-browser-form.lazy-W42LJ6X2.js +47 -0
  30. package/dist/iiif-browser-form.lazy-WAK45BLF.js +42 -0
  31. package/dist/iiif-browser-form.lazy-WVCVE6XK.js +21 -0
  32. package/dist/index.cjs +1550 -1378
  33. package/dist/index.d.cts +18 -16
  34. package/dist/index.d.ts +18 -16
  35. package/dist/index.js +1451 -1283
  36. package/package.json +13 -12
  37. package/dist/chunk-YSQDPG26.js +0 -30
  38. package/dist/iiif-browser-form.lazy-C3VAAY7I.js +0 -34
  39. package/dist/iiif-browser-form.lazy-QLO62DFA.js +0 -32
  40. package/dist/iiif-browser-form.lazy-ZADCSMAN.js +0 -32
  41. package/dist/manifest-browser-form.lazy-FLU4HSPW.js +0 -7
package/dist/index.js CHANGED
@@ -2,52 +2,298 @@ import {
2
2
  ManifestBrowserCreatorForm
3
3
  } from "./chunk-7FSKLOVQ.js";
4
4
 
5
- // src/Canvas/EmptyCanvas/index.tsx
6
- import { EmptyCanvasIcon } from "@manifest-editor/components";
7
- import { jsx } from "react/jsx-runtime";
8
- var emptyCanvas = {
9
- id: "@manifest-editor/empty-canvas",
10
- create: createEmptyCanvas,
11
- label: "Empty canvas",
12
- summary: "No content",
13
- icon: /* @__PURE__ */ jsx(EmptyCanvasIcon, {}),
14
- resourceType: "Canvas",
15
- resourceFields: ["id", "type", "label", "height", "width", "items"],
16
- supports: {
17
- parentTypes: ["Manifest"],
18
- parentFields: ["items"]
5
+ // src/Annotation/AudioAnnotation/create-audio-annotation.tsx
6
+ import { useEffect as useEffect2, useState as useState2 } from "react";
7
+
8
+ // ../ui/atoms/PaddedSidebarContainer.tsx
9
+ import styled from "styled-components";
10
+ var PaddedSidebarContainer = styled.div`
11
+ padding: 0 0.7em;
12
+ margin: 0.7em 0;
13
+ flex: 1;
14
+ overflow: visible;
15
+ `;
16
+
17
+ // src/Annotation/AudioAnnotation/create-audio-annotation.tsx
18
+ import { ErrorMessage } from "@manifest-editor/components";
19
+
20
+ // ../ui/ui/MediaControls/MediaControls.tsx
21
+ import { formatTime, useMediaActions, useMediaElements, useMediaState } from "react-iiif-vault";
22
+ import { useLayoutEffect, useState } from "react";
23
+
24
+ // ../ui/ui/MediaControls/MediaControls.icons.tsx
25
+ import { jsx, jsxs } from "react/jsx-runtime";
26
+ var PlayIcon = (props) => /* @__PURE__ */ jsxs("svg", { ...props, viewBox: "4 4 16 16", xmlns: "http://www.w3.org/2000/svg", height: "1em", width: "1em", children: [
27
+ props.title ? /* @__PURE__ */ jsx("title", { children: props.title }) : null,
28
+ /* @__PURE__ */ jsx("path", { d: "M0 0h24v24H0z", fill: "none" }),
29
+ /* @__PURE__ */ jsx("path", { d: "M8 5v14l11-7z", fill: "currentColor" })
30
+ ] });
31
+ var PauseIcon = (props) => /* @__PURE__ */ jsxs("svg", { ...props, viewBox: "4 4 16 16", xmlns: "http://www.w3.org/2000/svg", height: "1em", width: "1em", children: [
32
+ props.title ? /* @__PURE__ */ jsx("title", { children: props.title }) : null,
33
+ /* @__PURE__ */ jsx("path", { d: "M0 0h24v24H0z", fill: "none" }),
34
+ /* @__PURE__ */ jsx("path", { fill: "currentColor", d: "M6 19h4V5H6v14zm8-14v14h4V5h-4z" })
35
+ ] });
36
+ var VolumeUpIcon = ({ title, ...props }) => /* @__PURE__ */ jsxs("svg", { ...props, xmlns: "http://www.w3.org/2000/svg", height: "1em", width: "1em", viewBox: "2 2 20 20", children: [
37
+ title ? /* @__PURE__ */ jsx("title", { children: title }) : null,
38
+ /* @__PURE__ */ jsx("path", { d: "M0 0h24v24H0z", fill: "none" }),
39
+ /* @__PURE__ */ jsx(
40
+ "path",
41
+ {
42
+ fill: "currentColor",
43
+ d: "M3 9v6h4l5 5V4L7 9H3zm13.5 3A4.5 4.5 0 0 0 14 7.97v8.05c1.48-.73 2.5-2.25 2.5-4.02zM14 3.23v2.06c2.89.86 5 3.54 5 6.71s-2.11 5.85-5 6.71v2.06c4.01-.91 7-4.49 7-8.77s-2.99-7.86-7-8.77z"
44
+ }
45
+ )
46
+ ] });
47
+ var VolumeDownIcon = ({ title, ...props }) => /* @__PURE__ */ jsxs("svg", { ...props, xmlns: "http://www.w3.org/2000/svg", height: "1em", width: "1em", viewBox: "2 2 20 20", children: [
48
+ title ? /* @__PURE__ */ jsx("title", { children: title }) : null,
49
+ /* @__PURE__ */ jsx("path", { d: "M0 0h24v24H0z", fill: "none" }),
50
+ /* @__PURE__ */ jsx(
51
+ "path",
52
+ {
53
+ fill: "currentColor",
54
+ d: "M18.5 12c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02zM5 9v6h4l5 5V4L9 9H5z"
55
+ }
56
+ )
57
+ ] });
58
+ var VolumeOffIcon = ({ title, ...props }) => /* @__PURE__ */ jsxs("svg", { ...props, xmlns: "http://www.w3.org/2000/svg", height: "1em", width: "1em", viewBox: "2 2 20 20", children: [
59
+ title ? /* @__PURE__ */ jsx("title", { children: title }) : null,
60
+ /* @__PURE__ */ jsx("path", { d: "M0 0h24v24H0z", fill: "none" }),
61
+ /* @__PURE__ */ jsx(
62
+ "path",
63
+ {
64
+ fill: "currentColor",
65
+ d: "M16.5 12A4.5 4.5 0 0 0 14 7.97v2.21l2.45 2.45c.03-.2.05-.41.05-.63zm2.5 0c0 .94-.2 1.82-.54 2.64l1.51 1.51A8.796 8.796 0 0 0 21 12c0-4.28-2.99-7.86-7-8.77v2.06c2.89.86 5 3.54 5 6.71zM4.27 3 3 4.27 7.73 9H3v6h4l5 5v-6.73l4.25 4.25c-.67.52-1.42.93-2.25 1.18v2.06a8.99 8.99 0 0 0 3.69-1.81L19.73 21 21 19.73l-9-9L4.27 3zM12 4 9.91 6.09 12 8.18V4z"
66
+ }
67
+ )
68
+ ] });
69
+
70
+ // ../ui/ui/MediaControls/MediaControls.styles.ts
71
+ import styled2, { css } from "styled-components";
72
+ var AudioPlayerContainer = styled2.div`
73
+ background: #ededed; // Not in variable.
74
+ display: flex;
75
+ padding: 0.5em;
76
+ align-items: center;
77
+ `;
78
+ var BaseButton = styled2.button`
79
+ cursor: pointer;
80
+ padding: 0.75em 1.5em;
81
+ font-size: 0.938em;
82
+ background: none;
83
+ color: inherit;
84
+ border: none;
85
+ text-decoration: none;
86
+ display: inline-block;
87
+ vertical-align: top;
88
+ white-space: nowrap;
89
+ `;
90
+ var PlayButton = styled2(BaseButton)`
91
+ width: 2em;
92
+ height: 2em;
93
+ overflow: hidden;
94
+ font-size: 1.1em;
95
+ background: #fff;
96
+ border-radius: 3px;
97
+ padding: 0;
98
+ margin: 0;
99
+ color: #000; // Not in colours
100
+
101
+ &:disabled {
102
+ color: #999;
19
103
  }
20
- };
21
- async function createEmptyCanvas(data, ctx) {
22
- const canvasId = ctx.generateId("canvas");
23
- const page = ctx.embed({
24
- id: ctx.generateId("annotation-page", { id: canvasId, type: "Canvas" }),
25
- type: "AnnotationPage",
26
- items: []
27
- });
28
- return {
29
- id: canvasId,
30
- type: "Canvas",
31
- label: data.label || { en: [""] },
32
- height: data.height || 1e3,
33
- width: data.width || 1e3,
34
- items: [page]
35
- };
104
+ `;
105
+ var TimeDisplay = styled2.div`
106
+ margin: 0 1em;
107
+ font-size: 0.85em;
108
+ color: #000;
109
+ `;
110
+ var ProgressContainer = styled2.div`
111
+ flex: 1 1 0px;
112
+ height: 6px;
113
+ display: flex;
114
+ position: relative;
115
+ padding: 15px 0;
116
+ `;
117
+ var ProgressCurrent = styled2.div`
118
+ background: #d85680;
119
+ height: 6px;
120
+ pointer-events: none;
121
+ position: absolute;
122
+ top: 13px;
123
+ left: 0;
124
+ border-radius: 3px;
125
+ transition: width 200ms;
126
+ `;
127
+ var ProgressBackground = styled2.div`
128
+ position: absolute;
129
+ top: 13px;
130
+ left: 0;
131
+ height: 6px;
132
+ border-radius: 3px;
133
+ width: 100%;
134
+ pointer-events: none;
135
+ background: #fff;
136
+ `;
137
+ var SoundButton = styled2(BaseButton)`
138
+ width: 2em;
139
+ height: 2em;
140
+ overflow: hidden;
141
+ font-size: 1.1em;
142
+ background: transparent;
143
+ border-radius: 3px;
144
+ padding: 0;
145
+ margin: 0;
146
+ `;
147
+ var SoundContainer = styled2.div`
148
+ display: flex;
149
+ align-items: center;
150
+ `;
151
+ var VolumeContainer = styled2.div`
152
+ width: 0;
153
+ overflow: hidden;
154
+ transition: width 500ms;
155
+ color: #4a473f; // Not in colours
156
+
157
+ ${(props) => props.$isOpen && css`
158
+ width: 150px;
159
+ `}
160
+ `;
161
+ var VolumeSlider = styled2.input.attrs({ type: "range" })`
162
+ width: 120px;
163
+ margin: 0 15px;
164
+ `;
165
+
166
+ // ../ui/madoc/components/icons/Spinner.tsx
167
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
168
+ function Spinner(props) {
169
+ return /* @__PURE__ */ jsx2("svg", { width: "1em", height: "1em", viewBox: "0 0 42 42", xmlns: "http://www.w3.org/2000/svg", stroke: "#fff", ...props, children: /* @__PURE__ */ jsxs2("g", { transform: "translate(2 2)", strokeWidth: 5, fill: "none", fillRule: "evenodd", children: [
170
+ /* @__PURE__ */ jsx2("circle", { strokeOpacity: 0.5, cx: 18, cy: 18, r: 18 }),
171
+ /* @__PURE__ */ jsx2("path", { d: "M36 18c0-9.94-8.06-18-18-18", children: /* @__PURE__ */ jsx2(
172
+ "animateTransform",
173
+ {
174
+ attributeName: "transform",
175
+ type: "rotate",
176
+ from: "0 18 18",
177
+ to: "360 18 18",
178
+ dur: "1s",
179
+ repeatCount: "indefinite"
180
+ }
181
+ ) })
182
+ ] }) });
36
183
  }
37
184
 
38
- // ../ui/icons/TextFormatIcon.tsx
39
- import { jsx as jsx2, jsxs } from "react/jsx-runtime";
40
- var TextFormatIcon = (props) => /* @__PURE__ */ jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", height: "1em", width: "1em", ...props, children: [
41
- /* @__PURE__ */ jsx2("path", { d: "M0 0h24v24H0z", fill: "none" }),
42
- /* @__PURE__ */ jsx2("path", { d: "M5 17v2h14v-2H5zm4.5-4.2h5l.9 2.2h2.1L12.75 4h-1.5L6.5 15h2.1l.9-2.2zM12 5.98 13.87 11h-3.74L12 5.98z" })
43
- ] });
185
+ // ../ui/atoms/VisuallHidden/VisuallHidden.tsx
186
+ import styled3, { css as css2 } from "styled-components";
187
+ var visuallyHidden = css2`
188
+ clip: rect(1px, 1px, 1px, 1px);
189
+ clip-path: inset(50%);
190
+ height: 1px;
191
+ width: 1px;
192
+ margin: -1px;
193
+ overflow: hidden;
194
+ padding: 0;
195
+ position: absolute;
196
+ `;
197
+ var VisuallyHiddenLabel = styled3.label`
198
+ ${visuallyHidden}
199
+ `;
44
200
 
45
- // src/ContentResource/PlaintextCreator/create-plaintext.tsx
46
- import { LanguageFieldEditor, InputContainer, InputLabel, Input } from "@manifest-editor/editors";
201
+ // ../ui/ui/MediaControls/MediaControls.tsx
202
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
203
+ function MediaControls(props) {
204
+ const [isVolumeOpen, setIsVolumeOpen] = useState(false);
205
+ const { progress, currentTime, element } = useMediaElements();
206
+ const { duration, isMuted, volume, isPlaying, playRequested } = useMediaState();
207
+ const { setVolume, toggleMute, play, pause, setDurationPercent } = useMediaActions();
208
+ const Volume = isMuted || volume === 0 ? VolumeOffIcon : volume > 50 ? VolumeUpIcon : VolumeDownIcon;
209
+ useLayoutEffect(() => {
210
+ if (element.current) {
211
+ const $el = element.current;
212
+ if ($el.error && props.onError) {
213
+ props.onError($el.error?.message || "Unknown error");
214
+ return;
215
+ }
216
+ const onError = () => {
217
+ if (props.onError) {
218
+ props.onError($el.error?.message || "Unknown error");
219
+ }
220
+ };
221
+ const loadedMetadata = () => {
222
+ if ($el.duration && props.onDuration) {
223
+ props.onDuration($el.duration);
224
+ }
225
+ if (props.onDimensions && $el instanceof HTMLVideoElement) {
226
+ props.onDimensions($el.videoWidth, $el.videoHeight);
227
+ }
228
+ };
229
+ $el.addEventListener("loadedmetadata", loadedMetadata);
230
+ $el.addEventListener("error", onError);
231
+ return () => {
232
+ $el.removeEventListener("loadedmetadata", loadedMetadata);
233
+ $el.removeEventListener("error", onError);
234
+ };
235
+ }
236
+ }, []);
237
+ return /* @__PURE__ */ jsxs3(AudioPlayerContainer, { children: [
238
+ /* @__PURE__ */ jsx3(
239
+ PlayButton,
240
+ {
241
+ disabled: playRequested,
242
+ onClick: () => {
243
+ if (isPlaying) {
244
+ pause();
245
+ } else {
246
+ play();
247
+ }
248
+ },
249
+ children: playRequested ? /* @__PURE__ */ jsx3(Spinner, { stroke: "#999" }) : isPlaying ? /* @__PURE__ */ jsx3(PauseIcon, { title: "Pause" }) : /* @__PURE__ */ jsx3(PlayIcon, { title: "Play" })
250
+ }
251
+ ),
252
+ /* @__PURE__ */ jsx3(TimeDisplay, { ref: currentTime, children: "0:00" }),
253
+ /* @__PURE__ */ jsxs3(
254
+ ProgressContainer,
255
+ {
256
+ onClick: (e) => {
257
+ const { left, width } = e.currentTarget.getBoundingClientRect();
258
+ const percent = (e.pageX - left) / width;
259
+ setDurationPercent(percent);
260
+ },
261
+ children: [
262
+ /* @__PURE__ */ jsx3(ProgressBackground, {}),
263
+ duration ? /* @__PURE__ */ jsx3(ProgressCurrent, { ref: progress }) : null
264
+ ]
265
+ }
266
+ ),
267
+ /* @__PURE__ */ jsx3(TimeDisplay, { children: formatTime(duration) }),
268
+ /* @__PURE__ */ jsxs3(SoundContainer, { onMouseEnter: () => setIsVolumeOpen(true), onMouseLeave: () => setIsVolumeOpen(false), children: [
269
+ /* @__PURE__ */ jsx3(VolumeContainer, { $isOpen: isVolumeOpen, children: /* @__PURE__ */ jsxs3("span", { children: [
270
+ /* @__PURE__ */ jsx3(VisuallyHiddenLabel, { htmlFor: "audio-slider", children: "Change volume" }),
271
+ /* @__PURE__ */ jsx3(
272
+ VolumeSlider,
273
+ {
274
+ id: "audio-slider",
275
+ role: "slider",
276
+ disabled: isMuted,
277
+ min: "0",
278
+ max: "100",
279
+ value: volume,
280
+ onChange: (e) => setVolume(Number(e.currentTarget.value))
281
+ }
282
+ )
283
+ ] }) }),
284
+ /* @__PURE__ */ jsx3(SoundButton, { onClick: () => toggleMute(), children: /* @__PURE__ */ jsx3(Volume, { title: isMuted ? "Mute" : "Unmute" }) })
285
+ ] })
286
+ ] });
287
+ }
288
+
289
+ // src/Annotation/AudioAnnotation/create-audio-annotation.tsx
290
+ import { CanvasPanel } from "react-iiif-vault";
291
+ import { getValue } from "@iiif/helpers";
292
+ import { Input, InputContainer, InputLabel, LanguageFieldEditor, FormFieldWrapper } from "@manifest-editor/editors";
47
293
 
48
294
  // ../ui/atoms/Button.tsx
49
- import styled from "styled-components";
50
- var ButtonReset = styled.button`
295
+ import styled4 from "styled-components";
296
+ var ButtonReset = styled4.button`
51
297
  border: none;
52
298
  outline: none;
53
299
  background-color: transparent;
@@ -60,7 +306,7 @@ var ButtonReset = styled.button`
60
306
  color: inherit;
61
307
  }
62
308
  `;
63
- var Button = styled.button`
309
+ var Button = styled4.button`
64
310
  padding: 0.5rem 1rem;
65
311
  color: ${(props) => props.theme.color.main || "none"};
66
312
  background-color: ${(props) => props.theme.color.white || "white"};
@@ -79,12 +325,12 @@ var Button = styled.button`
79
325
  background: #eee;
80
326
  }
81
327
  `;
82
- var SmallButton = styled(Button)`
328
+ var SmallButton = styled4(Button)`
83
329
  padding: 0.25em ${(props) => props.theme.padding.xs || "0.4em"};
84
330
  height: unset;
85
331
  background: none;
86
332
  `;
87
- var SecondaryButton = styled(Button)`
333
+ var SecondaryButton = styled4(Button)`
88
334
  {
89
335
  padding: 0 ${(props) => props.theme.padding.medium || "1rem"};
90
336
  color: ${(props) => props.theme.color.main || "#B84C74;"};
@@ -104,7 +350,7 @@ var SecondaryButton = styled(Button)`
104
350
  }
105
351
  }
106
352
  `;
107
- var CalltoButton = styled(Button)`
353
+ var CalltoButton = styled4(Button)`
108
354
  padding: 0 ${(props) => props.theme.padding.medium || "1rem"};
109
355
  color: ${(props) => props.theme.color.white || "white"};
110
356
  background-color: ${(props) => props.theme.color.main || "#B84C74;"};
@@ -118,7 +364,7 @@ var CalltoButton = styled(Button)`
118
364
  background-color: ${(props) => props.theme.color.mainHover || "#B84C74;"};
119
365
  }
120
366
  `;
121
- var ButtonGroup = styled.div`
367
+ var ButtonGroup = styled4.div`
122
368
  display: flex;
123
369
  flex-direction: row;
124
370
  flex-wrap: wrap;
@@ -126,212 +372,138 @@ var ButtonGroup = styled.div`
126
372
  justify-content: ${(props) => props.$right ? "flex-end" : "flex-start"};
127
373
  `;
128
374
 
129
- // ../ui/atoms/PaddedSidebarContainer.tsx
130
- import styled2 from "styled-components";
131
- var PaddedSidebarContainer = styled2.div`
132
- padding: 0 0.7em;
133
- margin: 0.7em 0;
134
- flex: 1;
135
- overflow: visible;
136
- `;
137
-
138
- // src/ContentResource/PlaintextCreator/create-plaintext.tsx
139
- import { useState } from "react";
140
- import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
141
- async function createPlaintext(data, ctx) {
142
- return ctx.embed({
143
- id: data.url,
144
- type: "Text",
145
- label: data.label,
146
- format: "text/plain"
147
- });
148
- }
149
- function CreatePlaintextForm(props) {
150
- const [label, setLabel] = useState({ en: ["View as text"] });
151
- const onSubmit = (e) => {
152
- e.preventDefault();
153
- const data = new FormData(e.target);
154
- const formData = Object.fromEntries(data.entries());
155
- if (formData.url) {
156
- props.runCreate({ url: formData.url, label });
157
- }
375
+ // src/Annotation/AudioAnnotation/create-audio-annotation.tsx
376
+ import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
377
+ async function createAudioAnnotation(data, ctx) {
378
+ const annotation = {
379
+ id: ctx.generateId("annotation"),
380
+ type: "Annotation"
158
381
  };
159
- return /* @__PURE__ */ jsx3(PaddedSidebarContainer, { children: /* @__PURE__ */ jsxs2("form", { onSubmit, children: [
160
- /* @__PURE__ */ jsx3(
161
- LanguageFieldEditor,
162
- {
163
- focusId: "label",
164
- label: "Label",
165
- fields: label,
166
- onSave: (e) => setLabel(e.toInternationalString())
167
- }
168
- ),
169
- /* @__PURE__ */ jsxs2(InputContainer, { $wide: true, children: [
170
- /* @__PURE__ */ jsx3(InputLabel, { htmlFor: "id", children: "Link to plaintext" }),
171
- /* @__PURE__ */ jsx3(Input, { id: "url", name: "url", defaultValue: "" })
172
- ] }),
173
- /* @__PURE__ */ jsx3(Button, { type: "submit", children: "Create" })
174
- ] }) });
175
- }
176
-
177
- // src/ContentResource/PlaintextCreator/index.tsx
178
- import { jsx as jsx4 } from "react/jsx-runtime";
179
- var plaintextCreator = {
180
- id: "@manifest-editor/plaintext-creator",
181
- create: createPlaintext,
182
- label: "Plaintext",
183
- summary: "Add link to an plaintext",
184
- icon: /* @__PURE__ */ jsx4(TextFormatIcon, {}),
185
- render(ctx) {
186
- return /* @__PURE__ */ jsx4(CreatePlaintextForm, { ...ctx });
187
- },
188
- resourceType: "ContentResource",
189
- resourceFields: ["label", "format"],
190
- supports: {
191
- parentFields: ["seeAlso", "rendering", "homepage"]
192
- },
193
- staticFields: {
194
- format: "text/plain"
195
- }
196
- };
197
-
198
- // src/ContentResource/WebPageCreator/create-web-page.tsx
199
- import { LanguageFieldEditor as LanguageFieldEditor2, InputContainer as InputContainer2, InputLabel as InputLabel2, Input as Input2 } from "@manifest-editor/editors";
200
- import { useState as useState2 } from "react";
201
- import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
202
- async function createWebPage(data, ctx) {
203
- return ctx.embed({
382
+ const targetType = ctx.options.targetType;
383
+ const body = await ctx.embed({
204
384
  id: data.url,
205
- type: "Text",
206
- label: data.label,
207
- format: "text/html"
385
+ type: "Sound",
386
+ format: "audio/mp4",
387
+ duration: data.duration || 0
208
388
  });
209
- }
210
- function CreateWebPageForm(props) {
211
- const [label, setLabel] = useState2({ en: [""] });
212
- const onSubmit = (e) => {
213
- e.preventDefault();
214
- const data = new FormData(e.target);
215
- const formData = Object.fromEntries(data.entries());
216
- if (formData.url) {
217
- props.runCreate({ url: formData.url, label });
218
- }
219
- };
220
- return /* @__PURE__ */ jsx5(PaddedSidebarContainer, { children: /* @__PURE__ */ jsxs3("form", { onSubmit, children: [
221
- /* @__PURE__ */ jsx5(
222
- LanguageFieldEditor2,
223
- {
224
- focusId: "label",
225
- label: "Label",
226
- fields: label,
227
- onSave: (e) => setLabel(e.toInternationalString())
228
- }
229
- ),
230
- /* @__PURE__ */ jsxs3(InputContainer2, { $wide: true, children: [
231
- /* @__PURE__ */ jsx5(InputLabel2, { htmlFor: "id", children: "Link" }),
232
- /* @__PURE__ */ jsx5(Input2, { id: "url", name: "url", defaultValue: "" })
233
- ] }),
234
- /* @__PURE__ */ jsx5(Button, { type: "submit", children: "Create link" })
235
- ] }) });
236
- }
237
-
238
- // ../ui/icons/LinkIcon.tsx
239
- import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
240
- function LinkIcon() {
241
- return /* @__PURE__ */ jsxs4("svg", { xmlns: "http://www.w3.org/2000/svg", height: "24px", viewBox: "0 0 24 24", width: "24px", fill: "#000000", children: [
242
- /* @__PURE__ */ jsx6("path", { d: "M0 0h24v24H0z", fill: "none" }),
243
- /* @__PURE__ */ jsx6("path", { d: "M3.9 12c0-1.71 1.39-3.1 3.1-3.1h4V7H7c-2.76 0-5 2.24-5 5s2.24 5 5 5h4v-1.9H7c-1.71 0-3.1-1.39-3.1-3.1zM8 13h8v-2H8v2zm9-6h-4v1.9h4c1.71 0 3.1 1.39 3.1 3.1s-1.39 3.1-3.1 3.1h-4V17h4c2.76 0 5-2.24 5-5s-2.24-5-5-5z" })
244
- ] });
245
- }
246
-
247
- // src/ContentResource/WebPageCreator/index.tsx
248
- import { jsx as jsx7 } from "react/jsx-runtime";
249
- var webPageCreator = {
250
- id: "@manifest-editor/web-page-creator",
251
- create: createWebPage,
252
- label: "Web page",
253
- summary: "Add link to an external web page",
254
- icon: /* @__PURE__ */ jsx7(LinkIcon, {}),
255
- render(ctx) {
256
- return /* @__PURE__ */ jsx7(CreateWebPageForm, { ...ctx });
257
- },
258
- resourceType: "ContentResource",
259
- resourceFields: ["label", "format"],
260
- supports: {
261
- parentFields: ["seeAlso", "rendering", "homepage"]
389
+ if (targetType === "Annotation") {
390
+ return ctx.embed({
391
+ ...annotation,
392
+ label: getValue(data.label) && data.label,
393
+ motivation: data.motivation || ctx.options.initialData?.motivation || "painting",
394
+ body,
395
+ target: ctx.getTarget()
396
+ });
397
+ }
398
+ if (targetType === "Canvas") {
399
+ const canvasId = ctx.generateId("canvas");
400
+ const pageId = ctx.generateId("annotation-page", { id: canvasId, type: "Canvas" });
401
+ const annotationResource = ctx.embed({
402
+ ...annotation,
403
+ motivation: "painting",
404
+ body,
405
+ target: { type: "SpecificResource", source: { id: canvasId, type: "Canvas" } }
406
+ });
407
+ const page = ctx.embed({
408
+ id: pageId,
409
+ type: "AnnotationPage",
410
+ items: [annotationResource]
411
+ });
412
+ return ctx.embed({
413
+ id: canvasId,
414
+ type: "Canvas",
415
+ label: data.label || { en: ["Untitled HTML canvas"] },
416
+ duration: data.duration || 0,
417
+ items: [page]
418
+ });
262
419
  }
263
- };
264
-
265
- // src/ContentResource/HTMLBodyCreator/create-html-body.tsx
266
- import { InputContainer as InputContainer3, InputLabel as InputLabel3, RichTextLanguageField } from "@manifest-editor/editors";
267
- import { useConfig } from "@manifest-editor/shell";
268
- import { useState as useState3 } from "react";
269
- import { jsx as jsx8, jsxs as jsxs5 } from "react/jsx-runtime";
270
- async function createHtmlBody(data, ctx) {
271
- return ctx.embed({
272
- id: ctx.generateId(`html/${data.language}`),
273
- language: data.language,
274
- type: "TextualBody",
275
- format: "text/html",
276
- value: data.body || ""
277
- });
278
420
  }
279
- function CreateHTMLBodyForm(props) {
280
- const { i18n } = useConfig();
281
- const [body, setBodyValue] = useState3("<p></p>");
282
- const [language, setLang] = useState3(i18n.defaultLanguage);
421
+ function CreateAudioAnnotationForm(props) {
422
+ const [url, setUrl] = useState2("");
423
+ const [duration, setDuration] = useState2(0);
424
+ const [error, setError] = useState2("");
425
+ const [label, setLabel] = useState2({ en: [""] });
283
426
  const onSubmit = () => {
284
427
  props.runCreate({
285
- language,
286
- body
428
+ url,
429
+ duration,
430
+ label
287
431
  });
288
432
  };
289
- return /* @__PURE__ */ jsxs5(PaddedSidebarContainer, { children: [
290
- /* @__PURE__ */ jsxs5(InputContainer3, { children: [
291
- /* @__PURE__ */ jsx8(InputLabel3, { children: "HTML Body" }),
292
- /* @__PURE__ */ jsx8(
293
- RichTextLanguageField,
294
- {
295
- value: body,
296
- language,
297
- onUpdateLanguage: (lng) => setLang(lng),
298
- languages: i18n.availableLanguages,
299
- onUpdate: (e) => setBodyValue(e)
300
- }
301
- )
433
+ useEffect2(() => {
434
+ setError("");
435
+ setDuration(0);
436
+ if (url) {
437
+ const labelFromUrl = url.split("/").pop()?.split(".").shift();
438
+ setLabel({ en: [labelFromUrl || ""] });
439
+ }
440
+ }, [url]);
441
+ return /* @__PURE__ */ jsxs4(PaddedSidebarContainer, { children: [
442
+ error ? /* @__PURE__ */ jsx4(ErrorMessage, { children: error }) : null,
443
+ /* @__PURE__ */ jsxs4(InputContainer, { children: [
444
+ /* @__PURE__ */ jsx4(InputLabel, { htmlFor: "audio-url", children: "URL" }),
445
+ /* @__PURE__ */ jsx4(Input, { type: "text", id: "audio-url", value: url, onChange: (e) => setUrl(e.target.value) })
302
446
  ] }),
303
- /* @__PURE__ */ jsx8(Button, { onClick: () => onSubmit(), children: "Create" })
447
+ url && !error ? /* @__PURE__ */ jsx4("div", { children: /* @__PURE__ */ jsx4(
448
+ LanguageFieldEditor,
449
+ {
450
+ containerId: "label",
451
+ focusId: "label_",
452
+ label: "Label",
453
+ fields: label,
454
+ onSave: (e) => setLabel(e.toInternationalString())
455
+ },
456
+ url + "__" + duration
457
+ ) }) : null,
458
+ url && !error ? /* @__PURE__ */ jsx4(CanvasPanel.AudioHTML, { media: { url, duration, type: "Sound" }, children: /* @__PURE__ */ jsx4(
459
+ MediaControls,
460
+ {
461
+ onError: (error2) => setError(error2),
462
+ onDuration: (duration2) => setDuration(duration2)
463
+ },
464
+ url + "__" + duration
465
+ ) }, url + "__" + duration) : null,
466
+ duration && !error ? /* @__PURE__ */ jsxs4(FormFieldWrapper, { children: [
467
+ /* @__PURE__ */ jsx4(InputLabel, { htmlFor: "duration", children: "Duration" }),
468
+ /* @__PURE__ */ jsx4(Input, { type: "number", id: "duration", value: duration, onChange: (e) => setDuration(e.target.valueAsNumber) })
469
+ ] }) : null,
470
+ url && !error && /* @__PURE__ */ jsx4(Button, { onClick: onSubmit, children: "Add audio" })
304
471
  ] });
305
472
  }
306
473
 
307
- // src/ContentResource/HTMLBodyCreator/index.tsx
308
- import { jsx as jsx9 } from "react/jsx-runtime";
309
- var htmlBodyCreator = {
310
- id: "@manifest-editor/html-body-creator",
311
- create: createHtmlBody,
312
- label: "HTML Body",
313
- summary: "Add HTML annotation body",
314
- icon: /* @__PURE__ */ jsx9(TextFormatIcon, {}),
474
+ // src/Annotation/AudioAnnotation/index.tsx
475
+ import { AudioIcon } from "@manifest-editor/components";
476
+ import { jsx as jsx5 } from "react/jsx-runtime";
477
+ var audioAnnotation = {
478
+ id: "@manifest-editor/audio-annotation",
479
+ create: createAudioAnnotation,
480
+ label: "Audio",
481
+ summary: "Audio annotation",
482
+ icon: /* @__PURE__ */ jsx5(AudioIcon, {}),
315
483
  render(ctx) {
316
- return /* @__PURE__ */ jsx9(CreateHTMLBodyForm, { ...ctx });
484
+ return /* @__PURE__ */ jsx5(CreateAudioAnnotationForm, { ...ctx });
317
485
  },
318
- resourceType: "ContentResource",
319
- resourceFields: ["id", "language", "type", "format", "value"],
486
+ resourceType: "Annotation",
487
+ resourceFields: ["id", "type", "motivation", "body", "target"],
488
+ additionalTypes: ["Canvas"],
320
489
  supports: {
321
- parentTypes: ["Annotation"],
322
- parentFields: ["body"]
490
+ initialData: true,
491
+ parentTypes: ["AnnotationPage", "Manifest"],
492
+ parentFields: ["items"]
323
493
  },
324
494
  staticFields: {
325
- type: "TextualBody",
326
- format: "text/html"
495
+ type: "Annotation"
327
496
  }
328
497
  };
329
498
 
330
- // src/Annotation/HTMLAnnotation/create-html-annotation.tsx
331
- import { useState as useState4 } from "react";
332
- import { InputContainer as InputContainer4, InputLabel as InputLabel4, LanguageFieldEditor as LanguageFieldEditor3 } from "@manifest-editor/editors";
333
- import { jsx as jsx10, jsxs as jsxs6 } from "react/jsx-runtime";
334
- async function createHtmlAnnotation(data, ctx) {
499
+ // src/Annotation/CaptionedImageAnnotation/index.tsx
500
+ import { AddImageIcon } from "@manifest-editor/components";
501
+
502
+ // src/Annotation/CaptionedImageAnnotation/create-captioned-image-annotation.tsx
503
+ import { Input as Input2, InputContainer as InputContainer2, InputLabel as InputLabel2, LanguageFieldEditor as LanguageFieldEditor2 } from "@manifest-editor/editors";
504
+ import { useState as useState3 } from "react";
505
+ import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
506
+ async function createCaptionedImageAnnotation(data, ctx) {
335
507
  const annotation = {
336
508
  id: ctx.generateId("annotation"),
337
509
  type: "Annotation"
@@ -354,6 +526,16 @@ async function createHtmlAnnotation(data, ctx) {
354
526
  );
355
527
  }
356
528
  }
529
+ const resource = await ctx.create(
530
+ "@manifest-editor/image-url-creator",
531
+ { url: data.imageUrl },
532
+ {
533
+ parent: { resource: annotation, property: "body" }
534
+ }
535
+ );
536
+ if (resource) {
537
+ bodies.push(resource);
538
+ }
357
539
  if (targetType === "Annotation") {
358
540
  return ctx.embed({
359
541
  ...annotation,
@@ -364,12 +546,18 @@ async function createHtmlAnnotation(data, ctx) {
364
546
  }
365
547
  if (targetType === "Canvas") {
366
548
  const canvasId = ctx.generateId("canvas");
367
- const pageId = ctx.generateId("annotation-page", { id: canvasId, type: "Canvas" });
549
+ const pageId = ctx.generateId("annotation-page", {
550
+ id: canvasId,
551
+ type: "Canvas"
552
+ });
368
553
  const annotationResource = ctx.embed({
369
554
  ...annotation,
370
555
  motivation: "painting",
371
556
  body: bodies,
372
- target: { type: "SpecificResource", source: { id: canvasId, type: "Canvas" } }
557
+ target: {
558
+ type: "SpecificResource",
559
+ source: { id: canvasId, type: "Canvas" }
560
+ }
373
561
  });
374
562
  const page = ctx.embed({
375
563
  id: pageId,
@@ -386,18 +574,26 @@ async function createHtmlAnnotation(data, ctx) {
386
574
  });
387
575
  }
388
576
  }
389
- function CreateHTMLAnnotation(props) {
390
- const [body, setBody] = useState4({ en: [""] });
391
- const onSubmit = () => {
577
+ function CreateCaptionedImageAnnotation(props) {
578
+ const [body, setBody] = useState3({ en: [""] });
579
+ const onSubmit = (e) => {
580
+ e.preventDefault();
581
+ const data = new FormData(e.target);
582
+ const formData = Object.fromEntries(data.entries());
392
583
  props.runCreate({
393
- body
584
+ body,
585
+ imageUrl: formData.url
394
586
  });
395
587
  };
396
- return /* @__PURE__ */ jsxs6(PaddedSidebarContainer, { children: [
397
- /* @__PURE__ */ jsxs6(InputContainer4, { children: [
398
- /* @__PURE__ */ jsx10(InputLabel4, { children: "HTML Body" }),
399
- /* @__PURE__ */ jsx10(
400
- LanguageFieldEditor3,
588
+ return /* @__PURE__ */ jsx6("form", { onSubmit, children: /* @__PURE__ */ jsxs5(PaddedSidebarContainer, { children: [
589
+ /* @__PURE__ */ jsx6(InputContainer2, { children: /* @__PURE__ */ jsxs5(InputContainer2, { $wide: true, children: [
590
+ /* @__PURE__ */ jsx6(InputLabel2, { htmlFor: "id", children: "Link to Image" }),
591
+ /* @__PURE__ */ jsx6(Input2, { id: "url", name: "url", defaultValue: "" })
592
+ ] }) }),
593
+ /* @__PURE__ */ jsxs5(InputContainer2, { children: [
594
+ /* @__PURE__ */ jsx6(InputLabel2, { children: "Image Caption" }),
595
+ /* @__PURE__ */ jsx6(
596
+ LanguageFieldEditor2,
401
597
  {
402
598
  focusId: "html-content",
403
599
  label: "HTML Content",
@@ -406,28 +602,27 @@ function CreateHTMLAnnotation(props) {
406
602
  }
407
603
  )
408
604
  ] }),
409
- /* @__PURE__ */ jsx10(Button, { onClick: () => onSubmit(), children: "Create" })
410
- ] });
605
+ /* @__PURE__ */ jsx6(Button, { type: "submit", children: "Create" })
606
+ ] }) });
411
607
  }
412
608
 
413
- // src/Annotation/HTMLAnnotation/index.tsx
414
- import { HTMLIcon } from "@manifest-editor/components";
415
- import { jsx as jsx11 } from "react/jsx-runtime";
416
- var htmlAnnotation = {
417
- id: "@manifest-editor/html-annotation",
418
- create: createHtmlAnnotation,
419
- label: "HTML",
420
- summary: "HTML annotation",
421
- icon: /* @__PURE__ */ jsx11(HTMLIcon, {}),
609
+ // src/Annotation/CaptionedImageAnnotation/index.tsx
610
+ import { jsx as jsx7 } from "react/jsx-runtime";
611
+ var captionedImageAnnotation = {
612
+ id: "@manifest-editor/captioned-image-annotation",
613
+ create: createCaptionedImageAnnotation,
614
+ label: "Captioned image",
615
+ summary: "Add an image from a URL with caption",
616
+ icon: /* @__PURE__ */ jsx7(AddImageIcon, {}),
422
617
  render(ctx) {
423
- return /* @__PURE__ */ jsx11(CreateHTMLAnnotation, { ...ctx });
618
+ return /* @__PURE__ */ jsx7(CreateCaptionedImageAnnotation, { ...ctx });
424
619
  },
425
620
  resourceType: "Annotation",
426
621
  resourceFields: ["id", "type", "motivation", "body", "target"],
427
622
  additionalTypes: ["Canvas"],
428
623
  supports: {
429
624
  initialData: true,
430
- parentTypes: ["AnnotationPage", "Manifest"],
625
+ parentTypes: ["AnnotationPage"],
431
626
  parentFields: ["items"]
432
627
  },
433
628
  staticFields: {
@@ -435,306 +630,121 @@ var htmlAnnotation = {
435
630
  }
436
631
  };
437
632
 
438
- // ../ui/icons/ThumbnailStripIcon.tsx
439
- import { jsx as jsx12, jsxs as jsxs7 } from "react/jsx-runtime";
440
- var ThumbnailStripIcon = () => /* @__PURE__ */ jsx12(
441
- "svg",
442
- {
443
- xmlns: "http://www.w3.org/2000/svg",
444
- height: "24px",
445
- viewBox: "0 0 24 24",
446
- width: "24px",
447
- fill: "#000000",
448
- transform: "scale(-1, 1)",
449
- children: /* @__PURE__ */ jsxs7("g", { children: [
450
- /* @__PURE__ */ jsx12("rect", { fill: "none", height: "24", width: "24" }),
451
- /* @__PURE__ */ jsx12("path", { d: "M16,20H2V4h14V20z M18,8h4V4h-4V8z M18,20h4v-4h-4V20z M18,14h4v-4h-4V14z" })
452
- ] })
453
- }
454
- );
455
-
456
- // src/AnnotationPage/EmptyAnnotationPage/index.tsx
457
- import { jsx as jsx13 } from "react/jsx-runtime";
458
- var emptyAnnotationPage = {
459
- id: "@manifest-editor/empty-annotation-page",
460
- create: createEmptyAnnotationPage,
461
- label: "Empty annotation page",
462
- summary: "AnnotationPage with nothing",
463
- icon: /* @__PURE__ */ jsx13(ThumbnailStripIcon, {}),
464
- resourceType: "AnnotationPage",
465
- resourceFields: ["id", "type", "items"],
466
- supports: {
467
- parentTypes: ["Manifest", "Canvas", "Range"],
468
- parentFieldMap: {
469
- Manifest: ["annotations"],
470
- Canvas: ["items", "annotations"],
471
- Range: ["annotations"]
633
+ // src/Annotation/HTMLAnnotation/create-html-annotation.tsx
634
+ import { useState as useState4 } from "react";
635
+ import {
636
+ InputContainer as InputContainer3,
637
+ InputLabel as InputLabel3,
638
+ LanguageFieldEditor as LanguageFieldEditor3
639
+ } from "@manifest-editor/editors";
640
+ import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
641
+ async function createHtmlAnnotation(data, ctx) {
642
+ const annotation = {
643
+ id: ctx.generateId("annotation"),
644
+ type: "Annotation"
645
+ };
646
+ const targetType = ctx.options.targetType;
647
+ const languages = Object.keys(data.body);
648
+ const bodies = [];
649
+ for (const language of languages) {
650
+ const body = data.body[language].join("\n");
651
+ if (body) {
652
+ bodies.push(
653
+ await ctx.create(
654
+ "@manifest-editor/html-body-creator",
655
+ {
656
+ language,
657
+ body
658
+ },
659
+ { parent: { resource: annotation, property: "items" } }
660
+ )
661
+ );
472
662
  }
473
663
  }
474
- };
475
- async function createEmptyAnnotationPage(data, ctx) {
476
- return ctx.embed({
477
- id: ctx.generateId("annotations"),
478
- type: "AnnotationPage",
479
- items: []
480
- });
481
- }
482
-
483
- // src/ContentResource/YouTubeCreator/index.tsx
484
- import { YouTubeIcon } from "@manifest-editor/components";
485
-
486
- // src/ContentResource/YouTubeCreator/create-youtube-body.tsx
487
- import { InputContainer as InputContainer5, InputLabel as InputLabel5, Input as Input3 } from "@manifest-editor/editors";
488
- import { jsx as jsx14, jsxs as jsxs8 } from "react/jsx-runtime";
489
- var ytRegex = /^.*(?:(?:youtu\.be\/|v\/|vi\/|u\/\w\/|embed\/|shorts\/)|(?:(?:watch)?\?vi?=|&vi?=))([^#&?]*).*/;
490
- function getYouTubeId(url) {
491
- const id = (url || "").match(ytRegex);
492
- return id ? id[1] : null;
493
- }
494
- async function createYoutubeBody(data, ctx) {
495
- const id = getYouTubeId(data.youtubeUrl);
496
- const body = ctx.embed({
497
- id: `https://www.youtube.com/watch?v=${id}`,
498
- type: "Video",
499
- service: [
500
- {
501
- profile: "http://digirati.com/objectifier",
502
- params: {
503
- data: `https://www.youtube.com/embed/${id}`
504
- }
505
- },
506
- {
507
- id: `https://www.youtube.com/watch?v=${id}`,
508
- profile: "https://www.youtube.com"
509
- }
510
- ]
511
- });
512
- if (ctx.options.targetType === "Canvas") {
664
+ if (targetType === "Annotation") {
665
+ return ctx.embed({
666
+ ...annotation,
667
+ motivation: data.motivation || ctx.options.initialData?.motivation || "painting",
668
+ body: bodies,
669
+ target: ctx.getTarget()
670
+ });
671
+ }
672
+ if (targetType === "Canvas") {
513
673
  const canvasId = ctx.generateId("canvas");
514
- const pageId = ctx.generateId("annotation-page", { id: canvasId, type: "Canvas" });
515
- const annotation = await ctx.embed({
516
- id: ctx.generateId("annotation", { id: pageId, type: "AnnotationPage" }),
517
- type: "Annotation",
674
+ const pageId = ctx.generateId("annotation-page", {
675
+ id: canvasId,
676
+ type: "Canvas"
677
+ });
678
+ const annotationResource = ctx.embed({
679
+ ...annotation,
518
680
  motivation: "painting",
519
- body: [body],
520
- target: { type: "SpecificResource", source: { id: canvasId, type: "Canvas" } }
681
+ body: bodies,
682
+ target: {
683
+ type: "SpecificResource",
684
+ source: { id: canvasId, type: "Canvas" }
685
+ }
521
686
  });
522
687
  const page = ctx.embed({
523
688
  id: pageId,
524
689
  type: "AnnotationPage",
525
- items: [annotation]
690
+ items: [annotationResource]
526
691
  });
527
692
  return ctx.embed({
528
693
  id: canvasId,
529
694
  type: "Canvas",
530
- label: data.label || { en: ["Untitled YouTube canvas"] },
695
+ label: data.label || { en: ["Untitled HTML canvas"] },
531
696
  height: data.height || 1e3,
532
697
  width: data.width || 1e3,
533
- duration: data.duration || 1,
534
698
  items: [page]
535
699
  });
536
700
  }
537
- if (ctx.options.targetType === "Annotation" || ctx.options.targetType === "Canvas") {
538
- return ctx.embed({
539
- id: ctx.generateId("annotation"),
540
- type: "Annotation",
541
- motivation: "painting",
542
- body: [body],
543
- target: ctx.getTarget()
544
- });
545
- }
546
- return body;
547
- }
548
- function YouTubeForm(props) {
549
- const onSubmit = (e) => {
550
- e.preventDefault();
551
- const data = new FormData(e.target);
552
- const formData = Object.fromEntries(data.entries());
553
- if (formData.youtubeUrl) {
554
- props.runCreate({ youtubeUrl: formData.youtubeUrl });
555
- }
556
- };
557
- return /* @__PURE__ */ jsx14(PaddedSidebarContainer, { children: /* @__PURE__ */ jsxs8("form", { onSubmit, children: [
558
- /* @__PURE__ */ jsxs8(InputContainer5, { $wide: true, children: [
559
- /* @__PURE__ */ jsx14(InputLabel5, { htmlFor: "id", children: "Link to YouTube" }),
560
- /* @__PURE__ */ jsx14(Input3, { id: "youtubeUrl", name: "youtubeUrl", defaultValue: "" })
561
- ] }),
562
- /* @__PURE__ */ jsx14(Button, { type: "submit", children: "Create" })
563
- ] }) });
564
- }
565
-
566
- // src/ContentResource/YouTubeCreator/index.tsx
567
- import { jsx as jsx15 } from "react/jsx-runtime";
568
- var youTubeBodyCreator = {
569
- id: "@manifest-editor/youtube",
570
- create: createYoutubeBody,
571
- label: "YouTube",
572
- summary: "Embed YouTube",
573
- icon: /* @__PURE__ */ jsx15(YouTubeIcon, {}),
574
- resourceType: "ContentResource",
575
- additionalTypes: ["Canvas", "Annotation"],
576
- resourceFields: ["id", "type", "service"],
577
- render: (ctx) => /* @__PURE__ */ jsx15(YouTubeForm, { ...ctx }),
578
- supports: {
579
- parentTypes: ["Annotation", "Manifest", "AnnotationPage"],
580
- parentFieldMap: {
581
- Annotation: ["body"],
582
- Manifest: ["items"],
583
- AnnotationPage: ["items"]
584
- }
585
- },
586
- staticFields: {
587
- type: "Video"
588
- }
589
- };
590
-
591
- // src/ContentResource/ImageUrlCreator/create-image-url.tsx
592
- import { InputContainer as InputContainer6, InputLabel as InputLabel6, Input as Input4 } from "@manifest-editor/editors";
593
- import { getImageDimensions, getFormat } from "@manifest-editor/shell";
594
- import { jsx as jsx16, jsxs as jsxs9 } from "react/jsx-runtime";
595
- async function createImageUrl(data, ctx) {
596
- if (!data.height || !data.width) {
597
- const dimensions = await getImageDimensions(data.url);
598
- if (dimensions) {
599
- data.height = dimensions.height;
600
- data.width = dimensions.width;
601
- }
602
- }
603
- return ctx.embed({
604
- id: data.url,
605
- type: "Image",
606
- format: data.format || await getFormat(data.url),
607
- height: data.height,
608
- width: data.width,
609
- service: data.service ? Array.isArray(data.service) ? data.service : [data.service] : void 0
610
- });
611
701
  }
612
- function CreateImageUrlForm(props) {
613
- const onSubmit = (e) => {
614
- e.preventDefault();
615
- const data = new FormData(e.target);
616
- const formData = Object.fromEntries(data.entries());
617
- if (formData.url) {
618
- props.runCreate({ url: formData.url });
619
- }
702
+ function CreateHTMLAnnotation(props) {
703
+ const [body, setBody] = useState4({ en: [""] });
704
+ const onSubmit = () => {
705
+ props.runCreate({
706
+ body
707
+ });
620
708
  };
621
- return /* @__PURE__ */ jsx16(PaddedSidebarContainer, { children: /* @__PURE__ */ jsxs9("form", { onSubmit, children: [
622
- /* @__PURE__ */ jsxs9(InputContainer6, { $wide: true, children: [
623
- /* @__PURE__ */ jsx16(InputLabel6, { htmlFor: "id", children: "Link to Image" }),
624
- /* @__PURE__ */ jsx16(Input4, { id: "url", name: "url", defaultValue: "" })
709
+ return /* @__PURE__ */ jsxs6(PaddedSidebarContainer, { children: [
710
+ /* @__PURE__ */ jsxs6(InputContainer3, { children: [
711
+ /* @__PURE__ */ jsx8(InputLabel3, { children: "HTML Body" }),
712
+ /* @__PURE__ */ jsx8(
713
+ LanguageFieldEditor3,
714
+ {
715
+ focusId: "html-content",
716
+ label: "HTML Content",
717
+ fields: body,
718
+ onSave: (e) => setBody(e.toInternationalString())
719
+ }
720
+ )
625
721
  ] }),
626
- /* @__PURE__ */ jsx16(Button, { type: "submit", children: "Create" })
627
- ] }) });
722
+ /* @__PURE__ */ jsx8(Button, { onClick: () => onSubmit(), children: "Create" })
723
+ ] });
628
724
  }
629
725
 
630
- // src/ContentResource/ImageUrlCreator/index.tsx
631
- import { AddImageIcon } from "@manifest-editor/components";
632
- import { jsx as jsx17 } from "react/jsx-runtime";
633
- var imageUrlCreator = {
634
- id: "@manifest-editor/image-url-creator",
635
- create: createImageUrl,
636
- label: "Image",
637
- summary: "Image from a URL",
638
- icon: /* @__PURE__ */ jsx17(AddImageIcon, {}),
726
+ // src/Annotation/HTMLAnnotation/index.tsx
727
+ import { HTMLIcon as HTMLIcon2 } from "@manifest-editor/components";
728
+ import { jsx as jsx9 } from "react/jsx-runtime";
729
+ var htmlAnnotation = {
730
+ id: "@manifest-editor/html-annotation",
731
+ create: createHtmlAnnotation,
732
+ label: "HTML",
733
+ summary: "HTML annotation",
734
+ icon: /* @__PURE__ */ jsx9(HTMLIcon2, {}),
639
735
  render(ctx) {
640
- return /* @__PURE__ */ jsx17(CreateImageUrlForm, { ...ctx });
736
+ return /* @__PURE__ */ jsx9(CreateHTMLAnnotation, { ...ctx });
641
737
  },
642
- resourceType: "ContentResource",
643
- resourceFields: ["format"],
738
+ resourceType: "Annotation",
739
+ resourceFields: ["id", "type", "motivation", "body", "target"],
740
+ additionalTypes: ["Canvas"],
644
741
  supports: {
645
- parentFields: ["logo", "body", "thumbnail"]
742
+ initialData: true,
743
+ parentTypes: ["AnnotationPage", "Manifest"],
744
+ parentFields: ["items"]
646
745
  },
647
746
  staticFields: {
648
- type: "Image"
649
- }
650
- };
651
-
652
- // src/ContentResource/ImageServiceCreator/create-image-service.tsx
653
- import {
654
- imageServiceRequestToString,
655
- createImageServiceRequest,
656
- canonicalServiceUrl
657
- } from "@iiif/parser/image-3";
658
- import { InputContainer as InputContainer7, InputLabel as InputLabel7, Input as Input5 } from "@manifest-editor/editors";
659
- import { jsx as jsx18, jsxs as jsxs10 } from "react/jsx-runtime";
660
- async function createImageServer(data, ctx) {
661
- const request = createImageServiceRequest(data.service);
662
- const imageId = imageServiceRequestToString({
663
- identifier: request.identifier,
664
- server: request.server,
665
- scheme: request.scheme,
666
- type: "image",
667
- size: {
668
- max: !data.size?.width && !data.size?.height,
669
- confined: false,
670
- upscaled: false,
671
- ...data.size || {}
672
- },
673
- format: "jpg",
674
- // This isn't how it should be modelled, always full,
675
- // region: data.selector ? data.selector : { full: true },
676
- region: { full: true },
677
- rotation: { angle: 0 },
678
- quality: "default",
679
- prefix: request.prefix,
680
- originalPath: request.originalPath
681
- });
682
- const resource = ctx.embed({
683
- id: imageId,
684
- type: "Image",
685
- format: data.format || "image/jpeg",
686
- height: data.height || data.service.height,
687
- width: data.width || data.service.width,
688
- service: data.embedService === false ? void 0 : [data.service]
689
- });
690
- return resource;
691
- }
692
- function CreateImageServerForm(props) {
693
- const onSubmit = (e) => {
694
- e.preventDefault();
695
- const data = new FormData(e.target);
696
- const formData = Object.fromEntries(data.entries());
697
- const url = formData.url;
698
- if (url) {
699
- const canon = canonicalServiceUrl(url);
700
- fetch(canon).then((r) => r.json()).then((service) => {
701
- if (service["@id"]) {
702
- service.id = service["@id"];
703
- }
704
- if (service["@type"]) {
705
- service.type = service["@type"];
706
- }
707
- props.runCreate({ url: formData.url, service });
708
- });
709
- }
710
- };
711
- return /* @__PURE__ */ jsx18(PaddedSidebarContainer, { children: /* @__PURE__ */ jsxs10("form", { onSubmit, children: [
712
- /* @__PURE__ */ jsxs10(InputContainer7, { $wide: true, children: [
713
- /* @__PURE__ */ jsx18(InputLabel7, { htmlFor: "id", children: "Link to image service" }),
714
- /* @__PURE__ */ jsx18(Input5, { id: "url", name: "url", defaultValue: "" })
715
- ] }),
716
- /* @__PURE__ */ jsx18(Button, { type: "submit", children: "Create" })
717
- ] }) });
718
- }
719
-
720
- // src/ContentResource/ImageServiceCreator/index.tsx
721
- import { jsx as jsx19 } from "react/jsx-runtime";
722
- var imageServiceCreator = {
723
- id: "@manifest-editor/image-service-creator",
724
- create: createImageServer,
725
- label: "Image Service",
726
- summary: "Add an image from Image Service",
727
- icon: /* @__PURE__ */ jsx19(TextFormatIcon, {}),
728
- render(ctx) {
729
- return /* @__PURE__ */ jsx19(CreateImageServerForm, { ...ctx });
730
- },
731
- resourceType: "ContentResource",
732
- resourceFields: ["id", "type", "height", "width", "format", "service"],
733
- supports: {
734
- parentFields: ["logo", "body", "thumbnail"]
735
- },
736
- staticFields: {
737
- type: "Image"
747
+ type: "Annotation"
738
748
  }
739
749
  };
740
750
 
@@ -902,8 +912,76 @@ var resizeToFitService = {
902
912
  }
903
913
  };
904
914
 
915
+ // src/ContentResource/ImageServiceCreator/create-image-service.tsx
916
+ import {
917
+ imageServiceRequestToString,
918
+ createImageServiceRequest,
919
+ canonicalServiceUrl
920
+ } from "@iiif/parser/image-3";
921
+ import { InputContainer as InputContainer4, InputLabel as InputLabel4, Input as Input3 } from "@manifest-editor/editors";
922
+ import { jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
923
+ async function createImageServer(data, ctx) {
924
+ const request = createImageServiceRequest(data.service);
925
+ const imageId = imageServiceRequestToString({
926
+ identifier: request.identifier,
927
+ server: request.server,
928
+ scheme: request.scheme,
929
+ type: "image",
930
+ size: {
931
+ max: !data.size?.width && !data.size?.height,
932
+ confined: false,
933
+ upscaled: false,
934
+ ...data.size || {}
935
+ },
936
+ format: "jpg",
937
+ // This isn't how it should be modelled, always full,
938
+ // region: data.selector ? data.selector : { full: true },
939
+ region: { full: true },
940
+ rotation: { angle: 0 },
941
+ quality: "default",
942
+ prefix: request.prefix,
943
+ originalPath: request.originalPath
944
+ });
945
+ const resource = ctx.embed({
946
+ id: imageId,
947
+ type: "Image",
948
+ format: data.format || "image/jpeg",
949
+ height: data.height || data.service.height,
950
+ width: data.width || data.service.width,
951
+ service: data.embedService === false ? void 0 : [data.service]
952
+ });
953
+ return resource;
954
+ }
955
+ function CreateImageServerForm(props) {
956
+ const onSubmit = (e) => {
957
+ e.preventDefault();
958
+ const data = new FormData(e.target);
959
+ const formData = Object.fromEntries(data.entries());
960
+ const url = formData.url;
961
+ if (url) {
962
+ const canon = canonicalServiceUrl(url);
963
+ fetch(canon).then((r) => r.json()).then((service) => {
964
+ if (service["@id"]) {
965
+ service.id = service["@id"];
966
+ }
967
+ if (service["@type"]) {
968
+ service.type = service["@type"];
969
+ }
970
+ props.runCreate({ url: formData.url, service });
971
+ });
972
+ }
973
+ };
974
+ return /* @__PURE__ */ jsx10(PaddedSidebarContainer, { children: /* @__PURE__ */ jsxs7("form", { onSubmit, children: [
975
+ /* @__PURE__ */ jsxs7(InputContainer4, { $wide: true, children: [
976
+ /* @__PURE__ */ jsx10(InputLabel4, { htmlFor: "id", children: "Link to image service" }),
977
+ /* @__PURE__ */ jsx10(Input3, { id: "url", name: "url", defaultValue: "" })
978
+ ] }),
979
+ /* @__PURE__ */ jsx10(Button, { type: "submit", children: "Create" })
980
+ ] }) });
981
+ }
982
+
905
983
  // src/Annotation/ImageServiceAnnotation/create-service-annotation.tsx
906
- import { jsx as jsx20 } from "react/jsx-runtime";
984
+ import { jsx as jsx11 } from "react/jsx-runtime";
907
985
  async function createImageServiceAnnotation(data, ctx) {
908
986
  const annotation = {
909
987
  id: ctx.generateId("annotation"),
@@ -946,21 +1024,135 @@ async function createImageServiceAnnotation(data, ctx) {
946
1024
  }
947
1025
  }
948
1026
  function CreateImageServiceAnnotationForm(props) {
949
- return /* @__PURE__ */ jsx20(CreateImageServerForm, { ...props });
1027
+ return /* @__PURE__ */ jsx11(CreateImageServerForm, { ...props });
950
1028
  }
951
1029
 
952
1030
  // src/Annotation/ImageServiceAnnotation/index.tsx
953
1031
  import { IIIFLogo } from "@manifest-editor/components";
954
- import { jsx as jsx21 } from "react/jsx-runtime";
1032
+ import { jsx as jsx12 } from "react/jsx-runtime";
955
1033
  var imageServiceAnnotation = {
956
1034
  id: "@manifest-editor/image-service-annotation",
957
1035
  create: createImageServiceAnnotation,
958
1036
  label: "IIIF Image",
959
1037
  dependencies: ["@manifest-editor/image-service-creator"],
960
1038
  summary: "IIIF Image service",
961
- icon: /* @__PURE__ */ jsx21(IIIFLogo, { style: { padding: 10 } }),
1039
+ tags: ["image", "image-service"],
1040
+ icon: /* @__PURE__ */ jsx12(IIIFLogo, { style: { padding: 10 } }),
1041
+ render(ctx) {
1042
+ return /* @__PURE__ */ jsx12(CreateImageServiceAnnotationForm, { ...ctx });
1043
+ },
1044
+ resourceType: "Annotation",
1045
+ resourceFields: ["id", "type", "motivation", "body", "target"],
1046
+ additionalTypes: ["Canvas"],
1047
+ supports: {
1048
+ parentTypes: ["AnnotationPage", "Manifest"],
1049
+ parentFields: ["items"]
1050
+ },
1051
+ sideEffects: [resizeToFitService, resizeResourceToEmptyCanvas, repositionMultipleImages],
1052
+ staticFields: {
1053
+ type: "Annotation"
1054
+ }
1055
+ };
1056
+
1057
+ // src/ContentResource/ImageUrlCreator/create-image-url.tsx
1058
+ import { InputContainer as InputContainer5, InputLabel as InputLabel5, Input as Input4 } from "@manifest-editor/editors";
1059
+ import { getImageDimensions, getFormat } from "@manifest-editor/shell";
1060
+ import { jsx as jsx13, jsxs as jsxs8 } from "react/jsx-runtime";
1061
+ async function createImageUrl(data, ctx) {
1062
+ if (!data.height || !data.width) {
1063
+ const dimensions = await getImageDimensions(data.url);
1064
+ if (dimensions) {
1065
+ data.height = dimensions.height;
1066
+ data.width = dimensions.width;
1067
+ }
1068
+ }
1069
+ return ctx.embed({
1070
+ id: data.url,
1071
+ type: "Image",
1072
+ format: data.format || await getFormat(data.url),
1073
+ height: data.height,
1074
+ width: data.width,
1075
+ service: data.service ? Array.isArray(data.service) ? data.service : [data.service] : void 0
1076
+ });
1077
+ }
1078
+ function CreateImageUrlForm(props) {
1079
+ const onSubmit = (e) => {
1080
+ e.preventDefault();
1081
+ const data = new FormData(e.target);
1082
+ const formData = Object.fromEntries(data.entries());
1083
+ if (formData.url) {
1084
+ props.runCreate({ url: formData.url });
1085
+ }
1086
+ };
1087
+ return /* @__PURE__ */ jsx13(PaddedSidebarContainer, { children: /* @__PURE__ */ jsxs8("form", { onSubmit, children: [
1088
+ /* @__PURE__ */ jsxs8(InputContainer5, { $wide: true, children: [
1089
+ /* @__PURE__ */ jsx13(InputLabel5, { htmlFor: "id", children: "Link to Image" }),
1090
+ /* @__PURE__ */ jsx13(Input4, { id: "url", name: "url", defaultValue: "" })
1091
+ ] }),
1092
+ /* @__PURE__ */ jsx13(Button, { type: "submit", children: "Create" })
1093
+ ] }) });
1094
+ }
1095
+
1096
+ // src/Annotation/ImageUrlAnnotation/create-image-url-annotation.tsx
1097
+ import { jsx as jsx14 } from "react/jsx-runtime";
1098
+ async function createImageUrlAnnotation(data, ctx) {
1099
+ const annotation = {
1100
+ id: ctx.generateId("annotation"),
1101
+ type: "Annotation"
1102
+ };
1103
+ const targetType = ctx.options.targetType;
1104
+ const resource = await ctx.create("@manifest-editor/image-url-creator", data, {
1105
+ parent: { resource: annotation, property: "body" }
1106
+ });
1107
+ if (targetType === "Annotation") {
1108
+ return {
1109
+ ...annotation,
1110
+ body: [resource],
1111
+ motivation: data.motivation || "painting",
1112
+ target: ctx.getTarget()
1113
+ };
1114
+ }
1115
+ if (targetType === "Canvas") {
1116
+ const canvasId = ctx.generateId("canvas");
1117
+ const pageId = ctx.generateId("annotation-page", { id: canvasId, type: "Canvas" });
1118
+ const annotationResource = ctx.embed({
1119
+ ...annotation,
1120
+ motivation: "painting",
1121
+ body: [resource],
1122
+ target: { type: "SpecificResource", source: { id: canvasId, type: "Canvas" } }
1123
+ });
1124
+ const page = ctx.embed({
1125
+ id: pageId,
1126
+ type: "AnnotationPage",
1127
+ items: [annotationResource]
1128
+ });
1129
+ return ctx.embed({
1130
+ id: canvasId,
1131
+ type: "Canvas",
1132
+ label: data.label || { en: ["Untitled canvas"] },
1133
+ height: resource.resource.height || 1e3,
1134
+ width: resource.resource.width || 1e3,
1135
+ items: [page]
1136
+ });
1137
+ }
1138
+ }
1139
+ function CreateImageUrlAnnotationForm(props) {
1140
+ return /* @__PURE__ */ jsx14(CreateImageUrlForm, { ...props });
1141
+ }
1142
+
1143
+ // src/Annotation/ImageUrlAnnotation/index.tsx
1144
+ import { AddImageIcon as AddImageIcon2 } from "@manifest-editor/components";
1145
+ import { jsx as jsx15 } from "react/jsx-runtime";
1146
+ var imageUrlAnnotation = {
1147
+ id: "@manifest-editor/image-url-annotation",
1148
+ create: createImageUrlAnnotation,
1149
+ label: "Image",
1150
+ dependencies: ["@manifest-editor/image-url-creator"],
1151
+ summary: "Image from URL",
1152
+ tags: ["image"],
1153
+ icon: /* @__PURE__ */ jsx15(AddImageIcon2, {}),
962
1154
  render(ctx) {
963
- return /* @__PURE__ */ jsx21(CreateImageServiceAnnotationForm, { ...ctx });
1155
+ return /* @__PURE__ */ jsx15(CreateImageUrlAnnotationForm, { ...ctx });
964
1156
  },
965
1157
  resourceType: "Annotation",
966
1158
  resourceFields: ["id", "type", "motivation", "body", "target"],
@@ -976,14 +1168,14 @@ var imageServiceAnnotation = {
976
1168
  };
977
1169
 
978
1170
  // src/Annotation/NoBodyAnnotation/index.tsx
979
- import { EmptyCanvasIcon as EmptyCanvasIcon2 } from "@manifest-editor/components";
980
- import { jsx as jsx22 } from "react/jsx-runtime";
1171
+ import { EmptyCanvasIcon } from "@manifest-editor/components";
1172
+ import { jsx as jsx16 } from "react/jsx-runtime";
981
1173
  var noBodyAnnotation = {
982
1174
  id: "@manifest-editor/no-body-annotation",
983
1175
  create: createNoBodyAnnotation,
984
1176
  label: "Annotation without body",
985
1177
  summary: "Add an annotation that only has motivation and target",
986
- icon: /* @__PURE__ */ jsx22(EmptyCanvasIcon2, {}),
1178
+ icon: /* @__PURE__ */ jsx16(EmptyCanvasIcon, {}),
987
1179
  resourceType: "Annotation",
988
1180
  resourceFields: ["id", "type", "motivation", "target"],
989
1181
  supports: {
@@ -1007,192 +1199,73 @@ function createNoBodyAnnotation(data, ctx) {
1007
1199
  });
1008
1200
  }
1009
1201
 
1010
- // src/ContentResource/IIIFBrowserCreator/iiif-browser-creator.tsx
1011
- import { normaliseContentState, parseContentState } from "@iiif/helpers";
1012
- import { lazy } from "react";
1013
-
1014
- // ../../node_modules/.pnpm/tiny-invariant@1.3.3/node_modules/tiny-invariant/dist/esm/tiny-invariant.js
1015
- var isProduction = false;
1016
- var prefix = "Invariant failed";
1017
- function invariant(condition, message) {
1018
- if (condition) {
1019
- return;
1020
- }
1021
- if (isProduction) {
1022
- throw new Error(prefix);
1023
- }
1024
- var provided = typeof message === "function" ? message() : message;
1025
- var value = provided ? "".concat(prefix, ": ").concat(provided) : prefix;
1026
- throw new Error(value);
1027
- }
1202
+ // src/Annotation/VideoAnnotation/create-video-annotation.tsx
1203
+ import { getValue as getValue2 } from "@iiif/helpers";
1204
+ import { InputContainer as InputContainer6, InputLabel as InputLabel6, Input as Input5, LanguageFieldEditor as LanguageFieldEditor4, DimensionsTriplet } from "@manifest-editor/editors";
1205
+ import { ErrorMessage as ErrorMessage2 } from "@manifest-editor/components";
1206
+ import { useState as useState5, useEffect as useEffect3 } from "react";
1028
1207
 
1029
- // src/ContentResource/IIIFBrowserCreator/iiif-browser-creator.tsx
1030
- async function createFromIIIFBrowserOutput(data, ctx) {
1031
- const targetType = ctx.options.targetType;
1032
- if (Array.isArray(data.output)) {
1033
- throw new Error("Multiple items not yet supported");
1034
- }
1035
- const contentState = normaliseContentState(
1036
- typeof data.output === "string" ? parseContentState(data.output) : data.output
1037
- );
1038
- if (!contentState.target || contentState.target.length === 0) {
1039
- throw new Error("No target found in content state");
1040
- }
1041
- const target = contentState.target[0];
1042
- const type = target.source.type;
1043
- const previewVault = ctx.getPreviewVault();
1044
- if (targetType === "Canvas" || targetType === "Annotation") {
1045
- if (type === "Canvas") {
1046
- const canvasId = target.source.id;
1047
- const manifestId = target.source.partOf?.find((t) => t.type === "Manifest");
1048
- invariant(manifestId, "Could not load external resource without partOf");
1049
- const manifest = await previewVault.loadManifest(manifestId);
1050
- invariant(manifest, "Manifest not found");
1051
- const canvasRef = manifest.items.find((item) => item.id === canvasId);
1052
- invariant(canvasRef, "Canvas not found");
1053
- const canvas = previewVault.get(canvasRef);
1054
- if (targetType === "Canvas") {
1055
- return ctx.embed(previewVault.toPresentation3(canvas));
1056
- }
1057
- const annotationPage = previewVault.get(canvas.items[0]);
1058
- const annotation = previewVault.get(annotationPage.items[0]);
1059
- if (targetType === "Annotation") {
1060
- const fullAnnotation = previewVault.toPresentation3(annotation);
1061
- if (fullAnnotation && fullAnnotation?.body?.service) {
1062
- const width = fullAnnotation?.body.width;
1063
- const height = fullAnnotation?.body.height;
1064
- const service = Array.isArray(fullAnnotation?.body?.service) ? fullAnnotation?.body?.service[0] : fullAnnotation?.body?.service;
1065
- if (!service.width || !service.height) {
1066
- service.width = width;
1067
- service.height = height;
1068
- }
1069
- }
1070
- if (target.selector && target.selector.type === "BoxSelector") {
1071
- const selector = {
1072
- "@context": "http://iiif.io/api/annex/openannotation/context.json",
1073
- type: "iiif:ImageApiSelector",
1074
- region: [
1075
- ~~target.selector.spatial.x,
1076
- ~~target.selector.spatial.y,
1077
- ~~target.selector.spatial.width,
1078
- ~~target.selector.spatial.height
1079
- ].join(",")
1080
- };
1081
- fullAnnotation.body = {
1082
- id: ctx.generateId("SpecificResource", annotation),
1083
- type: "SpecificResource",
1084
- source: fullAnnotation.body,
1085
- selector
1086
- };
1087
- return ctx.embed({
1088
- ...fullAnnotation,
1089
- target: ctx.getTarget()
1090
- });
1091
- }
1092
- return ctx.embed({
1093
- ...previewVault.toPresentation3(annotation),
1094
- target: ctx.getTarget()
1095
- });
1096
- }
1097
- }
1098
- }
1099
- throw new Error("Not yet supported.");
1100
- }
1101
- var IIIFBrowserCreatorForm = lazy(() => import("./iiif-browser-form.lazy-PL3WMLU5.js"));
1102
-
1103
- // src/ContentResource/IIIFBrowserCreator/index.tsx
1104
- import { IIIFBrowserIcon } from "@manifest-editor/components";
1105
- import { jsx as jsx23 } from "react/jsx-runtime";
1106
- var iiifBrowserCreator = {
1107
- id: "@manifest-editor/iiif-browser-creator",
1108
- create: createFromIIIFBrowserOutput,
1109
- label: "IIIF Browser",
1110
- summary: "Browse IIIF Resources",
1111
- icon: /* @__PURE__ */ jsx23(IIIFBrowserIcon, {}),
1112
- render(ctx) {
1113
- return /* @__PURE__ */ jsx23(IIIFBrowserCreatorForm, { ...ctx });
1114
- },
1115
- resourceType: "ContentResource",
1116
- resourceFields: ["id", "language", "type", "format", "value"],
1117
- additionalTypes: ["Annotation", "Canvas"],
1118
- supports: {
1119
- parentTypes: ["Annotation", "Manifest", "AnnotationPage"],
1120
- parentFieldMap: {
1121
- Annotation: ["body"],
1122
- Manifest: ["items"],
1123
- AnnotationPage: ["items"]
1124
- }
1125
- },
1126
- sideEffects: [resizeToFitService, resizeResourceToEmptyCanvas, repositionMultipleImages],
1127
- staticFields: {}
1128
- };
1129
-
1130
- // src/Canvas/InternalCanvas/index.tsx
1131
- import { useManifest, useVault } from "react-iiif-vault";
1132
- import { IIIFExplorer } from "@manifest-editor/iiif-browser";
1133
- import { jsx as jsx24 } from "react/jsx-runtime";
1134
- var internalCanvas = {
1135
- id: "@manifest-editor/internal-canvas",
1136
- create: createInternalCanvas,
1137
- label: "Internal canvas link",
1138
- summary: "Canvas from the same manifest",
1139
- icon: /* @__PURE__ */ jsx24(ThumbnailStripIcon, {}),
1140
- resourceType: "Canvas",
1141
- resourceFields: ["id", "type", "label"],
1142
- supports: {
1143
- parentTypes: ["Manifest"],
1144
- parentFields: ["start"]
1145
- },
1146
- render: (ctx) => /* @__PURE__ */ jsx24(InternalCanvas, { ...ctx })
1147
- };
1148
- function InternalCanvas(props) {
1149
- const manifest = useManifest();
1150
- const vault = useVault();
1151
- invariant(manifest, "Manifest is not loaded");
1152
- return /* @__PURE__ */ jsx24(
1153
- IIIFExplorer,
1208
+ // ../ui/ui/VideoPlayer/VideoPlayer.tsx
1209
+ import { CanvasPanel as CanvasPanel2, MediaPlayerProvider, useSimpleMediaPlayer } from "react-iiif-vault";
1210
+ import { jsx as jsx17, jsxs as jsxs9 } from "react/jsx-runtime";
1211
+ function VideoPlayer({
1212
+ media,
1213
+ onError,
1214
+ duration,
1215
+ onDuration,
1216
+ onDimensions
1217
+ }) {
1218
+ const [{ element, currentTime, progress }, state, actions] = useSimpleMediaPlayer({ duration });
1219
+ return /* @__PURE__ */ jsxs9(
1220
+ MediaPlayerProvider,
1154
1221
  {
1155
- vault,
1156
- entry: manifest,
1157
- window: false,
1158
- hideHeader: true,
1159
- hideBack: true,
1160
- output: { type: "url" },
1161
- outputTargets: [
1162
- {
1163
- type: "callback",
1164
- label: "Select canvas",
1165
- cb: (url) => {
1166
- props.runCreate({ id: url });
1222
+ element,
1223
+ state,
1224
+ actions,
1225
+ currentTime,
1226
+ progress,
1227
+ children: [
1228
+ /* @__PURE__ */ jsx17(
1229
+ "div",
1230
+ {
1231
+ style: {
1232
+ maxHeight: 400,
1233
+ aspectRatio: `${media.width}/${media.height}`,
1234
+ position: "relative"
1235
+ },
1236
+ children: /* @__PURE__ */ jsx17(CanvasPanel2.VideoHTML, { element, media, playPause: actions.playPause })
1167
1237
  }
1168
- }
1169
- ],
1170
- outputTypes: ["Canvas"]
1238
+ ),
1239
+ /* @__PURE__ */ jsx17(MediaControls, { onError, onDuration, onDimensions })
1240
+ ]
1171
1241
  }
1172
1242
  );
1173
1243
  }
1174
- async function createInternalCanvas(data, ctx) {
1175
- return ctx.embed({ id: data.id, type: "Canvas" });
1176
- }
1177
1244
 
1178
- // src/Annotation/ImageUrlAnnotation/create-image-url-annotation.tsx
1179
- import { jsx as jsx25 } from "react/jsx-runtime";
1180
- async function createImageUrlAnnotation(data, ctx) {
1245
+ // src/Annotation/VideoAnnotation/create-video-annotation.tsx
1246
+ import { jsx as jsx18, jsxs as jsxs10 } from "react/jsx-runtime";
1247
+ async function createVideoAnnotation(data, ctx) {
1181
1248
  const annotation = {
1182
1249
  id: ctx.generateId("annotation"),
1183
1250
  type: "Annotation"
1184
1251
  };
1185
1252
  const targetType = ctx.options.targetType;
1186
- const resource = await ctx.create("@manifest-editor/image-url-creator", data, {
1187
- parent: { resource: annotation, property: "body" }
1253
+ const body = await ctx.embed({
1254
+ id: data.url,
1255
+ type: "Video",
1256
+ format: "video/mp4",
1257
+ width: data.width,
1258
+ height: data.height,
1259
+ duration: data.duration || 0
1188
1260
  });
1189
1261
  if (targetType === "Annotation") {
1190
- return {
1262
+ return ctx.embed({
1191
1263
  ...annotation,
1192
- body: [resource],
1193
- motivation: data.motivation || "painting",
1264
+ label: getValue2(data.label) && data.label,
1265
+ motivation: data.motivation || ctx.options.initialData?.motivation || "painting",
1266
+ body,
1194
1267
  target: ctx.getTarget()
1195
- };
1268
+ });
1196
1269
  }
1197
1270
  if (targetType === "Canvas") {
1198
1271
  const canvasId = ctx.generateId("canvas");
@@ -1200,7 +1273,7 @@ async function createImageUrlAnnotation(data, ctx) {
1200
1273
  const annotationResource = ctx.embed({
1201
1274
  ...annotation,
1202
1275
  motivation: "painting",
1203
- body: [resource],
1276
+ body,
1204
1277
  target: { type: "SpecificResource", source: { id: canvasId, type: "Canvas" } }
1205
1278
  });
1206
1279
  const page = ctx.embed({
@@ -1211,657 +1284,750 @@ async function createImageUrlAnnotation(data, ctx) {
1211
1284
  return ctx.embed({
1212
1285
  id: canvasId,
1213
1286
  type: "Canvas",
1214
- label: data.label || { en: ["Untitled canvas"] },
1215
- height: resource.resource.height || 1e3,
1216
- width: resource.resource.width || 1e3,
1287
+ label: data.label || { en: ["Untitled HTML canvas"] },
1288
+ duration: data.duration || 0,
1289
+ width: data.width,
1290
+ height: data.height,
1217
1291
  items: [page]
1218
1292
  });
1219
1293
  }
1220
1294
  }
1221
- function CreateImageUrlAnnotationForm(props) {
1222
- return /* @__PURE__ */ jsx25(CreateImageUrlForm, { ...props });
1295
+ function CreateVideoAnnotationForm(props) {
1296
+ const [url, setUrl] = useState5("");
1297
+ const [duration, setDuration] = useState5(0);
1298
+ const [width, setWidth] = useState5(0);
1299
+ const [height, setHeight] = useState5(0);
1300
+ const [error, setError] = useState5("");
1301
+ const [label, setLabel] = useState5({ en: [""] });
1302
+ const onSubmit = () => {
1303
+ props.runCreate({
1304
+ url,
1305
+ duration,
1306
+ label,
1307
+ width,
1308
+ height
1309
+ });
1310
+ };
1311
+ useEffect3(() => {
1312
+ setError("");
1313
+ setDuration(0);
1314
+ if (url) {
1315
+ const labelFromUrl = url.split("/").pop()?.split(".").shift();
1316
+ setLabel({ en: [labelFromUrl || ""] });
1317
+ }
1318
+ }, [url]);
1319
+ return /* @__PURE__ */ jsxs10(PaddedSidebarContainer, { children: [
1320
+ error ? /* @__PURE__ */ jsx18(ErrorMessage2, { children: error }) : null,
1321
+ /* @__PURE__ */ jsxs10(InputContainer6, { children: [
1322
+ /* @__PURE__ */ jsx18(InputLabel6, { htmlFor: "video-url", children: "URL" }),
1323
+ /* @__PURE__ */ jsx18(Input5, { type: "text", id: "video-url", value: url, onChange: (e) => setUrl(e.target.value) })
1324
+ ] }),
1325
+ url && !error ? /* @__PURE__ */ jsx18("div", { children: /* @__PURE__ */ jsx18(
1326
+ LanguageFieldEditor4,
1327
+ {
1328
+ containerId: "label",
1329
+ focusId: "label_",
1330
+ label: "Label",
1331
+ fields: label,
1332
+ onSave: (e) => setLabel(e.toInternationalString())
1333
+ },
1334
+ url + "__" + duration
1335
+ ) }) : null,
1336
+ url && !error ? /* @__PURE__ */ jsx18(
1337
+ VideoPlayer,
1338
+ {
1339
+ duration,
1340
+ onDuration: (d) => setDuration(d),
1341
+ onError: (err) => setError(err),
1342
+ media: {
1343
+ url,
1344
+ duration,
1345
+ height,
1346
+ width,
1347
+ type: "Video"
1348
+ },
1349
+ onDimensions: (w, h) => {
1350
+ setWidth(w || 0);
1351
+ setHeight(h || 0);
1352
+ }
1353
+ }
1354
+ ) : null,
1355
+ !error && (duration || width || height) ? /* @__PURE__ */ jsx18(InputContainer6, { $wide: true, children: /* @__PURE__ */ jsx18(
1356
+ DimensionsTriplet,
1357
+ {
1358
+ widthId: "width",
1359
+ width: width || 0,
1360
+ changeWidth: (v) => setWidth(v),
1361
+ heightId: "height",
1362
+ height: height || 0,
1363
+ changeHeight: (v) => setHeight(v),
1364
+ durationId: "duration",
1365
+ duration: duration || 0,
1366
+ changeDuration: (v) => setDuration(v)
1367
+ }
1368
+ ) }) : null,
1369
+ url && !error && /* @__PURE__ */ jsx18(Button, { onClick: onSubmit, children: "Add video" })
1370
+ ] });
1223
1371
  }
1224
1372
 
1225
- // src/Annotation/ImageUrlAnnotation/index.tsx
1226
- import { AddImageIcon as AddImageIcon2 } from "@manifest-editor/components";
1227
- import { jsx as jsx26 } from "react/jsx-runtime";
1228
- var imageUrlAnnotation = {
1229
- id: "@manifest-editor/image-url-annotation",
1230
- create: createImageUrlAnnotation,
1231
- label: "Image",
1232
- dependencies: ["@manifest-editor/image-url-creator"],
1233
- summary: "Image from URL",
1234
- icon: /* @__PURE__ */ jsx26(AddImageIcon2, {}),
1373
+ // src/Annotation/VideoAnnotation/index.tsx
1374
+ import { VideoIcon } from "@manifest-editor/components";
1375
+ import { jsx as jsx19 } from "react/jsx-runtime";
1376
+ var videoAnnotation = {
1377
+ id: "@manifest-editor/video-annotation",
1378
+ create: createVideoAnnotation,
1379
+ label: "Video",
1380
+ summary: "Video annotation",
1381
+ icon: /* @__PURE__ */ jsx19(VideoIcon, {}),
1235
1382
  render(ctx) {
1236
- return /* @__PURE__ */ jsx26(CreateImageUrlAnnotationForm, { ...ctx });
1383
+ return /* @__PURE__ */ jsx19(CreateVideoAnnotationForm, { ...ctx });
1237
1384
  },
1238
1385
  resourceType: "Annotation",
1239
1386
  resourceFields: ["id", "type", "motivation", "body", "target"],
1240
1387
  additionalTypes: ["Canvas"],
1241
1388
  supports: {
1389
+ initialData: true,
1242
1390
  parentTypes: ["AnnotationPage", "Manifest"],
1243
1391
  parentFields: ["items"]
1244
1392
  },
1245
- sideEffects: [resizeToFitService, resizeResourceToEmptyCanvas, repositionMultipleImages],
1246
1393
  staticFields: {
1247
1394
  type: "Annotation"
1248
1395
  }
1249
1396
  };
1250
1397
 
1251
- // src/Annotation/AudioAnnotation/create-audio-annotation.tsx
1252
- import { useEffect as useEffect2, useState as useState6 } from "react";
1253
-
1254
- // ../ui/atoms/callouts/ErrorMessage.tsx
1255
- import styled3, { css } from "styled-components";
1256
- var ErrorMessage = styled3.div`
1257
- border: 2px solid ${(props) => props.theme.color.danger || "red"};
1258
- width: 100%;
1259
- border-radius: 5px;
1260
- padding: ${(props) => props.theme.padding.small || "0.5rem"}
1261
- ${(props) => props.theme.padding.medium || "1rem"};
1262
- line-height: 1.9em;
1263
- display: flex;
1264
- align-items: center;
1265
- a {
1266
- color: ${(props) => props.theme.color.white || "white"};
1398
+ // ../ui/icons/ThumbnailStripIcon.tsx
1399
+ import { jsx as jsx20, jsxs as jsxs11 } from "react/jsx-runtime";
1400
+ var ThumbnailStripIcon = () => /* @__PURE__ */ jsx20(
1401
+ "svg",
1402
+ {
1403
+ xmlns: "http://www.w3.org/2000/svg",
1404
+ height: "24px",
1405
+ viewBox: "0 0 24 24",
1406
+ width: "24px",
1407
+ fill: "#000000",
1408
+ transform: "scale(-1, 1)",
1409
+ children: /* @__PURE__ */ jsxs11("g", { children: [
1410
+ /* @__PURE__ */ jsx20("rect", { fill: "none", height: "24", width: "24" }),
1411
+ /* @__PURE__ */ jsx20("path", { d: "M16,20H2V4h14V20z M18,8h4V4h-4V8z M18,20h4v-4h-4V20z M18,14h4v-4h-4V14z" })
1412
+ ] })
1267
1413
  }
1268
- ${(props) => props.$small && css`
1269
- font-size: 0.75em;
1270
- white-space: nowrap;
1271
- padding: ${props.theme.padding.xs || "0.25rem"} ${props.theme.padding.small || "0.5rem"};
1272
- line-height: 1.6em;
1273
- width: fit-content;
1274
- `}
1275
- `;
1276
-
1277
- // ../ui/ui/MediaControls/MediaControls.tsx
1278
- import { formatTime, useMediaActions, useMediaElements, useMediaState } from "react-iiif-vault";
1279
- import { useLayoutEffect, useState as useState5 } from "react";
1414
+ );
1280
1415
 
1281
- // ../ui/ui/MediaControls/MediaControls.icons.tsx
1282
- import { jsx as jsx27, jsxs as jsxs11 } from "react/jsx-runtime";
1283
- var PlayIcon = (props) => /* @__PURE__ */ jsxs11("svg", { ...props, viewBox: "4 4 16 16", xmlns: "http://www.w3.org/2000/svg", height: "1em", width: "1em", children: [
1284
- props.title ? /* @__PURE__ */ jsx27("title", { children: props.title }) : null,
1285
- /* @__PURE__ */ jsx27("path", { d: "M0 0h24v24H0z", fill: "none" }),
1286
- /* @__PURE__ */ jsx27("path", { d: "M8 5v14l11-7z", fill: "currentColor" })
1287
- ] });
1288
- var PauseIcon = (props) => /* @__PURE__ */ jsxs11("svg", { ...props, viewBox: "4 4 16 16", xmlns: "http://www.w3.org/2000/svg", height: "1em", width: "1em", children: [
1289
- props.title ? /* @__PURE__ */ jsx27("title", { children: props.title }) : null,
1290
- /* @__PURE__ */ jsx27("path", { d: "M0 0h24v24H0z", fill: "none" }),
1291
- /* @__PURE__ */ jsx27("path", { fill: "currentColor", d: "M6 19h4V5H6v14zm8-14v14h4V5h-4z" })
1292
- ] });
1293
- var VolumeUpIcon = ({ title, ...props }) => /* @__PURE__ */ jsxs11("svg", { ...props, xmlns: "http://www.w3.org/2000/svg", height: "1em", width: "1em", viewBox: "2 2 20 20", children: [
1294
- title ? /* @__PURE__ */ jsx27("title", { children: title }) : null,
1295
- /* @__PURE__ */ jsx27("path", { d: "M0 0h24v24H0z", fill: "none" }),
1296
- /* @__PURE__ */ jsx27(
1297
- "path",
1298
- {
1299
- fill: "currentColor",
1300
- d: "M3 9v6h4l5 5V4L7 9H3zm13.5 3A4.5 4.5 0 0 0 14 7.97v8.05c1.48-.73 2.5-2.25 2.5-4.02zM14 3.23v2.06c2.89.86 5 3.54 5 6.71s-2.11 5.85-5 6.71v2.06c4.01-.91 7-4.49 7-8.77s-2.99-7.86-7-8.77z"
1301
- }
1302
- )
1303
- ] });
1304
- var VolumeDownIcon = ({ title, ...props }) => /* @__PURE__ */ jsxs11("svg", { ...props, xmlns: "http://www.w3.org/2000/svg", height: "1em", width: "1em", viewBox: "2 2 20 20", children: [
1305
- title ? /* @__PURE__ */ jsx27("title", { children: title }) : null,
1306
- /* @__PURE__ */ jsx27("path", { d: "M0 0h24v24H0z", fill: "none" }),
1307
- /* @__PURE__ */ jsx27(
1308
- "path",
1309
- {
1310
- fill: "currentColor",
1311
- d: "M18.5 12c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02zM5 9v6h4l5 5V4L9 9H5z"
1312
- }
1313
- )
1314
- ] });
1315
- var VolumeOffIcon = ({ title, ...props }) => /* @__PURE__ */ jsxs11("svg", { ...props, xmlns: "http://www.w3.org/2000/svg", height: "1em", width: "1em", viewBox: "2 2 20 20", children: [
1316
- title ? /* @__PURE__ */ jsx27("title", { children: title }) : null,
1317
- /* @__PURE__ */ jsx27("path", { d: "M0 0h24v24H0z", fill: "none" }),
1318
- /* @__PURE__ */ jsx27(
1319
- "path",
1320
- {
1321
- fill: "currentColor",
1322
- d: "M16.5 12A4.5 4.5 0 0 0 14 7.97v2.21l2.45 2.45c.03-.2.05-.41.05-.63zm2.5 0c0 .94-.2 1.82-.54 2.64l1.51 1.51A8.796 8.796 0 0 0 21 12c0-4.28-2.99-7.86-7-8.77v2.06c2.89.86 5 3.54 5 6.71zM4.27 3 3 4.27 7.73 9H3v6h4l5 5v-6.73l4.25 4.25c-.67.52-1.42.93-2.25 1.18v2.06a8.99 8.99 0 0 0 3.69-1.81L19.73 21 21 19.73l-9-9L4.27 3zM12 4 9.91 6.09 12 8.18V4z"
1416
+ // src/AnnotationPage/EmptyAnnotationPage/index.tsx
1417
+ import { jsx as jsx21 } from "react/jsx-runtime";
1418
+ var emptyAnnotationPage = {
1419
+ id: "@manifest-editor/empty-annotation-page",
1420
+ create: createEmptyAnnotationPage,
1421
+ label: "Empty annotation page",
1422
+ summary: "AnnotationPage with nothing",
1423
+ icon: /* @__PURE__ */ jsx21(ThumbnailStripIcon, {}),
1424
+ resourceType: "AnnotationPage",
1425
+ resourceFields: ["id", "type", "items"],
1426
+ supports: {
1427
+ parentTypes: ["Manifest", "Canvas", "Range"],
1428
+ parentFieldMap: {
1429
+ Manifest: ["annotations"],
1430
+ Canvas: ["items", "annotations"],
1431
+ Range: ["annotations"]
1323
1432
  }
1324
- )
1325
- ] });
1326
-
1327
- // ../ui/ui/MediaControls/MediaControls.styles.ts
1328
- import styled4, { css as css2 } from "styled-components";
1329
- var AudioPlayerContainer = styled4.div`
1330
- background: #ededed; // Not in variable.
1331
- display: flex;
1332
- padding: 0.5em;
1333
- align-items: center;
1334
- `;
1335
- var BaseButton = styled4.button`
1336
- cursor: pointer;
1337
- padding: 0.75em 1.5em;
1338
- font-size: 0.938em;
1339
- background: none;
1340
- color: inherit;
1341
- border: none;
1342
- text-decoration: none;
1343
- display: inline-block;
1344
- vertical-align: top;
1345
- white-space: nowrap;
1346
- `;
1347
- var PlayButton = styled4(BaseButton)`
1348
- width: 2em;
1349
- height: 2em;
1350
- overflow: hidden;
1351
- font-size: 1.1em;
1352
- background: #fff;
1353
- border-radius: 3px;
1354
- padding: 0;
1355
- margin: 0;
1356
- color: #000; // Not in colours
1433
+ }
1434
+ };
1435
+ async function createEmptyAnnotationPage(data, ctx) {
1436
+ return ctx.embed({
1437
+ id: ctx.generateId("annotations"),
1438
+ label: data.label,
1439
+ type: "AnnotationPage",
1440
+ items: []
1441
+ });
1442
+ }
1357
1443
 
1358
- &:disabled {
1359
- color: #999;
1444
+ // src/Canvas/EmptyCanvas/index.tsx
1445
+ import { EmptyCanvasIcon as EmptyCanvasIcon2 } from "@manifest-editor/components";
1446
+ import { jsx as jsx22 } from "react/jsx-runtime";
1447
+ var emptyCanvas = {
1448
+ id: "@manifest-editor/empty-canvas",
1449
+ create: createEmptyCanvas,
1450
+ label: "Empty canvas",
1451
+ summary: "No content",
1452
+ icon: /* @__PURE__ */ jsx22(EmptyCanvasIcon2, {}),
1453
+ resourceType: "Canvas",
1454
+ resourceFields: ["id", "type", "label", "height", "width", "items"],
1455
+ supports: {
1456
+ parentTypes: ["Manifest"],
1457
+ parentFields: ["items"]
1360
1458
  }
1361
- `;
1362
- var TimeDisplay = styled4.div`
1363
- margin: 0 1em;
1364
- font-size: 0.85em;
1365
- color: #000;
1366
- `;
1367
- var ProgressContainer = styled4.div`
1368
- flex: 1 1 0px;
1369
- height: 6px;
1370
- display: flex;
1371
- position: relative;
1372
- padding: 15px 0;
1373
- `;
1374
- var ProgressCurrent = styled4.div`
1375
- background: #d85680;
1376
- height: 6px;
1377
- pointer-events: none;
1378
- position: absolute;
1379
- top: 13px;
1380
- left: 0;
1381
- border-radius: 3px;
1382
- transition: width 200ms;
1383
- `;
1384
- var ProgressBackground = styled4.div`
1385
- position: absolute;
1386
- top: 13px;
1387
- left: 0;
1388
- height: 6px;
1389
- border-radius: 3px;
1390
- width: 100%;
1391
- pointer-events: none;
1392
- background: #fff;
1393
- `;
1394
- var SoundButton = styled4(BaseButton)`
1395
- width: 2em;
1396
- height: 2em;
1397
- overflow: hidden;
1398
- font-size: 1.1em;
1399
- background: transparent;
1400
- border-radius: 3px;
1401
- padding: 0;
1402
- margin: 0;
1403
- `;
1404
- var SoundContainer = styled4.div`
1405
- display: flex;
1406
- align-items: center;
1407
- `;
1408
- var VolumeContainer = styled4.div`
1409
- width: 0;
1410
- overflow: hidden;
1411
- transition: width 500ms;
1412
- color: #4a473f; // Not in colours
1459
+ };
1460
+ async function createEmptyCanvas(data, ctx) {
1461
+ const canvasId = ctx.generateId("canvas");
1462
+ const page = ctx.embed({
1463
+ id: ctx.generateId("annotation-page", { id: canvasId, type: "Canvas" }),
1464
+ type: "AnnotationPage",
1465
+ items: []
1466
+ });
1467
+ return {
1468
+ id: canvasId,
1469
+ type: "Canvas",
1470
+ label: data.label || { en: [""] },
1471
+ height: data.height || 1e3,
1472
+ width: data.width || 1e3,
1473
+ items: [page]
1474
+ };
1475
+ }
1413
1476
 
1414
- ${(props) => props.$isOpen && css2`
1415
- width: 150px;
1416
- `}
1417
- `;
1418
- var VolumeSlider = styled4.input.attrs({ type: "range" })`
1419
- width: 120px;
1420
- margin: 0 15px;
1421
- `;
1477
+ // src/Canvas/InternalCanvas/index.tsx
1478
+ import { useManifest, useVault } from "react-iiif-vault";
1422
1479
 
1423
- // ../ui/madoc/components/icons/Spinner.tsx
1424
- import { jsx as jsx28, jsxs as jsxs12 } from "react/jsx-runtime";
1425
- function Spinner(props) {
1426
- return /* @__PURE__ */ jsx28("svg", { width: "1em", height: "1em", viewBox: "0 0 42 42", xmlns: "http://www.w3.org/2000/svg", stroke: "#fff", ...props, children: /* @__PURE__ */ jsxs12("g", { transform: "translate(2 2)", strokeWidth: 5, fill: "none", fillRule: "evenodd", children: [
1427
- /* @__PURE__ */ jsx28("circle", { strokeOpacity: 0.5, cx: 18, cy: 18, r: 18 }),
1428
- /* @__PURE__ */ jsx28("path", { d: "M36 18c0-9.94-8.06-18-18-18", children: /* @__PURE__ */ jsx28(
1429
- "animateTransform",
1430
- {
1431
- attributeName: "transform",
1432
- type: "rotate",
1433
- from: "0 18 18",
1434
- to: "360 18 18",
1435
- dur: "1s",
1436
- repeatCount: "indefinite"
1437
- }
1438
- ) })
1439
- ] }) });
1480
+ // ../../node_modules/.pnpm/tiny-invariant@1.3.3/node_modules/tiny-invariant/dist/esm/tiny-invariant.js
1481
+ var isProduction = false;
1482
+ var prefix = "Invariant failed";
1483
+ function invariant(condition, message) {
1484
+ if (condition) {
1485
+ return;
1486
+ }
1487
+ if (isProduction) {
1488
+ throw new Error(prefix);
1489
+ }
1490
+ var provided = typeof message === "function" ? message() : message;
1491
+ var value = provided ? "".concat(prefix, ": ").concat(provided) : prefix;
1492
+ throw new Error(value);
1440
1493
  }
1441
1494
 
1442
- // ../ui/atoms/VisuallHidden/VisuallHidden.tsx
1443
- import styled5, { css as css3 } from "styled-components";
1444
- var visuallyHidden = css3`
1445
- clip: rect(1px, 1px, 1px, 1px);
1446
- clip-path: inset(50%);
1447
- height: 1px;
1448
- width: 1px;
1449
- margin: -1px;
1450
- overflow: hidden;
1451
- padding: 0;
1452
- position: absolute;
1453
- `;
1454
- var VisuallyHiddenLabel = styled5.label`
1455
- ${visuallyHidden}
1456
- `;
1457
-
1458
- // ../ui/ui/MediaControls/MediaControls.tsx
1459
- import { jsx as jsx29, jsxs as jsxs13 } from "react/jsx-runtime";
1460
- function MediaControls(props) {
1461
- const [isVolumeOpen, setIsVolumeOpen] = useState5(false);
1462
- const { progress, currentTime, element } = useMediaElements();
1463
- const { duration, isMuted, volume, isPlaying, playRequested } = useMediaState();
1464
- const { setVolume, toggleMute, play, pause, setDurationPercent } = useMediaActions();
1465
- const Volume = isMuted || volume === 0 ? VolumeOffIcon : volume > 50 ? VolumeUpIcon : VolumeDownIcon;
1466
- useLayoutEffect(() => {
1467
- if (element.current) {
1468
- const $el = element.current;
1469
- if ($el.error && props.onError) {
1470
- props.onError($el.error?.message || "Unknown error");
1471
- return;
1472
- }
1473
- const onError = () => {
1474
- if (props.onError) {
1475
- props.onError($el.error?.message || "Unknown error");
1476
- }
1477
- };
1478
- const loadedMetadata = () => {
1479
- if ($el.duration && props.onDuration) {
1480
- props.onDuration($el.duration);
1481
- }
1482
- if (props.onDimensions && $el instanceof HTMLVideoElement) {
1483
- props.onDimensions($el.videoWidth, $el.videoHeight);
1495
+ // src/Canvas/InternalCanvas/index.tsx
1496
+ import { IIIFExplorer } from "@manifest-editor/iiif-browser";
1497
+ import { jsx as jsx23 } from "react/jsx-runtime";
1498
+ var internalCanvas = {
1499
+ id: "@manifest-editor/internal-canvas",
1500
+ create: createInternalCanvas,
1501
+ label: "Internal canvas link",
1502
+ summary: "Canvas from the same manifest",
1503
+ icon: /* @__PURE__ */ jsx23(ThumbnailStripIcon, {}),
1504
+ resourceType: "Canvas",
1505
+ resourceFields: ["id", "type", "label"],
1506
+ supports: {
1507
+ parentTypes: ["Manifest"],
1508
+ parentFields: ["start"]
1509
+ },
1510
+ render: (ctx) => /* @__PURE__ */ jsx23(InternalCanvas, { ...ctx })
1511
+ };
1512
+ function InternalCanvas(props) {
1513
+ const manifest = useManifest();
1514
+ const vault = useVault();
1515
+ invariant(manifest, "Manifest is not loaded");
1516
+ return /* @__PURE__ */ jsx23(
1517
+ IIIFExplorer,
1518
+ {
1519
+ vault,
1520
+ entry: manifest,
1521
+ window: false,
1522
+ hideHeader: true,
1523
+ hideBack: true,
1524
+ output: { type: "url" },
1525
+ outputTargets: [
1526
+ {
1527
+ type: "callback",
1528
+ label: "Select canvas",
1529
+ cb: (url) => {
1530
+ props.runCreate({ id: url });
1531
+ }
1484
1532
  }
1485
- };
1486
- $el.addEventListener("loadedmetadata", loadedMetadata);
1487
- $el.addEventListener("error", onError);
1488
- return () => {
1489
- $el.removeEventListener("loadedmetadata", loadedMetadata);
1490
- $el.removeEventListener("error", onError);
1491
- };
1533
+ ],
1534
+ outputTypes: ["Canvas"]
1492
1535
  }
1493
- }, []);
1494
- return /* @__PURE__ */ jsxs13(AudioPlayerContainer, { children: [
1495
- /* @__PURE__ */ jsx29(
1496
- PlayButton,
1497
- {
1498
- disabled: playRequested,
1499
- onClick: () => {
1500
- if (isPlaying) {
1501
- pause();
1502
- } else {
1503
- play();
1504
- }
1505
- },
1506
- children: playRequested ? /* @__PURE__ */ jsx29(Spinner, { stroke: "#999" }) : isPlaying ? /* @__PURE__ */ jsx29(PauseIcon, { title: "Pause" }) : /* @__PURE__ */ jsx29(PlayIcon, { title: "Play" })
1507
- }
1508
- ),
1509
- /* @__PURE__ */ jsx29(TimeDisplay, { ref: currentTime, children: "0:00" }),
1510
- /* @__PURE__ */ jsxs13(
1511
- ProgressContainer,
1512
- {
1513
- onClick: (e) => {
1514
- const { left, width } = e.currentTarget.getBoundingClientRect();
1515
- const percent = (e.pageX - left) / width;
1516
- setDurationPercent(percent);
1517
- },
1518
- children: [
1519
- /* @__PURE__ */ jsx29(ProgressBackground, {}),
1520
- duration ? /* @__PURE__ */ jsx29(ProgressCurrent, { ref: progress }) : null
1521
- ]
1522
- }
1523
- ),
1524
- /* @__PURE__ */ jsx29(TimeDisplay, { children: formatTime(duration) }),
1525
- /* @__PURE__ */ jsxs13(SoundContainer, { onMouseEnter: () => setIsVolumeOpen(true), onMouseLeave: () => setIsVolumeOpen(false), children: [
1526
- /* @__PURE__ */ jsx29(VolumeContainer, { $isOpen: isVolumeOpen, children: /* @__PURE__ */ jsxs13("span", { children: [
1527
- /* @__PURE__ */ jsx29(VisuallyHiddenLabel, { htmlFor: "audio-slider", children: "Change volume" }),
1528
- /* @__PURE__ */ jsx29(
1529
- VolumeSlider,
1530
- {
1531
- id: "audio-slider",
1532
- role: "slider",
1533
- disabled: isMuted,
1534
- min: "0",
1535
- max: "100",
1536
- value: volume,
1537
- onChange: (e) => setVolume(Number(e.currentTarget.value))
1538
- }
1539
- )
1540
- ] }) }),
1541
- /* @__PURE__ */ jsx29(SoundButton, { onClick: () => toggleMute(), children: /* @__PURE__ */ jsx29(Volume, { title: isMuted ? "Mute" : "Unmute" }) })
1542
- ] })
1543
- ] });
1536
+ );
1537
+ }
1538
+ async function createInternalCanvas(data, ctx) {
1539
+ return ctx.embed({ id: data.id, type: "Canvas" });
1544
1540
  }
1545
1541
 
1546
- // src/Annotation/AudioAnnotation/create-audio-annotation.tsx
1547
- import { CanvasPanel } from "react-iiif-vault";
1548
- import { getValue } from "@iiif/helpers";
1549
- import { Input as Input6, InputContainer as InputContainer8, InputLabel as InputLabel8, LanguageFieldEditor as LanguageFieldEditor4, FormFieldWrapper } from "@manifest-editor/editors";
1550
- import { jsx as jsx30, jsxs as jsxs14 } from "react/jsx-runtime";
1551
- async function createAudioAnnotation(data, ctx) {
1552
- const annotation = {
1553
- id: ctx.generateId("annotation"),
1554
- type: "Annotation"
1555
- };
1556
- const targetType = ctx.options.targetType;
1557
- const body = await ctx.embed({
1558
- id: data.url,
1559
- type: "Sound",
1560
- format: "audio/mp4",
1561
- duration: data.duration || 0
1542
+ // src/ContentResource/HTMLBodyCreator/create-html-body.tsx
1543
+ import { InputContainer as InputContainer7, InputLabel as InputLabel7, RichTextLanguageField } from "@manifest-editor/editors";
1544
+ import { useConfig } from "@manifest-editor/shell";
1545
+ import { useState as useState6 } from "react";
1546
+ import { jsx as jsx24, jsxs as jsxs12 } from "react/jsx-runtime";
1547
+ async function createHtmlBody(data, ctx) {
1548
+ return ctx.embed({
1549
+ id: ctx.generateId(`html/${data.language}`),
1550
+ language: data.language,
1551
+ type: "TextualBody",
1552
+ format: "text/html",
1553
+ value: data.body || ""
1562
1554
  });
1563
- if (targetType === "Annotation") {
1564
- return ctx.embed({
1565
- ...annotation,
1566
- label: getValue(data.label) && data.label,
1567
- motivation: data.motivation || ctx.options.initialData?.motivation || "painting",
1568
- body,
1569
- target: ctx.getTarget()
1570
- });
1571
- }
1572
- if (targetType === "Canvas") {
1573
- const canvasId = ctx.generateId("canvas");
1574
- const pageId = ctx.generateId("annotation-page", { id: canvasId, type: "Canvas" });
1575
- const annotationResource = ctx.embed({
1576
- ...annotation,
1577
- motivation: "painting",
1578
- body,
1579
- target: { type: "SpecificResource", source: { id: canvasId, type: "Canvas" } }
1580
- });
1581
- const page = ctx.embed({
1582
- id: pageId,
1583
- type: "AnnotationPage",
1584
- items: [annotationResource]
1585
- });
1586
- return ctx.embed({
1587
- id: canvasId,
1588
- type: "Canvas",
1589
- label: data.label || { en: ["Untitled HTML canvas"] },
1590
- duration: data.duration || 0,
1591
- items: [page]
1592
- });
1593
- }
1594
1555
  }
1595
- function CreateAudioAnnotationForm(props) {
1596
- const [url, setUrl] = useState6("");
1597
- const [duration, setDuration] = useState6(0);
1598
- const [error, setError] = useState6("");
1599
- const [label, setLabel] = useState6({ en: [""] });
1556
+ function CreateHTMLBodyForm(props) {
1557
+ const { i18n } = useConfig();
1558
+ const [body, setBodyValue] = useState6("<p></p>");
1559
+ const [language, setLang] = useState6(i18n.defaultLanguage);
1600
1560
  const onSubmit = () => {
1601
1561
  props.runCreate({
1602
- url,
1603
- duration,
1604
- label
1562
+ language,
1563
+ body
1605
1564
  });
1606
1565
  };
1607
- useEffect2(() => {
1608
- setError("");
1609
- setDuration(0);
1610
- if (url) {
1611
- const labelFromUrl = url.split("/").pop()?.split(".").shift();
1612
- setLabel({ en: [labelFromUrl || ""] });
1613
- }
1614
- }, [url]);
1615
- return /* @__PURE__ */ jsxs14(PaddedSidebarContainer, { children: [
1616
- error ? /* @__PURE__ */ jsx30(ErrorMessage, { children: error }) : null,
1617
- /* @__PURE__ */ jsxs14(InputContainer8, { children: [
1618
- /* @__PURE__ */ jsx30(InputLabel8, { htmlFor: "audio-url", children: "URL" }),
1619
- /* @__PURE__ */ jsx30(Input6, { type: "text", id: "audio-url", value: url, onChange: (e) => setUrl(e.target.value) })
1566
+ return /* @__PURE__ */ jsxs12(PaddedSidebarContainer, { children: [
1567
+ /* @__PURE__ */ jsxs12(InputContainer7, { children: [
1568
+ /* @__PURE__ */ jsx24(InputLabel7, { children: "HTML Body" }),
1569
+ /* @__PURE__ */ jsx24(
1570
+ RichTextLanguageField,
1571
+ {
1572
+ value: body,
1573
+ language,
1574
+ onUpdateLanguage: (lng) => setLang(lng),
1575
+ languages: i18n.availableLanguages,
1576
+ onUpdate: (e) => setBodyValue(e)
1577
+ }
1578
+ )
1620
1579
  ] }),
1621
- url && !error ? /* @__PURE__ */ jsx30("div", { children: /* @__PURE__ */ jsx30(
1622
- LanguageFieldEditor4,
1580
+ /* @__PURE__ */ jsx24(Button, { onClick: () => onSubmit(), children: "Create" })
1581
+ ] });
1582
+ }
1583
+
1584
+ // ../ui/icons/TextFormatIcon.tsx
1585
+ import { jsx as jsx25, jsxs as jsxs13 } from "react/jsx-runtime";
1586
+ var TextFormatIcon = (props) => /* @__PURE__ */ jsxs13("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", height: "1em", width: "1em", ...props, children: [
1587
+ /* @__PURE__ */ jsx25("path", { d: "M0 0h24v24H0z", fill: "none" }),
1588
+ /* @__PURE__ */ jsx25("path", { d: "M5 17v2h14v-2H5zm4.5-4.2h5l.9 2.2h2.1L12.75 4h-1.5L6.5 15h2.1l.9-2.2zM12 5.98 13.87 11h-3.74L12 5.98z" })
1589
+ ] });
1590
+
1591
+ // src/ContentResource/HTMLBodyCreator/index.tsx
1592
+ import { jsx as jsx26 } from "react/jsx-runtime";
1593
+ var htmlBodyCreator = {
1594
+ id: "@manifest-editor/html-body-creator",
1595
+ create: createHtmlBody,
1596
+ label: "HTML Body",
1597
+ summary: "Add HTML annotation body",
1598
+ icon: /* @__PURE__ */ jsx26(TextFormatIcon, {}),
1599
+ render(ctx) {
1600
+ return /* @__PURE__ */ jsx26(CreateHTMLBodyForm, { ...ctx });
1601
+ },
1602
+ resourceType: "ContentResource",
1603
+ resourceFields: ["id", "language", "type", "format", "value"],
1604
+ supports: {
1605
+ parentTypes: ["Annotation"],
1606
+ parentFields: ["body"]
1607
+ },
1608
+ staticFields: {
1609
+ type: "TextualBody",
1610
+ format: "text/html"
1611
+ }
1612
+ };
1613
+
1614
+ // src/ContentResource/IIIFBrowserCreator/index.tsx
1615
+ import { IIIFBrowserIcon } from "@manifest-editor/components";
1616
+
1617
+ // src/ContentResource/IIIFBrowserCreator/iiif-browser-creator.tsx
1618
+ import {
1619
+ normaliseContentState,
1620
+ parseContentState
1621
+ } from "@iiif/helpers";
1622
+ import { lazy } from "react";
1623
+ async function createFromIIIFBrowserOutput(data, ctx) {
1624
+ const targetType = ctx.options.targetType;
1625
+ if (Array.isArray(data.output)) {
1626
+ throw new Error("Multiple items not yet supported");
1627
+ }
1628
+ const contentState = normaliseContentState(
1629
+ typeof data.output === "string" ? parseContentState(data.output) : data.output
1630
+ );
1631
+ if (!contentState.target || contentState.target.length === 0) {
1632
+ throw new Error("No target found in content state");
1633
+ }
1634
+ const target = contentState.target[0];
1635
+ const type = target.source.type;
1636
+ const previewVault = ctx.getPreviewVault();
1637
+ if (targetType === "Canvas" || targetType === "Annotation") {
1638
+ if (type === "Canvas") {
1639
+ const canvasId = target.source.id;
1640
+ const manifestId = target.source.partOf?.find(
1641
+ (t) => t.type === "Manifest"
1642
+ );
1643
+ invariant(manifestId, "Could not load external resource without partOf");
1644
+ const manifest = await previewVault.loadManifest(manifestId);
1645
+ invariant(manifest, "Manifest not found");
1646
+ const canvasRef = manifest.items.find((item) => item.id === canvasId);
1647
+ invariant(canvasRef, "Canvas not found");
1648
+ const canvas = previewVault.get(canvasRef);
1649
+ if (targetType === "Canvas" && target.selector?.type !== "BoxSelector") {
1650
+ return ctx.embed(previewVault.toPresentation3(canvas));
1651
+ }
1652
+ const annotationPage = previewVault.get(canvas.items[0]);
1653
+ const annotation = previewVault.get(annotationPage.items[0]);
1654
+ if (targetType === "Annotation" || targetType === "Canvas") {
1655
+ const fullAnnotation = previewVault.toPresentation3(annotation);
1656
+ if (fullAnnotation?.body?.service) {
1657
+ const width = fullAnnotation?.body.width;
1658
+ const height = fullAnnotation?.body.height;
1659
+ const service = Array.isArray(fullAnnotation?.body?.service) ? fullAnnotation?.body?.service[0] : fullAnnotation?.body?.service;
1660
+ if (!service.width || !service.height) {
1661
+ service.width = width;
1662
+ service.height = height;
1663
+ }
1664
+ }
1665
+ if (target.selector && target.selector.type === "BoxSelector") {
1666
+ const selector = {
1667
+ "@context": "http://iiif.io/api/annex/openannotation/context.json",
1668
+ type: "iiif:ImageApiSelector",
1669
+ region: [
1670
+ ~~target.selector.spatial.x,
1671
+ ~~target.selector.spatial.y,
1672
+ ~~target.selector.spatial.width,
1673
+ ~~target.selector.spatial.height
1674
+ ].join(",")
1675
+ };
1676
+ fullAnnotation.body = {
1677
+ id: ctx.generateId("SpecificResource", annotation),
1678
+ type: "SpecificResource",
1679
+ source: fullAnnotation.body,
1680
+ selector
1681
+ };
1682
+ if (targetType === "Canvas") {
1683
+ const canvasId2 = ctx.generateId("Canvas", annotation);
1684
+ fullAnnotation.target = canvasId2;
1685
+ const pageId = ctx.generateId("Page", annotation);
1686
+ const annotationPage2 = ctx.embed({
1687
+ id: pageId,
1688
+ type: "AnnotationPage",
1689
+ items: [fullAnnotation]
1690
+ });
1691
+ return ctx.embed({
1692
+ id: canvasId2,
1693
+ type: "Canvas",
1694
+ width: ~~target.selector.spatial.width,
1695
+ height: ~~target.selector.spatial.height,
1696
+ items: [annotationPage2]
1697
+ });
1698
+ }
1699
+ return ctx.embed({
1700
+ ...fullAnnotation,
1701
+ target: ctx.getTarget()
1702
+ });
1703
+ }
1704
+ return ctx.embed({
1705
+ ...previewVault.toPresentation3(annotation),
1706
+ target: ctx.getTarget()
1707
+ });
1708
+ }
1709
+ }
1710
+ }
1711
+ throw new Error("Not yet supported.");
1712
+ }
1713
+ var IIIFBrowserCreatorForm = lazy(
1714
+ () => import("./iiif-browser-form.lazy-W42LJ6X2.js")
1715
+ );
1716
+
1717
+ // src/ContentResource/IIIFBrowserCreator/index.tsx
1718
+ import { jsx as jsx27 } from "react/jsx-runtime";
1719
+ var iiifBrowserCreator = {
1720
+ id: "@manifest-editor/iiif-browser-creator",
1721
+ create: createFromIIIFBrowserOutput,
1722
+ label: "IIIF Browser",
1723
+ summary: "Browse IIIF Resources",
1724
+ icon: /* @__PURE__ */ jsx27(IIIFBrowserIcon, {}),
1725
+ render(ctx) {
1726
+ return /* @__PURE__ */ jsx27(IIIFBrowserCreatorForm, { ...ctx });
1727
+ },
1728
+ hiddenModal: true,
1729
+ tags: ["image", "image-service"],
1730
+ resourceType: "ContentResource",
1731
+ resourceFields: ["id", "language", "type", "format", "value"],
1732
+ additionalTypes: ["Annotation", "Canvas"],
1733
+ supports: {
1734
+ parentTypes: ["Annotation", "Manifest", "AnnotationPage"],
1735
+ parentFieldMap: {
1736
+ Annotation: ["body"],
1737
+ Manifest: ["items"],
1738
+ AnnotationPage: ["items"]
1739
+ }
1740
+ },
1741
+ sideEffects: [
1742
+ resizeToFitService,
1743
+ resizeResourceToEmptyCanvas,
1744
+ repositionMultipleImages
1745
+ ],
1746
+ staticFields: {}
1747
+ };
1748
+
1749
+ // src/ContentResource/ImageServiceCreator/index.tsx
1750
+ import { jsx as jsx28 } from "react/jsx-runtime";
1751
+ var imageServiceCreator = {
1752
+ id: "@manifest-editor/image-service-creator",
1753
+ create: createImageServer,
1754
+ label: "Image Service",
1755
+ summary: "Add an image from Image Service",
1756
+ icon: /* @__PURE__ */ jsx28(TextFormatIcon, {}),
1757
+ render(ctx) {
1758
+ return /* @__PURE__ */ jsx28(CreateImageServerForm, { ...ctx });
1759
+ },
1760
+ resourceType: "ContentResource",
1761
+ resourceFields: ["id", "type", "height", "width", "format", "service"],
1762
+ supports: {
1763
+ parentFields: ["logo", "body", "thumbnail"]
1764
+ },
1765
+ staticFields: {
1766
+ type: "Image"
1767
+ }
1768
+ };
1769
+
1770
+ // src/ContentResource/ImageUrlCreator/index.tsx
1771
+ import { AddImageIcon as AddImageIcon3 } from "@manifest-editor/components";
1772
+ import { jsx as jsx29 } from "react/jsx-runtime";
1773
+ var imageUrlCreator = {
1774
+ id: "@manifest-editor/image-url-creator",
1775
+ create: createImageUrl,
1776
+ label: "Image",
1777
+ summary: "Image from a URL",
1778
+ icon: /* @__PURE__ */ jsx29(AddImageIcon3, {}),
1779
+ render(ctx) {
1780
+ return /* @__PURE__ */ jsx29(CreateImageUrlForm, { ...ctx });
1781
+ },
1782
+ resourceType: "ContentResource",
1783
+ resourceFields: ["format"],
1784
+ supports: {
1785
+ parentFields: ["logo", "body", "thumbnail"]
1786
+ },
1787
+ staticFields: {
1788
+ type: "Image"
1789
+ }
1790
+ };
1791
+
1792
+ // src/ContentResource/PlaintextCreator/create-plaintext.tsx
1793
+ import { LanguageFieldEditor as LanguageFieldEditor5, InputContainer as InputContainer8, InputLabel as InputLabel8, Input as Input6 } from "@manifest-editor/editors";
1794
+ import { useState as useState7 } from "react";
1795
+ import { jsx as jsx30, jsxs as jsxs14 } from "react/jsx-runtime";
1796
+ async function createPlaintext(data, ctx) {
1797
+ return ctx.embed({
1798
+ id: data.url,
1799
+ type: "Text",
1800
+ label: data.label,
1801
+ format: "text/plain"
1802
+ });
1803
+ }
1804
+ function CreatePlaintextForm(props) {
1805
+ const [label, setLabel] = useState7({ en: ["View as text"] });
1806
+ const onSubmit = (e) => {
1807
+ e.preventDefault();
1808
+ const data = new FormData(e.target);
1809
+ const formData = Object.fromEntries(data.entries());
1810
+ if (formData.url) {
1811
+ props.runCreate({ url: formData.url, label });
1812
+ }
1813
+ };
1814
+ return /* @__PURE__ */ jsx30(PaddedSidebarContainer, { children: /* @__PURE__ */ jsxs14("form", { onSubmit, children: [
1815
+ /* @__PURE__ */ jsx30(
1816
+ LanguageFieldEditor5,
1623
1817
  {
1624
- containerId: "label",
1625
- focusId: "label_",
1818
+ focusId: "label",
1626
1819
  label: "Label",
1627
1820
  fields: label,
1628
1821
  onSave: (e) => setLabel(e.toInternationalString())
1629
- },
1630
- url + "__" + duration
1631
- ) }) : null,
1632
- url && !error ? /* @__PURE__ */ jsx30(CanvasPanel.AudioHTML, { media: { url, duration, type: "Sound" }, children: /* @__PURE__ */ jsx30(
1633
- MediaControls,
1634
- {
1635
- onError: (error2) => setError(error2),
1636
- onDuration: (duration2) => setDuration(duration2)
1637
- },
1638
- url + "__" + duration
1639
- ) }, url + "__" + duration) : null,
1640
- duration && !error ? /* @__PURE__ */ jsxs14(FormFieldWrapper, { children: [
1641
- /* @__PURE__ */ jsx30(InputLabel8, { htmlFor: "duration", children: "Duration" }),
1642
- /* @__PURE__ */ jsx30(Input6, { type: "number", id: "duration", value: duration, onChange: (e) => setDuration(e.target.valueAsNumber) })
1643
- ] }) : null,
1644
- url && !error && /* @__PURE__ */ jsx30(Button, { onClick: onSubmit, children: "Add audio" })
1822
+ }
1823
+ ),
1824
+ /* @__PURE__ */ jsxs14(InputContainer8, { $wide: true, children: [
1825
+ /* @__PURE__ */ jsx30(InputLabel8, { htmlFor: "id", children: "Link to plaintext" }),
1826
+ /* @__PURE__ */ jsx30(Input6, { id: "url", name: "url", defaultValue: "" })
1827
+ ] }),
1828
+ /* @__PURE__ */ jsx30(Button, { type: "submit", children: "Create" })
1829
+ ] }) });
1830
+ }
1831
+
1832
+ // src/ContentResource/PlaintextCreator/index.tsx
1833
+ import { jsx as jsx31 } from "react/jsx-runtime";
1834
+ var plaintextCreator = {
1835
+ id: "@manifest-editor/plaintext-creator",
1836
+ create: createPlaintext,
1837
+ label: "Plaintext",
1838
+ summary: "Add link to an plaintext",
1839
+ icon: /* @__PURE__ */ jsx31(TextFormatIcon, {}),
1840
+ render(ctx) {
1841
+ return /* @__PURE__ */ jsx31(CreatePlaintextForm, { ...ctx });
1842
+ },
1843
+ resourceType: "ContentResource",
1844
+ resourceFields: ["label", "format"],
1845
+ supports: {
1846
+ parentFields: ["seeAlso", "rendering", "homepage"]
1847
+ },
1848
+ staticFields: {
1849
+ format: "text/plain"
1850
+ }
1851
+ };
1852
+
1853
+ // src/ContentResource/WebPageCreator/create-web-page.tsx
1854
+ import { LanguageFieldEditor as LanguageFieldEditor6, InputContainer as InputContainer9, InputLabel as InputLabel9, Input as Input7 } from "@manifest-editor/editors";
1855
+ import { useState as useState8 } from "react";
1856
+ import { jsx as jsx32, jsxs as jsxs15 } from "react/jsx-runtime";
1857
+ async function createWebPage(data, ctx) {
1858
+ return ctx.embed({
1859
+ id: data.url,
1860
+ type: "Text",
1861
+ label: data.label,
1862
+ format: "text/html"
1863
+ });
1864
+ }
1865
+ function CreateWebPageForm(props) {
1866
+ const [label, setLabel] = useState8({ en: [""] });
1867
+ const onSubmit = (e) => {
1868
+ e.preventDefault();
1869
+ const data = new FormData(e.target);
1870
+ const formData = Object.fromEntries(data.entries());
1871
+ if (formData.url) {
1872
+ props.runCreate({ url: formData.url, label });
1873
+ }
1874
+ };
1875
+ return /* @__PURE__ */ jsx32(PaddedSidebarContainer, { children: /* @__PURE__ */ jsxs15("form", { onSubmit, children: [
1876
+ /* @__PURE__ */ jsx32(
1877
+ LanguageFieldEditor6,
1878
+ {
1879
+ focusId: "label",
1880
+ label: "Label",
1881
+ fields: label,
1882
+ onSave: (e) => setLabel(e.toInternationalString())
1883
+ }
1884
+ ),
1885
+ /* @__PURE__ */ jsxs15(InputContainer9, { $wide: true, children: [
1886
+ /* @__PURE__ */ jsx32(InputLabel9, { htmlFor: "id", children: "Link" }),
1887
+ /* @__PURE__ */ jsx32(Input7, { id: "url", name: "url", defaultValue: "" })
1888
+ ] }),
1889
+ /* @__PURE__ */ jsx32(Button, { type: "submit", children: "Create link" })
1890
+ ] }) });
1891
+ }
1892
+
1893
+ // ../ui/icons/LinkIcon.tsx
1894
+ import { jsx as jsx33, jsxs as jsxs16 } from "react/jsx-runtime";
1895
+ function LinkIcon() {
1896
+ return /* @__PURE__ */ jsxs16("svg", { xmlns: "http://www.w3.org/2000/svg", height: "24px", viewBox: "0 0 24 24", width: "24px", fill: "#000000", children: [
1897
+ /* @__PURE__ */ jsx33("path", { d: "M0 0h24v24H0z", fill: "none" }),
1898
+ /* @__PURE__ */ jsx33("path", { d: "M3.9 12c0-1.71 1.39-3.1 3.1-3.1h4V7H7c-2.76 0-5 2.24-5 5s2.24 5 5 5h4v-1.9H7c-1.71 0-3.1-1.39-3.1-3.1zM8 13h8v-2H8v2zm9-6h-4v1.9h4c1.71 0 3.1 1.39 3.1 3.1s-1.39 3.1-3.1 3.1h-4V17h4c2.76 0 5-2.24 5-5s-2.24-5-5-5z" })
1645
1899
  ] });
1646
1900
  }
1647
1901
 
1648
- // src/Annotation/AudioAnnotation/index.tsx
1649
- import { AudioIcon } from "@manifest-editor/components";
1650
- import { jsx as jsx31 } from "react/jsx-runtime";
1651
- var audioAnnotation = {
1652
- id: "@manifest-editor/audio-annotation",
1653
- create: createAudioAnnotation,
1654
- label: "Audio",
1655
- summary: "Audio annotation",
1656
- icon: /* @__PURE__ */ jsx31(AudioIcon, {}),
1902
+ // src/ContentResource/WebPageCreator/index.tsx
1903
+ import { jsx as jsx34 } from "react/jsx-runtime";
1904
+ var webPageCreator = {
1905
+ id: "@manifest-editor/web-page-creator",
1906
+ create: createWebPage,
1907
+ label: "Web page",
1908
+ summary: "Add link to an external web page",
1909
+ icon: /* @__PURE__ */ jsx34(LinkIcon, {}),
1657
1910
  render(ctx) {
1658
- return /* @__PURE__ */ jsx31(CreateAudioAnnotationForm, { ...ctx });
1911
+ return /* @__PURE__ */ jsx34(CreateWebPageForm, { ...ctx });
1659
1912
  },
1660
- resourceType: "Annotation",
1661
- resourceFields: ["id", "type", "motivation", "body", "target"],
1662
- additionalTypes: ["Canvas"],
1913
+ resourceType: "ContentResource",
1914
+ resourceFields: ["label", "format"],
1663
1915
  supports: {
1664
- initialData: true,
1665
- parentTypes: ["AnnotationPage", "Manifest"],
1666
- parentFields: ["items"]
1667
- },
1668
- staticFields: {
1669
- type: "Annotation"
1916
+ parentFields: ["seeAlso", "rendering", "homepage"]
1670
1917
  }
1671
1918
  };
1672
1919
 
1673
- // src/Annotation/VideoAnnotation/create-video-annotation.tsx
1674
- import { getValue as getValue2 } from "@iiif/helpers";
1675
- import { InputContainer as InputContainer9, InputLabel as InputLabel9, Input as Input7, LanguageFieldEditor as LanguageFieldEditor5, DimensionsTriplet } from "@manifest-editor/editors";
1676
- import { useState as useState7, useEffect as useEffect3 } from "react";
1920
+ // src/ContentResource/YouTubeCreator/index.tsx
1921
+ import { YouTubeIcon } from "@manifest-editor/components";
1677
1922
 
1678
- // ../ui/ui/VideoPlayer/VideoPlayer.tsx
1679
- import { CanvasPanel as CanvasPanel2, MediaPlayerProvider, useSimpleMediaPlayer } from "react-iiif-vault";
1680
- import { jsx as jsx32, jsxs as jsxs15 } from "react/jsx-runtime";
1681
- function VideoPlayer({
1682
- media,
1683
- onError,
1684
- duration,
1685
- onDuration,
1686
- onDimensions
1687
- }) {
1688
- const [{ element, currentTime, progress }, state, actions] = useSimpleMediaPlayer({ duration });
1689
- return /* @__PURE__ */ jsxs15(
1690
- MediaPlayerProvider,
1691
- {
1692
- element,
1693
- state,
1694
- actions,
1695
- currentTime,
1696
- progress,
1697
- children: [
1698
- /* @__PURE__ */ jsx32(
1699
- "div",
1700
- {
1701
- style: {
1702
- maxHeight: 400,
1703
- aspectRatio: `${media.width}/${media.height}`,
1704
- position: "relative"
1705
- },
1706
- children: /* @__PURE__ */ jsx32(CanvasPanel2.VideoHTML, { element, media, playPause: actions.playPause })
1707
- }
1708
- ),
1709
- /* @__PURE__ */ jsx32(MediaControls, { onError, onDuration, onDimensions })
1710
- ]
1711
- }
1712
- );
1923
+ // src/ContentResource/YouTubeCreator/create-youtube-body.tsx
1924
+ import { Input as Input8, InputContainer as InputContainer10, InputLabel as InputLabel10 } from "@manifest-editor/editors";
1925
+ import { jsx as jsx35, jsxs as jsxs17 } from "react/jsx-runtime";
1926
+ var ytRegex = /^.*(?:(?:youtu\.be\/|v\/|vi\/|u\/\w\/|embed\/|shorts\/)|(?:(?:watch)?\?vi?=|&vi?=))([^#&?]*).*/;
1927
+ function getYouTubeId(url) {
1928
+ const id = (url || "").match(ytRegex);
1929
+ return id ? id[1] : null;
1713
1930
  }
1714
-
1715
- // src/Annotation/VideoAnnotation/create-video-annotation.tsx
1716
- import { jsx as jsx33, jsxs as jsxs16 } from "react/jsx-runtime";
1717
- async function createVideoAnnotation(data, ctx) {
1718
- const annotation = {
1719
- id: ctx.generateId("annotation"),
1720
- type: "Annotation"
1721
- };
1722
- const targetType = ctx.options.targetType;
1723
- const body = await ctx.embed({
1724
- id: data.url,
1931
+ async function createYoutubeBody(data, ctx) {
1932
+ const id = getYouTubeId(data.youtubeUrl);
1933
+ const body = ctx.embed({
1934
+ id: `https://www.youtube.com/watch?v=${id}`,
1725
1935
  type: "Video",
1726
- format: "video/mp4",
1727
- width: data.width,
1728
- height: data.height,
1729
- duration: data.duration || 0
1936
+ service: [
1937
+ {
1938
+ profile: "http://digirati.com/objectifier",
1939
+ params: {
1940
+ data: `https://www.youtube.com/embed/${id}`
1941
+ }
1942
+ },
1943
+ {
1944
+ id: `https://www.youtube.com/watch?v=${id}`,
1945
+ profile: "https://www.youtube.com"
1946
+ }
1947
+ ]
1730
1948
  });
1731
- if (targetType === "Annotation") {
1732
- return ctx.embed({
1733
- ...annotation,
1734
- label: getValue2(data.label) && data.label,
1735
- motivation: data.motivation || ctx.options.initialData?.motivation || "painting",
1736
- body,
1737
- target: ctx.getTarget()
1738
- });
1739
- }
1740
- if (targetType === "Canvas") {
1949
+ if (ctx.options.targetType === "Canvas") {
1741
1950
  const canvasId = ctx.generateId("canvas");
1742
- const pageId = ctx.generateId("annotation-page", { id: canvasId, type: "Canvas" });
1743
- const annotationResource = ctx.embed({
1744
- ...annotation,
1951
+ const pageId = ctx.generateId("annotation-page", {
1952
+ id: canvasId,
1953
+ type: "Canvas"
1954
+ });
1955
+ const annotation = ctx.embed({
1956
+ id: ctx.generateId("annotation", { id: pageId, type: "AnnotationPage" }),
1957
+ type: "Annotation",
1745
1958
  motivation: "painting",
1746
- body,
1747
- target: { type: "SpecificResource", source: { id: canvasId, type: "Canvas" } }
1959
+ body: [body],
1960
+ target: {
1961
+ type: "SpecificResource",
1962
+ source: { id: canvasId, type: "Canvas" }
1963
+ }
1748
1964
  });
1749
1965
  const page = ctx.embed({
1750
1966
  id: pageId,
1751
1967
  type: "AnnotationPage",
1752
- items: [annotationResource]
1968
+ items: [annotation]
1753
1969
  });
1754
1970
  return ctx.embed({
1755
1971
  id: canvasId,
1756
1972
  type: "Canvas",
1757
- label: data.label || { en: ["Untitled HTML canvas"] },
1758
- duration: data.duration || 0,
1759
- width: data.width,
1760
- height: data.height,
1973
+ label: data.label || { en: ["Untitled YouTube canvas"] },
1974
+ height: data.height || 1e3,
1975
+ width: data.width || 1e3,
1976
+ duration: data.duration || 1,
1761
1977
  items: [page]
1762
1978
  });
1763
1979
  }
1764
- }
1765
- function CreateVideoAnnotationForm(props) {
1766
- const [url, setUrl] = useState7("");
1767
- const [duration, setDuration] = useState7(0);
1768
- const [width, setWidth] = useState7(0);
1769
- const [height, setHeight] = useState7(0);
1770
- const [error, setError] = useState7("");
1771
- const [label, setLabel] = useState7({ en: [""] });
1772
- const onSubmit = () => {
1773
- props.runCreate({
1774
- url,
1775
- duration,
1776
- label,
1777
- width,
1778
- height
1980
+ if (ctx.options.targetType === "Annotation") {
1981
+ return ctx.embed({
1982
+ id: ctx.generateId("annotation"),
1983
+ type: "Annotation",
1984
+ motivation: "painting",
1985
+ body: [body],
1986
+ target: ctx.getTarget()
1779
1987
  });
1780
- };
1781
- useEffect3(() => {
1782
- setError("");
1783
- setDuration(0);
1784
- if (url) {
1785
- const labelFromUrl = url.split("/").pop()?.split(".").shift();
1786
- setLabel({ en: [labelFromUrl || ""] });
1988
+ }
1989
+ return body;
1990
+ }
1991
+ function YouTubeForm(props) {
1992
+ const onSubmit = (e) => {
1993
+ e.preventDefault();
1994
+ const data = new FormData(e.target);
1995
+ const formData = Object.fromEntries(data.entries());
1996
+ if (formData.youtubeUrl) {
1997
+ props.runCreate({ youtubeUrl: formData.youtubeUrl });
1787
1998
  }
1788
- }, [url]);
1789
- return /* @__PURE__ */ jsxs16(PaddedSidebarContainer, { children: [
1790
- error ? /* @__PURE__ */ jsx33(ErrorMessage, { children: error }) : null,
1791
- /* @__PURE__ */ jsxs16(InputContainer9, { children: [
1792
- /* @__PURE__ */ jsx33(InputLabel9, { htmlFor: "video-url", children: "URL" }),
1793
- /* @__PURE__ */ jsx33(Input7, { type: "text", id: "video-url", value: url, onChange: (e) => setUrl(e.target.value) })
1999
+ };
2000
+ return /* @__PURE__ */ jsx35(PaddedSidebarContainer, { children: /* @__PURE__ */ jsxs17("form", { onSubmit, children: [
2001
+ /* @__PURE__ */ jsxs17(InputContainer10, { $wide: true, children: [
2002
+ /* @__PURE__ */ jsx35(InputLabel10, { htmlFor: "id", children: "Link to YouTube" }),
2003
+ /* @__PURE__ */ jsx35(Input8, { id: "youtubeUrl", name: "youtubeUrl", defaultValue: "" })
1794
2004
  ] }),
1795
- url && !error ? /* @__PURE__ */ jsx33("div", { children: /* @__PURE__ */ jsx33(
1796
- LanguageFieldEditor5,
1797
- {
1798
- containerId: "label",
1799
- focusId: "label_",
1800
- label: "Label",
1801
- fields: label,
1802
- onSave: (e) => setLabel(e.toInternationalString())
1803
- },
1804
- url + "__" + duration
1805
- ) }) : null,
1806
- url && !error ? /* @__PURE__ */ jsx33(
1807
- VideoPlayer,
1808
- {
1809
- duration,
1810
- onDuration: (d) => setDuration(d),
1811
- onError: (err) => setError(err),
1812
- media: {
1813
- url,
1814
- duration,
1815
- height,
1816
- width,
1817
- type: "Video"
1818
- },
1819
- onDimensions: (w, h) => {
1820
- setWidth(w || 0);
1821
- setHeight(h || 0);
1822
- }
1823
- }
1824
- ) : null,
1825
- !error && (duration || width || height) ? /* @__PURE__ */ jsx33(InputContainer9, { $wide: true, children: /* @__PURE__ */ jsx33(
1826
- DimensionsTriplet,
1827
- {
1828
- widthId: "width",
1829
- width: width || 0,
1830
- changeWidth: (v) => setWidth(v),
1831
- heightId: "height",
1832
- height: height || 0,
1833
- changeHeight: (v) => setHeight(v),
1834
- durationId: "duration",
1835
- duration: duration || 0,
1836
- changeDuration: (v) => setDuration(v)
1837
- }
1838
- ) }) : null,
1839
- url && !error && /* @__PURE__ */ jsx33(Button, { onClick: onSubmit, children: "Add video" })
1840
- ] });
2005
+ /* @__PURE__ */ jsx35(Button, { type: "submit", children: "Create" })
2006
+ ] }) });
1841
2007
  }
1842
2008
 
1843
- // src/Annotation/VideoAnnotation/index.tsx
1844
- import { VideoIcon } from "@manifest-editor/components";
1845
- import { jsx as jsx34 } from "react/jsx-runtime";
1846
- var videoAnnotation = {
1847
- id: "@manifest-editor/video-annotation",
1848
- create: createVideoAnnotation,
1849
- label: "Video",
1850
- summary: "Video annotation",
1851
- icon: /* @__PURE__ */ jsx34(VideoIcon, {}),
1852
- render(ctx) {
1853
- return /* @__PURE__ */ jsx34(CreateVideoAnnotationForm, { ...ctx });
1854
- },
1855
- resourceType: "Annotation",
1856
- resourceFields: ["id", "type", "motivation", "body", "target"],
1857
- additionalTypes: ["Canvas"],
2009
+ // src/ContentResource/YouTubeCreator/index.tsx
2010
+ import { jsx as jsx36 } from "react/jsx-runtime";
2011
+ var youTubeBodyCreator = {
2012
+ id: "@manifest-editor/youtube",
2013
+ create: createYoutubeBody,
2014
+ label: "YouTube",
2015
+ summary: "Embed YouTube",
2016
+ icon: /* @__PURE__ */ jsx36(YouTubeIcon, {}),
2017
+ resourceType: "ContentResource",
2018
+ additionalTypes: ["Canvas", "Annotation"],
2019
+ resourceFields: ["id", "type", "service"],
2020
+ render: (ctx) => /* @__PURE__ */ jsx36(YouTubeForm, { ...ctx }),
1858
2021
  supports: {
1859
- initialData: true,
1860
- parentTypes: ["AnnotationPage", "Manifest"],
1861
- parentFields: ["items"]
2022
+ parentTypes: ["Annotation", "Manifest", "AnnotationPage"],
2023
+ parentFieldMap: {
2024
+ Annotation: ["body"],
2025
+ Manifest: ["items"],
2026
+ AnnotationPage: ["items"]
2027
+ }
1862
2028
  },
1863
2029
  staticFields: {
1864
- type: "Annotation"
2030
+ type: "Video"
1865
2031
  }
1866
2032
  };
1867
2033
 
@@ -1907,15 +2073,15 @@ var ManifestBrowserCreatorForm2 = lazy2(() => import("./manifest-browser-form.la
1907
2073
 
1908
2074
  // src/Manifest/ManifestBrowserCreator/index.tsx
1909
2075
  import { IIIFBrowserIcon as IIIFBrowserIcon2 } from "@manifest-editor/components";
1910
- import { jsx as jsx35 } from "react/jsx-runtime";
2076
+ import { jsx as jsx37 } from "react/jsx-runtime";
1911
2077
  var manifestBrowserCreator = {
1912
2078
  id: "@manifest-editor/manifest-browser-creator",
1913
2079
  create: createFromManifestBrowserOutput,
1914
2080
  label: "IIIF Browser",
1915
2081
  summary: "Browse IIIF Resources",
1916
- icon: /* @__PURE__ */ jsx35(IIIFBrowserIcon2, {}),
2082
+ icon: /* @__PURE__ */ jsx37(IIIFBrowserIcon2, {}),
1917
2083
  render(ctx) {
1918
- return /* @__PURE__ */ jsx35(ManifestBrowserCreatorForm, { ...ctx });
2084
+ return /* @__PURE__ */ jsx37(ManifestBrowserCreatorForm, { ...ctx });
1919
2085
  },
1920
2086
  resourceType: "Manifest",
1921
2087
  resourceFields: ["id", "label"],
@@ -1948,11 +2114,13 @@ var allCreators = [
1948
2114
  webPageCreator,
1949
2115
  emptyAnnotationPage,
1950
2116
  noBodyAnnotation,
1951
- internalCanvas
2117
+ internalCanvas,
2118
+ captionedImageAnnotation
1952
2119
  ];
1953
2120
  export {
1954
2121
  allCreators,
1955
2122
  audioAnnotation,
2123
+ captionedImageAnnotation,
1956
2124
  emptyAnnotationPage,
1957
2125
  emptyCanvas,
1958
2126
  htmlAnnotation,