@jini-ai/admin 0.6.15-rc.2 → 0.6.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent-plugins/adapters/memory.js +9 -3
- package/dist/agent-plugins/conformance/agent-plugins-api.conformance.js +1 -1
- package/dist/agent-plugins/controllers/agent-plugins.controller.js +14 -5
- package/dist/agent-plugins/package-file-tree.js +4 -6
- package/dist/agent-plugins/react/components/AgentPluginInspector.js +5 -2
- package/dist/agent-plugins/react/hooks/AgentPluginInspector.hooks.js +10 -4
- package/dist/agent-plugins/react/hooks/AgentPluginPanel.hooks.js +8 -5
- package/dist/agent-plugins/rules.js +3 -1
- package/dist/comments/adapters/http.js +2 -1
- package/dist/comments/adapters/memory.js +3 -1
- package/dist/comments/conformance/comments-api.conformance.js +3 -2
- package/dist/comments/controllers/comment-queue.controller.d.ts +1 -1
- package/dist/comments/controllers/comment-queue.controller.js +4 -2
- package/dist/core/entities/rules.js +13 -20
- package/dist/core/module/create-admin.js +2 -1
- package/dist/core/omit-keys.d.ts +7 -0
- package/dist/core/omit-keys.js +12 -0
- package/dist/core/ports/forms.d.ts +5 -3
- package/dist/core/transport/query-suffix.d.ts +3 -0
- package/dist/core/transport/query-suffix.js +5 -0
- package/dist/forms/adapters/http.js +3 -2
- package/dist/forms/adapters/memory.js +3 -2
- package/dist/forms/controllers/field-attributes-dialog.controller.js +17 -3
- package/dist/forms/controllers/form-fields-editor.controller.js +1 -2
- package/dist/forms/controllers/form-submission-detail.controller.js +1 -2
- package/dist/forms/controllers/form-submissions.controller.js +1 -2
- package/dist/forms/controllers/forms-list.controller.js +1 -2
- package/dist/forms/field-settings.js +15 -4
- package/dist/forms/react/hooks/forms-view.hooks.d.ts +2 -2
- package/dist/forms/react/hooks/forms-view.hooks.js +9 -1
- package/dist/forms/react/hooks/use-field-attributes-dialog.hooks.js +1 -3
- package/dist/forms/react/hooks/use-form-editor.hooks.js +8 -9
- package/dist/forms/react/hooks/use-form-submission-detail.hooks.js +1 -4
- package/dist/forms/react/hooks/use-form-submissions.hooks.js +1 -5
- package/dist/forms/react/hooks/use-forms-list.hooks.js +1 -2
- package/dist/forms/rules.js +7 -2
- package/dist/media/adapters/memory.js +5 -3
- package/dist/media/controllers/library.controller.js +4 -2
- package/dist/media/controllers/providers.controller.js +2 -1
- package/dist/media/react/components/MediaCard.js +11 -1
- package/dist/media/react/hooks/EditMediaPanel.hooks.js +18 -7
- package/dist/media/react/hooks/LibraryTab.hooks.js +3 -2
- package/dist/media/react/hooks/ProvidersTab.hooks.js +19 -9
- package/dist/media/rules.js +6 -3
- package/dist/playground/adapters/memory.js +26 -7
- package/dist/playground/conformance/playground-api.conformance.js +6 -3
- package/dist/playground/react/hooks/PlaygroundPorts.hooks.js +6 -2
- package/dist/react/components/ConfirmButton/ConfirmButton.hooks.js +1 -1
- package/dist/react/components/ConfirmButton/ConfirmButton.js +2 -5
- package/dist/react/components/ConfirmDialog/confirm-dialog-attrs.js +2 -2
- package/dist/react/components/InteractiveHtmlEditor/InteractiveHtmlEditor.js +3 -1
- package/dist/react/components/RowMenu/RowMenu.hooks.js +4 -8
- package/dist/react/components/Sidebar.js +1 -1
- package/dist/react/entities/EntityField.js +1 -0
- package/dist/react/entities/contribution.js +2 -1
- package/dist/react/entities/types.js +1 -1
- package/dist/react/overlays.js +2 -1
- package/dist/react/shell/AdminShell.hooks.js +1 -1
- package/dist/react/shell/use-admin-shell-session.js +2 -1
- package/dist/react/shell/use-admin-theme.js +6 -6
- package/dist/redirects/adapters/memory.js +16 -13
- package/dist/security/adapters/http.js +10 -11
- package/dist/security/adapters/memory.js +50 -15
- package/dist/security/conformance/security-api.conformance.js +6 -3
- package/dist/security/controllers/access-tokens.controller.js +7 -3
- package/dist/security/controllers/other-credentials.controller.js +4 -2
- package/dist/security/controllers/root-key.controller.js +12 -5
- package/dist/security/react/hooks/AccessTokensTab.hooks.js +8 -3
- package/dist/security/react/hooks/SecurityPorts.hooks.js +6 -2
- package/dist/security/rules.js +19 -5
- package/dist/seo/adapters/memory.js +5 -2
- package/dist/seo/controllers/seo-entry.controller.js +2 -1
- package/dist/seo/controllers/sitemap.controller.js +2 -1
- package/dist/seo/react/hooks/MediaPickerBridge.hooks.js +1 -1
- package/dist/seo/react/hooks/use-seo.hooks.js +1 -1
- package/dist/seo/react/hooks/use-share-image-warning.hooks.js +1 -1
- package/dist/seo/react/hooks/use-sitemap-modal.hooks.js +9 -1
- package/dist/seo/react/pages/SeoPage.js +7 -1
- package/dist/source-control/adapters/memory.js +30 -14
- package/dist/source-control/controllers/source-control.controller.js +5 -2
- package/dist/source-control/react/components/SourceControlProviderRow.js +6 -1
- package/dist/source-control/react/hooks/SourceControlPorts.hooks.js +6 -2
- package/dist/source-control/rules.js +9 -3
- package/dist/widgets/adapters/http.js +2 -1
- package/dist/widgets/adapters/memory.js +4 -2
- package/dist/widgets/conformance/widgets-api.conformance.js +5 -2
- package/dist/widgets/controllers/instance-editor.controller.js +3 -2
- package/dist/widgets/controllers/library.controller.js +2 -1
- package/dist/widgets/controllers/region-editor.controller.js +2 -1
- package/dist/widgets/controllers/regions.controller.js +2 -1
- package/dist/widgets/react/components/WidgetInstanceNotices.d.ts +1 -1
- package/dist/widgets/react/components/WidgetInstanceNotices.js +2 -0
- package/dist/widgets/react/hooks/use-widget-regions.hooks.js +5 -2
- package/dist/widgets/react/hooks/use-widgets-library.hooks.js +5 -2
- package/dist/widgets/react/pages/WidgetInstanceEditor.js +1 -1
- package/dist/widgets/react/pages/WidgetRegionEditor.js +1 -1
- package/dist/widgets/react/pages/WidgetsLibrary.js +1 -1
- package/dist/widgets/rules.js +5 -1
- package/package.json +13 -13
|
@@ -3,28 +3,44 @@ export function createMemorySourceControlApi({ providers = [], credentials = []
|
|
|
3
3
|
const catalog = Object.freeze(providers.map(p => Object.freeze({ ...p, ...(p.credential ? { credential: Object.freeze({ ...p.credential, fields: Object.freeze(p.credential.fields.map(f => Object.freeze({ ...f }))) }) } : {}) })));
|
|
4
4
|
const rows = new Map(credentials.map(row => [row.id, sourceControlSummary({ row })]));
|
|
5
5
|
let sequence = 0;
|
|
6
|
+
const nextSequence = () => { sequence += 1; return sequence; };
|
|
6
7
|
function fail(code) { throw Object.assign(new Error(code), { code }); }
|
|
7
|
-
function checkLabel(providerId, label, id) {
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
function checkLabel(providerId, label, id) {
|
|
9
|
+
if (!label.trim())
|
|
10
|
+
fail('VALIDATION');
|
|
11
|
+
if ([...rows.values()].some(r => r.providerId === providerId && r.label === label.trim() && r.id !== id))
|
|
12
|
+
fail('DUPLICATE_LABEL');
|
|
13
|
+
}
|
|
10
14
|
function validate(input) { const info = sourceControlProviders({ providers: catalog, credentials: [] }).find(p => p.id === input.providerId); if (!info || !sourceControlReady({ info, draft: { ...blankSourceControlDraft({}), token: input.token, values: input } }))
|
|
11
15
|
fail('VALIDATION'); }
|
|
12
|
-
function setDefault(row) {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
+
function setDefault(row) {
|
|
17
|
+
if (row.isDefault)
|
|
18
|
+
for (const old of rows.values())
|
|
19
|
+
if (old.providerId === row.providerId && old.id !== row.id && old.isDefault)
|
|
20
|
+
rows.set(old.id, sourceControlSummary({ row: { ...old, isDefault: false } }));
|
|
21
|
+
rows.set(row.id, sourceControlSummary({ row }));
|
|
22
|
+
return rows.get(row.id);
|
|
23
|
+
}
|
|
16
24
|
return {
|
|
17
25
|
async providers(_r, o = {}) { o.signal?.throwIfAborted(); return catalog; },
|
|
18
26
|
async list(_r, o = {}) { o.signal?.throwIfAborted(); return Object.freeze([...rows.values()]); },
|
|
19
27
|
async create(input, o = {}) { o.signal?.throwIfAborted(); validate(input.connection); const providerId = input.connection.providerId, label = input.label.trim(); checkLabel(providerId, label); let id; do {
|
|
20
|
-
id = `memory-source-${
|
|
28
|
+
id = `memory-source-${nextSequence()}`;
|
|
21
29
|
} while (rows.has(id)); const timestamp = now(); return setDefault({ id, providerId, label, configured: true, isDefault: input.isDefault ?? ![...rows.values()].some(r => r.providerId === providerId), createdAt: timestamp, updatedAt: timestamp }); },
|
|
22
|
-
async update({ id, patch }, o = {}) {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
if (
|
|
26
|
-
fail('
|
|
27
|
-
|
|
30
|
+
async update({ id, patch }, o = {}) {
|
|
31
|
+
o.signal?.throwIfAborted();
|
|
32
|
+
const old = rows.get(id);
|
|
33
|
+
if (!old)
|
|
34
|
+
fail('NOT_FOUND');
|
|
35
|
+
if (patch.connection) {
|
|
36
|
+
validate(patch.connection);
|
|
37
|
+
if (patch.connection.providerId !== old.providerId)
|
|
38
|
+
fail('VALIDATION');
|
|
39
|
+
}
|
|
40
|
+
const label = (patch.label ?? old.label).trim();
|
|
41
|
+
checkLabel(old.providerId, label, id);
|
|
42
|
+
return setDefault({ ...old, label, isDefault: patch.isDefault ?? old.isDefault, updatedAt: now() });
|
|
43
|
+
},
|
|
28
44
|
async remove({ id }, o = {}) { o.signal?.throwIfAborted(); const old = rows.get(id); rows.delete(id); if (old?.isDefault) {
|
|
29
45
|
const next = [...rows.values()].find(r => r.providerId === old.providerId);
|
|
30
46
|
if (next)
|
|
@@ -4,7 +4,9 @@ import { blankSourceControlDraft, sourceControlProviders, defaultSourceControlCr
|
|
|
4
4
|
function readySaveTarget({ state, providerId }) {
|
|
5
5
|
const draft = state.drafts[providerId] ?? blankSourceControlDraft({});
|
|
6
6
|
const info = sourceControlProviders({ providers: state.providers, credentials: state.credentials }).find(p => p.id === providerId);
|
|
7
|
-
|
|
7
|
+
if (!state.loaded || draft.saving || !info)
|
|
8
|
+
return null;
|
|
9
|
+
return sourceControlReady({ info, draft }) ? { info, draft } : null;
|
|
8
10
|
}
|
|
9
11
|
/** Replaces the row with `summary`'s id in place, or appends `summary` when it is new. */
|
|
10
12
|
function spliceCredential({ credentials, summary }) {
|
|
@@ -20,7 +22,8 @@ export function createSourceControlController({ api, permissions = [] }, _option
|
|
|
20
22
|
async function load(_required, _optional = {}) {
|
|
21
23
|
if (!canRead())
|
|
22
24
|
return;
|
|
23
|
-
|
|
25
|
+
generation += 1;
|
|
26
|
+
const ticket = generation, revision = credentialRevision;
|
|
24
27
|
store.set({ patch: { loading: true } });
|
|
25
28
|
// Catalog failure must not hide saved connections; retain each successful store independently.
|
|
26
29
|
await Promise.all([
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { Notice } from '@jini-ai/ui-kit/react';
|
|
3
3
|
import { SourceControlCredentialFields } from './SourceControlCredentialFields.js';
|
|
4
|
+
function providerRowBody(row) {
|
|
5
|
+
if (row.unlisted)
|
|
6
|
+
return _jsx(Notice, { children: "Its plugin is off or missing. Manage this token on the Access tokens page." });
|
|
7
|
+
return row.canEdit ? _jsx(SourceControlCredentialFields, { row: row }) : _jsx(Notice, { children: "Read-only connection" });
|
|
8
|
+
}
|
|
4
9
|
export function SourceControlProviderRow({ row }, _optional = {}) {
|
|
5
|
-
return _jsxs("details", { name: "jini-source-control-provider", open: row.defaultOpen, "data-jini-part": "source-control.provider", children: [_jsxs("summary", { children: [_jsx("h2", { children: row.heading }), row.saved && _jsxs(_Fragment, { children: [_jsx("span", { children: "Token saved" }), _jsxs("span", { children: ["Saved ", row.saved.updatedAt] }), _jsx("span", { children: "Replace token \u25BE" })] }), !row.saved && _jsx("span", { children: "Expand \u25BE" })] }),
|
|
10
|
+
return _jsxs("details", { name: "jini-source-control-provider", open: row.defaultOpen, "data-jini-part": "source-control.provider", children: [_jsxs("summary", { children: [_jsx("h2", { children: row.heading }), row.saved && _jsxs(_Fragment, { children: [_jsx("span", { children: "Token saved" }), _jsxs("span", { children: ["Saved ", row.saved.updatedAt] }), _jsx("span", { children: "Replace token \u25BE" })] }), !row.saved && _jsx("span", { children: "Expand \u25BE" })] }), providerRowBody(row)] });
|
|
6
11
|
}
|
|
7
12
|
//# sourceMappingURL=SourceControlProviderRow.js.map
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { createContext, useContext } from 'react';
|
|
2
2
|
import { AdminConfigError } from '../../../core/module/index.js';
|
|
3
3
|
export const SourceControlPortsContext = createContext(null);
|
|
4
|
-
export function useSourceControlPorts(_required = {}, _optional = {}) {
|
|
5
|
-
|
|
4
|
+
export function useSourceControlPorts(_required = {}, _optional = {}) {
|
|
5
|
+
const ports = useContext(SourceControlPortsContext);
|
|
6
|
+
if (!ports)
|
|
7
|
+
throw new AdminConfigError({ issues: ['React scope unavailable: source-control'] });
|
|
8
|
+
return ports;
|
|
9
|
+
}
|
|
6
10
|
//# sourceMappingURL=SourceControlPorts.hooks.js.map
|
|
@@ -22,9 +22,15 @@ export function buildSourceControlConnection({ info, draft }, _optional = {}) {
|
|
|
22
22
|
}
|
|
23
23
|
/** Explicit projection prevents accidental token, ciphertext or username disclosure. */
|
|
24
24
|
export function sourceControlSummary({ row }, _optional = {}) { return Object.freeze({ id: row.id, providerId: row.providerId, label: row.label, configured: true, isDefault: row.isDefault, createdAt: row.createdAt, updatedAt: row.updatedAt }); }
|
|
25
|
-
export function sourceControlErrorMessage({ error }, _optional = {}) {
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
export function sourceControlErrorMessage({ error }, _optional = {}) {
|
|
26
|
+
const e = error;
|
|
27
|
+
const marker = e?.code ?? e?.message;
|
|
28
|
+
if (marker === 'DUPLICATE_LABEL')
|
|
29
|
+
return 'This connection was already saved — reload and try again.';
|
|
30
|
+
if (marker === 'VALIDATION')
|
|
31
|
+
return 'Check the required credential fields.';
|
|
32
|
+
return 'Unable to save connection';
|
|
33
|
+
}
|
|
28
34
|
export function safeSourceControlUrl({ url }, _optional = {}) { try {
|
|
29
35
|
const parsed = new URL(url);
|
|
30
36
|
return parsed.protocol === 'https:' || parsed.protocol === 'http:' ? url : undefined;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { querySuffix } from '../../core/transport/query-suffix.js';
|
|
1
2
|
/** basePath is the host workspace root; the adapter adds /widgets and the shared /trash/items. */
|
|
2
3
|
export function createHttpWidgetsApi({ transport, basePath }, _optional = {}) {
|
|
3
4
|
const root = basePath.replace(/\/$/, '');
|
|
@@ -14,7 +15,7 @@ export function createHttpWidgetsApi({ transport, basePath }, _optional = {}) {
|
|
|
14
15
|
if (includeInactive)
|
|
15
16
|
query.set('includeInactive', 'true');
|
|
16
17
|
const suffix = query.toString();
|
|
17
|
-
return request(`${base}${suffix
|
|
18
|
+
return request(`${base}${querySuffix(suffix)}`, 'GET', undefined, options);
|
|
18
19
|
},
|
|
19
20
|
getWidget({ id }, options = {}) { return request(`${base}/${encodeURIComponent(id)}`, 'GET', undefined, options); },
|
|
20
21
|
createWidget(input, options = {}) { return request(base, 'POST', input, options); },
|
|
@@ -32,7 +32,8 @@ export function createMemoryWidgetsApi({ widgets = [], regions = [], areas = {},
|
|
|
32
32
|
options.signal?.throwIfAborted();
|
|
33
33
|
let id;
|
|
34
34
|
do {
|
|
35
|
-
|
|
35
|
+
widgetSequence += 1;
|
|
36
|
+
id = `fake-${widgetSequence}`;
|
|
36
37
|
} while (rows.some(w => w.id === id || w.slug === `fake-slug-${widgetSequence}`));
|
|
37
38
|
const widget = { id, workspaceId, slug: `fake-slug-${widgetSequence}`, title: input.title, status: 'active', widgetType: input.widgetType, config: copy(input.config), updatedAt: now(), version: 1 };
|
|
38
39
|
rows.push(widget);
|
|
@@ -61,7 +62,8 @@ export function createMemoryWidgetsApi({ widgets = [], regions = [], areas = {},
|
|
|
61
62
|
fail('WIDGETS_REGION_BOUND', 'Region already bound');
|
|
62
63
|
let id;
|
|
63
64
|
do {
|
|
64
|
-
|
|
65
|
+
areaSequence += 1;
|
|
66
|
+
id = `fake-area-${areaSequence}`;
|
|
65
67
|
} while (bindings.some(b => b.areaEntryId === id) || [...regionRows.values()].some(r => r.area.id === id));
|
|
66
68
|
const area = { id, workspaceId, regionKey, updatedAt: now(), version: 1 };
|
|
67
69
|
bindings.push({ workspaceId, regionKey, areaEntryId: id, updatedAt: area.updatedAt, placementCount: 0 });
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
/** Mutates an isolated adapter. Returns named checks and throws at the first contract failure. */
|
|
2
2
|
export async function runWidgetsApiConformance({ api }, _optional = {}) {
|
|
3
3
|
const checks = [];
|
|
4
|
-
function assert(condition, name) {
|
|
5
|
-
|
|
4
|
+
function assert(condition, name) {
|
|
5
|
+
if (!condition)
|
|
6
|
+
throw new Error(`Widgets API conformance: ${name}`);
|
|
7
|
+
checks.push(name);
|
|
8
|
+
}
|
|
6
9
|
async function rejects(run, name) { let rejected = false; try {
|
|
7
10
|
await run();
|
|
8
11
|
}
|
|
@@ -17,7 +17,8 @@ export function createWidgetInstanceEditorController({ api, params: initialParam
|
|
|
17
17
|
const isLiveLoad = ({ ticket }) => ticket === generation && !store.signal.aborted;
|
|
18
18
|
const isLiveSave = ({ savingFor }) => currentKey() === savingFor && !store.signal.aborted;
|
|
19
19
|
async function load(_required = {}, _optional = {}) {
|
|
20
|
-
|
|
20
|
+
generation += 1;
|
|
21
|
+
const ticket = generation;
|
|
21
22
|
const isNew = params.widgetId === null;
|
|
22
23
|
store.set({ patch: { isNew, saving: false } });
|
|
23
24
|
if (isNew) {
|
|
@@ -58,7 +59,7 @@ export function createWidgetInstanceEditorController({ api, params: initialParam
|
|
|
58
59
|
try {
|
|
59
60
|
if (isNew)
|
|
60
61
|
await createAndOpen({ widgetType, title, config });
|
|
61
|
-
|
|
62
|
+
if (!isNew && widget)
|
|
62
63
|
await updateExisting({ widget, title, config, savingFor });
|
|
63
64
|
}
|
|
64
65
|
catch (error) {
|
|
@@ -8,7 +8,8 @@ export function createWidgetsLibraryController({ api }, { t = widgetsEnglish } =
|
|
|
8
8
|
let generation = 0;
|
|
9
9
|
const isLiveRead = ({ ticket }) => ticket === generation && !store.signal.aborted;
|
|
10
10
|
async function load(_required = {}, _optional = {}) {
|
|
11
|
-
|
|
11
|
+
generation += 1;
|
|
12
|
+
const ticket = generation;
|
|
12
13
|
try {
|
|
13
14
|
// No includeInactive: trashed widgets belong to the shared Trash screen.
|
|
14
15
|
const result = await api.listWidgets({}, { signal: store.signal });
|
|
@@ -8,7 +8,8 @@ export function createWidgetRegionEditorController({ api, regionKey: initialKey
|
|
|
8
8
|
let regionKey = initialKey, generation = 0;
|
|
9
9
|
const isLiveRead = ({ ticket }) => ticket === generation && !store.signal.aborted;
|
|
10
10
|
async function load(_required = {}, _optional = {}) {
|
|
11
|
-
|
|
11
|
+
generation += 1;
|
|
12
|
+
const ticket = generation;
|
|
12
13
|
store.set({ patch: { loading: true, error: null, saving: false } });
|
|
13
14
|
try {
|
|
14
15
|
const result = await api.getWidgetRegion({ regionKey }, { signal: store.signal });
|
|
@@ -7,7 +7,8 @@ export function createWidgetRegionsController({ api }, { t = widgetsEnglish, nav
|
|
|
7
7
|
const store = createControllerStore({ initial: { regions: null, error: null, newRegionKey: '', binding: false } });
|
|
8
8
|
let generation = 0;
|
|
9
9
|
async function load(_required = {}, _optional = {}) {
|
|
10
|
-
|
|
10
|
+
generation += 1;
|
|
11
|
+
const ticket = generation;
|
|
11
12
|
try {
|
|
12
13
|
const result = await api.listWidgetRegions({}, { signal: store.signal });
|
|
13
14
|
if (ticket === generation && !store.signal.aborted)
|
|
@@ -10,5 +10,5 @@ import type { WidgetInstanceGuard } from '../../rules.js';
|
|
|
10
10
|
export declare function WidgetInstanceGuardNotice({ guard, t }: {
|
|
11
11
|
guard: WidgetInstanceGuard;
|
|
12
12
|
t: (key: string) => string;
|
|
13
|
-
}): import("react").JSX.Element;
|
|
13
|
+
}): import("react").JSX.Element | null;
|
|
14
14
|
//# sourceMappingURL=WidgetInstanceNotices.d.ts.map
|
|
@@ -20,6 +20,8 @@ export function WidgetInstanceGuardNotice({ guard, t }) {
|
|
|
20
20
|
return _jsx("div", { className: "notice error", children: t("No widget type specified.") });
|
|
21
21
|
case "unknown-type":
|
|
22
22
|
return _jsxs("div", { className: "notice error", children: [t('Unknown widget type "'), guard.widgetType, "\"."] });
|
|
23
|
+
default:
|
|
24
|
+
return null;
|
|
23
25
|
}
|
|
24
26
|
}
|
|
25
27
|
//# sourceMappingURL=WidgetInstanceNotices.js.map
|
|
@@ -13,8 +13,11 @@ export function useWidgetRegions({ api }, { t = widgetsEnglish, events, navigate
|
|
|
13
13
|
const latest = useRef({ t, navigate });
|
|
14
14
|
latest.current = { t, navigate };
|
|
15
15
|
const { controller, snapshot } = useController({ create: () => createWidgetRegionsController({ api: cached.api }, { t: key => latest.current.t(key), navigate: path => latest.current.navigate?.(path) }), dependencies: [cached.api] }, { start: ({ controller }) => { void controller.load(); } });
|
|
16
|
-
useEffect(() => {
|
|
17
|
-
|
|
16
|
+
useEffect(() => {
|
|
17
|
+
if (!controller)
|
|
18
|
+
return undefined;
|
|
19
|
+
return events?.subscribe({ resource: WIDGETS_REGIONS_RESOURCE, onRefresh: () => invalidate({ key: WIDGET_QUERY_KEYS.regions }) });
|
|
20
|
+
}, [controller, events, invalidate]);
|
|
18
21
|
return { ...mergeRegionsState({ snapshot, data: cached.query.data, queryError: cached.query.error }), setNewRegionKey: value => controller?.setNewRegionKey({ value }), bind: async () => { await controller?.bind(); }, t };
|
|
19
22
|
}
|
|
20
23
|
const EMPTY_REGIONS_STATE = { regions: null, error: null, newRegionKey: '', binding: false };
|
|
@@ -12,8 +12,11 @@ export function useWidgetsLibrary({ api }, { t = widgetsEnglish, locale = 'en',
|
|
|
12
12
|
const latestT = useRef(t);
|
|
13
13
|
latestT.current = t;
|
|
14
14
|
const { controller, snapshot } = useController({ create: () => createWidgetsLibraryController({ api: cached.api }, { t: key => latestT.current(key) }), dependencies: [cached.api] }, { start: ({ controller }) => { void controller.load(); } });
|
|
15
|
-
useEffect(() => {
|
|
16
|
-
|
|
15
|
+
useEffect(() => {
|
|
16
|
+
if (!controller)
|
|
17
|
+
return undefined;
|
|
18
|
+
return events?.subscribe({ resource: WIDGETS_LIBRARY_RESOURCE, onRefresh: () => invalidate({ key: WIDGET_QUERY_KEYS.library }) });
|
|
19
|
+
}, [controller, events, invalidate]);
|
|
17
20
|
return { ...mergeLibraryState({ snapshot, data: cached.query.data, queryError: cached.query.error }),
|
|
18
21
|
setCreateType: type => controller?.setCreateType({ type }), requestTrash: widget => controller?.requestTrash({ widget }),
|
|
19
22
|
confirmTrash: async () => { await controller?.confirmTrash(); }, cancelTrash: () => controller?.cancelTrash(), t, locale };
|
|
@@ -18,7 +18,7 @@ import { WhereUsedBanner, WidgetInstanceGuardNotice } from '../components/Widget
|
|
|
18
18
|
export function WidgetInstanceEditor(props) {
|
|
19
19
|
const { slots: { ConfigFields: WidgetConfigFields }, widgetTypes } = useWidgetsOptions();
|
|
20
20
|
const { widgetId, widgetType: queryWidgetType, useWidgetInstanceEditorHook = useWiredWidgetInstanceEditor } = props;
|
|
21
|
-
const { isNew, widget, whereUsed, title, setTitle, config, setConfig, message, error, fieldErrors,
|
|
21
|
+
const { isNew, widget, whereUsed, title, setTitle, config, setConfig, message, error, fieldErrors, saving, save, t, gate, onBackClick, } = useWidgetInstanceEditorView({ widgetId, widgetType: queryWidgetType, useWidgetInstanceEditorHook, widgetTypes });
|
|
22
22
|
if (gate.guard !== null)
|
|
23
23
|
return _jsx(WidgetInstanceGuardNotice, { guard: gate.guard, t: t });
|
|
24
24
|
// `widgetInstanceGuard`'s "no-type" case already covers a null `widgetType` above, but TS can't
|
|
@@ -9,7 +9,7 @@ import { WidgetRegionEditorHeaderActions } from '../components/WidgetRegionEdito
|
|
|
9
9
|
export function WidgetRegionEditor(props) {
|
|
10
10
|
const { slots: { AddControl: WidgetAddControl }, widgetTypes } = useWidgetsOptions();
|
|
11
11
|
const { regionKey, useWidgetRegionEditorHook = useWiredWidgetRegionEditor } = props;
|
|
12
|
-
const { area, placements, message, error, loading, saving, removeAt, moveAt, toggleEnabled, addPlacement, save, t,
|
|
12
|
+
const { area, placements, message, error, loading, saving, removeAt, moveAt, toggleEnabled, addPlacement, save, t, placementHandles } = useWidgetRegionEditorView({ regionKey, useWidgetRegionEditorHook });
|
|
13
13
|
if (error && !area)
|
|
14
14
|
return _jsx("div", { className: "notice error", children: error });
|
|
15
15
|
if (loading)
|
|
@@ -11,7 +11,7 @@ export { WidgetsLibraryNotices } from '../components/WidgetsLibraryNotices.js';
|
|
|
11
11
|
import { WidgetsLibraryNotices } from '../components/WidgetsLibraryNotices.js';
|
|
12
12
|
export function WidgetsLibrary({ useWidgetsLibraryHook = useWiredWidgetsLibrary } = {}) {
|
|
13
13
|
const { widgetTypes, headerActions, statusLabel } = useWidgetsOptions();
|
|
14
|
-
const { widgets, error, skippedCount, skippedIds, createType, setCreateType, pendingTrash, trashing, requestTrash, confirmTrash, cancelTrash, t,
|
|
14
|
+
const { widgets, error, skippedCount, skippedIds, createType, setCreateType, pendingTrash, trashing, requestTrash, confirmTrash, cancelTrash, t, rowHandleById, } = useWidgetsLibraryView({ useWidgetsLibraryHook });
|
|
15
15
|
if (error && !widgets)
|
|
16
16
|
return _jsx("div", { className: "notice error", children: error });
|
|
17
17
|
if (!widgets)
|
package/dist/widgets/rules.js
CHANGED
|
@@ -43,8 +43,12 @@ export function movePlacement({ items, index, direction }, _optional = {}) {
|
|
|
43
43
|
}
|
|
44
44
|
// Distinguishes draft keys created in the same millisecond without randomUUID.
|
|
45
45
|
let draftPlacementSequence = 0;
|
|
46
|
+
function fallbackDraftPlacementId() {
|
|
47
|
+
draftPlacementSequence += 1;
|
|
48
|
+
return `p-${Date.now()}-${draftPlacementSequence}`;
|
|
49
|
+
}
|
|
46
50
|
/** Draft identity survives reorder/remove/toggle until the server accepts the ordered placements. */
|
|
47
|
-
export function buildDraftPlacement({ widgetInstanceId }, { id = () => globalThis.crypto?.randomUUID?.() ??
|
|
51
|
+
export function buildDraftPlacement({ widgetInstanceId }, { id = () => globalThis.crypto?.randomUUID?.() ?? fallbackDraftPlacementId() } = {}) {
|
|
48
52
|
return { placementId: id(), widgetEntryId: widgetInstanceId, enabled: true, widgetTitle: null, widgetType: null, broken: false };
|
|
49
53
|
}
|
|
50
54
|
/** Conflict copy is domain-specific; other failures use the existing admin error describer. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jini-ai/admin",
|
|
3
|
-
"version": "0.6.15
|
|
3
|
+
"version": "0.6.15",
|
|
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": {
|
|
@@ -448,12 +448,12 @@
|
|
|
448
448
|
"peerDependencies": {
|
|
449
449
|
"react": "^18.3.0 || ^19.0.0",
|
|
450
450
|
"react-dom": "^18.3.0 || ^19.0.0",
|
|
451
|
-
"@jini-ai/agentic": "^0.6.
|
|
452
|
-
"@jini-ai/ui": "^0.6.
|
|
453
|
-
"@jini-ai/ui-kit": "^0.6.
|
|
454
|
-
"@jini-ai/agent-plugins": "^0.6.
|
|
455
|
-
"@jini-ai/cms": "^0.6.
|
|
456
|
-
"@jini-ai/core": "^0.6.
|
|
451
|
+
"@jini-ai/agentic": "^0.6.10",
|
|
452
|
+
"@jini-ai/ui": "^0.6.10",
|
|
453
|
+
"@jini-ai/ui-kit": "^0.6.10",
|
|
454
|
+
"@jini-ai/agent-plugins": "^0.6.10",
|
|
455
|
+
"@jini-ai/cms": "^0.6.10",
|
|
456
|
+
"@jini-ai/core": "^0.6.10"
|
|
457
457
|
},
|
|
458
458
|
"peerDependenciesMeta": {
|
|
459
459
|
"react": {
|
|
@@ -491,12 +491,12 @@
|
|
|
491
491
|
"react": "^19.2.0",
|
|
492
492
|
"react-dom": "^19.2.0",
|
|
493
493
|
"@testing-library/user-event": "^14.6.1",
|
|
494
|
-
"@jini-ai/agentic": "^0.6.15
|
|
495
|
-
"@jini-ai/
|
|
496
|
-
"@jini-ai/
|
|
497
|
-
"@jini-ai/cms": "^0.6.15
|
|
498
|
-
"@jini-ai/
|
|
499
|
-
"@jini-ai/ui
|
|
494
|
+
"@jini-ai/agentic": "^0.6.15",
|
|
495
|
+
"@jini-ai/agent-plugins": "^0.6.15",
|
|
496
|
+
"@jini-ai/ui-kit": "^0.6.15",
|
|
497
|
+
"@jini-ai/cms": "^0.6.15",
|
|
498
|
+
"@jini-ai/core": "^0.6.15",
|
|
499
|
+
"@jini-ai/ui": "^0.6.15"
|
|
500
500
|
},
|
|
501
501
|
"typesVersions": {
|
|
502
502
|
"*": {
|