@agent-native/toolkit 0.208.0 → 0.209.0-nightly-20261014070126

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 (46) hide show
  1. package/dist/app/chat/MultiTabAssistantChat.d.ts.map +1 -1
  2. package/dist/app/chat/MultiTabAssistantChat.js +12 -1
  3. package/dist/app/chat/MultiTabAssistantChat.js.map +1 -1
  4. package/dist/app/chat/agentkit-chat/guided-questions.d.ts +12 -0
  5. package/dist/app/chat/agentkit-chat/guided-questions.d.ts.map +1 -1
  6. package/dist/app/chat/agentkit-chat/guided-questions.js +32 -4
  7. package/dist/app/chat/agentkit-chat/guided-questions.js.map +1 -1
  8. package/dist/app/chat/agentkit-chat/index.d.ts +1 -1
  9. package/dist/app/chat/agentkit-chat/index.d.ts.map +1 -1
  10. package/dist/app/chat/agentkit-chat/index.js +1 -1
  11. package/dist/app/chat/agentkit-chat/index.js.map +1 -1
  12. package/dist/app/chat/chat/run-recovery.d.ts.map +1 -1
  13. package/dist/app/chat/chat/run-recovery.js +15 -8
  14. package/dist/app/chat/chat/run-recovery.js.map +1 -1
  15. package/dist/app/sharing/ShareButton.d.ts.map +1 -1
  16. package/dist/app/sharing/ShareButton.js +66 -73
  17. package/dist/app/sharing/ShareButton.js.map +1 -1
  18. package/dist/app/sharing/ShareDialog.d.ts.map +1 -1
  19. package/dist/app/sharing/ShareDialog.js +4 -12
  20. package/dist/app/sharing/ShareDialog.js.map +1 -1
  21. package/dist/sharing/ShareControls.d.ts +19 -1
  22. package/dist/sharing/ShareControls.d.ts.map +1 -1
  23. package/dist/sharing/ShareControls.js +14 -7
  24. package/dist/sharing/ShareControls.js.map +1 -1
  25. package/dist/sharing/SharePrimitives.js +1 -1
  26. package/dist/sharing/SharePrimitives.js.map +1 -1
  27. package/dist/sharing/index.d.ts +1 -1
  28. package/dist/sharing/index.d.ts.map +1 -1
  29. package/dist/sharing/index.js +1 -1
  30. package/dist/sharing/index.js.map +1 -1
  31. package/package.json +4 -4
  32. package/src/app/chat/MultiTabAssistantChat.spec.tsx +101 -2
  33. package/src/app/chat/MultiTabAssistantChat.tsx +14 -1
  34. package/src/app/chat/agentkit-chat/guided-questions.flow.spec.tsx +168 -0
  35. package/src/app/chat/agentkit-chat/guided-questions.tsx +54 -9
  36. package/src/app/chat/agentkit-chat/index.ts +2 -0
  37. package/src/app/chat/chat/run-recovery.spec.tsx +64 -0
  38. package/src/app/chat/chat/run-recovery.tsx +14 -8
  39. package/src/app/sharing/ShareButton.spec.tsx +125 -32
  40. package/src/app/sharing/ShareButton.tsx +119 -149
  41. package/src/app/sharing/ShareDialog.spec.tsx +31 -0
  42. package/src/app/sharing/ShareDialog.tsx +46 -71
  43. package/src/sharing/ShareControls.spec.tsx +86 -1
  44. package/src/sharing/ShareControls.tsx +83 -27
  45. package/src/sharing/SharePrimitives.tsx +1 -1
  46. package/src/sharing/index.ts +4 -0
@@ -16,6 +16,7 @@ import {
16
16
  JoinedShareControl,
17
17
  ShareCopyRow,
18
18
  ShareModeTabs,
19
+ ShareTabs,
19
20
  ShareTrigger,
20
21
  } from "@agent-native/toolkit/sharing";
21
22
  import {
@@ -39,8 +40,8 @@ import {
39
40
  IconCheck,
40
41
  IconChevronDown,
41
42
  IconLoader2,
42
- IconSearch,
43
43
  IconSearchOff,
44
+ IconSend2,
44
45
  IconUsersGroup,
45
46
  IconUserPlus,
46
47
  } from "@tabler/icons-react";
@@ -161,14 +162,15 @@ type Share = ShareButtonShare;
161
162
 
162
163
  const BUTTON_BASE =
163
164
  "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0";
164
- const BUTTON_PRIMARY_SM = cn(
165
+ const BUTTON_PRIMARY_ICON = cn(
165
166
  BUTTON_BASE,
166
- "h-11 px-4 bg-primary text-primary-foreground hover:bg-primary/90 sm:h-9",
167
+ "size-10 shrink-0 bg-primary p-0 text-primary-foreground hover:bg-primary/90 sm:size-8",
167
168
  );
168
169
  const BUTTON_GHOST_ICON = cn(
169
170
  BUTTON_BASE,
170
171
  "h-7 w-7 p-0 text-muted-foreground hover:bg-accent hover:text-accent-foreground",
171
172
  );
173
+ const SHARE_SECTION_LABEL = "text-xs font-medium text-muted-foreground";
172
174
  const SHARE_POPOVER_SURFACE =
173
175
  "border border-border bg-popover text-popover-foreground";
174
176
  const SHARE_NESTED_OVERLAY_ATTR = "data-agent-native-share-overlay";
@@ -427,7 +429,7 @@ export function ShareButton(props: ShareButtonProps) {
427
429
  sideOffset={6}
428
430
  data-agent-native-share-overlay=""
429
431
  className={cn(
430
- "z-[2000] w-[min(460px,92vw)] rounded-lg p-4 shadow-lg",
432
+ "z-[2000] w-[360px] max-w-[calc(100vw-1rem)] rounded-lg p-3 shadow-lg",
431
433
  SHARE_POPOVER_SURFACE,
432
434
  props.popoverClassName,
433
435
  )}
@@ -446,7 +448,6 @@ function SharePanel(
446
448
  },
447
449
  ) {
448
450
  const t = useT();
449
- const tabIds = useId();
450
451
  const { controller } = props;
451
452
  const {
452
453
  inviteEmail,
@@ -514,7 +515,7 @@ function SharePanel(
514
515
  linkType="share"
515
516
  />
516
517
  ) : props.shareUrlPlaceholder ? (
517
- <div className="mb-4 rounded-md border border-dashed border-border bg-muted/20 px-3 py-2.5 text-xs text-muted-foreground">
518
+ <div className="mb-3 rounded-md border border-dashed border-border bg-muted/20 px-3 py-2 text-xs text-muted-foreground">
518
519
  {props.shareUrlLabel ? (
519
520
  <div className="mb-0.5 font-medium text-foreground">
520
521
  {props.shareUrlLabel}
@@ -577,27 +578,33 @@ function SharePanel(
577
578
  ) : isLoading ? (
578
579
  <div>
579
580
  {showShareLinks ? (
580
- <div className="mb-4 h-9 rounded-md bg-muted animate-pulse" />
581
+ <div className="mb-3 h-8 rounded-md bg-muted animate-pulse" />
581
582
  ) : null}
582
- <div className="mb-2 text-sm font-semibold">{generalAccessLabel}</div>
583
- <div className="mb-4 h-9 rounded-md bg-muted animate-pulse" />
584
- <div className="mb-2 text-sm font-semibold">{peopleAccessLabel}</div>
585
- <div className="mb-4 h-7 rounded-md bg-muted animate-pulse" />
583
+ <div className={cn("mb-1.5", SHARE_SECTION_LABEL)}>
584
+ {generalAccessLabel}
585
+ </div>
586
+ <div className="mb-3 h-8 rounded-md bg-muted animate-pulse" />
587
+ <div className={cn("mb-1.5", SHARE_SECTION_LABEL)}>
588
+ {peopleAccessLabel}
589
+ </div>
590
+ <div className="mb-3 h-8 rounded-md bg-muted animate-pulse" />
586
591
  </div>
587
592
  ) : (
588
593
  <div className={props.agentTabContent ? "flex flex-col" : undefined}>
589
594
  {showShareLinks && shareUrlPlacement === "top" ? shareLinks : null}
590
595
 
591
- <div className={cn("mb-4", props.agentTabContent && "order-2")}>
596
+ <div className={cn("mb-3", props.agentTabContent && "order-2")}>
592
597
  {!props.agentTabContent ? (
593
- <div className="mb-2 text-sm font-semibold">{generalAccessLabel}</div>
598
+ <div className={cn("mb-1.5", SHARE_SECTION_LABEL)}>
599
+ {generalAccessLabel}
600
+ </div>
594
601
  ) : null}
595
- <div className="flex items-center gap-3">
602
+ <div className="flex items-center gap-2">
596
603
  <span
597
604
  aria-hidden
598
- className="inline-flex h-9 w-9 shrink-0 items-center justify-center rounded-full bg-muted text-muted-foreground"
605
+ className="inline-flex size-6 shrink-0 items-center justify-center rounded-full bg-muted text-muted-foreground"
599
606
  >
600
- <meta.Icon size={16} strokeWidth={1.75} />
607
+ <meta.Icon size={12} strokeWidth={1.75} />
601
608
  </span>
602
609
  <div className="min-w-0 flex-1">
603
610
  <VisibilitySelect
@@ -624,76 +631,78 @@ function SharePanel(
624
631
  </div>
625
632
  </div>
626
633
 
627
- <div className={cn("mb-4 space-y-4", props.agentTabContent && "order-1")}>
634
+ <div className={cn("mb-3 space-y-3", props.agentTabContent && "order-1")}>
628
635
  {!props.agentTabContent ? (
629
636
  <>
630
637
  {accessRequests}
631
- <div className="text-sm font-semibold">{peopleAccessLabel}</div>
638
+ <div className={SHARE_SECTION_LABEL}>{peopleAccessLabel}</div>
632
639
  </>
633
640
  ) : null}
634
641
  {canManage ? (
635
642
  <div className="space-y-2">
636
- <div className="flex flex-wrap items-stretch gap-2 sm:flex-nowrap">
637
- <div className="w-full sm:min-w-0 sm:flex-1">
638
- <MemberAutocomplete
639
- value={inviteEmail}
640
- open={suggestionsOpen && !props.basicSharingOnly}
641
- onOpenChange={(open) => {
642
- if (!props.basicSharingOnly) setSuggestionsOpen(open);
643
- }}
644
- onValueChange={(next) => {
645
- onInviteEmailChange(next);
646
- if (shareError) setShareError(null);
647
- }}
648
- onSelectSuggestion={(suggestion) => {
649
- if (suggestion.principalType === "group") {
650
- selectGroup(suggestion);
651
- } else {
652
- onInviteEmailChange(suggestion.email);
653
- }
654
- setSuggestionsOpen(false);
655
- if (shareError) setShareError(null);
656
- }}
657
- onSubmit={handleAdd}
658
- placeholder={
659
- policy.requireOrgMemberForUserShares
660
- ? t("agentChat.share.addPeopleOrganization", {
661
- defaultValue: "Add people from your organization",
662
- })
663
- : t("agentChat.share.addPeopleEmail", {
664
- defaultValue: policy.supportsGroupShares
665
- ? "Add people or groups"
666
- : "Add people by email",
667
- })
643
+ <div className="flex items-stretch gap-1.5">
644
+ <MemberAutocomplete
645
+ value={inviteEmail}
646
+ open={suggestionsOpen && !props.basicSharingOnly}
647
+ onOpenChange={(open) => {
648
+ if (!props.basicSharingOnly) setSuggestionsOpen(open);
649
+ }}
650
+ onValueChange={(next) => {
651
+ onInviteEmailChange(next);
652
+ if (shareError) setShareError(null);
653
+ }}
654
+ onSelectSuggestion={(suggestion) => {
655
+ if (suggestion.principalType === "group") {
656
+ selectGroup(suggestion);
657
+ } else {
658
+ onInviteEmailChange(suggestion.email);
668
659
  }
669
- suggestions={[
670
- ...memberSuggestions.map((member) => ({
671
- ...member,
672
- principalType: "user" as const,
673
- })),
674
- ...(policy.supportsGroupShares
675
- ? groupSuggestions.map((group) => ({
676
- ...group,
677
- principalType: "group" as const,
678
- }))
679
- : []),
680
- ]}
681
- search={memberSearch}
682
- />
683
- </div>
684
- <RoleSelect
685
- value={role}
686
- onChange={setRole}
687
- roleCopy={props.roleCopy}
688
- allowedRoles={allowedRolesFor(props)}
660
+ setSuggestionsOpen(false);
661
+ if (shareError) setShareError(null);
662
+ }}
663
+ onSubmit={handleAdd}
664
+ placeholder={
665
+ policy.requireOrgMemberForUserShares
666
+ ? t("agentChat.share.addPeopleOrganization", {
667
+ defaultValue: "Add people from your organization",
668
+ })
669
+ : t("agentChat.share.addPeopleEmail", {
670
+ defaultValue: policy.supportsGroupShares
671
+ ? "Add people or groups"
672
+ : "Add people by email",
673
+ })
674
+ }
675
+ suggestions={[
676
+ ...memberSuggestions.map((member) => ({
677
+ ...member,
678
+ principalType: "user" as const,
679
+ })),
680
+ ...(policy.supportsGroupShares
681
+ ? groupSuggestions.map((group) => ({
682
+ ...group,
683
+ principalType: "group" as const,
684
+ }))
685
+ : []),
686
+ ]}
687
+ search={memberSearch}
688
+ trailing={
689
+ <RoleSelect
690
+ value={role}
691
+ onChange={setRole}
692
+ roleCopy={props.roleCopy}
693
+ allowedRoles={allowedRolesFor(props)}
694
+ />
695
+ }
689
696
  />
690
697
  <button
691
698
  type="button"
692
699
  onClick={handleAdd}
693
700
  disabled={!hasInviteEmail}
694
- className={BUTTON_PRIMARY_SM}
701
+ aria-label={t("agentChat.share.add", { defaultValue: "Add" })}
702
+ title={t("agentChat.share.add", { defaultValue: "Add" })}
703
+ className={BUTTON_PRIMARY_ICON}
695
704
  >
696
- {t("agentChat.share.add", { defaultValue: "Add" })}
705
+ <IconSend2 />
697
706
  </button>
698
707
  </div>
699
708
  {shareError ? (
@@ -761,12 +770,12 @@ function SharePanel(
761
770
  {props.agentTabContent ? (
762
771
  <>
763
772
  {accessRequests}
764
- <div className="text-sm font-semibold">{peopleAccessLabel}</div>
773
+ <div className={SHARE_SECTION_LABEL}>{peopleAccessLabel}</div>
765
774
  </>
766
775
  ) : null}
767
776
  <ul className="flex list-none flex-col gap-1 p-0 m-0">
768
777
  {data?.ownerEmail ? (
769
- <li className="flex items-center gap-3 px-1 py-1.5 text-sm">
778
+ <li className="flex min-h-8 items-center gap-2 px-1.5 py-1 text-sm">
770
779
  <Avatar
771
780
  email={data.ownerEmail}
772
781
  image={memberImage(data.ownerEmail, knownMembers)}
@@ -784,7 +793,7 @@ function SharePanel(
784
793
  <li
785
794
  key={keyOf(s)}
786
795
  className={cn(
787
- "flex items-center gap-3 px-1 py-1.5 text-sm",
796
+ "flex min-h-8 items-center gap-2 px-1.5 py-1 text-sm",
788
797
  inFlight.has(keyOf(s)) && "opacity-60",
789
798
  )}
790
799
  >
@@ -832,7 +841,7 @@ function SharePanel(
832
841
  </li>
833
842
  ))}
834
843
  {!shares.length && !data?.ownerEmail ? (
835
- <li className="px-1 py-1.5 text-sm text-muted-foreground">
844
+ <li className="px-1.5 py-1 text-sm text-muted-foreground">
836
845
  {t("agentChat.share.noAccess", {
837
846
  defaultValue: "No one has access yet.",
838
847
  })}
@@ -844,7 +853,7 @@ function SharePanel(
844
853
  {shareError && !canManage ? (
845
854
  <div
846
855
  role="alert"
847
- className="mb-4 rounded-md border border-destructive/40 bg-destructive/10 px-3 py-2 text-xs text-destructive"
856
+ className="mb-3 rounded-md border border-destructive/40 bg-destructive/10 px-3 py-2 text-xs text-destructive"
848
857
  >
849
858
  {shareError}
850
859
  </div>
@@ -860,7 +869,7 @@ function SharePanel(
860
869
  ) : null}
861
870
 
862
871
  {props.accessNote ? (
863
- <div className="mb-4 rounded-md border border-border bg-muted/35 p-3 text-xs text-muted-foreground">
872
+ <div className="mb-3 rounded-md border border-border bg-muted/35 p-3 text-xs text-muted-foreground">
864
873
  {props.accessNote}
865
874
  </div>
866
875
  ) : null}
@@ -872,14 +881,17 @@ function SharePanel(
872
881
  );
873
882
 
874
883
  const panelTitle = props.panelTitle ? (
875
- <h2 className="text-base font-semibold leading-normal tracking-normal">
884
+ <h2 className="text-sm font-semibold leading-normal tracking-normal">
876
885
  {props.panelTitle}
877
886
  </h2>
878
887
  ) : null;
888
+ const shareOptionsLabel = t("agentChat.share.shareOptions", {
889
+ defaultValue: "Share options",
890
+ });
879
891
 
880
892
  if (props.agentTabContent) {
881
893
  const agentPanel = (
882
- <div className="space-y-4">
894
+ <div className="space-y-3">
883
895
  {props.agentTabContent}
884
896
  {!loadFailed && !isLoading && !props.basicSharingOnly ? (
885
897
  <AgentShareSection
@@ -892,13 +904,14 @@ function SharePanel(
892
904
  </div>
893
905
  );
894
906
  return (
895
- <div className="flex flex-col gap-4">
907
+ <div className="flex flex-col gap-3">
896
908
  {panelTitle}
897
909
  <ShareModeTabs
898
910
  value={activeShareTab === "share" ? "people" : activeShareTab}
899
911
  onValueChange={(value) =>
900
912
  handleShareTabChange(value === "people" ? "share" : value)
901
913
  }
914
+ listLabel={shareOptionsLabel}
902
915
  peopleLabel={props.peopleTabLabel ?? t("agentChat.share.people")}
903
916
  agentsLabel={props.agentsTabLabel ?? t("agentChat.share.agents")}
904
917
  people={sharePanel}
@@ -916,7 +929,7 @@ function SharePanel(
916
929
 
917
930
  if (!hasTabs) {
918
931
  return props.panelTitle ? (
919
- <div className="flex flex-col gap-4">
932
+ <div className="flex flex-col gap-3">
920
933
  {panelTitle}
921
934
  {sharePanel}
922
935
  </div>
@@ -938,54 +951,14 @@ function SharePanel(
938
951
  ? activeShareTab
939
952
  : "share";
940
953
  return (
941
- <div className="flex flex-col gap-4">
954
+ <div className="flex flex-col gap-3">
942
955
  {panelTitle}
943
- <div
944
- role="tablist"
945
- aria-label={t("agentChat.share.shareOptions", {
946
- defaultValue: "Share options",
947
- })}
948
- className="flex gap-1 rounded-xl bg-muted/70 p-1"
949
- >
950
- {tabs.map((tab, index) => {
951
- const active = tab.value === activeTab;
952
- const tabId = `${tabIds}-tab-${index}`;
953
- const panelId = `${tabIds}-panel-${index}`;
954
- return (
955
- <button
956
- key={tab.value}
957
- type="button"
958
- id={tabId}
959
- role="tab"
960
- aria-selected={active}
961
- aria-controls={panelId}
962
- disabled={tab.disabled}
963
- onClick={() => handleShareTabChange(tab.value)}
964
- className={cn(
965
- "h-11 min-w-0 flex-1 rounded-lg px-3 text-sm font-medium text-muted-foreground hover:bg-background/70 hover:text-foreground disabled:pointer-events-none disabled:opacity-50",
966
- active && "bg-background text-foreground shadow-sm",
967
- )}
968
- >
969
- <span className="block truncate">{tab.label}</span>
970
- </button>
971
- );
972
- })}
973
- </div>
974
- {tabs.map((tab, index) => {
975
- const active = tab.value === activeTab;
976
- return (
977
- <div
978
- key={tab.value}
979
- id={`${tabIds}-panel-${index}`}
980
- role="tabpanel"
981
- aria-labelledby={`${tabIds}-tab-${index}`}
982
- tabIndex={active ? 0 : -1}
983
- hidden={!active}
984
- >
985
- {active ? tab.content : null}
986
- </div>
987
- );
988
- })}
956
+ <ShareTabs
957
+ value={activeTab}
958
+ onValueChange={handleShareTabChange}
959
+ listLabel={shareOptionsLabel}
960
+ tabs={tabs}
961
+ />
989
962
  </div>
990
963
  );
991
964
  }
@@ -1094,6 +1067,8 @@ interface MemberAutocompleteProps {
1094
1067
  placeholder: string;
1095
1068
  suggestions: ShareSuggestion[];
1096
1069
  search: OrgMemberSearch;
1070
+ /** Rendered inside the field, after the input. */
1071
+ trailing?: ReactNode;
1097
1072
  }
1098
1073
 
1099
1074
  type ShareSuggestion =
@@ -1110,6 +1085,7 @@ function MemberAutocomplete({
1110
1085
  placeholder,
1111
1086
  suggestions,
1112
1087
  search,
1088
+ trailing,
1113
1089
  }: MemberAutocompleteProps) {
1114
1090
  const t = useT();
1115
1091
  const rawListboxId = useId();
@@ -1202,13 +1178,7 @@ function MemberAutocomplete({
1202
1178
  return (
1203
1179
  <Popover open={open} onOpenChange={onOpenChange}>
1204
1180
  <PopoverAnchor asChild>
1205
- <div className="relative flex-1 min-w-0">
1206
- <IconSearch
1207
- aria-hidden
1208
- size={15}
1209
- strokeWidth={1.8}
1210
- className="pointer-events-none absolute start-2.5 top-1/2 -translate-y-1/2 text-muted-foreground"
1211
- />
1181
+ <div className="flex h-10 min-w-0 flex-1 items-center overflow-hidden rounded-md border border-input bg-card focus-within:ring-1 focus-within:ring-ring sm:h-8">
1212
1182
  <input
1213
1183
  ref={inputRef}
1214
1184
  type="text"
@@ -1235,16 +1205,17 @@ function MemberAutocomplete({
1235
1205
  }}
1236
1206
  onKeyDown={handleKeyDown}
1237
1207
  autoComplete="off"
1238
- className="h-9 w-full min-w-0 rounded-md border border-input bg-card ps-8 pe-8 text-sm text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 focus:ring-offset-background"
1208
+ className="h-full min-w-0 flex-1 bg-transparent px-2.5 text-sm text-foreground placeholder:text-muted-foreground focus:outline-none"
1239
1209
  />
1240
1210
  {search.isLoading ? (
1241
1211
  <IconLoader2
1242
1212
  aria-hidden
1243
- size={15}
1213
+ size={14}
1244
1214
  strokeWidth={1.8}
1245
- className="pointer-events-none absolute end-2.5 top-1/2 -translate-y-1/2 animate-spin text-muted-foreground"
1215
+ className="me-2 shrink-0 animate-spin text-muted-foreground"
1246
1216
  />
1247
1217
  ) : null}
1218
+ {trailing}
1248
1219
  </div>
1249
1220
  </PopoverAnchor>
1250
1221
  <PopoverContent
@@ -1276,7 +1247,7 @@ function MemberAutocomplete({
1276
1247
  onMouseEnter={() => setActiveIndex(index)}
1277
1248
  onClick={() => chooseSuggestion(suggestion)}
1278
1249
  className={cn(
1279
- "flex cursor-pointer select-none flex-col rounded-sm px-3 py-2 text-sm outline-none",
1250
+ "flex cursor-pointer select-none flex-col rounded-sm px-2 py-1.5 text-sm outline-none",
1280
1251
  active
1281
1252
  ? "bg-accent text-accent-foreground"
1282
1253
  : "text-foreground hover:bg-accent hover:text-accent-foreground",
@@ -1295,7 +1266,6 @@ function MemberAutocomplete({
1295
1266
  email={suggestion.email}
1296
1267
  image={suggestion.image}
1297
1268
  label={suggestion.name?.trim() || suggestion.email}
1298
- className="h-6 w-6 text-[10px]"
1299
1269
  />
1300
1270
  )}
1301
1271
  <span className="truncate">
@@ -1416,14 +1386,14 @@ function CopyLinkField({
1416
1386
  copyLabel={t("agentChat.share.copy", { defaultValue: "Copy" })}
1417
1387
  copiedLabel={t("agentChat.share.copied", { defaultValue: "Copied" })}
1418
1388
  onCopy={copy}
1419
- className="mb-4"
1389
+ className="mb-3"
1420
1390
  />
1421
1391
  );
1422
1392
  }
1423
1393
 
1424
1394
  const selectContentClass = `${SHARE_NESTED_OVERLAY_Z} min-w-[12rem] overflow-hidden rounded-md border border-border bg-popover p-1 text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0`;
1425
1395
  const selectItemClass =
1426
- "relative flex w-full cursor-pointer select-none items-start gap-2 rounded-sm py-2 ps-8 pe-3 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50";
1396
+ "relative flex w-full cursor-pointer select-none items-start gap-2 rounded-sm py-1.5 ps-8 pe-3 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50";
1427
1397
 
1428
1398
  interface ShadSelectItemProps {
1429
1399
  value: string;
@@ -1440,7 +1410,7 @@ function SelectItems({ items }: { items: ShadSelectItemProps[] }) {
1440
1410
  value={it.value}
1441
1411
  className={selectItemClass}
1442
1412
  >
1443
- <span className="absolute start-2 top-2 flex h-4 w-4 items-center justify-center">
1413
+ <span className="absolute start-2 top-1.5 flex h-4 w-4 items-center justify-center">
1444
1414
  <Select.ItemIndicator>
1445
1415
  <IconCheck size={14} />
1446
1416
  </Select.ItemIndicator>
@@ -1489,11 +1459,11 @@ function RoleSelect(props: {
1489
1459
  props.plain
1490
1460
  ? cn(
1491
1461
  BUTTON_BASE,
1492
- "h-7 px-2 bg-transparent text-muted-foreground hover:bg-accent hover:text-accent-foreground",
1462
+ "h-7 gap-1 px-2 bg-transparent text-xs font-normal text-muted-foreground hover:bg-accent hover:text-accent-foreground [&_svg]:size-3.5",
1493
1463
  )
1494
1464
  : cn(
1495
1465
  BUTTON_BASE,
1496
- "h-11 px-3 border border-input bg-card hover:bg-accent hover:text-accent-foreground sm:h-9",
1466
+ "h-full gap-1 rounded-none bg-transparent px-2 text-xs font-normal text-muted-foreground hover:bg-accent hover:text-accent-foreground focus-visible:ring-inset focus-visible:ring-offset-0 [&_svg]:size-3.5",
1497
1467
  )
1498
1468
  }
1499
1469
  aria-label={t("agentChat.share.role", { defaultValue: "Role" })}
@@ -1547,7 +1517,7 @@ function VisibilitySelect(props: {
1547
1517
  <Select.Trigger
1548
1518
  className={cn(
1549
1519
  BUTTON_BASE,
1550
- "h-7 px-1 -ms-1 bg-transparent text-foreground hover:bg-accent hover:text-accent-foreground",
1520
+ "h-7 px-1 -ms-1 bg-transparent font-normal text-foreground hover:bg-accent hover:text-accent-foreground",
1551
1521
  props.sentenceTrigger && "w-full justify-between text-sm font-normal",
1552
1522
  )}
1553
1523
  aria-label={t("agentChat.share.generalAccess", {
@@ -1605,12 +1575,12 @@ function Avatar({
1605
1575
  <span
1606
1576
  aria-hidden
1607
1577
  className={cn(
1608
- "inline-flex h-7 w-7 shrink-0 items-center justify-center overflow-hidden rounded-full bg-muted text-[11px] font-semibold text-muted-foreground",
1578
+ "inline-flex size-6 shrink-0 items-center justify-center overflow-hidden rounded-full bg-muted text-[10px] font-semibold text-muted-foreground",
1609
1579
  className,
1610
1580
  )}
1611
1581
  >
1612
1582
  {org || group ? (
1613
- <IconUsersGroup size={14} strokeWidth={1.75} />
1583
+ <IconUsersGroup size={12} strokeWidth={1.75} />
1614
1584
  ) : profileImage || avatarUrl ? (
1615
1585
  <img
1616
1586
  src={profileImage || avatarUrl || undefined}
@@ -158,6 +158,37 @@ describe("ShareDialog primitive normalization", () => {
158
158
  ).toContain("mt-10");
159
159
  });
160
160
 
161
+ it("draws the tab row as underline tabs that switch between link and embed", async () => {
162
+ await renderDialog(vi.fn(), {
163
+ shareUrl: "https://share.example.test/doc-1",
164
+ embedUrl: "https://embed.example.test/doc-1",
165
+ });
166
+
167
+ const list = document.body.querySelector('[role="tablist"]');
168
+ expect(list?.className).toContain("bg-transparent");
169
+ expect(list?.className).not.toContain("bg-muted");
170
+ const tabs = Array.from(
171
+ document.body.querySelectorAll<HTMLElement>('[role="tab"]'),
172
+ );
173
+ expect(tabs.map((tab) => tab.textContent)).toEqual([
174
+ "share.link",
175
+ "share.embed",
176
+ ]);
177
+ for (const tab of tabs) expect(tab.className).toContain("after:h-0.5");
178
+ expect(tabs[0].getAttribute("aria-selected")).toBe("true");
179
+ expect(document.body.textContent).toContain("share.generalAccess");
180
+
181
+ act(() => {
182
+ tabs[1].dispatchEvent(
183
+ new MouseEvent("mousedown", { bubbles: true, button: 0 }),
184
+ );
185
+ });
186
+
187
+ expect(tabs[1].getAttribute("aria-selected")).toBe("true");
188
+ expect(document.body.textContent).toContain("share.embedUrl");
189
+ expect(document.body.textContent).not.toContain("share.generalAccess");
190
+ });
191
+
161
192
  it("keeps individual access on the primary link surface", async () => {
162
193
  await renderDialog(vi.fn(), {
163
194
  shareUrl: "https://share.example.test/doc-1",