@gen3/workspaces 0.12.51 → 0.12.53
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/dist/dts/components/HorizontalAccordian.js +2 -2
- package/dist/dts/components/HorizontalAccordian.js.map +1 -1
- package/dist/dts/components/MicroContainerReduxPanel.d.ts.map +1 -1
- package/dist/dts/components/MicroContainerReduxPanel.js +13 -12
- package/dist/dts/components/MicroContainerReduxPanel.js.map +1 -1
- package/dist/dts/components/TierSelectorLanding.d.ts.map +1 -1
- package/dist/dts/components/TierSelectorLanding.js +5 -4
- package/dist/dts/components/TierSelectorLanding.js.map +1 -1
- package/dist/dts/components/UpgradeActionsPanel.d.ts.map +1 -1
- package/dist/dts/components/UpgradeActionsPanel.js +4 -4
- package/dist/dts/components/UpgradeActionsPanel.js.map +1 -1
- package/dist/dts/components/WorkspaceTierCard.d.ts +1 -1
- package/dist/dts/components/WorkspaceTierCard.d.ts.map +1 -1
- package/dist/dts/components/WorkspaceTierCard.js +2 -2
- package/dist/dts/components/WorkspaceTierCard.js.map +1 -1
- package/dist/dts/components/WorkspaceTierCard.stories.d.ts +1 -1
- package/dist/dts/components/types.d.ts +1 -0
- package/dist/dts/components/types.d.ts.map +1 -1
- package/dist/dts/core/jegGatewayApi.d.ts +8 -8
- package/dist/dts/providers/MicroContainerReduxProvider.d.ts +2 -1
- package/dist/dts/providers/MicroContainerReduxProvider.d.ts.map +1 -1
- package/dist/dts/providers/MicroContainerReduxProvider.js +2 -2
- package/dist/dts/providers/MicroContainerReduxProvider.js.map +1 -1
- package/dist/dts/providers/useJEGWorkspaceResourceMonitor.d.ts +1 -1
- package/dist/dts/providers/useJEGWorkspaceResourceMonitor.d.ts.map +1 -1
- package/dist/dts/providers/useJEGWorkspaceResourceMonitor.js +5 -3
- package/dist/dts/providers/useJEGWorkspaceResourceMonitor.js.map +1 -1
- package/dist/dts/server/workspaceAssetsHandler.d.ts.map +1 -1
- package/dist/dts/server/workspaceAssetsHandler.js +3 -0
- package/dist/dts/server/workspaceAssetsHandler.js.map +1 -1
- package/dist/dts/types.d.ts +1 -1
- package/dist/dts/types.d.ts.map +1 -1
- package/dist/dts/workspace/WorkspaceCenter.d.ts.map +1 -1
- package/dist/dts/workspace/WorkspaceCenter.js +3 -2
- package/dist/dts/workspace/WorkspaceCenter.js.map +1 -1
- package/dist/dts/workspace/WorkspaceLayout/SettingsPanel.d.ts.map +1 -1
- package/dist/dts/workspace/WorkspaceLayout/SettingsPanel.js +1 -2
- package/dist/dts/workspace/WorkspaceLayout/SettingsPanel.js.map +1 -1
- package/dist/dts/workspace/WorkspaceLayout/WorkspaceToolbar.d.ts.map +1 -1
- package/dist/dts/workspace/WorkspaceLayout/WorkspaceToolbar.js +2 -2
- package/dist/dts/workspace/WorkspaceLayout/WorkspaceToolbar.js.map +1 -1
- package/dist/dts/workspace/tiers/FreeWorkspace.d.ts.map +1 -1
- package/dist/dts/workspace/tiers/FreeWorkspace.js +29 -18
- package/dist/dts/workspace/tiers/FreeWorkspace.js.map +1 -1
- package/dist/dts/workspace/tiers/RemoteComputeWorkspace.d.ts.map +1 -1
- package/dist/dts/workspace/tiers/RemoteComputeWorkspace.js +31 -19
- package/dist/dts/workspace/tiers/RemoteComputeWorkspace.js.map +1 -1
- package/dist/dts/workspace/tiers/types.d.ts +2 -0
- package/dist/dts/workspace/tiers/types.d.ts.map +1 -1
- package/dist/index/cjs/index.js +132 -114
- package/dist/index/cjs/index.js.map +1 -1
- package/dist/index/esm/index.js +133 -115
- package/dist/index/esm/index.js.map +1 -1
- package/dist/index.d.ts +4 -1
- package/dist/server/cjs/server.js +4 -1
- package/dist/server/cjs/server.js.map +1 -1
- package/dist/server/esm/server.js +4 -1
- package/dist/server/esm/server.js.map +1 -1
- package/package.json +10 -10
package/dist/index/esm/index.js
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
2
2
|
import React, { useCallback, useMemo, useState, useRef, useEffect, createContext, useImperativeHandle, forwardRef } from 'react';
|
|
3
3
|
import { mergeDefaultTailwindClassnames, ACTIVITY_CHANNEL, convertSecondsToMilliseconds, InfoRolloverButton, OverflowTooltippedLabel, PanelErrorBoundary, ProtectedContent, FixedNavPageLayout, ContentSource, getNavPageLayoutPropsFromConfig } from '@gen3/frontend';
|
|
4
|
-
import { Card, Stack, Badge, Text, List, Group, Tooltip, Button, LoadingOverlay, Alert, Loader, Divider, Select, ActionIcon, TextInput, Textarea, Table, Scroller, Tabs, Center } from '@mantine/core';
|
|
4
|
+
import { Card, Stack, Badge, Text, List, Group, Tooltip, Button, Container, SimpleGrid, LoadingOverlay, Alert, Loader, Divider, Select, ActionIcon, TextInput, Textarea, Table, Scroller, Tabs, Center } from '@mantine/core';
|
|
5
5
|
import { Icon } from '@iconify-icon/react';
|
|
6
|
+
import { useInterval, useElementSize, useDisclosure, useResizeObserver } from '@mantine/hooks';
|
|
6
7
|
import { GEN3_API, GEN3_WORKSPACE_API, gen3Api, useCoreSelector, selectJEGActiveWorkspaceStatus, useCoreDispatch, setJEGActiveWorkspaceStatus, WorkspaceStatus, setJEGRequestedWorkspaceStatus, RequestedWorkspaceStatus, setJEGActiveWorkspaceId, isFetchBaseQueryError, selectJEGRequestedWorkspaceStatus, selectJEGRequestedWorkspaceStatusTimestamp, isTimeGreaterThan, formatUptimeInMinutes, selectJEGKernelIds, removeManyJEGActiveKernels, upsertManyJEGActiveKernels, normalizeRtkError, selectAllJEGKernels, addJEGActiveKernel, selectWorkspaceTier, setWorkspaceTier, selectWorkspaceFullscreen, setWorkspaceFullscreen, useGetDictionaryFromUrlQuery, useUserAuth, GEN3_COMMONS_NAME } from '@gen3/core';
|
|
7
8
|
import { useDeepCompareEffect } from 'use-deep-compare';
|
|
8
9
|
import { notifications } from '@mantine/notifications';
|
|
9
|
-
import { useElementSize, useDisclosure, useResizeObserver } from '@mantine/hooks';
|
|
10
10
|
import { IoSettingsSharp } from 'react-icons/io5';
|
|
11
11
|
import { BsFillInfoCircleFill } from 'react-icons/bs';
|
|
12
12
|
import { MdKeyboardArrowDown, MdKeyboardArrowRight } from 'react-icons/md';
|
|
13
13
|
import { merge } from 'lodash';
|
|
14
14
|
|
|
15
|
-
const WorkspaceTierCard = ({ label, description, tier, features = [], tooltip = 'Launch workspace', buttonLabel = 'Launch', onSelectTier, baseColor })=>{
|
|
15
|
+
const WorkspaceTierCard = ({ label, description, tier, tierLabel = undefined, features = [], tooltip = 'Launch workspace', buttonLabel = 'Launch', onSelectTier, baseColor })=>{
|
|
16
16
|
const handleClick = useCallback(()=>onSelectTier(tier), [
|
|
17
17
|
onSelectTier,
|
|
18
18
|
tier
|
|
@@ -47,7 +47,7 @@ const WorkspaceTierCard = ({ label, description, tier, features = [], tooltip =
|
|
|
47
47
|
color: "primary.4",
|
|
48
48
|
size: "lg",
|
|
49
49
|
fw: 500,
|
|
50
|
-
children: tier
|
|
50
|
+
children: tierLabel ?? (tier === 'remote' ? 'paid' : 'free')
|
|
51
51
|
})
|
|
52
52
|
})
|
|
53
53
|
}),
|
|
@@ -113,10 +113,10 @@ const WorkspaceTierCard = ({ label, description, tier, features = [], tooltip =
|
|
|
113
113
|
};
|
|
114
114
|
|
|
115
115
|
const DEFAULT_CLASSNAMES = {
|
|
116
|
-
root: 'mx-auto w-full max-w-7xl p-6 md:p-
|
|
117
|
-
background: 'relative overflow-hidden md:p-
|
|
116
|
+
root: 'mx-auto w-full h-full overflow-auto max-w-7xl p-6 sm:p-2 md:p-4 lg:p-8',
|
|
117
|
+
background: 'relative overflow-hidden sm:p-2 md:p-4 lg:p-8',
|
|
118
118
|
label: 'text-md uppercase tracking-[0.28em] text-primary',
|
|
119
|
-
description: '
|
|
119
|
+
description: 'font-black leading-tight text-primary md:text-3xl sm:text-2xl xs:text-lg mt-4 mb-4',
|
|
120
120
|
additionalDescription: 'max-w-2xl text-md font-medium text-base-contrast',
|
|
121
121
|
button: 'mt-10 flex w-full items-center justify-center rounded-md bg-primary px-8 py-3 text-sm font-semibold text-primary-contrast shadow-sm hover:bg-primary focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary'
|
|
122
122
|
};
|
|
@@ -127,36 +127,43 @@ const TierSelectorLanding = ({ cards, onSelectTier, classNames, label = 'Gen3 Ju
|
|
|
127
127
|
const mergedClassnames = useMemo(()=>mergeDefaultTailwindClassnames(DEFAULT_CLASSNAMES, classNames ?? {}), [
|
|
128
128
|
classNames
|
|
129
129
|
]);
|
|
130
|
-
return /*#__PURE__*/ jsx(
|
|
130
|
+
return /*#__PURE__*/ jsx(Container, {
|
|
131
|
+
size: "xl",
|
|
131
132
|
className: mergedClassnames.root,
|
|
132
|
-
children: /*#__PURE__*/ jsxs(
|
|
133
|
+
children: /*#__PURE__*/ jsxs(Stack, {
|
|
134
|
+
gap: "lg",
|
|
133
135
|
className: mergedClassnames.background,
|
|
134
136
|
children: [
|
|
135
|
-
/*#__PURE__*/
|
|
136
|
-
|
|
137
|
-
children:
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
]
|
|
152
|
-
})
|
|
137
|
+
/*#__PURE__*/ jsxs(Stack, {
|
|
138
|
+
gap: "xs",
|
|
139
|
+
children: [
|
|
140
|
+
/*#__PURE__*/ jsx(Text, {
|
|
141
|
+
className: mergedClassnames.label,
|
|
142
|
+
children: label
|
|
143
|
+
}),
|
|
144
|
+
/*#__PURE__*/ jsx(Text, {
|
|
145
|
+
className: mergedClassnames.description,
|
|
146
|
+
children: description
|
|
147
|
+
}),
|
|
148
|
+
additionalDescriptions.map((desc)=>/*#__PURE__*/ jsx(Text, {
|
|
149
|
+
className: mergedClassnames.additionalDescription,
|
|
150
|
+
children: desc
|
|
151
|
+
}, desc.slice(0, 20)))
|
|
152
|
+
]
|
|
153
153
|
}),
|
|
154
|
-
/*#__PURE__*/ jsx(
|
|
155
|
-
|
|
154
|
+
/*#__PURE__*/ jsx(SimpleGrid, {
|
|
155
|
+
cols: {
|
|
156
|
+
base: 2,
|
|
157
|
+
sm: 2,
|
|
158
|
+
md: 2,
|
|
159
|
+
lg: 3
|
|
160
|
+
},
|
|
161
|
+
spacing: "md",
|
|
156
162
|
role: "list",
|
|
157
163
|
"aria-label": "Workspace tier selection",
|
|
158
164
|
children: Object.values(cards).map((card)=>/*#__PURE__*/ jsx(WorkspaceTierCard, {
|
|
159
165
|
tier: card.tier,
|
|
166
|
+
tierLabel: card.tierLabel,
|
|
160
167
|
onSelectTier: onSelectTier,
|
|
161
168
|
label: card.label,
|
|
162
169
|
description: card.description,
|
|
@@ -196,49 +203,60 @@ const FreeWorkspace = ({ assetBaseUrl = '/api/workspace-assets/free', onReady, o
|
|
|
196
203
|
}
|
|
197
204
|
};
|
|
198
205
|
}, []);
|
|
199
|
-
// Handle setting up iframe event listeners
|
|
206
|
+
// Handle setting up iframe event listeners for activity detection.
|
|
207
|
+
// Tries same-origin contentDocument access first; falls back to
|
|
208
|
+
// focus/blur heuristics when the iframe is cross-origin.
|
|
200
209
|
useEffect(()=>{
|
|
201
210
|
if (!jupyterReady) return;
|
|
202
211
|
const updateUserActivity = ()=>{
|
|
203
|
-
const timestamp = Date.now();
|
|
204
212
|
if (broadcastChannelRef.current) {
|
|
205
213
|
broadcastChannelRef.current.postMessage({
|
|
206
214
|
type: 'activity-update',
|
|
207
|
-
timestamp
|
|
215
|
+
timestamp: Date.now()
|
|
208
216
|
});
|
|
209
217
|
}
|
|
210
218
|
};
|
|
211
219
|
const iframe = iframeRef.current;
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
220
|
+
let usedDirectListeners = false;
|
|
221
|
+
if (iframe?.contentWindow) {
|
|
222
|
+
try {
|
|
223
|
+
const iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
|
|
224
|
+
if (iframeDoc) {
|
|
225
|
+
iframeDoc.addEventListener('mousedown', updateUserActivity);
|
|
226
|
+
iframeDoc.addEventListener('keypress', updateUserActivity);
|
|
227
|
+
iframeDoc.addEventListener('scroll', updateUserActivity);
|
|
228
|
+
iframeDoc.addEventListener('touchstart', updateUserActivity);
|
|
229
|
+
usedDirectListeners = true;
|
|
230
|
+
}
|
|
231
|
+
} catch {
|
|
232
|
+
// cross-origin — fall through to focus/blur fallback
|
|
223
233
|
}
|
|
224
|
-
}
|
|
225
|
-
|
|
234
|
+
}
|
|
235
|
+
// Fallback: treat iframe receiving focus as user activity.
|
|
236
|
+
// This fires when the user clicks into the iframe even if we
|
|
237
|
+
// cannot attach listeners to its document directly.
|
|
238
|
+
if (!usedDirectListeners) {
|
|
239
|
+
window.addEventListener('blur', updateUserActivity);
|
|
240
|
+
window.addEventListener('focus', updateUserActivity);
|
|
226
241
|
}
|
|
227
242
|
return ()=>{
|
|
228
|
-
if (iframe?.contentWindow) {
|
|
243
|
+
if (usedDirectListeners && iframe?.contentWindow) {
|
|
229
244
|
try {
|
|
230
245
|
const iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
|
|
231
246
|
if (iframeDoc) {
|
|
232
247
|
iframeDoc.removeEventListener('mousedown', updateUserActivity);
|
|
233
248
|
iframeDoc.removeEventListener('keypress', updateUserActivity);
|
|
234
249
|
iframeDoc.removeEventListener('scroll', updateUserActivity);
|
|
235
|
-
iframeDoc.removeEventListener('click', updateUserActivity);
|
|
236
250
|
iframeDoc.removeEventListener('touchstart', updateUserActivity);
|
|
237
251
|
}
|
|
238
252
|
} catch {
|
|
239
253
|
// Ignore cleanup errors
|
|
240
254
|
}
|
|
241
255
|
}
|
|
256
|
+
if (!usedDirectListeners) {
|
|
257
|
+
window.removeEventListener('blur', updateUserActivity);
|
|
258
|
+
window.removeEventListener('focus', updateUserActivity);
|
|
259
|
+
}
|
|
242
260
|
};
|
|
243
261
|
}, [
|
|
244
262
|
jupyterReady
|
|
@@ -683,6 +701,7 @@ const notifyUser = (title, message, status = 0)=>{
|
|
|
683
701
|
[1]: 'utility.2',
|
|
684
702
|
[2]: 'utility.4'
|
|
685
703
|
};
|
|
704
|
+
notifications.clean();
|
|
686
705
|
notifications.show({
|
|
687
706
|
title,
|
|
688
707
|
message,
|
|
@@ -706,7 +725,7 @@ const workspaceShutdownAlertLimit = 30000; // 5 minutes: 5 * 60 * 1000 TODO Figu
|
|
|
706
725
|
/**
|
|
707
726
|
* Monitors resource usage.
|
|
708
727
|
* Handles state changes and polls for workspace status using the JEG APIs
|
|
709
|
-
*/ const useJEGWorkspaceResourceMonitor = (workspaceId = null, monitorWorkspace)=>{
|
|
728
|
+
*/ const useJEGWorkspaceResourceMonitor = (workspaceId = null, monitorWorkspace, startTimeLimit = MAXIMUM_START_TIME_IN_MINUTES)=>{
|
|
710
729
|
// start with default polling interval of 1 second
|
|
711
730
|
const [pollingInterval, setPollingInterval] = useState(convertSecondsToMilliseconds(1));
|
|
712
731
|
const [error, setError] = useState(null);
|
|
@@ -870,8 +889,8 @@ const workspaceShutdownAlertLimit = 30000; // 5 minutes: 5 * 60 * 1000 TODO Figu
|
|
|
870
889
|
activeStatus
|
|
871
890
|
]);
|
|
872
891
|
useEffect(()=>{
|
|
873
|
-
// time out if exceeding maximum start
|
|
874
|
-
if (requestedStatus === RequestedWorkspaceStatus.Launch && isTimeGreaterThan(requestedStatusTimestamp,
|
|
892
|
+
// time out if exceeding maximum start time
|
|
893
|
+
if (requestedStatus === RequestedWorkspaceStatus.Launch && isTimeGreaterThan(requestedStatusTimestamp, startTimeLimit)) {
|
|
875
894
|
if (workspaceId) terminateWorkspace(encodeURIComponent(workspaceId));
|
|
876
895
|
dispatch(setJEGRequestedWorkspaceStatus(RequestedWorkspaceStatus.Terminate));
|
|
877
896
|
setError('Workspace failed to start. Shutting down');
|
|
@@ -882,7 +901,8 @@ const workspaceShutdownAlertLimit = 30000; // 5 minutes: 5 * 60 * 1000 TODO Figu
|
|
|
882
901
|
dispatch,
|
|
883
902
|
workspaceStatusData,
|
|
884
903
|
workspaceId,
|
|
885
|
-
terminateWorkspace
|
|
904
|
+
terminateWorkspace,
|
|
905
|
+
startTimeLimit
|
|
886
906
|
]);
|
|
887
907
|
};
|
|
888
908
|
|
|
@@ -899,10 +919,10 @@ const useMicroContainerReduxContext = ()=>{
|
|
|
899
919
|
}
|
|
900
920
|
return context;
|
|
901
921
|
};
|
|
902
|
-
function MicroContainerReduxProvider({ children, identifierTag, enabled }) {
|
|
922
|
+
function MicroContainerReduxProvider({ children, identifierTag, enabled, startTimeLimit = undefined }) {
|
|
903
923
|
const tag = identifierTag ?? (process.env.NEXT_PUBLIC_MICRO_CONTAINER_TAG || '');
|
|
904
924
|
const value = useMicroContainerRedux(tag, enabled);
|
|
905
|
-
useJEGWorkspaceResourceMonitor(value.containerHash);
|
|
925
|
+
useJEGWorkspaceResourceMonitor(value.containerHash, true, startTimeLimit);
|
|
906
926
|
return /*#__PURE__*/ jsx(MicroContainerReduxProviderContext.Provider, {
|
|
907
927
|
value: value,
|
|
908
928
|
children: children
|
|
@@ -936,19 +956,21 @@ const MicroContainerReduxPanel = ({ compact = false })=>{
|
|
|
936
956
|
coreDispatch
|
|
937
957
|
]);
|
|
938
958
|
/* ── Elapsed timer for launching state ── */ const [elapsedSec, setElapsedSec] = useState(0);
|
|
959
|
+
const interval = useInterval(()=>{
|
|
960
|
+
const elapsed = Math.floor((Date.now() - requestedStatusTimestamp) / 1000);
|
|
961
|
+
setElapsedSec(elapsed);
|
|
962
|
+
}, 1000);
|
|
939
963
|
useEffect(()=>{
|
|
940
|
-
if (status
|
|
964
|
+
if (status === WorkspaceStatus.Launching) {
|
|
965
|
+
interval.start();
|
|
966
|
+
} else {
|
|
941
967
|
setElapsedSec(0);
|
|
942
|
-
|
|
968
|
+
interval.stop();
|
|
943
969
|
}
|
|
944
|
-
|
|
945
|
-
const t = setInterval(()=>{
|
|
946
|
-
setElapsedSec(interval);
|
|
947
|
-
}, 1000);
|
|
948
|
-
return ()=>clearInterval(t);
|
|
970
|
+
return interval.stop;
|
|
949
971
|
}, [
|
|
950
|
-
|
|
951
|
-
|
|
972
|
+
status,
|
|
973
|
+
interval
|
|
952
974
|
]);
|
|
953
975
|
/* ── Auto-reset after 15 s in launch-error state ── */ useEffect(()=>{
|
|
954
976
|
if (status !== WorkspaceStatus.LaunchError) return;
|
|
@@ -1002,9 +1024,6 @@ const MicroContainerReduxPanel = ({ compact = false })=>{
|
|
|
1002
1024
|
});
|
|
1003
1025
|
}
|
|
1004
1026
|
/* ── launching ── */ if (status === WorkspaceStatus.Launching) {
|
|
1005
|
-
const mins = Math.floor(elapsedSec / 60);
|
|
1006
|
-
const secs = elapsedSec % 60;
|
|
1007
|
-
const elapsed = mins > 0 ? `${mins}m ${secs}s` : `${secs}s`;
|
|
1008
1027
|
return /*#__PURE__*/ jsxs("div", {
|
|
1009
1028
|
role: "status",
|
|
1010
1029
|
className: PanelStyle$2,
|
|
@@ -1027,7 +1046,9 @@ const MicroContainerReduxPanel = ({ compact = false })=>{
|
|
|
1027
1046
|
className: "mt-1 text-sm text-base-darker",
|
|
1028
1047
|
children: [
|
|
1029
1048
|
"Elapsed: ",
|
|
1030
|
-
|
|
1049
|
+
elapsedSec,
|
|
1050
|
+
" ",
|
|
1051
|
+
elapsedSec === 1 ? 'second' : 'seconds'
|
|
1031
1052
|
]
|
|
1032
1053
|
}),
|
|
1033
1054
|
/*#__PURE__*/ jsx("p", {
|
|
@@ -1175,49 +1196,60 @@ const MicroContainerReduxPanel = ({ compact = false })=>{
|
|
|
1175
1196
|
}
|
|
1176
1197
|
};
|
|
1177
1198
|
}, []);
|
|
1178
|
-
// Handle setting up iframe event listeners
|
|
1199
|
+
// Handle setting up iframe event listeners for activity detection.
|
|
1200
|
+
// Tries same-origin contentDocument access first; falls back to
|
|
1201
|
+
// focus/blur heuristics when the iframe is cross-origin.
|
|
1179
1202
|
useEffect(()=>{
|
|
1180
1203
|
if (!jupyterReady) return;
|
|
1181
1204
|
const updateUserActivity = ()=>{
|
|
1182
|
-
const timestamp = Date.now();
|
|
1183
1205
|
if (broadcastChannelRef.current) {
|
|
1184
1206
|
broadcastChannelRef.current.postMessage({
|
|
1185
1207
|
type: 'activity-update',
|
|
1186
|
-
timestamp
|
|
1208
|
+
timestamp: Date.now()
|
|
1187
1209
|
});
|
|
1188
1210
|
}
|
|
1189
1211
|
};
|
|
1190
1212
|
const iframe = iframeRef.current;
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1213
|
+
let usedDirectListeners = false;
|
|
1214
|
+
if (iframe?.contentWindow) {
|
|
1215
|
+
try {
|
|
1216
|
+
const iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
|
|
1217
|
+
if (iframeDoc) {
|
|
1218
|
+
iframeDoc.addEventListener('mousedown', updateUserActivity);
|
|
1219
|
+
iframeDoc.addEventListener('keypress', updateUserActivity);
|
|
1220
|
+
iframeDoc.addEventListener('scroll', updateUserActivity);
|
|
1221
|
+
iframeDoc.addEventListener('touchstart', updateUserActivity);
|
|
1222
|
+
usedDirectListeners = true;
|
|
1223
|
+
}
|
|
1224
|
+
} catch {
|
|
1225
|
+
// cross-origin — fall through to focus/blur fallback
|
|
1202
1226
|
}
|
|
1203
|
-
}
|
|
1204
|
-
|
|
1227
|
+
}
|
|
1228
|
+
// Fallback: treat iframe receiving focus as user activity.
|
|
1229
|
+
// This fires when the user clicks into the iframe even if we
|
|
1230
|
+
// cannot attach listeners to its document directly.
|
|
1231
|
+
if (!usedDirectListeners) {
|
|
1232
|
+
window.addEventListener('blur', updateUserActivity);
|
|
1233
|
+
window.addEventListener('focus', updateUserActivity);
|
|
1205
1234
|
}
|
|
1206
1235
|
return ()=>{
|
|
1207
|
-
if (iframe?.contentWindow) {
|
|
1236
|
+
if (usedDirectListeners && iframe?.contentWindow) {
|
|
1208
1237
|
try {
|
|
1209
1238
|
const iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
|
|
1210
1239
|
if (iframeDoc) {
|
|
1211
1240
|
iframeDoc.removeEventListener('mousedown', updateUserActivity);
|
|
1212
1241
|
iframeDoc.removeEventListener('keypress', updateUserActivity);
|
|
1213
1242
|
iframeDoc.removeEventListener('scroll', updateUserActivity);
|
|
1214
|
-
iframeDoc.removeEventListener('click', updateUserActivity);
|
|
1215
1243
|
iframeDoc.removeEventListener('touchstart', updateUserActivity);
|
|
1216
1244
|
}
|
|
1217
1245
|
} catch {
|
|
1218
1246
|
// Ignore cleanup errors
|
|
1219
1247
|
}
|
|
1220
1248
|
}
|
|
1249
|
+
if (!usedDirectListeners) {
|
|
1250
|
+
window.removeEventListener('blur', updateUserActivity);
|
|
1251
|
+
window.removeEventListener('focus', updateUserActivity);
|
|
1252
|
+
}
|
|
1221
1253
|
};
|
|
1222
1254
|
}, [
|
|
1223
1255
|
jupyterReady
|
|
@@ -1413,7 +1445,8 @@ const MicroContainerReduxPanel = ({ compact = false })=>{
|
|
|
1413
1445
|
className: "min-h-0 flex-1 border-0",
|
|
1414
1446
|
onLoad: ()=>{
|
|
1415
1447
|
setLoading(false);
|
|
1416
|
-
|
|
1448
|
+
// jupyterReady is set by the polling effect once the
|
|
1449
|
+
// JupyterLite app reports status === 'ready'
|
|
1417
1450
|
},
|
|
1418
1451
|
onError: ()=>{
|
|
1419
1452
|
setLoadError(true);
|
|
@@ -2152,27 +2185,11 @@ const UpgradeActionsPanel = ({ currentTier, onUpgradeToRemote, onRequestQuotaInc
|
|
|
2152
2185
|
/*#__PURE__*/ jsxs("div", {
|
|
2153
2186
|
className: "space-y-2 p-4 pt-0",
|
|
2154
2187
|
children: [
|
|
2155
|
-
/*#__PURE__*/
|
|
2156
|
-
children:
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
children: [
|
|
2161
|
-
/*#__PURE__*/ jsx("p", {
|
|
2162
|
-
className: "text-md font-bold text-base-contrast-max",
|
|
2163
|
-
children: "Current Tier"
|
|
2164
|
-
}),
|
|
2165
|
-
/*#__PURE__*/ jsx(Badge, {
|
|
2166
|
-
color: "primary.3",
|
|
2167
|
-
children: currentTier
|
|
2168
|
-
})
|
|
2169
|
-
]
|
|
2170
|
-
}),
|
|
2171
|
-
/*#__PURE__*/ jsx("p", {
|
|
2172
|
-
className: "mt-1 text-sm text-base-contrast-max",
|
|
2173
|
-
children: currentTier === 'free' ? 'Upgrade to remote kernels for long-running jobs and larger memory footprints.' : 'Remote compute is active. You can request expanded quota for larger workloads.'
|
|
2174
|
-
})
|
|
2175
|
-
]
|
|
2188
|
+
/*#__PURE__*/ jsx(Stack, {
|
|
2189
|
+
children: /*#__PURE__*/ jsx("p", {
|
|
2190
|
+
className: "my-1 text-sm text-base-contrast-max",
|
|
2191
|
+
children: currentTier === 'free' ? 'Upgrade to remote kernels for long-running jobs and larger memory footprints.' : 'Remote compute is active. You can request expanded quota for larger workloads.'
|
|
2192
|
+
})
|
|
2176
2193
|
}),
|
|
2177
2194
|
currentTier === 'free' && /*#__PURE__*/ jsx(Button, {
|
|
2178
2195
|
onClick: onUpgradeToRemote,
|
|
@@ -2239,7 +2256,7 @@ function HorizontalAccordion({ label, expanded, setExpanded, children, expandedW
|
|
|
2239
2256
|
});
|
|
2240
2257
|
const mergedClassNames = mergeDefaultTailwindClassnames(STYLING_DEFAULTS, classNames);
|
|
2241
2258
|
return /*#__PURE__*/ jsx("div", {
|
|
2242
|
-
className:
|
|
2259
|
+
className: `${mergedClassNames.root}`,
|
|
2243
2260
|
style: {
|
|
2244
2261
|
width: expanded ? expandedWidth : collapsedWidth,
|
|
2245
2262
|
transition: `width ${TRANSITION_MS}ms ease`
|
|
@@ -2274,7 +2291,7 @@ function HorizontalAccordion({ label, expanded, setExpanded, children, expandedW
|
|
|
2274
2291
|
]
|
|
2275
2292
|
}),
|
|
2276
2293
|
/*#__PURE__*/ jsx("div", {
|
|
2277
|
-
className: "overflow-hidden
|
|
2294
|
+
className: "overflow-hidden px-2",
|
|
2278
2295
|
style: {
|
|
2279
2296
|
opacity: expanded ? 1 : 0,
|
|
2280
2297
|
pointerEvents: expanded ? 'auto' : 'none',
|
|
@@ -2302,9 +2319,8 @@ const SettingsPanel = ({ showKernels, expanded, setExpanded, width = 380 })=>{
|
|
|
2302
2319
|
setExpanded: setExpanded,
|
|
2303
2320
|
rightSide: true,
|
|
2304
2321
|
expandedWidth: width,
|
|
2305
|
-
children: /*#__PURE__*/ jsxs(
|
|
2306
|
-
|
|
2307
|
-
className: PanelStyle,
|
|
2322
|
+
children: /*#__PURE__*/ jsxs("div", {
|
|
2323
|
+
className: `${PanelStyle} overflow-y-scroll`,
|
|
2308
2324
|
children: [
|
|
2309
2325
|
showKernels && /*#__PURE__*/ jsx(KernelLifecyclePanel, {}),
|
|
2310
2326
|
/*#__PURE__*/ jsx(UpgradeActionsPanel, {
|
|
@@ -2320,7 +2336,7 @@ const WorkspaceToolbar = /*#__PURE__*/ forwardRef(({ toolbarConfiguration }, ref
|
|
|
2320
2336
|
const workspaceTier = useCoreSelector((state)=>selectWorkspaceTier(state));
|
|
2321
2337
|
const workspaceStatus = useCoreSelector((state)=>selectJEGActiveWorkspaceStatus(state));
|
|
2322
2338
|
const isFullscreen = useCoreSelector((state)=>selectWorkspaceFullscreen(state));
|
|
2323
|
-
const { showStatus, showStop } = toolbarConfiguration || {};
|
|
2339
|
+
const { showStatus, showStop, tierLabel } = toolbarConfiguration || {};
|
|
2324
2340
|
const coreDispatch = useCoreDispatch();
|
|
2325
2341
|
const toggleFullscreen = useCallback(()=>{
|
|
2326
2342
|
coreDispatch(setWorkspaceFullscreen(!isFullscreen));
|
|
@@ -2373,8 +2389,8 @@ const WorkspaceToolbar = /*#__PURE__*/ forwardRef(({ toolbarConfiguration }, ref
|
|
|
2373
2389
|
children: toolbarConfiguration?.description
|
|
2374
2390
|
}),
|
|
2375
2391
|
/*#__PURE__*/ jsx(Badge, {
|
|
2376
|
-
"aria-label": `Workspace tier: ${workspaceTier}`,
|
|
2377
|
-
children: workspaceTier
|
|
2392
|
+
"aria-label": `Workspace tier: ${tierLabel ?? workspaceTier}`,
|
|
2393
|
+
children: tierLabel ?? workspaceTier
|
|
2378
2394
|
})
|
|
2379
2395
|
]
|
|
2380
2396
|
})
|
|
@@ -3973,7 +3989,7 @@ const WorkspaceCenter = ({ workspaces, landingPage, tierConfiguration })=>{
|
|
|
3973
3989
|
]);
|
|
3974
3990
|
if (!workspaceTier) {
|
|
3975
3991
|
return /*#__PURE__*/ jsx("div", {
|
|
3976
|
-
className: "w-full bg-base-lightest ",
|
|
3992
|
+
className: "w-full h-full overflow-hidden bg-base-lightest ",
|
|
3977
3993
|
children: /*#__PURE__*/ jsx(TierSelectorLanding, {
|
|
3978
3994
|
cards: workspaces,
|
|
3979
3995
|
onSelectTier: handleSelectTier,
|
|
@@ -3991,9 +4007,11 @@ const WorkspaceCenter = ({ workspaces, landingPage, tierConfiguration })=>{
|
|
|
3991
4007
|
});
|
|
3992
4008
|
}
|
|
3993
4009
|
if (workspaceTier === 'remote') {
|
|
4010
|
+
const config = tierConfiguration.remote;
|
|
3994
4011
|
return /*#__PURE__*/ jsx(ProtectedContent, {
|
|
3995
4012
|
children: /*#__PURE__*/ jsx(MicroContainerReduxProvider, {
|
|
3996
4013
|
enabled: true,
|
|
4014
|
+
startTimeLimit: config?.startTimeLimit,
|
|
3997
4015
|
children: /*#__PURE__*/ jsx(WorkspaceLayout, {
|
|
3998
4016
|
tierConfiguration: tierConfiguration.remote,
|
|
3999
4017
|
children: /*#__PURE__*/ jsx(RemoteComputeWorkspace, {
|