@object-ui/app-shell 6.0.1 → 6.0.2
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 +25 -0
- package/dist/console/AppContent.js +1 -1
- package/dist/console/home/HomeLayout.d.ts +6 -1
- package/dist/console/home/HomeLayout.js +2 -2
- package/dist/console/marketplace/MarketplacePackagePage.js +21 -10
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/index.js +1 -0
- package/dist/hooks/useChatConversation.d.ts +31 -0
- package/dist/hooks/useChatConversation.js +188 -0
- package/dist/layout/ConsoleFloatingChatbot.d.ts +7 -1
- package/dist/layout/ConsoleFloatingChatbot.js +16 -7
- package/dist/layout/ConsoleLayout.d.ts +6 -1
- package/dist/layout/ConsoleLayout.js +2 -2
- package/dist/views/RecordDetailView.js +2 -1
- package/package.json +25 -25
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,30 @@
|
|
|
1
1
|
# @object-ui/app-shell — Changelog
|
|
2
2
|
|
|
3
|
+
## 6.0.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- d0e63f1: Migrate AI chat history from localStorage to the server-backed
|
|
8
|
+
`ai_conversations` / `ai_messages` REST API. The studio `AiChatPanel`,
|
|
9
|
+
the console `ConsoleFloatingChatbot`, and any other consumer of the new
|
|
10
|
+
`useChatConversation` hook (in `@object-ui/app-shell`) now resolve a
|
|
11
|
+
durable conversation id per signed-in user, hydrate prior messages on
|
|
12
|
+
mount, and rotate the conversation on reset. The previous
|
|
13
|
+
`objectstack:ai-chat-messages` localStorage entries are no longer read
|
|
14
|
+
or written.
|
|
15
|
+
- @object-ui/types@6.0.2
|
|
16
|
+
- @object-ui/core@6.0.2
|
|
17
|
+
- @object-ui/i18n@6.0.2
|
|
18
|
+
- @object-ui/react@6.0.2
|
|
19
|
+
- @object-ui/components@6.0.2
|
|
20
|
+
- @object-ui/fields@6.0.2
|
|
21
|
+
- @object-ui/layout@6.0.2
|
|
22
|
+
- @object-ui/data-objectstack@6.0.2
|
|
23
|
+
- @object-ui/auth@6.0.2
|
|
24
|
+
- @object-ui/permissions@6.0.2
|
|
25
|
+
- @object-ui/collaboration@6.0.2
|
|
26
|
+
- @object-ui/providers@6.0.2
|
|
27
|
+
|
|
3
28
|
## 6.0.1
|
|
4
29
|
|
|
5
30
|
### Patch Changes
|
|
@@ -237,7 +237,7 @@ export function AppContent({ extraRoutes, extraRoutesNoApp } = {}) {
|
|
|
237
237
|
const expressionUser = user
|
|
238
238
|
? { name: user.name, email: user.email, role: user.role ?? 'user' }
|
|
239
239
|
: { name: 'Anonymous', email: '', role: 'guest' };
|
|
240
|
-
return (_jsxs(ExpressionProvider, { user: expressionUser, app: activeApp, data: {}, children: [_jsx(NavigationSyncEffect, {}), _jsxs(ConsoleLayout, { activeAppName: activeApp.name, activeApp: activeApp, onAppChange: handleAppChange, objects: allObjects, connectionState: connectionState, children: [_jsx(CommandPalette, { apps: apps, activeApp: activeApp, objects: allObjects, onAppChange: handleAppChange, dataSource: dataSource }), _jsx(KeyboardShortcutsDialog, {}), _jsx(OnboardingWalkthrough, {}), _jsx(ErrorBoundary, { children: _jsx(Suspense, { fallback: _jsx(LoadingScreen, {}), children: _jsx(RouteFader, { children: _jsxs(Routes, { children: [_jsx(Route, { path: "/", element: _jsx(Navigate, { to: resolveLandingRoute(activeApp), replace: true }) }), _jsx(Route, { path: ":objectName", element: _jsx(ObjectView, { dataSource: dataSource, objects: allObjects, onEdit: handleEdit, externalRefreshKey: refreshKey }) }), _jsx(Route, { path: ":objectName/new", element: _jsx(RecordFormPage, { mode: "create" }) }), _jsx(Route, { path: ":objectName/view/:viewId", element: _jsx(ObjectView, { dataSource: dataSource, objects: allObjects, onEdit: handleEdit, externalRefreshKey: refreshKey }) }), _jsx(Route, { path: ":objectName/record/:recordId", element: _jsx(RecordDetailView, { dataSource: dataSource, objects: allObjects, onEdit: handleEdit }, refreshKey) }), _jsx(Route, { path: ":objectName/record/:recordId/edit", element: _jsx(RecordFormPage, { mode: "edit" }) }), _jsx(Route, { path: "dashboard/:dashboardName", element: _jsx(DashboardView, { dataSource: dataSource }) }), _jsx(Route, { path: "report/:reportName", element: _jsx(ReportView, { dataSource: dataSource }) }), _jsx(Route, { path: "page/:pageName", element: _jsx(PageView, {}) }), _jsx(Route, { path: "design/page/:pageName", element: _jsx(PageDesignPage, {}) }), _jsx(Route, { path: "design/dashboard/:dashboardName", element: _jsx(DashboardDesignPage, {}) }), _jsx(Route, { path: "search", element: _jsx(SearchResultsPage, {}) }), _jsx(Route, { path: "create-app", element: _jsx(CreateAppPage, {}) }), _jsx(Route, { path: "edit-app/:editAppName", element: _jsx(EditAppPage, {}) }), _jsx(Route, { path: "system/marketplace", element: _jsx(MarketplacePage, {}) }), _jsx(Route, { path: "system/marketplace/installed", element: _jsx(MarketplaceInstalledPage, {}) }), _jsx(Route, { path: "system/marketplace/:packageId", element: _jsx(MarketplacePackagePage, {}) }), extraRoutes, _jsx(Route, { path: ":objectName/:maybeRecordId", element: _jsx(ShorthandRecordRedirect, {}) }), _jsx(Route, { path: "*", element: _jsx(RouteNotFound, {}) })] }) }) }) }), currentObjectDef && (_jsx(ModalForm, { schema: {
|
|
240
|
+
return (_jsxs(ExpressionProvider, { user: expressionUser, app: activeApp, data: {}, children: [_jsx(NavigationSyncEffect, {}), _jsxs(ConsoleLayout, { activeAppName: activeApp.name, activeApp: activeApp, onAppChange: handleAppChange, objects: allObjects, connectionState: connectionState, userId: user?.id, children: [_jsx(CommandPalette, { apps: apps, activeApp: activeApp, objects: allObjects, onAppChange: handleAppChange, dataSource: dataSource }), _jsx(KeyboardShortcutsDialog, {}), _jsx(OnboardingWalkthrough, {}), _jsx(ErrorBoundary, { children: _jsx(Suspense, { fallback: _jsx(LoadingScreen, {}), children: _jsx(RouteFader, { children: _jsxs(Routes, { children: [_jsx(Route, { path: "/", element: _jsx(Navigate, { to: resolveLandingRoute(activeApp), replace: true }) }), _jsx(Route, { path: ":objectName", element: _jsx(ObjectView, { dataSource: dataSource, objects: allObjects, onEdit: handleEdit, externalRefreshKey: refreshKey }) }), _jsx(Route, { path: ":objectName/new", element: _jsx(RecordFormPage, { mode: "create" }) }), _jsx(Route, { path: ":objectName/view/:viewId", element: _jsx(ObjectView, { dataSource: dataSource, objects: allObjects, onEdit: handleEdit, externalRefreshKey: refreshKey }) }), _jsx(Route, { path: ":objectName/record/:recordId", element: _jsx(RecordDetailView, { dataSource: dataSource, objects: allObjects, onEdit: handleEdit }, refreshKey) }), _jsx(Route, { path: ":objectName/record/:recordId/edit", element: _jsx(RecordFormPage, { mode: "edit" }) }), _jsx(Route, { path: "dashboard/:dashboardName", element: _jsx(DashboardView, { dataSource: dataSource }) }), _jsx(Route, { path: "report/:reportName", element: _jsx(ReportView, { dataSource: dataSource }) }), _jsx(Route, { path: "page/:pageName", element: _jsx(PageView, {}) }), _jsx(Route, { path: "design/page/:pageName", element: _jsx(PageDesignPage, {}) }), _jsx(Route, { path: "design/dashboard/:dashboardName", element: _jsx(DashboardDesignPage, {}) }), _jsx(Route, { path: "search", element: _jsx(SearchResultsPage, {}) }), _jsx(Route, { path: "create-app", element: _jsx(CreateAppPage, {}) }), _jsx(Route, { path: "edit-app/:editAppName", element: _jsx(EditAppPage, {}) }), _jsx(Route, { path: "system/marketplace", element: _jsx(MarketplacePage, {}) }), _jsx(Route, { path: "system/marketplace/installed", element: _jsx(MarketplaceInstalledPage, {}) }), _jsx(Route, { path: "system/marketplace/:packageId", element: _jsx(MarketplacePackagePage, {}) }), extraRoutes, _jsx(Route, { path: ":objectName/:maybeRecordId", element: _jsx(ShorthandRecordRedirect, {}) }), _jsx(Route, { path: "*", element: _jsx(RouteNotFound, {}) })] }) }) }) }), currentObjectDef && (_jsx(ModalForm, { schema: {
|
|
241
241
|
type: 'object-form',
|
|
242
242
|
formType: 'modal',
|
|
243
243
|
objectName: currentObjectDef.name,
|
|
@@ -10,6 +10,11 @@
|
|
|
10
10
|
import React from 'react';
|
|
11
11
|
interface HomeLayoutProps {
|
|
12
12
|
children: React.ReactNode;
|
|
13
|
+
/**
|
|
14
|
+
* Signed-in user id. Forwarded to the floating chatbot so it can hydrate
|
|
15
|
+
* server-backed conversation history.
|
|
16
|
+
*/
|
|
17
|
+
userId?: string;
|
|
13
18
|
}
|
|
14
|
-
export declare function HomeLayout({ children }: HomeLayoutProps): import("react/jsx-runtime").JSX.Element;
|
|
19
|
+
export declare function HomeLayout({ children, userId }: HomeLayoutProps): import("react/jsx-runtime").JSX.Element;
|
|
15
20
|
export {};
|
|
@@ -15,7 +15,7 @@ import { useDiscovery } from '@object-ui/react';
|
|
|
15
15
|
// Lightweight FAB stub — the heavy chat chunk graph only downloads on
|
|
16
16
|
// first hover/click. See ../../layout/ConsoleChatbotFab.tsx.
|
|
17
17
|
import { ConsoleChatbotFab } from '../../layout/ConsoleChatbotFab';
|
|
18
|
-
export function HomeLayout({ children }) {
|
|
18
|
+
export function HomeLayout({ children, userId }) {
|
|
19
19
|
const { setContext } = useNavigationContext();
|
|
20
20
|
const { isAiEnabled } = useDiscovery();
|
|
21
21
|
// Render the chatbot whenever AI is reachable. If the developer has explicitly
|
|
@@ -26,5 +26,5 @@ export function HomeLayout({ children }) {
|
|
|
26
26
|
useEffect(() => {
|
|
27
27
|
setContext('home');
|
|
28
28
|
}, [setContext]);
|
|
29
|
-
return (_jsxs("div", { className: "flex min-h-svh w-full flex-col bg-background", "data-testid": "home-layout", children: [_jsx("header", { className: "sticky top-0 z-30 flex h-14 w-full shrink-0 items-center gap-2 border-b bg-background px-2 sm:px-4", children: _jsx(AppHeader, { variant: "home" }) }), _jsx("main", { className: "flex-1 min-w-0 overflow-auto pb-20 sm:pb-0", children: children }), showChatbot && _jsx(ConsoleChatbotFab, { appLabel: "Workspace", objects: [] })] }));
|
|
29
|
+
return (_jsxs("div", { className: "flex min-h-svh w-full flex-col bg-background", "data-testid": "home-layout", children: [_jsx("header", { className: "sticky top-0 z-30 flex h-14 w-full shrink-0 items-center gap-2 border-b bg-background px-2 sm:px-4", children: _jsx(AppHeader, { variant: "home" }) }), _jsx("main", { className: "flex-1 min-w-0 overflow-auto pb-20 sm:pb-0", children: children }), showChatbot && _jsx(ConsoleChatbotFab, { appLabel: "Workspace", objects: [], userId: userId })] }));
|
|
30
30
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
/**
|
|
3
3
|
* Marketplace Package Detail Page.
|
|
4
4
|
*
|
|
@@ -7,8 +7,8 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
7
7
|
*/
|
|
8
8
|
import { useEffect, useState } from 'react';
|
|
9
9
|
import { useNavigate, useParams } from 'react-router-dom';
|
|
10
|
-
import { Button, Badge, Card, CardContent, CardHeader, CardTitle, Skeleton, Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, Label, Checkbox, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@object-ui/components';
|
|
11
|
-
import { ArrowLeft, ExternalLink, Download, AlertCircle, Package, Trash2 } from 'lucide-react';
|
|
10
|
+
import { Button, Badge, Card, CardContent, CardHeader, CardTitle, Skeleton, Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, Label, Checkbox, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem, } from '@object-ui/components';
|
|
11
|
+
import { ArrowLeft, ExternalLink, Download, AlertCircle, Package, Trash2, MoreHorizontal, CheckCircle2 } from 'lucide-react';
|
|
12
12
|
import { PackageIcon } from './PackageIcon';
|
|
13
13
|
import { MarkdownText } from './MarkdownText';
|
|
14
14
|
import { getMarketplacePackage, installPackage, installLocal, uninstallLocal, listLocalInstalls, listCloudEnvironments, listInstallableOrgIds, cloudInstallDeepLink, } from './marketplaceApi';
|
|
@@ -186,18 +186,29 @@ export function MarketplacePackagePage() {
|
|
|
186
186
|
}
|
|
187
187
|
};
|
|
188
188
|
if (loading) {
|
|
189
|
-
return (_jsxs("div", { className: "flex flex-col gap-6 p-4 sm:p-6", children: [_jsx(Skeleton, { className: "h-8 w-32" }), _jsx(Skeleton, { className: "h-16 w-full" }), _jsx(Skeleton, { className: "h-64 w-full" })] }));
|
|
189
|
+
return (_jsxs("div", { className: "mx-auto w-full max-w-6xl flex flex-col gap-6 p-4 sm:p-6", children: [_jsx(Skeleton, { className: "h-8 w-32" }), _jsx(Skeleton, { className: "h-16 w-full" }), _jsx(Skeleton, { className: "h-64 w-full" })] }));
|
|
190
190
|
}
|
|
191
191
|
if (error || !data) {
|
|
192
|
-
return (_jsxs("div", { className: "flex flex-col gap-6 p-4 sm:p-6", children: [_jsxs(Button, { variant: "ghost", size: "sm", 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-3 rounded-md border border-destructive/30 bg-destructive/5 p-4 text-sm", children: [_jsx(AlertCircle, { className: "h-4 w-4 mt-0.5 text-destructive", "aria-hidden": "true" }), _jsxs("div", { children: [_jsx("div", { className: "font-medium text-destructive", children: "Failed to load package" }), _jsx("div", { className: "text-muted-foreground mt-1", children: error ?? 'Not found.' })] })] })] }));
|
|
192
|
+
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", 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-3 rounded-md border border-destructive/30 bg-destructive/5 p-4 text-sm", children: [_jsx(AlertCircle, { className: "h-4 w-4 mt-0.5 text-destructive", "aria-hidden": "true" }), _jsxs("div", { children: [_jsx("div", { className: "font-medium text-destructive", children: "Failed to load package" }), _jsx("div", { className: "text-muted-foreground mt-1", children: error ?? 'Not found.' })] })] })] }));
|
|
193
193
|
}
|
|
194
194
|
const pkg = data.package;
|
|
195
195
|
const latestVersion = pkg.latest_version?.version ?? data.versions[0]?.version ?? null;
|
|
196
196
|
const localInstall = localInstalls.find((i) => i.manifestId === pkg.manifest_id) ?? null;
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
197
|
+
const supportsLocal = getRuntimeConfig().features.installLocal;
|
|
198
|
+
const primaryDisabled = !latestVersion || installingLocal || installing;
|
|
199
|
+
const primaryAction = supportsLocal
|
|
200
|
+
? {
|
|
201
|
+
label: installingLocal
|
|
202
|
+
? 'Working…'
|
|
203
|
+
: localInstall
|
|
204
|
+
? 'Reinstall'
|
|
205
|
+
: 'Install',
|
|
206
|
+
onClick: doInstallLocal,
|
|
207
|
+
}
|
|
208
|
+
: {
|
|
209
|
+
label: installing ? 'Installing…' : 'Install to cloud…',
|
|
210
|
+
onClick: openInstall,
|
|
211
|
+
};
|
|
212
|
+
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)
|
|
202
213
|
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' }))] })] }) })] }));
|
|
203
214
|
}
|
package/dist/hooks/index.d.ts
CHANGED
|
@@ -7,3 +7,4 @@ export { useRecentItems, type RecentItem } from './useRecentItems';
|
|
|
7
7
|
export { useRecordApprovals, type ApprovalProcessLite, type ApprovalRequestLite } from './useRecordApprovals';
|
|
8
8
|
export { useResponsiveSidebar } from './useResponsiveSidebar';
|
|
9
9
|
export { useTrackRouteAsRecent, type UseTrackRouteAsRecentOptions } from './useTrackRouteAsRecent';
|
|
10
|
+
export { useChatConversation, type HydratedUIMessage, type UseChatConversationOptions, type UseChatConversationReturn, } from './useChatConversation';
|
package/dist/hooks/index.js
CHANGED
|
@@ -7,3 +7,4 @@ export { useRecentItems } from './useRecentItems';
|
|
|
7
7
|
export { useRecordApprovals } from './useRecordApprovals';
|
|
8
8
|
export { useResponsiveSidebar } from './useResponsiveSidebar';
|
|
9
9
|
export { useTrackRouteAsRecent } from './useTrackRouteAsRecent';
|
|
10
|
+
export { useChatConversation, } from './useChatConversation';
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/** Minimal UIMessage shape compatible with `@ai-sdk/react`'s `useChat`. */
|
|
2
|
+
export interface HydratedUIMessage {
|
|
3
|
+
id: string;
|
|
4
|
+
role: 'user' | 'assistant' | 'system';
|
|
5
|
+
parts: Array<{
|
|
6
|
+
type: 'text';
|
|
7
|
+
text: string;
|
|
8
|
+
}>;
|
|
9
|
+
}
|
|
10
|
+
export interface UseChatConversationOptions {
|
|
11
|
+
/** Authenticated user id; hook is inert until this is defined. */
|
|
12
|
+
userId: string | undefined;
|
|
13
|
+
/**
|
|
14
|
+
* Optional scope (e.g. agent name) for keying separate conversations under
|
|
15
|
+
* the same user.
|
|
16
|
+
*/
|
|
17
|
+
scope?: string;
|
|
18
|
+
/**
|
|
19
|
+
* Base URL of the AI service (no trailing slash). Hook calls
|
|
20
|
+
* `${apiBase}/conversations[/...]`. Required.
|
|
21
|
+
*/
|
|
22
|
+
apiBase: string;
|
|
23
|
+
}
|
|
24
|
+
export interface UseChatConversationReturn {
|
|
25
|
+
conversationId: string | undefined;
|
|
26
|
+
initialMessages: HydratedUIMessage[];
|
|
27
|
+
isLoading: boolean;
|
|
28
|
+
/** Delete the current conversation + start a fresh one. */
|
|
29
|
+
reset: () => Promise<void>;
|
|
30
|
+
}
|
|
31
|
+
export declare function useChatConversation(options: UseChatConversationOptions): UseChatConversationReturn;
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
|
|
2
|
+
/**
|
|
3
|
+
* Server-backed AI chat conversation lifecycle.
|
|
4
|
+
*
|
|
5
|
+
* Binds a chat UI to an `ai_conversations` row owned by the signed-in user.
|
|
6
|
+
* On mount it tries the cached id (per user, optionally per scope); falls
|
|
7
|
+
* back to creating a fresh conversation when the cached one is gone
|
|
8
|
+
* (404/403).
|
|
9
|
+
*/
|
|
10
|
+
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
11
|
+
const CACHE_PREFIX = 'objectstack:ai-chat-conversation-id';
|
|
12
|
+
function cacheKey(userId, scope) {
|
|
13
|
+
return scope ? `${CACHE_PREFIX}:${userId}:${scope}` : `${CACHE_PREFIX}:${userId}`;
|
|
14
|
+
}
|
|
15
|
+
function readCache(key) {
|
|
16
|
+
try {
|
|
17
|
+
return localStorage.getItem(key) ?? undefined;
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
function writeCache(key, value) {
|
|
24
|
+
try {
|
|
25
|
+
if (value)
|
|
26
|
+
localStorage.setItem(key, value);
|
|
27
|
+
else
|
|
28
|
+
localStorage.removeItem(key);
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
/* ignore — private mode, quota, etc. */
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function contentToText(content) {
|
|
35
|
+
if (typeof content === 'string')
|
|
36
|
+
return content;
|
|
37
|
+
if (Array.isArray(content)) {
|
|
38
|
+
return content
|
|
39
|
+
.map((part) => {
|
|
40
|
+
if (typeof part === 'string')
|
|
41
|
+
return part;
|
|
42
|
+
if (part &&
|
|
43
|
+
typeof part === 'object' &&
|
|
44
|
+
'text' in part &&
|
|
45
|
+
typeof part.text === 'string') {
|
|
46
|
+
return part.text;
|
|
47
|
+
}
|
|
48
|
+
return '';
|
|
49
|
+
})
|
|
50
|
+
.join('');
|
|
51
|
+
}
|
|
52
|
+
return '';
|
|
53
|
+
}
|
|
54
|
+
function toUIMessages(rows) {
|
|
55
|
+
if (!rows)
|
|
56
|
+
return [];
|
|
57
|
+
const out = [];
|
|
58
|
+
rows.forEach((row, idx) => {
|
|
59
|
+
const role = row.role;
|
|
60
|
+
if (role !== 'user' && role !== 'assistant' && role !== 'system')
|
|
61
|
+
return;
|
|
62
|
+
const text = contentToText(row.content);
|
|
63
|
+
if (!text)
|
|
64
|
+
return;
|
|
65
|
+
out.push({
|
|
66
|
+
id: row.id ?? `msg-${idx}`,
|
|
67
|
+
role,
|
|
68
|
+
parts: [{ type: 'text', text }],
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
return out;
|
|
72
|
+
}
|
|
73
|
+
async function fetchConversation(apiBase, id) {
|
|
74
|
+
const res = await fetch(`${apiBase}/conversations/${encodeURIComponent(id)}`, {
|
|
75
|
+
credentials: 'include',
|
|
76
|
+
});
|
|
77
|
+
if (res.status === 404 || res.status === 403)
|
|
78
|
+
return null;
|
|
79
|
+
if (!res.ok)
|
|
80
|
+
throw new Error(`GET conversation failed: ${res.status}`);
|
|
81
|
+
return (await res.json());
|
|
82
|
+
}
|
|
83
|
+
async function createConversation(apiBase) {
|
|
84
|
+
const res = await fetch(`${apiBase}/conversations`, {
|
|
85
|
+
method: 'POST',
|
|
86
|
+
credentials: 'include',
|
|
87
|
+
headers: { 'Content-Type': 'application/json' },
|
|
88
|
+
body: '{}',
|
|
89
|
+
});
|
|
90
|
+
if (!res.ok)
|
|
91
|
+
throw new Error(`POST conversation failed: ${res.status}`);
|
|
92
|
+
return (await res.json());
|
|
93
|
+
}
|
|
94
|
+
async function deleteConversation(apiBase, id) {
|
|
95
|
+
await fetch(`${apiBase}/conversations/${encodeURIComponent(id)}`, {
|
|
96
|
+
method: 'DELETE',
|
|
97
|
+
credentials: 'include',
|
|
98
|
+
}).catch(() => {
|
|
99
|
+
/* best-effort */
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
export function useChatConversation(options) {
|
|
103
|
+
const { userId, scope, apiBase } = options;
|
|
104
|
+
const [conversationId, setConversationId] = useState(undefined);
|
|
105
|
+
const [initialMessages, setInitialMessages] = useState([]);
|
|
106
|
+
const [isLoading, setIsLoading] = useState(Boolean(userId));
|
|
107
|
+
const mountedRef = useRef(true);
|
|
108
|
+
useEffect(() => {
|
|
109
|
+
mountedRef.current = true;
|
|
110
|
+
return () => {
|
|
111
|
+
mountedRef.current = false;
|
|
112
|
+
};
|
|
113
|
+
}, []);
|
|
114
|
+
useEffect(() => {
|
|
115
|
+
if (!userId) {
|
|
116
|
+
setConversationId(undefined);
|
|
117
|
+
setInitialMessages([]);
|
|
118
|
+
setIsLoading(false);
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
let cancelled = false;
|
|
122
|
+
const key = cacheKey(userId, scope);
|
|
123
|
+
setIsLoading(true);
|
|
124
|
+
(async () => {
|
|
125
|
+
try {
|
|
126
|
+
const cached = readCache(key);
|
|
127
|
+
if (cached) {
|
|
128
|
+
const existing = await fetchConversation(apiBase, cached);
|
|
129
|
+
if (cancelled)
|
|
130
|
+
return;
|
|
131
|
+
if (existing) {
|
|
132
|
+
setConversationId(existing.id);
|
|
133
|
+
setInitialMessages(toUIMessages(existing.messages));
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
writeCache(key, undefined);
|
|
137
|
+
}
|
|
138
|
+
const fresh = await createConversation(apiBase);
|
|
139
|
+
if (cancelled)
|
|
140
|
+
return;
|
|
141
|
+
writeCache(key, fresh.id);
|
|
142
|
+
setConversationId(fresh.id);
|
|
143
|
+
setInitialMessages(toUIMessages(fresh.messages));
|
|
144
|
+
}
|
|
145
|
+
catch {
|
|
146
|
+
if (!cancelled) {
|
|
147
|
+
setConversationId(undefined);
|
|
148
|
+
setInitialMessages([]);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
finally {
|
|
152
|
+
if (!cancelled)
|
|
153
|
+
setIsLoading(false);
|
|
154
|
+
}
|
|
155
|
+
})();
|
|
156
|
+
return () => {
|
|
157
|
+
cancelled = true;
|
|
158
|
+
};
|
|
159
|
+
}, [userId, scope, apiBase]);
|
|
160
|
+
const reset = useCallback(async () => {
|
|
161
|
+
if (!userId)
|
|
162
|
+
return;
|
|
163
|
+
const key = cacheKey(userId, scope);
|
|
164
|
+
setIsLoading(true);
|
|
165
|
+
try {
|
|
166
|
+
if (conversationId)
|
|
167
|
+
await deleteConversation(apiBase, conversationId);
|
|
168
|
+
writeCache(key, undefined);
|
|
169
|
+
const fresh = await createConversation(apiBase);
|
|
170
|
+
writeCache(key, fresh.id);
|
|
171
|
+
if (!mountedRef.current)
|
|
172
|
+
return;
|
|
173
|
+
setConversationId(fresh.id);
|
|
174
|
+
setInitialMessages([]);
|
|
175
|
+
}
|
|
176
|
+
catch {
|
|
177
|
+
if (mountedRef.current) {
|
|
178
|
+
setConversationId(undefined);
|
|
179
|
+
setInitialMessages([]);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
finally {
|
|
183
|
+
if (mountedRef.current)
|
|
184
|
+
setIsLoading(false);
|
|
185
|
+
}
|
|
186
|
+
}, [conversationId, userId, scope, apiBase]);
|
|
187
|
+
return { conversationId, initialMessages, isLoading, reset };
|
|
188
|
+
}
|
|
@@ -14,6 +14,12 @@ export interface ConsoleFloatingChatbotProps {
|
|
|
14
14
|
defaultAgent?: string;
|
|
15
15
|
/** Whether the floating panel should open immediately on mount. */
|
|
16
16
|
defaultOpen?: boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Authenticated user id. When provided, the chat hydrates from (and writes
|
|
19
|
+
* to) a server-backed `ai_conversations` row keyed by `userId` + agent.
|
|
20
|
+
* Inert until defined — the floating panel still works in local-only mode.
|
|
21
|
+
*/
|
|
22
|
+
userId?: string;
|
|
17
23
|
}
|
|
18
|
-
export default function ConsoleFloatingChatbot({ appLabel, objects, apiBase: apiBaseProp, defaultAgent: defaultAgentProp, defaultOpen, }: ConsoleFloatingChatbotProps): import("react/jsx-runtime").JSX.Element;
|
|
24
|
+
export default function ConsoleFloatingChatbot({ appLabel, objects, apiBase: apiBaseProp, defaultAgent: defaultAgentProp, defaultOpen, userId, }: ConsoleFloatingChatbotProps): import("react/jsx-runtime").JSX.Element;
|
|
19
25
|
export {};
|
|
@@ -14,6 +14,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
14
14
|
import React from 'react';
|
|
15
15
|
import { FloatingChatbot, useObjectChat, useAgents, useHitlInChat, } from '@object-ui/plugin-chatbot';
|
|
16
16
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@object-ui/components';
|
|
17
|
+
import { useChatConversation } from '../hooks';
|
|
17
18
|
const DEFAULT_AI_PATH = '/api/v1/ai';
|
|
18
19
|
function resolveApiBase(explicit) {
|
|
19
20
|
if (explicit)
|
|
@@ -25,7 +26,7 @@ function resolveApiBase(explicit) {
|
|
|
25
26
|
const serverUrl = env.VITE_SERVER_URL ?? '';
|
|
26
27
|
return `${serverUrl.replace(/\/$/, '')}${DEFAULT_AI_PATH}`;
|
|
27
28
|
}
|
|
28
|
-
function ChatbotInner({ appLabel, objects, agents, agentsLoading, agentsError, activeAgent, onAgentChange, chatApi, apiBase, defaultOpen = false, }) {
|
|
29
|
+
function ChatbotInner({ appLabel, objects, agents, agentsLoading, agentsError, activeAgent, onAgentChange, chatApi, apiBase, defaultOpen = false, conversationId, }) {
|
|
29
30
|
const objectNames = objects.map((o) => o.label || o.name).join(', ');
|
|
30
31
|
const activeAgentLabel = React.useMemo(() => {
|
|
31
32
|
const found = agents.find((a) => a.name === activeAgent);
|
|
@@ -36,7 +37,7 @@ function ChatbotInner({ appLabel, objects, agents, agentsLoading, agentsError, a
|
|
|
36
37
|
: `Hello! I'm your **${appLabel}** assistant. ${agentsError ? '(Backend unreachable — running in offline demo mode.)' : ''}`;
|
|
37
38
|
const { messages, isLoading, error, sendMessage, stop, reload, clear, } = useObjectChat({
|
|
38
39
|
api: chatApi,
|
|
39
|
-
conversationId
|
|
40
|
+
conversationId,
|
|
40
41
|
body: {
|
|
41
42
|
context: {
|
|
42
43
|
activeApp: appLabel,
|
|
@@ -85,7 +86,7 @@ function ChatbotInner({ appLabel, objects, agents, agentsLoading, agentsError, a
|
|
|
85
86
|
? 'Loading agents...'
|
|
86
87
|
: 'Ask anything...', onSendMessage: (content) => sendMessage(content), onClear: clear, onStop: isLoading ? stop : undefined, onReload: reload, isLoading: isLoading, error: error, enableMarkdown: true, onToolApprove: hitl.decide, toolDecisions: hitl.decisions, toolApproveLabel: "Approve & run", toolDenyLabel: "Reject", toolDenyReason: "Operator rejected from chat" }));
|
|
87
88
|
}
|
|
88
|
-
export default function ConsoleFloatingChatbot({ appLabel, objects, apiBase: apiBaseProp, defaultAgent: defaultAgentProp, defaultOpen = false, }) {
|
|
89
|
+
export default function ConsoleFloatingChatbot({ appLabel, objects, apiBase: apiBaseProp, defaultAgent: defaultAgentProp, defaultOpen = false, userId, }) {
|
|
89
90
|
const apiBase = React.useMemo(() => resolveApiBase(apiBaseProp), [apiBaseProp]);
|
|
90
91
|
const env = import.meta.env ?? {};
|
|
91
92
|
const envDefaultAgent = env.VITE_AI_DEFAULT_AGENT;
|
|
@@ -101,8 +102,16 @@ export default function ConsoleFloatingChatbot({ appLabel, objects, apiBase: api
|
|
|
101
102
|
const chatApi = activeAgent
|
|
102
103
|
? `${apiBase}/agents/${encodeURIComponent(activeAgent)}/chat`
|
|
103
104
|
: undefined;
|
|
104
|
-
//
|
|
105
|
-
//
|
|
106
|
-
//
|
|
107
|
-
|
|
105
|
+
// Server-backed conversation. Scoped by agent so each agent gets its own
|
|
106
|
+
// persistent history. Hook is inert until `userId` is provided; without it
|
|
107
|
+
// the FAB continues to work in local-only mode (no persistence).
|
|
108
|
+
const { conversationId } = useChatConversation({
|
|
109
|
+
userId,
|
|
110
|
+
scope: activeAgent,
|
|
111
|
+
apiBase,
|
|
112
|
+
});
|
|
113
|
+
// `key` forces a clean remount whenever the chat endpoint OR the resolved
|
|
114
|
+
// conversation id changes — required because `useObjectChat` locks its mode
|
|
115
|
+
// (api vs local) and its `conversationId` on first render.
|
|
116
|
+
return (_jsx(ChatbotInner, { appLabel: appLabel, objects: objects, agents: agents, agentsLoading: agentsLoading, agentsError: agentsError, activeAgent: activeAgent, onAgentChange: setActiveAgent, chatApi: chatApi, apiBase: apiBase, defaultOpen: defaultOpen, conversationId: conversationId }, `${chatApi ?? 'local'}:${conversationId ?? 'pending'}`));
|
|
108
117
|
}
|
|
@@ -16,7 +16,12 @@ interface ConsoleLayoutProps {
|
|
|
16
16
|
onAppChange: (name: string) => void;
|
|
17
17
|
objects: any[];
|
|
18
18
|
connectionState?: ConnectionState;
|
|
19
|
+
/**
|
|
20
|
+
* Signed-in user id. Forwarded to the floating chatbot so it can hydrate
|
|
21
|
+
* server-backed conversation history. Omit for unauthenticated/local-only.
|
|
22
|
+
*/
|
|
23
|
+
userId?: string;
|
|
19
24
|
}
|
|
20
25
|
/** Floating chatbot wired with useObjectChat for demo auto-response */
|
|
21
|
-
export declare function ConsoleLayout({ children, activeAppName, activeApp, onAppChange, objects, connectionState }: ConsoleLayoutProps): import("react/jsx-runtime").JSX.Element;
|
|
26
|
+
export declare function ConsoleLayout({ children, activeAppName, activeApp, onAppChange, objects, connectionState, userId, }: ConsoleLayoutProps): import("react/jsx-runtime").JSX.Element;
|
|
22
27
|
export {};
|
|
@@ -28,7 +28,7 @@ function ConsoleLayoutInner({ children }) {
|
|
|
28
28
|
}
|
|
29
29
|
/** Floating chatbot wired with useObjectChat for demo auto-response */
|
|
30
30
|
// (moved to ./ConsoleFloatingChatbot.tsx for code-splitting)
|
|
31
|
-
export function ConsoleLayout({ children, activeAppName, activeApp, onAppChange, objects, connectionState }) {
|
|
31
|
+
export function ConsoleLayout({ children, activeAppName, activeApp, onAppChange, objects, connectionState, userId, }) {
|
|
32
32
|
const appLabel = resolveI18nLabel(activeApp?.label) || activeAppName;
|
|
33
33
|
const { isAiEnabled } = useDiscovery();
|
|
34
34
|
// Trust an explicit `VITE_AI_BASE_URL` opt-in even when discovery reports
|
|
@@ -51,5 +51,5 @@ export function ConsoleLayout({ children, activeAppName, activeApp, onAppChange,
|
|
|
51
51
|
? `${resolveI18nLabel(activeApp.label)} — ObjectStack Console`
|
|
52
52
|
: undefined,
|
|
53
53
|
}
|
|
54
|
-
: undefined, children: [_jsx(ConsoleLayoutInner, { children: children }), showChatbot && (_jsx(ConsoleChatbotFab, { appLabel: appLabel, objects: objects }))] }) }));
|
|
54
|
+
: undefined, children: [_jsx(ConsoleLayoutInner, { children: children }), showChatbot && (_jsx(ConsoleChatbotFab, { appLabel: appLabel, objects: objects, userId: userId }))] }) }));
|
|
55
55
|
}
|
|
@@ -386,7 +386,8 @@ export function RecordDetailView({ dataSource, objects, onEdit, objectNameOverri
|
|
|
386
386
|
const shouldRefresh = action.refreshAfter !== false;
|
|
387
387
|
if (shouldRefresh)
|
|
388
388
|
setActionRefreshKey(k => k + 1);
|
|
389
|
-
|
|
389
|
+
const result = json?.data;
|
|
390
|
+
return { success: true, data: result, reload: shouldRefresh };
|
|
390
391
|
}
|
|
391
392
|
catch (error) {
|
|
392
393
|
return { success: false, error: error.message };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@object-ui/app-shell",
|
|
3
|
-
"version": "6.0.
|
|
3
|
+
"version": "6.0.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"description": "Minimal application shell for ObjectUI - framework-agnostic rendering engine",
|
|
@@ -28,35 +28,35 @@
|
|
|
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.
|
|
32
|
-
"@object-ui/collaboration": "6.0.
|
|
33
|
-
"@object-ui/components": "6.0.
|
|
34
|
-
"@object-ui/core": "6.0.
|
|
35
|
-
"@object-ui/data-objectstack": "6.0.
|
|
36
|
-
"@object-ui/fields": "6.0.
|
|
37
|
-
"@object-ui/i18n": "6.0.
|
|
38
|
-
"@object-ui/layout": "6.0.
|
|
39
|
-
"@object-ui/permissions": "6.0.
|
|
40
|
-
"@object-ui/providers": "6.0.
|
|
41
|
-
"@object-ui/react": "6.0.
|
|
42
|
-
"@object-ui/types": "6.0.
|
|
31
|
+
"@object-ui/auth": "6.0.2",
|
|
32
|
+
"@object-ui/collaboration": "6.0.2",
|
|
33
|
+
"@object-ui/components": "6.0.2",
|
|
34
|
+
"@object-ui/core": "6.0.2",
|
|
35
|
+
"@object-ui/data-objectstack": "6.0.2",
|
|
36
|
+
"@object-ui/fields": "6.0.2",
|
|
37
|
+
"@object-ui/i18n": "6.0.2",
|
|
38
|
+
"@object-ui/layout": "6.0.2",
|
|
39
|
+
"@object-ui/permissions": "6.0.2",
|
|
40
|
+
"@object-ui/providers": "6.0.2",
|
|
41
|
+
"@object-ui/react": "6.0.2",
|
|
42
|
+
"@object-ui/types": "6.0.2"
|
|
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.
|
|
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.
|
|
48
|
+
"@object-ui/plugin-calendar": "^6.0.2",
|
|
49
|
+
"@object-ui/plugin-charts": "^6.0.2",
|
|
50
|
+
"@object-ui/plugin-chatbot": "^6.0.2",
|
|
51
|
+
"@object-ui/plugin-dashboard": "^6.0.2",
|
|
52
|
+
"@object-ui/plugin-designer": "^6.0.2",
|
|
53
|
+
"@object-ui/plugin-detail": "^6.0.2",
|
|
54
|
+
"@object-ui/plugin-form": "^6.0.2",
|
|
55
|
+
"@object-ui/plugin-grid": "^6.0.2",
|
|
56
|
+
"@object-ui/plugin-kanban": "^6.0.2",
|
|
57
|
+
"@object-ui/plugin-list": "^6.0.2",
|
|
58
|
+
"@object-ui/plugin-report": "^6.0.2",
|
|
59
|
+
"@object-ui/plugin-view": "^6.0.2"
|
|
60
60
|
},
|
|
61
61
|
"devDependencies": {
|
|
62
62
|
"@types/node": "^25.9.0",
|