@agent-native/toolkit 0.207.0-nightly-20261011153008 → 0.207.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/agent-native.eject.json +1 -0
- package/dist/app/auth/AuthPage.d.ts.map +1 -1
- package/dist/app/auth/AuthPage.js +24 -55
- package/dist/app/auth/AuthPage.js.map +1 -1
- package/dist/app/chat/chat/run-recovery.d.ts.map +1 -1
- package/dist/app/chat/chat/run-recovery.js +21 -5
- package/dist/app/chat/chat/run-recovery.js.map +1 -1
- package/dist/app/chat/chat-first/ChatFirstAgentActivityPanel.d.ts.map +1 -1
- package/dist/app/chat/chat-first/ChatFirstAgentActivityPanel.js +3 -2
- package/dist/app/chat/chat-first/ChatFirstAgentActivityPanel.js.map +1 -1
- package/dist/app/i18n/catalogs/ar-SA.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/ar-SA.js +10 -2
- package/dist/app/i18n/catalogs/ar-SA.js.map +1 -1
- package/dist/app/i18n/catalogs/de-DE.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/de-DE.js +10 -2
- package/dist/app/i18n/catalogs/de-DE.js.map +1 -1
- package/dist/app/i18n/catalogs/en-US.d.ts +10 -2
- package/dist/app/i18n/catalogs/en-US.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/en-US.js +10 -2
- package/dist/app/i18n/catalogs/en-US.js.map +1 -1
- package/dist/app/i18n/catalogs/es-ES.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/es-ES.js +10 -2
- package/dist/app/i18n/catalogs/es-ES.js.map +1 -1
- package/dist/app/i18n/catalogs/fr-FR.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/fr-FR.js +10 -2
- package/dist/app/i18n/catalogs/fr-FR.js.map +1 -1
- package/dist/app/i18n/catalogs/hi-IN.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/hi-IN.js +10 -2
- package/dist/app/i18n/catalogs/hi-IN.js.map +1 -1
- package/dist/app/i18n/catalogs/ja-JP.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/ja-JP.js +10 -2
- package/dist/app/i18n/catalogs/ja-JP.js.map +1 -1
- package/dist/app/i18n/catalogs/ko-KR.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/ko-KR.js +10 -2
- package/dist/app/i18n/catalogs/ko-KR.js.map +1 -1
- package/dist/app/i18n/catalogs/pt-BR.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/pt-BR.js +10 -2
- package/dist/app/i18n/catalogs/pt-BR.js.map +1 -1
- package/dist/app/i18n/catalogs/zh-CN.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/zh-CN.js +10 -2
- package/dist/app/i18n/catalogs/zh-CN.js.map +1 -1
- package/dist/app/i18n/catalogs/zh-TW.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/zh-TW.js +10 -2
- package/dist/app/i18n/catalogs/zh-TW.js.map +1 -1
- package/dist/app/onboarding/OnboardingBanner.d.ts.map +1 -1
- package/dist/app/onboarding/OnboardingBanner.js +37 -3
- package/dist/app/onboarding/OnboardingBanner.js.map +1 -1
- package/dist/app/onboarding/OnboardingPanel.d.ts.map +1 -1
- package/dist/app/onboarding/OnboardingPanel.js +49 -11
- package/dist/app/onboarding/OnboardingPanel.js.map +1 -1
- package/dist/app/progress/RunsTray.d.ts.map +1 -1
- package/dist/app/progress/RunsTray.js +136 -17
- package/dist/app/progress/RunsTray.js.map +1 -1
- package/dist/app/settings/BuilderConnectPopover.d.ts +1 -0
- package/dist/app/settings/BuilderConnectPopover.d.ts.map +1 -1
- package/dist/app/settings/BuilderConnectPopover.js +21 -5
- package/dist/app/settings/BuilderConnectPopover.js.map +1 -1
- package/dist/app/settings/shell/pages/audit.d.ts.map +1 -1
- package/dist/app/settings/shell/pages/audit.js +3 -3
- package/dist/app/settings/shell/pages/audit.js.map +1 -1
- package/dist/app/settings/useBuilderStatus.d.ts +1 -0
- package/dist/app/settings/useBuilderStatus.d.ts.map +1 -1
- package/dist/app/settings/useBuilderStatus.js +1 -0
- package/dist/app/settings/useBuilderStatus.js.map +1 -1
- package/dist/app/shared/AgentMark.d.ts +6 -0
- package/dist/app/shared/AgentMark.d.ts.map +1 -0
- package/dist/app/shared/AgentMark.js +5 -0
- package/dist/app/shared/AgentMark.js.map +1 -0
- package/dist/app/shared/index.d.ts +1 -0
- package/dist/app/shared/index.d.ts.map +1 -1
- package/dist/app/shared/index.js +1 -0
- package/dist/app/shared/index.js.map +1 -1
- package/dist/app/sharing/ShareButton.d.ts +1 -0
- package/dist/app/sharing/ShareButton.d.ts.map +1 -1
- package/dist/app/sharing/ShareButton.js +2 -2
- package/dist/app/sharing/ShareButton.js.map +1 -1
- package/dist/app/styles/agent-native.css +1 -0
- package/dist/app-shell/sidebar.d.ts.map +1 -1
- package/dist/app-shell/sidebar.js +3 -3
- package/dist/app-shell/sidebar.js.map +1 -1
- package/dist/sharing/ShareControls.d.ts +1 -0
- package/dist/sharing/ShareControls.d.ts.map +1 -1
- package/dist/sharing/ShareControls.js +5 -2
- package/dist/sharing/ShareControls.js.map +1 -1
- package/package.json +4 -4
- package/src/app/auth/AuthPage.attribution.spec.tsx +268 -0
- package/src/app/auth/AuthPage.tsx +27 -54
- package/src/app/chat/chat/run-recovery.spec.tsx +76 -0
- package/src/app/chat/chat/run-recovery.tsx +101 -23
- package/src/app/chat/chat-first/ChatFirstAgentActivityPanel.tsx +11 -5
- package/src/app/i18n/catalogs/ar-SA.ts +17 -2
- package/src/app/i18n/catalogs/de-DE.ts +18 -2
- package/src/app/i18n/catalogs/en-US.ts +17 -2
- package/src/app/i18n/catalogs/es-ES.ts +17 -2
- package/src/app/i18n/catalogs/fr-FR.ts +18 -2
- package/src/app/i18n/catalogs/hi-IN.ts +17 -2
- package/src/app/i18n/catalogs/ja-JP.ts +17 -2
- package/src/app/i18n/catalogs/ko-KR.ts +17 -2
- package/src/app/i18n/catalogs/pt-BR.ts +17 -2
- package/src/app/i18n/catalogs/zh-CN.ts +16 -2
- package/src/app/i18n/catalogs/zh-TW.ts +16 -2
- package/src/app/onboarding/OnboardingBanner.tsx +70 -5
- package/src/app/onboarding/OnboardingPanel.save-scope.spec.tsx +143 -2
- package/src/app/onboarding/OnboardingPanel.tsx +72 -5
- package/src/app/progress/RunsTray.spec.tsx +577 -0
- package/src/app/progress/RunsTray.tsx +253 -51
- package/src/app/settings/BuilderConnectPopover.spec.tsx +39 -1
- package/src/app/settings/BuilderConnectPopover.tsx +27 -4
- package/src/app/settings/shell/pages/audit.tsx +7 -3
- package/src/app/settings/useBuilderStatus.spec.tsx +21 -1
- package/src/app/settings/useBuilderStatus.ts +2 -0
- package/src/app/shared/AgentMark.tsx +34 -0
- package/src/app/shared/index.ts +1 -0
- package/src/app/sharing/ShareButton.tsx +3 -0
- package/src/app/styles/agent-native.css +1 -0
- package/src/app-shell/sidebar.tsx +29 -21
- package/src/sharing/ShareControls.spec.tsx +36 -0
- package/src/sharing/ShareControls.tsx +17 -2
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import {
|
|
2
|
+
requestAgentChatThreadOpen,
|
|
3
|
+
requestAgentTaskOpen,
|
|
4
|
+
} from "@agent-native/core/client/agent-chat";
|
|
1
5
|
import { agentNativePath } from "@agent-native/core/client/api-path";
|
|
2
6
|
import { useFormatters, useT } from "@agent-native/core/client/i18n";
|
|
3
7
|
import { useChangeVersion } from "@agent-native/core/client/use-change-version";
|
|
@@ -12,6 +16,7 @@ import {
|
|
|
12
16
|
Tooltip as DesignSystemTooltip,
|
|
13
17
|
} from "@agent-native/toolkit/design-system";
|
|
14
18
|
import {
|
|
19
|
+
DropdownMenuItem,
|
|
15
20
|
DropdownMenuSub,
|
|
16
21
|
DropdownMenuSubContent,
|
|
17
22
|
DropdownMenuSubTrigger,
|
|
@@ -27,7 +32,7 @@ import {
|
|
|
27
32
|
IconSubtask,
|
|
28
33
|
IconX,
|
|
29
34
|
} from "@tabler/icons-react";
|
|
30
|
-
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
35
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
31
36
|
|
|
32
37
|
type AgentRunDto = AgentRun;
|
|
33
38
|
type BackgroundAgentRunDto = {
|
|
@@ -288,6 +293,8 @@ export function RunsTray({
|
|
|
288
293
|
showRecent,
|
|
289
294
|
});
|
|
290
295
|
|
|
296
|
+
const openRunThread = onOpenThread ?? openAgentRunThread;
|
|
297
|
+
|
|
291
298
|
if (!hasRuns && hideWhenIdle) return null;
|
|
292
299
|
|
|
293
300
|
const trigger = (
|
|
@@ -372,7 +379,8 @@ export function RunsTray({
|
|
|
372
379
|
terminalCount={terminalCount}
|
|
373
380
|
onDismiss={dismissRun}
|
|
374
381
|
onStop={stopRun}
|
|
375
|
-
onOpenThread={
|
|
382
|
+
onOpenThread={openRunThread}
|
|
383
|
+
insideDropdownMenu={false}
|
|
376
384
|
/>
|
|
377
385
|
</DesignSystemPopover>
|
|
378
386
|
);
|
|
@@ -390,6 +398,9 @@ export function RunsTrayMenuItem({
|
|
|
390
398
|
>) {
|
|
391
399
|
const t = useT();
|
|
392
400
|
const [submenuOpen, setSubmenuOpen] = useState(false);
|
|
401
|
+
const triggerRef = useRef<HTMLDivElement>(null);
|
|
402
|
+
const keepOpenRef = useRef(false);
|
|
403
|
+
const keepOpenTimerRef = useRef<number | null>(null);
|
|
393
404
|
const {
|
|
394
405
|
runs,
|
|
395
406
|
hasRuns,
|
|
@@ -408,23 +419,41 @@ export function RunsTrayMenuItem({
|
|
|
408
419
|
showRecent,
|
|
409
420
|
});
|
|
410
421
|
|
|
422
|
+
const openRunThread = onOpenThread ?? openAgentRunThread;
|
|
423
|
+
const keepMenuOpen = () => {
|
|
424
|
+
keepOpenRef.current = true;
|
|
425
|
+
window.clearTimeout(keepOpenTimerRef.current ?? undefined);
|
|
426
|
+
keepOpenTimerRef.current = window.setTimeout(() => {
|
|
427
|
+
keepOpenRef.current = false;
|
|
428
|
+
}, 250);
|
|
429
|
+
};
|
|
430
|
+
// Radix restores focus to the parent menu while these actions update rows.
|
|
431
|
+
const onFocusOutside = (event: Event) => {
|
|
432
|
+
const target = event.target;
|
|
433
|
+
if (
|
|
434
|
+
keepOpenRef.current &&
|
|
435
|
+
target instanceof HTMLElement &&
|
|
436
|
+
target.matches('[role="menu"][data-radix-menu-content]')
|
|
437
|
+
) {
|
|
438
|
+
event.preventDefault();
|
|
439
|
+
keepOpenRef.current = false;
|
|
440
|
+
window.clearTimeout(keepOpenTimerRef.current ?? undefined);
|
|
441
|
+
}
|
|
442
|
+
};
|
|
443
|
+
|
|
444
|
+
useEffect(
|
|
445
|
+
() => () => window.clearTimeout(keepOpenTimerRef.current ?? undefined),
|
|
446
|
+
[],
|
|
447
|
+
);
|
|
448
|
+
|
|
411
449
|
if (!hasRuns && hideWhenIdle) return null;
|
|
412
450
|
|
|
413
451
|
return (
|
|
414
452
|
<DropdownMenuSub open={submenuOpen} onOpenChange={setSubmenuOpen}>
|
|
415
453
|
<DropdownMenuSubTrigger
|
|
454
|
+
ref={triggerRef}
|
|
416
455
|
aria-label={t("runsTray.ariaAgentRuns", { label: triggerLabel })}
|
|
417
456
|
className="cursor-pointer gap-2"
|
|
418
|
-
onClick={(event) => {
|
|
419
|
-
event.preventDefault();
|
|
420
|
-
setSubmenuOpen(true);
|
|
421
|
-
}}
|
|
422
|
-
onKeyDown={(event) => {
|
|
423
|
-
if (event.key === "Enter" || event.key === " ") {
|
|
424
|
-
event.preventDefault();
|
|
425
|
-
setSubmenuOpen(true);
|
|
426
|
-
}
|
|
427
|
-
}}
|
|
428
457
|
>
|
|
429
458
|
<TriggerIcon
|
|
430
459
|
size={14}
|
|
@@ -453,6 +482,7 @@ export function RunsTrayMenuItem({
|
|
|
453
482
|
<DropdownMenuSubContent
|
|
454
483
|
sideOffset={6}
|
|
455
484
|
className="an-runs-tray__menu w-80 max-w-[calc(100vw-24px)] p-0"
|
|
485
|
+
onFocusOutside={onFocusOutside}
|
|
456
486
|
>
|
|
457
487
|
<RunsTrayContent
|
|
458
488
|
runs={runs}
|
|
@@ -461,7 +491,10 @@ export function RunsTrayMenuItem({
|
|
|
461
491
|
terminalCount={terminalCount}
|
|
462
492
|
onDismiss={dismissRun}
|
|
463
493
|
onStop={stopRun}
|
|
464
|
-
onOpenThread={
|
|
494
|
+
onOpenThread={openRunThread}
|
|
495
|
+
onKeepMenuOpen={() => triggerRef.current?.focus()}
|
|
496
|
+
onActionStart={keepMenuOpen}
|
|
497
|
+
insideDropdownMenu
|
|
465
498
|
/>
|
|
466
499
|
</DropdownMenuSubContent>
|
|
467
500
|
</DropdownMenuSub>
|
|
@@ -476,6 +509,9 @@ function RunsTrayContent({
|
|
|
476
509
|
onDismiss,
|
|
477
510
|
onStop,
|
|
478
511
|
onOpenThread,
|
|
512
|
+
onKeepMenuOpen,
|
|
513
|
+
onActionStart,
|
|
514
|
+
insideDropdownMenu,
|
|
479
515
|
}: {
|
|
480
516
|
runs: AgentRunDto[];
|
|
481
517
|
hasRuns: boolean;
|
|
@@ -484,6 +520,9 @@ function RunsTrayContent({
|
|
|
484
520
|
onDismiss: (runId: string) => void;
|
|
485
521
|
onStop: (runId: string) => void;
|
|
486
522
|
onOpenThread?: (threadId: string, run: AgentRunDto) => void;
|
|
523
|
+
onKeepMenuOpen?: () => void;
|
|
524
|
+
onActionStart?: () => void;
|
|
525
|
+
insideDropdownMenu: boolean;
|
|
487
526
|
}) {
|
|
488
527
|
const t = useT();
|
|
489
528
|
return (
|
|
@@ -514,6 +553,9 @@ function RunsTrayContent({
|
|
|
514
553
|
onDismiss={onDismiss}
|
|
515
554
|
onStop={onStop}
|
|
516
555
|
onOpenThread={onOpenThread}
|
|
556
|
+
onKeepMenuOpen={onKeepMenuOpen}
|
|
557
|
+
onActionStart={onActionStart}
|
|
558
|
+
insideDropdownMenu={insideDropdownMenu}
|
|
517
559
|
/>
|
|
518
560
|
))}
|
|
519
561
|
</div>
|
|
@@ -619,6 +661,29 @@ function getRunThreadId(run: AgentRunDto): string | undefined {
|
|
|
619
661
|
return match?.[1] ? decodeURIComponent(match[1]) : undefined;
|
|
620
662
|
}
|
|
621
663
|
|
|
664
|
+
function openAgentRunThread(threadId: string, run: AgentRunDto): void {
|
|
665
|
+
const metadata = run.metadata ?? {};
|
|
666
|
+
const parentThreadId =
|
|
667
|
+
typeof metadata.parentThreadId === "string"
|
|
668
|
+
? metadata.parentThreadId.trim()
|
|
669
|
+
: "";
|
|
670
|
+
const isAgentTeam =
|
|
671
|
+
metadata.kind === "agent-team" || metadata.source === "agent-teams";
|
|
672
|
+
if (isAgentTeam && parentThreadId && parentThreadId !== threadId) {
|
|
673
|
+
requestAgentTaskOpen({
|
|
674
|
+
threadId,
|
|
675
|
+
parentThreadId,
|
|
676
|
+
description:
|
|
677
|
+
typeof metadata.description === "string"
|
|
678
|
+
? metadata.description
|
|
679
|
+
: run.title || "",
|
|
680
|
+
name: typeof metadata.name === "string" ? metadata.name : "",
|
|
681
|
+
});
|
|
682
|
+
return;
|
|
683
|
+
}
|
|
684
|
+
requestAgentChatThreadOpen({ threadId });
|
|
685
|
+
}
|
|
686
|
+
|
|
622
687
|
function formatRunTime(
|
|
623
688
|
run: AgentRunDto,
|
|
624
689
|
t: ReturnType<typeof useT>,
|
|
@@ -676,11 +741,17 @@ function RunRow({
|
|
|
676
741
|
onDismiss,
|
|
677
742
|
onStop,
|
|
678
743
|
onOpenThread,
|
|
744
|
+
onKeepMenuOpen,
|
|
745
|
+
onActionStart,
|
|
746
|
+
insideDropdownMenu,
|
|
679
747
|
}: {
|
|
680
748
|
run: AgentRunDto;
|
|
681
749
|
onDismiss: (runId: string) => void;
|
|
682
750
|
onStop: (runId: string) => void;
|
|
683
751
|
onOpenThread?: (threadId: string, run: AgentRunDto) => void;
|
|
752
|
+
onKeepMenuOpen?: () => void;
|
|
753
|
+
onActionStart?: () => void;
|
|
754
|
+
insideDropdownMenu: boolean;
|
|
684
755
|
}) {
|
|
685
756
|
const t = useT();
|
|
686
757
|
const formatters = useFormatters();
|
|
@@ -689,20 +760,166 @@ function RunRow({
|
|
|
689
760
|
const isRunning = run.status === "running";
|
|
690
761
|
const canStop = isRunning && (isAgentTeamRun(run) || isBackgroundRun(run));
|
|
691
762
|
const backgroundStatus = getBackgroundStatus(run);
|
|
763
|
+
const actionButtonRef = useRef<HTMLButtonElement>(null);
|
|
764
|
+
const titleButtonRef = useRef<HTMLButtonElement>(null);
|
|
765
|
+
const pointerStartedOnRunRef = useRef<string | null>(null);
|
|
766
|
+
const pointerStartedOnActionRef = useRef(false);
|
|
767
|
+
const pointerStartedOnAnyActionRef = useRef(false);
|
|
768
|
+
const pointerReleasedOnActionRef = useRef(false);
|
|
769
|
+
const pointerReleasedOnSuppressedTitleRef = useRef(false);
|
|
770
|
+
|
|
771
|
+
useEffect(() => {
|
|
772
|
+
if (!insideDropdownMenu) return;
|
|
773
|
+
const isActionTarget = (target: EventTarget | null) =>
|
|
774
|
+
target instanceof Node &&
|
|
775
|
+
actionButtonRef.current?.contains(target) === true;
|
|
776
|
+
const getRunTarget = (target: EventTarget | null) =>
|
|
777
|
+
target instanceof Element
|
|
778
|
+
? target.closest<HTMLElement>(
|
|
779
|
+
"[data-runs-tray-title], [data-runs-tray-action]",
|
|
780
|
+
)
|
|
781
|
+
: null;
|
|
782
|
+
const isTitleTarget = (target: EventTarget | null) =>
|
|
783
|
+
target instanceof Node &&
|
|
784
|
+
titleButtonRef.current?.contains(target) === true;
|
|
785
|
+
const recordPointerDown = (event: PointerEvent) => {
|
|
786
|
+
const runTarget = getRunTarget(event.target);
|
|
787
|
+
pointerStartedOnRunRef.current =
|
|
788
|
+
runTarget?.dataset.runsTrayTitle ??
|
|
789
|
+
runTarget?.dataset.runsTrayAction ??
|
|
790
|
+
null;
|
|
791
|
+
pointerStartedOnActionRef.current = isActionTarget(event.target);
|
|
792
|
+
pointerStartedOnAnyActionRef.current =
|
|
793
|
+
runTarget?.dataset.runsTrayAction !== undefined;
|
|
794
|
+
pointerReleasedOnActionRef.current = false;
|
|
795
|
+
pointerReleasedOnSuppressedTitleRef.current = false;
|
|
796
|
+
};
|
|
797
|
+
const recordPointerUp = (event: PointerEvent) => {
|
|
798
|
+
const pointerStartedOutsideThisRun =
|
|
799
|
+
pointerStartedOnRunRef.current !== run.id;
|
|
800
|
+
pointerReleasedOnActionRef.current = isActionTarget(event.target);
|
|
801
|
+
pointerReleasedOnSuppressedTitleRef.current =
|
|
802
|
+
isTitleTarget(event.target) &&
|
|
803
|
+
(pointerStartedOutsideThisRun || pointerStartedOnAnyActionRef.current);
|
|
804
|
+
window.setTimeout(() => {
|
|
805
|
+
pointerStartedOnRunRef.current = null;
|
|
806
|
+
pointerStartedOnActionRef.current = false;
|
|
807
|
+
pointerStartedOnAnyActionRef.current = false;
|
|
808
|
+
pointerReleasedOnActionRef.current = false;
|
|
809
|
+
pointerReleasedOnSuppressedTitleRef.current = false;
|
|
810
|
+
}, 0);
|
|
811
|
+
};
|
|
812
|
+
document.addEventListener("pointerdown", recordPointerDown, true);
|
|
813
|
+
document.addEventListener("pointerup", recordPointerUp, true);
|
|
814
|
+
return () => {
|
|
815
|
+
document.removeEventListener("pointerdown", recordPointerDown, true);
|
|
816
|
+
document.removeEventListener("pointerup", recordPointerUp, true);
|
|
817
|
+
};
|
|
818
|
+
}, [insideDropdownMenu]);
|
|
819
|
+
|
|
820
|
+
const titleButton =
|
|
821
|
+
threadId && onOpenThread ? (
|
|
822
|
+
<button
|
|
823
|
+
type="button"
|
|
824
|
+
className="group flex min-w-0 flex-1 flex-col text-left focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
|
825
|
+
ref={insideDropdownMenu ? titleButtonRef : undefined}
|
|
826
|
+
data-runs-tray-title={insideDropdownMenu ? run.id : undefined}
|
|
827
|
+
onClick={() => {
|
|
828
|
+
if (pointerReleasedOnSuppressedTitleRef.current) return;
|
|
829
|
+
onOpenThread(threadId, run);
|
|
830
|
+
}}
|
|
831
|
+
>
|
|
832
|
+
<span className="inline-flex min-w-0 max-w-full items-center gap-1.5">
|
|
833
|
+
<span className="truncate font-medium text-foreground group-hover:underline">
|
|
834
|
+
{run.title}
|
|
835
|
+
</span>
|
|
836
|
+
<IconExternalLink size={12} className="shrink-0" aria-hidden />
|
|
837
|
+
<span className="sr-only">{t("runsTray.open")}</span>
|
|
838
|
+
</span>
|
|
839
|
+
{run.step ? (
|
|
840
|
+
<span className="mt-0.5 max-w-full truncate text-xs text-muted-foreground">
|
|
841
|
+
{run.step}
|
|
842
|
+
</span>
|
|
843
|
+
) : null}
|
|
844
|
+
</button>
|
|
845
|
+
) : null;
|
|
846
|
+
const actionButton =
|
|
847
|
+
canStop || !isRunning ? (
|
|
848
|
+
<button
|
|
849
|
+
type="button"
|
|
850
|
+
ref={insideDropdownMenu ? actionButtonRef : undefined}
|
|
851
|
+
data-runs-tray-action={insideDropdownMenu ? run.id : undefined}
|
|
852
|
+
aria-label={
|
|
853
|
+
canStop
|
|
854
|
+
? t("runsTray.stopRun", { title: run.title })
|
|
855
|
+
: t("runsTray.hideRun", { title: run.title })
|
|
856
|
+
}
|
|
857
|
+
className={cn(
|
|
858
|
+
"inline-flex h-6 w-6 cursor-pointer items-center justify-center rounded text-muted-foreground hover:bg-accent/60",
|
|
859
|
+
canStop ? "hover:text-destructive" : "hover:text-foreground",
|
|
860
|
+
)}
|
|
861
|
+
onPointerMove={onActionStart}
|
|
862
|
+
onPointerDown={onActionStart}
|
|
863
|
+
onClick={(event) => {
|
|
864
|
+
const isPointerActivation =
|
|
865
|
+
event.detail > 0 || pointerReleasedOnActionRef.current;
|
|
866
|
+
if (
|
|
867
|
+
insideDropdownMenu &&
|
|
868
|
+
isPointerActivation &&
|
|
869
|
+
!pointerStartedOnActionRef.current
|
|
870
|
+
) {
|
|
871
|
+
return;
|
|
872
|
+
}
|
|
873
|
+
pointerStartedOnActionRef.current = false;
|
|
874
|
+
pointerReleasedOnActionRef.current = false;
|
|
875
|
+
onActionStart?.();
|
|
876
|
+
if (canStop) {
|
|
877
|
+
onStop(run.id);
|
|
878
|
+
} else {
|
|
879
|
+
onKeepMenuOpen?.();
|
|
880
|
+
onDismiss(run.id);
|
|
881
|
+
}
|
|
882
|
+
}}
|
|
883
|
+
>
|
|
884
|
+
{canStop ? (
|
|
885
|
+
<IconPlayerStop size={13} aria-hidden />
|
|
886
|
+
) : (
|
|
887
|
+
<IconX size={13} aria-hidden />
|
|
888
|
+
)}
|
|
889
|
+
</button>
|
|
890
|
+
) : null;
|
|
692
891
|
|
|
693
892
|
return (
|
|
694
893
|
<div className="flex flex-col gap-1.5 px-3 py-2.5 text-sm">
|
|
695
894
|
<div className="flex items-start justify-between gap-2">
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
895
|
+
{titleButton ? (
|
|
896
|
+
insideDropdownMenu ? (
|
|
897
|
+
<DropdownMenuItem
|
|
898
|
+
asChild
|
|
899
|
+
className="min-w-0 flex-1 justify-start p-0"
|
|
900
|
+
onSelect={(event) => {
|
|
901
|
+
if (pointerReleasedOnSuppressedTitleRef.current) {
|
|
902
|
+
event.preventDefault();
|
|
903
|
+
}
|
|
904
|
+
}}
|
|
905
|
+
>
|
|
906
|
+
{titleButton}
|
|
907
|
+
</DropdownMenuItem>
|
|
908
|
+
) : (
|
|
909
|
+
titleButton
|
|
910
|
+
)
|
|
911
|
+
) : (
|
|
912
|
+
<div className="min-w-0 flex-1">
|
|
913
|
+
<div className="truncate font-medium text-foreground">
|
|
914
|
+
{run.title}
|
|
703
915
|
</div>
|
|
704
|
-
|
|
705
|
-
|
|
916
|
+
{run.step ? (
|
|
917
|
+
<div className="mt-0.5 truncate text-xs text-muted-foreground">
|
|
918
|
+
{run.step}
|
|
919
|
+
</div>
|
|
920
|
+
) : null}
|
|
921
|
+
</div>
|
|
922
|
+
)}
|
|
706
923
|
<StatusPill status={run.status} backgroundStatus={backgroundStatus} />
|
|
707
924
|
</div>
|
|
708
925
|
{run.percent != null || isRunning ? (
|
|
@@ -732,36 +949,21 @@ function RunRow({
|
|
|
732
949
|
{formatRunTime(run, t, formatDate)}
|
|
733
950
|
</span>
|
|
734
951
|
<div className="flex shrink-0 items-center gap-1">
|
|
735
|
-
{
|
|
736
|
-
<
|
|
737
|
-
|
|
738
|
-
className="
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
) : null}
|
|
745
|
-
{canStop ? (
|
|
746
|
-
<button
|
|
747
|
-
type="button"
|
|
748
|
-
aria-label={t("runsTray.stopRun", { title: run.title })}
|
|
749
|
-
className="inline-flex h-6 w-6 cursor-pointer items-center justify-center rounded text-muted-foreground hover:bg-accent/60 hover:text-destructive"
|
|
750
|
-
onClick={() => onStop(run.id)}
|
|
751
|
-
>
|
|
752
|
-
<IconPlayerStop size={13} aria-hidden />
|
|
753
|
-
</button>
|
|
754
|
-
) : null}
|
|
755
|
-
{!isRunning ? (
|
|
756
|
-
<button
|
|
757
|
-
type="button"
|
|
758
|
-
aria-label={t("runsTray.hideRun", { title: run.title })}
|
|
759
|
-
className="inline-flex h-6 w-6 cursor-pointer items-center justify-center rounded text-muted-foreground hover:bg-accent/60 hover:text-foreground"
|
|
760
|
-
onClick={() => onDismiss(run.id)}
|
|
952
|
+
{actionButton && insideDropdownMenu ? (
|
|
953
|
+
<DropdownMenuItem
|
|
954
|
+
asChild
|
|
955
|
+
className="h-6 w-6 justify-center p-0"
|
|
956
|
+
onSelect={(event) => {
|
|
957
|
+
event.preventDefault();
|
|
958
|
+
onActionStart?.();
|
|
959
|
+
onKeepMenuOpen?.();
|
|
960
|
+
}}
|
|
761
961
|
>
|
|
762
|
-
|
|
763
|
-
</
|
|
764
|
-
) :
|
|
962
|
+
{actionButton}
|
|
963
|
+
</DropdownMenuItem>
|
|
964
|
+
) : (
|
|
965
|
+
actionButton
|
|
966
|
+
)}
|
|
765
967
|
</div>
|
|
766
968
|
</div>
|
|
767
969
|
</div>
|
|
@@ -143,6 +143,9 @@ describe("BuilderConnectPopover", () => {
|
|
|
143
143
|
consent?.querySelector<HTMLButtonElement>("[data-testid='create']")
|
|
144
144
|
?.disabled,
|
|
145
145
|
).toBe(true);
|
|
146
|
+
expect(consent?.textContent).toContain(
|
|
147
|
+
"New account creation isn't enabled for this app.",
|
|
148
|
+
);
|
|
146
149
|
|
|
147
150
|
click(consent?.querySelector("[data-testid='create']") as HTMLElement);
|
|
148
151
|
expect(onConnect).not.toHaveBeenCalled();
|
|
@@ -187,6 +190,41 @@ describe("BuilderConnectPopover", () => {
|
|
|
187
190
|
expect(flow.start).not.toHaveBeenCalled();
|
|
188
191
|
});
|
|
189
192
|
|
|
193
|
+
it("shows a resolved state when status checks are disabled", async () => {
|
|
194
|
+
const flow = {
|
|
195
|
+
connecting: false,
|
|
196
|
+
statusCheckEnabled: false,
|
|
197
|
+
statusResolved: false,
|
|
198
|
+
agentNativeProvisioningEnabled: false,
|
|
199
|
+
start: vi.fn(),
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
render(
|
|
203
|
+
React.createElement(
|
|
204
|
+
DeferredBuilderConnectPopover,
|
|
205
|
+
{
|
|
206
|
+
flow,
|
|
207
|
+
contentTestId: "consent",
|
|
208
|
+
primaryTestId: "create",
|
|
209
|
+
secondaryTestId: "sign-in",
|
|
210
|
+
},
|
|
211
|
+
trigger(),
|
|
212
|
+
),
|
|
213
|
+
);
|
|
214
|
+
click(connectButton());
|
|
215
|
+
await finishLazyLoad();
|
|
216
|
+
|
|
217
|
+
const consent = document.querySelector("[data-testid='consent']");
|
|
218
|
+
expect(consent?.textContent).toContain(
|
|
219
|
+
"New account activation isn't available in this mode.",
|
|
220
|
+
);
|
|
221
|
+
expect(consent?.textContent).not.toContain("Checking whether");
|
|
222
|
+
expect(
|
|
223
|
+
consent?.querySelector<HTMLButtonElement>("[data-testid='create']")
|
|
224
|
+
?.disabled,
|
|
225
|
+
).toBe(true);
|
|
226
|
+
});
|
|
227
|
+
|
|
190
228
|
it("offers a retry after a Builder status read fails", () => {
|
|
191
229
|
const retry = vi.fn(() => true);
|
|
192
230
|
const flow = {
|
|
@@ -371,7 +409,7 @@ describe("BuilderConnectPopover", () => {
|
|
|
371
409
|
|
|
372
410
|
const consent = document.querySelector("[data-testid='consent']");
|
|
373
411
|
expect(consent?.textContent).toContain(
|
|
374
|
-
"Create
|
|
412
|
+
"Create a new account to activate free credits, or continue with an existing account.",
|
|
375
413
|
);
|
|
376
414
|
expect(consent?.textContent).toContain(
|
|
377
415
|
"You already have a Builder.io account",
|
|
@@ -32,6 +32,7 @@ type BuilderConnectChoiceFlow = Pick<BuilderConnectFlow, "connecting"> & {
|
|
|
32
32
|
error?: string | null;
|
|
33
33
|
errorKind?: BuilderConnectErrorKind | null;
|
|
34
34
|
retry?: () => boolean | void;
|
|
35
|
+
statusCheckEnabled?: boolean;
|
|
35
36
|
statusReadSettledCount?: number;
|
|
36
37
|
statusResolved?: boolean;
|
|
37
38
|
};
|
|
@@ -92,6 +93,7 @@ export function BuilderConnectChoicePanel({
|
|
|
92
93
|
})}
|
|
93
94
|
</h2>
|
|
94
95
|
<p
|
|
96
|
+
id="builder-connect-popover-description"
|
|
95
97
|
className="text-xs leading-5 text-muted-foreground"
|
|
96
98
|
role={flow.connecting ? "status" : undefined}
|
|
97
99
|
aria-live={flow.connecting ? "polite" : undefined}
|
|
@@ -101,10 +103,30 @@ export function BuilderConnectChoicePanel({
|
|
|
101
103
|
defaultValue:
|
|
102
104
|
"Creating your Builder.io account and activating free credits.",
|
|
103
105
|
})
|
|
104
|
-
:
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
106
|
+
: canProvisionAccount
|
|
107
|
+
? t("agentChat.onboarding.builderActivationDescription", {
|
|
108
|
+
defaultValue:
|
|
109
|
+
"Create a new account to activate free credits, or continue with an existing account.",
|
|
110
|
+
})
|
|
111
|
+
: statusReadFailed
|
|
112
|
+
? t("agentChat.onboarding.builderActivationCheckFailed", {
|
|
113
|
+
defaultValue:
|
|
114
|
+
"Couldn't check whether this app can create new accounts.",
|
|
115
|
+
})
|
|
116
|
+
: flow.statusCheckEnabled === false
|
|
117
|
+
? t("agentChat.onboarding.builderActivationUnavailableInMode", {
|
|
118
|
+
defaultValue:
|
|
119
|
+
"New account activation isn't available in this mode. You can still connect an existing account.",
|
|
120
|
+
})
|
|
121
|
+
: flow.statusResolved === false
|
|
122
|
+
? t("agentChat.onboarding.builderActivationChecking", {
|
|
123
|
+
defaultValue:
|
|
124
|
+
"Checking whether new account activation is available.",
|
|
125
|
+
})
|
|
126
|
+
: t("agentChat.onboarding.builderActivationUnavailable", {
|
|
127
|
+
defaultValue:
|
|
128
|
+
"New account creation isn't enabled for this app. You can still connect an existing account.",
|
|
129
|
+
})}
|
|
108
130
|
</p>
|
|
109
131
|
{flow.accountExists ? (
|
|
110
132
|
<div
|
|
@@ -160,6 +182,7 @@ export function BuilderConnectChoicePanel({
|
|
|
160
182
|
type="button"
|
|
161
183
|
data-testid={primaryTestId}
|
|
162
184
|
className="w-full"
|
|
185
|
+
aria-describedby="builder-connect-popover-description"
|
|
163
186
|
onClick={onCreateAndActivate}
|
|
164
187
|
disabled={flow.connecting || !canProvisionAccount}
|
|
165
188
|
>
|
|
@@ -31,10 +31,11 @@ import {
|
|
|
31
31
|
SelectValue,
|
|
32
32
|
} from "@agent-native/toolkit/ui/select";
|
|
33
33
|
import { cn } from "@agent-native/toolkit/utils";
|
|
34
|
-
import { IconHistory, IconSettings
|
|
34
|
+
import { IconHistory, IconSettings } from "@tabler/icons-react";
|
|
35
35
|
import { useInfiniteQuery } from "@tanstack/react-query";
|
|
36
36
|
import { useEffect, useState, type ReactNode } from "react";
|
|
37
37
|
|
|
38
|
+
import { AgentMark } from "../../../shared/AgentMark.js";
|
|
38
39
|
import { SettingsGroup } from "../../SettingsRow.js";
|
|
39
40
|
|
|
40
41
|
export const AUDIT_PAGE_SIZE = 25;
|
|
@@ -99,10 +100,13 @@ function initials(email: string): string {
|
|
|
99
100
|
|
|
100
101
|
function ActorMark({ event }: { event: AuditEvent }) {
|
|
101
102
|
if (event.actorKind === "agent" || event.actorKind === "system") {
|
|
102
|
-
const Icon = event.actorKind === "agent" ? IconSparkles : IconSettings;
|
|
103
103
|
return (
|
|
104
104
|
<span className="flex size-8 shrink-0 items-center justify-center rounded-full border border-border bg-background text-muted-foreground">
|
|
105
|
-
|
|
105
|
+
{event.actorKind === "agent" ? (
|
|
106
|
+
<AgentMark className="size-4" />
|
|
107
|
+
) : (
|
|
108
|
+
<IconSettings className="size-4" aria-hidden="true" />
|
|
109
|
+
)}
|
|
106
110
|
</span>
|
|
107
111
|
);
|
|
108
112
|
}
|
|
@@ -106,6 +106,9 @@ function BuilderConnectProbeContent({
|
|
|
106
106
|
{flow.statusResolved ? "resolved" : "unresolved"}{" "}
|
|
107
107
|
{flow.accountExists ? "account-exists" : "no-account-exists"}
|
|
108
108
|
</output>
|
|
109
|
+
<output data-testid="status-check-enabled">
|
|
110
|
+
{flow.statusCheckEnabled ? "enabled" : "disabled"}
|
|
111
|
+
</output>
|
|
109
112
|
<output data-testid="credential-source">
|
|
110
113
|
{flow.credentialSource ?? "none"}
|
|
111
114
|
</output>
|
|
@@ -324,6 +327,21 @@ describe("useBuilderStatus", () => {
|
|
|
324
327
|
);
|
|
325
328
|
});
|
|
326
329
|
|
|
330
|
+
it("distinguishes a disabled status check from one still loading", async () => {
|
|
331
|
+
const fetchMock = vi.fn();
|
|
332
|
+
vi.stubGlobal("fetch", fetchMock);
|
|
333
|
+
|
|
334
|
+
await act(async () => {
|
|
335
|
+
root.render(<BuilderConnectProbe enabled={false} />);
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
expect(
|
|
339
|
+
container.querySelector('[data-testid="status-check-enabled"]')
|
|
340
|
+
?.textContent,
|
|
341
|
+
).toBe("disabled");
|
|
342
|
+
expect(fetchMock).not.toHaveBeenCalled();
|
|
343
|
+
});
|
|
344
|
+
|
|
327
345
|
it("keeps the last good Builder status when a refresh fails", async () => {
|
|
328
346
|
vi.stubGlobal(
|
|
329
347
|
"fetch",
|
|
@@ -2580,7 +2598,9 @@ describe("useBuilderConnectFlow", () => {
|
|
|
2580
2598
|
document.querySelectorAll<HTMLButtonElement>(
|
|
2581
2599
|
"[data-radix-popper-content-wrapper] button",
|
|
2582
2600
|
),
|
|
2583
|
-
).find((button) =>
|
|
2601
|
+
).find((button) =>
|
|
2602
|
+
button.textContent?.includes("I have a Builder.io account"),
|
|
2603
|
+
);
|
|
2584
2604
|
expect(existingAccountAction).toBeDefined();
|
|
2585
2605
|
|
|
2586
2606
|
await act(async () => existingAccountAction?.click());
|
|
@@ -249,6 +249,7 @@ export type BuilderConnectErrorKind = "status-read" | "connection" | "launch";
|
|
|
249
249
|
export interface BuilderConnectFlow {
|
|
250
250
|
configured: boolean;
|
|
251
251
|
provisionAccount?: boolean;
|
|
252
|
+
statusCheckEnabled: boolean;
|
|
252
253
|
statusResolved: boolean;
|
|
253
254
|
statusReadSettledCount: number;
|
|
254
255
|
envManaged: boolean;
|
|
@@ -2092,6 +2093,7 @@ export function useBuilderConnectFlow(
|
|
|
2092
2093
|
configured,
|
|
2093
2094
|
provisionAccount,
|
|
2094
2095
|
codeChangeConfigured,
|
|
2096
|
+
statusCheckEnabled: enabled,
|
|
2095
2097
|
statusResolved,
|
|
2096
2098
|
statusReadSettledCount,
|
|
2097
2099
|
envManaged,
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { SVGProps } from "react";
|
|
2
|
+
|
|
3
|
+
export interface AgentMarkProps extends SVGProps<SVGSVGElement> {
|
|
4
|
+
size?: number | string;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function AgentMark({ size = 24, className, ...props }: AgentMarkProps) {
|
|
8
|
+
return (
|
|
9
|
+
<svg
|
|
10
|
+
data-agent-mark
|
|
11
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
12
|
+
width={size}
|
|
13
|
+
height={size}
|
|
14
|
+
viewBox="0 0 24 24"
|
|
15
|
+
fill="none"
|
|
16
|
+
className={className}
|
|
17
|
+
aria-hidden="true"
|
|
18
|
+
focusable="false"
|
|
19
|
+
{...props}
|
|
20
|
+
>
|
|
21
|
+
<path
|
|
22
|
+
d="M18.4 5.2a9.15 9.15 0 1 0 1.8 2.4"
|
|
23
|
+
stroke="currentColor"
|
|
24
|
+
strokeWidth="1.7"
|
|
25
|
+
strokeLinecap="round"
|
|
26
|
+
/>
|
|
27
|
+
<circle cx="18.55" cy="4.65" r="1.45" fill="currentColor" />
|
|
28
|
+
<path
|
|
29
|
+
d="m12 6.8 1.7 3.5 3.5 1.7-3.5 1.7-1.7 3.5-1.7-3.5-3.5-1.7 3.5-1.7L12 6.8Z"
|
|
30
|
+
fill="currentColor"
|
|
31
|
+
/>
|
|
32
|
+
</svg>
|
|
33
|
+
);
|
|
34
|
+
}
|
package/src/app/shared/index.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export * from "./AboutAgentNativeDialog.js";
|
|
2
2
|
export * from "./AppShellSkeleton.js";
|
|
3
3
|
export * from "./AgentNativeIcon.js";
|
|
4
|
+
export * from "./AgentMark.js";
|
|
4
5
|
export * from "./AppSidebar.js";
|
|
5
6
|
export * from "./AppearancePicker.js";
|
|
6
7
|
export * from "./ClientOnly.js";
|