@manifest-editor/creators 1.2.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/dist/iiif-browser-form.lazy-3EZNJN4M.js +21 -0
  2. package/dist/iiif-browser-form.lazy-3YUQOIEL.js +21 -0
  3. package/dist/iiif-browser-form.lazy-4CDWA6JD.js +47 -0
  4. package/dist/iiif-browser-form.lazy-B2RXVKR5.js +21 -0
  5. package/dist/iiif-browser-form.lazy-DCCYGD42.js +35 -0
  6. package/dist/iiif-browser-form.lazy-DU2NJUCE.js +46 -0
  7. package/dist/iiif-browser-form.lazy-DUTUHLUT.js +50 -0
  8. package/dist/iiif-browser-form.lazy-EJZ754MG.js +42 -0
  9. package/dist/iiif-browser-form.lazy-ENQRBLTJ.js +21 -0
  10. package/dist/iiif-browser-form.lazy-FMKQQWDE.js +33 -0
  11. package/dist/iiif-browser-form.lazy-FOIWPPI7.js +47 -0
  12. package/dist/iiif-browser-form.lazy-G7XAQYYG.js +51 -0
  13. package/dist/iiif-browser-form.lazy-GO6U6NE5.js +32 -0
  14. package/dist/iiif-browser-form.lazy-GVEEZX37.js +33 -0
  15. package/dist/iiif-browser-form.lazy-HBWB7QGF.js +31 -0
  16. package/dist/iiif-browser-form.lazy-I7ZNZHID.js +48 -0
  17. package/dist/iiif-browser-form.lazy-IX5DFVDG.js +47 -0
  18. package/dist/iiif-browser-form.lazy-KPB57B25.js +21 -0
  19. package/dist/iiif-browser-form.lazy-KYOKLDRV.js +47 -0
  20. package/dist/iiif-browser-form.lazy-NDPLPCQQ.js +43 -0
  21. package/dist/iiif-browser-form.lazy-OQLU6NDJ.js +51 -0
  22. package/dist/iiif-browser-form.lazy-OZNQO2OO.js +21 -0
  23. package/dist/iiif-browser-form.lazy-QR55LXI4.js +21 -0
  24. package/dist/iiif-browser-form.lazy-R5TMXUBV.js +19 -0
  25. package/dist/iiif-browser-form.lazy-RWEJWTIW.js +21 -0
  26. package/dist/iiif-browser-form.lazy-T6LT6BJD.js +48 -0
  27. package/dist/iiif-browser-form.lazy-TWQYATEG.js +21 -0
  28. package/dist/iiif-browser-form.lazy-UPZXU6JB.js +51 -0
  29. package/dist/iiif-browser-form.lazy-W42LJ6X2.js +47 -0
  30. package/dist/iiif-browser-form.lazy-WAK45BLF.js +42 -0
  31. package/dist/iiif-browser-form.lazy-WVCVE6XK.js +21 -0
  32. package/dist/index.cjs +1550 -1378
  33. package/dist/index.d.cts +18 -16
  34. package/dist/index.d.ts +18 -16
  35. package/dist/index.js +1451 -1283
  36. package/package.json +13 -12
  37. package/dist/chunk-YSQDPG26.js +0 -30
  38. package/dist/iiif-browser-form.lazy-C3VAAY7I.js +0 -34
  39. package/dist/iiif-browser-form.lazy-QLO62DFA.js +0 -32
  40. package/dist/iiif-browser-form.lazy-ZADCSMAN.js +0 -32
  41. package/dist/manifest-browser-form.lazy-FLU4HSPW.js +0 -7
package/dist/index.cjs CHANGED
@@ -37,37 +37,48 @@ __export(iiif_browser_form_lazy_exports, {
37
37
  });
38
38
  function IIIFBrowserCreatorForm(props) {
39
39
  const vault = (0, import_shell3.usePreviewVault)();
40
- const { addHistory, clearHistory } = (0, import_shell3.usePreviewHistory)();
41
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_shell3.PreviewVaultBoundary, { children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
42
- import_iiif_browser.IIIFExplorer,
40
+ const output = (0, import_react7.useMemo)(() => {
41
+ return [
42
+ {
43
+ type: "callback",
44
+ label: "Select",
45
+ supportedTypes: [
46
+ "Canvas",
47
+ "CanvasList",
48
+ "CanvasRegion",
49
+ "ImageService",
50
+ "ImageServiceRegion"
51
+ ],
52
+ cb: (resource) => props.runCreate({ output: resource }),
53
+ format: { type: "content-state" }
54
+ }
55
+ ];
56
+ }, [props.runCreate]);
57
+ return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_shell3.PreviewVaultBoundary, { children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
58
+ import_iiif_browser2.IIIFBrowser,
43
59
  {
44
- window: false,
45
- hideHeader: true,
46
- outputTypes: ["Canvas", "CanvasList", "CanvasRegion", "ImageService", "ImageServiceRegion"],
47
60
  vault,
48
- output: { type: "content-state" },
49
- homepageCollection: import_shell3.HOMEPAGE_COLLECTION,
50
- clearHomepageCollection: clearHistory,
51
- onHistory: addHistory,
52
- height: 500,
53
- outputTargets: [
54
- {
55
- type: "callback",
56
- label: "Select",
57
- cb: (resource) => props.runCreate({ output: resource })
58
- }
59
- ],
60
- allowRemoveEntry: true
61
+ className: "iiif-browser border-none border-t rounded-none h-[70vh] min-h-[60vh] max-h-full max-w-full",
62
+ output,
63
+ navigation: {
64
+ canCropImage: true,
65
+ canSelectCanvas: true,
66
+ canSelectManifest: false,
67
+ canSelectCollection: false,
68
+ // @todo fix the output so this one works.
69
+ multiSelect: false
70
+ }
61
71
  }
62
72
  ) });
63
73
  }
64
- var import_iiif_browser, import_shell3, import_jsx_runtime23;
74
+ var import_shell3, import_iiif_browser2, import_react7, import_jsx_runtime27;
65
75
  var init_iiif_browser_form_lazy = __esm({
66
76
  "src/ContentResource/IIIFBrowserCreator/iiif-browser-form.lazy.tsx"() {
67
77
  "use strict";
68
- import_iiif_browser = require("@manifest-editor/iiif-browser");
69
78
  import_shell3 = require("@manifest-editor/shell");
70
- import_jsx_runtime23 = require("react/jsx-runtime");
79
+ import_iiif_browser2 = require("iiif-browser");
80
+ import_react7 = require("react");
81
+ import_jsx_runtime27 = require("react/jsx-runtime");
71
82
  }
72
83
  });
73
84
 
@@ -79,7 +90,7 @@ __export(manifest_browser_form_lazy_exports, {
79
90
  function ManifestBrowserCreatorForm(props) {
80
91
  const vault = (0, import_shell4.usePreviewVault)();
81
92
  const { addHistory, clearHistory } = (0, import_shell4.usePreviewHistory)();
82
- return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_jsx_runtime36.Fragment, { children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_shell4.PreviewVaultBoundary, { children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
93
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_jsx_runtime38.Fragment, { children: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_shell4.PreviewVaultBoundary, { children: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
83
94
  import_iiif_browser3.IIIFExplorer,
84
95
  {
85
96
  window: false,
@@ -101,13 +112,13 @@ function ManifestBrowserCreatorForm(props) {
101
112
  }
102
113
  ) }) });
103
114
  }
104
- var import_iiif_browser3, import_shell4, import_jsx_runtime36;
115
+ var import_iiif_browser3, import_shell4, import_jsx_runtime38;
105
116
  var init_manifest_browser_form_lazy = __esm({
106
117
  "src/Manifest/ManifestBrowserCreator/manifest-browser-form.lazy.tsx"() {
107
118
  "use strict";
108
119
  import_iiif_browser3 = require("@manifest-editor/iiif-browser");
109
120
  import_shell4 = require("@manifest-editor/shell");
110
- import_jsx_runtime36 = require("react/jsx-runtime");
121
+ import_jsx_runtime38 = require("react/jsx-runtime");
111
122
  }
112
123
  });
113
124
 
@@ -116,6 +127,7 @@ var src_exports = {};
116
127
  __export(src_exports, {
117
128
  allCreators: () => allCreators,
118
129
  audioAnnotation: () => audioAnnotation,
130
+ captionedImageAnnotation: () => captionedImageAnnotation,
119
131
  emptyAnnotationPage: () => emptyAnnotationPage,
120
132
  emptyCanvas: () => emptyCanvas,
121
133
  htmlAnnotation: () => htmlAnnotation,
@@ -135,363 +147,396 @@ __export(src_exports, {
135
147
  });
136
148
  module.exports = __toCommonJS(src_exports);
137
149
 
138
- // src/Canvas/EmptyCanvas/index.tsx
150
+ // src/Annotation/AudioAnnotation/create-audio-annotation.tsx
151
+ var import_react2 = require("react");
152
+
153
+ // ../ui/atoms/PaddedSidebarContainer.tsx
154
+ var import_styled_components = __toESM(require("styled-components"), 1);
155
+ var PaddedSidebarContainer = import_styled_components.default.div`
156
+ padding: 0 0.7em;
157
+ margin: 0.7em 0;
158
+ flex: 1;
159
+ overflow: visible;
160
+ `;
161
+
162
+ // src/Annotation/AudioAnnotation/create-audio-annotation.tsx
139
163
  var import_components = require("@manifest-editor/components");
140
- var import_jsx_runtime = require("react/jsx-runtime");
141
- var emptyCanvas = {
142
- id: "@manifest-editor/empty-canvas",
143
- create: createEmptyCanvas,
144
- label: "Empty canvas",
145
- summary: "No content",
146
- icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_components.EmptyCanvasIcon, {}),
147
- resourceType: "Canvas",
148
- resourceFields: ["id", "type", "label", "height", "width", "items"],
149
- supports: {
150
- parentTypes: ["Manifest"],
151
- parentFields: ["items"]
152
- }
153
- };
154
- async function createEmptyCanvas(data, ctx) {
155
- const canvasId = ctx.generateId("canvas");
156
- const page = ctx.embed({
157
- id: ctx.generateId("annotation-page", { id: canvasId, type: "Canvas" }),
158
- type: "AnnotationPage",
159
- items: []
160
- });
161
- return {
162
- id: canvasId,
163
- type: "Canvas",
164
- label: data.label || { en: [""] },
165
- height: data.height || 1e3,
166
- width: data.width || 1e3,
167
- items: [page]
168
- };
169
- }
170
164
 
171
- // ../ui/icons/TextFormatIcon.tsx
172
- var import_jsx_runtime2 = require("react/jsx-runtime");
173
- var TextFormatIcon = (props) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", height: "1em", width: "1em", ...props, children: [
174
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M0 0h24v24H0z", fill: "none" }),
175
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("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" })
176
- ] });
165
+ // ../ui/ui/MediaControls/MediaControls.tsx
166
+ var import_react_iiif_vault = require("react-iiif-vault");
167
+ var import_react = require("react");
177
168
 
178
- // src/ContentResource/PlaintextCreator/create-plaintext.tsx
179
- var import_editors = require("@manifest-editor/editors");
169
+ // ../ui/ui/MediaControls/MediaControls.icons.tsx
170
+ var import_jsx_runtime = require("react/jsx-runtime");
171
+ var PlayIcon = (props) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { ...props, viewBox: "4 4 16 16", xmlns: "http://www.w3.org/2000/svg", height: "1em", width: "1em", children: [
172
+ props.title ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("title", { children: props.title }) : null,
173
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M0 0h24v24H0z", fill: "none" }),
174
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M8 5v14l11-7z", fill: "currentColor" })
175
+ ] });
176
+ var PauseIcon = (props) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { ...props, viewBox: "4 4 16 16", xmlns: "http://www.w3.org/2000/svg", height: "1em", width: "1em", children: [
177
+ props.title ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("title", { children: props.title }) : null,
178
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M0 0h24v24H0z", fill: "none" }),
179
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { fill: "currentColor", d: "M6 19h4V5H6v14zm8-14v14h4V5h-4z" })
180
+ ] });
181
+ var VolumeUpIcon = ({ title, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { ...props, xmlns: "http://www.w3.org/2000/svg", height: "1em", width: "1em", viewBox: "2 2 20 20", children: [
182
+ title ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("title", { children: title }) : null,
183
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M0 0h24v24H0z", fill: "none" }),
184
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
185
+ "path",
186
+ {
187
+ fill: "currentColor",
188
+ 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"
189
+ }
190
+ )
191
+ ] });
192
+ var VolumeDownIcon = ({ title, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { ...props, xmlns: "http://www.w3.org/2000/svg", height: "1em", width: "1em", viewBox: "2 2 20 20", children: [
193
+ title ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("title", { children: title }) : null,
194
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M0 0h24v24H0z", fill: "none" }),
195
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
196
+ "path",
197
+ {
198
+ fill: "currentColor",
199
+ 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"
200
+ }
201
+ )
202
+ ] });
203
+ var VolumeOffIcon = ({ title, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { ...props, xmlns: "http://www.w3.org/2000/svg", height: "1em", width: "1em", viewBox: "2 2 20 20", children: [
204
+ title ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("title", { children: title }) : null,
205
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M0 0h24v24H0z", fill: "none" }),
206
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
207
+ "path",
208
+ {
209
+ fill: "currentColor",
210
+ 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"
211
+ }
212
+ )
213
+ ] });
180
214
 
181
- // ../ui/atoms/Button.tsx
182
- var import_styled_components = __toESM(require("styled-components"), 1);
183
- var ButtonReset = import_styled_components.default.button`
184
- border: none;
185
- outline: none;
186
- background-color: transparent;
187
- margin: 0;
188
- padding: 0;
189
- font-size: inherit;
190
- color: inherit;
191
- cursor: pointer;
192
- &:hover {
193
- color: inherit;
194
- }
195
- `;
196
- var Button = import_styled_components.default.button`
197
- padding: 0.5rem 1rem;
198
- color: ${(props) => props.theme.color.main || "none"};
199
- background-color: ${(props) => props.theme.color.white || "white"};
200
- border-radius: inherit;
201
- border: none;
202
- cursor: pointer;
215
+ // ../ui/ui/MediaControls/MediaControls.styles.ts
216
+ var import_styled_components2 = __toESM(require("styled-components"), 1);
217
+ var AudioPlayerContainer = import_styled_components2.default.div`
218
+ background: #ededed; // Not in variable.
203
219
  display: flex;
220
+ padding: 0.5em;
204
221
  align-items: center;
205
- font-size: 0.85em;
206
- :disabled {
207
- opacity: 0.65;
208
- cursor: not-allowed;
209
- }
210
-
211
- &:hover {
212
- background: #eee;
213
- }
214
222
  `;
215
- var SmallButton = (0, import_styled_components.default)(Button)`
216
- padding: 0.25em ${(props) => props.theme.padding.xs || "0.4em"};
217
- height: unset;
223
+ var BaseButton = import_styled_components2.default.button`
224
+ cursor: pointer;
225
+ padding: 0.75em 1.5em;
226
+ font-size: 0.938em;
218
227
  background: none;
228
+ color: inherit;
229
+ border: none;
230
+ text-decoration: none;
231
+ display: inline-block;
232
+ vertical-align: top;
233
+ white-space: nowrap;
219
234
  `;
220
- var SecondaryButton = (0, import_styled_components.default)(Button)`
221
- {
222
- padding: 0 ${(props) => props.theme.padding.medium || "1rem"};
223
- color: ${(props) => props.theme.color.main || "#B84C74;"};
224
- background-color: ${(props) => props.theme.color.white || "white"};
225
- border: 1px solid ${(props) => props.theme.color.main || "#B84C74;"};
226
- border-radius: 0.25rem;
227
- cursor: pointer;
228
- height: 2rem;
229
- display: flex;
230
- align-items: center;
231
- white-space: nowrap;
232
- font-size: 0.75rem;
235
+ var PlayButton = (0, import_styled_components2.default)(BaseButton)`
236
+ width: 2em;
237
+ height: 2em;
238
+ overflow: hidden;
239
+ font-size: 1.1em;
240
+ background: #fff;
241
+ border-radius: 3px;
242
+ padding: 0;
243
+ margin: 0;
244
+ color: #000; // Not in colours
233
245
 
234
- :disabled {
235
- opacity: 0.65;
236
- cursor: not-allowed;
237
- }
246
+ &:disabled {
247
+ color: #999;
238
248
  }
239
249
  `;
240
- var CalltoButton = (0, import_styled_components.default)(Button)`
241
- padding: 0 ${(props) => props.theme.padding.medium || "1rem"};
242
- color: ${(props) => props.theme.color.white || "white"};
243
- background-color: ${(props) => props.theme.color.main || "#B84C74;"};
244
- border: 1px solid #eee;
245
- border-radius: 0.25rem;
246
- cursor: pointer;
247
- justify-content: center;
250
+ var TimeDisplay = import_styled_components2.default.div`
251
+ margin: 0 1em;
252
+ font-size: 0.85em;
253
+ color: #000;
254
+ `;
255
+ var ProgressContainer = import_styled_components2.default.div`
256
+ flex: 1 1 0px;
257
+ height: 6px;
248
258
  display: flex;
249
- align-items: center;
250
- &:hover {
251
- background-color: ${(props) => props.theme.color.mainHover || "#B84C74;"};
252
- }
259
+ position: relative;
260
+ padding: 15px 0;
261
+ `;
262
+ var ProgressCurrent = import_styled_components2.default.div`
263
+ background: #d85680;
264
+ height: 6px;
265
+ pointer-events: none;
266
+ position: absolute;
267
+ top: 13px;
268
+ left: 0;
269
+ border-radius: 3px;
270
+ transition: width 200ms;
271
+ `;
272
+ var ProgressBackground = import_styled_components2.default.div`
273
+ position: absolute;
274
+ top: 13px;
275
+ left: 0;
276
+ height: 6px;
277
+ border-radius: 3px;
278
+ width: 100%;
279
+ pointer-events: none;
280
+ background: #fff;
281
+ `;
282
+ var SoundButton = (0, import_styled_components2.default)(BaseButton)`
283
+ width: 2em;
284
+ height: 2em;
285
+ overflow: hidden;
286
+ font-size: 1.1em;
287
+ background: transparent;
288
+ border-radius: 3px;
289
+ padding: 0;
290
+ margin: 0;
253
291
  `;
254
- var ButtonGroup = import_styled_components.default.div`
292
+ var SoundContainer = import_styled_components2.default.div`
255
293
  display: flex;
256
- flex-direction: row;
257
- flex-wrap: wrap;
258
- align-items: flex-start;
259
- justify-content: ${(props) => props.$right ? "flex-end" : "flex-start"};
294
+ align-items: center;
260
295
  `;
296
+ var VolumeContainer = import_styled_components2.default.div`
297
+ width: 0;
298
+ overflow: hidden;
299
+ transition: width 500ms;
300
+ color: #4a473f; // Not in colours
261
301
 
262
- // ../ui/atoms/PaddedSidebarContainer.tsx
263
- var import_styled_components2 = __toESM(require("styled-components"), 1);
264
- var PaddedSidebarContainer = import_styled_components2.default.div`
265
- padding: 0 0.7em;
266
- margin: 0.7em 0;
267
- flex: 1;
268
- overflow: visible;
302
+ ${(props) => props.$isOpen && import_styled_components2.css`
303
+ width: 150px;
304
+ `}
305
+ `;
306
+ var VolumeSlider = import_styled_components2.default.input.attrs({ type: "range" })`
307
+ width: 120px;
308
+ margin: 0 15px;
269
309
  `;
270
310
 
271
- // src/ContentResource/PlaintextCreator/create-plaintext.tsx
272
- var import_react = require("react");
273
- var import_jsx_runtime3 = require("react/jsx-runtime");
274
- async function createPlaintext(data, ctx) {
275
- return ctx.embed({
276
- id: data.url,
277
- type: "Text",
278
- label: data.label,
279
- format: "text/plain"
280
- });
281
- }
282
- function CreatePlaintextForm(props) {
283
- const [label, setLabel] = (0, import_react.useState)({ en: ["View as text"] });
284
- const onSubmit = (e) => {
285
- e.preventDefault();
286
- const data = new FormData(e.target);
287
- const formData = Object.fromEntries(data.entries());
288
- if (formData.url) {
289
- props.runCreate({ url: formData.url, label });
290
- }
291
- };
292
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(PaddedSidebarContainer, { children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("form", { onSubmit, children: [
293
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
294
- import_editors.LanguageFieldEditor,
311
+ // ../ui/madoc/components/icons/Spinner.tsx
312
+ var import_jsx_runtime2 = require("react/jsx-runtime");
313
+ function Spinner(props) {
314
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { width: "1em", height: "1em", viewBox: "0 0 42 42", xmlns: "http://www.w3.org/2000/svg", stroke: "#fff", ...props, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("g", { transform: "translate(2 2)", strokeWidth: 5, fill: "none", fillRule: "evenodd", children: [
315
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { strokeOpacity: 0.5, cx: 18, cy: 18, r: 18 }),
316
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M36 18c0-9.94-8.06-18-18-18", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
317
+ "animateTransform",
295
318
  {
296
- focusId: "label",
297
- label: "Label",
298
- fields: label,
299
- onSave: (e) => setLabel(e.toInternationalString())
319
+ attributeName: "transform",
320
+ type: "rotate",
321
+ from: "0 18 18",
322
+ to: "360 18 18",
323
+ dur: "1s",
324
+ repeatCount: "indefinite"
300
325
  }
301
- ),
302
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_editors.InputContainer, { $wide: true, children: [
303
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_editors.InputLabel, { htmlFor: "id", children: "Link to plaintext" }),
304
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_editors.Input, { id: "url", name: "url", defaultValue: "" })
305
- ] }),
306
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Button, { type: "submit", children: "Create" })
326
+ ) })
307
327
  ] }) });
308
328
  }
309
329
 
310
- // src/ContentResource/PlaintextCreator/index.tsx
311
- var import_jsx_runtime4 = require("react/jsx-runtime");
312
- var plaintextCreator = {
313
- id: "@manifest-editor/plaintext-creator",
314
- create: createPlaintext,
315
- label: "Plaintext",
316
- summary: "Add link to an plaintext",
317
- icon: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(TextFormatIcon, {}),
318
- render(ctx) {
319
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(CreatePlaintextForm, { ...ctx });
320
- },
321
- resourceType: "ContentResource",
322
- resourceFields: ["label", "format"],
323
- supports: {
324
- parentFields: ["seeAlso", "rendering", "homepage"]
325
- },
326
- staticFields: {
327
- format: "text/plain"
328
- }
329
- };
330
+ // ../ui/atoms/VisuallHidden/VisuallHidden.tsx
331
+ var import_styled_components3 = __toESM(require("styled-components"), 1);
332
+ var visuallyHidden = import_styled_components3.css`
333
+ clip: rect(1px, 1px, 1px, 1px);
334
+ clip-path: inset(50%);
335
+ height: 1px;
336
+ width: 1px;
337
+ margin: -1px;
338
+ overflow: hidden;
339
+ padding: 0;
340
+ position: absolute;
341
+ `;
342
+ var VisuallyHiddenLabel = import_styled_components3.default.label`
343
+ ${visuallyHidden}
344
+ `;
330
345
 
331
- // src/ContentResource/WebPageCreator/create-web-page.tsx
332
- var import_editors2 = require("@manifest-editor/editors");
333
- var import_react2 = require("react");
334
- var import_jsx_runtime5 = require("react/jsx-runtime");
335
- async function createWebPage(data, ctx) {
336
- return ctx.embed({
337
- id: data.url,
338
- type: "Text",
339
- label: data.label,
340
- format: "text/html"
341
- });
342
- }
343
- function CreateWebPageForm(props) {
344
- const [label, setLabel] = (0, import_react2.useState)({ en: [""] });
345
- const onSubmit = (e) => {
346
- e.preventDefault();
347
- const data = new FormData(e.target);
348
- const formData = Object.fromEntries(data.entries());
349
- if (formData.url) {
350
- props.runCreate({ url: formData.url, label });
346
+ // ../ui/ui/MediaControls/MediaControls.tsx
347
+ var import_jsx_runtime3 = require("react/jsx-runtime");
348
+ function MediaControls(props) {
349
+ const [isVolumeOpen, setIsVolumeOpen] = (0, import_react.useState)(false);
350
+ const { progress, currentTime, element } = (0, import_react_iiif_vault.useMediaElements)();
351
+ const { duration, isMuted, volume, isPlaying, playRequested } = (0, import_react_iiif_vault.useMediaState)();
352
+ const { setVolume, toggleMute, play, pause, setDurationPercent } = (0, import_react_iiif_vault.useMediaActions)();
353
+ const Volume = isMuted || volume === 0 ? VolumeOffIcon : volume > 50 ? VolumeUpIcon : VolumeDownIcon;
354
+ (0, import_react.useLayoutEffect)(() => {
355
+ if (element.current) {
356
+ const $el = element.current;
357
+ if ($el.error && props.onError) {
358
+ props.onError($el.error?.message || "Unknown error");
359
+ return;
360
+ }
361
+ const onError = () => {
362
+ if (props.onError) {
363
+ props.onError($el.error?.message || "Unknown error");
364
+ }
365
+ };
366
+ const loadedMetadata = () => {
367
+ if ($el.duration && props.onDuration) {
368
+ props.onDuration($el.duration);
369
+ }
370
+ if (props.onDimensions && $el instanceof HTMLVideoElement) {
371
+ props.onDimensions($el.videoWidth, $el.videoHeight);
372
+ }
373
+ };
374
+ $el.addEventListener("loadedmetadata", loadedMetadata);
375
+ $el.addEventListener("error", onError);
376
+ return () => {
377
+ $el.removeEventListener("loadedmetadata", loadedMetadata);
378
+ $el.removeEventListener("error", onError);
379
+ };
351
380
  }
352
- };
353
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(PaddedSidebarContainer, { children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("form", { onSubmit, children: [
354
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
355
- import_editors2.LanguageFieldEditor,
381
+ }, []);
382
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(AudioPlayerContainer, { children: [
383
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
384
+ PlayButton,
356
385
  {
357
- focusId: "label",
358
- label: "Label",
359
- fields: label,
360
- onSave: (e) => setLabel(e.toInternationalString())
386
+ disabled: playRequested,
387
+ onClick: () => {
388
+ if (isPlaying) {
389
+ pause();
390
+ } else {
391
+ play();
392
+ }
393
+ },
394
+ children: playRequested ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Spinner, { stroke: "#999" }) : isPlaying ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(PauseIcon, { title: "Pause" }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(PlayIcon, { title: "Play" })
361
395
  }
362
396
  ),
363
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_editors2.InputContainer, { $wide: true, children: [
364
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_editors2.InputLabel, { htmlFor: "id", children: "Link" }),
365
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_editors2.Input, { id: "url", name: "url", defaultValue: "" })
366
- ] }),
367
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Button, { type: "submit", children: "Create link" })
368
- ] }) });
369
- }
370
-
371
- // ../ui/icons/LinkIcon.tsx
372
- var import_jsx_runtime6 = require("react/jsx-runtime");
373
- function LinkIcon() {
374
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("svg", { xmlns: "http://www.w3.org/2000/svg", height: "24px", viewBox: "0 0 24 24", width: "24px", fill: "#000000", children: [
375
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "M0 0h24v24H0z", fill: "none" }),
376
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("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" })
397
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(TimeDisplay, { ref: currentTime, children: "0:00" }),
398
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
399
+ ProgressContainer,
400
+ {
401
+ onClick: (e) => {
402
+ const { left, width } = e.currentTarget.getBoundingClientRect();
403
+ const percent = (e.pageX - left) / width;
404
+ setDurationPercent(percent);
405
+ },
406
+ children: [
407
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ProgressBackground, {}),
408
+ duration ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ProgressCurrent, { ref: progress }) : null
409
+ ]
410
+ }
411
+ ),
412
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(TimeDisplay, { children: (0, import_react_iiif_vault.formatTime)(duration) }),
413
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(SoundContainer, { onMouseEnter: () => setIsVolumeOpen(true), onMouseLeave: () => setIsVolumeOpen(false), children: [
414
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(VolumeContainer, { $isOpen: isVolumeOpen, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { children: [
415
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(VisuallyHiddenLabel, { htmlFor: "audio-slider", children: "Change volume" }),
416
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
417
+ VolumeSlider,
418
+ {
419
+ id: "audio-slider",
420
+ role: "slider",
421
+ disabled: isMuted,
422
+ min: "0",
423
+ max: "100",
424
+ value: volume,
425
+ onChange: (e) => setVolume(Number(e.currentTarget.value))
426
+ }
427
+ )
428
+ ] }) }),
429
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(SoundButton, { onClick: () => toggleMute(), children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Volume, { title: isMuted ? "Mute" : "Unmute" }) })
430
+ ] })
377
431
  ] });
378
432
  }
379
433
 
380
- // src/ContentResource/WebPageCreator/index.tsx
381
- var import_jsx_runtime7 = require("react/jsx-runtime");
382
- var webPageCreator = {
383
- id: "@manifest-editor/web-page-creator",
384
- create: createWebPage,
385
- label: "Web page",
386
- summary: "Add link to an external web page",
387
- icon: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(LinkIcon, {}),
388
- render(ctx) {
389
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(CreateWebPageForm, { ...ctx });
390
- },
391
- resourceType: "ContentResource",
392
- resourceFields: ["label", "format"],
393
- supports: {
394
- parentFields: ["seeAlso", "rendering", "homepage"]
395
- }
396
- };
397
-
398
- // src/ContentResource/HTMLBodyCreator/create-html-body.tsx
399
- var import_editors3 = require("@manifest-editor/editors");
400
- var import_shell = require("@manifest-editor/shell");
401
- var import_react3 = require("react");
402
- var import_jsx_runtime8 = require("react/jsx-runtime");
403
- async function createHtmlBody(data, ctx) {
404
- return ctx.embed({
405
- id: ctx.generateId(`html/${data.language}`),
406
- language: data.language,
407
- type: "TextualBody",
408
- format: "text/html",
409
- value: data.body || ""
410
- });
411
- }
412
- function CreateHTMLBodyForm(props) {
413
- const { i18n } = (0, import_shell.useConfig)();
414
- const [body, setBodyValue] = (0, import_react3.useState)("<p></p>");
415
- const [language, setLang] = (0, import_react3.useState)(i18n.defaultLanguage);
416
- const onSubmit = () => {
417
- props.runCreate({
418
- language,
419
- body
420
- });
421
- };
422
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(PaddedSidebarContainer, { children: [
423
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_editors3.InputContainer, { children: [
424
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_editors3.InputLabel, { children: "HTML Body" }),
425
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
426
- import_editors3.RichTextLanguageField,
427
- {
428
- value: body,
429
- language,
430
- onUpdateLanguage: (lng) => setLang(lng),
431
- languages: i18n.availableLanguages,
432
- onUpdate: (e) => setBodyValue(e)
433
- }
434
- )
435
- ] }),
436
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Button, { onClick: () => onSubmit(), children: "Create" })
437
- ] });
438
- }
434
+ // src/Annotation/AudioAnnotation/create-audio-annotation.tsx
435
+ var import_react_iiif_vault2 = require("react-iiif-vault");
436
+ var import_helpers = require("@iiif/helpers");
437
+ var import_editors = require("@manifest-editor/editors");
439
438
 
440
- // src/ContentResource/HTMLBodyCreator/index.tsx
441
- var import_jsx_runtime9 = require("react/jsx-runtime");
442
- var htmlBodyCreator = {
443
- id: "@manifest-editor/html-body-creator",
444
- create: createHtmlBody,
445
- label: "HTML Body",
446
- summary: "Add HTML annotation body",
447
- icon: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(TextFormatIcon, {}),
448
- render(ctx) {
449
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(CreateHTMLBodyForm, { ...ctx });
450
- },
451
- resourceType: "ContentResource",
452
- resourceFields: ["id", "language", "type", "format", "value"],
453
- supports: {
454
- parentTypes: ["Annotation"],
455
- parentFields: ["body"]
456
- },
457
- staticFields: {
458
- type: "TextualBody",
459
- format: "text/html"
439
+ // ../ui/atoms/Button.tsx
440
+ var import_styled_components4 = __toESM(require("styled-components"), 1);
441
+ var ButtonReset = import_styled_components4.default.button`
442
+ border: none;
443
+ outline: none;
444
+ background-color: transparent;
445
+ margin: 0;
446
+ padding: 0;
447
+ font-size: inherit;
448
+ color: inherit;
449
+ cursor: pointer;
450
+ &:hover {
451
+ color: inherit;
452
+ }
453
+ `;
454
+ var Button = import_styled_components4.default.button`
455
+ padding: 0.5rem 1rem;
456
+ color: ${(props) => props.theme.color.main || "none"};
457
+ background-color: ${(props) => props.theme.color.white || "white"};
458
+ border-radius: inherit;
459
+ border: none;
460
+ cursor: pointer;
461
+ display: flex;
462
+ align-items: center;
463
+ font-size: 0.85em;
464
+ :disabled {
465
+ opacity: 0.65;
466
+ cursor: not-allowed;
460
467
  }
461
- };
462
468
 
463
- // src/Annotation/HTMLAnnotation/create-html-annotation.tsx
464
- var import_react4 = require("react");
465
- var import_editors4 = require("@manifest-editor/editors");
466
- var import_jsx_runtime10 = require("react/jsx-runtime");
467
- async function createHtmlAnnotation(data, ctx) {
468
- const annotation = {
469
- id: ctx.generateId("annotation"),
470
- type: "Annotation"
471
- };
472
- const targetType = ctx.options.targetType;
473
- const languages = Object.keys(data.body);
474
- const bodies = [];
475
- for (const language of languages) {
476
- const body = data.body[language].join("\n");
477
- if (body) {
478
- bodies.push(
479
- await ctx.create(
480
- "@manifest-editor/html-body-creator",
481
- {
482
- language,
483
- body
484
- },
485
- { parent: { resource: annotation, property: "items" } }
486
- )
487
- );
469
+ &:hover {
470
+ background: #eee;
471
+ }
472
+ `;
473
+ var SmallButton = (0, import_styled_components4.default)(Button)`
474
+ padding: 0.25em ${(props) => props.theme.padding.xs || "0.4em"};
475
+ height: unset;
476
+ background: none;
477
+ `;
478
+ var SecondaryButton = (0, import_styled_components4.default)(Button)`
479
+ {
480
+ padding: 0 ${(props) => props.theme.padding.medium || "1rem"};
481
+ color: ${(props) => props.theme.color.main || "#B84C74;"};
482
+ background-color: ${(props) => props.theme.color.white || "white"};
483
+ border: 1px solid ${(props) => props.theme.color.main || "#B84C74;"};
484
+ border-radius: 0.25rem;
485
+ cursor: pointer;
486
+ height: 2rem;
487
+ display: flex;
488
+ align-items: center;
489
+ white-space: nowrap;
490
+ font-size: 0.75rem;
491
+
492
+ :disabled {
493
+ opacity: 0.65;
494
+ cursor: not-allowed;
488
495
  }
489
496
  }
497
+ `;
498
+ var CalltoButton = (0, import_styled_components4.default)(Button)`
499
+ padding: 0 ${(props) => props.theme.padding.medium || "1rem"};
500
+ color: ${(props) => props.theme.color.white || "white"};
501
+ background-color: ${(props) => props.theme.color.main || "#B84C74;"};
502
+ border: 1px solid #eee;
503
+ border-radius: 0.25rem;
504
+ cursor: pointer;
505
+ justify-content: center;
506
+ display: flex;
507
+ align-items: center;
508
+ &:hover {
509
+ background-color: ${(props) => props.theme.color.mainHover || "#B84C74;"};
510
+ }
511
+ `;
512
+ var ButtonGroup = import_styled_components4.default.div`
513
+ display: flex;
514
+ flex-direction: row;
515
+ flex-wrap: wrap;
516
+ align-items: flex-start;
517
+ justify-content: ${(props) => props.$right ? "flex-end" : "flex-start"};
518
+ `;
519
+
520
+ // src/Annotation/AudioAnnotation/create-audio-annotation.tsx
521
+ var import_jsx_runtime4 = require("react/jsx-runtime");
522
+ async function createAudioAnnotation(data, ctx) {
523
+ const annotation = {
524
+ id: ctx.generateId("annotation"),
525
+ type: "Annotation"
526
+ };
527
+ const targetType = ctx.options.targetType;
528
+ const body = await ctx.embed({
529
+ id: data.url,
530
+ type: "Sound",
531
+ format: "audio/mp4",
532
+ duration: data.duration || 0
533
+ });
490
534
  if (targetType === "Annotation") {
491
535
  return ctx.embed({
492
536
  ...annotation,
537
+ label: (0, import_helpers.getValue)(data.label) && data.label,
493
538
  motivation: data.motivation || ctx.options.initialData?.motivation || "painting",
494
- body: bodies,
539
+ body,
495
540
  target: ctx.getTarget()
496
541
  });
497
542
  }
@@ -501,7 +546,7 @@ async function createHtmlAnnotation(data, ctx) {
501
546
  const annotationResource = ctx.embed({
502
547
  ...annotation,
503
548
  motivation: "painting",
504
- body: bodies,
549
+ body,
505
550
  target: { type: "SpecificResource", source: { id: canvasId, type: "Canvas" } }
506
551
  });
507
552
  const page = ctx.embed({
@@ -513,47 +558,75 @@ async function createHtmlAnnotation(data, ctx) {
513
558
  id: canvasId,
514
559
  type: "Canvas",
515
560
  label: data.label || { en: ["Untitled HTML canvas"] },
516
- height: data.height || 1e3,
517
- width: data.width || 1e3,
561
+ duration: data.duration || 0,
518
562
  items: [page]
519
563
  });
520
564
  }
521
565
  }
522
- function CreateHTMLAnnotation(props) {
523
- const [body, setBody] = (0, import_react4.useState)({ en: [""] });
566
+ function CreateAudioAnnotationForm(props) {
567
+ const [url, setUrl] = (0, import_react2.useState)("");
568
+ const [duration, setDuration] = (0, import_react2.useState)(0);
569
+ const [error, setError] = (0, import_react2.useState)("");
570
+ const [label, setLabel] = (0, import_react2.useState)({ en: [""] });
524
571
  const onSubmit = () => {
525
572
  props.runCreate({
526
- body
573
+ url,
574
+ duration,
575
+ label
527
576
  });
528
577
  };
529
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(PaddedSidebarContainer, { children: [
530
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_editors4.InputContainer, { children: [
531
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_editors4.InputLabel, { children: "HTML Body" }),
532
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
533
- import_editors4.LanguageFieldEditor,
534
- {
535
- focusId: "html-content",
536
- label: "HTML Content",
537
- fields: body,
538
- onSave: (e) => setBody(e.toInternationalString())
539
- }
540
- )
578
+ (0, import_react2.useEffect)(() => {
579
+ setError("");
580
+ setDuration(0);
581
+ if (url) {
582
+ const labelFromUrl = url.split("/").pop()?.split(".").shift();
583
+ setLabel({ en: [labelFromUrl || ""] });
584
+ }
585
+ }, [url]);
586
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(PaddedSidebarContainer, { children: [
587
+ error ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_components.ErrorMessage, { children: error }) : null,
588
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_editors.InputContainer, { children: [
589
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_editors.InputLabel, { htmlFor: "audio-url", children: "URL" }),
590
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_editors.Input, { type: "text", id: "audio-url", value: url, onChange: (e) => setUrl(e.target.value) })
541
591
  ] }),
542
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Button, { onClick: () => onSubmit(), children: "Create" })
592
+ url && !error ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
593
+ import_editors.LanguageFieldEditor,
594
+ {
595
+ containerId: "label",
596
+ focusId: "label_",
597
+ label: "Label",
598
+ fields: label,
599
+ onSave: (e) => setLabel(e.toInternationalString())
600
+ },
601
+ url + "__" + duration
602
+ ) }) : null,
603
+ url && !error ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_iiif_vault2.CanvasPanel.AudioHTML, { media: { url, duration, type: "Sound" }, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
604
+ MediaControls,
605
+ {
606
+ onError: (error2) => setError(error2),
607
+ onDuration: (duration2) => setDuration(duration2)
608
+ },
609
+ url + "__" + duration
610
+ ) }, url + "__" + duration) : null,
611
+ duration && !error ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_editors.FormFieldWrapper, { children: [
612
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_editors.InputLabel, { htmlFor: "duration", children: "Duration" }),
613
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_editors.Input, { type: "number", id: "duration", value: duration, onChange: (e) => setDuration(e.target.valueAsNumber) })
614
+ ] }) : null,
615
+ url && !error && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Button, { onClick: onSubmit, children: "Add audio" })
543
616
  ] });
544
617
  }
545
618
 
546
- // src/Annotation/HTMLAnnotation/index.tsx
619
+ // src/Annotation/AudioAnnotation/index.tsx
547
620
  var import_components2 = require("@manifest-editor/components");
548
- var import_jsx_runtime11 = require("react/jsx-runtime");
549
- var htmlAnnotation = {
550
- id: "@manifest-editor/html-annotation",
551
- create: createHtmlAnnotation,
552
- label: "HTML",
553
- summary: "HTML annotation",
554
- icon: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_components2.HTMLIcon, {}),
621
+ var import_jsx_runtime5 = require("react/jsx-runtime");
622
+ var audioAnnotation = {
623
+ id: "@manifest-editor/audio-annotation",
624
+ create: createAudioAnnotation,
625
+ label: "Audio",
626
+ summary: "Audio annotation",
627
+ icon: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_components2.AudioIcon, {}),
555
628
  render(ctx) {
556
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(CreateHTMLAnnotation, { ...ctx });
629
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(CreateAudioAnnotationForm, { ...ctx });
557
630
  },
558
631
  resourceType: "Annotation",
559
632
  resourceFields: ["id", "type", "motivation", "body", "target"],
@@ -568,309 +641,258 @@ var htmlAnnotation = {
568
641
  }
569
642
  };
570
643
 
571
- // ../ui/icons/ThumbnailStripIcon.tsx
572
- var import_jsx_runtime12 = require("react/jsx-runtime");
573
- var ThumbnailStripIcon = () => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
574
- "svg",
575
- {
576
- xmlns: "http://www.w3.org/2000/svg",
577
- height: "24px",
578
- viewBox: "0 0 24 24",
579
- width: "24px",
580
- fill: "#000000",
581
- transform: "scale(-1, 1)",
582
- children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("g", { children: [
583
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("rect", { fill: "none", height: "24", width: "24" }),
584
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "M16,20H2V4h14V20z M18,8h4V4h-4V8z M18,20h4v-4h-4V20z M18,14h4v-4h-4V14z" })
585
- ] })
586
- }
587
- );
644
+ // src/Annotation/CaptionedImageAnnotation/index.tsx
645
+ var import_components3 = require("@manifest-editor/components");
588
646
 
589
- // src/AnnotationPage/EmptyAnnotationPage/index.tsx
590
- var import_jsx_runtime13 = require("react/jsx-runtime");
591
- var emptyAnnotationPage = {
592
- id: "@manifest-editor/empty-annotation-page",
593
- create: createEmptyAnnotationPage,
594
- label: "Empty annotation page",
595
- summary: "AnnotationPage with nothing",
596
- icon: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(ThumbnailStripIcon, {}),
597
- resourceType: "AnnotationPage",
598
- resourceFields: ["id", "type", "items"],
599
- supports: {
600
- parentTypes: ["Manifest", "Canvas", "Range"],
601
- parentFieldMap: {
602
- Manifest: ["annotations"],
603
- Canvas: ["items", "annotations"],
604
- Range: ["annotations"]
647
+ // src/Annotation/CaptionedImageAnnotation/create-captioned-image-annotation.tsx
648
+ var import_editors2 = require("@manifest-editor/editors");
649
+ var import_react3 = require("react");
650
+ var import_jsx_runtime6 = require("react/jsx-runtime");
651
+ async function createCaptionedImageAnnotation(data, ctx) {
652
+ const annotation = {
653
+ id: ctx.generateId("annotation"),
654
+ type: "Annotation"
655
+ };
656
+ const targetType = ctx.options.targetType;
657
+ const languages = Object.keys(data.body);
658
+ const bodies = [];
659
+ for (const language of languages) {
660
+ const body = data.body[language].join("\n");
661
+ if (body) {
662
+ bodies.push(
663
+ await ctx.create(
664
+ "@manifest-editor/html-body-creator",
665
+ {
666
+ language,
667
+ body
668
+ },
669
+ { parent: { resource: annotation, property: "items" } }
670
+ )
671
+ );
605
672
  }
606
673
  }
607
- };
608
- async function createEmptyAnnotationPage(data, ctx) {
609
- return ctx.embed({
610
- id: ctx.generateId("annotations"),
611
- type: "AnnotationPage",
612
- items: []
613
- });
614
- }
615
-
616
- // src/ContentResource/YouTubeCreator/index.tsx
617
- var import_components3 = require("@manifest-editor/components");
618
-
619
- // src/ContentResource/YouTubeCreator/create-youtube-body.tsx
620
- var import_editors5 = require("@manifest-editor/editors");
621
- var import_jsx_runtime14 = require("react/jsx-runtime");
622
- var ytRegex = /^.*(?:(?:youtu\.be\/|v\/|vi\/|u\/\w\/|embed\/|shorts\/)|(?:(?:watch)?\?vi?=|&vi?=))([^#&?]*).*/;
623
- function getYouTubeId(url) {
624
- const id = (url || "").match(ytRegex);
625
- return id ? id[1] : null;
626
- }
627
- async function createYoutubeBody(data, ctx) {
628
- const id = getYouTubeId(data.youtubeUrl);
629
- const body = ctx.embed({
630
- id: `https://www.youtube.com/watch?v=${id}`,
631
- type: "Video",
632
- service: [
633
- {
634
- profile: "http://digirati.com/objectifier",
635
- params: {
636
- data: `https://www.youtube.com/embed/${id}`
637
- }
638
- },
639
- {
640
- id: `https://www.youtube.com/watch?v=${id}`,
641
- profile: "https://www.youtube.com"
642
- }
643
- ]
644
- });
645
- if (ctx.options.targetType === "Canvas") {
674
+ const resource = await ctx.create(
675
+ "@manifest-editor/image-url-creator",
676
+ { url: data.imageUrl },
677
+ {
678
+ parent: { resource: annotation, property: "body" }
679
+ }
680
+ );
681
+ if (resource) {
682
+ bodies.push(resource);
683
+ }
684
+ if (targetType === "Annotation") {
685
+ return ctx.embed({
686
+ ...annotation,
687
+ motivation: data.motivation || ctx.options.initialData?.motivation || "painting",
688
+ body: bodies,
689
+ target: ctx.getTarget()
690
+ });
691
+ }
692
+ if (targetType === "Canvas") {
646
693
  const canvasId = ctx.generateId("canvas");
647
- const pageId = ctx.generateId("annotation-page", { id: canvasId, type: "Canvas" });
648
- const annotation = await ctx.embed({
649
- id: ctx.generateId("annotation", { id: pageId, type: "AnnotationPage" }),
650
- type: "Annotation",
694
+ const pageId = ctx.generateId("annotation-page", {
695
+ id: canvasId,
696
+ type: "Canvas"
697
+ });
698
+ const annotationResource = ctx.embed({
699
+ ...annotation,
651
700
  motivation: "painting",
652
- body: [body],
653
- target: { type: "SpecificResource", source: { id: canvasId, type: "Canvas" } }
701
+ body: bodies,
702
+ target: {
703
+ type: "SpecificResource",
704
+ source: { id: canvasId, type: "Canvas" }
705
+ }
654
706
  });
655
707
  const page = ctx.embed({
656
708
  id: pageId,
657
709
  type: "AnnotationPage",
658
- items: [annotation]
710
+ items: [annotationResource]
659
711
  });
660
712
  return ctx.embed({
661
713
  id: canvasId,
662
714
  type: "Canvas",
663
- label: data.label || { en: ["Untitled YouTube canvas"] },
715
+ label: data.label || { en: ["Untitled HTML canvas"] },
664
716
  height: data.height || 1e3,
665
717
  width: data.width || 1e3,
666
- duration: data.duration || 1,
667
718
  items: [page]
668
719
  });
669
720
  }
670
- if (ctx.options.targetType === "Annotation" || ctx.options.targetType === "Canvas") {
671
- return ctx.embed({
672
- id: ctx.generateId("annotation"),
673
- type: "Annotation",
674
- motivation: "painting",
675
- body: [body],
676
- target: ctx.getTarget()
677
- });
678
- }
679
- return body;
680
721
  }
681
- function YouTubeForm(props) {
722
+ function CreateCaptionedImageAnnotation(props) {
723
+ const [body, setBody] = (0, import_react3.useState)({ en: [""] });
682
724
  const onSubmit = (e) => {
683
725
  e.preventDefault();
684
726
  const data = new FormData(e.target);
685
727
  const formData = Object.fromEntries(data.entries());
686
- if (formData.youtubeUrl) {
687
- props.runCreate({ youtubeUrl: formData.youtubeUrl });
688
- }
728
+ props.runCreate({
729
+ body,
730
+ imageUrl: formData.url
731
+ });
689
732
  };
690
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(PaddedSidebarContainer, { children: /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("form", { onSubmit, children: [
691
- /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_editors5.InputContainer, { $wide: true, children: [
692
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_editors5.InputLabel, { htmlFor: "id", children: "Link to YouTube" }),
693
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_editors5.Input, { id: "youtubeUrl", name: "youtubeUrl", defaultValue: "" })
733
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("form", { onSubmit, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(PaddedSidebarContainer, { children: [
734
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_editors2.InputContainer, { children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_editors2.InputContainer, { $wide: true, children: [
735
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_editors2.InputLabel, { htmlFor: "id", children: "Link to Image" }),
736
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_editors2.Input, { id: "url", name: "url", defaultValue: "" })
737
+ ] }) }),
738
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_editors2.InputContainer, { children: [
739
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_editors2.InputLabel, { children: "Image Caption" }),
740
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
741
+ import_editors2.LanguageFieldEditor,
742
+ {
743
+ focusId: "html-content",
744
+ label: "HTML Content",
745
+ fields: body,
746
+ onSave: (e) => setBody(e.toInternationalString())
747
+ }
748
+ )
694
749
  ] }),
695
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Button, { type: "submit", children: "Create" })
750
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Button, { type: "submit", children: "Create" })
696
751
  ] }) });
697
752
  }
698
753
 
699
- // src/ContentResource/YouTubeCreator/index.tsx
700
- var import_jsx_runtime15 = require("react/jsx-runtime");
701
- var youTubeBodyCreator = {
702
- id: "@manifest-editor/youtube",
703
- create: createYoutubeBody,
704
- label: "YouTube",
705
- summary: "Embed YouTube",
706
- icon: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_components3.YouTubeIcon, {}),
707
- resourceType: "ContentResource",
708
- additionalTypes: ["Canvas", "Annotation"],
709
- resourceFields: ["id", "type", "service"],
710
- render: (ctx) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(YouTubeForm, { ...ctx }),
754
+ // src/Annotation/CaptionedImageAnnotation/index.tsx
755
+ var import_jsx_runtime7 = require("react/jsx-runtime");
756
+ var captionedImageAnnotation = {
757
+ id: "@manifest-editor/captioned-image-annotation",
758
+ create: createCaptionedImageAnnotation,
759
+ label: "Captioned image",
760
+ summary: "Add an image from a URL with caption",
761
+ icon: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_components3.AddImageIcon, {}),
762
+ render(ctx) {
763
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(CreateCaptionedImageAnnotation, { ...ctx });
764
+ },
765
+ resourceType: "Annotation",
766
+ resourceFields: ["id", "type", "motivation", "body", "target"],
767
+ additionalTypes: ["Canvas"],
711
768
  supports: {
712
- parentTypes: ["Annotation", "Manifest", "AnnotationPage"],
713
- parentFieldMap: {
714
- Annotation: ["body"],
715
- Manifest: ["items"],
716
- AnnotationPage: ["items"]
717
- }
769
+ initialData: true,
770
+ parentTypes: ["AnnotationPage"],
771
+ parentFields: ["items"]
718
772
  },
719
773
  staticFields: {
720
- type: "Video"
774
+ type: "Annotation"
721
775
  }
722
776
  };
723
777
 
724
- // src/ContentResource/ImageUrlCreator/create-image-url.tsx
725
- var import_editors6 = require("@manifest-editor/editors");
726
- var import_shell2 = require("@manifest-editor/shell");
727
- var import_jsx_runtime16 = require("react/jsx-runtime");
728
- async function createImageUrl(data, ctx) {
729
- if (!data.height || !data.width) {
730
- const dimensions = await (0, import_shell2.getImageDimensions)(data.url);
731
- if (dimensions) {
732
- data.height = dimensions.height;
733
- data.width = dimensions.width;
778
+ // src/Annotation/HTMLAnnotation/create-html-annotation.tsx
779
+ var import_react4 = require("react");
780
+ var import_editors3 = require("@manifest-editor/editors");
781
+ var import_jsx_runtime8 = require("react/jsx-runtime");
782
+ async function createHtmlAnnotation(data, ctx) {
783
+ const annotation = {
784
+ id: ctx.generateId("annotation"),
785
+ type: "Annotation"
786
+ };
787
+ const targetType = ctx.options.targetType;
788
+ const languages = Object.keys(data.body);
789
+ const bodies = [];
790
+ for (const language of languages) {
791
+ const body = data.body[language].join("\n");
792
+ if (body) {
793
+ bodies.push(
794
+ await ctx.create(
795
+ "@manifest-editor/html-body-creator",
796
+ {
797
+ language,
798
+ body
799
+ },
800
+ { parent: { resource: annotation, property: "items" } }
801
+ )
802
+ );
734
803
  }
735
804
  }
736
- return ctx.embed({
737
- id: data.url,
738
- type: "Image",
739
- format: data.format || await (0, import_shell2.getFormat)(data.url),
740
- height: data.height,
741
- width: data.width,
742
- service: data.service ? Array.isArray(data.service) ? data.service : [data.service] : void 0
743
- });
744
- }
745
- function CreateImageUrlForm(props) {
746
- const onSubmit = (e) => {
747
- e.preventDefault();
748
- const data = new FormData(e.target);
749
- const formData = Object.fromEntries(data.entries());
750
- if (formData.url) {
751
- props.runCreate({ url: formData.url });
752
- }
753
- };
754
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(PaddedSidebarContainer, { children: /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("form", { onSubmit, children: [
755
- /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_editors6.InputContainer, { $wide: true, children: [
756
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_editors6.InputLabel, { htmlFor: "id", children: "Link to Image" }),
757
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_editors6.Input, { id: "url", name: "url", defaultValue: "" })
758
- ] }),
759
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(Button, { type: "submit", children: "Create" })
760
- ] }) });
761
- }
762
-
763
- // src/ContentResource/ImageUrlCreator/index.tsx
764
- var import_components4 = require("@manifest-editor/components");
765
- var import_jsx_runtime17 = require("react/jsx-runtime");
766
- var imageUrlCreator = {
767
- id: "@manifest-editor/image-url-creator",
768
- create: createImageUrl,
769
- label: "Image",
770
- summary: "Image from a URL",
771
- icon: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_components4.AddImageIcon, {}),
772
- render(ctx) {
773
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(CreateImageUrlForm, { ...ctx });
774
- },
775
- resourceType: "ContentResource",
776
- resourceFields: ["format"],
777
- supports: {
778
- parentFields: ["logo", "body", "thumbnail"]
779
- },
780
- staticFields: {
781
- type: "Image"
805
+ if (targetType === "Annotation") {
806
+ return ctx.embed({
807
+ ...annotation,
808
+ motivation: data.motivation || ctx.options.initialData?.motivation || "painting",
809
+ body: bodies,
810
+ target: ctx.getTarget()
811
+ });
812
+ }
813
+ if (targetType === "Canvas") {
814
+ const canvasId = ctx.generateId("canvas");
815
+ const pageId = ctx.generateId("annotation-page", {
816
+ id: canvasId,
817
+ type: "Canvas"
818
+ });
819
+ const annotationResource = ctx.embed({
820
+ ...annotation,
821
+ motivation: "painting",
822
+ body: bodies,
823
+ target: {
824
+ type: "SpecificResource",
825
+ source: { id: canvasId, type: "Canvas" }
826
+ }
827
+ });
828
+ const page = ctx.embed({
829
+ id: pageId,
830
+ type: "AnnotationPage",
831
+ items: [annotationResource]
832
+ });
833
+ return ctx.embed({
834
+ id: canvasId,
835
+ type: "Canvas",
836
+ label: data.label || { en: ["Untitled HTML canvas"] },
837
+ height: data.height || 1e3,
838
+ width: data.width || 1e3,
839
+ items: [page]
840
+ });
782
841
  }
783
- };
784
-
785
- // src/ContentResource/ImageServiceCreator/create-image-service.tsx
786
- var import_image_3 = require("@iiif/parser/image-3");
787
- var import_editors7 = require("@manifest-editor/editors");
788
- var import_jsx_runtime18 = require("react/jsx-runtime");
789
- async function createImageServer(data, ctx) {
790
- const request = (0, import_image_3.createImageServiceRequest)(data.service);
791
- const imageId = (0, import_image_3.imageServiceRequestToString)({
792
- identifier: request.identifier,
793
- server: request.server,
794
- scheme: request.scheme,
795
- type: "image",
796
- size: {
797
- max: !data.size?.width && !data.size?.height,
798
- confined: false,
799
- upscaled: false,
800
- ...data.size || {}
801
- },
802
- format: "jpg",
803
- // This isn't how it should be modelled, always full,
804
- // region: data.selector ? data.selector : { full: true },
805
- region: { full: true },
806
- rotation: { angle: 0 },
807
- quality: "default",
808
- prefix: request.prefix,
809
- originalPath: request.originalPath
810
- });
811
- const resource = ctx.embed({
812
- id: imageId,
813
- type: "Image",
814
- format: data.format || "image/jpeg",
815
- height: data.height || data.service.height,
816
- width: data.width || data.service.width,
817
- service: data.embedService === false ? void 0 : [data.service]
818
- });
819
- return resource;
820
842
  }
821
- function CreateImageServerForm(props) {
822
- const onSubmit = (e) => {
823
- e.preventDefault();
824
- const data = new FormData(e.target);
825
- const formData = Object.fromEntries(data.entries());
826
- const url = formData.url;
827
- if (url) {
828
- const canon = (0, import_image_3.canonicalServiceUrl)(url);
829
- fetch(canon).then((r) => r.json()).then((service) => {
830
- if (service["@id"]) {
831
- service.id = service["@id"];
832
- }
833
- if (service["@type"]) {
834
- service.type = service["@type"];
835
- }
836
- props.runCreate({ url: formData.url, service });
837
- });
838
- }
843
+ function CreateHTMLAnnotation(props) {
844
+ const [body, setBody] = (0, import_react4.useState)({ en: [""] });
845
+ const onSubmit = () => {
846
+ props.runCreate({
847
+ body
848
+ });
839
849
  };
840
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(PaddedSidebarContainer, { children: /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("form", { onSubmit, children: [
841
- /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_editors7.InputContainer, { $wide: true, children: [
842
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_editors7.InputLabel, { htmlFor: "id", children: "Link to image service" }),
843
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_editors7.Input, { id: "url", name: "url", defaultValue: "" })
850
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(PaddedSidebarContainer, { children: [
851
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_editors3.InputContainer, { children: [
852
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_editors3.InputLabel, { children: "HTML Body" }),
853
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
854
+ import_editors3.LanguageFieldEditor,
855
+ {
856
+ focusId: "html-content",
857
+ label: "HTML Content",
858
+ fields: body,
859
+ onSave: (e) => setBody(e.toInternationalString())
860
+ }
861
+ )
844
862
  ] }),
845
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(Button, { type: "submit", children: "Create" })
846
- ] }) });
863
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Button, { onClick: () => onSubmit(), children: "Create" })
864
+ ] });
847
865
  }
848
866
 
849
- // src/ContentResource/ImageServiceCreator/index.tsx
850
- var import_jsx_runtime19 = require("react/jsx-runtime");
851
- var imageServiceCreator = {
852
- id: "@manifest-editor/image-service-creator",
853
- create: createImageServer,
854
- label: "Image Service",
855
- summary: "Add an image from Image Service",
856
- icon: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(TextFormatIcon, {}),
867
+ // src/Annotation/HTMLAnnotation/index.tsx
868
+ var import_components4 = require("@manifest-editor/components");
869
+ var import_jsx_runtime9 = require("react/jsx-runtime");
870
+ var htmlAnnotation = {
871
+ id: "@manifest-editor/html-annotation",
872
+ create: createHtmlAnnotation,
873
+ label: "HTML",
874
+ summary: "HTML annotation",
875
+ icon: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_components4.HTMLIcon, {}),
857
876
  render(ctx) {
858
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(CreateImageServerForm, { ...ctx });
877
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(CreateHTMLAnnotation, { ...ctx });
859
878
  },
860
- resourceType: "ContentResource",
861
- resourceFields: ["id", "type", "height", "width", "format", "service"],
879
+ resourceType: "Annotation",
880
+ resourceFields: ["id", "type", "motivation", "body", "target"],
881
+ additionalTypes: ["Canvas"],
862
882
  supports: {
863
- parentFields: ["logo", "body", "thumbnail"]
883
+ initialData: true,
884
+ parentTypes: ["AnnotationPage", "Manifest"],
885
+ parentFields: ["items"]
864
886
  },
865
887
  staticFields: {
866
- type: "Image"
888
+ type: "Annotation"
867
889
  }
868
890
  };
869
891
 
870
892
  // src/side-effects/reposition-multiple-images.ts
871
- var import_image_32 = require("@iiif/parser/image-3");
893
+ var import_image_3 = require("@iiif/parser/image-3");
872
894
  var import_editor_api = require("@manifest-editor/editor-api");
873
- var import_editors8 = require("@manifest-editor/editors");
895
+ var import_editors4 = require("@manifest-editor/editors");
874
896
  var repositionMultipleImages = {
875
897
  canvasInteraction: true,
876
898
  spatial: true,
@@ -897,7 +919,7 @@ var repositionMultipleImages = {
897
919
  const body = bodies[0];
898
920
  const selector = fullAnno.body[0]?.selector;
899
921
  const serviceList = body.service || [];
900
- const imageService = serviceList.find((s) => (0, import_image_32.isImageService)(s));
922
+ const imageService = serviceList.find((s) => (0, import_image_3.isImageService)(s));
901
923
  if (imageService) {
902
924
  if (imageService.width && imageService.height) {
903
925
  if (imageService.width !== body.width) {
@@ -918,7 +940,7 @@ var repositionMultipleImages = {
918
940
  }
919
941
  }
920
942
  const editor = new import_editor_api.EditorInstance({ vault, reference: result });
921
- const imagePosition = (0, import_editors8.centerRectangles)(
943
+ const imagePosition = (0, import_editors4.centerRectangles)(
922
944
  canvas,
923
945
  {
924
946
  width,
@@ -1031,13 +1053,77 @@ var resizeToFitService = {
1031
1053
  }
1032
1054
  };
1033
1055
 
1034
- // src/Annotation/ImageServiceAnnotation/create-service-annotation.tsx
1035
- var import_jsx_runtime20 = require("react/jsx-runtime");
1036
- async function createImageServiceAnnotation(data, ctx) {
1037
- const annotation = {
1038
- id: ctx.generateId("annotation"),
1039
- type: "Annotation"
1040
- };
1056
+ // src/ContentResource/ImageServiceCreator/create-image-service.tsx
1057
+ var import_image_32 = require("@iiif/parser/image-3");
1058
+ var import_editors5 = require("@manifest-editor/editors");
1059
+ var import_jsx_runtime10 = require("react/jsx-runtime");
1060
+ async function createImageServer(data, ctx) {
1061
+ const request = (0, import_image_32.createImageServiceRequest)(data.service);
1062
+ const imageId = (0, import_image_32.imageServiceRequestToString)({
1063
+ identifier: request.identifier,
1064
+ server: request.server,
1065
+ scheme: request.scheme,
1066
+ type: "image",
1067
+ size: {
1068
+ max: !data.size?.width && !data.size?.height,
1069
+ confined: false,
1070
+ upscaled: false,
1071
+ ...data.size || {}
1072
+ },
1073
+ format: "jpg",
1074
+ // This isn't how it should be modelled, always full,
1075
+ // region: data.selector ? data.selector : { full: true },
1076
+ region: { full: true },
1077
+ rotation: { angle: 0 },
1078
+ quality: "default",
1079
+ prefix: request.prefix,
1080
+ originalPath: request.originalPath
1081
+ });
1082
+ const resource = ctx.embed({
1083
+ id: imageId,
1084
+ type: "Image",
1085
+ format: data.format || "image/jpeg",
1086
+ height: data.height || data.service.height,
1087
+ width: data.width || data.service.width,
1088
+ service: data.embedService === false ? void 0 : [data.service]
1089
+ });
1090
+ return resource;
1091
+ }
1092
+ function CreateImageServerForm(props) {
1093
+ const onSubmit = (e) => {
1094
+ e.preventDefault();
1095
+ const data = new FormData(e.target);
1096
+ const formData = Object.fromEntries(data.entries());
1097
+ const url = formData.url;
1098
+ if (url) {
1099
+ const canon = (0, import_image_32.canonicalServiceUrl)(url);
1100
+ fetch(canon).then((r) => r.json()).then((service) => {
1101
+ if (service["@id"]) {
1102
+ service.id = service["@id"];
1103
+ }
1104
+ if (service["@type"]) {
1105
+ service.type = service["@type"];
1106
+ }
1107
+ props.runCreate({ url: formData.url, service });
1108
+ });
1109
+ }
1110
+ };
1111
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(PaddedSidebarContainer, { children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("form", { onSubmit, children: [
1112
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_editors5.InputContainer, { $wide: true, children: [
1113
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_editors5.InputLabel, { htmlFor: "id", children: "Link to image service" }),
1114
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_editors5.Input, { id: "url", name: "url", defaultValue: "" })
1115
+ ] }),
1116
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Button, { type: "submit", children: "Create" })
1117
+ ] }) });
1118
+ }
1119
+
1120
+ // src/Annotation/ImageServiceAnnotation/create-service-annotation.tsx
1121
+ var import_jsx_runtime11 = require("react/jsx-runtime");
1122
+ async function createImageServiceAnnotation(data, ctx) {
1123
+ const annotation = {
1124
+ id: ctx.generateId("annotation"),
1125
+ type: "Annotation"
1126
+ };
1041
1127
  const targetType = ctx.options.targetType;
1042
1128
  const resource = await ctx.create("@manifest-editor/image-service-creator", data, {
1043
1129
  parent: { resource: annotation, property: "body" }
@@ -1075,21 +1161,22 @@ async function createImageServiceAnnotation(data, ctx) {
1075
1161
  }
1076
1162
  }
1077
1163
  function CreateImageServiceAnnotationForm(props) {
1078
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(CreateImageServerForm, { ...props });
1164
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(CreateImageServerForm, { ...props });
1079
1165
  }
1080
1166
 
1081
1167
  // src/Annotation/ImageServiceAnnotation/index.tsx
1082
1168
  var import_components5 = require("@manifest-editor/components");
1083
- var import_jsx_runtime21 = require("react/jsx-runtime");
1169
+ var import_jsx_runtime12 = require("react/jsx-runtime");
1084
1170
  var imageServiceAnnotation = {
1085
1171
  id: "@manifest-editor/image-service-annotation",
1086
1172
  create: createImageServiceAnnotation,
1087
1173
  label: "IIIF Image",
1088
1174
  dependencies: ["@manifest-editor/image-service-creator"],
1089
1175
  summary: "IIIF Image service",
1090
- icon: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_components5.IIIFLogo, { style: { padding: 10 } }),
1176
+ tags: ["image", "image-service"],
1177
+ icon: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_components5.IIIFLogo, { style: { padding: 10 } }),
1091
1178
  render(ctx) {
1092
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(CreateImageServiceAnnotationForm, { ...ctx });
1179
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(CreateImageServiceAnnotationForm, { ...ctx });
1093
1180
  },
1094
1181
  resourceType: "Annotation",
1095
1182
  resourceFields: ["id", "type", "motivation", "body", "target"],
@@ -1104,15 +1191,128 @@ var imageServiceAnnotation = {
1104
1191
  }
1105
1192
  };
1106
1193
 
1107
- // src/Annotation/NoBodyAnnotation/index.tsx
1194
+ // src/ContentResource/ImageUrlCreator/create-image-url.tsx
1195
+ var import_editors6 = require("@manifest-editor/editors");
1196
+ var import_shell = require("@manifest-editor/shell");
1197
+ var import_jsx_runtime13 = require("react/jsx-runtime");
1198
+ async function createImageUrl(data, ctx) {
1199
+ if (!data.height || !data.width) {
1200
+ const dimensions = await (0, import_shell.getImageDimensions)(data.url);
1201
+ if (dimensions) {
1202
+ data.height = dimensions.height;
1203
+ data.width = dimensions.width;
1204
+ }
1205
+ }
1206
+ return ctx.embed({
1207
+ id: data.url,
1208
+ type: "Image",
1209
+ format: data.format || await (0, import_shell.getFormat)(data.url),
1210
+ height: data.height,
1211
+ width: data.width,
1212
+ service: data.service ? Array.isArray(data.service) ? data.service : [data.service] : void 0
1213
+ });
1214
+ }
1215
+ function CreateImageUrlForm(props) {
1216
+ const onSubmit = (e) => {
1217
+ e.preventDefault();
1218
+ const data = new FormData(e.target);
1219
+ const formData = Object.fromEntries(data.entries());
1220
+ if (formData.url) {
1221
+ props.runCreate({ url: formData.url });
1222
+ }
1223
+ };
1224
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(PaddedSidebarContainer, { children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("form", { onSubmit, children: [
1225
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_editors6.InputContainer, { $wide: true, children: [
1226
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_editors6.InputLabel, { htmlFor: "id", children: "Link to Image" }),
1227
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_editors6.Input, { id: "url", name: "url", defaultValue: "" })
1228
+ ] }),
1229
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Button, { type: "submit", children: "Create" })
1230
+ ] }) });
1231
+ }
1232
+
1233
+ // src/Annotation/ImageUrlAnnotation/create-image-url-annotation.tsx
1234
+ var import_jsx_runtime14 = require("react/jsx-runtime");
1235
+ async function createImageUrlAnnotation(data, ctx) {
1236
+ const annotation = {
1237
+ id: ctx.generateId("annotation"),
1238
+ type: "Annotation"
1239
+ };
1240
+ const targetType = ctx.options.targetType;
1241
+ const resource = await ctx.create("@manifest-editor/image-url-creator", data, {
1242
+ parent: { resource: annotation, property: "body" }
1243
+ });
1244
+ if (targetType === "Annotation") {
1245
+ return {
1246
+ ...annotation,
1247
+ body: [resource],
1248
+ motivation: data.motivation || "painting",
1249
+ target: ctx.getTarget()
1250
+ };
1251
+ }
1252
+ if (targetType === "Canvas") {
1253
+ const canvasId = ctx.generateId("canvas");
1254
+ const pageId = ctx.generateId("annotation-page", { id: canvasId, type: "Canvas" });
1255
+ const annotationResource = ctx.embed({
1256
+ ...annotation,
1257
+ motivation: "painting",
1258
+ body: [resource],
1259
+ target: { type: "SpecificResource", source: { id: canvasId, type: "Canvas" } }
1260
+ });
1261
+ const page = ctx.embed({
1262
+ id: pageId,
1263
+ type: "AnnotationPage",
1264
+ items: [annotationResource]
1265
+ });
1266
+ return ctx.embed({
1267
+ id: canvasId,
1268
+ type: "Canvas",
1269
+ label: data.label || { en: ["Untitled canvas"] },
1270
+ height: resource.resource.height || 1e3,
1271
+ width: resource.resource.width || 1e3,
1272
+ items: [page]
1273
+ });
1274
+ }
1275
+ }
1276
+ function CreateImageUrlAnnotationForm(props) {
1277
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(CreateImageUrlForm, { ...props });
1278
+ }
1279
+
1280
+ // src/Annotation/ImageUrlAnnotation/index.tsx
1108
1281
  var import_components6 = require("@manifest-editor/components");
1109
- var import_jsx_runtime22 = require("react/jsx-runtime");
1282
+ var import_jsx_runtime15 = require("react/jsx-runtime");
1283
+ var imageUrlAnnotation = {
1284
+ id: "@manifest-editor/image-url-annotation",
1285
+ create: createImageUrlAnnotation,
1286
+ label: "Image",
1287
+ dependencies: ["@manifest-editor/image-url-creator"],
1288
+ summary: "Image from URL",
1289
+ tags: ["image"],
1290
+ icon: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_components6.AddImageIcon, {}),
1291
+ render(ctx) {
1292
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(CreateImageUrlAnnotationForm, { ...ctx });
1293
+ },
1294
+ resourceType: "Annotation",
1295
+ resourceFields: ["id", "type", "motivation", "body", "target"],
1296
+ additionalTypes: ["Canvas"],
1297
+ supports: {
1298
+ parentTypes: ["AnnotationPage", "Manifest"],
1299
+ parentFields: ["items"]
1300
+ },
1301
+ sideEffects: [resizeToFitService, resizeResourceToEmptyCanvas, repositionMultipleImages],
1302
+ staticFields: {
1303
+ type: "Annotation"
1304
+ }
1305
+ };
1306
+
1307
+ // src/Annotation/NoBodyAnnotation/index.tsx
1308
+ var import_components7 = require("@manifest-editor/components");
1309
+ var import_jsx_runtime16 = require("react/jsx-runtime");
1110
1310
  var noBodyAnnotation = {
1111
1311
  id: "@manifest-editor/no-body-annotation",
1112
1312
  create: createNoBodyAnnotation,
1113
1313
  label: "Annotation without body",
1114
1314
  summary: "Add an annotation that only has motivation and target",
1115
- icon: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_components6.EmptyCanvasIcon, {}),
1315
+ icon: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_components7.EmptyCanvasIcon, {}),
1116
1316
  resourceType: "Annotation",
1117
1317
  resourceFields: ["id", "type", "motivation", "target"],
1118
1318
  supports: {
@@ -1136,150 +1336,322 @@ function createNoBodyAnnotation(data, ctx) {
1136
1336
  });
1137
1337
  }
1138
1338
 
1139
- // src/ContentResource/IIIFBrowserCreator/iiif-browser-creator.tsx
1140
- var import_helpers = require("@iiif/helpers");
1339
+ // src/Annotation/VideoAnnotation/create-video-annotation.tsx
1340
+ var import_helpers2 = require("@iiif/helpers");
1341
+ var import_editors7 = require("@manifest-editor/editors");
1342
+ var import_components8 = require("@manifest-editor/components");
1141
1343
  var import_react5 = require("react");
1142
1344
 
1143
- // ../../node_modules/.pnpm/tiny-invariant@1.3.3/node_modules/tiny-invariant/dist/esm/tiny-invariant.js
1144
- var isProduction = false;
1145
- var prefix = "Invariant failed";
1146
- function invariant(condition, message) {
1147
- if (condition) {
1148
- return;
1149
- }
1150
- if (isProduction) {
1151
- throw new Error(prefix);
1152
- }
1153
- var provided = typeof message === "function" ? message() : message;
1154
- var value = provided ? "".concat(prefix, ": ").concat(provided) : prefix;
1155
- throw new Error(value);
1345
+ // ../ui/ui/VideoPlayer/VideoPlayer.tsx
1346
+ var import_react_iiif_vault3 = require("react-iiif-vault");
1347
+ var import_jsx_runtime17 = require("react/jsx-runtime");
1348
+ function VideoPlayer({
1349
+ media,
1350
+ onError,
1351
+ duration,
1352
+ onDuration,
1353
+ onDimensions
1354
+ }) {
1355
+ const [{ element, currentTime, progress }, state, actions] = (0, import_react_iiif_vault3.useSimpleMediaPlayer)({ duration });
1356
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
1357
+ import_react_iiif_vault3.MediaPlayerProvider,
1358
+ {
1359
+ element,
1360
+ state,
1361
+ actions,
1362
+ currentTime,
1363
+ progress,
1364
+ children: [
1365
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
1366
+ "div",
1367
+ {
1368
+ style: {
1369
+ maxHeight: 400,
1370
+ aspectRatio: `${media.width}/${media.height}`,
1371
+ position: "relative"
1372
+ },
1373
+ children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_react_iiif_vault3.CanvasPanel.VideoHTML, { element, media, playPause: actions.playPause })
1374
+ }
1375
+ ),
1376
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(MediaControls, { onError, onDuration, onDimensions })
1377
+ ]
1378
+ }
1379
+ );
1156
1380
  }
1157
1381
 
1158
- // src/ContentResource/IIIFBrowserCreator/iiif-browser-creator.tsx
1159
- async function createFromIIIFBrowserOutput(data, ctx) {
1382
+ // src/Annotation/VideoAnnotation/create-video-annotation.tsx
1383
+ var import_jsx_runtime18 = require("react/jsx-runtime");
1384
+ async function createVideoAnnotation(data, ctx) {
1385
+ const annotation = {
1386
+ id: ctx.generateId("annotation"),
1387
+ type: "Annotation"
1388
+ };
1160
1389
  const targetType = ctx.options.targetType;
1161
- if (Array.isArray(data.output)) {
1162
- throw new Error("Multiple items not yet supported");
1163
- }
1164
- const contentState = (0, import_helpers.normaliseContentState)(
1165
- typeof data.output === "string" ? (0, import_helpers.parseContentState)(data.output) : data.output
1166
- );
1167
- if (!contentState.target || contentState.target.length === 0) {
1168
- throw new Error("No target found in content state");
1390
+ const body = await ctx.embed({
1391
+ id: data.url,
1392
+ type: "Video",
1393
+ format: "video/mp4",
1394
+ width: data.width,
1395
+ height: data.height,
1396
+ duration: data.duration || 0
1397
+ });
1398
+ if (targetType === "Annotation") {
1399
+ return ctx.embed({
1400
+ ...annotation,
1401
+ label: (0, import_helpers2.getValue)(data.label) && data.label,
1402
+ motivation: data.motivation || ctx.options.initialData?.motivation || "painting",
1403
+ body,
1404
+ target: ctx.getTarget()
1405
+ });
1169
1406
  }
1170
- const target = contentState.target[0];
1171
- const type = target.source.type;
1172
- const previewVault = ctx.getPreviewVault();
1173
- if (targetType === "Canvas" || targetType === "Annotation") {
1174
- if (type === "Canvas") {
1175
- const canvasId = target.source.id;
1176
- const manifestId = target.source.partOf?.find((t) => t.type === "Manifest");
1177
- invariant(manifestId, "Could not load external resource without partOf");
1178
- const manifest = await previewVault.loadManifest(manifestId);
1179
- invariant(manifest, "Manifest not found");
1180
- const canvasRef = manifest.items.find((item) => item.id === canvasId);
1181
- invariant(canvasRef, "Canvas not found");
1182
- const canvas = previewVault.get(canvasRef);
1183
- if (targetType === "Canvas") {
1184
- return ctx.embed(previewVault.toPresentation3(canvas));
1185
- }
1186
- const annotationPage = previewVault.get(canvas.items[0]);
1187
- const annotation = previewVault.get(annotationPage.items[0]);
1188
- if (targetType === "Annotation") {
1189
- const fullAnnotation = previewVault.toPresentation3(annotation);
1190
- if (fullAnnotation && fullAnnotation?.body?.service) {
1191
- const width = fullAnnotation?.body.width;
1192
- const height = fullAnnotation?.body.height;
1193
- const service = Array.isArray(fullAnnotation?.body?.service) ? fullAnnotation?.body?.service[0] : fullAnnotation?.body?.service;
1194
- if (!service.width || !service.height) {
1195
- service.width = width;
1196
- service.height = height;
1197
- }
1198
- }
1199
- if (target.selector && target.selector.type === "BoxSelector") {
1200
- const selector = {
1201
- "@context": "http://iiif.io/api/annex/openannotation/context.json",
1202
- type: "iiif:ImageApiSelector",
1203
- region: [
1204
- ~~target.selector.spatial.x,
1205
- ~~target.selector.spatial.y,
1206
- ~~target.selector.spatial.width,
1207
- ~~target.selector.spatial.height
1208
- ].join(",")
1209
- };
1210
- fullAnnotation.body = {
1211
- id: ctx.generateId("SpecificResource", annotation),
1212
- type: "SpecificResource",
1213
- source: fullAnnotation.body,
1214
- selector
1215
- };
1216
- return ctx.embed({
1217
- ...fullAnnotation,
1218
- target: ctx.getTarget()
1219
- });
1407
+ if (targetType === "Canvas") {
1408
+ const canvasId = ctx.generateId("canvas");
1409
+ const pageId = ctx.generateId("annotation-page", { id: canvasId, type: "Canvas" });
1410
+ const annotationResource = ctx.embed({
1411
+ ...annotation,
1412
+ motivation: "painting",
1413
+ body,
1414
+ target: { type: "SpecificResource", source: { id: canvasId, type: "Canvas" } }
1415
+ });
1416
+ const page = ctx.embed({
1417
+ id: pageId,
1418
+ type: "AnnotationPage",
1419
+ items: [annotationResource]
1420
+ });
1421
+ return ctx.embed({
1422
+ id: canvasId,
1423
+ type: "Canvas",
1424
+ label: data.label || { en: ["Untitled HTML canvas"] },
1425
+ duration: data.duration || 0,
1426
+ width: data.width,
1427
+ height: data.height,
1428
+ items: [page]
1429
+ });
1430
+ }
1431
+ }
1432
+ function CreateVideoAnnotationForm(props) {
1433
+ const [url, setUrl] = (0, import_react5.useState)("");
1434
+ const [duration, setDuration] = (0, import_react5.useState)(0);
1435
+ const [width, setWidth] = (0, import_react5.useState)(0);
1436
+ const [height, setHeight] = (0, import_react5.useState)(0);
1437
+ const [error, setError] = (0, import_react5.useState)("");
1438
+ const [label, setLabel] = (0, import_react5.useState)({ en: [""] });
1439
+ const onSubmit = () => {
1440
+ props.runCreate({
1441
+ url,
1442
+ duration,
1443
+ label,
1444
+ width,
1445
+ height
1446
+ });
1447
+ };
1448
+ (0, import_react5.useEffect)(() => {
1449
+ setError("");
1450
+ setDuration(0);
1451
+ if (url) {
1452
+ const labelFromUrl = url.split("/").pop()?.split(".").shift();
1453
+ setLabel({ en: [labelFromUrl || ""] });
1454
+ }
1455
+ }, [url]);
1456
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(PaddedSidebarContainer, { children: [
1457
+ error ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_components8.ErrorMessage, { children: error }) : null,
1458
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_editors7.InputContainer, { children: [
1459
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_editors7.InputLabel, { htmlFor: "video-url", children: "URL" }),
1460
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_editors7.Input, { type: "text", id: "video-url", value: url, onChange: (e) => setUrl(e.target.value) })
1461
+ ] }),
1462
+ url && !error ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
1463
+ import_editors7.LanguageFieldEditor,
1464
+ {
1465
+ containerId: "label",
1466
+ focusId: "label_",
1467
+ label: "Label",
1468
+ fields: label,
1469
+ onSave: (e) => setLabel(e.toInternationalString())
1470
+ },
1471
+ url + "__" + duration
1472
+ ) }) : null,
1473
+ url && !error ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
1474
+ VideoPlayer,
1475
+ {
1476
+ duration,
1477
+ onDuration: (d) => setDuration(d),
1478
+ onError: (err) => setError(err),
1479
+ media: {
1480
+ url,
1481
+ duration,
1482
+ height,
1483
+ width,
1484
+ type: "Video"
1485
+ },
1486
+ onDimensions: (w, h) => {
1487
+ setWidth(w || 0);
1488
+ setHeight(h || 0);
1220
1489
  }
1221
- return ctx.embed({
1222
- ...previewVault.toPresentation3(annotation),
1223
- target: ctx.getTarget()
1224
- });
1225
1490
  }
1226
- }
1227
- }
1228
- throw new Error("Not yet supported.");
1491
+ ) : null,
1492
+ !error && (duration || width || height) ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_editors7.InputContainer, { $wide: true, children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
1493
+ import_editors7.DimensionsTriplet,
1494
+ {
1495
+ widthId: "width",
1496
+ width: width || 0,
1497
+ changeWidth: (v) => setWidth(v),
1498
+ heightId: "height",
1499
+ height: height || 0,
1500
+ changeHeight: (v) => setHeight(v),
1501
+ durationId: "duration",
1502
+ duration: duration || 0,
1503
+ changeDuration: (v) => setDuration(v)
1504
+ }
1505
+ ) }) : null,
1506
+ url && !error && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(Button, { onClick: onSubmit, children: "Add video" })
1507
+ ] });
1229
1508
  }
1230
- var IIIFBrowserCreatorForm2 = (0, import_react5.lazy)(() => Promise.resolve().then(() => (init_iiif_browser_form_lazy(), iiif_browser_form_lazy_exports)));
1231
1509
 
1232
- // src/ContentResource/IIIFBrowserCreator/index.tsx
1233
- var import_components7 = require("@manifest-editor/components");
1234
- var import_jsx_runtime24 = require("react/jsx-runtime");
1235
- var iiifBrowserCreator = {
1236
- id: "@manifest-editor/iiif-browser-creator",
1237
- create: createFromIIIFBrowserOutput,
1238
- label: "IIIF Browser",
1239
- summary: "Browse IIIF Resources",
1240
- icon: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_components7.IIIFBrowserIcon, {}),
1510
+ // src/Annotation/VideoAnnotation/index.tsx
1511
+ var import_components9 = require("@manifest-editor/components");
1512
+ var import_jsx_runtime19 = require("react/jsx-runtime");
1513
+ var videoAnnotation = {
1514
+ id: "@manifest-editor/video-annotation",
1515
+ create: createVideoAnnotation,
1516
+ label: "Video",
1517
+ summary: "Video annotation",
1518
+ icon: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_components9.VideoIcon, {}),
1241
1519
  render(ctx) {
1242
- return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(IIIFBrowserCreatorForm2, { ...ctx });
1520
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(CreateVideoAnnotationForm, { ...ctx });
1243
1521
  },
1244
- resourceType: "ContentResource",
1245
- resourceFields: ["id", "language", "type", "format", "value"],
1246
- additionalTypes: ["Annotation", "Canvas"],
1522
+ resourceType: "Annotation",
1523
+ resourceFields: ["id", "type", "motivation", "body", "target"],
1524
+ additionalTypes: ["Canvas"],
1247
1525
  supports: {
1248
- parentTypes: ["Annotation", "Manifest", "AnnotationPage"],
1526
+ initialData: true,
1527
+ parentTypes: ["AnnotationPage", "Manifest"],
1528
+ parentFields: ["items"]
1529
+ },
1530
+ staticFields: {
1531
+ type: "Annotation"
1532
+ }
1533
+ };
1534
+
1535
+ // ../ui/icons/ThumbnailStripIcon.tsx
1536
+ var import_jsx_runtime20 = require("react/jsx-runtime");
1537
+ var ThumbnailStripIcon = () => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
1538
+ "svg",
1539
+ {
1540
+ xmlns: "http://www.w3.org/2000/svg",
1541
+ height: "24px",
1542
+ viewBox: "0 0 24 24",
1543
+ width: "24px",
1544
+ fill: "#000000",
1545
+ transform: "scale(-1, 1)",
1546
+ children: /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("g", { children: [
1547
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("rect", { fill: "none", height: "24", width: "24" }),
1548
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("path", { d: "M16,20H2V4h14V20z M18,8h4V4h-4V8z M18,20h4v-4h-4V20z M18,14h4v-4h-4V14z" })
1549
+ ] })
1550
+ }
1551
+ );
1552
+
1553
+ // src/AnnotationPage/EmptyAnnotationPage/index.tsx
1554
+ var import_jsx_runtime21 = require("react/jsx-runtime");
1555
+ var emptyAnnotationPage = {
1556
+ id: "@manifest-editor/empty-annotation-page",
1557
+ create: createEmptyAnnotationPage,
1558
+ label: "Empty annotation page",
1559
+ summary: "AnnotationPage with nothing",
1560
+ icon: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ThumbnailStripIcon, {}),
1561
+ resourceType: "AnnotationPage",
1562
+ resourceFields: ["id", "type", "items"],
1563
+ supports: {
1564
+ parentTypes: ["Manifest", "Canvas", "Range"],
1249
1565
  parentFieldMap: {
1250
- Annotation: ["body"],
1251
- Manifest: ["items"],
1252
- AnnotationPage: ["items"]
1566
+ Manifest: ["annotations"],
1567
+ Canvas: ["items", "annotations"],
1568
+ Range: ["annotations"]
1253
1569
  }
1254
- },
1255
- sideEffects: [resizeToFitService, resizeResourceToEmptyCanvas, repositionMultipleImages],
1256
- staticFields: {}
1570
+ }
1571
+ };
1572
+ async function createEmptyAnnotationPage(data, ctx) {
1573
+ return ctx.embed({
1574
+ id: ctx.generateId("annotations"),
1575
+ label: data.label,
1576
+ type: "AnnotationPage",
1577
+ items: []
1578
+ });
1579
+ }
1580
+
1581
+ // src/Canvas/EmptyCanvas/index.tsx
1582
+ var import_components10 = require("@manifest-editor/components");
1583
+ var import_jsx_runtime22 = require("react/jsx-runtime");
1584
+ var emptyCanvas = {
1585
+ id: "@manifest-editor/empty-canvas",
1586
+ create: createEmptyCanvas,
1587
+ label: "Empty canvas",
1588
+ summary: "No content",
1589
+ icon: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_components10.EmptyCanvasIcon, {}),
1590
+ resourceType: "Canvas",
1591
+ resourceFields: ["id", "type", "label", "height", "width", "items"],
1592
+ supports: {
1593
+ parentTypes: ["Manifest"],
1594
+ parentFields: ["items"]
1595
+ }
1257
1596
  };
1597
+ async function createEmptyCanvas(data, ctx) {
1598
+ const canvasId = ctx.generateId("canvas");
1599
+ const page = ctx.embed({
1600
+ id: ctx.generateId("annotation-page", { id: canvasId, type: "Canvas" }),
1601
+ type: "AnnotationPage",
1602
+ items: []
1603
+ });
1604
+ return {
1605
+ id: canvasId,
1606
+ type: "Canvas",
1607
+ label: data.label || { en: [""] },
1608
+ height: data.height || 1e3,
1609
+ width: data.width || 1e3,
1610
+ items: [page]
1611
+ };
1612
+ }
1258
1613
 
1259
1614
  // src/Canvas/InternalCanvas/index.tsx
1260
- var import_react_iiif_vault = require("react-iiif-vault");
1261
- var import_iiif_browser2 = require("@manifest-editor/iiif-browser");
1262
- var import_jsx_runtime25 = require("react/jsx-runtime");
1615
+ var import_react_iiif_vault4 = require("react-iiif-vault");
1616
+
1617
+ // ../../node_modules/.pnpm/tiny-invariant@1.3.3/node_modules/tiny-invariant/dist/esm/tiny-invariant.js
1618
+ var isProduction = false;
1619
+ var prefix = "Invariant failed";
1620
+ function invariant(condition, message) {
1621
+ if (condition) {
1622
+ return;
1623
+ }
1624
+ if (isProduction) {
1625
+ throw new Error(prefix);
1626
+ }
1627
+ var provided = typeof message === "function" ? message() : message;
1628
+ var value = provided ? "".concat(prefix, ": ").concat(provided) : prefix;
1629
+ throw new Error(value);
1630
+ }
1631
+
1632
+ // src/Canvas/InternalCanvas/index.tsx
1633
+ var import_iiif_browser = require("@manifest-editor/iiif-browser");
1634
+ var import_jsx_runtime23 = require("react/jsx-runtime");
1263
1635
  var internalCanvas = {
1264
1636
  id: "@manifest-editor/internal-canvas",
1265
1637
  create: createInternalCanvas,
1266
1638
  label: "Internal canvas link",
1267
1639
  summary: "Canvas from the same manifest",
1268
- icon: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(ThumbnailStripIcon, {}),
1640
+ icon: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(ThumbnailStripIcon, {}),
1269
1641
  resourceType: "Canvas",
1270
1642
  resourceFields: ["id", "type", "label"],
1271
1643
  supports: {
1272
1644
  parentTypes: ["Manifest"],
1273
1645
  parentFields: ["start"]
1274
1646
  },
1275
- render: (ctx) => /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(InternalCanvas, { ...ctx })
1647
+ render: (ctx) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(InternalCanvas, { ...ctx })
1276
1648
  };
1277
1649
  function InternalCanvas(props) {
1278
- const manifest = (0, import_react_iiif_vault.useManifest)();
1279
- const vault = (0, import_react_iiif_vault.useVault)();
1650
+ const manifest = (0, import_react_iiif_vault4.useManifest)();
1651
+ const vault = (0, import_react_iiif_vault4.useVault)();
1280
1652
  invariant(manifest, "Manifest is not loaded");
1281
- return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
1282
- import_iiif_browser2.IIIFExplorer,
1653
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
1654
+ import_iiif_browser.IIIFExplorer,
1283
1655
  {
1284
1656
  vault,
1285
1657
  entry: manifest,
@@ -1304,699 +1676,498 @@ async function createInternalCanvas(data, ctx) {
1304
1676
  return ctx.embed({ id: data.id, type: "Canvas" });
1305
1677
  }
1306
1678
 
1307
- // src/Annotation/ImageUrlAnnotation/create-image-url-annotation.tsx
1308
- var import_jsx_runtime26 = require("react/jsx-runtime");
1309
- async function createImageUrlAnnotation(data, ctx) {
1310
- const annotation = {
1311
- id: ctx.generateId("annotation"),
1312
- type: "Annotation"
1313
- };
1314
- const targetType = ctx.options.targetType;
1315
- const resource = await ctx.create("@manifest-editor/image-url-creator", data, {
1316
- parent: { resource: annotation, property: "body" }
1679
+ // src/ContentResource/HTMLBodyCreator/create-html-body.tsx
1680
+ var import_editors8 = require("@manifest-editor/editors");
1681
+ var import_shell2 = require("@manifest-editor/shell");
1682
+ var import_react6 = require("react");
1683
+ var import_jsx_runtime24 = require("react/jsx-runtime");
1684
+ async function createHtmlBody(data, ctx) {
1685
+ return ctx.embed({
1686
+ id: ctx.generateId(`html/${data.language}`),
1687
+ language: data.language,
1688
+ type: "TextualBody",
1689
+ format: "text/html",
1690
+ value: data.body || ""
1317
1691
  });
1318
- if (targetType === "Annotation") {
1319
- return {
1320
- ...annotation,
1321
- body: [resource],
1322
- motivation: data.motivation || "painting",
1323
- target: ctx.getTarget()
1324
- };
1325
- }
1326
- if (targetType === "Canvas") {
1327
- const canvasId = ctx.generateId("canvas");
1328
- const pageId = ctx.generateId("annotation-page", { id: canvasId, type: "Canvas" });
1329
- const annotationResource = ctx.embed({
1330
- ...annotation,
1331
- motivation: "painting",
1332
- body: [resource],
1333
- target: { type: "SpecificResource", source: { id: canvasId, type: "Canvas" } }
1334
- });
1335
- const page = ctx.embed({
1336
- id: pageId,
1337
- type: "AnnotationPage",
1338
- items: [annotationResource]
1339
- });
1340
- return ctx.embed({
1341
- id: canvasId,
1342
- type: "Canvas",
1343
- label: data.label || { en: ["Untitled canvas"] },
1344
- height: resource.resource.height || 1e3,
1345
- width: resource.resource.width || 1e3,
1346
- items: [page]
1347
- });
1348
- }
1349
1692
  }
1350
- function CreateImageUrlAnnotationForm(props) {
1351
- return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(CreateImageUrlForm, { ...props });
1693
+ function CreateHTMLBodyForm(props) {
1694
+ const { i18n } = (0, import_shell2.useConfig)();
1695
+ const [body, setBodyValue] = (0, import_react6.useState)("<p></p>");
1696
+ const [language, setLang] = (0, import_react6.useState)(i18n.defaultLanguage);
1697
+ const onSubmit = () => {
1698
+ props.runCreate({
1699
+ language,
1700
+ body
1701
+ });
1702
+ };
1703
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(PaddedSidebarContainer, { children: [
1704
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(import_editors8.InputContainer, { children: [
1705
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_editors8.InputLabel, { children: "HTML Body" }),
1706
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
1707
+ import_editors8.RichTextLanguageField,
1708
+ {
1709
+ value: body,
1710
+ language,
1711
+ onUpdateLanguage: (lng) => setLang(lng),
1712
+ languages: i18n.availableLanguages,
1713
+ onUpdate: (e) => setBodyValue(e)
1714
+ }
1715
+ )
1716
+ ] }),
1717
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(Button, { onClick: () => onSubmit(), children: "Create" })
1718
+ ] });
1352
1719
  }
1353
1720
 
1354
- // src/Annotation/ImageUrlAnnotation/index.tsx
1355
- var import_components8 = require("@manifest-editor/components");
1356
- var import_jsx_runtime27 = require("react/jsx-runtime");
1357
- var imageUrlAnnotation = {
1358
- id: "@manifest-editor/image-url-annotation",
1359
- create: createImageUrlAnnotation,
1360
- label: "Image",
1361
- dependencies: ["@manifest-editor/image-url-creator"],
1362
- summary: "Image from URL",
1363
- icon: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_components8.AddImageIcon, {}),
1721
+ // ../ui/icons/TextFormatIcon.tsx
1722
+ var import_jsx_runtime25 = require("react/jsx-runtime");
1723
+ var TextFormatIcon = (props) => /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", height: "1em", width: "1em", ...props, children: [
1724
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("path", { d: "M0 0h24v24H0z", fill: "none" }),
1725
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("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" })
1726
+ ] });
1727
+
1728
+ // src/ContentResource/HTMLBodyCreator/index.tsx
1729
+ var import_jsx_runtime26 = require("react/jsx-runtime");
1730
+ var htmlBodyCreator = {
1731
+ id: "@manifest-editor/html-body-creator",
1732
+ create: createHtmlBody,
1733
+ label: "HTML Body",
1734
+ summary: "Add HTML annotation body",
1735
+ icon: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(TextFormatIcon, {}),
1364
1736
  render(ctx) {
1365
- return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(CreateImageUrlAnnotationForm, { ...ctx });
1737
+ return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(CreateHTMLBodyForm, { ...ctx });
1366
1738
  },
1367
- resourceType: "Annotation",
1368
- resourceFields: ["id", "type", "motivation", "body", "target"],
1369
- additionalTypes: ["Canvas"],
1739
+ resourceType: "ContentResource",
1740
+ resourceFields: ["id", "language", "type", "format", "value"],
1370
1741
  supports: {
1371
- parentTypes: ["AnnotationPage", "Manifest"],
1372
- parentFields: ["items"]
1742
+ parentTypes: ["Annotation"],
1743
+ parentFields: ["body"]
1373
1744
  },
1374
- sideEffects: [resizeToFitService, resizeResourceToEmptyCanvas, repositionMultipleImages],
1375
1745
  staticFields: {
1376
- type: "Annotation"
1746
+ type: "TextualBody",
1747
+ format: "text/html"
1377
1748
  }
1378
1749
  };
1379
1750
 
1380
- // src/Annotation/AudioAnnotation/create-audio-annotation.tsx
1381
- var import_react7 = require("react");
1751
+ // src/ContentResource/IIIFBrowserCreator/index.tsx
1752
+ var import_components11 = require("@manifest-editor/components");
1382
1753
 
1383
- // ../ui/atoms/callouts/ErrorMessage.tsx
1384
- var import_styled_components3 = __toESM(require("styled-components"), 1);
1385
- var ErrorMessage = import_styled_components3.default.div`
1386
- border: 2px solid ${(props) => props.theme.color.danger || "red"};
1387
- width: 100%;
1388
- border-radius: 5px;
1389
- padding: ${(props) => props.theme.padding.small || "0.5rem"}
1390
- ${(props) => props.theme.padding.medium || "1rem"};
1391
- line-height: 1.9em;
1392
- display: flex;
1393
- align-items: center;
1394
- a {
1395
- color: ${(props) => props.theme.color.white || "white"};
1754
+ // src/ContentResource/IIIFBrowserCreator/iiif-browser-creator.tsx
1755
+ var import_helpers3 = require("@iiif/helpers");
1756
+ var import_react8 = require("react");
1757
+ async function createFromIIIFBrowserOutput(data, ctx) {
1758
+ const targetType = ctx.options.targetType;
1759
+ if (Array.isArray(data.output)) {
1760
+ throw new Error("Multiple items not yet supported");
1396
1761
  }
1397
- ${(props) => props.$small && import_styled_components3.css`
1398
- font-size: 0.75em;
1399
- white-space: nowrap;
1400
- padding: ${props.theme.padding.xs || "0.25rem"} ${props.theme.padding.small || "0.5rem"};
1401
- line-height: 1.6em;
1402
- width: fit-content;
1403
- `}
1404
- `;
1405
-
1406
- // ../ui/ui/MediaControls/MediaControls.tsx
1407
- var import_react_iiif_vault2 = require("react-iiif-vault");
1408
- var import_react6 = require("react");
1409
-
1410
- // ../ui/ui/MediaControls/MediaControls.icons.tsx
1411
- var import_jsx_runtime28 = require("react/jsx-runtime");
1412
- var PlayIcon = (props) => /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("svg", { ...props, viewBox: "4 4 16 16", xmlns: "http://www.w3.org/2000/svg", height: "1em", width: "1em", children: [
1413
- props.title ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("title", { children: props.title }) : null,
1414
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("path", { d: "M0 0h24v24H0z", fill: "none" }),
1415
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("path", { d: "M8 5v14l11-7z", fill: "currentColor" })
1416
- ] });
1417
- var PauseIcon = (props) => /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("svg", { ...props, viewBox: "4 4 16 16", xmlns: "http://www.w3.org/2000/svg", height: "1em", width: "1em", children: [
1418
- props.title ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("title", { children: props.title }) : null,
1419
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("path", { d: "M0 0h24v24H0z", fill: "none" }),
1420
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("path", { fill: "currentColor", d: "M6 19h4V5H6v14zm8-14v14h4V5h-4z" })
1421
- ] });
1422
- var VolumeUpIcon = ({ title, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("svg", { ...props, xmlns: "http://www.w3.org/2000/svg", height: "1em", width: "1em", viewBox: "2 2 20 20", children: [
1423
- title ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("title", { children: title }) : null,
1424
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("path", { d: "M0 0h24v24H0z", fill: "none" }),
1425
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
1426
- "path",
1427
- {
1428
- fill: "currentColor",
1429
- 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"
1430
- }
1431
- )
1432
- ] });
1433
- var VolumeDownIcon = ({ title, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("svg", { ...props, xmlns: "http://www.w3.org/2000/svg", height: "1em", width: "1em", viewBox: "2 2 20 20", children: [
1434
- title ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("title", { children: title }) : null,
1435
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("path", { d: "M0 0h24v24H0z", fill: "none" }),
1436
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
1437
- "path",
1438
- {
1439
- fill: "currentColor",
1440
- 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"
1441
- }
1442
- )
1443
- ] });
1444
- var VolumeOffIcon = ({ title, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("svg", { ...props, xmlns: "http://www.w3.org/2000/svg", height: "1em", width: "1em", viewBox: "2 2 20 20", children: [
1445
- title ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("title", { children: title }) : null,
1446
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("path", { d: "M0 0h24v24H0z", fill: "none" }),
1447
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
1448
- "path",
1449
- {
1450
- fill: "currentColor",
1451
- 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"
1452
- }
1453
- )
1454
- ] });
1455
-
1456
- // ../ui/ui/MediaControls/MediaControls.styles.ts
1457
- var import_styled_components4 = __toESM(require("styled-components"), 1);
1458
- var AudioPlayerContainer = import_styled_components4.default.div`
1459
- background: #ededed; // Not in variable.
1460
- display: flex;
1461
- padding: 0.5em;
1462
- align-items: center;
1463
- `;
1464
- var BaseButton = import_styled_components4.default.button`
1465
- cursor: pointer;
1466
- padding: 0.75em 1.5em;
1467
- font-size: 0.938em;
1468
- background: none;
1469
- color: inherit;
1470
- border: none;
1471
- text-decoration: none;
1472
- display: inline-block;
1473
- vertical-align: top;
1474
- white-space: nowrap;
1475
- `;
1476
- var PlayButton = (0, import_styled_components4.default)(BaseButton)`
1477
- width: 2em;
1478
- height: 2em;
1479
- overflow: hidden;
1480
- font-size: 1.1em;
1481
- background: #fff;
1482
- border-radius: 3px;
1483
- padding: 0;
1484
- margin: 0;
1485
- color: #000; // Not in colours
1486
-
1487
- &:disabled {
1488
- color: #999;
1762
+ const contentState = (0, import_helpers3.normaliseContentState)(
1763
+ typeof data.output === "string" ? (0, import_helpers3.parseContentState)(data.output) : data.output
1764
+ );
1765
+ if (!contentState.target || contentState.target.length === 0) {
1766
+ throw new Error("No target found in content state");
1489
1767
  }
1490
- `;
1491
- var TimeDisplay = import_styled_components4.default.div`
1492
- margin: 0 1em;
1493
- font-size: 0.85em;
1494
- color: #000;
1495
- `;
1496
- var ProgressContainer = import_styled_components4.default.div`
1497
- flex: 1 1 0px;
1498
- height: 6px;
1499
- display: flex;
1500
- position: relative;
1501
- padding: 15px 0;
1502
- `;
1503
- var ProgressCurrent = import_styled_components4.default.div`
1504
- background: #d85680;
1505
- height: 6px;
1506
- pointer-events: none;
1507
- position: absolute;
1508
- top: 13px;
1509
- left: 0;
1510
- border-radius: 3px;
1511
- transition: width 200ms;
1512
- `;
1513
- var ProgressBackground = import_styled_components4.default.div`
1514
- position: absolute;
1515
- top: 13px;
1516
- left: 0;
1517
- height: 6px;
1518
- border-radius: 3px;
1519
- width: 100%;
1520
- pointer-events: none;
1521
- background: #fff;
1522
- `;
1523
- var SoundButton = (0, import_styled_components4.default)(BaseButton)`
1524
- width: 2em;
1525
- height: 2em;
1526
- overflow: hidden;
1527
- font-size: 1.1em;
1528
- background: transparent;
1529
- border-radius: 3px;
1530
- padding: 0;
1531
- margin: 0;
1532
- `;
1533
- var SoundContainer = import_styled_components4.default.div`
1534
- display: flex;
1535
- align-items: center;
1536
- `;
1537
- var VolumeContainer = import_styled_components4.default.div`
1538
- width: 0;
1539
- overflow: hidden;
1540
- transition: width 500ms;
1541
- color: #4a473f; // Not in colours
1542
-
1543
- ${(props) => props.$isOpen && import_styled_components4.css`
1544
- width: 150px;
1545
- `}
1546
- `;
1547
- var VolumeSlider = import_styled_components4.default.input.attrs({ type: "range" })`
1548
- width: 120px;
1549
- margin: 0 15px;
1550
- `;
1551
-
1552
- // ../ui/madoc/components/icons/Spinner.tsx
1553
- var import_jsx_runtime29 = require("react/jsx-runtime");
1554
- function Spinner(props) {
1555
- return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("svg", { width: "1em", height: "1em", viewBox: "0 0 42 42", xmlns: "http://www.w3.org/2000/svg", stroke: "#fff", ...props, children: /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("g", { transform: "translate(2 2)", strokeWidth: 5, fill: "none", fillRule: "evenodd", children: [
1556
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("circle", { strokeOpacity: 0.5, cx: 18, cy: 18, r: 18 }),
1557
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("path", { d: "M36 18c0-9.94-8.06-18-18-18", children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
1558
- "animateTransform",
1559
- {
1560
- attributeName: "transform",
1561
- type: "rotate",
1562
- from: "0 18 18",
1563
- to: "360 18 18",
1564
- dur: "1s",
1565
- repeatCount: "indefinite"
1566
- }
1567
- ) })
1568
- ] }) });
1569
- }
1570
-
1571
- // ../ui/atoms/VisuallHidden/VisuallHidden.tsx
1572
- var import_styled_components5 = __toESM(require("styled-components"), 1);
1573
- var visuallyHidden = import_styled_components5.css`
1574
- clip: rect(1px, 1px, 1px, 1px);
1575
- clip-path: inset(50%);
1576
- height: 1px;
1577
- width: 1px;
1578
- margin: -1px;
1579
- overflow: hidden;
1580
- padding: 0;
1581
- position: absolute;
1582
- `;
1583
- var VisuallyHiddenLabel = import_styled_components5.default.label`
1584
- ${visuallyHidden}
1585
- `;
1586
-
1587
- // ../ui/ui/MediaControls/MediaControls.tsx
1588
- var import_jsx_runtime30 = require("react/jsx-runtime");
1589
- function MediaControls(props) {
1590
- const [isVolumeOpen, setIsVolumeOpen] = (0, import_react6.useState)(false);
1591
- const { progress, currentTime, element } = (0, import_react_iiif_vault2.useMediaElements)();
1592
- const { duration, isMuted, volume, isPlaying, playRequested } = (0, import_react_iiif_vault2.useMediaState)();
1593
- const { setVolume, toggleMute, play, pause, setDurationPercent } = (0, import_react_iiif_vault2.useMediaActions)();
1594
- const Volume = isMuted || volume === 0 ? VolumeOffIcon : volume > 50 ? VolumeUpIcon : VolumeDownIcon;
1595
- (0, import_react6.useLayoutEffect)(() => {
1596
- if (element.current) {
1597
- const $el = element.current;
1598
- if ($el.error && props.onError) {
1599
- props.onError($el.error?.message || "Unknown error");
1600
- return;
1601
- }
1602
- const onError = () => {
1603
- if (props.onError) {
1604
- props.onError($el.error?.message || "Unknown error");
1605
- }
1606
- };
1607
- const loadedMetadata = () => {
1608
- if ($el.duration && props.onDuration) {
1609
- props.onDuration($el.duration);
1610
- }
1611
- if (props.onDimensions && $el instanceof HTMLVideoElement) {
1612
- props.onDimensions($el.videoWidth, $el.videoHeight);
1613
- }
1614
- };
1615
- $el.addEventListener("loadedmetadata", loadedMetadata);
1616
- $el.addEventListener("error", onError);
1617
- return () => {
1618
- $el.removeEventListener("loadedmetadata", loadedMetadata);
1619
- $el.removeEventListener("error", onError);
1620
- };
1621
- }
1622
- }, []);
1623
- return /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(AudioPlayerContainer, { children: [
1624
- /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
1625
- PlayButton,
1626
- {
1627
- disabled: playRequested,
1628
- onClick: () => {
1629
- if (isPlaying) {
1630
- pause();
1631
- } else {
1632
- play();
1633
- }
1634
- },
1635
- children: playRequested ? /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(Spinner, { stroke: "#999" }) : isPlaying ? /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(PauseIcon, { title: "Pause" }) : /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(PlayIcon, { title: "Play" })
1636
- }
1637
- ),
1638
- /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(TimeDisplay, { ref: currentTime, children: "0:00" }),
1639
- /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(
1640
- ProgressContainer,
1641
- {
1642
- onClick: (e) => {
1643
- const { left, width } = e.currentTarget.getBoundingClientRect();
1644
- const percent = (e.pageX - left) / width;
1645
- setDurationPercent(percent);
1646
- },
1647
- children: [
1648
- /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(ProgressBackground, {}),
1649
- duration ? /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(ProgressCurrent, { ref: progress }) : null
1650
- ]
1768
+ const target = contentState.target[0];
1769
+ const type = target.source.type;
1770
+ const previewVault = ctx.getPreviewVault();
1771
+ if (targetType === "Canvas" || targetType === "Annotation") {
1772
+ if (type === "Canvas") {
1773
+ const canvasId = target.source.id;
1774
+ const manifestId = target.source.partOf?.find(
1775
+ (t) => t.type === "Manifest"
1776
+ );
1777
+ invariant(manifestId, "Could not load external resource without partOf");
1778
+ const manifest = await previewVault.loadManifest(manifestId);
1779
+ invariant(manifest, "Manifest not found");
1780
+ const canvasRef = manifest.items.find((item) => item.id === canvasId);
1781
+ invariant(canvasRef, "Canvas not found");
1782
+ const canvas = previewVault.get(canvasRef);
1783
+ if (targetType === "Canvas" && target.selector?.type !== "BoxSelector") {
1784
+ return ctx.embed(previewVault.toPresentation3(canvas));
1651
1785
  }
1652
- ),
1653
- /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(TimeDisplay, { children: (0, import_react_iiif_vault2.formatTime)(duration) }),
1654
- /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(SoundContainer, { onMouseEnter: () => setIsVolumeOpen(true), onMouseLeave: () => setIsVolumeOpen(false), children: [
1655
- /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(VolumeContainer, { $isOpen: isVolumeOpen, children: /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("span", { children: [
1656
- /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(VisuallyHiddenLabel, { htmlFor: "audio-slider", children: "Change volume" }),
1657
- /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
1658
- VolumeSlider,
1659
- {
1660
- id: "audio-slider",
1661
- role: "slider",
1662
- disabled: isMuted,
1663
- min: "0",
1664
- max: "100",
1665
- value: volume,
1666
- onChange: (e) => setVolume(Number(e.currentTarget.value))
1786
+ const annotationPage = previewVault.get(canvas.items[0]);
1787
+ const annotation = previewVault.get(annotationPage.items[0]);
1788
+ if (targetType === "Annotation" || targetType === "Canvas") {
1789
+ const fullAnnotation = previewVault.toPresentation3(annotation);
1790
+ if (fullAnnotation?.body?.service) {
1791
+ const width = fullAnnotation?.body.width;
1792
+ const height = fullAnnotation?.body.height;
1793
+ const service = Array.isArray(fullAnnotation?.body?.service) ? fullAnnotation?.body?.service[0] : fullAnnotation?.body?.service;
1794
+ if (!service.width || !service.height) {
1795
+ service.width = width;
1796
+ service.height = height;
1667
1797
  }
1668
- )
1669
- ] }) }),
1670
- /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(SoundButton, { onClick: () => toggleMute(), children: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(Volume, { title: isMuted ? "Mute" : "Unmute" }) })
1671
- ] })
1672
- ] });
1798
+ }
1799
+ if (target.selector && target.selector.type === "BoxSelector") {
1800
+ const selector = {
1801
+ "@context": "http://iiif.io/api/annex/openannotation/context.json",
1802
+ type: "iiif:ImageApiSelector",
1803
+ region: [
1804
+ ~~target.selector.spatial.x,
1805
+ ~~target.selector.spatial.y,
1806
+ ~~target.selector.spatial.width,
1807
+ ~~target.selector.spatial.height
1808
+ ].join(",")
1809
+ };
1810
+ fullAnnotation.body = {
1811
+ id: ctx.generateId("SpecificResource", annotation),
1812
+ type: "SpecificResource",
1813
+ source: fullAnnotation.body,
1814
+ selector
1815
+ };
1816
+ if (targetType === "Canvas") {
1817
+ const canvasId2 = ctx.generateId("Canvas", annotation);
1818
+ fullAnnotation.target = canvasId2;
1819
+ const pageId = ctx.generateId("Page", annotation);
1820
+ const annotationPage2 = ctx.embed({
1821
+ id: pageId,
1822
+ type: "AnnotationPage",
1823
+ items: [fullAnnotation]
1824
+ });
1825
+ return ctx.embed({
1826
+ id: canvasId2,
1827
+ type: "Canvas",
1828
+ width: ~~target.selector.spatial.width,
1829
+ height: ~~target.selector.spatial.height,
1830
+ items: [annotationPage2]
1831
+ });
1832
+ }
1833
+ return ctx.embed({
1834
+ ...fullAnnotation,
1835
+ target: ctx.getTarget()
1836
+ });
1837
+ }
1838
+ return ctx.embed({
1839
+ ...previewVault.toPresentation3(annotation),
1840
+ target: ctx.getTarget()
1841
+ });
1842
+ }
1843
+ }
1844
+ }
1845
+ throw new Error("Not yet supported.");
1673
1846
  }
1847
+ var IIIFBrowserCreatorForm2 = (0, import_react8.lazy)(
1848
+ () => Promise.resolve().then(() => (init_iiif_browser_form_lazy(), iiif_browser_form_lazy_exports))
1849
+ );
1674
1850
 
1675
- // src/Annotation/AudioAnnotation/create-audio-annotation.tsx
1676
- var import_react_iiif_vault3 = require("react-iiif-vault");
1677
- var import_helpers2 = require("@iiif/helpers");
1851
+ // src/ContentResource/IIIFBrowserCreator/index.tsx
1852
+ var import_jsx_runtime28 = require("react/jsx-runtime");
1853
+ var iiifBrowserCreator = {
1854
+ id: "@manifest-editor/iiif-browser-creator",
1855
+ create: createFromIIIFBrowserOutput,
1856
+ label: "IIIF Browser",
1857
+ summary: "Browse IIIF Resources",
1858
+ icon: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_components11.IIIFBrowserIcon, {}),
1859
+ render(ctx) {
1860
+ return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(IIIFBrowserCreatorForm2, { ...ctx });
1861
+ },
1862
+ hiddenModal: true,
1863
+ tags: ["image", "image-service"],
1864
+ resourceType: "ContentResource",
1865
+ resourceFields: ["id", "language", "type", "format", "value"],
1866
+ additionalTypes: ["Annotation", "Canvas"],
1867
+ supports: {
1868
+ parentTypes: ["Annotation", "Manifest", "AnnotationPage"],
1869
+ parentFieldMap: {
1870
+ Annotation: ["body"],
1871
+ Manifest: ["items"],
1872
+ AnnotationPage: ["items"]
1873
+ }
1874
+ },
1875
+ sideEffects: [
1876
+ resizeToFitService,
1877
+ resizeResourceToEmptyCanvas,
1878
+ repositionMultipleImages
1879
+ ],
1880
+ staticFields: {}
1881
+ };
1882
+
1883
+ // src/ContentResource/ImageServiceCreator/index.tsx
1884
+ var import_jsx_runtime29 = require("react/jsx-runtime");
1885
+ var imageServiceCreator = {
1886
+ id: "@manifest-editor/image-service-creator",
1887
+ create: createImageServer,
1888
+ label: "Image Service",
1889
+ summary: "Add an image from Image Service",
1890
+ icon: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(TextFormatIcon, {}),
1891
+ render(ctx) {
1892
+ return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(CreateImageServerForm, { ...ctx });
1893
+ },
1894
+ resourceType: "ContentResource",
1895
+ resourceFields: ["id", "type", "height", "width", "format", "service"],
1896
+ supports: {
1897
+ parentFields: ["logo", "body", "thumbnail"]
1898
+ },
1899
+ staticFields: {
1900
+ type: "Image"
1901
+ }
1902
+ };
1903
+
1904
+ // src/ContentResource/ImageUrlCreator/index.tsx
1905
+ var import_components12 = require("@manifest-editor/components");
1906
+ var import_jsx_runtime30 = require("react/jsx-runtime");
1907
+ var imageUrlCreator = {
1908
+ id: "@manifest-editor/image-url-creator",
1909
+ create: createImageUrl,
1910
+ label: "Image",
1911
+ summary: "Image from a URL",
1912
+ icon: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_components12.AddImageIcon, {}),
1913
+ render(ctx) {
1914
+ return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(CreateImageUrlForm, { ...ctx });
1915
+ },
1916
+ resourceType: "ContentResource",
1917
+ resourceFields: ["format"],
1918
+ supports: {
1919
+ parentFields: ["logo", "body", "thumbnail"]
1920
+ },
1921
+ staticFields: {
1922
+ type: "Image"
1923
+ }
1924
+ };
1925
+
1926
+ // src/ContentResource/PlaintextCreator/create-plaintext.tsx
1678
1927
  var import_editors9 = require("@manifest-editor/editors");
1928
+ var import_react9 = require("react");
1679
1929
  var import_jsx_runtime31 = require("react/jsx-runtime");
1680
- async function createAudioAnnotation(data, ctx) {
1681
- const annotation = {
1682
- id: ctx.generateId("annotation"),
1683
- type: "Annotation"
1684
- };
1685
- const targetType = ctx.options.targetType;
1686
- const body = await ctx.embed({
1930
+ async function createPlaintext(data, ctx) {
1931
+ return ctx.embed({
1687
1932
  id: data.url,
1688
- type: "Sound",
1689
- format: "audio/mp4",
1690
- duration: data.duration || 0
1933
+ type: "Text",
1934
+ label: data.label,
1935
+ format: "text/plain"
1691
1936
  });
1692
- if (targetType === "Annotation") {
1693
- return ctx.embed({
1694
- ...annotation,
1695
- label: (0, import_helpers2.getValue)(data.label) && data.label,
1696
- motivation: data.motivation || ctx.options.initialData?.motivation || "painting",
1697
- body,
1698
- target: ctx.getTarget()
1699
- });
1700
- }
1701
- if (targetType === "Canvas") {
1702
- const canvasId = ctx.generateId("canvas");
1703
- const pageId = ctx.generateId("annotation-page", { id: canvasId, type: "Canvas" });
1704
- const annotationResource = ctx.embed({
1705
- ...annotation,
1706
- motivation: "painting",
1707
- body,
1708
- target: { type: "SpecificResource", source: { id: canvasId, type: "Canvas" } }
1709
- });
1710
- const page = ctx.embed({
1711
- id: pageId,
1712
- type: "AnnotationPage",
1713
- items: [annotationResource]
1714
- });
1715
- return ctx.embed({
1716
- id: canvasId,
1717
- type: "Canvas",
1718
- label: data.label || { en: ["Untitled HTML canvas"] },
1719
- duration: data.duration || 0,
1720
- items: [page]
1721
- });
1722
- }
1723
1937
  }
1724
- function CreateAudioAnnotationForm(props) {
1725
- const [url, setUrl] = (0, import_react7.useState)("");
1726
- const [duration, setDuration] = (0, import_react7.useState)(0);
1727
- const [error, setError] = (0, import_react7.useState)("");
1728
- const [label, setLabel] = (0, import_react7.useState)({ en: [""] });
1729
- const onSubmit = () => {
1730
- props.runCreate({
1731
- url,
1732
- duration,
1733
- label
1734
- });
1735
- };
1736
- (0, import_react7.useEffect)(() => {
1737
- setError("");
1738
- setDuration(0);
1739
- if (url) {
1740
- const labelFromUrl = url.split("/").pop()?.split(".").shift();
1741
- setLabel({ en: [labelFromUrl || ""] });
1938
+ function CreatePlaintextForm(props) {
1939
+ const [label, setLabel] = (0, import_react9.useState)({ en: ["View as text"] });
1940
+ const onSubmit = (e) => {
1941
+ e.preventDefault();
1942
+ const data = new FormData(e.target);
1943
+ const formData = Object.fromEntries(data.entries());
1944
+ if (formData.url) {
1945
+ props.runCreate({ url: formData.url, label });
1742
1946
  }
1743
- }, [url]);
1744
- return /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(PaddedSidebarContainer, { children: [
1745
- error ? /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(ErrorMessage, { children: error }) : null,
1746
- /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(import_editors9.InputContainer, { children: [
1747
- /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(import_editors9.InputLabel, { htmlFor: "audio-url", children: "URL" }),
1748
- /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(import_editors9.Input, { type: "text", id: "audio-url", value: url, onChange: (e) => setUrl(e.target.value) })
1749
- ] }),
1750
- url && !error ? /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
1947
+ };
1948
+ return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(PaddedSidebarContainer, { children: /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("form", { onSubmit, children: [
1949
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
1751
1950
  import_editors9.LanguageFieldEditor,
1752
1951
  {
1753
- containerId: "label",
1754
- focusId: "label_",
1952
+ focusId: "label",
1755
1953
  label: "Label",
1756
1954
  fields: label,
1757
1955
  onSave: (e) => setLabel(e.toInternationalString())
1758
- },
1759
- url + "__" + duration
1760
- ) }) : null,
1761
- url && !error ? /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(import_react_iiif_vault3.CanvasPanel.AudioHTML, { media: { url, duration, type: "Sound" }, children: /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
1762
- MediaControls,
1763
- {
1764
- onError: (error2) => setError(error2),
1765
- onDuration: (duration2) => setDuration(duration2)
1766
- },
1767
- url + "__" + duration
1768
- ) }, url + "__" + duration) : null,
1769
- duration && !error ? /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(import_editors9.FormFieldWrapper, { children: [
1770
- /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(import_editors9.InputLabel, { htmlFor: "duration", children: "Duration" }),
1771
- /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(import_editors9.Input, { type: "number", id: "duration", value: duration, onChange: (e) => setDuration(e.target.valueAsNumber) })
1772
- ] }) : null,
1773
- url && !error && /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(Button, { onClick: onSubmit, children: "Add audio" })
1956
+ }
1957
+ ),
1958
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(import_editors9.InputContainer, { $wide: true, children: [
1959
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(import_editors9.InputLabel, { htmlFor: "id", children: "Link to plaintext" }),
1960
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(import_editors9.Input, { id: "url", name: "url", defaultValue: "" })
1961
+ ] }),
1962
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(Button, { type: "submit", children: "Create" })
1963
+ ] }) });
1964
+ }
1965
+
1966
+ // src/ContentResource/PlaintextCreator/index.tsx
1967
+ var import_jsx_runtime32 = require("react/jsx-runtime");
1968
+ var plaintextCreator = {
1969
+ id: "@manifest-editor/plaintext-creator",
1970
+ create: createPlaintext,
1971
+ label: "Plaintext",
1972
+ summary: "Add link to an plaintext",
1973
+ icon: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(TextFormatIcon, {}),
1974
+ render(ctx) {
1975
+ return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(CreatePlaintextForm, { ...ctx });
1976
+ },
1977
+ resourceType: "ContentResource",
1978
+ resourceFields: ["label", "format"],
1979
+ supports: {
1980
+ parentFields: ["seeAlso", "rendering", "homepage"]
1981
+ },
1982
+ staticFields: {
1983
+ format: "text/plain"
1984
+ }
1985
+ };
1986
+
1987
+ // src/ContentResource/WebPageCreator/create-web-page.tsx
1988
+ var import_editors10 = require("@manifest-editor/editors");
1989
+ var import_react10 = require("react");
1990
+ var import_jsx_runtime33 = require("react/jsx-runtime");
1991
+ async function createWebPage(data, ctx) {
1992
+ return ctx.embed({
1993
+ id: data.url,
1994
+ type: "Text",
1995
+ label: data.label,
1996
+ format: "text/html"
1997
+ });
1998
+ }
1999
+ function CreateWebPageForm(props) {
2000
+ const [label, setLabel] = (0, import_react10.useState)({ en: [""] });
2001
+ const onSubmit = (e) => {
2002
+ e.preventDefault();
2003
+ const data = new FormData(e.target);
2004
+ const formData = Object.fromEntries(data.entries());
2005
+ if (formData.url) {
2006
+ props.runCreate({ url: formData.url, label });
2007
+ }
2008
+ };
2009
+ return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(PaddedSidebarContainer, { children: /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("form", { onSubmit, children: [
2010
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
2011
+ import_editors10.LanguageFieldEditor,
2012
+ {
2013
+ focusId: "label",
2014
+ label: "Label",
2015
+ fields: label,
2016
+ onSave: (e) => setLabel(e.toInternationalString())
2017
+ }
2018
+ ),
2019
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(import_editors10.InputContainer, { $wide: true, children: [
2020
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_editors10.InputLabel, { htmlFor: "id", children: "Link" }),
2021
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_editors10.Input, { id: "url", name: "url", defaultValue: "" })
2022
+ ] }),
2023
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(Button, { type: "submit", children: "Create link" })
2024
+ ] }) });
2025
+ }
2026
+
2027
+ // ../ui/icons/LinkIcon.tsx
2028
+ var import_jsx_runtime34 = require("react/jsx-runtime");
2029
+ function LinkIcon() {
2030
+ return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("svg", { xmlns: "http://www.w3.org/2000/svg", height: "24px", viewBox: "0 0 24 24", width: "24px", fill: "#000000", children: [
2031
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("path", { d: "M0 0h24v24H0z", fill: "none" }),
2032
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("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" })
1774
2033
  ] });
1775
2034
  }
1776
2035
 
1777
- // src/Annotation/AudioAnnotation/index.tsx
1778
- var import_components9 = require("@manifest-editor/components");
1779
- var import_jsx_runtime32 = require("react/jsx-runtime");
1780
- var audioAnnotation = {
1781
- id: "@manifest-editor/audio-annotation",
1782
- create: createAudioAnnotation,
1783
- label: "Audio",
1784
- summary: "Audio annotation",
1785
- icon: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_components9.AudioIcon, {}),
2036
+ // src/ContentResource/WebPageCreator/index.tsx
2037
+ var import_jsx_runtime35 = require("react/jsx-runtime");
2038
+ var webPageCreator = {
2039
+ id: "@manifest-editor/web-page-creator",
2040
+ create: createWebPage,
2041
+ label: "Web page",
2042
+ summary: "Add link to an external web page",
2043
+ icon: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(LinkIcon, {}),
1786
2044
  render(ctx) {
1787
- return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(CreateAudioAnnotationForm, { ...ctx });
2045
+ return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(CreateWebPageForm, { ...ctx });
1788
2046
  },
1789
- resourceType: "Annotation",
1790
- resourceFields: ["id", "type", "motivation", "body", "target"],
1791
- additionalTypes: ["Canvas"],
2047
+ resourceType: "ContentResource",
2048
+ resourceFields: ["label", "format"],
1792
2049
  supports: {
1793
- initialData: true,
1794
- parentTypes: ["AnnotationPage", "Manifest"],
1795
- parentFields: ["items"]
1796
- },
1797
- staticFields: {
1798
- type: "Annotation"
2050
+ parentFields: ["seeAlso", "rendering", "homepage"]
1799
2051
  }
1800
2052
  };
1801
2053
 
1802
- // src/Annotation/VideoAnnotation/create-video-annotation.tsx
1803
- var import_helpers3 = require("@iiif/helpers");
1804
- var import_editors10 = require("@manifest-editor/editors");
1805
- var import_react8 = require("react");
2054
+ // src/ContentResource/YouTubeCreator/index.tsx
2055
+ var import_components13 = require("@manifest-editor/components");
1806
2056
 
1807
- // ../ui/ui/VideoPlayer/VideoPlayer.tsx
1808
- var import_react_iiif_vault4 = require("react-iiif-vault");
1809
- var import_jsx_runtime33 = require("react/jsx-runtime");
1810
- function VideoPlayer({
1811
- media,
1812
- onError,
1813
- duration,
1814
- onDuration,
1815
- onDimensions
1816
- }) {
1817
- const [{ element, currentTime, progress }, state, actions] = (0, import_react_iiif_vault4.useSimpleMediaPlayer)({ duration });
1818
- return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
1819
- import_react_iiif_vault4.MediaPlayerProvider,
1820
- {
1821
- element,
1822
- state,
1823
- actions,
1824
- currentTime,
1825
- progress,
1826
- children: [
1827
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
1828
- "div",
1829
- {
1830
- style: {
1831
- maxHeight: 400,
1832
- aspectRatio: `${media.width}/${media.height}`,
1833
- position: "relative"
1834
- },
1835
- children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_react_iiif_vault4.CanvasPanel.VideoHTML, { element, media, playPause: actions.playPause })
1836
- }
1837
- ),
1838
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(MediaControls, { onError, onDuration, onDimensions })
1839
- ]
1840
- }
1841
- );
2057
+ // src/ContentResource/YouTubeCreator/create-youtube-body.tsx
2058
+ var import_editors11 = require("@manifest-editor/editors");
2059
+ var import_jsx_runtime36 = require("react/jsx-runtime");
2060
+ var ytRegex = /^.*(?:(?:youtu\.be\/|v\/|vi\/|u\/\w\/|embed\/|shorts\/)|(?:(?:watch)?\?vi?=|&vi?=))([^#&?]*).*/;
2061
+ function getYouTubeId(url) {
2062
+ const id = (url || "").match(ytRegex);
2063
+ return id ? id[1] : null;
1842
2064
  }
1843
-
1844
- // src/Annotation/VideoAnnotation/create-video-annotation.tsx
1845
- var import_jsx_runtime34 = require("react/jsx-runtime");
1846
- async function createVideoAnnotation(data, ctx) {
1847
- const annotation = {
1848
- id: ctx.generateId("annotation"),
1849
- type: "Annotation"
1850
- };
1851
- const targetType = ctx.options.targetType;
1852
- const body = await ctx.embed({
1853
- id: data.url,
2065
+ async function createYoutubeBody(data, ctx) {
2066
+ const id = getYouTubeId(data.youtubeUrl);
2067
+ const body = ctx.embed({
2068
+ id: `https://www.youtube.com/watch?v=${id}`,
1854
2069
  type: "Video",
1855
- format: "video/mp4",
1856
- width: data.width,
1857
- height: data.height,
1858
- duration: data.duration || 0
2070
+ service: [
2071
+ {
2072
+ profile: "http://digirati.com/objectifier",
2073
+ params: {
2074
+ data: `https://www.youtube.com/embed/${id}`
2075
+ }
2076
+ },
2077
+ {
2078
+ id: `https://www.youtube.com/watch?v=${id}`,
2079
+ profile: "https://www.youtube.com"
2080
+ }
2081
+ ]
1859
2082
  });
1860
- if (targetType === "Annotation") {
1861
- return ctx.embed({
1862
- ...annotation,
1863
- label: (0, import_helpers3.getValue)(data.label) && data.label,
1864
- motivation: data.motivation || ctx.options.initialData?.motivation || "painting",
1865
- body,
1866
- target: ctx.getTarget()
1867
- });
1868
- }
1869
- if (targetType === "Canvas") {
2083
+ if (ctx.options.targetType === "Canvas") {
1870
2084
  const canvasId = ctx.generateId("canvas");
1871
- const pageId = ctx.generateId("annotation-page", { id: canvasId, type: "Canvas" });
1872
- const annotationResource = ctx.embed({
1873
- ...annotation,
2085
+ const pageId = ctx.generateId("annotation-page", {
2086
+ id: canvasId,
2087
+ type: "Canvas"
2088
+ });
2089
+ const annotation = ctx.embed({
2090
+ id: ctx.generateId("annotation", { id: pageId, type: "AnnotationPage" }),
2091
+ type: "Annotation",
1874
2092
  motivation: "painting",
1875
- body,
1876
- target: { type: "SpecificResource", source: { id: canvasId, type: "Canvas" } }
2093
+ body: [body],
2094
+ target: {
2095
+ type: "SpecificResource",
2096
+ source: { id: canvasId, type: "Canvas" }
2097
+ }
1877
2098
  });
1878
2099
  const page = ctx.embed({
1879
2100
  id: pageId,
1880
2101
  type: "AnnotationPage",
1881
- items: [annotationResource]
2102
+ items: [annotation]
1882
2103
  });
1883
2104
  return ctx.embed({
1884
2105
  id: canvasId,
1885
2106
  type: "Canvas",
1886
- label: data.label || { en: ["Untitled HTML canvas"] },
1887
- duration: data.duration || 0,
1888
- width: data.width,
1889
- height: data.height,
2107
+ label: data.label || { en: ["Untitled YouTube canvas"] },
2108
+ height: data.height || 1e3,
2109
+ width: data.width || 1e3,
2110
+ duration: data.duration || 1,
1890
2111
  items: [page]
1891
2112
  });
1892
2113
  }
1893
- }
1894
- function CreateVideoAnnotationForm(props) {
1895
- const [url, setUrl] = (0, import_react8.useState)("");
1896
- const [duration, setDuration] = (0, import_react8.useState)(0);
1897
- const [width, setWidth] = (0, import_react8.useState)(0);
1898
- const [height, setHeight] = (0, import_react8.useState)(0);
1899
- const [error, setError] = (0, import_react8.useState)("");
1900
- const [label, setLabel] = (0, import_react8.useState)({ en: [""] });
1901
- const onSubmit = () => {
1902
- props.runCreate({
1903
- url,
1904
- duration,
1905
- label,
1906
- width,
1907
- height
2114
+ if (ctx.options.targetType === "Annotation") {
2115
+ return ctx.embed({
2116
+ id: ctx.generateId("annotation"),
2117
+ type: "Annotation",
2118
+ motivation: "painting",
2119
+ body: [body],
2120
+ target: ctx.getTarget()
1908
2121
  });
1909
- };
1910
- (0, import_react8.useEffect)(() => {
1911
- setError("");
1912
- setDuration(0);
1913
- if (url) {
1914
- const labelFromUrl = url.split("/").pop()?.split(".").shift();
1915
- setLabel({ en: [labelFromUrl || ""] });
2122
+ }
2123
+ return body;
2124
+ }
2125
+ function YouTubeForm(props) {
2126
+ const onSubmit = (e) => {
2127
+ e.preventDefault();
2128
+ const data = new FormData(e.target);
2129
+ const formData = Object.fromEntries(data.entries());
2130
+ if (formData.youtubeUrl) {
2131
+ props.runCreate({ youtubeUrl: formData.youtubeUrl });
1916
2132
  }
1917
- }, [url]);
1918
- return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(PaddedSidebarContainer, { children: [
1919
- error ? /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(ErrorMessage, { children: error }) : null,
1920
- /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(import_editors10.InputContainer, { children: [
1921
- /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_editors10.InputLabel, { htmlFor: "video-url", children: "URL" }),
1922
- /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_editors10.Input, { type: "text", id: "video-url", value: url, onChange: (e) => setUrl(e.target.value) })
2133
+ };
2134
+ return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(PaddedSidebarContainer, { children: /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("form", { onSubmit, children: [
2135
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(import_editors11.InputContainer, { $wide: true, children: [
2136
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_editors11.InputLabel, { htmlFor: "id", children: "Link to YouTube" }),
2137
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_editors11.Input, { id: "youtubeUrl", name: "youtubeUrl", defaultValue: "" })
1923
2138
  ] }),
1924
- url && !error ? /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
1925
- import_editors10.LanguageFieldEditor,
1926
- {
1927
- containerId: "label",
1928
- focusId: "label_",
1929
- label: "Label",
1930
- fields: label,
1931
- onSave: (e) => setLabel(e.toInternationalString())
1932
- },
1933
- url + "__" + duration
1934
- ) }) : null,
1935
- url && !error ? /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
1936
- VideoPlayer,
1937
- {
1938
- duration,
1939
- onDuration: (d) => setDuration(d),
1940
- onError: (err) => setError(err),
1941
- media: {
1942
- url,
1943
- duration,
1944
- height,
1945
- width,
1946
- type: "Video"
1947
- },
1948
- onDimensions: (w, h) => {
1949
- setWidth(w || 0);
1950
- setHeight(h || 0);
1951
- }
1952
- }
1953
- ) : null,
1954
- !error && (duration || width || height) ? /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_editors10.InputContainer, { $wide: true, children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
1955
- import_editors10.DimensionsTriplet,
1956
- {
1957
- widthId: "width",
1958
- width: width || 0,
1959
- changeWidth: (v) => setWidth(v),
1960
- heightId: "height",
1961
- height: height || 0,
1962
- changeHeight: (v) => setHeight(v),
1963
- durationId: "duration",
1964
- duration: duration || 0,
1965
- changeDuration: (v) => setDuration(v)
1966
- }
1967
- ) }) : null,
1968
- url && !error && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(Button, { onClick: onSubmit, children: "Add video" })
1969
- ] });
2139
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(Button, { type: "submit", children: "Create" })
2140
+ ] }) });
1970
2141
  }
1971
2142
 
1972
- // src/Annotation/VideoAnnotation/index.tsx
1973
- var import_components10 = require("@manifest-editor/components");
1974
- var import_jsx_runtime35 = require("react/jsx-runtime");
1975
- var videoAnnotation = {
1976
- id: "@manifest-editor/video-annotation",
1977
- create: createVideoAnnotation,
1978
- label: "Video",
1979
- summary: "Video annotation",
1980
- icon: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_components10.VideoIcon, {}),
1981
- render(ctx) {
1982
- return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(CreateVideoAnnotationForm, { ...ctx });
1983
- },
1984
- resourceType: "Annotation",
1985
- resourceFields: ["id", "type", "motivation", "body", "target"],
1986
- additionalTypes: ["Canvas"],
2143
+ // src/ContentResource/YouTubeCreator/index.tsx
2144
+ var import_jsx_runtime37 = require("react/jsx-runtime");
2145
+ var youTubeBodyCreator = {
2146
+ id: "@manifest-editor/youtube",
2147
+ create: createYoutubeBody,
2148
+ label: "YouTube",
2149
+ summary: "Embed YouTube",
2150
+ icon: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(import_components13.YouTubeIcon, {}),
2151
+ resourceType: "ContentResource",
2152
+ additionalTypes: ["Canvas", "Annotation"],
2153
+ resourceFields: ["id", "type", "service"],
2154
+ render: (ctx) => /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(YouTubeForm, { ...ctx }),
1987
2155
  supports: {
1988
- initialData: true,
1989
- parentTypes: ["AnnotationPage", "Manifest"],
1990
- parentFields: ["items"]
2156
+ parentTypes: ["Annotation", "Manifest", "AnnotationPage"],
2157
+ parentFieldMap: {
2158
+ Annotation: ["body"],
2159
+ Manifest: ["items"],
2160
+ AnnotationPage: ["items"]
2161
+ }
1991
2162
  },
1992
2163
  staticFields: {
1993
- type: "Annotation"
2164
+ type: "Video"
1994
2165
  }
1995
2166
  };
1996
2167
 
1997
2168
  // src/Manifest/ManifestBrowserCreator/manifest-browser-creator.tsx
1998
2169
  var import_helpers4 = require("@iiif/helpers");
1999
- var import_react9 = require("react");
2170
+ var import_react11 = require("react");
2000
2171
  async function createFromManifestBrowserOutput(data, ctx) {
2001
2172
  const targetType = ctx.options.targetType;
2002
2173
  if (Array.isArray(data.output)) {
@@ -2032,20 +2203,20 @@ async function createFromManifestBrowserOutput(data, ctx) {
2032
2203
  }
2033
2204
  throw new Error("Not yet supported.");
2034
2205
  }
2035
- var ManifestBrowserCreatorForm2 = (0, import_react9.lazy)(() => Promise.resolve().then(() => (init_manifest_browser_form_lazy(), manifest_browser_form_lazy_exports)));
2206
+ var ManifestBrowserCreatorForm2 = (0, import_react11.lazy)(() => Promise.resolve().then(() => (init_manifest_browser_form_lazy(), manifest_browser_form_lazy_exports)));
2036
2207
 
2037
2208
  // src/Manifest/ManifestBrowserCreator/index.tsx
2038
2209
  init_manifest_browser_form_lazy();
2039
- var import_components11 = require("@manifest-editor/components");
2040
- var import_jsx_runtime37 = require("react/jsx-runtime");
2210
+ var import_components14 = require("@manifest-editor/components");
2211
+ var import_jsx_runtime39 = require("react/jsx-runtime");
2041
2212
  var manifestBrowserCreator = {
2042
2213
  id: "@manifest-editor/manifest-browser-creator",
2043
2214
  create: createFromManifestBrowserOutput,
2044
2215
  label: "IIIF Browser",
2045
2216
  summary: "Browse IIIF Resources",
2046
- icon: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(import_components11.IIIFBrowserIcon, {}),
2217
+ icon: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(import_components14.IIIFBrowserIcon, {}),
2047
2218
  render(ctx) {
2048
- return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(ManifestBrowserCreatorForm, { ...ctx });
2219
+ return /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(ManifestBrowserCreatorForm, { ...ctx });
2049
2220
  },
2050
2221
  resourceType: "Manifest",
2051
2222
  resourceFields: ["id", "label"],
@@ -2078,5 +2249,6 @@ var allCreators = [
2078
2249
  webPageCreator,
2079
2250
  emptyAnnotationPage,
2080
2251
  noBodyAnnotation,
2081
- internalCanvas
2252
+ internalCanvas,
2253
+ captionedImageAnnotation
2082
2254
  ];