@datalayer/agent-runtimes 1.3.10 → 1.3.11

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 (56) hide show
  1. package/lib/api/runtimes/environments.d.ts +22 -1
  2. package/lib/api/runtimes/environments.js +31 -0
  3. package/lib/chat/ChatFloating.d.ts +8 -1
  4. package/lib/chat/ChatFloating.js +5 -2
  5. package/lib/chat/display/ButtonGlow.d.ts +9 -0
  6. package/lib/chat/display/ButtonGlow.js +60 -0
  7. package/lib/chat/display/EmptyState.js +4 -1
  8. package/lib/chat/header/ChatHeaderBase.js +4 -56
  9. package/lib/chat/header/ChatViewModeToggle.d.ts +29 -0
  10. package/lib/chat/header/ChatViewModeToggle.js +55 -0
  11. package/lib/chat/index.d.ts +1 -0
  12. package/lib/chat/index.js +1 -0
  13. package/lib/components/code-sandboxes/CodeSandboxLauncher.d.ts +8 -0
  14. package/lib/components/code-sandboxes/CodeSandboxLauncher.js +54 -5
  15. package/lib/components/code-sandboxes/codeSandboxEnvironments.d.ts +12 -1
  16. package/lib/components/code-sandboxes/codeSandboxEnvironments.js +38 -1
  17. package/lib/components/icons/ArtifactIcon.js +2 -1
  18. package/lib/examples/DecksAgent.d.ts +1 -1
  19. package/lib/examples/DecksAgent.js +1 -1
  20. package/lib/loop/core/index.d.ts +11 -2
  21. package/lib/loop/embed/LoopEmbed.js +6 -0
  22. package/lib/loop/plugins/chat/ChatView.js +14 -5
  23. package/lib/loop/plugins/page-layout/index.d.ts +36 -0
  24. package/lib/loop/plugins/page-layout/index.js +87 -7
  25. package/lib/loop/presets.d.ts +14 -0
  26. package/lib/loop/presets.js +3 -1
  27. package/lib/models/Deck.d.ts +14 -0
  28. package/lib/models/Deck.js +5 -0
  29. package/lib/models/Environment.d.ts +117 -0
  30. package/lib/models/ItemType.d.ts +1 -1
  31. package/lib/models/Runtime.d.ts +6 -0
  32. package/lib/models/SpaceItem.d.ts +2 -1
  33. package/lib/models/environments.generated.d.ts +29 -1
  34. package/lib/models/environments.generated.js +1 -1
  35. package/lib/models/index.d.ts +1 -0
  36. package/lib/models/index.js +1 -0
  37. package/lib/protocols/BrowserAgentAdapter.d.ts +9 -1
  38. package/lib/protocols/BrowserAgentAdapter.js +11 -4
  39. package/lib/runtimes/actions.d.ts +5 -0
  40. package/lib/runtimes/actions.js +19 -0
  41. package/lib/specs/agents/agents.d.ts +2 -1
  42. package/lib/specs/agents/agents.js +314 -130
  43. package/lib/specs/frontendTools.d.ts +1 -0
  44. package/lib/specs/frontendTools.js +31 -0
  45. package/lib/specs/mcpServers.js +13 -13
  46. package/lib/specs/models.js +4 -4
  47. package/lib/specs/outputs.js +9 -0
  48. package/lib/specs/skills.js +7 -7
  49. package/lib/specs/teams/teams.js +1 -1
  50. package/lib/types/outputs.d.ts +2 -0
  51. package/package.json +1 -1
  52. package/scripts/codegen/generate_mcp_servers.py +2 -2
  53. package/scripts/codegen/generate_outputs.py +2 -0
  54. package/scripts/codegen/generate_skills.py +2 -2
  55. package/scripts/codegen/__pycache__/compose.cpython-313.pyc +0 -0
  56. package/scripts/codegen/__pycache__/versioning.cpython-313.pyc +0 -0
@@ -1,5 +1,5 @@
1
1
  import type { EnvironmentData, ListEnvironmentsResponse } from '../../models/EnvironmentDTO';
2
- import type { ICreateEnvironmentBuildsRequest, ICreateEnvironmentRequest, ICreateEnvironmentVersionRequest, IDatalayerEnvironment, IEnvironmentArtifactsPage, IEnvironmentBuildLogChunk, IEnvironmentBuildLogPage, IEnvironmentBuildRecord, IEnvironmentBuildsCreated, IEnvironmentBuildsPage, IEnvironmentFork, IEnvironmentPublicationRecord, IEnvironmentRecord, IEnvironmentsError, IEnvironmentsPageQuery, IEnvironmentTrial, IEnvironmentValidationReport, IEnvironmentVersionRecord, IEnvironmentVersionsPage, IForkEnvironmentVersionRequest, IListEnvironmentsQuery, IPromoteEnvironmentVersionRequest, ITrialEnvironmentVersionRequest, IUpdateEnvironmentRequest, IUpdateEnvironmentVersionRequest, IValidateEnvironmentVersionRequest } from '../../models/Environment';
2
+ import type { ICreateEnvironmentBuildsRequest, ICreateEnvironmentRequest, ICreateEnvironmentVersionRequest, IDatalayerEnvironment, IEnvironmentArtifactsPage, IEnvironmentBuildLogChunk, IEnvironmentBuildLogPage, IEnvironmentBuildRecord, IEnvironmentBuildsCreated, IEnvironmentBuildsPage, IEnvironmentFork, IEnvironmentPublicationRecord, IEnvironmentQuotas, IEnvironmentRecord, IEnvironmentVersionSandboxes, IEnvironmentsError, IEnvironmentsPageQuery, IEnvironmentTrial, IEnvironmentValidationReport, IEnvironmentVersionRecord, IEnvironmentVersionsPage, IForkEnvironmentVersionRequest, IListEnvironmentsQuery, IPromoteEnvironmentVersionRequest, ITrialEnvironmentVersionRequest, IUpdateEnvironmentRequest, IUpdateEnvironmentVersionRequest, IValidateEnvironmentVersionRequest } from '../../models/Environment';
3
3
  /** What every environments call may carry. */
4
4
  export interface IEnvironmentsRequestOptions {
5
5
  /** Sent as `X-Correlation-Id`; a refusal answers with it. */
@@ -197,6 +197,27 @@ export declare const createEnvironmentBuilds: (token: string, versionUid: string
197
197
  * @returns Promise resolving to one page of builds
198
198
  */
199
199
  export declare const listEnvironmentBuilds: (token: string, versionUid: string, page?: IEnvironmentsPageQuery, options?: IEnvironmentsRequestOptions, baseUrl?: string) => Promise<IEnvironmentBuildsPage>;
200
+ /**
201
+ * The sandboxes running a version now: yours by uid, anybody else's counted (E2-19).
202
+ * @param token - Authentication token
203
+ * @param versionUid - The version's uid
204
+ * @param options - Correlation id and abort signal
205
+ * @param baseUrl - Base URL for the API (defaults to production Runtimes URL)
206
+ * @returns Promise resolving to the version's live sandboxes
207
+ */
208
+ export declare const getEnvironmentVersionSandboxes: (token: string, versionUid: string, options?: IEnvironmentsRequestOptions, baseUrl?: string) => Promise<IEnvironmentVersionSandboxes>;
209
+ /**
210
+ * An owner's build quotas and what it holds against each (E1-13, E2-19).
211
+ * @param token - Authentication token
212
+ * @param owner - The caller's own when empty; an organization's by its uid
213
+ * @param options - Correlation id and abort signal
214
+ * @param baseUrl - Base URL for the API (defaults to production Runtimes URL)
215
+ * @returns Promise resolving to the limits and the holdings
216
+ */
217
+ export declare const getEnvironmentQuotas: (token: string, owner?: {
218
+ ownerType?: "user" | "organization";
219
+ ownerUid?: string;
220
+ }, options?: IEnvironmentsRequestOptions, baseUrl?: string) => Promise<IEnvironmentQuotas>;
200
221
  /**
201
222
  * List a version's artifacts, one per variant and region.
202
223
  * @param token - Authentication token
@@ -293,6 +293,37 @@ export const listEnvironmentBuilds = async (token, versionUid, page = {}, option
293
293
  validateToken(token);
294
294
  return send(token, 'GET', registryUrl(baseUrl, `/environment-versions/${segment(versionUid, 'Version UID')}/builds`, { ...page }), options);
295
295
  };
296
+ /**
297
+ * The sandboxes running a version now: yours by uid, anybody else's counted (E2-19).
298
+ * @param token - Authentication token
299
+ * @param versionUid - The version's uid
300
+ * @param options - Correlation id and abort signal
301
+ * @param baseUrl - Base URL for the API (defaults to production Runtimes URL)
302
+ * @returns Promise resolving to the version's live sandboxes
303
+ */
304
+ export const getEnvironmentVersionSandboxes = async (token, versionUid, options = {}, baseUrl = DEFAULT_SERVICE_URLS.RUNTIMES) => {
305
+ validateToken(token);
306
+ return send(token, 'GET', registryUrl(baseUrl, `/environment-versions/${segment(versionUid, 'Version UID')}/sandboxes`), options);
307
+ };
308
+ /**
309
+ * An owner's build quotas and what it holds against each (E1-13, E2-19).
310
+ * @param token - Authentication token
311
+ * @param owner - The caller's own when empty; an organization's by its uid
312
+ * @param options - Correlation id and abort signal
313
+ * @param baseUrl - Base URL for the API (defaults to production Runtimes URL)
314
+ * @returns Promise resolving to the limits and the holdings
315
+ */
316
+ export const getEnvironmentQuotas = async (token, owner = {}, options = {}, baseUrl = DEFAULT_SERVICE_URLS.RUNTIMES) => {
317
+ validateToken(token);
318
+ const query = {};
319
+ if (owner.ownerType) {
320
+ query.ownerType = owner.ownerType;
321
+ }
322
+ if (owner.ownerUid) {
323
+ query.ownerUid = owner.ownerUid;
324
+ }
325
+ return send(token, 'GET', registryUrl(baseUrl, '/environment-quotas', query), options);
326
+ };
296
327
  /**
297
328
  * List a version's artifacts, one per variant and region.
298
329
  * @param token - Authentication token
@@ -49,6 +49,13 @@ export interface ChatFloatingProps extends ChatCommonProps {
49
49
  buttonTooltip?: string;
50
50
  /** Brand color override. Defaults to the theme's `accent.emphasis` token. */
51
51
  brandColor?: string;
52
+ /**
53
+ * A soft light breathing around the button while the chat is closed, as
54
+ * if it were alive (see `ButtonGlow`). Still, and dimmer, for a reader who
55
+ * asks the system for reduced motion.
56
+ * @default true
57
+ */
58
+ buttonGlow?: boolean;
52
59
  /** Offset from edge (in pixels) */
53
60
  offset?: number;
54
61
  /** Animation duration (in ms) */
@@ -92,5 +99,5 @@ export interface ChatFloatingProps extends ChatCommonProps {
92
99
  * ChatFloating component
93
100
  * A floating chat window built on ChatBase
94
101
  */
95
- export declare function ChatFloating({ endpoint, protocol: protocolProp, useStore: useStoreMode, title, description, position, defaultOpen, width, height, showHeader, showButton, showNewChatButton, showClearButton, showSettingsButton, enableKeyboardShortcuts, toggleShortcut, showPoweredBy, promptVariant, mentionableAgents, poweredByProps, clickOutsideToClose, escapeToClose, className, onSettingsClick, onNewChat, onOpen, onClose, onStateUpdate, children, brandIcon, buttonIcon, buttonTooltip, brandColor, offset, animationDuration, renderToolResult, frontendTools, initialState: _initialState, suggestions, submitOnSuggestionClick, hideMessagesAfterToolUI, defaultViewMode, onViewModeChange, sidebarMountPoint, showPanelBackdrop, availableModels, showModelSelector, showToolsMenu, showSkillsMenu, showTokenUsage, showContextRing, themeVariant, colorMode, runtimeId, historyEndpoint, authToken, historyAuthToken, pendingPrompt, showInformation, onInformationClick, onToolCallStart, onToolCallComplete, showToolApprovalBanner, pendingApprovals, onApproveApproval, onRejectApproval, panelProps, launching, launchingMessage, }: ChatFloatingProps): React.JSX.Element;
102
+ export declare function ChatFloating({ endpoint, protocol: protocolProp, useStore: useStoreMode, title, description, position, defaultOpen, width, height, showHeader, showButton, showNewChatButton, showClearButton, showSettingsButton, enableKeyboardShortcuts, toggleShortcut, showPoweredBy, promptVariant, mentionableAgents, poweredByProps, clickOutsideToClose, escapeToClose, className, onSettingsClick, onNewChat, onOpen, onClose, onStateUpdate, children, brandIcon, buttonIcon, buttonTooltip, brandColor, buttonGlow, offset, animationDuration, renderToolResult, frontendTools, initialState: _initialState, suggestions, submitOnSuggestionClick, hideMessagesAfterToolUI, defaultViewMode, onViewModeChange, sidebarMountPoint, showPanelBackdrop, availableModels, showModelSelector, showToolsMenu, showSkillsMenu, showTokenUsage, showContextRing, themeVariant, colorMode, runtimeId, historyEndpoint, authToken, historyAuthToken, pendingPrompt, showInformation, onInformationClick, onToolCallStart, onToolCallComplete, showToolApprovalBanner, pendingApprovals, onApproveApproval, onRejectApproval, panelProps, launching, launchingMessage, }: ChatFloatingProps): React.JSX.Element;
96
103
  export default ChatFloating;
@@ -23,6 +23,7 @@ import { XIcon, CommentDiscussionIcon, GrabberIcon, } from '@primer/octicons-rea
23
23
  import { AiAgentIcon } from '@datalayer/icons-react';
24
24
  import { createPortal } from 'react-dom';
25
25
  import { ChatBase } from './base/ChatBase';
26
+ import { ButtonGlow } from './display/ButtonGlow';
26
27
  import { useViewportDrag } from './useViewportDrag';
27
28
  import { disabledChatViewModes, resolveMountPoint } from './viewModes';
28
29
  import { useChatOpen, useChatMessages, useChatStore, } from '../stores/chatStore';
@@ -46,7 +47,7 @@ function useIsMobile(breakpoint = 640) {
46
47
  * ChatFloating component
47
48
  * A floating chat window built on ChatBase
48
49
  */
49
- export function ChatFloating({ endpoint, protocol: protocolProp, useStore: useStoreMode = true, title = 'Chat', description = 'Start a conversation with the AI agent.', position = 'bottom-right', defaultOpen = false, width = 400, height = 550, showHeader = true, showButton = true, showNewChatButton = true, showClearButton = true, showSettingsButton = false, enableKeyboardShortcuts = true, toggleShortcut = '/', showPoweredBy = true, promptVariant, mentionableAgents, poweredByProps, clickOutsideToClose = false, escapeToClose = true, className, onSettingsClick, onNewChat, onOpen, onClose, onStateUpdate, children, brandIcon, buttonIcon, buttonTooltip = 'Chat with AI', brandColor, offset = 20, animationDuration = 200, renderToolResult, frontendTools, initialState: _initialState, suggestions, submitOnSuggestionClick = true, hideMessagesAfterToolUI = false, defaultViewMode = 'floating', onViewModeChange, sidebarMountPoint = null, showPanelBackdrop = false, availableModels, showModelSelector = false, showToolsMenu = false, showSkillsMenu = false, showTokenUsage = true, showContextRing = false, themeVariant, colorMode, runtimeId, historyEndpoint, authToken, historyAuthToken, pendingPrompt, showInformation = false, onInformationClick, onToolCallStart, onToolCallComplete, showToolApprovalBanner, pendingApprovals, onApproveApproval, onRejectApproval, panelProps, launching = false, launchingMessage, }) {
50
+ export function ChatFloating({ endpoint, protocol: protocolProp, useStore: useStoreMode = true, title = 'Chat', description = 'Start a conversation with the AI agent.', position = 'bottom-right', defaultOpen = false, width = 400, height = 550, showHeader = true, showButton = true, showNewChatButton = true, showClearButton = true, showSettingsButton = false, enableKeyboardShortcuts = true, toggleShortcut = '/', showPoweredBy = true, promptVariant, mentionableAgents, poweredByProps, clickOutsideToClose = false, escapeToClose = true, className, onSettingsClick, onNewChat, onOpen, onClose, onStateUpdate, children, brandIcon, buttonIcon, buttonTooltip = 'Chat with AI', brandColor, buttonGlow = true, offset = 20, animationDuration = 200, renderToolResult, frontendTools, initialState: _initialState, suggestions, submitOnSuggestionClick = true, hideMessagesAfterToolUI = false, defaultViewMode = 'floating', onViewModeChange, sidebarMountPoint = null, showPanelBackdrop = false, availableModels, showModelSelector = false, showToolsMenu = false, showSkillsMenu = false, showTokenUsage = true, showContextRing = false, themeVariant, colorMode, runtimeId, historyEndpoint, authToken, historyAuthToken, pendingPrompt, showInformation = false, onInformationClick, onToolCallStart, onToolCallComplete, showToolApprovalBanner, pendingApprovals, onApproveApproval, onRejectApproval, panelProps, launching = false, launchingMessage, }) {
50
51
  // Store-based state
51
52
  const storeIsOpen = useChatOpen();
52
53
  const storeMessages = useChatMessages();
@@ -555,7 +556,9 @@ export function ChatFloating({ endpoint, protocol: protocolProp, useStore: useSt
555
556
  }, children: _jsxs(Box, { sx: {
556
557
  position: 'relative',
557
558
  display: 'inline-flex',
558
- }, onMouseEnter: () => setIsHovered(true), onMouseLeave: () => setIsHovered(false), children: [_jsx(Tooltip, { text: `${buttonTooltip}${shortcutHint ? ` (${shortcutHint})` : ''}`, direction: position.includes('right') ? 'w' : 'e', children: _jsx(IconButton, { icon: buttonIcon
559
+ // The glow's halos go behind the button, not behind the page.
560
+ isolation: 'isolate',
561
+ }, onMouseEnter: () => setIsHovered(true), onMouseLeave: () => setIsHovered(false), children: [buttonGlow && _jsx(ButtonGlow, { color: brandColor }), _jsx(Tooltip, { text: `${buttonTooltip}${shortcutHint ? ` (${shortcutHint})` : ''}`, direction: position.includes('right') ? 'w' : 'e', children: _jsx(IconButton, { icon: buttonIcon
559
562
  ? buttonIcon
560
563
  : CommentDiscussionIcon, "aria-label": buttonTooltip, onClick: handleToggle, size: "large", sx: {
561
564
  width: 56,
@@ -0,0 +1,9 @@
1
+ export interface ButtonGlowProps {
2
+ /**
3
+ * The colour of the light: the button's own. A theme token or any CSS
4
+ * colour; the theme's `accent.emphasis` when absent.
5
+ */
6
+ color?: string;
7
+ }
8
+ export declare function ButtonGlow({ color }: ButtonGlowProps): import("react").JSX.Element;
9
+ export default ButtonGlow;
@@ -0,0 +1,60 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /*
3
+ * Copyright (c) 2025-2026 Datalayer, Inc.
4
+ * Distributed under the terms of the Modified BSD License.
5
+ */
6
+ /**
7
+ * A soft light breathing around a round button, as if it were alive.
8
+ *
9
+ * Two blurred halos in the button's colour swell and fade on cycles of
10
+ * different lengths, 3.4 s and 5.3 s, so the light drifts rather than
11
+ * blinks: the two never line up the same way twice in a row. For a reader
12
+ * who asks the system for less motion, the halos hold still, a little dimmer.
13
+ *
14
+ * The halos sit behind the button: the parent must be `position: relative`
15
+ * and make a stacking context of its own (`isolation: 'isolate'`), or they
16
+ * would slip behind the page instead.
17
+ *
18
+ * @module chat/display/ButtonGlow
19
+ */
20
+ import { Box } from '@datalayer/primer-addons';
21
+ const halo = (color) => ({
22
+ position: 'absolute',
23
+ inset: 0,
24
+ borderRadius: '50%',
25
+ bg: color || 'accent.emphasis',
26
+ zIndex: -1,
27
+ pointerEvents: 'none',
28
+ willChange: 'transform, opacity',
29
+ });
30
+ export function ButtonGlow({ color }) {
31
+ return (_jsxs(_Fragment, { children: [_jsx(Box, { "aria-hidden": true, "data-button-glow": "breath", sx: {
32
+ ...halo(color),
33
+ filter: 'blur(10px)',
34
+ animation: 'agentRuntimesGlowBreath 3.4s ease-in-out infinite',
35
+ '@keyframes agentRuntimesGlowBreath': {
36
+ '0%, 100%': { transform: 'scale(0.9)', opacity: 0.35 },
37
+ '50%': { transform: 'scale(1.25)', opacity: 0.7 },
38
+ },
39
+ '@media (prefers-reduced-motion: reduce)': {
40
+ animation: 'none',
41
+ transform: 'scale(1.1)',
42
+ opacity: 0.4,
43
+ },
44
+ } }), _jsx(Box, { "aria-hidden": true, "data-button-glow": "aura", sx: {
45
+ ...halo(color),
46
+ filter: 'blur(18px)',
47
+ animation: 'agentRuntimesGlowAura 5.3s ease-in-out infinite',
48
+ '@keyframes agentRuntimesGlowAura': {
49
+ '0%, 100%': { transform: 'scale(1)', opacity: 0.15 },
50
+ '40%': { transform: 'scale(1.55)', opacity: 0.4 },
51
+ '70%': { transform: 'scale(1.3)', opacity: 0.25 },
52
+ },
53
+ '@media (prefers-reduced-motion: reduce)': {
54
+ animation: 'none',
55
+ transform: 'scale(1.3)',
56
+ opacity: 0.2,
57
+ },
58
+ } })] }));
59
+ }
60
+ export default ButtonGlow;
@@ -50,6 +50,9 @@ export function ChatEmptyState({ emptyState, brandIcon, description, suggestions
50
50
  cursor: 'pointer',
51
51
  display: 'inline-flex',
52
52
  alignItems: 'center',
53
+ // A short opener sits in the middle of its fixed-width chip, not
54
+ // against its left edge with the rest of the chip empty.
55
+ justifyContent: 'center',
53
56
  width: SUGGESTION_CHIP_WIDTH,
54
57
  flex: `0 0 ${SUGGESTION_CHIP_WIDTH}px`,
55
58
  '&:hover': {
@@ -57,7 +60,7 @@ export function ChatEmptyState({ emptyState, brandIcon, description, suggestions
57
60
  color: 'var(--button-primary-fgColor-rest)',
58
61
  borderColor: 'accent.emphasis',
59
62
  },
60
- }, onClick: () => handleSuggestionClick(suggestion), children: _jsx(Truncate, { title: suggestion.message, maxWidth: "100%", sx: { minWidth: 0 }, children: suggestion.title }) }, index))) }));
63
+ }, onClick: () => handleSuggestionClick(suggestion), children: _jsx(Truncate, { inline: true, title: suggestion.message, maxWidth: "100%", sx: { minWidth: 0 }, children: suggestion.title }) }, index))) }));
61
64
  return (_jsx(ThemeProvider, { children: _jsxs(Box, { sx: {
62
65
  display: 'flex',
63
66
  flexDirection: 'column',
@@ -1,9 +1,9 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Heading, IconButton, Label, Text, Tooltip, Truncate, } from '@primer/react';
2
+ import { Heading, IconButton, Label, Text, Truncate } from '@primer/react';
3
3
  import { Box } from '@datalayer/primer-addons';
4
4
  import { KernelIndicator } from '@datalayer/jupyter-react';
5
- import { PlusIcon, TrashIcon, GearIcon, CommentDiscussionIcon, DeviceMobileIcon, SidebarExpandIcon, GrabberIcon, InfoIcon, } from '@primer/octicons-react';
6
- import { CHAT_VIEW_MODES, SIDEBAR_NEEDS_MOUNT_POINT } from '../viewModes';
5
+ import { PlusIcon, TrashIcon, GearIcon, InfoIcon, } from '@primer/octicons-react';
6
+ import { ChatViewModeToggle } from './ChatViewModeToggle';
7
7
  import { AiAgentIcon } from '@datalayer/icons-react';
8
8
  import { EphemeralSurfaceControl } from '../EphemeralSurfaceControl';
9
9
  export function toRuntimeExecutionState(runtimeStatus) {
@@ -29,16 +29,6 @@ export function toRuntimeExecutionState(runtimeStatus) {
29
29
  return undefined;
30
30
  }
31
31
  // ---------------------------------------------------------------------------
32
- // Props
33
- // ---------------------------------------------------------------------------
34
- /** The drawing of each mode in the header's toggle. */
35
- const VIEW_MODE_ICONS = {
36
- floating: CommentDiscussionIcon,
37
- 'floating-small': DeviceMobileIcon,
38
- 'floating-draggable': GrabberIcon,
39
- sidebar: SidebarExpandIcon,
40
- };
41
- // ---------------------------------------------------------------------------
42
32
  // Component
43
33
  // ---------------------------------------------------------------------------
44
34
  export function ChatBaseHeader({ title, subtitle, disableReason, brandIcon, headerContent, headerActions, showInformation, onInformationClick, padding, kernelIndicatorState, kernelIndicatorPlacement = 'left', runtimeStatus, kernel, kernelEnvironmentName, kernelCpu, kernelMemory, kernelGpu, headerButtons, messageCount, onNewChat, onClear, chatViewMode, onChatViewModeChange, disabledViewModes = [], showEphemeralSurfaceControl = false, enableEphemeralNotebookOption = false, enableEphemeralDocumentOption = false, ephemeralSurfaceMode = 'none', onEphemeralSurfaceModeChange, }) {
@@ -90,47 +80,5 @@ export function ChatBaseHeader({ title, subtitle, disableReason, brandIcon, head
90
80
  color: 'fg.muted',
91
81
  minWidth: 0,
92
82
  maxWidth: '100%',
93
- }, children: _jsx(Truncate, { title: subtitle, maxWidth: "40ch", children: subtitle }) }))] })), disableReason && (_jsx(Label, { variant: "attention", sx: { flexShrink: 0 }, children: disableReason })), headerContent, showInformation && (_jsx(IconButton, { icon: InfoIcon, "aria-label": "Information", variant: "invisible", size: "small", onClick: onInformationClick })), kernelIndicatorPlacement === 'center' && kernelIndicatorElement] }), _jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 1, flexShrink: 0 }, children: [kernelIndicatorPlacement === 'right' && kernelIndicatorElement, headerButtons?.showNewChat && (_jsx(IconButton, { icon: PlusIcon, "aria-label": "New chat", variant: "invisible", size: "small", onClick: onNewChat })), headerButtons?.showClear && messageCount > 0 && (_jsx(IconButton, { icon: TrashIcon, "aria-label": "Clear messages", variant: "invisible", size: "small", onClick: onClear })), headerButtons?.showSettings && (_jsx(IconButton, { icon: GearIcon, "aria-label": "Settings", variant: "invisible", size: "small", onClick: headerButtons.onSettings })), showEphemeralSurfaceControl && (_jsx(EphemeralSurfaceControl, { mode: ephemeralSurfaceMode, onChange: onEphemeralSurfaceModeChange, enableNotebook: enableEphemeralNotebookOption, enableDocument: enableEphemeralDocumentOption })), chatViewMode && onChatViewModeChange && (_jsx(Box, { sx: {
94
- display: 'inline-flex',
95
- alignItems: 'center',
96
- bg: 'neutral.muted',
97
- borderRadius: '6px',
98
- p: '2px',
99
- gap: '1px',
100
- }, children: CHAT_VIEW_MODES.map(({ mode, label }) => {
101
- const ModeIcon = VIEW_MODE_ICONS[mode];
102
- const selected = chatViewMode === mode;
103
- /* Greyed rather than left out: the person sees the mode
104
- exists, and that this page has no place for it. */
105
- const disabled = disabledViewModes.includes(mode);
106
- return (_jsx(Tooltip, { text: disabled && mode === 'sidebar'
107
- ? `${label} — ${SIDEBAR_NEEDS_MOUNT_POINT}`
108
- : label, direction: "n", children: _jsx(Box, { as: "button", "aria-label": label, "aria-disabled": disabled || undefined, onClick: () => {
109
- if (!disabled) {
110
- onChatViewModeChange(mode);
111
- }
112
- }, sx: {
113
- display: 'inline-flex',
114
- alignItems: 'center',
115
- justifyContent: 'center',
116
- width: 26,
117
- height: 24,
118
- borderRadius: '4px',
119
- border: 'none',
120
- cursor: disabled ? 'not-allowed' : 'pointer',
121
- opacity: disabled ? 0.45 : 1,
122
- bg: selected ? 'canvas.default' : 'transparent',
123
- boxShadow: selected ? 'shadow.small' : 'none',
124
- color: selected ? 'fg.default' : 'fg.muted',
125
- transition: 'all 0.15s ease',
126
- '&:hover': disabled
127
- ? {}
128
- : {
129
- color: 'fg.default',
130
- bg: selected
131
- ? 'canvas.default'
132
- : 'neutral.subtle',
133
- },
134
- }, children: _jsx(ModeIcon, { size: 14 }) }) }, mode));
135
- }) })), headerActions] })] }) }));
83
+ }, children: _jsx(Truncate, { title: subtitle, maxWidth: "40ch", children: subtitle }) }))] })), disableReason && (_jsx(Label, { variant: "attention", sx: { flexShrink: 0 }, children: disableReason })), headerContent, showInformation && (_jsx(IconButton, { icon: InfoIcon, "aria-label": "Information", variant: "invisible", size: "small", onClick: onInformationClick })), kernelIndicatorPlacement === 'center' && kernelIndicatorElement] }), _jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 1, flexShrink: 0 }, children: [kernelIndicatorPlacement === 'right' && kernelIndicatorElement, headerButtons?.showNewChat && (_jsx(IconButton, { icon: PlusIcon, "aria-label": "New chat", variant: "invisible", size: "small", onClick: onNewChat })), headerButtons?.showClear && messageCount > 0 && (_jsx(IconButton, { icon: TrashIcon, "aria-label": "Clear messages", variant: "invisible", size: "small", onClick: onClear })), headerButtons?.showSettings && (_jsx(IconButton, { icon: GearIcon, "aria-label": "Settings", variant: "invisible", size: "small", onClick: headerButtons.onSettings })), showEphemeralSurfaceControl && (_jsx(EphemeralSurfaceControl, { mode: ephemeralSurfaceMode, onChange: onEphemeralSurfaceModeChange, enableNotebook: enableEphemeralNotebookOption, enableDocument: enableEphemeralDocumentOption })), chatViewMode && onChatViewModeChange && (_jsx(ChatViewModeToggle, { value: chatViewMode, onChange: onChatViewModeChange, disabledModes: disabledViewModes })), headerActions] })] }) }));
136
84
  }
@@ -0,0 +1,29 @@
1
+ /**
2
+ * The segmented toggle for how a chat is shown — the four modes of
3
+ * `CHAT_VIEW_MODES`, drawn as icons in a pill, the current one raised.
4
+ *
5
+ * It grew up in the chat header, and lives apart from it so the same
6
+ * control can stand elsewhere: a composer floating over a page as a
7
+ * draggable card carries it in its own footer, where the person deciding
8
+ * how the chat should sit has the chat under their hands. The rules — the
9
+ * order, the words, which modes are greyed out — stay in `viewModes.ts`;
10
+ * this is only their drawing.
11
+ *
12
+ * @module chat/header/ChatViewModeToggle
13
+ */
14
+ import type { JSX } from 'react';
15
+ import { type Icon } from '@primer/octicons-react';
16
+ import type { ChatViewMode } from '../../types/chat';
17
+ /** The drawing of each mode. */
18
+ export declare const VIEW_MODE_ICONS: Record<ChatViewMode, Icon>;
19
+ export type ChatViewModeToggleProps = {
20
+ /** The mode the chat is in now. */
21
+ value?: ChatViewMode;
22
+ /** The mode the person picked. */
23
+ onChange: (mode: ChatViewMode) => void;
24
+ /** Modes shown greyed out: the sidebar, when the host has no mount point. */
25
+ disabledModes?: readonly ChatViewMode[];
26
+ /** Where the tooltips open. Above by default; below for a control at the top of its window. */
27
+ tooltipDirection?: 'n' | 's';
28
+ };
29
+ export declare function ChatViewModeToggle({ value, onChange, disabledModes, tooltipDirection, }: ChatViewModeToggleProps): JSX.Element;
@@ -0,0 +1,55 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { Tooltip } from '@primer/react';
3
+ import { Box } from '@datalayer/primer-addons';
4
+ import { CommentDiscussionIcon, DeviceMobileIcon, GrabberIcon, SidebarExpandIcon, } from '@primer/octicons-react';
5
+ import { CHAT_VIEW_MODES, SIDEBAR_NEEDS_MOUNT_POINT } from '../viewModes';
6
+ /** The drawing of each mode. */
7
+ export const VIEW_MODE_ICONS = {
8
+ floating: CommentDiscussionIcon,
9
+ 'floating-small': DeviceMobileIcon,
10
+ 'floating-draggable': GrabberIcon,
11
+ sidebar: SidebarExpandIcon,
12
+ };
13
+ export function ChatViewModeToggle({ value, onChange, disabledModes = [], tooltipDirection = 'n', }) {
14
+ return (_jsx(Box, { role: "group", "aria-label": "Display mode", "data-chat-view-mode-toggle": "", sx: {
15
+ display: 'inline-flex',
16
+ alignItems: 'center',
17
+ bg: 'neutral.muted',
18
+ borderRadius: '6px',
19
+ p: '2px',
20
+ gap: '1px',
21
+ }, children: CHAT_VIEW_MODES.map(({ mode, label }) => {
22
+ const ModeIcon = VIEW_MODE_ICONS[mode];
23
+ const selected = value === mode;
24
+ /* Greyed rather than left out: the person sees the mode exists, and
25
+ that this page has no place for it. */
26
+ const disabled = disabledModes.includes(mode);
27
+ return (_jsx(Tooltip, { text: disabled && mode === 'sidebar'
28
+ ? `${label} — ${SIDEBAR_NEEDS_MOUNT_POINT}`
29
+ : label, direction: tooltipDirection, children: _jsx(Box, { as: "button", type: "button", "aria-label": label, "aria-pressed": selected, "aria-disabled": disabled || undefined, onClick: () => {
30
+ if (!disabled) {
31
+ onChange(mode);
32
+ }
33
+ }, sx: {
34
+ display: 'inline-flex',
35
+ alignItems: 'center',
36
+ justifyContent: 'center',
37
+ width: 26,
38
+ height: 24,
39
+ borderRadius: '4px',
40
+ border: 'none',
41
+ cursor: disabled ? 'not-allowed' : 'pointer',
42
+ opacity: disabled ? 0.45 : 1,
43
+ bg: selected ? 'canvas.default' : 'transparent',
44
+ boxShadow: selected ? 'shadow.small' : 'none',
45
+ color: selected ? 'fg.default' : 'fg.muted',
46
+ transition: 'all 0.15s ease',
47
+ '&:hover': disabled
48
+ ? {}
49
+ : {
50
+ color: 'fg.default',
51
+ bg: selected ? 'canvas.default' : 'neutral.subtle',
52
+ },
53
+ }, children: _jsx(ModeIcon, { size: 14 }) }) }, mode));
54
+ }) }));
55
+ }
@@ -59,6 +59,7 @@ export { McpStatusIndicator, type McpStatusIndicatorProps, type McpServerStatus,
59
59
  export { PoweredByTag, type PoweredByTagProps } from './display/PoweredByTag';
60
60
  export { FloatingBrandButton, type FloatingBrandButtonProps, } from './display/FloatingBrandButton';
61
61
  export { ChatHeader, type ChatHeaderProps } from './header/ChatHeader';
62
+ export { ChatViewModeToggle, VIEW_MODE_ICONS, type ChatViewModeToggleProps, } from './header/ChatViewModeToggle';
62
63
  export { MessagePart, type MessagePartProps, TextPart, type TextPartProps, ReasoningPart, type ReasoningPartProps, ToolPart, type ToolPartProps, DynamicToolPart, type DynamicToolPartProps, } from './parts';
63
64
  export { ToolCallDisplay, type ToolCallDisplayProps } from './tools';
64
65
  export { ToolApprovalDialog, useToolApprovalDialog, type ToolApprovalDialogProps, } from './tools/ToolApprovalDialog';
package/lib/chat/index.js CHANGED
@@ -68,6 +68,7 @@ export { McpStatusIndicator, SandboxStatusIndicator, } from './indicators';
68
68
  export { PoweredByTag } from './display/PoweredByTag';
69
69
  export { FloatingBrandButton, } from './display/FloatingBrandButton';
70
70
  export { ChatHeader } from './header/ChatHeader';
71
+ export { ChatViewModeToggle, VIEW_MODE_ICONS, } from './header/ChatViewModeToggle';
71
72
  // Components - Message part renderers
72
73
  export { MessagePart, TextPart, ReasoningPart, ToolPart, DynamicToolPart, } from './parts';
73
74
  // Components - Tool UI
@@ -62,6 +62,14 @@ export interface ICodeSandboxLauncherProps {
62
62
  * Optional submit button label override.
63
63
  */
64
64
  submitLabel?: string;
65
+ /**
66
+ * A published environment to offer and select: the version uid of its
67
+ * publication, as the Library page that sent the reader here names it.
68
+ * Nobody's listing holds another account's environment, so it is read from
69
+ * the publication, offered under *Library* and priced by the rate the
70
+ * publication carries (D-12, E2-19).
71
+ */
72
+ libraryVersionUid?: string;
65
73
  }
66
74
  /**
67
75
  * The launcher of a new code sandbox.
@@ -10,7 +10,8 @@ import { NO_RUNTIME_AVAILABLE_LABEL } from '@datalayer/core/lib/i18n';
10
10
  import { isCodeSandboxProviderAvailable } from './codeSandboxProviders';
11
11
  import { CodeSandboxVariant, codeSandboxVariantOf, codeSandboxVariantTitle, } from '../../models/CodeSandboxVariant';
12
12
  import { CodeSandboxEnvironmentSelect, } from './CodeSandboxEnvironmentSelect';
13
- import { codeSandboxEnvironmentOptions, codeSandboxEnvironmentVersion, firstLaunchableCodeSandboxEnvironment, } from './codeSandboxEnvironments';
13
+ import { codeSandboxEnvironmentOptions, codeSandboxEnvironmentVersion, firstLaunchableCodeSandboxEnvironment, libraryEnvironmentOf, } from './codeSandboxEnvironments';
14
+ import { getEnvironmentPublication } from '../../api/runtimes/environments';
14
15
  import { getCodeSandboxGivenName, listCodeSandboxGivenNames, nextCodeSandboxGivenName, setCodeSandboxGivenName, } from './CodeSandboxNames';
15
16
  import { iamStore, runtimesStore, useCoreStore, useIAMStore, } from '../../state/substates';
16
17
  import { createRuntime as createRuntimeRecord } from '../../runtimes/actions';
@@ -42,7 +43,7 @@ const NOT_AVAILABLE_RETRIES = 5;
42
43
  * The launcher of a new code sandbox.
43
44
  */
44
45
  export function CodeSandboxLauncher(props) {
45
- const { dialogTitle, kernelSnapshot, manager, onSubmit, markdownParser, sanitizer, upgradeSubscription, usageRoute = '/plans', submitLabel, startRuntime = true, } = props;
46
+ const { dialogTitle, kernelSnapshot, manager, onSubmit, markdownParser, sanitizer, upgradeSubscription, usageRoute = '/plans', submitLabel, startRuntime = true, libraryVersionUid, } = props;
46
47
  const hasExample = startRuntime === 'with-example';
47
48
  const shouldStartRuntime = startRuntime !== 'defer';
48
49
  const user = iamStore.getState().user;
@@ -56,7 +57,45 @@ export function CodeSandboxLauncher(props) {
56
57
  * below.
57
58
  */
58
59
  const jupyterAvailable = isCodeSandboxProviderAvailable('local', runtimesStore.getState().multiServiceManager);
59
- const environments = manager.environments.get();
60
+ const listed = manager.environments.get();
61
+ /*
62
+ * A publication somebody else owns, which no listing of the viewer's holds
63
+ * (D-12, E2-19): read by its version uid, offered under *Library* beside
64
+ * the rest, priced by the rate the publication carries, and selected below,
65
+ * since it is what the reader came here to launch.
66
+ */
67
+ const [libraryEntry, setLibraryEntry] = useState();
68
+ const [libraryRefusal, setLibraryRefusal] = useState('');
69
+ useEffect(() => {
70
+ if (!libraryVersionUid) {
71
+ return undefined;
72
+ }
73
+ let current = true;
74
+ getEnvironmentPublication(iamStore.getState().token ?? '', libraryVersionUid, {}, runtimesStore.getState().runtimesUrl)
75
+ .then(publication => {
76
+ if (!current) {
77
+ return;
78
+ }
79
+ const entry = libraryEnvironmentOf(publication);
80
+ if (entry) {
81
+ setLibraryEntry(entry);
82
+ }
83
+ else {
84
+ setLibraryRefusal('This environment is no longer published, so it cannot be launched from the Library.');
85
+ }
86
+ })
87
+ .catch(() => {
88
+ if (current) {
89
+ setLibraryRefusal('This published environment could not be read, so it cannot be launched from here.');
90
+ }
91
+ });
92
+ return () => {
93
+ current = false;
94
+ };
95
+ }, [libraryVersionUid]);
96
+ const environments = useMemo(() => libraryEntry && !listed.some(entry => entry.name === libraryEntry.name)
97
+ ? [...listed, libraryEntry]
98
+ : listed, [listed, libraryEntry]);
60
99
  /*
61
100
  * The sandboxes of this Jupyter Server, offered beside those of the platform.
62
101
  *
@@ -168,6 +207,17 @@ export function CodeSandboxLauncher(props) {
168
207
  const [runtimeName, setRuntimeName] = useState(() => givenNameFor(firstLaunchableCodeSandboxEnvironment(environments)));
169
208
  // Whether the runtim name has been changed by the user or not
170
209
  const [hasCustomRuntimeName, setHasCustomRuntimeName] = useState(false);
210
+ // The publication the reader came to launch, selected once it is read.
211
+ useEffect(() => {
212
+ if (!libraryEntry) {
213
+ return;
214
+ }
215
+ setSelection(libraryEntry.name);
216
+ if (!hasCustomRuntimeName) {
217
+ setRuntimeName(givenNameFor(libraryEntry));
218
+ }
219
+ // eslint-disable-next-line react-hooks/exhaustive-deps
220
+ }, [libraryEntry]);
171
221
  const [homeFolder, setHomeFolder] = useState(false);
172
222
  const [openExample, setOpenExample] = useState(false);
173
223
  const [waitingForRuntime, setWaitingForRuntime] = useState(false);
@@ -294,7 +344,6 @@ export function CodeSandboxLauncher(props) {
294
344
  }
295
345
  }
296
346
  else if (shouldStartRuntime) {
297
- success = false;
298
347
  let availableTrial = 1;
299
348
  let retryDelay = NOT_AVAILABLE_INIT_RETRY;
300
349
  // Should return success status.
@@ -437,7 +486,7 @@ export function CodeSandboxLauncher(props) {
437
486
  event.preventDefault();
438
487
  handleSubmitRuntime();
439
488
  }
440
- }, children: [_jsxs(FormControl, { disabled: !!kernelSnapshot?.environment || environments.length === 0, children: [_jsx(FormControl.Label, { children: "Environment" }), _jsx(CodeSandboxEnvironmentSelect, { disabled: !!kernelSnapshot?.environment || environments.length === 0, options: environmentOptions, selectedKey: selection, onSelect: handleSelectionChange }), _jsx(FormControl.Caption, { children: _jsx(_Fragment, { children: markdownParser ? (_jsx(Box, { sx: { img: { maxWidth: '100%' } }, children: _jsx(Markdown, { text: description, markdownParser: markdownParser, sanitizer: sanitizer }) })) : sanitizer ? (
489
+ }, children: [libraryRefusal ? (_jsx(Text, { as: "p", sx: { color: 'attention.fg' }, "data-testid": "library-refusal", children: libraryRefusal })) : null, _jsxs(FormControl, { disabled: !!kernelSnapshot?.environment || environments.length === 0, children: [_jsx(FormControl.Label, { children: "Environment" }), _jsx(CodeSandboxEnvironmentSelect, { disabled: !!kernelSnapshot?.environment || environments.length === 0, options: environmentOptions, selectedKey: selection, onSelect: handleSelectionChange }), _jsx(FormControl.Caption, { children: _jsx(_Fragment, { children: markdownParser ? (_jsx(Box, { sx: { img: { maxWidth: '100%' } }, children: _jsx(Markdown, { text: description, markdownParser: markdownParser, sanitizer: sanitizer }) })) : sanitizer ? (
441
490
  // The description of an environment carries markup. With no
442
491
  // parser to render it, the sanitizer of the application is
443
492
  // enough to show it as what it is rather than as its tags.
@@ -14,7 +14,7 @@
14
14
  *
15
15
  * @module components/code-sandboxes/codeSandboxEnvironments
16
16
  */
17
- import type { IDatalayerEnvironment } from '../../models';
17
+ import type { IDatalayerEnvironment, IEnvironmentPublicationRecord } from '../../models';
18
18
  import type { ICodeSandboxEnvironmentOption } from './CodeSandboxEnvironmentSelect';
19
19
  /**
20
20
  * The variant a sandbox of THIS platform starts on.
@@ -84,3 +84,14 @@ export declare function codeSandboxEnvironmentOption(environment: IDatalayerEnvi
84
84
  export declare function codeSandboxEnvironmentOptions(environments: readonly IDatalayerEnvironment[], viewer?: ICodeSandboxEnvironmentViewer, decorate?: (environment: IDatalayerEnvironment) => Partial<ICodeSandboxEnvironmentOption>): ICodeSandboxEnvironmentOption[];
85
85
  /** The first entry a launcher may open on: never one that cannot be launched. */
86
86
  export declare function firstLaunchableCodeSandboxEnvironment(environments: readonly IDatalayerEnvironment[]): IDatalayerEnvironment | undefined;
87
+ /**
88
+ * A publication, as the picker offers it to somebody who does not own it
89
+ * (D-12, E2-19).
90
+ *
91
+ * Nobody's listing holds another account's environment, so a reader who came
92
+ * from its Library page is offered it from the publication itself: launched by
93
+ * its environment uid and the version the publication froze, listed under
94
+ * *Library*, and priced by the rate the publication carries. `undefined` for a
95
+ * publication that has been withdrawn, which is no longer anyone's to launch.
96
+ */
97
+ export declare function libraryEnvironmentOf(publication: IEnvironmentPublicationRecord): IDatalayerEnvironment | undefined;
@@ -121,7 +121,7 @@ export function codeSandboxEnvironmentOption(environment, viewer, extra = {}) {
121
121
  return {
122
122
  key: environment.name,
123
123
  title: environment.title || environment.name,
124
- name: environment.name,
124
+ name: environment.label || environment.name,
125
125
  group: CODE_SANDBOX_ENVIRONMENT_GROUP_TITLES[group],
126
126
  version: version === undefined ? undefined : `v${version}`,
127
127
  sizeClass: environment.sizeClass || undefined,
@@ -145,3 +145,40 @@ export function codeSandboxEnvironmentOptions(environments, viewer, decorate) {
145
145
  export function firstLaunchableCodeSandboxEnvironment(environments) {
146
146
  return environments.find(isCodeSandboxEnvironmentLaunchable);
147
147
  }
148
+ /**
149
+ * A publication, as the picker offers it to somebody who does not own it
150
+ * (D-12, E2-19).
151
+ *
152
+ * Nobody's listing holds another account's environment, so a reader who came
153
+ * from its Library page is offered it from the publication itself: launched by
154
+ * its environment uid and the version the publication froze, listed under
155
+ * *Library*, and priced by the rate the publication carries. `undefined` for a
156
+ * publication that has been withdrawn, which is no longer anyone's to launch.
157
+ */
158
+ export function libraryEnvironmentOf(publication) {
159
+ if (publication.status !== 'published') {
160
+ return undefined;
161
+ }
162
+ const snapshot = publication.snapshot;
163
+ const spec = snapshot.spec
164
+ ?.spec;
165
+ const version = snapshot.versionNumber;
166
+ return {
167
+ name: publication.environmentUid,
168
+ label: snapshot.environmentName || undefined,
169
+ uid: publication.environmentUid,
170
+ title: snapshot.title || snapshot.environmentName,
171
+ description: snapshot.readme ?? '',
172
+ language: spec?.language?.name ?? 'python',
173
+ origin: 'user',
174
+ owner: publication.ownerUid,
175
+ fromLibrary: true,
176
+ sizeClass: snapshot.sizeClass,
177
+ burning_rate: publication.burningRate ?? undefined,
178
+ promotedVersion: version === null || version === undefined
179
+ ? null
180
+ : { uid: publication.versionUid, version, status: 'ready' },
181
+ variants: Object.keys(snapshot.variants ?? {}),
182
+ availableVariants: Object.keys(snapshot.variants ?? {}),
183
+ };
184
+ }
@@ -3,7 +3,7 @@
3
3
  * Distributed under the terms of the Modified BSD License.
4
4
  */
5
5
  import { createElement } from 'react';
6
- import { DatabaseIcon, RowsIcon, BookIcon, DiffModifiedIcon, PencilIcon, FeedIssueDraftIcon, FeedRocketIcon, MilestoneIcon, OrganizationIcon, PeopleIcon, RocketIcon, FileIcon, StackIcon, GearIcon, GraphIcon, NumberIcon, PaperAirplaneIcon, ArchiveIcon, TagIcon, MortarBoardIcon, ContainerIcon, HomeIcon, ShareIcon, FileDirectoryIcon, TrophyIcon, QuestionIcon, PersonIcon, MailIcon, ZapIcon, } from '@primer/octicons-react';
6
+ import { DatabaseIcon, RowsIcon, BookIcon, DiffModifiedIcon, PencilIcon, FeedIssueDraftIcon, FeedRocketIcon, MilestoneIcon, OrganizationIcon, PeopleIcon, RocketIcon, FileIcon, StackIcon, GearIcon, GraphIcon, NumberIcon, PaperAirplaneIcon, ArchiveIcon, TagIcon, MortarBoardIcon, ContainerIcon, HomeIcon, ShareIcon, FileDirectoryIcon, TrophyIcon, QuestionIcon, PersonIcon, MailIcon, FileMediaIcon, ZapIcon, } from '@primer/octicons-react';
7
7
  import { GalileoIcon, CircleWhiteIcon, BookOpenIcon, CameraIcon, CellIcon, NotebookOutlineIcon, } from '@datalayer/icons-react';
8
8
  const ARTIFACT_ICONS = new Map([
9
9
  ['assignment', MilestoneIcon],
@@ -13,6 +13,7 @@ const ARTIFACT_ICONS = new Map([
13
13
  ['credits', NumberIcon],
14
14
  ['dataset', StackIcon],
15
15
  ['datasource', DatabaseIcon],
16
+ ['deck', FileMediaIcon],
16
17
  ['document', FileIcon],
17
18
  ['documentation', BookOpenIcon],
18
19
  ['environment', ContainerIcon],