@manifest-editor/creators 1.2.0 → 1.3.1

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