@datalayer/core 1.1.49 → 1.1.56

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 (82) hide show
  1. package/README.md +19 -42
  2. package/lib/client/auth/AuthenticationManager.js +6 -4
  3. package/lib/client/auth/strategies.js +2 -0
  4. package/lib/components/animation/AnimatedText.d.ts +98 -0
  5. package/lib/components/animation/AnimatedText.js +220 -0
  6. package/lib/components/animation/index.d.ts +1 -0
  7. package/lib/components/{tokens → animation}/index.js +1 -1
  8. package/lib/components/auth/{LoginToken.d.ts → SigInAPIKey.d.ts} +3 -3
  9. package/lib/components/auth/{LoginToken.js → SigInAPIKey.js} +5 -5
  10. package/lib/components/auth/{Login.d.ts → SignIn.d.ts} +3 -3
  11. package/lib/components/auth/{Login.js → SignIn.js} +13 -13
  12. package/lib/components/auth/{Login.stories.d.ts → SignIn.stories.d.ts} +2 -2
  13. package/lib/components/auth/{Login.stories.js → SignIn.stories.js} +9 -9
  14. package/lib/components/auth/index.d.ts +2 -2
  15. package/lib/components/auth/index.js +2 -2
  16. package/lib/components/avatars/UserAvatar.d.ts +7 -1
  17. package/lib/components/avatars/UserAvatar.js +19 -6
  18. package/lib/components/avatars/UserProfileAvatar.js +2 -6
  19. package/lib/components/billing/BillingEntitySelect.js +10 -2
  20. package/lib/components/collaboration/LiveEditorCollaborators.d.ts +31 -0
  21. package/lib/components/collaboration/LiveEditorCollaborators.js +107 -0
  22. package/lib/components/collaboration/index.d.ts +1 -0
  23. package/lib/components/collaboration/index.js +9 -0
  24. package/lib/components/display/DatalayerBox.d.ts +3 -1
  25. package/lib/components/display/DatalayerBox.js +7 -7
  26. package/lib/components/display/JupyterDialog.d.ts +2 -0
  27. package/lib/components/display/JupyterDialog.js +10 -4
  28. package/lib/components/iam/ExternalTokenSilentLogin.js +8 -4
  29. package/lib/components/index.d.ts +2 -0
  30. package/lib/components/index.js +2 -0
  31. package/lib/components/labels/StatusLabels.d.ts +42 -0
  32. package/lib/components/labels/StatusLabels.js +61 -0
  33. package/lib/components/labels/index.d.ts +1 -0
  34. package/lib/components/labels/index.js +1 -0
  35. package/lib/components/principal/Principal.d.ts +2 -0
  36. package/lib/components/principal/Principal.js +11 -1
  37. package/lib/components/principal/PrincipalAppearance.d.ts +136 -0
  38. package/lib/components/principal/PrincipalAppearance.js +203 -0
  39. package/lib/components/principal/PrincipalAvatar.d.ts +5 -2
  40. package/lib/components/principal/PrincipalAvatar.js +16 -3
  41. package/lib/components/principal/PrincipalBadge.js +7 -0
  42. package/lib/components/principal/PrincipalDetailsOverlay.d.ts +2 -1
  43. package/lib/components/principal/PrincipalDetailsOverlay.js +2 -2
  44. package/lib/components/principal/index.d.ts +1 -0
  45. package/lib/components/principal/index.js +1 -0
  46. package/lib/components/progress/ConsumptionBar.d.ts +9 -0
  47. package/lib/components/progress/ConsumptionBar.js +10 -2
  48. package/lib/components/sharing/ShareAccessComponent.js +6 -1
  49. package/lib/config/Configuration.d.ts +17 -8
  50. package/lib/config/Configuration.js +12 -5
  51. package/lib/hooks/useCache.d.ts +2 -0
  52. package/lib/hooks/useCache.js +35 -11
  53. package/lib/hooks/usePrincipalCacheStore.d.ts +2 -0
  54. package/lib/hooks/usePrincipalCacheStore.js +2 -0
  55. package/lib/models/IAM.d.ts +10 -0
  56. package/lib/models/Organization.d.ts +2 -0
  57. package/lib/models/Organization.js +2 -0
  58. package/lib/models/Profile.d.ts +2 -0
  59. package/lib/models/Team.d.ts +2 -0
  60. package/lib/models/Team.js +2 -0
  61. package/lib/models/User.d.ts +4 -0
  62. package/lib/models/User.js +4 -0
  63. package/lib/models/UserDTO.d.ts +6 -0
  64. package/lib/models/UserDTO.js +8 -0
  65. package/lib/state/substates/CoreState.js +5 -4
  66. package/lib/state/substates/IAMState.js +6 -0
  67. package/lib/state/substates/LayoutState.js +62 -7
  68. package/lib/state/substates/OrganizationState.d.ts +6 -0
  69. package/lib/state/substates/OrganizationState.js +5 -0
  70. package/lib/state/substates/ProfileState.d.ts +34 -0
  71. package/lib/state/substates/ProfileState.js +68 -0
  72. package/lib/state/substates/TeamState.d.ts +6 -0
  73. package/lib/state/substates/TeamState.js +3 -0
  74. package/lib/state/substates/index.d.ts +1 -0
  75. package/lib/state/substates/index.js +1 -0
  76. package/lib/utils/Jwt.d.ts +0 -6
  77. package/lib/utils/Jwt.js +8 -1
  78. package/lib/views/iam/SignInSimple.d.ts +1 -1
  79. package/lib/views/iam/SignInSimple.js +131 -97
  80. package/lib/views/profile/UserBadge.js +6 -3
  81. package/package.json +3 -3
  82. package/lib/components/tokens/index.d.ts +0 -1
@@ -2,9 +2,9 @@ import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { BrowserRouter } from 'react-router-dom';
3
3
  import { QueryClientProvider } from '@tanstack/react-query';
4
4
  import { queryClient } from '../../utils/cli/query.js';
5
- import { Login } from './Login.js';
5
+ import { SignIn } from './SignIn.js';
6
6
  /**
7
- * Login component provides a comprehensive authentication interface with multiple methods:
7
+ * SignIn component provides a comprehensive authentication interface with multiple methods:
8
8
  * - Email/password authentication
9
9
  * - OAuth (GitHub, LinkedIn)
10
10
  * - Direct token input
@@ -12,8 +12,8 @@ import { Login } from './Login.js';
12
12
  * The component includes form validation, loading states, and error handling.
13
13
  */
14
14
  const meta = {
15
- title: 'Components/Auth/Login',
16
- component: Login,
15
+ title: 'Components/Auth/SignIn',
16
+ component: SignIn,
17
17
  parameters: {
18
18
  layout: 'fullscreen',
19
19
  },
@@ -28,7 +28,7 @@ export default meta;
28
28
  */
29
29
  export const Default = {
30
30
  args: {
31
- heading: 'Login to Datalayer',
31
+ heading: 'Sign in to Datalayer',
32
32
  homeRoute: '/home',
33
33
  loginRoute: '/login',
34
34
  showEmailLogin: true,
@@ -41,7 +41,7 @@ export const Default = {
41
41
  */
42
42
  export const EmailOnly = {
43
43
  args: {
44
- heading: 'Login to Datalayer',
44
+ heading: 'Sign in to Datalayer',
45
45
  homeRoute: '/home',
46
46
  loginRoute: '/login',
47
47
  showEmailLogin: true,
@@ -54,7 +54,7 @@ export const EmailOnly = {
54
54
  */
55
55
  export const OAuthOnly = {
56
56
  args: {
57
- heading: 'Login to Datalayer',
57
+ heading: 'Sign in to Datalayer',
58
58
  homeRoute: '/home',
59
59
  loginRoute: '/login',
60
60
  showEmailLogin: false,
@@ -67,7 +67,7 @@ export const OAuthOnly = {
67
67
  */
68
68
  export const TokenOnly = {
69
69
  args: {
70
- heading: 'Login to Datalayer',
70
+ heading: 'Sign in to Datalayer',
71
71
  homeRoute: '/home',
72
72
  loginRoute: '/login',
73
73
  showEmailLogin: false,
@@ -93,7 +93,7 @@ export const CustomHeading = {
93
93
  */
94
94
  export const WithPasswordReset = {
95
95
  args: {
96
- heading: 'Login to Datalayer',
96
+ heading: 'Sign in to Datalayer',
97
97
  homeRoute: '/home',
98
98
  loginRoute: '/login',
99
99
  passwordRoute: '/password-reset',
@@ -1,5 +1,5 @@
1
1
  /**
2
2
  * Authentication components for Datalayer platform
3
3
  */
4
- export * from './Login';
5
- export * from './LoginToken';
4
+ export * from './SignIn';
5
+ export * from './SigInAPIKey';
@@ -5,7 +5,7 @@
5
5
  /**
6
6
  * Authentication components for Datalayer platform
7
7
  */
8
- export * from './Login.js';
8
+ export * from './SignIn.js';
9
9
  // LoginCLI is excluded from build - it's meant for CLI apps with full routing.
10
10
  // export * from './LoginCLI.js';
11
- export * from './LoginToken.js';
11
+ export * from './SigInAPIKey.js';
@@ -5,12 +5,18 @@
5
5
  export declare function hasRealAvatar(url?: string): boolean;
6
6
  export type UserAvatarProps = {
7
7
  avatarUrl?: string;
8
+ avatarIcon?: string;
8
9
  /** Avatar edge length in pixels. Defaults to 100. */
9
10
  size?: number;
10
11
  /** Render with rounded square corners instead of a circle. Defaults to true. */
11
12
  square?: boolean;
12
13
  /** Fallback icon size. Defaults to ~48% of `size`. */
13
14
  iconSize?: number;
15
+ /** Optional background color override for the default (non-photo) avatar. */
16
+ fallbackBackground?: string;
17
+ /** Optional icon foreground color override for the default avatar. */
18
+ fallbackForeground?: string;
19
+ className?: string;
14
20
  };
15
- export declare const UserAvatar: ({ avatarUrl, size, square, iconSize, }: UserAvatarProps) => JSX.Element;
21
+ export declare const UserAvatar: ({ avatarUrl, avatarIcon, size, square, iconSize, fallbackBackground, fallbackForeground, className, }: UserAvatarProps) => JSX.Element;
16
22
  export default UserAvatar;
@@ -14,6 +14,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
14
14
  import { AlienIcon } from '@datalayer/icons-react';
15
15
  import { Box, useColorPalette } from '@datalayer/primer-addons';
16
16
  import { DLAvatar } from './DLAvatar.js';
17
+ import { getPrincipalAvatarIcon } from '../principal/PrincipalAppearance.js';
17
18
  /**
18
19
  * Returns `true` when the given URL points to a real user avatar (i.e. not a
19
20
  * Gravatar default placeholder).
@@ -27,21 +28,33 @@ export function hasRealAvatar(url) {
27
28
  }
28
29
  return true;
29
30
  }
30
- export const UserAvatar = ({ avatarUrl, size = 100, square = true, iconSize, }) => {
31
+ export const UserAvatar = ({ avatarUrl, avatarIcon, size = 100, square = true, iconSize, fallbackBackground, fallbackForeground, className, }) => {
31
32
  const palette = useColorPalette();
33
+ const SelectedIcon = getPrincipalAvatarIcon(avatarIcon);
34
+ if (SelectedIcon) {
35
+ return (_jsx(Box, { className: className, sx: {
36
+ width: size,
37
+ height: size,
38
+ borderRadius: square ? 2 : '50%',
39
+ overflow: 'hidden',
40
+ display: 'inline-flex',
41
+ alignItems: 'center',
42
+ justifyContent: 'center',
43
+ }, children: _jsx(SelectedIcon, { size: size, colored: true }) }));
44
+ }
32
45
  if (hasRealAvatar(avatarUrl)) {
33
- return _jsx(DLAvatar, { square: square, src: avatarUrl, size: size });
46
+ return _jsx(DLAvatar, { className: className, square: square, src: avatarUrl, size: size });
34
47
  }
35
48
  const resolvedIconSize = iconSize ?? Math.round(size * 0.48);
36
- return (_jsx(Box, { sx: {
49
+ return (_jsx(Box, { className: className, sx: {
37
50
  width: size,
38
51
  height: size,
39
52
  borderRadius: square ? 2 : '50%',
40
- bg: 'accent.subtle',
53
+ bg: fallbackBackground || 'accent.subtle',
41
54
  display: 'flex',
42
55
  alignItems: 'center',
43
56
  justifyContent: 'center',
44
- '--datalayer-icon-fg': palette.primary,
45
- }, children: _jsx(AlienIcon, { size: resolvedIconSize, themed: true, colormoded: true }) }));
57
+ '--datalayer-icon-fg': fallbackForeground || palette.primary,
58
+ }, children: _jsx(AlienIcon, { size: resolvedIconSize, themed: true, colormode: true }) }));
46
59
  };
47
60
  export default UserAvatar;
@@ -6,13 +6,9 @@ import { jsx as _jsx } from "react/jsx-runtime";
6
6
  import { Link } from '@primer/react';
7
7
  import { Box } from '@datalayer/primer-addons';
8
8
  import { AvatarSkeleton } from '../../components/display/index.js';
9
- import { DLAvatar } from './DLAvatar.js';
9
+ import { UserAvatar } from './UserAvatar.js';
10
10
  const Profile = ({ user, size = 100, onClick }) => {
11
- return (_jsx(Box, { style: { width: size }, children: _jsx(DLAvatar
12
- // square
13
- , {
14
- // square
15
- src: user?.avatarUrl, size: size, onClick: onClick }) }));
11
+ return (_jsx(Box, { style: { width: size }, onClick: onClick, children: _jsx(UserAvatar, { avatarUrl: user?.avatarUrl, avatarIcon: user?.avatarIcon, size: size }) }));
16
12
  };
17
13
  export const UserProfileAvatar = ({ onClick, user, size = 100, }) => {
18
14
  return user ? (onClick ? (_jsx(Link, { href: "javascript: return false;", onClick: onClick, children: _jsx(Profile, { user: user, size: size, onClick: onClick }) })) : (_jsx(Profile, { user: user, size: size, onClick: onClick }))) : (_jsx(AvatarSkeleton, { size: size }));
@@ -17,7 +17,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
17
17
  * resolved account via `onSelectedAccountChange`.
18
18
  */
19
19
  import { useCallback, useEffect, useMemo, useRef, Fragment } from 'react';
20
- import { ActionList, ActionMenu, Button, Flash, FormControl, Label, Spinner, Text, } from '@primer/react';
20
+ import { ActionList, ActionMenu, Button, FormControl, Label, Spinner, Text, } from '@primer/react';
21
21
  import { OrganizationIcon, PeopleIcon, PersonIcon, } from '@primer/octicons-react';
22
22
  import { Box } from '@datalayer/primer-addons';
23
23
  import { useCache } from '../../hooks/useCache.js';
@@ -324,7 +324,15 @@ export function BillingEntitySelect({ value, onChange, onSelectedAccountChange,
324
324
  ? 'Organization'
325
325
  : selectedAccount.accountType === 'team'
326
326
  ? 'Team'
327
- : 'User', children: selectedAccount.accountType === 'organization' ? (_jsx(OrganizationIcon, { size: 12 })) : selectedAccount.accountType === 'team' ? (_jsx(PeopleIcon, { size: 12 })) : (_jsx(PersonIcon, { size: 12 })) }), _jsx(Label, { size: "small", variant: selectedAccount.isPaidPlan ? 'success' : 'attention', children: formatBillingEntityPlanLabel(selectedAccount.planName) })] })] })) : !hasEligibleAccount ? (emptyMessage) : ('Select a billing entity') }) }), _jsxs(ActionMenu.Overlay, { width: "large", children: [_jsx(Box, { sx: { p: 2 }, children: _jsx(Flash, { variant: "default", children: flashMessage }) }), _jsx(ActionList, { selectionVariant: "single", children: isLoading ? (_jsx(ActionList.Item, { disabled: true, children: _jsxs(Box, { sx: {
327
+ : 'User', children: selectedAccount.accountType === 'organization' ? (_jsx(OrganizationIcon, { size: 12 })) : selectedAccount.accountType === 'team' ? (_jsx(PeopleIcon, { size: 12 })) : (_jsx(PersonIcon, { size: 12 })) }), _jsx(Label, { size: "small", variant: selectedAccount.isPaidPlan ? 'success' : 'attention', children: formatBillingEntityPlanLabel(selectedAccount.planName) })] })] })) : !hasEligibleAccount ? (emptyMessage) : ('Select a billing entity') }) }), _jsxs(ActionMenu.Overlay, { width: "large", children: [_jsx(Box, { sx: { p: 2 }, children: _jsx(Box, { sx: {
328
+ borderRadius: 2,
329
+ border: '1px solid',
330
+ borderColor: 'border.default',
331
+ bg: 'canvas.subtle',
332
+ color: 'fg.muted',
333
+ px: 3,
334
+ py: 2,
335
+ }, children: _jsx(Text, { sx: { fontSize: 1, lineHeight: 1.5 }, children: flashMessage }) }) }), _jsx(ActionList, { selectionVariant: "single", children: isLoading ? (_jsx(ActionList.Item, { disabled: true, children: _jsxs(Box, { sx: {
328
336
  display: 'inline-flex',
329
337
  alignItems: 'center',
330
338
  gap: 2,
@@ -0,0 +1,31 @@
1
+ export type LiveCollaborator = {
2
+ /** Stable identity — a user uid, or the awareness client id. */
3
+ id: string;
4
+ name: string;
5
+ avatarUrl?: string;
6
+ avatarIcon?: string;
7
+ /** Cursor color of the presence, worn by the initials disc. */
8
+ color?: string;
9
+ };
10
+ /**
11
+ * The slice of a Yjs-style awareness this component reads. Structural, so
12
+ * any provider — Jupyter collaboration, Datalayer spacer — fits without a
13
+ * dependency on its package.
14
+ */
15
+ export type IAwarenessLike = {
16
+ getStates(): Map<number, Record<string, any>>;
17
+ on(event: 'change', handler: () => void): void;
18
+ off(event: 'change', handler: () => void): void;
19
+ };
20
+ export type LiveEditorCollaboratorsProps = {
21
+ /** The presence of the room, followed live when given. */
22
+ awareness?: IAwarenessLike | null;
23
+ /** The collaborators known without a room — owner and granted. */
24
+ collaborators?: LiveCollaborator[];
25
+ /** Diameter of a face, in pixels. */
26
+ size?: number;
27
+ /** How many faces at most; the stack keeps the first ones. */
28
+ max?: number;
29
+ };
30
+ export declare function LiveEditorCollaborators(props: LiveEditorCollaboratorsProps): JSX.Element | null;
31
+ export default LiveEditorCollaborators;
@@ -0,0 +1,107 @@
1
+ import { jsx as _jsx } 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
+ * Copyright (c) 2023-2026 Datalayer, Inc.
8
+ * Distributed under the terms of the Modified BSD License.
9
+ */
10
+ /**
11
+ * The faces on a shared document: who is here now, and who belongs here.
12
+ *
13
+ * One avatar stack for every surface that names collaborators — the cards of
14
+ * the home page, the headers of the notebook and document editors, in the
15
+ * web application and in JupyterLab alike. Two sources feed it, both
16
+ * optional and freely combined:
17
+ *
18
+ * - `awareness`: the presence channel of a collaborative editor. The stack
19
+ * follows it live — people appear as they join and leave as they go.
20
+ * - `collaborators`: what is known without a room, the owner and the granted
21
+ * of an item. They fill the stack when there is no presence, and complete
22
+ * it when there is.
23
+ *
24
+ * Every face uses the shared principal avatar rules: a selected icon wins,
25
+ * then a connected-account picture, then the standard personal fallback.
26
+ *
27
+ * @module components/collaboration/LiveEditorCollaborators
28
+ */
29
+ import { useEffect, useMemo, useState } from 'react';
30
+ import { AvatarStack } from '@primer/react';
31
+ import { PrincipalAvatar } from '../principal/PrincipalAvatar.js';
32
+ /** The `user` field of an awareness state, in its several spellings. */
33
+ function collaboratorOfState(clientId, state) {
34
+ const user = state?.user;
35
+ if (!user) {
36
+ return undefined;
37
+ }
38
+ const name = user.display_name || user.displayName || user.name || user.username || '';
39
+ if (!name) {
40
+ return undefined;
41
+ }
42
+ return {
43
+ id: String(user.uid ?? user.id ?? user.username ?? clientId),
44
+ name,
45
+ avatarUrl: user.avatar_url || user.avatarUrl || undefined,
46
+ avatarIcon: user.avatar_icon_s || user.avatarIcon || undefined,
47
+ color: user.color || undefined
48
+ };
49
+ }
50
+ export function LiveEditorCollaborators(props) {
51
+ const { awareness, collaborators = [], max = 5, size = 24 } = props;
52
+ const [present, setPresent] = useState([]);
53
+ useEffect(() => {
54
+ if (!awareness) {
55
+ setPresent([]);
56
+ return;
57
+ }
58
+ const read = () => {
59
+ const seen = new Map();
60
+ awareness.getStates().forEach((state, clientId) => {
61
+ const collaborator = collaboratorOfState(clientId, state);
62
+ // One face per person, however many tabs they have open.
63
+ if (collaborator && !seen.has(collaborator.id)) {
64
+ seen.set(collaborator.id, collaborator);
65
+ }
66
+ });
67
+ setPresent(Array.from(seen.values()));
68
+ };
69
+ read();
70
+ awareness.on('change', read);
71
+ return () => {
72
+ awareness.off('change', read);
73
+ };
74
+ }, [awareness]);
75
+ const faces = useMemo(() => {
76
+ // Who is here leads; who merely belongs completes. Folded by id and
77
+ // then by name: the same person often arrives twice — once under their
78
+ // platform uid, once under the identity the room gave them.
79
+ const merged = new Map();
80
+ const byName = new Map();
81
+ for (const collaborator of [...present, ...collaborators]) {
82
+ const nameKey = collaborator.name.trim().toLowerCase();
83
+ const key = merged.has(collaborator.id)
84
+ ? collaborator.id
85
+ : (byName.get(nameKey) ?? collaborator.id);
86
+ const known = merged.get(key);
87
+ if (!known) {
88
+ merged.set(key, collaborator);
89
+ byName.set(nameKey, key);
90
+ }
91
+ else if ((!known.avatarIcon && collaborator.avatarIcon) ||
92
+ (!known.avatarUrl && collaborator.avatarUrl)) {
93
+ merged.set(key, {
94
+ ...known,
95
+ avatarIcon: known.avatarIcon || collaborator.avatarIcon,
96
+ avatarUrl: known.avatarUrl || collaborator.avatarUrl,
97
+ });
98
+ }
99
+ }
100
+ return Array.from(merged.values()).slice(0, max);
101
+ }, [collaborators, max, present]);
102
+ if (!faces.length) {
103
+ return null;
104
+ }
105
+ return (_jsx(AvatarStack, { size: size, children: faces.map(face => (_jsx(PrincipalAvatar, { kind: "personal", size: size, alt: face.name, avatarUrl: face.avatarUrl, avatarIcon: face.avatarIcon, square: false }, face.id))) }));
106
+ }
107
+ export default LiveEditorCollaborators;
@@ -0,0 +1 @@
1
+ export * from './LiveEditorCollaborators';
@@ -0,0 +1,9 @@
1
+ /*
2
+ * Copyright (c) 2023-2025 Datalayer, Inc.
3
+ * Distributed under the terms of the Modified BSD License.
4
+ */
5
+ /*
6
+ * Copyright (c) 2023-2026 Datalayer, Inc.
7
+ * Distributed under the terms of the Modified BSD License.
8
+ */
9
+ export * from './LiveEditorCollaborators.js';
@@ -1,8 +1,10 @@
1
+ import type { PropsWithChildren, ReactNode } from 'react';
1
2
  type DatalayerBoxProps = {
2
3
  id?: string;
3
4
  title: string;
5
+ titleAction?: ReactNode;
4
6
  linkLabel?: string;
5
7
  linkRoute?: string;
6
8
  };
7
- export declare const DatalayerBox: (props: React.PropsWithChildren<DatalayerBoxProps>) => import("react/jsx-runtime").JSX.Element;
9
+ export declare const DatalayerBox: (props: PropsWithChildren<DatalayerBoxProps>) => import("react/jsx-runtime").JSX.Element;
8
10
  export default DatalayerBox;
@@ -8,19 +8,19 @@ import { Box } from '@datalayer/primer-addons';
8
8
  import { ArrowRightIcon } from '@primer/octicons-react';
9
9
  import { useNavigate } from '../../hooks/index.js';
10
10
  export const DatalayerBox = (props) => {
11
- const { title, linkLabel, linkRoute, children } = props;
11
+ const { title, titleAction, linkLabel, linkRoute, children } = props;
12
12
  const navigate = useNavigate();
13
13
  return (_jsxs(_Fragment, { children: [_jsxs(Box, { sx: {
14
14
  display: 'flex',
15
15
  alignItems: 'center',
16
16
  justifyContent: 'space-between',
17
17
  paddingRight: 4,
18
- }, children: [_jsx(Text, { as: "h2", sx: { borderLeft: '6px solid #28b899', paddingLeft: 2 }, children: title }), linkRoute && linkLabel && (_jsxs(Link, { href: "javascript: return false;", onClick: e => navigate(linkRoute), sx: {
19
- display: 'inline-flex',
20
- alignItems: 'center',
21
- gap: 1,
22
- whiteSpace: 'nowrap',
23
- }, children: [linkLabel, _jsx(ArrowRightIcon, {})] }))] }), _jsx(Box, { sx: {
18
+ }, children: [_jsx(Text, { as: "h2", sx: { borderLeft: '6px solid #28b899', paddingLeft: 2 }, children: title }), (titleAction || (linkRoute && linkLabel)) && (_jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2 }, children: [titleAction, linkRoute && linkLabel && (_jsxs(Link, { href: "javascript: return false;", onClick: e => navigate(linkRoute), sx: {
19
+ display: 'inline-flex',
20
+ alignItems: 'center',
21
+ gap: 1,
22
+ whiteSpace: 'nowrap',
23
+ }, children: [linkLabel, _jsx(ArrowRightIcon, {})] }))] }))] }), _jsx(Box, { sx: {
24
24
  borderColor: 'border.default',
25
25
  borderStyle: 'solid',
26
26
  borderWidth: '1',
@@ -45,6 +45,8 @@ export interface IDialogWrapperOptions<T> {
45
45
  export declare class JupyterDialog<T> extends ReactWidget {
46
46
  protected body: JSXElementConstructor<PropsWithChildren<DialogProps & {
47
47
  setValue: (v: T | Error) => void;
48
+ /** Whether an accepted answer is being acted on. */
49
+ pending?: boolean;
48
50
  }>>;
49
51
  protected checkbox: Partial<Dialog.ICheckbox> | null;
50
52
  protected buttons: Dialog.IButton[];
@@ -56,10 +56,16 @@ export class JupyterDialog extends ReactWidget {
56
56
  this.dialogTitle = options.title;
57
57
  this.onWillAccept = options.onWillAccept;
58
58
  }
59
- _renderBody = (props) => (_jsx(PrimerDialog.Body, { children: createElement(this.body, {
60
- ...props,
61
- setValue: this.setValue,
62
- }) }));
59
+ _renderBody = (props) => {
60
+ const pending = this._pendingButtonIndex !== null;
61
+ return (_jsx(PrimerDialog.Body, { children: _jsx("div", { "aria-busy": pending, ...(pending ? { inert: '' } : {}), style: pending
62
+ ? { pointerEvents: 'none', opacity: 0.6 }
63
+ : undefined, children: createElement(this.body, {
64
+ ...props,
65
+ setValue: this.setValue,
66
+ pending,
67
+ }) }) }));
68
+ };
63
69
  _renderFooter = (props) => (_jsx(DialogFooter, { ...props, checkbox: this.checkbox, setChecked: this.setChecked }));
64
70
  render() {
65
71
  // TODO title color is enforced for JupyterLab.
@@ -3,10 +3,12 @@ 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 { useEffect } from 'react';
6
+ import { useContext, useEffect } from 'react';
7
+ import { QueryClient, QueryClientContext, QueryClientProvider, } from '@tanstack/react-query';
7
8
  import { CenteredSpinner } from '../../components/display/index.js';
8
9
  import { useIAMStore } from '../../state/index.js';
9
10
  import { useToast, useIAM } from '../../hooks/index.js';
11
+ const externalTokenQueryClient = new QueryClient();
10
12
  const ExternalTokenSilentLoginRoute = (props) => {
11
13
  const { message } = props;
12
14
  const { loginAndNavigate } = useIAM();
@@ -16,8 +18,8 @@ const ExternalTokenSilentLoginRoute = (props) => {
16
18
  if (externalToken) {
17
19
  loginAndNavigate(externalToken, logout, checkIAMToken)
18
20
  .catch(error => {
19
- console.debug('Failed to login with the provided token.', error);
20
- enqueueToast('Failed to login with the provided token.', {
21
+ console.debug('Failed to sign in with the provided token.', error);
22
+ enqueueToast('Failed to sign in with the provided token.', {
21
23
  variant: 'error',
22
24
  });
23
25
  })
@@ -29,7 +31,9 @@ const ExternalTokenSilentLoginRoute = (props) => {
29
31
  return _jsx(CenteredSpinner, { size: "small", message: message });
30
32
  };
31
33
  export const ExternalTokenSilentLogin = (props) => {
34
+ const queryClient = useContext(QueryClientContext);
32
35
  // No navigation provider needed anymore - auto-detection works without it
33
- return _jsx(ExternalTokenSilentLoginRoute, { ...props });
36
+ const content = _jsx(ExternalTokenSilentLoginRoute, { ...props });
37
+ return queryClient ? (content) : (_jsx(QueryClientProvider, { client: externalTokenQueryClient, children: content }));
34
38
  };
35
39
  export default ExternalTokenSilentLogin;
@@ -1,5 +1,7 @@
1
1
  export * from './auth';
2
+ export * from './animation';
2
3
  export * from './billing';
4
+ export * from './collaboration';
3
5
  export * from './sharing';
4
6
  export * from './sparklines';
5
7
  export * from './time';
@@ -3,7 +3,9 @@
3
3
  * Distributed under the terms of the Modified BSD License.
4
4
  */
5
5
  export * from './auth/index.js';
6
+ export * from './animation/index.js';
6
7
  export * from './billing/index.js';
8
+ export * from './collaboration/index.js';
7
9
  export * from './sharing/index.js';
8
10
  export * from './sparklines/index.js';
9
11
  export * from './time/index.js';
@@ -0,0 +1,42 @@
1
+ /**
2
+ * The small pills that qualify an entry.
3
+ *
4
+ * A menu entry, a card, a row of a switcher: what qualifies them is always
5
+ * the same mark — one lowercase word in a semantic tone — and it was written
6
+ * out at every place that needed one. These are that mark, named after what
7
+ * they say, so `alpha` looks the same in the user menu, on an integration and
8
+ * wherever it is needed next.
9
+ *
10
+ * @module components/labels/StatusLabels
11
+ */
12
+ import type { ReactNode } from 'react';
13
+ /** The tones a status pill is read in. */
14
+ export type StatusLabelTone = 'accent' | 'attention' | 'success' | 'neutral';
15
+ export type StatusLabelProps = {
16
+ /** Word of the pill; each label has one of its own. */
17
+ children?: ReactNode;
18
+ size?: 'small' | 'large';
19
+ sx?: Record<string, unknown>;
20
+ };
21
+ /**
22
+ * The pill the named labels below are made of.
23
+ *
24
+ * Exported for a qualification that has no label of its own yet; prefer one of
25
+ * the named ones, so the same thing is said the same way everywhere.
26
+ */
27
+ export declare function StatusLabel(props: StatusLabelProps & {
28
+ tone: StatusLabelTone;
29
+ }): JSX.Element;
30
+ /**
31
+ * A feature that is being tried out.
32
+ */
33
+ export declare function AlphaLabel(props?: StatusLabelProps): JSX.Element;
34
+ /**
35
+ * Something reserved to the administrators of the platform.
36
+ */
37
+ export declare function AdminLabel(props?: StatusLabelProps): JSX.Element;
38
+ /**
39
+ * The one that is taken when none is chosen — the default space of an account.
40
+ */
41
+ export declare function DefaultLabel(props?: StatusLabelProps): JSX.Element;
42
+ export default StatusLabel;
@@ -0,0 +1,61 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { Label } from '@primer/react';
3
+ const TONES = {
4
+ accent: {
5
+ bg: 'accent.subtle',
6
+ color: 'accent.fg',
7
+ borderColor: 'accent.muted',
8
+ },
9
+ attention: {
10
+ bg: 'attention.subtle',
11
+ color: 'attention.fg',
12
+ borderColor: 'attention.muted',
13
+ },
14
+ success: {
15
+ bg: 'success.subtle',
16
+ color: 'success.fg',
17
+ borderColor: 'success.muted',
18
+ },
19
+ neutral: {
20
+ bg: 'neutral.subtle',
21
+ color: 'fg.muted',
22
+ borderColor: 'border.default',
23
+ },
24
+ };
25
+ /**
26
+ * The pill the named labels below are made of.
27
+ *
28
+ * Exported for a qualification that has no label of its own yet; prefer one of
29
+ * the named ones, so the same thing is said the same way everywhere.
30
+ */
31
+ export function StatusLabel(props) {
32
+ const { children, size = 'small', sx, tone } = props;
33
+ return (_jsx(Label, { variant: "secondary", size: size, sx: {
34
+ textTransform: 'lowercase',
35
+ lineHeight: 1.2,
36
+ ...TONES[tone],
37
+ ...sx,
38
+ }, children: children }));
39
+ }
40
+ /**
41
+ * A feature that is being tried out.
42
+ */
43
+ export function AlphaLabel(props = {}) {
44
+ const { children = 'alpha', ...rest } = props;
45
+ return (_jsx(StatusLabel, { tone: "accent", ...rest, children: children }));
46
+ }
47
+ /**
48
+ * Something reserved to the administrators of the platform.
49
+ */
50
+ export function AdminLabel(props = {}) {
51
+ const { children = 'admin', ...rest } = props;
52
+ return (_jsx(StatusLabel, { tone: "attention", ...rest, children: children }));
53
+ }
54
+ /**
55
+ * The one that is taken when none is chosen — the default space of an account.
56
+ */
57
+ export function DefaultLabel(props = {}) {
58
+ const { children = 'default', ...rest } = props;
59
+ return (_jsx(StatusLabel, { tone: "accent", ...rest, children: children }));
60
+ }
61
+ export default StatusLabel;
@@ -1 +1,2 @@
1
+ export * from './StatusLabels';
1
2
  export * from './VisibilityLabel';
@@ -2,4 +2,5 @@
2
2
  * Copyright (c) 2023-2025 Datalayer, Inc.
3
3
  * Distributed under the terms of the Modified BSD License.
4
4
  */
5
+ export * from './StatusLabels.js';
5
6
  export * from './VisibilityLabel.js';
@@ -26,6 +26,8 @@ export type PrincipalDescriptor = {
26
26
  email?: string;
27
27
  origin?: string;
28
28
  avatarUrl?: string;
29
+ avatarIcon?: string;
30
+ banner?: string;
29
31
  };
30
32
  export type PrincipalProps = {
31
33
  principal: PrincipalDescriptor;