@jini-ai/admin 0.4.12 → 0.4.13

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 (151) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/dist/agent-plugins/controllers/agent-plugin-files.controller.js +14 -9
  3. package/dist/agent-plugins/controllers/agent-plugins.controller.js +12 -9
  4. package/dist/agent-plugins/package-file-tree.js +54 -51
  5. package/dist/agent-plugins/react/components/AgentPluginRow.d.ts +1 -1
  6. package/dist/agent-plugins/react/components/AgentPluginRow.js +9 -2
  7. package/dist/agent-plugins/react/hooks/AgentPluginInspector.hooks.d.ts +5 -5
  8. package/dist/agent-plugins/react/hooks/AgentPluginInspector.hooks.js +14 -5
  9. package/dist/agent-plugins/react/hooks/AgentPluginPanel.hooks.d.ts +15 -13
  10. package/dist/agent-plugins/react/hooks/AgentPluginPanel.hooks.js +54 -40
  11. package/dist/agent-plugins/react/hooks/AgentPluginsPage.hooks.d.ts +13 -13
  12. package/dist/agent-plugins/react/hooks/AgentPluginsPage.hooks.js +23 -11
  13. package/dist/browser/navigation.js +44 -27
  14. package/dist/comments/adapters/memory.js +30 -12
  15. package/dist/comments/conformance/comments-api.conformance.js +55 -33
  16. package/dist/comments/controllers/comment-queue.controller.js +13 -4
  17. package/dist/comments/react/hooks/use-comment-queue.hooks.d.ts +17 -2
  18. package/dist/comments/react/hooks/use-comment-queue.hooks.js +79 -30
  19. package/dist/comments/react/hooks/use-comment-settings.hooks.js +9 -5
  20. package/dist/comments/react/index.d.ts +2 -1
  21. package/dist/comments/react/index.js +1 -1
  22. package/dist/consent/react/ConsentFields.d.ts +35 -0
  23. package/dist/consent/react/ConsentFields.js +74 -0
  24. package/dist/consent/react/ConsentPreviewFrame.d.ts +14 -0
  25. package/dist/consent/react/ConsentPreviewFrame.js +14 -0
  26. package/dist/consent/react/ConsentSettingsPanel.d.ts +27 -4
  27. package/dist/consent/react/ConsentSettingsPanel.js +6 -17
  28. package/dist/consent/react/index.d.ts +6 -1
  29. package/dist/consent/react/index.js +2 -0
  30. package/dist/consent/react/settings-panel.hooks.d.ts +28 -1
  31. package/dist/consent/react/settings-panel.hooks.js +26 -0
  32. package/dist/core/entities/rules.js +126 -81
  33. package/dist/core/manifest/rules.js +10 -10
  34. package/dist/core/module/create-admin.js +195 -117
  35. package/dist/core/ports/redirects.d.ts +8 -6
  36. package/dist/core/ports/redirects.js +8 -6
  37. package/dist/core/routing/rules.js +9 -4
  38. package/dist/core/transport/http.js +37 -20
  39. package/dist/forms/adapters/memory.js +22 -11
  40. package/dist/forms/conformance/forms-api.conformance.d.ts +6 -4
  41. package/dist/forms/conformance/forms-api.conformance.js +42 -46
  42. package/dist/forms/field-settings.js +150 -115
  43. package/dist/forms/html-rules.js +45 -28
  44. package/dist/forms/react/components/FieldSettingsRows.d.ts +22 -0
  45. package/dist/forms/react/components/FieldSettingsRows.js +29 -0
  46. package/dist/forms/react/components/FieldSettingsSection.d.ts +1 -1
  47. package/dist/forms/react/components/FieldSettingsSection.js +5 -6
  48. package/dist/forms/react/components/FormEditorFieldsBody.d.ts +1 -1
  49. package/dist/forms/react/components/FormEditorFieldsBody.js +4 -15
  50. package/dist/forms/react/components/FormEditorFieldsBodyParts.d.ts +18 -0
  51. package/dist/forms/react/components/FormEditorFieldsBodyParts.js +32 -0
  52. package/dist/forms/react/components/FormSubmissions.d.ts +1 -1
  53. package/dist/forms/react/components/FormSubmissions.js +4 -20
  54. package/dist/forms/react/components/FormSubmissionsTable.d.ts +19 -0
  55. package/dist/forms/react/components/FormSubmissionsTable.js +22 -0
  56. package/dist/media/adapters/memory.js +31 -26
  57. package/dist/media/controllers/edit-media.controller.js +2 -1
  58. package/dist/media/controllers/library.controller.js +48 -35
  59. package/dist/media/controllers/providers.controller.js +2 -1
  60. package/dist/media/html-attributes.js +29 -15
  61. package/dist/media/index.d.ts +1 -1
  62. package/dist/media/index.js +1 -1
  63. package/dist/media/react/components/MediaIcons.d.ts +0 -3
  64. package/dist/media/react/components/MediaIcons.js +0 -9
  65. package/dist/media/react/components/MediaLightbox.d.ts +2 -1
  66. package/dist/media/react/components/MediaLightbox.js +11 -5
  67. package/dist/media/react/hooks/LibraryTab.hooks.d.ts +55 -45
  68. package/dist/media/react/hooks/LibraryTab.hooks.js +107 -70
  69. package/dist/media/react/hooks/MediaCard.hooks.d.ts +27 -31
  70. package/dist/media/react/hooks/MediaCard.hooks.js +52 -26
  71. package/dist/media/react/hooks/MediaGrid.hooks.d.ts +1 -1
  72. package/dist/media/react/hooks/MediaLightbox.hooks.d.ts +10 -14
  73. package/dist/media/react/hooks/MediaLightbox.hooks.js +9 -56
  74. package/dist/media/react/hooks/MediaPage.hooks.d.ts +6 -0
  75. package/dist/media/react/hooks/MediaPage.hooks.js +29 -12
  76. package/dist/media/react/hooks/ProvidersTab.hooks.d.ts +33 -33
  77. package/dist/media/react/hooks/ProvidersTab.hooks.js +119 -79
  78. package/dist/media/react/hooks/UploadButton.hooks.d.ts +7 -5
  79. package/dist/media/react/hooks/UploadButton.hooks.js +36 -24
  80. package/dist/media/react/index.js +4 -0
  81. package/dist/media/react/tabs/LibraryTab.d.ts +1 -1
  82. package/dist/media/react/tabs/LibraryTab.js +2 -2
  83. package/dist/media/rules.d.ts +7 -0
  84. package/dist/media/rules.js +25 -2
  85. package/dist/react/components/ConfirmDialog/ConfirmDialog.js +5 -8
  86. package/dist/react/components/ConfirmDialog/confirm-dialog-attrs.d.ts +20 -0
  87. package/dist/react/components/ConfirmDialog/confirm-dialog-attrs.js +24 -0
  88. package/dist/react/components/InteractiveHtmlEditor/InteractiveHtmlEditor.js +16 -7
  89. package/dist/react/components/RowMenu/RowMenu.js +16 -10
  90. package/dist/react/components/Sidebar.js +32 -26
  91. package/dist/react/entities/EntityEdit.js +17 -14
  92. package/dist/react/entities/EntityField.d.ts +5 -2
  93. package/dist/react/entities/EntityField.js +38 -23
  94. package/dist/react/entities/EntityList.js +6 -5
  95. package/dist/react/entities/RelationTruncationNotice.d.ts +9 -0
  96. package/dist/react/entities/RelationTruncationNotice.js +9 -0
  97. package/dist/react/entities/use-entity-edit.d.ts +4 -2
  98. package/dist/react/entities/use-entity-edit.js +52 -33
  99. package/dist/react/shell/AdminShell.js +40 -23
  100. package/dist/react/shell/model.d.ts +4 -1
  101. package/dist/react/shell/model.js +3 -2
  102. package/dist/react/shell/use-admin-shell-session.js +25 -26
  103. package/dist/redirects/adapters/memory.js +9 -2
  104. package/dist/redirects/conformance/redirects-api.conformance.js +66 -36
  105. package/dist/redirects/react/hooks/use-import-redirects-form.hooks.d.ts +1 -1
  106. package/dist/redirects/react/hooks/use-import-redirects-form.hooks.js +10 -3
  107. package/dist/redirects/react/hooks/wired.hooks.d.ts +1 -1
  108. package/dist/security/adapters/http.js +25 -10
  109. package/dist/security/adapters/memory.js +19 -5
  110. package/dist/security/controllers/access-tokens.controller.js +27 -26
  111. package/dist/security/controllers/other-credentials.controller.js +4 -2
  112. package/dist/security/controllers/root-key.controller.js +17 -9
  113. package/dist/security/react/hooks/AccessTokensTab.hooks.d.ts +40 -38
  114. package/dist/security/react/hooks/AccessTokensTab.hooks.js +36 -9
  115. package/dist/security/react/hooks/RootKeyTab.hooks.d.ts +14 -14
  116. package/dist/security/react/hooks/RootKeyTab.hooks.js +12 -3
  117. package/dist/security/react/tabs/RootKeyTab.d.ts +1 -1
  118. package/dist/security/react/tabs/RootKeyTab.js +12 -3
  119. package/dist/security/rules.js +45 -25
  120. package/dist/seo/controllers/seo.controller.js +4 -2
  121. package/dist/seo/react/components/MediaRefField.d.ts +1 -1
  122. package/dist/seo/react/components/MediaRefField.js +19 -2
  123. package/dist/seo/react/hooks/MediaRefField.hooks.js +3 -1
  124. package/dist/seo/react/hooks/SeoPage.hooks.d.ts +1 -1
  125. package/dist/seo/react/hooks/use-share-image-warning.hooks.js +48 -34
  126. package/dist/seo/react/pages/SeoPage.d.ts +4 -4
  127. package/dist/seo/react/pages/SeoPage.js +26 -6
  128. package/dist/seo/rules.js +39 -10
  129. package/dist/source-control/conformance/source-control-api.conformance.js +68 -38
  130. package/dist/source-control/controllers/source-control.controller.js +20 -8
  131. package/dist/source-control/react/hooks/ProvidersTab.hooks.d.ts +2 -1
  132. package/dist/source-control/react/hooks/ProvidersTab.hooks.js +18 -11
  133. package/dist/widgets/conformance/widgets-api.conformance.js +15 -1
  134. package/dist/widgets/controllers/instance-editor.controller.js +33 -24
  135. package/dist/widgets/controllers/library.controller.js +18 -12
  136. package/dist/widgets/controllers/region-editor.controller.js +6 -5
  137. package/dist/widgets/react/hooks/page-views.hooks.d.ts +1 -0
  138. package/dist/widgets/react/hooks/page-views.hooks.js +3 -2
  139. package/dist/widgets/react/hooks/use-widget-regions.hooks.d.ts +1 -1
  140. package/dist/widgets/react/hooks/use-widget-regions.hooks.js +9 -3
  141. package/dist/widgets/react/hooks/use-widgets-library.hooks.js +9 -2
  142. package/dist/widgets/react/pages/WidgetInstanceEditor.d.ts +1 -1
  143. package/dist/widgets/react/pages/WidgetInstanceEditor.js +7 -8
  144. package/dist/widgets/rules.d.ts +14 -2
  145. package/dist/widgets/rules.js +9 -6
  146. package/package.json +17 -8
  147. package/src/themes/default/admin.css +51 -0
  148. package/src/themes/default/chat.css +21 -0
  149. package/src/themes/default/manifest.json +29 -0
  150. package/dist/media/styles.d.ts +0 -4
  151. package/dist/media/styles.js +0 -5
@@ -23,6 +23,39 @@ export function createLibraryController({ api }, { query = {} } = {}) {
23
23
  retryTimer = null;
24
24
  }
25
25
  let readAbort = null, generation = 0;
26
+ /** Store patch for an accepted full library: filtered rows plus whole-library tab counts.
27
+ * @complexity O(n) in the library size. */
28
+ function libraryView({ media }, _optional = {}) {
29
+ return { items: queryMedia({ media, query: store.getSnapshot().query }), counts: countMediaTabs({ media }) };
30
+ }
31
+ /** Commits a read that is still the latest one; items are frozen so consumers cannot mutate rows. */
32
+ function acceptRead({ all }, _optional = {}) {
33
+ readFailed = false;
34
+ retryAttempt = 0;
35
+ library = all;
36
+ const { items, counts } = libraryView({ media: all });
37
+ store.set({
38
+ patch: {
39
+ items: Object.freeze(items.map((item) => Object.freeze({ ...item }))),
40
+ counts,
41
+ hasUntyped: all.some((item) => item.contentType === null),
42
+ error: null,
43
+ },
44
+ });
45
+ }
46
+ /** Publishes the latest read's failure and, when the failure is transient, schedules a retry. */
47
+ function failRead({ error }, _optional = {}) {
48
+ readFailed = true;
49
+ store.set({
50
+ patch: { loading: false, error: error instanceof Error ? error.message : 'Failed to load media' },
51
+ });
52
+ // Retry reads only, never destructive writes. Keep trying at a capped delay so
53
+ // recovery needs no manual repair, while an offline service isn't hammered.
54
+ if (isRetryableMediaError({ error })) {
55
+ const delay = Math.min(1000 * 2 ** Math.min(retryAttempt++, 5), 30000);
56
+ retryTimer = setTimeout(() => { retryTimer = null; void load(); }, delay);
57
+ }
58
+ }
26
59
  async function load(_required = {}, _optional = {}) {
27
60
  if (store.signal.aborted)
28
61
  return;
@@ -34,35 +67,13 @@ export function createLibraryController({ api }, { query = {} } = {}) {
34
67
  store.set({ patch: { loading: true, error: null } });
35
68
  try {
36
69
  const all = await api.list({}, { signal: abort.signal });
37
- const items = queryMedia({ media: all, query: store.getSnapshot().query });
38
- if (ticket === generation) {
39
- readFailed = false;
40
- retryAttempt = 0;
41
- library = all;
42
- store.set({
43
- patch: {
44
- items: Object.freeze(items.map((item) => Object.freeze({ ...item }))),
45
- counts: countMediaTabs({ media: all }),
46
- hasUntyped: all.some((item) => item.contentType === null),
47
- error: null,
48
- },
49
- });
50
- }
70
+ if (ticket === generation)
71
+ acceptRead({ all });
51
72
  }
52
73
  catch (error) {
53
74
  // A later background failure must not blank an already-rendered screen.
54
- if (!abort.signal.aborted && ticket === generation) {
55
- readFailed = true;
56
- store.set({
57
- patch: { loading: false, error: error instanceof Error ? error.message : 'Failed to load media' },
58
- });
59
- // Retry reads only, never destructive writes. Keep trying at a capped delay so
60
- // recovery needs no manual repair, while an offline service isn't hammered.
61
- if (isRetryableMediaError({ error })) {
62
- const delay = Math.min(1000 * 2 ** Math.min(retryAttempt++, 5), 30000);
63
- retryTimer = setTimeout(() => { retryTimer = null; void load(); }, delay);
64
- }
65
- }
75
+ if (!abort.signal.aborted && ticket === generation)
76
+ failRead({ error });
66
77
  }
67
78
  finally {
68
79
  if (ticket === generation)
@@ -76,7 +87,7 @@ export function createLibraryController({ api }, { query = {} } = {}) {
76
87
  return;
77
88
  if (all) {
78
89
  library = all;
79
- store.set({ patch: { items: queryMedia({ media: all, query: store.getSnapshot().query }), counts: countMediaTabs({ media: all }), hasUntyped: all.some(item => item.contentType === null), error: null } });
90
+ store.set({ patch: { ...libraryView({ media: all }), hasUntyped: all.some(item => item.contentType === null), error: null } });
80
91
  }
81
92
  if (error)
82
93
  store.set({ patch: { error: error.message } });
@@ -100,17 +111,18 @@ export function createLibraryController({ api }, { query = {} } = {}) {
100
111
  readAbort?.abort();
101
112
  cancelRetry();
102
113
  store.set({ patch: { loading: false, error: null } });
114
+ // A single exit: a write disposed mid-flight or a failed write reports false.
115
+ let saved = false;
103
116
  try {
104
117
  await run();
105
- if (store.signal.aborted)
106
- return false;
107
- await load();
108
- return true;
118
+ if (!store.signal.aborted) {
119
+ await load();
120
+ saved = true;
121
+ }
109
122
  }
110
123
  catch (error) {
111
- if (!store.signal.aborted)
112
- store.set({ patch: { loading: false, error: error instanceof Error ? error.message : 'Media write failed' } });
113
- return false;
124
+ // The store ignores patches once disposed, so a write failing after dispose publishes nothing.
125
+ store.set({ patch: { loading: false, error: error instanceof Error ? error.message : 'Media write failed' } });
114
126
  }
115
127
  finally {
116
128
  if (id === undefined)
@@ -118,6 +130,7 @@ export function createLibraryController({ api }, { query = {} } = {}) {
118
130
  const pendingIds = store.getSnapshot().pendingIds.filter(value => value !== id);
119
131
  store.set({ patch: { busy: pendingIds.length > 0, pendingIds } });
120
132
  }
133
+ return saved;
121
134
  });
122
135
  writes = operation.catch(() => undefined);
123
136
  return operation;
@@ -168,7 +181,7 @@ export function createLibraryController({ api }, { query = {} } = {}) {
168
181
  // A failed follow-up read must not leave a permanently deleted card focusable.
169
182
  if (library) {
170
183
  library = library.filter(row => row.id !== item.id);
171
- store.set({ patch: { items: queryMedia({ media: library, query: store.getSnapshot().query }), counts: countMediaTabs({ media: library }) } });
184
+ store.set({ patch: libraryView({ media: library }) });
172
185
  }
173
186
  });
174
187
  if (saved)
@@ -46,11 +46,12 @@ export function createProvidersController({ api }, _optional = {}) {
46
46
  }
47
47
  catch {
48
48
  store.set({ patch: { error: 'Failed to save provider credentials' } });
49
- return false;
50
49
  }
51
50
  finally {
52
51
  store.set({ patch: { saving: false } });
53
52
  }
53
+ // Reached only through the catch above: the failure is already recorded in `error`.
54
+ return false;
54
55
  }
55
56
  return {
56
57
  getSnapshot: store.getSnapshot,
@@ -80,7 +80,9 @@ const ASCII_WHITESPACE_OR_CONTROL_MAX_CODE_POINT = 0x20;
80
80
  function isUnsafeMediaUrlValue(value) {
81
81
  let stripped = "";
82
82
  for (const char of value) {
83
- if ((char.codePointAt(0) ?? 0) > ASCII_WHITESPACE_OR_CONTROL_MAX_CODE_POINT)
83
+ // `char` is never empty, so `charCodeAt(0)` is always a number; for an astral character it is
84
+ // the high surrogate (>= 0xD800), which is kept exactly as the full code point would be.
85
+ if (char.charCodeAt(0) > ASCII_WHITESPACE_OR_CONTROL_MAX_CODE_POINT)
84
86
  stripped += char;
85
87
  }
86
88
  stripped = stripped.toLowerCase();
@@ -165,15 +167,11 @@ export function parseMediaHtmlAttributes({ text }, _optional = {}) {
165
167
  // Non-whitespace text between the previous match and this one is a fragment the token pattern
166
168
  // could not parse as a name (e.g. a stray quote) — the tokenizer has lost sync, so everything
167
169
  // parsed so far is discarded rather than trusted.
168
- const skipped = trimmed.slice(consumed, match.index);
169
- if (skipped.trim() !== "") {
170
- const malformed = { reason: "malformed", attribute: skipped.trim() };
171
- errors.push(malformed);
172
- return { attributes: {}, errors, error: malformed };
173
- }
170
+ const skipped = trimmed.slice(consumed, match.index).trim();
171
+ if (skipped !== "")
172
+ return malformedMediaHtmlAttributes({ errors, fragment: skipped });
174
173
  consumed = match.index + match[0].length;
175
- const name = match[1].toLowerCase();
176
- const value = match[2] ?? match[3] ?? match[4] ?? "";
174
+ const { name, value } = readMediaHtmlAttributeToken({ match });
177
175
  const rejection = classifyMediaHtmlAttributeToken(name, value);
178
176
  if (rejection) {
179
177
  errors.push(rejection);
@@ -181,14 +179,30 @@ export function parseMediaHtmlAttributes({ text }, _optional = {}) {
181
179
  }
182
180
  attributes[name] = value;
183
181
  }
184
- const trailing = trimmed.slice(consumed);
185
- if (trailing.trim() !== "") {
186
- const malformed = { reason: "malformed", attribute: trailing.trim() };
187
- errors.push(malformed);
188
- return { attributes: {}, errors, error: malformed };
189
- }
182
+ const trailing = trimmed.slice(consumed).trim();
183
+ if (trailing !== "")
184
+ return malformedMediaHtmlAttributes({ errors, fragment: trailing });
190
185
  return { attributes, errors, error: errors[0] ?? null };
191
186
  }
187
+ /** The lowercased name and the value of one {@link HTML_ATTRIBUTE_TOKEN} match: whichever of the
188
+ * double-quoted, single-quoted or unquoted value groups matched, or `""` for a bare boolean name.
189
+ *
190
+ * @complexity O(n) in the name's length (one lowercase pass).
191
+ */
192
+ function readMediaHtmlAttributeToken({ match }, _optional = {}) {
193
+ return { name: match[1].toLowerCase(), value: match[2] ?? match[3] ?? match[4] ?? "" };
194
+ }
195
+ /** The fail-closed result once the tokenizer loses sync: `fragment` (already trimmed) is appended
196
+ * to `errors` as the final `malformed` entry, and every accepted attribute is discarded — see
197
+ * {@link parseMediaHtmlAttributes}'s header for why. Mutates `errors` (the caller's own list).
198
+ *
199
+ * @complexity O(1).
200
+ */
201
+ function malformedMediaHtmlAttributes({ errors, fragment }, _optional = {}) {
202
+ const malformed = { reason: "malformed", attribute: fragment };
203
+ errors.push(malformed);
204
+ return { attributes: {}, errors, error: malformed };
205
+ }
192
206
  /** The hint names the rejected token so operators can distinguish a typo from a hard no.
193
207
  * This is client feedback only; the server must independently enforce write/render policy. */
194
208
  export function describeMediaHtmlAttributeError({ error }, _optional = {}) {
@@ -3,7 +3,7 @@ export { mediaApiToken, mediaProvidersToken } from './ports.js';
3
3
  export type { MediaApiPort, MediaProvidersPort, MediaEventsPort } from './ports.js';
4
4
  export type { MediaAsset, MediaQuery, UploadInput, MediaMetadataPatch, MediaRequestOptions, MediaProvider, MediaProviderOption, MediaProviderConfiguration, MediaProviderConfigurationMap, MediaTabId, MediaContentTabId, MediaOrderBy, } from './models.js';
5
5
  export { mediaMessagesEn } from './messages.en.js';
6
- export { filterMediaByTab, resolveActiveTab, queryMedia, diffMediaMetadata, acceptsMedia, safeMediaUrl, canReplaceMedia, canRestoreMedia, isRetryableMediaError, parseOptionalPixelSize, formatByteSize, formatUploadDate, countMediaTabs, mediaEmbedSnippet, mediaRowHandles, } from './rules.js';
6
+ export { filterMediaByTab, resolveActiveTab, queryMedia, diffMediaMetadata, acceptsMedia, safeMediaUrl, canReplaceMedia, canRestoreMedia, isRetryableMediaError, parseOptionalPixelSize, formatByteSize, formatUploadDate, mediaFileName, countMediaTabs, mediaEmbedSnippet, mediaRowHandles, } from './rules.js';
7
7
  export { createLibraryController } from './controllers/library.controller.js';
8
8
  export type { LibraryState } from './controllers/library.controller.js';
9
9
  export { createEditMediaController } from './controllers/edit-media.controller.js';
@@ -1,7 +1,7 @@
1
1
  export { mediaModule } from './media.module.js';
2
2
  export { mediaApiToken, mediaProvidersToken } from './ports.js';
3
3
  export { mediaMessagesEn } from './messages.en.js';
4
- export { filterMediaByTab, resolveActiveTab, queryMedia, diffMediaMetadata, acceptsMedia, safeMediaUrl, canReplaceMedia, canRestoreMedia, isRetryableMediaError, parseOptionalPixelSize, formatByteSize, formatUploadDate, countMediaTabs, mediaEmbedSnippet, mediaRowHandles, } from './rules.js';
4
+ export { filterMediaByTab, resolveActiveTab, queryMedia, diffMediaMetadata, acceptsMedia, safeMediaUrl, canReplaceMedia, canRestoreMedia, isRetryableMediaError, parseOptionalPixelSize, formatByteSize, formatUploadDate, mediaFileName, countMediaTabs, mediaEmbedSnippet, mediaRowHandles, } from './rules.js';
5
5
  export { createLibraryController } from './controllers/library.controller.js';
6
6
  export { createEditMediaController } from './controllers/edit-media.controller.js';
7
7
  export { createProvidersController } from './controllers/providers.controller.js';
@@ -4,9 +4,6 @@ export declare function ReloadIcon(_required: Record<string, never>, _optional?:
4
4
  export declare function HiddenEyeIcon(_required: Record<string, never>, _optional?: Record<string, never>): import("react").JSX.Element;
5
5
  export declare function EyeIcon(_required: Record<string, never>, _optional?: Record<string, never>): import("react").JSX.Element;
6
6
  export declare function ExpandIcon(_required: Record<string, never>, _optional?: Record<string, never>): import("react").JSX.Element;
7
- export declare function CloseIcon(_required: Record<string, never>, _optional?: Record<string, never>): import("react").JSX.Element;
8
- export declare function PreviousIcon(_required: Record<string, never>, _optional?: Record<string, never>): import("react").JSX.Element;
9
- export declare function NextIcon(_required: Record<string, never>, _optional?: Record<string, never>): import("react").JSX.Element;
10
7
  export declare function AllIcon(_required: Record<string, never>, _optional?: Record<string, never>): import("react").JSX.Element;
11
8
  export declare function ImageIcon(_required: Record<string, never>, _optional?: Record<string, never>): import("react").JSX.Element;
12
9
  export declare function VideoIcon(_required: Record<string, never>, _optional?: Record<string, never>): import("react").JSX.Element;
@@ -15,15 +15,6 @@ export function EyeIcon(_required, _optional = {}) {
15
15
  export function ExpandIcon(_required, _optional = {}) {
16
16
  return _jsx("svg", { className: "jini-icon", viewBox: "0 0 18 18", fill: "none", stroke: "currentColor", strokeWidth: "1.5", "aria-hidden": "true", children: _jsx("path", { className: "jini-icon-path", d: "M6 2H2v4m10-4h4v4M2 12v4h4m10-4v4h-4" }) });
17
17
  }
18
- export function CloseIcon(_required, _optional = {}) {
19
- return _jsx("svg", { className: "jini-icon", viewBox: "0 0 18 18", fill: "none", stroke: "currentColor", strokeWidth: "1.5", "aria-hidden": "true", children: _jsx("path", { className: "jini-icon-path", d: "m4 4 10 10M14 4 4 14" }) });
20
- }
21
- export function PreviousIcon(_required, _optional = {}) {
22
- return _jsx("svg", { className: "jini-icon", viewBox: "0 0 18 18", fill: "none", stroke: "currentColor", strokeWidth: "1.5", "aria-hidden": "true", children: _jsx("path", { className: "jini-icon-path", d: "m11 4-5 5 5 5" }) });
23
- }
24
- export function NextIcon(_required, _optional = {}) {
25
- return _jsx("svg", { className: "jini-icon", viewBox: "0 0 18 18", fill: "none", stroke: "currentColor", strokeWidth: "1.5", "aria-hidden": "true", children: _jsx("path", { className: "jini-icon-path", d: "m7 4 5 5-5 5" }) });
26
- }
27
18
  export function AllIcon(_required, _optional = {}) {
28
19
  return _jsxs("svg", { className: "jini-icon", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [_jsx("rect", { className: "jini-icon-rect", x: "4", y: "4", width: "6.5", height: "6.5", rx: "1.2" }), _jsx("rect", { className: "jini-icon-rect", x: "13.5", y: "4", width: "6.5", height: "6.5", rx: "1.2" }), _jsx("rect", { className: "jini-icon-rect", x: "4", y: "13.5", width: "6.5", height: "6.5", rx: "1.2" }), _jsx("rect", { className: "jini-icon-rect", x: "13.5", y: "13.5", width: "6.5", height: "6.5", rx: "1.2" })] });
29
20
  }
@@ -1,5 +1,6 @@
1
1
  import type { MediaLightboxProps } from '../hooks/MediaLightbox.hooks.js';
2
2
  /** One shared dialog for the grid: mounting N real dialogs to show at most one open
3
- * at a time buys nothing and costs N DOM subtrees. */
3
+ * at a time buys nothing and costs N DOM subtrees. Chrome, keyboard and focus come from
4
+ * the shared `Lightbox`; this only supplies the asset and its agent-addressable parts. */
4
5
  export declare function MediaLightbox(props: MediaLightboxProps, _optional?: Record<string, never>): import("react").JSX.Element;
5
6
  //# sourceMappingURL=MediaLightbox.d.ts.map
@@ -1,12 +1,18 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { MediaDialog } from './MediaDialog.js';
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { Lightbox } from '@jini-ai/ui/admin-widgets';
3
3
  import { useMediaLightbox } from '../hooks/MediaLightbox.hooks.js';
4
- import { CloseIcon, PreviousIcon, NextIcon } from './MediaIcons.js';
5
4
  import { MediaPreview } from './MediaCard.js';
6
5
  /** One shared dialog for the grid: mounting N real dialogs to show at most one open
7
- * at a time buys nothing and costs N DOM subtrees. */
6
+ * at a time buys nothing and costs N DOM subtrees. Chrome, keyboard and focus come from
7
+ * the shared `Lightbox`; this only supplies the asset and its agent-addressable parts. */
8
8
  export function MediaLightbox(props, _optional = {}) {
9
9
  const vm = useMediaLightbox(props);
10
- return (_jsxs(MediaDialog, { className: "jini-media-lightbox", dialogRef: vm.dialogRef, attrs: { 'data-jini-part': 'media.lightbox', 'data-jini-size': 'large' }, title: vm.title, open: vm.open, onClose: vm.onClose, children: [_jsxs("div", { className: "jini-media-lightbox-header", children: [_jsx("span", { className: "jini-media-lightbox-title", "aria-hidden": "true", children: vm.item?.title }), _jsx("span", { className: "jini-media-lightbox-counter", children: vm.position }), _jsxs("button", { type: "button", className: "jini-media-lightbox-close", ref: vm.closeRef, "data-jini-part": "media.lightbox.close", "data-agent-element": "media-lightbox-close", "data-jini-autofocus": true, onClick: vm.onClose, children: [_jsx(CloseIcon, {}), _jsx("span", { className: "jini-visually-hidden", children: "Close" })] })] }), _jsxs("div", { className: "jini-media-lightbox-stage", children: [vm.hasPrev && _jsxs("button", { type: "button", className: "jini-media-lightbox-nav jini-media-lightbox-nav-prev", "data-jini-part": "media.lightbox.previous", "data-agent-element": "media-lightbox-prev", onClick: vm.previous, children: [_jsx(PreviousIcon, {}), _jsx("span", { className: "jini-visually-hidden", children: "Previous" })] }), _jsx("div", { className: "jini-media-lightbox-media", children: vm.item && _jsx(MediaPreview, { item: vm.item, api: vm.api }, vm.item.id) }), vm.hasNext && _jsxs("button", { type: "button", className: "jini-media-lightbox-nav jini-media-lightbox-nav-next", "data-jini-part": "media.lightbox.next", "data-agent-element": "media-lightbox-next", onClick: vm.next, children: [_jsx(NextIcon, {}), _jsx("span", { className: "jini-visually-hidden", children: "Next" })] })] })] }));
10
+ return (_jsx(Lightbox, { ...vm.lightbox, className: "jini-media-lightbox", labels: { close: 'Close', previous: 'Previous', next: 'Next' }, partAttrs: MEDIA_LIGHTBOX_PARTS, children: vm.item && _jsx("div", { className: "jini-media-lightbox-media", children: _jsx(MediaPreview, { item: vm.item, api: props.api }, vm.item.id) }) }));
11
11
  }
12
+ const MEDIA_LIGHTBOX_PARTS = {
13
+ dialog: { 'data-jini-part': 'media.lightbox', 'data-jini-size': 'large' },
14
+ close: { 'data-jini-part': 'media.lightbox.close', 'data-agent-element': 'media-lightbox-close' },
15
+ previous: { 'data-jini-part': 'media.lightbox.previous', 'data-agent-element': 'media-lightbox-prev' },
16
+ next: { 'data-jini-part': 'media.lightbox.next', 'data-agent-element': 'media-lightbox-next' },
17
+ };
12
18
  //# sourceMappingURL=MediaLightbox.js.map
@@ -1,19 +1,65 @@
1
+ import type { RefObject } from 'react';
1
2
  import type { TabViewProps } from '../../../core/react/bind-react.js';
2
3
  import { createLibraryController } from '../../controllers/library.controller.js';
3
4
  import type { MediaAsset } from '../../models.js';
5
+ import type { LibraryState } from '../../controllers/library.controller.js';
6
+ import type { MediaApiPort } from '../../ports.js';
7
+ type LibraryController = ReturnType<typeof createLibraryController>;
4
8
  export declare function useLibraryTab({ params, permissions }: TabViewProps, _optional?: Record<string, never>): {
5
- emptyLabel: string;
6
- canUpload: boolean;
7
- success: string | null;
8
- gridRef: import("react").RefObject<HTMLDivElement | null>;
9
- onRetry: () => void;
9
+ purgeDialog: {
10
+ filename?: string;
11
+ open: boolean;
12
+ pending: boolean;
13
+ };
14
+ upload({ input, alt }: Parameters<LibraryController["upload"]>[0]): Promise<boolean>;
15
+ cancelPurge: () => void | undefined;
16
+ confirmPurge: () => Promise<void> | undefined;
17
+ t: {
18
+ readonly media: "Media";
19
+ readonly all: "All";
20
+ readonly images: "Images";
21
+ readonly videos: "Videos";
22
+ readonly providers: "External Providers";
23
+ readonly upload: "Upload";
24
+ readonly title: "Title";
25
+ readonly alt: "Alt text";
26
+ readonly save: "Save";
27
+ readonly cancel: "Cancel";
28
+ readonly empty: "No media found";
29
+ readonly loading: "Loading media…";
30
+ readonly choose: "Choose media";
31
+ readonly trash: "Trash";
32
+ readonly purge: "Delete permanently";
33
+ readonly purgeWarning: "This permanently deletes the file. This cannot be undone.";
34
+ readonly edit: "Edit metadata";
35
+ readonly preview: "View larger";
36
+ readonly replace: "Replace file";
37
+ readonly unavailable: "Provider service unavailable";
38
+ readonly untyped: "Some assets have no recorded content type and only appear under All.";
39
+ };
40
+ loading: boolean;
41
+ busy: boolean;
42
+ untyped: boolean;
10
43
  statusFilter: string;
44
+ orderBy: import("../../models.js").MediaOrderBy;
45
+ search: string;
11
46
  onStatus: ({ value }: {
12
47
  value: string;
13
48
  }) => void;
49
+ onSearch: ({ value: search }: {
50
+ value: string;
51
+ }) => void;
52
+ onOrder: ({ value }: {
53
+ value: string;
54
+ }) => void;
55
+ emptyLabel: string;
56
+ canUpload: boolean;
57
+ success: string | null;
58
+ gridRef: RefObject<HTMLDivElement | null>;
59
+ onRetry: () => void;
14
60
  canPurge: boolean;
15
61
  controller: {
16
- getSnapshot: () => Readonly<import("../../index.js").LibraryState>;
62
+ getSnapshot: () => Readonly<LibraryState>;
17
63
  subscribe: ({ listener }: {
18
64
  listener: () => void;
19
65
  }) => () => void;
@@ -44,7 +90,7 @@ export declare function useLibraryTab({ params, permissions }: TabViewProps, _op
44
90
  }, _optional?: Record<string, never>): Promise<boolean>;
45
91
  dispose(_required?: Record<string, never>, _optional?: Record<string, never>): void;
46
92
  } | null;
47
- snapshot: import("../../index.js").LibraryState | null;
93
+ snapshot: LibraryState | null;
48
94
  actions: {
49
95
  onPurge?: () => void | undefined;
50
96
  onRestore?: () => void;
@@ -55,7 +101,7 @@ export declare function useLibraryTab({ params, permissions }: TabViewProps, _op
55
101
  busy: boolean;
56
102
  }[];
57
103
  items: readonly MediaAsset[];
58
- mediaApi: import("../../ports.js").MediaApiPort;
104
+ mediaApi: MediaApiPort;
59
105
  editing: MediaAsset | null;
60
106
  closeEdit: () => void;
61
107
  saved: ({ message }?: {
@@ -64,42 +110,6 @@ export declare function useLibraryTab({ params, permissions }: TabViewProps, _op
64
110
  lightboxIndex: number | null;
65
111
  onNavigate: import("react").Dispatch<import("react").SetStateAction<number | null>>;
66
112
  closeLightbox: () => void;
67
- onSearch: ({ value: search }: {
68
- value: string;
69
- }) => void;
70
- orderBy: import("../../models.js").MediaOrderBy;
71
- onOrder: ({ value }: {
72
- value: string;
73
- }) => void;
74
- search: string;
75
- loading: boolean;
76
- busy: boolean;
77
- untyped: boolean;
78
- upload({ input, alt }: Parameters<ReturnType<typeof createLibraryController>["upload"]>[0]): Promise<boolean>;
79
- cancelPurge: () => void | undefined;
80
- confirmPurge: () => Promise<void> | undefined;
81
- t: {
82
- readonly media: "Media";
83
- readonly all: "All";
84
- readonly images: "Images";
85
- readonly videos: "Videos";
86
- readonly providers: "External Providers";
87
- readonly upload: "Upload";
88
- readonly title: "Title";
89
- readonly alt: "Alt text";
90
- readonly save: "Save";
91
- readonly cancel: "Cancel";
92
- readonly empty: "No media found";
93
- readonly loading: "Loading media…";
94
- readonly choose: "Choose media";
95
- readonly trash: "Trash";
96
- readonly purge: "Delete permanently";
97
- readonly purgeWarning: "This permanently deletes the file. This cannot be undone.";
98
- readonly edit: "Edit metadata";
99
- readonly preview: "View larger";
100
- readonly replace: "Replace file";
101
- readonly unavailable: "Provider service unavailable";
102
- readonly untyped: "Some assets have no recorded content type and only appear under All.";
103
- };
104
113
  };
114
+ export {};
105
115
  //# sourceMappingURL=LibraryTab.hooks.d.ts.map