@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.
- package/CHANGELOG.md +128 -0
- package/README.md +10 -1
- package/dist/console/AppContent.js +24 -2
- package/dist/console/ai/AiChatPage.d.ts +8 -0
- package/dist/console/ai/AiChatPage.js +188 -0
- package/dist/console/ai/ConversationsSidebar.d.ts +7 -0
- package/dist/console/ai/ConversationsSidebar.js +111 -0
- package/dist/console/auth/LoginPage.js +19 -2
- package/dist/console/auth/RegisterPage.js +30 -1
- package/dist/console/marketplace/MarketplaceAccessDenied.js +3 -1
- package/dist/console/marketplace/MarketplaceInstalledPage.js +11 -3
- package/dist/console/marketplace/MarketplacePackagePage.js +83 -17
- package/dist/console/marketplace/MarketplacePage.js +55 -18
- package/dist/console/marketplace/marketplaceApi.d.ts +20 -0
- package/dist/console/marketplace/usePackageL10n.d.ts +38 -0
- package/dist/console/marketplace/usePackageL10n.js +110 -0
- package/dist/console/organizations/CreateWorkspaceDialog.js +29 -1
- package/dist/console/organizations/OrganizationsPage.js +24 -3
- package/dist/context/FavoritesProvider.d.ts +40 -2
- package/dist/context/FavoritesProvider.js +201 -20
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/index.js +1 -0
- package/dist/hooks/useChatConversation.d.ts +7 -0
- package/dist/hooks/useChatConversation.js +37 -5
- package/dist/hooks/useConversationList.d.ts +25 -0
- package/dist/hooks/useConversationList.js +131 -0
- package/dist/hooks/useNavPins.d.ts +11 -4
- package/dist/hooks/useNavPins.js +104 -53
- package/dist/index.d.ts +7 -0
- package/dist/index.js +14 -0
- package/dist/layout/AppHeader.js +2 -2
- package/dist/layout/AppSidebar.js +20 -1
- package/dist/layout/UnifiedSidebar.js +1 -1
- package/dist/providers/ExpressionProvider.d.ts +11 -1
- package/dist/providers/ExpressionProvider.js +11 -6
- package/dist/services/builtinComponents.d.ts +1 -0
- package/dist/services/builtinComponents.js +166 -0
- package/dist/services/componentRegistry.d.ts +63 -0
- package/dist/services/componentRegistry.js +36 -0
- package/dist/views/ComponentNavView.d.ts +6 -0
- package/dist/views/ComponentNavView.js +26 -0
- package/dist/views/RecordDetailView.js +72 -0
- package/dist/views/RecordFormPage.js +15 -3
- package/dist/views/SearchResultsPage.js +4 -0
- package/dist/views/metadata-admin/DesignerEditorWrapper.d.ts +58 -0
- package/dist/views/metadata-admin/DesignerEditorWrapper.js +140 -0
- package/dist/views/metadata-admin/DirectoryPage.d.ts +1 -0
- package/dist/views/metadata-admin/DirectoryPage.js +135 -0
- package/dist/views/metadata-admin/LayeredDiff.d.ts +6 -0
- package/dist/views/metadata-admin/LayeredDiff.js +26 -0
- package/dist/views/metadata-admin/PageShell.d.ts +34 -0
- package/dist/views/metadata-admin/PageShell.js +33 -0
- package/dist/views/metadata-admin/PermissionMatrixEditor.d.ts +5 -0
- package/dist/views/metadata-admin/PermissionMatrixEditor.js +288 -0
- package/dist/views/metadata-admin/QuickFind.d.ts +5 -0
- package/dist/views/metadata-admin/QuickFind.js +152 -0
- package/dist/views/metadata-admin/ResourceEditPage.d.ts +7 -0
- package/dist/views/metadata-admin/ResourceEditPage.js +256 -0
- package/dist/views/metadata-admin/ResourceHistoryPage.d.ts +5 -0
- package/dist/views/metadata-admin/ResourceHistoryPage.js +97 -0
- package/dist/views/metadata-admin/ResourceListPage.d.ts +4 -0
- package/dist/views/metadata-admin/ResourceListPage.js +144 -0
- package/dist/views/metadata-admin/ResourceRouter.d.ts +10 -0
- package/dist/views/metadata-admin/ResourceRouter.js +47 -0
- package/dist/views/metadata-admin/SchemaForm.d.ts +99 -0
- package/dist/views/metadata-admin/SchemaForm.js +556 -0
- package/dist/views/metadata-admin/default-schemas.d.ts +6 -0
- package/dist/views/metadata-admin/default-schemas.js +207 -0
- package/dist/views/metadata-admin/i18n.d.ts +33 -0
- package/dist/views/metadata-admin/i18n.js +303 -0
- package/dist/views/metadata-admin/index.d.ts +31 -0
- package/dist/views/metadata-admin/index.js +33 -0
- package/dist/views/metadata-admin/predicate.d.ts +31 -0
- package/dist/views/metadata-admin/predicate.js +150 -0
- package/dist/views/metadata-admin/registry.d.ts +125 -0
- package/dist/views/metadata-admin/registry.js +48 -0
- package/dist/views/metadata-admin/useMetadata.d.ts +37 -0
- package/dist/views/metadata-admin/useMetadata.js +96 -0
- package/dist/views/metadata-admin/widgets.d.ts +68 -0
- package/dist/views/metadata-admin/widgets.js +287 -0
- package/package.json +29 -28
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
|
|
3
|
+
/**
|
|
4
|
+
* Built-in widget renderers for SchemaForm.
|
|
5
|
+
*
|
|
6
|
+
* These cover the common widget hints declared in spec `*.form.ts`
|
|
7
|
+
* files (e.g. `widget: 'ref:object'`, `widget: 'master-detail'`).
|
|
8
|
+
*
|
|
9
|
+
* Each widget receives `WidgetProps`:
|
|
10
|
+
* - schema — the JSONSchema fragment for THIS field (so widgets
|
|
11
|
+
* for nested arrays can read items.properties)
|
|
12
|
+
* - value — the current value
|
|
13
|
+
* - onChange — write-back callback
|
|
14
|
+
* - readOnly — disable
|
|
15
|
+
* - context — out-of-band data (object list, ObjectQL fields, …)
|
|
16
|
+
*
|
|
17
|
+
* To register a new widget, add an entry to `WIDGETS` below. To wire
|
|
18
|
+
* extra context (e.g. a fields list), extend `WidgetContext` in
|
|
19
|
+
* SchemaForm.tsx and prefetch it in ResourceEditPage.
|
|
20
|
+
*/
|
|
21
|
+
import * as React from 'react';
|
|
22
|
+
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Input, Button, } from '@object-ui/components';
|
|
23
|
+
import { Plus, Trash2 } from 'lucide-react';
|
|
24
|
+
/* -------------------------------------------------------------------------- */
|
|
25
|
+
/* ref:object — pick an object by name */
|
|
26
|
+
/* -------------------------------------------------------------------------- */
|
|
27
|
+
function RefObjectWidget({ id, value, onChange, readOnly, context, }) {
|
|
28
|
+
const names = context?.objectNames ?? [];
|
|
29
|
+
const v = value == null ? '' : String(value);
|
|
30
|
+
if (context?.objectsLoading) {
|
|
31
|
+
return (_jsx(Input, { id: id, value: v, disabled: true, placeholder: "Loading objects\u2026" }));
|
|
32
|
+
}
|
|
33
|
+
// If list is empty (e.g. no objects defined yet), fall back to a
|
|
34
|
+
// freeform text input so the user can still type a value.
|
|
35
|
+
if (names.length === 0) {
|
|
36
|
+
return (_jsx(Input, { id: id, value: v, disabled: readOnly, onChange: (e) => onChange(e.target.value || undefined), placeholder: "object_name (no objects detected)" }));
|
|
37
|
+
}
|
|
38
|
+
return (_jsxs(Select, { value: v, onValueChange: (next) => onChange(next || undefined), disabled: readOnly, children: [_jsx(SelectTrigger, { id: id, children: _jsx(SelectValue, { placeholder: "Select object\u2026" }) }), _jsx(SelectContent, { children: names.map((n) => (_jsx(SelectItem, { value: n, children: n }, n))) })] }));
|
|
39
|
+
}
|
|
40
|
+
/* -------------------------------------------------------------------------- */
|
|
41
|
+
/* object-selector — multi-select object picker */
|
|
42
|
+
/* -------------------------------------------------------------------------- */
|
|
43
|
+
function ObjectSelectorWidget({ id, value, onChange, readOnly, context, fieldSpec, }) {
|
|
44
|
+
const names = context?.objectNames ?? [];
|
|
45
|
+
const multiple = fieldSpec?.multiple ?? false;
|
|
46
|
+
// Parse value: string[], string (comma-separated), or empty
|
|
47
|
+
const selectedValues = React.useMemo(() => {
|
|
48
|
+
if (!value)
|
|
49
|
+
return [];
|
|
50
|
+
if (Array.isArray(value))
|
|
51
|
+
return value.map(String);
|
|
52
|
+
return String(value).split(',').map(s => s.trim()).filter(Boolean);
|
|
53
|
+
}, [value]);
|
|
54
|
+
const handleToggle = (objName) => {
|
|
55
|
+
if (readOnly)
|
|
56
|
+
return;
|
|
57
|
+
if (!multiple) {
|
|
58
|
+
onChange(objName);
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
const newSelection = selectedValues.includes(objName)
|
|
62
|
+
? selectedValues.filter(v => v !== objName)
|
|
63
|
+
: [...selectedValues, objName];
|
|
64
|
+
onChange(newSelection);
|
|
65
|
+
};
|
|
66
|
+
const handleRemove = (objName) => {
|
|
67
|
+
if (readOnly)
|
|
68
|
+
return;
|
|
69
|
+
const newSelection = selectedValues.filter(v => v !== objName);
|
|
70
|
+
onChange(multiple ? newSelection : '');
|
|
71
|
+
};
|
|
72
|
+
if (context?.objectsLoading) {
|
|
73
|
+
return _jsx(Input, { id: id, value: "Loading objects...", readOnly: true, disabled: true });
|
|
74
|
+
}
|
|
75
|
+
return (_jsxs("div", { className: "space-y-2", children: [selectedValues.length > 0 && (_jsx("div", { className: "flex flex-wrap gap-2", children: selectedValues.map(obj => (_jsxs("div", { className: "inline-flex items-center gap-1 rounded bg-secondary px-2 py-1 text-sm", children: [_jsx("span", { children: obj }), !readOnly && (_jsx("button", { type: "button", onClick: () => handleRemove(obj), className: "text-muted-foreground hover:text-foreground", children: "\u00D7" }))] }, obj))) })), _jsxs(Select, { value: "", onValueChange: handleToggle, disabled: readOnly || names.length === 0, children: [_jsx(SelectTrigger, { id: id, children: _jsx(SelectValue, { placeholder: multiple ? "Add objects..." : "Select object..." }) }), _jsx(SelectContent, { children: names.map(name => (_jsxs(SelectItem, { value: name, disabled: !multiple && selectedValues.includes(name), children: [name, selectedValues.includes(name) && ' ✓'] }, name))) })] })] }));
|
|
76
|
+
}
|
|
77
|
+
/* -------------------------------------------------------------------------- */
|
|
78
|
+
/* field-selector — smart field picker (depends on selected object) */
|
|
79
|
+
/* -------------------------------------------------------------------------- */
|
|
80
|
+
function FieldSelectorWidget({ id, value, onChange, readOnly, fieldSpec, formData, }) {
|
|
81
|
+
const [fields, setFields] = React.useState([]);
|
|
82
|
+
const [loading, setLoading] = React.useState(false);
|
|
83
|
+
// Resolve dependency: fieldSpec.dependsOn or fieldSpec.reference or 'objectName'
|
|
84
|
+
const dependsOnRaw = fieldSpec?.dependsOn || fieldSpec?.reference || 'objectName';
|
|
85
|
+
const dependsOnField = Array.isArray(dependsOnRaw) ? dependsOnRaw[0] : dependsOnRaw;
|
|
86
|
+
const objectName = formData?.[dependsOnField];
|
|
87
|
+
// Load fields when objectName changes
|
|
88
|
+
React.useEffect(() => {
|
|
89
|
+
if (!objectName) {
|
|
90
|
+
setFields([]);
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
setLoading(true);
|
|
94
|
+
fetch(`/api/v1/objects/${objectName}/fields`)
|
|
95
|
+
.then(r => r.json())
|
|
96
|
+
.then(data => {
|
|
97
|
+
setFields(data.fields || []);
|
|
98
|
+
setLoading(false);
|
|
99
|
+
})
|
|
100
|
+
.catch(err => {
|
|
101
|
+
console.error('Failed to load fields:', err);
|
|
102
|
+
setFields([]);
|
|
103
|
+
setLoading(false);
|
|
104
|
+
});
|
|
105
|
+
}, [objectName]);
|
|
106
|
+
const multiple = fieldSpec?.multiple ?? false;
|
|
107
|
+
// Parse value
|
|
108
|
+
const selectedValues = React.useMemo(() => {
|
|
109
|
+
if (!value)
|
|
110
|
+
return [];
|
|
111
|
+
if (Array.isArray(value))
|
|
112
|
+
return value.map(String);
|
|
113
|
+
return String(value).split(',').map(s => s.trim()).filter(Boolean);
|
|
114
|
+
}, [value]);
|
|
115
|
+
const handleToggle = (fieldName) => {
|
|
116
|
+
if (readOnly)
|
|
117
|
+
return;
|
|
118
|
+
if (!multiple) {
|
|
119
|
+
onChange(fieldName);
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
const newSelection = selectedValues.includes(fieldName)
|
|
123
|
+
? selectedValues.filter(v => v !== fieldName)
|
|
124
|
+
: [...selectedValues, fieldName];
|
|
125
|
+
onChange(newSelection);
|
|
126
|
+
};
|
|
127
|
+
const handleRemove = (fieldName) => {
|
|
128
|
+
if (readOnly)
|
|
129
|
+
return;
|
|
130
|
+
const newSelection = selectedValues.filter(v => v !== fieldName);
|
|
131
|
+
onChange(multiple ? newSelection : '');
|
|
132
|
+
};
|
|
133
|
+
if (!objectName) {
|
|
134
|
+
return _jsx(Input, { id: id, value: "(Select an object first)", readOnly: true, disabled: true });
|
|
135
|
+
}
|
|
136
|
+
if (loading) {
|
|
137
|
+
return _jsx(Input, { id: id, value: "Loading fields...", readOnly: true, disabled: true });
|
|
138
|
+
}
|
|
139
|
+
return (_jsxs("div", { className: "space-y-2", children: [selectedValues.length > 0 && (_jsx("div", { className: "flex flex-wrap gap-2", children: selectedValues.map(field => {
|
|
140
|
+
const fieldMeta = fields.find(f => f.name === field);
|
|
141
|
+
return (_jsxs("div", { className: "inline-flex items-center gap-1 rounded bg-secondary px-2 py-1 text-sm", children: [_jsx("span", { children: fieldMeta?.label || field }), _jsx("code", { className: "text-xs text-muted-foreground", children: fieldMeta?.type }), !readOnly && (_jsx("button", { type: "button", onClick: () => handleRemove(field), className: "text-muted-foreground hover:text-foreground", children: "\u00D7" }))] }, field));
|
|
142
|
+
}) })), _jsxs(Select, { value: "", onValueChange: handleToggle, disabled: readOnly || fields.length === 0, children: [_jsx(SelectTrigger, { id: id, children: _jsx(SelectValue, { placeholder: multiple ? "Add fields..." : "Select field..." }) }), _jsx(SelectContent, { children: fields.map(f => (_jsx(SelectItem, { value: f.name, disabled: !multiple && selectedValues.includes(f.name), children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { children: f.label || f.name }), _jsx("code", { className: "text-xs text-muted-foreground", children: f.type }), selectedValues.includes(f.name) && ' ✓'] }) }, f.name))) })] })] }));
|
|
143
|
+
}
|
|
144
|
+
/* -------------------------------------------------------------------------- */
|
|
145
|
+
/* master-detail — inline row editor for array-of-object fields */
|
|
146
|
+
/* -------------------------------------------------------------------------- */
|
|
147
|
+
function MasterDetailWidget({ schema, value, onChange, readOnly, context, }) {
|
|
148
|
+
// Unwrap anyOf/oneOf: pick the first array-of-object branch.
|
|
149
|
+
let resolved = schema;
|
|
150
|
+
if (resolved?.anyOf || resolved?.oneOf) {
|
|
151
|
+
const branches = (resolved.anyOf ?? resolved.oneOf);
|
|
152
|
+
const objBranch = branches.find((b) => b?.type === 'array' && b?.items?.type === 'object');
|
|
153
|
+
if (objBranch)
|
|
154
|
+
resolved = { ...resolved, ...objBranch };
|
|
155
|
+
}
|
|
156
|
+
const items = (resolved?.items ?? {});
|
|
157
|
+
const itemProps = (items.properties ?? {});
|
|
158
|
+
const required = new Set(Array.isArray(items.required) ? items.required : []);
|
|
159
|
+
const rows = Array.isArray(value) ? value : [];
|
|
160
|
+
const cols = Object.keys(itemProps);
|
|
161
|
+
if (cols.length === 0) {
|
|
162
|
+
// Falls back to JSON if the array items aren't a typed object.
|
|
163
|
+
return (_jsxs("div", { className: "rounded border border-dashed border-amber-500/40 bg-amber-500/5 p-2 text-xs text-amber-700 dark:text-amber-300", children: ["master-detail widget requires ", _jsx("code", { children: "items.properties" }), " on the JSON schema (or an ", _jsx("code", { children: "anyOf" }), " branch that has them)."] }));
|
|
164
|
+
}
|
|
165
|
+
function updateRow(idx, patch) {
|
|
166
|
+
const next = rows.slice();
|
|
167
|
+
next[idx] = { ...(next[idx] ?? {}), ...patch };
|
|
168
|
+
onChange(next);
|
|
169
|
+
}
|
|
170
|
+
function addRow() {
|
|
171
|
+
onChange([...rows, {}]);
|
|
172
|
+
}
|
|
173
|
+
function removeRow(idx) {
|
|
174
|
+
const next = rows.slice();
|
|
175
|
+
next.splice(idx, 1);
|
|
176
|
+
onChange(next);
|
|
177
|
+
}
|
|
178
|
+
return (_jsxs("div", { className: "space-y-2", children: [_jsx("div", { className: "overflow-x-auto rounded border border-border/40", children: _jsxs("table", { className: "w-full text-sm", children: [_jsx("thead", { className: "bg-muted/40", children: _jsxs("tr", { children: [cols.map((c) => (_jsxs("th", { className: "px-2 py-1.5 text-left text-xs font-medium text-muted-foreground", children: [itemProps[c]?.title ?? c, required.has(c) && (_jsx("span", { className: "text-destructive ml-0.5", children: "*" }))] }, c))), _jsx("th", { className: "w-8" })] }) }), _jsxs("tbody", { children: [rows.length === 0 && (_jsx("tr", { children: _jsx("td", { colSpan: cols.length + 1, className: "px-2 py-3 text-center text-xs text-muted-foreground", children: "No rows. Click + to add." }) })), rows.map((row, idx) => (_jsxs("tr", { className: "border-t border-border/30", children: [cols.map((c) => (_jsx("td", { className: "p-1", children: _jsx(RowCell, { schema: itemProps[c], value: (row ?? {})[c], readOnly: readOnly, context: context, onChange: (v) => updateRow(idx, { [c]: v }) }) }, c))), _jsx("td", { className: "p-1 text-right", children: _jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => removeRow(idx), disabled: readOnly, className: "h-7 w-7 p-0", "aria-label": "Remove row", children: _jsx(Trash2, { className: "h-3.5 w-3.5" }) }) })] }, idx)))] })] }) }), _jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: addRow, disabled: readOnly, children: [_jsx(Plus, { className: "h-3.5 w-3.5 mr-1" }), " Add row"] })] }));
|
|
179
|
+
}
|
|
180
|
+
function RowCell({ schema, value, readOnly, context, onChange, }) {
|
|
181
|
+
// ref:object hint inside a row cell
|
|
182
|
+
if (schema?.widget === 'ref:object') {
|
|
183
|
+
return (_jsx(RefObjectWidget, { schema: schema, value: value, onChange: onChange, readOnly: readOnly, context: context }));
|
|
184
|
+
}
|
|
185
|
+
// enum → dropdown
|
|
186
|
+
const enumVals = schema?.enum;
|
|
187
|
+
if (Array.isArray(enumVals) && enumVals.length > 0) {
|
|
188
|
+
return (_jsxs(Select, { value: value == null ? '' : String(value), onValueChange: (v) => onChange(v || undefined), disabled: readOnly, children: [_jsx(SelectTrigger, { className: "h-8 text-xs", children: _jsx(SelectValue, { placeholder: "\u2014" }) }), _jsx(SelectContent, { children: enumVals.map((o) => (_jsx(SelectItem, { value: String(o), children: String(o) }, String(o)))) })] }));
|
|
189
|
+
}
|
|
190
|
+
// boolean → checkbox-ish
|
|
191
|
+
if (schema?.type === 'boolean') {
|
|
192
|
+
return (_jsx("input", { type: "checkbox", checked: !!value, disabled: readOnly, onChange: (e) => onChange(e.target.checked), className: "h-4 w-4" }));
|
|
193
|
+
}
|
|
194
|
+
// number
|
|
195
|
+
if (schema?.type === 'number' || schema?.type === 'integer') {
|
|
196
|
+
return (_jsx(Input, { type: "number", value: value == null ? '' : String(value), disabled: readOnly, onChange: (e) => {
|
|
197
|
+
const n = e.target.valueAsNumber;
|
|
198
|
+
onChange(Number.isFinite(n) ? n : undefined);
|
|
199
|
+
}, className: "h-8 text-xs" }));
|
|
200
|
+
}
|
|
201
|
+
// default: text
|
|
202
|
+
return (_jsx(Input, { value: value == null ? '' : String(value), disabled: readOnly, onChange: (e) => onChange(e.target.value || undefined), className: "h-8 text-xs" }));
|
|
203
|
+
}
|
|
204
|
+
/* -------------------------------------------------------------------------- */
|
|
205
|
+
/* string-tags — chip input for string[] (e.g. searchableFields) */
|
|
206
|
+
/* -------------------------------------------------------------------------- */
|
|
207
|
+
function StringTagsWidget({ id, value, onChange, readOnly, }) {
|
|
208
|
+
const tags = Array.isArray(value) ? value : [];
|
|
209
|
+
const [draft, setDraft] = React.useState('');
|
|
210
|
+
function add(raw) {
|
|
211
|
+
const parts = raw
|
|
212
|
+
.split(/[,\n]/)
|
|
213
|
+
.map((s) => s.trim())
|
|
214
|
+
.filter(Boolean);
|
|
215
|
+
if (parts.length === 0)
|
|
216
|
+
return;
|
|
217
|
+
const next = [...tags];
|
|
218
|
+
for (const p of parts)
|
|
219
|
+
if (!next.includes(p))
|
|
220
|
+
next.push(p);
|
|
221
|
+
onChange(next);
|
|
222
|
+
setDraft('');
|
|
223
|
+
}
|
|
224
|
+
function remove(idx) {
|
|
225
|
+
const next = tags.slice();
|
|
226
|
+
next.splice(idx, 1);
|
|
227
|
+
onChange(next);
|
|
228
|
+
}
|
|
229
|
+
return (_jsx("div", { className: "rounded border border-input bg-background p-1.5", children: _jsxs("div", { className: "flex flex-wrap items-center gap-1", children: [tags.map((t, i) => (_jsxs("span", { className: "inline-flex items-center gap-1 rounded bg-muted px-1.5 py-0.5 text-xs", children: [_jsx("span", { className: "font-mono", children: t }), !readOnly && (_jsx("button", { type: "button", "aria-label": `Remove ${t}`, onClick: () => remove(i), className: "text-muted-foreground hover:text-destructive", children: "\u00D7" }))] }, i))), _jsx("input", { id: id, type: "text", value: draft, disabled: readOnly, placeholder: tags.length === 0 ? 'Type and press Enter…' : '', onChange: (e) => setDraft(e.target.value), onKeyDown: (e) => {
|
|
230
|
+
if (e.key === 'Enter' || e.key === ',') {
|
|
231
|
+
e.preventDefault();
|
|
232
|
+
add(draft);
|
|
233
|
+
}
|
|
234
|
+
else if (e.key === 'Backspace' && !draft && tags.length > 0) {
|
|
235
|
+
remove(tags.length - 1);
|
|
236
|
+
}
|
|
237
|
+
}, onBlur: () => draft && add(draft), className: "min-w-[8rem] flex-1 bg-transparent text-sm outline-none" })] }) }));
|
|
238
|
+
}
|
|
239
|
+
/* -------------------------------------------------------------------------- */
|
|
240
|
+
/* registry */
|
|
241
|
+
/* -------------------------------------------------------------------------- */
|
|
242
|
+
export const WIDGETS = {
|
|
243
|
+
'ref:object': RefObjectWidget,
|
|
244
|
+
'object-selector': ObjectSelectorWidget,
|
|
245
|
+
'field-selector': FieldSelectorWidget,
|
|
246
|
+
'master-detail': MasterDetailWidget,
|
|
247
|
+
'string-tags': StringTagsWidget,
|
|
248
|
+
'code': CodeWidget,
|
|
249
|
+
// Reasonable fallbacks until dedicated builders ship:
|
|
250
|
+
'filter-builder': MasterDetailWidget,
|
|
251
|
+
};
|
|
252
|
+
/* -------------------------------------------------------------------------- */
|
|
253
|
+
/* CodeWidget — Monaco editor for `type: 'code'` fields */
|
|
254
|
+
/* -------------------------------------------------------------------------- */
|
|
255
|
+
/**
|
|
256
|
+
* Infer language from fieldSpec.language → schema.format → field name.
|
|
257
|
+
*/
|
|
258
|
+
function inferCodeLanguage(fieldSpec, schema) {
|
|
259
|
+
if (fieldSpec?.language)
|
|
260
|
+
return fieldSpec.language;
|
|
261
|
+
if (typeof schema?.format === 'string') {
|
|
262
|
+
const f = schema.format.toLowerCase();
|
|
263
|
+
if (f === 'sql' || f === 'javascript' || f === 'typescript' || f === 'json' || f === 'yaml' || f === 'html' || f === 'css' || f === 'python')
|
|
264
|
+
return f;
|
|
265
|
+
}
|
|
266
|
+
// Common hook/action field name heuristics
|
|
267
|
+
const name = fieldSpec?.field?.toLowerCase() ?? '';
|
|
268
|
+
if (name.includes('sql') || name === 'query')
|
|
269
|
+
return 'sql';
|
|
270
|
+
if (name === 'source' || name === 'body' || name === 'script' || name === 'handler')
|
|
271
|
+
return 'javascript';
|
|
272
|
+
if (name === 'expression' || name === 'predicate' || name === 'formula' || name === 'condition')
|
|
273
|
+
return 'javascript';
|
|
274
|
+
return 'javascript';
|
|
275
|
+
}
|
|
276
|
+
const LazyCodeEditor = React.lazy(() => import('@object-ui/plugin-editor').then((m) => ({ default: m.CodeEditorRenderer })));
|
|
277
|
+
export function CodeWidget({ schema, value, onChange, readOnly, fieldSpec, }) {
|
|
278
|
+
const language = inferCodeLanguage(fieldSpec, schema);
|
|
279
|
+
const stringValue = typeof value === 'string' ? value : (value == null ? '' : String(value));
|
|
280
|
+
return (_jsxs("div", { className: "rounded-md border border-border/50 overflow-hidden", children: [_jsxs("div", { className: "flex items-center justify-between px-2 py-1 bg-muted/40 border-b border-border/30 text-[10px] font-mono text-muted-foreground", children: [_jsx("span", { children: language }), readOnly && _jsx("span", { children: "read-only" })] }), _jsx(React.Suspense, { fallback: _jsx("div", { className: "h-[280px] flex items-center justify-center text-xs text-muted-foreground", children: "Loading editor\u2026" }), children: _jsx(LazyCodeEditor, { schema: {
|
|
281
|
+
type: 'code',
|
|
282
|
+
language,
|
|
283
|
+
theme: 'vs-dark',
|
|
284
|
+
height: '280px',
|
|
285
|
+
readOnly,
|
|
286
|
+
}, value: stringValue, onChange: (v) => onChange(v ?? '') }) })] }));
|
|
287
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@object-ui/app-shell",
|
|
3
|
-
"version": "6.0
|
|
3
|
+
"version": "6.2.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"description": "Minimal application shell for ObjectUI - framework-agnostic rendering engine",
|
|
@@ -25,42 +25,43 @@
|
|
|
25
25
|
"./styles.css": "./src/styles.css"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@sentry/react": "^
|
|
28
|
+
"@sentry/react": "^10.53.1",
|
|
29
29
|
"lucide-react": "^1.16.0",
|
|
30
30
|
"sonner": "^2.0.7",
|
|
31
|
-
"@object-ui/auth": "6.0
|
|
32
|
-
"@object-ui/collaboration": "6.0
|
|
33
|
-
"@object-ui/components": "6.0
|
|
34
|
-
"@object-ui/
|
|
35
|
-
"@object-ui/
|
|
36
|
-
"@object-ui/
|
|
37
|
-
"@object-ui/
|
|
38
|
-
"@object-ui/
|
|
39
|
-
"@object-ui/
|
|
40
|
-
"@object-ui/
|
|
41
|
-
"@object-ui/
|
|
42
|
-
"@object-ui/
|
|
31
|
+
"@object-ui/auth": "6.2.0",
|
|
32
|
+
"@object-ui/collaboration": "6.2.0",
|
|
33
|
+
"@object-ui/components": "6.2.0",
|
|
34
|
+
"@object-ui/plugin-editor": "6.2.0",
|
|
35
|
+
"@object-ui/core": "6.2.0",
|
|
36
|
+
"@object-ui/data-objectstack": "6.2.0",
|
|
37
|
+
"@object-ui/fields": "6.2.0",
|
|
38
|
+
"@object-ui/i18n": "6.2.0",
|
|
39
|
+
"@object-ui/layout": "6.2.0",
|
|
40
|
+
"@object-ui/permissions": "6.2.0",
|
|
41
|
+
"@object-ui/providers": "6.2.0",
|
|
42
|
+
"@object-ui/react": "6.2.0",
|
|
43
|
+
"@object-ui/types": "6.2.0"
|
|
43
44
|
},
|
|
44
45
|
"peerDependencies": {
|
|
45
46
|
"react": "^18.0.0 || ^19.0.0",
|
|
46
47
|
"react-dom": "^18.0.0 || ^19.0.0",
|
|
47
48
|
"react-router-dom": "^6.0.0 || ^7.0.0",
|
|
48
|
-
"@object-ui/plugin-calendar": "^6.0
|
|
49
|
-
"@object-ui/plugin-charts": "^6.0
|
|
50
|
-
"@object-ui/plugin-chatbot": "^6.0
|
|
51
|
-
"@object-ui/plugin-dashboard": "^6.0
|
|
52
|
-
"@object-ui/plugin-designer": "^6.0
|
|
53
|
-
"@object-ui/plugin-detail": "^6.0
|
|
54
|
-
"@object-ui/plugin-form": "^6.0
|
|
55
|
-
"@object-ui/plugin-grid": "^6.0
|
|
56
|
-
"@object-ui/plugin-kanban": "^6.0
|
|
57
|
-
"@object-ui/plugin-list": "^6.0
|
|
58
|
-
"@object-ui/plugin-report": "^6.0
|
|
59
|
-
"@object-ui/plugin-view": "^6.0
|
|
49
|
+
"@object-ui/plugin-calendar": "^6.2.0",
|
|
50
|
+
"@object-ui/plugin-charts": "^6.2.0",
|
|
51
|
+
"@object-ui/plugin-chatbot": "^6.2.0",
|
|
52
|
+
"@object-ui/plugin-dashboard": "^6.2.0",
|
|
53
|
+
"@object-ui/plugin-designer": "^6.2.0",
|
|
54
|
+
"@object-ui/plugin-detail": "^6.2.0",
|
|
55
|
+
"@object-ui/plugin-form": "^6.2.0",
|
|
56
|
+
"@object-ui/plugin-grid": "^6.2.0",
|
|
57
|
+
"@object-ui/plugin-kanban": "^6.2.0",
|
|
58
|
+
"@object-ui/plugin-list": "^6.2.0",
|
|
59
|
+
"@object-ui/plugin-report": "^6.2.0",
|
|
60
|
+
"@object-ui/plugin-view": "^6.2.0"
|
|
60
61
|
},
|
|
61
62
|
"devDependencies": {
|
|
62
|
-
"@types/node": "^25.9.
|
|
63
|
-
"@types/react": "19.2.
|
|
63
|
+
"@types/node": "^25.9.1",
|
|
64
|
+
"@types/react": "19.2.15",
|
|
64
65
|
"@types/react-dom": "19.2.3",
|
|
65
66
|
"react": "19.2.6",
|
|
66
67
|
"react-dom": "19.2.6",
|