@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.
- package/LICENSE +21 -0
- package/README.md +211 -0
- package/dist/admin/FocalPointField.d.ts +22 -0
- package/dist/admin/FocalPointField.d.ts.map +1 -0
- package/dist/admin/FocalPointField.js +71 -0
- package/dist/admin/FocalPointField.js.map +1 -0
- package/dist/admin/MediaLibrary.d.ts +43 -0
- package/dist/admin/MediaLibrary.d.ts.map +1 -0
- package/dist/admin/MediaLibrary.js +698 -0
- package/dist/admin/MediaLibrary.js.map +1 -0
- package/dist/admin/MediaListPickerField.d.ts +14 -0
- package/dist/admin/MediaListPickerField.d.ts.map +1 -0
- package/dist/admin/MediaListPickerField.js +161 -0
- package/dist/admin/MediaListPickerField.js.map +1 -0
- package/dist/admin/MediaPickerField.d.ts +25 -0
- package/dist/admin/MediaPickerField.d.ts.map +1 -0
- package/dist/admin/MediaPickerField.js +82 -0
- package/dist/admin/MediaPickerField.js.map +1 -0
- package/dist/admin/MediaUrlField.d.ts +14 -0
- package/dist/admin/MediaUrlField.d.ts.map +1 -0
- package/dist/admin/MediaUrlField.js +29 -0
- package/dist/admin/MediaUrlField.js.map +1 -0
- package/dist/admin/index.d.ts +11 -0
- package/dist/admin/index.d.ts.map +1 -0
- package/dist/admin/index.js +49 -0
- package/dist/admin/index.js.map +1 -0
- package/dist/admin/messages.d.ts +24 -0
- package/dist/admin/messages.d.ts.map +1 -0
- package/dist/admin/messages.js +28 -0
- package/dist/admin/messages.js.map +1 -0
- package/dist/blocks/file/index.d.ts +2 -0
- package/dist/blocks/file/index.d.ts.map +1 -0
- package/dist/blocks/file/index.js +109 -0
- package/dist/blocks/file/index.js.map +1 -0
- package/dist/blocks/file/normalize.d.ts +10 -0
- package/dist/blocks/file/normalize.d.ts.map +1 -0
- package/dist/blocks/file/normalize.js +45 -0
- package/dist/blocks/file/normalize.js.map +1 -0
- package/dist/blocks/image/index.d.ts +2 -0
- package/dist/blocks/image/index.d.ts.map +1 -0
- package/dist/blocks/image/index.js +141 -0
- package/dist/blocks/image/index.js.map +1 -0
- package/dist/blocks/image/normalize.d.ts +7 -0
- package/dist/blocks/image/normalize.d.ts.map +1 -0
- package/dist/blocks/image/normalize.js +20 -0
- package/dist/blocks/image/normalize.js.map +1 -0
- package/dist/blocks.d.ts +2 -0
- package/dist/blocks.d.ts.map +1 -0
- package/dist/blocks.js +6 -0
- package/dist/blocks.js.map +1 -0
- package/dist/builder.d.ts +80 -0
- package/dist/builder.d.ts.map +1 -0
- package/dist/builder.js +61 -0
- package/dist/builder.js.map +1 -0
- package/dist/fields.d.ts +3 -0
- package/dist/fields.d.ts.map +1 -0
- package/dist/fields.js +7 -0
- package/dist/fields.js.map +1 -0
- package/dist/image-dimensions.d.ts +7 -0
- package/dist/image-dimensions.d.ts.map +1 -0
- package/dist/image-dimensions.js +142 -0
- package/dist/image-dimensions.js.map +1 -0
- package/dist/index.d.ts +62 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +216 -0
- package/dist/index.js.map +1 -0
- package/dist/lookup.d.ts +43 -0
- package/dist/lookup.d.ts.map +1 -0
- package/dist/lookup.js +154 -0
- package/dist/lookup.js.map +1 -0
- package/dist/magic-bytes.d.ts +4 -0
- package/dist/magic-bytes.d.ts.map +1 -0
- package/dist/magic-bytes.js +107 -0
- package/dist/magic-bytes.js.map +1 -0
- package/dist/mcp-tools.d.ts +10 -0
- package/dist/mcp-tools.d.ts.map +1 -0
- package/dist/mcp-tools.js +43 -0
- package/dist/mcp-tools.js.map +1 -0
- package/dist/media-blocks.d.ts +3 -0
- package/dist/media-blocks.d.ts.map +1 -0
- package/dist/media-blocks.js +10 -0
- package/dist/media-blocks.js.map +1 -0
- package/dist/meta.d.ts +12 -0
- package/dist/meta.d.ts.map +1 -0
- package/dist/meta.js +20 -0
- package/dist/meta.js.map +1 -0
- package/dist/mime.d.ts +3 -0
- package/dist/mime.d.ts.map +1 -0
- package/dist/mime.js +58 -0
- package/dist/mime.js.map +1 -0
- package/dist/read-service.d.ts +66 -0
- package/dist/read-service.d.ts.map +1 -0
- package/dist/read-service.js +156 -0
- package/dist/read-service.js.map +1 -0
- package/dist/rpc.d.ts +7 -0
- package/dist/rpc.d.ts.map +1 -0
- package/dist/rpc.js +352 -0
- package/dist/rpc.js.map +1 -0
- package/dist/serve-route.d.ts +19 -0
- package/dist/serve-route.d.ts.map +1 -0
- package/dist/serve-route.js +122 -0
- package/dist/serve-route.js.map +1 -0
- package/dist/upload-route.d.ts +17 -0
- package/dist/upload-route.d.ts.map +1 -0
- package/dist/upload-route.js +106 -0
- package/dist/upload-route.js.map +1 -0
- 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 @@
|
|
|
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 @@
|
|
|
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"}
|
package/dist/blocks.d.ts
ADDED
|
@@ -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"}
|
package/dist/builder.js
ADDED
|
@@ -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"}
|
package/dist/fields.d.ts
ADDED
|
@@ -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"}
|