@proveanything/smartlinks 1.13.1 → 1.13.3

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/dist/api/asset.js CHANGED
@@ -316,7 +316,7 @@ export var asset;
316
316
  if (typeof options.offset === 'number')
317
317
  params.set('offset', String(options.offset));
318
318
  const qs = params.toString();
319
- const path = `/admin/collection/${encodeURIComponent(options.collectionId)}/assets${qs ? `?${qs}` : ''}`;
319
+ const path = `/admin/collection/${encodeURIComponent(options.collectionId)}/asset${qs ? `?${qs}` : ''}`;
320
320
  return request(path);
321
321
  }
322
322
  asset.listAdmin = listAdmin;
@@ -324,7 +324,7 @@ export var asset;
324
324
  * Get a single asset by ID (admin).
325
325
  */
326
326
  async function getAdmin(collectionId, assetId) {
327
- const path = `/admin/collection/${encodeURIComponent(collectionId)}/assets/${encodeURIComponent(assetId)}`;
327
+ const path = `/admin/collection/${encodeURIComponent(collectionId)}/asset/${encodeURIComponent(assetId)}`;
328
328
  return request(path);
329
329
  }
330
330
  asset.getAdmin = getAdmin;
@@ -332,7 +332,7 @@ export var asset;
332
332
  * Update asset metadata (admin). Use `replaceFile` to swap the file.
333
333
  */
334
334
  async function updateAdmin(options) {
335
- const path = `/admin/collection/${encodeURIComponent(options.collectionId)}/assets/${encodeURIComponent(options.assetId)}`;
335
+ const path = `/admin/collection/${encodeURIComponent(options.collectionId)}/asset/${encodeURIComponent(options.assetId)}`;
336
336
  const { collectionId: _c, assetId: _a } = options, body = __rest(options, ["collectionId", "assetId"]);
337
337
  return put(path, body);
338
338
  }
@@ -342,7 +342,7 @@ export var asset;
342
342
  * into `versions[]` on the asset.
343
343
  */
344
344
  async function replaceFile(options) {
345
- const path = `/admin/collection/${encodeURIComponent(options.collectionId)}/assets/${encodeURIComponent(options.assetId)}/replace`;
345
+ const path = `/admin/collection/${encodeURIComponent(options.collectionId)}/asset/${encodeURIComponent(options.assetId)}/replace`;
346
346
  const formData = new FormData();
347
347
  formData.append('file', options.file);
348
348
  if (options.onProgress && typeof window !== 'undefined' && !isProxyEnabled()) {
@@ -395,7 +395,7 @@ export var asset;
395
395
  if (typeof options.graceDays === 'number')
396
396
  params.set('graceDays', String(options.graceDays));
397
397
  const qs = params.toString();
398
- const path = `/admin/collection/${encodeURIComponent(options.collectionId)}/assets/${encodeURIComponent(options.assetId)}${qs ? `?${qs}` : ''}`;
398
+ const path = `/admin/collection/${encodeURIComponent(options.collectionId)}/asset/${encodeURIComponent(options.assetId)}${qs ? `?${qs}` : ''}`;
399
399
  return del(path);
400
400
  }
401
401
  asset.deleteAdmin = deleteAdmin;
@@ -403,7 +403,7 @@ export var asset;
403
403
  * Restore a soft-deleted asset (clears `deletedAt`).
404
404
  */
405
405
  async function restoreAdmin(collectionId, assetId) {
406
- const path = `/admin/collection/${encodeURIComponent(collectionId)}/assets/${encodeURIComponent(assetId)}/restore`;
406
+ const path = `/admin/collection/${encodeURIComponent(collectionId)}/asset/${encodeURIComponent(assetId)}/restore`;
407
407
  return post(path, {});
408
408
  }
409
409
  asset.restoreAdmin = restoreAdmin;
@@ -411,7 +411,7 @@ export var asset;
411
411
  * Soft-delete multiple assets in one request.
412
412
  */
413
413
  async function bulkDelete(options) {
414
- const path = `/admin/collection/${encodeURIComponent(options.collectionId)}/assets/bulk-delete`;
414
+ const path = `/admin/collection/${encodeURIComponent(options.collectionId)}/asset/bulk-delete`;
415
415
  const body = { assetIds: options.assetIds };
416
416
  if (typeof options.graceDays === 'number')
417
417
  body.graceDays = options.graceDays;
@@ -1,6 +1,6 @@
1
1
  # Smartlinks API Summary
2
2
 
3
- Version: 1.13.1 | Generated: 2026-05-07T10:59:17.642Z
3
+ Version: 1.13.3 | Generated: 2026-05-08T10:30:25.776Z
4
4
 
5
5
  This is a concise summary of all available API functions and types.
6
6
 
@@ -1439,6 +1439,16 @@ interface DeepLinkEntry {
1439
1439
  * Do NOT include platform context params (collectionId, appId, productId, etc.) —
1440
1440
  * those are injected by the platform automatically.
1441
1441
  params?: Record<string, string>;
1442
+ * When `true`, this entry is also available as a dynamic data context for widgets
1443
+ * (in addition to being a navigable page / container route).
1444
+ *
1445
+ * Entries with `widget: true` appear in the widget config picker so an admin can
1446
+ * select this dataset to drive how the widget renders. The widget receives `params`
1447
+ * and decides its own presentation — no separate rendering contract is required.
1448
+ *
1449
+ * Omit (or `false`) for entries that are only meaningful as full-page navigation
1450
+ * (e.g. multi-step forms, settings pages, checkout flows).
1451
+ widget?: true;
1442
1452
  }
1443
1453
  ```
1444
1454
 
@@ -103,7 +103,7 @@ All admin endpoints require authentication. Base path: `/api/admin/collection/:c
103
103
  ### List assets
104
104
 
105
105
  ```
106
- GET /assets
106
+ GET /asset
107
107
  ```
108
108
 
109
109
  | Parameter | Type | Description |
@@ -140,7 +140,7 @@ const { data, total } = await Api.asset.listAdmin({
140
140
  ### Get asset
141
141
 
142
142
  ```
143
- GET /assets/:assetId
143
+ GET /asset/:assetId
144
144
  ```
145
145
 
146
146
  Returns `Asset` or `404`.
@@ -152,7 +152,7 @@ Returns `Asset` or `404`.
152
152
  ### Upload asset
153
153
 
154
154
  ```
155
- POST /assets
155
+ POST /asset
156
156
  ```
157
157
 
158
158
  Use the existing `Api.asset.upload()` method (file) or `Api.asset.uploadFromUrl()` (URL import).
@@ -162,7 +162,7 @@ Use the existing `Api.asset.upload()` method (file) or `Api.asset.uploadFromUrl(
162
162
  ### Update asset metadata
163
163
 
164
164
  ```
165
- PUT /assets/:assetId
165
+ PUT /asset/:assetId
166
166
  ```
167
167
 
168
168
  Updates metadata only. Use `/replace` to swap the file.
@@ -182,7 +182,7 @@ await Api.asset.updateAdmin({
182
182
  ### Replace file
183
183
 
184
184
  ```
185
- POST /assets/:assetId/replace
185
+ POST /asset/:assetId/replace
186
186
  ```
187
187
 
188
188
  Replaces the file; the previous URL is snapshotted into `versions[]`.
@@ -201,7 +201,7 @@ await Api.asset.replaceFile({
201
201
  ### Delete asset (soft)
202
202
 
203
203
  ```
204
- DELETE /assets/:assetId?graceDays=30
204
+ DELETE /asset/:assetId?graceDays=30
205
205
  ```
206
206
 
207
207
  Sets `deletedAt` and schedules CDN purge after `graceDays` (default 30). Recoverable until purge.
@@ -215,7 +215,7 @@ await Api.asset.deleteAdmin({ collectionId: 'my-collection', assetId: 'abc123',
215
215
  ### Restore asset
216
216
 
217
217
  ```
218
- POST /assets/:assetId/restore
218
+ POST /asset/:assetId/restore
219
219
  ```
220
220
 
221
221
  Clears `deletedAt`. Asset becomes active again.
@@ -229,7 +229,7 @@ await Api.asset.restoreAdmin('my-collection', 'abc123')
229
229
  ### Bulk delete
230
230
 
231
231
  ```
232
- POST /assets/bulk-delete
232
+ POST /asset/bulk-delete
233
233
  ```
234
234
 
235
235
  ```typescript
@@ -21,6 +21,7 @@ This doc covers both sides of the contract: **suppliers** declare their navigabl
21
21
  - [Portal Navigation Menu](#portal-navigation-menu)
22
22
  - [AI Orchestration](#ai-orchestration)
23
23
  - [Cross-App Navigation](#cross-app-navigation)
24
+ - [Widget Config Picker — Dynamic Data Context](#widget-config-picker--dynamic-data-context)
24
25
  - [Link Picker — Admin Configuration](#link-picker--admin-configuration)
25
26
  - [Rendering Links at Runtime](#rendering-links-at-runtime)
26
27
  - [TypeScript Types](#typescript-types)
@@ -191,6 +192,18 @@ interface DeepLinkEntry {
191
192
  * are injected automatically — do NOT include them here.
192
193
  */
193
194
  params?: Record<string, string>;
195
+
196
+ /**
197
+ * When `true`, this entry is also available as a dynamic data context for widgets.
198
+ *
199
+ * Entries with `widget: true` appear in the widget config picker so an admin can
200
+ * select this dataset to drive how the widget renders. The widget receives `params`
201
+ * and decides its own presentation — no additional contract is required.
202
+ *
203
+ * Omit for entries that are only meaningful as full-page navigation
204
+ * (e.g. multi-step forms, settings pages, checkout flows).
205
+ */
206
+ widget?: true;
194
207
  }
195
208
  ```
196
209
 
@@ -199,9 +212,12 @@ interface DeepLinkEntry {
199
212
  | `title` | ✅ | Displayed in menus and offered to AI agents. Should be concise and human-readable. |
200
213
  | `path` | ❌ | Hash route within the app. Defaults to `/` if omitted. |
201
214
  | `params` | ❌ | App-specific query params only. Platform params are injected automatically. |
215
+ | `widget` | ❌ | Set to `true` to make this entry selectable in the widget config picker as a dynamic data context. |
202
216
 
203
217
  > **Tip:** Different entries can share the same `path` if they differ by `params` — for example, two entries pointing at `/settings` with different `tab` params.
204
218
 
219
+ > **Container vs widget:** All entries are navigable via a container (full-page render). `widget: true` additionally opts the entry into the widget picker — use it for datasets that make sense as a compact card, e.g. a specific gallery view or a named product showcase. Don't set it on management/settings pages.
220
+
205
221
  ---
206
222
 
207
223
  ## Quick Start
@@ -500,6 +516,58 @@ if (entry) {
500
516
 
501
517
  ---
502
518
 
519
+ ### Widget Config Picker — Dynamic Data Context
520
+
521
+ When a widget's admin settings include a "which view/dataset to show" picker, use the `widget: true` entries from `appConfig.linkable` as the source. Filter to entries with `widget: true` across all installed apps (or just the widget's own app) and let the admin select one. Persist only the `params` — the title is ephemeral display.
522
+
523
+ ```typescript
524
+ // Fetch linkable entries for a specific app and filter to widget-eligible ones
525
+ const config = await SL.appConfiguration.getConfig({ collectionId, appId: 'media-gallery' });
526
+ const widgetViews = (config?.linkable ?? []).filter(e => e.widget === true);
527
+
528
+ // widgetViews might be:
529
+ // [
530
+ // { title: "Gallery: Summer 2026", params: { viewId: "sum26" }, widget: true },
531
+ // { title: "Gallery: Archive", params: { viewId: "archive" }, widget: true },
532
+ // ]
533
+ ```
534
+
535
+ ```tsx
536
+ // Widget admin settings — view picker
537
+ <select
538
+ value={JSON.stringify(settings.viewParams ?? {})}
539
+ onChange={e => saveSettings({ ...settings, viewParams: JSON.parse(e.target.value) })}
540
+ >
541
+ <option value="{}">— Default view —</option>
542
+ {widgetViews.map(entry => (
543
+ <option key={entry.title} value={JSON.stringify(entry.params)}>
544
+ {entry.title}
545
+ </option>
546
+ ))}
547
+ </select>
548
+ ```
549
+
550
+ The widget then reads `settings.viewParams` at render time and uses it to fetch or filter its data. The widget owns all rendering decisions — `params` is just a key.
551
+
552
+ **Supply side** — when the media gallery creates or renames a gallery view, it syncs `appConfig.linkable` as usual, marking card-appropriate views with `widget: true`:
553
+
554
+ ```typescript
555
+ const linkable = galleryViews.map(view => ({
556
+ title: view.name,
557
+ params: { viewId: view.id },
558
+ widget: view.isCardReady ? true : undefined, // only opt in views that make sense as cards
559
+ }));
560
+
561
+ await SL.appConfiguration.setConfig({
562
+ collectionId, appId: 'media-gallery', admin: true,
563
+ config: { ...current, linkable },
564
+ });
565
+ ```
566
+
567
+ > **Rule of thumb:** set `widget: true` on dataset entries (gallery views, product showcases, named feeds). Leave it off on management/utility pages (settings, upload config, multi-step forms).
568
+
569
+ ---
570
+
503
571
  ### Link Picker — Admin Configuration
504
572
 
505
573
  When an admin needs to configure any outbound link — to another installed app, a specific page within an app, or an external URL — **never use a bare text input**. Use `<LinkPicker />` from `@proveanything/smartlinks-utils-ui`. It handles all three `LinkTarget` kinds and produces a single `LinkTarget` value to persist.
@@ -656,6 +724,12 @@ export interface DeepLinkEntry {
656
724
  * Do NOT include platform context params (collectionId, appId, etc.).
657
725
  */
658
726
  params?: Record<string, string>;
727
+ /**
728
+ * When `true`, this entry is also available as a dynamic data context for widgets
729
+ * (in addition to being a navigable page / container route).
730
+ * Omit for entries that are only meaningful as full-page navigation.
731
+ */
732
+ widget?: true;
659
733
  }
660
734
 
661
735
  /** Convenience alias for an array of DeepLinkEntry */
package/dist/openapi.yaml CHANGED
@@ -105,7 +105,7 @@ paths:
105
105
  application/json:
106
106
  schema:
107
107
  $ref: "#/components/schemas/CollectionCreateRequest"
108
- /admin/collection/assets:
108
+ /admin/collection/asset:
109
109
  get:
110
110
  tags:
111
111
  - asset
@@ -216,7 +216,7 @@ paths:
216
216
  application/json:
217
217
  schema:
218
218
  $ref: "#/components/schemas/UpdateAssetOptions"
219
- /admin/collection/assets/replace:
219
+ /admin/collection/asset/replace:
220
220
  post:
221
221
  tags:
222
222
  - asset
@@ -1050,7 +1050,7 @@ paths:
1050
1050
  application/json:
1051
1051
  schema:
1052
1052
  $ref: "#/components/schemas/ResponsesRequest"
1053
- /admin/collection/{collectionId}/assets/{assetId}:
1053
+ /admin/collection/{collectionId}/asset/{assetId}:
1054
1054
  get:
1055
1055
  tags:
1056
1056
  - asset
@@ -1082,7 +1082,7 @@ paths:
1082
1082
  description: Unauthorized
1083
1083
  404:
1084
1084
  description: Not found
1085
- /admin/collection/{collectionId}/assets/{assetId}/restore:
1085
+ /admin/collection/{collectionId}/asset/{assetId}/restore:
1086
1086
  post:
1087
1087
  tags:
1088
1088
  - asset
@@ -15058,6 +15058,9 @@ components:
15058
15058
  type: object
15059
15059
  additionalProperties:
15060
15060
  type: string
15061
+ widget:
15062
+ type: object
15063
+ additionalProperties: true
15061
15064
  required:
15062
15065
  - title
15063
15066
  ExecutorContext:
@@ -96,6 +96,18 @@ export interface DeepLinkEntry {
96
96
  * those are injected by the platform automatically.
97
97
  */
98
98
  params?: Record<string, string>;
99
+ /**
100
+ * When `true`, this entry is also available as a dynamic data context for widgets
101
+ * (in addition to being a navigable page / container route).
102
+ *
103
+ * Entries with `widget: true` appear in the widget config picker so an admin can
104
+ * select this dataset to drive how the widget renders. The widget receives `params`
105
+ * and decides its own presentation — no separate rendering contract is required.
106
+ *
107
+ * Omit (or `false`) for entries that are only meaningful as full-page navigation
108
+ * (e.g. multi-step forms, settings pages, checkout flows).
109
+ */
110
+ widget?: true;
99
111
  }
100
112
  /**
101
113
  * Context object passed to every executor factory function.
@@ -1,6 +1,6 @@
1
1
  # Smartlinks API Summary
2
2
 
3
- Version: 1.13.1 | Generated: 2026-05-07T10:59:17.642Z
3
+ Version: 1.13.3 | Generated: 2026-05-08T10:30:25.776Z
4
4
 
5
5
  This is a concise summary of all available API functions and types.
6
6
 
@@ -1439,6 +1439,16 @@ interface DeepLinkEntry {
1439
1439
  * Do NOT include platform context params (collectionId, appId, productId, etc.) —
1440
1440
  * those are injected by the platform automatically.
1441
1441
  params?: Record<string, string>;
1442
+ * When `true`, this entry is also available as a dynamic data context for widgets
1443
+ * (in addition to being a navigable page / container route).
1444
+ *
1445
+ * Entries with `widget: true` appear in the widget config picker so an admin can
1446
+ * select this dataset to drive how the widget renders. The widget receives `params`
1447
+ * and decides its own presentation — no separate rendering contract is required.
1448
+ *
1449
+ * Omit (or `false`) for entries that are only meaningful as full-page navigation
1450
+ * (e.g. multi-step forms, settings pages, checkout flows).
1451
+ widget?: true;
1442
1452
  }
1443
1453
  ```
1444
1454
 
package/docs/assets.md CHANGED
@@ -103,7 +103,7 @@ All admin endpoints require authentication. Base path: `/api/admin/collection/:c
103
103
  ### List assets
104
104
 
105
105
  ```
106
- GET /assets
106
+ GET /asset
107
107
  ```
108
108
 
109
109
  | Parameter | Type | Description |
@@ -140,7 +140,7 @@ const { data, total } = await Api.asset.listAdmin({
140
140
  ### Get asset
141
141
 
142
142
  ```
143
- GET /assets/:assetId
143
+ GET /asset/:assetId
144
144
  ```
145
145
 
146
146
  Returns `Asset` or `404`.
@@ -152,7 +152,7 @@ Returns `Asset` or `404`.
152
152
  ### Upload asset
153
153
 
154
154
  ```
155
- POST /assets
155
+ POST /asset
156
156
  ```
157
157
 
158
158
  Use the existing `Api.asset.upload()` method (file) or `Api.asset.uploadFromUrl()` (URL import).
@@ -162,7 +162,7 @@ Use the existing `Api.asset.upload()` method (file) or `Api.asset.uploadFromUrl(
162
162
  ### Update asset metadata
163
163
 
164
164
  ```
165
- PUT /assets/:assetId
165
+ PUT /asset/:assetId
166
166
  ```
167
167
 
168
168
  Updates metadata only. Use `/replace` to swap the file.
@@ -182,7 +182,7 @@ await Api.asset.updateAdmin({
182
182
  ### Replace file
183
183
 
184
184
  ```
185
- POST /assets/:assetId/replace
185
+ POST /asset/:assetId/replace
186
186
  ```
187
187
 
188
188
  Replaces the file; the previous URL is snapshotted into `versions[]`.
@@ -201,7 +201,7 @@ await Api.asset.replaceFile({
201
201
  ### Delete asset (soft)
202
202
 
203
203
  ```
204
- DELETE /assets/:assetId?graceDays=30
204
+ DELETE /asset/:assetId?graceDays=30
205
205
  ```
206
206
 
207
207
  Sets `deletedAt` and schedules CDN purge after `graceDays` (default 30). Recoverable until purge.
@@ -215,7 +215,7 @@ await Api.asset.deleteAdmin({ collectionId: 'my-collection', assetId: 'abc123',
215
215
  ### Restore asset
216
216
 
217
217
  ```
218
- POST /assets/:assetId/restore
218
+ POST /asset/:assetId/restore
219
219
  ```
220
220
 
221
221
  Clears `deletedAt`. Asset becomes active again.
@@ -229,7 +229,7 @@ await Api.asset.restoreAdmin('my-collection', 'abc123')
229
229
  ### Bulk delete
230
230
 
231
231
  ```
232
- POST /assets/bulk-delete
232
+ POST /asset/bulk-delete
233
233
  ```
234
234
 
235
235
  ```typescript
@@ -21,6 +21,7 @@ This doc covers both sides of the contract: **suppliers** declare their navigabl
21
21
  - [Portal Navigation Menu](#portal-navigation-menu)
22
22
  - [AI Orchestration](#ai-orchestration)
23
23
  - [Cross-App Navigation](#cross-app-navigation)
24
+ - [Widget Config Picker — Dynamic Data Context](#widget-config-picker--dynamic-data-context)
24
25
  - [Link Picker — Admin Configuration](#link-picker--admin-configuration)
25
26
  - [Rendering Links at Runtime](#rendering-links-at-runtime)
26
27
  - [TypeScript Types](#typescript-types)
@@ -191,6 +192,18 @@ interface DeepLinkEntry {
191
192
  * are injected automatically — do NOT include them here.
192
193
  */
193
194
  params?: Record<string, string>;
195
+
196
+ /**
197
+ * When `true`, this entry is also available as a dynamic data context for widgets.
198
+ *
199
+ * Entries with `widget: true` appear in the widget config picker so an admin can
200
+ * select this dataset to drive how the widget renders. The widget receives `params`
201
+ * and decides its own presentation — no additional contract is required.
202
+ *
203
+ * Omit for entries that are only meaningful as full-page navigation
204
+ * (e.g. multi-step forms, settings pages, checkout flows).
205
+ */
206
+ widget?: true;
194
207
  }
195
208
  ```
196
209
 
@@ -199,9 +212,12 @@ interface DeepLinkEntry {
199
212
  | `title` | ✅ | Displayed in menus and offered to AI agents. Should be concise and human-readable. |
200
213
  | `path` | ❌ | Hash route within the app. Defaults to `/` if omitted. |
201
214
  | `params` | ❌ | App-specific query params only. Platform params are injected automatically. |
215
+ | `widget` | ❌ | Set to `true` to make this entry selectable in the widget config picker as a dynamic data context. |
202
216
 
203
217
  > **Tip:** Different entries can share the same `path` if they differ by `params` — for example, two entries pointing at `/settings` with different `tab` params.
204
218
 
219
+ > **Container vs widget:** All entries are navigable via a container (full-page render). `widget: true` additionally opts the entry into the widget picker — use it for datasets that make sense as a compact card, e.g. a specific gallery view or a named product showcase. Don't set it on management/settings pages.
220
+
205
221
  ---
206
222
 
207
223
  ## Quick Start
@@ -500,6 +516,58 @@ if (entry) {
500
516
 
501
517
  ---
502
518
 
519
+ ### Widget Config Picker — Dynamic Data Context
520
+
521
+ When a widget's admin settings include a "which view/dataset to show" picker, use the `widget: true` entries from `appConfig.linkable` as the source. Filter to entries with `widget: true` across all installed apps (or just the widget's own app) and let the admin select one. Persist only the `params` — the title is ephemeral display.
522
+
523
+ ```typescript
524
+ // Fetch linkable entries for a specific app and filter to widget-eligible ones
525
+ const config = await SL.appConfiguration.getConfig({ collectionId, appId: 'media-gallery' });
526
+ const widgetViews = (config?.linkable ?? []).filter(e => e.widget === true);
527
+
528
+ // widgetViews might be:
529
+ // [
530
+ // { title: "Gallery: Summer 2026", params: { viewId: "sum26" }, widget: true },
531
+ // { title: "Gallery: Archive", params: { viewId: "archive" }, widget: true },
532
+ // ]
533
+ ```
534
+
535
+ ```tsx
536
+ // Widget admin settings — view picker
537
+ <select
538
+ value={JSON.stringify(settings.viewParams ?? {})}
539
+ onChange={e => saveSettings({ ...settings, viewParams: JSON.parse(e.target.value) })}
540
+ >
541
+ <option value="{}">— Default view —</option>
542
+ {widgetViews.map(entry => (
543
+ <option key={entry.title} value={JSON.stringify(entry.params)}>
544
+ {entry.title}
545
+ </option>
546
+ ))}
547
+ </select>
548
+ ```
549
+
550
+ The widget then reads `settings.viewParams` at render time and uses it to fetch or filter its data. The widget owns all rendering decisions — `params` is just a key.
551
+
552
+ **Supply side** — when the media gallery creates or renames a gallery view, it syncs `appConfig.linkable` as usual, marking card-appropriate views with `widget: true`:
553
+
554
+ ```typescript
555
+ const linkable = galleryViews.map(view => ({
556
+ title: view.name,
557
+ params: { viewId: view.id },
558
+ widget: view.isCardReady ? true : undefined, // only opt in views that make sense as cards
559
+ }));
560
+
561
+ await SL.appConfiguration.setConfig({
562
+ collectionId, appId: 'media-gallery', admin: true,
563
+ config: { ...current, linkable },
564
+ });
565
+ ```
566
+
567
+ > **Rule of thumb:** set `widget: true` on dataset entries (gallery views, product showcases, named feeds). Leave it off on management/utility pages (settings, upload config, multi-step forms).
568
+
569
+ ---
570
+
503
571
  ### Link Picker — Admin Configuration
504
572
 
505
573
  When an admin needs to configure any outbound link — to another installed app, a specific page within an app, or an external URL — **never use a bare text input**. Use `<LinkPicker />` from `@proveanything/smartlinks-utils-ui`. It handles all three `LinkTarget` kinds and produces a single `LinkTarget` value to persist.
@@ -656,6 +724,12 @@ export interface DeepLinkEntry {
656
724
  * Do NOT include platform context params (collectionId, appId, etc.).
657
725
  */
658
726
  params?: Record<string, string>;
727
+ /**
728
+ * When `true`, this entry is also available as a dynamic data context for widgets
729
+ * (in addition to being a navigable page / container route).
730
+ * Omit for entries that are only meaningful as full-page navigation.
731
+ */
732
+ widget?: true;
659
733
  }
660
734
 
661
735
  /** Convenience alias for an array of DeepLinkEntry */
package/openapi.yaml CHANGED
@@ -105,7 +105,7 @@ paths:
105
105
  application/json:
106
106
  schema:
107
107
  $ref: "#/components/schemas/CollectionCreateRequest"
108
- /admin/collection/assets:
108
+ /admin/collection/asset:
109
109
  get:
110
110
  tags:
111
111
  - asset
@@ -216,7 +216,7 @@ paths:
216
216
  application/json:
217
217
  schema:
218
218
  $ref: "#/components/schemas/UpdateAssetOptions"
219
- /admin/collection/assets/replace:
219
+ /admin/collection/asset/replace:
220
220
  post:
221
221
  tags:
222
222
  - asset
@@ -1050,7 +1050,7 @@ paths:
1050
1050
  application/json:
1051
1051
  schema:
1052
1052
  $ref: "#/components/schemas/ResponsesRequest"
1053
- /admin/collection/{collectionId}/assets/{assetId}:
1053
+ /admin/collection/{collectionId}/asset/{assetId}:
1054
1054
  get:
1055
1055
  tags:
1056
1056
  - asset
@@ -1082,7 +1082,7 @@ paths:
1082
1082
  description: Unauthorized
1083
1083
  404:
1084
1084
  description: Not found
1085
- /admin/collection/{collectionId}/assets/{assetId}/restore:
1085
+ /admin/collection/{collectionId}/asset/{assetId}/restore:
1086
1086
  post:
1087
1087
  tags:
1088
1088
  - asset
@@ -15058,6 +15058,9 @@ components:
15058
15058
  type: object
15059
15059
  additionalProperties:
15060
15060
  type: string
15061
+ widget:
15062
+ type: object
15063
+ additionalProperties: true
15061
15064
  required:
15062
15065
  - title
15063
15066
  ExecutorContext:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@proveanything/smartlinks",
3
- "version": "1.13.1",
3
+ "version": "1.13.3",
4
4
  "description": "Official JavaScript/TypeScript SDK for the Smartlinks API",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",