@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
@@ -7,12 +7,6 @@ export declare const FORCE_ACTIVATE_RUNTIMES_PLUGINS = false;
7
7
  * and interact with the Datalayer platform services.
8
8
  */
9
9
  export type IDatalayerCoreConfig = {
10
- /**
11
- * Datalayer RUN URL.
12
- * The base URL for the Datalayer platform API.
13
- * @example "https://prod1.datalayer.run"
14
- */
15
- datalayerUrl: string;
16
10
  /**
17
11
  * Datalayer API authentication token.
18
12
  * Used for authenticating requests to the Datalayer platform.
@@ -47,10 +41,18 @@ export type IDatalayerCoreConfig = {
47
41
  * IAM API URL.
48
42
  */
49
43
  iamUrl: string;
44
+ /**
45
+ * Manager API URL.
46
+ */
47
+ managerUrl: string;
50
48
  /**
51
49
  * Runtimes API URL.
52
50
  */
53
51
  runtimesUrl: string;
52
+ /**
53
+ * Scheduler API URL.
54
+ */
55
+ schedulerUrl: string;
54
56
  /**
55
57
  * Spacer API URL.
56
58
  */
@@ -68,9 +70,9 @@ export type IDatalayerCoreConfig = {
68
70
  */
69
71
  aiInferenceUrl: string;
70
72
  /**
71
- * MCP Servers API URL.
73
+ * Jupyter MCP Server URL.
72
74
  */
73
- mcpServersUrl: string;
75
+ jupyterMcpServerUrl: string;
74
76
  /**
75
77
  * OTEL (OpenTelemetry) API URL.
76
78
  *
@@ -175,6 +177,13 @@ export declare class DatalayerConfiguration {
175
177
  /**
176
178
  * Default configuration values for Datalayer
177
179
  */
180
+ /**
181
+ * Where a service lives when nothing says otherwise.
182
+ *
183
+ * Each service has a URL of its own — `iamUrl`, `runtimesUrl`, … — and this is
184
+ * what each of them falls back to.
185
+ */
186
+ export declare const DEFAULT_DATALAYER_SERVICE_URL = "https://prod1.datalayer.run";
178
187
  export declare const DEFAULT_DATALAYER_CONFIG: Partial<IDatalayerCoreConfig>;
179
188
  /**
180
189
  * Type guard to check if a config object is a valid IDatalayerConfig
@@ -25,8 +25,15 @@ export class DatalayerConfiguration {
25
25
  /**
26
26
  * Default configuration values for Datalayer
27
27
  */
28
+ /**
29
+ * Where a service lives when nothing says otherwise.
30
+ *
31
+ * Each service has a URL of its own — `iamUrl`, `runtimesUrl`, … — and this is
32
+ * what each of them falls back to.
33
+ */
34
+ export const DEFAULT_DATALAYER_SERVICE_URL = 'https://prod1.datalayer.run';
28
35
  export const DEFAULT_DATALAYER_CONFIG = {
29
- datalayerUrl: 'https://prod1.datalayer.run',
36
+ iamUrl: DEFAULT_DATALAYER_SERVICE_URL,
30
37
  credits: 100,
31
38
  cpuEnvironment: 'ai-agents-env',
32
39
  gpuEnvironment: 'ai-env',
@@ -37,7 +44,7 @@ export const DEFAULT_DATALAYER_CONFIG = {
37
44
  export function isDatalayerConfig(config) {
38
45
  return (config &&
39
46
  typeof config === 'object' &&
40
- typeof config.datalayerUrl === 'string' &&
47
+ typeof config.iamUrl === 'string' &&
41
48
  typeof config.token === 'string' &&
42
49
  typeof config.credits === 'number' &&
43
50
  typeof config.cpuEnvironment === 'string' &&
@@ -51,10 +58,10 @@ export function isDatalayerConfig(config) {
51
58
  export function mergeConfigWithDefaults(config) {
52
59
  if (!config)
53
60
  return undefined;
54
- // If we have required fields (token and datalayerUrl), merge with defaults for optional fields
55
- if (config.token && config.datalayerUrl) {
61
+ // If we have required fields (token and iamUrl), merge with defaults for optional fields
62
+ if (config.token && config.iamUrl) {
56
63
  return {
57
- datalayerUrl: config.datalayerUrl,
64
+ iamUrl: config.iamUrl,
58
65
  token: config.token,
59
66
  credits: config.credits ?? DEFAULT_DATALAYER_CONFIG.credits,
60
67
  cpuEnvironment: config.cpuEnvironment ?? DEFAULT_DATALAYER_CONFIG.cpuEnvironment,
@@ -270,6 +270,8 @@ export declare const useCache: ({ loginRoute }?: CacheProps) => {
270
270
  email: string;
271
271
  firstName: string;
272
272
  lastName: string;
273
+ avatarIcon?: string;
274
+ banner?: string;
273
275
  }, unknown>;
274
276
  useWhoami: () => import("@tanstack/react-query").UseQueryResult<any, Error>;
275
277
  useChangePassword: () => import("@tanstack/react-query").UseMutationResult<any, Error, {
@@ -39,7 +39,7 @@
39
39
  import { useQuery, useQueries, useMutation, useQueryClient, } from '@tanstack/react-query';
40
40
  import { BOOTSTRAP_USER_ONBOARDING, asContact, asDatasource, asInvite, asOrganization, asSecret, asTeam, asToken, asUser, } from '../models/index.js';
41
41
  import { asPage, asSpace } from './cacheConverters.js';
42
- import { useCoreStore, useIAMStore } from '../state/index.js';
42
+ import { useCoreStore, useIAMStore, profileStore } from '../state/index.js';
43
43
  import { asDisplayName, namesAsInitials, asArray } from '../utils/index.js';
44
44
  import { useDatalayer } from './useDatalayer.js';
45
45
  import { useAuthorization } from './useAuthorization.js';
@@ -977,7 +977,11 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
977
977
  token,
978
978
  });
979
979
  if (resp.me) {
980
- return toUser(resp.me);
980
+ const me = toUser(resp.me);
981
+ // The full profile — banner and avatar included — feeds the
982
+ // profile store every surface reads.
983
+ profileStore.getState().setProfileFromUser(me);
984
+ return me;
981
985
  }
982
986
  return null;
983
987
  },
@@ -989,15 +993,18 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
989
993
  */
990
994
  const useUpdateMe = () => {
991
995
  return useMutation({
992
- mutationFn: async ({ email, firstName, lastName, }) => {
996
+ mutationFn: async ({ email, firstName, lastName, avatarIcon, banner, }) => {
993
997
  return requestDatalayer({
994
998
  url: `${configuration.iamUrl}/api/iam/v1/me`,
995
999
  method: 'PUT',
996
- body: { email, firstName, lastName },
1000
+ body: { email, firstName, lastName, avatarIcon, banner },
997
1001
  });
998
1002
  },
999
1003
  onSuccess: () => {
1000
1004
  queryClient.invalidateQueries({ queryKey: queryKeys.auth.me() });
1005
+ // Every cached view of the user — the public profile page, the
1006
+ // admin list and detail, the search seeds — is now out of date.
1007
+ queryClient.invalidateQueries({ queryKey: queryKeys.users.all() });
1001
1008
  },
1002
1009
  });
1003
1010
  };
@@ -1105,11 +1112,16 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
1105
1112
  first_name_t: profile.first_name,
1106
1113
  last_name_t: profile.last_name,
1107
1114
  avatar_url_s: profile.avatar_url,
1115
+ avatar_icon_s: profile.avatar_icon_s,
1116
+ banner_s: profile.banner_s,
1108
1117
  origin_s: profile.origin,
1109
1118
  });
1119
+ // Stale seeding (updatedAt: 0): the public profile is a partial
1120
+ // shape, and a fresh-marked seed would overwrite a richer detail
1121
+ // entry and be served as the whole truth for the staleTime.
1110
1122
  if (mappedUser) {
1111
- queryClient.setQueryData(queryKeys.users.detail(mappedUser.id), mappedUser);
1112
- queryClient.setQueryData(queryKeys.users.byHandle(mappedUser.handle), mappedUser);
1123
+ queryClient.setQueryData(queryKeys.users.detail(mappedUser.id), mappedUser, { updatedAt: 0 });
1124
+ queryClient.setQueryData(queryKeys.users.byHandle(mappedUser.handle), mappedUser, { updatedAt: 0 });
1113
1125
  }
1114
1126
  }
1115
1127
  return profile;
@@ -1135,10 +1147,16 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
1135
1147
  if (resp.success && resp.users) {
1136
1148
  const users = resp.users.map((u) => {
1137
1149
  const user = toUser(u);
1138
- // Pre-populate individual caches
1150
+ // Pre-populate individual caches so a detail view renders
1151
+ // instantly — but as STALE data (updatedAt: 0): a search answer
1152
+ // can be a partial shape (a member's search has no email or
1153
+ // settings), and a fresh-marked partial would be served as the
1154
+ // whole truth for the staleTime. Stale renders and revalidates.
1139
1155
  if (user) {
1140
- queryClient.setQueryData(queryKeys.users.detail(user.id), user);
1141
- queryClient.setQueryData(queryKeys.users.byHandle(user.handle), user);
1156
+ queryClient.setQueryData(queryKeys.users.detail(user.id), user, {
1157
+ updatedAt: 0,
1158
+ });
1159
+ queryClient.setQueryData(queryKeys.users.byHandle(user.handle), user, { updatedAt: 0 });
1142
1160
  }
1143
1161
  return user;
1144
1162
  });
@@ -1173,9 +1191,11 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
1173
1191
  const users = resp.users
1174
1192
  .map((u) => {
1175
1193
  const user = toUser(u);
1194
+ // Stale seeding, as in useSearchUsers: the bulk answer may be
1195
+ // a partial shape, so it renders but revalidates.
1176
1196
  if (user) {
1177
- queryClient.setQueryData(queryKeys.users.detail(user.id), user);
1178
- queryClient.setQueryData(queryKeys.users.byHandle(user.handle), user);
1197
+ queryClient.setQueryData(queryKeys.users.detail(user.id), user, { updatedAt: 0 });
1198
+ queryClient.setQueryData(queryKeys.users.byHandle(user.handle), user, { updatedAt: 0 });
1179
1199
  }
1180
1200
  return user;
1181
1201
  })
@@ -1369,6 +1389,8 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
1369
1389
  body: {
1370
1390
  name: organization.name,
1371
1391
  description: organization.description,
1392
+ avatarIcon: organization.avatarIcon,
1393
+ banner: organization.banner,
1372
1394
  },
1373
1395
  });
1374
1396
  },
@@ -1479,6 +1501,8 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
1479
1501
  body: {
1480
1502
  name: team.name,
1481
1503
  description: team.description,
1504
+ avatarIcon: team.avatarIcon,
1505
+ banner: team.banner,
1482
1506
  },
1483
1507
  });
1484
1508
  },
@@ -19,6 +19,8 @@ export type CachedPrincipal = {
19
19
  email?: string;
20
20
  origin?: string;
21
21
  avatarUrl?: string;
22
+ avatarIcon?: string;
23
+ banner?: string;
22
24
  /** Team-specific: parent organization handle. */
23
25
  organizationHandle?: string;
24
26
  /** Team-specific: parent organization display name. */
@@ -18,6 +18,8 @@ const MERGEABLE_KEYS = [
18
18
  'email',
19
19
  'origin',
20
20
  'avatarUrl',
21
+ 'avatarIcon',
22
+ 'banner',
21
23
  'organizationHandle',
22
24
  'organizationName',
23
25
  'memberCount',
@@ -83,6 +83,8 @@ export interface UserMeResponse {
83
83
  last_name_t: string;
84
84
  /** Avatar URL for the user (may be empty string) */
85
85
  avatar_url_s: string;
86
+ avatar_icon_s?: string;
87
+ banner_s?: string;
86
88
  /** Array of roles assigned to the user */
87
89
  roles_ss: string[];
88
90
  /** Allow additional fields that may come from the server */
@@ -127,6 +129,8 @@ export interface ShareablePrincipal {
127
129
  description?: string | null;
128
130
  email?: string | null;
129
131
  avatar_url?: string | null;
132
+ avatar_icon?: string | null;
133
+ banner?: string | null;
130
134
  /** Parent organization UID — set when kind === 'team'. */
131
135
  organization_uid?: string | null;
132
136
  /** Parent organization handle — set when kind === 'team'. */
@@ -151,6 +155,10 @@ export interface PrincipalSearchUser {
151
155
  origin?: string;
152
156
  avatar_url_s?: string;
153
157
  avatarUrl?: string;
158
+ avatar_icon_s?: string;
159
+ avatarIcon?: string;
160
+ banner_s?: string;
161
+ banner?: string;
154
162
  [key: string]: any;
155
163
  }
156
164
  export interface PrincipalSearchTeam {
@@ -160,6 +168,8 @@ export interface PrincipalSearchTeam {
160
168
  description_t?: string;
161
169
  organization_uid?: string;
162
170
  organization_handle_s?: string;
171
+ avatar_icon_s?: string;
172
+ banner_s?: string;
163
173
  [key: string]: any;
164
174
  }
165
175
  export interface PrincipalSearchOrganization {
@@ -16,6 +16,8 @@ export type IBaseOrganization = {
16
16
  name: string;
17
17
  displayName?: string;
18
18
  description: string;
19
+ avatarIcon?: string;
20
+ banner?: string;
19
21
  public: boolean;
20
22
  creationDate: Date;
21
23
  members: IOrganizationMember[];
@@ -25,6 +25,8 @@ export function asOrganization(org) {
25
25
  name: org.name_t,
26
26
  displayName: org.display_name_t || org.name_t,
27
27
  description: org.description_t,
28
+ avatarIcon: org.avatar_icon_s ?? org.avatarIcon,
29
+ banner: org.banner_s ?? org.banner,
28
30
  public: org.public_b,
29
31
  members,
30
32
  teams: [],
@@ -32,6 +32,8 @@ export interface Profile {
32
32
  roles_ss?: string[];
33
33
  /** Avatar URL */
34
34
  avatar_url_s?: string;
35
+ avatar_icon_s?: string;
36
+ banner_s?: string;
35
37
  /** Onboarding state as JSON string */
36
38
  onboarding_s?: string;
37
39
  /** New password request timestamp */
@@ -8,6 +8,8 @@ export type IBaseTeam = {
8
8
  handle: string;
9
9
  name: string;
10
10
  description: string;
11
+ avatarIcon?: string;
12
+ banner?: string;
11
13
  public: boolean;
12
14
  creationDate: Date;
13
15
  lastUpdateDate?: Date;
@@ -18,6 +18,8 @@ export function asTeam(t, organizationId) {
18
18
  type: 'team',
19
19
  name: t.name_t,
20
20
  description: t.description_t,
21
+ avatarIcon: t.avatar_icon_s ?? t.avatarIcon,
22
+ banner: t.banner_s ?? t.banner,
21
23
  public: t.public_b,
22
24
  members,
23
25
  organization: {
@@ -25,6 +25,8 @@ export declare class User implements IUser {
25
25
  credits?: number;
26
26
  creditsCustomerId?: string;
27
27
  avatarUrl?: string;
28
+ avatarIcon?: string;
29
+ banner?: string;
28
30
  origin?: string;
29
31
  invites?: Array<IInvite>;
30
32
  iamProviders: Array<IIAMProviderLinked>;
@@ -63,6 +65,8 @@ export type IUser = IBaseUser & {
63
65
  credits?: number;
64
66
  creditsCustomerId?: string;
65
67
  avatarUrl?: string;
68
+ avatarIcon?: string;
69
+ banner?: string;
66
70
  origin?: string;
67
71
  invites?: Array<IInvite>;
68
72
  iamProviders: Array<IIAMProviderLinked>;
@@ -41,6 +41,8 @@ export class User {
41
41
  credits;
42
42
  creditsCustomerId;
43
43
  avatarUrl;
44
+ avatarIcon;
45
+ banner;
44
46
  origin;
45
47
  invites;
46
48
  iamProviders;
@@ -64,6 +66,8 @@ export class User {
64
66
  this.friendlyName =
65
67
  toFriendlyName(u.first_name_t, u.last_name_t) || u.handle_s;
66
68
  this.avatarUrl = u.avatar_url_s;
69
+ this.avatarIcon = u.avatar_icon_s ?? u.avatarIcon;
70
+ this.banner = u.banner_s ?? u.banner;
67
71
  this.origin = u.origin_s;
68
72
  this.joinDate = u.join_ts_dt ? new Date(u.join_ts_dt) : undefined;
69
73
  this.credits = u.credits_i ? Number(u.credits_i) : 0;
@@ -23,6 +23,8 @@ export interface UserData {
23
23
  last_name_t: string;
24
24
  /** Display name shown in the UI */
25
25
  avatar_url_s: string;
26
+ avatar_icon_s?: string;
27
+ banner_s?: string;
26
28
  /** Additional fields that may be present in the response */
27
29
  [key: string]: any;
28
30
  }
@@ -43,6 +45,8 @@ export interface UserJSON {
43
45
  handle: string;
44
46
  /** URL to the user's avatar image */
45
47
  avatarUrl: string;
48
+ avatarIcon?: string;
49
+ banner?: string;
46
50
  }
47
51
  /**
48
52
  * User model representing a Datalayer platform user.
@@ -65,6 +69,8 @@ export declare class UserDTO {
65
69
  get lastName(): string;
66
70
  get displayName(): string;
67
71
  get avatarUrl(): string;
72
+ get avatarIcon(): string | undefined;
73
+ get banner(): string | undefined;
68
74
  /**
69
75
  * Get user data in camelCase format.
70
76
  * Returns only the core fields that consumers need.
@@ -43,6 +43,12 @@ export class UserDTO {
43
43
  get avatarUrl() {
44
44
  return this._data.avatar_url_s;
45
45
  }
46
+ get avatarIcon() {
47
+ return this._data.avatar_icon_s;
48
+ }
49
+ get banner() {
50
+ return this._data.banner_s;
51
+ }
46
52
  // ========================================================================
47
53
  // Utility Methods
48
54
  // ========================================================================
@@ -63,6 +69,8 @@ export class UserDTO {
63
69
  email: this.email,
64
70
  handle: this.handle,
65
71
  avatarUrl: this.avatarUrl,
72
+ avatarIcon: this.avatarIcon,
73
+ banner: this.banner,
66
74
  };
67
75
  validateJSON(obj, 'User');
68
76
  return obj;
@@ -7,7 +7,6 @@ import { useStore } from 'zustand';
7
7
  import { configLogger } from '../../utils/Logger.js';
8
8
  let loadConfigurationFromServer = true;
9
9
  let initialConfiguration = {
10
- datalayerUrl: 'https://prod1.datalayer.run',
11
10
  token: '',
12
11
  credits: 0,
13
12
  cpuEnvironment: 'ai-agents-env"',
@@ -17,12 +16,14 @@ let initialConfiguration = {
17
16
  loadConfigurationFromServer: true,
18
17
  jupyterServerless: false,
19
18
  iamUrl: 'https://prod1.datalayer.run',
19
+ managerUrl: 'https://prod1.datalayer.run',
20
20
  runtimesUrl: 'https://r1.datalayer.run',
21
+ schedulerUrl: 'https://prod1.datalayer.run',
21
22
  libraryUrl: 'https://prod1.datalayer.run',
22
23
  spacerUrl: 'https://prod1.datalayer.run',
23
24
  aiAgentsUrl: 'https://prod1.datalayer.run',
24
25
  aiInferenceUrl: 'https://prod1.datalayer.run',
25
- mcpServersUrl: 'https://prod1.datalayer.run',
26
+ jupyterMcpServerUrl: 'https://mcp.datalayer.run/mcp',
26
27
  otelUrl: 'https://prod1.datalayer.run',
27
28
  // Defaults to prod so telemetry is consumed from the production OTEL service
28
29
  // even when runtimes export elsewhere (see `getOtelConsumeUrl` and the
@@ -34,8 +35,8 @@ let initialConfiguration = {
34
35
  supportUrl: 'https://prod1.datalayer.run',
35
36
  launcher: {
36
37
  category: 'Datalayer',
37
- name: 'Datalayer Runtimes',
38
- icon: 'https://raw.githubusercontent.com/datalayer/icons/main/svg/data1/jupyter-base.svg',
38
+ name: 'Datalayer',
39
+ icon: null,
39
40
  rank: 1,
40
41
  },
41
42
  brand: {
@@ -10,6 +10,7 @@ import { getStoredToken, getStoredUser, loadRefreshTokenFromCookie, storeToken,
10
10
  import { requestDatalayerAPI, } from '../../api/DatalayerApi.js';
11
11
  import { getCookie, setCookie, deleteCookie } from '../../utils/index.js';
12
12
  import { coreStore } from './CoreState.js';
13
+ import { profileStore } from './ProfileState.js';
13
14
  /**
14
15
  * Limit to warn about low credits in milliseconds.
15
16
  */
@@ -66,6 +67,7 @@ export const iamStore = createStore((set, get) => {
66
67
  logout: () => {
67
68
  storeUser();
68
69
  storeToken();
70
+ profileStore.getState().clearProfile();
69
71
  set({
70
72
  credits: undefined,
71
73
  creditsReservations: [],
@@ -106,6 +108,7 @@ export const iamStore = createStore((set, get) => {
106
108
  ...user,
107
109
  },
108
110
  };
111
+ profileStore.getState().setProfileFromUser(updatedState.user);
109
112
  /*
110
113
  if (state.user?.email && !updatedState.user.email) {
111
114
  updatedState.user.email = state.user.email;
@@ -200,6 +203,9 @@ export const iamStore = createStore((set, get) => {
200
203
  setLogin: (user, token) => set((state) => {
201
204
  storeUser(user);
202
205
  storeToken(token);
206
+ // The profile store is what the surfaces (user menu, sidebars,
207
+ // profile views) read: every flow that learns the user feeds it.
208
+ profileStore.getState().setProfileFromUser(user);
203
209
  return {
204
210
  user,
205
211
  token,
@@ -4,6 +4,58 @@
4
4
  */
5
5
  import { createStore } from 'zustand/vanilla';
6
6
  import { useStore } from 'zustand';
7
+ /**
8
+ * Name of the cookie holding the space last selected by the user.
9
+ *
10
+ * The selection has to survive a reload, as the whole user interface is scoped
11
+ * by it; it lives in a cookie next to the principal context written by
12
+ * `usePrincipalStore`.
13
+ */
14
+ const SPACE_CONTEXT_COOKIE = 'datalayer-space-context';
15
+ const SPACE_CONTEXT_COOKIE_MAX_AGE = 60 * 60 * 24 * 365;
16
+ const readSpaceContextCookie = () => {
17
+ if (typeof document === 'undefined') {
18
+ return undefined;
19
+ }
20
+ const escaped = SPACE_CONTEXT_COOKIE.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
21
+ const match = document.cookie.match(new RegExp(`(?:^|;\\s*)${escaped}=([^;]*)`));
22
+ if (!match?.[1]) {
23
+ return undefined;
24
+ }
25
+ try {
26
+ const parsed = JSON.parse(decodeURIComponent(match[1]));
27
+ if (!parsed || typeof parsed !== 'object' || typeof parsed.id !== 'string') {
28
+ return undefined;
29
+ }
30
+ return {
31
+ id: parsed.id,
32
+ handle: typeof parsed.handle === 'string' ? parsed.handle : undefined,
33
+ name: typeof parsed.name === 'string' ? parsed.name : undefined,
34
+ };
35
+ }
36
+ catch {
37
+ return undefined;
38
+ }
39
+ };
40
+ const writeSpaceContextCookie = (space) => {
41
+ if (typeof document === 'undefined') {
42
+ return;
43
+ }
44
+ if (!space?.id) {
45
+ document.cookie =
46
+ `${SPACE_CONTEXT_COOKIE}=;` + ' path=/; max-age=0; SameSite=Lax';
47
+ return;
48
+ }
49
+ const value = encodeURIComponent(JSON.stringify({
50
+ id: space.id,
51
+ handle: space.handle,
52
+ name: space.name,
53
+ }));
54
+ document.cookie =
55
+ `${SPACE_CONTEXT_COOKIE}=${value};` +
56
+ ` path=/; max-age=${SPACE_CONTEXT_COOKIE_MAX_AGE}; SameSite=Lax`;
57
+ };
58
+ const initialSpaceContext = readSpaceContextCookie();
7
59
  export const layoutStore = createStore((set, get) => ({
8
60
  backdrop: undefined,
9
61
  banner: undefined,
@@ -15,7 +67,8 @@ export const layoutStore = createStore((set, get) => ({
15
67
  organization: undefined,
16
68
  rightPortal: undefined,
17
69
  screenCapture: undefined,
18
- space: undefined,
70
+ // Restored from the cookie; the full space is hydrated once loaded.
71
+ space: initialSpaceContext,
19
72
  team: undefined,
20
73
  hideBackdrop: () => set((state) => ({
21
74
  backdrop: { open: false, message: undefined },
@@ -65,13 +118,15 @@ export const layoutStore = createStore((set, get) => ({
65
118
  };
66
119
  }),
67
120
  updateLayoutSpace: (space) => set((state) => {
121
+ const next = space
122
+ ? {
123
+ ...state.space,
124
+ ...space,
125
+ }
126
+ : undefined;
127
+ writeSpaceContextCookie(next);
68
128
  return {
69
- space: space
70
- ? {
71
- ...state.space,
72
- ...space,
73
- }
74
- : undefined,
129
+ space: next,
75
130
  };
76
131
  }),
77
132
  setItem: (item) => set((state) => ({ item })),
@@ -4,6 +4,12 @@ export type IOrganizationState = {
4
4
  };
5
5
  export type OrganizationState = IOrganizationState & {
6
6
  updateOrganizations: (organizations: IAnyOrganization[]) => void;
7
+ /**
8
+ * Merge what just changed into the organization of the list — the way a
9
+ * settings save keeps every surface reading this store current (avatar,
10
+ * banner, name…) without refetching the list.
11
+ */
12
+ updateOrganization: (organization: Partial<IAnyOrganization> & Pick<IAnyOrganization, 'id'>) => void;
7
13
  };
8
14
  export declare const organizationStore: import("zustand").StoreApi<OrganizationState>;
9
15
  export declare function useOrganizationStore(): OrganizationState;
@@ -9,6 +9,11 @@ export const organizationStore = createStore((set, get) => ({
9
9
  updateOrganizations: (organizations) => set((state) => ({
10
10
  organizations,
11
11
  })),
12
+ updateOrganization: organization => set((state) => ({
13
+ organizations: state.organizations.map(known => known.id === organization.id
14
+ ? { ...known, ...organization }
15
+ : known),
16
+ })),
12
17
  }));
13
18
  export function useOrganizationStore(selector) {
14
19
  return useStore(organizationStore, selector);
@@ -0,0 +1,34 @@
1
+ import type { IUser } from '../../models';
2
+ /** The profile of the authenticated user, as the surfaces show it. */
3
+ export type IProfile = {
4
+ id?: string;
5
+ uid?: string;
6
+ handle?: string;
7
+ email?: string;
8
+ firstName?: string;
9
+ lastName?: string;
10
+ displayName?: string;
11
+ initials?: string;
12
+ avatarUrl?: string;
13
+ avatarIcon?: string;
14
+ banner?: string;
15
+ origin?: string;
16
+ roles?: string[];
17
+ };
18
+ export type ProfileState = {
19
+ profile?: IProfile;
20
+ /** Replace the profile wholesale. */
21
+ setProfile: (profile?: IProfile) => void;
22
+ /** Merge what a flow just learned into the profile. */
23
+ updateProfile: (partial: Partial<IProfile>) => void;
24
+ /** The IAM user as the profile — login and refresh feed through this. */
25
+ setProfileFromUser: (user?: IUser) => void;
26
+ /** Logout. */
27
+ clearProfile: () => void;
28
+ };
29
+ export declare const profileStore: import("zustand").StoreApi<ProfileState>;
30
+ export declare function useProfileStore(): ProfileState;
31
+ export declare function useProfileStore<T>(selector: (state: ProfileState) => T): T;
32
+ /** The profile, for surfaces that show it. */
33
+ export declare const useProfile: () => IProfile | undefined;
34
+ export default useProfileStore;