@manifest-editor/creators 1.0.2

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