@agent-native/dispatch 0.41.1-nightly-20261104110058 → 0.42.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.
- package/CHANGELOG.md +26 -55
- package/dist/actions/get-hosted-apps-settings.d.ts +5 -0
- package/dist/actions/get-hosted-apps-settings.d.ts.map +1 -0
- package/dist/actions/get-hosted-apps-settings.js +25 -0
- package/dist/actions/get-hosted-apps-settings.js.map +1 -0
- package/dist/actions/index.d.ts.map +1 -1
- package/dist/actions/index.js +4 -0
- package/dist/actions/index.js.map +1 -1
- package/dist/actions/list-connected-agents.d.ts +1 -0
- package/dist/actions/list-connected-agents.js +11 -10
- package/dist/actions/list-connected-agents.js.map +1 -1
- package/dist/actions/list_apps.js +3 -2
- package/dist/actions/list_apps.js.map +1 -1
- package/dist/actions/provider-api-register.d.ts +10 -10
- package/dist/actions/set-hosted-apps-settings.d.ts +7 -0
- package/dist/actions/set-hosted-apps-settings.d.ts.map +1 -0
- package/dist/actions/set-hosted-apps-settings.js +35 -0
- package/dist/actions/set-hosted-apps-settings.js.map +1 -0
- package/dist/components/app-card-tiers.d.ts +13 -0
- package/dist/components/app-card-tiers.d.ts.map +1 -0
- package/dist/components/app-card-tiers.js +22 -0
- package/dist/components/app-card-tiers.js.map +1 -0
- package/dist/components/app-list-row.d.ts +12 -4
- package/dist/components/app-list-row.d.ts.map +1 -1
- package/dist/components/app-list-row.js +19 -4
- package/dist/components/app-list-row.js.map +1 -1
- package/dist/components/app-open-actions.d.ts +4 -0
- package/dist/components/app-open-actions.d.ts.map +1 -1
- package/dist/components/app-open-actions.js +9 -0
- package/dist/components/app-open-actions.js.map +1 -1
- package/dist/components/app-pin-button.d.ts +9 -0
- package/dist/components/app-pin-button.d.ts.map +1 -0
- package/dist/components/app-pin-button.js +29 -0
- package/dist/components/app-pin-button.js.map +1 -0
- package/dist/components/app-tier-sections.d.ts +27 -0
- package/dist/components/app-tier-sections.d.ts.map +1 -0
- package/dist/components/app-tier-sections.js +44 -0
- package/dist/components/app-tier-sections.js.map +1 -0
- package/dist/components/chat-home-apps.d.ts.map +1 -1
- package/dist/components/chat-home-apps.js +19 -26
- package/dist/components/chat-home-apps.js.map +1 -1
- package/dist/components/connected-app-card.d.ts +3 -1
- package/dist/components/connected-app-card.d.ts.map +1 -1
- package/dist/components/connected-app-card.js +6 -3
- package/dist/components/connected-app-card.js.map +1 -1
- package/dist/components/create-app-popover.d.ts +4 -0
- package/dist/components/create-app-popover.d.ts.map +1 -1
- package/dist/components/create-app-popover.js +6 -0
- package/dist/components/create-app-popover.js.map +1 -1
- package/dist/components/dispatch-control-plane.d.ts.map +1 -1
- package/dist/components/dispatch-control-plane.js +25 -35
- package/dist/components/dispatch-control-plane.js.map +1 -1
- package/dist/components/hosted-apps-settings-group.d.ts +6 -0
- package/dist/components/hosted-apps-settings-group.d.ts.map +1 -0
- package/dist/components/hosted-apps-settings-group.js +76 -0
- package/dist/components/hosted-apps-settings-group.js.map +1 -0
- package/dist/components/layout/Layout.d.ts +6 -4
- package/dist/components/layout/Layout.d.ts.map +1 -1
- package/dist/components/layout/Layout.js +97 -80
- package/dist/components/layout/Layout.js.map +1 -1
- package/dist/components/workspace-app-card.d.ts.map +1 -1
- package/dist/components/workspace-app-card.js +9 -20
- package/dist/components/workspace-app-card.js.map +1 -1
- package/dist/components/workspace-app-layout-notice.d.ts +9 -0
- package/dist/components/workspace-app-layout-notice.d.ts.map +1 -0
- package/dist/components/workspace-app-layout-notice.js +27 -0
- package/dist/components/workspace-app-layout-notice.js.map +1 -0
- package/dist/components/workspace-app-search.d.ts.map +1 -1
- package/dist/components/workspace-app-search.js +11 -5
- package/dist/components/workspace-app-search.js.map +1 -1
- package/dist/components/workspace-template-card.d.ts.map +1 -1
- package/dist/components/workspace-template-card.js +7 -2
- package/dist/components/workspace-template-card.js.map +1 -1
- package/dist/db/index.d.ts.map +1 -1
- package/dist/db/index.js +1 -0
- package/dist/db/index.js.map +1 -1
- package/dist/lib/other-apps.d.ts +10 -1
- package/dist/lib/other-apps.d.ts.map +1 -1
- package/dist/lib/other-apps.js +7 -1
- package/dist/lib/other-apps.js.map +1 -1
- package/dist/lib/workspace-app-layout.d.ts +62 -6
- package/dist/lib/workspace-app-layout.d.ts.map +1 -1
- package/dist/lib/workspace-app-layout.js +196 -41
- package/dist/lib/workspace-app-layout.js.map +1 -1
- package/dist/lib/workspace-app-lists.d.ts +17 -0
- package/dist/lib/workspace-app-lists.d.ts.map +1 -0
- package/dist/lib/workspace-app-lists.js +21 -0
- package/dist/lib/workspace-app-lists.js.map +1 -0
- package/dist/lib/workspace-apps.d.ts +3 -1
- package/dist/lib/workspace-apps.d.ts.map +1 -1
- package/dist/lib/workspace-apps.js +5 -1
- package/dist/lib/workspace-apps.js.map +1 -1
- package/dist/routes/pages/apps.d.ts.map +1 -1
- package/dist/routes/pages/apps.js +23 -35
- package/dist/routes/pages/apps.js.map +1 -1
- package/dist/routes/pages/settings-page.d.ts.map +1 -1
- package/dist/routes/pages/settings-page.js +24 -3
- package/dist/routes/pages/settings-page.js.map +1 -1
- package/dist/server/lib/app-creation-store.d.ts.map +1 -1
- package/dist/server/lib/app-creation-store.js +13 -18
- package/dist/server/lib/app-creation-store.js.map +1 -1
- package/dist/server/lib/app-roles.d.ts +2 -0
- package/dist/server/lib/app-roles.d.ts.map +1 -1
- package/dist/server/lib/app-roles.js +26 -7
- package/dist/server/lib/app-roles.js.map +1 -1
- package/dist/server/lib/hosted-apps-settings-schema.d.ts +6 -0
- package/dist/server/lib/hosted-apps-settings-schema.d.ts.map +1 -0
- package/dist/server/lib/hosted-apps-settings-schema.js +6 -0
- package/dist/server/lib/hosted-apps-settings-schema.js.map +1 -0
- package/dist/server/lib/hosted-apps-settings.d.ts +27 -0
- package/dist/server/lib/hosted-apps-settings.d.ts.map +1 -0
- package/dist/server/lib/hosted-apps-settings.js +72 -0
- package/dist/server/lib/hosted-apps-settings.js.map +1 -0
- package/dist/server/plugins/agent-chat.js +6 -3
- package/dist/server/plugins/agent-chat.js.map +1 -1
- package/dist/server/plugins/integrations.js +3 -3
- package/dist/server/plugins/integrations.js.map +1 -1
- package/dist/shared/hosted-apps-settings.d.ts +3 -0
- package/dist/shared/hosted-apps-settings.d.ts.map +1 -0
- package/dist/shared/hosted-apps-settings.js +3 -0
- package/dist/shared/hosted-apps-settings.js.map +1 -0
- package/package.json +4 -4
- package/src/actions/get-hosted-apps-settings.ts +29 -0
- package/src/actions/hosted-apps-settings.spec.ts +100 -0
- package/src/actions/index.ts +4 -0
- package/src/actions/list-connected-agents.spec.ts +67 -0
- package/src/actions/list-connected-agents.ts +14 -15
- package/src/actions/list_apps.spec.ts +16 -0
- package/src/actions/list_apps.ts +5 -2
- package/src/actions/set-hosted-apps-settings.ts +42 -0
- package/src/components/app-card-tiers.tsx +85 -0
- package/src/components/app-list-row.spec.tsx +54 -0
- package/src/components/app-list-row.tsx +50 -8
- package/src/components/app-open-actions.tsx +16 -0
- package/src/components/app-pin-button.spec.tsx +68 -0
- package/src/components/app-pin-button.tsx +66 -0
- package/src/components/app-tier-sections.spec.tsx +155 -0
- package/src/components/app-tier-sections.tsx +158 -0
- package/src/components/chat-home-apps.spec.tsx +135 -29
- package/src/components/chat-home-apps.tsx +115 -111
- package/src/components/connected-app-card.spec.tsx +18 -2
- package/src/components/connected-app-card.tsx +18 -1
- package/src/components/create-app-popover.tsx +22 -0
- package/src/components/dispatch-control-plane.spec.tsx +89 -20
- package/src/components/dispatch-control-plane.tsx +91 -125
- package/src/components/hosted-apps-settings-group.spec.tsx +218 -0
- package/src/components/hosted-apps-settings-group.tsx +189 -0
- package/src/components/layout/Layout.spec.tsx +356 -49
- package/src/components/layout/Layout.tsx +189 -142
- package/src/components/workspace-app-card.spec.tsx +33 -29
- package/src/components/workspace-app-card.tsx +13 -31
- package/src/components/workspace-app-layout-notice.spec.tsx +106 -0
- package/src/components/workspace-app-layout-notice.tsx +65 -0
- package/src/components/workspace-app-search.spec.tsx +53 -0
- package/src/components/workspace-app-search.tsx +39 -32
- package/src/components/workspace-template-card.spec.tsx +10 -0
- package/src/components/workspace-template-card.tsx +7 -2
- package/src/db/index.spec.ts +9 -0
- package/src/db/index.ts +1 -0
- package/src/lib/other-apps.spec.ts +25 -0
- package/src/lib/other-apps.ts +9 -0
- package/src/lib/workspace-app-layout.spec.ts +61 -3
- package/src/lib/workspace-app-layout.store.spec.tsx +261 -0
- package/src/lib/workspace-app-layout.ts +265 -48
- package/src/lib/workspace-app-lists.ts +32 -0
- package/src/lib/workspace-apps.ts +10 -2
- package/src/routes/pages/apps.spec.tsx +133 -5
- package/src/routes/pages/apps.tsx +79 -140
- package/src/routes/pages/chat.spec.tsx +66 -49
- package/src/routes/pages/settings-page.tsx +31 -3
- package/src/routes/pages/settings.spec.tsx +37 -0
- package/src/server/lib/app-creation-store.spec.ts +14 -0
- package/src/server/lib/app-creation-store.ts +16 -29
- package/src/server/lib/app-creation-store.write-observation.spec.ts +216 -0
- package/src/server/lib/app-roles.spec.ts +48 -1
- package/src/server/lib/app-roles.ts +44 -12
- package/src/server/lib/hosted-apps-settings-schema.ts +6 -0
- package/src/server/lib/hosted-apps-settings.spec.ts +154 -0
- package/src/server/lib/hosted-apps-settings.ts +108 -0
- package/src/server/plugins/agent-chat.spec.ts +16 -0
- package/src/server/plugins/agent-chat.ts +7 -2
- package/src/server/plugins/integrations.ts +3 -3
- package/src/shared/hosted-apps-settings.ts +3 -0
- package/src/styles/dispatch.css +40 -11
- package/dist/components/available-apps-section.d.ts +0 -9
- package/dist/components/available-apps-section.d.ts.map +0 -1
- package/dist/components/available-apps-section.js +0 -97
- package/dist/components/available-apps-section.js.map +0 -1
- package/dist/components/other-apps-section.d.ts +0 -35
- package/dist/components/other-apps-section.d.ts.map +0 -1
- package/dist/components/other-apps-section.js +0 -79
- package/dist/components/other-apps-section.js.map +0 -1
- package/src/components/available-apps-section.tsx +0 -239
- package/src/components/other-apps-section.spec.ts +0 -50
- package/src/components/other-apps-section.tsx +0 -254
|
@@ -5,7 +5,6 @@ import {
|
|
|
5
5
|
emitChatFirstSessionWatch,
|
|
6
6
|
getChatFirstSurfaceTabsStore,
|
|
7
7
|
navigateWithAgentChatViewTransition,
|
|
8
|
-
readChatFirstAppLayout,
|
|
9
8
|
readChatFirstMode,
|
|
10
9
|
resolveChatFirstBrowserTarget,
|
|
11
10
|
resolveChatFirstAppTarget,
|
|
@@ -51,6 +50,7 @@ import type { ChatFirstPrimaryTab } from "@agent-native/toolkit/app/chat/chat-fi
|
|
|
51
50
|
import type {
|
|
52
51
|
ChatFirstAgentActivity,
|
|
53
52
|
ChatFirstAppItem,
|
|
53
|
+
ChatFirstAppRailSection,
|
|
54
54
|
ChatFirstCopy,
|
|
55
55
|
ChatFirstEmbedTarget,
|
|
56
56
|
} from "@agent-native/toolkit/app/chat/chat-first/types";
|
|
@@ -107,7 +107,12 @@ import {
|
|
|
107
107
|
type ConnectedAppSummary,
|
|
108
108
|
} from "../../lib/other-apps";
|
|
109
109
|
import { cn } from "../../lib/utils";
|
|
110
|
-
import {
|
|
110
|
+
import {
|
|
111
|
+
tierWorkspaceApps,
|
|
112
|
+
useWorkspaceAppLayout,
|
|
113
|
+
appListsStatus,
|
|
114
|
+
type WorkspaceAppsStatus,
|
|
115
|
+
} from "../../lib/workspace-app-layout";
|
|
111
116
|
import {
|
|
112
117
|
isDispatchWorkspaceAppId,
|
|
113
118
|
isPathMountedWorkspaceApp,
|
|
@@ -153,6 +158,10 @@ import {
|
|
|
153
158
|
WorkspaceAppFrame,
|
|
154
159
|
WorkspaceAppKeepAlive,
|
|
155
160
|
} from "../workspace-app-host";
|
|
161
|
+
import {
|
|
162
|
+
AppNotice,
|
|
163
|
+
WorkspaceAppLayoutNotice,
|
|
164
|
+
} from "../workspace-app-layout-notice";
|
|
156
165
|
import { Header } from "./Header";
|
|
157
166
|
import {
|
|
158
167
|
HeaderActionsProvider,
|
|
@@ -248,9 +257,9 @@ export interface DispatchNavContentProps extends DispatchSidebarBrandProps {
|
|
|
248
257
|
collapsible?: boolean;
|
|
249
258
|
onCollapsedChange?: (collapsed: boolean) => void;
|
|
250
259
|
chatFirstAppLayout?: ChatFirstAppLayoutPreference;
|
|
251
|
-
|
|
260
|
+
onChatFirstTogglePinned?: (appId: string) => void;
|
|
252
261
|
chatFirstApps?: readonly ChatFirstAppItem[];
|
|
253
|
-
|
|
262
|
+
chatFirstAppsStatus?: WorkspaceAppsStatus;
|
|
254
263
|
chatFirstAppsError?: string | null;
|
|
255
264
|
chatFirstActiveAppId?: string;
|
|
256
265
|
chatFirstActivePrimaryTab?: ChatFirstPrimaryTab;
|
|
@@ -267,11 +276,29 @@ export interface DispatchLayoutProps extends DispatchSidebarBrandProps {
|
|
|
267
276
|
}
|
|
268
277
|
|
|
269
278
|
const CHROMELESS_PATHS = ["/approval", "/browser-chat", "/browser-connect"];
|
|
279
|
+
// With nothing pinned, the sidebar suggests this many apps; All apps has the rest.
|
|
280
|
+
const CHAT_FIRST_RAIL_SUGGESTED_APPS = 3;
|
|
281
|
+
const NO_CHAT_FIRST_APPS: ChatFirstAppItem[] = [];
|
|
270
282
|
const SIDEBAR_COLLAPSE_KEY = "dispatch.sidebar.collapsed";
|
|
271
283
|
const CHAT_HISTORY_SOURCE_KEY = "dispatch.chat-history.source";
|
|
272
284
|
export const CHAT_FIRST_SURFACE_PANEL_TOGGLE_CLASS_NAME =
|
|
273
285
|
"absolute right-3 top-2 z-20";
|
|
274
286
|
|
|
287
|
+
function appIds(apps: readonly ChatFirstAppItem[]): string[] {
|
|
288
|
+
return apps.map((app) => app.id);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
function renderChatFirstRailIcon(app: ChatFirstAppItem) {
|
|
292
|
+
return (
|
|
293
|
+
<AppIcon
|
|
294
|
+
id={app.id}
|
|
295
|
+
name={app.name}
|
|
296
|
+
size="sm"
|
|
297
|
+
className="size-7 rounded-lg"
|
|
298
|
+
/>
|
|
299
|
+
);
|
|
300
|
+
}
|
|
301
|
+
|
|
275
302
|
interface DispatchChatFirstPane {
|
|
276
303
|
appId: string;
|
|
277
304
|
placement?: ChatFirstAppSurfacePlacement;
|
|
@@ -295,8 +322,11 @@ const DispatchExtensionsContext = createContext<
|
|
|
295
322
|
DispatchExtensionConfig | undefined
|
|
296
323
|
>(undefined);
|
|
297
324
|
interface DispatchWorkspaceAppLauncher {
|
|
325
|
+
/** Empty until `workspaceAppsStatus` is "ready". */
|
|
298
326
|
workspaceApps: readonly WorkspaceAppSummary[];
|
|
299
|
-
|
|
327
|
+
// Waits for the hosted list too, which is only queried on chat routes and in
|
|
328
|
+
// chat-first mode; elsewhere it stays "loading".
|
|
329
|
+
workspaceAppsStatus: WorkspaceAppsStatus;
|
|
300
330
|
error?: unknown;
|
|
301
331
|
errorQueryName?: "list-workspace-apps" | "list-connected-agents";
|
|
302
332
|
openApp: (app: ChatFirstAppItem) => void;
|
|
@@ -447,11 +477,8 @@ const DISPATCH_CHAT_FIRST_COPY_KEYS: Record<string, string> = {
|
|
|
447
477
|
noWorkspaceApps: "chatFirstNoWorkspaceApps",
|
|
448
478
|
createApp: "chatFirstCreateApp",
|
|
449
479
|
retry: "chatFirstRetry",
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
pinApp: "chatFirstPinApp",
|
|
453
|
-
removePinned: "chatFirstRemovePinned",
|
|
454
|
-
pinTop: "chatFirstPinTop",
|
|
480
|
+
unpinApp: "unpinApp",
|
|
481
|
+
pinApp: "pinApp",
|
|
455
482
|
openSideSurfaces: "chatFirstOpenSideSurfaces",
|
|
456
483
|
closeTab: "chatFirstCloseTab",
|
|
457
484
|
close: "chatFirstClose",
|
|
@@ -469,30 +496,11 @@ const DISPATCH_CHAT_FIRST_COPY_KEYS: Record<string, string> = {
|
|
|
469
496
|
browserClose: "chatFirstBrowserClose",
|
|
470
497
|
browserPage: "chatFirstBrowserPage",
|
|
471
498
|
browserInvalidUrl: "chatFirstBrowserInvalidUrl",
|
|
472
|
-
browserPreviewStarting: "chatFirstBrowserPreviewStarting",
|
|
473
|
-
browserPreviewError: "chatFirstBrowserPreviewError",
|
|
474
499
|
appUnavailable: "chatFirstAppUnavailable",
|
|
475
500
|
appLoading: "chatFirstLoadingApp",
|
|
476
|
-
agentActivityEyebrow: "chatFirstAgentActivityEyebrow",
|
|
477
|
-
agentActivityTitle: "chatFirstAgentActivityTitle",
|
|
478
|
-
agentActivityDescription: "chatFirstAgentActivityDescription",
|
|
479
|
-
refreshAgentActivity: "chatFirstRefreshAgentActivity",
|
|
480
|
-
noAgentSessions: "chatFirstNoAgentSessions",
|
|
481
|
-
startAgentSession: "chatFirstStartAgentSession",
|
|
482
501
|
watchSession: "chatFirstWatchSession",
|
|
483
502
|
copySessionId: "chatFirstCopySessionId",
|
|
484
|
-
copySessionIdFor: "chatFirstCopySessionIdFor",
|
|
485
503
|
sessionIdCopied: "chatFirstSessionIdCopied",
|
|
486
|
-
sessionIdShort: "chatFirstSessionIdShort",
|
|
487
|
-
copied: "chatFirstCopied",
|
|
488
|
-
agentActivityStatusQueued: "chatFirstAgentActivityStatusQueued",
|
|
489
|
-
agentActivityStatusRunning: "chatFirstAgentActivityStatusRunning",
|
|
490
|
-
agentActivityStatusPaused: "chatFirstAgentActivityStatusPaused",
|
|
491
|
-
agentActivityStatusNeedsApproval: "chatFirstAgentActivityStatusNeedsApproval",
|
|
492
|
-
agentActivityStatusCompleted: "chatFirstAgentActivityStatusCompleted",
|
|
493
|
-
agentActivityStatusErrored: "chatFirstAgentActivityStatusErrored",
|
|
494
|
-
agentActivityStatusRecent: "chatFirstAgentActivityStatusRecent",
|
|
495
|
-
agentActivityStatusUnknown: "chatFirstAgentActivityStatusUnknown",
|
|
496
504
|
watchingSession: "chatFirstWatchingSession",
|
|
497
505
|
session: "chatFirstSession",
|
|
498
506
|
stopWatchingSession: "chatFirstStopWatchingSession",
|
|
@@ -550,7 +558,12 @@ function createDispatchChatFirstCopy(
|
|
|
550
558
|
}
|
|
551
559
|
}
|
|
552
560
|
const translationKey = DISPATCH_CHAT_FIRST_COPY_KEYS[key];
|
|
553
|
-
if (!translationKey)
|
|
561
|
+
if (!translationKey) {
|
|
562
|
+
return t(`agentChat.chatFirst.${key}`, {
|
|
563
|
+
...(values ?? {}),
|
|
564
|
+
defaultValue: defaultChatFirstCopy(key, values),
|
|
565
|
+
});
|
|
566
|
+
}
|
|
554
567
|
return t(`dispatch.pages.${translationKey}`, {
|
|
555
568
|
...(values ?? {}),
|
|
556
569
|
defaultValue: defaultChatFirstCopy(key, values),
|
|
@@ -612,7 +625,7 @@ function readChatHistoryIncludesExternal(): boolean {
|
|
|
612
625
|
function DispatchChatsSection({
|
|
613
626
|
onNavigate,
|
|
614
627
|
showNewChat = false,
|
|
615
|
-
|
|
628
|
+
appsRail,
|
|
616
629
|
chatFirstMode = false,
|
|
617
630
|
chatFirstEmbedded = false,
|
|
618
631
|
collapsed = false,
|
|
@@ -620,7 +633,7 @@ function DispatchChatsSection({
|
|
|
620
633
|
}: {
|
|
621
634
|
onNavigate?: () => void;
|
|
622
635
|
showNewChat?: boolean;
|
|
623
|
-
|
|
636
|
+
appsRail?: ReactNode;
|
|
624
637
|
chatFirstMode?: boolean;
|
|
625
638
|
chatFirstEmbedded?: boolean;
|
|
626
639
|
collapsed?: boolean;
|
|
@@ -774,12 +787,33 @@ function DispatchChatsSection({
|
|
|
774
787
|
);
|
|
775
788
|
|
|
776
789
|
const collapsedChatFirst = collapsed && chatFirstMode;
|
|
790
|
+
const showChatFirstHistory =
|
|
791
|
+
!collapsedChatFirst &&
|
|
792
|
+
chatFirstMode &&
|
|
793
|
+
(history.loading ||
|
|
794
|
+
history.sections.some(
|
|
795
|
+
(section) => section.items.length > 0 || section.error != null,
|
|
796
|
+
));
|
|
797
|
+
// The source toggle filters every section, so it sits in the first header.
|
|
798
|
+
const chatFirstHistorySections = history.sections.map((section, index) =>
|
|
799
|
+
index === 0
|
|
800
|
+
? {
|
|
801
|
+
...section,
|
|
802
|
+
label: (
|
|
803
|
+
<div className="flex flex-1 items-center justify-between gap-2">
|
|
804
|
+
<span>{section.label}</span>
|
|
805
|
+
{sourceToggle}
|
|
806
|
+
</div>
|
|
807
|
+
),
|
|
808
|
+
}
|
|
809
|
+
: section,
|
|
810
|
+
);
|
|
777
811
|
|
|
778
812
|
return (
|
|
779
813
|
<div
|
|
780
814
|
className={cn(
|
|
781
|
-
"min-w-0
|
|
782
|
-
showNewChat
|
|
815
|
+
"min-w-0",
|
|
816
|
+
showNewChat ? "dispatch-chat-first-chats" : "space-y-0.5",
|
|
783
817
|
)}
|
|
784
818
|
>
|
|
785
819
|
{showNewChat && chatFirstNavigation ? (
|
|
@@ -798,18 +832,12 @@ function DispatchChatsSection({
|
|
|
798
832
|
/>
|
|
799
833
|
</nav>
|
|
800
834
|
) : null}
|
|
801
|
-
{
|
|
802
|
-
{!collapsedChatFirst ? (
|
|
835
|
+
{!collapsedChatFirst && !showChatFirstHistory ? (
|
|
803
836
|
<div className="flex justify-end px-2 pt-0.5">{sourceToggle}</div>
|
|
804
837
|
) : null}
|
|
805
|
-
{
|
|
806
|
-
chatFirstMode &&
|
|
807
|
-
(history.loading ||
|
|
808
|
-
history.sections.some(
|
|
809
|
-
(section) => section.items.length > 0 || section.error,
|
|
810
|
-
)) ? (
|
|
838
|
+
{showChatFirstHistory ? (
|
|
811
839
|
<ChatFirstChatHistory
|
|
812
|
-
sections={
|
|
840
|
+
sections={chatFirstHistorySections}
|
|
813
841
|
{...history.listProps}
|
|
814
842
|
onDelete={listProps.onDelete}
|
|
815
843
|
onNewChat={() => void handleNewChat()}
|
|
@@ -925,6 +953,7 @@ function DispatchChatsSection({
|
|
|
925
953
|
className="min-w-0 px-2"
|
|
926
954
|
/>
|
|
927
955
|
) : null}
|
|
956
|
+
{appsRail}
|
|
928
957
|
</div>
|
|
929
958
|
);
|
|
930
959
|
}
|
|
@@ -940,9 +969,9 @@ export function NavContent({
|
|
|
940
969
|
collapsible = false,
|
|
941
970
|
onCollapsedChange,
|
|
942
971
|
chatFirstAppLayout,
|
|
943
|
-
|
|
944
|
-
chatFirstApps =
|
|
945
|
-
|
|
972
|
+
onChatFirstTogglePinned,
|
|
973
|
+
chatFirstApps = NO_CHAT_FIRST_APPS,
|
|
974
|
+
chatFirstAppsStatus = "ready",
|
|
946
975
|
chatFirstAppsError,
|
|
947
976
|
chatFirstActiveAppId,
|
|
948
977
|
chatFirstActivePrimaryTab,
|
|
@@ -998,51 +1027,105 @@ export function NavContent({
|
|
|
998
1027
|
const feedbackButton = (
|
|
999
1028
|
<FeedbackButton variant={collapsed ? "icon" : "sidebar"} side="right" />
|
|
1000
1029
|
);
|
|
1001
|
-
const chatFirstCreateAppTrigger = (
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1030
|
+
const chatFirstCreateAppTrigger = useMemo(
|
|
1031
|
+
() => (
|
|
1032
|
+
<CreateAppPopover
|
|
1033
|
+
align="start"
|
|
1034
|
+
trigger={
|
|
1035
|
+
<button
|
|
1036
|
+
type="button"
|
|
1037
|
+
className="flex size-6 items-center justify-center rounded text-sidebar-foreground/55 hover:bg-sidebar-accent hover:text-sidebar-foreground"
|
|
1038
|
+
aria-label={chatFirstCopy("createWorkspaceApp")}
|
|
1039
|
+
title={chatFirstCopy("createWorkspaceApp")}
|
|
1040
|
+
>
|
|
1041
|
+
<IconPlus size={14} aria-hidden="true" />
|
|
1042
|
+
</button>
|
|
1043
|
+
}
|
|
1044
|
+
/>
|
|
1045
|
+
),
|
|
1046
|
+
[chatFirstCopy],
|
|
1015
1047
|
);
|
|
1016
1048
|
|
|
1049
|
+
const chatFirstAppTiers = useMemo(
|
|
1050
|
+
() =>
|
|
1051
|
+
tierWorkspaceApps(
|
|
1052
|
+
chatFirstApps,
|
|
1053
|
+
chatFirstAppLayout ?? { pinnedIds: [], orderedIds: [] },
|
|
1054
|
+
),
|
|
1055
|
+
[chatFirstAppLayout, chatFirstApps],
|
|
1056
|
+
);
|
|
1057
|
+
// One section: the user's pins, or until they pin something, a few apps
|
|
1058
|
+
// (workspace apps first, else hosted ones). The open app always shows.
|
|
1059
|
+
const chatFirstAppSections = useMemo<ChatFirstAppRailSection[]>(() => {
|
|
1060
|
+
const { pinned, apps, hosted } = chatFirstAppTiers;
|
|
1061
|
+
const ids = appIds(
|
|
1062
|
+
pinned.length > 0
|
|
1063
|
+
? pinned
|
|
1064
|
+
: (apps.length > 0 ? apps : hosted).slice(
|
|
1065
|
+
0,
|
|
1066
|
+
CHAT_FIRST_RAIL_SUGGESTED_APPS,
|
|
1067
|
+
),
|
|
1068
|
+
);
|
|
1069
|
+
if (
|
|
1070
|
+
chatFirstActiveAppId &&
|
|
1071
|
+
!ids.includes(chatFirstActiveAppId) &&
|
|
1072
|
+
chatFirstApps.some((app) => app.id === chatFirstActiveAppId)
|
|
1073
|
+
) {
|
|
1074
|
+
ids.push(chatFirstActiveAppId);
|
|
1075
|
+
}
|
|
1076
|
+
return [
|
|
1077
|
+
{
|
|
1078
|
+
id: pinned.length > 0 ? "pinned" : "apps",
|
|
1079
|
+
label:
|
|
1080
|
+
pinned.length > 0
|
|
1081
|
+
? t("dispatch.pages.pinnedApps")
|
|
1082
|
+
: t("dispatch.nav.apps"),
|
|
1083
|
+
appIds: ids,
|
|
1084
|
+
action: chatFirstCreateAppTrigger,
|
|
1085
|
+
...(chatFirstAppsStatus === "ready" && ids.length === 0
|
|
1086
|
+
? { emptyLabel: chatFirstCopy("noWorkspaceApps") }
|
|
1087
|
+
: {}),
|
|
1088
|
+
},
|
|
1089
|
+
];
|
|
1090
|
+
}, [
|
|
1091
|
+
chatFirstActiveAppId,
|
|
1092
|
+
chatFirstAppTiers,
|
|
1093
|
+
chatFirstApps,
|
|
1094
|
+
chatFirstAppsStatus,
|
|
1095
|
+
chatFirstCopy,
|
|
1096
|
+
chatFirstCreateAppTrigger,
|
|
1097
|
+
t,
|
|
1098
|
+
]);
|
|
1099
|
+
|
|
1100
|
+
const openChatFirstRailApp = useCallback(
|
|
1101
|
+
(app: ChatFirstAppItem) => {
|
|
1102
|
+
onChatFirstAppOpen?.(app);
|
|
1103
|
+
onNavigate?.();
|
|
1104
|
+
},
|
|
1105
|
+
[onChatFirstAppOpen, onNavigate],
|
|
1106
|
+
);
|
|
1107
|
+
const openChatFirstAllApps = useCallback(() => {
|
|
1108
|
+
void navigate(dispatchNavLinkTarget("/apps"));
|
|
1109
|
+
onNavigate?.();
|
|
1110
|
+
}, [navigate, onNavigate]);
|
|
1111
|
+
|
|
1017
1112
|
const chatFirstAppsRail = (
|
|
1018
1113
|
<ChatFirstAppsRail
|
|
1019
1114
|
apps={chatFirstApps}
|
|
1115
|
+
sections={chatFirstAppSections}
|
|
1020
1116
|
activeAppId={chatFirstActiveAppId}
|
|
1021
1117
|
activeTab={chatFirstActivePrimaryTab}
|
|
1022
1118
|
collapsed={collapsed}
|
|
1023
1119
|
grayscaleInactiveIcons={false}
|
|
1024
|
-
loading={
|
|
1120
|
+
loading={chatFirstAppsStatus === "loading"}
|
|
1025
1121
|
error={chatFirstAppsError}
|
|
1026
1122
|
layout={chatFirstAppLayout}
|
|
1027
|
-
|
|
1123
|
+
onTogglePinned={onChatFirstTogglePinned}
|
|
1028
1124
|
onRetry={onChatFirstAppsRetry}
|
|
1029
|
-
onOpenApp={
|
|
1030
|
-
|
|
1031
|
-
onNavigate?.();
|
|
1032
|
-
}}
|
|
1033
|
-
onOpenAllApps={() => {
|
|
1034
|
-
void navigate(dispatchNavLinkTarget("/apps"));
|
|
1035
|
-
onNavigate?.();
|
|
1036
|
-
}}
|
|
1125
|
+
onOpenApp={openChatFirstRailApp}
|
|
1126
|
+
onOpenAllApps={openChatFirstAllApps}
|
|
1037
1127
|
createAppTrigger={chatFirstCreateAppTrigger}
|
|
1038
|
-
renderIcon={
|
|
1039
|
-
<AppIcon
|
|
1040
|
-
id={app.id}
|
|
1041
|
-
name={app.name}
|
|
1042
|
-
size="sm"
|
|
1043
|
-
className="size-7 rounded-lg"
|
|
1044
|
-
/>
|
|
1045
|
-
)}
|
|
1128
|
+
renderIcon={renderChatFirstRailIcon}
|
|
1046
1129
|
copy={chatFirstCopy}
|
|
1047
1130
|
/>
|
|
1048
1131
|
);
|
|
@@ -1185,7 +1268,7 @@ export function NavContent({
|
|
|
1185
1268
|
onNavigate?.();
|
|
1186
1269
|
},
|
|
1187
1270
|
}}
|
|
1188
|
-
|
|
1271
|
+
appsRail={chatFirstAppsRail}
|
|
1189
1272
|
/>
|
|
1190
1273
|
</div>
|
|
1191
1274
|
) : null}
|
|
@@ -1357,11 +1440,10 @@ function DispatchLayout({
|
|
|
1357
1440
|
`${url.pathname}${url.search}${url.hash}`,
|
|
1358
1441
|
);
|
|
1359
1442
|
}, [electronEmbedded, location.pathname, location.search]);
|
|
1360
|
-
const
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
);
|
|
1364
|
-
const chatFirstAppLayoutHydratedRef = useRef(false);
|
|
1443
|
+
const { layout: chatFirstAppLayout, togglePinned: toggleChatFirstAppPinned } =
|
|
1444
|
+
useWorkspaceAppLayout({
|
|
1445
|
+
hydrate: chatFirstMode,
|
|
1446
|
+
});
|
|
1365
1447
|
const chatFirstAppsQuery = useActionQuery<WorkspaceAppSummary[]>(
|
|
1366
1448
|
"list-workspace-apps",
|
|
1367
1449
|
{ includeAgentCards: false, includeArchived: true },
|
|
@@ -1381,6 +1463,7 @@ function DispatchLayout({
|
|
|
1381
1463
|
filterBuiltInApps(
|
|
1382
1464
|
chatFirstConnectedAppsQuery.data ?? [],
|
|
1383
1465
|
chatFirstAppsQuery.data ?? [],
|
|
1466
|
+
{ includeHiddenFromLaunchers: true },
|
|
1384
1467
|
),
|
|
1385
1468
|
[chatFirstAppsQuery.data, chatFirstConnectedAppsQuery.data],
|
|
1386
1469
|
);
|
|
@@ -1390,8 +1473,6 @@ function DispatchLayout({
|
|
|
1390
1473
|
);
|
|
1391
1474
|
const chatFirstWorkspaceAppsLoading = chatFirstAppsQuery.isLoading;
|
|
1392
1475
|
const chatFirstConnectedAppsLoading = chatFirstConnectedAppsQuery.isLoading;
|
|
1393
|
-
const chatFirstAppsLoading =
|
|
1394
|
-
chatFirstWorkspaceAppsLoading || chatFirstConnectedAppsLoading;
|
|
1395
1476
|
const chatFirstAppsFailed = chatFirstAppsQuery.isError;
|
|
1396
1477
|
const chatFirstAppsError =
|
|
1397
1478
|
chatFirstAppsQuery.error ?? chatFirstConnectedAppsQuery.error;
|
|
@@ -1401,6 +1482,10 @@ function DispatchLayout({
|
|
|
1401
1482
|
: chatFirstConnectedAppsQuery.error != null
|
|
1402
1483
|
? "list-connected-agents"
|
|
1403
1484
|
: undefined;
|
|
1485
|
+
const chatFirstWorkspaceAppsStatus = appListsStatus(
|
|
1486
|
+
chatFirstAppsQuery,
|
|
1487
|
+
chatFirstConnectedAppsQuery,
|
|
1488
|
+
);
|
|
1404
1489
|
const chatFirstResolvableApps = useMemo(
|
|
1405
1490
|
() =>
|
|
1406
1491
|
mergeChatFirstWorkspaceApps(
|
|
@@ -1412,6 +1497,7 @@ function DispatchLayout({
|
|
|
1412
1497
|
description: app.description,
|
|
1413
1498
|
url: app.homeUrl?.trim() || app.url,
|
|
1414
1499
|
source: "builtin" as const,
|
|
1500
|
+
hiddenFromLaunchers: app.hiddenFromLaunchers,
|
|
1415
1501
|
})),
|
|
1416
1502
|
).filter((app) => app.status !== "pending" && app.archived !== true),
|
|
1417
1503
|
[
|
|
@@ -1422,8 +1508,12 @@ function DispatchLayout({
|
|
|
1422
1508
|
);
|
|
1423
1509
|
const chatFirstWorkspaceApps = useMemo(
|
|
1424
1510
|
() =>
|
|
1425
|
-
|
|
1426
|
-
|
|
1511
|
+
chatFirstWorkspaceAppsStatus === "ready"
|
|
1512
|
+
? chatFirstResolvableApps.filter(
|
|
1513
|
+
isWorkspaceAppVisibleInDefaultLaunchers,
|
|
1514
|
+
)
|
|
1515
|
+
: [],
|
|
1516
|
+
[chatFirstResolvableApps, chatFirstWorkspaceAppsStatus],
|
|
1427
1517
|
);
|
|
1428
1518
|
const chatFirstAppRegistrations = useMemo<ChatFirstAppRegistration[]>(() => {
|
|
1429
1519
|
return chatFirstResolvableApps.map((app) => ({
|
|
@@ -1486,7 +1576,7 @@ function DispatchLayout({
|
|
|
1486
1576
|
const chatHomeAppLauncher = useMemo<DispatchWorkspaceAppLauncher>(
|
|
1487
1577
|
() => ({
|
|
1488
1578
|
workspaceApps: chatFirstWorkspaceApps,
|
|
1489
|
-
|
|
1579
|
+
workspaceAppsStatus: chatFirstWorkspaceAppsStatus,
|
|
1490
1580
|
error: chatFirstAppsError,
|
|
1491
1581
|
errorQueryName: chatFirstAppsErrorQueryName,
|
|
1492
1582
|
openApp: openChatFirstApp,
|
|
@@ -1498,10 +1588,10 @@ function DispatchLayout({
|
|
|
1498
1588
|
[
|
|
1499
1589
|
chatFirstAppsError,
|
|
1500
1590
|
chatFirstAppsErrorQueryName,
|
|
1501
|
-
chatFirstAppsLoading,
|
|
1502
1591
|
chatFirstAppsQuery.refetch,
|
|
1503
1592
|
chatFirstConnectedAppsQuery.refetch,
|
|
1504
1593
|
chatFirstWorkspaceApps,
|
|
1594
|
+
chatFirstWorkspaceAppsStatus,
|
|
1505
1595
|
openChatFirstApp,
|
|
1506
1596
|
],
|
|
1507
1597
|
);
|
|
@@ -1787,40 +1877,6 @@ function DispatchLayout({
|
|
|
1787
1877
|
);
|
|
1788
1878
|
}, []);
|
|
1789
1879
|
|
|
1790
|
-
useEffect(() => {
|
|
1791
|
-
if (
|
|
1792
|
-
!chatFirstMode ||
|
|
1793
|
-
!isChatRoute ||
|
|
1794
|
-
chatFirstAppLayoutHydratedRef.current
|
|
1795
|
-
) {
|
|
1796
|
-
return;
|
|
1797
|
-
}
|
|
1798
|
-
chatFirstAppLayoutHydratedRef.current = true;
|
|
1799
|
-
void readClientAppState<unknown>("chat-first-app-layout")
|
|
1800
|
-
.then((value) => {
|
|
1801
|
-
if (!value || typeof value !== "object") return;
|
|
1802
|
-
setChatFirstAppLayout(normalizeWorkspaceAppLayout(value));
|
|
1803
|
-
})
|
|
1804
|
-
.catch(() => {
|
|
1805
|
-
// Device-local layout remains the fallback when workspace state is unavailable.
|
|
1806
|
-
});
|
|
1807
|
-
}, [chatFirstMode, isChatRoute]);
|
|
1808
|
-
|
|
1809
|
-
const persistChatFirstAppLayout = useCallback(
|
|
1810
|
-
(layout: ChatFirstAppLayoutPreference) => {
|
|
1811
|
-
const normalizedLayout = normalizeWorkspaceAppLayout(layout);
|
|
1812
|
-
setChatFirstAppLayout(normalizedLayout);
|
|
1813
|
-
void writeClientAppState("chat-first-app-layout", normalizedLayout).catch(
|
|
1814
|
-
() => {
|
|
1815
|
-
setChatFirstNotice(
|
|
1816
|
-
"App order changed locally, but workspace state could not be synced.",
|
|
1817
|
-
);
|
|
1818
|
-
},
|
|
1819
|
-
);
|
|
1820
|
-
},
|
|
1821
|
-
[],
|
|
1822
|
-
);
|
|
1823
|
-
|
|
1824
1880
|
useEffect(() => {
|
|
1825
1881
|
if (!chatFirstMode) {
|
|
1826
1882
|
setChatFirstPane(null);
|
|
@@ -2313,6 +2369,7 @@ function DispatchLayout({
|
|
|
2313
2369
|
<div className="relative flex min-w-0 flex-1 flex-col overflow-hidden">
|
|
2314
2370
|
<Header showAgentToggle={false} />
|
|
2315
2371
|
{notice}
|
|
2372
|
+
<WorkspaceAppLayoutNotice />
|
|
2316
2373
|
<InvitationBanner />
|
|
2317
2374
|
<main className="relative flex min-h-0 flex-1 overflow-hidden">
|
|
2318
2375
|
<div className="min-w-0 flex-1 overflow-y-auto">
|
|
@@ -2394,22 +2451,12 @@ function DispatchLayout({
|
|
|
2394
2451
|
const appContent = (
|
|
2395
2452
|
<div className="relative flex h-full min-h-0 min-w-0 flex-1 flex-col overflow-hidden">
|
|
2396
2453
|
{notice}
|
|
2454
|
+
<WorkspaceAppLayoutNotice />
|
|
2397
2455
|
<InvitationBanner />
|
|
2398
2456
|
{isChatRoute && chatFirstMode && chatFirstNotice ? (
|
|
2399
|
-
<
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
aria-live="polite"
|
|
2403
|
-
>
|
|
2404
|
-
<span>{chatFirstNotice}</span>
|
|
2405
|
-
<button
|
|
2406
|
-
type="button"
|
|
2407
|
-
onClick={() => setChatFirstNotice(null)}
|
|
2408
|
-
aria-label="Dismiss app notice"
|
|
2409
|
-
>
|
|
2410
|
-
Dismiss
|
|
2411
|
-
</button>
|
|
2412
|
-
</div>
|
|
2457
|
+
<AppNotice onDismiss={() => setChatFirstNotice(null)}>
|
|
2458
|
+
{chatFirstNotice}
|
|
2459
|
+
</AppNotice>
|
|
2413
2460
|
) : null}
|
|
2414
2461
|
<main
|
|
2415
2462
|
className={cn(
|
|
@@ -2551,9 +2598,9 @@ function DispatchLayout({
|
|
|
2551
2598
|
chatFirstEmbedded={chatFirstEmbedded}
|
|
2552
2599
|
collapsed={sidebarCollapsed}
|
|
2553
2600
|
chatFirstAppLayout={chatFirstAppLayout}
|
|
2554
|
-
|
|
2601
|
+
onChatFirstTogglePinned={toggleChatFirstAppPinned}
|
|
2555
2602
|
chatFirstApps={chatFirstAppItems}
|
|
2556
|
-
|
|
2603
|
+
chatFirstAppsStatus={chatFirstWorkspaceAppsStatus}
|
|
2557
2604
|
chatFirstAppsError={
|
|
2558
2605
|
chatFirstAppsError ? chatFirstCopy("appsLoadError") : null
|
|
2559
2606
|
}
|
|
@@ -2599,9 +2646,9 @@ function DispatchLayout({
|
|
|
2599
2646
|
chatFirstEmbedded={chatFirstEmbedded}
|
|
2600
2647
|
collapsed={false}
|
|
2601
2648
|
chatFirstAppLayout={chatFirstAppLayout}
|
|
2602
|
-
|
|
2649
|
+
onChatFirstTogglePinned={toggleChatFirstAppPinned}
|
|
2603
2650
|
chatFirstApps={chatFirstAppItems}
|
|
2604
|
-
|
|
2651
|
+
chatFirstAppsStatus={chatFirstWorkspaceAppsStatus}
|
|
2605
2652
|
chatFirstAppsError={
|
|
2606
2653
|
chatFirstAppsError ? chatFirstCopy("appsLoadError") : null
|
|
2607
2654
|
}
|
|
@@ -341,29 +341,27 @@ describe("WorkspaceAppCard", () => {
|
|
|
341
341
|
".app-open-actions__primary",
|
|
342
342
|
);
|
|
343
343
|
expect(openButton).not.toBeNull();
|
|
344
|
-
expect(openButton?.textContent).toContain("Open
|
|
344
|
+
expect(openButton?.textContent).toContain("Open");
|
|
345
345
|
expect(container.querySelector('a[href="/analytics"]')).toBeNull();
|
|
346
346
|
|
|
347
|
-
expect(
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
).not.toBeNull();
|
|
352
|
-
expect(document.body.textContent).not.toContain("Open in new tab");
|
|
347
|
+
expect(container.querySelector(".app-open-actions__menu")).not.toBeNull();
|
|
348
|
+
expect(document.body.textContent).not.toContain(
|
|
349
|
+
"dispatch.pages.chatFirstOpenInNewTab",
|
|
350
|
+
);
|
|
353
351
|
expect(document.body.textContent).not.toContain("Add app");
|
|
354
352
|
|
|
355
353
|
await act(async () => {
|
|
356
354
|
container
|
|
357
|
-
.querySelector<HTMLButtonElement>(
|
|
358
|
-
'button[aria-label="Open options for Analytics"]',
|
|
359
|
-
)
|
|
355
|
+
.querySelector<HTMLButtonElement>(".app-open-actions__menu")
|
|
360
356
|
?.dispatchEvent(
|
|
361
357
|
new MouseEvent("pointerdown", { bubbles: true, button: 0 }),
|
|
362
358
|
);
|
|
363
359
|
});
|
|
364
360
|
const newTabItem = Array.from(
|
|
365
361
|
document.querySelectorAll<HTMLElement>('[role="menuitem"]'),
|
|
366
|
-
).find((item) =>
|
|
362
|
+
).find((item) =>
|
|
363
|
+
item.textContent?.includes("dispatch.pages.chatFirstOpenInNewTab"),
|
|
364
|
+
);
|
|
367
365
|
expect(newTabItem).not.toBeUndefined();
|
|
368
366
|
expect(newTabItem?.getAttribute("href")).toBe(
|
|
369
367
|
"https://analytics.agent-native.com/home",
|
|
@@ -431,7 +429,7 @@ describe("WorkspaceAppCard", () => {
|
|
|
431
429
|
".app-open-actions__primary",
|
|
432
430
|
);
|
|
433
431
|
expect(openButton).not.toBeNull();
|
|
434
|
-
expect(openButton?.textContent).toContain("Open
|
|
432
|
+
expect(openButton?.textContent).toContain("Open");
|
|
435
433
|
expect(
|
|
436
434
|
container.querySelector(
|
|
437
435
|
'a[href="https://agent-workspace.builder.io/feedback-leaderboard/leaderboard"]',
|
|
@@ -686,7 +684,7 @@ describe("WorkspaceAppCard", () => {
|
|
|
686
684
|
expect(document.activeElement).toBe(settingsButton);
|
|
687
685
|
});
|
|
688
686
|
|
|
689
|
-
it("
|
|
687
|
+
it("pins from a pressed button beside Open, not from the open menu", async () => {
|
|
690
688
|
const onTogglePinned = vi.fn();
|
|
691
689
|
await act(async () => {
|
|
692
690
|
root.render(
|
|
@@ -707,24 +705,32 @@ describe("WorkspaceAppCard", () => {
|
|
|
707
705
|
);
|
|
708
706
|
});
|
|
709
707
|
|
|
710
|
-
const
|
|
711
|
-
'button[aria-label="
|
|
708
|
+
const pinButton = container.querySelector<HTMLButtonElement>(
|
|
709
|
+
'button[aria-label="dispatch.pages.unpinApp"]',
|
|
712
710
|
);
|
|
713
|
-
expect(
|
|
711
|
+
expect(pinButton?.getAttribute("aria-pressed")).toBe("true");
|
|
712
|
+
// The button stays visible while this is set, and on hover otherwise.
|
|
713
|
+
expect(pinButton?.getAttribute("data-pinned")).toBe("true");
|
|
714
|
+
const openActions = container.querySelector(".app-open-actions");
|
|
715
|
+
expect(
|
|
716
|
+
pinButton!.compareDocumentPosition(openActions!) &
|
|
717
|
+
Node.DOCUMENT_POSITION_FOLLOWING,
|
|
718
|
+
).toBeTruthy();
|
|
719
|
+
await act(async () => pinButton?.click());
|
|
720
|
+
expect(onTogglePinned).toHaveBeenCalledOnce();
|
|
714
721
|
|
|
715
722
|
await act(async () => {
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
723
|
+
container
|
|
724
|
+
.querySelector<HTMLButtonElement>(".app-open-actions__menu")
|
|
725
|
+
?.dispatchEvent(
|
|
726
|
+
new MouseEvent("pointerdown", { bubbles: true, button: 0 }),
|
|
727
|
+
);
|
|
719
728
|
});
|
|
720
|
-
|
|
721
|
-
const pinItem = Array.from(
|
|
729
|
+
const menuItems = Array.from(
|
|
722
730
|
document.querySelectorAll<HTMLElement>('[role="menuitem"]'),
|
|
723
|
-
).
|
|
724
|
-
expect(
|
|
725
|
-
|
|
726
|
-
await act(async () => pinItem?.click());
|
|
727
|
-
expect(onTogglePinned).toHaveBeenCalledOnce();
|
|
731
|
+
).map((item) => item.textContent);
|
|
732
|
+
expect(menuItems.length).toBeGreaterThan(0);
|
|
733
|
+
expect(menuItems.join(" ")).not.toMatch(/pin/i);
|
|
728
734
|
});
|
|
729
735
|
|
|
730
736
|
it("labels per-app context as agent resources while preserving workspace scope", async () => {
|
|
@@ -808,9 +814,7 @@ describe("WorkspaceAppCard", () => {
|
|
|
808
814
|
expect(container.textContent).toContain("Open in Builder");
|
|
809
815
|
expect(container.textContent).not.toContain("pending-branch-id");
|
|
810
816
|
expect(container.querySelector(".app-open-actions")).toBeNull();
|
|
811
|
-
expect(
|
|
812
|
-
container.querySelector('button[aria-label="Open options for New app"]'),
|
|
813
|
-
).toBeNull();
|
|
817
|
+
expect(container.querySelector(".app-open-actions__menu")).toBeNull();
|
|
814
818
|
});
|
|
815
819
|
|
|
816
820
|
it("shows ownership metadata in the settings menu", async () => {
|