@agent-native/dispatch 0.20.4 → 0.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/dist/actions/get-agent-thread-debug.d.ts +2 -0
  2. package/dist/actions/provider-api-register.d.ts +7 -7
  3. package/dist/actions/search-agent-threads.js +2 -2
  4. package/dist/actions/search-agent-threads.js.map +1 -1
  5. package/dist/actions/start-workspace-app-creation.js +1 -1
  6. package/dist/actions/start-workspace-app-creation.js.map +1 -1
  7. package/dist/components/create-app-popover.d.ts.map +1 -1
  8. package/dist/components/create-app-popover.js +5 -71
  9. package/dist/components/create-app-popover.js.map +1 -1
  10. package/dist/components/layout/HeaderActions.d.ts +1 -17
  11. package/dist/components/layout/HeaderActions.d.ts.map +1 -1
  12. package/dist/components/layout/HeaderActions.js +1 -51
  13. package/dist/components/layout/HeaderActions.js.map +1 -1
  14. package/dist/components/layout/Layout.d.ts +23 -1
  15. package/dist/components/layout/Layout.d.ts.map +1 -1
  16. package/dist/components/layout/Layout.js +707 -18
  17. package/dist/components/layout/Layout.js.map +1 -1
  18. package/dist/components/ui/dialog.d.ts +1 -19
  19. package/dist/components/ui/dialog.d.ts.map +1 -1
  20. package/dist/components/ui/dialog.js +1 -22
  21. package/dist/components/ui/dialog.js.map +1 -1
  22. package/dist/components/ui/dropdown-menu.d.ts +1 -27
  23. package/dist/components/ui/dropdown-menu.d.ts.map +1 -1
  24. package/dist/components/ui/dropdown-menu.js +1 -35
  25. package/dist/components/ui/dropdown-menu.js.map +1 -1
  26. package/dist/components/ui/popover.d.ts +2 -1
  27. package/dist/components/ui/popover.d.ts.map +1 -1
  28. package/dist/components/ui/popover.js +2 -1
  29. package/dist/components/ui/popover.js.map +1 -1
  30. package/dist/components/ui/select.d.ts +1 -13
  31. package/dist/components/ui/select.d.ts.map +1 -1
  32. package/dist/components/ui/select.js +1 -26
  33. package/dist/components/ui/select.js.map +1 -1
  34. package/dist/hooks/use-mobile.d.ts +1 -1
  35. package/dist/hooks/use-mobile.d.ts.map +1 -1
  36. package/dist/hooks/use-mobile.js +1 -15
  37. package/dist/hooks/use-mobile.js.map +1 -1
  38. package/dist/lib/automation-display.js +1 -1
  39. package/dist/lib/automation-display.js.map +1 -1
  40. package/dist/lib/workspace-apps.d.ts +6 -0
  41. package/dist/lib/workspace-apps.d.ts.map +1 -1
  42. package/dist/lib/workspace-apps.js +21 -0
  43. package/dist/lib/workspace-apps.js.map +1 -1
  44. package/dist/routes/pages/settings.d.ts.map +1 -1
  45. package/dist/routes/pages/settings.js +21 -1
  46. package/dist/routes/pages/settings.js.map +1 -1
  47. package/dist/routes/pages/thread-debug.d.ts.map +1 -1
  48. package/dist/routes/pages/thread-debug.js +288 -118
  49. package/dist/routes/pages/thread-debug.js.map +1 -1
  50. package/dist/server/lib/app-creation-store.d.ts.map +1 -1
  51. package/dist/server/lib/app-creation-store.js +13 -0
  52. package/dist/server/lib/app-creation-store.js.map +1 -1
  53. package/dist/server/lib/thread-debug-store.d.ts +2 -0
  54. package/dist/server/lib/thread-debug-store.d.ts.map +1 -1
  55. package/dist/server/lib/thread-debug-store.js +6 -2
  56. package/dist/server/lib/thread-debug-store.js.map +1 -1
  57. package/dist/server/plugins/agent-chat.js +1 -0
  58. package/dist/server/plugins/agent-chat.js.map +1 -1
  59. package/dist/server/plugins/integrations.js +1 -1
  60. package/dist/server/plugins/integrations.js.map +1 -1
  61. package/package.json +3 -3
  62. package/src/actions/search-agent-threads.ts +2 -2
  63. package/src/actions/start-workspace-app-creation.ts +1 -1
  64. package/src/components/create-app-popover.spec.tsx +22 -0
  65. package/src/components/create-app-popover.tsx +30 -94
  66. package/src/components/layout/HeaderActions.tsx +1 -84
  67. package/src/components/layout/Layout.spec.tsx +35 -10
  68. package/src/components/layout/Layout.tsx +1394 -74
  69. package/src/components/ui/dialog.tsx +1 -120
  70. package/src/components/ui/dropdown-menu.tsx +1 -198
  71. package/src/components/ui/popover.tsx +3 -1
  72. package/src/components/ui/select.tsx +1 -158
  73. package/src/hooks/use-mobile.tsx +1 -21
  74. package/src/lib/automation-display.spec.ts +1 -1
  75. package/src/lib/automation-display.ts +1 -1
  76. package/src/lib/workspace-apps.ts +24 -0
  77. package/src/routes/pages/settings.tsx +72 -0
  78. package/src/routes/pages/thread-debug.spec.tsx +103 -5
  79. package/src/routes/pages/thread-debug.tsx +1051 -451
  80. package/src/server/lib/app-creation-store.spec.ts +13 -0
  81. package/src/server/lib/app-creation-store.ts +13 -0
  82. package/src/server/lib/mcp-gateway.spec.ts +18 -0
  83. package/src/server/lib/thread-debug-store.spec.ts +17 -0
  84. package/src/server/lib/thread-debug-store.ts +9 -2
  85. package/src/server/plugins/agent-chat.ts +1 -0
  86. package/src/server/plugins/integrations.ts +1 -1
  87. package/src/styles/dispatch.css +29 -0
@@ -1,22 +1,29 @@
1
1
  import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { AgentSidebar, focusAgentChat, navigateWithAgentChatViewTransition, useAgentChatHomeHandoff, useAgentChatHomeHandoffLinks, useChatThreads, } from "@agent-native/core/client/agent-chat";
2
+ import { CHAT_FIRST_MODE_CHANGED_EVENT, AgentChatSurface, chatFirstSurfaceTabId, AgentSidebar, ChatFirstSurfacePanelToggle, closeChatFirstSessionWatch, emitChatFirstSessionWatch, getChatFirstSurfaceTabsStore, focusAgentChat, navigateWithAgentChatViewTransition, readChatFirstAppLayout, readChatFirstMode, resolveChatFirstBrowserTarget, resolveChatFirstAppTarget, subscribeChatFirstOpenBrowser, subscribeChatFirstOpenApp, useChatFirstSessionWatch, useChatFirstSurfacePanel, useChatFirstSurfaceResize, useChatFirstSurfaceTabs, useAgentChatHomeHandoff, useAgentChatHomeHandoffLinks, useChatThreads, } from "@agent-native/core/client/agent-chat";
3
3
  import { appBasePath, appPath } from "@agent-native/core/client/api-path";
4
+ import { readClientAppState, writeClientAppState, } from "@agent-native/core/client/application-state";
5
+ import { ChatFirstAgentsPane, ChatFirstAppPane, ChatFirstAppsRail, ChatFirstBrowserPane, ChatFirstChatHistory, ChatFirstPrimaryNavigation, ChatFirstSessionWatchPane, ChatFirstSurfacePanel, ChatFirstSurfaceContent, ChatFirstSurfaceTabs, defaultChatFirstCopy, } from "@agent-native/core/client/chat-first";
6
+ import { writeClipboardText } from "@agent-native/core/client/clipboard";
7
+ import { useActionMutation, useActionQuery, } from "@agent-native/core/client/hooks";
4
8
  import { useT } from "@agent-native/core/client/i18n";
5
9
  import { openCommandMenu } from "@agent-native/core/client/navigation";
6
10
  import { InvitationBanner, OrgSwitcher } from "@agent-native/core/client/org";
7
11
  import { FeedbackButton } from "@agent-native/core/client/ui";
8
12
  import { SidebarFooterActions } from "@agent-native/toolkit/app-shell";
9
13
  import { ChatHistoryRail, } from "@agent-native/toolkit/chat-history";
10
- import { IconApps, IconBrandSlack, IconBrandTelegram, IconMessageQuestion, IconBroadcast, IconLayoutSidebarLeftCollapse, IconLayoutSidebarLeftExpand, IconSettings, IconShield, IconSearch, IconWorld, IconDeviceDesktop, } from "@tabler/icons-react";
11
- import { createContext, useEffect, useMemo, useRef, useState, useContext, } from "react";
14
+ import { IconApps, IconBrandSlack, IconBrandTelegram, IconCopy, IconEye, IconMessageQuestion, IconBroadcast, IconLayoutSidebarLeftCollapse, IconLayoutSidebarLeftExpand, IconSettings, IconShield, IconSearch, IconWorld, IconDeviceDesktop, IconPlus, } from "@tabler/icons-react";
15
+ import { createContext, useEffect, useMemo, useRef, useState, useContext, useCallback, } from "react";
12
16
  import { Link, useLocation, useNavigate } from "react-router";
17
+ import { toast } from "sonner";
13
18
  import { cn } from "../../lib/utils.js";
19
+ import { mergeChatFirstWorkspaceApps, } from "../../lib/workspace-apps.js";
20
+ import { AppIcon } from "../app-icon.js";
21
+ import { CreateAppPopover } from "../create-app-popover.js";
14
22
  import { Sheet, SheetContent, SheetDescription, SheetTitle } from "../ui/sheet.js";
15
23
  import { Skeleton } from "../ui/skeleton.js";
16
24
  import { Tooltip, TooltipContent, TooltipTrigger } from "../ui/tooltip.js";
17
25
  import { Header } from "./Header.js";
18
26
  import { HeaderActionsProvider } from "./HeaderActions.js";
19
- import { WorkspaceAppsRail } from "./workspace-apps-rail.js";
20
27
  const PRIMARY_NAV_ITEMS = [
21
28
  {
22
29
  id: "overview",
@@ -59,6 +66,10 @@ const DISPATCH_SIDEBAR_LABEL = "Dispatch";
59
66
  const CHROMELESS_PATHS = ["/approval", "/browser-chat", "/browser-connect"];
60
67
  const SIDEBAR_COLLAPSE_KEY = "dispatch.sidebar.collapsed";
61
68
  const CHAT_HISTORY_SOURCE_KEY = "dispatch.chat-history.source";
69
+ const CHAT_FIRST_PANE_STATE_KEY = "chat-first-pane";
70
+ export function buildChatFirstEmbedSessionInput(appId, path) {
71
+ return { app: appId, path, chrome: "minimal" };
72
+ }
62
73
  const DispatchExtensionsContext = createContext(undefined);
63
74
  export function useDispatchExtensions() {
64
75
  return useContext(DispatchExtensionsContext);
@@ -120,8 +131,147 @@ function dispatchNavLinkTarget(path) {
120
131
  const routerHasBasename = pathname === basePath || pathname.startsWith(`${basePath}/`);
121
132
  return routerHasBasename ? path : appPath(path);
122
133
  }
134
+ function chatFirstResolutionMessage(reason) {
135
+ switch (reason) {
136
+ case "empty-detail":
137
+ return "The agent did not provide an app target to open.";
138
+ case "invalid-url":
139
+ return "The requested app route was not registered for this workspace app.";
140
+ case "unknown-app":
141
+ return "That app is not available in this workspace.";
142
+ }
143
+ return "The requested app could not be opened.";
144
+ }
145
+ function chatFirstBrowserResolutionMessage(reason) {
146
+ return reason === "empty-detail"
147
+ ? "The agent did not provide a browser URL to open."
148
+ : "The requested browser URL is not a safe HTTP(S) address.";
149
+ }
150
+ const DISPATCH_CHAT_FIRST_COPY_KEYS = {
151
+ workspaceApps: "chatFirstWorkspaceApps",
152
+ createWorkspaceApp: "chatFirstCreateWorkspaceApp",
153
+ openApp: "chatFirstOpenApp",
154
+ appsLoadError: "chatFirstAppsLoadError",
155
+ noWorkspaceApps: "chatFirstNoWorkspaceApps",
156
+ createApp: "chatFirstCreateApp",
157
+ retry: "chatFirstRetry",
158
+ dismiss: "chatFirstDismiss",
159
+ unpinApp: "chatFirstUnpinApp",
160
+ pinApp: "chatFirstPinApp",
161
+ removePinned: "chatFirstRemovePinned",
162
+ pinTop: "chatFirstPinTop",
163
+ openSideSurfaces: "chatFirstOpenSideSurfaces",
164
+ closeTab: "chatFirstCloseTab",
165
+ close: "chatFirstClose",
166
+ closeOthers: "chatFirstCloseOthers",
167
+ closeToRight: "chatFirstCloseToRight",
168
+ closeAll: "chatFirstCloseAll",
169
+ unavailable: "chatFirstUnavailable",
170
+ openActivity: "chatFirstOpenActivity",
171
+ deferred: "chatFirstDeferred",
172
+ browserBack: "chatFirstBrowserBack",
173
+ browserForward: "chatFirstBrowserForward",
174
+ browserReload: "chatFirstBrowserReload",
175
+ browserAddress: "chatFirstBrowserAddress",
176
+ browserOpenExternal: "chatFirstBrowserOpenExternal",
177
+ browserClose: "chatFirstBrowserClose",
178
+ browserPage: "chatFirstBrowserPage",
179
+ browserInvalidUrl: "chatFirstBrowserInvalidUrl",
180
+ browserPreviewStarting: "chatFirstBrowserPreviewStarting",
181
+ browserPreviewError: "chatFirstBrowserPreviewError",
182
+ appUnavailable: "chatFirstAppUnavailable",
183
+ appLoading: "chatFirstLoadingApp",
184
+ agentActivityEyebrow: "chatFirstAgentActivityEyebrow",
185
+ agentActivityTitle: "chatFirstAgentActivityTitle",
186
+ agentActivityDescription: "chatFirstAgentActivityDescription",
187
+ refreshAgentActivity: "chatFirstRefreshAgentActivity",
188
+ noAgentSessions: "chatFirstNoAgentSessions",
189
+ startAgentSession: "chatFirstStartAgentSession",
190
+ watchSession: "chatFirstWatchSession",
191
+ copySessionId: "chatFirstCopySessionId",
192
+ copySessionIdFor: "chatFirstCopySessionIdFor",
193
+ sessionIdCopied: "chatFirstSessionIdCopied",
194
+ sessionIdShort: "chatFirstSessionIdShort",
195
+ copied: "chatFirstCopied",
196
+ agentActivityStatusQueued: "chatFirstAgentActivityStatusQueued",
197
+ agentActivityStatusRunning: "chatFirstAgentActivityStatusRunning",
198
+ agentActivityStatusPaused: "chatFirstAgentActivityStatusPaused",
199
+ agentActivityStatusNeedsApproval: "chatFirstAgentActivityStatusNeedsApproval",
200
+ agentActivityStatusCompleted: "chatFirstAgentActivityStatusCompleted",
201
+ agentActivityStatusErrored: "chatFirstAgentActivityStatusErrored",
202
+ agentActivityStatusRecent: "chatFirstAgentActivityStatusRecent",
203
+ agentActivityStatusUnknown: "chatFirstAgentActivityStatusUnknown",
204
+ watchingSession: "chatFirstWatchingSession",
205
+ session: "chatFirstSession",
206
+ stopWatchingSession: "chatFirstStopWatchingSession",
207
+ stopWatching: "chatFirstStopWatching",
208
+ watchedSession: "chatFirstWatchedSession",
209
+ agentsTitle: "chatFirstAgentsTitle",
210
+ };
211
+ const DISPATCH_CHAT_FIRST_SURFACE_COPY_KEYS = {
212
+ browser: {
213
+ label: "chatFirstSurfaceBrowserLabel",
214
+ reason: "chatFirstSurfaceBrowserReason",
215
+ },
216
+ terminal: {
217
+ label: "chatFirstSurfaceTerminalLabel",
218
+ reason: "chatFirstSurfaceTerminalReason",
219
+ },
220
+ files: {
221
+ label: "chatFirstSurfaceFilesLabel",
222
+ reason: "chatFirstSurfaceFilesReason",
223
+ },
224
+ diff: {
225
+ label: "chatFirstSurfaceDiffLabel",
226
+ reason: "chatFirstSurfaceDiffReason",
227
+ },
228
+ "side-chat": {
229
+ label: "chatFirstSurfaceSideChatLabel",
230
+ reason: "chatFirstSurfaceSideChatReason",
231
+ },
232
+ agents: {
233
+ label: "chatFirstSurfaceAgentsLabel",
234
+ reason: "chatFirstSurfaceAgentsReason",
235
+ },
236
+ };
237
+ function createDispatchChatFirstCopy(t) {
238
+ return (key, values) => {
239
+ if (key.startsWith("surface.")) {
240
+ const [, kind, field] = key.split(".");
241
+ const surface = DISPATCH_CHAT_FIRST_SURFACE_COPY_KEYS[kind];
242
+ const translationKey = surface && (field === "label" || field === "reason")
243
+ ? surface[field]
244
+ : undefined;
245
+ if (translationKey) {
246
+ return t(`dispatch.pages.${translationKey}`, {
247
+ ...(values ?? {}),
248
+ defaultValue: defaultChatFirstCopy(key, values),
249
+ });
250
+ }
251
+ }
252
+ const translationKey = DISPATCH_CHAT_FIRST_COPY_KEYS[key];
253
+ if (!translationKey)
254
+ return defaultChatFirstCopy(key, values);
255
+ return t(`dispatch.pages.${translationKey}`, {
256
+ ...(values ?? {}),
257
+ defaultValue: defaultChatFirstCopy(key, values),
258
+ });
259
+ };
260
+ }
123
261
  function chatThreadPath(threadId) {
124
- return `/chat/${encodeURIComponent(threadId)}`;
262
+ return threadId ? `/chat/${encodeURIComponent(threadId)}` : "/chat";
263
+ }
264
+ function persistedChatFirstPane(value) {
265
+ if (!value || typeof value !== "object")
266
+ return null;
267
+ const record = value;
268
+ if (typeof record.appId !== "string" || !record.appId.trim())
269
+ return null;
270
+ return {
271
+ appId: record.appId,
272
+ ...(typeof record.path === "string" ? { path: record.path } : {}),
273
+ ...(typeof record.view === "string" ? { view: record.view } : {}),
274
+ };
125
275
  }
126
276
  function threadIdFromPath(pathname) {
127
277
  const match = pathname.match(/^\/chat\/([^/]+)/);
@@ -185,8 +335,9 @@ function threadUpdatedAt(thread) {
185
335
  ? thread.createdAt
186
336
  : 0;
187
337
  }
188
- function DispatchChatsSection({ onNavigate }) {
338
+ function DispatchChatsSection({ onNavigate, showNewChat = false, prelude, chatFirstMode = false, chatFirstEmbedded = false, chatFirstNavigation, }) {
189
339
  const t = useT();
340
+ const chatFirstCopy = useMemo(() => createDispatchChatFirstCopy(t), [t]);
190
341
  const navigate = useNavigate();
191
342
  const location = useLocation();
192
343
  const [includeExternal, setIncludeExternal] = useState(false);
@@ -266,7 +417,7 @@ function DispatchChatsSection({ onNavigate }) {
266
417
  if (threadId)
267
418
  openThread(threadId, { isNew: true });
268
419
  }
269
- return (_jsxs("div", { className: "ms-4 min-w-0 space-y-0.5", children: [_jsx("div", { className: "flex justify-end px-2 pt-0.5", children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("button", { type: "button", "data-dispatch-chat-source-toggle": true, "aria-pressed": includeExternal, "aria-label": includeExternal
420
+ return (_jsxs("div", { className: cn("ms-4 min-w-0 space-y-0.5", showNewChat && "dispatch-chat-first-chats"), children: [showNewChat && chatFirstNavigation ? (_jsx("nav", { className: "space-y-0.5 px-2 py-2", children: _jsx(ChatFirstPrimaryNavigation, { copy: chatFirstCopy, onNewChat: () => void handleNewChat(), onOpenIntegrations: chatFirstNavigation.onOpenIntegrations, onOpenScheduled: chatFirstNavigation.onOpenScheduled, onSearch: openCommandMenu }) })) : null, prelude, !chatFirstMode ? (_jsx("div", { className: "flex justify-end px-2 pt-0.5", children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("button", { type: "button", "data-dispatch-chat-source-toggle": true, "aria-pressed": includeExternal, "aria-label": includeExternal
270
421
  ? t("dispatch.sidebar.showLocalChats", {
271
422
  defaultValue: "Show local chats",
272
423
  })
@@ -278,9 +429,33 @@ function DispatchChatsSection({ onNavigate }) {
278
429
  })
279
430
  : t("dispatch.sidebar.showAllChats", {
280
431
  defaultValue: "Show all chats",
281
- }) })] }) }), chatsLoading &&
432
+ }) })] }) })) : null, !chatFirstMode &&
433
+ chatsLoading &&
282
434
  visibleThreads.length === 0 &&
283
- Array.from({ length: 3 }).map((_, index) => (_jsxs("div", { className: "flex items-center gap-2 px-3 py-1", children: [_jsx(Skeleton, { className: "size-3.5 shrink-0 rounded-sm" }), _jsx(Skeleton, { className: "h-3 w-3/4 rounded" })] }, `chat-skeleton-${index}`))), _jsx(ChatHistoryRail, { items: chatItems, activeId: displayedActiveThreadId, onSelect: (threadId) => openThread(threadId), onNewChat: () => void handleNewChat(), railLabels: {
435
+ Array.from({ length: 3 }).map((_, index) => (_jsxs("div", { className: "flex items-center gap-2 px-3 py-1", children: [_jsx(Skeleton, { className: "size-3.5 shrink-0 rounded-sm" }), _jsx(Skeleton, { className: "h-3 w-3/4 rounded" })] }, `chat-skeleton-${index}`))), chatFirstMode ? (_jsx(ChatFirstChatHistory, { items: chatItems, activeId: displayedActiveThreadId, loading: chatsLoading && visibleThreads.length === 0, loadingLabel: _jsx("div", { className: "space-y-1 px-2 py-1", children: [0, 1, 2].map((index) => (_jsxs("div", { className: "flex items-center gap-2 px-1 py-1", children: [_jsx(Skeleton, { className: "size-3.5 shrink-0 rounded-sm" }), _jsx(Skeleton, { className: "h-3 w-3/4 rounded" })] }, index))) }), label: t("dispatch.sidebar.chats", { defaultValue: "Chats" }), onSelect: (threadId) => openThread(threadId), renameMaxLength: 160, onRename: (threadId, title) => void renameThread(threadId, title), labels: {
436
+ options: (item) => t("dispatch.sidebar.chatOptions", {
437
+ title: item.titleText ?? "",
438
+ }),
439
+ renameInput: (item) => t("dispatch.sidebar.renameThread", {
440
+ title: item.titleText ?? "",
441
+ }),
442
+ rename: t("dispatch.sidebar.renameChat"),
443
+ }, renderAdditionalRowActions: (item, closeMenu) => (_jsxs(_Fragment, { children: [_jsxs("button", { type: "button", role: "menuitem", className: "an-chat-history-row__menu-item", onClick: () => {
444
+ closeMenu();
445
+ void writeClipboardText(item.id).then((copied) => {
446
+ toast[copied ? "success" : "error"](copied
447
+ ? "Session ID copied"
448
+ : "Could not copy the session ID");
449
+ });
450
+ }, children: [_jsx(IconCopy, { size: 13, strokeWidth: 1.8 }), _jsx("span", { children: "Copy session ID" })] }), !chatFirstEmbedded ? (_jsxs("button", { type: "button", role: "menuitem", className: "an-chat-history-row__menu-item", onClick: () => {
451
+ closeMenu();
452
+ emitChatFirstSessionWatch({
453
+ sessionId: item.id,
454
+ title: item.titleText,
455
+ kind: "agent-chat",
456
+ sourceSessionId: displayedActiveThreadId ?? undefined,
457
+ });
458
+ }, children: [_jsx(IconEye, { size: 13, strokeWidth: 1.8 }), _jsx("span", { children: "Watch and message session" })] })) : null] })), className: "min-w-0" })) : (_jsx(ChatHistoryRail, { items: chatItems, activeId: displayedActiveThreadId, onSelect: (threadId) => openThread(threadId), onNewChat: () => void handleNewChat(), railLabels: {
284
459
  newChat: t("dispatch.sidebar.newChat"),
285
460
  showMore: t("dispatch.sidebar.chats"),
286
461
  showLess: t("dispatch.sidebar.chats"),
@@ -292,10 +467,26 @@ function DispatchChatsSection({ onNavigate }) {
292
467
  title: item.titleText ?? "",
293
468
  }),
294
469
  rename: t("dispatch.sidebar.renameChat"),
295
- }, className: "min-w-0" })] }));
470
+ }, renderAdditionalRowActions: (item, closeMenu) => (_jsxs(_Fragment, { children: [_jsxs("button", { type: "button", role: "menuitem", className: "an-chat-history-row__menu-item", onClick: () => {
471
+ closeMenu();
472
+ void writeClipboardText(item.id).then((copied) => {
473
+ toast[copied ? "success" : "error"](copied
474
+ ? "Session ID copied"
475
+ : "Could not copy the session ID");
476
+ });
477
+ }, children: [_jsx(IconCopy, { size: 13, strokeWidth: 1.8 }), _jsx("span", { children: "Copy session ID" })] }), chatFirstMode && !chatFirstEmbedded ? (_jsxs("button", { type: "button", role: "menuitem", className: "an-chat-history-row__menu-item", onClick: () => {
478
+ closeMenu();
479
+ emitChatFirstSessionWatch({
480
+ sessionId: item.id,
481
+ title: item.titleText,
482
+ kind: "agent-chat",
483
+ sourceSessionId: displayedActiveThreadId ?? undefined,
484
+ });
485
+ }, children: [_jsx(IconEye, { size: 13, strokeWidth: 1.8 }), _jsx("span", { children: "Watch and message session" })] })) : null] })), className: "min-w-0" }))] }));
296
486
  }
297
- export function NavContent({ onNavigate, extensions, collapsed = false, collapsible = false, onCollapsedChange, }) {
487
+ export function NavContent({ onNavigate, extensions, chatFirstMode = false, chatFirstEmbedded = false, collapsed = false, collapsible = false, onCollapsedChange, chatFirstAppLayout, onChatFirstAppLayoutChange, chatFirstApps = [], chatFirstAppsLoading = false, chatFirstAppsError, chatFirstActiveAppId, onChatFirstAppOpen, onChatFirstAppsRetry, }) {
298
488
  const t = useT();
489
+ const chatFirstCopy = useMemo(() => createDispatchChatFirstCopy(t), [t]);
299
490
  const location = useLocation();
300
491
  const navigate = useNavigate();
301
492
  const extensionNavItems = extensions?.navItems ?? EMPTY_NAV_ITEMS;
@@ -317,6 +508,11 @@ export function NavContent({ onNavigate, extensions, collapsed = false, collapsi
317
508
  : t("sidebar.collapseSidebar"), className: "flex h-8 w-8 shrink-0 items-center justify-center rounded-md text-sidebar-foreground/65 transition-colors hover:bg-sidebar-accent hover:text-sidebar-accent-foreground", children: collapsed ? (_jsx(IconLayoutSidebarLeftExpand, { className: "h-4 w-4 rtl:-scale-x-100" })) : (_jsx(IconLayoutSidebarLeftCollapse, { className: "h-4 w-4 rtl:-scale-x-100" })) }) }), _jsx(TooltipContent, { side: "right", children: collapsed ? t("sidebar.expandSidebar") : t("sidebar.collapseSidebar") })] })) : null;
318
509
  const searchButton = (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("button", { type: "button", onClick: openCommandMenu, "aria-label": t("sidebar.search"), className: "flex h-8 w-8 items-center justify-center rounded-md text-sidebar-foreground/65 transition-colors hover:bg-sidebar-accent hover:text-sidebar-accent-foreground", children: _jsx(IconSearch, { className: "h-4 w-4" }) }) }), _jsx(TooltipContent, { side: "right", children: t("sidebar.search") })] }));
319
510
  const feedbackButton = (_jsx(FeedbackButton, { variant: collapsed ? "icon" : "sidebar", side: "right", className: collapsed ? "size-8" : "min-w-0" }));
511
+ const chatFirstCreateAppTrigger = (_jsx(CreateAppPopover, { align: "start", trigger: _jsx("button", { type: "button", className: "flex size-6 items-center justify-center rounded text-sidebar-foreground/55 hover:bg-sidebar-accent hover:text-sidebar-foreground", "aria-label": chatFirstCopy("createWorkspaceApp"), title: chatFirstCopy("createWorkspaceApp"), children: _jsx(IconPlus, { size: 14, "aria-hidden": "true" }) }) }));
512
+ const chatFirstAppsRail = (_jsx(ChatFirstAppsRail, { apps: chatFirstApps, activeAppId: chatFirstActiveAppId, collapsed: collapsed, loading: chatFirstAppsLoading, error: chatFirstAppsError, layout: chatFirstAppLayout, onLayoutChange: onChatFirstAppLayoutChange, onRetry: onChatFirstAppsRetry, onOpenApp: (app) => {
513
+ onChatFirstAppOpen?.(app);
514
+ onNavigate?.();
515
+ }, createAppTrigger: chatFirstCreateAppTrigger, renderIcon: (app) => (_jsx(AppIcon, { id: app.id, name: app.name, size: "sm", className: "size-5 rounded-md" })), copy: chatFirstCopy }));
320
516
  const renderNavItem = (item) => {
321
517
  const Icon = item.icon;
322
518
  const itemMatchesLocalPath = navItemMatchesPath(item, localPathname);
@@ -334,7 +530,7 @@ export function NavContent({ onNavigate, extensions, collapsed = false, collapsi
334
530
  return;
335
531
  }
336
532
  onNavigate?.();
337
- }, "aria-label": label, className: cn("flex size-9 items-center justify-center rounded-md text-sm", itemMatchesLocalPath
533
+ }, "aria-label": label, "aria-current": itemMatchesLocalPath ? "page" : undefined, className: cn("flex size-9 items-center justify-center rounded-md text-sm", itemMatchesLocalPath
338
534
  ? "bg-sidebar-accent font-medium text-sidebar-accent-foreground"
339
535
  : "text-sidebar-foreground/70 hover:bg-sidebar-accent hover:text-sidebar-accent-foreground"), children: Icon ? (_jsx(Icon, { size: 16, className: "shrink-0" })) : (_jsx("span", { className: "h-4 w-4 shrink-0", "aria-hidden": "true" })) }) }), _jsx(TooltipContent, { side: "right", children: label })] })) : (_jsxs(Link, { to: dispatchNavLinkTarget(item.to), onClick: (event) => {
340
536
  if (item.id === "chat" &&
@@ -349,11 +545,20 @@ export function NavContent({ onNavigate, extensions, collapsed = false, collapsi
349
545
  return;
350
546
  }
351
547
  onNavigate?.();
352
- }, className: cn("flex h-8 w-full items-center gap-2 rounded-md px-2 text-sm", itemMatchesLocalPath
548
+ }, "aria-current": itemMatchesLocalPath ? "page" : undefined, className: cn("flex h-8 w-full items-center gap-2 rounded-md px-2 text-sm", itemMatchesLocalPath
353
549
  ? "bg-sidebar-accent font-medium text-sidebar-accent-foreground"
354
- : "text-sidebar-foreground/70 hover:bg-sidebar-accent hover:text-sidebar-accent-foreground"), children: [Icon ? (_jsx(Icon, { size: 16, className: "shrink-0" })) : (_jsx("span", { className: "h-4 w-4 shrink-0", "aria-hidden": "true" })), _jsx("span", { className: "truncate", children: label })] })), !collapsed && item.id === "chat" && itemMatchesLocalPath ? (_jsx(DispatchChatsSection, { onNavigate: onNavigate })) : null] }, item.id));
550
+ : "text-sidebar-foreground/70 hover:bg-sidebar-accent hover:text-sidebar-accent-foreground"), children: [Icon ? (_jsx(Icon, { size: 16, className: "shrink-0" })) : (_jsx("span", { className: "h-4 w-4 shrink-0", "aria-hidden": "true" })), _jsx("span", { className: "truncate", children: label })] })), !collapsed && item.id === "chat" && itemMatchesLocalPath ? (_jsx(DispatchChatsSection, { onNavigate: onNavigate, chatFirstMode: chatFirstMode, chatFirstEmbedded: chatFirstEmbedded })) : null] }, item.id));
355
551
  };
356
- return (_jsxs(_Fragment, { children: [_jsx("div", { className: cn("flex h-12 shrink-0 items-center border-b border-sidebar-border", collapsed ? "justify-center px-0" : "px-4"), children: _jsxs("div", { className: cn("flex items-center", collapsed ? "justify-center" : "gap-2"), children: [_jsx("img", { src: appPath("/agent-native-icon-light.svg"), alt: "", "aria-hidden": "true", width: 35, height: 20, className: cn("block shrink-0 object-contain object-center dark:hidden", collapsed ? "h-4 w-7" : "h-5 w-[35px]") }), _jsx("img", { src: appPath("/agent-native-icon-dark.svg"), alt: "", "aria-hidden": "true", width: 35, height: 20, className: cn("hidden shrink-0 object-contain object-center dark:block", collapsed ? "h-4 w-7" : "h-5 w-[35px]") }), !collapsed && (_jsx("div", { className: "min-w-0 flex-1", children: _jsx("div", { "data-dispatch-sidebar-label": true, className: "truncate text-lg font-bold tracking-tight text-foreground", children: DISPATCH_SIDEBAR_LABEL }) }))] }) }), _jsxs("div", { className: "flex min-h-0 flex-1 flex-col overflow-y-auto", children: [_jsxs("nav", { className: cn("py-2", collapsed ? "px-1.5" : "px-2"), children: [_jsx("ul", { className: cn(collapsed ? "flex flex-col items-center gap-1" : "space-y-0.5"), children: primaryNavItems.map(renderNavItem) }), _jsx(WorkspaceAppsRail, { collapsed: collapsed, onNavigate: onNavigate })] }), _jsxs("div", { className: "mt-auto shrink-0", children: [_jsx("nav", { className: cn("py-1", collapsed ? "px-1.5" : "px-2"), children: _jsx("ul", { className: cn(collapsed ? "flex flex-col items-center gap-1" : "space-y-0.5"), children: BOTTOM_NAV_ITEMS.map(renderNavItem) }) }), _jsx("div", { className: cn("py-2", collapsed ? "flex justify-center px-1" : "px-3"), children: _jsx(OrgSwitcher, { compact: collapsed, reserveSpace: true }) })] }), _jsx(SidebarFooterActions, { collapsed: collapsed, feedback: feedbackButton, search: searchButton, collapse: collapseButton })] })] }));
552
+ return (_jsxs(_Fragment, { children: [_jsx("div", { className: cn("flex h-12 shrink-0 items-center border-b border-sidebar-border", collapsed ? "justify-center px-0" : "px-4"), children: _jsxs("div", { className: cn("flex items-center", collapsed ? "justify-center" : "gap-2"), children: [_jsx("img", { src: appPath("/agent-native-icon-light.svg"), alt: "", "aria-hidden": "true", width: 35, height: 20, className: cn("block shrink-0 object-contain object-center dark:hidden", collapsed ? "h-4 w-7" : "h-5 w-[35px]") }), _jsx("img", { src: appPath("/agent-native-icon-dark.svg"), alt: "", "aria-hidden": "true", width: 35, height: 20, className: cn("hidden shrink-0 object-contain object-center dark:block", collapsed ? "h-4 w-7" : "h-5 w-[35px]") }), !collapsed && (_jsx("div", { className: "min-w-0 flex-1", children: _jsx("div", { "data-dispatch-sidebar-label": true, className: "truncate text-lg font-bold tracking-tight text-foreground", children: DISPATCH_SIDEBAR_LABEL }) }))] }) }), chatFirstMode ? (_jsx("div", { className: "flex min-h-0 flex-1 flex-col overflow-y-auto", children: !collapsed ? (_jsx(DispatchChatsSection, { onNavigate: onNavigate, showNewChat: true, chatFirstMode: true, chatFirstEmbedded: chatFirstEmbedded, chatFirstNavigation: {
553
+ onOpenIntegrations: () => {
554
+ navigate(dispatchNavLinkTarget("/admin/integrations"));
555
+ onNavigate?.();
556
+ },
557
+ onOpenScheduled: () => {
558
+ navigate(dispatchNavLinkTarget("/admin/automations"));
559
+ onNavigate?.();
560
+ },
561
+ }, prelude: chatFirstAppsRail })) : (chatFirstAppsRail) })) : null, _jsxs("div", { className: cn("flex min-h-0 flex-1 flex-col overflow-y-auto", chatFirstMode && "hidden"), children: [_jsx("nav", { className: cn("py-2", collapsed ? "px-1.5" : "px-2"), children: _jsx("ul", { className: cn(collapsed ? "flex flex-col items-center gap-1" : "space-y-0.5"), children: primaryNavItems.map(renderNavItem) }) }), _jsxs("div", { className: "mt-auto shrink-0", children: [_jsx("nav", { className: cn("py-1", collapsed ? "px-1.5" : "px-2"), children: _jsx("ul", { className: cn(collapsed ? "flex flex-col items-center gap-1" : "space-y-0.5"), children: BOTTOM_NAV_ITEMS.map(renderNavItem) }) }), _jsx("div", { className: cn("py-2", collapsed ? "flex justify-center px-1" : "px-3"), children: _jsx(OrgSwitcher, { compact: collapsed, reserveSpace: true }) })] }), _jsx(SidebarFooterActions, { collapsed: collapsed, feedback: feedbackButton, search: searchButton, collapse: collapseButton })] }), chatFirstMode && collapsed ? (_jsx("div", { className: "mt-auto shrink-0", children: _jsx(SidebarFooterActions, { collapsed: true, feedback: feedbackButton, search: searchButton, collapse: collapseButton }) })) : null] }));
357
562
  }
358
563
  export function Layout({ children, extensions, agentPageHref, }) {
359
564
  const t = useT();
@@ -372,7 +577,181 @@ export function Layout({ children, extensions, agentPageHref, }) {
372
577
  });
373
578
  const localPathname = localDispatchPath(location.pathname);
374
579
  const isChatRoute = localPathname === "/chat" || localPathname.startsWith("/chat/");
580
+ const chatFirstSurfaceScope = threadIdFromPath(localPathname) ?? "new";
375
581
  const isWorkspaceAppHostRoute = localPathname.startsWith("/apps/");
582
+ const [chatFirstPreference, setChatFirstPreference] = useState(() => readChatFirstMode());
583
+ const chatFirstEmbedded = typeof window !== "undefined" &&
584
+ new URLSearchParams(window.location.search).get("chatFirst") === "1";
585
+ const chatFirstMode = extensions?.chatFirst === true || chatFirstPreference || chatFirstEmbedded;
586
+ const [chatFirstAppLayout, setChatFirstAppLayout] = useState(() => readChatFirstAppLayout());
587
+ const chatFirstAppLayoutHydratedRef = useRef(false);
588
+ const chatFirstAppsQuery = useActionQuery("list-workspace-apps", { includeAgentCards: false }, { enabled: chatFirstMode && isChatRoute });
589
+ const chatFirstWorkspaceApps = useMemo(() => mergeChatFirstWorkspaceApps(chatFirstAppsQuery.data), [chatFirstAppsQuery.data]);
590
+ const chatFirstAppRegistrations = useMemo(() => chatFirstWorkspaceApps.map((app) => ({
591
+ id: app.id,
592
+ name: app.name,
593
+ path: app.path,
594
+ url: app.url,
595
+ enabled: app.status !== "pending" && app.archived !== true,
596
+ })), [chatFirstWorkspaceApps]);
597
+ const chatFirstAppItems = useMemo(() => chatFirstWorkspaceApps.map((app) => ({
598
+ id: app.id,
599
+ name: app.name,
600
+ })), [chatFirstWorkspaceApps]);
601
+ const chatFirstCopy = useMemo(() => createDispatchChatFirstCopy(t), [t]);
602
+ const createChatFirstEmbedSession = useActionMutation("create_embed_session", { skipActionQueryInvalidation: true });
603
+ const [chatFirstPane, setChatFirstPane] = useState(null);
604
+ const [chatFirstEmbedUrl, setChatFirstEmbedUrl] = useState(null);
605
+ const [chatFirstEmbedError, setChatFirstEmbedError] = useState(null);
606
+ const [chatFirstEmbedAttempt, setChatFirstEmbedAttempt] = useState(0);
607
+ const [chatFirstNotice, setChatFirstNotice] = useState(null);
608
+ const chatFirstSessionWatch = useChatFirstSessionWatch();
609
+ const chatFirstSurfaceTabs = useChatFirstSurfaceTabs(chatFirstSurfaceScope);
610
+ const chatFirstSurfaceTabsStore = getChatFirstSurfaceTabsStore(chatFirstSurfaceScope);
611
+ const chatFirstAgentsQuery = useActionQuery("search-agent-threads", { sourceId: "current", limit: 12 }, {
612
+ enabled: chatFirstMode &&
613
+ isChatRoute &&
614
+ chatFirstSurfaceTabs.activeTabId?.startsWith("agents:") === true,
615
+ });
616
+ const chatFirstSurfacePanel = useChatFirstSurfacePanel(chatFirstSurfaceScope);
617
+ const { setOpen: setChatFirstSurfacePanelOpen } = chatFirstSurfacePanel;
618
+ const chatFirstSurfaceResize = useChatFirstSurfaceResize(chatFirstSurfaceScope);
619
+ const chatFirstPaneHydratedRef = useRef(false);
620
+ const pendingChatFirstOpenAppRef = useRef(null);
621
+ const previousChatFirstSurfaceScopeRef = useRef(chatFirstSurfaceScope);
622
+ const previousChatFirstSurfaceTabCountRef = useRef(null);
623
+ const activeChatFirstSurfaceTab = useMemo(() => chatFirstSurfaceTabs.tabs.find((tab) => tab.id === chatFirstSurfaceTabs.activeTabId) ?? null, [chatFirstSurfaceTabs]);
624
+ const activeChatFirstApp = useMemo(() => activeChatFirstSurfaceTab?.kind === "app" &&
625
+ activeChatFirstSurfaceTab.appId
626
+ ? (chatFirstWorkspaceApps.find((app) => app.id === activeChatFirstSurfaceTab.appId) ?? null)
627
+ : null, [activeChatFirstSurfaceTab, chatFirstWorkspaceApps]);
628
+ const chatFirstAgentActivities = useMemo(() => (chatFirstAgentsQuery.data?.threads ?? []).map((thread) => ({
629
+ sessionId: thread.id,
630
+ title: thread.title || chatFirstCopy("agentsTitle", { name: thread.id }),
631
+ subtitle: thread.snippet || thread.preview || chatFirstCopy("session"),
632
+ status: "recent",
633
+ ...(thread.updatedAt ? { updatedAt: thread.updatedAt } : {}),
634
+ })), [chatFirstAgentsQuery.data, chatFirstCopy]);
635
+ const chatFirstEmbedPath = activeChatFirstSurfaceTab?.kind === "app"
636
+ ? (activeChatFirstSurfaceTab.path ??
637
+ chatFirstPane?.path ??
638
+ activeChatFirstApp?.path)
639
+ : null;
640
+ useEffect(() => {
641
+ if (!chatFirstMode || !isChatRoute || !activeChatFirstApp) {
642
+ setChatFirstEmbedUrl(null);
643
+ setChatFirstEmbedError(null);
644
+ return;
645
+ }
646
+ if (!chatFirstEmbedPath?.startsWith("/")) {
647
+ setChatFirstEmbedUrl(null);
648
+ setChatFirstEmbedError(chatFirstCopy("appUnavailable"));
649
+ return;
650
+ }
651
+ let cancelled = false;
652
+ setChatFirstEmbedUrl(null);
653
+ setChatFirstEmbedError(null);
654
+ void createChatFirstEmbedSession
655
+ .mutateAsync(buildChatFirstEmbedSessionInput(activeChatFirstApp.id, chatFirstEmbedPath))
656
+ .then((result) => {
657
+ if (!cancelled)
658
+ setChatFirstEmbedUrl(result.startUrl);
659
+ })
660
+ .catch((cause) => {
661
+ if (!cancelled) {
662
+ setChatFirstEmbedError(cause instanceof Error
663
+ ? cause.message
664
+ : chatFirstCopy("appUnavailable"));
665
+ }
666
+ });
667
+ return () => {
668
+ cancelled = true;
669
+ };
670
+ }, [
671
+ activeChatFirstApp,
672
+ chatFirstCopy,
673
+ chatFirstEmbedAttempt,
674
+ chatFirstEmbedPath,
675
+ chatFirstMode,
676
+ createChatFirstEmbedSession.mutateAsync,
677
+ isChatRoute,
678
+ ]);
679
+ const persistChatFirstPane = useCallback((pane) => {
680
+ setChatFirstPane(pane);
681
+ void writeClientAppState(CHAT_FIRST_PANE_STATE_KEY, pane).catch(() => {
682
+ setChatFirstNotice("The app pane opened locally, but its focus could not be synced.");
683
+ });
684
+ }, []);
685
+ const openChatFirstPane = useCallback((pane) => {
686
+ setChatFirstNotice(null);
687
+ closeChatFirstSessionWatch();
688
+ const app = chatFirstAppRegistrations.find((candidate) => candidate.id === pane.appId);
689
+ chatFirstSurfaceTabsStore.open({
690
+ id: chatFirstSurfaceTabId("app", `${pane.appId}:${pane.path ?? pane.view ?? "/"}`),
691
+ kind: "app",
692
+ title: app?.name ?? pane.appId,
693
+ appId: pane.appId,
694
+ ...(pane.path ? { path: pane.path } : {}),
695
+ ...(pane.view ? { view: pane.view } : {}),
696
+ });
697
+ persistChatFirstPane(pane);
698
+ }, [
699
+ chatFirstAppRegistrations,
700
+ chatFirstSurfaceTabsStore,
701
+ persistChatFirstPane,
702
+ ]);
703
+ const openChatFirstSurface = useCallback((kind) => {
704
+ if (kind !== "agents")
705
+ return;
706
+ persistChatFirstPane(null);
707
+ closeChatFirstSessionWatch();
708
+ chatFirstSurfaceTabsStore.open({
709
+ id: chatFirstSurfaceTabId("agents", "activity"),
710
+ kind: "agents",
711
+ title: "Agents",
712
+ });
713
+ }, [chatFirstSurfaceTabsStore, persistChatFirstPane]);
714
+ const resolveChatFirstOpenBrowser = useCallback((detail) => {
715
+ const resolution = resolveChatFirstBrowserTarget(detail);
716
+ if (resolution.status === "unresolved") {
717
+ setChatFirstNotice(chatFirstBrowserResolutionMessage(resolution.reason));
718
+ return;
719
+ }
720
+ persistChatFirstPane(null);
721
+ closeChatFirstSessionWatch();
722
+ setChatFirstNotice(null);
723
+ chatFirstSurfaceTabsStore.open({
724
+ id: chatFirstSurfaceTabId("browser", resolution.target.url),
725
+ kind: "browser",
726
+ title: resolution.target.title ?? "Browser",
727
+ url: resolution.target.url,
728
+ });
729
+ }, [chatFirstSurfaceTabsStore, persistChatFirstPane]);
730
+ const resolveChatFirstOpenApp = useCallback((detail) => {
731
+ if (chatFirstAppsQuery.isLoading) {
732
+ pendingChatFirstOpenAppRef.current = detail;
733
+ return;
734
+ }
735
+ if (chatFirstAppsQuery.isError) {
736
+ pendingChatFirstOpenAppRef.current = null;
737
+ setChatFirstNotice("Workspace apps could not be loaded, so the requested app was not opened.");
738
+ return;
739
+ }
740
+ const resolution = resolveChatFirstAppTarget(detail, chatFirstAppRegistrations, {
741
+ currentOrigin: typeof window === "undefined" ? undefined : window.location.origin,
742
+ });
743
+ pendingChatFirstOpenAppRef.current = null;
744
+ if (resolution.status === "unresolved") {
745
+ setChatFirstNotice(chatFirstResolutionMessage(resolution.reason));
746
+ return;
747
+ }
748
+ openChatFirstPane(resolution.target);
749
+ }, [
750
+ chatFirstAppRegistrations,
751
+ chatFirstAppsQuery.isError,
752
+ chatFirstAppsQuery.isLoading,
753
+ openChatFirstPane,
754
+ ]);
376
755
  const sidebarBeforeAppRef = useRef(null);
377
756
  const sidebarAutoCollapsedRef = useRef(false);
378
757
  const chatHomeHandoffActive = useAgentChatHomeHandoff({
@@ -386,6 +765,194 @@ export function Layout({ children, extensions, agentPageHref, }) {
386
765
  requireActiveHandoff: true,
387
766
  };
388
767
  useAgentChatHomeHandoffLinks(chatHandoffLinkOptions);
768
+ useEffect(() => {
769
+ const handleModeChange = (event) => {
770
+ const enabled = event.detail
771
+ ?.enabled;
772
+ setChatFirstPreference(enabled === true);
773
+ };
774
+ window.addEventListener(CHAT_FIRST_MODE_CHANGED_EVENT, handleModeChange);
775
+ return () => window.removeEventListener(CHAT_FIRST_MODE_CHANGED_EVENT, handleModeChange);
776
+ }, []);
777
+ useEffect(() => {
778
+ if (!chatFirstMode ||
779
+ !isChatRoute ||
780
+ chatFirstAppLayoutHydratedRef.current) {
781
+ return;
782
+ }
783
+ chatFirstAppLayoutHydratedRef.current = true;
784
+ void readClientAppState("chat-first-app-layout")
785
+ .then((value) => {
786
+ if (!value || typeof value !== "object")
787
+ return;
788
+ const candidate = value;
789
+ const ids = (input) => Array.isArray(input)
790
+ ? input.filter((id) => typeof id === "string" && id.trim().length > 0)
791
+ : [];
792
+ setChatFirstAppLayout({
793
+ pinnedIds: [...new Set(ids(candidate.pinnedIds))],
794
+ orderedIds: [...new Set(ids(candidate.orderedIds))],
795
+ });
796
+ })
797
+ .catch(() => {
798
+ // Device-local layout remains the fallback when workspace state is unavailable.
799
+ });
800
+ }, [chatFirstMode, isChatRoute]);
801
+ const persistChatFirstAppLayout = useCallback((layout) => {
802
+ setChatFirstAppLayout(layout);
803
+ void writeClientAppState("chat-first-app-layout", layout).catch(() => {
804
+ setChatFirstNotice("App order changed locally, but workspace state could not be synced.");
805
+ });
806
+ }, []);
807
+ useEffect(() => {
808
+ if (!chatFirstMode || !isChatRoute) {
809
+ setChatFirstPane(null);
810
+ closeChatFirstSessionWatch();
811
+ chatFirstSurfaceTabsStore.closeAll();
812
+ setChatFirstNotice(null);
813
+ pendingChatFirstOpenAppRef.current = null;
814
+ chatFirstPaneHydratedRef.current = false;
815
+ return;
816
+ }
817
+ const unsubscribeApp = subscribeChatFirstOpenApp(resolveChatFirstOpenApp);
818
+ const unsubscribeBrowser = subscribeChatFirstOpenBrowser(resolveChatFirstOpenBrowser);
819
+ return () => {
820
+ unsubscribeApp();
821
+ unsubscribeBrowser();
822
+ };
823
+ }, [
824
+ chatFirstMode,
825
+ isChatRoute,
826
+ chatFirstSurfaceTabsStore,
827
+ resolveChatFirstOpenApp,
828
+ resolveChatFirstOpenBrowser,
829
+ ]);
830
+ useEffect(() => {
831
+ if (!chatFirstMode ||
832
+ !isChatRoute ||
833
+ chatFirstPaneHydratedRef.current ||
834
+ chatFirstAppsQuery.isLoading ||
835
+ chatFirstAppsQuery.isError) {
836
+ return;
837
+ }
838
+ chatFirstPaneHydratedRef.current = true;
839
+ if (chatFirstPane)
840
+ return;
841
+ let active = true;
842
+ void readClientAppState(CHAT_FIRST_PANE_STATE_KEY)
843
+ .then((value) => {
844
+ if (!active)
845
+ return;
846
+ const persisted = persistedChatFirstPane(value);
847
+ if (!persisted)
848
+ return;
849
+ const resolution = resolveChatFirstAppTarget({
850
+ app: persisted.appId,
851
+ path: persisted.path,
852
+ view: persisted.view,
853
+ }, chatFirstAppRegistrations, {
854
+ currentOrigin: typeof window === "undefined"
855
+ ? undefined
856
+ : window.location.origin,
857
+ });
858
+ if (resolution.status === "ready")
859
+ openChatFirstPane(resolution.target);
860
+ })
861
+ .catch(() => {
862
+ if (active) {
863
+ setChatFirstNotice("The last app pane could not be restored from workspace state.");
864
+ }
865
+ });
866
+ return () => {
867
+ active = false;
868
+ };
869
+ }, [
870
+ chatFirstAppsQuery.isError,
871
+ chatFirstAppsQuery.isLoading,
872
+ chatFirstAppRegistrations,
873
+ chatFirstMode,
874
+ chatFirstPane,
875
+ isChatRoute,
876
+ openChatFirstPane,
877
+ ]);
878
+ useEffect(() => {
879
+ if (previousChatFirstSurfaceScopeRef.current === chatFirstSurfaceScope) {
880
+ return;
881
+ }
882
+ previousChatFirstSurfaceScopeRef.current = chatFirstSurfaceScope;
883
+ persistChatFirstPane(null);
884
+ setChatFirstEmbedUrl(null);
885
+ setChatFirstEmbedError(null);
886
+ setChatFirstNotice(null);
887
+ pendingChatFirstOpenAppRef.current = null;
888
+ chatFirstPaneHydratedRef.current = true;
889
+ chatFirstSurfaceTabsStore.closeAll();
890
+ setChatFirstSurfacePanelOpen(false);
891
+ closeChatFirstSessionWatch();
892
+ }, [
893
+ chatFirstSurfaceScope,
894
+ chatFirstSurfaceTabsStore,
895
+ persistChatFirstPane,
896
+ setChatFirstSurfacePanelOpen,
897
+ ]);
898
+ useEffect(() => {
899
+ const tabCount = chatFirstSurfaceTabs.tabs.length;
900
+ const previousTabCount = previousChatFirstSurfaceTabCountRef.current;
901
+ if (!chatFirstMode || !isChatRoute) {
902
+ setChatFirstSurfacePanelOpen(false);
903
+ }
904
+ else if (tabCount > 0 &&
905
+ (previousTabCount === null || previousTabCount === 0)) {
906
+ setChatFirstSurfacePanelOpen(true);
907
+ }
908
+ else if (tabCount === 0 &&
909
+ previousTabCount !== null &&
910
+ previousTabCount > 0) {
911
+ setChatFirstSurfacePanelOpen(false);
912
+ }
913
+ previousChatFirstSurfaceTabCountRef.current = tabCount;
914
+ }, [
915
+ chatFirstMode,
916
+ setChatFirstSurfacePanelOpen,
917
+ chatFirstSurfaceTabs.tabs.length,
918
+ isChatRoute,
919
+ ]);
920
+ useEffect(() => {
921
+ const activeTab = activeChatFirstSurfaceTab;
922
+ if (activeTab?.kind === "side-chat" &&
923
+ activeTab.session &&
924
+ !chatFirstSessionWatch.target) {
925
+ emitChatFirstSessionWatch(activeTab.session);
926
+ }
927
+ }, [activeChatFirstSurfaceTab, chatFirstSessionWatch.target]);
928
+ useEffect(() => {
929
+ const pending = pendingChatFirstOpenAppRef.current;
930
+ if (!pending || chatFirstAppsQuery.isLoading)
931
+ return;
932
+ resolveChatFirstOpenApp(pending);
933
+ }, [
934
+ chatFirstAppsQuery.data,
935
+ chatFirstAppsQuery.isError,
936
+ chatFirstAppsQuery.isLoading,
937
+ resolveChatFirstOpenApp,
938
+ ]);
939
+ useEffect(() => {
940
+ const target = chatFirstSessionWatch.target;
941
+ if (!target || !chatFirstMode || !isChatRoute)
942
+ return;
943
+ setChatFirstPane(null);
944
+ chatFirstSurfaceTabsStore.open({
945
+ id: chatFirstSurfaceTabId("side-chat", target.sessionId),
946
+ kind: "side-chat",
947
+ title: target.title ? `Watch · ${target.title}` : "Watched session",
948
+ session: target,
949
+ });
950
+ }, [
951
+ chatFirstMode,
952
+ chatFirstSessionWatch.target,
953
+ chatFirstSurfaceTabsStore,
954
+ isChatRoute,
955
+ ]);
389
956
  useEffect(() => {
390
957
  if (isWorkspaceAppHostRoute) {
391
958
  if (!sidebarAutoCollapsedRef.current) {
@@ -416,9 +983,114 @@ export function Layout({ children, extensions, agentPageHref, }) {
416
983
  // Ignore storage failures; the in-memory preference still works.
417
984
  }
418
985
  }, [isWorkspaceAppHostRoute, sidebarCollapsed]);
986
+ const activateChatFirstSurfaceTab = useCallback((tab) => {
987
+ chatFirstSurfaceTabsStore.activate(tab.id);
988
+ if (tab.kind === "app" && tab.appId) {
989
+ closeChatFirstSessionWatch();
990
+ persistChatFirstPane({
991
+ appId: tab.appId,
992
+ ...(tab.path ? { path: tab.path } : {}),
993
+ ...(tab.view ? { view: tab.view } : {}),
994
+ });
995
+ return;
996
+ }
997
+ if (tab.kind === "browser" && tab.url) {
998
+ persistChatFirstPane(null);
999
+ closeChatFirstSessionWatch();
1000
+ return;
1001
+ }
1002
+ if (tab.kind === "side-chat" && tab.session) {
1003
+ persistChatFirstPane(null);
1004
+ emitChatFirstSessionWatch(tab.session);
1005
+ return;
1006
+ }
1007
+ if (tab.kind === "agents") {
1008
+ persistChatFirstPane(null);
1009
+ closeChatFirstSessionWatch();
1010
+ }
1011
+ }, [
1012
+ chatFirstSurfaceTabs.tabs,
1013
+ chatFirstSurfaceTabsStore,
1014
+ persistChatFirstPane,
1015
+ ]);
1016
+ const closeChatFirstSurfaceTab = useCallback((tab) => {
1017
+ const isActive = chatFirstSurfaceTabs.activeTabId === tab.id;
1018
+ if (tab.kind === "app" && isActive)
1019
+ persistChatFirstPane(null);
1020
+ if (tab.kind === "side-chat" && isActive) {
1021
+ closeChatFirstSessionWatch();
1022
+ }
1023
+ chatFirstSurfaceTabsStore.close(tab.id);
1024
+ }, [chatFirstSurfaceTabs, chatFirstSurfaceTabsStore, persistChatFirstPane]);
1025
+ const closeAllChatFirstSurfaceTabs = useCallback(() => {
1026
+ persistChatFirstPane(null);
1027
+ closeChatFirstSessionWatch();
1028
+ chatFirstSurfaceTabsStore.closeAll();
1029
+ }, [chatFirstSurfaceTabsStore, persistChatFirstPane]);
1030
+ const renderChatFirstWatchChat = useCallback((target) => (_jsx(AgentChatSurface, { mode: "page", className: "h-full min-h-0 w-full", defaultMode: "chat", storageKey: `dispatch-session-watch:${target.sessionId}`, restoreActiveThread: false, threadUrlSync: {
1031
+ routeThreadId: target.sessionId,
1032
+ getPath: chatThreadPath,
1033
+ navigate: () => undefined,
1034
+ }, showHeader: false, showTabBar: false, showPageNewChatButton: false, chatOnly: true, dynamicSuggestions: false, suggestions: [], emptyStateDisplay: "hidden", composerPlaceholder: t("dispatch.pages.chatFirstSessionMessagePlaceholder") })), [t]);
1035
+ const renderChatFirstSurfaceTab = useCallback((tab) => {
1036
+ if (tab.kind === "app") {
1037
+ if (tab.id !== chatFirstSurfaceTabs.activeTabId)
1038
+ return null;
1039
+ const app = tab.appId
1040
+ ? (chatFirstWorkspaceApps.find((candidate) => candidate.id === tab.appId) ?? null)
1041
+ : null;
1042
+ return (_jsx(ChatFirstAppPane, { app: app, status: chatFirstEmbedError
1043
+ ? "error"
1044
+ : chatFirstEmbedUrl
1045
+ ? "ready"
1046
+ : chatFirstAppsQuery.isLoading
1047
+ ? "loading"
1048
+ : app
1049
+ ? "loading"
1050
+ : "unresolved", embedUrl: chatFirstEmbedUrl, errorMessage: chatFirstEmbedError, onRetry: () => setChatFirstEmbedAttempt((value) => value + 1), renderEmbed: ({ url, title }) => (_jsx("iframe", { "data-dispatch-chat-first-app-frame": true, src: url, title: title ?? chatFirstCopy("appUnavailable"), referrerPolicy: "no-referrer", allow: "clipboard-read; clipboard-write", className: "h-full w-full border-0 bg-background" })), copy: chatFirstCopy }));
1051
+ }
1052
+ if (tab.kind === "browser" && tab.url) {
1053
+ return (_jsx(ChatFirstBrowserPane, { url: tab.url, title: tab.title, onClose: () => closeChatFirstSurfaceTab(tab), renderEmbed: ({ url, title, key }) => (_jsx("iframe", { src: url, title: title ?? chatFirstCopy("browserPage"), referrerPolicy: "no-referrer", allow: "clipboard-read; clipboard-write", className: "h-full w-full border-0 bg-background" }, key)), copy: chatFirstCopy }));
1054
+ }
1055
+ if (tab.kind === "side-chat") {
1056
+ const target = tab.session ??
1057
+ (tab.id === chatFirstSurfaceTabs.activeTabId
1058
+ ? chatFirstSessionWatch.target
1059
+ : null);
1060
+ return target ? (_jsx(ChatFirstSessionWatchPane, { target: target, onClose: () => closeChatFirstSurfaceTab(tab), renderChat: renderChatFirstWatchChat, copy: chatFirstCopy })) : null;
1061
+ }
1062
+ if (tab.kind === "agents") {
1063
+ return (_jsx(ChatFirstAgentsPane, { activities: chatFirstAgentActivities, loading: chatFirstAgentsQuery.isLoading, error: chatFirstAgentsQuery.isError
1064
+ ? chatFirstAgentsQuery.error.message
1065
+ : null, onRefresh: () => void chatFirstAgentsQuery.refetch(), onWatch: (activity) => emitChatFirstSessionWatch({
1066
+ sessionId: activity.sessionId,
1067
+ title: activity.title,
1068
+ kind: "agent-chat",
1069
+ ...(activity.goalId ? { goalId: activity.goalId } : {}),
1070
+ }), copy: chatFirstCopy }));
1071
+ }
1072
+ return null;
1073
+ }, [
1074
+ chatFirstAgentActivities,
1075
+ chatFirstAgentsQuery.error,
1076
+ chatFirstAgentsQuery.isError,
1077
+ chatFirstAgentsQuery.isLoading,
1078
+ chatFirstAppsQuery.isLoading,
1079
+ chatFirstCopy,
1080
+ chatFirstEmbedError,
1081
+ chatFirstEmbedUrl,
1082
+ chatFirstSessionWatch.target,
1083
+ chatFirstSurfaceTabs.activeTabId,
1084
+ chatFirstWorkspaceApps,
1085
+ closeChatFirstSurfaceTab,
1086
+ renderChatFirstWatchChat,
1087
+ ]);
419
1088
  if (CHROMELESS_PATHS.some((path) => localPathname === path)) {
420
1089
  return _jsx(_Fragment, { children: children });
421
1090
  }
1091
+ if (isWorkspaceAppHostRoute) {
1092
+ return (_jsx(DispatchExtensionsContext.Provider, { value: extensions, children: _jsx(HeaderActionsProvider, { children: _jsx("div", { "data-dispatch-workspace-app-chrome-less": true, className: "h-screen w-full overflow-hidden bg-background", children: children }) }) }));
1093
+ }
422
1094
  const showHeader = !isChatRoute && !pageOwnsToolbar(localPathname);
423
1095
  function openAskAgentFullscreen() {
424
1096
  focusAgentChat();
@@ -429,10 +1101,27 @@ export function Layout({ children, extensions, agentPageHref, }) {
429
1101
  t("dispatch.sidebar.suggestionRouteSlack"),
430
1102
  t("dispatch.sidebar.suggestionGrantKey"),
431
1103
  ];
432
- const appContent = (_jsxs("div", { className: "flex h-full min-w-0 flex-1 flex-col overflow-hidden", children: [showHeader ? _jsx(Header, { onOpenMobile: () => setMobileOpen(true) }) : null, _jsx(InvitationBanner, {}), _jsx("main", { className: cn("flex-1", isChatRoute || isWorkspaceAppHostRoute
1104
+ const appContent = (_jsxs("div", { className: "relative flex h-full min-w-0 flex-1 flex-col overflow-hidden", children: [showHeader ? _jsx(Header, { onOpenMobile: () => setMobileOpen(true) }) : null, _jsx(InvitationBanner, {}), isChatRoute && chatFirstMode ? (_jsx(ChatFirstSurfacePanelToggle, { open: chatFirstSurfacePanel.open, onToggle: chatFirstSurfacePanel.toggle, className: "absolute right-3 top-2 z-[4]" })) : null, isChatRoute && chatFirstMode && chatFirstNotice ? (_jsxs("div", { className: "dispatch-chat-first-notice", role: "status", "aria-live": "polite", children: [_jsx("span", { children: chatFirstNotice }), _jsx("button", { type: "button", onClick: () => setChatFirstNotice(null), "aria-label": "Dismiss app notice", children: "Dismiss" })] })) : null, _jsx("main", { className: cn("flex-1", isChatRoute || isWorkspaceAppHostRoute
433
1105
  ? "min-h-0 overflow-hidden"
434
1106
  : "overflow-y-auto"), children: showHeader ? (_jsx("div", { className: "mx-auto max-w-7xl space-y-10 px-4 py-6 sm:px-6", children: children })) : (children) })] }));
435
- const content = isChatRoute ? (_jsx("div", { className: "agent-layout-main-surface flex min-w-0 flex-1 overflow-hidden", children: appContent })) : (_jsx(AgentSidebar, { position: "right", defaultOpen: false, agentPageHref: agentPageHref, chatViewTransition: true, storageKey: "dispatch", openOnChatRunning: chatHomeHandoffActive, onFullscreenRequest: openAskAgentFullscreen, emptyStateText: t("dispatch.sidebar.emptyAgentText"), suggestions: sidebarSuggestions, children: appContent }));
436
- return (_jsx(DispatchExtensionsContext.Provider, { value: extensions, children: _jsx(HeaderActionsProvider, { children: _jsxs("div", { className: "agent-layout-shell flex h-screen w-full overflow-hidden bg-background", children: [_jsx("aside", { "data-collapsed": sidebarCollapsed ? "true" : "false", className: cn("agent-layout-left-drawer hidden shrink-0 flex-col border-e !border-e-sidebar-border bg-sidebar text-sidebar-foreground transition-[width] duration-200 ease-out lg:flex", sidebarCollapsed ? "w-14" : "w-56"), children: _jsx(NavContent, { extensions: extensions, collapsed: sidebarCollapsed, collapsible: true, onCollapsedChange: setSidebarCollapsed }) }), _jsx(Sheet, { open: mobileOpen, onOpenChange: setMobileOpen, children: _jsxs(SheetContent, { side: "left", className: "w-72 p-0 bg-sidebar text-sidebar-foreground [&>button]:hidden", children: [_jsx(SheetTitle, { className: "sr-only", children: t("dispatch.nav.navigation") }), _jsx(SheetDescription, { className: "sr-only", children: t("dispatch.nav.navigationDescription") }), _jsx("div", { className: "flex h-full w-full flex-col", children: _jsx(NavContent, { extensions: extensions, collapsed: false, onNavigate: () => setMobileOpen(false) }) })] }) }), content] }) }) }));
1107
+ const content = isChatRoute ? (_jsxs("div", { className: cn("agent-layout-main-surface flex min-w-0 flex-1 overflow-hidden", chatFirstMode && "dispatch-chat-first-surface"), children: [appContent, chatFirstMode && chatFirstSurfacePanel.open ? (_jsxs(ChatFirstSurfacePanel, { width: chatFirstSurfaceResize.width, onResizePointerDown: chatFirstSurfaceResize.onPointerDown, copy: chatFirstCopy, children: [_jsx(ChatFirstSurfaceTabs, { tabs: chatFirstSurfaceTabs.tabs, activeTabId: chatFirstSurfaceTabs.activeTabId, onActivate: activateChatFirstSurfaceTab, onClose: closeChatFirstSurfaceTab, onCloseOthers: (tab) => {
1108
+ activateChatFirstSurfaceTab(tab);
1109
+ chatFirstSurfaceTabsStore.closeOthers(tab.id);
1110
+ }, onCloseToRight: (tab) => {
1111
+ const targetIndex = chatFirstSurfaceTabs.tabs.findIndex((candidate) => candidate.id === tab.id);
1112
+ const activeIndex = chatFirstSurfaceTabs.tabs.findIndex((candidate) => candidate.id === chatFirstSurfaceTabs.activeTabId);
1113
+ if (activeIndex > targetIndex)
1114
+ activateChatFirstSurfaceTab(tab);
1115
+ chatFirstSurfaceTabsStore.closeToRight(tab.id);
1116
+ }, onCloseAll: closeAllChatFirstSurfaceTabs, onOpenSurface: openChatFirstSurface, copy: chatFirstCopy }), chatFirstSurfaceTabs.tabs.length > 0 ? (_jsx(ChatFirstSurfaceContent, { tabs: chatFirstSurfaceTabs.tabs, activeTabId: chatFirstSurfaceTabs.activeTabId, renderTab: renderChatFirstSurfaceTab })) : null] })) : null] })) : (_jsx(AgentSidebar, { position: "right", defaultOpen: false, agentPageHref: agentPageHref, chatViewTransition: true, storageKey: "dispatch", openOnChatRunning: chatHomeHandoffActive, onFullscreenRequest: openAskAgentFullscreen, emptyStateText: t("dispatch.sidebar.emptyAgentText"), suggestions: sidebarSuggestions, children: appContent }));
1117
+ return (_jsx(DispatchExtensionsContext.Provider, { value: extensions, children: _jsx(HeaderActionsProvider, { children: _jsxs("div", { className: "agent-layout-shell flex h-screen w-full overflow-hidden bg-background", children: [_jsx("aside", { "data-collapsed": sidebarCollapsed ? "true" : "false", className: cn("agent-layout-left-drawer hidden shrink-0 flex-col border-e !border-e-sidebar-border bg-sidebar text-sidebar-foreground transition-[width] duration-200 ease-out lg:flex", sidebarCollapsed ? "w-14" : "w-56"), children: _jsx(NavContent, { extensions: extensions, chatFirstMode: chatFirstMode, chatFirstEmbedded: chatFirstEmbedded, collapsed: sidebarCollapsed, chatFirstAppLayout: chatFirstAppLayout, onChatFirstAppLayoutChange: persistChatFirstAppLayout, chatFirstApps: chatFirstAppItems, chatFirstAppsLoading: chatFirstAppsQuery.isLoading, chatFirstAppsError: chatFirstAppsQuery.isError
1118
+ ? chatFirstCopy("appsLoadError")
1119
+ : null, chatFirstActiveAppId: activeChatFirstSurfaceTab?.kind === "app"
1120
+ ? activeChatFirstSurfaceTab.appId
1121
+ : undefined, onChatFirstAppOpen: (app) => openChatFirstPane({ appId: app.id }), onChatFirstAppsRetry: () => void chatFirstAppsQuery.refetch(), collapsible: true, onCollapsedChange: setSidebarCollapsed }) }), _jsx(Sheet, { open: mobileOpen, onOpenChange: setMobileOpen, children: _jsxs(SheetContent, { side: "left", className: "w-72 p-0 bg-sidebar text-sidebar-foreground [&>button]:hidden", children: [_jsx(SheetTitle, { className: "sr-only", children: t("dispatch.nav.navigation") }), _jsx(SheetDescription, { className: "sr-only", children: t("dispatch.nav.navigationDescription") }), _jsx("div", { className: "flex h-full w-full flex-col", children: _jsx(NavContent, { extensions: extensions, chatFirstMode: chatFirstMode, chatFirstEmbedded: chatFirstEmbedded, collapsed: false, chatFirstAppLayout: chatFirstAppLayout, onChatFirstAppLayoutChange: persistChatFirstAppLayout, chatFirstApps: chatFirstAppItems, chatFirstAppsLoading: chatFirstAppsQuery.isLoading, chatFirstAppsError: chatFirstAppsQuery.isError
1122
+ ? chatFirstCopy("appsLoadError")
1123
+ : null, chatFirstActiveAppId: activeChatFirstSurfaceTab?.kind === "app"
1124
+ ? activeChatFirstSurfaceTab.appId
1125
+ : undefined, onChatFirstAppOpen: (app) => openChatFirstPane({ appId: app.id }), onChatFirstAppsRetry: () => void chatFirstAppsQuery.refetch(), onNavigate: () => setMobileOpen(false) }) })] }) }), content] }) }) }));
437
1126
  }
438
1127
  //# sourceMappingURL=Layout.js.map