@datalayer/agent-runtimes 1.0.21 → 1.0.25
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.
- package/README.md +2 -2
- package/lib/AgentNode.js +26 -26
- package/lib/chat/Chat.d.ts +1 -1
- package/lib/chat/Chat.js +2 -2
- package/lib/chat/base/ChatBase.js +44 -9
- package/lib/chat/document/EphemeralDocument.d.ts +12 -1
- package/lib/chat/document/EphemeralDocument.js +4 -4
- package/lib/chat/notebook/EphemeralNotebook.d.ts +11 -1
- package/lib/chat/notebook/EphemeralNotebook.js +13 -2
- package/lib/collaboration/DatalayerCollaborationProvider.d.ts +1 -1
- package/lib/collaboration/DatalayerCollaborationProvider.js +5 -5
- package/lib/components/code-sandboxes/CodeSandboxLauncher.d.ts +59 -0
- package/lib/components/code-sandboxes/CodeSandboxLauncher.js +272 -0
- package/lib/components/code-sandboxes/CodeSandboxPicker.d.ts +71 -0
- package/lib/components/code-sandboxes/CodeSandboxPicker.js +163 -0
- package/lib/components/code-sandboxes/NewCodeSandboxControls.d.ts +42 -0
- package/lib/components/code-sandboxes/NewCodeSandboxControls.js +76 -0
- package/lib/components/code-sandboxes/index.d.ts +3 -0
- package/lib/components/code-sandboxes/index.js +7 -0
- package/lib/components/progress/CreditsIndicator.d.ts +7 -0
- package/lib/components/progress/CreditsIndicator.js +2 -2
- package/lib/components/runtimes/RuntimeLauncherDialog.d.ts +5 -55
- package/lib/components/runtimes/RuntimeLauncherDialog.js +5 -269
- package/lib/components/runtimes/RuntimePickerBase.d.ts +6 -67
- package/lib/components/runtimes/RuntimePickerBase.js +6 -151
- package/lib/components/runtimes/RuntimePickerCell.d.ts +2 -2
- package/lib/components/runtimes/RuntimePickerCell.js +4 -4
- package/lib/components/runtimes/RuntimePickerNotebook.js +35 -70
- package/lib/components/runtimes/RuntimeReservationControl.js +8 -1
- package/lib/components/runtimes/RuntimeSimplePicker.js +1 -1
- package/lib/components/runtimes/RuntimeUtils.js +34 -18
- package/lib/components/runtimes/index.d.ts +1 -0
- package/lib/components/runtimes/index.js +2 -0
- package/lib/context/CostUsageChart.d.ts +2 -2
- package/lib/context/CostUsageChart.js +7 -7
- package/lib/context/TokenUsageChart.d.ts +2 -2
- package/lib/context/TokenUsageChart.js +6 -6
- package/lib/context/TurnGraphChart.d.ts +1 -1
- package/lib/context/TurnGraphChart.js +6 -6
- package/lib/examples/AgentEvalsExample.js +1 -1
- package/lib/examples/AgentGuardrailsExample.js +3 -3
- package/lib/examples/AgentInferenceProviderExample.js +1 -1
- package/lib/examples/AgentMonitoringExample.js +6 -6
- package/lib/examples/AgentOtelExample.d.ts +2 -2
- package/lib/examples/AgentOtelExample.js +5 -5
- package/lib/examples/AgentspecsExample.js +1 -1
- package/lib/examples/NotebookCollaborationExample.js +8 -8
- package/lib/examples/main.js +5 -5
- package/lib/examples2/DatalayerNotebookExample.js +8 -8
- package/lib/examples2/main.js +5 -5
- package/lib/hooks/useAgentRuntimes.js +3 -3
- package/lib/hooks/useMonitoring.d.ts +5 -5
- package/lib/hooks/useMonitoring.js +10 -10
- package/lib/protocols/AGUIAdapter.js +4 -0
- package/lib/protocols/VercelAIAdapter.js +3 -0
- package/lib/types/chat.d.ts +23 -0
- package/lib/types/tools.d.ts +11 -0
- package/package.json +3 -3
- package/scripts/codegen/__pycache__/versioning.cpython-313.pyc +0 -0
- package/lib/components/toolbars/AssignmentEditorToolbar.d.ts +0 -4
- package/lib/components/toolbars/AssignmentEditorToolbar.js +0 -18
- package/lib/components/toolbars/DocumentEditorToolbar.d.ts +0 -2
- package/lib/components/toolbars/DocumentEditorToolbar.js +0 -12
- package/lib/components/toolbars/NotebookEditorToolbar.d.ts +0 -29
- package/lib/components/toolbars/NotebookEditorToolbar.js +0 -30
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
/*
|
|
3
|
+
* Copyright (c) 2025-2026 Datalayer, Inc.
|
|
4
|
+
* Distributed under the terms of the Modified BSD License.
|
|
5
|
+
*/
|
|
6
|
+
import { useCallback, useEffect, useMemo, useState } from 'react';
|
|
7
|
+
import { useIsMounted } from 'usehooks-ts';
|
|
8
|
+
import { Button, FormControl, Select, Spinner, Text, TextInput, ToggleSwitch, } from '@primer/react';
|
|
9
|
+
import { Dialog } from '@primer/react/experimental';
|
|
10
|
+
import { Box } from '@datalayer/primer-addons';
|
|
11
|
+
import { useJupyterReactStore } from '@datalayer/jupyter-react';
|
|
12
|
+
import { USAGE_ROUTE } from '@datalayer/core/lib/routes';
|
|
13
|
+
import { useNavigate } from '@datalayer/core/lib/hooks';
|
|
14
|
+
import { NO_RUNTIME_AVAILABLE_LABEL } from '@datalayer/core/lib/i18n';
|
|
15
|
+
import { iamStore, useCoreStore, useIAMStore } from '../../state/substates';
|
|
16
|
+
import { createNotebook, sleep } from '@datalayer/core/lib/utils';
|
|
17
|
+
import { Markdown } from '@datalayer/core/lib/components/display';
|
|
18
|
+
import { Timer } from '@datalayer/core/lib/components/progress';
|
|
19
|
+
import { FlashClosable } from '@datalayer/core/lib/components/flashes';
|
|
20
|
+
import { creditsLimitFor, NewCodeSandboxControls, useNewCodeSandboxAllowance, } from './NewCodeSandboxControls';
|
|
21
|
+
/**
|
|
22
|
+
* The text a fragment of markup reads as.
|
|
23
|
+
*
|
|
24
|
+
* @param html Markup to read
|
|
25
|
+
*/
|
|
26
|
+
function asPlainText(html) {
|
|
27
|
+
if (!html || typeof DOMParser === 'undefined') {
|
|
28
|
+
return html;
|
|
29
|
+
}
|
|
30
|
+
return (new DOMParser().parseFromString(html, 'text/html').body.textContent ?? '');
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Initial time in milliseconds before retrying in case no kernels are available
|
|
34
|
+
*/
|
|
35
|
+
const NOT_AVAILABLE_INIT_RETRY = 10_000;
|
|
36
|
+
/**
|
|
37
|
+
* Number of trials in case of unavailable code sandboxes
|
|
38
|
+
*/
|
|
39
|
+
const NOT_AVAILABLE_RETRIES = 5;
|
|
40
|
+
/**
|
|
41
|
+
* The launcher of a new code sandbox.
|
|
42
|
+
*/
|
|
43
|
+
export function CodeSandboxLauncher(props) {
|
|
44
|
+
const { dialogTitle, kernelSnapshot, manager, onSubmit, markdownParser, sanitizer, upgradeSubscription, submitLabel, startRuntime = true, } = props;
|
|
45
|
+
const hasExample = startRuntime === 'with-example';
|
|
46
|
+
const shouldStartRuntime = startRuntime !== 'defer';
|
|
47
|
+
const user = iamStore.getState().user;
|
|
48
|
+
const environments = manager.environments.get();
|
|
49
|
+
const { configuration } = useCoreStore();
|
|
50
|
+
const { refreshCredits } = useIAMStore();
|
|
51
|
+
let navigate;
|
|
52
|
+
try {
|
|
53
|
+
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
54
|
+
navigate = useNavigate();
|
|
55
|
+
}
|
|
56
|
+
catch (reason) {
|
|
57
|
+
// TODO when would this component be shown outside of a react-router? navigation is only available within a react-router.
|
|
58
|
+
console.warn(reason);
|
|
59
|
+
}
|
|
60
|
+
const jupyterReactStore = useJupyterReactStore();
|
|
61
|
+
const jupyterLabAdapter = jupyterReactStore.jupyterLabAdapter;
|
|
62
|
+
const [selection, setSelection] = useState((kernelSnapshot?.environment || environments[0]?.name) ?? '');
|
|
63
|
+
const [timeLimit, setTimeLimit] = useState(10);
|
|
64
|
+
const [runtimeName, setRuntimeName] = useState(environments[0]?.runtime?.givenNameTemplate || environments[0]?.title || '');
|
|
65
|
+
// Whether the runtim name has been changed by the user or not
|
|
66
|
+
const [hasCustomRuntimeName, setHasCustomRuntimeName] = useState(false);
|
|
67
|
+
const [userStorage, setUserStorage] = useState(false);
|
|
68
|
+
const [openExample, setOpenExample] = useState(false);
|
|
69
|
+
const [waitingForRuntime, setWaitingForRuntime] = useState(false);
|
|
70
|
+
const [error, setError] = useState();
|
|
71
|
+
const [flashLevel, setFlashLevel] = useState('danger');
|
|
72
|
+
const isMounted = useIsMounted();
|
|
73
|
+
useEffect(() => {
|
|
74
|
+
if (shouldStartRuntime) {
|
|
75
|
+
refreshCredits();
|
|
76
|
+
}
|
|
77
|
+
}, [shouldStartRuntime]);
|
|
78
|
+
const spec = useMemo(() => environments.find(spec => spec.name === selection), [environments, selection]);
|
|
79
|
+
const description = spec?.description ?? '';
|
|
80
|
+
const burningRate = spec?.burning_rate ?? 1;
|
|
81
|
+
// The arithmetic the picker asks too — one copy for both, see the module.
|
|
82
|
+
const allowance = useNewCodeSandboxAllowance(burningRate);
|
|
83
|
+
const effectiveMaxMinutes = allowance.effectiveMaxMinutes;
|
|
84
|
+
const outOfCredits = shouldStartRuntime && allowance.outOfCredits;
|
|
85
|
+
const handleSelectionChange = useCallback((e) => {
|
|
86
|
+
const selection = e.target.value;
|
|
87
|
+
setSelection(selection);
|
|
88
|
+
if (!hasCustomRuntimeName) {
|
|
89
|
+
const spec = environments.find(env => env.name === selection);
|
|
90
|
+
setRuntimeName(spec?.runtime?.givenNameTemplate || spec?.title || '');
|
|
91
|
+
}
|
|
92
|
+
}, [setSelection, hasCustomRuntimeName]);
|
|
93
|
+
const handleSubmitRuntime = useCallback(async () => {
|
|
94
|
+
if (selection) {
|
|
95
|
+
setError(undefined);
|
|
96
|
+
setWaitingForRuntime(shouldStartRuntime);
|
|
97
|
+
const spec = environments.find(s => s.name === selection);
|
|
98
|
+
const desc = {
|
|
99
|
+
name: selection,
|
|
100
|
+
language: spec?.language ?? '',
|
|
101
|
+
location: 'remote',
|
|
102
|
+
displayName: runtimeName ?? spec?.title,
|
|
103
|
+
};
|
|
104
|
+
const creditsLimit = creditsLimitFor(timeLimit, burningRate);
|
|
105
|
+
desc.params = {};
|
|
106
|
+
if (startRuntime === 'defer') {
|
|
107
|
+
desc.params['creditsLimit'] = creditsLimit;
|
|
108
|
+
}
|
|
109
|
+
if (userStorage) {
|
|
110
|
+
desc.params['capabilities'] = ['user_storage'];
|
|
111
|
+
}
|
|
112
|
+
let success = true;
|
|
113
|
+
if (shouldStartRuntime) {
|
|
114
|
+
success = false;
|
|
115
|
+
let availableTrial = 1;
|
|
116
|
+
let retryDelay = NOT_AVAILABLE_INIT_RETRY;
|
|
117
|
+
// Should return success status.
|
|
118
|
+
const startNewKernel = async () => {
|
|
119
|
+
try {
|
|
120
|
+
const connection = await manager.runtimesManager.startNew({
|
|
121
|
+
environmentName: selection,
|
|
122
|
+
type: 'notebook',
|
|
123
|
+
givenName: runtimeName,
|
|
124
|
+
creditsLimit: creditsLimit,
|
|
125
|
+
capabilities: userStorage ? ['user_storage'] : undefined,
|
|
126
|
+
snapshot: kernelSnapshot?.id,
|
|
127
|
+
}, {
|
|
128
|
+
username: user?.handle,
|
|
129
|
+
handleComms: true,
|
|
130
|
+
});
|
|
131
|
+
desc.kernelId = connection.id;
|
|
132
|
+
if (jupyterLabAdapter?.jupyterLab && hasExample && openExample) {
|
|
133
|
+
const example = environments.find(spec => spec.name === selection)?.example;
|
|
134
|
+
if (example) {
|
|
135
|
+
const options = {
|
|
136
|
+
kernelId: connection.id,
|
|
137
|
+
kernelName: connection.name,
|
|
138
|
+
};
|
|
139
|
+
createNotebook({
|
|
140
|
+
app: jupyterLabAdapter.jupyterLab,
|
|
141
|
+
name: selection,
|
|
142
|
+
url: example,
|
|
143
|
+
options,
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
// Close the connection as we are not using it.
|
|
148
|
+
connection.dispose();
|
|
149
|
+
}
|
|
150
|
+
catch (error) {
|
|
151
|
+
let msg = _jsx(Text, { children: "Failed to create the remote runtime\u2026" });
|
|
152
|
+
let level = 'danger';
|
|
153
|
+
let retry = false;
|
|
154
|
+
if (error.response?.status === 503) {
|
|
155
|
+
if (availableTrial++ <= NOT_AVAILABLE_RETRIES) {
|
|
156
|
+
retry = true;
|
|
157
|
+
msg = (_jsxs(Text, { children: ["The runtime you have requested is currently not available due to resource limitations. Leave this dialog open, new trial in ", _jsx(Timer, { duration: retryDelay * 0.001 }), ` (${availableTrial - 1}/${NOT_AVAILABLE_RETRIES}).`] }));
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
msg = _jsx(Text, { children: NO_RUNTIME_AVAILABLE_LABEL });
|
|
161
|
+
}
|
|
162
|
+
level = 'warning';
|
|
163
|
+
}
|
|
164
|
+
else if (error.name === 'MaxRuntimesExceededError') {
|
|
165
|
+
msg = (_jsx(Text, { children: "You reached your remote runtime limits. Stop existing runtimes before starting new ones." }));
|
|
166
|
+
level = 'warning';
|
|
167
|
+
}
|
|
168
|
+
else if (error.name === 'RuntimeUnreachable') {
|
|
169
|
+
msg = (_jsx(Text, { children: "The runtime has been created but can not be accessed. Please contact your IT support team to report this issue." }));
|
|
170
|
+
}
|
|
171
|
+
setFlashLevel(level);
|
|
172
|
+
console.error(msg, error);
|
|
173
|
+
setError(msg);
|
|
174
|
+
if (retry) {
|
|
175
|
+
await sleep(retryDelay);
|
|
176
|
+
retryDelay *= 2;
|
|
177
|
+
if (isMounted()) {
|
|
178
|
+
return await startNewKernel();
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
return false;
|
|
182
|
+
}
|
|
183
|
+
finally {
|
|
184
|
+
setWaitingForRuntime(false);
|
|
185
|
+
}
|
|
186
|
+
return true;
|
|
187
|
+
};
|
|
188
|
+
// Start the kernel if the reservation succeeded.
|
|
189
|
+
success = await startNewKernel();
|
|
190
|
+
}
|
|
191
|
+
if (success && isMounted()) {
|
|
192
|
+
if (!shouldStartRuntime) {
|
|
193
|
+
setWaitingForRuntime(false);
|
|
194
|
+
}
|
|
195
|
+
onSubmit(desc);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}, [
|
|
199
|
+
manager,
|
|
200
|
+
selection,
|
|
201
|
+
startRuntime,
|
|
202
|
+
runtimeName,
|
|
203
|
+
onSubmit,
|
|
204
|
+
userStorage,
|
|
205
|
+
openExample,
|
|
206
|
+
jupyterLabAdapter,
|
|
207
|
+
timeLimit,
|
|
208
|
+
shouldStartRuntime,
|
|
209
|
+
isMounted,
|
|
210
|
+
]);
|
|
211
|
+
const handleSwitchClick = useCallback((e) => {
|
|
212
|
+
e.preventDefault();
|
|
213
|
+
setOpenExample(!openExample);
|
|
214
|
+
}, [openExample]);
|
|
215
|
+
const handleUpgrade = useCallback(() => {
|
|
216
|
+
if (upgradeSubscription) {
|
|
217
|
+
navigate?.(upgradeSubscription);
|
|
218
|
+
}
|
|
219
|
+
}, [navigate, upgradeSubscription]);
|
|
220
|
+
const handleKernelNameChange = useCallback((e) => {
|
|
221
|
+
if (typeof e.target.value === 'string') {
|
|
222
|
+
setRuntimeName(e.target.value);
|
|
223
|
+
setHasCustomRuntimeName(true);
|
|
224
|
+
}
|
|
225
|
+
}, []);
|
|
226
|
+
// TODO title color is enforced for JupyterLab.
|
|
227
|
+
// This may be fixed in the jupyter-react theme (Primer generates h1 for the dialog title).
|
|
228
|
+
return (_jsx(Dialog, { title: _jsx("span", { style: { color: 'var(--fgColor-default)' }, children: dialogTitle || 'Launch a new Code Sandbox' }), onClose: () => {
|
|
229
|
+
onSubmit(undefined);
|
|
230
|
+
}, footerButtons: [
|
|
231
|
+
{
|
|
232
|
+
buttonType: 'default',
|
|
233
|
+
onClick: () => {
|
|
234
|
+
onSubmit(undefined);
|
|
235
|
+
},
|
|
236
|
+
content: 'Cancel',
|
|
237
|
+
disabled: waitingForRuntime,
|
|
238
|
+
},
|
|
239
|
+
{
|
|
240
|
+
buttonType: 'primary',
|
|
241
|
+
onClick: handleSubmitRuntime,
|
|
242
|
+
content: waitingForRuntime ? (_jsx(Spinner, { size: "small" })) : ((submitLabel ?? ((startRuntime ?? true) ? 'Launch' : 'Assign'))),
|
|
243
|
+
disabled: waitingForRuntime || !selection || outOfCredits,
|
|
244
|
+
autoFocus: true,
|
|
245
|
+
},
|
|
246
|
+
], children: _jsxs(Box, { as: "form", onKeyDown: event => {
|
|
247
|
+
if (event.defaultPrevented) {
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
if (event.key === 'Enter') {
|
|
251
|
+
event.preventDefault();
|
|
252
|
+
handleSubmitRuntime();
|
|
253
|
+
}
|
|
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 ? (
|
|
255
|
+
// The description of an environment carries markup. With no
|
|
256
|
+
// parser to render it, the sanitizer of the application is
|
|
257
|
+
// enough to show it as what it is rather than as its tags.
|
|
258
|
+
_jsx(Box, { sx: { img: { maxWidth: '100%' } }, dangerouslySetInnerHTML: {
|
|
259
|
+
__html: sanitizer.sanitize(description),
|
|
260
|
+
} })) : (
|
|
261
|
+
// Neither a parser nor a sanitizer: the text of the markup,
|
|
262
|
+
// 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
|
|
264
|
+
? () => {
|
|
265
|
+
navigate(USAGE_ROUTE);
|
|
266
|
+
}
|
|
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 &&
|
|
268
|
+
jupyterLabAdapter?.jupyterLab &&
|
|
269
|
+
!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
|
+
}
|
|
271
|
+
// Backward-compatible aliases (deprecated: use CodeSandboxLauncher).
|
|
272
|
+
export const RuntimeLauncherDialog = CodeSandboxLauncher;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { ReactElement, ReactNode } from 'react';
|
|
2
|
+
import { ISessionContext } from '@jupyterlab/apputils';
|
|
3
|
+
import { ITranslator } from '@jupyterlab/translation';
|
|
4
|
+
import { IRuntimeDesc } from '../../models';
|
|
5
|
+
import { IMultiServiceManager } from '../../runtimes';
|
|
6
|
+
type IDisplayMode = 'menu' | 'radio';
|
|
7
|
+
/**
|
|
8
|
+
* {@link CodeSandboxPicker} properties
|
|
9
|
+
*/
|
|
10
|
+
export interface ICodeSandboxPickerProps {
|
|
11
|
+
/**
|
|
12
|
+
* The display mode.
|
|
13
|
+
*/
|
|
14
|
+
display: IDisplayMode;
|
|
15
|
+
/**
|
|
16
|
+
* Additional actions items to be placed at the top of the picker
|
|
17
|
+
*/
|
|
18
|
+
preActions?: ReactNode;
|
|
19
|
+
/**
|
|
20
|
+
* Additional actions items to be placed at the bottom of the picker
|
|
21
|
+
*/
|
|
22
|
+
postActions?: ReactNode;
|
|
23
|
+
/**
|
|
24
|
+
* Whether the picker is disabled or not.
|
|
25
|
+
*/
|
|
26
|
+
disabled?: boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Runtime description passing this filter function will be displayed.
|
|
29
|
+
*/
|
|
30
|
+
filterRuntime?: (desc: IRuntimeDesc) => boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Session preference.
|
|
33
|
+
*/
|
|
34
|
+
preference?: {
|
|
35
|
+
id?: string;
|
|
36
|
+
kernelDisplayName?: string;
|
|
37
|
+
language?: string;
|
|
38
|
+
location?: string;
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* Runtime description.
|
|
42
|
+
*/
|
|
43
|
+
runtimeDesc?: IRuntimeDesc;
|
|
44
|
+
/**
|
|
45
|
+
* Set runtime description.
|
|
46
|
+
*/
|
|
47
|
+
setRuntimeDesc: (desc?: IRuntimeDesc) => void;
|
|
48
|
+
/**
|
|
49
|
+
* Document session context.
|
|
50
|
+
*/
|
|
51
|
+
sessionContext?: ISessionContext;
|
|
52
|
+
/**
|
|
53
|
+
* Application service manager
|
|
54
|
+
*/
|
|
55
|
+
multiServiceManager: IMultiServiceManager;
|
|
56
|
+
/**
|
|
57
|
+
* Application translator.
|
|
58
|
+
*/
|
|
59
|
+
translator?: ITranslator;
|
|
60
|
+
/**
|
|
61
|
+
* Change the look depending on the menu integration.
|
|
62
|
+
*/
|
|
63
|
+
variant?: 'document' | 'cell';
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* The picker of a code sandbox: what runs already, and what could run.
|
|
67
|
+
*/
|
|
68
|
+
export declare function CodeSandboxPicker(props: ICodeSandboxPickerProps): ReactElement;
|
|
69
|
+
export declare const RuntimePickerBase: typeof CodeSandboxPicker;
|
|
70
|
+
export type IRuntimePickerBaseProps = ICodeSandboxPickerProps;
|
|
71
|
+
export default CodeSandboxPicker;
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
/*
|
|
3
|
+
* Copyright (c) 2025-2026 Datalayer, Inc.
|
|
4
|
+
* Distributed under the terms of the Modified BSD License.
|
|
5
|
+
*/
|
|
6
|
+
import { useEffect, useMemo, useState } from 'react';
|
|
7
|
+
import { nullTranslator } from '@jupyterlab/translation';
|
|
8
|
+
//import { kernelIcon } from '@jupyterlab/ui-components';
|
|
9
|
+
import { ActionList, ActionMenu, IconButton, Text, RadioGroup, Radio, FormControl, LabelGroup, Label, } from '@primer/react';
|
|
10
|
+
import CloudUploadIcon from '@datalayer/icons-react/data1/CloudUploadIcon';
|
|
11
|
+
import { Box } from '@datalayer/primer-addons';
|
|
12
|
+
import { CpuIcon } from '@primer/octicons-react';
|
|
13
|
+
import { BrowserIcon, LaptopSimpleIcon } from '@datalayer/icons-react';
|
|
14
|
+
import { CreditsIndicator } from '../progress';
|
|
15
|
+
import { isRuntimeRemote } from '../../runtimes';
|
|
16
|
+
import { getGroupedRuntimeDescs, } from '../runtimes/RuntimeUtils';
|
|
17
|
+
/**
|
|
18
|
+
* Maximal runtime display name length after which it is trimmed.
|
|
19
|
+
*/
|
|
20
|
+
const RUNTIME_DISPLAY_NAME_MAX_LENGTH = 25;
|
|
21
|
+
/**
|
|
22
|
+
* The picker of a code sandbox: what runs already, and what could run.
|
|
23
|
+
*/
|
|
24
|
+
export function CodeSandboxPicker(props) {
|
|
25
|
+
const { disabled, display, filterRuntime, multiServiceManager, postActions, preActions, preference, runtimeDesc, sessionContext, setRuntimeDesc, translator, variant, } = props;
|
|
26
|
+
const [groupedRuntimeDescs, setGroupedRuntimeDescs] = useState(getGroupedRuntimeDescs(multiServiceManager, preference?.id, translator, filterRuntime, variant));
|
|
27
|
+
const trans = useMemo(() => (translator ?? nullTranslator).load('jupyterlab'), [translator]);
|
|
28
|
+
const [defaultSet, setDefaultSet] = useState(false);
|
|
29
|
+
const [remoteModelsReady, setRemoteModelsReady] = useState(!multiServiceManager.remote);
|
|
30
|
+
// Trick because overflow is an unknown prop of ActionMenu.Overlay.
|
|
31
|
+
const overlayProps = {
|
|
32
|
+
maxHeight: 'large',
|
|
33
|
+
width: (variant === 'cell' ? 'small' : 'auto'),
|
|
34
|
+
};
|
|
35
|
+
useEffect(() => {
|
|
36
|
+
let disposed = false;
|
|
37
|
+
const updateGroupedRuntimeDescs = () => {
|
|
38
|
+
if (disposed) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
setGroupedRuntimeDescs(getGroupedRuntimeDescs(multiServiceManager, preference?.id, translator, filterRuntime, variant));
|
|
42
|
+
};
|
|
43
|
+
const refreshRuntimeModels = multiServiceManager.remote?.runtimesManager.refreshRuntimeModels?.();
|
|
44
|
+
if (refreshRuntimeModels) {
|
|
45
|
+
refreshRuntimeModels
|
|
46
|
+
.catch(reason => {
|
|
47
|
+
console.error('Failed to resolve remote runtimes for the runtime picker.', reason);
|
|
48
|
+
})
|
|
49
|
+
.finally(() => {
|
|
50
|
+
if (disposed) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
updateGroupedRuntimeDescs();
|
|
54
|
+
setRemoteModelsReady(true);
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
setRemoteModelsReady(true);
|
|
59
|
+
}
|
|
60
|
+
// Recompute immediately in case a runtime changed between the initial
|
|
61
|
+
// render and connecting the manager signals.
|
|
62
|
+
updateGroupedRuntimeDescs();
|
|
63
|
+
multiServiceManager.browser?.kernels.runningChanged.connect(updateGroupedRuntimeDescs);
|
|
64
|
+
multiServiceManager.browser?.kernelspecs.specsChanged.connect(updateGroupedRuntimeDescs);
|
|
65
|
+
multiServiceManager.browser?.sessions.runningChanged.connect(updateGroupedRuntimeDescs);
|
|
66
|
+
multiServiceManager.local.kernels.runningChanged.connect(updateGroupedRuntimeDescs);
|
|
67
|
+
multiServiceManager.local.kernelspecs.specsChanged.connect(updateGroupedRuntimeDescs);
|
|
68
|
+
multiServiceManager.local.sessions.runningChanged.connect(updateGroupedRuntimeDescs);
|
|
69
|
+
multiServiceManager.remote?.runtimesManager.changed.connect(updateGroupedRuntimeDescs);
|
|
70
|
+
multiServiceManager.remote?.environments.changed.connect(updateGroupedRuntimeDescs);
|
|
71
|
+
return () => {
|
|
72
|
+
disposed = true;
|
|
73
|
+
multiServiceManager.browser?.kernels.runningChanged.disconnect(updateGroupedRuntimeDescs);
|
|
74
|
+
multiServiceManager.browser?.kernelspecs.specsChanged.disconnect(updateGroupedRuntimeDescs);
|
|
75
|
+
multiServiceManager.browser?.sessions.runningChanged.disconnect(updateGroupedRuntimeDescs);
|
|
76
|
+
multiServiceManager.local.kernels.runningChanged.disconnect(updateGroupedRuntimeDescs);
|
|
77
|
+
multiServiceManager.local.kernelspecs.specsChanged.disconnect(updateGroupedRuntimeDescs);
|
|
78
|
+
multiServiceManager.local.sessions.runningChanged.disconnect(updateGroupedRuntimeDescs);
|
|
79
|
+
multiServiceManager.remote?.runtimesManager.changed.disconnect(updateGroupedRuntimeDescs);
|
|
80
|
+
multiServiceManager.remote?.environments.changed.disconnect(updateGroupedRuntimeDescs);
|
|
81
|
+
};
|
|
82
|
+
}, [multiServiceManager, preference?.id, translator, filterRuntime, variant]);
|
|
83
|
+
useEffect(() => {
|
|
84
|
+
if (defaultSet) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
if (sessionContext && groupedRuntimeDescs) {
|
|
88
|
+
const kernelId = sessionContext.session?.kernel?.id;
|
|
89
|
+
if (kernelId) {
|
|
90
|
+
let matched = false;
|
|
91
|
+
Object.entries(groupedRuntimeDescs).forEach(([group, runtimeDescs]) => {
|
|
92
|
+
runtimeDescs.forEach(runtimeDesc => {
|
|
93
|
+
if (runtimeDesc.kernelId === kernelId) {
|
|
94
|
+
matched = true;
|
|
95
|
+
setRuntimeDesc(runtimeDesc);
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
if (!matched && !remoteModelsReady) {
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
setDefaultSet(true);
|
|
105
|
+
}, [
|
|
106
|
+
defaultSet,
|
|
107
|
+
groupedRuntimeDescs,
|
|
108
|
+
remoteModelsReady,
|
|
109
|
+
sessionContext,
|
|
110
|
+
setRuntimeDesc,
|
|
111
|
+
]);
|
|
112
|
+
// For cell using submenu instead of group would be nice unfortunately the feature
|
|
113
|
+
// is not yet implemented in the component there has been a not-great demo story.
|
|
114
|
+
// https://github.com/primer/react/pull/3585
|
|
115
|
+
return (_jsx(_Fragment, { children: display === 'menu' ? (
|
|
116
|
+
/*
|
|
117
|
+
* Section for Menu display.
|
|
118
|
+
*/
|
|
119
|
+
_jsxs(ActionMenu, { children: [variant === 'cell' ? (_jsx(ActionMenu.Anchor, { children: _jsx(IconButton, { disabled: disabled || groupedRuntimeDescs === null,
|
|
120
|
+
// icon={() => <kernelIcon.react className="dla-Cell-runtime-icon" tag={'span'} />}
|
|
121
|
+
icon: () => (_jsx("span", { className: "dla-Cell-runtime-icon", children: _jsx(CpuIcon, {}) })), "aria-label": trans.__('Assign a Runtime to the Cell.'), title: trans.__('Assign a Runtime to the Cell.'), size: "small", variant: "invisible" }) })) : (_jsxs(ActionMenu.Button, { variant: "default", disabled: disabled || groupedRuntimeDescs === null, children: [_jsx(Text, { fontWeight: 'bold', children: trans.__('Runtime:') }), ' ' + (runtimeDesc?.displayName ?? trans.__('No Runtime'))] })), _jsx(ActionMenu.Overlay, { ...overlayProps, width: "medium", sx: { overflowY: 'auto' }, side: variant === 'cell' ? 'outside-left' : 'outside-right', children: _jsxs(ActionList, { selectionVariant: "single", children: [variant === 'cell' && (_jsxs(ActionList.Item, { selected: runtimeDesc === undefined, onSelect: () => {
|
|
122
|
+
setRuntimeDesc(undefined);
|
|
123
|
+
}, children: [preference?.location && (_jsx(ActionList.LeadingVisual, { children: preference.location === 'local' ? (_jsx(LaptopSimpleIcon, {})) : preference.location === 'browser' ? (_jsx(BrowserIcon, {})) : (_jsx(CloudUploadIcon, {})) })), trans.__('Assign the Notebook Runtime')] }, 'null')), !!preActions && preActions, Object.entries(groupedRuntimeDescs ?? {}).map(([group, runtimeDescs]) => (_jsxs(ActionList.Group, { children: [_jsx(ActionList.GroupHeading, { children: group }), runtimeDescs.map(candidateRuntimeDesc => {
|
|
124
|
+
const annotation = candidateRuntimeDesc.podName
|
|
125
|
+
? ` - ${candidateRuntimeDesc.podName.split('-', 2).reverse()[0]}`
|
|
126
|
+
: candidateRuntimeDesc.kernelId
|
|
127
|
+
? ` - ${candidateRuntimeDesc.kernelId}`
|
|
128
|
+
: '';
|
|
129
|
+
const fullDisplayName = (candidateRuntimeDesc.displayName ?? '') + annotation;
|
|
130
|
+
const displayName = (candidateRuntimeDesc.displayName?.length ?? 0) >
|
|
131
|
+
RUNTIME_DISPLAY_NAME_MAX_LENGTH
|
|
132
|
+
? candidateRuntimeDesc.displayName.slice(0, RUNTIME_DISPLAY_NAME_MAX_LENGTH) + '…'
|
|
133
|
+
: (candidateRuntimeDesc.displayName ?? '');
|
|
134
|
+
return (_jsxs(ActionList.Item, { title: fullDisplayName, selected: (candidateRuntimeDesc.location ===
|
|
135
|
+
runtimeDesc?.location ||
|
|
136
|
+
(isRuntimeRemote(candidateRuntimeDesc.location) &&
|
|
137
|
+
isRuntimeRemote(runtimeDesc?.location ?? 'local'))) &&
|
|
138
|
+
(candidateRuntimeDesc.kernelId ??
|
|
139
|
+
candidateRuntimeDesc.name) ===
|
|
140
|
+
(runtimeDesc?.kernelId ?? runtimeDesc?.name), onSelect: () => {
|
|
141
|
+
setRuntimeDesc(candidateRuntimeDesc);
|
|
142
|
+
}, children: [_jsx(ActionList.LeadingVisual, { children: candidateRuntimeDesc.location === 'local' ? (_jsx(LaptopSimpleIcon, {})) : candidateRuntimeDesc.location === 'browser' ? (_jsx(BrowserIcon, {})) : (_jsx(CloudUploadIcon, {})) }), displayName + annotation.slice(0, 10)] }, candidateRuntimeDesc.name));
|
|
143
|
+
})] }, group))), !!postActions && (_jsxs(_Fragment, { children: [_jsx(ActionList.Divider, {}), postActions] }))] }) })] })) : (
|
|
144
|
+
/*
|
|
145
|
+
* Section for Radio display.
|
|
146
|
+
*/
|
|
147
|
+
_jsxs(_Fragment, { children: [defaultSet && (_jsx(RadioGroup, { name: "kernel-options", "aria-labelledby": "kernel-options", children: Object.entries(groupedRuntimeDescs ?? {}).map(([group, runtimeDescs]) => (_jsxs(Box, { children: [_jsx(Box, { as: "h4", style: { marginTop: 0 }, children: group }), runtimeDescs.map(k => {
|
|
148
|
+
return (
|
|
149
|
+
// A kernel identifies a runtime that already runs; an
|
|
150
|
+
// environment to start one in has none, and is named
|
|
151
|
+
// by where it runs and what it runs.
|
|
152
|
+
_jsxs(Box, { title: k.name, children: [_jsxs(FormControl, { children: [_jsx(Radio, { value: k.kernelId, onChange: () => {
|
|
153
|
+
setRuntimeDesc(k);
|
|
154
|
+
}, checked: (k.location === runtimeDesc?.location ||
|
|
155
|
+
(isRuntimeRemote(k.location) &&
|
|
156
|
+
isRuntimeRemote(runtimeDesc?.location ?? 'local'))) &&
|
|
157
|
+
(k.kernelId ?? k.name) ===
|
|
158
|
+
(runtimeDesc?.kernelId ?? runtimeDesc?.name) }), _jsx(FormControl.Label, { children: _jsxs(Box, { display: "flex", children: [_jsx(Box, { children: k.displayName }), k.kernelId && k.location === 'remote' && (_jsx(Box, { ml: 3, mt: 1, children: _jsx(CreditsIndicator, { kernelId: k.kernelId, serviceManager: multiServiceManager.remote }, "credits-indicator") }))] }) }), _jsx(FormControl.Caption, { children: _jsxs(LabelGroup, { sx: { marginTop: 1 }, children: [_jsx(Label, { variant: "secondary", children: k.name }), _jsx(Label, { variant: "secondary", sx: { marginLeft: 1 }, children: k.location }), k.burningRate && (_jsxs(Label, { variant: "sponsors", sx: { marginLeft: 1 }, children: [k.burningRate, " credits/second"] })), k.gpu && (_jsx(Label, { variant: "success", sx: { marginLeft: 1 }, children: "GPU" }))] }) })] }), _jsx(ActionList.Divider, {})] }, `${k.location}:${k.kernelId ?? k.name}`));
|
|
159
|
+
})] }, group))) })), !!postActions && _jsx(_Fragment, { children: postActions })] })) }));
|
|
160
|
+
}
|
|
161
|
+
// Backward-compatible aliases (deprecated: use CodeSandboxPicker).
|
|
162
|
+
export const RuntimePickerBase = CodeSandboxPicker;
|
|
163
|
+
export default CodeSandboxPicker;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/** What the account allows for a new sandbox of a given burning rate. */
|
|
2
|
+
export interface INewCodeSandboxAllowance {
|
|
3
|
+
/** Minutes the available credits pay for; undefined without a rate. */
|
|
4
|
+
maxFromCredits?: number;
|
|
5
|
+
/** The ceiling offered by the reservation control. */
|
|
6
|
+
effectiveMaxMinutes: number;
|
|
7
|
+
/** Whether a paid start is impossible: no runs left and no credits. */
|
|
8
|
+
outOfCredits: boolean;
|
|
9
|
+
hasKnownCredits: boolean;
|
|
10
|
+
hasKnownRunAllowance: boolean;
|
|
11
|
+
hasRemainingRuns: boolean;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* The credits arithmetic both the launcher and the picker ask.
|
|
15
|
+
*
|
|
16
|
+
* @param burningRate Credits per second of the chosen environment
|
|
17
|
+
*/
|
|
18
|
+
export declare function useNewCodeSandboxAllowance(burningRate: number | undefined): INewCodeSandboxAllowance;
|
|
19
|
+
/** The credits a time reservation stands for. */
|
|
20
|
+
export declare function creditsLimitFor(timeLimitMinutes: number, burningRate: number): number;
|
|
21
|
+
export interface INewCodeSandboxControlsProps {
|
|
22
|
+
/** Whether the time reservation is offered; it is by default. */
|
|
23
|
+
withReservation?: boolean;
|
|
24
|
+
/** Credits per second of the chosen environment. */
|
|
25
|
+
burningRate?: number;
|
|
26
|
+
timeLimit: number;
|
|
27
|
+
onTimeChange: (minutes: number) => void;
|
|
28
|
+
/** The ceiling of the reservation control. */
|
|
29
|
+
max: number;
|
|
30
|
+
disabled?: boolean;
|
|
31
|
+
error?: string;
|
|
32
|
+
/** Offered next to the error when credits can be added from here. */
|
|
33
|
+
addCredits?: () => void;
|
|
34
|
+
userStorage: boolean;
|
|
35
|
+
onUserStorageToggle: () => void;
|
|
36
|
+
storageDisabled?: boolean;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* The two questions of a new sandbox: the time reservation, and the storage.
|
|
40
|
+
* The storage is withheld on white-label deployments, as it always was.
|
|
41
|
+
*/
|
|
42
|
+
export declare function NewCodeSandboxControls(props: INewCodeSandboxControlsProps): JSX.Element;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } 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
|
+
* What launching a NEW code sandbox asks of the user, in one place.
|
|
8
|
+
*
|
|
9
|
+
* The launcher dialog and the picker both offer to start a sandbox, and both
|
|
10
|
+
* used to carry their own copy of the same questions — how long to reserve,
|
|
11
|
+
* whether to mount the user storage — and of the same arithmetic over the
|
|
12
|
+
* credits of the account. This module is the single copy: the hook computes
|
|
13
|
+
* what the account allows, the component renders the two controls, and the
|
|
14
|
+
* helper turns a time limit into the credits reservation.
|
|
15
|
+
*
|
|
16
|
+
* @module components/code-sandboxes/NewCodeSandboxControls
|
|
17
|
+
*/
|
|
18
|
+
import { FormControl, ToggleSwitch, Tooltip, IconButton } from '@primer/react';
|
|
19
|
+
import { AlertIcon } from '@primer/octicons-react';
|
|
20
|
+
import { useCoreStore, useIAMStore } from '../../state/substates';
|
|
21
|
+
import { RuntimeReservationControl, MAXIMAL_RUNTIME_TIME_RESERVATION_MINUTES, } from '../runtimes/RuntimeReservationControl';
|
|
22
|
+
/**
|
|
23
|
+
* The credits arithmetic both the launcher and the picker ask.
|
|
24
|
+
*
|
|
25
|
+
* @param burningRate Credits per second of the chosen environment
|
|
26
|
+
*/
|
|
27
|
+
export function useNewCodeSandboxAllowance(burningRate) {
|
|
28
|
+
const { credits, user } = useIAMStore();
|
|
29
|
+
const includedRuns = user?.subscription?.usage?.included_runs ??
|
|
30
|
+
user?.subscription?.included_runs;
|
|
31
|
+
const currentRuns = user?.subscription?.usage?.current_runs ??
|
|
32
|
+
user?.subscription?.current_runs ??
|
|
33
|
+
user?.subscription?.used_runs;
|
|
34
|
+
const hasKnownRunAllowance = typeof includedRuns === 'number';
|
|
35
|
+
const hasRemainingRuns = hasKnownRunAllowance &&
|
|
36
|
+
typeof currentRuns === 'number' &&
|
|
37
|
+
includedRuns > 0 &&
|
|
38
|
+
currentRuns < includedRuns;
|
|
39
|
+
const hasKnownCredits = typeof credits?.available === 'number';
|
|
40
|
+
const maxFromCredits = burningRate
|
|
41
|
+
? Math.floor((credits?.available ?? 0) / burningRate / 60.0)
|
|
42
|
+
: undefined;
|
|
43
|
+
const effectiveMaxMinutes = hasKnownCredits && hasKnownRunAllowance && !hasRemainingRuns
|
|
44
|
+
? Math.max(1, maxFromCredits ?? 0)
|
|
45
|
+
: Math.max(10, maxFromCredits && maxFromCredits > 0 ? maxFromCredits : 0);
|
|
46
|
+
const outOfCredits = hasKnownCredits &&
|
|
47
|
+
hasKnownRunAllowance &&
|
|
48
|
+
!hasRemainingRuns &&
|
|
49
|
+
((credits?.available ?? 0) <= 0 || (maxFromCredits ?? 0) < Number.EPSILON);
|
|
50
|
+
return {
|
|
51
|
+
maxFromCredits,
|
|
52
|
+
effectiveMaxMinutes,
|
|
53
|
+
outOfCredits,
|
|
54
|
+
hasKnownCredits,
|
|
55
|
+
hasKnownRunAllowance,
|
|
56
|
+
hasRemainingRuns,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
/** The credits a time reservation stands for. */
|
|
60
|
+
export function creditsLimitFor(timeLimitMinutes, burningRate) {
|
|
61
|
+
return (Math.min(timeLimitMinutes, MAXIMAL_RUNTIME_TIME_RESERVATION_MINUTES) *
|
|
62
|
+
burningRate *
|
|
63
|
+
60);
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* The two questions of a new sandbox: the time reservation, and the storage.
|
|
67
|
+
* The storage is withheld on white-label deployments, as it always was.
|
|
68
|
+
*/
|
|
69
|
+
export function NewCodeSandboxControls(props) {
|
|
70
|
+
const { addCredits, burningRate, withReservation = true, disabled, error, max, onTimeChange, onUserStorageToggle, storageDisabled, timeLimit, userStorage, } = props;
|
|
71
|
+
const { configuration } = useCoreStore();
|
|
72
|
+
return (_jsxs(_Fragment, { children: [withReservation && (_jsx(RuntimeReservationControl, { addCredits: addCredits, disabled: disabled, label: 'Time reservation', max: max, time: timeLimit, burningRate: burningRate, onTimeChange: onTimeChange, error: error })), !configuration.whiteLabel && (_jsxs(FormControl, { disabled: storageDisabled, layout: "horizontal", children: [_jsxs(FormControl.Label, { id: "new-sandbox-user-storage-label", children: ["User storage", _jsx(Tooltip, { text: "The runtime will be slower to start.", direction: "e", style: { marginLeft: 3 }, children: _jsx(IconButton, { icon: AlertIcon, "aria-label": "", variant: "invisible" }) })] }), _jsx(ToggleSwitch, { disabled: storageDisabled, checked: userStorage, size: "small", onClick: event => {
|
|
73
|
+
event.preventDefault();
|
|
74
|
+
onUserStorageToggle();
|
|
75
|
+
}, "aria-labelledby": "new-sandbox-user-storage-label" })] }))] }));
|
|
76
|
+
}
|
|
@@ -25,6 +25,13 @@ type ICreditsIndicatorProps = {
|
|
|
25
25
|
* Duration is the kernel max duration
|
|
26
26
|
*/
|
|
27
27
|
onUpdate?: (progress: number, duration: number) => void;
|
|
28
|
+
/**
|
|
29
|
+
* Paddings of the indicator, each defaulting to the current ones.
|
|
30
|
+
*/
|
|
31
|
+
paddingLeft?: number | string;
|
|
32
|
+
paddingRight?: number | string;
|
|
33
|
+
paddingTop?: number | string;
|
|
34
|
+
paddingBottom?: number | string;
|
|
28
35
|
};
|
|
29
36
|
/**
|
|
30
37
|
* Credits indicator component.
|