@datalayer/agent-runtimes 1.0.25 → 1.0.28

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 (55) hide show
  1. package/lib/components/agents/index.d.ts +6 -0
  2. package/lib/components/{students → agents}/index.js +6 -1
  3. package/lib/components/{NotificationEventCard.js → cards/NotificationEventCard.js} +1 -1
  4. package/lib/components/{OutputCard.js → cards/OutputCard.js} +1 -1
  5. package/lib/components/cards/index.d.ts +9 -0
  6. package/lib/components/cards/index.js +13 -0
  7. package/lib/components/{runtimes/RuntimeCellVariables.d.ts → code-sandboxes/CodeSandboxCellVariables.d.ts} +3 -3
  8. package/lib/components/{runtimes/RuntimeCellVariables.js → code-sandboxes/CodeSandboxCellVariables.js} +2 -2
  9. package/lib/components/{runtimes/RuntimeCellVariablesDialog.d.ts → code-sandboxes/CodeSandboxCellVariablesDialog.d.ts} +4 -4
  10. package/lib/components/{runtimes/RuntimeCellVariablesDialog.js → code-sandboxes/CodeSandboxCellVariablesDialog.js} +4 -4
  11. package/lib/components/code-sandboxes/CodeSandboxLauncher.d.ts +0 -2
  12. package/lib/components/code-sandboxes/CodeSandboxLauncher.js +98 -11
  13. package/lib/components/code-sandboxes/CodeSandboxNames.d.ts +47 -0
  14. package/lib/components/code-sandboxes/CodeSandboxNames.js +166 -0
  15. package/lib/components/code-sandboxes/CodeSandboxPicker.d.ts +30 -5
  16. package/lib/components/code-sandboxes/CodeSandboxPicker.js +239 -55
  17. package/lib/components/{runtimes/RuntimePickerCell.d.ts → code-sandboxes/CodeSandboxPickerCell.d.ts} +5 -5
  18. package/lib/components/{runtimes/RuntimePickerCell.js → code-sandboxes/CodeSandboxPickerCell.js} +6 -6
  19. package/lib/components/{runtimes/RuntimeReservationControl.d.ts → code-sandboxes/CodeSandboxReservationControl.d.ts} +3 -3
  20. package/lib/components/{runtimes/RuntimeReservationControl.js → code-sandboxes/CodeSandboxReservationControl.js} +5 -5
  21. package/lib/components/{runtimes/RuntimeTransfer.d.ts → code-sandboxes/CodeSandboxTransfer.d.ts} +7 -1
  22. package/lib/components/{runtimes/RuntimeUtils.d.ts → code-sandboxes/CodeSandboxUtils.d.ts} +3 -3
  23. package/lib/components/{runtimes/RuntimeUtils.js → code-sandboxes/CodeSandboxUtils.js} +15 -10
  24. package/lib/components/code-sandboxes/CodeSandboxVariables.d.ts +71 -0
  25. package/lib/components/{runtimes/RuntimeVariables.js → code-sandboxes/CodeSandboxVariables.js} +77 -2
  26. package/lib/components/code-sandboxes/NewCodeSandboxControls.d.ts +17 -0
  27. package/lib/components/code-sandboxes/NewCodeSandboxControls.js +5 -5
  28. package/lib/components/code-sandboxes/index.d.ts +20 -1
  29. package/lib/components/code-sandboxes/index.js +20 -1
  30. package/lib/components/index.d.ts +4 -6
  31. package/lib/components/index.js +4 -6
  32. package/lib/components/progress/CreditsIndicator.js +1 -1
  33. package/package.json +2 -2
  34. package/lib/components/runtimes/RuntimeLauncherDialog.d.ts +0 -7
  35. package/lib/components/runtimes/RuntimeLauncherDialog.js +0 -11
  36. package/lib/components/runtimes/RuntimePickerBase.d.ts +0 -8
  37. package/lib/components/runtimes/RuntimePickerBase.js +0 -12
  38. package/lib/components/runtimes/RuntimePickerNotebook.d.ts +0 -42
  39. package/lib/components/runtimes/RuntimePickerNotebook.js +0 -197
  40. package/lib/components/runtimes/RuntimeSimplePicker.d.ts +0 -38
  41. package/lib/components/runtimes/RuntimeSimplePicker.js +0 -156
  42. package/lib/components/runtimes/RuntimeVariables.d.ts +0 -38
  43. package/lib/components/runtimes/index.d.ts +0 -12
  44. package/lib/components/runtimes/index.js +0 -17
  45. package/lib/components/students/StudentItemStatus.d.ts +0 -7
  46. package/lib/components/students/StudentItemStatus.js +0 -68
  47. package/lib/components/students/index.d.ts +0 -1
  48. package/scripts/codegen/__pycache__/versioning.cpython-313.pyc +0 -0
  49. /package/lib/components/{AgentSummary.d.ts → agents/AgentSummary.d.ts} +0 -0
  50. /package/lib/components/{AgentSummary.js → agents/AgentSummary.js} +0 -0
  51. /package/lib/components/{NotificationEventCard.d.ts → cards/NotificationEventCard.d.ts} +0 -0
  52. /package/lib/components/{OutputCard.d.ts → cards/OutputCard.d.ts} +0 -0
  53. /package/lib/components/{ToolApprovalCard.d.ts → cards/ToolApprovalCard.d.ts} +0 -0
  54. /package/lib/components/{ToolApprovalCard.js → cards/ToolApprovalCard.js} +0 -0
  55. /package/lib/components/{runtimes/RuntimeTransfer.js → code-sandboxes/CodeSandboxTransfer.js} +0 -0
@@ -0,0 +1,6 @@
1
+ /**
2
+ * What an agent shows of itself.
3
+ *
4
+ * @module components/agents
5
+ */
6
+ export * from './AgentSummary';
@@ -2,4 +2,9 @@
2
2
  * Copyright (c) 2025-2026 Datalayer, Inc.
3
3
  * Distributed under the terms of the Modified BSD License.
4
4
  */
5
- export * from './StudentItemStatus';
5
+ /**
6
+ * What an agent shows of itself.
7
+ *
8
+ * @module components/agents
9
+ */
10
+ export * from './AgentSummary';
@@ -9,7 +9,7 @@ import { Box } from '@datalayer/primer-addons';
9
9
  import { ChevronDownIcon, DownloadIcon, EyeClosedIcon, EyeIcon, TrashIcon, } from '@primer/octicons-react';
10
10
  import { createMarkdownDownloadPayload, downloadTextPayload, formatDurationMs, formatRelativeTime, } from '@datalayer/core/lib/utils';
11
11
  import { Streamdown } from 'streamdown';
12
- import { streamdownMarkdownStyles } from '../chat/styles/streamdownStyles';
12
+ import { streamdownMarkdownStyles } from '../../chat/styles/streamdownStyles';
13
13
  const EVENT_KIND_VARIANT = {
14
14
  'agent-start-requested': 'attention',
15
15
  'agent-assigned': 'accent',
@@ -9,7 +9,7 @@ import { Box } from '@datalayer/primer-addons';
9
9
  import { ChevronDownIcon, DownloadIcon, EyeClosedIcon, EyeIcon, TrashIcon, } from '@primer/octicons-react';
10
10
  import { createMarkdownDownloadPayload, downloadTextPayload, formatDurationMs, formatRelativeTime, } from '@datalayer/core/lib/utils';
11
11
  import { Streamdown } from 'streamdown';
12
- import { streamdownMarkdownStyles } from '../chat/styles/streamdownStyles';
12
+ import { streamdownMarkdownStyles } from '../../chat/styles/streamdownStyles';
13
13
  export function OutputCard({ event, onToggleRead, onDelete, onOpenAgent, }) {
14
14
  const [isOutputExpanded, setIsOutputExpanded] = useState(false);
15
15
  const outputText = event.kind === 'agent-output' && event.payload?.outputs
@@ -0,0 +1,9 @@
1
+ /**
2
+ * The cards an agent run is read through: the notifications it emitted, the
3
+ * outputs it produced, and the tool calls waiting for an approval.
4
+ *
5
+ * @module components/cards
6
+ */
7
+ export * from './NotificationEventCard';
8
+ export * from './OutputCard';
9
+ export * from './ToolApprovalCard';
@@ -0,0 +1,13 @@
1
+ /*
2
+ * Copyright (c) 2025-2026 Datalayer, Inc.
3
+ * Distributed under the terms of the Modified BSD License.
4
+ */
5
+ /**
6
+ * The cards an agent run is read through: the notifications it emitted, the
7
+ * outputs it produced, and the tool calls waiting for an approval.
8
+ *
9
+ * @module components/cards
10
+ */
11
+ export * from './NotificationEventCard';
12
+ export * from './OutputCard';
13
+ export * from './ToolApprovalCard';
@@ -1,6 +1,6 @@
1
1
  import { ITranslator } from '@jupyterlab/translation';
2
2
  /**
3
- * {@link RuntimeCellVariables} properties
3
+ * {@link CodeSandboxCellVariables} properties
4
4
  */
5
5
  interface IRuntimeCellVariablesProps {
6
6
  /**
@@ -35,5 +35,5 @@ interface IRuntimeCellVariablesProps {
35
35
  /**
36
36
  * Runtime Cell Variables Picker.
37
37
  */
38
- export declare function RuntimeCellVariables(props: IRuntimeCellVariablesProps): JSX.Element;
39
- export default RuntimeCellVariables;
38
+ export declare function CodeSandboxCellVariables(props: IRuntimeCellVariablesProps): JSX.Element;
39
+ export default CodeSandboxCellVariables;
@@ -11,7 +11,7 @@ import { Box } from '@datalayer/primer-addons';
11
11
  /**
12
12
  * Runtime Cell Variables Picker.
13
13
  */
14
- export function RuntimeCellVariables(props) {
14
+ export function CodeSandboxCellVariables(props) {
15
15
  const { getInputOptions, getOutputOptions, inputs, output, setInputs, setOutput, translator, } = props;
16
16
  const trans = useMemo(() => (translator ?? nullTranslator).load('jupyterlab'), [translator]);
17
17
  const inputForOutputRef = useRef(null);
@@ -169,4 +169,4 @@ export function RuntimeCellVariables(props) {
169
169
  .filter(item => item.selected)
170
170
  .map(item => item.id), onSelectedChange: onSelectedInputsChange, "aria-labelledby": "cell-input-variables", selectionVariant: "multiple", emptyStateText: trans.__('No available variables.'), loading: inputLoading }) })] })] }), _jsxs(FormControl, { children: [_jsx(FormControl.Label, { id: "cell-output-variables", children: trans.__('Output') }), _jsxs(Autocomplete, { children: [_jsx(Autocomplete.Input, { ref: inputForOutputRef, value: output, onChange: onOutputChange }), _jsx(Autocomplete.Overlay, { children: _jsx(Autocomplete.Menu, { items: outputsState ?? [], selectedItemIds: output ? [output] : [], "aria-labelledby": "cell-output-variables", loading: getOutputOptions && !outputsState, emptyStateText: trans.__('Not among the available variables.'), onOpenChange: handleOutputMenuOpenChange }) })] })] })] }));
171
171
  }
172
- export default RuntimeCellVariables;
172
+ export default CodeSandboxCellVariables;
@@ -2,9 +2,9 @@ import { ISessionContext } from '@jupyterlab/apputils';
2
2
  import { CodeCellModel } from '@jupyterlab/cells';
3
3
  import { ITranslator } from '@jupyterlab/translation';
4
4
  /**
5
- * {@link RuntimeCellVariablesDialog} properties
5
+ * {@link CodeSandboxCellVariablesDialog} properties
6
6
  */
7
- export interface IRuntimeCellVariablesDialogProps {
7
+ export interface ICodeSandboxCellVariablesDialogProps {
8
8
  /**
9
9
  * Code cell model
10
10
  */
@@ -33,5 +33,5 @@ export interface IRuntimeCellVariablesDialogProps {
33
33
  /**
34
34
  * Dialog to define the runtime cell variables to transfer
35
35
  */
36
- export declare function RuntimeCellVariablesDialog(props: IRuntimeCellVariablesDialogProps): JSX.Element;
37
- export default RuntimeCellVariablesDialog;
36
+ export declare function CodeSandboxCellVariablesDialog(props: ICodeSandboxCellVariablesDialogProps): JSX.Element;
37
+ export default CodeSandboxCellVariablesDialog;
@@ -9,11 +9,11 @@ import { nullTranslator } from '@jupyterlab/translation';
9
9
  import { JSONExt } from '@lumino/coreutils';
10
10
  import { KernelExecutor } from '@datalayer/jupyter-react';
11
11
  import { RuntimeSnippetsFacade } from '../../jupyter';
12
- import { RuntimeCellVariables } from './RuntimeCellVariables';
12
+ import { CodeSandboxCellVariables } from './CodeSandboxCellVariables';
13
13
  /**
14
14
  * Dialog to define the runtime cell variables to transfer
15
15
  */
16
- export function RuntimeCellVariablesDialog(props) {
16
+ export function CodeSandboxCellVariablesDialog(props) {
17
17
  const { onClose, model, preference, sessionContext, translator } = props;
18
18
  const [inputs, setInputs] = useState([]);
19
19
  const [output, setOutput] = useState();
@@ -96,6 +96,6 @@ export function RuntimeCellVariablesDialog(props) {
96
96
  onClick: setVariables,
97
97
  autoFocus: true,
98
98
  },
99
- ], children: _jsx(RuntimeCellVariables, { inputs: inputs, getInputOptions: sessionContext ? getInputCandidates : undefined, setInputs: setInputs, output: output ?? undefined, getOutputOptions: getOutputCandidates, setOutput: setOutput, translator: translator }) }));
99
+ ], children: _jsx(CodeSandboxCellVariables, { inputs: inputs, getInputOptions: sessionContext ? getInputCandidates : undefined, setInputs: setInputs, output: output ?? undefined, getOutputOptions: getOutputCandidates, setOutput: setOutput, translator: translator }) }));
100
100
  }
101
- export default RuntimeCellVariablesDialog;
101
+ export default CodeSandboxCellVariablesDialog;
@@ -55,5 +55,3 @@ export interface ICodeSandboxLauncherProps {
55
55
  * The launcher of a new code sandbox.
56
56
  */
57
57
  export declare function CodeSandboxLauncher(props: ICodeSandboxLauncherProps): JSX.Element;
58
- export declare const RuntimeLauncherDialog: typeof CodeSandboxLauncher;
59
- export type IRuntimeLauncherDialogProps = ICodeSandboxLauncherProps;
@@ -1,4 +1,4 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  /*
3
3
  * Copyright (c) 2025-2026 Datalayer, Inc.
4
4
  * Distributed under the terms of the Modified BSD License.
@@ -12,7 +12,8 @@ import { useJupyterReactStore } from '@datalayer/jupyter-react';
12
12
  import { USAGE_ROUTE } from '@datalayer/core/lib/routes';
13
13
  import { useNavigate } from '@datalayer/core/lib/hooks';
14
14
  import { NO_RUNTIME_AVAILABLE_LABEL } from '@datalayer/core/lib/i18n';
15
- import { iamStore, useCoreStore, useIAMStore } from '../../state/substates';
15
+ import { getCodeSandboxGivenName, listCodeSandboxGivenNames, nextCodeSandboxGivenName, setCodeSandboxGivenName, } from './CodeSandboxNames';
16
+ import { iamStore, runtimesStore, useCoreStore, useIAMStore, } from '../../state/substates';
16
17
  import { createNotebook, sleep } from '@datalayer/core/lib/utils';
17
18
  import { Markdown } from '@datalayer/core/lib/components/display';
18
19
  import { Timer } from '@datalayer/core/lib/components/progress';
@@ -46,6 +47,30 @@ export function CodeSandboxLauncher(props) {
46
47
  const shouldStartRuntime = startRuntime !== 'defer';
47
48
  const user = iamStore.getState().user;
48
49
  const environments = manager.environments.get();
50
+ /*
51
+ * The sandboxes of this Jupyter Server, offered beside those of the platform.
52
+ *
53
+ * Inside JupyterLab a sandbox can be a kernel of the server, and the picker
54
+ * has always offered both — the launcher offered only the environments of
55
+ * the platform, so "Launch a Code Sandbox" could not launch the very kind
56
+ * that costs nothing and starts at once. The value of a local choice is
57
+ * prefixed, since a kernelspec and an environment are free to share a name.
58
+ */
59
+ const localSpecs = useMemo(() => {
60
+ const specs = runtimesStore.getState().multiServiceManager?.local?.kernelspecs.specs
61
+ ?.kernelspecs ?? {};
62
+ return Object.values(specs)
63
+ .filter(Boolean)
64
+ .map(spec => ({
65
+ name: spec.name,
66
+ title: spec.display_name,
67
+ language: spec.language,
68
+ }));
69
+ }, []);
70
+ const LOCAL_PREFIX = 'local:';
71
+ const localSpecOf = (value) => value.startsWith(LOCAL_PREFIX)
72
+ ? localSpecs.find(spec => spec.name === value.slice(LOCAL_PREFIX.length))
73
+ : undefined;
49
74
  const { configuration } = useCoreStore();
50
75
  const { refreshCredits } = useIAMStore();
51
76
  let navigate;
@@ -61,7 +86,24 @@ export function CodeSandboxLauncher(props) {
61
86
  const jupyterLabAdapter = jupyterReactStore.jupyterLabAdapter;
62
87
  const [selection, setSelection] = useState((kernelSnapshot?.environment || environments[0]?.name) ?? '');
63
88
  const [timeLimit, setTimeLimit] = useState(10);
64
- const [runtimeName, setRuntimeName] = useState(environments[0]?.runtime?.givenNameTemplate || environments[0]?.title || '');
89
+ /*
90
+ * The name the sandbox is given, and what is offered for it.
91
+ *
92
+ * The environment says what to call one of its sandboxes; the sandboxes that
93
+ * already run say which of those names are taken — a second sandbox of an
94
+ * environment is offered that name numbered, exactly as an unnamed one is
95
+ * given one. Both go through `nextCodeSandboxGivenName`, so what is proposed
96
+ * here and what is assigned elsewhere never drift apart.
97
+ */
98
+ const takenGivenNames = useCallback(() => [
99
+ ...manager.runtimesManager
100
+ .get()
101
+ .map(runtime => runtime.given_name)
102
+ .filter(Boolean),
103
+ ...listCodeSandboxGivenNames(),
104
+ ], [manager]);
105
+ const givenNameFor = useCallback((spec) => nextCodeSandboxGivenName(spec?.runtime?.givenNameTemplate || spec?.title || '', takenGivenNames()), [takenGivenNames]);
106
+ const [runtimeName, setRuntimeName] = useState(() => givenNameFor(environments[0]));
65
107
  // Whether the runtim name has been changed by the user or not
66
108
  const [hasCustomRuntimeName, setHasCustomRuntimeName] = useState(false);
67
109
  const [userStorage, setUserStorage] = useState(false);
@@ -75,6 +117,7 @@ export function CodeSandboxLauncher(props) {
75
117
  refreshCredits();
76
118
  }
77
119
  }, [shouldStartRuntime]);
120
+ const isLocalSelection = selection.startsWith(LOCAL_PREFIX);
78
121
  const spec = useMemo(() => environments.find(spec => spec.name === selection), [environments, selection]);
79
122
  const description = spec?.description ?? '';
80
123
  const burningRate = spec?.burning_rate ?? 1;
@@ -86,11 +129,49 @@ export function CodeSandboxLauncher(props) {
86
129
  const selection = e.target.value;
87
130
  setSelection(selection);
88
131
  if (!hasCustomRuntimeName) {
89
- const spec = environments.find(env => env.name === selection);
90
- setRuntimeName(spec?.runtime?.givenNameTemplate || spec?.title || '');
132
+ const local = localSpecOf(selection);
133
+ setRuntimeName(givenNameFor(local ?? environments.find(env => env.name === selection)));
91
134
  }
92
- }, [setSelection, hasCustomRuntimeName]);
135
+ }, [environments, givenNameFor, setSelection, hasCustomRuntimeName]);
93
136
  const handleSubmitRuntime = useCallback(async () => {
137
+ const localSpec = localSpecOf(selection);
138
+ if (localSpec) {
139
+ /*
140
+ * A sandbox of this Jupyter Server: started here, named here.
141
+ *
142
+ * Nothing to reserve, no credits to weigh, no pod to wait for — the
143
+ * kernel is started and the choice is handed back as any other, with
144
+ * the name the user gave it, which is the local answer to `given_name`.
145
+ */
146
+ setError(undefined);
147
+ const kernels = runtimesStore.getState().multiServiceManager?.local?.kernels;
148
+ if (!kernels) {
149
+ setError(_jsx(_Fragment, { children: "The Jupyter Server of this page is not reachable." }));
150
+ return;
151
+ }
152
+ setWaitingForRuntime(true);
153
+ try {
154
+ const connection = await kernels.startNew({ name: localSpec.name });
155
+ setCodeSandboxGivenName(connection.id, runtimeName);
156
+ onSubmit({
157
+ kernelId: connection.id,
158
+ name: localSpec.name,
159
+ language: localSpec.language ?? '',
160
+ location: 'local',
161
+ displayName: getCodeSandboxGivenName(connection.id) ?? localSpec.title,
162
+ });
163
+ }
164
+ catch (reason) {
165
+ console.warn('Failed to start a code sandbox of this server.', reason);
166
+ setError(_jsx(_Fragment, { children: "The code sandbox could not be started." }));
167
+ }
168
+ finally {
169
+ if (isMounted()) {
170
+ setWaitingForRuntime(false);
171
+ }
172
+ }
173
+ return;
174
+ }
94
175
  if (selection) {
95
176
  setError(undefined);
96
177
  setWaitingForRuntime(shouldStartRuntime);
@@ -195,6 +276,8 @@ export function CodeSandboxLauncher(props) {
195
276
  onSubmit(desc);
196
277
  }
197
278
  }
279
+ // `localSpecOf` closes over `localSpecs`, which is built once.
280
+ // eslint-disable-next-line react-hooks/exhaustive-deps
198
281
  }, [
199
282
  manager,
200
283
  selection,
@@ -251,7 +334,7 @@ export function CodeSandboxLauncher(props) {
251
334
  event.preventDefault();
252
335
  handleSubmitRuntime();
253
336
  }
254
- }, children: [_jsxs(FormControl, { disabled: !!kernelSnapshot?.environment || environments.length === 0, children: [_jsx(FormControl.Label, { children: "Environment" }), _jsx(Select, { name: "environment", disabled: !!kernelSnapshot?.environment || environments.length === 0, value: selection, onChange: handleSelectionChange, block: true, children: environments.map(spec => (_jsxs(Select.Option, { value: spec.name, children: [spec.name, spec.title && (_jsxs(_Fragment, { children: [' - ', spec.title] }))] }, spec.name))) }), _jsx(FormControl.Caption, { children: _jsx(_Fragment, { children: markdownParser ? (_jsx(Box, { sx: { img: { maxWidth: '100%' } }, children: _jsx(Markdown, { text: description, markdownParser: markdownParser, sanitizer: sanitizer }) })) : sanitizer ? (
337
+ }, children: [_jsxs(FormControl, { disabled: !!kernelSnapshot?.environment || environments.length === 0, children: [_jsx(FormControl.Label, { children: "Environment" }), _jsxs(Select, { name: "environment", disabled: !!kernelSnapshot?.environment || environments.length === 0, value: selection, onChange: handleSelectionChange, block: true, children: [environments.map(spec => (_jsxs(Select.Option, { value: spec.name, children: [spec.name, spec.title && (_jsxs(_Fragment, { children: [' - ', spec.title] }))] }, spec.name))), localSpecs.map(spec => (_jsxs(Select.Option, { value: `${LOCAL_PREFIX}${spec.name}`, children: [spec.name, spec.title && _jsx(_Fragment, { children: ` - ${spec.title}` }), ' - Jupyter Server'] }, `${LOCAL_PREFIX}${spec.name}`)))] }), _jsx(FormControl.Caption, { children: _jsx(_Fragment, { children: markdownParser ? (_jsx(Box, { sx: { img: { maxWidth: '100%' } }, children: _jsx(Markdown, { text: description, markdownParser: markdownParser, sanitizer: sanitizer }) })) : sanitizer ? (
255
338
  // The description of an environment carries markup. With no
256
339
  // parser to render it, the sanitizer of the application is
257
340
  // enough to show it as what it is rather than as its tags.
@@ -260,13 +343,17 @@ export function CodeSandboxLauncher(props) {
260
343
  } })) : (
261
344
  // Neither a parser nor a sanitizer: the text of the markup,
262
345
  // which is still better than the markup itself.
263
- asPlainText(description)) }) })] }), _jsx(NewCodeSandboxControls, { withReservation: !!startRuntime, burningRate: burningRate, timeLimit: timeLimit, onTimeChange: setTimeLimit, max: effectiveMaxMinutes, disabled: outOfCredits, error: outOfCredits ? 'You must add credits to your account.' : undefined, addCredits: navigate
346
+ asPlainText(description)) }) })] }), _jsx(NewCodeSandboxControls
347
+ // A sandbox of this server costs nothing and starts at once: it has
348
+ // no time to reserve and no storage of the platform to mount.
349
+ , {
350
+ // A sandbox of this server costs nothing and starts at once: it has
351
+ // no time to reserve and no storage of the platform to mount.
352
+ withReservation: !!startRuntime && !isLocalSelection, withUserStorage: !isLocalSelection, burningRate: burningRate, timeLimit: timeLimit, onTimeChange: setTimeLimit, max: effectiveMaxMinutes, disabled: outOfCredits, error: outOfCredits ? 'You must add credits to your account.' : undefined, addCredits: navigate
264
353
  ? () => {
265
354
  navigate(USAGE_ROUTE);
266
355
  }
267
- : undefined, userStorage: userStorage, onUserStorageToggle: () => setUserStorage(current => !current) }), _jsxs(FormControl, { sx: { paddingTop: '10px' }, children: [_jsx(FormControl.Label, { children: "Runtime name" }), _jsx(TextInput, { name: "name", value: runtimeName, onChange: handleKernelNameChange, block: true })] }), hasExample &&
356
+ : undefined, userStorage: userStorage, onUserStorageToggle: () => setUserStorage(current => !current) }), _jsxs(FormControl, { sx: { paddingTop: '10px' }, children: [_jsx(FormControl.Label, { children: "Given Name" }), _jsx(TextInput, { name: "name", value: runtimeName, onChange: handleKernelNameChange, block: true })] }), hasExample &&
268
357
  jupyterLabAdapter?.jupyterLab &&
269
358
  !configuration.whiteLabel && (_jsxs(FormControl, { sx: { paddingTop: '10px' }, children: [_jsx(FormControl.Label, { id: "open-example-label", children: "Open example notebook" }), _jsx(ToggleSwitch, { disabled: !environments.find(spec => spec.name === selection)?.example, checked: openExample, size: "small", onClick: handleSwitchClick, "aria-labelledby": "open-example-label" })] })), error && (_jsx(FlashClosable, { variant: flashLevel, actions: navigate && upgradeSubscription && flashLevel === 'warning' ? (_jsx(Button, { onClick: handleUpgrade, title: 'Upgrade your subscription.', children: "Upgrade" })) : undefined, children: error }))] }) }));
270
359
  }
271
- // Backward-compatible aliases (deprecated: use CodeSandboxLauncher).
272
- export const RuntimeLauncherDialog = CodeSandboxLauncher;
@@ -0,0 +1,47 @@
1
+ /**
2
+ * The given name of a sandbox, when it has one.
3
+ *
4
+ * @param kernelId Kernel of the sandbox
5
+ */
6
+ export declare function getCodeSandboxGivenName(kernelId?: string): string | undefined;
7
+ /**
8
+ * Name a sandbox, or rename it.
9
+ *
10
+ * @param kernelId Kernel of the sandbox
11
+ * @param givenName What to call it; empty forgets the name
12
+ */
13
+ export declare function setCodeSandboxGivenName(kernelId: string, givenName: string): void;
14
+ /**
15
+ * Drop the names of the sandboxes that are no longer running.
16
+ *
17
+ * @param runningKernelIds The kernels that are still there
18
+ */
19
+ export declare function pruneCodeSandboxGivenNames(runningKernelIds: Iterable<string>): void;
20
+ /**
21
+ * Every given name in use by a sandbox of a server or of the browser.
22
+ */
23
+ export declare function listCodeSandboxGivenNames(): string[];
24
+ /**
25
+ * The next free name built on a base, numbered when the base is taken.
26
+ *
27
+ * "Python 3 (ipykernel)", then "Python 3 (ipykernel) 2", then 3 — one rule for
28
+ * the name a sandbox is given on first sight and for the name the launcher
29
+ * offers, so what the launcher proposes is what an unnamed one would get.
30
+ *
31
+ * @param base What to call it, before any numbering
32
+ * @param taken The names already in use
33
+ */
34
+ export declare function nextCodeSandboxGivenName(base: string, taken: Iterable<string>): string;
35
+ /**
36
+ * The given name of a sandbox, giving it one when it has none.
37
+ *
38
+ * The name of what it runs, and a number when a sandbox of that kind is
39
+ * already named — the way two runtimes of one environment are told apart.
40
+ * Assigned once and kept: a name that changed with what else is running would
41
+ * be no name at all.
42
+ *
43
+ * @param kernelId Kernel of the sandbox
44
+ * @param specDisplayName What the kernelspec of the sandbox is called
45
+ */
46
+ export declare function ensureCodeSandboxGivenName(kernelId: string, specDisplayName: string): string;
47
+ export default ensureCodeSandboxGivenName;
@@ -0,0 +1,166 @@
1
+ /*
2
+ * Copyright (c) 2025-2026 Datalayer, Inc.
3
+ * Distributed under the terms of the Modified BSD License.
4
+ */
5
+ /*
6
+ * Copyright (c) 2023-2025 Datalayer, Inc.
7
+ * Distributed under the terms of the Modified BSD License.
8
+ */
9
+ /**
10
+ * The given name of a code sandbox of a Jupyter Server or of the browser.
11
+ *
12
+ * A sandbox of the platform is named when it is launched — `given_name`, the
13
+ * field the launcher fills from the template of its environment — and it
14
+ * carries that name everywhere it is listed. A kernel of a Jupyter Server has
15
+ * no such field: the API gives its identifier and its kernelspec, and nothing
16
+ * that is the name of THIS sandbox. `python3` is what it runs, and reads the
17
+ * same on every one of them.
18
+ *
19
+ * The name was taken from the session instead, and that was the wrong place:
20
+ * a session is a standard Jupyter session, it ties a kernel to a document, it
21
+ * comes and goes as documents open and close, and several can name one kernel.
22
+ * A sandbox then wore the name of a document, changed name when another
23
+ * document took it, and kept the name of a document that had been closed.
24
+ *
25
+ * The given name is held here, by kernel, and given on first sight — from the
26
+ * kernelspec, which is what the launcher does for a runtime of the platform.
27
+ * It is kept in the browser: the kernels of a server outlive the page that
28
+ * lists them, so the name has to outlive it too, and the server has nowhere to
29
+ * put it.
30
+ *
31
+ * @module components/code-sandboxes/CodeSandboxNames
32
+ */
33
+ /** Where the names are kept, and under which key. */
34
+ const STORAGE_KEY = 'datalayer:code-sandbox-names';
35
+ /** The names, by kernel identifier, as they were last written. */
36
+ let names;
37
+ function load() {
38
+ if (names) {
39
+ return names;
40
+ }
41
+ try {
42
+ const raw = globalThis.localStorage?.getItem(STORAGE_KEY);
43
+ names = raw ? JSON.parse(raw) : {};
44
+ }
45
+ catch {
46
+ // A browser that refuses its storage keeps the names for this page only.
47
+ names = {};
48
+ }
49
+ return names;
50
+ }
51
+ function save() {
52
+ try {
53
+ globalThis.localStorage?.setItem(STORAGE_KEY, JSON.stringify(load()));
54
+ }
55
+ catch {
56
+ /* the names stay in memory */
57
+ }
58
+ }
59
+ /**
60
+ * The given name of a sandbox, when it has one.
61
+ *
62
+ * @param kernelId Kernel of the sandbox
63
+ */
64
+ export function getCodeSandboxGivenName(kernelId) {
65
+ return kernelId ? load()[kernelId] : undefined;
66
+ }
67
+ /**
68
+ * Name a sandbox, or rename it.
69
+ *
70
+ * @param kernelId Kernel of the sandbox
71
+ * @param givenName What to call it; empty forgets the name
72
+ */
73
+ export function setCodeSandboxGivenName(kernelId, givenName) {
74
+ const stored = load();
75
+ if (givenName) {
76
+ stored[kernelId] = asGivenName(givenName);
77
+ }
78
+ else {
79
+ delete stored[kernelId];
80
+ }
81
+ save();
82
+ }
83
+ /**
84
+ * Drop the names of the sandboxes that are no longer running.
85
+ *
86
+ * @param runningKernelIds The kernels that are still there
87
+ */
88
+ export function pruneCodeSandboxGivenNames(runningKernelIds) {
89
+ const alive = new Set(runningKernelIds);
90
+ const stored = load();
91
+ let dropped = false;
92
+ Object.keys(stored).forEach(kernelId => {
93
+ if (!alive.has(kernelId)) {
94
+ delete stored[kernelId];
95
+ dropped = true;
96
+ }
97
+ });
98
+ if (dropped) {
99
+ save();
100
+ }
101
+ }
102
+ /**
103
+ * A name a sandbox can wear, out of whatever it was built from.
104
+ *
105
+ * A kernelspec is free to call itself with a picture — a laptop, a snake —
106
+ * and some do. A glyph makes a poor name: it says nothing about WHICH sandbox
107
+ * this is, it reads as a broken character in the fonts of the toolbar, and it
108
+ * is not what anyone would type. Pictures and their variation selectors are
109
+ * dropped, and a name left with nothing is replaced rather than shown empty.
110
+ */
111
+ function asGivenName(base) {
112
+ const stripped = (base ?? '')
113
+ // Pictographs, their skin tones, their joiners and their selectors.
114
+ .replace(/[\u{1F000}-\u{1FAFF}\u{2190}-\u{2BFF}\u{FE00}-\u{FE0F}\u{200D}]/gu, ' ')
115
+ .replace(/\s+/g, ' ')
116
+ .trim();
117
+ return stripped || 'Code Sandbox';
118
+ }
119
+ /**
120
+ * Every given name in use by a sandbox of a server or of the browser.
121
+ */
122
+ export function listCodeSandboxGivenNames() {
123
+ return Object.values(load());
124
+ }
125
+ /**
126
+ * The next free name built on a base, numbered when the base is taken.
127
+ *
128
+ * "Python 3 (ipykernel)", then "Python 3 (ipykernel) 2", then 3 — one rule for
129
+ * the name a sandbox is given on first sight and for the name the launcher
130
+ * offers, so what the launcher proposes is what an unnamed one would get.
131
+ *
132
+ * @param base What to call it, before any numbering
133
+ * @param taken The names already in use
134
+ */
135
+ export function nextCodeSandboxGivenName(base, taken) {
136
+ const used = new Set(taken);
137
+ const stem = asGivenName(base);
138
+ let candidate = stem;
139
+ let ordinal = 1;
140
+ while (used.has(candidate)) {
141
+ ordinal += 1;
142
+ candidate = `${stem} ${ordinal}`;
143
+ }
144
+ return candidate;
145
+ }
146
+ /**
147
+ * The given name of a sandbox, giving it one when it has none.
148
+ *
149
+ * The name of what it runs, and a number when a sandbox of that kind is
150
+ * already named — the way two runtimes of one environment are told apart.
151
+ * Assigned once and kept: a name that changed with what else is running would
152
+ * be no name at all.
153
+ *
154
+ * @param kernelId Kernel of the sandbox
155
+ * @param specDisplayName What the kernelspec of the sandbox is called
156
+ */
157
+ export function ensureCodeSandboxGivenName(kernelId, specDisplayName) {
158
+ const existing = getCodeSandboxGivenName(kernelId);
159
+ if (existing) {
160
+ return existing;
161
+ }
162
+ const candidate = nextCodeSandboxGivenName(specDisplayName, listCodeSandboxGivenNames());
163
+ setCodeSandboxGivenName(kernelId, candidate);
164
+ return candidate;
165
+ }
166
+ export default ensureCodeSandboxGivenName;
@@ -2,7 +2,8 @@ import { ReactElement, ReactNode } from 'react';
2
2
  import { ISessionContext } from '@jupyterlab/apputils';
3
3
  import { ITranslator } from '@jupyterlab/translation';
4
4
  import { IRuntimeDesc } from '../../models';
5
- import { IMultiServiceManager } from '../../runtimes';
5
+ import { type IMultiServiceManager } from '../../runtimes';
6
+ import type { CodeSandboxTransfer } from './CodeSandboxTransfer';
6
7
  type IDisplayMode = 'menu' | 'radio';
7
8
  /**
8
9
  * {@link CodeSandboxPicker} properties
@@ -38,13 +39,18 @@ export interface ICodeSandboxPickerProps {
38
39
  location?: string;
39
40
  };
40
41
  /**
41
- * Runtime description.
42
+ * The sandbox shown as chosen.
43
+ *
44
+ * Given with {@link setRuntimeDesc}, the choice belongs to the host — a
45
+ * cell keeps it in its metadata. Omitted, the picker holds it itself and
46
+ * reports it through {@link onTransferChange}, which is what a dialog with
47
+ * no state of its own needs.
42
48
  */
43
49
  runtimeDesc?: IRuntimeDesc;
44
50
  /**
45
51
  * Set runtime description.
46
52
  */
47
- setRuntimeDesc: (desc?: IRuntimeDesc) => void;
53
+ setRuntimeDesc?: (desc?: IRuntimeDesc) => void;
48
54
  /**
49
55
  * Document session context.
50
56
  */
@@ -61,11 +67,30 @@ export interface ICodeSandboxPickerProps {
61
67
  * Change the look depending on the menu integration.
62
68
  */
63
69
  variant?: 'document' | 'cell';
70
+ /**
71
+ * Whether the questions of a NEW sandbox are asked below the list.
72
+ *
73
+ * They are the launcher's own — see `NewCodeSandboxControls` — and only
74
+ * concern a sandbox that does not run yet.
75
+ */
76
+ withNewSandboxControls?: boolean;
77
+ /**
78
+ * Whether the variables of the current sandbox may be carried to the
79
+ * chosen one, when both speak a language the snippets can serialize.
80
+ */
81
+ withVariablesTransfer?: boolean;
82
+ /**
83
+ * Called with the answer a dialog would return: the sandbox to assign and
84
+ * the variables to carry over.
85
+ *
86
+ * The sandbox is named the way the callers start it: `remote-` and
87
+ * `browser-` prefix an environment to start one in, a local environment is
88
+ * named by its specification alone, and `id` names one that already runs.
89
+ */
90
+ onTransferChange?: (transfer: CodeSandboxTransfer) => void;
64
91
  }
65
92
  /**
66
93
  * The picker of a code sandbox: what runs already, and what could run.
67
94
  */
68
95
  export declare function CodeSandboxPicker(props: ICodeSandboxPickerProps): ReactElement;
69
- export declare const RuntimePickerBase: typeof CodeSandboxPicker;
70
- export type IRuntimePickerBaseProps = ICodeSandboxPickerProps;
71
96
  export default CodeSandboxPicker;