@object-ui/app-shell 6.0.4 → 6.2.0

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 (81) hide show
  1. package/CHANGELOG.md +128 -0
  2. package/README.md +10 -1
  3. package/dist/console/AppContent.js +24 -2
  4. package/dist/console/ai/AiChatPage.d.ts +8 -0
  5. package/dist/console/ai/AiChatPage.js +188 -0
  6. package/dist/console/ai/ConversationsSidebar.d.ts +7 -0
  7. package/dist/console/ai/ConversationsSidebar.js +111 -0
  8. package/dist/console/auth/LoginPage.js +19 -2
  9. package/dist/console/auth/RegisterPage.js +30 -1
  10. package/dist/console/marketplace/MarketplaceAccessDenied.js +3 -1
  11. package/dist/console/marketplace/MarketplaceInstalledPage.js +11 -3
  12. package/dist/console/marketplace/MarketplacePackagePage.js +83 -17
  13. package/dist/console/marketplace/MarketplacePage.js +55 -18
  14. package/dist/console/marketplace/marketplaceApi.d.ts +20 -0
  15. package/dist/console/marketplace/usePackageL10n.d.ts +38 -0
  16. package/dist/console/marketplace/usePackageL10n.js +110 -0
  17. package/dist/console/organizations/CreateWorkspaceDialog.js +29 -1
  18. package/dist/console/organizations/OrganizationsPage.js +24 -3
  19. package/dist/context/FavoritesProvider.d.ts +40 -2
  20. package/dist/context/FavoritesProvider.js +201 -20
  21. package/dist/hooks/index.d.ts +1 -0
  22. package/dist/hooks/index.js +1 -0
  23. package/dist/hooks/useChatConversation.d.ts +7 -0
  24. package/dist/hooks/useChatConversation.js +37 -5
  25. package/dist/hooks/useConversationList.d.ts +25 -0
  26. package/dist/hooks/useConversationList.js +131 -0
  27. package/dist/hooks/useNavPins.d.ts +11 -4
  28. package/dist/hooks/useNavPins.js +104 -53
  29. package/dist/index.d.ts +7 -0
  30. package/dist/index.js +14 -0
  31. package/dist/layout/AppHeader.js +2 -2
  32. package/dist/layout/AppSidebar.js +20 -1
  33. package/dist/layout/UnifiedSidebar.js +1 -1
  34. package/dist/providers/ExpressionProvider.d.ts +11 -1
  35. package/dist/providers/ExpressionProvider.js +11 -6
  36. package/dist/services/builtinComponents.d.ts +1 -0
  37. package/dist/services/builtinComponents.js +166 -0
  38. package/dist/services/componentRegistry.d.ts +63 -0
  39. package/dist/services/componentRegistry.js +36 -0
  40. package/dist/views/ComponentNavView.d.ts +6 -0
  41. package/dist/views/ComponentNavView.js +26 -0
  42. package/dist/views/RecordDetailView.js +72 -0
  43. package/dist/views/RecordFormPage.js +15 -3
  44. package/dist/views/SearchResultsPage.js +4 -0
  45. package/dist/views/metadata-admin/DesignerEditorWrapper.d.ts +58 -0
  46. package/dist/views/metadata-admin/DesignerEditorWrapper.js +140 -0
  47. package/dist/views/metadata-admin/DirectoryPage.d.ts +1 -0
  48. package/dist/views/metadata-admin/DirectoryPage.js +135 -0
  49. package/dist/views/metadata-admin/LayeredDiff.d.ts +6 -0
  50. package/dist/views/metadata-admin/LayeredDiff.js +26 -0
  51. package/dist/views/metadata-admin/PageShell.d.ts +34 -0
  52. package/dist/views/metadata-admin/PageShell.js +33 -0
  53. package/dist/views/metadata-admin/PermissionMatrixEditor.d.ts +5 -0
  54. package/dist/views/metadata-admin/PermissionMatrixEditor.js +288 -0
  55. package/dist/views/metadata-admin/QuickFind.d.ts +5 -0
  56. package/dist/views/metadata-admin/QuickFind.js +152 -0
  57. package/dist/views/metadata-admin/ResourceEditPage.d.ts +7 -0
  58. package/dist/views/metadata-admin/ResourceEditPage.js +256 -0
  59. package/dist/views/metadata-admin/ResourceHistoryPage.d.ts +5 -0
  60. package/dist/views/metadata-admin/ResourceHistoryPage.js +97 -0
  61. package/dist/views/metadata-admin/ResourceListPage.d.ts +4 -0
  62. package/dist/views/metadata-admin/ResourceListPage.js +144 -0
  63. package/dist/views/metadata-admin/ResourceRouter.d.ts +10 -0
  64. package/dist/views/metadata-admin/ResourceRouter.js +47 -0
  65. package/dist/views/metadata-admin/SchemaForm.d.ts +99 -0
  66. package/dist/views/metadata-admin/SchemaForm.js +556 -0
  67. package/dist/views/metadata-admin/default-schemas.d.ts +6 -0
  68. package/dist/views/metadata-admin/default-schemas.js +207 -0
  69. package/dist/views/metadata-admin/i18n.d.ts +33 -0
  70. package/dist/views/metadata-admin/i18n.js +303 -0
  71. package/dist/views/metadata-admin/index.d.ts +31 -0
  72. package/dist/views/metadata-admin/index.js +33 -0
  73. package/dist/views/metadata-admin/predicate.d.ts +31 -0
  74. package/dist/views/metadata-admin/predicate.js +150 -0
  75. package/dist/views/metadata-admin/registry.d.ts +125 -0
  76. package/dist/views/metadata-admin/registry.js +48 -0
  77. package/dist/views/metadata-admin/useMetadata.d.ts +37 -0
  78. package/dist/views/metadata-admin/useMetadata.js +96 -0
  79. package/dist/views/metadata-admin/widgets.d.ts +68 -0
  80. package/dist/views/metadata-admin/widgets.js +287 -0
  81. package/package.json +29 -28
@@ -0,0 +1,150 @@
1
+ // Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
2
+ /**
3
+ * Tiny predicate evaluator for FormView `visibleOn` expressions.
4
+ *
5
+ * This is an interim stand-in for the full CEL runtime
6
+ * (`@objectstack/formula`) — we keep it intentionally small so the
7
+ * Setup admin engine has zero new runtime dependencies. When CEL is
8
+ * unified across the platform (ROADMAP M9), swap `evaluatePredicate`
9
+ * for the real CEL evaluator without touching SchemaForm.tsx.
10
+ *
11
+ * Supported subset (covers everything used in spec `*.form.ts` today):
12
+ * - `path == 'literal'` string equality
13
+ * - `path != 'literal'` string inequality
14
+ * - `path == 123` number equality
15
+ * - `path == true|false` boolean equality
16
+ * - `path in ['a','b']` membership
17
+ * - `!path` negation (truthy)
18
+ * - `path` truthy check
19
+ * - `path && expr` conjunction
20
+ * - `path || expr` disjunction
21
+ *
22
+ * Paths support dot notation: `data.type`, `data.config.kind`.
23
+ *
24
+ * On any parse error → returns `true` (fail-open): better to show a
25
+ * field than to silently hide it.
26
+ */
27
+ export function evaluatePredicate(expr, ctx) {
28
+ if (expr == null)
29
+ return true;
30
+ const source = typeof expr === 'string' ? expr : expr.source;
31
+ if (!source)
32
+ return true;
33
+ try {
34
+ return evalExpr(source.trim(), ctx);
35
+ }
36
+ catch {
37
+ return true;
38
+ }
39
+ }
40
+ function evalExpr(expr, ctx) {
41
+ // Handle || (lowest precedence)
42
+ const orParts = splitTopLevel(expr, '||');
43
+ if (orParts.length > 1) {
44
+ return orParts.some((p) => evalExpr(p.trim(), ctx));
45
+ }
46
+ // Handle &&
47
+ const andParts = splitTopLevel(expr, '&&');
48
+ if (andParts.length > 1) {
49
+ return andParts.every((p) => evalExpr(p.trim(), ctx));
50
+ }
51
+ // Handle negation
52
+ if (expr.startsWith('!')) {
53
+ return !evalExpr(expr.slice(1).trim(), ctx);
54
+ }
55
+ // Handle 'in'
56
+ const inMatch = expr.match(/^(.+?)\s+in\s+(\[.*\])$/);
57
+ if (inMatch) {
58
+ const left = resolveValue(inMatch[1].trim(), ctx);
59
+ const right = parseLiteral(inMatch[2]);
60
+ return Array.isArray(right) && right.includes(left);
61
+ }
62
+ // Handle == / != (CEL-style loose equality: null == undefined)
63
+ const eqMatch = expr.match(/^(.+?)\s*(==|!=)\s*(.+)$/);
64
+ if (eqMatch) {
65
+ const left = resolveValue(eqMatch[1].trim(), ctx);
66
+ const right = parseLiteral(eqMatch[3].trim());
67
+ const nullish = (v) => v === null || v === undefined;
68
+ const equal = nullish(left) && nullish(right) ? true : left === right;
69
+ return eqMatch[2] === '==' ? equal : !equal;
70
+ }
71
+ // Bare truthy check
72
+ return Boolean(resolveValue(expr, ctx));
73
+ }
74
+ function splitTopLevel(expr, op) {
75
+ const out = [];
76
+ let depth = 0;
77
+ let inStr = null;
78
+ let buf = '';
79
+ for (let i = 0; i < expr.length; i++) {
80
+ const ch = expr[i];
81
+ if (inStr) {
82
+ buf += ch;
83
+ if (ch === inStr && expr[i - 1] !== '\\')
84
+ inStr = null;
85
+ continue;
86
+ }
87
+ if (ch === "'" || ch === '"') {
88
+ inStr = ch;
89
+ buf += ch;
90
+ continue;
91
+ }
92
+ if (ch === '(' || ch === '[')
93
+ depth++;
94
+ else if (ch === ')' || ch === ']')
95
+ depth--;
96
+ if (depth === 0 &&
97
+ expr.slice(i, i + op.length) === op &&
98
+ // Not a sub-operator (e.g. == when looking for =)
99
+ expr[i + op.length] !== '=') {
100
+ out.push(buf);
101
+ buf = '';
102
+ i += op.length - 1;
103
+ continue;
104
+ }
105
+ buf += ch;
106
+ }
107
+ if (buf)
108
+ out.push(buf);
109
+ return out;
110
+ }
111
+ function resolveValue(path, ctx) {
112
+ // Allow literals on the left side too.
113
+ if (/^['"]/.test(path) || /^-?\d/.test(path) || path === 'true' || path === 'false' || path === 'null') {
114
+ return parseLiteral(path);
115
+ }
116
+ const segs = path.split('.');
117
+ let cur = ctx;
118
+ for (const seg of segs) {
119
+ if (cur == null)
120
+ return undefined;
121
+ cur = cur[seg];
122
+ }
123
+ return cur;
124
+ }
125
+ function parseLiteral(raw) {
126
+ const s = raw.trim();
127
+ if (s === 'true')
128
+ return true;
129
+ if (s === 'false')
130
+ return false;
131
+ if (s === 'null')
132
+ return null;
133
+ if (/^-?\d+(\.\d+)?$/.test(s))
134
+ return Number(s);
135
+ if ((s.startsWith("'") && s.endsWith("'")) ||
136
+ (s.startsWith('"') && s.endsWith('"'))) {
137
+ return s.slice(1, -1);
138
+ }
139
+ if (s.startsWith('[') && s.endsWith(']')) {
140
+ try {
141
+ // JSON-parse after normalising single-quoted strings to double.
142
+ const json = s.replace(/'([^']*)'/g, (_, inner) => JSON.stringify(inner));
143
+ return JSON.parse(json);
144
+ }
145
+ catch {
146
+ return [];
147
+ }
148
+ }
149
+ return s;
150
+ }
@@ -0,0 +1,125 @@
1
+ /**
2
+ * MetadataResourceRegistry — per-type overrides for the generic metadata
3
+ * admin engine (Phase 3c).
4
+ *
5
+ * The engine drives all 27 metadata types from a single ListPage /
6
+ * EditPage / HistoryPage shell. By default everything is rendered via
7
+ * a JSONSchema-driven AutoForm (using the rich `/meta/types` registry
8
+ * row's `schema` field). Specialised editors (ObjectManager,
9
+ * FieldDesigner, ObjectViewConfigurator, PermissionMatrix, …) opt in
10
+ * by calling `registerMetadataResource()` to override the default
11
+ * components for their type.
12
+ *
13
+ * This keeps the contract for "add a new metadata type" trivial:
14
+ * 1. Define its Zod schema in `packages/spec/src/<domain>/`.
15
+ * 2. Add it to `DEFAULT_METADATA_TYPE_REGISTRY` (framework).
16
+ * 3. Done. It shows up in the Setup app's Metadata Directory with
17
+ * a working list / create / edit / history out of the box.
18
+ *
19
+ * Conventions:
20
+ * • `primaryKey` defaults to `'name'` (the universal metadata
21
+ * short-name; ADR-0006).
22
+ * • `searchableFields` defaults to `['name','label','description']`.
23
+ * • `listColumns` defaults to inferring from primary + label.
24
+ * • `supportsHistory` defaults to true (every overlay goes through
25
+ * `sys_metadata_history`).
26
+ */
27
+ import type { ComponentType, ReactNode } from 'react';
28
+ export type MetadataDomain = 'data' | 'ui' | 'automation' | 'ai' | 'system' | 'platform' | 'identity' | 'security' | 'other';
29
+ /**
30
+ * One row in the registry — describes how the generic engine should
31
+ * render and edit a metadata type. All fields are optional; sensible
32
+ * defaults apply if a type isn't registered at all.
33
+ */
34
+ export interface MetadataResourceConfig {
35
+ /** Metadata type, e.g. 'view', 'flow', 'agent'. */
36
+ type: string;
37
+ /** Display label. Falls back to server-side label or the type id. */
38
+ label?: string;
39
+ /** Long-form description shown in the page hero. */
40
+ description?: string;
41
+ /** Lucide icon name (e.g. 'database', 'workflow'). */
42
+ iconName?: string;
43
+ /** Coarse grouping for the directory page. */
44
+ domain?: MetadataDomain;
45
+ /** Primary key field, default `'name'`. */
46
+ primaryKey?: string;
47
+ /** Fields searched by the free-text filter. Default `['name','label','description']`. */
48
+ searchableFields?: string[];
49
+ /** Columns rendered in the list page. */
50
+ listColumns?: Array<{
51
+ key: string;
52
+ label: string;
53
+ /** Optional cell renderer. `value` is `item[key]`. */
54
+ render?: (value: unknown, item: Record<string, unknown>) => ReactNode;
55
+ /** Column width hint in CSS (e.g. `'180px'`, `'30%'`). */
56
+ width?: string;
57
+ }>;
58
+ /**
59
+ * Fully custom list page. When provided, the generic shell is bypassed.
60
+ * Receives `{ type }`. Use this for ObjectManager, etc.
61
+ */
62
+ ListPage?: ComponentType<{
63
+ type: string;
64
+ }>;
65
+ /**
66
+ * Fully custom edit page. Receives `{ type, name }`.
67
+ */
68
+ EditPage?: ComponentType<{
69
+ type: string;
70
+ name: string;
71
+ }>;
72
+ /**
73
+ * Fully custom create page. Receives `{ type }`.
74
+ */
75
+ CreatePage?: ComponentType<{
76
+ type: string;
77
+ }>;
78
+ /** Fields hidden from the AutoForm (still serialised on save). */
79
+ hiddenFields?: string[];
80
+ /** Suggested form field order (top to bottom). */
81
+ fieldOrder?: string[];
82
+ /** Whether the type opts in to the history tab. Default true. */
83
+ supportsHistory?: boolean;
84
+ /**
85
+ * Override the empty-state copy on the list page (e.g. "No views yet —
86
+ * Studio's Page Designer creates these for you").
87
+ */
88
+ emptyStateHint?: string;
89
+ /**
90
+ * Fallback JSONSchema used by the generic SchemaForm when the
91
+ * framework's `/meta/types` endpoint doesn't expose a `schema` field
92
+ * for this type (most types today). This lets us deliver a usable
93
+ * form-driven create/edit experience without first wiring full
94
+ * Zod→JSONSchema generation in the framework registry.
95
+ *
96
+ * If the server registry DOES return a `schema`, this is ignored.
97
+ */
98
+ defaultSchema?: Record<string, unknown>;
99
+ }
100
+ /**
101
+ * Register (or merge) an entry. Idempotent — re-registering with the
102
+ * same `type` merges the new fields with any existing entry so that
103
+ * bespoke editors (e.g. PermissionMatrixEditor) and generic-engine
104
+ * defaults (e.g. `defaultSchema`) can be registered independently and
105
+ * coexist. Explicit `undefined` values do not overwrite.
106
+ */
107
+ export declare function registerMetadataResource(config: MetadataResourceConfig): void;
108
+ /** Look up an entry. Returns `undefined` when the type isn't registered. */
109
+ export declare function getMetadataResource(type: string): MetadataResourceConfig | undefined;
110
+ /** Snapshot of all registered entries (diagnostics; directory page). */
111
+ export declare function listMetadataResources(): MetadataResourceConfig[];
112
+ /**
113
+ * Merge a registered config (if any) with server-side defaults from
114
+ * `/meta/types`. Server fields win for label/description/domain
115
+ * (the spec is source of truth); user-registered fields win for
116
+ * everything else (UI behaviour).
117
+ */
118
+ export declare function resolveResourceConfig(type: string, serverEntry?: {
119
+ label?: string;
120
+ description?: string;
121
+ domain?: string;
122
+ allowOrgOverride?: boolean;
123
+ }): MetadataResourceConfig & {
124
+ allowOrgOverride?: boolean;
125
+ };
@@ -0,0 +1,48 @@
1
+ // Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
2
+ const REGISTRY = new Map();
3
+ /**
4
+ * Register (or merge) an entry. Idempotent — re-registering with the
5
+ * same `type` merges the new fields with any existing entry so that
6
+ * bespoke editors (e.g. PermissionMatrixEditor) and generic-engine
7
+ * defaults (e.g. `defaultSchema`) can be registered independently and
8
+ * coexist. Explicit `undefined` values do not overwrite.
9
+ */
10
+ export function registerMetadataResource(config) {
11
+ const prev = REGISTRY.get(config.type);
12
+ if (!prev) {
13
+ REGISTRY.set(config.type, config);
14
+ return;
15
+ }
16
+ const merged = { ...prev };
17
+ for (const [k, v] of Object.entries(config)) {
18
+ if (v !== undefined)
19
+ merged[k] = v;
20
+ }
21
+ REGISTRY.set(config.type, merged);
22
+ }
23
+ /** Look up an entry. Returns `undefined` when the type isn't registered. */
24
+ export function getMetadataResource(type) {
25
+ return REGISTRY.get(type);
26
+ }
27
+ /** Snapshot of all registered entries (diagnostics; directory page). */
28
+ export function listMetadataResources() {
29
+ return Array.from(REGISTRY.values());
30
+ }
31
+ /**
32
+ * Merge a registered config (if any) with server-side defaults from
33
+ * `/meta/types`. Server fields win for label/description/domain
34
+ * (the spec is source of truth); user-registered fields win for
35
+ * everything else (UI behaviour).
36
+ */
37
+ export function resolveResourceConfig(type, serverEntry) {
38
+ const registered = REGISTRY.get(type) ?? { type };
39
+ return {
40
+ ...registered,
41
+ type,
42
+ label: registered.label ?? serverEntry?.label ?? type,
43
+ description: registered.description ?? serverEntry?.description,
44
+ domain: (registered.domain ?? serverEntry?.domain ?? 'other'),
45
+ allowOrgOverride: serverEntry?.allowOrgOverride,
46
+ defaultSchema: registered.defaultSchema,
47
+ };
48
+ }
@@ -0,0 +1,37 @@
1
+ import { MetadataClient } from '@object-ui/data-objectstack';
2
+ export interface RichMetadataTypeEntry {
3
+ type: string;
4
+ label?: string;
5
+ description?: string;
6
+ domain?: string;
7
+ allowOrgOverride?: boolean;
8
+ /** 'registry' = ADR opt-in; 'env' = unlocked via OBJECTSTACK_METADATA_WRITABLE. */
9
+ overrideSource?: 'registry' | 'env';
10
+ supportsOverlay?: boolean;
11
+ loadOrder?: number;
12
+ /** JSONSchema for the type's item shape (Phase 3a addition). */
13
+ schema?: Record<string, unknown>;
14
+ /** Canonical FormView layout for the type's editor (Phase 3c+). */
15
+ form?: Record<string, unknown>;
16
+ /** UI hints (icon, color, etc.) the framework may include. */
17
+ ui?: Record<string, unknown>;
18
+ }
19
+ /** Use a single MetadataClient for the whole admin engine. */
20
+ export declare function useMetadataClient(environmentId?: string): MetadataClient;
21
+ /**
22
+ * Fetch and cache the rich `/meta/types` registry response. Most pages
23
+ * only need it once per session, so we memoise per (client) instance.
24
+ */
25
+ export declare function useMetadataTypes(client: MetadataClient): {
26
+ loading: boolean;
27
+ error: string | null;
28
+ entries: RichMetadataTypeEntry[];
29
+ };
30
+ /**
31
+ * Build a stable lookup map by type id from the entries array. Most
32
+ * pages need O(1) access to "what's the label / writable status for
33
+ * type X?".
34
+ */
35
+ export declare function useTypesIndex(entries: RichMetadataTypeEntry[]): Record<string, RichMetadataTypeEntry>;
36
+ /** Free-text filter helper used by list pages + QuickFind. */
37
+ export declare function matchesQuery(item: Record<string, unknown>, query: string, fields?: string[]): boolean;
@@ -0,0 +1,96 @@
1
+ // Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
2
+ /**
3
+ * Shared hooks + types for the metadata-admin engine (Phase 3c).
4
+ *
5
+ * Centralises the MetadataClient creation and the rich `/meta/types`
6
+ * row shape so every page reads from the same source of truth.
7
+ *
8
+ * Why a tiny barrel?
9
+ * • DirectoryPage, ListPage, EditPage, HistoryPage and QuickFind
10
+ * all need (client, typesRegistry). Letting each page re-fetch
11
+ * would be wasteful and produce flickering badges.
12
+ * • The hook caches the client per `baseUrl + environmentId` pair
13
+ * so `client.withEnvironment(...)` swaps without remounting
14
+ * consumers.
15
+ */
16
+ import { useEffect, useMemo, useState } from 'react';
17
+ import { MetadataClient } from '@object-ui/data-objectstack';
18
+ /** Use a single MetadataClient for the whole admin engine. */
19
+ export function useMetadataClient(environmentId) {
20
+ return useMemo(() => {
21
+ const c = new MetadataClient({ baseUrl: '' });
22
+ return environmentId ? c.withEnvironment(environmentId) : c;
23
+ }, [environmentId]);
24
+ }
25
+ /**
26
+ * Fetch and cache the rich `/meta/types` registry response. Most pages
27
+ * only need it once per session, so we memoise per (client) instance.
28
+ */
29
+ export function useMetadataTypes(client) {
30
+ const [loading, setLoading] = useState(true);
31
+ const [error, setError] = useState(null);
32
+ const [entries, setEntries] = useState([]);
33
+ useEffect(() => {
34
+ let cancelled = false;
35
+ setLoading(true);
36
+ setError(null);
37
+ (async () => {
38
+ try {
39
+ const result = await client.listTypes();
40
+ let list;
41
+ if (Array.isArray(result)) {
42
+ list = result.map((t) => typeof t === 'string' ? { type: t } : t);
43
+ }
44
+ else {
45
+ const rich = result?.entries;
46
+ if (Array.isArray(rich) && rich.length > 0) {
47
+ list = rich;
48
+ }
49
+ else {
50
+ const names = result?.types ?? [];
51
+ list = names.map((t) => ({ type: t }));
52
+ }
53
+ }
54
+ if (!cancelled) {
55
+ setEntries(list);
56
+ setLoading(false);
57
+ }
58
+ }
59
+ catch (err) {
60
+ if (!cancelled) {
61
+ setError(err?.message ?? String(err));
62
+ setLoading(false);
63
+ }
64
+ }
65
+ })();
66
+ return () => {
67
+ cancelled = true;
68
+ };
69
+ }, [client]);
70
+ return { loading, error, entries };
71
+ }
72
+ /**
73
+ * Build a stable lookup map by type id from the entries array. Most
74
+ * pages need O(1) access to "what's the label / writable status for
75
+ * type X?".
76
+ */
77
+ export function useTypesIndex(entries) {
78
+ return useMemo(() => {
79
+ const idx = {};
80
+ for (const e of entries)
81
+ idx[e.type] = e;
82
+ return idx;
83
+ }, [entries]);
84
+ }
85
+ /** Free-text filter helper used by list pages + QuickFind. */
86
+ export function matchesQuery(item, query, fields = ['name', 'label', 'description']) {
87
+ if (!query)
88
+ return true;
89
+ const q = query.toLowerCase();
90
+ for (const f of fields) {
91
+ const v = item[f];
92
+ if (typeof v === 'string' && v.toLowerCase().includes(q))
93
+ return true;
94
+ }
95
+ return false;
96
+ }
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Built-in widget renderers for SchemaForm.
3
+ *
4
+ * These cover the common widget hints declared in spec `*.form.ts`
5
+ * files (e.g. `widget: 'ref:object'`, `widget: 'master-detail'`).
6
+ *
7
+ * Each widget receives `WidgetProps`:
8
+ * - schema — the JSONSchema fragment for THIS field (so widgets
9
+ * for nested arrays can read items.properties)
10
+ * - value — the current value
11
+ * - onChange — write-back callback
12
+ * - readOnly — disable
13
+ * - context — out-of-band data (object list, ObjectQL fields, …)
14
+ *
15
+ * To register a new widget, add an entry to `WIDGETS` below. To wire
16
+ * extra context (e.g. a fields list), extend `WidgetContext` in
17
+ * SchemaForm.tsx and prefetch it in ResourceEditPage.
18
+ */
19
+ import * as React from 'react';
20
+ export interface WidgetContext {
21
+ /** Names of all object metadata records (for `ref:object`). */
22
+ objectNames?: string[];
23
+ /** Loading flag for the object list. */
24
+ objectsLoading?: boolean;
25
+ }
26
+ export interface WidgetProps {
27
+ id?: string;
28
+ schema: Record<string, any>;
29
+ value: unknown;
30
+ onChange: (v: unknown) => void;
31
+ readOnly?: boolean;
32
+ context?: WidgetContext;
33
+ /** Optional FormFieldSpec with type/options/reference/constraints */
34
+ fieldSpec?: {
35
+ field: string;
36
+ type?: string;
37
+ options?: Array<{
38
+ label: string;
39
+ value: string;
40
+ color?: string;
41
+ }>;
42
+ reference?: string;
43
+ maxLength?: number;
44
+ minLength?: number;
45
+ min?: number;
46
+ max?: number;
47
+ multiple?: boolean;
48
+ dependsOn?: string | string[];
49
+ /** Sub-fields for `composite` / `repeater` types */
50
+ fields?: Array<any>;
51
+ /** Code editor language (for type=code) */
52
+ language?: string;
53
+ /** Form-level helpers passed through from FormField */
54
+ label?: string;
55
+ placeholder?: string;
56
+ helpText?: string;
57
+ widget?: string;
58
+ colSpan?: number;
59
+ immutable?: boolean;
60
+ readonly?: boolean;
61
+ required?: boolean;
62
+ };
63
+ /** All form data (for reading dependency values) */
64
+ formData?: Record<string, unknown>;
65
+ }
66
+ export type WidgetRenderer = (props: WidgetProps) => React.ReactElement;
67
+ export declare const WIDGETS: Record<string, WidgetRenderer>;
68
+ export declare function CodeWidget({ schema, value, onChange, readOnly, fieldSpec, }: WidgetProps): import("react/jsx-runtime").JSX.Element;