@plumix/plugin-media 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +211 -0
  3. package/dist/admin/FocalPointField.d.ts +22 -0
  4. package/dist/admin/FocalPointField.d.ts.map +1 -0
  5. package/dist/admin/FocalPointField.js +71 -0
  6. package/dist/admin/FocalPointField.js.map +1 -0
  7. package/dist/admin/MediaLibrary.d.ts +43 -0
  8. package/dist/admin/MediaLibrary.d.ts.map +1 -0
  9. package/dist/admin/MediaLibrary.js +698 -0
  10. package/dist/admin/MediaLibrary.js.map +1 -0
  11. package/dist/admin/MediaListPickerField.d.ts +14 -0
  12. package/dist/admin/MediaListPickerField.d.ts.map +1 -0
  13. package/dist/admin/MediaListPickerField.js +161 -0
  14. package/dist/admin/MediaListPickerField.js.map +1 -0
  15. package/dist/admin/MediaPickerField.d.ts +25 -0
  16. package/dist/admin/MediaPickerField.d.ts.map +1 -0
  17. package/dist/admin/MediaPickerField.js +82 -0
  18. package/dist/admin/MediaPickerField.js.map +1 -0
  19. package/dist/admin/MediaUrlField.d.ts +14 -0
  20. package/dist/admin/MediaUrlField.d.ts.map +1 -0
  21. package/dist/admin/MediaUrlField.js +29 -0
  22. package/dist/admin/MediaUrlField.js.map +1 -0
  23. package/dist/admin/index.d.ts +11 -0
  24. package/dist/admin/index.d.ts.map +1 -0
  25. package/dist/admin/index.js +49 -0
  26. package/dist/admin/index.js.map +1 -0
  27. package/dist/admin/messages.d.ts +24 -0
  28. package/dist/admin/messages.d.ts.map +1 -0
  29. package/dist/admin/messages.js +28 -0
  30. package/dist/admin/messages.js.map +1 -0
  31. package/dist/blocks/file/index.d.ts +2 -0
  32. package/dist/blocks/file/index.d.ts.map +1 -0
  33. package/dist/blocks/file/index.js +109 -0
  34. package/dist/blocks/file/index.js.map +1 -0
  35. package/dist/blocks/file/normalize.d.ts +10 -0
  36. package/dist/blocks/file/normalize.d.ts.map +1 -0
  37. package/dist/blocks/file/normalize.js +45 -0
  38. package/dist/blocks/file/normalize.js.map +1 -0
  39. package/dist/blocks/image/index.d.ts +2 -0
  40. package/dist/blocks/image/index.d.ts.map +1 -0
  41. package/dist/blocks/image/index.js +141 -0
  42. package/dist/blocks/image/index.js.map +1 -0
  43. package/dist/blocks/image/normalize.d.ts +7 -0
  44. package/dist/blocks/image/normalize.d.ts.map +1 -0
  45. package/dist/blocks/image/normalize.js +20 -0
  46. package/dist/blocks/image/normalize.js.map +1 -0
  47. package/dist/blocks.d.ts +2 -0
  48. package/dist/blocks.d.ts.map +1 -0
  49. package/dist/blocks.js +6 -0
  50. package/dist/blocks.js.map +1 -0
  51. package/dist/builder.d.ts +80 -0
  52. package/dist/builder.d.ts.map +1 -0
  53. package/dist/builder.js +61 -0
  54. package/dist/builder.js.map +1 -0
  55. package/dist/fields.d.ts +3 -0
  56. package/dist/fields.d.ts.map +1 -0
  57. package/dist/fields.js +7 -0
  58. package/dist/fields.js.map +1 -0
  59. package/dist/image-dimensions.d.ts +7 -0
  60. package/dist/image-dimensions.d.ts.map +1 -0
  61. package/dist/image-dimensions.js +142 -0
  62. package/dist/image-dimensions.js.map +1 -0
  63. package/dist/index.d.ts +62 -0
  64. package/dist/index.d.ts.map +1 -0
  65. package/dist/index.js +216 -0
  66. package/dist/index.js.map +1 -0
  67. package/dist/lookup.d.ts +43 -0
  68. package/dist/lookup.d.ts.map +1 -0
  69. package/dist/lookup.js +154 -0
  70. package/dist/lookup.js.map +1 -0
  71. package/dist/magic-bytes.d.ts +4 -0
  72. package/dist/magic-bytes.d.ts.map +1 -0
  73. package/dist/magic-bytes.js +107 -0
  74. package/dist/magic-bytes.js.map +1 -0
  75. package/dist/mcp-tools.d.ts +10 -0
  76. package/dist/mcp-tools.d.ts.map +1 -0
  77. package/dist/mcp-tools.js +43 -0
  78. package/dist/mcp-tools.js.map +1 -0
  79. package/dist/media-blocks.d.ts +3 -0
  80. package/dist/media-blocks.d.ts.map +1 -0
  81. package/dist/media-blocks.js +10 -0
  82. package/dist/media-blocks.js.map +1 -0
  83. package/dist/meta.d.ts +12 -0
  84. package/dist/meta.d.ts.map +1 -0
  85. package/dist/meta.js +20 -0
  86. package/dist/meta.js.map +1 -0
  87. package/dist/mime.d.ts +3 -0
  88. package/dist/mime.d.ts.map +1 -0
  89. package/dist/mime.js +58 -0
  90. package/dist/mime.js.map +1 -0
  91. package/dist/read-service.d.ts +66 -0
  92. package/dist/read-service.d.ts.map +1 -0
  93. package/dist/read-service.js +156 -0
  94. package/dist/read-service.js.map +1 -0
  95. package/dist/rpc.d.ts +7 -0
  96. package/dist/rpc.d.ts.map +1 -0
  97. package/dist/rpc.js +352 -0
  98. package/dist/rpc.js.map +1 -0
  99. package/dist/serve-route.d.ts +19 -0
  100. package/dist/serve-route.d.ts.map +1 -0
  101. package/dist/serve-route.js +122 -0
  102. package/dist/serve-route.js.map +1 -0
  103. package/dist/upload-route.d.ts +17 -0
  104. package/dist/upload-route.d.ts.map +1 -0
  105. package/dist/upload-route.js +106 -0
  106. package/dist/upload-route.js.map +1 -0
  107. package/package.json +97 -0
@@ -0,0 +1,109 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { defineBlock } from "plumix/blocks";
3
+ import { formatSize, normalizeFileMedia, sanitizeHref } from "./normalize.js";
4
+ // Self-contained inline styles: the admin canvas iframe doesn't load the site
5
+ // stylesheet, so the file "chip" has to carry its own layout — otherwise the
6
+ // name + meta spans collapse into one run of text ("Download0 B").
7
+ const CHIP = {
8
+ display: "inline-flex",
9
+ alignItems: "center",
10
+ gap: "0.5rem",
11
+ maxWidth: "100%",
12
+ padding: "0.625rem 0.875rem",
13
+ border: "1px solid #d0d7de",
14
+ borderRadius: "6px",
15
+ textDecoration: "none",
16
+ color: "inherit",
17
+ };
18
+ const META = { color: "#6a737d", fontSize: "0.875em" };
19
+ const PLACEHOLDER = {
20
+ display: "flex",
21
+ alignItems: "center",
22
+ minHeight: "3rem",
23
+ padding: "0.75rem 1rem",
24
+ border: "1px dashed #d0d7de",
25
+ borderRadius: "6px",
26
+ background: "#f6f8fa",
27
+ color: "#57606a",
28
+ fontSize: "0.875rem",
29
+ };
30
+ export const fileBlock = defineBlock({
31
+ name: "media/file",
32
+ title: { id: "plugin.media.block.file.title", message: "File" },
33
+ icon: "File",
34
+ category: "media",
35
+ description: {
36
+ id: "plugin.media.block.file.description",
37
+ message: "Downloadable file with size + MIME label.",
38
+ },
39
+ keywords: [
40
+ { id: "plugin.media.block.file.keyword.download", message: "download" },
41
+ { id: "plugin.media.block.file.keyword.attachment", message: "attachment" },
42
+ ],
43
+ inputs: [
44
+ // The picked library asset — a { id, url, filename, mime } snapshot the
45
+ // media picker writes. The fields below are manual escape hatches / overrides.
46
+ {
47
+ name: "media",
48
+ type: "media",
49
+ label: {
50
+ id: "plugin.media.block.file.input.media.label",
51
+ message: "File",
52
+ },
53
+ },
54
+ {
55
+ name: "href",
56
+ type: "url",
57
+ label: {
58
+ id: "plugin.media.block.file.input.href.label",
59
+ message: "Download URL",
60
+ },
61
+ },
62
+ {
63
+ name: "filename",
64
+ type: "text",
65
+ label: {
66
+ id: "plugin.media.block.file.input.filename.label",
67
+ message: "Filename",
68
+ },
69
+ },
70
+ {
71
+ name: "size",
72
+ type: "number",
73
+ label: {
74
+ id: "plugin.media.block.file.input.size.label",
75
+ message: "Size (bytes)",
76
+ },
77
+ },
78
+ {
79
+ name: "mime",
80
+ type: "text",
81
+ label: {
82
+ id: "plugin.media.block.file.input.mime.label",
83
+ message: "MIME type",
84
+ },
85
+ },
86
+ ],
87
+ defaults: { media: null, href: "", filename: "", size: 0, mime: "" },
88
+ render: ({ attrs, context }) => {
89
+ // A picked asset's url wins over the raw href escape hatch.
90
+ const media = normalizeFileMedia(attrs.media);
91
+ const href = sanitizeHref(media?.url ?? attrs.href);
92
+ if (!href) {
93
+ // No file yet: render nothing on the public page (unfinished draft); in
94
+ // the editor keep the block visible + selectable with a prompt.
95
+ if (!context.editing)
96
+ return null;
97
+ return (_jsx("div", { "data-plumix-file-placeholder": "", style: PLACEHOLDER, children: "Pick or upload a file, or paste a download URL." }));
98
+ }
99
+ const picked = media?.filename ??
100
+ (typeof attrs.filename === "string" ? attrs.filename : "");
101
+ const filename = picked.length > 0 ? picked : "Download";
102
+ const mime = media?.mime ?? (typeof attrs.mime === "string" ? attrs.mime : "");
103
+ // `size` is manual-only — the picker's snapshot carries no byte size, so
104
+ // unlike filename/mime it can't come from `media`.
105
+ const meta = [formatSize(attrs.size), mime].filter(Boolean).join(" · ");
106
+ return (_jsxs("a", { href: href, download: filename, rel: "noopener noreferrer", style: CHIP, children: [_jsx("span", { "data-plumix-file-name": "", children: filename }), meta.length > 0 ? (_jsx("span", { "data-plumix-file-meta": "", style: META, children: meta })) : null] }));
107
+ },
108
+ });
109
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/blocks/file/index.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ export declare function sanitizeHref(raw: unknown): string | undefined;
2
+ interface FileMedia {
3
+ readonly url: string;
4
+ readonly filename: string;
5
+ readonly mime: string;
6
+ }
7
+ export declare function normalizeFileMedia(raw: unknown): FileMedia | null;
8
+ export declare function formatSize(bytes: unknown): string | undefined;
9
+ export {};
10
+ //# sourceMappingURL=normalize.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"normalize.d.ts","sourceRoot":"","sources":["../../../src/blocks/file/normalize.ts"],"names":[],"mappings":"AAMA,wBAAgB,YAAY,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAK7D;AAID,UAAU,SAAS;IACjB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AACD,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,OAAO,GAAG,SAAS,GAAG,IAAI,CASjE;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAkB7D"}
@@ -0,0 +1,45 @@
1
+ // http(s), root-relative, parent-relative, mailto:, and tel: pass; the
2
+ // last two are required for contact-card "email me the file" flows.
3
+ // Everything else is silently dropped so a hostile attribute value
4
+ // never reaches the rendered anchor.
5
+ const SAFE_HREF = /^(https?:\/\/|mailto:|tel:|\/|\.\.?\/)/i;
6
+ export function sanitizeHref(raw) {
7
+ if (typeof raw !== "string")
8
+ return undefined;
9
+ const trimmed = raw.trim();
10
+ if (trimmed === "" || !SAFE_HREF.test(trimmed))
11
+ return undefined;
12
+ return trimmed;
13
+ }
14
+ export function normalizeFileMedia(raw) {
15
+ if (!raw || typeof raw !== "object")
16
+ return null;
17
+ const obj = raw;
18
+ if (typeof obj.url !== "string" || obj.url === "")
19
+ return null;
20
+ return {
21
+ url: obj.url,
22
+ filename: typeof obj.filename === "string" ? obj.filename : "",
23
+ mime: typeof obj.mime === "string" ? obj.mime : "",
24
+ };
25
+ }
26
+ export function formatSize(bytes) {
27
+ // Public-site SSR render. Decimal separator stays `.` until content
28
+ // i18n lands — the visitor-facing locale resolver is the seam where
29
+ // this should pull from. Admin-side formatting (MediaLibrary card +
30
+ // detail) uses `Intl.NumberFormat(locale, ...)` against `i18n.locale`.
31
+ // `<= 0` (not `< 0`): 0 is the unset default, so it reads as "no size"
32
+ // rather than a literal "0 B".
33
+ if (typeof bytes !== "number" || !Number.isFinite(bytes) || bytes <= 0) {
34
+ return undefined;
35
+ }
36
+ const units = ["B", "KB", "MB", "GB"];
37
+ let size = bytes;
38
+ let i = 0;
39
+ while (size >= 1024 && i < units.length - 1) {
40
+ size /= 1024;
41
+ i += 1;
42
+ }
43
+ return `${size.toFixed(size >= 100 || i === 0 ? 0 : 1)} ${units[i]}`;
44
+ }
45
+ //# sourceMappingURL=normalize.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"normalize.js","sourceRoot":"","sources":["../../../src/blocks/file/normalize.ts"],"names":[],"mappings":"AAAA,uEAAuE;AACvE,oEAAoE;AACpE,mEAAmE;AACnE,qCAAqC;AACrC,MAAM,SAAS,GAAG,yCAAyC,CAAC;AAE5D,MAAM,UAAU,YAAY,CAAC,GAAY;IACvC,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,OAAO,SAAS,CAAC;IAC9C,MAAM,OAAO,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;IAC3B,IAAI,OAAO,KAAK,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC;QAAE,OAAO,SAAS,CAAC;IACjE,OAAO,OAAO,CAAC;AACjB,CAAC;AASD,MAAM,UAAU,kBAAkB,CAAC,GAAY;IAC7C,IAAI,CAAC,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IACjD,MAAM,GAAG,GAAG,GAA8B,CAAC;IAC3C,IAAI,OAAO,GAAG,CAAC,GAAG,KAAK,QAAQ,IAAI,GAAG,CAAC,GAAG,KAAK,EAAE;QAAE,OAAO,IAAI,CAAC;IAC/D,OAAO;QACL,GAAG,EAAE,GAAG,CAAC,GAAG;QACZ,QAAQ,EAAE,OAAO,GAAG,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE;QAC9D,IAAI,EAAE,OAAO,GAAG,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE;KACnD,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,KAAc;IACvC,oEAAoE;IACpE,oEAAoE;IACpE,oEAAoE;IACpE,uEAAuE;IACvE,uEAAuE;IACvE,+BAA+B;IAC/B,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,EAAE,CAAC;QACvE,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,MAAM,KAAK,GAAG,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;IACtC,IAAI,IAAI,GAAG,KAAK,CAAC;IACjB,IAAI,CAAC,GAAG,CAAC,CAAC;IACV,OAAO,IAAI,IAAI,IAAI,IAAI,CAAC,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC5C,IAAI,IAAI,IAAI,CAAC;QACb,CAAC,IAAI,CAAC,CAAC;IACT,CAAC;IACD,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;AACvE,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare const imageBlock: import("@plumix/blocks").BlockSpec<Readonly<Record<string, unknown>>, Readonly<Record<string, import("@plumix/blocks").BlockLoaderFn>>>;
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/blocks/image/index.tsx"],"names":[],"mappings":"AA4BA,eAAO,MAAM,UAAU,yIA2JrB,CAAC"}
@@ -0,0 +1,141 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { defineBlock } from "plumix/blocks";
3
+ import { Image } from "plumix/blocks/renderer";
4
+ import { normalizeFocalPoint } from "./normalize.js";
5
+ // The media picker writes a { id, url, alt, width, height } snapshot. Read just
6
+ // what render needs, tolerating a null/legacy value. A missing/null asset alt
7
+ // projects to "" here (the picker stores alt as string | null).
8
+ function normalizeMediaValue(raw) {
9
+ if (!raw || typeof raw !== "object")
10
+ return null;
11
+ const obj = raw;
12
+ if (typeof obj.url !== "string" || obj.url === "")
13
+ return null;
14
+ return {
15
+ url: obj.url,
16
+ alt: typeof obj.alt === "string" ? obj.alt : "",
17
+ width: typeof obj.width === "number" ? obj.width : null,
18
+ height: typeof obj.height === "number" ? obj.height : null,
19
+ };
20
+ }
21
+ export const imageBlock = defineBlock({
22
+ name: "media/image",
23
+ title: { id: "plugin.media.block.image.title", message: "Image" },
24
+ icon: "Image",
25
+ category: "media",
26
+ description: {
27
+ id: "plugin.media.block.image.description",
28
+ message: "Image with alt text, caption, and focal-point cropping.",
29
+ },
30
+ keywords: [
31
+ { id: "plugin.media.block.image.keyword.picture", message: "picture" },
32
+ { id: "plugin.media.block.image.keyword.photo", message: "photo" },
33
+ { id: "plugin.media.block.image.keyword.media", message: "media" },
34
+ ],
35
+ inputs: [
36
+ // The picked library asset — a snapshot { id, url, alt, width, height }
37
+ // written by the media picker. Render prefers it; `src` below is the escape
38
+ // hatch for an unmanaged external URL.
39
+ {
40
+ name: "media",
41
+ type: "media",
42
+ label: {
43
+ id: "plugin.media.block.image.input.media.label",
44
+ message: "Image",
45
+ },
46
+ accept: "image/",
47
+ },
48
+ {
49
+ name: "src",
50
+ type: "url",
51
+ label: {
52
+ id: "plugin.media.block.image.input.src.label",
53
+ message: "Source URL",
54
+ },
55
+ },
56
+ {
57
+ name: "alt",
58
+ type: "text",
59
+ label: {
60
+ id: "plugin.media.block.image.input.alt.label",
61
+ message: "Alternative text",
62
+ },
63
+ },
64
+ {
65
+ name: "caption",
66
+ type: "text",
67
+ label: {
68
+ id: "plugin.media.block.image.input.caption.label",
69
+ message: "Caption",
70
+ },
71
+ },
72
+ {
73
+ // An on/off setting → a Switch, consistent with the Styles tab's
74
+ // visibility toggles (the repo's on/off convention).
75
+ name: "priority",
76
+ type: "boolean",
77
+ label: {
78
+ id: "plugin.media.block.image.input.priority.label",
79
+ message: "High priority (load eagerly)",
80
+ },
81
+ },
82
+ // A visual crop anchor: click/drag a dot on the image preview. `sizes` and
83
+ // display `width` are deliberately not authored here — `sizes` is a
84
+ // dev-only responsive hint the renderer defaults sensibly, and width lives
85
+ // in the Styles tab's Size section (both would edit `node.style.width`).
86
+ {
87
+ name: "focalPoint",
88
+ type: "focalPoint",
89
+ label: {
90
+ id: "plugin.media.block.image.input.focalPoint.label",
91
+ message: "Focal point",
92
+ },
93
+ },
94
+ ],
95
+ defaults: {
96
+ media: null,
97
+ src: "",
98
+ alt: "",
99
+ caption: "",
100
+ priority: false,
101
+ focalPoint: { x: 0.5, y: 0.5 },
102
+ },
103
+ render: ({ attrs, context }) => {
104
+ // A picked library asset snapshots its url/alt; prefer it over the raw
105
+ // `src` escape hatch so a managed image survives a stale typed URL.
106
+ const media = normalizeMediaValue(attrs.media);
107
+ const rawSrc = typeof attrs.src === "string" ? attrs.src : "";
108
+ // `media.url` is guaranteed non-empty when present (normalizeMediaValue
109
+ // drops a blank url), so ?? cleanly falls through to the raw src.
110
+ const src = media?.url ?? rawSrc;
111
+ const caption = typeof attrs.caption === "string" ? attrs.caption : "";
112
+ // Empty source: show a placeholder in the editor so the block stays visible
113
+ // and selectable; render nothing on the public page (an empty image block is
114
+ // an unfinished draft, not content).
115
+ if (src === "") {
116
+ if (!context.editing)
117
+ return null;
118
+ return (_jsxs("figure", { "data-plumix-image-placeholder": "", style: { margin: 0 }, children: [_jsx("span", { "data-plumix-image-placeholder-icon": "", "aria-hidden": "true" }), _jsx("span", { children: "No image" })] }));
119
+ }
120
+ // Block alt overrides the asset's alt; an empty block alt falls back to the
121
+ // snapshot's (?? won't do this — "" is a set-but-empty override to skip).
122
+ const blockAlt = typeof attrs.alt === "string" ? attrs.alt : "";
123
+ const alt = blockAlt !== "" ? blockAlt : (media?.alt ?? "");
124
+ const sizes = typeof attrs.sizes === "string" ? attrs.sizes : undefined;
125
+ const priority = attrs.priority === true;
126
+ const focal = normalizeFocalPoint(attrs.focalPoint);
127
+ // The style-bound `width` lands on the block wrapper; the img caps to that
128
+ // box (`display:block; max-width:100%`) so setting width constrains the
129
+ // image down to the chosen width. `height:auto` holds aspect.
130
+ const style = {
131
+ display: "block",
132
+ maxWidth: "100%",
133
+ height: "auto",
134
+ ...(focal && {
135
+ objectPosition: `${(focal.x * 100).toFixed(0)}% ${(focal.y * 100).toFixed(0)}%`,
136
+ }),
137
+ };
138
+ return (_jsxs("figure", { style: { margin: 0 }, children: [media?.width != null && media.height != null ? (_jsx(Image, { src: src, alt: alt, width: media.width, height: media.height, sizes: sizes, priority: priority, style: style })) : (_jsx("img", { src: src, alt: alt, loading: priority ? "eager" : "lazy", decoding: priority ? "sync" : "async", ...(priority && { fetchPriority: "high" }), style: style })), caption.length > 0 ? _jsx("figcaption", { children: caption }) : null] }));
139
+ },
140
+ });
141
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/blocks/image/index.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ interface FocalPoint {
2
+ readonly x: number;
3
+ readonly y: number;
4
+ }
5
+ export declare function normalizeFocalPoint(raw: unknown): FocalPoint | undefined;
6
+ export {};
7
+ //# sourceMappingURL=normalize.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"normalize.d.ts","sourceRoot":"","sources":["../../../src/blocks/image/normalize.ts"],"names":[],"mappings":"AAAA,UAAU,UAAU;IAClB,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;CACpB;AAQD,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,OAAO,GAAG,UAAU,GAAG,SAAS,CAYxE"}
@@ -0,0 +1,20 @@
1
+ function clamp01(n) {
2
+ if (n < 0)
3
+ return 0;
4
+ if (n > 1)
5
+ return 1;
6
+ return n;
7
+ }
8
+ export function normalizeFocalPoint(raw) {
9
+ if (typeof raw !== "object" || raw === null)
10
+ return undefined;
11
+ const { x, y } = raw;
12
+ if (typeof x !== "number" ||
13
+ typeof y !== "number" ||
14
+ !Number.isFinite(x) ||
15
+ !Number.isFinite(y)) {
16
+ return undefined;
17
+ }
18
+ return { x: clamp01(x), y: clamp01(y) };
19
+ }
20
+ //# sourceMappingURL=normalize.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"normalize.js","sourceRoot":"","sources":["../../../src/blocks/image/normalize.ts"],"names":[],"mappings":"AAKA,SAAS,OAAO,CAAC,CAAS;IACxB,IAAI,CAAC,GAAG,CAAC;QAAE,OAAO,CAAC,CAAC;IACpB,IAAI,CAAC,GAAG,CAAC;QAAE,OAAO,CAAC,CAAC;IACpB,OAAO,CAAC,CAAC;AACX,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,GAAY;IAC9C,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI;QAAE,OAAO,SAAS,CAAC;IAC9D,MAAM,EAAE,CAAC,EAAE,CAAC,EAAE,GAAG,GAAmC,CAAC;IACrD,IACE,OAAO,CAAC,KAAK,QAAQ;QACrB,OAAO,CAAC,KAAK,QAAQ;QACrB,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;QACnB,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,EACnB,CAAC;QACD,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,OAAO,EAAE,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;AAC1C,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { mediaBlocks as default } from "./media-blocks.js";
2
+ //# sourceMappingURL=blocks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blocks.d.ts","sourceRoot":"","sources":["../src/blocks.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,WAAW,IAAI,OAAO,EAAE,MAAM,mBAAmB,CAAC"}
package/dist/blocks.js ADDED
@@ -0,0 +1,6 @@
1
+ // The `editorBlocksModule` contract (a default-exported `BlockSpec[]`) that the
2
+ // plumix vite plugin imports into the generated editor entry so the canvas can
3
+ // render media blocks. Kept as a dedicated re-export so `media-blocks.ts` has a
4
+ // single named export (no named+default duplicate).
5
+ export { mediaBlocks as default } from "./media-blocks.js";
6
+ //# sourceMappingURL=blocks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blocks.js","sourceRoot":"","sources":["../src/blocks.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,+EAA+E;AAC/E,gFAAgF;AAChF,oDAAoD;AACpD,OAAO,EAAE,WAAW,IAAI,OAAO,EAAE,MAAM,mBAAmB,CAAC"}
@@ -0,0 +1,80 @@
1
+ import type { MediaListMetaBoxField, MediaMetaBoxField, MetaBoxFieldSpan } from "plumix/plugin";
2
+ /**
3
+ * Per-field options for the `media()` builder.
4
+ *
5
+ * `accept` filters the picker grid client-side (UX hint) AND the
6
+ * `mediaLookupAdapter`'s `list({ ids })` path on the server (security
7
+ * boundary — re-validated at write time). Format: a single MIME prefix
8
+ * string (`"image/"` matches every `image/*` mime) OR a readonly array
9
+ * of exact MIME matches (`["image/png", "application/pdf"]`).
10
+ *
11
+ * `default` is a `MediaValue` shape (`{ id, ... }`) — the meta pipeline
12
+ * normalizes the cached fields on every write, so a new entry with a
13
+ * `default: { id: "42" }` ships with the canonical `{ id, mime,
14
+ * filename }` after the first save.
15
+ */
16
+ export interface MediaFieldOptions {
17
+ readonly key: string;
18
+ readonly label: string;
19
+ readonly required?: boolean;
20
+ readonly description?: string;
21
+ readonly default?: MediaValue;
22
+ readonly span?: MetaBoxFieldSpan;
23
+ readonly accept?: string | readonly string[];
24
+ }
25
+ /**
26
+ * Storage shape for a single `media` field. The meta pipeline writes
27
+ * `{ id, ...adapterCached }` on every save (mime + filename today;
28
+ * width/height/etc once the upload pipeline captures them). Renders
29
+ * read straight from this object — no `lookup.resolve` per field.
30
+ */
31
+ export interface MediaValue {
32
+ readonly id: string;
33
+ readonly mime?: string;
34
+ readonly filename?: string;
35
+ }
36
+ /**
37
+ * Build a typed `media` reference field. Stored as a `MediaValue`
38
+ * object — admin thumbnails render without a resolve round-trip, the
39
+ * meta pipeline rewrites the cached fields on every write so the
40
+ * snapshot stays close to the asset.
41
+ *
42
+ * The picker opens the existing Media Library in modal/picker mode,
43
+ * filters the grid to MIME `accept` if set, and emits the bare id +
44
+ * cached fields back to the form on selection.
45
+ *
46
+ * Single-value only — `mediaList()` covers the multi case (slice
47
+ * #132).
48
+ */
49
+ export declare function media(options: MediaFieldOptions): MediaMetaBoxField;
50
+ /**
51
+ * Per-field options for the `mediaList()` builder. Multi-value
52
+ * counterpart to `media()` — same `accept` semantics, plus a `max`
53
+ * length cap. Storage is `MediaValue[]`.
54
+ */
55
+ export interface MediaListFieldOptions {
56
+ readonly key: string;
57
+ readonly label: string;
58
+ readonly required?: boolean;
59
+ readonly description?: string;
60
+ readonly default?: readonly MediaValue[];
61
+ readonly span?: MetaBoxFieldSpan;
62
+ readonly accept?: string | readonly string[];
63
+ /** Max items allowed in the array. Omitted = unbounded. */
64
+ readonly max?: number;
65
+ }
66
+ /**
67
+ * Build a typed `mediaList` reference field — the multi-value
68
+ * counterpart to `media()`. Storage is `MediaValue[]` —
69
+ * `[{ id, mime?, filename? }, ...]`. The meta pipeline rewrites
70
+ * each entry's cached fields on every write so reads can render
71
+ * thumbnails without a per-item resolve round-trip.
72
+ *
73
+ * Picker stays open across selections (so authors can pick several
74
+ * without re-opening) and auto-stops when `max` is reached.
75
+ * Selected items render as a vertical list with up/down reorder
76
+ * and per-item removal; drag-reorder is deferred to a follow-up
77
+ * (see `MediaListPickerField`).
78
+ */
79
+ export declare function mediaList(options: MediaListFieldOptions): MediaListMetaBoxField;
80
+ //# sourceMappingURL=builder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"builder.d.ts","sourceRoot":"","sources":["../src/builder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,qBAAqB,EACrB,iBAAiB,EACjB,gBAAgB,EACjB,MAAM,eAAe,CAAC;AAIvB;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,OAAO,CAAC,EAAE,UAAU,CAAC;IAC9B,QAAQ,CAAC,IAAI,CAAC,EAAE,gBAAgB,CAAC;IACjC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAAC;CAC9C;AAED;;;;;GAKG;AACH,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,KAAK,CAAC,OAAO,EAAE,iBAAiB,GAAG,iBAAiB,CAanE;AAED;;;;GAIG;AACH,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,UAAU,EAAE,CAAC;IACzC,QAAQ,CAAC,IAAI,CAAC,EAAE,gBAAgB,CAAC;IACjC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAAC;IAC7C,2DAA2D;IAC3D,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,SAAS,CACvB,OAAO,EAAE,qBAAqB,GAC7B,qBAAqB,CAmBvB"}
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Build a typed `media` reference field. Stored as a `MediaValue`
3
+ * object — admin thumbnails render without a resolve round-trip, the
4
+ * meta pipeline rewrites the cached fields on every write so the
5
+ * snapshot stays close to the asset.
6
+ *
7
+ * The picker opens the existing Media Library in modal/picker mode,
8
+ * filters the grid to MIME `accept` if set, and emits the bare id +
9
+ * cached fields back to the form on selection.
10
+ *
11
+ * Single-value only — `mediaList()` covers the multi case (slice
12
+ * #132).
13
+ */
14
+ export function media(options) {
15
+ const scope = { accept: options.accept };
16
+ return {
17
+ key: options.key,
18
+ label: options.label,
19
+ type: "json",
20
+ inputType: "media",
21
+ referenceTarget: { kind: "media", scope, valueShape: "object" },
22
+ required: options.required,
23
+ description: options.description,
24
+ default: options.default,
25
+ span: options.span,
26
+ };
27
+ }
28
+ /**
29
+ * Build a typed `mediaList` reference field — the multi-value
30
+ * counterpart to `media()`. Storage is `MediaValue[]` —
31
+ * `[{ id, mime?, filename? }, ...]`. The meta pipeline rewrites
32
+ * each entry's cached fields on every write so reads can render
33
+ * thumbnails without a per-item resolve round-trip.
34
+ *
35
+ * Picker stays open across selections (so authors can pick several
36
+ * without re-opening) and auto-stops when `max` is reached.
37
+ * Selected items render as a vertical list with up/down reorder
38
+ * and per-item removal; drag-reorder is deferred to a follow-up
39
+ * (see `MediaListPickerField`).
40
+ */
41
+ export function mediaList(options) {
42
+ const scope = { accept: options.accept };
43
+ return {
44
+ key: options.key,
45
+ label: options.label,
46
+ type: "json",
47
+ inputType: "mediaList",
48
+ referenceTarget: {
49
+ kind: "media",
50
+ scope,
51
+ valueShape: "object",
52
+ multiple: true,
53
+ },
54
+ max: options.max,
55
+ required: options.required,
56
+ description: options.description,
57
+ default: options.default,
58
+ span: options.span,
59
+ };
60
+ }
61
+ //# sourceMappingURL=builder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"builder.js","sourceRoot":"","sources":["../src/builder.ts"],"names":[],"mappings":"AA4CA;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,KAAK,CAAC,OAA0B;IAC9C,MAAM,KAAK,GAAoB,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC;IAC1D,OAAO;QACL,GAAG,EAAE,OAAO,CAAC,GAAG;QAChB,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,IAAI,EAAE,MAAM;QACZ,SAAS,EAAE,OAAO;QAClB,eAAe,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE;QAC/D,QAAQ,EAAE,OAAO,CAAC,QAAQ;QAC1B,WAAW,EAAE,OAAO,CAAC,WAAW;QAChC,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,IAAI,EAAE,OAAO,CAAC,IAAI;KACnB,CAAC;AACJ,CAAC;AAmBD;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,SAAS,CACvB,OAA8B;IAE9B,MAAM,KAAK,GAAoB,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC;IAC1D,OAAO;QACL,GAAG,EAAE,OAAO,CAAC,GAAG;QAChB,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,IAAI,EAAE,MAAM;QACZ,SAAS,EAAE,WAAW;QACtB,eAAe,EAAE;YACf,IAAI,EAAE,OAAO;YACb,KAAK;YACL,UAAU,EAAE,QAAQ;YACpB,QAAQ,EAAE,IAAI;SACf;QACD,GAAG,EAAE,OAAO,CAAC,GAAG;QAChB,QAAQ,EAAE,OAAO,CAAC,QAAQ;QAC1B,WAAW,EAAE,OAAO,CAAC,WAAW;QAChC,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,IAAI,EAAE,OAAO,CAAC,IAAI;KACnB,CAAC;AACJ,CAAC"}
@@ -0,0 +1,3 @@
1
+ export { media, mediaList } from "./builder.js";
2
+ export type { MediaFieldOptions, MediaListFieldOptions, MediaValue, } from "./builder.js";
3
+ //# sourceMappingURL=fields.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fields.d.ts","sourceRoot":"","sources":["../src/fields.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAChD,YAAY,EACV,iBAAiB,EACjB,qBAAqB,EACrB,UAAU,GACX,MAAM,cAAc,CAAC"}
package/dist/fields.js ADDED
@@ -0,0 +1,7 @@
1
+ // Public field-builder surface. Imported via `@plumix/plugin-media/fields`
2
+ // so consumers can pull the typed `media()` builder without colliding
3
+ // with `media()` (the plugin descriptor factory) exported from the
4
+ // package root. Mirrors the `plumix/fields` subpath convention from
5
+ // core.
6
+ export { media, mediaList } from "./builder.js";
7
+ //# sourceMappingURL=fields.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fields.js","sourceRoot":"","sources":["../src/fields.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,sEAAsE;AACtE,mEAAmE;AACnE,oEAAoE;AACpE,QAAQ;AAER,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC"}
@@ -0,0 +1,7 @@
1
+ export interface ImageDimensions {
2
+ readonly width: number;
3
+ readonly height: number;
4
+ }
5
+ export declare const DIMENSION_SAMPLE_SIZE = 65536;
6
+ export declare function readImageDimensions(bytes: Uint8Array, mime: string): ImageDimensions | null;
7
+ //# sourceMappingURL=image-dimensions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"image-dimensions.d.ts","sourceRoot":"","sources":["../src/image-dimensions.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAKD,eAAO,MAAM,qBAAqB,QAAQ,CAAC;AAwH3C,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,UAAU,EACjB,IAAI,EAAE,MAAM,GACX,eAAe,GAAG,IAAI,CAgBxB"}