@happyvertical/smrt-assets 0.30.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 (119) hide show
  1. package/AGENTS.md +78 -0
  2. package/CLAUDE.md +1 -0
  3. package/LICENSE +7 -0
  4. package/README.md +136 -0
  5. package/dist/__smrt-register__.d.ts +2 -0
  6. package/dist/__smrt-register__.d.ts.map +1 -0
  7. package/dist/asset-association.d.ts +16 -0
  8. package/dist/asset-association.d.ts.map +1 -0
  9. package/dist/asset-associations.d.ts +27 -0
  10. package/dist/asset-associations.d.ts.map +1 -0
  11. package/dist/asset-capabilities.d.ts +137 -0
  12. package/dist/asset-capabilities.d.ts.map +1 -0
  13. package/dist/asset-conventions.d.ts +76 -0
  14. package/dist/asset-conventions.d.ts.map +1 -0
  15. package/dist/asset-metafield.d.ts +27 -0
  16. package/dist/asset-metafield.d.ts.map +1 -0
  17. package/dist/asset-metafields.d.ts +27 -0
  18. package/dist/asset-metafields.d.ts.map +1 -0
  19. package/dist/asset-runtime.d.ts +218 -0
  20. package/dist/asset-runtime.d.ts.map +1 -0
  21. package/dist/asset-serving.d.ts +146 -0
  22. package/dist/asset-serving.d.ts.map +1 -0
  23. package/dist/asset-status.d.ts +15 -0
  24. package/dist/asset-status.d.ts.map +1 -0
  25. package/dist/asset-statuses.d.ts +25 -0
  26. package/dist/asset-statuses.d.ts.map +1 -0
  27. package/dist/asset-store.d.ts +200 -0
  28. package/dist/asset-store.d.ts.map +1 -0
  29. package/dist/asset-type.d.ts +15 -0
  30. package/dist/asset-type.d.ts.map +1 -0
  31. package/dist/asset-types.d.ts +28 -0
  32. package/dist/asset-types.d.ts.map +1 -0
  33. package/dist/asset.d.ts +158 -0
  34. package/dist/asset.d.ts.map +1 -0
  35. package/dist/assets.d.ts +125 -0
  36. package/dist/assets.d.ts.map +1 -0
  37. package/dist/folder.d.ts +16 -0
  38. package/dist/folder.d.ts.map +1 -0
  39. package/dist/folders.d.ts +45 -0
  40. package/dist/folders.d.ts.map +1 -0
  41. package/dist/index.d.ts +21 -0
  42. package/dist/index.d.ts.map +1 -0
  43. package/dist/index.js +2285 -0
  44. package/dist/index.js.map +1 -0
  45. package/dist/manifest.json +4079 -0
  46. package/dist/media-bundle-persistence.d.ts +99 -0
  47. package/dist/media-bundle-persistence.d.ts.map +1 -0
  48. package/dist/owned-asset-helpers.d.ts +20 -0
  49. package/dist/owned-asset-helpers.d.ts.map +1 -0
  50. package/dist/playground.d.ts +2 -0
  51. package/dist/playground.d.ts.map +1 -0
  52. package/dist/playground.js +127 -0
  53. package/dist/playground.js.map +1 -0
  54. package/dist/smrt-knowledge.json +1922 -0
  55. package/dist/svelte/ActionBar.svelte +203 -0
  56. package/dist/svelte/ActionBar.svelte.d.ts +5 -0
  57. package/dist/svelte/ActionBar.svelte.d.ts.map +1 -0
  58. package/dist/svelte/AssetDetail.svelte +521 -0
  59. package/dist/svelte/AssetDetail.svelte.d.ts +35 -0
  60. package/dist/svelte/AssetDetail.svelte.d.ts.map +1 -0
  61. package/dist/svelte/AssetGrid.svelte +351 -0
  62. package/dist/svelte/AssetGrid.svelte.d.ts +5 -0
  63. package/dist/svelte/AssetGrid.svelte.d.ts.map +1 -0
  64. package/dist/svelte/AssetList.svelte +436 -0
  65. package/dist/svelte/AssetList.svelte.d.ts +5 -0
  66. package/dist/svelte/AssetList.svelte.d.ts.map +1 -0
  67. package/dist/svelte/AssetManager.svelte +381 -0
  68. package/dist/svelte/AssetManager.svelte.d.ts +5 -0
  69. package/dist/svelte/AssetManager.svelte.d.ts.map +1 -0
  70. package/dist/svelte/AssetToolbar.svelte +388 -0
  71. package/dist/svelte/AssetToolbar.svelte.d.ts +5 -0
  72. package/dist/svelte/AssetToolbar.svelte.d.ts.map +1 -0
  73. package/dist/svelte/CreateAssetModal.svelte +373 -0
  74. package/dist/svelte/CreateAssetModal.svelte.d.ts +19 -0
  75. package/dist/svelte/CreateAssetModal.svelte.d.ts.map +1 -0
  76. package/dist/svelte/__tests__/ActionBar.test.js +72 -0
  77. package/dist/svelte/__tests__/AssetDetail.test.js +57 -0
  78. package/dist/svelte/__tests__/AssetGrid.test.js +69 -0
  79. package/dist/svelte/__tests__/AssetList.test.js +72 -0
  80. package/dist/svelte/__tests__/AssetManager.test.js +21 -0
  81. package/dist/svelte/__tests__/AssetManagerRoute.test.js +16 -0
  82. package/dist/svelte/__tests__/AssetToolbar.test.js +39 -0
  83. package/dist/svelte/__tests__/CreateAssetModal.test.js +42 -0
  84. package/dist/svelte/i18n.d.ts +76 -0
  85. package/dist/svelte/i18n.d.ts.map +1 -0
  86. package/dist/svelte/i18n.js +87 -0
  87. package/dist/svelte/index.d.ts +19 -0
  88. package/dist/svelte/index.d.ts.map +1 -0
  89. package/dist/svelte/index.js +30 -0
  90. package/dist/svelte/playground/AssetDetailPreview.svelte +131 -0
  91. package/dist/svelte/playground/AssetDetailPreview.svelte.d.ts +8 -0
  92. package/dist/svelte/playground/AssetDetailPreview.svelte.d.ts.map +1 -0
  93. package/dist/svelte/playground/CreateAssetModalPreview.svelte +151 -0
  94. package/dist/svelte/playground/CreateAssetModalPreview.svelte.d.ts +4 -0
  95. package/dist/svelte/playground/CreateAssetModalPreview.svelte.d.ts.map +1 -0
  96. package/dist/svelte/playground.d.ts +60 -0
  97. package/dist/svelte/playground.d.ts.map +1 -0
  98. package/dist/svelte/playground.js +93 -0
  99. package/dist/svelte/routes/AssetManagerRoute.svelte +209 -0
  100. package/dist/svelte/routes/AssetManagerRoute.svelte.d.ts +4 -0
  101. package/dist/svelte/routes/AssetManagerRoute.svelte.d.ts.map +1 -0
  102. package/dist/svelte/routes/index.d.ts +2 -0
  103. package/dist/svelte/routes/index.d.ts.map +1 -0
  104. package/dist/svelte/routes/index.js +1 -0
  105. package/dist/svelte/routes/shared.d.ts +25 -0
  106. package/dist/svelte/routes/shared.d.ts.map +1 -0
  107. package/dist/svelte/routes/shared.js +31 -0
  108. package/dist/svelte/types.d.ts +179 -0
  109. package/dist/svelte/types.d.ts.map +1 -0
  110. package/dist/svelte/types.js +6 -0
  111. package/dist/types.d.ts +80 -0
  112. package/dist/types.d.ts.map +1 -0
  113. package/dist/types.js +2 -0
  114. package/dist/types.js.map +1 -0
  115. package/dist/ui.d.ts +10 -0
  116. package/dist/ui.d.ts.map +1 -0
  117. package/dist/ui.js +85 -0
  118. package/dist/ui.js.map +1 -0
  119. package/package.json +102 -0
@@ -0,0 +1,381 @@
1
+ <script lang="ts">
2
+ /**
3
+ * AssetManager - Main shell component for the Asset Manager
4
+ *
5
+ * Orchestrates the toolbar, grid/list views, action bar, detail drawer,
6
+ * and create modal. Can operate in 'manage' mode (full CRUD) or 'pick'
7
+ * mode (selection-only for embedding as a picker).
8
+ *
9
+ * @example
10
+ * ```svelte
11
+ * <AssetManager
12
+ * tenantId={tenant.id}
13
+ * domain="products"
14
+ * mode="manage"
15
+ * customActions={[
16
+ * { label: 'Set as Cover', action: (selected) => setCover(selected[0]) }
17
+ * ]}
18
+ * />
19
+ * ```
20
+ */
21
+
22
+ import { useI18n } from '@happyvertical/smrt-ui/i18n';
23
+ import ActionBar from './ActionBar.svelte';
24
+ import AssetDetail from './AssetDetail.svelte';
25
+ import AssetGrid from './AssetGrid.svelte';
26
+ import AssetList from './AssetList.svelte';
27
+ import AssetToolbar from './AssetToolbar.svelte';
28
+ import CreateAssetModal from './CreateAssetModal.svelte';
29
+ import { M } from './i18n.js';
30
+ import type {
31
+ AssetFilters,
32
+ AssetManagerProps,
33
+ AssetSort,
34
+ AssetViewMode,
35
+ PersistedAsset,
36
+ } from './types';
37
+
38
+ const { t } = useI18n();
39
+
40
+ let {
41
+ tenantId,
42
+ dbFilters = {},
43
+ mode = 'manage',
44
+ accept,
45
+ customActions = [],
46
+ uploader,
47
+ onSelect,
48
+ onselect,
49
+ onConfirm,
50
+ onconfirm,
51
+ initialView = 'grid',
52
+ showFolders = false,
53
+ }: AssetManagerProps = $props();
54
+
55
+ function getInitialManagerState() {
56
+ return {
57
+ view: initialView,
58
+ };
59
+ }
60
+
61
+ const initialManagerState = getInitialManagerState();
62
+ let view = $state<AssetViewMode>(initialManagerState.view);
63
+ let selectedIds = $state<Set<string>>(new Set());
64
+ let assets = $state<PersistedAsset[]>([]);
65
+ let loading = $state(false);
66
+ let showCreateModal = $state(false);
67
+ let showDetail = $state(false);
68
+ let detailAsset = $state<PersistedAsset | null>(null);
69
+ let pastedFile = $state<File | null>(null);
70
+ let filters = $state<AssetFilters>({
71
+ search: '',
72
+ types: [],
73
+ tags: [],
74
+ mimePatterns: [],
75
+ });
76
+ let sort = $state<AssetSort>({
77
+ field: 'createdAt',
78
+ direction: 'desc',
79
+ });
80
+ let managerDragOver = $state(false);
81
+
82
+ $effect(() => {
83
+ const nextMimePatterns = accept ? [accept] : [];
84
+ if (
85
+ filters.mimePatterns.length !== nextMimePatterns.length ||
86
+ filters.mimePatterns.some(
87
+ (pattern, index) => pattern !== nextMimePatterns[index],
88
+ )
89
+ ) {
90
+ filters = { ...filters, mimePatterns: nextMimePatterns };
91
+ }
92
+ });
93
+
94
+ const selectedAssets = $derived(
95
+ assets.filter((asset) => selectedIds.has(asset.id)),
96
+ );
97
+
98
+ function handleViewChange(newView: AssetViewMode) {
99
+ view = newView;
100
+ }
101
+
102
+ function handleFilterChange(newFilters: AssetFilters) {
103
+ filters = newFilters;
104
+ }
105
+
106
+ function handleSortChange(newSort: AssetSort) {
107
+ sort = newSort;
108
+ }
109
+
110
+ function handleSelectionChange(ids: Set<string>) {
111
+ selectedIds = ids;
112
+ (onSelect ?? onselect)?.(assets.filter((asset) => ids.has(asset.id)));
113
+ }
114
+
115
+ function handleClearSelection() {
116
+ selectedIds = new Set();
117
+ (onSelect ?? onselect)?.([]);
118
+ }
119
+
120
+ function handleAssetClick(asset: PersistedAsset) {
121
+ if (mode === 'pick') {
122
+ const next = new Set(selectedIds);
123
+ if (next.has(asset.id)) {
124
+ next.delete(asset.id);
125
+ } else {
126
+ next.add(asset.id);
127
+ }
128
+ handleSelectionChange(next);
129
+ return;
130
+ }
131
+
132
+ detailAsset = asset;
133
+ showDetail = true;
134
+ }
135
+
136
+ function handleDetailClose() {
137
+ showDetail = false;
138
+ detailAsset = null;
139
+ }
140
+
141
+ async function handleDetailSave(
142
+ asset: PersistedAsset,
143
+ updates: {
144
+ name?: string;
145
+ description?: string;
146
+ alt?: string;
147
+ title?: string;
148
+ caption?: string;
149
+ },
150
+ ) {
151
+ Object.assign(asset, updates);
152
+ console.log('Save asset:', asset.id, updates);
153
+ }
154
+
155
+ function handleAssetDblClick(asset: PersistedAsset) {
156
+ if (mode === 'pick') {
157
+ (onConfirm ?? onconfirm)?.([asset]);
158
+ }
159
+ }
160
+
161
+ function handleUploaderClose() {
162
+ showCreateModal = false;
163
+ pastedFile = null;
164
+ }
165
+
166
+ function handleUpload() {
167
+ pastedFile = null;
168
+ showCreateModal = true;
169
+ }
170
+
171
+ function handleCreate(data: {
172
+ file: File;
173
+ name: string;
174
+ description: string;
175
+ altText: string;
176
+ }) {
177
+ console.log('Create asset:', data);
178
+ showCreateModal = false;
179
+ }
180
+
181
+ function handleDelete(toDelete: PersistedAsset[]) {
182
+ assets = assets.filter(
183
+ (asset) => !toDelete.some((item) => item.id === asset.id),
184
+ );
185
+ console.log(
186
+ 'Delete assets:',
187
+ toDelete.map((asset) => asset.id),
188
+ );
189
+ selectedIds = new Set();
190
+ }
191
+
192
+ function handlePaste(event: ClipboardEvent) {
193
+ const items = event.clipboardData?.items;
194
+ if (!items) return;
195
+
196
+ for (const item of Array.from(items)) {
197
+ if (!item.type.startsWith('image/')) continue;
198
+
199
+ const file = item.getAsFile();
200
+ if (!file) continue;
201
+
202
+ event.preventDefault();
203
+ pastedFile = file;
204
+ showCreateModal = true;
205
+ break;
206
+ }
207
+ }
208
+
209
+ function handleManagerDragOver(event: DragEvent) {
210
+ event.preventDefault();
211
+ managerDragOver = true;
212
+ }
213
+
214
+ function handleManagerDragLeave(event: DragEvent) {
215
+ const relatedTarget = event.relatedTarget as Node | null;
216
+ const currentTarget = event.currentTarget as Node;
217
+ if (relatedTarget && currentTarget.contains(relatedTarget)) return;
218
+ managerDragOver = false;
219
+ }
220
+
221
+ function handleManagerDrop(event: DragEvent) {
222
+ event.preventDefault();
223
+ managerDragOver = false;
224
+ const file = event.dataTransfer?.files?.[0];
225
+ if (file) {
226
+ pastedFile = file;
227
+ showCreateModal = true;
228
+ }
229
+ }
230
+ </script>
231
+
232
+ <!-- svelte-ignore a11y_no_static_element_interactions -->
233
+ <div
234
+ class="asset-manager"
235
+ class:asset-manager--drag-over={managerDragOver}
236
+ onpaste={handlePaste}
237
+ ondragover={handleManagerDragOver}
238
+ ondragleave={handleManagerDragLeave}
239
+ ondrop={handleManagerDrop}
240
+ >
241
+ <!-- Toolbar -->
242
+ <AssetToolbar
243
+ {view}
244
+ {filters}
245
+ {sort}
246
+ onViewChange={handleViewChange}
247
+ onFilterChange={handleFilterChange}
248
+ onSortChange={handleSortChange}
249
+ onUpload={handleUpload}
250
+ />
251
+
252
+ <!-- Action Bar (visible when items selected) -->
253
+ <ActionBar
254
+ selectedAssets={selectedAssets}
255
+ {customActions}
256
+ onClearSelection={handleClearSelection}
257
+ onDelete={handleDelete}
258
+ />
259
+
260
+ <!-- Main content area -->
261
+ <div class="asset-manager__content">
262
+ {#if view === 'grid'}
263
+ <AssetGrid
264
+ {assets}
265
+ {selectedIds}
266
+ {loading}
267
+ {mode}
268
+ onSelectionChange={handleSelectionChange}
269
+ onAssetClick={handleAssetClick}
270
+ onAssetDblClick={handleAssetDblClick}
271
+ />
272
+ {:else if view === 'list'}
273
+ <AssetList
274
+ {assets}
275
+ {selectedIds}
276
+ {sort}
277
+ {loading}
278
+ onSelectionChange={handleSelectionChange}
279
+ onAssetClick={handleAssetClick}
280
+ onSortChange={handleSortChange}
281
+ />
282
+ {/if}
283
+ </div>
284
+
285
+ <!-- Asset Detail Drawer -->
286
+ <AssetDetail
287
+ asset={detailAsset}
288
+ open={showDetail}
289
+ onclose={handleDetailClose}
290
+ onsave={handleDetailSave}
291
+ ondelete={(asset) => {
292
+ handleDelete([asset as PersistedAsset]);
293
+ handleDetailClose();
294
+ }}
295
+ />
296
+
297
+ <!-- Drag overlay indicator -->
298
+ {#if managerDragOver}
299
+ <div class="drag-overlay">
300
+ <div class="drag-overlay__content">
301
+ <svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
302
+ <path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4"></path>
303
+ <polyline points="17 8 12 3 7 8"></polyline>
304
+ <line x1="12" y1="3" x2="12" y2="15"></line>
305
+ </svg>
306
+ <p>{t(M['assets.asset_manager.drop_file_to_upload'])}</p>
307
+ </div>
308
+ </div>
309
+ {/if}
310
+
311
+ <!-- Create modal or custom uploader -->
312
+ {#if uploader}
313
+ {@render uploader({
314
+ open: showCreateModal,
315
+ initialFile: pastedFile,
316
+ onClose: handleUploaderClose,
317
+ onclose: handleUploaderClose,
318
+ onCreate: handleCreate,
319
+ oncreate: handleCreate
320
+ })}
321
+ {:else}
322
+ <CreateAssetModal
323
+ open={showCreateModal}
324
+ initialFile={pastedFile}
325
+ oncreate={handleCreate}
326
+ onclose={handleUploaderClose}
327
+ />
328
+ {/if}
329
+ </div>
330
+
331
+ <style>
332
+ .asset-manager {
333
+ position: relative;
334
+ display: flex;
335
+ flex-direction: column;
336
+ height: 100%;
337
+ min-height: 400px;
338
+ background: var(--smrt-color-surface, #ffffff);
339
+ border: 1px solid var(--smrt-color-outline-variant, #e5e7eb);
340
+ border-radius: var(--smrt-radius-large, 0.75rem);
341
+ overflow: hidden;
342
+ font-family: var(--smrt-font-family, inherit);
343
+ }
344
+
345
+ .asset-manager--drag-over {
346
+ border-color: var(--smrt-color-primary, #005ac1);
347
+ }
348
+
349
+ .asset-manager__content {
350
+ flex: 1;
351
+ overflow-y: auto;
352
+ }
353
+
354
+ /* Drag overlay */
355
+ .drag-overlay {
356
+ position: absolute;
357
+ inset: 0;
358
+ display: flex;
359
+ align-items: center;
360
+ justify-content: center;
361
+ background: color-mix(in srgb, var(--smrt-color-primary) 8%, transparent);
362
+ border: 2px dashed var(--smrt-color-primary, #005ac1);
363
+ border-radius: var(--smrt-radius-large, 0.75rem);
364
+ z-index: 10;
365
+ pointer-events: none;
366
+ }
367
+
368
+ .drag-overlay__content {
369
+ display: flex;
370
+ flex-direction: column;
371
+ align-items: center;
372
+ gap: var(--smrt-spacing-2, 0.5rem);
373
+ color: var(--smrt-color-primary, #005ac1);
374
+ font-weight: var(--smrt-typography-weight-medium, 500);
375
+ }
376
+
377
+ .drag-overlay__content p {
378
+ margin: 0;
379
+ font-size: var(--smrt-typography-body-large-size, 1.1rem);
380
+ }
381
+ </style>
@@ -0,0 +1,5 @@
1
+ import type { AssetManagerProps } from './types';
2
+ declare const AssetManager: import("svelte").Component<AssetManagerProps, {}, "">;
3
+ type AssetManager = ReturnType<typeof AssetManager>;
4
+ export default AssetManager;
5
+ //# sourceMappingURL=AssetManager.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AssetManager.svelte.d.ts","sourceRoot":"","sources":["../../src/svelte/AssetManager.svelte.ts"],"names":[],"mappings":"AA8BA,OAAO,KAAK,EAEV,iBAAiB,EAIlB,MAAM,SAAS,CAAC;AAyQjB,QAAA,MAAM,YAAY,uDAAwC,CAAC;AAC3D,KAAK,YAAY,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;AACpD,eAAe,YAAY,CAAC"}