@datalayer/core 1.2.19 → 1.2.21

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 (171) hide show
  1. package/lib/api/evals/client.d.ts +6 -6
  2. package/lib/api/evals/client.js +5 -5
  3. package/lib/api/evals/status.d.ts +3 -3
  4. package/lib/api/evals/status.js +3 -3
  5. package/lib/api/evals/types.d.ts +9 -9
  6. package/lib/api/iam/secrets.js +2 -2
  7. package/lib/api/iam/servicePrincipals.js +1 -1
  8. package/lib/api/iam/trials.js +1 -1
  9. package/lib/api/orchestration/generated.d.ts +12 -12
  10. package/lib/api/orchestration/generated.js +1 -1
  11. package/lib/api/orchestration/index.d.ts +2 -2
  12. package/lib/api/orchestration/index.js +2 -2
  13. package/lib/api/orchestration/lifecycle.d.ts +1 -1
  14. package/lib/api/orchestration/lifecycle.js +1 -1
  15. package/lib/api/scheduler/client.d.ts +1 -1
  16. package/lib/api/scheduler/client.js +1 -1
  17. package/lib/api/scheduler/types.d.ts +1 -1
  18. package/lib/api/spacer/comments.d.ts +1 -1
  19. package/lib/api/spacer/comments.js +1 -1
  20. package/lib/api/spacer/spaces.d.ts +1 -1
  21. package/lib/api/spacer/spaces.js +1 -1
  22. package/lib/components/avatars/UserAvatar.d.ts +10 -11
  23. package/lib/components/avatars/UserAvatar.js +23 -28
  24. package/lib/components/avatars/UserProfileAvatar.js +1 -1
  25. package/lib/components/avatars/avatarChoice.d.ts +42 -0
  26. package/lib/components/avatars/avatarChoice.js +65 -0
  27. package/lib/components/avatars/index.d.ts +1 -0
  28. package/lib/components/avatars/index.js +1 -0
  29. package/lib/components/collaboration/LiveEditorCollaborators.d.ts +3 -4
  30. package/lib/components/collaboration/LiveEditorCollaborators.js +7 -5
  31. package/lib/components/emoji/faceEmojis.js +48 -0
  32. package/lib/components/emoji/fluent/loaders.js +46 -0
  33. package/lib/components/emoji/fluent/svg/1f30a.d.ts +2 -0
  34. package/lib/components/emoji/fluent/svg/1f30a.js +5 -0
  35. package/lib/components/emoji/fluent/svg/1f310.d.ts +2 -0
  36. package/lib/components/emoji/fluent/svg/1f310.js +5 -0
  37. package/lib/components/emoji/fluent/svg/1f324.d.ts +2 -0
  38. package/lib/components/emoji/fluent/svg/1f324.js +5 -0
  39. package/lib/components/emoji/fluent/svg/1f326.d.ts +2 -0
  40. package/lib/components/emoji/fluent/svg/1f326.js +5 -0
  41. package/lib/components/emoji/fluent/svg/1f327.d.ts +2 -0
  42. package/lib/components/emoji/fluent/svg/1f327.js +5 -0
  43. package/lib/components/emoji/fluent/svg/1f33e.d.ts +2 -0
  44. package/lib/components/emoji/fluent/svg/1f33e.js +5 -0
  45. package/lib/components/emoji/fluent/svg/1f372.d.ts +2 -0
  46. package/lib/components/emoji/fluent/svg/1f372.js +5 -0
  47. package/lib/components/emoji/fluent/svg/1f373.d.ts +2 -0
  48. package/lib/components/emoji/fluent/svg/1f373.js +5 -0
  49. package/lib/components/emoji/fluent/svg/1f39b.d.ts +2 -0
  50. package/lib/components/emoji/fluent/svg/1f39b.js +5 -0
  51. package/lib/components/emoji/fluent/svg/1f3ab.d.ts +2 -0
  52. package/lib/components/emoji/fluent/svg/1f3ab.js +5 -0
  53. package/lib/components/emoji/fluent/svg/1f41b.d.ts +2 -0
  54. package/lib/components/emoji/fluent/svg/1f41b.js +5 -0
  55. package/lib/components/emoji/fluent/svg/1f441.d.ts +2 -0
  56. package/lib/components/emoji/fluent/svg/1f441.js +5 -0
  57. package/lib/components/emoji/fluent/svg/1f465.d.ts +2 -0
  58. package/lib/components/emoji/fluent/svg/1f465.js +5 -0
  59. package/lib/components/emoji/fluent/svg/1f4b9.d.ts +2 -0
  60. package/lib/components/emoji/fluent/svg/1f4b9.js +5 -0
  61. package/lib/components/emoji/fluent/svg/1f4be.d.ts +2 -0
  62. package/lib/components/emoji/fluent/svg/1f4be.js +5 -0
  63. package/lib/components/emoji/fluent/svg/1f4c1.d.ts +2 -0
  64. package/lib/components/emoji/fluent/svg/1f4c1.js +5 -0
  65. package/lib/components/emoji/fluent/svg/1f4c2.d.ts +2 -0
  66. package/lib/components/emoji/fluent/svg/1f4c2.js +5 -0
  67. package/lib/components/emoji/fluent/svg/1f4c4.d.ts +2 -0
  68. package/lib/components/emoji/fluent/svg/1f4c4.js +5 -0
  69. package/lib/components/emoji/fluent/svg/1f4d3.d.ts +2 -0
  70. package/lib/components/emoji/fluent/svg/1f4d3.js +5 -0
  71. package/lib/components/emoji/fluent/svg/1f4d6.d.ts +2 -0
  72. package/lib/components/emoji/fluent/svg/1f4d6.js +5 -0
  73. package/lib/components/emoji/fluent/svg/1f4dc.d.ts +2 -0
  74. package/lib/components/emoji/fluent/svg/1f4dc.js +5 -0
  75. package/lib/components/emoji/fluent/svg/1f4e1.d.ts +2 -0
  76. package/lib/components/emoji/fluent/svg/1f4e1.js +5 -0
  77. package/lib/components/emoji/fluent/svg/1f4e2.d.ts +2 -0
  78. package/lib/components/emoji/fluent/svg/1f4e2.js +5 -0
  79. package/lib/components/emoji/fluent/svg/1f4e7.d.ts +2 -0
  80. package/lib/components/emoji/fluent/svg/1f4e7.js +5 -0
  81. package/lib/components/emoji/fluent/svg/1f500.d.ts +2 -0
  82. package/lib/components/emoji/fluent/svg/1f500.js +5 -0
  83. package/lib/components/emoji/fluent/svg/1f501.d.ts +2 -0
  84. package/lib/components/emoji/fluent/svg/1f501.js +5 -0
  85. package/lib/components/emoji/fluent/svg/1f504.d.ts +2 -0
  86. package/lib/components/emoji/fluent/svg/1f504.js +5 -0
  87. package/lib/components/emoji/fluent/svg/1f52d.d.ts +2 -0
  88. package/lib/components/emoji/fluent/svg/1f52d.js +5 -0
  89. package/lib/components/emoji/fluent/svg/1f552.d.ts +2 -0
  90. package/lib/components/emoji/fluent/svg/1f552.js +5 -0
  91. package/lib/components/emoji/fluent/svg/1f58b.d.ts +2 -0
  92. package/lib/components/emoji/fluent/svg/1f58b.js +5 -0
  93. package/lib/components/emoji/fluent/svg/1f5b2.d.ts +2 -0
  94. package/lib/components/emoji/fluent/svg/1f5b2.js +5 -0
  95. package/lib/components/emoji/fluent/svg/1f5c3.d.ts +2 -0
  96. package/lib/components/emoji/fluent/svg/1f5c3.js +5 -0
  97. package/lib/components/emoji/fluent/svg/1f5d1.d.ts +2 -0
  98. package/lib/components/emoji/fluent/svg/1f5d1.js +5 -0
  99. package/lib/components/emoji/fluent/svg/1f5dc.d.ts +2 -0
  100. package/lib/components/emoji/fluent/svg/1f5dc.js +5 -0
  101. package/lib/components/emoji/fluent/svg/1f604.d.ts +2 -0
  102. package/lib/components/emoji/fluent/svg/1f604.js +5 -0
  103. package/lib/components/emoji/fluent/svg/1f917.d.ts +2 -0
  104. package/lib/components/emoji/fluent/svg/1f917.js +5 -0
  105. package/lib/components/emoji/fluent/svg/1f9d1-200d-2696.d.ts +2 -0
  106. package/lib/components/emoji/fluent/svg/1f9d1-200d-2696.js +5 -0
  107. package/lib/components/emoji/fluent/svg/1fa79.d.ts +2 -0
  108. package/lib/components/emoji/fluent/svg/1fa79.js +5 -0
  109. package/lib/components/emoji/fluent/svg/1fa9d.d.ts +2 -0
  110. package/lib/components/emoji/fluent/svg/1fa9d.js +5 -0
  111. package/lib/components/emoji/fluent/svg/1fa9f.d.ts +2 -0
  112. package/lib/components/emoji/fluent/svg/1fa9f.js +5 -0
  113. package/lib/components/emoji/fluent/svg/23f3.d.ts +2 -0
  114. package/lib/components/emoji/fluent/svg/23f3.js +5 -0
  115. package/lib/components/emoji/fluent/svg/2611.d.ts +2 -0
  116. package/lib/components/emoji/fluent/svg/2611.js +5 -0
  117. package/lib/components/emoji/fluent/svg/2702.d.ts +2 -0
  118. package/lib/components/emoji/fluent/svg/2702.js +5 -0
  119. package/lib/components/emoji/fluent/svg/270f.d.ts +2 -0
  120. package/lib/components/emoji/fluent/svg/270f.js +5 -0
  121. package/lib/components/emoji/fluent/svg/2728.d.ts +2 -0
  122. package/lib/components/emoji/fluent/svg/2728.js +5 -0
  123. package/lib/components/emoji/fluent/svg/2b1c.d.ts +2 -0
  124. package/lib/components/emoji/fluent/svg/2b1c.js +5 -0
  125. package/lib/components/principal/Principal.js +1 -1
  126. package/lib/components/principal/PrincipalAppearance.d.ts +24 -1
  127. package/lib/components/principal/PrincipalAppearance.js +105 -34
  128. package/lib/components/principal/PrincipalAvatar.d.ts +5 -7
  129. package/lib/components/principal/PrincipalAvatar.js +12 -18
  130. package/lib/components/principal/PrincipalDetailsOverlay.d.ts +4 -7
  131. package/lib/components/principal/PrincipalDetailsOverlay.js +2 -2
  132. package/lib/components/principal/PrincipalSwitcherMenu.js +1 -1
  133. package/lib/hooks/unlinkIAMProvider.d.ts +22 -0
  134. package/lib/hooks/unlinkIAMProvider.js +24 -0
  135. package/lib/hooks/useCache.d.ts +8 -1
  136. package/lib/hooks/useCache.js +37 -6
  137. package/lib/hooks/useNavigate.js +8 -2
  138. package/lib/models/IAMProviderLinked.d.ts +9 -0
  139. package/lib/models/IAMProviderLinked.js +34 -5
  140. package/lib/models/McpTask.d.ts +18 -1
  141. package/lib/models/McpTask.js +15 -0
  142. package/lib/models/Profile.d.ts +2 -2
  143. package/lib/models/Secret.d.ts +1 -1
  144. package/lib/models/Secret.js +1 -1
  145. package/lib/models/StartedBy.d.ts +1 -1
  146. package/lib/models/StartedBy.js +1 -1
  147. package/lib/models/User.js +5 -12
  148. package/lib/models/UserOnboarding.d.ts +9 -0
  149. package/lib/models/UserOnboarding.js +33 -0
  150. package/lib/portable/index.d.ts +3 -2
  151. package/lib/portable/index.js +4 -2
  152. package/lib/portable/states.d.ts +39 -0
  153. package/lib/portable/states.js +71 -0
  154. package/lib/state/substates/NavigationState.d.ts +5 -3
  155. package/lib/state/substates/NavigationState.js +12 -3
  156. package/lib/views/mcp/AlertDestinations.js +2 -2
  157. package/lib/views/mcp/ApprovalQueue.js +2 -2
  158. package/lib/views/mcp/EnterpriseConsole.js +2 -2
  159. package/lib/views/mcp/McpDashboard.js +2 -2
  160. package/lib/views/mcp/McpHome.js +4 -4
  161. package/lib/views/mcp/McpObservability.js +5 -5
  162. package/lib/views/mcp/NotebookRuns.js +2 -2
  163. package/lib/views/mcp/OrganizationUsage.js +2 -2
  164. package/lib/views/mcp/Policies.js +2 -2
  165. package/lib/views/mcp/RunDetail.d.ts +1 -1
  166. package/lib/views/mcp/RunDetail.js +4 -1
  167. package/lib/views/mcp/ScimProvisioning.js +2 -2
  168. package/lib/views/mcp/ToolAccess.js +2 -2
  169. package/lib/views/secrets/SecretNew.js +1 -1
  170. package/package.json +1 -1
  171. package/scripts/generate-orchestration-types.py +12 -12
@@ -0,0 +1,5 @@
1
+ /*
2
+ * ✨ sparkles — Fluent Emoji, Flat, by Microsoft (MIT,
3
+ * see ../LICENSE). Written by scripts/generate-fluent-emoji.py: do not edit.
4
+ */
5
+ export default '<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M10.5194 7.0517C10.2265 6.93064 9.99626 6.69861 9.88117 6.41614L8.929 4.25725C8.75112 3.91425 8.23842 3.91425 8.071 4.25725L7.11883 6.41614C6.99327 6.69861 6.76308 6.92055 6.48057 7.0517L5.26682 7.57629C4.91106 7.74779 4.91106 8.24212 5.26682 8.41362L6.48057 8.93821C6.77354 9.05927 7.00374 9.2913 7.11883 9.57377L8.071 11.7427C8.24888 12.0858 8.76158 12.0858 8.929 11.7427L9.88117 9.57377C10.0067 9.2913 10.2369 9.06936 10.5194 8.93821L11.7332 8.41362C12.0889 8.24212 12.0889 7.74779 11.7332 7.57629L10.5194 7.0517Z" fill="#F9C23C"/> <path d="M25.5744 13.5546C24.7045 13.1673 24.0166 12.4539 23.6525 11.5775L20.7897 4.81023C20.2637 3.72992 18.7363 3.72992 18.2103 4.81023L15.3475 11.5775C14.9733 12.4539 14.2854 13.1673 13.4256 13.5546L9.80419 15.1955C8.73194 15.7254 8.73194 17.2746 9.80419 17.8045L13.4256 19.4454C14.2955 19.8327 14.9834 20.5461 15.3475 21.4225L18.2103 28.1898C18.7363 29.2701 20.2637 29.2701 20.7897 28.1898L23.6525 21.4225C24.0267 20.5461 24.7146 19.8327 25.5744 19.4454L29.1958 17.8045C30.2681 17.2746 30.2681 15.7254 29.1958 15.1955L25.5744 13.5546Z" fill="#F9C23C"/> <path d="M8.2811 20.3304C8.44173 20.7222 8.73465 21.0258 9.10315 21.2021L10.6528 21.927C11.1157 22.1621 11.1157 22.8379 10.6528 23.073L9.10315 23.7979C8.73465 23.9742 8.44173 24.2876 8.2811 24.6696L7.05276 27.6474C6.82598 28.1175 6.17402 28.1175 5.94724 27.6474L4.7189 24.6696C4.55827 24.2778 4.26535 23.9742 3.89685 23.7979L2.34724 23.073C1.88425 22.8379 1.88425 22.1621 2.34724 21.927L3.89685 21.2021C4.26535 21.0258 4.55827 20.7124 4.7189 20.3304L5.94724 17.3526C6.17402 16.8825 6.82598 16.8825 7.05276 17.3526L8.2811 20.3304Z" fill="#F9C23C"/> </svg>';
@@ -0,0 +1,2 @@
1
+ declare const _default: "<svg width=\"32\" height=\"32\" viewBox=\"0 0 32 32\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M4.02783 1.5C2.64712 1.5 1.52783 2.61929 1.52783 4V28C1.52783 29.3807 2.64712 30.5 4.02783 30.5H28.0278C29.4085 30.5 30.5278 29.3807 30.5278 28V4C30.5278 2.61929 29.4085 1.5 28.0278 1.5H4.02783Z\" fill=\"#9B9B9B\"/> <path d=\"M2.52783 4C2.52783 3.17157 3.19941 2.5 4.02783 2.5H28.0278C28.8563 2.5 29.5278 3.17157 29.5278 4V28C29.5278 28.8284 28.8563 29.5 28.0278 29.5H4.02783C3.19941 29.5 2.52783 28.8284 2.52783 28V4Z\" fill=\"white\"/> </svg>";
2
+ export default _default;
@@ -0,0 +1,5 @@
1
+ /*
2
+ * ⬜ white large square — Fluent Emoji, Flat, by Microsoft (MIT,
3
+ * see ../LICENSE). Written by scripts/generate-fluent-emoji.py: do not edit.
4
+ */
5
+ export default '<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M4.02783 1.5C2.64712 1.5 1.52783 2.61929 1.52783 4V28C1.52783 29.3807 2.64712 30.5 4.02783 30.5H28.0278C29.4085 30.5 30.5278 29.3807 30.5278 28V4C30.5278 2.61929 29.4085 1.5 28.0278 1.5H4.02783Z" fill="#9B9B9B"/> <path d="M2.52783 4C2.52783 3.17157 3.19941 2.5 4.02783 2.5H28.0278C28.8563 2.5 29.5278 3.17157 29.5278 4V28C29.5278 28.8284 28.8563 29.5 28.0278 29.5H4.02783C3.19941 29.5 2.52783 28.8284 2.52783 28V4Z" fill="white"/> </svg>';
@@ -288,6 +288,6 @@ export const Principal = ({ principal, isAdmin = false, avatarSize = 20, gap = 2
288
288
  resolvedIsPublic,
289
289
  upsertCachedPrincipal,
290
290
  ]);
291
- return (_jsxs(Box, { display: "inline-flex", alignItems: "center", gap: gap, minWidth: 0, sx: sx, children: [_jsx(PrincipalAvatar, { kind: resolvedPrincipal.kind, avatarUrl: resolvedPrincipal.avatarUrl, avatarIcon: resolvedPrincipal.avatarIcon, alt: resolvedPrincipal.displayName, size: avatarSize, square: square }), _jsx(PrincipalDetailsOverlay, { kind: resolvedPrincipal.kind, uid: resolvedPrincipal.uid, displayName: resolvedPrincipal.displayName, name: resolvedPrincipal.name, description: resolvedPrincipal.description, handle: resolvedPrincipal.handle, accountHandle: resolvedPrincipal.accountHandle, firstName: resolvedPrincipal.firstName, lastName: resolvedPrincipal.lastName, email: resolvedPrincipal.email, origin: resolvedPrincipal.origin, avatarUrl: resolvedPrincipal.avatarUrl, avatarIcon: resolvedPrincipal.avatarIcon, organizationName: teamOrganizationNameResolved, memberCount: teamMemberCount, isPublic: resolvedIsPublic, isAdmin: isAdmin })] }));
291
+ return (_jsxs(Box, { display: "inline-flex", alignItems: "center", gap: gap, minWidth: 0, sx: sx, children: [_jsx(PrincipalAvatar, { kind: resolvedPrincipal.kind, avatarUrl: resolvedPrincipal.avatarUrl, avatar: resolvedPrincipal.avatarIcon, alt: resolvedPrincipal.displayName, size: avatarSize, square: square }), _jsx(PrincipalDetailsOverlay, { kind: resolvedPrincipal.kind, uid: resolvedPrincipal.uid, displayName: resolvedPrincipal.displayName, name: resolvedPrincipal.name, description: resolvedPrincipal.description, handle: resolvedPrincipal.handle, accountHandle: resolvedPrincipal.accountHandle, firstName: resolvedPrincipal.firstName, lastName: resolvedPrincipal.lastName, email: resolvedPrincipal.email, origin: resolvedPrincipal.origin, avatarUrl: resolvedPrincipal.avatarUrl, avatar: resolvedPrincipal.avatarIcon, organizationName: teamOrganizationNameResolved, memberCount: teamMemberCount, isPublic: resolvedIsPublic, isAdmin: isAdmin })] }));
292
292
  };
293
293
  export default Principal;
@@ -1,5 +1,6 @@
1
1
  import type { JSX } from 'react';
2
2
  import { type ComponentType, type SVGProps } from 'react';
3
+ import { type AvatarChoice } from '../avatars/avatarChoice';
3
4
  /**
4
5
  * Who a principal is: a person, an organization, a team — or a service
5
6
  * principal, the one a deployed LOOP application acts in its own name through
@@ -114,7 +115,29 @@ export declare const PRINCIPAL_BANNERS: readonly [{
114
115
  readonly Component: ComponentType<any>;
115
116
  }];
116
117
  export declare function getPrincipalBannerForSeed(seed: string): string;
117
- export declare function getPrincipalAvatarIcon(name?: string): AvatarComponent | undefined;
118
+ /** The names of the avatar set: the icons an avatar may be. */
119
+ export declare const PRINCIPAL_AVATAR_ICON_NAMES: ReadonlySet<string>;
120
+ /** The avatar a kept value names; throws `AvatarRefused` for anything else. */
121
+ export declare function parsePrincipalAvatar(value: string): AvatarChoice;
122
+ /**
123
+ * The avatar a kept value names, or `undefined` when there is none or it is
124
+ * refused — said once on the console, in its sentence: a picture that cannot
125
+ * be drawn is drawn as no choice, never as a broken page.
126
+ */
127
+ export declare function principalAvatarOf(value: string | undefined): AvatarChoice | undefined;
128
+ /**
129
+ * The drawing of a chosen avatar — an emoji or an icon of the set, by its
130
+ * kept value — on nothing: whatever holds it gives it its disc
131
+ * (`UserAvatar`, `PrincipalAvatar`, an application's face). `null` for no
132
+ * choice, or a refused one.
133
+ */
134
+ export declare function ChosenAvatar({ avatar, size, label, }: {
135
+ avatar: string | undefined;
136
+ /** Its edge, in pixels. */
137
+ size: number;
138
+ /** What it is called for a screen reader; empty, it is decoration. */
139
+ label?: string;
140
+ }): JSX.Element | null;
118
141
  export declare function PrincipalBannerImage({ banner, height, }: {
119
142
  banner?: string;
120
143
  height?: number;
@@ -1,8 +1,11 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { Suspense, lazy, useEffect, useState, } from 'react';
3
- import { Box } from '@datalayer/primer-addons';
4
- import { Button, Text, Tooltip } from '@primer/react';
3
+ import { Box, useColorPalette } from '@datalayer/primer-addons';
4
+ import { Button, SegmentedControl, Text, Tooltip } from '@primer/react';
5
5
  import { Dialog } from '@primer/react/experimental';
6
+ import { AvatarRefused, emojiAvatar, iconAvatar, parseAvatar, } from '../avatars/avatarChoice.js';
7
+ import { FACE_EMOJIS } from '../emoji/faceEmojis.js';
8
+ import { FluentEmoji } from '../emoji/FluentEmoji.js';
6
9
  /**
7
10
  * An avatar icon, fetched the first time it is drawn.
8
11
  *
@@ -577,8 +580,50 @@ export function getPrincipalBannerForSeed(seed) {
577
580
  }
578
581
  return PRINCIPAL_BANNERS[Math.abs(hash) % PRINCIPAL_BANNERS.length].name;
579
582
  }
580
- export function getPrincipalAvatarIcon(name) {
581
- return PRINCIPAL_AVATAR_ICONS.find(option => option.name === name)?.Icon;
583
+ /** The names of the avatar set: the icons an avatar may be. */
584
+ export const PRINCIPAL_AVATAR_ICON_NAMES = new Set(PRINCIPAL_AVATAR_ICONS.map(option => option.name));
585
+ /** The avatar a kept value names; throws `AvatarRefused` for anything else. */
586
+ export function parsePrincipalAvatar(value) {
587
+ return parseAvatar(value, PRINCIPAL_AVATAR_ICON_NAMES);
588
+ }
589
+ /**
590
+ * The avatar a kept value names, or `undefined` when there is none or it is
591
+ * refused — said once on the console, in its sentence: a picture that cannot
592
+ * be drawn is drawn as no choice, never as a broken page.
593
+ */
594
+ export function principalAvatarOf(value) {
595
+ if (!value) {
596
+ return undefined;
597
+ }
598
+ try {
599
+ return parsePrincipalAvatar(value);
600
+ }
601
+ catch (error) {
602
+ if (error instanceof AvatarRefused) {
603
+ console.error(`Avatar refused: ${error.message}`);
604
+ return undefined;
605
+ }
606
+ throw error;
607
+ }
608
+ }
609
+ /**
610
+ * The drawing of a chosen avatar — an emoji or an icon of the set, by its
611
+ * kept value — on nothing: whatever holds it gives it its disc
612
+ * (`UserAvatar`, `PrincipalAvatar`, an application's face). `null` for no
613
+ * choice, or a refused one.
614
+ */
615
+ export function ChosenAvatar({ avatar, size, label = '', }) {
616
+ const choice = principalAvatarOf(avatar);
617
+ if (!choice) {
618
+ return null;
619
+ }
620
+ if (choice.kind === 'emoji') {
621
+ return _jsx(FluentEmoji, { emoji: choice.emoji, size: size, label: label });
622
+ }
623
+ const Icon = PRINCIPAL_AVATAR_ICONS.find(option => option.name === choice.name).Icon;
624
+ // The plain icon, coloured by the theme — never the `colored` variant,
625
+ // whose fixed palette belongs to the emoji it is drawn from.
626
+ return (_jsx(Icon, { size: size, themed: true, colormode: true, "aria-label": label || undefined, "aria-hidden": label ? undefined : true }));
582
627
  }
583
628
  export function PrincipalBannerImage({ banner, height = 180, }) {
584
629
  const Banner = PRINCIPAL_BANNERS.find(option => option.name === banner)?.Component;
@@ -601,8 +646,26 @@ export function PrincipalBannerImage({ banner, height = 180, }) {
601
646
  }
602
647
  export function PrincipalAvatarPicker({ value, onChange, disabled = false, }) {
603
648
  const [open, setOpen] = useState(false);
604
- const selected = PRINCIPAL_AVATAR_ICONS.find(option => option.name === value);
605
- return (_jsxs(_Fragment, { children: [_jsx(Button, { type: "button", disabled: disabled, onClick: () => setOpen(true), children: selected ? `Change Avatar (${selected.label})` : 'Choose Avatar' }), open ? (_jsx(Dialog, { title: "Choose an Avatar", onClose: () => setOpen(false),
649
+ const chosen = principalAvatarOf(value);
650
+ // The colour a chosen avatar is drawn in (`UserAvatar`'s `--datalayer-icon-fg`):
651
+ // the icons are offered as they will look.
652
+ const palette = useColorPalette();
653
+ // The tab the dialog opens on: what is chosen, the icons when nothing is.
654
+ const [tab, setTab] = useState(chosen?.kind === 'emoji' ? 'emoji' : 'icons');
655
+ const choose = (next) => {
656
+ onChange(next);
657
+ setOpen(false);
658
+ };
659
+ const selectedLabel = chosen?.kind === 'emoji'
660
+ ? chosen.emoji
661
+ : chosen
662
+ ? PRINCIPAL_AVATAR_ICONS.find(option => option.name === chosen.name)
663
+ ?.label
664
+ : undefined;
665
+ return (_jsxs(_Fragment, { children: [_jsx(Button, { type: "button", disabled: disabled, onClick: () => {
666
+ setTab(chosen?.kind === 'emoji' ? 'emoji' : 'icons');
667
+ setOpen(true);
668
+ }, leadingVisual: chosen ? () => _jsx(ChosenAvatar, { avatar: value, size: 16 }) : undefined, children: selectedLabel ? `Change Avatar (${selectedLabel})` : 'Choose Avatar' }), open ? (_jsxs(Dialog, { title: "Choose an Avatar", onClose: () => setOpen(false),
606
669
  // The widest the dialog names — 640px — which is what it falls
607
670
  // back to should the width below ever stop applying.
608
671
  width: "xlarge",
@@ -622,34 +685,42 @@ export function PrincipalAvatarPicker({ value, onChange, disabled = false, }) {
622
685
  width: 'min(1080px, calc(100vw - 32px))',
623
686
  maxWidth: 'none',
624
687
  },
625
- }, children: _jsx(Box, { display: "grid",
626
- // Twelve to a line, whatever the dialog is wide: the columns
627
- // share it evenly and `minmax(0, …)` lets them shrink under
628
- // their content, which a long name would otherwise widen.
629
- gridTemplateColumns: [
630
- 'repeat(6, minmax(0, 1fr))',
631
- 'repeat(12, minmax(0, 1fr))',
632
- ], gap: 2, children: PRINCIPAL_AVATAR_ICONS.map(({ name, label, description, Icon }) => (
633
- // A button drawn as a card rather than a Primer `Button`: the
634
- // avatar is named under it, as the banners are under theirs.
635
- // The name under a card is clipped when it is long, and a
636
- // picture says only so much: the tooltip gives the whole name
637
- // and what the picture is of.
638
- _jsx(Tooltip, { text: description ? `${label} — ${description}` : label, direction: "n", children: _jsxs(Box, { as: "button", type: "button", "aria-label": description ? `${label} — ${description}` : label, "aria-pressed": value === name, onClick: () => {
639
- onChange(name);
640
- setOpen(false);
641
- }, display: "block", width: "100%", p: 0, overflow: "hidden", appearance: "none", color: "fg.default", cursor: "pointer", border: "1px solid", borderRadius: 2, borderColor: value === name ? 'accent.emphasis' : 'border.default', bg: value === name ? 'accent.subtle' : 'canvas.default', hover: { borderColor: 'accent.emphasis' }, children: [_jsx(Box, { height: 72, display: "flex", alignItems: "center", justifyContent: "center", children: _jsx(Icon, { size: 52, themed: true, colormode: true }) }), _jsx(Text, { sx: {
642
- display: 'block',
643
- px: 2,
644
- py: 1,
645
- fontSize: 0,
646
- textAlign: 'center',
647
- // A name too long for the card is cut rather than
648
- // stretching the grid it sits in.
649
- overflow: 'hidden',
650
- textOverflow: 'ellipsis',
651
- whiteSpace: 'nowrap',
652
- }, children: label })] }) }, name))) }) })) : null] }));
688
+ }, children: [_jsxs(SegmentedControl, { "aria-label": "Avatar kind", sx: { mb: 3 }, onChange: index => setTab(index === 0 ? 'emoji' : 'icons'), children: [_jsx(SegmentedControl.Button, { selected: tab === 'emoji', children: "Emoji" }), _jsx(SegmentedControl.Button, { selected: tab === 'icons', children: "Icons" })] }), tab === 'emoji' ? (_jsx(Box, { display: "grid", gridTemplateColumns: [
689
+ 'repeat(6, minmax(0, 1fr))',
690
+ 'repeat(12, minmax(0, 1fr))',
691
+ ], gap: 2, children: FACE_EMOJIS.map(emoji => {
692
+ const next = emojiAvatar(emoji);
693
+ const pressed = value === next;
694
+ return (_jsx(Box, { as: "button", type: "button", "aria-label": emoji, "aria-pressed": pressed, onClick: () => choose(next), display: "flex", alignItems: "center", justifyContent: "center", height: 72, width: "100%", p: 0, appearance: "none", cursor: "pointer", border: "1px solid", borderRadius: 2, borderColor: pressed ? 'accent.emphasis' : 'border.default', bg: pressed ? 'accent.subtle' : 'canvas.default', hover: { borderColor: 'accent.emphasis' }, children: _jsx(FluentEmoji, { emoji: emoji, size: 44, label: "" }) }, emoji));
695
+ }) })) : null, tab === 'icons' ? (_jsx(Box, { style: { '--datalayer-icon-fg': palette.primary }, display: "grid",
696
+ // Twelve to a line, whatever the dialog is wide: the columns
697
+ // share it evenly and `minmax(0, …)` lets them shrink under
698
+ // their content, which a long name would otherwise widen.
699
+ gridTemplateColumns: [
700
+ 'repeat(6, minmax(0, 1fr))',
701
+ 'repeat(12, minmax(0, 1fr))',
702
+ ], gap: 2, children: PRINCIPAL_AVATAR_ICONS.map(({ name, label, description, Icon }) => (
703
+ // A button drawn as a card rather than a Primer `Button`: the
704
+ // avatar is named under it, as the banners are under theirs.
705
+ // The name under a card is clipped when it is long, and a
706
+ // picture says only so much: the tooltip gives the whole name
707
+ // and what the picture is of.
708
+ _jsx(Tooltip, { text: description ? `${label} — ${description}` : label, direction: "n", children: _jsxs(Box, { as: "button", type: "button", "aria-label": description ? `${label} — ${description}` : label, "aria-pressed": value === iconAvatar(name), onClick: () => choose(iconAvatar(name)), display: "block", width: "100%", p: 0, overflow: "hidden", appearance: "none", color: "fg.default", cursor: "pointer", border: "1px solid", borderRadius: 2, borderColor: value === iconAvatar(name)
709
+ ? 'accent.emphasis'
710
+ : 'border.default', bg: value === iconAvatar(name)
711
+ ? 'accent.subtle'
712
+ : 'canvas.default', hover: { borderColor: 'accent.emphasis' }, children: [_jsx(Box, { height: 72, display: "flex", alignItems: "center", justifyContent: "center", children: _jsx(Icon, { size: 52, themed: true, colormode: true }) }), _jsx(Text, { sx: {
713
+ display: 'block',
714
+ px: 2,
715
+ py: 1,
716
+ fontSize: 0,
717
+ textAlign: 'center',
718
+ // A name too long for the card is cut rather than
719
+ // stretching the grid it sits in.
720
+ overflow: 'hidden',
721
+ textOverflow: 'ellipsis',
722
+ whiteSpace: 'nowrap',
723
+ }, children: label })] }) }, name))) })) : null] })) : null] }));
653
724
  }
654
725
  export function PrincipalBannerPicker({ value, onChange, disabled = false, showPreview = true, }) {
655
726
  const [open, setOpen] = useState(false);
@@ -4,14 +4,12 @@ export type PrincipalAvatarKind = PrincipalType;
4
4
  export type PrincipalAvatarProps = {
5
5
  kind: PrincipalAvatarKind;
6
6
  avatarUrl?: string;
7
- avatarIcon?: string;
8
7
  /**
9
- * A literal emoji this platform draws for a face of its own — an agent —
10
- * rather than a person's chosen picture or icon. Checked before
11
- * `avatarUrl`/`avatarIcon`, and only for `kind: 'personal'`: an agent is a
12
- * personal-shaped seat in every roster it appears in today.
8
+ * The chosen avatar, as it is kept: `emoji:…` or
9
+ * `@datalayer/icons-react:…` (`components/avatars/avatarChoice`). An
10
+ * agent's face is its emoji, as `emoji:…`.
13
11
  */
14
- avatarEmoji?: string;
12
+ avatar?: string;
15
13
  alt?: string;
16
14
  size?: number;
17
15
  square?: boolean;
@@ -24,5 +22,5 @@ export type PrincipalAvatarProps = {
24
22
  ring?: boolean;
25
23
  className?: string;
26
24
  };
27
- export declare function PrincipalAvatar({ kind, avatarUrl, avatarIcon, avatarEmoji, alt, size, square, ring, className, }: PrincipalAvatarProps): JSX.Element;
25
+ export declare function PrincipalAvatar({ kind, avatarUrl, avatar, alt, size, square, ring, className, }: PrincipalAvatarProps): JSX.Element;
28
26
  export default PrincipalAvatar;
@@ -1,18 +1,14 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import { Box } from '@datalayer/primer-addons';
2
+ import { EntityAvatar } from '@datalayer/primer-addons';
3
3
  import { OrganizationIcon, PeopleIcon } from '@primer/octicons-react';
4
4
  import { UserAvatar } from '../avatars/index.js';
5
- import { getPrincipalAvatarIcon, } from './PrincipalAppearance.js';
5
+ import { ChosenAvatar, principalAvatarOf, } from './PrincipalAppearance.js';
6
6
  function getFallbackIconSize(size) {
7
7
  return Math.max(12, Math.round(size * 0.62));
8
8
  }
9
- export function PrincipalAvatar({ kind, avatarUrl, avatarIcon, avatarEmoji, alt, size = 20, square = false, ring = false, className, }) {
10
- const SelectedIcon = getPrincipalAvatarIcon(avatarIcon);
11
- // Outside the edge rather than a border, so the avatar keeps the size it
12
- // was asked for — see `UserAvatar`, which draws its ring the same way.
13
- const ringSx = ring
14
- ? { boxShadow: '0 0 0 1px var(--borderColor-default, currentColor)' }
15
- : undefined;
9
+ export function PrincipalAvatar({ kind, avatarUrl, avatar, alt, size = 20, square = false, ring = false, className, }) {
10
+ const chosen = principalAvatarOf(avatar);
11
+ const shape = square ? 'rounded' : 'circle';
16
12
  /*
17
13
  * A person is drawn by `UserAvatar`, whatever they wear.
18
14
  *
@@ -25,17 +21,15 @@ export function PrincipalAvatar({ kind, avatarUrl, avatarIcon, avatarEmoji, alt,
25
21
  // A service principal — an application's — is drawn as a person is: its
26
22
  // chosen avatar, or its emoji, on the same disc (LOOP I-07).
27
23
  if (kind === 'personal' || kind === 'service') {
28
- return (_jsx(UserAvatar, { avatarUrl: avatarUrl, avatarIcon: avatarIcon, avatarEmoji: avatarEmoji, size: size, square: square, iconSize: getFallbackIconSize(size), ring: ring, className: className }));
24
+ return (_jsx(UserAvatar, { avatarUrl: avatarUrl, avatar: avatar, size: size, square: square, iconSize: getFallbackIconSize(size), ring: ring, className: className }));
29
25
  }
30
- if (SelectedIcon) {
31
- return (_jsx(Box, { className: className, width: size, height: size, display: "inline-flex", alignItems: "center", justifyContent: "center", overflow: "hidden", borderRadius: square ? 2 : '50%',
32
- // The disc of `UserAvatar`, for the same reason: the shape is what
33
- // says "avatar", and an icon drawn on nothing shows none of it.
34
- bg: "accent.subtle", sx: ringSx, "aria-label": alt || `${kind} avatar`, children: _jsx(SelectedIcon, { size: getFallbackIconSize(size), themed: true, colormode: true }) }));
26
+ if (chosen) {
27
+ return (
28
+ // The disc of `UserAvatar`, for the same reason: the shape is what says
29
+ // "avatar", and an icon drawn on nothing shows none of it.
30
+ _jsx(EntityAvatar, { className: className, size: size, shape: shape, ring: ring, alt: alt || `${kind} avatar`, children: _jsx(ChosenAvatar, { avatar: avatar, size: getFallbackIconSize(size) }) }));
35
31
  }
36
- const iconSize = getFallbackIconSize(size);
37
- const borderRadius = square ? 2 : '50%';
38
32
  const Icon = kind === 'team' ? PeopleIcon : OrganizationIcon;
39
- return (_jsx(Box, { className: className, width: size, height: size, display: "inline-flex", alignItems: "center", justifyContent: "center", bg: "canvas.subtle", borderRadius: borderRadius, border: "1px solid", borderColor: "border.default", sx: ringSx, "aria-label": alt || (kind === 'team' ? 'Team' : 'Organization'), children: _jsx(Icon, { size: iconSize }) }));
33
+ return (_jsx(EntityAvatar, { className: className, size: size, shape: shape, ring: ring, ground: "canvas", alt: alt || (kind === 'team' ? 'Team' : 'Organization'), children: _jsx(Icon, { size: getFallbackIconSize(size) }) }));
40
34
  }
41
35
  export default PrincipalAvatar;
@@ -14,14 +14,11 @@ export type PrincipalDetailsOverlayProps = {
14
14
  email?: string;
15
15
  origin?: string;
16
16
  avatarUrl?: string;
17
- avatarIcon?: string;
18
17
  /**
19
- * A literal emoji, for a face this platform draws rather than a person's
20
- * own choice of picture or icon — an agent, checked before `avatarUrl`/
21
- * `avatarIcon` since the emoji is its real identity here, not a
22
- * placeholder standing in for one.
18
+ * The chosen avatar, as it is kept: `emoji:…` (an agent's face is its
19
+ * emoji) or `@datalayer/icons-react:…`.
23
20
  */
24
- avatarEmoji?: string;
21
+ avatar?: string;
25
22
  /** Team-specific: parent organization display name. */
26
23
  organizationName?: string;
27
24
  /** Team-specific: number of members. */
@@ -59,7 +56,7 @@ export declare function buildPrincipalProfilePath({ kind, uid, handle, accountHa
59
56
  * anchored on an avatar ({@link PrincipalHoverCard}) both render this, so
60
57
  * the two never drift.
61
58
  */
62
- export declare function PrincipalDetailsCard({ kind, uid, displayName, name, description, handle, accountHandle, firstName, lastName, email, origin, avatarUrl, avatarIcon, avatarEmoji, organizationName, memberCount, isPublic, isAdmin, onNavigate, }: PrincipalDetailsOverlayProps): JSX.Element;
59
+ export declare function PrincipalDetailsCard({ kind, uid, displayName, name, description, handle, accountHandle, firstName, lastName, email, origin, avatarUrl, avatar, organizationName, memberCount, isPublic, isAdmin, onNavigate, }: PrincipalDetailsOverlayProps): JSX.Element;
63
60
  /**
64
61
  * A principal's details, shown from a click on their name.
65
62
  *
@@ -89,7 +89,7 @@ export function buildPrincipalProfilePath({ kind, uid, handle, accountHandle, is
89
89
  * anchored on an avatar ({@link PrincipalHoverCard}) both render this, so
90
90
  * the two never drift.
91
91
  */
92
- export function PrincipalDetailsCard({ kind, uid, displayName, name, description, handle, accountHandle, firstName, lastName, email, origin, avatarUrl, avatarIcon, avatarEmoji, organizationName, memberCount, isPublic, isAdmin = false, onNavigate, }) {
92
+ export function PrincipalDetailsCard({ kind, uid, displayName, name, description, handle, accountHandle, firstName, lastName, email, origin, avatarUrl, avatar, organizationName, memberCount, isPublic, isAdmin = false, onNavigate, }) {
93
93
  const navigate = useNavigate();
94
94
  // Where a profile or organization link goes: the router of this subtree,
95
95
  // unless the caller knows of one that can actually serve it — see
@@ -140,7 +140,7 @@ export function PrincipalDetailsCard({ kind, uid, displayName, name, description
140
140
  const organizationPath = teamOrganizationHandle
141
141
  ? `/${teamOrganizationHandle.replace(/^@+/, '')}`
142
142
  : '';
143
- return (_jsxs(Box, { display: "grid", gap: 3, p: 4, minWidth: 420, children: [_jsxs(Box, { display: "flex", alignItems: "center", gap: 2, children: [_jsx(PrincipalAvatar, { kind: kind, avatarUrl: avatarUrl, avatarIcon: avatarIcon, avatarEmoji: avatarEmoji, alt: normalizedDisplayName, size: 40,
143
+ return (_jsxs(Box, { display: "grid", gap: 3, p: 4, minWidth: 420, children: [_jsxs(Box, { display: "flex", alignItems: "center", gap: 2, children: [_jsx(PrincipalAvatar, { kind: kind, avatarUrl: avatarUrl, avatar: avatar, alt: normalizedDisplayName, size: 40,
144
144
  // The card sets the avatar on its own ground, the same colour
145
145
  // the avatar itself is on: without an edge of its own it has
146
146
  // no visible shape at all. Round, and ringed.
@@ -226,7 +226,7 @@ export function PrincipalSwitcherMenu({ maxLabelChars = 48, fullWidth = true, sh
226
226
  if (personalUid && personalHandle) {
227
227
  selectUser(personalUid, personalHandle);
228
228
  }
229
- }, children: [_jsx(ActionList.LeadingVisual, { children: _jsx(UserAvatar, { avatarUrl: user?.avatarUrl, avatarIcon: user?.avatarIcon, size: 16, square: false, ring: true }) }), _jsxs(Box, { as: "span", display: "block", minWidth: 0, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", children: [String(user?.friendlyName ||
229
+ }, children: [_jsx(ActionList.LeadingVisual, { children: _jsx(UserAvatar, { avatarUrl: user?.avatarUrl, avatar: user?.avatarIcon, size: 16, square: false, ring: true }) }), _jsxs(Box, { as: "span", display: "block", minWidth: 0, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", children: [String(user?.friendlyName ||
230
230
  user?.displayName ||
231
231
  personalHandle ||
232
232
  'Me').trim(), _jsxs(Box, { as: "span", color: "fg.muted", children: [' ', "\u00B7 ", _jsx(DisplayHandle, { handle: personalHandle || 'me' })] })] }), isPlatformAdmin ? (_jsx(ActionList.TrailingVisual, { children: _jsx(AdminLabel, {}) })) : null] })] }), _jsxs(ActionList.Group, { children: [_jsx(ActionList.GroupHeading, { children: "Organizations" }), organizations.length === 0 ? (_jsx(ActionList.Item, { disabled: true, children: "No organizations" })) : (organizations.map((organization) => {
@@ -0,0 +1,22 @@
1
+ import type { IIAMProviderName } from '../models/IAMProvidersSpecs';
2
+ /** What `requestDatalayer` resolves: on failure, `success: false` and IAM's sentence. */
3
+ type UnlinkResponse = {
4
+ success?: boolean;
5
+ message?: string;
6
+ };
7
+ type Request = (request: {
8
+ url: string;
9
+ method: 'DELETE';
10
+ notifyOnError: false;
11
+ }) => Promise<UnlinkResponse>;
12
+ /**
13
+ * Unlink a provider from the signed-in account
14
+ * (`DELETE /api/iam/v1/me/providers/{provider}`).
15
+ *
16
+ * IAM answers 409 when the provider is the account's only way to sign in and
17
+ * 404 when it is not linked; either way its sentence is thrown as the error's
18
+ * message for the page to show, and nothing is toasted. On success `refresh`
19
+ * reads the user again before this resolves.
20
+ */
21
+ export declare function unlinkIAMProvider(request: Request, iamUrl: string, provider: IIAMProviderName, refresh: () => Promise<void>): Promise<void>;
22
+ export {};
@@ -0,0 +1,24 @@
1
+ /*
2
+ * Copyright (c) 2023-2025 Datalayer, Inc.
3
+ * Distributed under the terms of the Modified BSD License.
4
+ */
5
+ /**
6
+ * Unlink a provider from the signed-in account
7
+ * (`DELETE /api/iam/v1/me/providers/{provider}`).
8
+ *
9
+ * IAM answers 409 when the provider is the account's only way to sign in and
10
+ * 404 when it is not linked; either way its sentence is thrown as the error's
11
+ * message for the page to show, and nothing is toasted. On success `refresh`
12
+ * reads the user again before this resolves.
13
+ */
14
+ export async function unlinkIAMProvider(request, iamUrl, provider, refresh) {
15
+ const response = await request({
16
+ url: `${iamUrl}/api/iam/v1/me/providers/${encodeURIComponent(provider)}`,
17
+ method: 'DELETE',
18
+ notifyOnError: false,
19
+ });
20
+ if (!response?.success) {
21
+ throw new Error(response?.message || `The ${provider} account could not be unlinked.`);
22
+ }
23
+ await refresh();
24
+ }
@@ -33,7 +33,7 @@
33
33
  * ```
34
34
  */
35
35
  import { QueryClient, UseQueryOptions, UseMutationOptions } from '@tanstack/react-query';
36
- import { IAnyOrganization, IContact, IIAMToken, IOrganization, ISchool, ISecret, IStudent, ITeam, IUser, IUserOnboarding, IUserSettings } from '../models';
36
+ import { IAnyOrganization, IContact, IIAMProviderName, IIAMToken, IOrganization, ISchool, ISecret, IStudent, ITeam, IUser, IUserOnboarding, IUserSettings } from '../models';
37
37
  type IAnySpace = any;
38
38
  export type CacheProps = {
39
39
  loginRoute?: string;
@@ -486,6 +486,7 @@ export declare const useCache: ({ loginRoute }?: CacheProps) => {
486
486
  useGetGitHubProfile: () => import("@tanstack/react-query").UseMutationResult<any, Error, string, unknown>;
487
487
  useGetGoogleProfile: () => import("@tanstack/react-query").UseMutationResult<any, Error, string, unknown>;
488
488
  useGetLinkedinProfile: () => import("@tanstack/react-query").UseMutationResult<any, Error, void, unknown>;
489
+ useUnlinkIAMProvider: () => import("@tanstack/react-query").UseMutationResult<void, Error, IIAMProviderName, unknown>;
489
490
  usePostLinkedinShare: () => import("@tanstack/react-query").UseMutationResult<any, Error, {
490
491
  postText: string;
491
492
  }, unknown>;
@@ -1015,6 +1016,12 @@ export declare const useCache: ({ loginRoute }?: CacheProps) => {
1015
1016
  useSecrets: (options?: {
1016
1017
  principalUid?: string;
1017
1018
  principalKind?: "personal" | "organization" | "team";
1019
+ } & {
1020
+ /**
1021
+ * `false` reads nothing: a page a visitor opens signed out, whose 401
1022
+ * would otherwise send them to the sign-in.
1023
+ */
1024
+ enabled?: boolean;
1018
1025
  }) => import("@tanstack/react-query").UseQueryResult<any, Error>;
1019
1026
  useCreateSecret: (options?: {
1020
1027
  principalUid?: string;
@@ -40,6 +40,7 @@ import { useQuery, useQueries, useMutation, useQueryClient, } from '@tanstack/re
40
40
  import { BOOTSTRAP_USER_ONBOARDING, asContact, asInvite, asOrganization, asSecret, asTeam, asToken, asUser, } from '../models/index.js';
41
41
  import { asSpace } from './cacheConverters.js';
42
42
  import { libraryAppFieldsOf } from './libraryApp.js';
43
+ import { unlinkIAMProvider } from './unlinkIAMProvider.js';
43
44
  import { useCoreStore, useIAMStore, profileStore } from '../state/index.js';
44
45
  import { asDisplayName, namesAsInitials, asArray } from '../utils/index.js';
45
46
  import { useDatalayer } from './useDatalayer.js';
@@ -1270,7 +1271,7 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
1270
1271
  tags: Array.isArray(raw_evalset.tags_ss) ? raw_evalset.tags_ss : [],
1271
1272
  /*
1272
1273
  * What the runs of a benchmark say about it, projected onto its
1273
- * library card whenever a run moves (BENCHMARK.md, B2-10 and B1-09).
1274
+ * library card whenever a run moves (B2-10 and B1-09).
1274
1275
  *
1275
1276
  * These were dropped here, so a card in the Library or on the home
1276
1277
  * page could show a name and a description and nothing that would
@@ -1296,7 +1297,7 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
1296
1297
  };
1297
1298
  };
1298
1299
  /**
1299
- * A published environment version as a library artifact (PLAN_ENV E2-16).
1300
+ * A published environment version as a library artifact (E2-16).
1300
1301
  *
1301
1302
  * Keyed on the version uid, so one published version is one card, and read
1302
1303
  * from the publication's immutable snapshot: its name, title, README (the
@@ -2853,6 +2854,7 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
2853
2854
  },
2854
2855
  ...DEFAULT_QUERY_OPTIONS,
2855
2856
  refetchOnMount: true, // Override to refetch when stale after invalidation
2857
+ enabled: options?.enabled ?? true,
2856
2858
  });
2857
2859
  };
2858
2860
  /**
@@ -5740,14 +5742,17 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
5740
5742
  });
5741
5743
  };
5742
5744
  /**
5743
- * Confirm password change with token
5745
+ * Confirm password change with the token the reset email carried. The
5746
+ * handle and token travel in the body, never in the URL: paths are written
5747
+ * to access logs.
5744
5748
  */
5745
5749
  const useConfirmPasswordWithToken = () => {
5746
5750
  return useMutation({
5747
5751
  mutationFn: async ({ userHandle, token, }) => {
5748
5752
  return requestDatalayer({
5749
- url: `${configuration.iamUrl}/api/iam/v1/password/confirm/users/${userHandle}/tokens/${token}`,
5750
- method: 'PUT',
5753
+ url: `${configuration.iamUrl}/api/iam/v1/password-reset/confirm`,
5754
+ method: 'POST',
5755
+ body: { handle: userHandle, token },
5751
5756
  });
5752
5757
  },
5753
5758
  });
@@ -7273,13 +7278,38 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
7273
7278
  },
7274
7279
  });
7275
7280
  };
7276
- /** Get the server-connected LinkedIn profile. */
7281
+ /**
7282
+ * Unlink a provider (github, google, linkedin, x) from the signed-in
7283
+ * account; IAM deletes the link and the tokens it holds for it.
7284
+ *
7285
+ * IAM's refusal (409: the provider is the account's only way to sign in)
7286
+ * is the error's message, for the page to show: nothing is toasted. On
7287
+ * success the signed-in user is read again, so the settings pages and the
7288
+ * header's provider icons follow its linked providers.
7289
+ */
7290
+ const useUnlinkIAMProvider = () => {
7291
+ const iamStore = useIAMStore();
7292
+ return useMutation({
7293
+ mutationFn: async (provider) => unlinkIAMProvider(requestDatalayer, configuration.iamUrl, provider, async () => {
7294
+ queryClient.invalidateQueries({ queryKey: queryKeys.auth.me() });
7295
+ await iamStore.refreshUser();
7296
+ }),
7297
+ });
7298
+ };
7299
+ /**
7300
+ * Get the server-connected LinkedIn profile.
7301
+ *
7302
+ * IAM answers 404 when no LinkedIn posting account is connected: a state
7303
+ * the caller shows (a link button), never an error toast. The resolved
7304
+ * response then carries `success: false`, `status` and `message`.
7305
+ */
7277
7306
  const useGetLinkedinProfile = () => {
7278
7307
  return useMutation({
7279
7308
  mutationFn: async () => {
7280
7309
  return requestDatalayer({
7281
7310
  url: `${configuration.iamUrl}/api/iam/v1/social/linkedin/profile`,
7282
7311
  method: 'GET',
7312
+ notifyOnError: false,
7283
7313
  });
7284
7314
  },
7285
7315
  });
@@ -8426,6 +8456,7 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
8426
8456
  useGetGitHubProfile,
8427
8457
  useGetGoogleProfile,
8428
8458
  useGetLinkedinProfile,
8459
+ useUnlinkIAMProvider,
8429
8460
  usePostLinkedinShare,
8430
8461
  usePostLinkedinShareWithUpload,
8431
8462
  useGetBlueskyConnection,
@@ -25,6 +25,7 @@
25
25
  import { useCallback, useMemo } from 'react';
26
26
  import { useLayoutStore } from '../state/index.js';
27
27
  import { navigationStore } from '../state/substates/NavigationState.js';
28
+ import { iamStore } from '../state/substates/IAMState.js';
28
29
  import { createNativeNavigate } from '../navigation/adapters/native.js';
29
30
  // Import React Router hooks from our wrapper
30
31
  import { useNavigateRR } from '../navigation/adapters/react-router.js';
@@ -133,8 +134,13 @@ export const useNavigate = () => {
133
134
  document.body.scrollTop = 0;
134
135
  }
135
136
  // Where the reader is now, for the shell they are in. A new tab and a
136
- // step back through history are both already past by here.
137
- navigationStore.getState().rememberRoute(to);
137
+ // step back through history are both already past by here. Only for
138
+ // somebody signed in: the shells are theirs, and a page a visitor read
139
+ // signed out (`/features`) would otherwise come back as the Platform's
140
+ // last page after they sign in.
141
+ if (iamStore.getState().user) {
142
+ navigationStore.getState().rememberRoute(to);
143
+ }
138
144
  if (rrNavigate) {
139
145
  rrNavigate(to, { replace: options.replace, state: options.state });
140
146
  return;
@@ -1,6 +1,15 @@
1
1
  import { JSONObject } from '@lumino/coreutils';
2
2
  import { IIAMProviderName } from './IAMProvidersSpecs';
3
+ /**
4
+ * One `iam_providers` child of the user IAM returns (whoami, login): its
5
+ * Solr fields `iam_provider_name_s`, `linked_account_s`,
6
+ * `linked_account_url_s`, `linked_account_id_s`.
7
+ */
3
8
  export declare function asIAMProviderLinked(iamProvider: any): IIAMProviderLinked;
9
+ /** Whether the user has linked this provider in IAM. */
10
+ export declare function hasLinkedIAMProvider(user: {
11
+ iamProviders?: Array<IIAMProviderLinked>;
12
+ } | null | undefined, name: IIAMProviderName): boolean;
4
13
  export type IIAMProviderLinked = {
5
14
  iamProviderName: IIAMProviderName;
6
15
  linkedAccount: JSONObject;