@agent-native/core 0.184.0 → 0.184.1-nightly-20260922203115
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/corpus/README.md +1 -1
- package/corpus/templates/analytics/changelog/2026-09-22-apps-start-with-an-app-shaped-skeleton-while-session-data-lo.md +6 -0
- package/corpus/templates/calendar/changelog/2026-09-22-apps-start-with-an-app-shaped-skeleton-while-session-data-lo.md +6 -0
- package/corpus/templates/clips/changelog/2026-09-22-apps-start-with-an-app-shaped-skeleton-while-session-data-lo.md +6 -0
- package/corpus/templates/content/AGENTS.md +1 -1
- package/corpus/templates/content/actions/_document-search-ranking.ts +191 -0
- package/corpus/templates/content/actions/search-documents.ts +184 -49
- package/corpus/templates/content/changelog/2026-09-21-search-ranks-title-matches-first.md +6 -0
- package/corpus/templates/content/docs/product/capabilities/content.knowledge.search.md +11 -8
- package/corpus/templates/design/.generated/bridge/editor-chrome.generated.ts +10 -3
- package/corpus/templates/design/.generated/bridge/nav.generated.ts +1 -0
- package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +67 -6
- package/corpus/templates/design/app/components/design/bridge/nav.bridge.ts +16 -0
- package/corpus/templates/design/app/components/design/multi-screen/board-surface-html.ts +26 -0
- package/corpus/templates/design/changelog/2026-09-22-apps-start-with-an-app-shaped-skeleton-while-session-data-lo.md +6 -0
- package/corpus/templates/design/changelog/2026-09-22-blocks-can-be-selected-with-one-click-inside-screens.md +6 -0
- package/corpus/templates/mail/app/components/layout/AppLayout.tsx +136 -123
- package/corpus/templates/mail/changelog/2026-09-22-apps-start-with-an-app-shaped-skeleton-while-session-data-lo.md +6 -0
- package/corpus/templates/slides/changelog/2026-09-22-apps-start-with-an-app-shaped-skeleton-while-session-data-lo.md +6 -0
- package/dist/client/AppShellSkeleton.d.ts +5 -0
- package/dist/client/AppShellSkeleton.js +69 -0
- package/dist/client/ClientOnly.d.ts +1 -1
- package/dist/client/ClientOnly.js +1 -1
- package/dist/client/LanguagePicker.d.ts +6 -0
- package/dist/client/LanguagePicker.js +128 -0
- package/dist/client/analytics.d.ts +3 -2
- package/dist/client/analytics.js +1 -1
- package/dist/client/app-providers.d.ts +3 -3
- package/dist/client/app-providers.js +53 -23
- package/dist/client/client-status-requests.d.ts +5 -0
- package/dist/client/client-status-requests.js +31 -18
- package/dist/client/i18n.d.ts +1 -6
- package/dist/client/i18n.js +3 -117
- package/dist/client/onboarding/first-run-startup-gate.js +14 -3
- package/dist/client/require-session.d.ts +1 -1
- package/dist/client/require-session.js +3 -3
- package/dist/client/session-replay-context.d.ts +16 -0
- package/dist/client/session-replay-context.js +93 -0
- package/dist/client/session-replay.d.ts +2 -26
- package/dist/client/session-replay.js +1 -64
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/routes.d.ts +1 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/deploy/build.js +19 -29
- package/dist/file-upload/actions/upload-image.d.ts +1 -1
- package/dist/observability/routes.d.ts +1 -1
- package/dist/progress/routes.d.ts +1 -1
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/secrets/routes.d.ts +6 -6
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/core-routes-plugin.d.ts +1 -1
- package/dist/shared/ssr-session-bootstrap.d.ts +3 -0
- package/dist/shared/ssr-session-bootstrap.js +44 -0
- package/dist/sharing/actions/list-resource-shares.d.ts +1 -1
- package/dist/triggers/actions/manage-automation.d.ts +2 -2
- package/package.json +3 -3
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Picker, useDesignSystemComponent, } from "@agent-native/toolkit/design-system";
|
|
3
|
+
import * as PopoverPrimitive from "@radix-ui/react-popover";
|
|
4
|
+
import { IconCheck, IconChevronDown, IconLanguage } from "@tabler/icons-react";
|
|
5
|
+
import { useState } from "react";
|
|
6
|
+
import { DEFAULT_LOCALE, localeDisplayName, normalizeLocalizationPreference, resolveLocaleFromCandidates, } from "../localization/shared.js";
|
|
7
|
+
import { useLocale } from "./i18n.js";
|
|
8
|
+
import { cn } from "./utils.js";
|
|
9
|
+
const LANGUAGE_PICKER_COPY = {
|
|
10
|
+
"en-US": {
|
|
11
|
+
label: "Language",
|
|
12
|
+
system: "System",
|
|
13
|
+
systemDescription: "Use your browser language",
|
|
14
|
+
},
|
|
15
|
+
"zh-CN": {
|
|
16
|
+
label: "语言",
|
|
17
|
+
system: "系统",
|
|
18
|
+
systemDescription: "使用浏览器语言",
|
|
19
|
+
},
|
|
20
|
+
"zh-TW": {
|
|
21
|
+
label: "語言",
|
|
22
|
+
system: "系統",
|
|
23
|
+
systemDescription: "使用瀏覽器語言",
|
|
24
|
+
},
|
|
25
|
+
"es-ES": {
|
|
26
|
+
label: "Idioma",
|
|
27
|
+
system: "Sistema",
|
|
28
|
+
systemDescription: "Usar el idioma del navegador",
|
|
29
|
+
},
|
|
30
|
+
"fr-FR": {
|
|
31
|
+
label: "Langue",
|
|
32
|
+
system: "Système",
|
|
33
|
+
systemDescription: "Utiliser la langue du navigateur",
|
|
34
|
+
},
|
|
35
|
+
"de-DE": {
|
|
36
|
+
label: "Sprache",
|
|
37
|
+
system: "System",
|
|
38
|
+
systemDescription: "Browsersprache verwenden",
|
|
39
|
+
},
|
|
40
|
+
"ja-JP": {
|
|
41
|
+
label: "言語",
|
|
42
|
+
system: "システム",
|
|
43
|
+
systemDescription: "ブラウザーの言語を使用",
|
|
44
|
+
},
|
|
45
|
+
"ko-KR": {
|
|
46
|
+
label: "언어",
|
|
47
|
+
system: "시스템",
|
|
48
|
+
systemDescription: "브라우저 언어 사용",
|
|
49
|
+
},
|
|
50
|
+
"pt-BR": {
|
|
51
|
+
label: "Idioma",
|
|
52
|
+
system: "Sistema",
|
|
53
|
+
systemDescription: "Usar o idioma do navegador",
|
|
54
|
+
},
|
|
55
|
+
"hi-IN": {
|
|
56
|
+
label: "भाषा",
|
|
57
|
+
system: "सिस्टम",
|
|
58
|
+
systemDescription: "ब्राउज़र की भाषा का उपयोग करें",
|
|
59
|
+
},
|
|
60
|
+
"ar-SA": {
|
|
61
|
+
label: "اللغة",
|
|
62
|
+
system: "النظام",
|
|
63
|
+
systemDescription: "استخدام لغة المتصفح",
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
function browserLanguageCandidates() {
|
|
67
|
+
if (typeof navigator === "undefined")
|
|
68
|
+
return [];
|
|
69
|
+
return navigator.languages?.length
|
|
70
|
+
? [...navigator.languages]
|
|
71
|
+
: navigator.language
|
|
72
|
+
? [navigator.language]
|
|
73
|
+
: [];
|
|
74
|
+
}
|
|
75
|
+
export function LanguagePicker({ className, includeSystem = true, label, variant = "select", }) {
|
|
76
|
+
const { locale, preference, setPreference, supportedLocales, localeMetadata, } = useLocale();
|
|
77
|
+
const [open, setOpen] = useState(false);
|
|
78
|
+
const copy = LANGUAGE_PICKER_COPY[locale] ?? LANGUAGE_PICKER_COPY[DEFAULT_LOCALE];
|
|
79
|
+
const systemCopy = LANGUAGE_PICKER_COPY[resolveLocaleFromCandidates(browserLanguageCandidates())] ?? LANGUAGE_PICKER_COPY[DEFAULT_LOCALE];
|
|
80
|
+
const resolvedLabel = label ?? copy.label;
|
|
81
|
+
const options = [
|
|
82
|
+
...(includeSystem
|
|
83
|
+
? [
|
|
84
|
+
{
|
|
85
|
+
value: "system",
|
|
86
|
+
label: systemCopy.system,
|
|
87
|
+
description: systemCopy.systemDescription,
|
|
88
|
+
},
|
|
89
|
+
]
|
|
90
|
+
: []),
|
|
91
|
+
...supportedLocales.map((code) => ({
|
|
92
|
+
value: code,
|
|
93
|
+
label: localeDisplayName(code, localeMetadata),
|
|
94
|
+
description: code,
|
|
95
|
+
})),
|
|
96
|
+
];
|
|
97
|
+
const selected = options.find((option) => option.value === preference);
|
|
98
|
+
const selectedLabel = selected?.label ?? preference;
|
|
99
|
+
const triggerLabel = `${resolvedLabel}: ${selectedLabel}`;
|
|
100
|
+
const customPicker = useDesignSystemComponent("Picker");
|
|
101
|
+
function handleOptionClick(value) {
|
|
102
|
+
setOpen(false);
|
|
103
|
+
void setPreference(normalizeLocalizationPreference(value).locale);
|
|
104
|
+
}
|
|
105
|
+
if (variant === "select" && customPicker) {
|
|
106
|
+
return (_jsx(Picker, { mode: "select", options: options, value: preference, onChange: (value) => {
|
|
107
|
+
if (value == null)
|
|
108
|
+
return;
|
|
109
|
+
void setPreference(normalizeLocalizationPreference(value).locale);
|
|
110
|
+
}, placeholder: selectedLabel, "aria-label": triggerLabel, className: className }));
|
|
111
|
+
}
|
|
112
|
+
return (_jsx("div", { className: className, children: _jsxs(PopoverPrimitive.Root, { open: open, onOpenChange: setOpen, children: [_jsx(PopoverPrimitive.Trigger, { asChild: true, children: _jsxs("button", { type: "button", "aria-label": triggerLabel, title: triggerLabel, "data-language-picker-trigger": true, className: cn("shrink-0 rounded-md outline-none transition-colors focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2", variant === "ghost-icon"
|
|
113
|
+
? "flex h-9 w-9 items-center justify-center text-muted-foreground hover:bg-accent hover:text-foreground data-[state=open]:bg-accent data-[state=open]:text-foreground"
|
|
114
|
+
: "border border-border bg-background text-foreground hover:border-foreground/30 hover:bg-accent/40 hover:text-foreground data-[state=open]:border-foreground/30 data-[state=open]:bg-accent/40", variant === "icon"
|
|
115
|
+
? "flex h-8 w-8 items-center justify-center"
|
|
116
|
+
: variant === "select"
|
|
117
|
+
? "flex h-9 w-full items-center justify-between gap-2 px-3 text-start text-sm"
|
|
118
|
+
: null), children: [_jsxs("span", { className: "flex min-w-0 items-center gap-2", children: [_jsx(IconLanguage, { className: "h-4 w-4 shrink-0 text-muted-foreground" }), variant === "select" ? (_jsx("span", { className: "truncate", children: selectedLabel })) : (_jsx("span", { className: "sr-only", children: triggerLabel }))] }), variant === "select" ? (_jsx(IconChevronDown, { className: "h-3.5 w-3.5 shrink-0 text-muted-foreground", "aria-hidden": "true" })) : null] }) }), _jsx(PopoverPrimitive.Portal, { children: _jsx(PopoverPrimitive.Content, { align: variant === "select" ? "start" : "end", sideOffset: 6, role: "menu", className: cn(
|
|
119
|
+
// compositing-ok: popover content unmounts on close.
|
|
120
|
+
"z-[9999] max-h-[min(20rem,var(--radix-popover-content-available-height))] overflow-y-auto rounded-lg border border-border bg-popover p-1 text-popover-foreground shadow-lg outline-none will-change-[transform,opacity] data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:duration-100 data-[state=open]:duration-150 data-[state=closed]:ease-in data-[state=open]:ease-out data-[side=bottom]:slide-in-from-top-1 data-[side=left]:slide-in-from-right-1 data-[side=right]:slide-in-from-left-1 data-[side=top]:slide-in-from-bottom-1", variant === "icon" || variant === "ghost-icon"
|
|
121
|
+
? "min-w-56"
|
|
122
|
+
: "w-[min(20rem,calc(100vw-2rem))] min-w-[var(--radix-popover-trigger-width)]"), children: options.map((option) => {
|
|
123
|
+
const optionSelected = option.value === preference;
|
|
124
|
+
return (_jsxs("button", { type: "button", role: "menuitemradio", "aria-checked": optionSelected, title: option.description, onClick: () => handleOptionClick(option.value), className: cn("flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-start text-sm outline-none transition-colors hover:bg-accent/60 hover:text-foreground focus-visible:bg-accent/60 focus-visible:text-foreground", optionSelected
|
|
125
|
+
? "bg-accent/40 text-foreground"
|
|
126
|
+
: "text-muted-foreground"), children: [_jsx(IconCheck, { className: cn("h-3.5 w-3.5 shrink-0", optionSelected ? "opacity-100" : "opacity-0"), "aria-hidden": "true" }), _jsx("span", { className: "truncate", children: option.label })] }, option.value));
|
|
127
|
+
}) }) })] }) }));
|
|
128
|
+
}
|
|
@@ -4,8 +4,9 @@ export { clearAnalyticsSessionId, setAnalyticsSessionId, } from "./analytics-ses
|
|
|
4
4
|
import type { SessionReplayOptions, SessionReplayStartResult } from "./session-replay.js";
|
|
5
5
|
export { scrubUrl } from "./url-scrub.js";
|
|
6
6
|
export { addErrorBreadcrumb, captureException, captureMessage, isErrorCaptureInstalled, type CaptureExceptionContext, type CapturedExceptionEvent, type ExceptionBreadcrumb, type ExceptionLevel, } from "./error-capture.js";
|
|
7
|
-
export type { SessionReplayConsoleOptions, SessionReplayNetworkOptions, SessionReplayOptions, SessionReplayStartResult,
|
|
8
|
-
export { getSessionReplayContext, getSessionReplayUrl, } from "./session-replay.js";
|
|
7
|
+
export type { SessionReplayConsoleOptions, SessionReplayNetworkOptions, SessionReplayOptions, SessionReplayStartResult, SessionReplayUrlMatcher, } from "./session-replay.js";
|
|
8
|
+
export { getSessionReplayContext, getSessionReplayUrl, } from "./session-replay-context.js";
|
|
9
|
+
export type { SessionReplayContext, SessionReplayLinkOptions, } from "./session-replay-context.js";
|
|
9
10
|
declare global {
|
|
10
11
|
interface Window {
|
|
11
12
|
gtag?: (...args: any[]) => void;
|
package/dist/client/analytics.js
CHANGED
|
@@ -17,7 +17,7 @@ import { isDynamicImportFailureMessage } from "./route-chunk-recovery.js";
|
|
|
17
17
|
import { scrubUrl } from "./url-scrub.js";
|
|
18
18
|
export { scrubUrl } from "./url-scrub.js";
|
|
19
19
|
export { addErrorBreadcrumb, captureException, captureMessage, isErrorCaptureInstalled, } from "./error-capture.js";
|
|
20
|
-
export { getSessionReplayContext, getSessionReplayUrl, } from "./session-replay.js";
|
|
20
|
+
export { getSessionReplayContext, getSessionReplayUrl, } from "./session-replay-context.js";
|
|
21
21
|
let _getDefaultProps = null;
|
|
22
22
|
let _configuredAnalyticsClientPlatform = null;
|
|
23
23
|
let _agentNativeAnalyticsPublicKey = null;
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
* <AppProviders
|
|
18
18
|
* queryClient={queryClient}
|
|
19
19
|
* isPublicPath={isPublicBookingPath(location.pathname)}
|
|
20
|
-
* clientOnlyFallback={<
|
|
20
|
+
* clientOnlyFallback={<AppShellSkeleton />}
|
|
21
21
|
* >
|
|
22
22
|
* ...
|
|
23
23
|
* </AppProviders>
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
* default), `<ClientOnly>` hydrates the shared SSR shell and
|
|
28
28
|
* `<RequireSession>` redirects signed-out visitors to the framework sign-in
|
|
29
29
|
* page before private app chrome mounts. When `clientOnlyFallback` is
|
|
30
|
-
* omitted, `<
|
|
30
|
+
* omitted, `<AppShellSkeleton />` is used.
|
|
31
31
|
*
|
|
32
32
|
* Customisation props:
|
|
33
33
|
* themeAttribute — passed to next-themes ThemeProvider `attribute`.
|
|
@@ -116,7 +116,7 @@ export interface AppProvidersProps {
|
|
|
116
116
|
isPublicPath?: boolean;
|
|
117
117
|
/**
|
|
118
118
|
* Fallback rendered by `<ClientOnly>` while JS hydrates on private paths.
|
|
119
|
-
* Defaults to `<
|
|
119
|
+
* Defaults to `<AppShellSkeleton />`.
|
|
120
120
|
*/
|
|
121
121
|
clientOnlyFallback?: React.ReactNode;
|
|
122
122
|
/**
|
|
@@ -18,7 +18,7 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
|
|
|
18
18
|
* <AppProviders
|
|
19
19
|
* queryClient={queryClient}
|
|
20
20
|
* isPublicPath={isPublicBookingPath(location.pathname)}
|
|
21
|
-
* clientOnlyFallback={<
|
|
21
|
+
* clientOnlyFallback={<AppShellSkeleton />}
|
|
22
22
|
* >
|
|
23
23
|
* ...
|
|
24
24
|
* </AppProviders>
|
|
@@ -28,7 +28,7 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
|
|
|
28
28
|
* default), `<ClientOnly>` hydrates the shared SSR shell and
|
|
29
29
|
* `<RequireSession>` redirects signed-out visitors to the framework sign-in
|
|
30
30
|
* page before private app chrome mounts. When `clientOnlyFallback` is
|
|
31
|
-
* omitted, `<
|
|
31
|
+
* omitted, `<AppShellSkeleton />` is used.
|
|
32
32
|
*
|
|
33
33
|
* Customisation props:
|
|
34
34
|
* themeAttribute — passed to next-themes ThemeProvider `attribute`.
|
|
@@ -61,9 +61,10 @@ import { useEffect, useRef } from "react";
|
|
|
61
61
|
import { useInRouterContext } from "react-router";
|
|
62
62
|
import { isHumanReadableDocumentTitle, normalizeDocumentTitle, } from "../shared/document-title.js";
|
|
63
63
|
import { getSsrBetaRedirectScriptBody } from "../shared/ssr-beta-redirect.js";
|
|
64
|
+
import { getSsrSessionBootstrapScriptBody } from "../shared/ssr-session-bootstrap.js";
|
|
64
65
|
import { agentNativePath, frameworkRoutePrefix } from "./api-path.js";
|
|
66
|
+
import { AppShellSkeleton } from "./AppShellSkeleton.js";
|
|
65
67
|
import { ClientOnly } from "./ClientOnly.js";
|
|
66
|
-
import { DefaultSpinner } from "./DefaultSpinner.js";
|
|
67
68
|
import { EnvironmentBadge } from "./EnvironmentBadge.js";
|
|
68
69
|
import { AgentNativeI18nProvider, } from "./i18n.js";
|
|
69
70
|
import { FirstRunOnboardingStartupGate } from "./onboarding/first-run-startup-gate.js";
|
|
@@ -74,19 +75,27 @@ import { RuntimeConfigNotice } from "./RuntimeConfigNotice.js";
|
|
|
74
75
|
import { EMBEDDED_THEME_CHANGE_EVENT, applyEmbeddedThemeUpdate, parseEmbeddedThemeUpdate, } from "./theme.js";
|
|
75
76
|
import { scheduleAfterPaint } from "./use-after-paint.js";
|
|
76
77
|
import { useSession } from "./use-session.js";
|
|
77
|
-
import { createAgentNativeServerActionWebMcpRegistration } from "./webmcp.js";
|
|
78
78
|
const DEFAULT_TOASTER = (_jsx(Toaster, { richColors: true, position: "bottom-left", offset: { bottom: 44, left: 32 }, mobileOffset: { bottom: 44, left: 16 } }));
|
|
79
79
|
function EarlyBetaRedirectScript() {
|
|
80
80
|
return (_jsx("script", { "data-agent-native-beta-redirect": "1", dangerouslySetInnerHTML: {
|
|
81
81
|
__html: getSsrBetaRedirectScriptBody(agentNativePath("/_agent-native/auth/session"), frameworkRoutePrefix()),
|
|
82
82
|
} }));
|
|
83
83
|
}
|
|
84
|
+
function EarlySessionBootstrapScript() {
|
|
85
|
+
return (_jsx("script", { "data-agent-native-session-bootstrap": "1", dangerouslySetInnerHTML: {
|
|
86
|
+
__html: getSsrSessionBootstrapScriptBody(agentNativePath("/_agent-native/auth/session")),
|
|
87
|
+
} }));
|
|
88
|
+
}
|
|
84
89
|
function RoutedAppEnhancements() {
|
|
85
90
|
const isInRouter = useInRouterContext();
|
|
86
91
|
if (!isInRouter)
|
|
87
92
|
return null;
|
|
88
93
|
return (_jsxs(_Fragment, { children: [_jsx(AgentNativeRouteWarmup, {}), _jsx(RouteTransitionIndicator, {})] }));
|
|
89
94
|
}
|
|
95
|
+
let webMcpModulePromise = null;
|
|
96
|
+
function loadWebMcpModule() {
|
|
97
|
+
return (webMcpModulePromise ??= import("./webmcp.js"));
|
|
98
|
+
}
|
|
90
99
|
function AgentNativeWebMcpRegistration({ excludeActionNames, }) {
|
|
91
100
|
const excludeActionNamesKey = JSON.stringify(excludeActionNames ?? []);
|
|
92
101
|
useEffect(() => {
|
|
@@ -95,16 +104,27 @@ function AgentNativeWebMcpRegistration({ excludeActionNames, }) {
|
|
|
95
104
|
// paint-aligned window — only the cookie-session-gated variant defers.
|
|
96
105
|
// Ownership is local to this effect: two coexisting surfaces each stop
|
|
97
106
|
// only the registration they created.
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
107
|
+
let disposed = false;
|
|
108
|
+
let registration = null;
|
|
109
|
+
void loadWebMcpModule()
|
|
110
|
+
.then(({ createAgentNativeServerActionWebMcpRegistration }) => {
|
|
111
|
+
if (disposed)
|
|
112
|
+
return;
|
|
113
|
+
registration = createAgentNativeServerActionWebMcpRegistration({
|
|
114
|
+
excludeActionNames,
|
|
115
|
+
});
|
|
116
|
+
void registration.start().catch(() => {
|
|
117
|
+
// WebMCP is progressive enhancement. Session expiry or a transient
|
|
118
|
+
// manifest failure must not prevent the authenticated app from
|
|
119
|
+
// loading.
|
|
120
|
+
});
|
|
121
|
+
})
|
|
122
|
+
.catch(() => {
|
|
123
|
+
// A failed optional WebMCP chunk must not delay the app shell.
|
|
105
124
|
});
|
|
106
125
|
return () => {
|
|
107
|
-
|
|
126
|
+
disposed = true;
|
|
127
|
+
registration?.stop();
|
|
108
128
|
};
|
|
109
129
|
}, [excludeActionNamesKey]);
|
|
110
130
|
return null;
|
|
@@ -139,19 +159,29 @@ function SessionGatedAgentNativeWebMcpRegistration({ excludeActionNames, }) {
|
|
|
139
159
|
registrationRef.current?.stop();
|
|
140
160
|
registrationRef.current = null;
|
|
141
161
|
registrationExcludeActionNamesKeyRef.current = null;
|
|
162
|
+
let disposed = false;
|
|
142
163
|
const cancel = scheduleAfterPaint(() => {
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
164
|
+
void loadWebMcpModule()
|
|
165
|
+
.then(({ createAgentNativeServerActionWebMcpRegistration }) => {
|
|
166
|
+
if (disposed)
|
|
167
|
+
return;
|
|
168
|
+
const registration = createAgentNativeServerActionWebMcpRegistration({
|
|
169
|
+
excludeActionNames,
|
|
170
|
+
});
|
|
171
|
+
void registration.start().catch(() => {
|
|
172
|
+
// WebMCP is progressive enhancement. Session expiry or a transient
|
|
173
|
+
// manifest failure must not prevent the authenticated app from
|
|
174
|
+
// loading.
|
|
175
|
+
});
|
|
176
|
+
registrationRef.current = registration;
|
|
177
|
+
registrationExcludeActionNamesKeyRef.current = excludeActionNamesKey;
|
|
178
|
+
})
|
|
179
|
+
.catch(() => {
|
|
180
|
+
// A failed optional WebMCP chunk must not delay the app shell.
|
|
150
181
|
});
|
|
151
|
-
registrationRef.current = registration;
|
|
152
|
-
registrationExcludeActionNamesKeyRef.current = excludeActionNamesKey;
|
|
153
182
|
});
|
|
154
183
|
return () => {
|
|
184
|
+
disposed = true;
|
|
155
185
|
cancel();
|
|
156
186
|
};
|
|
157
187
|
// Unmount stops exactly the registration this surface created, whether
|
|
@@ -256,11 +286,11 @@ function publicPathI18n(i18n) {
|
|
|
256
286
|
return { ...(i18n ?? {}), persistPreference: false };
|
|
257
287
|
}
|
|
258
288
|
export function AppProviders({ queryClient, isPublicPath = false, clientOnlyFallback, sessionBypass = false, disableWebMcp = false, webMcpExcludeActionNames, showEnvironmentBadge = false, defaultTheme, themeAttribute, tooltipDelayDuration, toaster, disableThemeTransitions, i18n, documentTitleFallback, children, }) {
|
|
259
|
-
const fallback = clientOnlyFallback ?? _jsx(
|
|
289
|
+
const fallback = clientOnlyFallback ?? _jsx(AppShellSkeleton, {});
|
|
260
290
|
if (isPublicPath) {
|
|
261
291
|
return (_jsx(ProvidersInner, { queryClient: queryClient, defaultTheme: defaultTheme, themeAttribute: themeAttribute, tooltipDelayDuration: tooltipDelayDuration, toaster: toaster, disableThemeTransitions: disableThemeTransitions, disableWebMcp: disableWebMcp, webMcpExcludeActionNames: webMcpExcludeActionNames, sessionBypass: sessionBypass, i18n: publicPathI18n(i18n), documentTitleFallback: documentTitleFallback, showProductionEnvironmentBadge: false, showEnvironmentBadge: showEnvironmentBadge, children: children }));
|
|
262
292
|
}
|
|
263
293
|
// Keep the bootstrap outside ClientOnly so the HTML parser can run it before
|
|
264
294
|
// the authenticated client bundle starts.
|
|
265
|
-
return (_jsxs(_Fragment, { children: [!sessionBypass && _jsx(EarlyBetaRedirectScript, {}), _jsx(ClientOnly, { fallback: fallback, children: _jsx(ProvidersInner, { queryClient: queryClient, defaultTheme: defaultTheme, themeAttribute: themeAttribute, tooltipDelayDuration: tooltipDelayDuration, toaster: toaster, disableThemeTransitions: disableThemeTransitions, disableWebMcp: disableWebMcp, webMcpExcludeActionNames: webMcpExcludeActionNames, sessionBypass: sessionBypass, i18n: i18n, documentTitleFallback: documentTitleFallback, showProductionEnvironmentBadge: !sessionBypass, showEnvironmentBadge: showEnvironmentBadge, children: _jsx(RequireSession, { bypass: sessionBypass, fallback: fallback, children: sessionBypass ? (children) : (_jsx(FirstRunOnboardingStartupGate, { children: children })) }) }) })] }));
|
|
295
|
+
return (_jsxs(_Fragment, { children: [!sessionBypass && (_jsxs(_Fragment, { children: [_jsx(EarlySessionBootstrapScript, {}), _jsx(EarlyBetaRedirectScript, {})] })), _jsx(ClientOnly, { fallback: fallback, children: _jsx(ProvidersInner, { queryClient: queryClient, defaultTheme: defaultTheme, themeAttribute: themeAttribute, tooltipDelayDuration: tooltipDelayDuration, toaster: toaster, disableThemeTransitions: disableThemeTransitions, disableWebMcp: disableWebMcp, webMcpExcludeActionNames: webMcpExcludeActionNames, sessionBypass: sessionBypass, i18n: i18n, documentTitleFallback: documentTitleFallback, showProductionEnvironmentBadge: !sessionBypass, showEnvironmentBadge: showEnvironmentBadge, children: _jsx(RequireSession, { bypass: sessionBypass, fallback: fallback, children: sessionBypass ? (children) : (_jsx(FirstRunOnboardingStartupGate, { children: children })) }) }) })] }));
|
|
266
296
|
}
|
|
@@ -5,6 +5,11 @@ export type ClientStatusResult<T> = {
|
|
|
5
5
|
state: "unavailable";
|
|
6
6
|
status?: number;
|
|
7
7
|
};
|
|
8
|
+
declare global {
|
|
9
|
+
interface Window {
|
|
10
|
+
__agentNativeSessionBootstrap?: Promise<ClientStatusResult<unknown>>;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
8
13
|
export declare function invalidateClientStatusRequest(path: string): void;
|
|
9
14
|
export declare function invalidateClientStatusRequests(): void;
|
|
10
15
|
export declare function fetchAgentEngineStatus<T = unknown>(): Promise<ClientStatusResult<T>>;
|
|
@@ -2,6 +2,7 @@ import { agentNativePath } from "./api-path.js";
|
|
|
2
2
|
import { agentNativeApiDisabledReason } from "./api-surface.js";
|
|
3
3
|
const RESULT_TTL_MS = 500;
|
|
4
4
|
const REQUEST_TIMEOUT_MS = 15_000;
|
|
5
|
+
const SESSION_STATUS_PATH = "/_agent-native/auth/session";
|
|
5
6
|
const cache = new Map();
|
|
6
7
|
const requests = new Map();
|
|
7
8
|
const requestControllers = new Map();
|
|
@@ -56,23 +57,29 @@ async function fetchClientStatus(path) {
|
|
|
56
57
|
resolve({ state: "unavailable" });
|
|
57
58
|
}, REQUEST_TIMEOUT_MS);
|
|
58
59
|
});
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
60
|
+
const bootstrappedSession = path === SESSION_STATUS_PATH && typeof window !== "undefined"
|
|
61
|
+
? window.__agentNativeSessionBootstrap
|
|
62
|
+
: undefined;
|
|
63
|
+
if (bootstrappedSession)
|
|
64
|
+
delete window.__agentNativeSessionBootstrap;
|
|
65
|
+
const transport = bootstrappedSession ??
|
|
66
|
+
fetch(url, {
|
|
67
|
+
cache: "no-store",
|
|
68
|
+
credentials: "same-origin",
|
|
69
|
+
...(controller ? { signal: controller.signal } : {}),
|
|
70
|
+
})
|
|
71
|
+
.then(async (response) => {
|
|
72
|
+
if (!response.ok) {
|
|
73
|
+
return { state: "unavailable", status: response.status };
|
|
74
|
+
}
|
|
75
|
+
try {
|
|
76
|
+
return { state: "available", value: await response.json() };
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
return { state: "unavailable", status: response.status };
|
|
80
|
+
}
|
|
81
|
+
})
|
|
82
|
+
.catch(() => ({ state: "unavailable" }));
|
|
76
83
|
const request = Promise.race([transport, timeout])
|
|
77
84
|
.then((result) => {
|
|
78
85
|
if (generation === requestGeneration &&
|
|
@@ -101,6 +108,9 @@ async function fetchClientStatus(path) {
|
|
|
101
108
|
}
|
|
102
109
|
export function invalidateClientStatusRequest(path) {
|
|
103
110
|
const url = agentNativePath(path);
|
|
111
|
+
if (path === SESSION_STATUS_PATH && typeof window !== "undefined") {
|
|
112
|
+
delete window.__agentNativeSessionBootstrap;
|
|
113
|
+
}
|
|
104
114
|
requestGenerations.set(url, (requestGenerations.get(url) ?? 0) + 1);
|
|
105
115
|
cache.delete(url);
|
|
106
116
|
requestControllers.get(url)?.abort();
|
|
@@ -109,6 +119,9 @@ export function invalidateClientStatusRequest(path) {
|
|
|
109
119
|
}
|
|
110
120
|
export function invalidateClientStatusRequests() {
|
|
111
121
|
generation += 1;
|
|
122
|
+
if (typeof window !== "undefined") {
|
|
123
|
+
delete window.__agentNativeSessionBootstrap;
|
|
124
|
+
}
|
|
112
125
|
cache.clear();
|
|
113
126
|
for (const controller of requestControllers.values()) {
|
|
114
127
|
controller.abort();
|
|
@@ -126,5 +139,5 @@ export function fetchBuilderStatus() {
|
|
|
126
139
|
return fetchClientStatus("/_agent-native/builder/status");
|
|
127
140
|
}
|
|
128
141
|
export function fetchAuthSessionStatus() {
|
|
129
|
-
return fetchClientStatus(
|
|
142
|
+
return fetchClientStatus(SESSION_STATUS_PATH);
|
|
130
143
|
}
|
package/dist/client/i18n.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import React from "react";
|
|
|
2
2
|
import { type LocaleCode, type LocaleMetadata, type LocaleMetadataMap, type LocalePreference, type LocalizationPreference } from "../localization/shared.js";
|
|
3
3
|
export { DEFAULT_LOCALE, LOCALE_HYDRATION_GLOBAL, LOCALE_METADATA, LOCALE_STORAGE_KEY, SUPPORTED_LOCALES, isLocaleCode, isValidLocaleCode, localeDirection, localeMetadataFor, type BuiltinLocaleCode, normalizeLocaleCode, normalizeLocalePreference, normalizeLocalizationPreference, resolveLocaleFromCandidates, resolveLocaleFromPreference, type LocaleCode, type LocaleMetadata, type LocalePreference, type LocalizationPreference, } from "../localization/shared.js";
|
|
4
4
|
export { getLocaleInitScript } from "../localization/server.js";
|
|
5
|
+
export { LanguagePicker } from "./LanguagePicker.js";
|
|
5
6
|
export type LocaleMessages = Record<string, unknown>;
|
|
6
7
|
export type AgentNativeI18nLocaleLoader = () => Promise<LocaleMessages | {
|
|
7
8
|
default: LocaleMessages;
|
|
@@ -76,9 +77,3 @@ export declare function useFormatters(): {
|
|
|
76
77
|
formatRelativeTime(value: number, unit: Intl.RelativeTimeFormatUnit, options?: Intl.RelativeTimeFormatOptions): string;
|
|
77
78
|
formatList(value: string[], options?: Intl.ListFormatOptions): string;
|
|
78
79
|
};
|
|
79
|
-
export declare function LanguagePicker({ className, includeSystem, label, variant, }: {
|
|
80
|
-
className?: string;
|
|
81
|
-
includeSystem?: boolean;
|
|
82
|
-
label?: string;
|
|
83
|
-
variant?: "select" | "icon" | "ghost-icon";
|
|
84
|
-
}): React.JSX.Element;
|
package/dist/client/i18n.js
CHANGED
|
@@ -1,20 +1,17 @@
|
|
|
1
|
-
import { jsx as _jsx
|
|
2
|
-
import { Picker, useDesignSystemComponent, } from "@agent-native/toolkit/design-system";
|
|
3
|
-
import * as PopoverPrimitive from "@radix-ui/react-popover";
|
|
4
|
-
import { IconCheck, IconChevronDown, IconLanguage } from "@tabler/icons-react";
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
5
2
|
import i18next from "i18next";
|
|
6
3
|
import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState, } from "react";
|
|
7
4
|
import { I18nextProvider, initReactI18next, useTranslation, } from "react-i18next";
|
|
8
5
|
import { coreMessagesForLocale, englishAgentChatMessages, loadCoreMessagesForLocale, normalizeCoreMessageOverrides, } from "../localization/core-messages.js";
|
|
9
6
|
import defaultEnglishMessages from "../localization/default-messages.js";
|
|
10
|
-
import { DEFAULT_LOCALE, LOCALE_HYDRATION_GLOBAL, LOCALE_STORAGE_KEY, SUPPORTED_LOCALES, isValidLocaleCode,
|
|
7
|
+
import { DEFAULT_LOCALE, LOCALE_HYDRATION_GLOBAL, LOCALE_STORAGE_KEY, SUPPORTED_LOCALES, isValidLocaleCode, localeDirection, localeMetadataFor, normalizeLocaleCode, normalizeLocalizationPreference, resolveLocaleFromCandidates, resolveLocaleFromPreference, } from "../localization/shared.js";
|
|
11
8
|
import { injectedAgentNativeConfig } from "./app-config.js";
|
|
12
9
|
import { setClientAppState } from "./application-state.js";
|
|
13
10
|
import { callAction } from "./use-action.js";
|
|
14
11
|
import { useSession } from "./use-session.js";
|
|
15
|
-
import { cn } from "./utils.js";
|
|
16
12
|
export { DEFAULT_LOCALE, LOCALE_HYDRATION_GLOBAL, LOCALE_METADATA, LOCALE_STORAGE_KEY, SUPPORTED_LOCALES, isLocaleCode, isValidLocaleCode, localeDirection, localeMetadataFor, normalizeLocaleCode, normalizeLocalePreference, normalizeLocalizationPreference, resolveLocaleFromCandidates, resolveLocaleFromPreference, } from "../localization/shared.js";
|
|
17
13
|
export { getLocaleInitScript } from "../localization/server.js";
|
|
14
|
+
export { LanguagePicker } from "./LanguagePicker.js";
|
|
18
15
|
function isAgentNativeI18nModuleNamespace(value) {
|
|
19
16
|
return Object.prototype.toString.call(value) === "[object Module]";
|
|
20
17
|
}
|
|
@@ -48,63 +45,6 @@ function catalogLocaleMetadata(locales) {
|
|
|
48
45
|
const LocaleContext = globalThis.__AGENT_NATIVE_LOCALE_CONTEXT__ ??
|
|
49
46
|
(globalThis.__AGENT_NATIVE_LOCALE_CONTEXT__ =
|
|
50
47
|
createContext(null));
|
|
51
|
-
const LANGUAGE_PICKER_COPY = {
|
|
52
|
-
"en-US": {
|
|
53
|
-
label: "Language",
|
|
54
|
-
system: "System",
|
|
55
|
-
systemDescription: "Use your browser language",
|
|
56
|
-
},
|
|
57
|
-
"zh-CN": {
|
|
58
|
-
label: "语言",
|
|
59
|
-
system: "系统",
|
|
60
|
-
systemDescription: "使用浏览器语言",
|
|
61
|
-
},
|
|
62
|
-
"zh-TW": {
|
|
63
|
-
label: "語言",
|
|
64
|
-
system: "系統",
|
|
65
|
-
systemDescription: "使用瀏覽器語言",
|
|
66
|
-
},
|
|
67
|
-
"es-ES": {
|
|
68
|
-
label: "Idioma",
|
|
69
|
-
system: "Sistema",
|
|
70
|
-
systemDescription: "Usar el idioma del navegador",
|
|
71
|
-
},
|
|
72
|
-
"fr-FR": {
|
|
73
|
-
label: "Langue",
|
|
74
|
-
system: "Système",
|
|
75
|
-
systemDescription: "Utiliser la langue du navigateur",
|
|
76
|
-
},
|
|
77
|
-
"de-DE": {
|
|
78
|
-
label: "Sprache",
|
|
79
|
-
system: "System",
|
|
80
|
-
systemDescription: "Browsersprache verwenden",
|
|
81
|
-
},
|
|
82
|
-
"ja-JP": {
|
|
83
|
-
label: "言語",
|
|
84
|
-
system: "システム",
|
|
85
|
-
systemDescription: "ブラウザーの言語を使用",
|
|
86
|
-
},
|
|
87
|
-
"ko-KR": {
|
|
88
|
-
label: "언어",
|
|
89
|
-
system: "시스템",
|
|
90
|
-
systemDescription: "브라우저 언어 사용",
|
|
91
|
-
},
|
|
92
|
-
"pt-BR": {
|
|
93
|
-
label: "Idioma",
|
|
94
|
-
system: "Sistema",
|
|
95
|
-
systemDescription: "Usar o idioma do navegador",
|
|
96
|
-
},
|
|
97
|
-
"hi-IN": {
|
|
98
|
-
label: "भाषा",
|
|
99
|
-
system: "सिस्टम",
|
|
100
|
-
systemDescription: "ब्राउज़र की भाषा का उपयोग करें",
|
|
101
|
-
},
|
|
102
|
-
"ar-SA": {
|
|
103
|
-
label: "اللغة",
|
|
104
|
-
system: "النظام",
|
|
105
|
-
systemDescription: "استخدام لغة المتصفح",
|
|
106
|
-
},
|
|
107
|
-
};
|
|
108
48
|
function browserLanguageCandidates() {
|
|
109
49
|
if (typeof navigator === "undefined")
|
|
110
50
|
return [];
|
|
@@ -665,57 +605,3 @@ export function useFormatters() {
|
|
|
665
605
|
},
|
|
666
606
|
}), [locale]);
|
|
667
607
|
}
|
|
668
|
-
export function LanguagePicker({ className, includeSystem = true, label, variant = "select", }) {
|
|
669
|
-
const { locale, preference, setPreference, supportedLocales, localeMetadata, } = useLocale();
|
|
670
|
-
const [open, setOpen] = useState(false);
|
|
671
|
-
const copy = LANGUAGE_PICKER_COPY[locale] ?? LANGUAGE_PICKER_COPY[DEFAULT_LOCALE];
|
|
672
|
-
const systemCopy = LANGUAGE_PICKER_COPY[resolveLocaleFromCandidates(browserLanguageCandidates())] ?? LANGUAGE_PICKER_COPY[DEFAULT_LOCALE];
|
|
673
|
-
const resolvedLabel = label ?? copy.label;
|
|
674
|
-
const options = [
|
|
675
|
-
...(includeSystem
|
|
676
|
-
? [
|
|
677
|
-
{
|
|
678
|
-
value: "system",
|
|
679
|
-
label: systemCopy.system,
|
|
680
|
-
description: systemCopy.systemDescription,
|
|
681
|
-
},
|
|
682
|
-
]
|
|
683
|
-
: []),
|
|
684
|
-
...supportedLocales.map((code) => ({
|
|
685
|
-
value: code,
|
|
686
|
-
label: localeDisplayName(code, localeMetadata),
|
|
687
|
-
description: code,
|
|
688
|
-
})),
|
|
689
|
-
];
|
|
690
|
-
const selected = options.find((option) => option.value === preference);
|
|
691
|
-
const selectedLabel = selected?.label ?? preference;
|
|
692
|
-
const triggerLabel = `${resolvedLabel}: ${selectedLabel}`;
|
|
693
|
-
const customPicker = useDesignSystemComponent("Picker");
|
|
694
|
-
function handleOptionClick(value) {
|
|
695
|
-
setOpen(false);
|
|
696
|
-
void setPreference(normalizeLocalizationPreference(value).locale);
|
|
697
|
-
}
|
|
698
|
-
if (variant === "select" && customPicker) {
|
|
699
|
-
return (_jsx(Picker, { mode: "select", options: options, value: preference, onChange: (value) => {
|
|
700
|
-
if (value == null)
|
|
701
|
-
return;
|
|
702
|
-
void setPreference(normalizeLocalizationPreference(value).locale);
|
|
703
|
-
}, placeholder: selectedLabel, "aria-label": triggerLabel, className: className }));
|
|
704
|
-
}
|
|
705
|
-
return (_jsx("div", { className: className, children: _jsxs(PopoverPrimitive.Root, { open: open, onOpenChange: setOpen, children: [_jsx(PopoverPrimitive.Trigger, { asChild: true, children: _jsxs("button", { type: "button", "aria-label": triggerLabel, title: triggerLabel, "data-language-picker-trigger": true, className: cn("shrink-0 rounded-md outline-none transition-colors focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2", variant === "ghost-icon"
|
|
706
|
-
? "flex h-9 w-9 items-center justify-center text-muted-foreground hover:bg-accent hover:text-foreground data-[state=open]:bg-accent data-[state=open]:text-foreground"
|
|
707
|
-
: "border border-border bg-background text-foreground hover:border-foreground/30 hover:bg-accent/40 hover:text-foreground data-[state=open]:border-foreground/30 data-[state=open]:bg-accent/40", variant === "icon"
|
|
708
|
-
? "flex h-8 w-8 items-center justify-center"
|
|
709
|
-
: variant === "select"
|
|
710
|
-
? "flex h-9 w-full items-center justify-between gap-2 px-3 text-start text-sm"
|
|
711
|
-
: null), children: [_jsxs("span", { className: "flex min-w-0 items-center gap-2", children: [_jsx(IconLanguage, { className: "h-4 w-4 shrink-0 text-muted-foreground" }), variant === "select" ? (_jsx("span", { className: "truncate", children: selectedLabel })) : (_jsx("span", { className: "sr-only", children: triggerLabel }))] }), variant === "select" ? (_jsx(IconChevronDown, { className: "h-3.5 w-3.5 shrink-0 text-muted-foreground", "aria-hidden": "true" })) : null] }) }), _jsx(PopoverPrimitive.Portal, { children: _jsx(PopoverPrimitive.Content, { align: variant === "select" ? "start" : "end", sideOffset: 6, role: "menu", className: cn(
|
|
712
|
-
// compositing-ok: popover content unmounts on close.
|
|
713
|
-
"z-[9999] max-h-[min(20rem,var(--radix-popover-content-available-height))] overflow-y-auto rounded-lg border border-border bg-popover p-1 text-popover-foreground shadow-lg outline-none will-change-[transform,opacity] data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:duration-100 data-[state=open]:duration-150 data-[state=closed]:ease-in data-[state=open]:ease-out data-[side=bottom]:slide-in-from-top-1 data-[side=left]:slide-in-from-right-1 data-[side=right]:slide-in-from-left-1 data-[side=top]:slide-in-from-bottom-1", variant === "icon" || variant === "ghost-icon"
|
|
714
|
-
? "min-w-56"
|
|
715
|
-
: "w-[min(20rem,calc(100vw-2rem))] min-w-[var(--radix-popover-trigger-width)]"), children: options.map((option) => {
|
|
716
|
-
const optionSelected = option.value === preference;
|
|
717
|
-
return (_jsxs("button", { type: "button", role: "menuitemradio", "aria-checked": optionSelected, title: option.description, onClick: () => handleOptionClick(option.value), className: cn("flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-start text-sm outline-none transition-colors hover:bg-accent/60 hover:text-foreground focus-visible:bg-accent/60 focus-visible:text-foreground", optionSelected
|
|
718
|
-
? "bg-accent/40 text-foreground"
|
|
719
|
-
: "text-muted-foreground"), children: [_jsx(IconCheck, { className: cn("h-3.5 w-3.5 shrink-0", optionSelected ? "opacity-100" : "opacity-0"), "aria-hidden": "true" }), _jsx("span", { className: "truncate", children: option.label })] }, option.value));
|
|
720
|
-
}) }) })] }) }));
|
|
721
|
-
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Suspense, createContext, lazy, useContext, useEffect, useState, } from "react";
|
|
3
|
-
import {
|
|
3
|
+
import { FIRST_RUN_ONBOARDING_COOKIE } from "../../shared/first-run-onboarding.js";
|
|
4
|
+
import { AppShellSkeleton } from "../AppShellSkeleton.js";
|
|
4
5
|
import { isFirstRunOnboardingEnabled } from "./first-run-enabled.js";
|
|
5
6
|
import { fetchFirstRunOnboardingStatus } from "./first-run-status.js";
|
|
6
7
|
import { trackOnboardingEvent } from "./use-onboarding.js";
|
|
@@ -9,12 +10,22 @@ const FirstRunOnboarding = lazy(() => import("./FirstRunOnboarding.js").then((mo
|
|
|
9
10
|
default: module.FirstRunOnboarding,
|
|
10
11
|
})));
|
|
11
12
|
const FirstRunOnboardingGateContext = createContext(false);
|
|
13
|
+
function hasFirstRunOnboardingCookie() {
|
|
14
|
+
if (typeof document === "undefined")
|
|
15
|
+
return true;
|
|
16
|
+
const prefix = `${FIRST_RUN_ONBOARDING_COOKIE}=`;
|
|
17
|
+
return document.cookie.split(";").some((cookie) => {
|
|
18
|
+
const entry = cookie.trim();
|
|
19
|
+
return entry.startsWith(prefix) && entry.slice(prefix.length) === "1";
|
|
20
|
+
});
|
|
21
|
+
}
|
|
12
22
|
export function useFirstRunOnboardingGateOwnsSurface() {
|
|
13
23
|
return useContext(FirstRunOnboardingGateContext);
|
|
14
24
|
}
|
|
15
25
|
export function FirstRunOnboardingStartupGate({ children, }) {
|
|
16
26
|
const previewMode = useOnboardingPreviewMode();
|
|
17
|
-
const
|
|
27
|
+
const [hadFirstRunCookie] = useState(hasFirstRunOnboardingCookie);
|
|
28
|
+
const shouldResolve = isFirstRunOnboardingEnabled() && !previewMode && hadFirstRunCookie;
|
|
18
29
|
const [decision, setDecision] = useState(shouldResolve ? "pending" : "ineligible");
|
|
19
30
|
useEffect(() => {
|
|
20
31
|
if (!shouldResolve) {
|
|
@@ -55,5 +66,5 @@ export function FirstRunOnboardingStartupGate({ children, }) {
|
|
|
55
66
|
return (_jsxs(FirstRunOnboardingGateContext.Provider, { value: ownsSurface, children: [app, decision === "pending" && _jsx(FirstRunOnboardingStartupLoading, {}), ownsSurface && (_jsx(Suspense, { fallback: _jsx(FirstRunOnboardingStartupLoading, {}), children: _jsx(FirstRunOnboarding, { initialFirstRun: true }) }))] }));
|
|
56
67
|
}
|
|
57
68
|
function FirstRunOnboardingStartupLoading() {
|
|
58
|
-
return (_jsx("div", { "data-first-run-startup-loading": "true", "aria-busy": "true", className: "fixed inset-0 z-[110] bg-background", children: _jsx(
|
|
69
|
+
return (_jsx("div", { "data-first-run-startup-loading": "true", "aria-busy": "true", className: "fixed inset-0 z-[110] bg-background", children: _jsx(AppShellSkeleton, {}) }));
|
|
59
70
|
}
|
|
@@ -32,7 +32,7 @@ export interface RequireSessionProps {
|
|
|
32
32
|
children: React.ReactNode;
|
|
33
33
|
/**
|
|
34
34
|
* Rendered while the session is being resolved and while a redirect is in
|
|
35
|
-
* flight. Defaults to the framework `<
|
|
35
|
+
* flight. Defaults to the framework `<AppShellSkeleton />`.
|
|
36
36
|
*/
|
|
37
37
|
fallback?: React.ReactNode;
|
|
38
38
|
/**
|