@jay-framework/wix-media 0.19.6 → 0.20.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/agent-kit/designer/wix-media.md +17 -0
- package/dist/index.d.ts +40 -1
- package/dist/index.js +164 -6
- package/package.json +12 -11
|
@@ -28,11 +28,28 @@ URL format: `https://static.wixstatic.com/media/{mediaId}/v1/{mode}/{params}/fil
|
|
|
28
28
|
- **crop** — extract a rectangle at specific coordinates
|
|
29
29
|
`<img src="https://static.wixstatic.com/media/{mediaId}/v1/crop/x_100,y_50,w_800,h_600/file.jpg" alt="" />`
|
|
30
30
|
|
|
31
|
+
#### Crop + Resize (chained transforms)
|
|
32
|
+
|
|
33
|
+
Add a `/crop/` segment before `/fill/` (or `/fit/`) to first extract a region, then resize it:
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
/v1/crop/x_{x},y_{y},w_{cropW},h_{cropH}/fill/w_{outputW},h_{outputH}/file.ext
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Example — crop a 2688×1278 region starting at (0, 514), then fill-resize to 1437×683:
|
|
40
|
+
|
|
41
|
+
```html
|
|
42
|
+
<img src="https://static.wixstatic.com/media/{mediaId}/v1/crop/x_0,y_514,w_2688,h_1278/fill/w_1437,h_683/file.jpg" alt="" />
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
The crop coordinates refer to the original image pixels. The fill/fit parameters set the final output size.
|
|
46
|
+
|
|
31
47
|
#### Parameters
|
|
32
48
|
|
|
33
49
|
- `w_{width}` — target width (1–5000 px)
|
|
34
50
|
- `h_{height}` — target height (1–5000 px)
|
|
35
51
|
- `x_{x},y_{y}` — crop start position (crop mode only)
|
|
52
|
+
- `w_{width},h_{height}` — crop region size (crop mode / chained crop)
|
|
36
53
|
- `q_{quality}` — JPEG quality (1–100)
|
|
37
54
|
|
|
38
55
|
#### Output format (set via file extension)
|
package/dist/index.d.ts
CHANGED
|
@@ -16,9 +16,48 @@ interface MediaFileInfo {
|
|
|
16
16
|
folderId: string;
|
|
17
17
|
folderName: string;
|
|
18
18
|
}
|
|
19
|
+
interface UploadUrlResult {
|
|
20
|
+
uploadUrl: string;
|
|
21
|
+
}
|
|
22
|
+
interface WixMediaService {
|
|
23
|
+
listPublicFiles(): Promise<MediaFileInfo[]>;
|
|
24
|
+
generateUploadUrl(mimeType: string, fileName: string): Promise<UploadUrlResult>;
|
|
25
|
+
}
|
|
19
26
|
|
|
20
27
|
declare function generateMediaIndex(files: MediaFileInfo[]): string;
|
|
21
28
|
|
|
29
|
+
/**
|
|
30
|
+
* Generate Add Menu items for indexed Wix Media Manager files (Design Log #19 M19.2).
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
interface MediaAddMenuInteraction {
|
|
34
|
+
mode: 'stage-place' | 'reference';
|
|
35
|
+
persistOnPage?: boolean;
|
|
36
|
+
stagePromptTemplate?: string;
|
|
37
|
+
}
|
|
38
|
+
interface MediaAddMenuItem {
|
|
39
|
+
id: string;
|
|
40
|
+
title: string;
|
|
41
|
+
category: string;
|
|
42
|
+
subCategory: string;
|
|
43
|
+
pluginName: string;
|
|
44
|
+
packageName: string;
|
|
45
|
+
thumbnail?: string;
|
|
46
|
+
prompt: string;
|
|
47
|
+
interaction?: MediaAddMenuInteraction;
|
|
48
|
+
}
|
|
49
|
+
/** Preview URL for picker chips — raster images, vectors, and SVGs served from Wix CDN. */
|
|
50
|
+
declare function thumbnailUrlForMedia(file: MediaFileInfo): string | undefined;
|
|
51
|
+
declare function buildMediaAddMenuItems(files: MediaFileInfo[]): MediaAddMenuItem[];
|
|
52
|
+
|
|
53
|
+
declare const ADD_MENU_GENERATED_REL = "agent-kit/aiditor/add-menu/wix-media.generated.yaml";
|
|
54
|
+
declare function writeGeneratedAddMenuCatalog(projectRoot: string, items: MediaAddMenuItem[]): string;
|
|
55
|
+
|
|
56
|
+
declare function refreshMediaAddMenuCatalog(projectRoot: string, mediaService: WixMediaService): Promise<{
|
|
57
|
+
itemCount: number;
|
|
58
|
+
outputRel: string;
|
|
59
|
+
}>;
|
|
60
|
+
|
|
22
61
|
declare function setupWixMedia(ctx: PluginSetupContext): Promise<PluginSetupResult>;
|
|
23
62
|
declare function generateWixMediaReferences(ctx: PluginReferencesContext): Promise<PluginReferencesResult>;
|
|
24
63
|
|
|
@@ -32,4 +71,4 @@ declare const uploadPublic: _jay_framework_fullstack_component.JayCliCommand<{
|
|
|
32
71
|
dryRun?: boolean;
|
|
33
72
|
}, [_jay_framework_fullstack_component.ConsoleContext]>;
|
|
34
73
|
|
|
35
|
-
export { generateMediaIndex, generateWixMediaReferences, rebuildIndex, setupWixMedia, uploadPublic, validate };
|
|
74
|
+
export { ADD_MENU_GENERATED_REL, buildMediaAddMenuItems, generateMediaIndex, generateWixMediaReferences, rebuildIndex, refreshMediaAddMenuCatalog, setupWixMedia, thumbnailUrlForMedia, uploadPublic, validate, writeGeneratedAddMenuCatalog };
|
package/dist/index.js
CHANGED
|
@@ -2,6 +2,7 @@ import { walkElements, resolveBinding } from "@jay-framework/compiler-shared";
|
|
|
2
2
|
import { parseTemplateParts } from "@jay-framework/compiler-jay-html";
|
|
3
3
|
import * as fs from "fs";
|
|
4
4
|
import * as path from "path";
|
|
5
|
+
import * as yaml from "js-yaml";
|
|
5
6
|
import { registerService, getService } from "@jay-framework/stack-server-runtime";
|
|
6
7
|
import { WIX_CLIENT_SERVICE } from "@jay-framework/wix-server-client";
|
|
7
8
|
import { files, folders } from "@wix/media";
|
|
@@ -12,6 +13,8 @@ const WIXSTATIC_MEDIA_RE = /static\.wixstatic\.com\/media\//;
|
|
|
12
13
|
const V1_TRANSFORM_RE = /\/v1\//;
|
|
13
14
|
const IMAGE_EXTENSIONS_RE = /\.(jpe?g|png|gif|webp|svg|bmp|ico)$/i;
|
|
14
15
|
const SRCSET_UNENCODED_COMMA_RE = /\/v1\/[^/]+\/[^/]*[a-z]_\d+,[a-z]_\d+/;
|
|
16
|
+
const WIDTH_PARAM_RE = /w_(\d+)/;
|
|
17
|
+
const SRCSET_MIN_WIDTH_THRESHOLD = 400;
|
|
15
18
|
const MEDIA_SRC_ATTRS = ["src", "poster"];
|
|
16
19
|
const MEDIA_SRCSET_ATTRS = ["srcset"];
|
|
17
20
|
const MEDIA_ELEMENTS = /* @__PURE__ */ new Set(["img", "video", "source"]);
|
|
@@ -80,6 +83,24 @@ const validate = (ctx) => {
|
|
|
80
83
|
}
|
|
81
84
|
}
|
|
82
85
|
}
|
|
86
|
+
if (tagName === "img" && !el.getAttribute("srcset")) {
|
|
87
|
+
const srcValue = el.getAttribute("src");
|
|
88
|
+
if (srcValue) {
|
|
89
|
+
const srcParts = parseTemplateParts(srcValue);
|
|
90
|
+
const staticText = srcParts.filter((p) => p.kind === "static").map((p) => p.value).join("");
|
|
91
|
+
if (V1_TRANSFORM_RE.test(staticText)) {
|
|
92
|
+
const widthMatch = staticText.match(WIDTH_PARAM_RE);
|
|
93
|
+
if (widthMatch && parseInt(widthMatch[1], 10) >= SRCSET_MIN_WIDTH_THRESHOLD) {
|
|
94
|
+
findings.push({
|
|
95
|
+
severity: "warning",
|
|
96
|
+
message: "This image is ≥400px wide but has no srcset attribute. Add srcset with sizes for common breakpoints to serve appropriately sized images across devices. See agent-kit/wix-media.md for the responsive image pattern.",
|
|
97
|
+
element: "<img>",
|
|
98
|
+
attribute: "src"
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
83
104
|
for (const attr of MEDIA_SRCSET_ATTRS) {
|
|
84
105
|
const value = el.getAttribute(attr);
|
|
85
106
|
if (!value) continue;
|
|
@@ -100,7 +121,7 @@ const validate = (ctx) => {
|
|
|
100
121
|
});
|
|
101
122
|
return findings;
|
|
102
123
|
};
|
|
103
|
-
function formatDimensions(file) {
|
|
124
|
+
function formatDimensions$1(file) {
|
|
104
125
|
if (file.width && file.height) {
|
|
105
126
|
return `${file.width}x${file.height}`;
|
|
106
127
|
}
|
|
@@ -120,12 +141,134 @@ function generateMediaIndex(files2) {
|
|
|
120
141
|
lines.push("| ------ | ---- | ------------ | ---- | ---------- | ------ | --- |");
|
|
121
142
|
for (const file of files2) {
|
|
122
143
|
lines.push(
|
|
123
|
-
`| ${escapeMarkdownCell(file.folderName)} | ${file.slug} | ${escapeMarkdownCell(file.displayName)} | ${file.mediaType} | ${formatDimensions(file)} | ${formatLabels(file.labels)} | ${file.url} |`
|
|
144
|
+
`| ${escapeMarkdownCell(file.folderName)} | ${file.slug} | ${escapeMarkdownCell(file.displayName)} | ${file.mediaType} | ${formatDimensions$1(file)} | ${formatLabels(file.labels)} | ${file.url} |`
|
|
124
145
|
);
|
|
125
146
|
}
|
|
126
147
|
lines.push("");
|
|
127
148
|
return lines.join("\n");
|
|
128
149
|
}
|
|
150
|
+
const CATEGORY = "Media";
|
|
151
|
+
function subCategoryForMediaType(mediaType) {
|
|
152
|
+
switch (mediaType.toLowerCase()) {
|
|
153
|
+
case "image":
|
|
154
|
+
return "Images";
|
|
155
|
+
case "video":
|
|
156
|
+
return "Videos";
|
|
157
|
+
case "audio":
|
|
158
|
+
return "Audio";
|
|
159
|
+
case "document":
|
|
160
|
+
return "Documents";
|
|
161
|
+
default:
|
|
162
|
+
return "Other";
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
function uniqueItemId(slug, mediaId, usedIds) {
|
|
166
|
+
const base = slug.trim() || mediaId.slice(0, 8);
|
|
167
|
+
let id = `wix-media:${base}`;
|
|
168
|
+
let suffix = 2;
|
|
169
|
+
while (usedIds.has(id)) {
|
|
170
|
+
id = `wix-media:${base}-${suffix++}`;
|
|
171
|
+
}
|
|
172
|
+
usedIds.add(id);
|
|
173
|
+
return id;
|
|
174
|
+
}
|
|
175
|
+
function formatDimensions(file) {
|
|
176
|
+
if (file.width && file.height) {
|
|
177
|
+
return `${file.width}×${file.height}`;
|
|
178
|
+
}
|
|
179
|
+
return "-";
|
|
180
|
+
}
|
|
181
|
+
function formatTitle(file) {
|
|
182
|
+
const folder = file.folderName?.trim();
|
|
183
|
+
if (folder && folder !== "Media Root" && folder !== "Unknown") {
|
|
184
|
+
return `${folder} — ${file.displayName}`;
|
|
185
|
+
}
|
|
186
|
+
return file.displayName;
|
|
187
|
+
}
|
|
188
|
+
const VISUAL_FILE_EXTENSION_RE = /\.(svg|png|jpe?g|gif|webp|bmp|ico)($|[?#])/i;
|
|
189
|
+
function hasVisualFileExtension(file) {
|
|
190
|
+
return VISUAL_FILE_EXTENSION_RE.test(`${file.displayName} ${file.url}`);
|
|
191
|
+
}
|
|
192
|
+
function isPreviewableVisualMedia(file) {
|
|
193
|
+
const type = file.mediaType.toLowerCase();
|
|
194
|
+
if (type === "image" || type === "vector") return true;
|
|
195
|
+
return hasVisualFileExtension(file);
|
|
196
|
+
}
|
|
197
|
+
function thumbnailUrlForMedia(file) {
|
|
198
|
+
const url = file.url.trim();
|
|
199
|
+
if (!url || !isPreviewableVisualMedia(file)) return void 0;
|
|
200
|
+
return url;
|
|
201
|
+
}
|
|
202
|
+
function stagePlaceInteractionForMedia(file) {
|
|
203
|
+
const mediaType = file.mediaType.toLowerCase();
|
|
204
|
+
if (mediaType === "image" || mediaType === "vector" || isPreviewableVisualMedia(file)) {
|
|
205
|
+
return {
|
|
206
|
+
mode: "stage-place",
|
|
207
|
+
persistOnPage: true,
|
|
208
|
+
stagePromptTemplate: `Place this image at the marker location on the page.
|
|
209
|
+
URL: ${file.url}`
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
if (mediaType === "video") {
|
|
213
|
+
return {
|
|
214
|
+
mode: "stage-place",
|
|
215
|
+
persistOnPage: true,
|
|
216
|
+
stagePromptTemplate: `Place this video at the marker location on the page.
|
|
217
|
+
URL: ${file.url}`
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
return void 0;
|
|
221
|
+
}
|
|
222
|
+
function buildMediaPrompt(file) {
|
|
223
|
+
const labels = file.labels.length > 0 ? `Labels: ${file.labels.join(", ")}
|
|
224
|
+
` : "";
|
|
225
|
+
const folderLine = file.folderName && file.folderName !== "Unknown" ? `Folder: ${file.folderName}
|
|
226
|
+
` : "";
|
|
227
|
+
return [
|
|
228
|
+
"Use this Wix Media Manager asset in jay-html (do not copy to public/):",
|
|
229
|
+
`URL: ${file.url}`,
|
|
230
|
+
`Media id: ${file.id}`,
|
|
231
|
+
`Slug: ${file.slug}`,
|
|
232
|
+
`Type: ${file.mediaType} · ${formatDimensions(file)}`,
|
|
233
|
+
folderLine.trimEnd(),
|
|
234
|
+
labels.trimEnd(),
|
|
235
|
+
"Full media index: agent-kit/references/wix-media/MEDIA-INDEX.md",
|
|
236
|
+
"See agent-kit/designer/wix-media.md for fit/fill/crop transforms."
|
|
237
|
+
].filter((line) => line.length > 0).join("\n");
|
|
238
|
+
}
|
|
239
|
+
function buildMediaAddMenuItems(files2) {
|
|
240
|
+
const usedIds = /* @__PURE__ */ new Set();
|
|
241
|
+
return files2.map((file) => {
|
|
242
|
+
const thumbnail = thumbnailUrlForMedia(file);
|
|
243
|
+
const interaction = stagePlaceInteractionForMedia(file);
|
|
244
|
+
return {
|
|
245
|
+
id: uniqueItemId(file.slug, file.id, usedIds),
|
|
246
|
+
title: formatTitle(file),
|
|
247
|
+
category: CATEGORY,
|
|
248
|
+
subCategory: subCategoryForMediaType(file.mediaType),
|
|
249
|
+
pluginName: "wix-media",
|
|
250
|
+
packageName: "@jay-framework/wix-media",
|
|
251
|
+
...thumbnail ? { thumbnail } : {},
|
|
252
|
+
...interaction ? { interaction } : {},
|
|
253
|
+
prompt: buildMediaPrompt(file)
|
|
254
|
+
};
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
const ADD_MENU_GENERATED_REL = "agent-kit/aiditor/add-menu/wix-media.generated.yaml";
|
|
258
|
+
function writeGeneratedAddMenuCatalog(projectRoot, items) {
|
|
259
|
+
const outputPath = path.join(projectRoot, ADD_MENU_GENERATED_REL);
|
|
260
|
+
fs.mkdirSync(path.dirname(outputPath), { recursive: true });
|
|
261
|
+
const header = "# DO NOT EDIT BY HAND — regenerated by wix-media (setup / rebuild-index)\n\n";
|
|
262
|
+
const body = yaml.dump({ items }, { indent: 2, lineWidth: 120, noRefs: true });
|
|
263
|
+
fs.writeFileSync(outputPath, header + body, "utf-8");
|
|
264
|
+
return ADD_MENU_GENERATED_REL;
|
|
265
|
+
}
|
|
266
|
+
async function refreshMediaAddMenuCatalog(projectRoot, mediaService) {
|
|
267
|
+
const files2 = await mediaService.listPublicFiles();
|
|
268
|
+
const items = buildMediaAddMenuItems(files2);
|
|
269
|
+
const outputRel = writeGeneratedAddMenuCatalog(projectRoot, items);
|
|
270
|
+
return { itemCount: items.length, outputRel };
|
|
271
|
+
}
|
|
129
272
|
const WIX_MEDIA_SERVICE_MARKER = createJayService("Wix Media Service");
|
|
130
273
|
let filesClientInstance;
|
|
131
274
|
let foldersClientInstance;
|
|
@@ -288,9 +431,14 @@ async function generateWixMediaReferences(ctx) {
|
|
|
288
431
|
const mediaIndexContent = generateMediaIndex(mediaFiles);
|
|
289
432
|
const mediaIndexPath = path.join(ctx.referencesDir, "MEDIA-INDEX.md");
|
|
290
433
|
fs.writeFileSync(mediaIndexPath, mediaIndexContent, "utf-8");
|
|
434
|
+
const addMenuItems = buildMediaAddMenuItems(mediaFiles);
|
|
435
|
+
const addMenuGenerated = writeGeneratedAddMenuCatalog(ctx.projectRoot, addMenuItems);
|
|
291
436
|
return {
|
|
292
|
-
referencesCreated: [
|
|
293
|
-
|
|
437
|
+
referencesCreated: [
|
|
438
|
+
`agent-kit/references/${ctx.pluginName}/MEDIA-INDEX.md`,
|
|
439
|
+
addMenuGenerated
|
|
440
|
+
],
|
|
441
|
+
message: `${mediaFiles.length} media files indexed; ${addMenuItems.length} Add Menu items.`
|
|
294
442
|
};
|
|
295
443
|
}
|
|
296
444
|
const rebuildIndex = makeCliCommand("rebuild-index").withServices(CONSOLE_CONTEXT).withHandler(async (input, console) => {
|
|
@@ -309,8 +457,13 @@ const rebuildIndex = makeCliCommand("rebuild-index").withServices(CONSOLE_CONTEX
|
|
|
309
457
|
const indexContent = generateMediaIndex(files2);
|
|
310
458
|
const indexPath = path$1.join(referencesDir, "MEDIA-INDEX.md");
|
|
311
459
|
fs$1.writeFileSync(indexPath, indexContent, "utf-8");
|
|
460
|
+
const addMenuPath = writeGeneratedAddMenuCatalog(
|
|
461
|
+
console.projectRoot,
|
|
462
|
+
buildMediaAddMenuItems(files2)
|
|
463
|
+
);
|
|
312
464
|
console.log(`Media index written to ${indexPath}`);
|
|
313
|
-
|
|
465
|
+
console.log(`Add Menu catalog written to ${addMenuPath}`);
|
|
466
|
+
return { success: true, fileCount: files2.length, addMenuPath };
|
|
314
467
|
});
|
|
315
468
|
const UPLOAD_INDEX_FILE = ".wix-media-uploads.json";
|
|
316
469
|
const MEDIA_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
@@ -450,10 +603,15 @@ const uploadPublic = makeCliCommand("upload-public").withServices(CONSOLE_CONTEX
|
|
|
450
603
|
return { success: failed === 0 };
|
|
451
604
|
});
|
|
452
605
|
export {
|
|
606
|
+
ADD_MENU_GENERATED_REL,
|
|
607
|
+
buildMediaAddMenuItems,
|
|
453
608
|
generateMediaIndex,
|
|
454
609
|
generateWixMediaReferences,
|
|
455
610
|
rebuildIndex,
|
|
611
|
+
refreshMediaAddMenuCatalog,
|
|
456
612
|
setupWixMedia,
|
|
613
|
+
thumbnailUrlForMedia,
|
|
457
614
|
uploadPublic,
|
|
458
|
-
validate
|
|
615
|
+
validate,
|
|
616
|
+
writeGeneratedAddMenuCatalog
|
|
459
617
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jay-framework/wix-media",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.20.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Wix Media Manager plugin for Jay Framework — media validation, index generation, and agent-kit references",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -25,19 +25,20 @@
|
|
|
25
25
|
"test": "vitest run"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@jay-framework/compiler-jay-html": "^0.
|
|
29
|
-
"@jay-framework/compiler-shared": "^0.
|
|
30
|
-
"@jay-framework/component": "^0.
|
|
31
|
-
"@jay-framework/fullstack-component": "^0.
|
|
32
|
-
"@jay-framework/stack-server-runtime": "^0.
|
|
33
|
-
"@jay-framework/wix-server-client": "^0.
|
|
34
|
-
"@jay-framework/wix-utils": "^0.
|
|
28
|
+
"@jay-framework/compiler-jay-html": "^0.20.0",
|
|
29
|
+
"@jay-framework/compiler-shared": "^0.20.0",
|
|
30
|
+
"@jay-framework/component": "^0.20.0",
|
|
31
|
+
"@jay-framework/fullstack-component": "^0.20.0",
|
|
32
|
+
"@jay-framework/stack-server-runtime": "^0.20.0",
|
|
33
|
+
"@jay-framework/wix-server-client": "^0.20.0",
|
|
34
|
+
"@jay-framework/wix-utils": "^0.20.0",
|
|
35
35
|
"@wix/media": "^1.0.247",
|
|
36
|
-
"@wix/sdk": "^1.21.5"
|
|
36
|
+
"@wix/sdk": "^1.21.5",
|
|
37
|
+
"js-yaml": "^4.1.0"
|
|
37
38
|
},
|
|
38
39
|
"devDependencies": {
|
|
39
|
-
"@jay-framework/compiler-jay-stack": "^0.
|
|
40
|
-
"@jay-framework/vite-plugin": "^0.
|
|
40
|
+
"@jay-framework/compiler-jay-stack": "^0.20.0",
|
|
41
|
+
"@jay-framework/vite-plugin": "^0.20.0",
|
|
41
42
|
"@types/node": "^20.11.0",
|
|
42
43
|
"node-html-parser": "^6.1.0",
|
|
43
44
|
"rimraf": "^5.0.5",
|