@object-ui/app-shell 6.0.4 → 6.1.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 CHANGED
@@ -1,5 +1,23 @@
1
1
  # @object-ui/app-shell — Changelog
2
2
 
3
+ ## 6.1.0
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [991b62d]
8
+ - @object-ui/core@6.1.0
9
+ - @object-ui/types@6.1.0
10
+ - @object-ui/components@6.1.0
11
+ - @object-ui/data-objectstack@6.1.0
12
+ - @object-ui/fields@6.1.0
13
+ - @object-ui/layout@6.1.0
14
+ - @object-ui/react@6.1.0
15
+ - @object-ui/auth@6.1.0
16
+ - @object-ui/collaboration@6.1.0
17
+ - @object-ui/permissions@6.1.0
18
+ - @object-ui/providers@6.1.0
19
+ - @object-ui/i18n@6.1.0
20
+
3
21
  ## 6.0.4
4
22
 
5
23
  ### Patch Changes
@@ -15,11 +15,13 @@ import { MarkdownText } from './MarkdownText';
15
15
  import { MarketplaceAccessDenied } from './MarketplaceAccessDenied';
16
16
  import { getMarketplacePackage, installPackage, installLocal, uninstallLocal, listLocalInstalls, listCloudEnvironments, listInstallableOrgIds, cloudInstallDeepLink, } from './marketplaceApi';
17
17
  import { getRuntimeConfig } from '../../runtime-config';
18
+ import { useMetadata } from '../../providers/MetadataProvider';
18
19
  export function MarketplacePackagePage() {
19
20
  const navigate = useNavigate();
20
21
  const { packageId, appName } = useParams();
21
22
  const isAdmin = useIsWorkspaceAdmin();
22
23
  const basePath = appName ? `/apps/${appName}` : '';
24
+ const { refresh: refreshMetadata } = useMetadata();
23
25
  const [data, setData] = useState(null);
24
26
  const [loading, setLoading] = useState(true);
25
27
  const [error, setError] = useState(null);
@@ -73,6 +75,18 @@ export function MarketplacePackagePage() {
73
75
  setInstallOpen(true);
74
76
  setInstallResult(null);
75
77
  setEnvsError(null);
78
+ // When the runtime tells us which env we're on (tenant subdomain
79
+ // like demo.objectos.app), skip the picker entirely — the operator's
80
+ // domain already identifies the target. Just open the dialog with
81
+ // the env pre-selected so the sample-data checkbox + Install button
82
+ // remain in place.
83
+ const currentEnvId = getRuntimeConfig().defaultEnvironmentId;
84
+ if (currentEnvId) {
85
+ setEnvsLoading(false);
86
+ setSelectedEnv(currentEnvId);
87
+ setEnvs([]);
88
+ return;
89
+ }
76
90
  setEnvsLoading(true);
77
91
  try {
78
92
  const [list, adminOrgIds] = await Promise.all([
@@ -137,9 +151,15 @@ export function MarketplacePackagePage() {
137
151
  setLocalResult(null);
138
152
  try {
139
153
  const result = await installLocal({ packageId });
154
+ try {
155
+ await refreshMetadata('app');
156
+ }
157
+ catch {
158
+ // Non-fatal: the install succeeded; the app list will refresh on next navigation.
159
+ }
140
160
  setLocalResult({
141
161
  ok: true,
142
- message: `Installed v${result.version} to this runtime. Refresh the console to see "${data?.package?.display_name ?? result.manifestId}" in the app switcher.`,
162
+ message: `Installed v${result.version} to this runtime. "${data?.package?.display_name ?? result.manifestId}" should now appear in the app switcher.`,
143
163
  });
144
164
  }
145
165
  catch (e) {
@@ -176,6 +196,12 @@ export function MarketplacePackagePage() {
176
196
  setLocalResult(null);
177
197
  try {
178
198
  await uninstallLocal(localInstall.manifestId);
199
+ try {
200
+ await refreshMetadata('app');
201
+ }
202
+ catch {
203
+ // Non-fatal.
204
+ }
179
205
  setLocalResult({
180
206
  ok: true,
181
207
  message: `Removed cached manifest for ${localInstall.manifestId}. Restart the runtime to fully unload the app from the running kernel.`,
@@ -215,5 +241,7 @@ export function MarketplacePackagePage() {
215
241
  if (!isAdmin)
216
242
  return _jsx(MarketplaceAccessDenied, {});
217
243
  return (_jsxs("div", { className: "mx-auto w-full max-w-6xl flex flex-col gap-6 p-4 sm:p-6", children: [_jsxs(Button, { variant: "ghost", size: "sm", className: "self-start -ml-2 text-muted-foreground hover:text-foreground", onClick: () => navigate(`${basePath}/system/marketplace`), children: [_jsx(ArrowLeft, { className: "h-4 w-4 mr-1.5", "aria-hidden": "true" }), "Back to marketplace"] }), _jsxs("div", { className: "flex items-start gap-5 flex-wrap sm:flex-nowrap rounded-2xl border bg-gradient-to-br from-primary/5 via-background to-background p-6 sm:p-8", children: [_jsx(PackageIcon, { iconUrl: pkg.icon_url, displayName: pkg.display_name, manifestId: pkg.manifest_id, className: "h-20 w-20 rounded-2xl shadow-sm ring-1 ring-border shrink-0", initialClassName: "text-3xl font-bold" }), _jsxs("div", { className: "flex-1 min-w-0", children: [_jsxs("div", { className: "flex items-center gap-2 flex-wrap", children: [_jsx("h1", { className: "text-2xl sm:text-3xl font-bold tracking-tight truncate", children: pkg.display_name || pkg.manifest_id }), pkg.homepage_url && (_jsxs("a", { href: pkg.homepage_url, target: "_blank", rel: "noopener noreferrer", className: "inline-flex items-center gap-1 text-xs text-muted-foreground hover:text-foreground transition-colors", title: "Homepage", children: [_jsx(ExternalLink, { className: "h-3.5 w-3.5", "aria-hidden": "true" }), _jsx("span", { className: "hidden sm:inline", children: "Homepage" })] }))] }), _jsxs("div", { className: "text-sm text-muted-foreground mt-2 flex flex-wrap items-center gap-1.5", children: [_jsx("code", { className: "font-mono text-xs px-1.5 py-0.5 rounded bg-muted", children: pkg.manifest_id }), latestVersion && _jsxs(Badge, { variant: "outline", children: ["v", latestVersion] }), pkg.publisher && pkg.publisher !== 'private' && (_jsx(Badge, { variant: pkg.publisher === 'objectstack' ? 'default' : 'secondary', children: pkg.publisher })), pkg.category && _jsx(Badge, { variant: "outline", children: pkg.category }), pkg.license && _jsx(Badge, { variant: "outline", className: "font-normal", children: pkg.license }), localInstall && (_jsxs(Badge, { variant: "default", className: "bg-green-600 hover:bg-green-600 gap-1", children: [_jsx(CheckCircle2, { className: "h-3 w-3", "aria-hidden": "true" }), "Installed \u00B7 v", localInstall.version] }))] }), pkg.description && (_jsx("p", { className: "text-sm sm:text-base text-foreground/80 mt-3 max-w-2xl leading-relaxed", children: pkg.description }))] }), _jsxs("div", { className: "flex items-center gap-2 shrink-0 self-start", children: [_jsxs(Button, { onClick: primaryAction.onClick, disabled: primaryDisabled, size: "lg", className: "min-w-[8rem]", children: [_jsx(Download, { className: "h-4 w-4 mr-1.5", "aria-hidden": "true" }), primaryAction.label] }), localInstall && (_jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsx(Button, { variant: "outline", size: "lg", className: "px-2.5", "aria-label": "More install options", children: _jsx(MoreHorizontal, { className: "h-4 w-4", "aria-hidden": "true" }) }) }), _jsx(DropdownMenuContent, { align: "end", className: "w-56", children: _jsxs(DropdownMenuItem, { onSelect: doUninstallLocal, disabled: installingLocal, className: "text-destructive focus:text-destructive", children: [_jsx(Trash2, { className: "h-4 w-4 mr-2", "aria-hidden": "true" }), "Uninstall from this runtime"] }) })] }))] })] }), localResult && (_jsxs("div", { role: "status", className: `flex items-start gap-2 rounded-md border p-3 text-sm whitespace-pre-wrap ${localResult.ok ? 'border-green-500/30 bg-green-500/5 text-green-700 dark:text-green-400' : 'border-destructive/30 bg-destructive/5 text-destructive'}`, children: [localResult.ok ? _jsx(CheckCircle2, { className: "h-4 w-4 mt-0.5 shrink-0", "aria-hidden": "true" }) : _jsx(AlertCircle, { className: "h-4 w-4 mt-0.5 shrink-0", "aria-hidden": "true" }), _jsx("div", { className: "flex-1", children: localResult.message }), _jsx("button", { type: "button", className: "text-xs underline opacity-60 hover:opacity-100", onClick: () => setLocalResult(null), children: "Dismiss" })] })), _jsxs("div", { className: "grid gap-4 lg:grid-cols-3", children: [_jsx("div", { className: "lg:col-span-2 space-y-4", children: _jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx(CardTitle, { className: "text-base", children: "About" }) }), _jsx(CardContent, { children: pkg.readme ? (_jsx(MarkdownText, { source: pkg.readme })) : (_jsx("p", { className: "text-sm text-muted-foreground", children: "No readme provided." })) })] }) }), _jsx("div", { className: "space-y-4", children: _jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx(CardTitle, { className: "text-base", children: "Versions" }) }), _jsx(CardContent, { children: data.versions.length === 0 ? (_jsx("p", { className: "text-sm text-muted-foreground", children: "No approved versions." })) : (_jsx("ul", { className: "space-y-2", children: data.versions.map((v) => (_jsxs("li", { className: "flex items-center justify-between gap-2 text-sm", children: [_jsxs("span", { className: "flex items-center gap-1.5", children: [_jsx(Package, { className: "h-3.5 w-3.5 text-muted-foreground", "aria-hidden": "true" }), _jsxs("code", { className: "font-mono", children: ["v", v.version] }), v.is_prerelease && _jsx(Badge, { variant: "outline", className: "text-xs", children: "pre" })] }), _jsx("span", { className: "text-xs text-muted-foreground", children: v.published_at ? new Date(v.published_at).toLocaleDateString() : '—' })] }, v.id))) })) })] }) })] }), _jsx(Dialog, { open: installOpen, onOpenChange: (o) => { setInstallOpen(o); if (!o)
218
- setInstallResult(null); }, children: _jsxs(DialogContent, { children: [_jsxs(DialogHeader, { children: [_jsxs(DialogTitle, { children: ["Install ", pkg.display_name || pkg.manifest_id] }), _jsx(DialogDescription, { children: "Choose an environment to install this app into. You need to be signed into ObjectStack Cloud." })] }), envsLoading ? (_jsx(Skeleton, { className: "h-10 w-full" })) : envsError ? (_jsxs("div", { className: "rounded-md border border-amber-500/30 bg-amber-500/5 p-3 text-sm space-y-2", children: [_jsxs("div", { className: "flex items-start gap-2", children: [_jsx(AlertCircle, { className: "h-4 w-4 mt-0.5 text-amber-600", "aria-hidden": "true" }), _jsx("div", { className: "flex-1", children: envsError })] }), _jsx("a", { href: cloudInstallDeepLink(pkg.id), target: "_blank", rel: "noopener noreferrer", children: _jsxs(Button, { variant: "outline", size: "sm", className: "w-full", children: [_jsx(ExternalLink, { className: "h-4 w-4 mr-1.5", "aria-hidden": "true" }), "Open on cloud"] }) })] })) : envs.length === 0 ? (_jsx("p", { className: "text-sm text-muted-foreground", children: "No environments found in your active organization." })) : (_jsxs("div", { className: "space-y-4", children: [_jsxs("div", { className: "space-y-1.5", children: [_jsx(Label, { htmlFor: "env-select", children: "Environment" }), _jsxs(Select, { value: selectedEnv, onValueChange: setSelectedEnv, children: [_jsx(SelectTrigger, { id: "env-select", children: _jsx(SelectValue, { placeholder: "Pick an environment" }) }), _jsx(SelectContent, { children: envs.map((e) => (_jsxs(SelectItem, { value: e.id, children: [e.display_name || e.hostname || e.id, e.plan && _jsxs("span", { className: "text-muted-foreground", children: [" \u00B7 ", e.plan] })] }, e.id))) })] })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Checkbox, { id: "seed", checked: seedSampleData, onCheckedChange: (c) => setSeedSampleData(c === true) }), _jsx(Label, { htmlFor: "seed", className: "text-sm font-normal cursor-pointer", children: "Include sample data" })] })] })), installResult && (_jsx("div", { className: `rounded-md border p-3 text-sm ${installResult.ok ? 'border-green-500/30 bg-green-500/5 text-green-700' : 'border-destructive/30 bg-destructive/5 text-destructive'}`, children: installResult.message })), _jsxs(DialogFooter, { children: [_jsx(Button, { variant: "outline", onClick: () => setInstallOpen(false), children: "Close" }), !envsError && (_jsx(Button, { onClick: doInstall, disabled: !selectedEnv || installing || installResult?.ok === true, children: installing ? 'Installing…' : 'Install' }))] })] }) })] }));
244
+ setInstallResult(null); }, children: _jsxs(DialogContent, { children: [_jsxs(DialogHeader, { children: [_jsxs(DialogTitle, { children: ["Install ", pkg.display_name || pkg.manifest_id] }), _jsx(DialogDescription, { children: getRuntimeConfig().defaultEnvironmentId
245
+ ? `Install into this environment (${typeof window !== 'undefined' ? window.location.host : ''}).`
246
+ : 'Choose an environment to install this app into. You need to be signed into ObjectStack Cloud.' })] }), envsLoading ? (_jsx(Skeleton, { className: "h-10 w-full" })) : envsError ? (_jsxs("div", { className: "rounded-md border border-amber-500/30 bg-amber-500/5 p-3 text-sm space-y-2", children: [_jsxs("div", { className: "flex items-start gap-2", children: [_jsx(AlertCircle, { className: "h-4 w-4 mt-0.5 text-amber-600", "aria-hidden": "true" }), _jsx("div", { className: "flex-1", children: envsError })] }), _jsx("a", { href: cloudInstallDeepLink(pkg.id), target: "_blank", rel: "noopener noreferrer", children: _jsxs(Button, { variant: "outline", size: "sm", className: "w-full", children: [_jsx(ExternalLink, { className: "h-4 w-4 mr-1.5", "aria-hidden": "true" }), "Open on cloud"] }) })] })) : getRuntimeConfig().defaultEnvironmentId ? (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Checkbox, { id: "seed", checked: seedSampleData, onCheckedChange: (c) => setSeedSampleData(c === true) }), _jsx(Label, { htmlFor: "seed", className: "text-sm font-normal cursor-pointer", children: "Include sample data" })] })) : envs.length === 0 ? (_jsx("p", { className: "text-sm text-muted-foreground", children: "No environments found in your active organization." })) : (_jsxs("div", { className: "space-y-4", children: [_jsxs("div", { className: "space-y-1.5", children: [_jsx(Label, { htmlFor: "env-select", children: "Environment" }), _jsxs(Select, { value: selectedEnv, onValueChange: setSelectedEnv, children: [_jsx(SelectTrigger, { id: "env-select", children: _jsx(SelectValue, { placeholder: "Pick an environment" }) }), _jsx(SelectContent, { children: envs.map((e) => (_jsxs(SelectItem, { value: e.id, children: [e.display_name || e.hostname || e.id, e.plan && _jsxs("span", { className: "text-muted-foreground", children: [" \u00B7 ", e.plan] })] }, e.id))) })] })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Checkbox, { id: "seed", checked: seedSampleData, onCheckedChange: (c) => setSeedSampleData(c === true) }), _jsx(Label, { htmlFor: "seed", className: "text-sm font-normal cursor-pointer", children: "Include sample data" })] })] })), installResult && (_jsx("div", { className: `rounded-md border p-3 text-sm ${installResult.ok ? 'border-green-500/30 bg-green-500/5 text-green-700' : 'border-destructive/30 bg-destructive/5 text-destructive'}`, children: installResult.message })), _jsxs(DialogFooter, { children: [_jsx(Button, { variant: "outline", onClick: () => setInstallOpen(false), children: "Close" }), !envsError && (_jsx(Button, { onClick: doInstall, disabled: !selectedEnv || installing || installResult?.ok === true, children: installing ? 'Installing…' : 'Install' }))] })] }) })] }));
219
247
  }
@@ -387,6 +387,18 @@ export function RecordDetailView({ dataSource, objects, onEdit, objectNameOverri
387
387
  if (shouldRefresh)
388
388
  setActionRefreshKey(k => k + 1);
389
389
  const result = json?.data;
390
+ // ── redirectUrl convention ────────────────────────────────────────
391
+ // A script-action handler can return `{ redirectUrl: 'https://…' }`
392
+ // to ask the UI to open the URL in a new tab. Used by the
393
+ // `sso_as_owner` action on sys_environment to drop the operator
394
+ // into the env runtime as its admin. Same pattern works for any
395
+ // future "deep link" style action.
396
+ if (result && typeof result === 'object' && typeof result.redirectUrl === 'string') {
397
+ try {
398
+ window.open(result.redirectUrl, '_blank', 'noopener,noreferrer');
399
+ }
400
+ catch { /* popup blocked — fall through */ }
401
+ }
390
402
  return { success: true, data: result, reload: shouldRefresh };
391
403
  }
392
404
  catch (error) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@object-ui/app-shell",
3
- "version": "6.0.4",
3
+ "version": "6.1.0",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "description": "Minimal application shell for ObjectUI - framework-agnostic rendering engine",
@@ -28,39 +28,39 @@
28
28
  "@sentry/react": "^8.55.2",
29
29
  "lucide-react": "^1.16.0",
30
30
  "sonner": "^2.0.7",
31
- "@object-ui/auth": "6.0.4",
32
- "@object-ui/collaboration": "6.0.4",
33
- "@object-ui/components": "6.0.4",
34
- "@object-ui/core": "6.0.4",
35
- "@object-ui/data-objectstack": "6.0.4",
36
- "@object-ui/fields": "6.0.4",
37
- "@object-ui/i18n": "6.0.4",
38
- "@object-ui/layout": "6.0.4",
39
- "@object-ui/permissions": "6.0.4",
40
- "@object-ui/providers": "6.0.4",
41
- "@object-ui/react": "6.0.4",
42
- "@object-ui/types": "6.0.4"
31
+ "@object-ui/auth": "6.1.0",
32
+ "@object-ui/collaboration": "6.1.0",
33
+ "@object-ui/components": "6.1.0",
34
+ "@object-ui/core": "6.1.0",
35
+ "@object-ui/data-objectstack": "6.1.0",
36
+ "@object-ui/fields": "6.1.0",
37
+ "@object-ui/i18n": "6.1.0",
38
+ "@object-ui/layout": "6.1.0",
39
+ "@object-ui/permissions": "6.1.0",
40
+ "@object-ui/providers": "6.1.0",
41
+ "@object-ui/react": "6.1.0",
42
+ "@object-ui/types": "6.1.0"
43
43
  },
44
44
  "peerDependencies": {
45
45
  "react": "^18.0.0 || ^19.0.0",
46
46
  "react-dom": "^18.0.0 || ^19.0.0",
47
47
  "react-router-dom": "^6.0.0 || ^7.0.0",
48
- "@object-ui/plugin-calendar": "^6.0.4",
49
- "@object-ui/plugin-charts": "^6.0.4",
50
- "@object-ui/plugin-chatbot": "^6.0.4",
51
- "@object-ui/plugin-dashboard": "^6.0.4",
52
- "@object-ui/plugin-designer": "^6.0.4",
53
- "@object-ui/plugin-detail": "^6.0.4",
54
- "@object-ui/plugin-form": "^6.0.4",
55
- "@object-ui/plugin-grid": "^6.0.4",
56
- "@object-ui/plugin-kanban": "^6.0.4",
57
- "@object-ui/plugin-list": "^6.0.4",
58
- "@object-ui/plugin-report": "^6.0.4",
59
- "@object-ui/plugin-view": "^6.0.4"
48
+ "@object-ui/plugin-calendar": "^6.1.0",
49
+ "@object-ui/plugin-charts": "^6.1.0",
50
+ "@object-ui/plugin-chatbot": "^6.1.0",
51
+ "@object-ui/plugin-dashboard": "^6.1.0",
52
+ "@object-ui/plugin-designer": "^6.1.0",
53
+ "@object-ui/plugin-detail": "^6.1.0",
54
+ "@object-ui/plugin-form": "^6.1.0",
55
+ "@object-ui/plugin-grid": "^6.1.0",
56
+ "@object-ui/plugin-kanban": "^6.1.0",
57
+ "@object-ui/plugin-list": "^6.1.0",
58
+ "@object-ui/plugin-report": "^6.1.0",
59
+ "@object-ui/plugin-view": "^6.1.0"
60
60
  },
61
61
  "devDependencies": {
62
- "@types/node": "^25.9.0",
63
- "@types/react": "19.2.14",
62
+ "@types/node": "^25.9.1",
63
+ "@types/react": "19.2.15",
64
64
  "@types/react-dom": "19.2.3",
65
65
  "react": "19.2.6",
66
66
  "react-dom": "19.2.6",