@datalayer/core 1.0.1 → 1.0.2

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.
@@ -2,11 +2,10 @@
2
2
  * Spacer API exports.
3
3
  *
4
4
  * Provides organized access to spacer functionality including
5
- * spaces, notebooks, cells, lexical documents, and agent spaces.
5
+ * spaces, notebooks, cells, lexical documents, and agent runtimes.
6
6
  *
7
7
  * @module api/spacer
8
8
  */
9
- export * as agentSpaces from './agentSpaces';
10
9
  export * as documents from './documents';
11
10
  export * as healthz from './healthz';
12
11
  export * as items from './items';
@@ -6,11 +6,10 @@
6
6
  * Spacer API exports.
7
7
  *
8
8
  * Provides organized access to spacer functionality including
9
- * spaces, notebooks, cells, lexical documents, and agent spaces.
9
+ * spaces, notebooks, cells, lexical documents, and agent runtimes.
10
10
  *
11
11
  * @module api/spacer
12
12
  */
13
- export * as agentSpaces from './agentSpaces';
14
13
  export * as documents from './documents';
15
14
  export * as healthz from './healthz';
16
15
  export * as items from './items';
@@ -5,6 +5,8 @@ type IBoringAvatarProps = {
5
5
  size?: number;
6
6
  square?: boolean;
7
7
  style?: object;
8
+ /** Custom color palette passed to boring-avatars. When omitted the default Datalayer palette is used. */
9
+ colors?: string[];
8
10
  };
9
- export declare const BoringAvatar: ({ displayName, variant, size, square, style, }: IBoringAvatarProps) => import("react/jsx-runtime").JSX.Element;
11
+ export declare const BoringAvatar: ({ displayName, variant, size, square, style, colors, }: IBoringAvatarProps) => import("react/jsx-runtime").JSX.Element;
10
12
  export default BoringAvatar;
@@ -7,23 +7,24 @@ import BoringAvatars from 'boring-avatars';
7
7
  // export const getRandomBoringAvatarVariant = () => VARIANTS[Math.floor(Math.random() * VARIANTS.length)] as VariantType;
8
8
  const getRandomBoringAvatarVariant = () => 'bauhaus';
9
9
  const RANDOM_BORING_AVATOR_VARIANT = getRandomBoringAvatarVariant();
10
- export const BoringAvatar = ({ displayName = '', variant, size = 40, square = false, style, }) => {
10
+ const DEFAULT_COLORS = [
11
+ '#000000',
12
+ '#146A7C',
13
+ '#16A085',
14
+ '#1ABC9C',
15
+ '#2ECC71',
16
+ '#59595C',
17
+ '#92A1C6',
18
+ '#C20D90',
19
+ '#C271B4',
20
+ '#F0AB3D',
21
+ ];
22
+ export const BoringAvatar = ({ displayName = '', variant, size = 40, square = false, style, colors, }) => {
11
23
  const resolvedVariant = variant ?? RANDOM_BORING_AVATOR_VARIANT;
12
24
  const safeName = String(displayName ?? '');
25
+ const resolvedColors = colors ?? DEFAULT_COLORS;
13
26
  try {
14
- return (_jsx("span", { style: { ...(style || {}) }, children: _jsx(BoringAvatars, { size: size, name: safeName, variant: resolvedVariant, square: square, colors: [
15
- '#000000',
16
- '#146A7C',
17
- '#16A085',
18
- '#1ABC9C',
19
- '#2ECC71',
20
- '#59595C',
21
- '#92A1C6',
22
- '#C20D90',
23
- '#C271B4',
24
- '#F0AB3D',
25
- // '#FFFFFF',
26
- ] }) }));
27
+ return (_jsx("span", { style: { ...(style || {}) }, children: _jsx(BoringAvatars, { size: size, name: safeName, variant: resolvedVariant, square: square, colors: resolvedColors }) }));
27
28
  }
28
29
  catch (error) {
29
30
  console.error('BoringAvatar error:', error);
@@ -1,12 +1,13 @@
1
1
  import type { StoryObj } from '@storybook/react-vite';
2
2
  declare const meta: {
3
3
  title: string;
4
- component: ({ displayName, variant, size, square, style, }: {
4
+ component: ({ displayName, variant, size, square, style, colors, }: {
5
5
  displayName?: string;
6
6
  variant?: "marble" | "beam" | "pixel" | "sunset" | "ring" | "bauhaus" | undefined;
7
7
  size?: number;
8
8
  square?: boolean;
9
9
  style?: object;
10
+ colors?: string[];
10
11
  }) => import("react/jsx-runtime").JSX.Element;
11
12
  tags: string[];
12
13
  parameters: {
@@ -1,3 +1,4 @@
1
+ import { type ReactNode } from 'react';
1
2
  import { DocumentRegistry } from '@jupyterlab/docregistry';
2
3
  import { Contents } from '@jupyterlab/services';
3
4
  /**
@@ -24,6 +25,11 @@ export interface IContentsBrowserProps {
24
25
  * Document registry.
25
26
  */
26
27
  documentRegistry?: DocumentRegistry;
28
+ /**
29
+ * Optional title for the browser heading.
30
+ * Defaults to "Contents Browser".
31
+ */
32
+ title?: ReactNode;
27
33
  }
28
34
  /**
29
35
  * Storage browser component.
@@ -3,7 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
3
3
  * Copyright (c) 2023-2025 Datalayer, Inc.
4
4
  * Distributed under the terms of the Modified BSD License.
5
5
  */
6
- import { useCallback, useEffect, useState } from 'react';
6
+ import { useCallback, useEffect, useState, } from 'react';
7
7
  import { PathExt } from '@jupyterlab/coreutils';
8
8
  import { PromiseDelegate } from '@lumino/coreutils';
9
9
  import { ActionList, ActionMenu, Heading, IconButton, Spinner, TreeView, } from '@primer/react';
@@ -26,7 +26,11 @@ export const CHUNK_SIZE = 1024 * 1024;
26
26
  * Storage browser component.
27
27
  */
28
28
  export function ContentsBrowser(props) {
29
- const { contents, localContents, documentRegistry } = props;
29
+ const { contents, localContents, documentRegistry, title = (_jsx(Heading, { as: "h4", sx: {
30
+ fontSize: 'var(--text-title-size-small)',
31
+ lineHeight: 'var(--text-title-lineHeight-medium)',
32
+ fontWeight: 'var(--text-title-weight-medium)',
33
+ }, children: "Contents Browser" })), } = props;
30
34
  const isMounted = useIsMounted();
31
35
  const { trackAsyncTask } = useToast();
32
36
  const [children, setChildren] = useState(null);
@@ -205,12 +209,7 @@ export function ContentsBrowser(props) {
205
209
  const onSelect = useCallback((item, refresh) => {
206
210
  setSelectedItem(item.path === selectedItem?.path ? null : { ...item, refresh });
207
211
  }, [selectedItem]);
208
- return (_jsxs(Box, { sx: { display: 'grid', gridTemplateAreas: `"header" "content"` }, children: [_jsxs(Box, { sx: { gridArea: 'header', display: 'flex', alignItems: 'center' }, children: [_jsx(Heading, { as: "h4", sx: {
209
- fontSize: 'var(--text-title-size-small)',
210
- lineHeight: 'var(--text-title-lineHeight-medium)',
211
- fontWeight: 'var(--text-title-weight-medium)',
212
- flex: '1 1 auto',
213
- }, children: "Contents Browser" }), _jsxs(Box, { children: [_jsx(IconButton, { variant: "invisible", "aria-label": 'Refresh contents browser.', title: 'Refresh contents browser.', icon: CounterClockWiseIcon, onClick: refresh }), _jsx(UploadIconButton, { label: 'Upload a file', multiple: true, upload: upload })] })] }), isLoading ? (_jsx(Box, { sx: {
212
+ return (_jsxs(Box, { sx: { display: 'grid', gridTemplateAreas: `"header" "content"` }, children: [_jsxs(Box, { sx: { gridArea: 'header', display: 'flex', alignItems: 'center' }, children: [_jsx(Box, { sx: { flex: '1 1 auto' }, children: title }), _jsxs(Box, { children: [_jsx(IconButton, { variant: "invisible", "aria-label": 'Refresh contents browser.', title: 'Refresh contents browser.', icon: CounterClockWiseIcon, onClick: refresh }), _jsx(UploadIconButton, { label: 'Upload a file', multiple: true, upload: upload })] })] }), isLoading ? (_jsx(Box, { sx: {
214
213
  gridArea: 'content',
215
214
  display: 'flex',
216
215
  alignItems: 'center',
@@ -23,5 +23,7 @@ export * from './useScreencapture';
23
23
  export * from './useToast';
24
24
  export * from './useUpload';
25
25
  export * from './useUser';
26
+ export * from './useProjects';
27
+ export * from './useProjectStore';
26
28
  export * from './useVisibilityObserver';
27
29
  export * from './useWindowSize';
@@ -28,5 +28,7 @@ export * from './useScreencapture';
28
28
  export * from './useToast';
29
29
  export * from './useUpload';
30
30
  export * from './useUser';
31
+ export * from './useProjects';
32
+ export * from './useProjectStore';
31
33
  export * from './useVisibilityObserver';
32
34
  export * from './useWindowSize';
@@ -36,7 +36,6 @@
36
36
  */
37
37
  import { UseQueryOptions, UseMutationOptions } from '@tanstack/react-query';
38
38
  import { IAnyOrganization, IAnySpace, IAssignment, ICell, IContact, ICourse, IDataset, IDatasource, IDocument, IEnvironment, IExercise, IIAMToken, ILesson, INotebook, IOrganization, IPage, ISchool, ISecret, ITeam, IUser, IUserOnboarding, IUserSettings } from '../models';
39
- import type { AgentSpaceData, CreateAgentSpaceRequest, UpdateAgentSpaceRequest } from '../api/spacer/agentSpaces';
40
39
  export type CacheProps = {
41
40
  loginRoute?: string;
42
41
  };
@@ -213,13 +212,6 @@ export declare const queryKeys: {
213
212
  readonly bySpace: (spaceId: string) => readonly ["items", "space", string];
214
213
  readonly search: (opts: ISearchOpts) => readonly ["items", "search", ISearchOpts];
215
214
  };
216
- readonly agentSpaces: {
217
- readonly all: () => readonly ["agentSpaces"];
218
- readonly lists: () => readonly ["agentSpaces", "list"];
219
- readonly details: () => readonly ["agentSpaces", "detail"];
220
- readonly detail: (id: string) => readonly ["agentSpaces", "detail", string];
221
- readonly public: () => readonly ["agentSpaces", "public"];
222
- };
223
215
  readonly agentRuntimes: {
224
216
  readonly all: () => readonly ["agentRuntimes"];
225
217
  readonly lists: () => readonly ["agentRuntimes", "list"];
@@ -488,20 +480,6 @@ export declare const useCache: ({ loginRoute }?: CacheProps) => {
488
480
  spaceHandle?: string;
489
481
  }, unknown>;
490
482
  useExportSpace: () => import("@tanstack/react-query").UseMutationResult<any, Error, string, unknown>;
491
- useAgentSpace: (uid: string | undefined) => import("@tanstack/react-query").UseQueryResult<AgentSpaceData, Error>;
492
- useAgentSpaces: () => import("@tanstack/react-query").UseQueryResult<AgentSpaceData[], Error>;
493
- usePublicAgentSpaces: () => import("@tanstack/react-query").UseQueryResult<AgentSpaceData[], Error>;
494
- useCreateAgentSpace: () => import("@tanstack/react-query").UseMutationResult<any, Error, CreateAgentSpaceRequest, unknown>;
495
- useUpdateAgentSpace: () => import("@tanstack/react-query").UseMutationResult<any, Error, {
496
- uid: string;
497
- data: UpdateAgentSpaceRequest;
498
- }, unknown>;
499
- useDeleteAgentSpace: () => import("@tanstack/react-query").UseMutationResult<any, Error, string, unknown>;
500
- useMakeAgentSpacePublic: () => import("@tanstack/react-query").UseMutationResult<any, Error, string, unknown>;
501
- useMakeAgentSpacePrivate: () => import("@tanstack/react-query").UseMutationResult<any, Error, string, unknown>;
502
- useRefreshAgentSpace: () => (uid: string) => void;
503
- useRefreshAgentSpaces: () => () => void;
504
- useRefreshPublicAgentSpaces: () => () => void;
505
483
  useAgentRuntime: (podName: string | undefined) => import("@tanstack/react-query").UseQueryResult<{
506
484
  status: "running" | "paused" | "starting" | "terminated" | "archived";
507
485
  name: string;
@@ -520,7 +498,7 @@ export declare const useCache: ({ loginRoute }?: CacheProps) => {
520
498
  burning_rate?: number;
521
499
  ingress?: string;
522
500
  token?: string;
523
- agentSpec?: AgentSpaceData["agentSpec"];
501
+ agentSpec?: any;
524
502
  }, Error>;
525
503
  useAgentRuntimes: () => import("@tanstack/react-query").UseQueryResult<{
526
504
  status: "running" | "paused" | "starting" | "terminated" | "archived";
@@ -540,7 +518,7 @@ export declare const useCache: ({ loginRoute }?: CacheProps) => {
540
518
  burning_rate?: number;
541
519
  ingress?: string;
542
520
  token?: string;
543
- agentSpec?: AgentSpaceData["agentSpec"];
521
+ agentSpec?: any;
544
522
  }[], Error>;
545
523
  useCreateAgentRuntime: () => import("@tanstack/react-query").UseMutationResult<any, Error, {
546
524
  environmentName?: string;
@@ -1036,13 +1014,6 @@ export declare const useCache: ({ loginRoute }?: CacheProps) => {
1036
1014
  readonly bySpace: (spaceId: string) => readonly ["items", "space", string];
1037
1015
  readonly search: (opts: ISearchOpts) => readonly ["items", "search", ISearchOpts];
1038
1016
  };
1039
- readonly agentSpaces: {
1040
- readonly all: () => readonly ["agentSpaces"];
1041
- readonly lists: () => readonly ["agentSpaces", "list"];
1042
- readonly details: () => readonly ["agentSpaces", "detail"];
1043
- readonly detail: (id: string) => readonly ["agentSpaces", "detail", string];
1044
- readonly public: () => readonly ["agentSpaces", "public"];
1045
- };
1046
1017
  readonly agentRuntimes: {
1047
1018
  readonly all: () => readonly ["agentRuntimes"];
1048
1019
  readonly lists: () => readonly ["agentRuntimes", "list"];
@@ -272,14 +272,6 @@ export const queryKeys = {
272
272
  bySpace: (spaceId) => [...queryKeys.items.all(), 'space', spaceId],
273
273
  search: (opts) => [...queryKeys.items.all(), 'search', opts],
274
274
  },
275
- // Agent Spaces
276
- agentSpaces: {
277
- all: () => ['agentSpaces'],
278
- lists: () => [...queryKeys.agentSpaces.all(), 'list'],
279
- details: () => [...queryKeys.agentSpaces.all(), 'detail'],
280
- detail: (id) => [...queryKeys.agentSpaces.details(), id],
281
- public: () => [...queryKeys.agentSpaces.all(), 'public'],
282
- },
283
275
  // Agent Runtimes (runtimes with ai-agents environment)
284
276
  agentRuntimes: {
285
277
  all: () => ['agentRuntimes'],
@@ -1370,219 +1362,6 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
1370
1362
  },
1371
1363
  });
1372
1364
  };
1373
- // ============================================================================
1374
- // Agent Spaces Hooks
1375
- // ============================================================================
1376
- /**
1377
- * Get agent space by ID
1378
- */
1379
- const useAgentSpace = (uid) => {
1380
- return useQuery({
1381
- queryKey: queryKeys.agentSpaces.detail(uid || ''),
1382
- queryFn: async () => {
1383
- const resp = await requestDatalayer({
1384
- url: `${configuration.spacerRunUrl}/api/spacer/v1/agent-spaces/${uid}`,
1385
- method: 'GET',
1386
- });
1387
- if (resp.success && resp.agentSpace) {
1388
- return resp.agentSpace;
1389
- }
1390
- throw new Error(resp.message || 'Failed to fetch agent space');
1391
- },
1392
- ...DEFAULT_QUERY_OPTIONS,
1393
- enabled: !!uid,
1394
- });
1395
- };
1396
- /**
1397
- * List user's agent spaces
1398
- */
1399
- const useAgentSpaces = () => {
1400
- return useQuery({
1401
- queryKey: queryKeys.agentSpaces.lists(),
1402
- queryFn: async () => {
1403
- const resp = await requestDatalayer({
1404
- url: `${configuration.spacerRunUrl}/api/spacer/v1/agent-spaces`,
1405
- method: 'GET',
1406
- });
1407
- if (resp.success && resp.agentSpaces) {
1408
- const agentSpaces = resp.agentSpaces;
1409
- // Set detail cache for each agent space
1410
- agentSpaces.forEach((agentSpace) => {
1411
- queryClient.setQueryData(queryKeys.agentSpaces.detail(agentSpace.id), agentSpace);
1412
- });
1413
- return agentSpaces;
1414
- }
1415
- return [];
1416
- },
1417
- ...DEFAULT_QUERY_OPTIONS,
1418
- enabled: !!user,
1419
- });
1420
- };
1421
- /**
1422
- * List public agent spaces (Library)
1423
- */
1424
- const usePublicAgentSpaces = () => {
1425
- return useQuery({
1426
- queryKey: queryKeys.agentSpaces.public(),
1427
- queryFn: async () => {
1428
- const resp = await requestDatalayer({
1429
- url: `${configuration.spacerRunUrl}/api/spacer/v1/agent-spaces/public`,
1430
- method: 'GET',
1431
- });
1432
- if (resp.success && resp.agentSpaces) {
1433
- return resp.agentSpaces;
1434
- }
1435
- return [];
1436
- },
1437
- ...DEFAULT_QUERY_OPTIONS,
1438
- });
1439
- };
1440
- /**
1441
- * Create agent space
1442
- */
1443
- const useCreateAgentSpace = () => {
1444
- return useMutation({
1445
- mutationFn: async (data) => {
1446
- return requestDatalayer({
1447
- url: `${configuration.spacerRunUrl}/api/spacer/v1/agent-spaces`,
1448
- method: 'POST',
1449
- body: data,
1450
- });
1451
- },
1452
- onSuccess: resp => {
1453
- if (resp.success && resp.agentSpace) {
1454
- const agentSpace = resp.agentSpace;
1455
- // Set detail cache
1456
- queryClient.setQueryData(queryKeys.agentSpaces.detail(agentSpace.id), agentSpace);
1457
- // Invalidate all agent space queries
1458
- queryClient.invalidateQueries({
1459
- queryKey: queryKeys.agentSpaces.all(),
1460
- });
1461
- }
1462
- },
1463
- });
1464
- };
1465
- /**
1466
- * Update agent space
1467
- */
1468
- const useUpdateAgentSpace = () => {
1469
- return useMutation({
1470
- mutationFn: async ({ uid, data, }) => {
1471
- return requestDatalayer({
1472
- url: `${configuration.spacerRunUrl}/api/spacer/v1/agent-spaces/${uid}`,
1473
- method: 'PUT',
1474
- body: data,
1475
- });
1476
- },
1477
- onSuccess: (resp, { uid }) => {
1478
- if (resp.success) {
1479
- // Invalidate detail cache
1480
- queryClient.invalidateQueries({
1481
- queryKey: queryKeys.agentSpaces.detail(uid),
1482
- });
1483
- // Invalidate all agent space queries
1484
- queryClient.invalidateQueries({
1485
- queryKey: queryKeys.agentSpaces.all(),
1486
- });
1487
- }
1488
- },
1489
- });
1490
- };
1491
- /**
1492
- * Delete agent space
1493
- */
1494
- const useDeleteAgentSpace = () => {
1495
- return useMutation({
1496
- mutationFn: async (uid) => {
1497
- return requestDatalayer({
1498
- url: `${configuration.spacerRunUrl}/api/spacer/v1/agent-spaces/${uid}`,
1499
- method: 'DELETE',
1500
- });
1501
- },
1502
- onSuccess: () => {
1503
- // Invalidate all agent space queries
1504
- queryClient.invalidateQueries({
1505
- queryKey: queryKeys.agentSpaces.all(),
1506
- });
1507
- },
1508
- });
1509
- };
1510
- /**
1511
- * Make agent space public
1512
- */
1513
- const useMakeAgentSpacePublic = () => {
1514
- return useMutation({
1515
- mutationFn: async (uid) => {
1516
- return requestDatalayer({
1517
- url: `${configuration.spacerRunUrl}/api/spacer/v1/agent-spaces/${uid}/public`,
1518
- method: 'POST',
1519
- });
1520
- },
1521
- onSuccess: (resp, uid) => {
1522
- if (resp.success) {
1523
- queryClient.invalidateQueries({
1524
- queryKey: queryKeys.agentSpaces.detail(uid),
1525
- });
1526
- queryClient.invalidateQueries({
1527
- queryKey: queryKeys.agentSpaces.all(),
1528
- });
1529
- }
1530
- },
1531
- });
1532
- };
1533
- /**
1534
- * Make agent space private
1535
- */
1536
- const useMakeAgentSpacePrivate = () => {
1537
- return useMutation({
1538
- mutationFn: async (uid) => {
1539
- return requestDatalayer({
1540
- url: `${configuration.spacerRunUrl}/api/spacer/v1/agent-spaces/${uid}/private`,
1541
- method: 'POST',
1542
- });
1543
- },
1544
- onSuccess: (resp, uid) => {
1545
- if (resp.success) {
1546
- queryClient.invalidateQueries({
1547
- queryKey: queryKeys.agentSpaces.detail(uid),
1548
- });
1549
- queryClient.invalidateQueries({
1550
- queryKey: queryKeys.agentSpaces.all(),
1551
- });
1552
- }
1553
- },
1554
- });
1555
- };
1556
- /**
1557
- * Refresh agent space data
1558
- */
1559
- const useRefreshAgentSpace = () => {
1560
- return (uid) => {
1561
- queryClient.invalidateQueries({
1562
- queryKey: queryKeys.agentSpaces.detail(uid),
1563
- });
1564
- };
1565
- };
1566
- /**
1567
- * Refresh agent spaces list
1568
- */
1569
- const useRefreshAgentSpaces = () => {
1570
- return () => {
1571
- queryClient.invalidateQueries({
1572
- queryKey: queryKeys.agentSpaces.all(),
1573
- });
1574
- };
1575
- };
1576
- /**
1577
- * Refresh public agent spaces list
1578
- */
1579
- const useRefreshPublicAgentSpaces = () => {
1580
- return () => {
1581
- queryClient.invalidateQueries({
1582
- queryKey: queryKeys.agentSpaces.public(),
1583
- });
1584
- };
1585
- };
1586
1365
  /**
1587
1366
  * List agent runtimes (runtimes with ai-agents-env environment)
1588
1367
  */
@@ -6538,18 +6317,6 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
6538
6317
  useRefreshUserSpace,
6539
6318
  useRefreshLayout,
6540
6319
  useExportSpace,
6541
- // Agent Spaces
6542
- useAgentSpace,
6543
- useAgentSpaces,
6544
- usePublicAgentSpaces,
6545
- useCreateAgentSpace,
6546
- useUpdateAgentSpace,
6547
- useDeleteAgentSpace,
6548
- useMakeAgentSpacePublic,
6549
- useMakeAgentSpacePrivate,
6550
- useRefreshAgentSpace,
6551
- useRefreshAgentSpaces,
6552
- useRefreshPublicAgentSpaces,
6553
6320
  // Agent Runtimes (runtimes with ai-agents environment)
6554
6321
  useAgentRuntime,
6555
6322
  useAgentRuntimes,
@@ -0,0 +1,58 @@
1
+ import type { Session } from '@jupyterlab/services';
2
+ import type { IKernelConnection } from '@jupyterlab/services/lib/kernel/kernel';
3
+ /**
4
+ * Per-project runtime state tracked in memory.
5
+ *
6
+ * This is **not** persisted — it represents live runtime objects
7
+ * (kernel connections, sessions) that only exist while the project
8
+ * view is mounted.
9
+ */
10
+ export type ProjectRuntimeEntry = {
11
+ /** The Jupyter session connection (notebook ↔ kernel bridge). */
12
+ sessionConnection?: Session.ISessionConnection;
13
+ /** The assigned agent pod name (mirrors project.attachedAgentPodName). */
14
+ agentPodName?: string;
15
+ /** Display name of the assigned agent. */
16
+ agentName?: string;
17
+ /** Agent runtime status (running, starting, terminated, etc.). */
18
+ agentStatus?: string;
19
+ /** The agent spec ID used to create the runtime. */
20
+ agentSpecId?: string;
21
+ };
22
+ export type ProjectStoreState = {
23
+ /** Map of projectId → runtime entry. */
24
+ projects: Record<string, ProjectRuntimeEntry>;
25
+ /** The currently active/viewed project ID (for sidebar highlighting). */
26
+ currentProjectId: string | undefined;
27
+ /** Set the currently active project (call on mount, clear on unmount). */
28
+ setCurrentProjectId: (projectId: string | undefined) => void;
29
+ /** Set or update the session connection for a project. */
30
+ setSessionConnection: (projectId: string, sessionConnection: Session.ISessionConnection | undefined) => void;
31
+ /** Set the assigned agent info for a project. */
32
+ setAgent: (projectId: string, agentPodName: string | undefined, agentName?: string, agentStatus?: string, agentSpecId?: string) => void;
33
+ /** Remove all runtime state for a project (e.g. on unmount). */
34
+ clearProject: (projectId: string) => void;
35
+ /** Get the kernel connection for a project, or undefined. */
36
+ getKernel: (projectId: string) => IKernelConnection | null | undefined;
37
+ /** Get the full entry for a project. */
38
+ getEntry: (projectId: string) => ProjectRuntimeEntry | undefined;
39
+ };
40
+ /**
41
+ * Zustand store for per-project runtime state (kernel connections, agents).
42
+ *
43
+ * Not persisted — the data is ephemeral and only valid while the project
44
+ * component is mounted and a kernel/agent is active.
45
+ *
46
+ * Usage:
47
+ * ```ts
48
+ * import { useProjectStore } from '@datalayer/core/lib/hooks';
49
+ *
50
+ * // In Project.tsx — store session when Notebook connects:
51
+ * const setSessionConnection = useProjectStore(s => s.setSessionConnection);
52
+ * <Notebook onSessionConnection={sc => setSessionConnection(projectId, sc)} />
53
+ *
54
+ * // Anywhere — read the kernel for a project:
55
+ * const kernel = useProjectStore(s => s.getKernel(projectId));
56
+ * ```
57
+ */
58
+ export declare const useProjectStore: import("zustand").UseBoundStore<import("zustand").StoreApi<ProjectStoreState>>;
@@ -0,0 +1,64 @@
1
+ /*
2
+ * Copyright (c) 2023-2025 Datalayer, Inc.
3
+ * Distributed under the terms of the Modified BSD License.
4
+ */
5
+ import { create } from 'zustand';
6
+ /**
7
+ * Zustand store for per-project runtime state (kernel connections, agents).
8
+ *
9
+ * Not persisted — the data is ephemeral and only valid while the project
10
+ * component is mounted and a kernel/agent is active.
11
+ *
12
+ * Usage:
13
+ * ```ts
14
+ * import { useProjectStore } from '@datalayer/core/lib/hooks';
15
+ *
16
+ * // In Project.tsx — store session when Notebook connects:
17
+ * const setSessionConnection = useProjectStore(s => s.setSessionConnection);
18
+ * <Notebook onSessionConnection={sc => setSessionConnection(projectId, sc)} />
19
+ *
20
+ * // Anywhere — read the kernel for a project:
21
+ * const kernel = useProjectStore(s => s.getKernel(projectId));
22
+ * ```
23
+ */
24
+ export const useProjectStore = create()((set, get) => ({
25
+ projects: {},
26
+ currentProjectId: undefined,
27
+ setCurrentProjectId: projectId => set({ currentProjectId: projectId }),
28
+ setSessionConnection: (projectId, sessionConnection) => set(state => ({
29
+ projects: {
30
+ ...state.projects,
31
+ [projectId]: {
32
+ ...state.projects[projectId],
33
+ sessionConnection,
34
+ },
35
+ },
36
+ })),
37
+ setAgent: (projectId, agentPodName, agentName, agentStatus, agentSpecId) => set(state => ({
38
+ projects: {
39
+ ...state.projects,
40
+ [projectId]: {
41
+ ...state.projects[projectId],
42
+ agentPodName,
43
+ agentName,
44
+ // When removing the agent, also clear the status.
45
+ // When setting, use provided status or preserve existing.
46
+ agentStatus: agentPodName !== undefined
47
+ ? (agentStatus ?? state.projects[projectId]?.agentStatus)
48
+ : undefined,
49
+ agentSpecId: agentPodName !== undefined
50
+ ? (agentSpecId ?? state.projects[projectId]?.agentSpecId)
51
+ : undefined,
52
+ },
53
+ },
54
+ })),
55
+ clearProject: projectId => set(state => {
56
+ const { [projectId]: _, ...rest } = state.projects;
57
+ return { projects: rest };
58
+ }),
59
+ getKernel: projectId => {
60
+ const entry = get().projects[projectId];
61
+ return entry?.sessionConnection?.kernel;
62
+ },
63
+ getEntry: projectId => get().projects[projectId],
64
+ }));