@object-ui/app-shell 6.1.0 → 6.2.1
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 +129 -0
- package/README.md +10 -1
- package/dist/console/AppContent.js +53 -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 +57 -19
- 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 +169 -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 +66 -6
- package/dist/views/RecordFormPage.js +15 -3
- package/dist/views/SearchResultsPage.js +4 -0
- package/dist/views/metadata-admin/DesignerEditorWrapper.d.ts +68 -0
- package/dist/views/metadata-admin/DesignerEditorWrapper.js +158 -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/MetadataDetailDrawer.d.ts +13 -0
- package/dist/views/metadata-admin/MetadataDetailDrawer.js +52 -0
- package/dist/views/metadata-admin/PageShell.d.ts +34 -0
- package/dist/views/metadata-admin/PageShell.js +40 -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/RelatedPanel.d.ts +33 -0
- package/dist/views/metadata-admin/RelatedPanel.js +171 -0
- package/dist/views/metadata-admin/ResourceEditPage.d.ts +13 -0
- package/dist/views/metadata-admin/ResourceEditPage.js +302 -0
- package/dist/views/metadata-admin/ResourceHistoryPage.d.ts +5 -0
- package/dist/views/metadata-admin/ResourceHistoryPage.js +100 -0
- package/dist/views/metadata-admin/ResourceListPage.d.ts +4 -0
- package/dist/views/metadata-admin/ResourceListPage.js +146 -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 +565 -0
- package/dist/views/metadata-admin/anchors.d.ts +1 -0
- package/dist/views/metadata-admin/anchors.js +229 -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 +33 -0
- package/dist/views/metadata-admin/index.js +39 -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 +232 -0
- package/dist/views/metadata-admin/registry.js +106 -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 +27 -26
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
|
|
2
|
+
/**
|
|
3
|
+
* Built-in anchor relationships for the Related tab.
|
|
4
|
+
*
|
|
5
|
+
* Each entry tells the metadata-admin engine "items of type X belong to
|
|
6
|
+
* (are anchored at) items of type Y when this predicate matches". The
|
|
7
|
+
* Related panel on the parent's edit page uses this registry to decide
|
|
8
|
+
* which `client.list(type)` calls to make and how to group the results.
|
|
9
|
+
*
|
|
10
|
+
* Adding a new relationship is intentionally a one-liner — declare it
|
|
11
|
+
* here (or in your own plugin's bootstrap) and the UI picks it up with
|
|
12
|
+
* no further wiring. Predicates are kept tiny because they run client-
|
|
13
|
+
* side over every item returned from `list()`.
|
|
14
|
+
*
|
|
15
|
+
* Why not parse the schema and infer this? Some types nest the anchor
|
|
16
|
+
* deep in `data.object` or `list.data.object`; some types live on the
|
|
17
|
+
* parent (e.g. `fields` is embedded inside the object item itself).
|
|
18
|
+
* Hand-rolled declarations are clearer than schema-walking heuristics.
|
|
19
|
+
*/
|
|
20
|
+
import { registerMetadataResource, anchorByField } from './registry';
|
|
21
|
+
export function registerBuiltinAnchors() {
|
|
22
|
+
// ── EMBEDDED: items stored inside the object body itself ──────────
|
|
23
|
+
//
|
|
24
|
+
// Fields, indexes, and embedded validations don't have a top-level
|
|
25
|
+
// metadata type; they live under `object.fields`, `object.indexes[]`,
|
|
26
|
+
// `object.validations[]`. We surface them with `source: 'embedded'`
|
|
27
|
+
// so the Related panel can list them without a separate API call.
|
|
28
|
+
//
|
|
29
|
+
// `fields` is stored as a NAME-KEYED MAP (`{ email: {...}, name: {...} }`)
|
|
30
|
+
// — not an array — so we adapt it into an array carrying the key as
|
|
31
|
+
// `name` for the panel to render.
|
|
32
|
+
registerMetadataResource({
|
|
33
|
+
type: '__object_field',
|
|
34
|
+
label: 'Field',
|
|
35
|
+
anchors: [{
|
|
36
|
+
anchorType: 'object',
|
|
37
|
+
source: 'embedded',
|
|
38
|
+
extract: (parent) => mapOrArrayToList(parent.fields),
|
|
39
|
+
groupLabel: 'Fields',
|
|
40
|
+
order: 5,
|
|
41
|
+
}],
|
|
42
|
+
});
|
|
43
|
+
registerMetadataResource({
|
|
44
|
+
type: '__object_index',
|
|
45
|
+
label: 'Index',
|
|
46
|
+
anchors: [{
|
|
47
|
+
anchorType: 'object',
|
|
48
|
+
source: 'embedded',
|
|
49
|
+
extract: (parent) => {
|
|
50
|
+
const raw = parent.indexes;
|
|
51
|
+
if (!Array.isArray(raw))
|
|
52
|
+
return [];
|
|
53
|
+
// Indexes have no `name`; synthesise one from their column list.
|
|
54
|
+
return raw.map((idx, i) => {
|
|
55
|
+
const fields = Array.isArray(idx.fields) ? idx.fields.join(',') : '';
|
|
56
|
+
const synthesised = fields ? `idx_${fields}` : `index_${i}`;
|
|
57
|
+
return { name: synthesised, ...idx };
|
|
58
|
+
});
|
|
59
|
+
},
|
|
60
|
+
groupLabel: 'Indexes',
|
|
61
|
+
order: 10,
|
|
62
|
+
}],
|
|
63
|
+
});
|
|
64
|
+
registerMetadataResource({
|
|
65
|
+
type: '__object_validation',
|
|
66
|
+
label: 'Embedded validation',
|
|
67
|
+
anchors: [{
|
|
68
|
+
anchorType: 'object',
|
|
69
|
+
source: 'embedded',
|
|
70
|
+
extract: (parent) => mapOrArrayToList(parent.validations),
|
|
71
|
+
groupLabel: 'Embedded Validations',
|
|
72
|
+
order: 15,
|
|
73
|
+
}],
|
|
74
|
+
});
|
|
75
|
+
// ── LIST: standalone child metadata types ─────────────────────────
|
|
76
|
+
// hook.object → object (beforeInsert / afterUpdate / …)
|
|
77
|
+
registerMetadataResource({
|
|
78
|
+
type: 'hook',
|
|
79
|
+
anchors: [{
|
|
80
|
+
anchorType: 'object',
|
|
81
|
+
match: anchorByField('object'),
|
|
82
|
+
groupLabel: 'Hooks',
|
|
83
|
+
order: 20,
|
|
84
|
+
}],
|
|
85
|
+
});
|
|
86
|
+
// approval.object → object (approval processes targeting this object)
|
|
87
|
+
registerMetadataResource({
|
|
88
|
+
type: 'approval',
|
|
89
|
+
anchors: [{
|
|
90
|
+
anchorType: 'object',
|
|
91
|
+
match: anchorByField('object'),
|
|
92
|
+
groupLabel: 'Approval Processes',
|
|
93
|
+
order: 70,
|
|
94
|
+
}],
|
|
95
|
+
});
|
|
96
|
+
// page.object → object (auto-generated record pages, etc.)
|
|
97
|
+
registerMetadataResource({
|
|
98
|
+
type: 'page',
|
|
99
|
+
anchors: [{
|
|
100
|
+
anchorType: 'object',
|
|
101
|
+
match: anchorByField('object'),
|
|
102
|
+
groupLabel: 'Pages',
|
|
103
|
+
order: 40,
|
|
104
|
+
}],
|
|
105
|
+
});
|
|
106
|
+
// view binds to an object via data.object (list view / form view variants)
|
|
107
|
+
registerMetadataResource({
|
|
108
|
+
type: 'view',
|
|
109
|
+
anchors: [{
|
|
110
|
+
anchorType: 'object',
|
|
111
|
+
match: anchorByField([
|
|
112
|
+
'data.object',
|
|
113
|
+
'list.data.object',
|
|
114
|
+
'form.data.object',
|
|
115
|
+
'object',
|
|
116
|
+
]),
|
|
117
|
+
groupLabel: 'Views',
|
|
118
|
+
order: 30,
|
|
119
|
+
}],
|
|
120
|
+
});
|
|
121
|
+
// flow / workflow may reference an object at the root or under `on`
|
|
122
|
+
registerMetadataResource({
|
|
123
|
+
type: 'flow',
|
|
124
|
+
anchors: [{
|
|
125
|
+
anchorType: 'object',
|
|
126
|
+
match: anchorByField(['object', 'on.object', 'trigger.object']),
|
|
127
|
+
groupLabel: 'Flows',
|
|
128
|
+
order: 50,
|
|
129
|
+
}],
|
|
130
|
+
});
|
|
131
|
+
registerMetadataResource({
|
|
132
|
+
type: 'workflow',
|
|
133
|
+
anchors: [{
|
|
134
|
+
anchorType: 'object',
|
|
135
|
+
match: anchorByField(['object', 'on.object']),
|
|
136
|
+
groupLabel: 'Workflow Rules',
|
|
137
|
+
order: 51,
|
|
138
|
+
}],
|
|
139
|
+
});
|
|
140
|
+
// trigger.object → object (low-level DB-style triggers)
|
|
141
|
+
registerMetadataResource({
|
|
142
|
+
type: 'trigger',
|
|
143
|
+
anchors: [{
|
|
144
|
+
anchorType: 'object',
|
|
145
|
+
match: anchorByField(['object', 'on.object']),
|
|
146
|
+
groupLabel: 'Triggers',
|
|
147
|
+
order: 52,
|
|
148
|
+
}],
|
|
149
|
+
});
|
|
150
|
+
// validation: usually embedded in the object, but standalone variants
|
|
151
|
+
// do exist. Match anything whose `object` points back at us.
|
|
152
|
+
registerMetadataResource({
|
|
153
|
+
type: 'validation',
|
|
154
|
+
anchors: [{
|
|
155
|
+
anchorType: 'object',
|
|
156
|
+
match: anchorByField('object'),
|
|
157
|
+
groupLabel: 'Validations',
|
|
158
|
+
order: 25,
|
|
159
|
+
}],
|
|
160
|
+
});
|
|
161
|
+
// permission has a sparse object-keyed map under `objects`. Match by
|
|
162
|
+
// membership of the parent name in that map.
|
|
163
|
+
registerMetadataResource({
|
|
164
|
+
type: 'permission',
|
|
165
|
+
anchors: [{
|
|
166
|
+
anchorType: 'object',
|
|
167
|
+
match: (item, name) => {
|
|
168
|
+
const objs = item?.objects;
|
|
169
|
+
return !!objs && typeof objs === 'object' && name in objs;
|
|
170
|
+
},
|
|
171
|
+
groupLabel: 'Permissions',
|
|
172
|
+
order: 60,
|
|
173
|
+
}],
|
|
174
|
+
});
|
|
175
|
+
// action — both record-scoped and object-scoped actions live here.
|
|
176
|
+
// Schemas vary, so accept any of the three common shapes.
|
|
177
|
+
registerMetadataResource({
|
|
178
|
+
type: 'action',
|
|
179
|
+
anchors: [{
|
|
180
|
+
anchorType: 'object',
|
|
181
|
+
match: anchorByField([
|
|
182
|
+
'object',
|
|
183
|
+
'target.object',
|
|
184
|
+
'on.object',
|
|
185
|
+
]),
|
|
186
|
+
groupLabel: 'Actions',
|
|
187
|
+
order: 55,
|
|
188
|
+
}],
|
|
189
|
+
});
|
|
190
|
+
// dashboard / report — surface ones bound to a specific object.
|
|
191
|
+
// Many will not have an explicit anchor; those simply don't appear.
|
|
192
|
+
registerMetadataResource({
|
|
193
|
+
type: 'dashboard',
|
|
194
|
+
anchors: [{
|
|
195
|
+
anchorType: 'object',
|
|
196
|
+
match: anchorByField(['object', 'data.object']),
|
|
197
|
+
groupLabel: 'Dashboards',
|
|
198
|
+
order: 80,
|
|
199
|
+
}],
|
|
200
|
+
});
|
|
201
|
+
registerMetadataResource({
|
|
202
|
+
type: 'report',
|
|
203
|
+
anchors: [{
|
|
204
|
+
anchorType: 'object',
|
|
205
|
+
match: anchorByField(['object', 'data.object']),
|
|
206
|
+
groupLabel: 'Reports',
|
|
207
|
+
order: 81,
|
|
208
|
+
}],
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Coerce an embedded "collection" into a list of `{ name, …rest }`.
|
|
213
|
+
*
|
|
214
|
+
* ObjectStack stores some embedded collections as name-keyed maps
|
|
215
|
+
* (`object.fields`) and others as arrays (`object.indexes`). Both want
|
|
216
|
+
* to render the same way in Related; this helper papers over the gap
|
|
217
|
+
* by injecting the map key as `name` when needed.
|
|
218
|
+
*/
|
|
219
|
+
function mapOrArrayToList(raw) {
|
|
220
|
+
if (Array.isArray(raw))
|
|
221
|
+
return raw;
|
|
222
|
+
if (raw && typeof raw === 'object') {
|
|
223
|
+
return Object.entries(raw).map(([k, v]) => {
|
|
224
|
+
const body = v && typeof v === 'object' ? v : {};
|
|
225
|
+
return { name: k, ...body };
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
return [];
|
|
229
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Register fallback schemas for every writable type. Idempotent — uses
|
|
3
|
+
* `registerMetadataResource` so any prior registration (e.g. a bespoke
|
|
4
|
+
* EditPage from PermissionMatrixEditor) is merged via the engine.
|
|
5
|
+
*/
|
|
6
|
+
export declare function registerDefaultMetadataSchemas(): void;
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
|
|
2
|
+
/**
|
|
3
|
+
* default-schemas — minimal JSONSchemas used by SchemaForm when the
|
|
4
|
+
* framework's `/meta/types` registry row doesn't carry a `schema` field
|
|
5
|
+
* (i.e. for every type today — full Zod→JSONSchema generation in the
|
|
6
|
+
* framework is a deferred milestone).
|
|
7
|
+
*
|
|
8
|
+
* These schemas cover the universal metadata header (name/label/
|
|
9
|
+
* description/scope) plus the small set of per-type "obvious" fields
|
|
10
|
+
* users expect on day one. Anything that doesn't fit here can still be
|
|
11
|
+
* edited via the raw JSON escape hatch — the schema is intentionally
|
|
12
|
+
* lax (no `additionalProperties: false`) so unknown payload keys round-
|
|
13
|
+
* trip untouched.
|
|
14
|
+
*
|
|
15
|
+
* When the framework starts emitting JSONSchemas in the registry, this
|
|
16
|
+
* file becomes a no-op (SchemaForm receives the real schema and ignores
|
|
17
|
+
* `defaultSchema`).
|
|
18
|
+
*/
|
|
19
|
+
import { registerMetadataResource } from './registry';
|
|
20
|
+
/** Shared header fields every metadata item has. */
|
|
21
|
+
const headerProps = {
|
|
22
|
+
name: {
|
|
23
|
+
type: 'string',
|
|
24
|
+
title: 'Name',
|
|
25
|
+
description: 'Machine name. snake_case, used as the URL path and DB key.',
|
|
26
|
+
pattern: '^[a-z_][a-z0-9_]*$',
|
|
27
|
+
},
|
|
28
|
+
label: {
|
|
29
|
+
type: 'string',
|
|
30
|
+
title: 'Label',
|
|
31
|
+
description: 'Human-readable display name.',
|
|
32
|
+
},
|
|
33
|
+
description: {
|
|
34
|
+
type: 'string',
|
|
35
|
+
title: 'Description',
|
|
36
|
+
format: 'multiline',
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
/** Per-type fallback schemas. */
|
|
40
|
+
const SCHEMAS = {
|
|
41
|
+
role: {
|
|
42
|
+
type: 'object',
|
|
43
|
+
title: 'Role',
|
|
44
|
+
required: ['name'],
|
|
45
|
+
properties: {
|
|
46
|
+
...headerProps,
|
|
47
|
+
level: {
|
|
48
|
+
type: 'string',
|
|
49
|
+
title: 'Level',
|
|
50
|
+
description: 'Coarse seniority tier (used by UI / sort order).',
|
|
51
|
+
enum: ['member', 'lead', 'manager', 'director', 'executive', 'admin'],
|
|
52
|
+
},
|
|
53
|
+
parentRole: {
|
|
54
|
+
type: 'string',
|
|
55
|
+
title: 'Parent Role',
|
|
56
|
+
description: 'Optional parent role name for hierarchy.',
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
profile: {
|
|
61
|
+
type: 'object',
|
|
62
|
+
title: 'Profile',
|
|
63
|
+
required: ['name'],
|
|
64
|
+
properties: {
|
|
65
|
+
...headerProps,
|
|
66
|
+
isProfile: { type: 'boolean', title: 'Is profile', description: 'Profiles are mutually exclusive (one per user).' },
|
|
67
|
+
objects: {
|
|
68
|
+
type: 'object',
|
|
69
|
+
title: 'Object Permissions',
|
|
70
|
+
description: 'Edit this via the Permission Matrix tab for a row-by-row UI.',
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
permission: {
|
|
75
|
+
type: 'object',
|
|
76
|
+
title: 'Permission Set',
|
|
77
|
+
required: ['name'],
|
|
78
|
+
properties: {
|
|
79
|
+
...headerProps,
|
|
80
|
+
isProfile: { type: 'boolean', title: 'Is profile' },
|
|
81
|
+
objects: { type: 'object', title: 'Object Permissions' },
|
|
82
|
+
fields: { type: 'object', title: 'Field Permissions' },
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
translation: {
|
|
86
|
+
type: 'object',
|
|
87
|
+
title: 'Translation Bundle',
|
|
88
|
+
required: ['name'],
|
|
89
|
+
properties: {
|
|
90
|
+
name: { ...headerProps.name, description: 'Bundle id (e.g. zh_CN, en_US, package-locale).' },
|
|
91
|
+
label: headerProps.label,
|
|
92
|
+
description: headerProps.description,
|
|
93
|
+
locale: {
|
|
94
|
+
type: 'string',
|
|
95
|
+
title: 'Locale',
|
|
96
|
+
description: 'BCP-47 tag, e.g. en, zh-Hans, ja-JP.',
|
|
97
|
+
},
|
|
98
|
+
strings: {
|
|
99
|
+
type: 'object',
|
|
100
|
+
title: 'Strings',
|
|
101
|
+
description: 'Key → translated string map.',
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
tool: {
|
|
106
|
+
type: 'object',
|
|
107
|
+
title: 'AI Tool',
|
|
108
|
+
required: ['name'],
|
|
109
|
+
properties: {
|
|
110
|
+
...headerProps,
|
|
111
|
+
kind: {
|
|
112
|
+
type: 'string',
|
|
113
|
+
title: 'Kind',
|
|
114
|
+
enum: ['function', 'http', 'mcp'],
|
|
115
|
+
},
|
|
116
|
+
inputSchema: { type: 'object', title: 'Input Schema (JSON Schema)' },
|
|
117
|
+
outputSchema: { type: 'object', title: 'Output Schema (JSON Schema)' },
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
skill: {
|
|
121
|
+
type: 'object',
|
|
122
|
+
title: 'AI Skill',
|
|
123
|
+
required: ['name'],
|
|
124
|
+
properties: {
|
|
125
|
+
...headerProps,
|
|
126
|
+
instructions: { type: 'string', title: 'Instructions', format: 'multiline' },
|
|
127
|
+
tools: { type: 'array', title: 'Tools', items: { type: 'string' } },
|
|
128
|
+
},
|
|
129
|
+
},
|
|
130
|
+
app: {
|
|
131
|
+
type: 'object',
|
|
132
|
+
title: 'Application',
|
|
133
|
+
required: ['name'],
|
|
134
|
+
properties: {
|
|
135
|
+
...headerProps,
|
|
136
|
+
icon: { type: 'string', title: 'Icon' },
|
|
137
|
+
navigation: { type: 'array', title: 'Navigation', items: { type: 'object' } },
|
|
138
|
+
},
|
|
139
|
+
},
|
|
140
|
+
page: {
|
|
141
|
+
type: 'object',
|
|
142
|
+
title: 'Page',
|
|
143
|
+
required: ['name'],
|
|
144
|
+
properties: {
|
|
145
|
+
...headerProps,
|
|
146
|
+
route: { type: 'string', title: 'Route', description: 'URL path under the app.' },
|
|
147
|
+
layout: { type: 'string', title: 'Layout' },
|
|
148
|
+
},
|
|
149
|
+
},
|
|
150
|
+
view: {
|
|
151
|
+
type: 'object',
|
|
152
|
+
title: 'View',
|
|
153
|
+
required: ['name'],
|
|
154
|
+
properties: {
|
|
155
|
+
...headerProps,
|
|
156
|
+
type: {
|
|
157
|
+
type: 'string',
|
|
158
|
+
title: 'View Type',
|
|
159
|
+
enum: ['grid', 'kanban', 'calendar', 'gantt', 'simple', 'tabbed', 'wizard', 'split', 'drawer', 'modal'],
|
|
160
|
+
},
|
|
161
|
+
},
|
|
162
|
+
},
|
|
163
|
+
dashboard: {
|
|
164
|
+
type: 'object',
|
|
165
|
+
title: 'Dashboard',
|
|
166
|
+
required: ['name'],
|
|
167
|
+
properties: {
|
|
168
|
+
...headerProps,
|
|
169
|
+
widgets: { type: 'array', title: 'Widgets', items: { type: 'object' } },
|
|
170
|
+
},
|
|
171
|
+
},
|
|
172
|
+
report: {
|
|
173
|
+
type: 'object',
|
|
174
|
+
title: 'Report',
|
|
175
|
+
required: ['name'],
|
|
176
|
+
properties: {
|
|
177
|
+
...headerProps,
|
|
178
|
+
object: { type: 'string', title: 'Object' },
|
|
179
|
+
filters: { type: 'array', title: 'Filters', items: { type: 'object' } },
|
|
180
|
+
},
|
|
181
|
+
},
|
|
182
|
+
email_template: {
|
|
183
|
+
type: 'object',
|
|
184
|
+
title: 'Email Template',
|
|
185
|
+
required: ['name'],
|
|
186
|
+
properties: {
|
|
187
|
+
...headerProps,
|
|
188
|
+
subject: { type: 'string', title: 'Subject' },
|
|
189
|
+
body: { type: 'string', title: 'Body (HTML / text)', format: 'multiline' },
|
|
190
|
+
from: { type: 'string', title: 'From address' },
|
|
191
|
+
},
|
|
192
|
+
},
|
|
193
|
+
};
|
|
194
|
+
/**
|
|
195
|
+
* Register fallback schemas for every writable type. Idempotent — uses
|
|
196
|
+
* `registerMetadataResource` so any prior registration (e.g. a bespoke
|
|
197
|
+
* EditPage from PermissionMatrixEditor) is merged via the engine.
|
|
198
|
+
*/
|
|
199
|
+
export function registerDefaultMetadataSchemas() {
|
|
200
|
+
for (const [type, schema] of Object.entries(SCHEMAS)) {
|
|
201
|
+
registerMetadataResource({
|
|
202
|
+
type,
|
|
203
|
+
defaultSchema: schema,
|
|
204
|
+
fieldOrder: ['name', 'label', 'description'],
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Metadata admin i18n bundle (Phase 3f).
|
|
3
|
+
*
|
|
4
|
+
* Lightweight static label table for the 27 built-in metadata types,
|
|
5
|
+
* plus a tiny `t()` helper for engine UI strings.
|
|
6
|
+
*
|
|
7
|
+
* Why not i18next? The engine already consumes `label` from the
|
|
8
|
+
* server's `/meta/types` response (which is sourced from
|
|
9
|
+
* `DEFAULT_METADATA_TYPE_REGISTRY`). This bundle exists as a fallback
|
|
10
|
+
* for environments without translation bundles configured, and as the
|
|
11
|
+
* single source of truth for Chinese labels until the platform's
|
|
12
|
+
* `setup.translation.ts` ships zh-CN coverage.
|
|
13
|
+
*
|
|
14
|
+
* Usage:
|
|
15
|
+
* import { translateMetadataType, t } from './i18n';
|
|
16
|
+
* translateMetadataType('view', 'zh-CN') // → '视图'
|
|
17
|
+
* t('engine.directory.title', 'zh-CN') // → '元数据'
|
|
18
|
+
*
|
|
19
|
+
* The DirectoryPage / PageShell call these to localise headings when
|
|
20
|
+
* the consumer hasn't wired the global i18n provider.
|
|
21
|
+
*/
|
|
22
|
+
export type SupportedLocale = 'en-US' | 'zh-CN';
|
|
23
|
+
export declare function translateMetadataType(type: string, locale?: SupportedLocale | string, fallback?: string): string;
|
|
24
|
+
export declare function translateMetadataDomain(domain: string, locale?: SupportedLocale | string): string;
|
|
25
|
+
export declare function t(key: string, locale?: SupportedLocale | string): string;
|
|
26
|
+
/**
|
|
27
|
+
* Format a translated string with `{token}` placeholders.
|
|
28
|
+
*
|
|
29
|
+
* tFormat('engine.directory.description', 'zh-CN', { count: 28, writable: 4 })
|
|
30
|
+
*/
|
|
31
|
+
export declare function tFormat(key: string, locale: SupportedLocale | string | undefined, vars: Record<string, string | number>): string;
|
|
32
|
+
/** Returns the locale string most browsers report (matches navigator.language). */
|
|
33
|
+
export declare function detectLocale(): SupportedLocale;
|