@agent-native/core 0.178.1 → 0.178.2-nightly-20260910195009

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.
Files changed (48) hide show
  1. package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +108 -121
  2. package/corpus/templates/assets/app/components/layout/Sidebar.tsx +87 -220
  3. package/corpus/templates/brain/app/components/layout/Sidebar.tsx +83 -221
  4. package/corpus/templates/calendar/app/components/layout/Sidebar.tsx +324 -460
  5. package/corpus/templates/chat/app/components/layout/Sidebar.tsx +57 -247
  6. package/corpus/templates/clips/app/components/library/library-layout.tsx +15 -48
  7. package/corpus/templates/clips/changelog/2026-09-10-open-mcp-connections-in-new-tabs.md +1 -0
  8. package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +84 -94
  9. package/corpus/templates/crm/app/components/layout/CrmLayout.tsx +1 -1
  10. package/corpus/templates/crm/app/components/layout/CrmSidebar.tsx +108 -296
  11. package/corpus/templates/design/app/components/layout/Sidebar.tsx +55 -212
  12. package/corpus/templates/factory/app/components/layout/Sidebar.tsx +72 -260
  13. package/corpus/templates/forms/app/components/layout/Sidebar.tsx +143 -362
  14. package/corpus/templates/macros/app/components/layout/AppLayout.tsx +64 -159
  15. package/corpus/templates/mail/app/components/layout/AppLayout.tsx +152 -165
  16. package/corpus/templates/plan/app/components/layout/Sidebar.tsx +82 -207
  17. package/corpus/templates/slides/app/components/layout/Sidebar.tsx +61 -234
  18. package/corpus/templates/tasks/app/components/layout/Sidebar.tsx +73 -225
  19. package/dist/automation/index.d.ts +1 -1
  20. package/dist/client/EnvironmentBadge.d.ts +7 -4
  21. package/dist/client/EnvironmentBadge.js +26 -18
  22. package/dist/client/FeedbackButton.js +23 -8
  23. package/dist/client/app-providers.js +1 -1
  24. package/dist/client/auth/AuthPage.js +0 -38
  25. package/dist/client/components/icons/AgentNativeIcon.js +1 -1
  26. package/dist/client/index.d.ts +1 -0
  27. package/dist/client/index.js +1 -0
  28. package/dist/client/ui/AppSidebar.d.ts +15 -0
  29. package/dist/client/ui/AppSidebar.js +34 -0
  30. package/dist/client/ui/index.d.ts +1 -0
  31. package/dist/client/ui/index.js +1 -0
  32. package/dist/collab/awareness.d.ts +2 -2
  33. package/dist/collab/struct-routes.d.ts +1 -1
  34. package/dist/config.d.ts +4 -0
  35. package/dist/config.js +19 -3
  36. package/dist/progress/routes.d.ts +1 -1
  37. package/dist/provider-api/actions/custom-provider-registration.d.ts +9 -9
  38. package/dist/provider-api/actions/provider-api.d.ts +15 -15
  39. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  40. package/dist/resources/handlers.d.ts +1 -1
  41. package/dist/secrets/routes.d.ts +9 -9
  42. package/dist/server/beta-opt-out-html.js +2 -2
  43. package/dist/templates/chat/app/components/layout/Sidebar.tsx +57 -247
  44. package/dist/vite/client.js +4 -1
  45. package/docs/content/template-clips-anonymous-intake.mdx +7 -5
  46. package/docs/content/template-clips-developers.mdx +1 -1
  47. package/package.json +3 -3
  48. package/src/templates/chat/app/components/layout/Sidebar.tsx +57 -247
@@ -1,36 +1,27 @@
1
1
  import { useT } from "@agent-native/core/client/i18n";
2
2
  import { openCommandMenu } from "@agent-native/core/client/navigation";
3
3
  import { OrgSwitcher } from "@agent-native/core/client/org";
4
- import { AgentNativeIcon, FeedbackButton } from "@agent-native/core/client/ui";
5
- import { SidebarFooterActions } from "@agent-native/toolkit/app-shell";
4
+ import {
5
+ AppSidebar,
6
+ FeedbackButton,
7
+ type AppSidebarItemDefinition,
8
+ } from "@agent-native/core/client/ui";
6
9
  import {
7
10
  IconCheckbox,
8
11
  IconForms,
9
12
  IconInbox,
10
- IconLayoutSidebarLeftCollapse,
11
- IconLayoutSidebarLeftExpand,
12
13
  IconSearch,
13
14
  IconSettings,
14
15
  } from "@tabler/icons-react";
15
- import { Link, useLocation } from "react-router";
16
+ import { useLocation } from "react-router";
16
17
 
18
+ import { Button } from "@/components/ui/button";
17
19
  import {
18
20
  Tooltip,
19
21
  TooltipContent,
20
22
  TooltipTrigger,
21
23
  } from "@/components/ui/tooltip";
22
24
  import { APP_TITLE } from "@/lib/app-config";
23
- import { cn } from "@/lib/utils";
24
-
25
- const NAV_ITEMS = [
26
- { icon: IconInbox, labelKey: "sidebar.navInbox", href: "/inbox" },
27
- { icon: IconCheckbox, labelKey: "sidebar.navTasks", href: "/tasks" },
28
- { icon: IconForms, labelKey: "sidebar.navFields", href: "/fields" },
29
- ];
30
-
31
- const BOTTOM_NAV_ITEMS = [
32
- { icon: IconSettings, labelKey: "header.pageSettings", href: "/settings" },
33
- ];
34
25
 
35
26
  interface SidebarProps {
36
27
  collapsed?: boolean;
@@ -45,228 +36,85 @@ export function Sidebar({
45
36
  }: SidebarProps) {
46
37
  const t = useT();
47
38
  const location = useLocation();
48
- const ToggleIcon = collapsed
49
- ? IconLayoutSidebarLeftExpand
50
- : IconLayoutSidebarLeftCollapse;
51
- const navClass = ({ isActive }: { isActive: boolean }) =>
52
- cn(
53
- "flex items-center text-sm transition-colors",
54
- collapsed
55
- ? "relative h-10 w-full justify-center rounded-none border-l-2 px-0"
56
- : "h-9 rounded-md gap-3 px-3",
57
- isActive
58
- ? collapsed
59
- ? "border-l-sidebar-accent-foreground/80 bg-sidebar-accent text-sidebar-accent-foreground"
60
- : "bg-sidebar-accent text-sidebar-accent-foreground"
61
- : collapsed
62
- ? "border-l-transparent text-sidebar-foreground/70 hover:bg-sidebar-accent/55 hover:text-sidebar-accent-foreground"
63
- : "text-sidebar-foreground hover:bg-sidebar-accent/65 hover:text-sidebar-accent-foreground",
64
- );
65
- const collapseButton = collapsible ? (
66
- <Tooltip>
67
- <TooltipTrigger asChild>
68
- <button
69
- type="button"
70
- onClick={() => onCollapsedChange?.(!collapsed)}
71
- className={cn(
72
- "flex shrink-0 items-center justify-center rounded-md text-sidebar-foreground/65 transition-colors hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
73
- collapsed ? "size-8" : "size-7",
74
- )}
75
- aria-label={
76
- collapsed
77
- ? t("sidebar.expandSidebar")
78
- : t("sidebar.collapseSidebar")
79
- }
80
- >
81
- <ToggleIcon className="size-4" />
82
- </button>
83
- </TooltipTrigger>
84
- <TooltipContent side="right">
85
- {collapsed ? t("sidebar.expandSidebar") : t("sidebar.collapseSidebar")}
86
- </TooltipContent>
87
- </Tooltip>
88
- ) : null;
39
+
40
+ const items: AppSidebarItemDefinition[] = [
41
+ {
42
+ to: "/inbox",
43
+ label: t("sidebar.navInbox"),
44
+ icon: IconInbox,
45
+ active: location.pathname.startsWith("/inbox"),
46
+ },
47
+ {
48
+ to: "/tasks",
49
+ label: t("sidebar.navTasks"),
50
+ icon: IconCheckbox,
51
+ active: location.pathname.startsWith("/tasks"),
52
+ },
53
+ {
54
+ to: "/fields",
55
+ label: t("sidebar.navFields"),
56
+ icon: IconForms,
57
+ active: location.pathname.startsWith("/fields"),
58
+ },
59
+ ];
60
+
61
+ const secondaryItems: AppSidebarItemDefinition[] = [
62
+ {
63
+ to: "/settings",
64
+ label: t("header.pageSettings"),
65
+ icon: IconSettings,
66
+ active: location.pathname.startsWith("/settings"),
67
+ },
68
+ ];
69
+
70
+ const feedbackButton = (
71
+ <FeedbackButton variant={collapsed ? "icon" : "sidebar"} side="right" />
72
+ );
73
+
74
+ const orgSwitcher = (
75
+ <OrgSwitcher
76
+ compact={collapsed}
77
+ reserveSpace
78
+ // Tasks does not mount /agent, so the default link would 404.
79
+ agentPath={null}
80
+ className={
81
+ collapsed
82
+ ? "!size-9 !p-0 [&>svg]:!size-4 !bg-transparent !text-primary hover:!bg-accent/60 hover:!text-primary"
83
+ : "min-w-0 flex-1 !bg-transparent !text-primary hover:!bg-accent/60 hover:!text-primary"
84
+ }
85
+ />
86
+ );
87
+
89
88
  const searchButton = (
90
89
  <Tooltip>
91
90
  <TooltipTrigger asChild>
92
- <button
91
+ <Button
93
92
  type="button"
93
+ variant="ghost"
94
+ size="icon"
95
+ className="size-9 shrink-0 text-primary hover:bg-accent/60 hover:text-primary"
94
96
  onClick={openCommandMenu}
95
97
  aria-label={t("sidebar.search")}
96
- className="flex size-8 items-center justify-center rounded-md text-sidebar-foreground/65 transition-colors hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
97
98
  >
98
99
  <IconSearch className="size-4" />
99
- </button>
100
+ </Button>
100
101
  </TooltipTrigger>
101
102
  <TooltipContent side="right">{t("sidebar.search")}</TooltipContent>
102
103
  </Tooltip>
103
104
  );
104
- const feedbackButton = (
105
- <FeedbackButton
106
- variant={collapsed ? "icon" : "sidebar"}
107
- className={collapsed ? "size-8" : "min-w-0"}
108
- side="right"
109
- />
110
- );
111
105
 
112
106
  return (
113
- <aside
114
- data-collapsed={collapsed ? "true" : "false"}
115
- className={cn(
116
- "flex h-full min-w-0 shrink-0 flex-col overflow-hidden border-r border-sidebar-border bg-sidebar text-sidebar-foreground transition-[width] duration-150",
117
- collapsed ? "w-12" : "w-60",
118
- )}
119
- >
120
- <div
121
- className={cn(
122
- "flex shrink-0 items-center border-b border-sidebar-border",
123
- collapsed ? "h-12 justify-center px-0" : "h-14 px-3",
124
- )}
125
- >
126
- <Link
127
- to="/tasks"
128
- onClick={(event) => {
129
- if (
130
- !collapsible ||
131
- !onCollapsedChange ||
132
- event.metaKey ||
133
- event.ctrlKey ||
134
- event.shiftKey ||
135
- event.altKey ||
136
- event.button !== 0
137
- ) {
138
- return;
139
- }
140
- event.preventDefault();
141
- onCollapsedChange(!collapsed);
142
- }}
143
- className={cn(
144
- "flex min-w-0 items-center rounded outline-none focus-visible:ring-2 focus-visible:ring-ring",
145
- collapsed ? "size-7 justify-center" : "flex-1 gap-3",
146
- )}
147
- aria-label={
148
- collapsible && onCollapsedChange
149
- ? collapsed
150
- ? t("sidebar.expandSidebar")
151
- : t("sidebar.collapseSidebar")
152
- : collapsed
153
- ? APP_TITLE
154
- : undefined
155
- }
156
- >
157
- <AgentNativeIcon
158
- aria-hidden="true"
159
- className="h-3.5 w-6 shrink-0 text-sidebar-accent-foreground"
160
- />
161
- <div className={cn("min-w-0", collapsed && "sr-only")}>
162
- <p className="truncate text-sm font-semibold text-sidebar-accent-foreground">
163
- {APP_TITLE}
164
- </p>
165
- </div>
166
- </Link>
167
- </div>
168
-
169
- <nav
170
- className={cn(
171
- "flex-1 overflow-y-auto",
172
- collapsed ? "px-0 py-2" : "px-2 py-3",
173
- )}
174
- >
175
- <div className={cn("grid", collapsed ? "gap-0" : "gap-1")}>
176
- {NAV_ITEMS.map((item) => {
177
- const Icon = item.icon;
178
- const isActive = location.pathname.startsWith(item.href);
179
- const label = t(item.labelKey);
180
- const link = (
181
- <Link
182
- to={item.href}
183
- className={navClass({ isActive })}
184
- aria-current={isActive ? "page" : undefined}
185
- aria-label={collapsed ? label : undefined}
186
- >
187
- <Icon className="size-4 shrink-0" />
188
- <span className={collapsed ? "sr-only" : "truncate"}>
189
- {label}
190
- </span>
191
- </Link>
192
- );
193
- return collapsed ? (
194
- <Tooltip key={item.href}>
195
- <TooltipTrigger asChild>{link}</TooltipTrigger>
196
- <TooltipContent side="right">{label}</TooltipContent>
197
- </Tooltip>
198
- ) : (
199
- <div key={item.href}>{link}</div>
200
- );
201
- })}
202
- </div>
203
- </nav>
204
-
205
- <div
206
- className={cn(
207
- "mt-auto shrink-0",
208
- collapsed && "border-t border-sidebar-border py-2",
209
- )}
210
- >
211
- <nav
212
- className={cn(
213
- "grid gap-1",
214
- collapsed
215
- ? "px-1 py-1 empty:hidden"
216
- : "border-t border-sidebar-border px-3 py-2 empty:hidden",
217
- )}
218
- >
219
- {BOTTOM_NAV_ITEMS.map((item) => {
220
- const Icon = item.icon;
221
- const isActive = location.pathname.startsWith(item.href);
222
- return (
223
- <Link
224
- key={item.href}
225
- to={item.href}
226
- aria-label={collapsed ? t(item.labelKey) : undefined}
227
- className={cn(
228
- "flex items-center rounded-md text-sm",
229
- collapsed ? "h-9 justify-center px-0" : "gap-3 px-3 py-2",
230
- isActive
231
- ? "bg-sidebar-accent text-sidebar-accent-foreground"
232
- : "text-sidebar-foreground hover:bg-sidebar-accent/65 hover:text-sidebar-accent-foreground",
233
- )}
234
- >
235
- <Icon className="size-4 shrink-0" />
236
- <span className={collapsed ? "sr-only" : "truncate"}>
237
- {t(item.labelKey)}
238
- </span>
239
- </Link>
240
- );
241
- })}
242
- </nav>
243
-
244
- <div
245
- className={cn(
246
- collapsed
247
- ? "px-1 py-1"
248
- : "border-t border-sidebar-border px-3 py-2",
249
- )}
250
- >
251
- <OrgSwitcher
252
- reserveSpace
253
- // Tasks does not mount /agent, so the default link would 404.
254
- agentPath={null}
255
- className={
256
- collapsed
257
- ? "h-8 justify-center px-0 [&>span]:sr-only [&>svg:last-child]:hidden"
258
- : undefined
259
- }
260
- />
261
- </div>
262
-
263
- <SidebarFooterActions
264
- collapsed={collapsed}
265
- feedback={feedbackButton}
266
- search={searchButton}
267
- collapse={collapseButton}
268
- />
269
- </div>
270
- </aside>
107
+ <AppSidebar
108
+ collapsed={collapsed}
109
+ collapsible={collapsible}
110
+ onCollapsedChange={onCollapsedChange}
111
+ brandName={APP_TITLE}
112
+ brandHref="/tasks"
113
+ items={items}
114
+ secondaryItems={secondaryItems}
115
+ feedback={feedbackButton}
116
+ orgSwitcher={orgSwitcher}
117
+ footerExtras={searchButton}
118
+ />
271
119
  );
272
120
  }
@@ -186,8 +186,8 @@ export declare function createAutomationCallbackHandler(runtime: AutomationRunti
186
186
  duplicate?: undefined;
187
187
  eventId?: undefined;
188
188
  } | {
189
- error?: undefined;
190
189
  accepted: boolean;
191
190
  duplicate: boolean;
192
191
  eventId: string;
192
+ error?: undefined;
193
193
  }>;
@@ -5,13 +5,16 @@ export declare function isAgentNativeDesktopUserAgent(userAgent: string | undefi
5
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
  export type EnvironmentBadgePlacement = "fixed" | "inline";
8
+ export interface EnvironmentBadgeProps {
9
+ placement?: EnvironmentBadgePlacement;
10
+ showProduction?: boolean;
11
+ badgeText?: string;
12
+ className?: string;
13
+ }
8
14
  /**
9
15
  * Environment indicator and first-party hosted lane switcher. Beta is
10
16
  * intentionally visible before authentication so a visitor can always leave
11
17
  * beta from the sign-in page. Production remains an internal auto-redirect
12
18
  * lane for authenticated staff.
13
19
  */
14
- export declare function EnvironmentBadge({ placement, showProduction, }?: {
15
- placement?: EnvironmentBadgePlacement;
16
- showProduction?: boolean;
17
- }): import("react").JSX.Element | null;
20
+ export declare function EnvironmentBadge({ placement, showProduction, badgeText, className, }?: EnvironmentBadgeProps): import("react").JSX.Element | null;
@@ -117,8 +117,10 @@ const environmentBadgePlacementClasses = {
117
117
  function EnvironmentLink({ label, href }) {
118
118
  return (_jsx(Button, { asChild: true, className: "w-full justify-center", size: "sm", variant: "outline", children: _jsx("a", { href: href, children: label }) }));
119
119
  }
120
- function EnvironmentBadgeContent({ environment, placement, targets, }) {
120
+ function EnvironmentBadgeContent({ environment, placement, targets, badgeText, className, }) {
121
121
  const [isHidden, setIsHidden] = useState(false);
122
+ const { session } = useSession();
123
+ const isBuilder = isBuilderIoEmployee(session?.email);
122
124
  if (typeof window === "undefined")
123
125
  return null;
124
126
  if (isHidden)
@@ -128,18 +130,24 @@ function EnvironmentBadgeContent({ environment, placement, targets, }) {
128
130
  const productionHref = buildEnvironmentOptOutUrl(currentHref, targets.productionHost);
129
131
  if (environment === "beta" ? !productionHref : !betaHref)
130
132
  return null;
131
- const label = environment === "beta" ? "beta" : "prod";
133
+ const label = badgeText ?? "alpha";
132
134
  const title = environment === "beta"
133
- ? "You're on Agent-Native Beta"
135
+ ? `You're on Agent-Native ${label.charAt(0).toUpperCase() + label.slice(1)}`
134
136
  : "You're on Agent-Native Production";
135
- return (_jsxs(Popover, { children: [_jsx(PopoverTrigger, { asChild: true, children: _jsx(Button, { "aria-label": `Open ${title.toLowerCase()} switcher`, className: cn(environmentBadgePlacementClasses[placement], environment === "beta"
136
- ? "border-primary/80"
137
- : "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: placement === "inline" ? "bottom" : "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." }), _jsxs("div", { className: "grid gap-2", children: [environment === "beta" ? (_jsx(EnvironmentLink, { href: productionHref, label: "Switch to production" })) : (_jsx(EnvironmentLink, { href: betaHref, label: "Go to beta" })), _jsx(Button, { className: "mt-2 -mb-2 w-full justify-center text-muted-foreground", onClick: () => setIsHidden(true), size: "sm", type: "button", variant: "ghost", children: "Hide badge" })] })] })] }));
137
+ const badgeClasses = cn(environmentBadgePlacementClasses[placement], environment === "beta"
138
+ ? "border-primary/80"
139
+ : "border-border/80 bg-background/95 text-foreground", className);
140
+ if (!isBuilder) {
141
+ return (_jsx("div", { "aria-label": title, className: cn(badgeClasses, "inline-flex items-center justify-center select-none", environment === "beta"
142
+ ? "border-primary/80 bg-primary text-primary-foreground"
143
+ : "border-border/80 bg-background/95 text-foreground"), role: "status", children: label }));
144
+ }
145
+ return (_jsxs(Popover, { children: [_jsx(PopoverTrigger, { asChild: true, children: _jsx(Button, { "aria-label": `Open ${title.toLowerCase()} switcher`, className: badgeClasses, size: "sm", variant: environment === "beta" ? "default" : "outline", children: label }) }), _jsxs(PopoverContent, { align: "start", className: "w-[280px] p-5", side: placement === "inline" ? "bottom" : "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." }), _jsxs("div", { className: "grid gap-2", children: [environment === "beta" ? (_jsx(EnvironmentLink, { href: productionHref, label: "Switch to production" })) : (_jsx(EnvironmentLink, { href: betaHref, label: "Go to beta" })), _jsx(Button, { className: "mt-2 -mb-2 w-full justify-center text-muted-foreground", onClick: () => setIsHidden(true), size: "sm", type: "button", variant: "ghost", children: "Hide badge" })] })] })] }));
138
146
  }
139
- function LocalEnvironmentBadge({ placement, }) {
140
- return (_jsx("div", { "aria-label": "Local development environment", className: cn(environmentBadgePlacementClasses[placement], "inline-flex items-center justify-center border border-border/80 bg-background/95 text-foreground"), role: "status", children: "dev" }));
147
+ function LocalEnvironmentBadge({ placement, badgeText = "alpha", className, }) {
148
+ return (_jsx("div", { "aria-label": "Local development environment", className: cn(environmentBadgePlacementClasses[placement], "inline-flex items-center justify-center border border-border/80 bg-background/95 text-foreground select-none", className), role: "status", children: badgeText }));
141
149
  }
142
- function ProductionEnvironmentBadge({ placement, targets, }) {
150
+ function ProductionEnvironmentBadge({ placement, targets, badgeText, className, }) {
143
151
  const { session, status } = useSession();
144
152
  const isEligible = typeof window !== "undefined" &&
145
153
  window.parent === window &&
@@ -174,9 +182,7 @@ function ProductionEnvironmentBadge({ placement, targets, }) {
174
182
  });
175
183
  window.location.replace(betaHref);
176
184
  }, [isEligible, session?.email, status, targets.betaHost]);
177
- if (!isEligible)
178
- return null;
179
- return (_jsx(EnvironmentBadgeContent, { environment: "production", placement: placement, targets: targets }));
185
+ return (_jsx(EnvironmentBadgeContent, { environment: "production", placement: placement, targets: targets, badgeText: badgeText, className: className }));
180
186
  }
181
187
  /**
182
188
  * Environment indicator and first-party hosted lane switcher. Beta is
@@ -184,12 +190,13 @@ function ProductionEnvironmentBadge({ placement, targets, }) {
184
190
  * beta from the sign-in page. Production remains an internal auto-redirect
185
191
  * lane for authenticated staff.
186
192
  */
187
- export function EnvironmentBadge({ placement = "fixed", showProduction = true, } = {}) {
193
+ export function EnvironmentBadge({ placement = "fixed", showProduction = true, badgeText, className, } = {}) {
188
194
  const [hydrated, setHydrated] = useState(false);
189
195
  const config = useMemo(injectedAgentNativeConfig, []);
190
196
  const hostname = typeof window === "undefined" ? undefined : window.location.hostname;
191
197
  const environment = resolveEnvironmentChannel(config, hostname);
192
198
  const targets = resolveEnvironmentTargets(hostname);
199
+ const resolvedBadgeText = badgeText ?? config.deployment?.badgeText ?? config.badgeText ?? "alpha";
193
200
  useEffect(() => {
194
201
  setHydrated(true);
195
202
  }, []);
@@ -200,14 +207,15 @@ export function EnvironmentBadge({ placement = "fixed", showProduction = true, }
200
207
  return null;
201
208
  }
202
209
  if (environment === "local") {
203
- return _jsx(LocalEnvironmentBadge, { placement: placement });
210
+ return (_jsx(LocalEnvironmentBadge, { placement: placement, badgeText: resolvedBadgeText, className: className }));
211
+ }
212
+ if (!targets) {
213
+ return (_jsx("div", { "aria-label": "Development environment", className: cn(environmentBadgePlacementClasses[placement], "inline-flex items-center justify-center border border-border/80 bg-background/95 text-foreground select-none", className), role: "status", children: resolvedBadgeText }));
204
214
  }
205
- if (!targets)
206
- return null;
207
215
  if (environment === "beta") {
208
- return (_jsx(EnvironmentBadgeContent, { environment: "beta", placement: placement, targets: targets }));
216
+ return (_jsx(EnvironmentBadgeContent, { environment: "beta", placement: placement, targets: targets, badgeText: resolvedBadgeText, className: className }));
209
217
  }
210
218
  if (!showProduction)
211
219
  return null;
212
- return _jsx(ProductionEnvironmentBadge, { placement: placement, targets: targets });
220
+ return (_jsx(ProductionEnvironmentBadge, { placement: placement, targets: targets, badgeText: resolvedBadgeText, className: className }));
213
221
  }
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import * as PopoverPrimitive from "@radix-ui/react-popover";
3
3
  import * as TooltipPrimitive from "@radix-ui/react-tooltip";
4
- import { IconMessage2, IconCheck } from "@tabler/icons-react";
4
+ import { IconCheck, IconMessageCircle } from "@tabler/icons-react";
5
5
  import { useState, useEffect, useRef, useCallback, } from "react";
6
6
  import { DEFAULT_LOCALE } from "../localization/shared.js";
7
7
  import { getFeedbackClientContext } from "./feedback-context.js";
@@ -15,7 +15,7 @@ function isSyntheticAgentNativeAnonymousEmail(value) {
15
15
  }
16
16
  const FEEDBACK_COPY = {
17
17
  "en-US": {
18
- label: "Feedback",
18
+ label: "Send feedback",
19
19
  placeholder: "What's working, what's broken, or what would you change?",
20
20
  submit: "Send feedback",
21
21
  submitting: "Sending...",
@@ -261,9 +261,19 @@ function clientHostname() {
261
261
  return location?.hostname;
262
262
  }
263
263
  function isFirstPartyHostname(hostname) {
264
- const normalized = hostname?.trim().toLowerCase();
264
+ const normalized = hostname?.trim().toLowerCase().split(":")[0];
265
265
  return (normalized === FIRST_PARTY_HOSTNAME ||
266
- normalized?.endsWith(`.${FIRST_PARTY_HOSTNAME}`) === true);
266
+ normalized?.endsWith(`.${FIRST_PARTY_HOSTNAME}`) === true ||
267
+ normalized?.endsWith(".netlify.app") === true ||
268
+ normalized?.endsWith(".builder.io") === true);
269
+ }
270
+ function isLocalDevHostname(hostname) {
271
+ const normalized = hostname?.trim().toLowerCase().split(":")[0];
272
+ return (normalized === "localhost" ||
273
+ normalized === "127.0.0.1" ||
274
+ normalized === "[::1]" ||
275
+ normalized === "0.0.0.0" ||
276
+ normalized?.endsWith(".local") === true);
267
277
  }
268
278
  function isLegacyFeedbackPageUrl(value, hostname) {
269
279
  const hasScheme = /^[a-z][a-z\d+.-]*:/i.test(value);
@@ -284,7 +294,11 @@ export function resolveFeedbackUrl(url, hostname = clientHostname()) {
284
294
  }
285
295
  if (url !== undefined)
286
296
  return null;
287
- return isFirstPartyHostname(hostname) ? FIRST_PARTY_FEEDBACK_URL : null;
297
+ // Local template/dev hosts are first-party too — otherwise the sidebar
298
+ // feedback row vanishes on 127.0.0.1 even though every app wires it.
299
+ return isFirstPartyHostname(hostname) || isLocalDevHostname(hostname)
300
+ ? FIRST_PARTY_FEEDBACK_URL
301
+ : null;
288
302
  }
289
303
  export function FeedbackButton(props) {
290
304
  const [mounted, setMounted] = useState(false);
@@ -392,13 +406,14 @@ function FeedbackPopoverButton({ variant = "sidebar", label, url, className, sid
392
406
  trigger = (_jsx(PopoverPrimitive.Trigger, { asChild: true, children: customTrigger }));
393
407
  }
394
408
  else if (variant === "icon") {
395
- trigger = (_jsx(TooltipPrimitive.Provider, { delayDuration: 200, children: _jsxs(TooltipPrimitive.Root, { children: [_jsx(TooltipPrimitive.Trigger, { asChild: true, children: _jsx(PopoverPrimitive.Trigger, { asChild: true, children: _jsx("button", { type: "button", "aria-label": resolvedLabel, className: cn("flex h-6 w-6 items-center justify-center rounded text-muted-foreground hover:text-foreground hover:bg-accent/50", className), children: _jsx(IconMessage2, { size: 14 }) }) }) }), _jsx(TooltipPrimitive.Portal, { children: _jsx(TooltipPrimitive.Content, { sideOffset: 6, className: "z-[100040] overflow-hidden rounded-md border border-border bg-popover px-2 py-1 text-[11px] text-foreground shadow-md animate-in fade-in-0 zoom-in-95 origin-[var(--radix-tooltip-content-transform-origin)]", children: resolvedLabel }) })] }) }));
409
+ trigger = (_jsx(TooltipPrimitive.Provider, { delayDuration: 200, children: _jsxs(TooltipPrimitive.Root, { children: [_jsx(TooltipPrimitive.Trigger, { asChild: true, children: _jsx(PopoverPrimitive.Trigger, { asChild: true, children: _jsx("button", { type: "button", "aria-label": resolvedLabel, className: cn("flex size-9 items-center justify-center rounded-md bg-transparent text-primary hover:bg-accent/60 hover:text-primary", className), children: _jsx(IconMessageCircle, { className: "size-4 shrink-0 text-primary" }) }) }) }), _jsx(TooltipPrimitive.Portal, { children: _jsx(TooltipPrimitive.Content, { sideOffset: 6, className: "z-[100040] overflow-hidden rounded-md border border-border bg-popover px-2 py-1 text-[11px] text-foreground shadow-md animate-in fade-in-0 zoom-in-95 origin-[var(--radix-tooltip-content-transform-origin)]", children: resolvedLabel }) })] }) }));
396
410
  }
397
411
  else if (variant === "outlined") {
398
- trigger = (_jsx(PopoverPrimitive.Trigger, { asChild: true, children: _jsxs("button", { type: "button", "aria-label": resolvedLabel, className: cn("flex h-8 items-center gap-2 rounded-md border border-border bg-transparent px-3 text-sm text-muted-foreground transition hover:border-foreground/40 hover:text-foreground", className), children: [_jsx(IconMessage2, { size: 14, stroke: 1.5 }), _jsx("span", { children: resolvedLabel })] }) }));
412
+ trigger = (_jsx(PopoverPrimitive.Trigger, { asChild: true, children: _jsxs("button", { type: "button", "aria-label": resolvedLabel, className: cn("flex h-8 items-center gap-2 rounded-md border border-border bg-transparent px-3 text-sm text-muted-foreground transition hover:border-foreground/40 hover:text-foreground", className), children: [_jsx(IconMessageCircle, { size: 14, stroke: 1.5 }), _jsx("span", { children: resolvedLabel })] }) }));
399
413
  }
400
414
  else {
401
- trigger = (_jsx(PopoverPrimitive.Trigger, { asChild: true, children: _jsxs("button", { type: "button", className: cn("flex w-full items-center gap-3 rounded-lg px-3 py-2 text-sm text-muted-foreground transition-colors hover:bg-accent/50 hover:text-foreground", className), children: [_jsx(IconMessage2, { className: "h-4 w-4" }), _jsx("span", { children: resolvedLabel })] }) }));
415
+ // Sidebar variant matches Clips footer feedback row spacing/density.
416
+ trigger = (_jsx(PopoverPrimitive.Trigger, { asChild: true, children: _jsxs("button", { type: "button", className: cn("flex h-auto w-full items-center justify-start gap-2 rounded bg-transparent px-2 py-1.5 text-xs font-normal text-primary hover:bg-accent/60 hover:text-primary", className), children: [_jsx(IconMessageCircle, { className: "size-4 shrink-0 text-primary" }), _jsx("span", { children: resolvedLabel })] }) }));
402
417
  }
403
418
  const resolvedSide = side ?? (variant === "sidebar" ? "top" : "bottom");
404
419
  const resolvedPlaceholder = placeholder ?? copy.placeholder;
@@ -169,7 +169,7 @@ function ProvidersInner({ queryClient, defaultTheme = "system", themeAttribute =
169
169
  const localizedChildren = i18n === false ? (children) : (_jsx(AgentNativeI18nProvider, { ...(i18n ?? {}), children: children }));
170
170
  return (_jsx(QueryClientProvider, { client: queryClient, children: _jsxs(ThemeProvider, { attribute: themeAttribute, defaultTheme: defaultTheme, enableSystem: true, disableTransitionOnChange: disableThemeTransitions, children: [_jsx(EmbeddedThemeSync, {}), _jsxs(TooltipProvider, { delayDuration: tooltipDelayDuration, children: [!disableWebMcp && _jsx(AgentNativeWebMcpActionRegistration, {}), localizedChildren, _jsx(DocumentTitleGuard, { fallbackTitle: documentTitleFallback }), _jsx(RuntimeConfigNotice, {}), _jsx(RoutedAppEnhancements, {}), showEnvironmentBadge ? (_jsx(EnvironmentBadge, { showProduction: showProductionEnvironmentBadge })) : null, toaster] })] }) }));
171
171
  }
172
- export function AppProviders({ queryClient, isPublicPath = false, clientOnlyFallback, sessionBypass = false, disableWebMcp = false, showEnvironmentBadge = true, defaultTheme, themeAttribute, tooltipDelayDuration, toaster, disableThemeTransitions, i18n, documentTitleFallback, children, }) {
172
+ export function AppProviders({ queryClient, isPublicPath = false, clientOnlyFallback, sessionBypass = false, disableWebMcp = false, showEnvironmentBadge = false, defaultTheme, themeAttribute, tooltipDelayDuration, toaster, disableThemeTransitions, i18n, documentTitleFallback, children, }) {
173
173
  const fallback = clientOnlyFallback ?? _jsx(DefaultSpinner, {});
174
174
  if (isPublicPath) {
175
175
  return (_jsx(ProvidersInner, { queryClient: queryClient, defaultTheme: defaultTheme, themeAttribute: themeAttribute, tooltipDelayDuration: tooltipDelayDuration, toaster: toaster, disableThemeTransitions: disableThemeTransitions, disableWebMcp: disableWebMcp, i18n: i18n, documentTitleFallback: documentTitleFallback, showProductionEnvironmentBadge: false, showEnvironmentBadge: showEnvironmentBadge, children: children }));
@@ -474,9 +474,6 @@ export function AuthPage(props) {
474
474
  }, []);
475
475
  const [googleBusy, setGoogleBusy] = React.useState(false);
476
476
  const [magicLinkBusy, setMagicLinkBusy] = React.useState(false);
477
- const [environmentVisible, setEnvironmentVisible] = React.useState(false);
478
- const [environmentOpen, setEnvironmentOpen] = React.useState(false);
479
- const [environmentProductionUrl, setEnvironmentProductionUrl] = React.useState("");
480
477
  const [copiedLocalMode, setCopiedLocalMode] = React.useState(false);
481
478
  const signupViewTrackedRef = React.useRef(false);
482
479
  const pendingSignupPassword = React.useRef("");
@@ -888,30 +885,6 @@ export function AuthPage(props) {
888
885
  // coercion-ok: beta controls are optional.
889
886
  }
890
887
  }, [localDevAllowed, props]);
891
- React.useEffect(() => {
892
- if (window.parent !== window)
893
- return;
894
- const hostname = window.location.hostname.toLowerCase().replace(/\.$/, "");
895
- const productionHost = hostname.startsWith("beta.")
896
- ? hostname.slice("beta.".length)
897
- : "";
898
- if (!productionHost ||
899
- props.environmentBetaHosts[productionHost] !== hostname) {
900
- return;
901
- }
902
- try {
903
- const productionUrl = new URL(window.location.href);
904
- productionUrl.protocol = "https:";
905
- productionUrl.hostname = productionHost;
906
- productionUrl.port = "";
907
- productionUrl.searchParams.set(props.betaOptOutQueryParam, String(Date.now() + props.betaOptOutDurationMs));
908
- setEnvironmentProductionUrl(productionUrl.toString());
909
- setEnvironmentVisible(true);
910
- }
911
- catch {
912
- // coercion-ok: malformed host metadata cannot produce a useful switcher.
913
- }
914
- }, [props]);
915
888
  const stopOAuthPolling = React.useCallback(() => {
916
889
  if (oauthPollTimer.current !== null) {
917
890
  window.clearInterval(oauthPollTimer.current);
@@ -1971,16 +1944,6 @@ export function AuthPage(props) {
1971
1944
  localeOptions.map((option) => (React.createElement("button", { type: "button", className: "locale-menu-item", role: "menuitemradio", "aria-checked": localePreference === option.value, "data-locale-value": option.value, key: option.value, onClick: () => selectLocale(option.value) },
1972
1945
  React.createElement("span", { className: "locale-menu-check", "aria-hidden": "true" }, "\u2713"),
1973
1946
  React.createElement("span", null, option.label)))))));
1974
- const environmentBadge = (React.createElement("div", { className: "environment-switcher", id: "environment-switcher", hidden: !environmentVisible },
1975
- React.createElement("button", { type: "button", className: "environment-badge", id: "environment-badge", "aria-expanded": environmentOpen, "aria-controls": "environment-popover", onClick: () => setEnvironmentOpen((open) => !open) }, "beta"),
1976
- React.createElement("div", { className: "environment-popover", id: "environment-popover", role: "dialog", "aria-labelledby": "environment-popover-title", hidden: !environmentOpen },
1977
- React.createElement("div", { className: "environment-popover-title", id: "environment-popover-title" }, "You're on Agent-Native Beta"),
1978
- React.createElement("div", { className: "environment-popover-copy" }, "Choose where you want to continue."),
1979
- React.createElement("a", { className: "environment-production-link", id: "environment-production-link", href: environmentProductionUrl }, "Switch to production"),
1980
- React.createElement("button", { type: "button", className: "environment-hide-badge", id: "environment-hide-badge", onClick: () => {
1981
- setEnvironmentOpen(false);
1982
- setEnvironmentVisible(false);
1983
- } }, "Hide badge"))));
1984
1947
  const marketingSurface = marketingCopy ? (React.createElement(MarketingHome, { appName: marketingCopy.appName, variant: "auth", background: marketingCopy.screenshotSrc ? null : (React.createElement(OceanBackground, { className: "auth-marketing-background" })), topRight: marketingCopy.learnMoreUrl ? (React.createElement("a", { className: "auth-marketing-learn-more", "data-auth-marketing-learn-more": "true", href: marketingCopy.learnMoreUrl, target: "_blank", rel: "noreferrer" },
1985
1948
  React.createElement("span", null, t("newToApp").replace("{appName}", marketingCopy.appName.replace(/^Agent-Native\s+/i, ""))),
1986
1949
  React.createElement("span", { "aria-hidden": "true" }, " - "),
@@ -2006,6 +1969,5 @@ export function AuthPage(props) {
2006
1969
  React.createElement("span", { "data-i18n": "openSource" }, t("openSource")))))))) : (React.createElement("div", { className: "auth-centered" }, authCard));
2007
1970
  return (React.createElement(React.Fragment, null,
2008
1971
  localePicker,
2009
- environmentBadge,
2010
1972
  initialPrompt ? (React.createElement("div", { className: "auth-centered" }, authCard)) : (marketingSurface)));
2011
1973
  }
@@ -6,5 +6,5 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
6
6
  * surrounding text color.
7
7
  */
8
8
  export function AgentNativeIcon({ size = 24, className, ...rest }) {
9
- return (_jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: size, height: size, viewBox: "0 0 114 66", fill: "none", className: className, ...rest, children: [_jsx("path", { d: "M24.5537 65.7695H0L15.0859 39.4619L37.708 0L60.4912 39.4619H39.6396L24.5537 65.7695Z", fill: "currentColor" }), _jsx("path", { d: "M89.446 0H114L76.2921 65.7704H51.7383L89.446 0Z", fill: "currentColor" })] }));
9
+ return (_jsxs("svg", { "data-agent-native-icon": true, xmlns: "http://www.w3.org/2000/svg", width: size, height: size, viewBox: "0 0 114 66", fill: "none", className: className, ...rest, children: [_jsx("path", { d: "M24.5537 65.7695H0L15.0859 39.4619L37.708 0L60.4912 39.4619H39.6396L24.5537 65.7695Z", fill: "currentColor" }), _jsx("path", { d: "M89.446 0H114L76.2921 65.7704H51.7383L89.446 0Z", fill: "currentColor" })] }));
10
10
  }
@@ -13,6 +13,7 @@ export * from "./ui/index.js";
13
13
  export { cn } from "@agent-native/toolkit/utils";
14
14
  export { AgentNativeI18nProvider, LanguagePicker, getLocaleInitScript, isLocaleCode, isValidLocaleCode, localeDirection, localeMetadataFor, normalizeLocaleCode, normalizeLocalePreference, normalizeLocalizationPreference, resolveLocaleFromCandidates, resolveLocaleFromPreference, useFormatters, useLocale, useT, DEFAULT_LOCALE, LOCALE_HYDRATION_GLOBAL, LOCALE_METADATA, LOCALE_STORAGE_KEY, SUPPORTED_LOCALES, type AgentNativeI18nCatalog, type AgentNativeI18nProviderProps, type BuiltinLocaleCode, type LocaleCode, type LocaleHydrationPayload, type LocaleMessages, type LocaleMetadata, type LocalePreference, type LocalizationPreference, } from "./i18n.js";
15
15
  export { AgentNativeIcon } from "./components/icons/AgentNativeIcon.js";
16
+ export { AppSidebar, AppSidebarHeader, AppSidebarNavItem, AppSidebarNavGroup, AppSidebarSection, AppSidebarFeedbackButton, AppSidebarFooter, useAppSidebar, type AppSidebarProps, type AppSidebarHeaderProps, type AppSidebarNavItemProps, type AppSidebarNavGroupProps, type AppSidebarSectionProps, type AppSidebarFeedbackButtonProps, type AppSidebarFooterProps, type AppSidebarItemDefinition, type AppSidebarContextValue, } from "./ui/AppSidebar.js";
16
17
  export { FeatureFlagsEditor, evaluatedFeatureFlagValues, featureFlagValue, useFeatureFlag, useFeatureFlags, type FeatureFlagActor, type FeatureFlagMetadata, type FeatureFlagRules, type SetFeatureFlagInput, } from "./feature-flags/index.js";
17
18
  export { ExperimentsSettings, useExperiment, useExperimentState, useExperiments, type ExperimentValues, type ExperimentsSettingsProps, } from "./experiments/index.js";
18
19
  export { withBuilderUtmTrackingParams } from "../shared/builder-link-tracking.js";