@datalayer/core 1.1.50 → 1.1.57

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 (81) 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/animation/index.js +5 -0
  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} +12 -12
  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} +4 -4
  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 +3 -1
  17. package/lib/components/avatars/UserAvatar.js +16 -3
  18. package/lib/components/avatars/UserProfileAvatar.js +2 -6
  19. package/lib/components/collaboration/LiveEditorCollaborators.d.ts +31 -0
  20. package/lib/components/collaboration/LiveEditorCollaborators.js +107 -0
  21. package/lib/components/collaboration/index.d.ts +1 -0
  22. package/lib/components/collaboration/index.js +9 -0
  23. package/lib/components/display/DatalayerBox.d.ts +3 -1
  24. package/lib/components/display/DatalayerBox.js +7 -7
  25. package/lib/components/display/JupyterDialog.d.ts +2 -0
  26. package/lib/components/display/JupyterDialog.js +10 -4
  27. package/lib/components/iam/ExternalTokenSilentLogin.js +2 -2
  28. package/lib/components/index.d.ts +2 -0
  29. package/lib/components/index.js +2 -0
  30. package/lib/components/labels/StatusLabels.d.ts +42 -0
  31. package/lib/components/labels/StatusLabels.js +61 -0
  32. package/lib/components/labels/index.d.ts +1 -0
  33. package/lib/components/labels/index.js +1 -0
  34. package/lib/components/principal/Principal.d.ts +2 -0
  35. package/lib/components/principal/Principal.js +11 -1
  36. package/lib/components/principal/PrincipalAppearance.d.ts +136 -0
  37. package/lib/components/principal/PrincipalAppearance.js +203 -0
  38. package/lib/components/principal/PrincipalAvatar.d.ts +5 -2
  39. package/lib/components/principal/PrincipalAvatar.js +16 -3
  40. package/lib/components/principal/PrincipalBadge.js +7 -0
  41. package/lib/components/principal/PrincipalDetailsOverlay.d.ts +2 -1
  42. package/lib/components/principal/PrincipalDetailsOverlay.js +2 -2
  43. package/lib/components/principal/index.d.ts +1 -0
  44. package/lib/components/principal/index.js +1 -0
  45. package/lib/components/progress/ConsumptionBar.d.ts +9 -0
  46. package/lib/components/progress/ConsumptionBar.js +10 -2
  47. package/lib/components/sharing/ShareAccessComponent.js +6 -1
  48. package/lib/components/students/StudentItemStatus.d.ts +33 -0
  49. package/lib/components/students/StudentItemStatus.js +68 -0
  50. package/lib/components/students/index.d.ts +1 -0
  51. package/lib/components/students/index.js +1 -0
  52. package/lib/config/Configuration.d.ts +17 -8
  53. package/lib/config/Configuration.js +12 -5
  54. package/lib/hooks/useCache.d.ts +2 -0
  55. package/lib/hooks/useCache.js +35 -11
  56. package/lib/hooks/usePrincipalCacheStore.d.ts +2 -0
  57. package/lib/hooks/usePrincipalCacheStore.js +2 -0
  58. package/lib/models/IAM.d.ts +10 -0
  59. package/lib/models/Organization.d.ts +2 -0
  60. package/lib/models/Organization.js +2 -0
  61. package/lib/models/Profile.d.ts +2 -0
  62. package/lib/models/Team.d.ts +2 -0
  63. package/lib/models/Team.js +2 -0
  64. package/lib/models/User.d.ts +4 -0
  65. package/lib/models/User.js +4 -0
  66. package/lib/models/UserDTO.d.ts +6 -0
  67. package/lib/models/UserDTO.js +8 -0
  68. package/lib/state/substates/CoreState.js +3 -2
  69. package/lib/state/substates/IAMState.js +6 -0
  70. package/lib/state/substates/LayoutState.js +62 -7
  71. package/lib/state/substates/OrganizationState.d.ts +6 -0
  72. package/lib/state/substates/OrganizationState.js +5 -0
  73. package/lib/state/substates/ProfileState.d.ts +34 -0
  74. package/lib/state/substates/ProfileState.js +68 -0
  75. package/lib/state/substates/TeamState.d.ts +6 -0
  76. package/lib/state/substates/TeamState.js +3 -0
  77. package/lib/state/substates/index.d.ts +1 -0
  78. package/lib/state/substates/index.js +1 -0
  79. package/lib/views/iam/SignInSimple.d.ts +1 -1
  80. package/lib/views/iam/SignInSimple.js +3 -4
  81. package/package.json +2 -2
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  [![Become a Sponsor](https://img.shields.io/static/v1?label=Become%20a%20Sponsor&message=%E2%9D%A4&logo=GitHub&style=flat&color=1ABC9C)](https://github.com/sponsors/datalayer)
4
4
 
5
- # ☰ 〇 Datalayer Core
5
+ # ☰ ⭕ Datalayer Core
6
6
 
7
7
  <p align="center">
8
8
  <strong>Python and Typescript libraries for Datalayer</strong>
@@ -18,7 +18,7 @@
18
18
 
19
19
  ## Overview
20
20
 
21
- Datalayer Core is the foundational package that powers the [Datalayer AI Platform](https://datalayer.app/). It provides a TypesScript and Python packages as a Command Line Interface (CLI) for AI engineers, data scientists, and researchers to seamlessly integrate scalable compute runtimes into their workflows.
21
+ Datalayer Core is the foundational package that powers the [Datalayer AI Platform](https://datalayer.ai). It provides a TypesScript and Python packages as a Command Line Interface (CLI) for AI engineers, data scientists, and researchers to seamlessly integrate scalable compute runtimes into their workflows.
22
22
 
23
23
  This package serves as the base foundation used by many other Datalayer packages, containing core application classes, configuration, and unified APIs for authentication, runtime management, and code execution in cloud-based environments.
24
24
 
@@ -66,8 +66,6 @@ npm install
66
66
 
67
67
  ## Quick Start with Python
68
68
 
69
- ### 1. Authentication
70
-
71
69
  Set your Datalayer token as an environment variable:
72
70
 
73
71
  ```bash
@@ -89,31 +87,19 @@ if client.authenticate():
89
87
  print("Successfully authenticated!")
90
88
  ```
91
89
 
92
- ### 2. Runtime, Snapshots, and Evals
93
-
94
- Runtime execution, snapshot workflows, and evals CLI are now documented and maintained in:
90
+ ## Architecture
95
91
 
96
- - [agent-runtimes README](https://github.com/datalayer/agent-runtimes/blob/main/README.md)
97
- - [agent-runtimes CLI docs](https://agent-runtimes.datalayer.tech/cli)
92
+ Datalayer Core serves as the foundation for the entire Datalayer ecosystem:
98
93
 
99
- Use `agent-runtimes` for runtime workloads (`RuntimeClient`, `@datalayer`, snapshots, and evals), and use `datalayer-core` for account/platform operations (authentication, secrets, API keys, usage, profile).
94
+ - **Base Classes**: Core application classes inherited by other Datalayer packages
95
+ - **Configuration Management**: Centralized configuration system for all Datalayer components
96
+ - **Authentication Layer**: Unified authentication across all Datalayer services
97
+ - **Runtime Abstraction**: Common interface for different types of compute runtimes
98
+ - **Resource Management**: Automatic cleanup and lifecycle management
100
99
 
101
100
  ## Examples
102
101
 
103
- Examples have moved to Agent Runtimes:
104
-
105
- - [Agent Runtimes examples README](https://github.com/datalayer/agent-runtimes/blob/main/examples/README.md)
106
- - [Agent Runtimes examples directory](https://github.com/datalayer/agent-runtimes/tree/main/examples)
107
-
108
- ## Platform Integration
109
-
110
- Datalayer adds AI capabilities and scalable compute runtimes to your development workflows. The platform is designed to seamlessly integrate into your existing processes and supercharge your computations with the processing power you need.
111
-
112
- Key platform features accessible through this Client and CLI:
113
-
114
- - **Remote Runtimes**: Execute code on powerful remote machines with CPU, RAM, and GPU resources
115
- - **Multiple Interfaces**: Access and consume runtimes through Python Client, CLI, or other integrated tools
116
- - **Scalable Compute**: Dynamically scale your computational resources based on workload requirements
102
+ - [OTEL example README](./examples/otel/README.md)
117
103
 
118
104
  ## Documentation
119
105
 
@@ -193,16 +179,6 @@ This Client is designed to be simple and extensible. We welcome contributions! P
193
179
 
194
180
  For issues and enhancement requests, please use the [GitHub issue tracker](https://github.com/datalayer/core/issues).
195
181
 
196
- ## Architecture
197
-
198
- Datalayer Core serves as the foundation for the entire Datalayer ecosystem:
199
-
200
- - **Base Classes**: Core application classes inherited by other Datalayer packages
201
- - **Configuration Management**: Centralized configuration system for all Datalayer components
202
- - **Authentication Layer**: Unified authentication across all Datalayer services
203
- - **Runtime Abstraction**: Common interface for different types of compute runtimes
204
- - **Resource Management**: Automatic cleanup and lifecycle management
205
-
206
182
  ## Use Cases
207
183
 
208
184
  - **AI/ML Development**: Scale your machine learning workflows with cloud compute using Client or CLI
@@ -219,15 +195,16 @@ This project is licensed under the [BSD 3-Clause License](https://github.com/dat
219
195
 
220
196
  - **Documentation**: [Datalayer Platform Documentation](https://datalayer.ai/docs/)
221
197
  - **Issues**: [GitHub Issues](https://github.com/datalayer/core/issues)
222
- - **Community**: [Datalayer Platform](https://datalayer.app/)
198
+ - **Community**: [Datalayer Platform](https://datalayer.ai)
223
199
 
224
200
  ---
225
201
 
226
- <p align="center">
227
- <img src="https://assets.datalayer.tech/datalayer-25.svg" alt="Datalayer Logo" width="200"></img>
228
- </p>
202
+ <div align="center">
229
203
 
230
- <p align="center">
231
- <strong>🚀 AI Agents for Data Analysis</strong><br></br>
232
- <a href="https://datalayer.app/">Get started with Datalayer today!</a>
233
- </p>
204
+ **If this project is helpful to you, please give us a ⭐️**
205
+
206
+ Made with ❤️ by [Datalayer](https://datalayer.ai)
207
+
208
+ <img src="https://assets.datalayer.tech/datalayer-25.svg" alt="Datalayer Logo" width="200"></img>
209
+
210
+ </div>
@@ -25,15 +25,17 @@ export class AuthenticationManager {
25
25
  */
26
26
  constructor(iamUrl, storage) {
27
27
  this.iamUrl = iamUrl;
28
- // Extract datalayer_url (remove /api/iam/v1 if present)
29
- const datalayerUrl = iamUrl.replace('/api/iam/v1', '');
30
- // CRITICAL: Pass datalayerUrl to storage for keyring compatibility
28
+ // Extract the service URL (remove /api/iam/v1 if present)
29
+ // The service URL the credentials are stored under: the IAM one, without
30
+ // the path of its API.
31
+ const serviceUrl = iamUrl.replace('/api/iam/v1', '');
32
+ // CRITICAL: Pass the service URL to storage for keyring compatibility
31
33
  if (!storage) {
32
34
  if (typeof window !== 'undefined') {
33
35
  this.storage = new BrowserStorage();
34
36
  }
35
37
  else {
36
- this.storage = new NodeStorage(datalayerUrl); // Pass datalayerUrl as serviceUrl
38
+ this.storage = new NodeStorage(serviceUrl);
37
39
  }
38
40
  }
39
41
  else {
@@ -39,6 +39,8 @@ class BaseAuthStrategy {
39
39
  first_name_t: response.profile.first_name_t || response.profile.first_name || '',
40
40
  last_name_t: response.profile.last_name_t || response.profile.last_name || '',
41
41
  avatar_url_s: response.profile.avatar_url_s || response.profile.avatar_url,
42
+ avatar_icon_s: response.profile.avatar_icon_s,
43
+ banner_s: response.profile.banner_s,
42
44
  };
43
45
  return new UserDTO(userData, undefined);
44
46
  }
@@ -0,0 +1,98 @@
1
+ /**
2
+ * One piece of an animated line.
3
+ *
4
+ * A string is written as it is. An object cycles through its words, and every
5
+ * cycling piece of the same line advances together — so a sentence can name
6
+ * two things that change at once and still read as one sentence rather than
7
+ * two independent slot machines.
8
+ */
9
+ export type AnimatedTextPart = string | AnimatedTextWords;
10
+ export type AnimatedTextWords = {
11
+ /** The words this piece cycles through. */
12
+ words: string[];
13
+ /**
14
+ * The colour of the words, as a Primer foreground token name — `accent`,
15
+ * `success`, `attention`, `severe`, `done`, `muted` — or any CSS colour.
16
+ * Given a token name the colour follows the theme, light or dark, because
17
+ * it resolves to the CSS variable Primer sets.
18
+ */
19
+ color?: AnimatedTextColor;
20
+ /** Overrides the background of this piece alone. */
21
+ background?: string;
22
+ };
23
+ export type AnimatedTextColor = 'accent' | 'success' | 'attention' | 'severe' | 'done' | 'muted' | 'default' | (string & {});
24
+ export interface AnimatedTextProps extends Omit<React.HTMLAttributes<HTMLSpanElement>, 'children' | 'color'> {
25
+ /** Text before the cycling words. Ignored when `parts` is given. */
26
+ prefix?: string;
27
+ /** The words to cycle through. Ignored when `parts` is given. */
28
+ words?: string[];
29
+ /** Text after the cycling words. Ignored when `parts` is given. */
30
+ suffix?: string;
31
+ /**
32
+ * The line, as a sequence of fixed and cycling pieces. Every cycling piece
33
+ * advances on the same tick, so several of them stay in step.
34
+ */
35
+ parts?: AnimatedTextPart[];
36
+ /** Colour applied to cycling pieces that do not name their own. */
37
+ color?: AnimatedTextColor;
38
+ /**
39
+ * The colours to draw cycling words in when neither the piece nor `color`
40
+ * names one. Each word takes a different colour, and changes colour as it
41
+ * changes word.
42
+ */
43
+ palette?: string[];
44
+ /**
45
+ * What sits behind the cycling words. White by default so a coloured word
46
+ * stays readable over artwork; pass `'transparent'` for a plain background
47
+ * where the plate is not wanted.
48
+ */
49
+ background?: string;
50
+ /**
51
+ * Hold every cycling piece at the width of the longest word it can show,
52
+ * instead of letting it size itself to whichever word is currently up.
53
+ *
54
+ * Off by default, because it changes the look: a piece is then as wide as
55
+ * its longest word even when a short one is showing, which is right for a
56
+ * line that reads as a slot being filled and wrong for one meant to read as
57
+ * an ordinary sentence.
58
+ *
59
+ * Turn it on when the line is set large, or is close to the width it has.
60
+ * Words of different lengths otherwise shove the rest of the sentence
61
+ * sideways on every tick, and the longest combination can take a line that
62
+ * fits on one row and drop it onto two — which, since a theme may set any
63
+ * typeface, is not something a caller can rule out by measuring.
64
+ *
65
+ * The reservation is made by the browser, not by a measurement: all of the
66
+ * words are laid into one grid cell and the ones not showing are hidden, so
67
+ * the piece is exactly as wide as the widest of them in the font and size it
68
+ * actually ends up with.
69
+ */
70
+ reserveWidth?: boolean;
71
+ intervalMs?: number;
72
+ transitionMs?: number;
73
+ }
74
+ /**
75
+ * A line of text in which one or more words change, in step.
76
+ *
77
+ * ```tsx
78
+ * <AnimatedText
79
+ * parts={[
80
+ * 'Bring ',
81
+ * { words: ['Claude', 'Codex', 'Cursor'], color: 'accent' },
82
+ * '. Datalayer gives it a secure endpoint to your ',
83
+ * { words: ['Notebooks', 'Data', 'Code Sandboxes'], color: 'success' },
84
+ * '.',
85
+ * ]}
86
+ * />
87
+ * ```
88
+ *
89
+ * Set large, or set close to the width available, pass `reserveWidth` so the
90
+ * cycling pieces hold the width of their longest word and the line neither
91
+ * shifts on each tick nor wraps on its longest combination:
92
+ *
93
+ * ```tsx
94
+ * <AnimatedText reserveWidth parts={[...]} background="transparent" />
95
+ * ```
96
+ */
97
+ export declare function AnimatedText({ prefix, words, suffix, parts, color, palette, background, reserveWidth, intervalMs, transitionMs, style, ...rest }: AnimatedTextProps): JSX.Element;
98
+ export default AnimatedText;
@@ -0,0 +1,220 @@
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
+ import { useEffect, useMemo, useState } from 'react';
7
+ /** The Primer foreground tokens, which carry the theme with them. */
8
+ const THEME_COLORS = {
9
+ accent: 'var(--fgColor-accent, var(--color-accent-fg))',
10
+ success: 'var(--fgColor-success, var(--color-success-fg))',
11
+ attention: 'var(--fgColor-attention, var(--color-attention-fg))',
12
+ severe: 'var(--fgColor-severe, var(--color-severe-fg))',
13
+ done: 'var(--fgColor-done, var(--color-done-fg))',
14
+ muted: 'var(--fgColor-muted, var(--color-fg-muted))',
15
+ default: 'inherit',
16
+ };
17
+ /**
18
+ * The colours the cycling words are drawn in when none is named.
19
+ *
20
+ * Fixed values rather than Primer's theme tokens, because the plate behind
21
+ * the words is white in either theme: a token would resolve to its light-mode
22
+ * variant in the dark and wash out. Every one of these sits between 4.8 and
23
+ * 5.4 contrast on white — comfortably readable, and none of them the pale or
24
+ * near-black ends of the palette.
25
+ */
26
+ const MEDIUM_PALETTE = [
27
+ '#0969da', // blue
28
+ '#1a7f37', // green
29
+ '#8250df', // purple
30
+ '#bc4c00', // orange
31
+ '#cf222e', // red
32
+ '#1b7c83', // teal
33
+ '#bf3989', // pink
34
+ ];
35
+ /**
36
+ * What sits behind the cycling words.
37
+ *
38
+ * A coloured word over a photograph or an illustrated hero is legible only by
39
+ * luck: the contrast changes with whatever the artwork happens to be doing
40
+ * behind that glyph. A solid plate underneath makes it legible by
41
+ * construction, whatever it is laid over.
42
+ */
43
+ const DEFAULT_BACKGROUND = '#ffffff';
44
+ /**
45
+ * Resolve a colour: a known token becomes the themed CSS variable, anything
46
+ * else is passed through so a caller can still give a literal colour.
47
+ */
48
+ function resolveColor(color) {
49
+ if (!color) {
50
+ return undefined;
51
+ }
52
+ return THEME_COLORS[color] ?? color;
53
+ }
54
+ /** Whether this reader has asked for less movement. */
55
+ function usePrefersReducedMotion() {
56
+ const [reduced, setReduced] = useState(false);
57
+ useEffect(() => {
58
+ if (typeof window === 'undefined' || !window.matchMedia) {
59
+ return;
60
+ }
61
+ const query = window.matchMedia('(prefers-reduced-motion: reduce)');
62
+ const update = () => setReduced(query.matches);
63
+ update();
64
+ query.addEventListener('change', update);
65
+ return () => query.removeEventListener('change', update);
66
+ }, []);
67
+ return reduced;
68
+ }
69
+ /**
70
+ * A line of text in which one or more words change, in step.
71
+ *
72
+ * ```tsx
73
+ * <AnimatedText
74
+ * parts={[
75
+ * 'Bring ',
76
+ * { words: ['Claude', 'Codex', 'Cursor'], color: 'accent' },
77
+ * '. Datalayer gives it a secure endpoint to your ',
78
+ * { words: ['Notebooks', 'Data', 'Code Sandboxes'], color: 'success' },
79
+ * '.',
80
+ * ]}
81
+ * />
82
+ * ```
83
+ *
84
+ * Set large, or set close to the width available, pass `reserveWidth` so the
85
+ * cycling pieces hold the width of their longest word and the line neither
86
+ * shifts on each tick nor wraps on its longest combination:
87
+ *
88
+ * ```tsx
89
+ * <AnimatedText reserveWidth parts={[...]} background="transparent" />
90
+ * ```
91
+ */
92
+ export function AnimatedText({ prefix, words, suffix, parts, color, palette = MEDIUM_PALETTE, background = DEFAULT_BACKGROUND, reserveWidth = false, intervalMs = 1800, transitionMs = 240, style, ...rest }) {
93
+ // The words still change for a reader who asked for less movement — that is
94
+ // what this component is for — but they do it without travelling, and the
95
+ // fade is dropped rather than merely shortened.
96
+ const reducedMotion = usePrefersReducedMotion();
97
+ // The older three-prop form is the same thing with one cycling piece.
98
+ const resolvedParts = useMemo(() => {
99
+ if (parts && parts.length > 0) {
100
+ return parts;
101
+ }
102
+ return [prefix ?? '', { words: words ?? [] }, suffix ?? ''];
103
+ }, [parts, prefix, suffix, words]);
104
+ const cyclingParts = useMemo(() => resolvedParts.filter((part) => typeof part !== 'string' &&
105
+ part.words.filter(word => word.trim().length > 0).length > 0), [resolvedParts]);
106
+ // One index for the whole line: what makes several cycling pieces change
107
+ // together rather than drifting apart.
108
+ const [step, setStep] = useState(0);
109
+ const [visible, setVisible] = useState(true);
110
+ // The tick has to run until every piece has come back round, so a line
111
+ // pairing three words with four does not repeat after three.
112
+ const cycleLength = useMemo(() => {
113
+ const lengths = cyclingParts.map(part => part.words.filter(word => word.trim().length > 0).length);
114
+ return lengths.reduce((total, length) => lowestCommonMultiple(total, length), 1);
115
+ }, [cyclingParts]);
116
+ useEffect(() => {
117
+ if (cycleLength <= 1) {
118
+ return;
119
+ }
120
+ const timer = setInterval(() => {
121
+ setVisible(false);
122
+ setTimeout(() => {
123
+ setStep(previous => (previous + 1) % cycleLength);
124
+ setVisible(true);
125
+ }, transitionMs);
126
+ }, intervalMs);
127
+ return () => {
128
+ clearInterval(timer);
129
+ };
130
+ }, [cycleLength, intervalMs, transitionMs]);
131
+ /**
132
+ * The colour of the cycling piece at `ordinal`, for the current step.
133
+ *
134
+ * Derived rather than random so a re-render does not repaint the line, and
135
+ * offset by the ordinal so two words showing at once never take the same
136
+ * colour. The palette length being prime keeps the pairing from settling
137
+ * into a short repeating pattern.
138
+ */
139
+ const paletteColor = (ordinal) => {
140
+ if (palette.length === 0) {
141
+ return undefined;
142
+ }
143
+ return palette[(step * 2 + ordinal * 3) % palette.length];
144
+ };
145
+ /** How a word arrives and leaves, and how far it travels doing it. */
146
+ const motion = reducedMotion
147
+ ? { transition: undefined }
148
+ : {
149
+ transition: `opacity ${transitionMs}ms ease, transform ${transitionMs}ms ease`,
150
+ };
151
+ const offset = reducedMotion ? 'translateY(0)' : 'translateY(0.25em)';
152
+ /** The plate the words of a piece are drawn on, and the colour they take. */
153
+ const pieceStyle = (part, ordinal) => {
154
+ const plate = part.background ?? background;
155
+ const hasPlate = plate !== 'transparent' && plate !== 'none';
156
+ return {
157
+ // An explicitly named colour always wins; otherwise the palette.
158
+ color: resolveColor(part.color ?? color) ?? paletteColor(ordinal),
159
+ backgroundColor: hasPlate ? plate : undefined,
160
+ // Enough to keep the plate clear of the glyphs without the words
161
+ // drifting apart from the fixed text around them.
162
+ padding: hasPlate ? '0 0.25em' : undefined,
163
+ borderRadius: hasPlate ? '0.25em' : undefined,
164
+ };
165
+ };
166
+ const wordStyle = (part, ordinal) => ({
167
+ ...pieceStyle(part, ordinal),
168
+ display: 'inline-block',
169
+ ...motion,
170
+ opacity: visible ? 1 : 0,
171
+ transform: visible ? 'translateY(0)' : offset,
172
+ });
173
+ /**
174
+ * The piece as a slot: a grid of one cell, holding every word it can show.
175
+ *
176
+ * The cell takes the width of its widest occupant, so the slot is the same
177
+ * width whichever word is up — which is the whole point of `reserveWidth`,
178
+ * and why the words have to be in the document rather than measured.
179
+ */
180
+ const slotStyle = (part, ordinal) => ({
181
+ ...pieceStyle(part, ordinal),
182
+ display: 'inline-grid',
183
+ justifyItems: 'center',
184
+ });
185
+ const wordInSlotStyle = (showing) => ({
186
+ gridArea: '1 / 1',
187
+ whiteSpace: 'nowrap',
188
+ // Hidden rather than merely transparent: the words not showing should be
189
+ // out of the accessibility tree and out of the way of the pointer, while
190
+ // still holding the cell open.
191
+ visibility: showing ? 'visible' : 'hidden',
192
+ ...motion,
193
+ opacity: showing && visible ? 1 : 0,
194
+ transform: showing && visible ? 'translateY(0)' : offset,
195
+ });
196
+ return (_jsx("span", { ...rest, style: style, children: resolvedParts.map((part, index) => {
197
+ if (typeof part === 'string') {
198
+ return _jsx("span", { children: part }, `fixed-${index}`);
199
+ }
200
+ const available = part.words.filter(word => word.trim().length > 0);
201
+ if (available.length === 0) {
202
+ return null;
203
+ }
204
+ const ordinal = cyclingParts.indexOf(part);
205
+ const current = step % available.length;
206
+ return (_jsx("span", { "aria-live": "polite", style: reserveWidth ? slotStyle(part, ordinal) : wordStyle(part, ordinal), children: reserveWidth
207
+ ? available.map((word, wordIndex) => (_jsx("span", { "aria-hidden": wordIndex === current ? undefined : true, style: wordInSlotStyle(wordIndex === current), children: word }, word)))
208
+ : available[current] }, `words-${index}`));
209
+ }) }));
210
+ }
211
+ function greatestCommonDivisor(a, b) {
212
+ return b === 0 ? a : greatestCommonDivisor(b, a % b);
213
+ }
214
+ function lowestCommonMultiple(a, b) {
215
+ if (a === 0 || b === 0) {
216
+ return Math.max(a, b);
217
+ }
218
+ return Math.abs(a * b) / greatestCommonDivisor(a, b);
219
+ }
220
+ export default AnimatedText;
@@ -0,0 +1 @@
1
+ export * from './AnimatedText';
@@ -0,0 +1,5 @@
1
+ /*
2
+ * Copyright (c) 2023-2025 Datalayer, Inc.
3
+ * Distributed under the terms of the Modified BSD License.
4
+ */
5
+ export * from './AnimatedText.js';
@@ -1,4 +1,4 @@
1
- export interface ILoginTokenProps {
1
+ export interface ISigninAPIKeyProps {
2
2
  /**
3
3
  * Home page route to navigate to after successful login
4
4
  */
@@ -16,5 +16,5 @@ export interface ILoginTokenProps {
16
16
  * Component for token-based authentication
17
17
  * Provides a simple button that expands to show a token input form
18
18
  */
19
- export declare const LoginToken: (props: ILoginTokenProps) => JSX.Element;
20
- export default LoginToken;
19
+ export declare const SigInAPIKey: (props: ISigninAPIKeyProps) => JSX.Element;
20
+ export default SigInAPIKey;
@@ -14,7 +14,7 @@ import { useIAMStore } from '../../state/index.js';
14
14
  * Component for token-based authentication
15
15
  * Provides a simple button that expands to show a token input form
16
16
  */
17
- export const LoginToken = (props) => {
17
+ export const SigInAPIKey = (props) => {
18
18
  const { homeRoute, disabled = false, style } = props;
19
19
  const [showForm, setShowForm] = useState(false);
20
20
  const [token, setToken] = useState('');
@@ -35,7 +35,7 @@ export const LoginToken = (props) => {
35
35
  setToken('');
36
36
  }
37
37
  catch (error) {
38
- console.error('Token login failed:', error);
38
+ console.error('Token sign-in failed:', error);
39
39
  enqueueToast('Failed to authenticate with provided token', {
40
40
  variant: 'error',
41
41
  });
@@ -45,7 +45,7 @@ export const LoginToken = (props) => {
45
45
  }
46
46
  };
47
47
  if (!showForm) {
48
- return (_jsx(Button, { onClick: () => setShowForm(true), style: style, disabled: disabled, children: "Login with Token" }));
48
+ return (_jsx(Button, { onClick: () => setShowForm(true), style: style, disabled: disabled, children: "Sign in with API Key" }));
49
49
  }
50
50
  return (_jsxs(Box, { style: style, children: [_jsxs(FormControl, { children: [_jsx(FormControl.Label, { children: "Your Authentication Token" }), _jsx(TextInput, { block: true, placeholder: "Paste your authentication token here", value: token, disabled: disabled || loading, onChange: e => setToken(e.target.value), onKeyDown: e => {
51
51
  if (e.key === 'Enter') {
@@ -55,9 +55,9 @@ export const LoginToken = (props) => {
55
55
  setShowForm(false);
56
56
  setToken('');
57
57
  }
58
- } }), _jsx(FormControl.Caption, { children: "Enter your Datalayer authentication token to log in" })] }), _jsxs(Box, { mt: 3, display: "flex", justifyContent: "flex-start", children: [_jsx(Button, { variant: "primary", onClick: handleLogin, disabled: disabled || loading || !token.trim(), sx: { mr: 2 }, children: loading ? 'Authenticating...' : 'Login' }), _jsx(Button, { variant: "default", disabled: disabled || loading, onClick: () => {
58
+ } }), _jsx(FormControl.Caption, { children: "Enter your Datalayer authentication token to log in" })] }), _jsxs(Box, { mt: 3, display: "flex", justifyContent: "flex-start", children: [_jsx(Button, { variant: "primary", onClick: handleLogin, disabled: disabled || loading || !token.trim(), sx: { mr: 2 }, children: loading ? 'Authenticating...' : 'Sign In' }), _jsx(Button, { variant: "default", disabled: disabled || loading, onClick: () => {
59
59
  setShowForm(false);
60
60
  setToken('');
61
61
  }, children: "Cancel" })] })] }));
62
62
  };
63
- export default LoginToken;
63
+ export default SigInAPIKey;
@@ -1,4 +1,4 @@
1
- export interface ILoginProps {
1
+ export interface ISigninProps {
2
2
  /**
3
3
  * Page heading
4
4
  */
@@ -40,5 +40,5 @@ export interface ILoginProps {
40
40
  */
41
41
  showTokenLogin?: boolean;
42
42
  }
43
- export declare const Login: (props: ILoginProps) => JSX.Element;
44
- export default Login;
43
+ export declare const SignIn: (props: ISigninProps) => JSX.Element;
44
+ export default SignIn;
@@ -14,8 +14,8 @@ import { useIAMStore } from '../../state/index.js';
14
14
  import { CenteredSpinner } from '../display/index.js';
15
15
  import { isInsideJupyterLab, validateLength } from '../../utils/index.js';
16
16
  import { useNavigate, useCache, useToast, useIAM } from '../../hooks/index.js';
17
- import { LoginToken } from './LoginToken.js';
18
- export const Login = (props) => {
17
+ import { SigInAPIKey } from './SigInAPIKey.js';
18
+ export const SignIn = (props) => {
19
19
  const { heading, homeRoute, loginRoute, showEmailLogin = true, showGitHubLogin = true, showGoogleLogin = true, showTokenLogin = true, } = props;
20
20
  const { useLogin, useOAuth2AuthorizationURL } = useCache({ loginRoute });
21
21
  const loginMutation = useLogin();
@@ -62,7 +62,7 @@ export const Login = (props) => {
62
62
  addIAMProviderAuthorizationURL(iamProvider.name, authUrl);
63
63
  }
64
64
  else {
65
- console.error(`Failed to get the Login URL from Datalayer IAM for provider ${iamProvider.name}.`);
65
+ console.error(`Failed to get the sign-in URL from Datalayer IAM for provider ${iamProvider.name}.`);
66
66
  }
67
67
  },
68
68
  });
@@ -82,7 +82,7 @@ export const Login = (props) => {
82
82
  setLoadingWithToken(1);
83
83
  loginAndNavigate(externalToken, logout, checkIAMToken, navigate, homeRoute)
84
84
  .catch(error => {
85
- console.debug('Failed to login with token from cookie..', error);
85
+ console.debug('Failed to sign in with token from cookie..', error);
86
86
  enqueueToast('Failed to check authentication.', { variant: 'error' });
87
87
  })
88
88
  .finally(() => {
@@ -127,14 +127,14 @@ export const Login = (props) => {
127
127
  navigate(homeRoute);
128
128
  }
129
129
  else {
130
- enqueueToast('Failed to login. Check your username and password.', {
130
+ enqueueToast('Failed to sign in. Check your username and password.', {
131
131
  variant: 'warning',
132
132
  });
133
133
  setValidationResult({
134
134
  handle: '',
135
135
  password: 'Invalid credentials',
136
136
  });
137
- console.debug(`Failed to login: ${resp.message}`, resp.errors ?? '');
137
+ console.debug(`Failed to sign in: ${resp.message}`, resp.errors ?? '');
138
138
  }
139
139
  },
140
140
  onSettled: () => {
@@ -190,15 +190,15 @@ export const Login = (props) => {
190
190
  : 'Show password', sx: { color: 'var(--fgColor-muted)' } }), sx: { overflow: 'visible' } }), validationResult.password && (_jsx(FormControl.Validation, { variant: validationResult.password ? 'error' : 'success', children: validationResult.password }))] }) }), _jsxs(Box, { mt: 5, children: [_jsx(Button, { variant: "primary", disabled: loading ||
191
191
  validationResult.handle !== '' ||
192
192
  validationResult.password !== '', onClick: submit, children: loading
193
- ? 'Login…'
193
+ ? 'Sign In…'
194
194
  : heading
195
- ? 'Login with Datalayer'
196
- : 'Login' }), _jsx(Box, { pt: 6 }), _jsx(Link, { href: "https://datalayer.ai/password", target: "_blank", children: "Forgot password?" })] })] })), _jsx(Box, { children: _jsxs(Box, { display: "flex", flexDirection: "column", sx: { margin: 'auto' }, children: [showGitHubLogin &&
195
+ ? 'Sign in with Datalayer'
196
+ : 'Sign In' }), _jsx(Box, { pt: 6 }), _jsx(Link, { href: "https://datalayer.ai/password", target: "_blank", children: "Forgot password?" })] })] })), _jsx(Box, { children: _jsxs(Box, { display: "flex", flexDirection: "column", sx: { margin: 'auto' }, children: [showGitHubLogin &&
197
197
  iamProvidersAuthorizationURL[IAMProvidersSpecs.GitHub.name] && (_jsx(Button, { leadingVisual: pendingOAuthProvider === IAMProvidersSpecs.GitHub.name
198
198
  ? () => _jsx(Spinner, { size: "small" })
199
- : MarkGithubIcon, onClick: () => startOAuthSignIn(IAMProvidersSpecs.GitHub), disabled: socialButtonsDisabled, style: { margin: '10px 0' }, children: "Login with GitHub" })), showGoogleLogin &&
199
+ : MarkGithubIcon, onClick: () => startOAuthSignIn(IAMProvidersSpecs.GitHub), disabled: socialButtonsDisabled, style: { margin: '10px 0' }, children: "Sign in with GitHub" })), showGoogleLogin &&
200
200
  iamProvidersAuthorizationURL[IAMProvidersSpecs.Google.name] && (_jsx(Button, { leadingVisual: pendingOAuthProvider === IAMProvidersSpecs.Google.name
201
201
  ? () => _jsx(Spinner, { size: "small" })
202
- : GoogleIcon, onClick: () => startOAuthSignIn(IAMProvidersSpecs.Google), disabled: socialButtonsDisabled, style: { margin: '10px 0' }, children: "Login with Google" })), showTokenLogin && (_jsx(LoginToken, { homeRoute: homeRoute, disabled: loading, style: { margin: '10px 0' } }))] }) })] }) })) : loadingWithToken ? (_jsx(CenteredSpinner, { message: "Checking authentication\u2026" })) : (_jsx(_Fragment, {})) })] }));
202
+ : GoogleIcon, onClick: () => startOAuthSignIn(IAMProvidersSpecs.Google), disabled: socialButtonsDisabled, style: { margin: '10px 0' }, children: "Sign in with Google" })), showTokenLogin && (_jsx(SigInAPIKey, { homeRoute: homeRoute, disabled: loading, style: { margin: '10px 0' } }))] }) })] }) })) : loadingWithToken ? (_jsx(CenteredSpinner, { message: "Checking authentication\u2026" })) : (_jsx(_Fragment, {})) })] }));
203
203
  };
204
- export default Login;
204
+ export default SignIn;
@@ -1,6 +1,6 @@
1
1
  import type { StoryObj } from '@storybook/react-vite';
2
2
  /**
3
- * Login component provides a comprehensive authentication interface with multiple methods:
3
+ * SignIn component provides a comprehensive authentication interface with multiple methods:
4
4
  * - Email/password authentication
5
5
  * - OAuth (GitHub, LinkedIn)
6
6
  * - Direct token input
@@ -9,7 +9,7 @@ import type { StoryObj } from '@storybook/react-vite';
9
9
  */
10
10
  declare const meta: {
11
11
  title: string;
12
- component: (props: import("./Login").ILoginProps) => JSX.Element;
12
+ component: (props: import("./SignIn").ISigninProps) => JSX.Element;
13
13
  parameters: {
14
14
  layout: string;
15
15
  };
@@ -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
  },