@manifest-editor/creators 1.2.0 → 1.3.1

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