@datalayer/core 1.0.25 → 1.1.43

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 (359) hide show
  1. package/README.md +11 -134
  2. package/lib/api/index.d.ts +0 -9
  3. package/lib/api/index.js +0 -9
  4. package/lib/cli-login.js +167 -0
  5. package/lib/client/auth/AuthenticationManager.d.ts +3 -3
  6. package/lib/client/auth/AuthenticationManager.js +13 -13
  7. package/lib/client/auth/strategies.d.ts +2 -2
  8. package/lib/client/auth/strategies.js +6 -6
  9. package/lib/client/base.d.ts +9 -12
  10. package/lib/client/base.js +17 -19
  11. package/lib/client/index.d.ts +10 -98
  12. package/lib/client/index.js +12 -21
  13. package/lib/client/mixins/IAMMixin.js +26 -26
  14. package/lib/components/auth/Login.js +2 -2
  15. package/lib/components/avatars/UserAvatar.d.ts +16 -0
  16. package/lib/components/avatars/UserAvatar.js +47 -0
  17. package/lib/components/avatars/index.d.ts +1 -0
  18. package/lib/components/avatars/index.js +1 -0
  19. package/lib/components/billing/BillableAccountSelect.js +4 -0
  20. package/lib/components/billing/index.js +4 -0
  21. package/lib/components/context/index.d.ts +0 -1
  22. package/lib/components/context/index.js +0 -1
  23. package/lib/components/display/DisplayHandle.d.ts +12 -0
  24. package/lib/components/display/DisplayHandle.js +11 -0
  25. package/lib/components/display/LiveRelativeTime.js +4 -0
  26. package/lib/components/display/index.d.ts +1 -0
  27. package/lib/components/display/index.js +1 -0
  28. package/lib/components/flashes/index.d.ts +0 -1
  29. package/lib/components/flashes/index.js +0 -1
  30. package/lib/components/index.d.ts +0 -1
  31. package/lib/components/index.js +0 -1
  32. package/lib/components/principal/Principal.d.ts +7 -0
  33. package/lib/components/principal/Principal.js +153 -16
  34. package/lib/components/principal/PrincipalAvatar.d.ts +1 -1
  35. package/lib/components/principal/PrincipalAvatar.js +4 -28
  36. package/lib/components/principal/PrincipalBadge.js +4 -4
  37. package/lib/components/principal/PrincipalDetailsOverlay.d.ts +4 -2
  38. package/lib/components/principal/PrincipalDetailsOverlay.js +27 -7
  39. package/lib/components/principal/PrincipalSwitcherMenu.js +79 -42
  40. package/lib/components/progress/index.d.ts +0 -1
  41. package/lib/components/progress/index.js +0 -1
  42. package/lib/components/sharing/ShareAccessComponent.d.ts +3 -2
  43. package/lib/components/sharing/ShareAccessComponent.js +194 -147
  44. package/lib/components/sharing/SharingEditor.js +4 -0
  45. package/lib/components/snippets/index.d.ts +1 -1
  46. package/lib/components/snippets/index.js +1 -1
  47. package/lib/components/students/StudentResultCircle.d.ts +21 -0
  48. package/lib/components/students/StudentResultCircle.js +59 -0
  49. package/lib/components/students/index.d.ts +1 -1
  50. package/lib/components/students/index.js +1 -1
  51. package/lib/components/time/LiveRelativeTime.js +4 -0
  52. package/lib/components/time/index.js +4 -0
  53. package/lib/components/timeline/index.js +4 -0
  54. package/lib/components/tokens/index.d.ts +1 -1
  55. package/lib/components/tokens/index.js +1 -1
  56. package/lib/components/toolbars/index.d.ts +1 -3
  57. package/lib/components/toolbars/index.js +1 -3
  58. package/lib/config/Configuration.d.ts +13 -13
  59. package/lib/config/Configuration.js +5 -5
  60. package/lib/hooks/cacheConverters.d.ts +21 -0
  61. package/lib/hooks/cacheConverters.js +110 -0
  62. package/lib/hooks/index.d.ts +0 -2
  63. package/lib/hooks/index.js +0 -2
  64. package/lib/hooks/useCache.d.ts +64 -35
  65. package/lib/hooks/useCache.js +661 -306
  66. package/lib/hooks/useDatalayer.js +3 -1
  67. package/lib/hooks/useIAM.js +2 -4
  68. package/lib/hooks/useLocationHandles.d.ts +3 -1
  69. package/lib/hooks/useLocationHandles.js +2 -1
  70. package/lib/hooks/usePrincipalStore.d.ts +2 -2
  71. package/lib/hooks/usePrincipalStore.js +97 -14
  72. package/lib/hooks/useSelectedPrincipal.js +1 -1
  73. package/lib/hooks/useUser.js +19 -2
  74. package/lib/index.d.ts +1 -6
  75. package/lib/index.js +1 -13
  76. package/lib/main.js +3 -3
  77. package/lib/mocks/index.js +1 -1
  78. package/lib/mocks/models/index.d.ts +0 -1
  79. package/lib/mocks/models/index.js +0 -1
  80. package/lib/models/Datasource.d.ts +2 -2
  81. package/lib/models/IAM.d.ts +1 -1
  82. package/lib/models/School.d.ts +12 -3
  83. package/lib/models/Secret.d.ts +2 -2
  84. package/lib/models/Student.d.ts +13 -3
  85. package/lib/models/Usage.d.ts +11 -1
  86. package/lib/models/UserDTO.d.ts +2 -2
  87. package/lib/models/UserSettings.d.ts +1 -1
  88. package/lib/models/UserSettings.js +1 -1
  89. package/lib/models/index.d.ts +0 -30
  90. package/lib/models/index.js +0 -30
  91. package/lib/otel/client/OtelClient.d.ts +2 -2
  92. package/lib/otel/client/OtelClient.js +2 -2
  93. package/lib/otel/hooks/index.js +5 -5
  94. package/lib/otel/types.d.ts +1 -1
  95. package/lib/otel/views/OtelLive.js +1 -1
  96. package/lib/routes/index.d.ts +1 -0
  97. package/lib/routes/index.js +1 -0
  98. package/lib/routes/publicPaths.d.ts +23 -0
  99. package/lib/routes/publicPaths.js +74 -0
  100. package/lib/state/State.d.ts +0 -12
  101. package/lib/state/State.js +0 -12
  102. package/lib/state/substates/CoreState.js +13 -13
  103. package/lib/state/substates/IAMState.d.ts +1 -1
  104. package/lib/state/substates/IAMState.js +14 -14
  105. package/lib/state/substates/LayoutState.d.ts +12 -1
  106. package/lib/state/substates/SurveysState.d.ts +1 -1
  107. package/lib/state/substates/SurveysState.js +10 -10
  108. package/lib/state/substates/index.d.ts +0 -7
  109. package/lib/state/substates/index.js +0 -7
  110. package/lib/utils/Handles.js +3 -1
  111. package/lib/utils/index.d.ts +0 -1
  112. package/lib/utils/index.js +0 -1
  113. package/lib/views/api-keys/APIKeys.js +12 -3
  114. package/lib/views/datasources/DatasourceDetail.d.ts +1 -1
  115. package/lib/views/datasources/DatasourceNew.d.ts +1 -1
  116. package/lib/views/datasources/Datasources.d.ts +1 -1
  117. package/lib/views/datasources/Datasources.js +12 -3
  118. package/lib/views/iam/SignInSimple.d.ts +37 -1
  119. package/lib/views/iam/SignInSimple.js +111 -13
  120. package/package.json +5 -3
  121. package/lib/App.css +0 -47
  122. package/lib/App.d.ts +0 -3
  123. package/lib/App.js +0 -21
  124. package/lib/api/runtimes/checkpoints.d.ts +0 -122
  125. package/lib/api/runtimes/checkpoints.js +0 -118
  126. package/lib/api/runtimes/environments.d.ts +0 -9
  127. package/lib/api/runtimes/environments.js +0 -29
  128. package/lib/api/runtimes/healthz.d.ts +0 -15
  129. package/lib/api/runtimes/healthz.js +0 -44
  130. package/lib/api/runtimes/index.d.ts +0 -12
  131. package/lib/api/runtimes/index.js +0 -16
  132. package/lib/api/runtimes/runtimes.d.ts +0 -138
  133. package/lib/api/runtimes/runtimes.js +0 -230
  134. package/lib/api/runtimes/snapshots.d.ts +0 -38
  135. package/lib/api/runtimes/snapshots.js +0 -82
  136. package/lib/api/scheduler/index.d.ts +0 -6
  137. package/lib/api/scheduler/index.js +0 -10
  138. package/lib/api/scheduler/schedules.d.ts +0 -85
  139. package/lib/api/scheduler/schedules.js +0 -78
  140. package/lib/api/spacer/documents.d.ts +0 -12
  141. package/lib/api/spacer/documents.js +0 -43
  142. package/lib/api/spacer/healthz.d.ts +0 -15
  143. package/lib/api/spacer/healthz.js +0 -44
  144. package/lib/api/spacer/index.d.ts +0 -15
  145. package/lib/api/spacer/index.js +0 -19
  146. package/lib/api/spacer/items.d.ts +0 -25
  147. package/lib/api/spacer/items.js +0 -55
  148. package/lib/api/spacer/lexicals.d.ts +0 -34
  149. package/lib/api/spacer/lexicals.js +0 -89
  150. package/lib/api/spacer/notebooks.d.ts +0 -34
  151. package/lib/api/spacer/notebooks.js +0 -89
  152. package/lib/api/spacer/spaces.d.ts +0 -84
  153. package/lib/api/spacer/spaces.js +0 -170
  154. package/lib/api/spacer/users.d.ts +0 -9
  155. package/lib/api/spacer/users.js +0 -29
  156. package/lib/client/mixins/RuntimesMixin.d.ts +0 -93
  157. package/lib/client/mixins/RuntimesMixin.js +0 -229
  158. package/lib/client/mixins/SpacerMixin.d.ts +0 -210
  159. package/lib/client/mixins/SpacerMixin.js +0 -615
  160. package/lib/client/utils/spacerUtils.d.ts +0 -18
  161. package/lib/client/utils/spacerUtils.js +0 -32
  162. package/lib/collaboration/DatalayerCollaboration.d.ts +0 -13
  163. package/lib/collaboration/DatalayerCollaboration.js +0 -28
  164. package/lib/collaboration/DatalayerCollaborationProvider.d.ts +0 -60
  165. package/lib/collaboration/DatalayerCollaborationProvider.js +0 -163
  166. package/lib/collaboration/index.d.ts +0 -2
  167. package/lib/collaboration/index.js +0 -6
  168. package/lib/components/context/SpaceSelect.d.ts +0 -2
  169. package/lib/components/context/SpaceSelect.js +0 -42
  170. package/lib/components/flashes/FlashDisclaimer.d.ts +0 -2
  171. package/lib/components/flashes/FlashDisclaimer.js +0 -17
  172. package/lib/components/icons/ArtifactIcon.d.ts +0 -10
  173. package/lib/components/icons/ArtifactIcon.js +0 -58
  174. package/lib/components/icons/index.d.ts +0 -1
  175. package/lib/components/icons/index.js +0 -5
  176. package/lib/components/nbgrader/NbGradesDetails.d.ts +0 -5
  177. package/lib/components/nbgrader/NbGradesDetails.js +0 -14
  178. package/lib/components/nbgrader/index.d.ts +0 -1
  179. package/lib/components/nbgrader/index.js +0 -5
  180. package/lib/components/progress/CreditsIndicator.d.ts +0 -33
  181. package/lib/components/progress/CreditsIndicator.js +0 -23
  182. package/lib/components/runtimes/RuntimeCellVariables.d.ts +0 -39
  183. package/lib/components/runtimes/RuntimeCellVariables.js +0 -172
  184. package/lib/components/runtimes/RuntimeCellVariablesDialog.d.ts +0 -37
  185. package/lib/components/runtimes/RuntimeCellVariablesDialog.js +0 -101
  186. package/lib/components/runtimes/RuntimeLauncherDialog.d.ts +0 -57
  187. package/lib/components/runtimes/RuntimeLauncherDialog.js +0 -275
  188. package/lib/components/runtimes/RuntimePickerBase.d.ts +0 -69
  189. package/lib/components/runtimes/RuntimePickerBase.js +0 -122
  190. package/lib/components/runtimes/RuntimePickerCell.d.ts +0 -34
  191. package/lib/components/runtimes/RuntimePickerCell.js +0 -104
  192. package/lib/components/runtimes/RuntimePickerNotebook.d.ts +0 -42
  193. package/lib/components/runtimes/RuntimePickerNotebook.js +0 -232
  194. package/lib/components/runtimes/RuntimeReservationControl.d.ts +0 -47
  195. package/lib/components/runtimes/RuntimeReservationControl.js +0 -42
  196. package/lib/components/runtimes/RuntimeSimplePicker.d.ts +0 -38
  197. package/lib/components/runtimes/RuntimeSimplePicker.js +0 -155
  198. package/lib/components/runtimes/RuntimeTransfer.d.ts +0 -16
  199. package/lib/components/runtimes/RuntimeTransfer.js +0 -5
  200. package/lib/components/runtimes/RuntimeUtils.d.ts +0 -17
  201. package/lib/components/runtimes/RuntimeUtils.js +0 -178
  202. package/lib/components/runtimes/RuntimeVariables.d.ts +0 -38
  203. package/lib/components/runtimes/RuntimeVariables.js +0 -75
  204. package/lib/components/runtimes/index.d.ts +0 -11
  205. package/lib/components/runtimes/index.js +0 -15
  206. package/lib/components/scheduler/ScheduleMenu.d.ts +0 -54
  207. package/lib/components/scheduler/ScheduleMenu.js +0 -154
  208. package/lib/components/scheduler/index.d.ts +0 -1
  209. package/lib/components/scheduler/index.js +0 -5
  210. package/lib/components/snapshots/SandboxSnapshotMenu.d.ts +0 -33
  211. package/lib/components/snapshots/SandboxSnapshotMenu.js +0 -169
  212. package/lib/components/snapshots/index.d.ts +0 -1
  213. package/lib/components/snapshots/index.js +0 -5
  214. package/lib/components/snippets/SnippetDialog.d.ts +0 -42
  215. package/lib/components/snippets/SnippetDialog.js +0 -42
  216. package/lib/components/students/StudentItemStatus.d.ts +0 -7
  217. package/lib/components/students/StudentItemStatus.js +0 -66
  218. package/lib/components/tokens/SpaceVariantToken.d.ts +0 -5
  219. package/lib/components/tokens/SpaceVariantToken.js +0 -23
  220. package/lib/components/toolbars/AssignmentEditorToolbar.d.ts +0 -4
  221. package/lib/components/toolbars/AssignmentEditorToolbar.js +0 -18
  222. package/lib/components/toolbars/DocumentEditorToolbar.d.ts +0 -2
  223. package/lib/components/toolbars/DocumentEditorToolbar.js +0 -12
  224. package/lib/components/toolbars/NotebookEditorToolbar.d.ts +0 -29
  225. package/lib/components/toolbars/NotebookEditorToolbar.js +0 -30
  226. package/lib/examples/CellExample.d.ts +0 -6
  227. package/lib/examples/CellExample.js +0 -20
  228. package/lib/examples/NotebookExample.d.ts +0 -6
  229. package/lib/examples/NotebookExample.js +0 -19
  230. package/lib/examples/lexical-theme.css +0 -436
  231. package/lib/examples/notebooks/IPyWidgetsExample.ipynb.json +0 -101
  232. package/lib/examples/notebooks/IPyWidgetsExampleWithState.ipynb.json +0 -112
  233. package/lib/examples/notebooks/Lite.ipynb.json +0 -128
  234. package/lib/examples/notebooks/Matplotlib.ipynb.json +0 -137
  235. package/lib/examples/notebooks/NotebookExample1.ipynb.json +0 -126
  236. package/lib/examples/notebooks/NotebookExample2.ipynb.json +0 -48
  237. package/lib/examples/notebooks/NotebookOutputs.ipynb.json +0 -49
  238. package/lib/examples/notebooks/NotebookToCExample.ipynb.json +0 -102
  239. package/lib/examples/notebooks/PyGWalker.ipynb.json +0 -55
  240. package/lib/hooks/useCellOutputshot.d.ts +0 -4
  241. package/lib/hooks/useCellOutputshot.js +0 -25
  242. package/lib/hooks/useProjects.d.ts +0 -628
  243. package/lib/hooks/useProjects.js +0 -190
  244. package/lib/hooks/useRuntimes.d.ts +0 -5
  245. package/lib/hooks/useRuntimes.js +0 -34
  246. package/lib/mocks/jupyter/index.js +0 -5
  247. package/lib/mocks/models/CourseMock.d.ts +0 -6
  248. package/lib/mocks/models/CourseMock.js +0 -29
  249. package/lib/mocks/models/SpaceMock.d.ts +0 -6
  250. package/lib/mocks/models/SpaceMock.js +0 -25
  251. package/lib/models/Assignment.d.ts +0 -7
  252. package/lib/models/Assignment.js +0 -5
  253. package/lib/models/Cell.d.ts +0 -8
  254. package/lib/models/Cell.js +0 -5
  255. package/lib/models/CodeBlock.d.ts +0 -10
  256. package/lib/models/CodeBlock.js +0 -5
  257. package/lib/models/CodeSandboxSnapshot.d.ts +0 -92
  258. package/lib/models/CodeSandboxSnapshot.js +0 -13
  259. package/lib/models/CodeSandboxSnapshotDTO.d.ts +0 -173
  260. package/lib/models/CodeSandboxSnapshotDTO.js +0 -139
  261. package/lib/models/CodefeedBlocks.d.ts +0 -7
  262. package/lib/models/CodefeedBlocks.js +0 -5
  263. package/lib/models/Content.d.ts +0 -5
  264. package/lib/models/Content.js +0 -5
  265. package/lib/models/Course.d.ts +0 -17
  266. package/lib/models/Course.js +0 -5
  267. package/lib/models/Dataset.d.ts +0 -16
  268. package/lib/models/Dataset.js +0 -5
  269. package/lib/models/Document.d.ts +0 -8
  270. package/lib/models/Document.js +0 -5
  271. package/lib/models/Environment.d.ts +0 -86
  272. package/lib/models/Environment.js +0 -5
  273. package/lib/models/EnvironmentDTO.d.ts +0 -125
  274. package/lib/models/EnvironmentDTO.js +0 -88
  275. package/lib/models/Exercise.d.ts +0 -16
  276. package/lib/models/Exercise.js +0 -5
  277. package/lib/models/Item.d.ts +0 -20
  278. package/lib/models/Item.js +0 -5
  279. package/lib/models/ItemDTO.d.ts +0 -74
  280. package/lib/models/ItemDTO.js +0 -186
  281. package/lib/models/ItemType.d.ts +0 -2
  282. package/lib/models/ItemType.js +0 -5
  283. package/lib/models/Lesson.d.ts +0 -5
  284. package/lib/models/Lesson.js +0 -5
  285. package/lib/models/LexicalDTO.d.ts +0 -155
  286. package/lib/models/LexicalDTO.js +0 -157
  287. package/lib/models/Library.d.ts +0 -10
  288. package/lib/models/Library.js +0 -5
  289. package/lib/models/Notebook.d.ts +0 -11
  290. package/lib/models/Notebook.js +0 -5
  291. package/lib/models/NotebookDTO.d.ts +0 -96
  292. package/lib/models/NotebookDTO.js +0 -153
  293. package/lib/models/Page.d.ts +0 -41
  294. package/lib/models/Page.js +0 -25
  295. package/lib/models/PageTag.d.ts +0 -19
  296. package/lib/models/PageTag.js +0 -94
  297. package/lib/models/ProjectDTO.d.ts +0 -89
  298. package/lib/models/ProjectDTO.js +0 -131
  299. package/lib/models/Runtime.d.ts +0 -123
  300. package/lib/models/Runtime.js +0 -18
  301. package/lib/models/RuntimeDTO.d.ts +0 -191
  302. package/lib/models/RuntimeDTO.js +0 -204
  303. package/lib/models/Space.d.ts +0 -35
  304. package/lib/models/Space.js +0 -66
  305. package/lib/models/SpaceDTO.d.ts +0 -334
  306. package/lib/models/SpaceDTO.js +0 -244
  307. package/lib/models/SpaceItem.d.ts +0 -10
  308. package/lib/models/SpaceItem.js +0 -5
  309. package/lib/models/SpaceMember.d.ts +0 -6
  310. package/lib/models/SpaceMember.js +0 -5
  311. package/lib/models/StudentItem.d.ts +0 -21
  312. package/lib/models/StudentItem.js +0 -5
  313. package/lib/services/DatalayerServiceManager.d.ts +0 -22
  314. package/lib/services/DatalayerServiceManager.js +0 -69
  315. package/lib/services/index.d.ts +0 -4
  316. package/lib/services/index.js +0 -7
  317. package/lib/services/reconnectToRuntime.d.ts +0 -32
  318. package/lib/services/reconnectToRuntime.js +0 -59
  319. package/lib/state/substates/CellState.d.ts +0 -12
  320. package/lib/state/substates/CellState.js +0 -22
  321. package/lib/state/substates/DocumentState.d.ts +0 -10
  322. package/lib/state/substates/DocumentState.js +0 -14
  323. package/lib/state/substates/GradeState.d.ts +0 -10
  324. package/lib/state/substates/GradeState.js +0 -14
  325. package/lib/state/substates/JupyterLabState.d.ts +0 -16
  326. package/lib/state/substates/JupyterLabState.js +0 -17
  327. package/lib/state/substates/NbformatState.d.ts +0 -13
  328. package/lib/state/substates/NbformatState.js +0 -16
  329. package/lib/state/substates/RuntimesState.d.ts +0 -79
  330. package/lib/state/substates/RuntimesState.js +0 -180
  331. package/lib/state/substates/SpaceState.d.ts +0 -11
  332. package/lib/state/substates/SpaceState.js +0 -16
  333. package/lib/stateful/index.d.ts +0 -2
  334. package/lib/stateful/index.js +0 -6
  335. package/lib/stateful/jupyter/exec/Python.d.ts +0 -5
  336. package/lib/stateful/jupyter/exec/Python.js +0 -100
  337. package/lib/stateful/jupyter/exec/Snippets.d.ts +0 -105
  338. package/lib/stateful/jupyter/exec/Snippets.js +0 -101
  339. package/lib/stateful/jupyter/exec/index.d.ts +0 -2
  340. package/lib/stateful/jupyter/exec/index.js +0 -6
  341. package/lib/stateful/jupyter/index.d.ts +0 -2
  342. package/lib/stateful/jupyter/index.js +0 -6
  343. package/lib/stateful/jupyter/kernelsHandler.d.ts +0 -8
  344. package/lib/stateful/jupyter/kernelsHandler.js +0 -39
  345. package/lib/stateful/runtimes/actions.d.ts +0 -102
  346. package/lib/stateful/runtimes/actions.js +0 -249
  347. package/lib/stateful/runtimes/apis.d.ts +0 -273
  348. package/lib/stateful/runtimes/apis.js +0 -5
  349. package/lib/stateful/runtimes/index.d.ts +0 -5
  350. package/lib/stateful/runtimes/index.js +0 -9
  351. package/lib/stateful/runtimes/settings.d.ts +0 -15
  352. package/lib/stateful/runtimes/settings.js +0 -31
  353. package/lib/stateful/runtimes/snapshots.d.ts +0 -25
  354. package/lib/stateful/runtimes/snapshots.js +0 -150
  355. package/lib/stateful/runtimes/utils.d.ts +0 -7
  356. package/lib/stateful/runtimes/utils.js +0 -14
  357. package/lib/utils/Cells.d.ts +0 -2
  358. package/lib/utils/Cells.js +0 -28
  359. /package/lib/{mocks/jupyter/index.d.ts → cli-login.d.ts} +0 -0
@@ -1,18 +1,139 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /*
3
+ * Copyright (c) 2023-2025 Datalayer, Inc.
4
+ * Distributed under the terms of the Modified BSD License.
5
+ */
6
+ /**
7
+ * Principal – common, tunable display for an actor (user / team /
8
+ * organization). Combines a {@link PrincipalAvatar} with a
9
+ * {@link PrincipalDetailsOverlay} so all spots that need to show
10
+ * "avatar + clickable name with details overlay" can share a single
11
+ * component.
12
+ */
13
+ import * as React from 'react';
2
14
  import { Box } from '@datalayer/primer-addons';
3
15
  import { useCache } from '../../hooks/index.js';
16
+ import { useIAMStore } from '../../state/index.js';
4
17
  import { PrincipalAvatar } from './PrincipalAvatar.js';
5
18
  import { PrincipalDetailsOverlay } from './PrincipalDetailsOverlay.js';
6
- export const Principal = ({ principal, isAdmin = false, avatarSize = 20, gap = 2, square = false, sx, }) => {
7
- const { useUser, useOrganization } = useCache();
8
- const hydratedUserQuery = useUser(principal.kind === 'user' ? String(principal.uid || '') : '');
9
- const hydratedOrgQuery = useOrganization(principal.kind === 'organization' ? String(principal.uid || '') : '');
10
- const hydratedEntity = principal.kind === 'user'
11
- ? hydratedUserQuery.data
19
+ /**
20
+ * Default maximum length applied to the placeholder shown before a principal's
21
+ * real display name has been resolved. The placeholder (derived from the given
22
+ * handle or uid) is truncated to this many characters.
23
+ */
24
+ const DEFAULT_PLACEHOLDER_MAX_LENGTH = 10;
25
+ /**
26
+ * Generic, non-informative display names that callers pass while the real
27
+ * entity is still loading. When the provided display name is one of these we
28
+ * prefer a handle/uid-derived placeholder instead.
29
+ */
30
+ const GENERIC_PLACEHOLDER_DISPLAY_NAMES = new Set([
31
+ 'unknown',
32
+ 'principal',
33
+ 'personal',
34
+ 'team',
35
+ 'organization',
36
+ 'billable account',
37
+ ]);
38
+ /** Truncate a placeholder string to `maxLength`, appending an ellipsis. */
39
+ function truncatePlaceholder(value, maxLength) {
40
+ const trimmed = value.trim();
41
+ if (maxLength <= 0 || trimmed.length <= maxLength) {
42
+ return trimmed;
43
+ }
44
+ return `${trimmed.slice(0, maxLength)}\u2026`;
45
+ }
46
+ export const Principal = ({ principal, isAdmin = false, avatarSize = 20, gap = 2, square = false, placeholderMaxLength = DEFAULT_PLACEHOLDER_MAX_LENGTH, sx, }) => {
47
+ const { useUser, useOrganizationByHandle, useTeam, useUserPublicProfileByHandle, useOrganizationPublicProfileByHandle, } = useCache();
48
+ // When no user is authenticated (anonymous visitor, e.g. public pages), the
49
+ // authenticated `useUser` / `useOrganization` endpoints return 401, which
50
+ // triggers a global logout + redirect to sign-in. For anonymous visitors we
51
+ // resolve the principal through the public-by-handle endpoints instead, which
52
+ // are anonymous-accessible and therefore never redirect.
53
+ const { user: authenticatedUser } = useIAMStore();
54
+ const isAnonymous = !authenticatedUser;
55
+ const normalizedUid = String(principal.uid || '').trim();
56
+ const normalizedProvidedHandle = String(principal.handle || '').trim();
57
+ const normalizedProvidedAccountHandle = String(principal.accountHandle || '').trim();
58
+ const providedHandleIsUidPlaceholder = !!normalizedProvidedHandle &&
59
+ !!normalizedUid &&
60
+ normalizedProvidedHandle === normalizedUid;
61
+ const providedAccountHandleIsUidPlaceholder = !!normalizedProvidedAccountHandle &&
62
+ !!normalizedUid &&
63
+ normalizedProvidedAccountHandle === normalizedUid;
64
+ const principalHandle = String((!providedHandleIsUidPlaceholder ? principal.handle : '') ||
65
+ (!providedAccountHandleIsUidPlaceholder ? principal.accountHandle : '') ||
66
+ '').trim();
67
+ const teamPathParts = principal.kind === 'team' ? principalHandle.split('/') : [];
68
+ const teamOrganizationHandleFromPath = String(teamPathParts[0] || '').trim();
69
+ const teamOrganizationHandleFromAccount = principal.kind === 'team' && !providedAccountHandleIsUidPlaceholder
70
+ ? String(principal.accountHandle || '').trim()
71
+ : '';
72
+ const teamOrganizationHandle = teamOrganizationHandleFromPath || teamOrganizationHandleFromAccount;
73
+ const hydratedUserQuery = useUser(!isAnonymous && principal.kind === 'personal'
74
+ ? String(principal.uid || '')
75
+ : '');
76
+ // Authenticated organizations are fetched by handle via the accounts
77
+ // endpoint (`/api/iam/v1/accounts/{handle}`). The by-uid organization
78
+ // endpoint (`/api/iam/v1/organizations/{uid}`) is not served and 404s, so we
79
+ // never look an organization up by uid here.
80
+ const organizationLookupHandle = String(principalHandle || '')
81
+ .replace(/^@+/, '')
82
+ .trim();
83
+ const hydratedOrgQuery = useOrganizationByHandle(!isAnonymous && principal.kind === 'organization'
84
+ ? organizationLookupHandle
85
+ : '');
86
+ const teamOrganizationQuery = useOrganizationByHandle(principal.kind === 'team' ? teamOrganizationHandle : '');
87
+ const hydratedTeamQuery = useTeam(principal.kind === 'team' ? String(principal.uid || '') : '', principal.kind === 'team'
88
+ ? String(teamOrganizationQuery.data?.id || '')
89
+ : '');
90
+ const hydratedPublicUserQuery = useUserPublicProfileByHandle(isAnonymous && principal.kind === 'personal' ? principalHandle : '');
91
+ const hydratedPublicOrgQuery = useOrganizationPublicProfileByHandle(isAnonymous && principal.kind === 'organization' ? principalHandle : '');
92
+ // Normalise the public (snake_case) profile payloads into the same camelCase
93
+ // shape the authenticated entities expose, so downstream logic is uniform.
94
+ const normalizedPublicUser = React.useMemo(() => {
95
+ const profile = hydratedPublicUserQuery.data;
96
+ if (!profile) {
97
+ return undefined;
98
+ }
99
+ const displayName = String(profile.display_name ||
100
+ [profile.first_name, profile.last_name].filter(Boolean).join(' ') ||
101
+ '').trim();
102
+ return {
103
+ displayName,
104
+ firstName: profile.first_name,
105
+ lastName: profile.last_name,
106
+ handle: profile.handle,
107
+ avatarUrl: profile.avatar_url,
108
+ origin: profile.origin,
109
+ email: profile.email,
110
+ };
111
+ }, [hydratedPublicUserQuery.data]);
112
+ const normalizedPublicOrg = React.useMemo(() => {
113
+ const profile = hydratedPublicOrgQuery.data;
114
+ if (!profile) {
115
+ return undefined;
116
+ }
117
+ return {
118
+ displayName: String(profile.display_name || profile.name || '').trim(),
119
+ name: profile.name,
120
+ description: profile.description || profile.description_t,
121
+ handle: profile.handle,
122
+ avatarUrl: profile.avatar_url,
123
+ };
124
+ }, [hydratedPublicOrgQuery.data]);
125
+ const hydratedEntity = principal.kind === 'personal'
126
+ ? isAnonymous
127
+ ? normalizedPublicUser
128
+ : hydratedUserQuery.data
12
129
  : principal.kind === 'organization'
13
- ? hydratedOrgQuery.data
14
- : undefined;
15
- const hydratedDisplayName = principal.kind === 'user'
130
+ ? isAnonymous
131
+ ? normalizedPublicOrg
132
+ : hydratedOrgQuery.data
133
+ : principal.kind === 'team'
134
+ ? hydratedTeamQuery.data
135
+ : undefined;
136
+ const hydratedDisplayName = principal.kind === 'personal'
16
137
  ? String(hydratedEntity?.displayName ||
17
138
  [
18
139
  hydratedEntity?.firstName,
@@ -27,16 +148,32 @@ export const Principal = ({ principal, isAdmin = false, avatarSize = 20, gap = 2
27
148
  const resolvedPrincipal = {
28
149
  ...principal,
29
150
  displayName: hydratedDisplayName ||
30
- principal.displayName ||
31
- principal.handle ||
32
- principal.uid ||
33
- 'Unknown',
34
- handle: principal.handle ||
151
+ (() => {
152
+ const providedDisplayName = String(principal.displayName || '').trim();
153
+ const providedIsGenericPlaceholder = !providedDisplayName ||
154
+ GENERIC_PLACEHOLDER_DISPLAY_NAMES.has(providedDisplayName.toLowerCase()) ||
155
+ (!!normalizedUid && providedDisplayName === normalizedUid);
156
+ // While the real display name is unresolved, prefer the given handle
157
+ // (or uid) as a placeholder, truncated to a maximum length. Only fall
158
+ // back to the caller-provided display name when it is meaningful.
159
+ const placeholder = truncatePlaceholder(principalHandle || normalizedProvidedHandle || normalizedUid || '', placeholderMaxLength);
160
+ if (providedIsGenericPlaceholder) {
161
+ return placeholder || providedDisplayName || 'Unknown';
162
+ }
163
+ return providedDisplayName || placeholder || 'Unknown';
164
+ })(),
165
+ handle: (!providedHandleIsUidPlaceholder ? principal.handle : undefined) ||
35
166
  String(hydratedEntity?.handle || '').trim() ||
36
167
  undefined,
37
- accountHandle: principal.accountHandle ||
168
+ accountHandle: (!providedAccountHandleIsUidPlaceholder ? principal.accountHandle : undefined) ||
38
169
  String(hydratedEntity?.handle || '').trim() ||
39
170
  undefined,
171
+ name: principal.name ||
172
+ String(hydratedEntity?.name || '').trim() ||
173
+ undefined,
174
+ description: principal.description ||
175
+ String(hydratedEntity?.description || '').trim() ||
176
+ undefined,
40
177
  avatarUrl: principal.avatarUrl || hydratedEntity?.avatarUrl || undefined,
41
178
  firstName: principal.firstName || hydratedEntity?.firstName || undefined,
42
179
  lastName: principal.lastName || hydratedEntity?.lastName || undefined,
@@ -49,6 +186,6 @@ export const Principal = ({ principal, isAdmin = false, avatarSize = 20, gap = 2
49
186
  gap,
50
187
  minWidth: 0,
51
188
  ...sx,
52
- }, children: [_jsx(PrincipalAvatar, { kind: resolvedPrincipal.kind, avatarUrl: resolvedPrincipal.avatarUrl, alt: resolvedPrincipal.displayName, size: avatarSize, square: square }), _jsx(PrincipalDetailsOverlay, { kind: resolvedPrincipal.kind, uid: resolvedPrincipal.uid, displayName: resolvedPrincipal.displayName, handle: resolvedPrincipal.handle, accountHandle: resolvedPrincipal.accountHandle, firstName: resolvedPrincipal.firstName, lastName: resolvedPrincipal.lastName, email: resolvedPrincipal.email, origin: resolvedPrincipal.origin, avatarUrl: resolvedPrincipal.avatarUrl, isAdmin: isAdmin })] }));
189
+ }, children: [_jsx(PrincipalAvatar, { kind: resolvedPrincipal.kind, avatarUrl: resolvedPrincipal.avatarUrl, 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, isAdmin: isAdmin })] }));
53
190
  };
54
191
  export default Principal;
@@ -1,4 +1,4 @@
1
- export type PrincipalAvatarKind = 'user' | 'team' | 'organization';
1
+ export type PrincipalAvatarKind = 'personal' | 'team' | 'organization';
2
2
  export type PrincipalAvatarProps = {
3
3
  kind: PrincipalAvatarKind;
4
4
  avatarUrl?: string;
@@ -3,42 +3,18 @@ import { jsx as _jsx } from "react/jsx-runtime";
3
3
  * Copyright (c) 2023-2025 Datalayer, Inc.
4
4
  * Distributed under the terms of the Modified BSD License.
5
5
  */
6
- import { Box, useColorPalette } from '@datalayer/primer-addons';
6
+ import { Box } from '@datalayer/primer-addons';
7
7
  import { OrganizationIcon, PeopleIcon } from '@primer/octicons-react';
8
- import { AlienIcon } from '@datalayer/icons-react';
9
- import { DLAvatar } from '../avatars/index.js';
10
- function hasRealAvatar(url) {
11
- if (!url) {
12
- return false;
13
- }
14
- if (url.startsWith('https://www.gravatar.com/avatar')) {
15
- return false;
16
- }
17
- return true;
18
- }
8
+ import { UserAvatar } from '../avatars/index.js';
19
9
  function getFallbackIconSize(size) {
20
10
  return Math.max(12, Math.round(size * 0.62));
21
11
  }
22
12
  export function PrincipalAvatar({ kind, avatarUrl, alt, size = 20, square = false, }) {
23
- const palette = useColorPalette();
24
- if (kind === 'user' && hasRealAvatar(avatarUrl)) {
25
- return (_jsx(DLAvatar, { src: avatarUrl, alt: alt || 'User', size: size, square: square }));
13
+ if (kind === 'personal') {
14
+ return (_jsx(UserAvatar, { avatarUrl: avatarUrl, size: size, square: square, iconSize: getFallbackIconSize(size) }));
26
15
  }
27
16
  const iconSize = getFallbackIconSize(size);
28
17
  const borderRadius = square ? 2 : '50%';
29
- if (kind === 'user') {
30
- return (_jsx(Box, { sx: {
31
- width: size,
32
- height: size,
33
- display: 'inline-flex',
34
- alignItems: 'center',
35
- justifyContent: 'center',
36
- bg: 'accent.subtle',
37
- borderRadius,
38
- overflow: 'hidden',
39
- '--datalayer-icon-fg': palette.primary,
40
- }, "aria-label": alt || 'User', children: _jsx(AlienIcon, { size: iconSize }) }));
41
- }
42
18
  const Icon = kind === 'team' ? PeopleIcon : OrganizationIcon;
43
19
  return (_jsx(Box, { sx: {
44
20
  width: size,
@@ -71,7 +71,7 @@ export const PrincipalBadge = ({ principal: providedPrincipal, showPrincipalLabe
71
71
  kind: 'team',
72
72
  uid: selectedPrincipalUid,
73
73
  handle: `${orgHandle}/${teamHandle}`,
74
- accountHandle: teamHandle,
74
+ accountHandle: orgHandle,
75
75
  displayName: `@${formatFriendlyHandle(orgHandle)}/${formatFriendlyHandle(teamHandle)}`,
76
76
  origin: 'Datalayer',
77
77
  };
@@ -85,7 +85,7 @@ export const PrincipalBadge = ({ principal: providedPrincipal, showPrincipalLabe
85
85
  ? `@${formatFriendlyHandle(resolvedHandle)}`
86
86
  : '@me';
87
87
  return {
88
- kind: 'user',
88
+ kind: 'personal',
89
89
  uid: user?.id || selectedPrincipalUid,
90
90
  displayName: fullName || fallbackHandle,
91
91
  handle: resolvedHandle,
@@ -110,7 +110,7 @@ export const PrincipalBadge = ({ principal: providedPrincipal, showPrincipalLabe
110
110
  user?.email,
111
111
  user?.avatarUrl,
112
112
  ]);
113
- const userLookupUid = basePrincipal.kind === 'user' ? String(basePrincipal.uid || '') : '';
113
+ const userLookupUid = basePrincipal.kind === 'personal' ? String(basePrincipal.uid || '') : '';
114
114
  const organizationLookupUid = basePrincipal.kind === 'organization'
115
115
  ? String(basePrincipal.uid || '')
116
116
  : '';
@@ -168,7 +168,7 @@ export const PrincipalBadge = ({ principal: providedPrincipal, showPrincipalLabe
168
168
  fallbackHandle;
169
169
  const origin = normalizeUserOrigin(resolvedUser?.origin || basePrincipal.origin);
170
170
  return {
171
- kind: 'user',
171
+ kind: 'personal',
172
172
  uid: resolvedUser?.uid || basePrincipal.uid,
173
173
  displayName: resolvedDisplayName,
174
174
  handle: resolvedHandle,
@@ -1,8 +1,10 @@
1
- export type PrincipalKind = 'user' | 'team' | 'organization';
1
+ export type PrincipalKind = 'personal' | 'team' | 'organization';
2
2
  export type PrincipalDetailsOverlayProps = {
3
3
  kind: PrincipalKind;
4
4
  uid?: string;
5
5
  displayName: string;
6
+ name?: string;
7
+ description?: string;
6
8
  handle?: string;
7
9
  accountHandle?: string;
8
10
  firstName?: string;
@@ -19,5 +21,5 @@ export declare function buildPrincipalProfilePath({ kind, uid, handle, accountHa
19
21
  accountHandle?: string;
20
22
  isAdmin?: boolean;
21
23
  }): string | null;
22
- export declare function PrincipalDetailsOverlay({ kind, uid, displayName, handle, accountHandle, firstName, lastName, email, origin, avatarUrl, isAdmin, }: PrincipalDetailsOverlayProps): JSX.Element;
24
+ export declare function PrincipalDetailsOverlay({ kind, uid, displayName, name, description, handle, accountHandle, firstName, lastName, email, origin, avatarUrl, isAdmin, }: PrincipalDetailsOverlayProps): JSX.Element;
23
25
  export default PrincipalDetailsOverlay;
@@ -3,7 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
3
3
  * Copyright (c) 2023-2025 Datalayer, Inc.
4
4
  * Distributed under the terms of the Modified BSD License.
5
5
  */
6
- import { ActionMenu, Box, Button, Text } from '@primer/react';
6
+ import { ActionMenu, Box, Button, Label, Text } from '@primer/react';
7
7
  import { useNavigate } from '../../hooks/index.js';
8
8
  import { PrincipalAvatar } from './PrincipalAvatar.js';
9
9
  function normalize(value) {
@@ -16,7 +16,7 @@ export function buildPrincipalProfilePath({ kind, uid, handle, accountHandle, is
16
16
  const safeHandle = normalizedHandle && normalizedHandle !== normalizedUid
17
17
  ? normalizedHandle
18
18
  : '';
19
- if (kind === 'user') {
19
+ if (kind === 'personal') {
20
20
  if (isAdmin && normalizedUid) {
21
21
  return `/admin/management/iam/users/${normalizedUid}`;
22
22
  }
@@ -45,7 +45,7 @@ export function buildPrincipalProfilePath({ kind, uid, handle, accountHandle, is
45
45
  }
46
46
  return null;
47
47
  }
48
- export function PrincipalDetailsOverlay({ kind, uid, displayName, handle, accountHandle, firstName, lastName, email, origin, avatarUrl, isAdmin = false, }) {
48
+ export function PrincipalDetailsOverlay({ kind, uid, displayName, name, description, handle, accountHandle, firstName, lastName, email, origin, avatarUrl, isAdmin = false, }) {
49
49
  const navigate = useNavigate();
50
50
  const normalizedDisplayName = normalize(displayName) ||
51
51
  normalize(handle) ||
@@ -53,10 +53,16 @@ export function PrincipalDetailsOverlay({ kind, uid, displayName, handle, accoun
53
53
  'Principal';
54
54
  const normalizedHandle = normalize(handle);
55
55
  const normalizedUid = normalize(uid);
56
+ const normalizedName = normalize(name);
57
+ const normalizedDescription = normalize(description);
58
+ const normalizedOrigin = normalize(origin) || 'Datalayer';
59
+ const resolvedHandle = normalizedHandle && normalizedHandle !== normalizedUid
60
+ ? normalizedHandle
61
+ : '';
56
62
  const targetPath = buildPrincipalProfilePath({
57
63
  kind,
58
64
  uid: normalizedUid,
59
- handle: normalizedHandle,
65
+ handle: resolvedHandle,
60
66
  accountHandle,
61
67
  isAdmin,
62
68
  });
@@ -72,15 +78,29 @@ export function PrincipalDetailsOverlay({ kind, uid, displayName, handle, accoun
72
78
  ':hover': {
73
79
  textDecoration: 'underline',
74
80
  },
75
- }, children: normalizedDisplayName }) }), _jsx(ActionMenu.Overlay, { width: "large", children: _jsxs(Box, { sx: { display: 'grid', gap: 3, p: 4, minWidth: 420 }, children: [_jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2 }, children: [_jsx(PrincipalAvatar, { kind: kind, avatarUrl: avatarUrl, alt: normalizedDisplayName, size: 40 }), _jsxs(Box, { sx: { display: 'grid', gap: 0.5 }, children: [_jsx(Text, { sx: { fontWeight: 'semibold' }, children: normalizedDisplayName }), normalizedHandle ? (_jsxs(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: ["@", normalizedHandle] })) : null] })] }), _jsxs(Box, { sx: {
81
+ }, children: normalizedDisplayName }) }), _jsx(ActionMenu.Overlay, { width: "large", children: _jsxs(Box, { sx: { display: 'grid', gap: 3, p: 4, minWidth: 420 }, children: [_jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2 }, children: [_jsx(PrincipalAvatar, { kind: kind, avatarUrl: avatarUrl, alt: normalizedDisplayName, size: 40 }), _jsxs(Box, { sx: { display: 'grid', gap: 0.5 }, children: [_jsx(Text, { sx: { fontWeight: 'semibold' }, children: normalizedDisplayName }), resolvedHandle ? (_jsxs(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: ["@", resolvedHandle] })) : null] })] }), _jsxs(Box, { sx: {
76
82
  display: 'grid',
77
83
  gridTemplateColumns: '110px 1fr',
78
84
  gap: 1,
79
85
  alignItems: 'baseline',
80
- }, children: [_jsx(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: "Type" }), _jsx(Text, { sx: { fontSize: 1 }, children: kind }), normalizedHandle ? (_jsxs(_Fragment, { children: [_jsx(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: "Handle" }), _jsxs(Text, { sx: { fontSize: 1 }, children: ["@", normalizedHandle] })] })) : null, normalizedUid ? (_jsxs(_Fragment, { children: [_jsx(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: "UID" }), _jsx(Text, { sx: { fontSize: 1 }, children: normalizedUid })] })) : null, kind === 'user' ? (_jsxs(_Fragment, { children: [_jsx(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: "First name" }), _jsx(Text, { sx: { fontSize: 1 }, children: firstName || 'N/A' }), _jsx(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: "Last name" }), _jsx(Text, { sx: { fontSize: 1 }, children: lastName || 'N/A' }), _jsx(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: "Origin" }), _jsx(Text, { sx: { fontSize: 1 }, children: origin || 'Datalayer' }), email ? (_jsxs(_Fragment, { children: [_jsx(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: "Email" }), _jsx(Text, { sx: { fontSize: 1 }, children: email })] })) : null] })) : (_jsxs(_Fragment, { children: [_jsx(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: "Origin" }), _jsx(Text, { sx: { fontSize: 1 }, children: origin || 'Datalayer' })] }))] }), _jsx(Box, { sx: { display: 'flex', justifyContent: 'flex-end' }, children: _jsx(Button, { size: "small", onClick: () => {
86
+ }, children: [_jsx(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: "Kind" }), _jsx(Text, { sx: { fontSize: 1 }, children: kind }), resolvedHandle ? (_jsxs(_Fragment, { children: [_jsx(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: "Handle" }), _jsxs(Text, { sx: { fontSize: 1 }, children: ["@", resolvedHandle] })] })) : null, normalizedUid ? (_jsxs(_Fragment, { children: [_jsx(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: "UID" }), _jsx(Text, { sx: { fontSize: 1 }, children: normalizedUid })] })) : null, kind === 'personal' ? (_jsxs(_Fragment, { children: [_jsx(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: "First name" }), _jsx(Text, { sx: { fontSize: 1 }, children: firstName || 'N/A' }), _jsx(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: "Last name" }), _jsx(Text, { sx: { fontSize: 1 }, children: lastName || 'N/A' }), email ? (_jsxs(_Fragment, { children: [_jsx(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: "Email" }), _jsx(Text, { sx: { fontSize: 1 }, children: email })] })) : null, _jsx(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: "Origin" }), _jsx(Label, { size: "small", variant: "secondary", title: normalizedOrigin, sx: {
87
+ justifySelf: 'start',
88
+ width: 'fit-content',
89
+ maxWidth: '100%',
90
+ overflow: 'hidden',
91
+ textOverflow: 'ellipsis',
92
+ whiteSpace: 'nowrap',
93
+ }, children: normalizedOrigin })] })) : (_jsxs(_Fragment, { children: [_jsx(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: "Name" }), _jsx(Text, { sx: { fontSize: 1 }, children: normalizedName || normalizedDisplayName }), _jsx(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: "Description" }), _jsx(Text, { sx: { fontSize: 1 }, children: normalizedDescription || 'N/A' }), _jsx(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: "Origin" }), _jsx(Label, { size: "small", variant: "secondary", title: normalizedOrigin, sx: {
94
+ justifySelf: 'start',
95
+ width: 'fit-content',
96
+ maxWidth: '100%',
97
+ overflow: 'hidden',
98
+ textOverflow: 'ellipsis',
99
+ whiteSpace: 'nowrap',
100
+ }, children: normalizedOrigin })] }))] }), _jsx(Box, { sx: { display: 'flex', justifyContent: 'flex-end' }, children: _jsx(Button, { size: "small", onClick: () => {
81
101
  if (targetPath) {
82
102
  navigate(targetPath);
83
103
  }
84
- }, disabled: !targetPath, children: "View profile" }) })] }) })] }));
104
+ }, disabled: !targetPath, children: "View Profile" }) })] }) })] }));
85
105
  }
86
106
  export default PrincipalDetailsOverlay;
@@ -4,8 +4,8 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
4
4
  * Distributed under the terms of the Modified BSD License.
5
5
  */
6
6
  import { useEffect, useMemo, useState } from 'react';
7
- import { ActionList, ActionMenu, Box, Text } from '@primer/react';
8
- import { OrganizationIcon, PeopleIcon, PersonIcon, } from '@primer/octicons-react';
7
+ import { ActionList, ActionMenu, Box, Button, Label, Text } from '@primer/react';
8
+ import { OrganizationIcon, PeopleIcon, PersonIcon, TriangleDownIcon, } from '@primer/octicons-react';
9
9
  import { useCache, useAuthorization } from '../../hooks/index.js';
10
10
  import { useCoreStore } from '../../state/index.js';
11
11
  import { useIAMStore } from '../../state/substates/index.js';
@@ -13,7 +13,7 @@ import { memberships as fetchMemberships } from '../../api/iam/profile.js';
13
13
  import { usePrincipalStore } from '../../hooks/usePrincipalStore.js';
14
14
  import { useBillableAccountStore } from '../../hooks/useBillableAccountStore.js';
15
15
  import { useSelectedPrincipal } from '../../hooks/useSelectedPrincipal.js';
16
- import { formatFriendlyHandle } from '../../utils/Handles.js';
16
+ import { DisplayHandle, displayHandleText } from '../display/DisplayHandle.js';
17
17
  function truncatePrincipalLabel(label, maxChars) {
18
18
  const trimmed = (label || '').trim();
19
19
  if (!trimmed) {
@@ -32,7 +32,7 @@ function truncatePrincipalLabel(label, maxChars) {
32
32
  * - selecting a team principal → billable account = the team's parent org
33
33
  */
34
34
  export function PrincipalSwitcherMenu({ maxLabelChars = 48, fullWidth = true, showClosedBorder = true, }) {
35
- const { user, token, iamRunUrl } = useIAMStore();
35
+ const { user, token, iamUrl } = useIAMStore();
36
36
  const { configuration } = useCoreStore();
37
37
  const { checkIsPlatformAdmin } = useAuthorization();
38
38
  const { useUserOrganizations } = useCache();
@@ -42,7 +42,7 @@ export function PrincipalSwitcherMenu({ maxLabelChars = 48, fullWidth = true, sh
42
42
  const isPlatformAdmin = user ? checkIsPlatformAdmin(user) : false;
43
43
  const [teams, setTeams] = useState([]);
44
44
  const [teamsLoading, setTeamsLoading] = useState(false);
45
- const selectUserPrincipal = usePrincipalStore(state => state.selectUserPrincipal);
45
+ const selectPersonalPrincipal = usePrincipalStore(state => state.selectPersonalPrincipal);
46
46
  const selectOrganizationPrincipal = usePrincipalStore(state => state.selectOrganizationPrincipal);
47
47
  const selectTeamPrincipal = usePrincipalStore(state => state.selectTeamPrincipal);
48
48
  const setBillableAccount = useBillableAccountStore(state => state.setBillableAccount);
@@ -50,7 +50,7 @@ export function PrincipalSwitcherMenu({ maxLabelChars = 48, fullWidth = true, sh
50
50
  const personalUid = user?.uid || user?.id || '';
51
51
  const personalHandle = user?.handle || '';
52
52
  const selectUser = (uid, handle) => {
53
- selectUserPrincipal(uid, handle);
53
+ selectPersonalPrincipal(uid, handle);
54
54
  setBillableAccount({ kind: 'user', uid, handle });
55
55
  };
56
56
  const selectOrganization = (uid, handle) => {
@@ -85,7 +85,7 @@ export function PrincipalSwitcherMenu({ maxLabelChars = 48, fullWidth = true, sh
85
85
  }
86
86
  setTeamsLoading(true);
87
87
  try {
88
- const baseUrl = iamRunUrl || configuration.iamRunUrl;
88
+ const baseUrl = iamUrl || configuration.iamUrl;
89
89
  const response = await fetchMemberships(token, baseUrl);
90
90
  const rawMemberships = Array.isArray(response?.memberships)
91
91
  ? response.memberships
@@ -120,7 +120,7 @@ export function PrincipalSwitcherMenu({ maxLabelChars = 48, fullWidth = true, sh
120
120
  return () => {
121
121
  cancelled = true;
122
122
  };
123
- }, [token, iamRunUrl, configuration.iamRunUrl]);
123
+ }, [token, iamUrl, configuration.iamUrl]);
124
124
  useEffect(() => {
125
125
  if (!personalUid || !personalHandle) {
126
126
  return;
@@ -129,21 +129,13 @@ export function PrincipalSwitcherMenu({ maxLabelChars = 48, fullWidth = true, sh
129
129
  selectUser(personalUid, personalHandle);
130
130
  return;
131
131
  }
132
- if (selectedPrincipalKind === 'organization' && isOrganizationsLoading) {
132
+ // Keep explicit organization/team selections stable across route transitions,
133
+ // even while memberships/organizations are still loading or temporarily
134
+ // unavailable for a given view.
135
+ if (selectedPrincipalKind === 'organization' || selectedPrincipalKind === 'team') {
133
136
  return;
134
137
  }
135
- if (selectedPrincipalKind === 'organization' && !selectedOrganization) {
136
- selectUser(personalUid, personalHandle);
137
- return;
138
- }
139
- if (selectedPrincipalKind === 'team' && teamsLoading) {
140
- return;
141
- }
142
- if (selectedPrincipalKind === 'team' && !selectedTeam) {
143
- selectUser(personalUid, personalHandle);
144
- return;
145
- }
146
- if (selectedPrincipalKind === 'user' &&
138
+ if (selectedPrincipalKind === 'personal' &&
147
139
  selectedPrincipalUid !== personalUid) {
148
140
  selectUser(personalUid, personalHandle);
149
141
  }
@@ -195,10 +187,10 @@ export function PrincipalSwitcherMenu({ maxLabelChars = 48, fullWidth = true, sh
195
187
  ''
196
188
  : '';
197
189
  const selectedPrincipalLabel = selectedPrincipalKind === 'team'
198
- ? `@${formatFriendlyHandle(effectiveOrganizationHandle || personalHandle || 'organization')}/${formatFriendlyHandle(effectiveHandle)}`
199
- : `@${formatFriendlyHandle(effectiveHandle)}`;
190
+ ? `${displayHandleText(effectiveOrganizationHandle || personalHandle || 'organization')}/${displayHandleText(effectiveHandle, { withAt: false })}`
191
+ : displayHandleText(effectiveHandle);
200
192
  const selectedPrincipalLabelClosed = truncatePrincipalLabel(selectedPrincipalLabel, maxLabelChars);
201
- const isCurrentUserPrincipal = selectedPrincipalKind === 'user';
193
+ const isCurrentUserPrincipal = selectedPrincipalKind === 'personal';
202
194
  const selectedItemSx = {
203
195
  bg: 'accent.subtle',
204
196
  borderColor: 'accent.muted',
@@ -206,30 +198,37 @@ export function PrincipalSwitcherMenu({ maxLabelChars = 48, fullWidth = true, sh
206
198
  fontWeight: 'semibold',
207
199
  };
208
200
  const adminBadgeSx = {
209
- ml: 'auto',
210
- px: 1,
211
- py: '2px',
212
- borderRadius: 999,
213
201
  bg: 'attention.subtle',
214
202
  color: 'attention.fg',
215
- fontSize: 0,
216
- fontWeight: 'semibold',
217
- lineHeight: 1.2,
218
203
  textTransform: 'lowercase',
204
+ lineHeight: 1.2,
219
205
  };
220
- return (_jsxs(ActionMenu, { children: [_jsx(ActionMenu.Anchor, { children: _jsxs(Box, { as: "button", type: "button", "aria-label": "Switch principal", sx: {
206
+ return (_jsxs(ActionMenu, { children: [_jsx(ActionMenu.Anchor, { children: showClosedBorder ? (_jsxs(Box, { as: "button", type: "button", "aria-label": "Switch principal", sx: {
221
207
  width: fullWidth ? '100%' : 'auto',
222
- p: 2,
208
+ boxSizing: 'border-box',
209
+ px: 2,
210
+ py: 2,
211
+ fontSize: 0,
223
212
  display: 'flex',
224
213
  alignItems: 'center',
225
214
  justifyContent: 'space-between',
226
215
  gap: 2,
227
- bg: showClosedBorder ? 'canvas.subtle' : 'transparent',
228
- border: showClosedBorder ? '1px solid' : 'none',
229
- borderColor: showClosedBorder ? 'border.default' : 'transparent',
216
+ bg: 'canvas.subtle',
217
+ border: '1px solid',
218
+ borderColor: 'border.default',
230
219
  borderRadius: 2,
231
220
  cursor: 'pointer',
232
221
  textAlign: 'left',
222
+ transition: 'background-color 120ms ease',
223
+ ':hover': {
224
+ bg: 'canvas.subtle',
225
+ },
226
+ ':focus-visible': {
227
+ bg: 'canvas.subtle',
228
+ outline: '2px solid',
229
+ outlineColor: 'accent.emphasis',
230
+ outlineOffset: '2px',
231
+ },
233
232
  }, children: [_jsx(Box, { sx: {
234
233
  display: 'inline-flex',
235
234
  alignItems: 'center',
@@ -239,24 +238,50 @@ export function PrincipalSwitcherMenu({ maxLabelChars = 48, fullWidth = true, sh
239
238
  display: 'block',
240
239
  color: 'accent.fg',
241
240
  fontWeight: 'semibold',
242
- fontSize: 1,
241
+ fontSize: 'inherit',
243
242
  whiteSpace: 'nowrap',
244
243
  overflow: 'hidden',
245
244
  textOverflow: 'ellipsis',
246
245
  maxWidth: '100%',
247
246
  }, children: selectedPrincipalLabelClosed }) }), isPlatformAdmin && isCurrentUserPrincipal ? (_jsx(Box, { sx: {
248
- ml: 'auto',
249
247
  flexShrink: 0,
250
248
  display: 'inline-flex',
251
249
  alignItems: 'center',
252
- }, children: _jsx(Box, { as: "span", sx: adminBadgeSx, children: "admin" }) })) : null] }) }), _jsx(ActionMenu.Overlay, { width: "medium", children: _jsxs(ActionList, { children: [_jsxs(ActionList.Group, { children: [_jsx(ActionList.GroupHeading, { children: "User" }), _jsxs(ActionList.Item, { disabled: isCurrentUserPrincipal, selected: isCurrentUserPrincipal, sx: isCurrentUserPrincipal ? selectedItemSx : undefined, onSelect: () => {
250
+ }, children: _jsx(Label, { variant: "secondary", size: "small", sx: adminBadgeSx, children: "admin" }) })) : null, _jsx(Box, { sx: {
251
+ display: 'inline-flex',
252
+ alignItems: 'center',
253
+ color: 'fg.muted',
254
+ flexShrink: 0,
255
+ ml: isPlatformAdmin && isCurrentUserPrincipal ? 1 : 0,
256
+ }, children: _jsx(TriangleDownIcon, { size: 12 }) })] })) : (_jsx(Button, { variant: "invisible", size: "small", "aria-label": "Switch principal", trailingVisual: TriangleDownIcon, sx: {
257
+ maxWidth: fullWidth ? '100%' : ['200px', '260px', '360px'],
258
+ width: fullWidth ? '100%' : 'auto',
259
+ }, children: _jsxs(Box, { sx: {
260
+ display: 'inline-flex',
261
+ alignItems: 'center',
262
+ gap: 1,
263
+ minWidth: 0,
264
+ }, children: [_jsx(Box, { sx: {
265
+ display: 'inline-flex',
266
+ alignItems: 'center',
267
+ color: 'fg.muted',
268
+ flexShrink: 0,
269
+ }, children: selectedPrincipalKind === 'organization' ? (_jsx(OrganizationIcon, { size: 16 })) : selectedPrincipalKind === 'team' ? (_jsx(PeopleIcon, { size: 16 })) : (_jsx(PersonIcon, { size: 16 })) }), _jsx(Box, { sx: {
270
+ minWidth: 0,
271
+ overflow: 'hidden',
272
+ textOverflow: 'ellipsis',
273
+ whiteSpace: 'nowrap',
274
+ }, children: _jsx(Text, { sx: {
275
+ color: 'accent.fg',
276
+ fontWeight: 'semibold',
277
+ }, children: selectedPrincipalLabelClosed }) }), isPlatformAdmin && isCurrentUserPrincipal ? (_jsx(Label, { variant: "secondary", size: "small", sx: adminBadgeSx, children: "admin" })) : null] }) })) }), _jsx(ActionMenu.Overlay, { width: "medium", children: _jsxs(ActionList, { children: [_jsxs(ActionList.Group, { children: [_jsx(ActionList.GroupHeading, { children: "Personal" }), _jsxs(ActionList.Item, { disabled: isCurrentUserPrincipal, selected: isCurrentUserPrincipal, sx: isCurrentUserPrincipal ? selectedItemSx : undefined, onSelect: () => {
253
278
  if (isCurrentUserPrincipal) {
254
279
  return;
255
280
  }
256
281
  if (personalUid && personalHandle) {
257
282
  selectUser(personalUid, personalHandle);
258
283
  }
259
- }, children: [_jsx(ActionList.LeadingVisual, { children: _jsx(PersonIcon, {}) }), "@", formatFriendlyHandle(personalHandle || 'me'), isPlatformAdmin ? (_jsx(ActionList.TrailingVisual, { children: _jsx(Box, { as: "span", sx: adminBadgeSx, children: "admin" }) })) : null] })] }), _jsxs(ActionList.Group, { children: [_jsx(ActionList.GroupHeading, { children: "Organizations" }), organizations.length === 0 ? (_jsx(ActionList.Item, { disabled: true, children: "No organizations" })) : (organizations.map((organization) => {
284
+ }, children: [_jsx(ActionList.LeadingVisual, { children: _jsx(PersonIcon, {}) }), _jsx(DisplayHandle, { handle: personalHandle || 'me' }), isPlatformAdmin ? (_jsx(ActionList.TrailingVisual, { children: _jsx(Label, { variant: "secondary", size: "small", sx: adminBadgeSx, children: "admin" }) })) : null] })] }), _jsxs(ActionList.Group, { children: [_jsx(ActionList.GroupHeading, { children: "Organizations" }), organizations.length === 0 ? (_jsx(ActionList.Item, { disabled: true, children: "No organizations" })) : (organizations.map((organization) => {
260
285
  const organizationUid = getOrganizationUid(organization);
261
286
  const isCurrentOrganizationPrincipal = selectedPrincipalKind === 'organization' &&
262
287
  selectedPrincipalUid === organizationUid;
@@ -269,7 +294,13 @@ export function PrincipalSwitcherMenu({ maxLabelChars = 48, fullWidth = true, sh
269
294
  if (organizationUid && organization.handle) {
270
295
  selectOrganization(organizationUid, organization.handle);
271
296
  }
272
- }, children: [_jsx(ActionList.LeadingVisual, { children: _jsx(OrganizationIcon, {}) }), "@", organization.handle] }, organizationUid));
297
+ }, children: [_jsx(ActionList.LeadingVisual, { children: _jsx(OrganizationIcon, {}) }), _jsx(Box, { as: "span", title: displayHandleText(organization.handle), sx: {
298
+ display: 'block',
299
+ minWidth: 0,
300
+ overflow: 'hidden',
301
+ textOverflow: 'ellipsis',
302
+ whiteSpace: 'nowrap',
303
+ }, children: _jsx(DisplayHandle, { handle: organization.handle }) })] }, organizationUid));
273
304
  }))] }), _jsxs(ActionList.Group, { children: [_jsx(ActionList.GroupHeading, { children: "Teams" }), teams.length === 0 ? (_jsx(ActionList.Item, { disabled: true, children: "No teams" })) : (teams.map(team => {
274
305
  const isCurrentTeamPrincipal = selectedPrincipalKind === 'team' &&
275
306
  selectedPrincipalUid === team.uid;
@@ -281,7 +312,13 @@ export function PrincipalSwitcherMenu({ maxLabelChars = 48, fullWidth = true, sh
281
312
  return;
282
313
  }
283
314
  selectTeam(team, orgHandle);
284
- }, children: [_jsx(ActionList.LeadingVisual, { children: _jsx(PeopleIcon, {}) }), "@", formatFriendlyHandle(orgHandle), "/", formatFriendlyHandle(team.handle)] }, team.uid));
315
+ }, children: [_jsx(ActionList.LeadingVisual, { children: _jsx(PeopleIcon, {}) }), _jsxs(Box, { as: "span", title: `${displayHandleText(orgHandle)}/${displayHandleText(team.handle, { withAt: false })}`, sx: {
316
+ display: 'block',
317
+ minWidth: 0,
318
+ overflow: 'hidden',
319
+ textOverflow: 'ellipsis',
320
+ whiteSpace: 'nowrap',
321
+ }, children: [_jsx(DisplayHandle, { handle: orgHandle }), "/", _jsx(DisplayHandle, { handle: team.handle, withAt: false })] })] }, team.uid));
285
322
  }))] })] }) })] }));
286
323
  }
287
324
  export default PrincipalSwitcherMenu;