@scaleflex/asset-picker 0.2.10 → 0.2.12
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/.claude/settings.local.json +14 -1
- package/README.md +202 -11
- package/dist/{asset-picker-CJ4AMXrc.cjs → asset-picker-CMsm4Ewp.cjs} +301 -152
- package/dist/{asset-picker-JBTs8qXV.js → asset-picker-DCcLalcQ.js} +1210 -895
- package/dist/asset-picker.d.ts.map +1 -1
- package/dist/components/filters/ap-filter-type.d.ts +18 -1
- package/dist/components/filters/ap-filter-type.d.ts.map +1 -1
- package/dist/components/filters/ap-filters-bar.d.ts.map +1 -1
- package/dist/components/filters/filters.constants.d.ts +1 -0
- package/dist/components/filters/filters.constants.d.ts.map +1 -1
- package/dist/components/toolbar/ap-content-toolbar.d.ts +1 -0
- package/dist/components/toolbar/ap-content-toolbar.d.ts.map +1 -1
- package/dist/define.cjs +1 -1
- package/dist/define.js +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +68 -2
- package/dist/react.cjs +1 -1
- package/dist/react.d.ts +82 -1
- package/dist/react.d.ts.map +1 -1
- package/dist/react.js +75 -31
- package/dist/services/filters.service.d.ts +1 -1
- package/dist/services/filters.service.d.ts.map +1 -1
- package/dist/store/index.d.ts.map +1 -1
- package/dist/store/store.types.d.ts +2 -1
- package/dist/store/store.types.d.ts.map +1 -1
- package/dist/types/asset.types.d.ts +6 -1
- package/dist/types/asset.types.d.ts.map +1 -1
- package/dist/utils/asset-helpers.d.ts +80 -0
- package/dist/utils/asset-helpers.d.ts.map +1 -0
- package/dist/utils/filter-serialize.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -45,7 +45,20 @@
|
|
|
45
45
|
"Bash(npx playwright:*)",
|
|
46
46
|
"Bash(find /Users/dmitry_stremous/scaleflex/asset-picker/src/components/views -type f -name *folder* -o -name *row* -o -name *card*)",
|
|
47
47
|
"Bash(pkill -f \"vite serve\")",
|
|
48
|
-
"Bash(pkill -f \"vite\")"
|
|
48
|
+
"Bash(pkill -f \"vite\")",
|
|
49
|
+
"Bash(curl -s \"https://api.filerobot.com/fbmjmuoeb/v5/files?format=json,regvar:api,select:internal&preview=2301&limit=2&sort=created_at:desc\" -H \"X-Filerobot-Key: SECU_47D57B3106A841F7A1FEA951846CC5F3\")",
|
|
50
|
+
"Bash(python3 -c \"import sys,json; d=json.load\\(sys.stdin\\); [print\\(f[''''name''''], f.get\\(''''extension'''',''''?''''\\), f.get\\(''''type'''',''''?''''\\)\\) for f in d.get\\(''''files'''',[]\\)]\")",
|
|
51
|
+
"Bash(python3 -c \"import sys,json; d=json.load\\(sys.stdin\\); print\\(''''total:'''', d.get\\(''''info'''',{}\\).get\\(''''total_files_count'''',0\\)\\); [print\\(f[''''name''''], f.get\\(''''extension'''',''''?''''\\), f.get\\(''''type'''',{}\\).get\\(''''value'''',''''?''''\\)\\) for f in d.get\\(''''files'''',[]\\)]\")",
|
|
52
|
+
"Bash(curl -s \"https://api.filerobot.com/fbmjmuoeb/v5/files?format=json,regvar:api,select:internal&preview=2301&limit=3&sort=created_at:desc\" -H \"X-Filerobot-Key: SECU_47D57B3106A841F7A1FEA951846CC5F3\")",
|
|
53
|
+
"Bash(python3 -c \"import sys,json; d=json.load\\(sys.stdin\\); print\\(''''total:'''', d.get\\(''''info'''',{}\\).get\\(''''total_files_count'''',0\\)\\); [print\\(f[''''name''''], f.get\\(''''extension'''',''''?''''\\), f.get\\(''''type'''',''''?''''\\)\\) for f in d.get\\(''''files'''',[]\\)]\")",
|
|
54
|
+
"Bash(curl -s 'https://api.filerobot.com/fbmjmuoeb/v5/files?preview=2301&q=type:%22image_svg%22&folder=%2F&sort=modified_at:desc&offset=0&limit=2&recursive=1' -H 'x-filerobot-key: SECU_47D57B3106A841F7A1FEA951846CC5F3')",
|
|
55
|
+
"Bash(python3 -c \"import sys,json; d=json.load\\(sys.stdin\\); print\\(''''total:'''', d.get\\(''''info'''',{}\\).get\\(''''total_files_count'''',0\\)\\); [print\\(f[''''name''''], f.get\\(''''type'''',{}\\).get\\(''''value'''',''''?''''\\) if isinstance\\(f.get\\(''''type''''\\),dict\\) else f.get\\(''''type'''',''''?''''\\)\\) for f in d.get\\(''''files'''',[]\\)]\")",
|
|
56
|
+
"Bash(curl -s 'https://api.filerobot.com/fbmjmuoeb/v5/files?preview=2301&q=type:%22image_jpg%22&folder=%2F&sort=modified_at:desc&offset=0&limit=2&recursive=1' -H 'x-filerobot-key: SECU_47D57B3106A841F7A1FEA951846CC5F3')",
|
|
57
|
+
"Bash(python3 -c \"import sys,json; d=json.load\\(sys.stdin\\); print\\(''''total:'''', d.get\\(''''info'''',{}\\).get\\(''''total_files_count'''',0\\)\\); [print\\(f[''''name'''']\\) for f in d.get\\(''''files'''',[]\\)]\")",
|
|
58
|
+
"Bash(curl -s 'https://api.filerobot.com/fbmjmuoeb/v5/filters/config' -H 'x-filerobot-key: SECU_47D57B3106A841F7A1FEA951846CC5F3')",
|
|
59
|
+
"Bash(curl -s 'https://api.filerobot.com/fbmjmuoeb/v5/filters?filter_by=filetype&format=list&limit=200' -H 'x-filerobot-key: SECU_47D57B3106A841F7A1FEA951846CC5F3')",
|
|
60
|
+
"Bash(curl -s 'https://api.filerobot.com/fbmjmuoeb/v5/files?format=json,regvar:api,select:internal&preview=2301&q=type:%22image_svg%22&folder=%2F&sort=created_at:desc&offset=0&limit=2&recursive=1' -H 'x-filerobot-key: SECU_47D57B3106A841F7A1FEA951846CC5F3')",
|
|
61
|
+
"Bash(curl -s 'https://api.filerobot.com/fbmjmuoeb/v5/files?preview=2301&q=type:%22image_png%22,%22image_jpeg%22,%22image_svg%22,%22image_webp%22,%22image_gif%22,%22image_other%22,%22image_psd%22,%22image_avif%22,%22image_tiff%22,%22image_eps%22&folder=%2F&sort=modified_at:desc&offset=0&limit=3&recursive=1' -H 'x-filerobot-key: SECU_47D57B3106A841F7A1FEA951846CC5F3')"
|
|
49
62
|
]
|
|
50
63
|
}
|
|
51
64
|
}
|
package/README.md
CHANGED
|
@@ -44,6 +44,8 @@
|
|
|
44
44
|
- [Public Methods](#public-methods)
|
|
45
45
|
- [Events](#events)
|
|
46
46
|
- [React API](#react-api)
|
|
47
|
+
- [Provider + Hook](#provider--hook-recommended)
|
|
48
|
+
- [Asset Utilities](#asset-utilities)
|
|
47
49
|
- [Theming](#theming)
|
|
48
50
|
- [Brand Color](#brand-color)
|
|
49
51
|
- [CSS Custom Properties](#css-custom-properties)
|
|
@@ -112,8 +114,8 @@ pnpm add @scaleflex/asset-picker
|
|
|
112
114
|
|
|
113
115
|
| Export path | Description |
|
|
114
116
|
|---|---|
|
|
115
|
-
| `@scaleflex/asset-picker` | `AssetPicker` class + all TypeScript types |
|
|
116
|
-
| `@scaleflex/asset-picker/react` | React wrapper component |
|
|
117
|
+
| `@scaleflex/asset-picker` | `AssetPicker` class + all TypeScript types + asset utility functions |
|
|
118
|
+
| `@scaleflex/asset-picker/react` | React wrapper component + `AssetPickerProvider` + `useAssetPicker` hook |
|
|
117
119
|
| `@scaleflex/asset-picker/define` | Side-effect import — registers `<sfx-asset-picker>` custom element |
|
|
118
120
|
|
|
119
121
|
Both ESM (`import`) and CJS (`require`) builds are provided.
|
|
@@ -248,7 +250,13 @@ Use when your application already has a SASS key — e.g. inside the Scaleflex H
|
|
|
248
250
|
| `rememberLastTab` | `boolean` | `false` | Persist the last active tab (assets/folders) and restore on next open |
|
|
249
251
|
| `defaultFilters` | `FiltersInput` | `undefined` | Filters pre-applied on open. User can modify/remove |
|
|
250
252
|
| `forcedFilters` | `FiltersInput` | `undefined` | Filters always active. Shown as locked chips the user cannot remove |
|
|
251
|
-
| `
|
|
253
|
+
| `displayMode` | `'modal' \| 'inline'` | `'modal'` | `'modal'` renders as a dialog overlay, `'inline'` renders in page flow |
|
|
254
|
+
| `gridSize` | `'normal' \| 'large'` | `'normal'` | Grid card density: `'normal'` (4 cols at ~1200px) or `'large'` (3 cols) |
|
|
255
|
+
| `stickyFilters` | `boolean` | `false` | Make the toolbar and filters bar sticky while scrolling content |
|
|
256
|
+
| `folderSelection` | `boolean` | `true` | Allow selecting folders via checkboxes |
|
|
257
|
+
| `folderSelectionMode` | `'folder' \| 'assets'` | `'folder'` | `'folder'` returns Folder objects; `'assets'` fetches folder contents and returns only Assets |
|
|
258
|
+
| `uploader` | `UploaderIntegrationConfig` | `undefined` | Enable integrated uploader. Adds an "Upload" button and drop zone. Requires `@scaleflex/uploader` |
|
|
259
|
+
| `onSelect` | `(assets: Asset[], folders?: Folder[]) => void` | `undefined` | Callback when assets are selected |
|
|
252
260
|
| `onCancel` | `() => void` | `undefined` | Callback when the picker is cancelled |
|
|
253
261
|
|
|
254
262
|
#### Sort fields
|
|
@@ -288,6 +296,13 @@ picker.config = {
|
|
|
288
296
|
};
|
|
289
297
|
```
|
|
290
298
|
|
|
299
|
+
```ts
|
|
300
|
+
// Lock to specific extensions using subtype values (category_extension)
|
|
301
|
+
forcedFilters: {
|
|
302
|
+
type: { values: ['image_svg', 'image_png'] },
|
|
303
|
+
},
|
|
304
|
+
```
|
|
305
|
+
|
|
291
306
|
**Behaviour:**
|
|
292
307
|
|
|
293
308
|
- **`defaultFilters`** are seeded into the applied filters state when `open()` is called. The user sees them as normal filter chips and can modify or remove them freely.
|
|
@@ -320,7 +335,7 @@ All events bubble and cross shadow DOM boundaries (`composed: true`).
|
|
|
320
335
|
| Event | Detail | Description |
|
|
321
336
|
|---|---|---|
|
|
322
337
|
| `ap-select` | `{ assets: Asset[] }` | Fired when the user confirms their selection |
|
|
323
|
-
| `ap-cancel` | `{ reason: 'backdrop' \| 'escape' \| 'button'
|
|
338
|
+
| `ap-cancel` | `{ reason: 'backdrop' \| 'escape' \| 'button' }` | Fired when the picker is closed without selecting |
|
|
324
339
|
| `ap-open` | `{ timestamp: number }` | Fired when the picker opens successfully |
|
|
325
340
|
| `ap-error` | `{ error: Error, context: string }` | Fired on initialisation or runtime errors |
|
|
326
341
|
|
|
@@ -355,7 +370,8 @@ import { AssetPicker, type AssetPickerRef, type AssetPickerProps } from '@scalef
|
|
|
355
370
|
|---|---|---|
|
|
356
371
|
| `config` | `AssetPickerConfig` | Configuration object (see [Config Options](#config-options)) |
|
|
357
372
|
| `open` | `boolean` | Controlled open state |
|
|
358
|
-
| `onSelect` | `(assets: Asset[]) => void` | Selection callback |
|
|
373
|
+
| `onSelect` | `(assets: Asset[], folders?: Folder[]) => void` | Selection callback (assets + optional folders) |
|
|
374
|
+
| `onSelectWithFolders` | `(result: { assets: Asset[]; folders: Folder[] }) => void` | Alternative callback that always includes folders |
|
|
359
375
|
| `onCancel` | `() => void` | Cancel callback |
|
|
360
376
|
| `className` | `string` | CSS class for the wrapper |
|
|
361
377
|
| `style` | `CSSProperties` | Inline styles for the wrapper |
|
|
@@ -392,6 +408,163 @@ const ref = useRef<AssetPickerRef>(null);
|
|
|
392
408
|
<AssetPicker ref={ref} config={config} onSelect={handleSelect} />
|
|
393
409
|
```
|
|
394
410
|
|
|
411
|
+
### Provider + Hook (recommended)
|
|
412
|
+
|
|
413
|
+
For apps that open the picker from many places, use `AssetPickerProvider` + `useAssetPicker()` to avoid managing open/close state yourself. One picker instance is mounted at the root and shared across the tree.
|
|
414
|
+
|
|
415
|
+
#### Setup
|
|
416
|
+
|
|
417
|
+
```tsx
|
|
418
|
+
import { AssetPickerProvider } from '@scaleflex/asset-picker/react';
|
|
419
|
+
|
|
420
|
+
function App() {
|
|
421
|
+
return (
|
|
422
|
+
<AssetPickerProvider
|
|
423
|
+
config={{
|
|
424
|
+
auth: {
|
|
425
|
+
mode: 'sassKey',
|
|
426
|
+
sassKey: 'YOUR_SASS_KEY',
|
|
427
|
+
projectToken: 'YOUR_TOKEN',
|
|
428
|
+
},
|
|
429
|
+
}}
|
|
430
|
+
>
|
|
431
|
+
<Dashboard />
|
|
432
|
+
</AssetPickerProvider>
|
|
433
|
+
);
|
|
434
|
+
}
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
#### Promise mode
|
|
438
|
+
|
|
439
|
+
```tsx
|
|
440
|
+
import { useAssetPicker } from '@scaleflex/asset-picker/react';
|
|
441
|
+
|
|
442
|
+
function ImageSelector() {
|
|
443
|
+
const picker = useAssetPicker();
|
|
444
|
+
|
|
445
|
+
const handleClick = async () => {
|
|
446
|
+
try {
|
|
447
|
+
const assets = await picker.open({ multiSelect: true });
|
|
448
|
+
console.log('Selected:', assets);
|
|
449
|
+
} catch {
|
|
450
|
+
console.log('User cancelled');
|
|
451
|
+
}
|
|
452
|
+
};
|
|
453
|
+
|
|
454
|
+
return <button onClick={handleClick}>Choose images</button>;
|
|
455
|
+
}
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
The promise resolves with the selected `Asset[]` on confirm, and rejects with `'cancelled'` when the user closes without selecting. Note: promise mode returns only assets — use callback mode with `onSelect(assets, folders)` if you need folder data.
|
|
459
|
+
|
|
460
|
+
#### Callback mode
|
|
461
|
+
|
|
462
|
+
```tsx
|
|
463
|
+
function VideoSelector() {
|
|
464
|
+
const picker = useAssetPicker();
|
|
465
|
+
|
|
466
|
+
return (
|
|
467
|
+
<button
|
|
468
|
+
onClick={() =>
|
|
469
|
+
picker.open({
|
|
470
|
+
forcedFilters: { type: { values: ['video'] } },
|
|
471
|
+
onSelect: (assets) => console.log(assets),
|
|
472
|
+
onCancel: () => console.log('Cancelled'),
|
|
473
|
+
})
|
|
474
|
+
}
|
|
475
|
+
>
|
|
476
|
+
Choose video
|
|
477
|
+
</button>
|
|
478
|
+
);
|
|
479
|
+
}
|
|
480
|
+
```
|
|
481
|
+
|
|
482
|
+
#### Config overrides
|
|
483
|
+
|
|
484
|
+
Any `AssetPickerConfig` property passed to `open()` is merged with (and overrides) the base config from the provider:
|
|
485
|
+
|
|
486
|
+
```tsx
|
|
487
|
+
// Base config has multiSelect: false
|
|
488
|
+
// This call overrides it to true and adds a forced filter
|
|
489
|
+
const assets = await picker.open({
|
|
490
|
+
multiSelect: true,
|
|
491
|
+
forcedFilters: { type: { values: ['image'] } },
|
|
492
|
+
});
|
|
493
|
+
```
|
|
494
|
+
|
|
495
|
+
#### Hook return type
|
|
496
|
+
|
|
497
|
+
```ts
|
|
498
|
+
interface UseAssetPickerReturn {
|
|
499
|
+
open(overrides?: OpenOptions): Promise<Asset[]>;
|
|
500
|
+
close(): void;
|
|
501
|
+
isOpen: boolean;
|
|
502
|
+
}
|
|
503
|
+
```
|
|
504
|
+
|
|
505
|
+
---
|
|
506
|
+
|
|
507
|
+
## Asset Utilities
|
|
508
|
+
|
|
509
|
+
Pure helper functions for working with `Asset` objects. Exported from the main entry point — no React required.
|
|
510
|
+
|
|
511
|
+
```ts
|
|
512
|
+
import {
|
|
513
|
+
getAltText,
|
|
514
|
+
getCdnUrl,
|
|
515
|
+
getAssetWidth,
|
|
516
|
+
getAssetHeight,
|
|
517
|
+
getAssetDimensions,
|
|
518
|
+
isTranscoded,
|
|
519
|
+
getTranscodedUrl,
|
|
520
|
+
getBestVideoUrl,
|
|
521
|
+
isVideo,
|
|
522
|
+
isImage,
|
|
523
|
+
isAudio,
|
|
524
|
+
} from '@scaleflex/asset-picker';
|
|
525
|
+
```
|
|
526
|
+
|
|
527
|
+
### Type checks
|
|
528
|
+
|
|
529
|
+
| Function | Returns | Description |
|
|
530
|
+
|---|---|---|
|
|
531
|
+
| `isImage(asset)` | `boolean` | `true` if `asset.type` starts with `"image"` |
|
|
532
|
+
| `isVideo(asset)` | `boolean` | `true` if `asset.type` starts with `"video"` |
|
|
533
|
+
| `isAudio(asset)` | `boolean` | `true` if `asset.type` starts with `"audio"` |
|
|
534
|
+
|
|
535
|
+
### URLs
|
|
536
|
+
|
|
537
|
+
| Function | Returns | Description |
|
|
538
|
+
|---|---|---|
|
|
539
|
+
| `getCdnUrl(asset)` | `string` | CDN URL, falling back to public URL, then `""` |
|
|
540
|
+
| `getBestVideoUrl(asset)` | `string` | Transcoded HLS URL > CDN URL > public URL |
|
|
541
|
+
| `getTranscodedUrl(asset)` | `string \| null` | HLS manifest URL, or `null` if not transcoded |
|
|
542
|
+
|
|
543
|
+
### Alt text
|
|
544
|
+
|
|
545
|
+
```ts
|
|
546
|
+
const alt = getAltText(asset); // uses first available language
|
|
547
|
+
const alt = getAltText(asset, 'fr'); // prefers French title
|
|
548
|
+
```
|
|
549
|
+
|
|
550
|
+
Resolution priority: `meta.alt` > `meta.title` (string or localized `Record<string, string>`) > filename without extension.
|
|
551
|
+
|
|
552
|
+
### Dimensions
|
|
553
|
+
|
|
554
|
+
| Function | Returns | Description |
|
|
555
|
+
|---|---|---|
|
|
556
|
+
| `getAssetWidth(asset)` | `number` | Width in px (`0` if unknown). Works for images and videos. |
|
|
557
|
+
| `getAssetHeight(asset)` | `number` | Height in px (`0` if unknown). Works for images and videos. |
|
|
558
|
+
| `getAssetDimensions(asset)` | `{ width, height }` | Both dimensions as an object. |
|
|
559
|
+
|
|
560
|
+
### Video transcoding
|
|
561
|
+
|
|
562
|
+
| Function | Returns | Description |
|
|
563
|
+
|---|---|---|
|
|
564
|
+
| `isTranscoded(asset)` | `boolean` | Whether the asset has a transcoded HLS version |
|
|
565
|
+
| `getTranscodedUrl(asset)` | `string \| null` | The HLS manifest URL, or `null` |
|
|
566
|
+
| `getBestVideoUrl(asset)` | `string` | Best playback URL (transcoded > CDN > public) |
|
|
567
|
+
|
|
395
568
|
---
|
|
396
569
|
|
|
397
570
|
## Theming
|
|
@@ -628,24 +801,36 @@ interface Asset {
|
|
|
628
801
|
};
|
|
629
802
|
created_at: string; // ISO timestamp
|
|
630
803
|
modified_at: string; // ISO timestamp
|
|
631
|
-
tags: Record<string,
|
|
804
|
+
tags: Record<string, Array<{ label: string; sid: string }>>
|
|
805
|
+
| Record<string, { label: string; sid: string }>
|
|
806
|
+
| string[];
|
|
632
807
|
labels: string[];
|
|
633
808
|
meta: {
|
|
634
|
-
title?: string
|
|
809
|
+
title?: string | Record<string, string>; // plain or localized by language code
|
|
635
810
|
description?: string;
|
|
636
811
|
alt?: string;
|
|
637
812
|
[key: string]: unknown;
|
|
638
813
|
};
|
|
639
814
|
info: {
|
|
815
|
+
img_type?: string; // Image format (e.g. "jpeg", "png")
|
|
640
816
|
img_w?: number; // Image width (px)
|
|
641
817
|
img_h?: number; // Image height (px)
|
|
642
|
-
duration?: number; //
|
|
818
|
+
duration?: number; // Audio duration (seconds)
|
|
819
|
+
video_duration?: number; // Video duration (seconds)
|
|
643
820
|
video_w?: number; // Video width (px)
|
|
644
821
|
video_h?: number; // Video height (px)
|
|
645
822
|
thumbnail?: string; // Thumbnail URL
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
823
|
+
preview?: string; // Preview URL
|
|
824
|
+
video_thumbnail?: string; // Video poster image URL
|
|
825
|
+
video_gif?: string; // Animated GIF preview URL
|
|
826
|
+
image_thumbnail?: string; // Image thumbnail URL
|
|
827
|
+
main_colors?: string[]; // Dominant colours (names)
|
|
828
|
+
main_colors_hex?: string[]; // Dominant colours (hex)
|
|
829
|
+
dominant_color?: string; // Most dominant colour (name)
|
|
830
|
+
dominant_color_hex?: string; // Most dominant colour (hex)
|
|
831
|
+
color_space?: string; // Colour space (e.g. "sRGB")
|
|
832
|
+
metadata?: Record<string, unknown>; // Embedded metadata (EXIF, IPTC, etc.)
|
|
833
|
+
playlists?: Array<{ playlists: string[]; resolution?: string }>; // HLS transcoded playlists
|
|
649
834
|
};
|
|
650
835
|
folder?: {
|
|
651
836
|
uuid: string;
|
|
@@ -666,8 +851,10 @@ interface Folder {
|
|
|
666
851
|
uuid: string;
|
|
667
852
|
name: string;
|
|
668
853
|
path: string;
|
|
854
|
+
owner?: string;
|
|
669
855
|
created_at: string;
|
|
670
856
|
modified_at?: string;
|
|
857
|
+
updated_at?: string;
|
|
671
858
|
count?: {
|
|
672
859
|
files_recursive?: number;
|
|
673
860
|
files_direct?: number;
|
|
@@ -675,6 +862,10 @@ interface Folder {
|
|
|
675
862
|
size?: {
|
|
676
863
|
total_recursive_bytes?: number;
|
|
677
864
|
};
|
|
865
|
+
visibility?: {
|
|
866
|
+
in_cdn?: { actual: string; set: string };
|
|
867
|
+
in_dam?: { actual: string; set: string };
|
|
868
|
+
};
|
|
678
869
|
}
|
|
679
870
|
```
|
|
680
871
|
|