@agent-native/core 0.168.12 → 0.168.13

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.
@@ -44,6 +44,21 @@ export function markDesktopAppDownloaded(): void {
44
44
  downloadedListeners.forEach((fn) => fn());
45
45
  }
46
46
 
47
+ // A failed launch attempt (the protocol handler is gone, e.g. after an
48
+ // uninstall) reverts both flags set by markDesktopAppDownloaded, so CTAs flip
49
+ // back to the install/download state instead of staying stuck on "Open".
50
+ export function clearDesktopAppDownloaded(): void {
51
+ try {
52
+ window.localStorage?.removeItem(DESKTOP_DOWNLOADED_STORAGE_KEY);
53
+ window.localStorage?.removeItem(DESKTOP_PROMO_DISMISSED_STORAGE_KEY);
54
+ } catch {
55
+ // coercion-ok: storage access is optional; the CTA falls back to the
56
+ // stale "downloaded" label this session, and the next failed launch
57
+ // attempt retries the reset.
58
+ }
59
+ downloadedListeners.forEach((fn) => fn());
60
+ }
61
+
47
62
  export function hasDismissedDesktopPromo(): boolean {
48
63
  try {
49
64
  return (
@@ -69,7 +84,7 @@ export function markDesktopPromoDismissed(): void {
69
84
  * to query whether the protocol is registered, so we watch for the tab losing
70
85
  * focus (the app taking over) within a short window; if that never happens we
71
86
  * assume the app is not installed and navigate to the fallback. A successful
72
- * launch self-heals the stored "downloaded" flag.
87
+ * launch self-heals the stored "downloaded" flag, and a failed one clears it.
73
88
  */
74
89
  export function attemptOpenDesktopApp(fallbackHref = "/download"): void {
75
90
  if (typeof window === "undefined") return;
@@ -95,7 +110,10 @@ export function attemptOpenDesktopApp(fallbackHref = "/download"): void {
95
110
 
96
111
  window.setTimeout(() => {
97
112
  cleanup();
98
- if (!launched) window.location.href = fallbackUrl;
113
+ if (!launched) {
114
+ clearDesktopAppDownloaded();
115
+ window.location.href = fallbackUrl;
116
+ }
99
117
  }, DESKTOP_APP_LAUNCH_FALLBACK_MS);
100
118
 
101
119
  try {
@@ -218,7 +218,15 @@ export default function Root() {
218
218
  <AppToolkitProvider>
219
219
  <AppProviders
220
220
  queryClient={queryClient}
221
- toaster={<Toaster richColors position="bottom-left" closeButton />}
221
+ toaster={
222
+ <Toaster
223
+ richColors
224
+ position="bottom-left"
225
+ closeButton
226
+ offset={{ bottom: 44, left: 32 }}
227
+ mobileOffset={{ bottom: 44, left: 16 }}
228
+ />
229
+ }
222
230
  i18n={{ catalog: i18nCatalog }}
223
231
  >
224
232
  <AppContent />
@@ -2,12 +2,13 @@ import type { AgentNativeDeploymentEnvironment, AgentNativeConfig } from "../con
2
2
  export { BETA_OPT_OUT_DURATION_MS, BETA_OPT_OUT_QUERY_PARAM, BETA_OPT_OUT_STORAGE_KEY, buildEnvironmentOptOutUrl, buildEnvironmentUrl, resolveEnvironmentTargets, type EnvironmentBadgeTargets, } from "../shared/environment-lanes.js";
3
3
  export declare function isBuilderIoEmployee(email: string | null | undefined): boolean;
4
4
  export declare function isAgentNativeDesktopUserAgent(userAgent: string | undefined): boolean;
5
- export declare function resolveEnvironmentChannel(config: AgentNativeConfig, hostname: string | undefined): Extract<AgentNativeDeploymentEnvironment, "beta" | "production"> | null;
5
+ export declare function resolveEnvironmentChannel(config: AgentNativeConfig, hostname: string | undefined): Extract<AgentNativeDeploymentEnvironment, "local" | "beta" | "production"> | null;
6
6
  export declare function isBetaOptOutActive(value: string | number | null | undefined, now?: number): boolean;
7
7
  /**
8
- * First-party hosted lane switcher. Beta is intentionally visible before
9
- * authentication so a visitor can always leave beta from the sign-in page.
10
- * Production remains an internal auto-redirect lane for authenticated staff.
8
+ * Environment indicator and first-party hosted lane switcher. Beta is
9
+ * intentionally visible before authentication so a visitor can always leave
10
+ * beta from the sign-in page. Production remains an internal auto-redirect
11
+ * lane for authenticated staff.
11
12
  */
12
13
  export declare function EnvironmentBadge({ showProduction, }?: {
13
14
  showProduction?: boolean;
@@ -16,7 +16,9 @@ export function isAgentNativeDesktopUserAgent(userAgent) {
16
16
  }
17
17
  export function resolveEnvironmentChannel(config, hostname) {
18
18
  const configured = config.deployment?.environment;
19
- if (configured === "beta" || configured === "production") {
19
+ if (configured === "local" ||
20
+ configured === "beta" ||
21
+ configured === "production") {
20
22
  return configured;
21
23
  }
22
24
  const targets = resolveEnvironmentTargets(hostname);
@@ -74,6 +76,7 @@ function consumeBetaOptOutQueryParam(sourceHref, now = Date.now()) {
74
76
  }
75
77
  return active;
76
78
  }
79
+ const environmentBadgePlacementClasses = "fixed bottom-3 left-3 z-[100] h-6 min-w-0 rounded-xl px-2 text-[11px] font-semibold uppercase tracking-[0.5px] shadow-sm backdrop-blur-sm";
77
80
  function EnvironmentLink({ label, href }) {
78
81
  return (_jsx(Button, { asChild: true, className: "w-full justify-center", size: "sm", variant: "outline", children: _jsx("a", { href: href, children: label }) }));
79
82
  }
@@ -89,9 +92,12 @@ function EnvironmentBadgeContent({ environment, targets, }) {
89
92
  const title = environment === "beta"
90
93
  ? "You're on Agent Native Beta"
91
94
  : "You're on Agent Native Production";
92
- return (_jsxs(Popover, { children: [_jsx(PopoverTrigger, { asChild: true, children: _jsx(Button, { "aria-label": `Open ${title.toLowerCase()} switcher`, className: cn("fixed bottom-3 right-3 z-[100] h-6 min-w-0 rounded-xl px-2 text-[11px] font-semibold uppercase tracking-[0.5px] shadow-sm backdrop-blur-sm", environment === "beta"
95
+ return (_jsxs(Popover, { children: [_jsx(PopoverTrigger, { asChild: true, children: _jsx(Button, { "aria-label": `Open ${title.toLowerCase()} switcher`, className: cn(environmentBadgePlacementClasses, environment === "beta"
93
96
  ? "border-primary/80"
94
- : "border-border/80 bg-background/95 text-foreground"), size: "sm", variant: environment === "beta" ? "default" : "outline", children: label }) }), _jsxs(PopoverContent, { align: "center", className: "w-[280px] p-5", side: "top", sideOffset: 8, children: [_jsx("div", { className: "mb-1 text-sm font-semibold leading-5", children: title }), _jsx("div", { className: "mb-4 text-sm text-muted-foreground", children: "Choose where you want to continue." }), _jsx("div", { className: "grid gap-2", children: environment === "beta" ? (_jsx(EnvironmentLink, { href: productionHref, label: "Switch to production" })) : (_jsx(EnvironmentLink, { href: betaHref, label: "Go to beta" })) })] })] }));
97
+ : "border-border/80 bg-background/95 text-foreground"), size: "sm", variant: environment === "beta" ? "default" : "outline", children: label }) }), _jsxs(PopoverContent, { align: "start", className: "w-[280px] p-5", side: "top", sideOffset: 8, children: [_jsx("div", { className: "mb-1 text-sm font-semibold leading-5", children: title }), _jsx("div", { className: "mb-4 text-sm text-muted-foreground", children: "Choose where you want to continue." }), _jsx("div", { className: "grid gap-2", children: environment === "beta" ? (_jsx(EnvironmentLink, { href: productionHref, label: "Switch to production" })) : (_jsx(EnvironmentLink, { href: betaHref, label: "Go to beta" })) })] })] }));
98
+ }
99
+ function LocalEnvironmentBadge() {
100
+ return (_jsx("div", { "aria-label": "Local development environment", className: cn(environmentBadgePlacementClasses, "inline-flex items-center justify-center border border-border/80 bg-background/95 text-foreground"), role: "status", children: "dev" }));
95
101
  }
96
102
  function ProductionEnvironmentBadge({ targets, }) {
97
103
  const { session, status } = useSession();
@@ -128,9 +134,10 @@ function ProductionEnvironmentBadge({ targets, }) {
128
134
  return _jsx(EnvironmentBadgeContent, { environment: "production", targets: targets });
129
135
  }
130
136
  /**
131
- * First-party hosted lane switcher. Beta is intentionally visible before
132
- * authentication so a visitor can always leave beta from the sign-in page.
133
- * Production remains an internal auto-redirect lane for authenticated staff.
137
+ * Environment indicator and first-party hosted lane switcher. Beta is
138
+ * intentionally visible before authentication so a visitor can always leave
139
+ * beta from the sign-in page. Production remains an internal auto-redirect
140
+ * lane for authenticated staff.
134
141
  */
135
142
  export function EnvironmentBadge({ showProduction = true, } = {}) {
136
143
  const config = useMemo(injectedAgentNativeConfig, []);
@@ -139,10 +146,14 @@ export function EnvironmentBadge({ showProduction = true, } = {}) {
139
146
  const targets = resolveEnvironmentTargets(hostname);
140
147
  if (typeof window === "undefined" ||
141
148
  window.parent !== window ||
142
- !environment ||
143
- !targets) {
149
+ !environment) {
144
150
  return null;
145
151
  }
152
+ if (environment === "local") {
153
+ return _jsx(LocalEnvironmentBadge, {});
154
+ }
155
+ if (!targets)
156
+ return null;
146
157
  if (environment === "beta") {
147
158
  return _jsx(EnvironmentBadgeContent, { environment: "beta", targets: targets });
148
159
  }
@@ -39,7 +39,8 @@
39
39
  * toaster — custom Toaster element rendered after children.
40
40
  * Pass `null` to suppress the built-in Toaster when
41
41
  * children already include a styled one.
42
- * Defaults to `<Toaster richColors position="bottom-left" />`.
42
+ * Defaults to a rich-color bottom-left toaster raised above
43
+ * the environment badge.
43
44
  * disableThemeTransitions — passed to next-themes ThemeProvider
44
45
  * `disableTransitionOnChange`. Defaults to `true`
45
46
  * (suppresses CSS transitions during theme switches,
@@ -75,7 +76,7 @@ export interface AppProvidersProps {
75
76
  * Custom Toaster element rendered after children inside TooltipProvider.
76
77
  * Pass `null` to suppress the built-in Toaster when children already
77
78
  * include a styled one.
78
- * Defaults to `<Toaster richColors position="bottom-left" />`.
79
+ * Defaults to a rich-color bottom-left toaster raised above the environment badge.
79
80
  */
80
81
  toaster?: React.ReactNode | null;
81
82
  /**
@@ -40,7 +40,8 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
40
40
  * toaster — custom Toaster element rendered after children.
41
41
  * Pass `null` to suppress the built-in Toaster when
42
42
  * children already include a styled one.
43
- * Defaults to `<Toaster richColors position="bottom-left" />`.
43
+ * Defaults to a rich-color bottom-left toaster raised above
44
+ * the environment badge.
44
45
  * disableThemeTransitions — passed to next-themes ThemeProvider
45
46
  * `disableTransitionOnChange`. Defaults to `true`
46
47
  * (suppresses CSS transitions during theme switches,
@@ -65,7 +66,7 @@ import { AgentNativeRouteWarmup } from "./route-warmup.js";
65
66
  import { RouteTransitionIndicator } from "./RouteTransitionIndicator.js";
66
67
  import { RuntimeConfigNotice } from "./RuntimeConfigNotice.js";
67
68
  import { EMBEDDED_THEME_CHANGE_EVENT, applyEmbeddedThemeUpdate, parseEmbeddedThemeUpdate, } from "./theme.js";
68
- const DEFAULT_TOASTER = _jsx(Toaster, { richColors: true, position: "bottom-left" });
69
+ const DEFAULT_TOASTER = (_jsx(Toaster, { richColors: true, position: "bottom-left", offset: { bottom: 44, left: 32 }, mobileOffset: { bottom: 44, left: 16 } }));
69
70
  function RoutedAppEnhancements() {
70
71
  const isInRouter = useInRouterContext();
71
72
  if (!isInRouter)
@@ -62,11 +62,11 @@ export declare const postAwareness: import("h3").EventHandlerWithFetch<import("h
62
62
  error: string;
63
63
  states?: undefined;
64
64
  } | {
65
- error?: undefined;
66
65
  states: {
67
66
  clientId: number;
68
67
  state: string;
69
68
  }[];
69
+ error?: undefined;
70
70
  }>>;
71
71
  /**
72
72
  * GET /_agent-native/collab/:docId/users
@@ -77,9 +77,9 @@ export declare const getActiveUsers: import("h3").EventHandlerWithFetch<import("
77
77
  error: string;
78
78
  users?: undefined;
79
79
  } | {
80
- error?: undefined;
81
80
  users: {
82
81
  clientId: number;
83
82
  lastSeen: number;
84
83
  }[];
84
+ error?: undefined;
85
85
  }>>;
@@ -26,8 +26,8 @@ export declare const getCollabState: import("h3").EventHandlerWithFetch<import("
26
26
  * Body: { update: string (base64), requestSource?: string }
27
27
  */
28
28
  export declare const postCollabUpdate: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
29
- error: string;
30
29
  ok?: undefined;
30
+ error: string;
31
31
  } | {
32
32
  error?: undefined;
33
33
  ok: boolean;
@@ -11,14 +11,14 @@
11
11
  * DELETE /_agent-native/notifications/:id — delete
12
12
  */
13
13
  export declare function createNotificationsHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<"" | import("./types.js").Notification[] | {
14
- error?: undefined;
15
14
  count: number;
16
15
  updated?: undefined;
16
+ error?: undefined;
17
17
  ok?: undefined;
18
18
  } | {
19
- error?: undefined;
20
19
  count?: undefined;
21
20
  updated: number;
21
+ error?: undefined;
22
22
  ok?: undefined;
23
23
  } | {
24
24
  count?: undefined;
@@ -26,8 +26,8 @@ export declare function createNotificationsHandler(): import("h3").EventHandlerW
26
26
  error: string;
27
27
  ok?: undefined;
28
28
  } | {
29
- error?: undefined;
30
29
  count?: undefined;
31
30
  updated?: undefined;
31
+ error?: undefined;
32
32
  ok: boolean;
33
33
  }>>;
@@ -15,6 +15,6 @@ export declare function createProgressHandler(): import("h3").EventHandlerWithFe
15
15
  error: string;
16
16
  ok?: undefined;
17
17
  } | {
18
- ok: boolean;
19
18
  error?: undefined;
19
+ ok: boolean;
20
20
  }>>;
@@ -48,8 +48,8 @@ export declare function handleUpdateResource(event: any): Promise<import("./stor
48
48
  }>;
49
49
  /** DELETE /_agent-native/resources/:id — delete a resource */
50
50
  export declare function handleDeleteResource(event: any): Promise<{
51
- error: string;
52
51
  ok?: undefined;
52
+ error: string;
53
53
  } | {
54
54
  error?: undefined;
55
55
  ok: boolean;
@@ -22,7 +22,7 @@ const environmentSwitcherStyles = `<style ${ENVIRONMENT_SWITCHER_STYLE_MARKER}>
22
22
  color-scheme: dark;
23
23
  .environment-switcher {
24
24
  position: fixed;
25
- right: max(0.75rem, env(safe-area-inset-right));
25
+ left: max(0.75rem, env(safe-area-inset-left));
26
26
  bottom: max(0.75rem, env(safe-area-inset-bottom));
27
27
  z-index: 100;
28
28
  }
@@ -59,9 +59,10 @@ const environmentSwitcherStyles = `<style ${ENVIRONMENT_SWITCHER_STYLE_MARKER}>
59
59
  }
60
60
  .environment-popover {
61
61
  position: absolute;
62
- right: 0;
62
+ left: 0;
63
63
  bottom: calc(100% + 0.5rem);
64
64
  width: min(17.5rem, calc(100vw - 1.5rem));
65
+ box-sizing: border-box;
65
66
  padding: 1.25rem;
66
67
  background: Canvas;
67
68
  color: CanvasText;
@@ -1639,7 +1639,7 @@ ${hasMarketing
1639
1639
  /* guard:allow-raw-color - standalone auth HTML has no app theme token layer */
1640
1640
  .environment-switcher {
1641
1641
  position: fixed;
1642
- right: max(0.75rem, env(safe-area-inset-right));
1642
+ left: max(0.75rem, env(safe-area-inset-left));
1643
1643
  bottom: max(0.75rem, env(safe-area-inset-bottom));
1644
1644
  z-index: 100;
1645
1645
  }
@@ -1674,9 +1674,10 @@ ${hasMarketing
1674
1674
  }
1675
1675
  .environment-popover {
1676
1676
  position: absolute;
1677
- right: 0;
1677
+ left: 0;
1678
1678
  bottom: calc(100% + 0.5rem);
1679
1679
  width: min(17.5rem, calc(100vw - 1.5rem));
1680
+ box-sizing: border-box;
1680
1681
  padding: 1.25rem;
1681
1682
  background: #141414;
1682
1683
  color: #fff;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-native/core",
3
- "version": "0.168.12",
3
+ "version": "0.168.13",
4
4
  "description": "Framework for agent-native application development — where AI agents and UI share SQL state, actions, and context",
5
5
  "homepage": "https://github.com/BuilderIO/agent-native#readme",
6
6
  "bugs": {