@jini-ai/admin 0.4.10 → 0.4.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +10 -0
- package/dist/comments/rules.d.ts +3 -3
- package/dist/comments/rules.js +3 -3
- package/dist/core/ports/seo.d.ts +20 -0
- package/dist/forms/field-settings.d.ts +1 -0
- package/dist/forms/field-settings.js +4 -4
- package/dist/forms/react/components/FieldAttributesDialog.js +1 -1
- package/dist/forms/react/hooks/use-forms-list.hooks.d.ts +1 -1
- package/dist/forms/rules.d.ts +2 -2
- package/dist/forms/rules.js +2 -2
- package/dist/redirects/react/hooks/RedirectsPage.hooks.js +1 -1
- package/dist/redirects/react/pages/RedirectsPage.js +2 -1
- package/dist/redirects/rules.d.ts +2 -2
- package/dist/redirects/rules.js +2 -2
- package/dist/seo/adapters/http.js +5 -0
- package/dist/seo/adapters/memory.js +1 -1
- package/dist/seo/messages.en.d.ts +15 -10
- package/dist/seo/messages.en.js +15 -10
- package/dist/seo/models.d.ts +11 -0
- package/dist/seo/react/components/MediaRefField.d.ts +17 -5
- package/dist/seo/react/components/MediaRefField.js +14 -4
- package/dist/seo/react/components/SitemapModal.js +1 -1
- package/dist/seo/react/hooks/MediaRefField.hooks.js +10 -2
- package/dist/seo/react/hooks/SeoPage.hooks.d.ts +1 -0
- package/dist/seo/react/hooks/SeoPage.hooks.js +2 -2
- package/dist/seo/react/hooks/SeoQuery.hooks.d.ts +1 -0
- package/dist/seo/react/hooks/SeoQuery.hooks.js +1 -0
- package/dist/seo/react/hooks/use-entry-picker.hooks.js +7 -2
- package/dist/seo/react/hooks/use-seo-entry-panel.hooks.js +13 -2
- package/dist/seo/react/hooks/use-share-image-warning.hooks.d.ts +4 -1
- package/dist/seo/react/hooks/use-share-image-warning.hooks.js +1 -0
- package/dist/seo/react/index.d.ts +45 -30
- package/dist/seo/react/pages/SeoPage.d.ts +3 -1
- package/dist/seo/react/pages/SeoPage.js +26 -24
- package/dist/seo/rules.d.ts +95 -4
- package/dist/seo/rules.js +134 -8
- package/dist/seo/seo.module.d.ts +15 -10
- package/package.json +14 -8
package/dist/seo/rules.js
CHANGED
|
@@ -23,7 +23,7 @@ export function sortIssuesBySeverity({ issues }, _optional = {}) {
|
|
|
23
23
|
/**
|
|
24
24
|
* One edited per-entry override field's outgoing value. An emptied text/URL box means "remove this
|
|
25
25
|
* override so the entry falls back to the site default", which on the wire is `null` — the clear
|
|
26
|
-
* sentinel `SeoExtFieldsPatch` documents (`
|
|
26
|
+
* sentinel `SeoExtFieldsPatch` documents (`features/seo/types.ts`) and
|
|
27
27
|
* `setEntrySeoOverrides` implements by `delete`-ing the key.
|
|
28
28
|
*
|
|
29
29
|
* It is NOT `""`. `write-service.ts` stores `""` as a genuine override and `seo.ts` resolves
|
|
@@ -46,6 +46,43 @@ export function overrideOrClear({ value }, _optional = {}) {
|
|
|
46
46
|
export function orEmpty({ value }, _optional = {}) {
|
|
47
47
|
return value ?? "";
|
|
48
48
|
}
|
|
49
|
+
/** One entry's effective X value next to its effective social preview value, per field. Images
|
|
50
|
+
* compare by source ref (the resolved URL is resized), falling back to the URL for pasted ones. */
|
|
51
|
+
function xAndSocial({ meta }) {
|
|
52
|
+
return {
|
|
53
|
+
twitterTitle: [meta.twitter.title, meta.openGraph.title],
|
|
54
|
+
twitterDescription: [meta.twitter.description, meta.openGraph.description],
|
|
55
|
+
twitterImage: [meta.twitter.imageRef ?? meta.twitter.image, meta.openGraph.imageRef ?? meta.openGraph.image],
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
/** Whether this entry's X card differs from its social preview in any field — i.e. an X override
|
|
59
|
+
* is in effect, so the "Customize for X/Twitter" disclosure opens on load. (2026-10-15, owner: one
|
|
60
|
+
* generic social preview, X only as an optional customization.)
|
|
61
|
+
* @complexity O(1). */
|
|
62
|
+
export function xIsCustomized({ meta }, _optional = {}) {
|
|
63
|
+
return Object.values(xAndSocial({ meta })).some(([x, social]) => x !== social);
|
|
64
|
+
}
|
|
65
|
+
/** An X field's untouched box value, handed to `fieldValue` as its resolved value (so a pending
|
|
66
|
+
* edit or clear still wins there). An X value equal to the social preview's is inherited, so the
|
|
67
|
+
* box stays empty and its placeholder shows the live social value — typing there is what creates
|
|
68
|
+
* an X override.
|
|
69
|
+
* @complexity O(1). */
|
|
70
|
+
export function xResolvedValue({ key, meta }, _optional = {}) {
|
|
71
|
+
const [x, social] = xAndSocial({ meta })[key];
|
|
72
|
+
return x === social ? '' : x ?? '';
|
|
73
|
+
}
|
|
74
|
+
/** The X field an edit to a social preview field should carry along: when X still shows the
|
|
75
|
+
* preview's value and has no pending edit of its own, the edit also clears X's stored override so
|
|
76
|
+
* X keeps following the preview after save. Without it, an X override saved equal to the preview
|
|
77
|
+
* (indistinguishable here, the screen sees only effective values) would silently pin X to the old
|
|
78
|
+
* text. Clearing an override that does not exist is a no-op on the server.
|
|
79
|
+
* @complexity O(1). */
|
|
80
|
+
export function inheritedXFor({ key, meta, touched }, _optional = {}) {
|
|
81
|
+
const xKey = { ogTitle: 'twitterTitle', ogDescription: 'twitterDescription', ogImage: 'twitterImage' }[key];
|
|
82
|
+
if (!xKey || xKey in touched)
|
|
83
|
+
return null;
|
|
84
|
+
return xResolvedValue({ key: xKey, meta }) === '' ? xKey : null;
|
|
85
|
+
}
|
|
49
86
|
/** A pending-action button's label — `react/pages/SeoPage.tsx` uses this for both the save-settings button
|
|
50
87
|
* ("Saving…"/"Save settings") and the regenerate-sitemap button ("Working…"/"Regenerate
|
|
51
88
|
* sitemap"), same `pending ? … : …` shape, different copy. Also reused by `SitemapModal.tsx`'s
|
|
@@ -89,10 +126,10 @@ export function filterSitemapEntries({ entries, query }, _optional = {}) {
|
|
|
89
126
|
* `MediaRefField`'s ref-building/preview logic (`ogImage`/`twitterImage`/`defaultOgImage` all
|
|
90
127
|
* share this shape — SPEC intent: "make the OG image selectable, with a preview"). The reference
|
|
91
128
|
* format itself is NOT reinvented here — it is verified, not guessed, against the one function that
|
|
92
|
-
* actually parses it server-side: `resolveSeoImageRef` (`
|
|
129
|
+
* actually parses it server-side: `resolveSeoImageRef` (`features/seo/media.ts`)
|
|
93
130
|
* accepts either an already-absolute URL, passed through unchanged, or a bare `{assetId}:{transformName}`
|
|
94
131
|
* pair split on the FIRST `:`. `isAbsoluteMediaRef`/`parseMediaRefAssetId` below duplicate that
|
|
95
|
-
* parsing rather than import across the app boundary (
|
|
132
|
+
* parsing rather than import across the app boundary (the admin UI has no dependency on the host server)
|
|
96
133
|
* — kept in lockstep by this doc comment naming the source of truth; a change to that split MUST
|
|
97
134
|
* update both.
|
|
98
135
|
*/
|
|
@@ -101,7 +138,7 @@ export function filterSitemapEntries({ entries, query }, _optional = {}) {
|
|
|
101
138
|
* `transformName: "public"` for the identical `{assetId}:{transformName}` shape it writes into a
|
|
102
139
|
* post body's image node. */
|
|
103
140
|
const SEO_IMAGE_TRANSFORM = "public";
|
|
104
|
-
/** Mirrors `resolveSeoImageRef`'s own `isAbsoluteUrl` (`
|
|
141
|
+
/** Mirrors `resolveSeoImageRef`'s own `isAbsoluteUrl` (`features/seo/media.ts`) —
|
|
105
142
|
* see this section's file-header doc for why this is a duplicate, not an import. */
|
|
106
143
|
const ABSOLUTE_URL_PATTERN = /^[a-z][a-z0-9+.-]*:\/\//i;
|
|
107
144
|
function isAbsoluteMediaRef(value) {
|
|
@@ -126,10 +163,68 @@ export function isStoredMediaRef({ value }, _optional = {}) {
|
|
|
126
163
|
// A URL typed one character at a time reaches "https:/" before it reaches "https://".
|
|
127
164
|
return !trimmed.includes(":/") && parseMediaRefAssetId(trimmed) !== null;
|
|
128
165
|
}
|
|
166
|
+
const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
167
|
+
/** What an image field shows for its value — the file's name and pixel size — in place of the raw
|
|
168
|
+
* `{slug}:public` reference or URL (owner, 2026-10-15: a read-only "Selected image" box "looks
|
|
169
|
+
* useless"). The library row wins when the host can list media; otherwise a readable slug or the
|
|
170
|
+
* URL's file name stands in, and an opaque id falls back to the translated "Image".
|
|
171
|
+
* Size prefers the stored dimensions, then the decoded preview's natural size.
|
|
172
|
+
* @example describeMediaRef({ value: 'sunset:public', media: [{ id: 'a1', slug: 'sunset', title: 'Sunset', width: 1200, height: 630 }] }) // { name: 'Sunset', size: '1200 × 630' }
|
|
173
|
+
* @complexity O(m) over the media rows; no I/O.
|
|
174
|
+
*/
|
|
175
|
+
export function describeMediaRef({ value, media }, { width, height, t = key => key, extensionFor } = {}) {
|
|
176
|
+
const trimmed = typeof value === 'string' ? value.trim() : '';
|
|
177
|
+
if (!trimmed)
|
|
178
|
+
return null;
|
|
179
|
+
const key = parseMediaRefAssetId(trimmed);
|
|
180
|
+
const row = key ? media?.find(item => item.id === key || item.slug === key) : undefined;
|
|
181
|
+
const name = row?.title ? withExtension({ name: row.title, extension: row.contentType && extensionFor ? extensionFor(row.contentType) : '' }) : fallbackMediaName({ key, value: trimmed, t });
|
|
182
|
+
return { name, size: pixelSize(row?.width ?? width, row?.height ?? height) };
|
|
183
|
+
}
|
|
184
|
+
/** A library title reads as a file name (owner, 2026-10-15: "it should be testing-page.png or
|
|
185
|
+
* whatever the extension is"). No original upload name is stored, so the extension comes from the
|
|
186
|
+
* stored bytes' type; a title that already ends in it is left alone. */
|
|
187
|
+
function withExtension({ name, extension }) {
|
|
188
|
+
return extension && !name.toLowerCase().endsWith(extension) ? `${name}${extension}` : name;
|
|
189
|
+
}
|
|
190
|
+
/** Whether an entry's social preview image comes from content or the site default, with no override
|
|
191
|
+
* of its own, so the field shows its source muted. "Using site default" is the original copy (owner,
|
|
192
|
+
* 2026-10-15). A pending edit is the entry's own, and so is a stored override the host flags
|
|
193
|
+
* (`imageOverridden`) even when it equals the default. A host without that flag can't tell such an
|
|
194
|
+
* override apart, so it reads as inherited (same image either way). Returns 'content' for an image
|
|
195
|
+
* the host identifies as content, 'default' for the site default, and false for an own/absent image.
|
|
196
|
+
* @complexity O(1). */
|
|
197
|
+
export function ogImageInherited({ meta, defaultOgImage, touched }, _optional = {}) {
|
|
198
|
+
if ('ogImage' in touched || meta.openGraph.imageOverridden)
|
|
199
|
+
return false;
|
|
200
|
+
const image = meta.openGraph.imageRef ?? meta.openGraph.image;
|
|
201
|
+
if (meta.openGraph.imageSource === 'content' && image)
|
|
202
|
+
return 'content';
|
|
203
|
+
return defaultOgImage && image === defaultOgImage ? 'default' : false;
|
|
204
|
+
}
|
|
205
|
+
function fallbackMediaName({ key, value, t }) {
|
|
206
|
+
if (!key)
|
|
207
|
+
return urlFileName(value);
|
|
208
|
+
return UUID_PATTERN.test(key) ? t('Image') : key;
|
|
209
|
+
}
|
|
210
|
+
function pixelSize(width, height) {
|
|
211
|
+
return width && height ? `${width} × ${height}` : null;
|
|
212
|
+
}
|
|
213
|
+
/** The last path segment of an image URL ("https://cdn.example/a/pic.png" → "pic.png"). */
|
|
214
|
+
function urlFileName(value) {
|
|
215
|
+
try {
|
|
216
|
+
const url = new URL(value, 'https://placeholder.invalid');
|
|
217
|
+
const last = url.pathname.split('/').filter(Boolean).pop();
|
|
218
|
+
return last ? decodeURIComponent(last) : url.hostname || value;
|
|
219
|
+
}
|
|
220
|
+
catch {
|
|
221
|
+
return value;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
129
224
|
/** Builds the `{slug}:public` reference `MediaRefField` writes into the field on selection —
|
|
130
225
|
* prefers the asset's readable slug over its id (readable-slugs S5b, 2026-09-23), matching
|
|
131
226
|
* `resolveSeoImageRef`'s own id-or-slug lookup server-side (`findMediaByIdOrSlug`,
|
|
132
|
-
* `
|
|
227
|
+
* `features/seo/media.ts`, S4) so a saved override reads as a slug, not an opaque
|
|
133
228
|
* uuid. Falls back to `id` when `slug` is empty — defensive only; a real upload always derives one
|
|
134
229
|
* (`deriveUniqueMediaSlug`). Same `transformName: "public"` `EmbedInsertControl.tsx` already writes
|
|
135
230
|
* for an inserted image node. An old stored `{assetId}:public` value keeps resolving regardless
|
|
@@ -149,7 +244,7 @@ export function buildMediaRef({ item }, _optional = {}) {
|
|
|
149
244
|
* "string"` also resolves to `null` rather than throwing — `MediaRefField`'s `value` prop is a
|
|
150
245
|
* `fieldValue(key, resolved) ?? ""` fallback chain (`react/pages/SeoPage.tsx`), where `??` does not replace a
|
|
151
246
|
* non-nullish-but-wrong-typed result from a misbehaving caller; matches `resolveSeoImageRef`'s own
|
|
152
|
-
* "never throws over one bad reference" contract (`
|
|
247
|
+
* "never throws over one bad reference" contract (`features/seo/media.ts`).
|
|
153
248
|
*
|
|
154
249
|
* @complexity O(1) — string parsing only, no I/O (the returned URL is a template; the browser
|
|
155
250
|
* performs the actual fetch only once it is used as an `<img src>`). */
|
|
@@ -205,7 +300,7 @@ export function shareImageWarning({ value, previewUrl, width, height, byteSize,
|
|
|
205
300
|
/** One `FormData` field as a string, with the caller's fallback for an absent field. Its own named
|
|
206
301
|
* function purely so {@link buildSeoSettingsPatch} below stays readable at a glance: written
|
|
207
302
|
* inline, the seven fields' `?? `/`||` chains scored the patch builder at the ESLint ceiling
|
|
208
|
-
* (
|
|
303
|
+
* (the admin lint policy enforces 9/9 as a hard error) for no gain in clarity. */
|
|
209
304
|
function formString(form, key, fallback = "") {
|
|
210
305
|
return String(form.get(key) ?? fallback);
|
|
211
306
|
}
|
|
@@ -214,7 +309,7 @@ function formString(form, key, fallback = "") {
|
|
|
214
309
|
*
|
|
215
310
|
* `null` is what CLEARS a site default: the server registers these three (`default_description`,
|
|
216
311
|
* `default_og_image`, `twitter_site`) as `nullable: true` and `buildScalarWrites`
|
|
217
|
-
* (`
|
|
312
|
+
* (`features/seo/settings.ts`) normalizes `null` to the `""` absent-sentinel that
|
|
218
313
|
* `getSeoSettings` reads back as `undefined`.
|
|
219
314
|
*
|
|
220
315
|
* It used to return `undefined`, under a comment claiming "an empty box means 'no site default'".
|
|
@@ -259,4 +354,35 @@ export const SEO_TAB_IDS = ['defaults', 'sitemap', 'entries'];
|
|
|
259
354
|
export function resolveSeoTabId({ tabId }, _optional = {}) {
|
|
260
355
|
return SEO_TAB_IDS.find(id => id === tabId) ?? 'defaults';
|
|
261
356
|
}
|
|
357
|
+
/** Colour band for the analysis score pill: the same success/warning/failure tones the issue pills
|
|
358
|
+
* use, so a 100 reads green and a failing page reads red at a glance.
|
|
359
|
+
* @example scoreTone({ score: 100 }) // 'success'
|
|
360
|
+
*/
|
|
361
|
+
export function scoreTone({ score }, _optional = {}) {
|
|
362
|
+
if (score >= 80)
|
|
363
|
+
return 'success';
|
|
364
|
+
return score >= 50 ? 'warning' : 'failure';
|
|
365
|
+
}
|
|
366
|
+
/**
|
|
367
|
+
* Picker rows for the Pages & posts tab. Pages lead because they are fewer and are the usual SEO
|
|
368
|
+
* targets (home, about, pricing); a published entry carries no pill, so only the exceptions
|
|
369
|
+
* (draft, scheduled, …) draw the eye. An untitled entry stays pickable by its id.
|
|
370
|
+
* @example entryPickerOptions({ posts, pages }, { t }) // [{ value: 'p1', label: 'About', group: 'Pages' }, …]
|
|
371
|
+
*/
|
|
372
|
+
export function entryPickerOptions({ posts, pages }, { t = key => key } = {}) {
|
|
373
|
+
// Two entries can share a title ("Landing Page" twice, owner 2026-10-15); both stay listed and
|
|
374
|
+
// each duplicate shows its slug as muted secondary text so the operator can tell them apart.
|
|
375
|
+
const rows = (entries, group) => {
|
|
376
|
+
const labelOf = (entry) => entry.title.trim() || entry.id;
|
|
377
|
+
const counts = new Map();
|
|
378
|
+
for (const entry of entries)
|
|
379
|
+
counts.set(labelOf(entry), (counts.get(labelOf(entry)) ?? 0) + 1);
|
|
380
|
+
return entries.map((entry) => ({
|
|
381
|
+
value: entry.id, label: labelOf(entry), group,
|
|
382
|
+
...(entry.status && entry.status !== 'published' ? { badge: t(entry.status) } : {}),
|
|
383
|
+
...((counts.get(labelOf(entry)) ?? 0) > 1 ? { detail: entry.slug || entry.id } : {}),
|
|
384
|
+
}));
|
|
385
|
+
};
|
|
386
|
+
return [...rows(pages, t('Pages')), ...rows(posts, t('Posts'))];
|
|
387
|
+
}
|
|
262
388
|
//# sourceMappingURL=rules.js.map
|
package/dist/seo/seo.module.d.ts
CHANGED
|
@@ -29,21 +29,24 @@ export declare const seoModule: {
|
|
|
29
29
|
readonly "Canonical URL": "Canonical URL";
|
|
30
30
|
readonly Noindex: "Noindex";
|
|
31
31
|
readonly Nofollow: "Nofollow";
|
|
32
|
-
readonly "
|
|
33
|
-
readonly "
|
|
34
|
-
readonly "
|
|
35
|
-
readonly "Twitter title": "Twitter title";
|
|
36
|
-
readonly "Twitter description": "Twitter description";
|
|
37
|
-
readonly "Twitter image": "Twitter image";
|
|
32
|
+
readonly "Preview title": "Preview title";
|
|
33
|
+
readonly "Preview description": "Preview description";
|
|
34
|
+
readonly "Preview image": "Preview image";
|
|
35
|
+
readonly "X/Twitter title": "X/Twitter title";
|
|
36
|
+
readonly "X/Twitter description": "X/Twitter description";
|
|
37
|
+
readonly "X/Twitter image": "X/Twitter image";
|
|
38
|
+
readonly "Customize for X/Twitter (optional)": "Customize for X/Twitter (optional)";
|
|
39
|
+
readonly "Same as the preview image": "Same as the preview image";
|
|
40
|
+
readonly "Used by Facebook, LinkedIn, Slack, Discord, iMessage, WhatsApp and X/Twitter": "Used by Facebook, LinkedIn, Slack, Discord, iMessage, WhatsApp and X/Twitter";
|
|
38
41
|
readonly "Saving\u2026": "Saving…";
|
|
39
42
|
readonly "Save overrides": "Save overrides";
|
|
40
43
|
readonly "Per-entry SEO": "Per-entry SEO";
|
|
41
44
|
readonly "Search appearance": "Search appearance";
|
|
42
45
|
readonly "Title template (must contain %s)": "Title template (must contain %s)";
|
|
43
46
|
readonly "Default meta description": "Default meta description";
|
|
44
|
-
readonly "Social
|
|
45
|
-
readonly "Default
|
|
46
|
-
readonly "Twitter @
|
|
47
|
+
readonly "Social preview": "Social preview";
|
|
48
|
+
readonly "Default preview image": "Default preview image";
|
|
49
|
+
readonly "X/Twitter @handle": "X/Twitter @handle";
|
|
47
50
|
readonly Crawling: "Crawling";
|
|
48
51
|
readonly "Default noindex": "Default noindex";
|
|
49
52
|
readonly "Default nofollow": "Default nofollow";
|
|
@@ -56,7 +59,7 @@ export declare const seoModule: {
|
|
|
56
59
|
readonly "View sitemap": "View sitemap";
|
|
57
60
|
readonly "Loading SEO settings\u2026": "Loading SEO settings…";
|
|
58
61
|
readonly Marketing: "Marketing";
|
|
59
|
-
readonly "Site-wide defaults for meta titles, descriptions,
|
|
62
|
+
readonly "Site-wide defaults for meta titles, descriptions, social previews, and robots directives.": "Site-wide defaults for meta titles, descriptions, social previews, and robots directives.";
|
|
60
63
|
readonly "Choose image": "Choose image";
|
|
61
64
|
readonly Remove: "Remove";
|
|
62
65
|
readonly "Sitemap \u00B7 {count} URLs": "Sitemap · {count} URLs";
|
|
@@ -84,6 +87,8 @@ export declare const seoModule: {
|
|
|
84
87
|
readonly "failed to load the sitemap": "failed to load the sitemap";
|
|
85
88
|
readonly "Could not load the sitemap (HTTP {status}).": "Could not load the sitemap (HTTP {status}).";
|
|
86
89
|
readonly "Selected image": "Selected image";
|
|
90
|
+
readonly "Using site default: {name}": "Using site default: {name}";
|
|
91
|
+
readonly "Using image from page content: {name}": "Using image from page content: {name}";
|
|
87
92
|
};
|
|
88
93
|
readonly pages: {
|
|
89
94
|
readonly settings: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jini-ai/admin",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.12",
|
|
4
4
|
"description": "Composable admin surface for Jini-hosted products: framework-free contracts, panel registry, and route model in `/core`; React shell and panels in `/react`. Import only the layers you need.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -79,6 +79,12 @@
|
|
|
79
79
|
"./consent/react": "browser"
|
|
80
80
|
},
|
|
81
81
|
"isolation": {
|
|
82
|
+
"shared": [
|
|
83
|
+
"src/react/components/DataTable.tsx",
|
|
84
|
+
"src/react/components/ConfirmDialog",
|
|
85
|
+
"src/react/components/RowMenu",
|
|
86
|
+
"src/react/types.ts"
|
|
87
|
+
],
|
|
82
88
|
"forbidden": {
|
|
83
89
|
"universal": [
|
|
84
90
|
"node:",
|
|
@@ -434,10 +440,10 @@
|
|
|
434
440
|
"react": "^18.3.0 || ^19.0.0",
|
|
435
441
|
"react-dom": "^18.3.0 || ^19.0.0",
|
|
436
442
|
"@jini-ai/agentic": "^0.4.4",
|
|
437
|
-
"@jini-ai/ui": "^0.5.
|
|
443
|
+
"@jini-ai/ui": "^0.5.5",
|
|
438
444
|
"@jini-ai/ui-kit": "^0.1.3",
|
|
439
445
|
"@jini-ai/agent-plugins": "^0.4.3",
|
|
440
|
-
"@jini-ai/cms": "^0.5.
|
|
446
|
+
"@jini-ai/cms": "^0.5.13",
|
|
441
447
|
"@jini-ai/core": "^0.4.3"
|
|
442
448
|
},
|
|
443
449
|
"peerDependenciesMeta": {
|
|
@@ -476,12 +482,12 @@
|
|
|
476
482
|
"react": "^19.2.0",
|
|
477
483
|
"react-dom": "^19.2.0",
|
|
478
484
|
"@testing-library/user-event": "^14.6.1",
|
|
479
|
-
"@jini-ai/cms": "^0.5.11",
|
|
480
|
-
"@jini-ai/ui-kit": "^0.1.4",
|
|
481
|
-
"@jini-ai/core": "^0.4.5",
|
|
482
|
-
"@jini-ai/ui": "^0.5.1",
|
|
483
485
|
"@jini-ai/agentic": "^0.4.4",
|
|
484
|
-
"@jini-ai/agent-plugins": "^0.4.
|
|
486
|
+
"@jini-ai/agent-plugins": "^0.4.7",
|
|
487
|
+
"@jini-ai/ui-kit": "^0.1.4",
|
|
488
|
+
"@jini-ai/cms": "^0.5.13",
|
|
489
|
+
"@jini-ai/core": "^0.4.7",
|
|
490
|
+
"@jini-ai/ui": "^0.5.5"
|
|
485
491
|
},
|
|
486
492
|
"typesVersions": {
|
|
487
493
|
"*": {
|