@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.
Files changed (59) hide show
  1. package/dist/dts/components/HorizontalAccordian.js +2 -2
  2. package/dist/dts/components/HorizontalAccordian.js.map +1 -1
  3. package/dist/dts/components/MicroContainerReduxPanel.d.ts.map +1 -1
  4. package/dist/dts/components/MicroContainerReduxPanel.js +13 -12
  5. package/dist/dts/components/MicroContainerReduxPanel.js.map +1 -1
  6. package/dist/dts/components/TierSelectorLanding.d.ts.map +1 -1
  7. package/dist/dts/components/TierSelectorLanding.js +5 -4
  8. package/dist/dts/components/TierSelectorLanding.js.map +1 -1
  9. package/dist/dts/components/UpgradeActionsPanel.d.ts.map +1 -1
  10. package/dist/dts/components/UpgradeActionsPanel.js +4 -4
  11. package/dist/dts/components/UpgradeActionsPanel.js.map +1 -1
  12. package/dist/dts/components/WorkspaceTierCard.d.ts +1 -1
  13. package/dist/dts/components/WorkspaceTierCard.d.ts.map +1 -1
  14. package/dist/dts/components/WorkspaceTierCard.js +2 -2
  15. package/dist/dts/components/WorkspaceTierCard.js.map +1 -1
  16. package/dist/dts/components/WorkspaceTierCard.stories.d.ts +1 -1
  17. package/dist/dts/components/types.d.ts +1 -0
  18. package/dist/dts/components/types.d.ts.map +1 -1
  19. package/dist/dts/core/jegGatewayApi.d.ts +8 -8
  20. package/dist/dts/providers/MicroContainerReduxProvider.d.ts +2 -1
  21. package/dist/dts/providers/MicroContainerReduxProvider.d.ts.map +1 -1
  22. package/dist/dts/providers/MicroContainerReduxProvider.js +2 -2
  23. package/dist/dts/providers/MicroContainerReduxProvider.js.map +1 -1
  24. package/dist/dts/providers/useJEGWorkspaceResourceMonitor.d.ts +1 -1
  25. package/dist/dts/providers/useJEGWorkspaceResourceMonitor.d.ts.map +1 -1
  26. package/dist/dts/providers/useJEGWorkspaceResourceMonitor.js +5 -3
  27. package/dist/dts/providers/useJEGWorkspaceResourceMonitor.js.map +1 -1
  28. package/dist/dts/server/workspaceAssetsHandler.d.ts.map +1 -1
  29. package/dist/dts/server/workspaceAssetsHandler.js +3 -0
  30. package/dist/dts/server/workspaceAssetsHandler.js.map +1 -1
  31. package/dist/dts/types.d.ts +1 -1
  32. package/dist/dts/types.d.ts.map +1 -1
  33. package/dist/dts/workspace/WorkspaceCenter.d.ts.map +1 -1
  34. package/dist/dts/workspace/WorkspaceCenter.js +3 -2
  35. package/dist/dts/workspace/WorkspaceCenter.js.map +1 -1
  36. package/dist/dts/workspace/WorkspaceLayout/SettingsPanel.d.ts.map +1 -1
  37. package/dist/dts/workspace/WorkspaceLayout/SettingsPanel.js +1 -2
  38. package/dist/dts/workspace/WorkspaceLayout/SettingsPanel.js.map +1 -1
  39. package/dist/dts/workspace/WorkspaceLayout/WorkspaceToolbar.d.ts.map +1 -1
  40. package/dist/dts/workspace/WorkspaceLayout/WorkspaceToolbar.js +2 -2
  41. package/dist/dts/workspace/WorkspaceLayout/WorkspaceToolbar.js.map +1 -1
  42. package/dist/dts/workspace/tiers/FreeWorkspace.d.ts.map +1 -1
  43. package/dist/dts/workspace/tiers/FreeWorkspace.js +29 -18
  44. package/dist/dts/workspace/tiers/FreeWorkspace.js.map +1 -1
  45. package/dist/dts/workspace/tiers/RemoteComputeWorkspace.d.ts.map +1 -1
  46. package/dist/dts/workspace/tiers/RemoteComputeWorkspace.js +31 -19
  47. package/dist/dts/workspace/tiers/RemoteComputeWorkspace.js.map +1 -1
  48. package/dist/dts/workspace/tiers/types.d.ts +2 -0
  49. package/dist/dts/workspace/tiers/types.d.ts.map +1 -1
  50. package/dist/index/cjs/index.js +132 -114
  51. package/dist/index/cjs/index.js.map +1 -1
  52. package/dist/index/esm/index.js +133 -115
  53. package/dist/index/esm/index.js.map +1 -1
  54. package/dist/index.d.ts +4 -1
  55. package/dist/server/cjs/server.js +4 -1
  56. package/dist/server/cjs/server.js.map +1 -1
  57. package/dist/server/esm/server.js +4 -1
  58. package/dist/server/esm/server.js.map +1 -1
  59. package/package.json +10 -10
@@ -5,16 +5,16 @@ var React = require('react');
5
5
  var frontend = require('@gen3/frontend');
6
6
  var core = require('@mantine/core');
7
7
  var react = require('@iconify-icon/react');
8
+ var hooks = require('@mantine/hooks');
8
9
  var core$1 = require('@gen3/core');
9
10
  var useDeepCompare = require('use-deep-compare');
10
11
  var notifications = require('@mantine/notifications');
11
- var hooks = require('@mantine/hooks');
12
12
  var io5 = require('react-icons/io5');
13
13
  var bs = require('react-icons/bs');
14
14
  var md = require('react-icons/md');
15
15
  var lodash = require('lodash');
16
16
 
17
- const WorkspaceTierCard = ({ label, description, tier, features = [], tooltip = 'Launch workspace', buttonLabel = 'Launch', onSelectTier, baseColor })=>{
17
+ const WorkspaceTierCard = ({ label, description, tier, tierLabel = undefined, features = [], tooltip = 'Launch workspace', buttonLabel = 'Launch', onSelectTier, baseColor })=>{
18
18
  const handleClick = React.useCallback(()=>onSelectTier(tier), [
19
19
  onSelectTier,
20
20
  tier
@@ -49,7 +49,7 @@ const WorkspaceTierCard = ({ label, description, tier, features = [], tooltip =
49
49
  color: "primary.4",
50
50
  size: "lg",
51
51
  fw: 500,
52
- children: tier !== 'free' ? 'paid' : 'free'
52
+ children: tierLabel ?? (tier === 'remote' ? 'paid' : 'free')
53
53
  })
54
54
  })
55
55
  }),
@@ -115,10 +115,10 @@ const WorkspaceTierCard = ({ label, description, tier, features = [], tooltip =
115
115
  };
116
116
 
117
117
  const DEFAULT_CLASSNAMES = {
118
- root: 'mx-auto w-full max-w-7xl p-6 md:p-10',
119
- background: 'relative overflow-hidden md:p-12',
118
+ root: 'mx-auto w-full h-full overflow-auto max-w-7xl p-6 sm:p-2 md:p-4 lg:p-8',
119
+ background: 'relative overflow-hidden sm:p-2 md:p-4 lg:p-8',
120
120
  label: 'text-md uppercase tracking-[0.28em] text-primary',
121
- description: 'text-4xl font-black leading-tight text-primary md:text-3xl sm:text-2xl mt-4 mb-4',
121
+ description: 'font-black leading-tight text-primary md:text-3xl sm:text-2xl xs:text-lg mt-4 mb-4',
122
122
  additionalDescription: 'max-w-2xl text-md font-medium text-base-contrast',
123
123
  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'
124
124
  };
@@ -129,36 +129,43 @@ const TierSelectorLanding = ({ cards, onSelectTier, classNames, label = 'Gen3 Ju
129
129
  const mergedClassnames = React.useMemo(()=>frontend.mergeDefaultTailwindClassnames(DEFAULT_CLASSNAMES, classNames ?? {}), [
130
130
  classNames
131
131
  ]);
132
- return /*#__PURE__*/ jsxRuntime.jsx("div", {
132
+ return /*#__PURE__*/ jsxRuntime.jsx(core.Container, {
133
+ size: "xl",
133
134
  className: mergedClassnames.root,
134
- children: /*#__PURE__*/ jsxRuntime.jsxs("div", {
135
+ children: /*#__PURE__*/ jsxRuntime.jsxs(core.Stack, {
136
+ gap: "lg",
135
137
  className: mergedClassnames.background,
136
138
  children: [
137
- /*#__PURE__*/ jsxRuntime.jsx("div", {
138
- className: "mb-10 flex flex-col gap-6 md:flex-row md:items-end md:justify-between",
139
- children: /*#__PURE__*/ jsxRuntime.jsxs("div", {
140
- children: [
141
- /*#__PURE__*/ jsxRuntime.jsx("p", {
142
- className: "text-md uppercase tracking-[0.28em] text-primary",
143
- children: label
144
- }),
145
- /*#__PURE__*/ jsxRuntime.jsx("p", {
146
- className: mergedClassnames.description,
147
- children: description
148
- }),
149
- additionalDescriptions.map((desc)=>/*#__PURE__*/ jsxRuntime.jsx("p", {
150
- className: mergedClassnames.additionalDescription,
151
- children: desc
152
- }, desc.slice(0, 20)))
153
- ]
154
- })
139
+ /*#__PURE__*/ jsxRuntime.jsxs(core.Stack, {
140
+ gap: "xs",
141
+ children: [
142
+ /*#__PURE__*/ jsxRuntime.jsx(core.Text, {
143
+ className: mergedClassnames.label,
144
+ children: label
145
+ }),
146
+ /*#__PURE__*/ jsxRuntime.jsx(core.Text, {
147
+ className: mergedClassnames.description,
148
+ children: description
149
+ }),
150
+ additionalDescriptions.map((desc)=>/*#__PURE__*/ jsxRuntime.jsx(core.Text, {
151
+ className: mergedClassnames.additionalDescription,
152
+ children: desc
153
+ }, desc.slice(0, 20)))
154
+ ]
155
155
  }),
156
- /*#__PURE__*/ jsxRuntime.jsx("div", {
157
- className: "grid gap-5 lg:grid-cols-3 md:grid-cols-2 sm:grid-cols-1",
156
+ /*#__PURE__*/ jsxRuntime.jsx(core.SimpleGrid, {
157
+ cols: {
158
+ base: 2,
159
+ sm: 2,
160
+ md: 2,
161
+ lg: 3
162
+ },
163
+ spacing: "md",
158
164
  role: "list",
159
165
  "aria-label": "Workspace tier selection",
160
166
  children: Object.values(cards).map((card)=>/*#__PURE__*/ jsxRuntime.jsx(WorkspaceTierCard, {
161
167
  tier: card.tier,
168
+ tierLabel: card.tierLabel,
162
169
  onSelectTier: onSelectTier,
163
170
  label: card.label,
164
171
  description: card.description,
@@ -198,49 +205,60 @@ const FreeWorkspace = ({ assetBaseUrl = '/api/workspace-assets/free', onReady, o
198
205
  }
199
206
  };
200
207
  }, []);
201
- // Handle setting up iframe event listeners
208
+ // Handle setting up iframe event listeners for activity detection.
209
+ // Tries same-origin contentDocument access first; falls back to
210
+ // focus/blur heuristics when the iframe is cross-origin.
202
211
  React.useEffect(()=>{
203
212
  if (!jupyterReady) return;
204
213
  const updateUserActivity = ()=>{
205
- const timestamp = Date.now();
206
214
  if (broadcastChannelRef.current) {
207
215
  broadcastChannelRef.current.postMessage({
208
216
  type: 'activity-update',
209
- timestamp
217
+ timestamp: Date.now()
210
218
  });
211
219
  }
212
220
  };
213
221
  const iframe = iframeRef.current;
214
- if (!iframe?.contentWindow) {
215
- return;
216
- }
217
- try {
218
- const iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
219
- if (iframeDoc) {
220
- iframeDoc.addEventListener('mousedown', updateUserActivity);
221
- iframeDoc.addEventListener('keypress', updateUserActivity);
222
- iframeDoc.addEventListener('scroll', updateUserActivity);
223
- iframeDoc.addEventListener('click', updateUserActivity);
224
- iframeDoc.addEventListener('touchstart', updateUserActivity);
222
+ let usedDirectListeners = false;
223
+ if (iframe?.contentWindow) {
224
+ try {
225
+ const iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
226
+ if (iframeDoc) {
227
+ iframeDoc.addEventListener('mousedown', updateUserActivity);
228
+ iframeDoc.addEventListener('keypress', updateUserActivity);
229
+ iframeDoc.addEventListener('scroll', updateUserActivity);
230
+ iframeDoc.addEventListener('touchstart', updateUserActivity);
231
+ usedDirectListeners = true;
232
+ }
233
+ } catch {
234
+ // cross-origin — fall through to focus/blur fallback
225
235
  }
226
- } catch (error) {
227
- console.warn('Cannot access iframe content - cross-origin restriction', error);
236
+ }
237
+ // Fallback: treat iframe receiving focus as user activity.
238
+ // This fires when the user clicks into the iframe even if we
239
+ // cannot attach listeners to its document directly.
240
+ if (!usedDirectListeners) {
241
+ window.addEventListener('blur', updateUserActivity);
242
+ window.addEventListener('focus', updateUserActivity);
228
243
  }
229
244
  return ()=>{
230
- if (iframe?.contentWindow) {
245
+ if (usedDirectListeners && iframe?.contentWindow) {
231
246
  try {
232
247
  const iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
233
248
  if (iframeDoc) {
234
249
  iframeDoc.removeEventListener('mousedown', updateUserActivity);
235
250
  iframeDoc.removeEventListener('keypress', updateUserActivity);
236
251
  iframeDoc.removeEventListener('scroll', updateUserActivity);
237
- iframeDoc.removeEventListener('click', updateUserActivity);
238
252
  iframeDoc.removeEventListener('touchstart', updateUserActivity);
239
253
  }
240
254
  } catch {
241
255
  // Ignore cleanup errors
242
256
  }
243
257
  }
258
+ if (!usedDirectListeners) {
259
+ window.removeEventListener('blur', updateUserActivity);
260
+ window.removeEventListener('focus', updateUserActivity);
261
+ }
244
262
  };
245
263
  }, [
246
264
  jupyterReady
@@ -685,6 +703,7 @@ const notifyUser = (title, message, status = 0)=>{
685
703
  [1]: 'utility.2',
686
704
  [2]: 'utility.4'
687
705
  };
706
+ notifications.notifications.clean();
688
707
  notifications.notifications.show({
689
708
  title,
690
709
  message,
@@ -708,7 +727,7 @@ const workspaceShutdownAlertLimit = 30000; // 5 minutes: 5 * 60 * 1000 TODO Figu
708
727
  /**
709
728
  * Monitors resource usage.
710
729
  * Handles state changes and polls for workspace status using the JEG APIs
711
- */ const useJEGWorkspaceResourceMonitor = (workspaceId = null, monitorWorkspace)=>{
730
+ */ const useJEGWorkspaceResourceMonitor = (workspaceId = null, monitorWorkspace, startTimeLimit = MAXIMUM_START_TIME_IN_MINUTES)=>{
712
731
  // start with default polling interval of 1 second
713
732
  const [pollingInterval, setPollingInterval] = React.useState(frontend.convertSecondsToMilliseconds(1));
714
733
  const [error, setError] = React.useState(null);
@@ -872,8 +891,8 @@ const workspaceShutdownAlertLimit = 30000; // 5 minutes: 5 * 60 * 1000 TODO Figu
872
891
  activeStatus
873
892
  ]);
874
893
  React.useEffect(()=>{
875
- // time out if exceeding maximum start timee
876
- if (requestedStatus === core$1.RequestedWorkspaceStatus.Launch && core$1.isTimeGreaterThan(requestedStatusTimestamp, MAXIMUM_START_TIME_IN_MINUTES)) {
894
+ // time out if exceeding maximum start time
895
+ if (requestedStatus === core$1.RequestedWorkspaceStatus.Launch && core$1.isTimeGreaterThan(requestedStatusTimestamp, startTimeLimit)) {
877
896
  if (workspaceId) terminateWorkspace(encodeURIComponent(workspaceId));
878
897
  dispatch(core$1.setJEGRequestedWorkspaceStatus(core$1.RequestedWorkspaceStatus.Terminate));
879
898
  setError('Workspace failed to start. Shutting down');
@@ -884,7 +903,8 @@ const workspaceShutdownAlertLimit = 30000; // 5 minutes: 5 * 60 * 1000 TODO Figu
884
903
  dispatch,
885
904
  workspaceStatusData,
886
905
  workspaceId,
887
- terminateWorkspace
906
+ terminateWorkspace,
907
+ startTimeLimit
888
908
  ]);
889
909
  };
890
910
 
@@ -901,10 +921,10 @@ const useMicroContainerReduxContext = ()=>{
901
921
  }
902
922
  return context;
903
923
  };
904
- function MicroContainerReduxProvider({ children, identifierTag, enabled }) {
924
+ function MicroContainerReduxProvider({ children, identifierTag, enabled, startTimeLimit = undefined }) {
905
925
  const tag = identifierTag ?? (process.env.NEXT_PUBLIC_MICRO_CONTAINER_TAG || '');
906
926
  const value = useMicroContainerRedux(tag, enabled);
907
- useJEGWorkspaceResourceMonitor(value.containerHash);
927
+ useJEGWorkspaceResourceMonitor(value.containerHash, true, startTimeLimit);
908
928
  return /*#__PURE__*/ jsxRuntime.jsx(MicroContainerReduxProviderContext.Provider, {
909
929
  value: value,
910
930
  children: children
@@ -938,19 +958,21 @@ const MicroContainerReduxPanel = ({ compact = false })=>{
938
958
  coreDispatch
939
959
  ]);
940
960
  /* ── Elapsed timer for launching state ── */ const [elapsedSec, setElapsedSec] = React.useState(0);
961
+ const interval = hooks.useInterval(()=>{
962
+ const elapsed = Math.floor((Date.now() - requestedStatusTimestamp) / 1000);
963
+ setElapsedSec(elapsed);
964
+ }, 1000);
941
965
  React.useEffect(()=>{
942
- if (status !== core$1.WorkspaceStatus.Launching) {
966
+ if (status === core$1.WorkspaceStatus.Launching) {
967
+ interval.start();
968
+ } else {
943
969
  setElapsedSec(0);
944
- return;
970
+ interval.stop();
945
971
  }
946
- const interval = Math.floor((Date.now() - requestedStatusTimestamp) / 1000);
947
- const t = setInterval(()=>{
948
- setElapsedSec(interval);
949
- }, 1000);
950
- return ()=>clearInterval(t);
972
+ return interval.stop;
951
973
  }, [
952
- requestedStatusTimestamp,
953
- status
974
+ status,
975
+ interval
954
976
  ]);
955
977
  /* ── Auto-reset after 15 s in launch-error state ── */ React.useEffect(()=>{
956
978
  if (status !== core$1.WorkspaceStatus.LaunchError) return;
@@ -1004,9 +1026,6 @@ const MicroContainerReduxPanel = ({ compact = false })=>{
1004
1026
  });
1005
1027
  }
1006
1028
  /* ── launching ── */ if (status === core$1.WorkspaceStatus.Launching) {
1007
- const mins = Math.floor(elapsedSec / 60);
1008
- const secs = elapsedSec % 60;
1009
- const elapsed = mins > 0 ? `${mins}m ${secs}s` : `${secs}s`;
1010
1029
  return /*#__PURE__*/ jsxRuntime.jsxs("div", {
1011
1030
  role: "status",
1012
1031
  className: PanelStyle$2,
@@ -1029,7 +1048,9 @@ const MicroContainerReduxPanel = ({ compact = false })=>{
1029
1048
  className: "mt-1 text-sm text-base-darker",
1030
1049
  children: [
1031
1050
  "Elapsed: ",
1032
- elapsed
1051
+ elapsedSec,
1052
+ " ",
1053
+ elapsedSec === 1 ? 'second' : 'seconds'
1033
1054
  ]
1034
1055
  }),
1035
1056
  /*#__PURE__*/ jsxRuntime.jsx("p", {
@@ -1177,49 +1198,60 @@ const MicroContainerReduxPanel = ({ compact = false })=>{
1177
1198
  }
1178
1199
  };
1179
1200
  }, []);
1180
- // Handle setting up iframe event listeners
1201
+ // Handle setting up iframe event listeners for activity detection.
1202
+ // Tries same-origin contentDocument access first; falls back to
1203
+ // focus/blur heuristics when the iframe is cross-origin.
1181
1204
  React.useEffect(()=>{
1182
1205
  if (!jupyterReady) return;
1183
1206
  const updateUserActivity = ()=>{
1184
- const timestamp = Date.now();
1185
1207
  if (broadcastChannelRef.current) {
1186
1208
  broadcastChannelRef.current.postMessage({
1187
1209
  type: 'activity-update',
1188
- timestamp
1210
+ timestamp: Date.now()
1189
1211
  });
1190
1212
  }
1191
1213
  };
1192
1214
  const iframe = iframeRef.current;
1193
- if (!iframe?.contentWindow) {
1194
- return;
1195
- }
1196
- try {
1197
- const iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
1198
- if (iframeDoc) {
1199
- iframeDoc.addEventListener('mousedown', updateUserActivity);
1200
- iframeDoc.addEventListener('keypress', updateUserActivity);
1201
- iframeDoc.addEventListener('scroll', updateUserActivity);
1202
- iframeDoc.addEventListener('click', updateUserActivity);
1203
- iframeDoc.addEventListener('touchstart', updateUserActivity);
1215
+ let usedDirectListeners = false;
1216
+ if (iframe?.contentWindow) {
1217
+ try {
1218
+ const iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
1219
+ if (iframeDoc) {
1220
+ iframeDoc.addEventListener('mousedown', updateUserActivity);
1221
+ iframeDoc.addEventListener('keypress', updateUserActivity);
1222
+ iframeDoc.addEventListener('scroll', updateUserActivity);
1223
+ iframeDoc.addEventListener('touchstart', updateUserActivity);
1224
+ usedDirectListeners = true;
1225
+ }
1226
+ } catch {
1227
+ // cross-origin — fall through to focus/blur fallback
1204
1228
  }
1205
- } catch (error) {
1206
- console.warn('Cannot access iframe content - cross-origin restriction', error);
1229
+ }
1230
+ // Fallback: treat iframe receiving focus as user activity.
1231
+ // This fires when the user clicks into the iframe even if we
1232
+ // cannot attach listeners to its document directly.
1233
+ if (!usedDirectListeners) {
1234
+ window.addEventListener('blur', updateUserActivity);
1235
+ window.addEventListener('focus', updateUserActivity);
1207
1236
  }
1208
1237
  return ()=>{
1209
- if (iframe?.contentWindow) {
1238
+ if (usedDirectListeners && iframe?.contentWindow) {
1210
1239
  try {
1211
1240
  const iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
1212
1241
  if (iframeDoc) {
1213
1242
  iframeDoc.removeEventListener('mousedown', updateUserActivity);
1214
1243
  iframeDoc.removeEventListener('keypress', updateUserActivity);
1215
1244
  iframeDoc.removeEventListener('scroll', updateUserActivity);
1216
- iframeDoc.removeEventListener('click', updateUserActivity);
1217
1245
  iframeDoc.removeEventListener('touchstart', updateUserActivity);
1218
1246
  }
1219
1247
  } catch {
1220
1248
  // Ignore cleanup errors
1221
1249
  }
1222
1250
  }
1251
+ if (!usedDirectListeners) {
1252
+ window.removeEventListener('blur', updateUserActivity);
1253
+ window.removeEventListener('focus', updateUserActivity);
1254
+ }
1223
1255
  };
1224
1256
  }, [
1225
1257
  jupyterReady
@@ -1415,7 +1447,8 @@ const MicroContainerReduxPanel = ({ compact = false })=>{
1415
1447
  className: "min-h-0 flex-1 border-0",
1416
1448
  onLoad: ()=>{
1417
1449
  setLoading(false);
1418
- setJupyterReady(true);
1450
+ // jupyterReady is set by the polling effect once the
1451
+ // JupyterLite app reports status === 'ready'
1419
1452
  },
1420
1453
  onError: ()=>{
1421
1454
  setLoadError(true);
@@ -2154,27 +2187,11 @@ const UpgradeActionsPanel = ({ currentTier, onUpgradeToRemote, onRequestQuotaInc
2154
2187
  /*#__PURE__*/ jsxRuntime.jsxs("div", {
2155
2188
  className: "space-y-2 p-4 pt-0",
2156
2189
  children: [
2157
- /*#__PURE__*/ jsxRuntime.jsxs(core.Stack, {
2158
- children: [
2159
- /*#__PURE__*/ jsxRuntime.jsxs(core.Group, {
2160
- justify: "space-between",
2161
- wrap: "nowrap",
2162
- children: [
2163
- /*#__PURE__*/ jsxRuntime.jsx("p", {
2164
- className: "text-md font-bold text-base-contrast-max",
2165
- children: "Current Tier"
2166
- }),
2167
- /*#__PURE__*/ jsxRuntime.jsx(core.Badge, {
2168
- color: "primary.3",
2169
- children: currentTier
2170
- })
2171
- ]
2172
- }),
2173
- /*#__PURE__*/ jsxRuntime.jsx("p", {
2174
- className: "mt-1 text-sm text-base-contrast-max",
2175
- 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.'
2176
- })
2177
- ]
2190
+ /*#__PURE__*/ jsxRuntime.jsx(core.Stack, {
2191
+ children: /*#__PURE__*/ jsxRuntime.jsx("p", {
2192
+ className: "my-1 text-sm text-base-contrast-max",
2193
+ 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.'
2194
+ })
2178
2195
  }),
2179
2196
  currentTier === 'free' && /*#__PURE__*/ jsxRuntime.jsx(core.Button, {
2180
2197
  onClick: onUpgradeToRemote,
@@ -2241,7 +2258,7 @@ function HorizontalAccordion({ label, expanded, setExpanded, children, expandedW
2241
2258
  });
2242
2259
  const mergedClassNames = frontend.mergeDefaultTailwindClassnames(STYLING_DEFAULTS, classNames);
2243
2260
  return /*#__PURE__*/ jsxRuntime.jsx("div", {
2244
- className: `overflow-hidden shrink-0 ${mergedClassNames.root}`,
2261
+ className: `${mergedClassNames.root}`,
2245
2262
  style: {
2246
2263
  width: expanded ? expandedWidth : collapsedWidth,
2247
2264
  transition: `width ${TRANSITION_MS}ms ease`
@@ -2276,7 +2293,7 @@ function HorizontalAccordion({ label, expanded, setExpanded, children, expandedW
2276
2293
  ]
2277
2294
  }),
2278
2295
  /*#__PURE__*/ jsxRuntime.jsx("div", {
2279
- className: "overflow-hidden h-full shrink-0 px-2",
2296
+ className: "overflow-hidden px-2",
2280
2297
  style: {
2281
2298
  opacity: expanded ? 1 : 0,
2282
2299
  pointerEvents: expanded ? 'auto' : 'none',
@@ -2304,9 +2321,8 @@ const SettingsPanel = ({ showKernels, expanded, setExpanded, width = 380 })=>{
2304
2321
  setExpanded: setExpanded,
2305
2322
  rightSide: true,
2306
2323
  expandedWidth: width,
2307
- children: /*#__PURE__*/ jsxRuntime.jsxs(core.Stack, {
2308
- gap: "sm",
2309
- className: PanelStyle,
2324
+ children: /*#__PURE__*/ jsxRuntime.jsxs("div", {
2325
+ className: `${PanelStyle} overflow-y-scroll`,
2310
2326
  children: [
2311
2327
  showKernels && /*#__PURE__*/ jsxRuntime.jsx(KernelLifecyclePanel, {}),
2312
2328
  /*#__PURE__*/ jsxRuntime.jsx(UpgradeActionsPanel, {
@@ -2322,7 +2338,7 @@ const WorkspaceToolbar = /*#__PURE__*/ React.forwardRef(({ toolbarConfiguration
2322
2338
  const workspaceTier = core$1.useCoreSelector((state)=>core$1.selectWorkspaceTier(state));
2323
2339
  const workspaceStatus = core$1.useCoreSelector((state)=>core$1.selectJEGActiveWorkspaceStatus(state));
2324
2340
  const isFullscreen = core$1.useCoreSelector((state)=>core$1.selectWorkspaceFullscreen(state));
2325
- const { showStatus, showStop } = toolbarConfiguration || {};
2341
+ const { showStatus, showStop, tierLabel } = toolbarConfiguration || {};
2326
2342
  const coreDispatch = core$1.useCoreDispatch();
2327
2343
  const toggleFullscreen = React.useCallback(()=>{
2328
2344
  coreDispatch(core$1.setWorkspaceFullscreen(!isFullscreen));
@@ -2375,8 +2391,8 @@ const WorkspaceToolbar = /*#__PURE__*/ React.forwardRef(({ toolbarConfiguration
2375
2391
  children: toolbarConfiguration?.description
2376
2392
  }),
2377
2393
  /*#__PURE__*/ jsxRuntime.jsx(core.Badge, {
2378
- "aria-label": `Workspace tier: ${workspaceTier}`,
2379
- children: workspaceTier
2394
+ "aria-label": `Workspace tier: ${tierLabel ?? workspaceTier}`,
2395
+ children: tierLabel ?? workspaceTier
2380
2396
  })
2381
2397
  ]
2382
2398
  })
@@ -3975,7 +3991,7 @@ const WorkspaceCenter = ({ workspaces, landingPage, tierConfiguration })=>{
3975
3991
  ]);
3976
3992
  if (!workspaceTier) {
3977
3993
  return /*#__PURE__*/ jsxRuntime.jsx("div", {
3978
- className: "w-full bg-base-lightest ",
3994
+ className: "w-full h-full overflow-hidden bg-base-lightest ",
3979
3995
  children: /*#__PURE__*/ jsxRuntime.jsx(TierSelectorLanding, {
3980
3996
  cards: workspaces,
3981
3997
  onSelectTier: handleSelectTier,
@@ -3993,9 +4009,11 @@ const WorkspaceCenter = ({ workspaces, landingPage, tierConfiguration })=>{
3993
4009
  });
3994
4010
  }
3995
4011
  if (workspaceTier === 'remote') {
4012
+ const config = tierConfiguration.remote;
3996
4013
  return /*#__PURE__*/ jsxRuntime.jsx(frontend.ProtectedContent, {
3997
4014
  children: /*#__PURE__*/ jsxRuntime.jsx(MicroContainerReduxProvider, {
3998
4015
  enabled: true,
4016
+ startTimeLimit: config?.startTimeLimit,
3999
4017
  children: /*#__PURE__*/ jsxRuntime.jsx(WorkspaceLayout, {
4000
4018
  tierConfiguration: tierConfiguration.remote,
4001
4019
  children: /*#__PURE__*/ jsxRuntime.jsx(RemoteComputeWorkspace, {