@jini-ai/admin 0.4.3 → 0.4.5

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 (48) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/dist/comments/comments.module.d.ts +4 -4
  3. package/dist/comments/messages.en.d.ts +4 -4
  4. package/dist/comments/messages.en.js +4 -4
  5. package/dist/comments/react/components/QueueSection.js +1 -1
  6. package/dist/comments/react/index.d.ts +16 -16
  7. package/dist/forms/forms.module.d.ts +3 -2
  8. package/dist/forms/html-rules.d.ts +14 -0
  9. package/dist/forms/html-rules.js +10 -0
  10. package/dist/forms/messages.en.d.ts +3 -2
  11. package/dist/forms/messages.en.js +4 -3
  12. package/dist/forms/react/components/FormEditorHeaderText.js +2 -2
  13. package/dist/forms/react/components/FormSubmissionDetail.d.ts +3 -1
  14. package/dist/forms/react/components/FormSubmissionDetail.js +3 -2
  15. package/dist/forms/react/components/FormSubmissions.js +1 -1
  16. package/dist/forms/react/hooks/forms-view.hooks.d.ts +1 -6
  17. package/dist/forms/react/hooks/forms-view.hooks.js +3 -4
  18. package/dist/forms/react/index.d.ts +9 -6
  19. package/dist/forms/react/index.js +3 -1
  20. package/dist/forms/react/pages/FormsList.js +2 -7
  21. package/dist/react/components/ConfirmDialog/ConfirmDialog.hooks.d.ts +3 -1
  22. package/dist/react/components/ConfirmDialog/ConfirmDialog.hooks.js +5 -1
  23. package/dist/react/components/ConfirmDialog/use-confirm-request.d.ts +22 -0
  24. package/dist/react/components/ConfirmDialog/use-confirm-request.js +53 -0
  25. package/dist/react/components/RowMenu/RowMenu.hooks.js +14 -3
  26. package/dist/react/index.d.ts +2 -0
  27. package/dist/react/index.js +1 -0
  28. package/dist/redirects/messages.en.d.ts +9 -7
  29. package/dist/redirects/messages.en.js +14 -9
  30. package/dist/redirects/react/hooks/RedirectsPage.hooks.js +2 -2
  31. package/dist/redirects/react/index.d.ts +36 -28
  32. package/dist/redirects/react/pages/RedirectsPage.js +1 -1
  33. package/dist/redirects/redirects.module.d.ts +9 -7
  34. package/dist/seo/messages.en.d.ts +5 -4
  35. package/dist/seo/messages.en.js +6 -5
  36. package/dist/seo/react/components/MediaRefField.d.ts +4 -0
  37. package/dist/seo/react/components/MediaRefField.js +2 -2
  38. package/dist/seo/react/components/SitemapModal.js +4 -2
  39. package/dist/seo/react/hooks/MediaRefField.hooks.d.ts +4 -1
  40. package/dist/seo/react/hooks/MediaRefField.hooks.js +3 -3
  41. package/dist/seo/react/index.d.ts +15 -12
  42. package/dist/seo/react/options.d.ts +7 -0
  43. package/dist/seo/react/pages/SeoPage.js +23 -23
  44. package/dist/seo/rules.d.ts +6 -0
  45. package/dist/seo/rules.js +8 -0
  46. package/dist/seo/seo.module.d.ts +5 -4
  47. package/dist/widgets/react/pages/WidgetRegionEditor.js +1 -1
  48. package/package.json +6 -6
@@ -12,13 +12,14 @@ const VIEWPORT_MARGIN = 8;
12
12
  * visible jump. Recomputed on scroll and resize too: a portaled menu is a DOM sibling of its anchor,
13
13
  * not a child, so it does not track the anchor's position on its own the way an in-flow popup would.
14
14
  *
15
- * Click-outside closes. Scoped to the open window only, same lifecycle discipline as
15
+ * Click-outside and Escape close. Escape is document-scoped so it also works after focus
16
+ * moves away from the popup. Scoped to the open window only, same lifecycle discipline as
16
17
  * `ConfirmButton`'s armed-only document listeners — a closed, idle `RowMenu` costs nothing beyond
17
18
  * its trigger button even with many mounted per table.
18
19
  *
19
20
  * @complexity O(1) per call and per scroll/resize/mousedown event.
20
21
  */
21
- function trackOpenMenu({ trigger, menu }, { window, document, onPosition, onDismiss }) {
22
+ function trackOpenMenu({ trigger, menu }, { window, document, onPosition, onDismiss, onEscape }) {
22
23
  function reposition() {
23
24
  const rect = trigger.getBoundingClientRect();
24
25
  const menuHeight = menu.offsetHeight;
@@ -41,14 +42,23 @@ function trackOpenMenu({ trigger, menu }, { window, document, onPosition, onDism
41
42
  return;
42
43
  onDismiss();
43
44
  }
45
+ function onDocKeyDown(e) {
46
+ if (e.key !== 'Escape' || e.defaultPrevented)
47
+ return;
48
+ e.preventDefault();
49
+ e.stopPropagation();
50
+ onEscape();
51
+ }
44
52
  reposition();
45
53
  window.addEventListener('scroll', reposition, true);
46
54
  window.addEventListener('resize', reposition);
47
55
  document.addEventListener('mousedown', onDocMouseDown);
56
+ document.addEventListener('keydown', onDocKeyDown);
48
57
  return () => {
49
58
  window.removeEventListener('scroll', reposition, true);
50
59
  window.removeEventListener('resize', reposition);
51
60
  document.removeEventListener('mousedown', onDocMouseDown);
61
+ document.removeEventListener('keydown', onDocKeyDown);
52
62
  };
53
63
  }
54
64
  /**
@@ -89,7 +99,7 @@ export function useRowMenu({ itemCount, window, document }) {
89
99
  let stop;
90
100
  return (menu) => {
91
101
  if (menu)
92
- stop = trackOpenMenu({ trigger: anchor, menu }, { window, document, onPosition: setPosition, onDismiss: () => close(false) });
102
+ stop = trackOpenMenu({ trigger: anchor, menu }, { window, document, onPosition: setPosition, onDismiss: () => close(false), onEscape: () => close(true) });
93
103
  else
94
104
  stop();
95
105
  };
@@ -140,6 +150,7 @@ export function useRowMenu({ itemCount, window, document }) {
140
150
  break;
141
151
  case 'Escape':
142
152
  e.preventDefault();
153
+ e.stopPropagation();
143
154
  close(true);
144
155
  break;
145
156
  case 'Tab':
@@ -37,6 +37,8 @@ export { ConfirmButton } from './components/ConfirmButton/ConfirmButton.js';
37
37
  export type { ConfirmButtonProps } from './components/ConfirmButton/ConfirmButton.js';
38
38
  export { ConfirmDialog } from './components/ConfirmDialog/ConfirmDialog.js';
39
39
  export type { ConfirmDialogProps } from './components/ConfirmDialog/ConfirmDialog.js';
40
+ export { useConfirmRequest } from './components/ConfirmDialog/use-confirm-request.js';
41
+ export type { ConfirmRequestController, ConfirmRequestPresentation } from './components/ConfirmDialog/use-confirm-request.js';
40
42
  export type { ConfirmDialogController, UseConfirmDialog } from './components/ConfirmDialog/ConfirmDialog.hooks.js';
41
43
  export { ConfirmDialogDefaultsProvider } from './components/ConfirmDialog/ConfirmDialog.hooks.js';
42
44
  export type { ConfirmDialogDefaults, ConfirmDialogDefaultsProviderProps, } from './components/ConfirmDialog/ConfirmDialog.hooks.js';
@@ -36,6 +36,7 @@
36
36
  export { resolveTone, toneClassName } from './types.js';
37
37
  export { ConfirmButton } from './components/ConfirmButton/ConfirmButton.js';
38
38
  export { ConfirmDialog } from './components/ConfirmDialog/ConfirmDialog.js';
39
+ export { useConfirmRequest } from './components/ConfirmDialog/use-confirm-request.js';
39
40
  // Host-wide `ConfirmDialog` defaults (today just a translated `cancelLabel`), set once at an app
40
41
  // root rather than passed at every call site — see `ConfirmDialogDefaultsProvider`'s doc comment.
41
42
  export { ConfirmDialogDefaultsProvider } from './components/ConfirmDialog/ConfirmDialog.hooks.js';
@@ -9,8 +9,7 @@ export declare const redirectsMessagesEn: {
9
9
  readonly Import: "Import";
10
10
  readonly Marketing: "Marketing";
11
11
  readonly Redirects: "Redirects";
12
- readonly "Manual URL redirect rules. Rules created automatically from a slug change (source": "Manual URL redirect rules. Rules created automatically from a slug change (source";
13
- readonly ") also show up here.": ") also show up here.";
12
+ readonly "Manage URL redirects, including those created automatically when a page's address changes.": "Manage URL redirects, including those created automatically when a page's address changes.";
14
13
  readonly "Match type": "Match type";
15
14
  readonly "From path": "From path";
16
15
  readonly "To target": "To target";
@@ -31,9 +30,9 @@ export declare const redirectsMessagesEn: {
31
30
  readonly "Loading redirects\u2026": "Loading redirects…";
32
31
  readonly "Not valid JSON.": "Not valid JSON.";
33
32
  readonly "Must be a JSON array of rule objects.": "Must be a JSON array of rule objects.";
34
- readonly exact: "exact";
35
- readonly prefix: "prefix";
36
- readonly wildcard: "wildcard";
33
+ readonly exact: "Exact match";
34
+ readonly prefix: "Starts with";
35
+ readonly wildcard: "Wildcard";
37
36
  readonly Status: "Status";
38
37
  readonly More: "More";
39
38
  readonly Delete: "Delete";
@@ -41,8 +40,8 @@ export declare const redirectsMessagesEn: {
41
40
  readonly Enable: "Enable";
42
41
  readonly "Saving\u2026": "Saving…";
43
42
  readonly Created: "Created";
44
- readonly active: "active";
45
- readonly disabled: "disabled";
43
+ readonly active: "Active";
44
+ readonly disabled: "Disabled";
46
45
  readonly "{created} created, {failed} failed.": "{created} created, {failed} failed.";
47
46
  readonly "Item {index} ({code})": "Item {index} ({code})";
48
47
  readonly "Actions for redirect rule from \"{fromPattern}\"": "Actions for redirect rule from \"{fromPattern}\"";
@@ -50,6 +49,9 @@ export declare const redirectsMessagesEn: {
50
49
  readonly " rule objects (1-500 items)": " rule objects (1-500 items)";
51
50
  readonly "Delete the redirect rule from \"": "Delete the redirect rule from \"";
52
51
  readonly "\"?": "\"?";
52
+ readonly manual: "Manual";
53
+ readonly auto_slug_change: "URL change";
54
+ readonly import: "Imported";
53
55
  };
54
56
  /** English fallback with the same named-placeholder interpolation as the host dictionary. */
55
57
  export declare const translateRedirectsEn: RedirectsTranslate;
@@ -8,8 +8,7 @@ export const redirectsMessagesEn = {
8
8
  "Import": "Import",
9
9
  "Marketing": "Marketing",
10
10
  "Redirects": "Redirects",
11
- "Manual URL redirect rules. Rules created automatically from a slug change (source": "Manual URL redirect rules. Rules created automatically from a slug change (source",
12
- ") also show up here.": ") also show up here.",
11
+ "Manage URL redirects, including those created automatically when a page's address changes.": "Manage URL redirects, including those created automatically when a page's address changes.",
13
12
  "Match type": "Match type",
14
13
  "From path": "From path",
15
14
  "To target": "To target",
@@ -30,9 +29,9 @@ export const redirectsMessagesEn = {
30
29
  "Loading redirects…": "Loading redirects…",
31
30
  "Not valid JSON.": "Not valid JSON.",
32
31
  "Must be a JSON array of rule objects.": "Must be a JSON array of rule objects.",
33
- "exact": "exact",
34
- "prefix": "prefix",
35
- "wildcard": "wildcard",
32
+ "exact": "Exact match",
33
+ "prefix": "Starts with",
34
+ "wildcard": "Wildcard",
36
35
  "Status": "Status",
37
36
  "More": "More",
38
37
  "Delete": "Delete",
@@ -40,16 +39,22 @@ export const redirectsMessagesEn = {
40
39
  "Enable": "Enable",
41
40
  "Saving…": "Saving…",
42
41
  "Created": "Created",
43
- "active": "active",
44
- "disabled": "disabled",
42
+ "active": "Active",
43
+ "disabled": "Disabled",
45
44
  "{created} created, {failed} failed.": "{created} created, {failed} failed.",
46
45
  "Item {index} ({code})": "Item {index} ({code})",
47
46
  "Actions for redirect rule from \"{fromPattern}\"": "Actions for redirect rule from \"{fromPattern}\"",
48
47
  "Paste a JSON array of ": "Paste a JSON array of ",
49
48
  " rule objects (1-500 items)": " rule objects (1-500 items)",
50
49
  "Delete the redirect rule from \"": "Delete the redirect rule from \"",
51
- "\"?": "\"?"
50
+ "\"?": "\"?",
51
+ "manual": "Manual",
52
+ "auto_slug_change": "URL change",
53
+ "import": "Imported",
52
54
  };
53
55
  /** English fallback with the same named-placeholder interpolation as the host dictionary. */
54
- export const translateRedirectsEn = (key, vars = {}) => key.replace(/\{(\w+)\}/g, (match, name) => String(vars[name] ?? match));
56
+ export const translateRedirectsEn = (key, vars = {}) => {
57
+ const template = Object.hasOwn(redirectsMessagesEn, key) ? redirectsMessagesEn[key] : key;
58
+ return template.replace(/\{(\w+)\}/g, (match, name) => String(vars[name] ?? match));
59
+ };
55
60
  //# sourceMappingURL=messages.en.js.map
@@ -12,9 +12,9 @@ export function useRedirectsPage({ useRedirectsHook = useWiredRedirects }, _opti
12
12
  const columns = [
13
13
  { key: 'from', header: t('From'), cell: (rule) => rule.fromPattern },
14
14
  { key: 'to', header: t('To'), cell: (rule) => rule.toTarget },
15
- { key: 'type', header: t('Type'), cell: (rule) => rule.matchType },
15
+ { key: 'type', header: t('Type'), cell: (rule) => t(rule.matchType, { serverLabel: rule.matchType }) },
16
16
  { key: 'code', header: t('Code'), cell: (rule) => rule.statusCode },
17
- { key: 'source', header: t('Source'), cell: (rule) => rule.source },
17
+ { key: 'source', header: t('Source'), cell: (rule) => t(rule.source, { serverLabel: rule.source }) },
18
18
  // The host's shared serverLabel owns enum localization, including unknown-value passthrough.
19
19
  { key: 'status', header: t('Status'), cell: (rule) => createElement('span', { className: `status status-${rule.status}` }, t(rule.status, { serverLabel: rule.status })) },
20
20
  { key: 'hits', header: t('Hits'), cell: (rule, index) => createElement(HitCountCell, { t, redirectId: rule.id, ...(rowMenuHandles[index] ? { agentHandleBase: rowMenuHandles[index] } : {}) }) },
@@ -24,8 +24,7 @@ export declare function redirects(_required: Record<string, never>, options?: Re
24
24
  readonly Import: "Import";
25
25
  readonly Marketing: "Marketing";
26
26
  readonly Redirects: "Redirects";
27
- readonly "Manual URL redirect rules. Rules created automatically from a slug change (source": "Manual URL redirect rules. Rules created automatically from a slug change (source";
28
- readonly ") also show up here.": ") also show up here.";
27
+ readonly "Manage URL redirects, including those created automatically when a page's address changes.": "Manage URL redirects, including those created automatically when a page's address changes.";
29
28
  readonly "Match type": "Match type";
30
29
  readonly "From path": "From path";
31
30
  readonly "To target": "To target";
@@ -46,9 +45,9 @@ export declare function redirects(_required: Record<string, never>, options?: Re
46
45
  readonly "Loading redirects\u2026": "Loading redirects…";
47
46
  readonly "Not valid JSON.": "Not valid JSON.";
48
47
  readonly "Must be a JSON array of rule objects.": "Must be a JSON array of rule objects.";
49
- readonly exact: "exact";
50
- readonly prefix: "prefix";
51
- readonly wildcard: "wildcard";
48
+ readonly exact: "Exact match";
49
+ readonly prefix: "Starts with";
50
+ readonly wildcard: "Wildcard";
52
51
  readonly Status: "Status";
53
52
  readonly More: "More";
54
53
  readonly Delete: "Delete";
@@ -56,8 +55,8 @@ export declare function redirects(_required: Record<string, never>, options?: Re
56
55
  readonly Enable: "Enable";
57
56
  readonly "Saving\u2026": "Saving…";
58
57
  readonly Created: "Created";
59
- readonly active: "active";
60
- readonly disabled: "disabled";
58
+ readonly active: "Active";
59
+ readonly disabled: "Disabled";
61
60
  readonly "{created} created, {failed} failed.": "{created} created, {failed} failed.";
62
61
  readonly "Item {index} ({code})": "Item {index} ({code})";
63
62
  readonly "Actions for redirect rule from \"{fromPattern}\"": "Actions for redirect rule from \"{fromPattern}\"";
@@ -65,6 +64,9 @@ export declare function redirects(_required: Record<string, never>, options?: Re
65
64
  readonly " rule objects (1-500 items)": " rule objects (1-500 items)";
66
65
  readonly "Delete the redirect rule from \"": "Delete the redirect rule from \"";
67
66
  readonly "\"?": "\"?";
67
+ readonly manual: "Manual";
68
+ readonly auto_slug_change: "URL change";
69
+ readonly import: "Imported";
68
70
  };
69
71
  pages: {
70
72
  readonly list: {
@@ -101,8 +103,7 @@ export declare function redirects(_required: Record<string, never>, options?: Re
101
103
  readonly Import: "Import";
102
104
  readonly Marketing: "Marketing";
103
105
  readonly Redirects: "Redirects";
104
- readonly "Manual URL redirect rules. Rules created automatically from a slug change (source": "Manual URL redirect rules. Rules created automatically from a slug change (source";
105
- readonly ") also show up here.": ") also show up here.";
106
+ readonly "Manage URL redirects, including those created automatically when a page's address changes.": "Manage URL redirects, including those created automatically when a page's address changes.";
106
107
  readonly "Match type": "Match type";
107
108
  readonly "From path": "From path";
108
109
  readonly "To target": "To target";
@@ -123,9 +124,9 @@ export declare function redirects(_required: Record<string, never>, options?: Re
123
124
  readonly "Loading redirects\u2026": "Loading redirects…";
124
125
  readonly "Not valid JSON.": "Not valid JSON.";
125
126
  readonly "Must be a JSON array of rule objects.": "Must be a JSON array of rule objects.";
126
- readonly exact: "exact";
127
- readonly prefix: "prefix";
128
- readonly wildcard: "wildcard";
127
+ readonly exact: "Exact match";
128
+ readonly prefix: "Starts with";
129
+ readonly wildcard: "Wildcard";
129
130
  readonly Status: "Status";
130
131
  readonly More: "More";
131
132
  readonly Delete: "Delete";
@@ -133,8 +134,8 @@ export declare function redirects(_required: Record<string, never>, options?: Re
133
134
  readonly Enable: "Enable";
134
135
  readonly "Saving\u2026": "Saving…";
135
136
  readonly Created: "Created";
136
- readonly active: "active";
137
- readonly disabled: "disabled";
137
+ readonly active: "Active";
138
+ readonly disabled: "Disabled";
138
139
  readonly "{created} created, {failed} failed.": "{created} created, {failed} failed.";
139
140
  readonly "Item {index} ({code})": "Item {index} ({code})";
140
141
  readonly "Actions for redirect rule from \"{fromPattern}\"": "Actions for redirect rule from \"{fromPattern}\"";
@@ -142,6 +143,9 @@ export declare function redirects(_required: Record<string, never>, options?: Re
142
143
  readonly " rule objects (1-500 items)": " rule objects (1-500 items)";
143
144
  readonly "Delete the redirect rule from \"": "Delete the redirect rule from \"";
144
145
  readonly "\"?": "\"?";
146
+ readonly manual: "Manual";
147
+ readonly auto_slug_change: "URL change";
148
+ readonly import: "Imported";
145
149
  };
146
150
  readonly pages: {
147
151
  readonly list: {
@@ -176,8 +180,7 @@ export declare function redirects(_required: Record<string, never>, options?: Re
176
180
  readonly Import: "Import";
177
181
  readonly Marketing: "Marketing";
178
182
  readonly Redirects: "Redirects";
179
- readonly "Manual URL redirect rules. Rules created automatically from a slug change (source": "Manual URL redirect rules. Rules created automatically from a slug change (source";
180
- readonly ") also show up here.": ") also show up here.";
183
+ readonly "Manage URL redirects, including those created automatically when a page's address changes.": "Manage URL redirects, including those created automatically when a page's address changes.";
181
184
  readonly "Match type": "Match type";
182
185
  readonly "From path": "From path";
183
186
  readonly "To target": "To target";
@@ -198,9 +201,9 @@ export declare function redirects(_required: Record<string, never>, options?: Re
198
201
  readonly "Loading redirects\u2026": "Loading redirects…";
199
202
  readonly "Not valid JSON.": "Not valid JSON.";
200
203
  readonly "Must be a JSON array of rule objects.": "Must be a JSON array of rule objects.";
201
- readonly exact: "exact";
202
- readonly prefix: "prefix";
203
- readonly wildcard: "wildcard";
204
+ readonly exact: "Exact match";
205
+ readonly prefix: "Starts with";
206
+ readonly wildcard: "Wildcard";
204
207
  readonly Status: "Status";
205
208
  readonly More: "More";
206
209
  readonly Delete: "Delete";
@@ -208,8 +211,8 @@ export declare function redirects(_required: Record<string, never>, options?: Re
208
211
  readonly Enable: "Enable";
209
212
  readonly "Saving\u2026": "Saving…";
210
213
  readonly Created: "Created";
211
- readonly active: "active";
212
- readonly disabled: "disabled";
214
+ readonly active: "Active";
215
+ readonly disabled: "Disabled";
213
216
  readonly "{created} created, {failed} failed.": "{created} created, {failed} failed.";
214
217
  readonly "Item {index} ({code})": "Item {index} ({code})";
215
218
  readonly "Actions for redirect rule from \"{fromPattern}\"": "Actions for redirect rule from \"{fromPattern}\"";
@@ -217,6 +220,9 @@ export declare function redirects(_required: Record<string, never>, options?: Re
217
220
  readonly " rule objects (1-500 items)": " rule objects (1-500 items)";
218
221
  readonly "Delete the redirect rule from \"": "Delete the redirect rule from \"";
219
222
  readonly "\"?": "\"?";
223
+ readonly manual: "Manual";
224
+ readonly auto_slug_change: "URL change";
225
+ readonly import: "Imported";
220
226
  };
221
227
  readonly pages: {
222
228
  readonly list: {
@@ -255,8 +261,7 @@ export declare function redirects(_required: Record<string, never>, options?: Re
255
261
  readonly Import: "Import";
256
262
  readonly Marketing: "Marketing";
257
263
  readonly Redirects: "Redirects";
258
- readonly "Manual URL redirect rules. Rules created automatically from a slug change (source": "Manual URL redirect rules. Rules created automatically from a slug change (source";
259
- readonly ") also show up here.": ") also show up here.";
264
+ readonly "Manage URL redirects, including those created automatically when a page's address changes.": "Manage URL redirects, including those created automatically when a page's address changes.";
260
265
  readonly "Match type": "Match type";
261
266
  readonly "From path": "From path";
262
267
  readonly "To target": "To target";
@@ -277,9 +282,9 @@ export declare function redirects(_required: Record<string, never>, options?: Re
277
282
  readonly "Loading redirects\u2026": "Loading redirects…";
278
283
  readonly "Not valid JSON.": "Not valid JSON.";
279
284
  readonly "Must be a JSON array of rule objects.": "Must be a JSON array of rule objects.";
280
- readonly exact: "exact";
281
- readonly prefix: "prefix";
282
- readonly wildcard: "wildcard";
285
+ readonly exact: "Exact match";
286
+ readonly prefix: "Starts with";
287
+ readonly wildcard: "Wildcard";
283
288
  readonly Status: "Status";
284
289
  readonly More: "More";
285
290
  readonly Delete: "Delete";
@@ -287,8 +292,8 @@ export declare function redirects(_required: Record<string, never>, options?: Re
287
292
  readonly Enable: "Enable";
288
293
  readonly "Saving\u2026": "Saving…";
289
294
  readonly Created: "Created";
290
- readonly active: "active";
291
- readonly disabled: "disabled";
295
+ readonly active: "Active";
296
+ readonly disabled: "Disabled";
292
297
  readonly "{created} created, {failed} failed.": "{created} created, {failed} failed.";
293
298
  readonly "Item {index} ({code})": "Item {index} ({code})";
294
299
  readonly "Actions for redirect rule from \"{fromPattern}\"": "Actions for redirect rule from \"{fromPattern}\"";
@@ -296,6 +301,9 @@ export declare function redirects(_required: Record<string, never>, options?: Re
296
301
  readonly " rule objects (1-500 items)": " rule objects (1-500 items)";
297
302
  readonly "Delete the redirect rule from \"": "Delete the redirect rule from \"";
298
303
  readonly "\"?": "\"?";
304
+ readonly manual: "Manual";
305
+ readonly auto_slug_change: "URL change";
306
+ readonly import: "Imported";
299
307
  };
300
308
  readonly pages: {
301
309
  readonly list: {
@@ -15,6 +15,6 @@ export function RedirectsPage({ useRedirectsHook = useWiredRedirects } = {}) {
15
15
  }
16
16
  if (!redirects)
17
17
  return _jsx("div", { className: "notice", children: t("Loading redirects…") });
18
- return (_jsxs("div", { className: "page", children: [_jsxs("div", { className: "page-header", children: [_jsxs("div", { className: "page-header-text", children: [_jsx("p", { className: "page-kicker", children: t("Marketing") }), _jsx("h1", { className: "page-title", children: t("Redirects") }), _jsxs("p", { className: "page-description", children: [t("Manual URL redirect rules. Rules created automatically from a slug change (source"), _jsx("code", { children: " auto_slug_change" }), t(") also show up here.")] })] }), _jsx("div", { className: "page-actions", children: headerActions })] }), error ? _jsx("div", { className: "notice error", children: describeApiError({ e: error, fallback: "request failed" }) }) : null, _jsxs("form", { className: "card form-measure", onSubmit: submitCreate, children: [_jsx("div", { className: "field-group", children: _jsxs("div", { className: "field-row", children: [_jsxs("div", { className: "field", children: [_jsx("label", { className: "field-label", htmlFor: "redirect-match-type", children: t("Match type") }), _jsxs("select", { id: "redirect-match-type", name: "matchType", defaultValue: "exact", ...agentHandle({ handle: "redirects-create-match-type" }, { role: "field", label: "New redirect's match type" }), children: [_jsx("option", { value: "exact", children: t("exact") }), _jsx("option", { value: "prefix", children: t("prefix") }), _jsx("option", { value: "wildcard", children: t("wildcard") })] })] }), _jsxs("div", { className: "field", children: [_jsx("label", { className: "field-label", htmlFor: "redirect-from-pattern", children: t("From path") }), _jsx("input", { id: "redirect-from-pattern", name: "fromPattern", placeholder: "/old-path", required: true, ...agentHandle({ handle: "redirects-create-from-pattern" }, { role: "field", label: "New redirect's source path or pattern" }) })] }), _jsxs("div", { className: "field", children: [_jsx("label", { className: "field-label", htmlFor: "redirect-to-target", children: t("To target") }), _jsx("input", { id: "redirect-to-target", name: "toTarget", placeholder: "/new-path or https://example.com/...", required: true, ...agentHandle({ handle: "redirects-create-to-target" }, { role: "field", label: "New redirect's destination path or URL" }) })] }), _jsxs("div", { className: "field", children: [_jsx("label", { className: "field-label", htmlFor: "redirect-status-code", children: t("Status code") }), _jsxs("select", { id: "redirect-status-code", name: "statusCode", defaultValue: "301", ...agentHandle({ handle: "redirects-create-status-code" }, { role: "field", label: "New redirect's HTTP status code" }), children: [_jsx("option", { value: "301", children: t("301 (permanent)") }), _jsx("option", { value: "302", children: t("302 (temporary)") }), _jsx("option", { value: "307", children: t("307 (temporary, method-preserving)") }), _jsx("option", { value: "308", children: t("308 (permanent, method-preserving)") })] })] })] }) }), _jsx("div", { className: "editor-actions form-actions", children: _jsx("button", { type: "submit", disabled: saving, ...agentHandle({ handle: "redirects-create-submit" }, { role: "button", label: "Add this redirect rule" }), children: saving ? t("Saving…") : t("Add redirect") }) })] }), _jsx(ImportRedirectsForm, { t: t, locale: locale }), _jsx(DataTable, { rows: redirects, rowKey: (rule) => rule.id, empty: _jsx("div", { className: "card", children: _jsx("div", { className: "empty-state", children: _jsx("p", { children: t("No redirect rules yet.") }) }) }), columns: columns }), _jsx(ConfirmDialog, { open: pendingDelete !== null, agentHandle: "redirects-delete", title: t("Delete redirect rule?"), body: deleteBody, confirmLabel: t("Delete"), destructive: true, pending: deletePending, onConfirm: confirmDelete, onCancel: cancelDelete })] }));
18
+ return (_jsxs("div", { className: "page", children: [_jsxs("div", { className: "page-header", children: [_jsxs("div", { className: "page-header-text", children: [_jsx("p", { className: "page-kicker", children: t("Marketing") }), _jsx("h1", { className: "page-title", children: t("Redirects") }), _jsx("p", { className: "page-description", children: t("Manage URL redirects, including those created automatically when a page's address changes.") })] }), _jsx("div", { className: "page-actions", children: headerActions })] }), error ? _jsx("div", { className: "notice error", children: describeApiError({ e: error, fallback: "request failed" }) }) : null, _jsxs("form", { className: "card form-measure", onSubmit: submitCreate, children: [_jsx("div", { className: "field-group", children: _jsxs("div", { className: "field-row", children: [_jsxs("div", { className: "field", children: [_jsx("label", { className: "field-label", htmlFor: "redirect-match-type", children: t("Match type") }), _jsxs("select", { id: "redirect-match-type", name: "matchType", defaultValue: "exact", ...agentHandle({ handle: "redirects-create-match-type" }, { role: "field", label: "New redirect's match type" }), children: [_jsx("option", { value: "exact", children: t("exact", { serverLabel: "exact" }) }), _jsx("option", { value: "prefix", children: t("prefix", { serverLabel: "prefix" }) }), _jsx("option", { value: "wildcard", children: t("wildcard", { serverLabel: "wildcard" }) })] })] }), _jsxs("div", { className: "field", children: [_jsx("label", { className: "field-label", htmlFor: "redirect-from-pattern", children: t("From path") }), _jsx("input", { id: "redirect-from-pattern", name: "fromPattern", placeholder: "/old-path", required: true, ...agentHandle({ handle: "redirects-create-from-pattern" }, { role: "field", label: "New redirect's source path or pattern" }) })] }), _jsxs("div", { className: "field", children: [_jsx("label", { className: "field-label", htmlFor: "redirect-to-target", children: t("To target") }), _jsx("input", { id: "redirect-to-target", name: "toTarget", placeholder: "/new-path or https://example.com/...", required: true, ...agentHandle({ handle: "redirects-create-to-target" }, { role: "field", label: "New redirect's destination path or URL" }) })] }), _jsxs("div", { className: "field", children: [_jsx("label", { className: "field-label", htmlFor: "redirect-status-code", children: t("Status code") }), _jsxs("select", { id: "redirect-status-code", name: "statusCode", defaultValue: "301", ...agentHandle({ handle: "redirects-create-status-code" }, { role: "field", label: "New redirect's HTTP status code" }), children: [_jsx("option", { value: "301", children: t("301 (permanent)") }), _jsx("option", { value: "302", children: t("302 (temporary)") }), _jsx("option", { value: "307", children: t("307 (temporary, method-preserving)") }), _jsx("option", { value: "308", children: t("308 (permanent, method-preserving)") })] })] })] }) }), _jsx("div", { className: "editor-actions form-actions", children: _jsx("button", { type: "submit", disabled: saving, ...agentHandle({ handle: "redirects-create-submit" }, { role: "button", label: "Add this redirect rule" }), children: saving ? t("Saving…") : t("Add redirect") }) })] }), _jsx(ImportRedirectsForm, { t: t, locale: locale }), _jsx(DataTable, { rows: redirects, rowKey: (rule) => rule.id, empty: _jsx("div", { className: "card", children: _jsx("div", { className: "empty-state", children: _jsx("p", { children: t("No redirect rules yet.") }) }) }), columns: columns }), _jsx(ConfirmDialog, { open: pendingDelete !== null, agentHandle: "redirects-delete", title: t("Delete redirect rule?"), body: deleteBody, confirmLabel: t("Delete"), destructive: true, pending: deletePending, onConfirm: confirmDelete, onCancel: cancelDelete })] }));
19
19
  }
20
20
  //# sourceMappingURL=RedirectsPage.js.map
@@ -16,8 +16,7 @@ export declare const redirectsModule: {
16
16
  readonly Import: "Import";
17
17
  readonly Marketing: "Marketing";
18
18
  readonly Redirects: "Redirects";
19
- readonly "Manual URL redirect rules. Rules created automatically from a slug change (source": "Manual URL redirect rules. Rules created automatically from a slug change (source";
20
- readonly ") also show up here.": ") also show up here.";
19
+ readonly "Manage URL redirects, including those created automatically when a page's address changes.": "Manage URL redirects, including those created automatically when a page's address changes.";
21
20
  readonly "Match type": "Match type";
22
21
  readonly "From path": "From path";
23
22
  readonly "To target": "To target";
@@ -38,9 +37,9 @@ export declare const redirectsModule: {
38
37
  readonly "Loading redirects\u2026": "Loading redirects…";
39
38
  readonly "Not valid JSON.": "Not valid JSON.";
40
39
  readonly "Must be a JSON array of rule objects.": "Must be a JSON array of rule objects.";
41
- readonly exact: "exact";
42
- readonly prefix: "prefix";
43
- readonly wildcard: "wildcard";
40
+ readonly exact: "Exact match";
41
+ readonly prefix: "Starts with";
42
+ readonly wildcard: "Wildcard";
44
43
  readonly Status: "Status";
45
44
  readonly More: "More";
46
45
  readonly Delete: "Delete";
@@ -48,8 +47,8 @@ export declare const redirectsModule: {
48
47
  readonly Enable: "Enable";
49
48
  readonly "Saving\u2026": "Saving…";
50
49
  readonly Created: "Created";
51
- readonly active: "active";
52
- readonly disabled: "disabled";
50
+ readonly active: "Active";
51
+ readonly disabled: "Disabled";
53
52
  readonly "{created} created, {failed} failed.": "{created} created, {failed} failed.";
54
53
  readonly "Item {index} ({code})": "Item {index} ({code})";
55
54
  readonly "Actions for redirect rule from \"{fromPattern}\"": "Actions for redirect rule from \"{fromPattern}\"";
@@ -57,6 +56,9 @@ export declare const redirectsModule: {
57
56
  readonly " rule objects (1-500 items)": " rule objects (1-500 items)";
58
57
  readonly "Delete the redirect rule from \"": "Delete the redirect rule from \"";
59
58
  readonly "\"?": "\"?";
59
+ readonly manual: "Manual";
60
+ readonly auto_slug_change: "URL change";
61
+ readonly import: "Imported";
60
62
  };
61
63
  readonly pages: {
62
64
  readonly list: {
@@ -22,10 +22,10 @@ export declare const seoMessagesEn: {
22
22
  readonly Nofollow: "Nofollow";
23
23
  readonly "OG title": "OG title";
24
24
  readonly "OG description": "OG description";
25
- readonly "OG image (media ref or URL)": "OG image (media ref or URL)";
25
+ readonly "OG image": "OG image";
26
26
  readonly "Twitter title": "Twitter title";
27
27
  readonly "Twitter description": "Twitter description";
28
- readonly "Twitter image (media ref or URL)": "Twitter image (media ref or URL)";
28
+ readonly "Twitter image": "Twitter image";
29
29
  readonly "Saving\u2026": "Saving…";
30
30
  readonly "Save overrides": "Save overrides";
31
31
  readonly "Per-entry SEO": "Per-entry SEO";
@@ -33,7 +33,7 @@ export declare const seoMessagesEn: {
33
33
  readonly "Title template (must contain %s)": "Title template (must contain %s)";
34
34
  readonly "Default meta description": "Default meta description";
35
35
  readonly "Social sharing": "Social sharing";
36
- readonly "Default Open Graph / Twitter image (media ref)": "Default Open Graph / Twitter image (media ref)";
36
+ readonly "Default Open Graph / Twitter image": "Default Open Graph / Twitter image";
37
37
  readonly "Twitter @site handle": "Twitter @site handle";
38
38
  readonly Crawling: "Crawling";
39
39
  readonly "Default noindex": "Default noindex";
@@ -41,7 +41,7 @@ export declare const seoMessagesEn: {
41
41
  readonly "Sitemap enabled": "Sitemap enabled";
42
42
  readonly "Save settings": "Save settings";
43
43
  readonly "Cached sitemap": "Cached sitemap";
44
- readonly "Force-rebuild the cached sitemap now, bypassing the normal cache-hit path.": "Force-rebuild the cached sitemap now, bypassing the normal cache-hit path.";
44
+ readonly "Rebuild the sitemap now to include the latest published content.": "Rebuild the sitemap now to include the latest published content.";
45
45
  readonly "Working\u2026": "Working…";
46
46
  readonly "Regenerate sitemap": "Regenerate sitemap";
47
47
  readonly "View sitemap": "View sitemap";
@@ -74,5 +74,6 @@ export declare const seoMessagesEn: {
74
74
  readonly "failed to regenerate sitemap": "failed to regenerate sitemap";
75
75
  readonly "failed to load the sitemap": "failed to load the sitemap";
76
76
  readonly "Could not load the sitemap (HTTP {status}).": "Could not load the sitemap (HTTP {status}).";
77
+ readonly "Selected image": "Selected image";
77
78
  };
78
79
  //# sourceMappingURL=messages.en.d.ts.map
@@ -22,10 +22,10 @@ export const seoMessagesEn = {
22
22
  "Nofollow": "Nofollow",
23
23
  "OG title": "OG title",
24
24
  "OG description": "OG description",
25
- "OG image (media ref or URL)": "OG image (media ref or URL)",
25
+ "OG image": "OG image",
26
26
  "Twitter title": "Twitter title",
27
27
  "Twitter description": "Twitter description",
28
- "Twitter image (media ref or URL)": "Twitter image (media ref or URL)",
28
+ "Twitter image": "Twitter image",
29
29
  "Saving…": "Saving…",
30
30
  "Save overrides": "Save overrides",
31
31
  "Per-entry SEO": "Per-entry SEO",
@@ -33,7 +33,7 @@ export const seoMessagesEn = {
33
33
  "Title template (must contain %s)": "Title template (must contain %s)",
34
34
  "Default meta description": "Default meta description",
35
35
  "Social sharing": "Social sharing",
36
- "Default Open Graph / Twitter image (media ref)": "Default Open Graph / Twitter image (media ref)",
36
+ "Default Open Graph / Twitter image": "Default Open Graph / Twitter image",
37
37
  "Twitter @site handle": "Twitter @site handle",
38
38
  "Crawling": "Crawling",
39
39
  "Default noindex": "Default noindex",
@@ -41,7 +41,7 @@ export const seoMessagesEn = {
41
41
  "Sitemap enabled": "Sitemap enabled",
42
42
  "Save settings": "Save settings",
43
43
  "Cached sitemap": "Cached sitemap",
44
- "Force-rebuild the cached sitemap now, bypassing the normal cache-hit path.": "Force-rebuild the cached sitemap now, bypassing the normal cache-hit path.",
44
+ "Rebuild the sitemap now to include the latest published content.": "Rebuild the sitemap now to include the latest published content.",
45
45
  "Working…": "Working…",
46
46
  "Regenerate sitemap": "Regenerate sitemap",
47
47
  "View sitemap": "View sitemap",
@@ -73,6 +73,7 @@ export const seoMessagesEn = {
73
73
  "Sitemap regeneration accepted.": "Sitemap regeneration accepted.",
74
74
  "failed to regenerate sitemap": "failed to regenerate sitemap",
75
75
  "failed to load the sitemap": "failed to load the sitemap",
76
- "Could not load the sitemap (HTTP {status}).": "Could not load the sitemap (HTTP {status})."
76
+ "Could not load the sitemap (HTTP {status}).": "Could not load the sitemap (HTTP {status}).",
77
+ "Selected image": "Selected image",
77
78
  };
78
79
  //# sourceMappingURL=messages.en.js.map
@@ -12,6 +12,10 @@ import { useWiredMediaRefField } from "../hooks/MediaRefField.hooks.js";
12
12
  * `react/hooks/MediaRefField.hooks.ts`, split out the same way `MediaPickerDialog`/`MediaPickerDialog.hooks
13
13
  * .tsx` does; this file stays props-and-JSX only. `MediaPickerDialog` itself is reused unchanged
14
14
  * (`components/MediaPickerDialog/MediaPickerDialog.tsx`) — no fork, no new picker UI.
15
+ *
16
+ * 2026-10-08 polish: the original editable-input rationale above still applies to pasted URLs
17
+ * and empty fields. A selected library reference now shows plain copy with its preview; Remove
18
+ * returns to manual entry. A hidden input preserves the exact FormData value on defaults saves.
15
19
  */
16
20
  export interface MediaRefFieldProps {
17
21
  locale: string;
@@ -5,8 +5,8 @@ import { useSeoCopy } from "../hooks/SeoPorts.hooks.js";
5
5
  import { useWiredMediaRefField, useMediaRefPresentation, handleSpread } from "../hooks/MediaRefField.hooks.js";
6
6
  export function MediaRefField({ locale, id, name, label, value, onChange, agentHandle: base, useField = useWiredMediaRefField, }, _optional = {}) {
7
7
  const t = useSeoCopy();
8
- const { Picker = MediaPickerBridge } = useMediaRefPresentation();
8
+ const { Picker = MediaPickerBridge, storedReference } = useMediaRefPresentation({ value });
9
9
  const { pickerOpen, openPicker, closePicker, handleSelect, clear, previewUrl, accept } = useField({ value, onChange });
10
- return (_jsxs("div", { className: "field media-ref-field", children: [_jsx("label", { className: "field-label", htmlFor: id, children: label }), _jsxs("div", { className: "media-ref-field-row", children: [_jsx("input", { id: id, name: name, value: value, onChange: (e) => onChange(e.target.value), ...(base ? agentHandle({ handle: base }, { role: "field", label }) : {}) }), _jsx("button", { type: "button", className: "btn-secondary", onClick: openPicker, ...handleSpread({ ...(base === undefined ? {} : { base }), suffix: "choose", label: `Choose an image for: ${label}` }), children: t({ locale: locale, key: "Choose image" }) }), value ? (_jsx("button", { type: "button", className: "btn-secondary", onClick: clear, ...handleSpread({ ...(base === undefined ? {} : { base }), suffix: "clear", label: `Remove the selected image for: ${label}` }), children: t({ locale: locale, key: "Remove" }) })) : null] }), previewUrl ? _jsx("img", { className: "media-ref-field-preview", src: previewUrl, alt: `${label} preview` }) : null, pickerOpen ? (_jsx(Picker, { onSelect: handleSelect, onCancel: closePicker, accept: accept, agentHandle: base ? `${base}-dialog` : undefined })) : null] }));
10
+ return (_jsxs("div", { className: "jini-field jini-media-ref-field", children: [_jsx("label", { className: "jini-field-label", htmlFor: id, children: label }), _jsxs("div", { className: "jini-media-ref-field-row", children: [_jsx("input", { className: "jini-input", id: id, name: storedReference ? undefined : name, value: storedReference ? t({ locale, key: "Selected image" }) : value, readOnly: storedReference, onChange: (e) => onChange(e.target.value), ...(base ? agentHandle({ handle: base }, { role: "field", label }) : {}) }), storedReference && name ? _jsx("input", { type: "hidden", name: name, value: value }) : null, _jsx("button", { type: "button", className: "jini-btn jini-btn-secondary", onClick: openPicker, ...handleSpread({ ...(base === undefined ? {} : { base }), suffix: "choose", label: `Choose an image for: ${label}` }), children: t({ locale: locale, key: "Choose image" }) }), value ? (_jsx("button", { type: "button", className: "jini-btn jini-btn-secondary", onClick: clear, ...handleSpread({ ...(base === undefined ? {} : { base }), suffix: "clear", label: `Remove the selected image for: ${label}` }), children: t({ locale: locale, key: "Remove" }) })) : null] }), previewUrl ? _jsx("img", { className: "jini-media-ref-field-preview", src: previewUrl, alt: `${label} preview` }) : null, pickerOpen ? (_jsx(Picker, { onSelect: handleSelect, onCancel: closePicker, accept: accept, agentHandle: base ? `${base}-dialog` : undefined })) : null] }));
11
11
  }
12
12
  //# sourceMappingURL=MediaRefField.js.map
@@ -1,7 +1,8 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { agentHandle } from "@jini-ai/agentic";
3
3
  import { Dialog } from "@jini-ai/ui-kit/react";
4
- import { useSeoCopy } from "../hooks/SeoPorts.hooks.js";
4
+ import { formatTimestamp } from "@jini-ai/ui/panel-kit";
5
+ import { useSeoCopy, useSeoOptions } from "../hooks/SeoPorts.hooks.js";
5
6
  import { actionLabel } from "../../rules.js";
6
7
  import { useWiredSitemapModal } from "../hooks/use-sitemap-modal.hooks.js";
7
8
  import { useSitemapModalRegenerate, useSitemapPresentation, useSitemapTable, useSitemapViewToggle } from "../hooks/SitemapModal.hooks.js";
@@ -30,13 +31,14 @@ function SitemapModalHeaderActions({ locale, modal }) {
30
31
  * `useSitemapModal` (memoized there), this only maps them to JSX. */
31
32
  function SitemapModalTable({ locale, modal }) {
32
33
  const t = useSeoCopy();
34
+ const { formatDate = formatTimestamp } = useSeoOptions();
33
35
  const { rowHandles } = useSitemapTable({ modal });
34
36
  if (modal.entries.length === 0)
35
37
  return _jsx("div", { className: "notice", children: t({ locale: locale, key: "The sitemap has no URLs yet." }) });
36
38
  return (_jsxs(_Fragment, { children: [_jsx("input", { type: "search", className: "sitemap-modal-filter", "aria-label": t({ locale: locale, key: "Filter sitemap URLs" }), placeholder: t({ locale: locale, key: "Filter by URL…" }), value: modal.filter, onChange: (e) => modal.setFilter(e.target.value), ...agentHandle({ handle: "seo-sitemap-filter" }, {
37
39
  role: "field",
38
40
  label: "Narrow the sitemap table by a substring match on the URL",
39
- }) }), modal.filteredEntries.length === 0 ? (_jsx("p", { className: "muted-cell", children: t({ locale: locale, key: "No URLs match this filter." }) })) : (_jsx("div", { className: "table-scroll", children: _jsxs("table", { className: "list-table", children: [_jsx("thead", { children: _jsxs("tr", { children: [_jsx("th", { children: t({ locale: locale, key: "URL" }) }), _jsx("th", { children: t({ locale: locale, key: "Last modified" }) })] }) }), _jsx("tbody", { children: modal.filteredEntries.map((entry, index) => (_jsxs("tr", { children: [_jsx("td", { children: _jsx("a", { href: entry.loc, target: "_blank", rel: "noreferrer", ...agentHandle({ handle: rowHandles[index] }, { role: "link", label: "Open this URL on the live site" }), children: entry.loc }) }), _jsx("td", { children: entry.lastmod ?? _jsx("span", { className: "muted-cell", children: t({ locale: locale, key: "—" }) }) })] }, entry.loc))) })] }) }))] }));
41
+ }) }), modal.filteredEntries.length === 0 ? (_jsx("p", { className: "muted-cell", children: t({ locale: locale, key: "No URLs match this filter." }) })) : (_jsx("div", { className: "table-scroll", children: _jsxs("table", { className: "list-table", children: [_jsx("thead", { children: _jsxs("tr", { children: [_jsx("th", { children: t({ locale: locale, key: "URL" }) }), _jsx("th", { children: t({ locale: locale, key: "Last modified" }) })] }) }), _jsx("tbody", { children: modal.filteredEntries.map((entry, index) => (_jsxs("tr", { children: [_jsx("td", { children: _jsx("a", { href: entry.loc, target: "_blank", rel: "noreferrer", ...agentHandle({ handle: rowHandles[index] }, { role: "link", label: "Open this URL on the live site" }), children: entry.loc }) }), _jsx("td", { children: entry.lastmod === null ? _jsx("span", { className: "muted-cell", children: t({ locale: locale, key: "—" }) }) : formatDate({ iso: entry.lastmod }) })] }, entry.loc))) })] }) }))] }));
40
42
  }
41
43
  /** The body's content-status switch (REQ 8/9: disabled, loading, error, then raw-vs-table) — kept
42
44
  * as its own flat sequence of guards rather than a nested ternary inline in {@link SitemapModal}.
@@ -11,8 +11,11 @@ export declare function useWiredMediaRefField({ value, onChange }: {
11
11
  onChange: (value: string) => void;
12
12
  }, _optional?: Record<string, never>): MediaRefFieldController;
13
13
  /** Slot or existing promise picker; no picker implementation is forked into SEO. */
14
- export declare function useMediaRefPresentation(_required?: Record<string, never>, _optional?: Record<string, never>): {
14
+ export declare function useMediaRefPresentation({ value }: {
15
+ value: string;
16
+ }, _optional?: Record<string, never>): {
15
17
  Picker: import("react").ComponentType<import("../options.js").SeoMediaPickerSlotProps> | undefined;
18
+ storedReference: boolean;
16
19
  };
17
20
  export declare function handleSpread({ base, suffix, label }: {
18
21
  base?: string;
@@ -2,7 +2,7 @@ import { useRef } from 'react';
2
2
  import { agentHandle } from '@jini-ai/agentic';
3
3
  import { useController } from '../../../core/react/use-controller.js';
4
4
  import { createMediaRefController } from '../../controllers/media-ref.controller.js';
5
- import { resolveMediaRefPreviewUrl } from '../../rules.js';
5
+ import { isStoredMediaRef, resolveMediaRefPreviewUrl } from '../../rules.js';
6
6
  import { useSeoPorts, useSeoOptions } from './SeoPorts.hooks.js';
7
7
  export function useMediaRefField({ value, onChange, api }, _optional = {}) {
8
8
  const change = useRef(onChange);
@@ -20,9 +20,9 @@ export function useWiredMediaRefField({ value, onChange }, _optional = {}) {
20
20
  return useMediaRefField({ value, onChange, api });
21
21
  }
22
22
  /** Slot or existing promise picker; no picker implementation is forked into SEO. */
23
- export function useMediaRefPresentation(_required = {}, _optional = {}) {
23
+ export function useMediaRefPresentation({ value }, _optional = {}) {
24
24
  const { slots } = useSeoOptions();
25
- return { Picker: slots?.MediaPickerDialog };
25
+ return { Picker: slots?.MediaPickerDialog, storedReference: isStoredMediaRef({ value }) };
26
26
  }
27
27
  export function handleSpread({ base, suffix, label }, _optional = {}) {
28
28
  return base ? agentHandle({ handle: `${base}-${suffix}` }, { role: 'button', label }) : {};