mito-ai 0.1.44__py3-none-any.whl → 0.1.45__py3-none-any.whl

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.

Potentially problematic release.


This version of mito-ai might be problematic. Click here for more details.

Files changed (52) hide show
  1. mito_ai/_version.py +1 -1
  2. mito_ai/anthropic_client.py +2 -3
  3. mito_ai/app_deploy/app_deploy_utils.py +25 -0
  4. mito_ai/app_deploy/handlers.py +9 -12
  5. mito_ai/app_deploy/models.py +4 -1
  6. mito_ai/completions/handlers.py +27 -1
  7. mito_ai/completions/models.py +1 -0
  8. mito_ai/completions/prompt_builders/prompt_constants.py +22 -4
  9. mito_ai/streamlit_conversion/streamlit_agent_handler.py +5 -2
  10. mito_ai/streamlit_conversion/streamlit_utils.py +5 -7
  11. mito_ai/streamlit_conversion/validate_streamlit_app.py +34 -25
  12. mito_ai/streamlit_preview/handlers.py +3 -0
  13. mito_ai/tests/deploy_app/test_app_deploy_utils.py +71 -0
  14. mito_ai/tests/providers/test_anthropic_client.py +2 -2
  15. mito_ai/tests/streamlit_conversion/test_streamlit_agent_handler.py +0 -84
  16. mito_ai/tests/streamlit_conversion/test_validate_streamlit_app.py +0 -15
  17. mito_ai/tests/utils/test_anthropic_utils.py +4 -4
  18. mito_ai/utils/anthropic_utils.py +11 -19
  19. {mito_ai-0.1.44.data → mito_ai-0.1.45.data}/data/share/jupyter/labextensions/mito_ai/build_log.json +100 -100
  20. {mito_ai-0.1.44.data → mito_ai-0.1.45.data}/data/share/jupyter/labextensions/mito_ai/package.json +2 -2
  21. {mito_ai-0.1.44.data → mito_ai-0.1.45.data}/data/share/jupyter/labextensions/mito_ai/schemas/mito_ai/package.json.orig +1 -1
  22. mito_ai-0.1.44.data/data/share/jupyter/labextensions/mito_ai/static/lib_index_js.cf2e3ad2797fbb53826b.js → mito_ai-0.1.45.data/data/share/jupyter/labextensions/mito_ai/static/lib_index_js.0c3368195d954d2ed033.js +543 -105
  23. mito_ai-0.1.45.data/data/share/jupyter/labextensions/mito_ai/static/lib_index_js.0c3368195d954d2ed033.js.map +1 -0
  24. mito_ai-0.1.44.data/data/share/jupyter/labextensions/mito_ai/static/remoteEntry.5482493d1270f55b7283.js → mito_ai-0.1.45.data/data/share/jupyter/labextensions/mito_ai/static/remoteEntry.684f82575fcc2e3b350c.js +16 -16
  25. mito_ai-0.1.44.data/data/share/jupyter/labextensions/mito_ai/static/remoteEntry.5482493d1270f55b7283.js.map → mito_ai-0.1.45.data/data/share/jupyter/labextensions/mito_ai/static/remoteEntry.684f82575fcc2e3b350c.js.map +1 -1
  26. {mito_ai-0.1.44.dist-info → mito_ai-0.1.45.dist-info}/METADATA +2 -2
  27. {mito_ai-0.1.44.dist-info → mito_ai-0.1.45.dist-info}/RECORD +51 -49
  28. mito_ai-0.1.44.data/data/share/jupyter/labextensions/mito_ai/static/lib_index_js.cf2e3ad2797fbb53826b.js.map +0 -1
  29. {mito_ai-0.1.44.data → mito_ai-0.1.45.data}/data/etc/jupyter/jupyter_server_config.d/mito_ai.json +0 -0
  30. {mito_ai-0.1.44.data → mito_ai-0.1.45.data}/data/share/jupyter/labextensions/mito_ai/schemas/mito_ai/toolbar-buttons.json +0 -0
  31. {mito_ai-0.1.44.data → mito_ai-0.1.45.data}/data/share/jupyter/labextensions/mito_ai/static/node_modules_process_browser_js.4b128e94d31a81ebd209.js +0 -0
  32. {mito_ai-0.1.44.data → mito_ai-0.1.45.data}/data/share/jupyter/labextensions/mito_ai/static/node_modules_process_browser_js.4b128e94d31a81ebd209.js.map +0 -0
  33. {mito_ai-0.1.44.data → mito_ai-0.1.45.data}/data/share/jupyter/labextensions/mito_ai/static/style.js +0 -0
  34. {mito_ai-0.1.44.data → mito_ai-0.1.45.data}/data/share/jupyter/labextensions/mito_ai/static/style_index_js.5876024bb17dbd6a3ee6.js +0 -0
  35. {mito_ai-0.1.44.data → mito_ai-0.1.45.data}/data/share/jupyter/labextensions/mito_ai/static/style_index_js.5876024bb17dbd6a3ee6.js.map +0 -0
  36. {mito_ai-0.1.44.data → mito_ai-0.1.45.data}/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_aws-amplify_auth_dist_esm_providers_cognito_apis_signOut_mjs-node_module-75790d.688c25857e7b81b1740f.js +0 -0
  37. {mito_ai-0.1.44.data → mito_ai-0.1.45.data}/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_aws-amplify_auth_dist_esm_providers_cognito_apis_signOut_mjs-node_module-75790d.688c25857e7b81b1740f.js.map +0 -0
  38. {mito_ai-0.1.44.data → mito_ai-0.1.45.data}/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_aws-amplify_auth_dist_esm_providers_cognito_tokenProvider_tokenProvider_-72f1c8.a917210f057fcfe224ad.js +0 -0
  39. {mito_ai-0.1.44.data → mito_ai-0.1.45.data}/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_aws-amplify_auth_dist_esm_providers_cognito_tokenProvider_tokenProvider_-72f1c8.a917210f057fcfe224ad.js.map +0 -0
  40. {mito_ai-0.1.44.data → mito_ai-0.1.45.data}/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_aws-amplify_dist_esm_index_mjs.6bac1a8c4cc93f15f6b7.js +0 -0
  41. {mito_ai-0.1.44.data → mito_ai-0.1.45.data}/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_aws-amplify_dist_esm_index_mjs.6bac1a8c4cc93f15f6b7.js.map +0 -0
  42. {mito_ai-0.1.44.data → mito_ai-0.1.45.data}/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_aws-amplify_ui-react_dist_esm_index_mjs.4fcecd65bef9e9847609.js +0 -0
  43. {mito_ai-0.1.44.data → mito_ai-0.1.45.data}/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_aws-amplify_ui-react_dist_esm_index_mjs.4fcecd65bef9e9847609.js.map +0 -0
  44. {mito_ai-0.1.44.data → mito_ai-0.1.45.data}/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_react-dom_client_js-node_modules_aws-amplify_ui-react_dist_styles_css.b43d4249e4d3dac9ad7b.js +0 -0
  45. {mito_ai-0.1.44.data → mito_ai-0.1.45.data}/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_react-dom_client_js-node_modules_aws-amplify_ui-react_dist_styles_css.b43d4249e4d3dac9ad7b.js.map +0 -0
  46. {mito_ai-0.1.44.data → mito_ai-0.1.45.data}/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_semver_index_js.3f6754ac5116d47de76b.js +0 -0
  47. {mito_ai-0.1.44.data → mito_ai-0.1.45.data}/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_semver_index_js.3f6754ac5116d47de76b.js.map +0 -0
  48. {mito_ai-0.1.44.data → mito_ai-0.1.45.data}/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_vscode-diff_dist_index_js.ea55f1f9346638aafbcf.js +0 -0
  49. {mito_ai-0.1.44.data → mito_ai-0.1.45.data}/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_vscode-diff_dist_index_js.ea55f1f9346638aafbcf.js.map +0 -0
  50. {mito_ai-0.1.44.dist-info → mito_ai-0.1.45.dist-info}/WHEEL +0 -0
  51. {mito_ai-0.1.44.dist-info → mito_ai-0.1.45.dist-info}/entry_points.txt +0 -0
  52. {mito_ai-0.1.44.dist-info → mito_ai-0.1.45.dist-info}/licenses/LICENSE +0 -0
@@ -1081,9 +1081,10 @@ const ChatInput = ({ app, initialContent, placeholder, onSave, onCancel, isEditi
1081
1081
  if (!textarea)
1082
1082
  return;
1083
1083
  textarea.style.minHeight = 'auto';
1084
+ const maxHeight = 350;
1084
1085
  textarea.style.height = !textarea.value || resetHeight
1085
1086
  ? '80px'
1086
- : `${Math.max(80, textarea.scrollHeight)}px`;
1087
+ : `${Math.min(maxHeight, Math.max(80, textarea.scrollHeight))}px`;
1087
1088
  };
1088
1089
  (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => {
1089
1090
  adjustHeight();
@@ -1262,7 +1263,7 @@ const ChatInput = ({ app, initialContent, placeholder, onSave, onCancel, isEditi
1262
1263
  }
1263
1264
  }
1264
1265
  }, [agentModeEnabled, additionalContext, activeCellCode]);
1265
- return (react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: (0,_utils_classNames__WEBPACK_IMPORTED_MODULE_5__.classNames)("chat-input-container") },
1266
+ return (react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: (0,_utils_classNames__WEBPACK_IMPORTED_MODULE_5__.classNames)("chat-input-container", { "editing": isEditing }) },
1266
1267
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: 'context-container' },
1267
1268
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_components_DatabaseButton__WEBPACK_IMPORTED_MODULE_6__["default"], { app: app }),
1268
1269
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_components_AttachFileButton__WEBPACK_IMPORTED_MODULE_7__["default"], { onFileUploaded: handleFileUpload, notebookTracker: notebookTracker }),
@@ -2145,6 +2146,8 @@ const ChatTaskpane = ({ notebookTracker, renderMimeRegistry, contextManager, app
2145
2146
  const shouldContinueAgentExecution = (0,react__WEBPACK_IMPORTED_MODULE_1__.useRef)(true);
2146
2147
  const streamingContentRef = (0,react__WEBPACK_IMPORTED_MODULE_1__.useRef)('');
2147
2148
  const streamHandlerRef = (0,react__WEBPACK_IMPORTED_MODULE_1__.useRef)(null);
2149
+ // Track active requests for cancellation
2150
+ const activeRequestControllerRef = (0,react__WEBPACK_IMPORTED_MODULE_1__.useRef)(null);
2148
2151
  // State for managing next steps from responses
2149
2152
  // If the user hides the next steps, we keep them hidden until they re-open them
2150
2153
  const [nextSteps, setNextSteps] = (0,react__WEBPACK_IMPORTED_MODULE_1__.useState)([]);
@@ -2542,6 +2545,9 @@ const ChatTaskpane = ({ notebookTracker, renderMimeRegistry, contextManager, app
2542
2545
  };
2543
2546
  const _sendMessageAndSaveResponse = async (completionRequest, newChatHistoryManager) => {
2544
2547
  var _a, _b, _c, _d;
2548
+ // Create AbortController for this request
2549
+ const abortController = new AbortController();
2550
+ activeRequestControllerRef.current = abortController;
2545
2551
  // Capture the completion request for debugging
2546
2552
  (0,_SettingsManager_profiler_ProfilerPage__WEBPACK_IMPORTED_MODULE_14__.captureCompletionRequest)(completionRequest);
2547
2553
  if (completionRequest.stream) {
@@ -2610,7 +2616,15 @@ const ChatTaskpane = ({ notebookTracker, renderMimeRegistry, contextManager, app
2610
2616
  // NON-STREAMING RESPONSES
2611
2617
  // Once we move everything to streaming, we can remove everything in this else block
2612
2618
  try {
2619
+ // Check if request was aborted before making the call
2620
+ if (abortController.signal.aborted) {
2621
+ throw new Error('Request aborted');
2622
+ }
2613
2623
  const aiResponse = await websocketClient.sendMessage(completionRequest);
2624
+ // Check if request was aborted after receiving response
2625
+ if (abortController.signal.aborted) {
2626
+ throw new Error('Request aborted');
2627
+ }
2614
2628
  if (aiResponse.error) {
2615
2629
  console.group('Error calling OpenAI API:');
2616
2630
  console.error('Title:', aiResponse.error.title);
@@ -2645,6 +2659,11 @@ const ChatTaskpane = ({ notebookTracker, renderMimeRegistry, contextManager, app
2645
2659
  }
2646
2660
  }
2647
2661
  catch (error) {
2662
+ // Check if this was an abort error
2663
+ if (error.message === 'Request aborted') {
2664
+ // Don't show error message for aborted requests
2665
+ return false;
2666
+ }
2648
2667
  addAIMessageFromResponseAndUpdateState(error.title ? error.title : `${error}`, 'chat', newChatHistoryManager, false);
2649
2668
  addAIMessageFromResponseAndUpdateState(error.hint ? error.hint : `${error}`, completionRequest.metadata.promptType, newChatHistoryManager, true);
2650
2669
  }
@@ -2654,6 +2673,10 @@ const ChatTaskpane = ({ notebookTracker, renderMimeRegistry, contextManager, app
2654
2673
  setLoadingAIResponse(false);
2655
2674
  }
2656
2675
  }
2676
+ // Clean up AbortController
2677
+ if (activeRequestControllerRef.current === abortController) {
2678
+ activeRequestControllerRef.current = null;
2679
+ }
2657
2680
  return true;
2658
2681
  };
2659
2682
  const addAIMessageFromResponseAndUpdateState = (messageContent, promptType, chatHistoryManager, mitoAIConnectionError = false, mitoAIConnectionErrorType = null) => {
@@ -2664,19 +2687,33 @@ const ChatTaskpane = ({ notebookTracker, renderMimeRegistry, contextManager, app
2664
2687
  chatHistoryManager.addAIMessageFromResponse(messageContent, promptType, mitoAIConnectionError, mitoAIConnectionErrorType);
2665
2688
  setChatHistoryManager(chatHistoryManager);
2666
2689
  };
2667
- const markAgentForStopping = () => {
2668
- // Signal that the agent should stop after current task
2690
+ const markAgentForStopping = async (reason = 'naturalConclusion') => {
2691
+ // Signal that the agent should stop immediately
2669
2692
  shouldContinueAgentExecution.current = false;
2670
- // Update UI to show stopping state
2671
- setAgentExecutionStatus('stopping');
2672
- };
2673
- const finalizeAgentStop = () => {
2674
- // Notify user that agent has been stopped
2675
- shouldContinueAgentExecution.current = false;
2676
- const newChatHistoryManager = getDuplicateChatHistoryManager();
2677
- addAIMessageFromResponseAndUpdateState("Agent execution stopped. You can continue the conversation or start a new one.", 'chat', newChatHistoryManager);
2678
- // Reset agent to idle state
2693
+ // Update state/UI
2679
2694
  setAgentExecutionStatus('idle');
2695
+ setLoadingAIResponse(false);
2696
+ if (reason === 'userStop') {
2697
+ // Immediately abort any ongoing requests
2698
+ if (activeRequestControllerRef.current) {
2699
+ activeRequestControllerRef.current.abort();
2700
+ activeRequestControllerRef.current = null;
2701
+ }
2702
+ // Add feedback message based on reason
2703
+ const newChatHistoryManager = getDuplicateChatHistoryManager();
2704
+ addAIMessageFromResponseAndUpdateState("Agent stopped by user.", 'chat', newChatHistoryManager);
2705
+ // Send stop message to backend
2706
+ await websocketClient.sendMessage({
2707
+ type: "stop_agent",
2708
+ message_id: _lumino_coreutils__WEBPACK_IMPORTED_MODULE_3__.UUID.uuid4(),
2709
+ metadata: {
2710
+ promptType: "stop_agent",
2711
+ threadId: activeThreadIdRef.current
2712
+ },
2713
+ stream: false
2714
+ });
2715
+ }
2716
+ return;
2680
2717
  };
2681
2718
  const startAgentExecution = async (input, messageIndex, additionalContext) => {
2682
2719
  agentTargetNotebookPanelRef.current = notebookTracker.currentWidget;
@@ -2693,7 +2730,7 @@ const ChatTaskpane = ({ notebookTracker, renderMimeRegistry, contextManager, app
2693
2730
  while (!isAgentFinished && agentExecutionDepth <= AGENT_EXECUTION_DEPTH_LIMIT) {
2694
2731
  // Check if we should continue execution
2695
2732
  if (!shouldContinueAgentExecution.current) {
2696
- finalizeAgentStop();
2733
+ await markAgentForStopping();
2697
2734
  break;
2698
2735
  }
2699
2736
  // Only the first message sent to the Agent should contain the user's input.
@@ -2718,7 +2755,7 @@ const ChatTaskpane = ({ notebookTracker, renderMimeRegistry, contextManager, app
2718
2755
  if (!securityCheck.safe) {
2719
2756
  console.error('Security Warning:', securityCheck.reason);
2720
2757
  addAIMessageFromResponseAndUpdateState(`I cannot execute this code without your approval because this code did not pass my security checks. ${securityCheck.reason}. For your safety, I am stopping execution of this plan.`, 'agent:execution', chatHistoryManager);
2721
- finalizeAgentStop();
2758
+ await markAgentForStopping();
2722
2759
  break;
2723
2760
  }
2724
2761
  }
@@ -2726,28 +2763,32 @@ const ChatTaskpane = ({ notebookTracker, renderMimeRegistry, contextManager, app
2726
2763
  const agentResponse = aiDisplayOptimizedChatItem === null || aiDisplayOptimizedChatItem === void 0 ? void 0 : aiDisplayOptimizedChatItem.agentResponse;
2727
2764
  if (agentTargetNotebookPanelRef.current === null) {
2728
2765
  // If the agent target notebook panel is not set, we don't know where to run the code so we stop
2766
+ await markAgentForStopping();
2729
2767
  isAgentFinished = true;
2730
2768
  break;
2731
2769
  }
2732
2770
  if (agentResponse === undefined) {
2733
2771
  // If the agent response is undefined, we need to send a message to the agent
2772
+ await markAgentForStopping();
2734
2773
  isAgentFinished = true;
2735
2774
  break;
2736
2775
  }
2737
2776
  if (agentResponse.type === 'finished_task') {
2738
2777
  // If the agent told us that it is finished, we can stop
2778
+ await markAgentForStopping();
2739
2779
  isAgentFinished = true;
2740
2780
  break;
2741
2781
  }
2742
2782
  if (agentResponse.type === 'cell_update' && (agentResponse.cell_update === undefined || agentResponse.cell_update === null)) {
2743
2783
  // If the agent's response is not formatted correctly, stop. This is for typechecking mostly
2784
+ await markAgentForStopping();
2744
2785
  isAgentFinished = true;
2745
2786
  break;
2746
2787
  }
2747
2788
  if (agentResponse.type === 'cell_update' && agentResponse.cell_update) {
2748
2789
  // Run the code and handle any errors
2749
2790
  await (0,_utils_agentActions__WEBPACK_IMPORTED_MODULE_18__.acceptAndRunCellUpdate)(agentResponse.cell_update, agentTargetNotebookPanelRef.current);
2750
- const status = await (0,_utils_agentActions__WEBPACK_IMPORTED_MODULE_18__.retryIfExecutionError)(agentTargetNotebookPanelRef.current, app, sendAgentSmartDebugMessage, shouldContinueAgentExecution, finalizeAgentStop, chatHistoryManagerRef);
2791
+ const status = await (0,_utils_agentActions__WEBPACK_IMPORTED_MODULE_18__.retryIfExecutionError)(agentTargetNotebookPanelRef.current, app, sendAgentSmartDebugMessage, shouldContinueAgentExecution, markAgentForStopping, chatHistoryManagerRef);
2751
2792
  if (status === 'interupted') {
2752
2793
  break;
2753
2794
  }
@@ -2772,7 +2813,7 @@ const ChatTaskpane = ({ notebookTracker, renderMimeRegistry, contextManager, app
2772
2813
  if (!result.success && result.errorMessage && result.errorCellId) {
2773
2814
  // Set the error cell as active so the error retry logic can work with it
2774
2815
  (0,_utils_notebook__WEBPACK_IMPORTED_MODULE_19__.setActiveCellByIDInNotebookPanel)(agentTargetNotebookPanelRef.current, result.errorCellId);
2775
- const status = await (0,_utils_agentActions__WEBPACK_IMPORTED_MODULE_18__.retryIfExecutionError)(agentTargetNotebookPanelRef.current, app, sendAgentSmartDebugMessage, shouldContinueAgentExecution, finalizeAgentStop, chatHistoryManagerRef);
2816
+ const status = await (0,_utils_agentActions__WEBPACK_IMPORTED_MODULE_18__.retryIfExecutionError)(agentTargetNotebookPanelRef.current, app, sendAgentSmartDebugMessage, shouldContinueAgentExecution, markAgentForStopping, chatHistoryManagerRef);
2776
2817
  if (status === 'interupted') {
2777
2818
  break;
2778
2819
  }
@@ -2786,7 +2827,8 @@ const ChatTaskpane = ({ notebookTracker, renderMimeRegistry, contextManager, app
2786
2827
  if (agentExecutionDepth > AGENT_EXECUTION_DEPTH_LIMIT) {
2787
2828
  addAIMessageFromResponseAndUpdateState("Since I've been working for a while now, give my work a review and then tell me how to continue.", 'agent:execution', chatHistoryManager);
2788
2829
  }
2789
- setAgentExecutionStatus('idle');
2830
+ // Use markAgentForStopping for natural conclusion to ensure consistent cleanup
2831
+ await markAgentForStopping();
2790
2832
  };
2791
2833
  const updateCodeDiffStripes = (aiMessage, updateCellID) => {
2792
2834
  if (!aiMessage) {
@@ -3157,7 +3199,7 @@ const ChatTaskpane = ({ notebookTracker, renderMimeRegistry, contextManager, app
3157
3199
  } },
3158
3200
  react__WEBPACK_IMPORTED_MODULE_1___default().createElement("span", { className: "submit-text" }, "Submit"),
3159
3201
  " \u23CE"))),
3160
- (agentExecutionStatus === 'working' || agentExecutionStatus === 'stopping') && (react__WEBPACK_IMPORTED_MODULE_1___default().createElement("button", { className: "button-base button-red stop-agent-button", onClick: markAgentForStopping, disabled: agentExecutionStatus === 'stopping', "data-testid": "stop-agent-button" }, agentExecutionStatus === 'stopping' ? (react__WEBPACK_IMPORTED_MODULE_1___default().createElement("div", { className: "stop-agent-button-content" },
3202
+ (agentExecutionStatus === 'working' || agentExecutionStatus === 'stopping') && (react__WEBPACK_IMPORTED_MODULE_1___default().createElement("button", { className: "button-base button-red stop-agent-button", onClick: () => void markAgentForStopping('userStop'), disabled: agentExecutionStatus === 'stopping', "data-testid": "stop-agent-button" }, agentExecutionStatus === 'stopping' ? (react__WEBPACK_IMPORTED_MODULE_1___default().createElement("div", { className: "stop-agent-button-content" },
3161
3203
  "Stopping",
3162
3204
  react__WEBPACK_IMPORTED_MODULE_1___default().createElement(_components_LoadingCircle__WEBPACK_IMPORTED_MODULE_40__["default"], null),
3163
3205
  " ")) : ('Stop Agent')))));
@@ -3573,6 +3615,7 @@ __webpack_require__.r(__webpack_exports__);
3573
3615
  * Handles common mistakes like string instead of array, missing fields, etc.
3574
3616
  */
3575
3617
  function validateAndCorrectAgentResponse(agentResponse) {
3618
+ var _a;
3576
3619
  // Create a copy to avoid mutating the original
3577
3620
  const correctedResponse = { ...agentResponse };
3578
3621
  // Ensure type is valid. Default to finished_task if not valid.
@@ -3594,6 +3637,8 @@ function validateAndCorrectAgentResponse(agentResponse) {
3594
3637
  // Correct analysis_assumptions - handle string to array conversion
3595
3638
  if (correctedResponse.analysis_assumptions !== undefined && correctedResponse.analysis_assumptions !== null) {
3596
3639
  correctedResponse.analysis_assumptions = correctStringArray(correctedResponse.analysis_assumptions);
3640
+ // No empty strings in the assumptions
3641
+ correctedResponse.analysis_assumptions = (_a = correctedResponse.analysis_assumptions) === null || _a === void 0 ? void 0 : _a.filter(assumption => assumption.trim() !== '');
3597
3642
  }
3598
3643
  // For now we don't validate the cell_update object itself, as this is more complex and has
3599
3644
  // not caused issues thus far.
@@ -3611,7 +3656,7 @@ function correctStringArray(value) {
3611
3656
  if (typeof value === 'string') {
3612
3657
  return [value];
3613
3658
  }
3614
- return null;
3659
+ return undefined;
3615
3660
  }
3616
3661
 
3617
3662
 
@@ -3696,28 +3741,24 @@ __webpack_require__.r(__webpack_exports__);
3696
3741
  */
3697
3742
 
3698
3743
 
3699
- const deployAppNotification = (url, appManagerService) => {
3744
+ const deployAppNotification = (url, appManagerService, notificationId) => {
3700
3745
  // Total deployment time in milliseconds (3 minutes = 180000ms)
3701
3746
  const totalDeploymentTime = 180000;
3702
3747
  // Create an array of deployment steps
3703
3748
  const deploymentSteps = [
3704
- "Step 1/7: Preparing your app...",
3705
- "Step 2/7: Assembling clouds...",
3706
- "Step 3/7: Building your app...",
3707
- "Step 4/7: Configuring network settings...",
3708
- "Step 5/7: Adding final touches...",
3709
- "Step 6/7: Running security checks...",
3749
+ "Step 2/7: Preparing your app...",
3750
+ "Step 3/7: Assembling clouds...",
3751
+ "Step 4/7: Building your app...",
3752
+ "Step 5/7: Configuring network settings...",
3753
+ "Step 6/7: Adding final touches...",
3754
+ "Step 7/7: Running security checks...",
3710
3755
  "Deployment complete! Your app is ready."
3711
3756
  ];
3712
- // Create initial "in progress" notification to get notificaiton id
3713
- const notificationId = _jupyterlab_apputils__WEBPACK_IMPORTED_MODULE_0__.Notification.emit(deploymentSteps[0], 'in-progress', {
3714
- autoClose: false
3715
- });
3716
3757
  // Calculate time between steps (evenly distribute throughout the total deployment time)
3717
3758
  const stepInterval = totalDeploymentTime / (deploymentSteps.length - 1);
3718
3759
  let retryCount = 5;
3719
3760
  // Update message at each step interval
3720
- for (let i = 1; i < deploymentSteps.length; i++) {
3761
+ for (let i = 0; i < deploymentSteps.length; i++) {
3721
3762
  setTimeout(() => {
3722
3763
  const isLastStep = i === deploymentSteps.length - 1;
3723
3764
  if (isLastStep) {
@@ -3784,6 +3825,127 @@ const deployAppNotification = (url, appManagerService) => {
3784
3825
  };
3785
3826
 
3786
3827
 
3828
+ /***/ }),
3829
+
3830
+ /***/ "./lib/Extensions/AppDeploy/DeployFilesSelector.js":
3831
+ /*!*********************************************************!*\
3832
+ !*** ./lib/Extensions/AppDeploy/DeployFilesSelector.js ***!
3833
+ \*********************************************************/
3834
+ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
3835
+
3836
+ __webpack_require__.r(__webpack_exports__);
3837
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
3838
+ /* harmony export */ FileUploadPopup: () => (/* binding */ FileUploadPopup)
3839
+ /* harmony export */ });
3840
+ /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ "webpack/sharing/consume/default/react");
3841
+ /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
3842
+ /* harmony import */ var _style_ConnectionForm_css__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../../style/ConnectionForm.css */ "./style/ConnectionForm.css");
3843
+ /* harmony import */ var _style_button_css__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../../style/button.css */ "./style/button.css");
3844
+ /* harmony import */ var _style_FilesSelector_css__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../../style/FilesSelector.css */ "./style/FilesSelector.css");
3845
+ /* harmony import */ var _jupyterlab_ui_components__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! @jupyterlab/ui-components */ "webpack/sharing/consume/default/@jupyterlab/ui-components");
3846
+ /* harmony import */ var _jupyterlab_ui_components__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(_jupyterlab_ui_components__WEBPACK_IMPORTED_MODULE_4__);
3847
+ /*
3848
+ * Copyright (c) Saga Inc.
3849
+ * Distributed under the terms of the GNU Affero General Public License v3.0 License.
3850
+ */
3851
+
3852
+
3853
+
3854
+
3855
+
3856
+ const FileIcon = _jupyterlab_ui_components__WEBPACK_IMPORTED_MODULE_4__.fileIcon.react;
3857
+ const FolderIcon = _jupyterlab_ui_components__WEBPACK_IMPORTED_MODULE_4__.folderIcon.react;
3858
+ const CloseIcon = _jupyterlab_ui_components__WEBPACK_IMPORTED_MODULE_4__.closeIcon.react;
3859
+ const FileUploadPopup = ({ filePath, onClose, onSubmit }) => {
3860
+ const [items, setItems] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)([]);
3861
+ const [selectedItems, setSelectedItems] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(new Set());
3862
+ // Figure out current notebook directory
3863
+ const getNotebookDir = () => {
3864
+ if (!filePath)
3865
+ return '';
3866
+ const parts = filePath.split('/');
3867
+ parts.pop(); // remove notebook filename
3868
+ return parts.join('/');
3869
+ };
3870
+ const alwaysSelected = ['requirements.txt', 'app.py'];
3871
+ (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => {
3872
+ const nbDir = getNotebookDir();
3873
+ const apiPath = nbDir ? `/api/contents/${nbDir}` : '/api/contents/';
3874
+ fetch(apiPath)
3875
+ .then(res => res.json())
3876
+ .then(data => {
3877
+ const entries = data.content.map((item) => ({
3878
+ name: item.name,
3879
+ type: item.type, // "file" or "directory"
3880
+ }))
3881
+ .sort((a, b) => a.name.localeCompare(b.name));
3882
+ setItems(entries);
3883
+ // Pre-select default files
3884
+ const defaultFiles = new Set();
3885
+ entries.forEach(entry => {
3886
+ if (alwaysSelected.includes(entry.name)) {
3887
+ defaultFiles.add(entry.name);
3888
+ }
3889
+ });
3890
+ setSelectedItems(defaultFiles);
3891
+ })
3892
+ .catch(err => console.error('Error fetching files/dirs:', err));
3893
+ }, []);
3894
+ const handleCheckboxChange = (name) => {
3895
+ setSelectedItems(prev => {
3896
+ const newSet = new Set(prev);
3897
+ if (newSet.has(name))
3898
+ newSet.delete(name);
3899
+ else
3900
+ newSet.add(name);
3901
+ return newSet;
3902
+ });
3903
+ };
3904
+ const handleSelectAll = (checked) => {
3905
+ if (checked) {
3906
+ // select all, nothing excluded
3907
+ setSelectedItems(new Set(items.map(i => i.name)));
3908
+ }
3909
+ else {
3910
+ // keep only alwaysSelected
3911
+ setSelectedItems(new Set(alwaysSelected));
3912
+ }
3913
+ };
3914
+ const handleSubmit = () => {
3915
+ const selectedPaths = Array.from(selectedItems);
3916
+ onSubmit(selectedPaths);
3917
+ onClose();
3918
+ };
3919
+ const allSelected = items.length > 0 && selectedItems.size === items.length;
3920
+ const partiallySelected = selectedItems.size > 0 && selectedItems.size < items.length;
3921
+ return (react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "modal-overlay" },
3922
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "modal-content" },
3923
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "modal-header" },
3924
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement("h3", null, "Upload files Required for the App"),
3925
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", { onClick: onClose, className: "modal-close-button", title: "Close" },
3926
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(CloseIcon, null))),
3927
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "modal-subheader" },
3928
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement("p", { className: "modal-subtext" }, "Select the files and/or directories that are required to render the app. For example, if your app reads data from a csv file, you must select it here.")),
3929
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "modal-body" }, items.length === 0 ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement("p", null, "No items found.")) : (react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null,
3930
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "files-selector-select-all" },
3931
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement("label", { className: "checkbox-label" },
3932
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement("input", { type: "checkbox", checked: allSelected, ref: el => {
3933
+ if (el)
3934
+ el.indeterminate = partiallySelected;
3935
+ }, onChange: e => handleSelectAll(e.target.checked) }),
3936
+ "Select All")),
3937
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "file-list-scrollable" },
3938
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement("ul", { className: "file-list" }, items.map((item, index) => (react__WEBPACK_IMPORTED_MODULE_0___default().createElement("li", { key: index },
3939
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement("label", { className: "checkbox-label" },
3940
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement("input", { type: "checkbox", checked: selectedItems.has(item.name), onChange: () => handleCheckboxChange(item.name), disabled: alwaysSelected.includes(item.name), title: alwaysSelected.includes(item.name) ? "Required for deploying your app" : undefined }),
3941
+ item.type === 'directory' ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement(FolderIcon, null) : react__WEBPACK_IMPORTED_MODULE_0___default().createElement(FileIcon, null),
3942
+ " ",
3943
+ item.name))))))))),
3944
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "modal-footer" },
3945
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", { className: "files-selector-submit-button", onClick: handleSubmit, disabled: selectedItems.size === 0 }, "Deploy App")))));
3946
+ };
3947
+
3948
+
3787
3949
  /***/ }),
3788
3950
 
3789
3951
  /***/ "./lib/Extensions/AppDeploy/DeployStreamlitApp.js":
@@ -3802,9 +3964,10 @@ __webpack_require__.r(__webpack_exports__);
3802
3964
  /* harmony import */ var _fileUtils__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./fileUtils */ "./lib/Extensions/AppDeploy/fileUtils.js");
3803
3965
  /* harmony import */ var _lumino_coreutils__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @lumino/coreutils */ "webpack/sharing/consume/default/@lumino/coreutils");
3804
3966
  /* harmony import */ var _lumino_coreutils__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_lumino_coreutils__WEBPACK_IMPORTED_MODULE_1__);
3805
- /* harmony import */ var _DeployAppNotification__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./DeployAppNotification */ "./lib/Extensions/AppDeploy/DeployAppNotification.js");
3967
+ /* harmony import */ var _DeployAppNotification__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./DeployAppNotification */ "./lib/Extensions/AppDeploy/DeployAppNotification.js");
3806
3968
  /* harmony import */ var _auth__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./auth */ "./lib/Extensions/AppDeploy/auth.js");
3807
3969
  /* harmony import */ var _authPopupUtils__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./authPopupUtils */ "./lib/Extensions/AppDeploy/authPopupUtils.js");
3970
+ /* harmony import */ var _FilesSelectorUtils__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./FilesSelectorUtils */ "./lib/Extensions/AppDeploy/FilesSelectorUtils.js");
3808
3971
  /*
3809
3972
  * Copyright (c) Saga Inc.
3810
3973
  * Distributed under the terms of the GNU Affero General Public License v3.0 License.
@@ -3816,11 +3979,13 @@ __webpack_require__.r(__webpack_exports__);
3816
3979
 
3817
3980
 
3818
3981
 
3982
+
3819
3983
  /*
3820
3984
  This function generates the requirements.txt file needed to host the streamlit app,
3821
3985
  and deploys it!
3822
3986
  */
3823
3987
  const deployStreamlitApp = async (notebookTracker, appDeployService, appManagerService) => {
3988
+ let selectedFiles = [];
3824
3989
  let jwtToken = await (0,_auth__WEBPACK_IMPORTED_MODULE_2__.getJWTToken)();
3825
3990
  if (!jwtToken) {
3826
3991
  // No token found, show authentication popup
@@ -3849,10 +4014,24 @@ const deployStreamlitApp = async (notebookTracker, appDeployService, appManagerS
3849
4014
  return;
3850
4015
  }
3851
4016
  const notebookPath = notebookPanel.context.path;
4017
+ const notificationId = _jupyterlab_apputils__WEBPACK_IMPORTED_MODULE_0__.Notification.emit('Step 1/7: Gathering requirements...', 'in-progress', {
4018
+ autoClose: false
4019
+ });
3852
4020
  // Build the requirements.txt file
3853
4021
  const requirementsContent = await (0,_requirementsUtils__WEBPACK_IMPORTED_MODULE_4__.generateRequirementsTxt)(notebookTracker);
3854
4022
  // Save the files to the current directory
3855
4023
  await (0,_fileUtils__WEBPACK_IMPORTED_MODULE_5__.saveFileWithKernel)(notebookTracker, './requirements.txt', requirementsContent);
4024
+ try {
4025
+ _jupyterlab_apputils__WEBPACK_IMPORTED_MODULE_0__.Notification.dismiss(notificationId);
4026
+ selectedFiles = await (0,_FilesSelectorUtils__WEBPACK_IMPORTED_MODULE_6__.fileSelectorPopup)(notebookPath);
4027
+ }
4028
+ catch (error) {
4029
+ console.log('File selection failed:', error);
4030
+ return;
4031
+ }
4032
+ const newNotificationId = _jupyterlab_apputils__WEBPACK_IMPORTED_MODULE_0__.Notification.emit("Step 2/7: Preparing your app...", 'in-progress', {
4033
+ autoClose: false
4034
+ });
3856
4035
  // After building the files, we need to send a request to the backend to deploy the app
3857
4036
  try {
3858
4037
  console.log("Sending request to deploy the app");
@@ -3861,17 +4040,21 @@ const deployStreamlitApp = async (notebookTracker, appDeployService, appManagerS
3861
4040
  type: 'deploy-app',
3862
4041
  message_id: _lumino_coreutils__WEBPACK_IMPORTED_MODULE_1__.UUID.uuid4(),
3863
4042
  notebook_path: notebookPath,
3864
- jwt_token: jwtToken
4043
+ jwt_token: jwtToken,
4044
+ selected_files: selectedFiles
3865
4045
  });
3866
4046
  if (response.error) {
3867
- _jupyterlab_apputils__WEBPACK_IMPORTED_MODULE_0__.Notification.emit(response.error.title, 'error', {
4047
+ _jupyterlab_apputils__WEBPACK_IMPORTED_MODULE_0__.Notification.update({
4048
+ id: newNotificationId,
4049
+ message: response.error.title,
4050
+ type: 'error',
3868
4051
  autoClose: false
3869
4052
  });
3870
4053
  }
3871
4054
  else {
3872
4055
  console.log("App deployment response:", response);
3873
4056
  const url = response.url;
3874
- (0,_DeployAppNotification__WEBPACK_IMPORTED_MODULE_6__.deployAppNotification)(url, appManagerService);
4057
+ (0,_DeployAppNotification__WEBPACK_IMPORTED_MODULE_7__.deployAppNotification)(url, appManagerService, newNotificationId);
3875
4058
  }
3876
4059
  }
3877
4060
  catch (error) {
@@ -3881,6 +4064,59 @@ const deployStreamlitApp = async (notebookTracker, appDeployService, appManagerS
3881
4064
  };
3882
4065
 
3883
4066
 
4067
+ /***/ }),
4068
+
4069
+ /***/ "./lib/Extensions/AppDeploy/FilesSelectorUtils.js":
4070
+ /*!********************************************************!*\
4071
+ !*** ./lib/Extensions/AppDeploy/FilesSelectorUtils.js ***!
4072
+ \********************************************************/
4073
+ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
4074
+
4075
+ __webpack_require__.r(__webpack_exports__);
4076
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
4077
+ /* harmony export */ fileSelectorPopup: () => (/* binding */ fileSelectorPopup)
4078
+ /* harmony export */ });
4079
+ /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ "webpack/sharing/consume/default/react");
4080
+ /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
4081
+ /* harmony import */ var react_dom_client__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! react-dom/client */ "./node_modules/react-dom/client.js");
4082
+ /* harmony import */ var _DeployFilesSelector__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./DeployFilesSelector */ "./lib/Extensions/AppDeploy/DeployFilesSelector.js");
4083
+ /*
4084
+ * Copyright (c) Saga Inc.
4085
+ * Distributed under the terms of the GNU Affero General Public License v3.0 License.
4086
+ */
4087
+
4088
+
4089
+
4090
+ /**
4091
+ * Shows a file selector popup
4092
+ */
4093
+ const fileSelectorPopup = (notebookPath) => {
4094
+ return new Promise((resolve, reject) => {
4095
+ console.log("Starting file selector for:", notebookPath);
4096
+ // Create a container for the popup
4097
+ const popupContainer = document.createElement('div');
4098
+ popupContainer.id = 'file-selector-popup-container';
4099
+ document.body.appendChild(popupContainer);
4100
+ // Create root
4101
+ const root = (0,react_dom_client__WEBPACK_IMPORTED_MODULE_1__.createRoot)(popupContainer);
4102
+ const handleSubmit = (items) => {
4103
+ // Clean up the popup
4104
+ root.unmount();
4105
+ document.body.removeChild(popupContainer);
4106
+ resolve(items);
4107
+ };
4108
+ const handleClose = () => {
4109
+ // Clean up the popup
4110
+ root.unmount();
4111
+ document.body.removeChild(popupContainer);
4112
+ reject(new Error('File selection cancelled'));
4113
+ };
4114
+ // Render the AuthPopup
4115
+ root.render(react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_DeployFilesSelector__WEBPACK_IMPORTED_MODULE_2__.FileUploadPopup, { filePath: notebookPath, onClose: handleClose, onSubmit: handleSubmit }));
4116
+ });
4117
+ };
4118
+
4119
+
3884
4120
  /***/ }),
3885
4121
 
3886
4122
  /***/ "./lib/Extensions/AppDeploy/auth-popup-deploy.js":
@@ -4266,70 +4502,77 @@ const generateRequirementsTxt = async (notebookTracker) => {
4266
4502
  // Use the kernel to execute Python code using pipreqs
4267
4503
  const session = notebookPanel.sessionContext.session;
4268
4504
  if (session) {
4269
- // Collect all code in the notebook to analyze with pipreqs
4270
- const notebook = notebookPanel.content;
4271
- let codeContent = '';
4272
- // Gather all code cells content
4273
- notebook.widgets.forEach(cell => {
4274
- if (cell.model.type === 'code') {
4275
- const source = cell.model.sharedModel.source;
4276
- // Filter out lines that start with shell commands
4277
- const filteredLines = source.split('\n').filter(line => {
4278
- const trimmed = line.trim();
4279
- return !trimmed.startsWith('!') && !trimmed.startsWith('%');
4280
- });
4281
- if (filteredLines.length > 0) {
4282
- codeContent += filteredLines.join('\n') + '\n\n';
4283
- }
4284
- }
4285
- });
4286
- // Create Python code to run pipreqs on a temporary directory
4505
+ const appPyPath = `app.py`;
4506
+ // Create Python code to run pipreqs on the app.py file
4287
4507
  const pythonCode = `
4288
4508
  import subprocess
4289
4509
  import os
4290
- import tempfile
4291
-
4292
- # Create a temporary directory for pipreqs to analyze
4293
- with tempfile.TemporaryDirectory() as temp_dir:
4294
- # Save the notebook code to a temporary Python file
4295
- temp_file = os.path.join(temp_dir, "notebook_code.py")
4296
- with open(temp_file, "w") as f:
4297
- f.write("""${codeContent.replace(/\\/g, '\\\\').replace(/"""/g, '\\"\\"\\"')}""")
4298
-
4299
- # Make sure pipreqs is installed. Then
4300
- # 1. Create a requirements.in file
4301
- # 2. From the requirements.in file, generate the requirements.txt file with the canonical PyPI name of the packages
4302
- # and the versions as they exist on the user's terminal
4303
- try:
4304
- # Run pipreqs on the temporary directory
4305
- generate_req_in_file = subprocess.run(
4306
- ['pipreqs', '--savepath', 'requirements.in', '--force', temp_dir],
4307
- capture_output=True,
4308
- text=True
4309
- )
4310
-
4311
- print("Log: ", generate_req_in_file.stderr)
4312
-
4313
- command_for_generating_req_txt_file = r"""
4314
- cat requirements.in | while read line; do \\
4315
- pkg=$(echo $line | cut -d'=' -f1 | tr -d '[:space:]'); \\
4316
- version=$(pip show "$pkg" 2>/dev/null | grep -i '^Version:' | awk '{print $2}'); \\
4317
- name=$(pip show "$pkg" 2>/dev/null | grep -i '^Name:' | awk '{print $2}'); \\
4318
- if [[ -n "$name" && -n "$version" ]]; then echo "$name==$version"; else echo "$line"; fi; \\
4319
- done"""
4320
-
4321
- generate_req_txt_file = subprocess.run(
4322
- command_for_generating_req_txt_file,
4323
- shell=True,
4324
- executable="/bin/bash",
4325
- capture_output=True,
4326
- text=True,
4327
- check=True
4328
- )
4329
- print(generate_req_txt_file.stdout)
4330
-
4331
- except Exception as e:
4332
- print(f"Error running pipreqs: {e}")
4510
+
4511
+ # Check if app.py exists in the notebook directory
4512
+ app_py_path = os.path.join(os.getcwd(), "${appPyPath}")
4513
+ if not os.path.exists(app_py_path):
4514
+ print(f"Error: app.py not found at {app_py_path}")
4515
+ exit(1)
4516
+
4517
+ # Make sure pipreqs is installed. Then
4518
+ # 1. Create a requirements.in file
4519
+ # 2. From the requirements.in file, generate the requirements.txt file with the canonical PyPI name of the packages
4520
+ # and the versions as they exist on the user's terminal
4521
+ try:
4522
+ # Run pipreqs on the directory containing app.py
4523
+ notebook_dir = os.path.dirname(app_py_path)
4524
+ generate_req_in_file = subprocess.run(
4525
+ ['pipreqs', '--encoding=utf-8', '--savepath', 'requirements.in', '--force', notebook_dir],
4526
+ capture_output=True,
4527
+ text=True
4528
+ )
4529
+
4530
+ print("Log: ", generate_req_in_file.stderr)
4531
+
4532
+ # Read requirements.in and process each line
4533
+ requirements_in_path = os.path.join(notebook_dir, 'requirements.in')
4534
+ if os.path.exists(requirements_in_path):
4535
+ with open(requirements_in_path, 'r') as f:
4536
+ lines = f.readlines()
4537
+
4538
+ processed_requirements = []
4539
+ for line in lines:
4540
+ line = line.strip()
4541
+ if not line:
4542
+ continue
4543
+
4544
+ # Extract package name (everything before =)
4545
+ pkg_name = line.split('=')[0].strip()
4546
+
4547
+ # Get package info using pip show
4548
+ try:
4549
+ result = subprocess.run(['pip', 'show', pkg_name],
4550
+ capture_output=True, text=True, check=True)
4551
+ output = result.stdout
4552
+
4553
+ # Parse the output to get Name and Version
4554
+ name = None
4555
+ version = None
4556
+ for output_line in output.split('\\n'):
4557
+ if output_line.startswith('Name:'):
4558
+ name = output_line.split(':', 1)[1].strip()
4559
+ elif output_line.startswith('Version:'):
4560
+ version = output_line.split(':', 1)[1].strip()
4561
+
4562
+ if name and version:
4563
+ processed_requirements.append(f"{name}=={version}")
4564
+ else:
4565
+ processed_requirements.append(line)
4566
+ except subprocess.CalledProcessError:
4567
+ # If pip show fails, use the original line
4568
+ processed_requirements.append(line)
4569
+
4570
+ # Print the processed requirements
4571
+ for req in processed_requirements:
4572
+ print(req)
4573
+
4574
+ except Exception as e:
4575
+ print(f"Error running pipreqs: {e}")
4333
4576
  `;
4334
4577
  const kernel = session.kernel;
4335
4578
  if (kernel === null) {
@@ -4351,6 +4594,7 @@ with tempfile.TemporaryDirectory() as temp_dir:
4351
4594
  console.error(text);
4352
4595
  }
4353
4596
  else {
4597
+ console.log('Found dependencies:\n', text);
4354
4598
  resultText += text;
4355
4599
  }
4356
4600
  }
@@ -4927,7 +5171,7 @@ const startStreamlitPreviewAndNotify = async (notebookPath, force_recreate = fal
4927
5171
  id: notificationId,
4928
5172
  message: 'Streamlit preview started successfully!',
4929
5173
  type: 'default',
4930
- autoClose: false
5174
+ autoClose: 5 * 1000
4931
5175
  });
4932
5176
  return { previewData, notificationId };
4933
5177
  };
@@ -9565,7 +9809,7 @@ const acceptAndRunCellUpdate = async (cellUpdate, notebookPanel) => {
9565
9809
  // gets the most up to date data.
9566
9810
  await (0,_sleep__WEBPACK_IMPORTED_MODULE_2__.sleep)(1000);
9567
9811
  };
9568
- const retryIfExecutionError = async (notebookPanel, app, sendAgentSmartDebugMessage, shouldContinueAgentExecution, finalizeAgentStop, chatHistoryManagerRef) => {
9812
+ const retryIfExecutionError = async (notebookPanel, app, sendAgentSmartDebugMessage, shouldContinueAgentExecution, markAgentForStopping, chatHistoryManagerRef) => {
9569
9813
  var _a;
9570
9814
  const cell = notebookPanel.content.activeCell;
9571
9815
  // Note: If you update the max retries, update the message we display on each failure
@@ -9576,7 +9820,7 @@ const retryIfExecutionError = async (notebookPanel, app, sendAgentSmartDebugMess
9576
9820
  const MAX_RUN_ALL_CELLS_ATTEMPTS = 2; // Only allow two run_all_cells attempt per error cycle
9577
9821
  while ((0,_notebook__WEBPACK_IMPORTED_MODULE_1__.didCellExecutionError)(cell) && attempts < MAX_RETRIES) {
9578
9822
  if (!shouldContinueAgentExecution.current) {
9579
- finalizeAgentStop();
9823
+ await markAgentForStopping();
9580
9824
  return 'interupted';
9581
9825
  }
9582
9826
  // If the code cell has an error, we need to send the error to the AI
@@ -10421,8 +10665,8 @@ __webpack_require__.r(__webpack_exports__);
10421
10665
  * Copyright (c) Saga Inc.
10422
10666
  * Distributed under the terms of the GNU Affero General Public License v3.0 License.
10423
10667
  */
10424
- const CLAUDE_SONNET_DISPLAY_NAME = 'Claude 4 Sonnet';
10425
- const CLAUDE_SONNET_MODEL_NAME = 'claude-sonnet-4-20250514';
10668
+ const CLAUDE_SONNET_DISPLAY_NAME = 'Claude 4.5 Sonnet';
10669
+ const CLAUDE_SONNET_MODEL_NAME = 'claude-sonnet-4-5-20250929';
10426
10670
  const GPT_4_1_DISPLAY_NAME = 'GPT 4.1';
10427
10671
  const GPT_4_1_MODEL_NAME = 'gpt-4.1';
10428
10672
 
@@ -13264,6 +13508,10 @@ ___CSS_LOADER_EXPORT___.push([module.id, `/*
13264
13508
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
13265
13509
  }
13266
13510
 
13511
+ .chat-input-container.editing {
13512
+ margin-top: 0;
13513
+ }
13514
+
13267
13515
  .chat-input-text-area-container {
13268
13516
  position: relative;
13269
13517
  height: min-content;
@@ -13275,7 +13523,7 @@ ___CSS_LOADER_EXPORT___.push([module.id, `/*
13275
13523
  resize: none;
13276
13524
  width: 100%;
13277
13525
  padding: 10px;
13278
- overflow-y: hidden;
13526
+ overflow-y: auto;
13279
13527
  box-sizing: border-box;
13280
13528
  flex-shrink: 0 !important;
13281
13529
  background-color: var(--chat-user-message-background-color);
@@ -13344,7 +13592,7 @@ ___CSS_LOADER_EXPORT___.push([module.id, `/*
13344
13592
 
13345
13593
  .context-button:disabled:hover {
13346
13594
  background-color: var(--jp-layout-color2);
13347
- }`, "",{"version":3,"sources":["webpack://./style/ChatInput.css"],"names":[],"mappings":"AAAA;;;EAGE;;AAEF;EACE,gBAAgB;EAChB,SAAS;EACT,cAAc;EACd,gBAAgB;EAChB,2DAA2D;EAC3D;;;iCAG+B;EAC/B,WAAW;EACX,mBAAmB;EACnB,iDAAiD;AACnD;;AAEA;EACE,kBAAkB;EAClB,mBAAmB;AACrB;;AAEA;EACE,aAAa;EACb,YAAY;EACZ,YAAY;EACZ,WAAW;EACX,aAAa;EACb,kBAAkB;EAClB,sBAAsB;EACtB,yBAAyB;EACzB,2DAA2D;EAC3D,eAAe;EACf,gBAAgB;EAChB,qCAAqC;EACrC,gBAAgB;EAChB,iDAAiD;AACnD;;AAEA;EACE,aAAa;AACf;;AAEA;EACE,2BAA2B;EAC3B;;;iCAG+B;AACjC;;AAEA;EACE,+BAA+B;AACjC;;AAEA;EACE,+BAA+B;EAC/B,2BAA2B;AAC7B;;AAEA;EACE,+BAA+B;AACjC;;AAEA;EACE,aAAa;EACb,mBAAmB;EACnB,yCAAyC;EACzC,yCAAyC;EACzC,kBAAkB;EAClB,gBAAgB;EAChB,eAAe;EACf,yCAAyC;EACzC,eAAe;EACf,oCAAoC;EACpC,sCAAsC;AACxC;;AAEA;EACE,yCAAyC;AAC3C;;AAEA,0BAA0B;AAC1B;EACE,YAAY;EACZ,mBAAmB;EACnB,yCAAyC;AAC3C;;AAEA;EACE,YAAY;EACZ,mBAAmB;EACnB,yCAAyC;AAC3C;;AAEA;EACE,yCAAyC;AAC3C","sourcesContent":["/*\n * Copyright (c) Saga Inc.\n * Distributed under the terms of the GNU Affero General Public License v3.0 License.\n */\n\n.chat-input-container {\n position: sticky;\n bottom: 0;\n margin: 10px 0;\n margin-top: auto;\n background-color: var(--chat-user-message-background-color);\n box-shadow: \n 0 4px 12px rgba(0, 0, 0, 0.08),\n 0 2px 4px rgba(0, 0, 0, 0.06),\n 0 0 0 1px rgba(0, 0, 0, 0.02);\n width: 100%;\n border-radius: 12px;\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n.chat-input-text-area-container {\n position: relative;\n height: min-content;\n}\n\n.chat-input {\n outline: none;\n border: none;\n resize: none;\n width: 100%;\n padding: 10px;\n overflow-y: hidden;\n box-sizing: border-box;\n flex-shrink: 0 !important;\n background-color: var(--chat-user-message-background-color);\n font-size: 12px;\n font-weight: 400;\n font-family: var(--jp-ui-font-family);\n line-height: 1.2;\n transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n.active-cell-preview-container {\n padding: 10px;\n}\n\n.chat-input-container:focus-within {\n transform: translateY(-1px);\n box-shadow: \n 0 2px 8px rgba(0, 0, 0, 0.12),\n 0 8px 16px rgba(0, 0, 0, 0.08),\n 0 0 0 1px rgba(0, 0, 0, 0.04);\n}\n\n.chat-input:focus {\n color: var(--jp-ui-font-color1);\n}\n\n.chat-input::placeholder {\n color: var(--jp-ui-font-color2);\n transition: color 0.2s ease;\n}\n\n.chat-input:focus::placeholder {\n color: var(--jp-ui-font-color3);\n}\n\n.context-button {\n display: flex;\n align-items: center;\n background-color: var(--jp-layout-color2);\n border: 1px solid var(--jp-border-color1);\n border-radius: 3px;\n padding: 4px 8px;\n font-size: 12px;\n height: var(--chat-context-button-height);\n cursor: pointer;\n color: var(--jp-content-font-color1);\n transition: background-color 0.2s ease;\n}\n\n.context-button:hover {\n background-color: var(--jp-layout-color3);\n}\n\n/* Disabled state styles */\n.chat-input:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n background-color: var(--jp-layout-color2);\n}\n\n.context-button:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n background-color: var(--jp-layout-color2);\n}\n\n.context-button:disabled:hover {\n background-color: var(--jp-layout-color2);\n}"],"sourceRoot":""}]);
13595
+ }`, "",{"version":3,"sources":["webpack://./style/ChatInput.css"],"names":[],"mappings":"AAAA;;;EAGE;;AAEF;EACE,gBAAgB;EAChB,SAAS;EACT,cAAc;EACd,gBAAgB;EAChB,2DAA2D;EAC3D;;;iCAG+B;EAC/B,WAAW;EACX,mBAAmB;EACnB,iDAAiD;AACnD;;AAEA;EACE,aAAa;AACf;;AAEA;EACE,kBAAkB;EAClB,mBAAmB;AACrB;;AAEA;EACE,aAAa;EACb,YAAY;EACZ,YAAY;EACZ,WAAW;EACX,aAAa;EACb,gBAAgB;EAChB,sBAAsB;EACtB,yBAAyB;EACzB,2DAA2D;EAC3D,eAAe;EACf,gBAAgB;EAChB,qCAAqC;EACrC,gBAAgB;EAChB,iDAAiD;AACnD;;AAEA;EACE,aAAa;AACf;;AAEA;EACE,2BAA2B;EAC3B;;;iCAG+B;AACjC;;AAEA;EACE,+BAA+B;AACjC;;AAEA;EACE,+BAA+B;EAC/B,2BAA2B;AAC7B;;AAEA;EACE,+BAA+B;AACjC;;AAEA;EACE,aAAa;EACb,mBAAmB;EACnB,yCAAyC;EACzC,yCAAyC;EACzC,kBAAkB;EAClB,gBAAgB;EAChB,eAAe;EACf,yCAAyC;EACzC,eAAe;EACf,oCAAoC;EACpC,sCAAsC;AACxC;;AAEA;EACE,yCAAyC;AAC3C;;AAEA,0BAA0B;AAC1B;EACE,YAAY;EACZ,mBAAmB;EACnB,yCAAyC;AAC3C;;AAEA;EACE,YAAY;EACZ,mBAAmB;EACnB,yCAAyC;AAC3C;;AAEA;EACE,yCAAyC;AAC3C","sourcesContent":["/*\n * Copyright (c) Saga Inc.\n * Distributed under the terms of the GNU Affero General Public License v3.0 License.\n */\n\n.chat-input-container {\n position: sticky;\n bottom: 0;\n margin: 10px 0;\n margin-top: auto;\n background-color: var(--chat-user-message-background-color);\n box-shadow: \n 0 4px 12px rgba(0, 0, 0, 0.08),\n 0 2px 4px rgba(0, 0, 0, 0.06),\n 0 0 0 1px rgba(0, 0, 0, 0.02);\n width: 100%;\n border-radius: 12px;\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n.chat-input-container.editing {\n margin-top: 0;\n}\n\n.chat-input-text-area-container {\n position: relative;\n height: min-content;\n}\n\n.chat-input {\n outline: none;\n border: none;\n resize: none;\n width: 100%;\n padding: 10px;\n overflow-y: auto;\n box-sizing: border-box;\n flex-shrink: 0 !important;\n background-color: var(--chat-user-message-background-color);\n font-size: 12px;\n font-weight: 400;\n font-family: var(--jp-ui-font-family);\n line-height: 1.2;\n transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n.active-cell-preview-container {\n padding: 10px;\n}\n\n.chat-input-container:focus-within {\n transform: translateY(-1px);\n box-shadow: \n 0 2px 8px rgba(0, 0, 0, 0.12),\n 0 8px 16px rgba(0, 0, 0, 0.08),\n 0 0 0 1px rgba(0, 0, 0, 0.04);\n}\n\n.chat-input:focus {\n color: var(--jp-ui-font-color1);\n}\n\n.chat-input::placeholder {\n color: var(--jp-ui-font-color2);\n transition: color 0.2s ease;\n}\n\n.chat-input:focus::placeholder {\n color: var(--jp-ui-font-color3);\n}\n\n.context-button {\n display: flex;\n align-items: center;\n background-color: var(--jp-layout-color2);\n border: 1px solid var(--jp-border-color1);\n border-radius: 3px;\n padding: 4px 8px;\n font-size: 12px;\n height: var(--chat-context-button-height);\n cursor: pointer;\n color: var(--jp-content-font-color1);\n transition: background-color 0.2s ease;\n}\n\n.context-button:hover {\n background-color: var(--jp-layout-color3);\n}\n\n/* Disabled state styles */\n.chat-input:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n background-color: var(--jp-layout-color2);\n}\n\n.context-button:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n background-color: var(--jp-layout-color2);\n}\n\n.context-button:disabled:hover {\n background-color: var(--jp-layout-color2);\n}"],"sourceRoot":""}]);
13348
13596
  // Exports
13349
13597
  /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);
13350
13598
 
@@ -14617,6 +14865,142 @@ ___CSS_LOADER_EXPORT___.push([module.id, `/*
14617
14865
  /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);
14618
14866
 
14619
14867
 
14868
+ /***/ }),
14869
+
14870
+ /***/ "./node_modules/css-loader/dist/cjs.js!./style/FilesSelector.css":
14871
+ /*!***********************************************************************!*\
14872
+ !*** ./node_modules/css-loader/dist/cjs.js!./style/FilesSelector.css ***!
14873
+ \***********************************************************************/
14874
+ /***/ ((module, __webpack_exports__, __webpack_require__) => {
14875
+
14876
+ __webpack_require__.r(__webpack_exports__);
14877
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
14878
+ /* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__)
14879
+ /* harmony export */ });
14880
+ /* harmony import */ var _node_modules_css_loader_dist_runtime_sourceMaps_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../node_modules/css-loader/dist/runtime/sourceMaps.js */ "./node_modules/css-loader/dist/runtime/sourceMaps.js");
14881
+ /* harmony import */ var _node_modules_css_loader_dist_runtime_sourceMaps_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_sourceMaps_js__WEBPACK_IMPORTED_MODULE_0__);
14882
+ /* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../node_modules/css-loader/dist/runtime/api.js */ "./node_modules/css-loader/dist/runtime/api.js");
14883
+ /* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);
14884
+ // Imports
14885
+
14886
+
14887
+ var ___CSS_LOADER_EXPORT___ = _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_node_modules_css_loader_dist_runtime_sourceMaps_js__WEBPACK_IMPORTED_MODULE_0___default()));
14888
+ // Module
14889
+ ___CSS_LOADER_EXPORT___.push([module.id, `/*
14890
+ * Copyright (c) Saga Inc.
14891
+ * Distributed under the terms of the GNU Affero General Public License v3.0 License.
14892
+ */
14893
+
14894
+
14895
+ .file-list {
14896
+ list-style: none;
14897
+ padding-left: 0;
14898
+ margin: 0;
14899
+ }
14900
+
14901
+ .file-list li {
14902
+ margin-bottom: 6px;
14903
+ }
14904
+
14905
+ .modal-footer {
14906
+ margin-top: 16px;
14907
+ display: flex;
14908
+ justify-content: flex-end;
14909
+ }
14910
+
14911
+ .files-selector-submit-button {
14912
+ margin-top: 8px;
14913
+ background-color: var(--purple-400) !important;
14914
+ color: var(--purple-700) !important;
14915
+ border: none !important;
14916
+ border-radius: 4px !important;
14917
+ font-weight: 500 !important;
14918
+ padding: 8px 16px !important;
14919
+ width: 100% !important;
14920
+ max-width: 100% !important;
14921
+ box-sizing: border-box !important;
14922
+ }
14923
+
14924
+ .files-selector-submit-button:hover {
14925
+ background-color: var(--purple-500) !important;
14926
+ }
14927
+
14928
+ .file-list input[type="checkbox"],
14929
+ .select-all input[type="checkbox"]{
14930
+ transform: scale(1.3);
14931
+ margin-right: 6px;
14932
+ accent-color: var(--purple-500);
14933
+ }
14934
+
14935
+ .file-list svg {
14936
+ color: grey;
14937
+ margin-right: 4px;
14938
+ vertical-align: middle;
14939
+ }
14940
+
14941
+ .select-all label {
14942
+ font-weight: 500;
14943
+ display: flex;
14944
+ align-items: center;
14945
+ gap: 6px;
14946
+ }
14947
+
14948
+ /* Modal header */
14949
+ .modal-header {
14950
+ display: flex;
14951
+ align-items: center;
14952
+ justify-content: space-between;
14953
+ gap: 8px;
14954
+ margin-bottom: 1px;
14955
+ }
14956
+
14957
+ .modal-title {
14958
+ margin: 0;
14959
+ font-size: 1.2rem;
14960
+ }
14961
+
14962
+
14963
+ .modal-subtext {
14964
+ font-size: 0.7rem;
14965
+ color: #555;
14966
+ margin: 0;
14967
+ font-style: italic;
14968
+ }
14969
+
14970
+ .modal-subheader{
14971
+ margin-bottom: 7px;
14972
+ }
14973
+
14974
+ /* Checkbox label for spacing */
14975
+ .checkbox-label {
14976
+ display: flex;
14977
+ align-items: center;
14978
+ gap: 6px;
14979
+ }
14980
+
14981
+ /* Scrollable file list */
14982
+ .file-list-scrollable {
14983
+ max-height: 300px;
14984
+ overflow-y: auto;
14985
+ margin-top: 8px;
14986
+ }
14987
+
14988
+ /* File list items */
14989
+ .file-list {
14990
+ list-style: none;
14991
+ padding-left: 0;
14992
+ margin: 0;
14993
+ }
14994
+
14995
+ /* Select all section */
14996
+ .files-selector-select-all {
14997
+ margin-bottom: 8px;
14998
+ }
14999
+ `, "",{"version":3,"sources":["webpack://./style/FilesSelector.css"],"names":[],"mappings":"AAAA;;;EAGE;;;AAGF;EACE,gBAAgB;EAChB,eAAe;EACf,SAAS;AACX;;AAEA;EACE,kBAAkB;AACpB;;AAEA;EACE,gBAAgB;EAChB,aAAa;EACb,yBAAyB;AAC3B;;AAEA;EACE,eAAe;EACf,8CAA8C;EAC9C,mCAAmC;EACnC,uBAAuB;EACvB,6BAA6B;EAC7B,2BAA2B;EAC3B,4BAA4B;EAC5B,sBAAsB;EACtB,0BAA0B;EAC1B,iCAAiC;AACnC;;AAEA;EACE,8CAA8C;AAChD;;AAEA;;EAEE,qBAAqB;EACrB,iBAAiB;EACjB,+BAA+B;AACjC;;AAEA;EACE,WAAW;EACX,iBAAiB;EACjB,sBAAsB;AACxB;;AAEA;EACE,gBAAgB;EAChB,aAAa;EACb,mBAAmB;EACnB,QAAQ;AACV;;AAEA,iBAAiB;AACjB;EACE,aAAa;EACb,mBAAmB;EACnB,8BAA8B;EAC9B,QAAQ;EACR,kBAAkB;AACpB;;AAEA;EACE,SAAS;EACT,iBAAiB;AACnB;;;AAGA;EACE,iBAAiB;EACjB,WAAW;EACX,SAAS;EACT,kBAAkB;AACpB;;AAEA;EACE,kBAAkB;AACpB;;AAEA,+BAA+B;AAC/B;EACE,aAAa;EACb,mBAAmB;EACnB,QAAQ;AACV;;AAEA,yBAAyB;AACzB;EACE,iBAAiB;EACjB,gBAAgB;EAChB,eAAe;AACjB;;AAEA,oBAAoB;AACpB;EACE,gBAAgB;EAChB,eAAe;EACf,SAAS;AACX;;AAEA,uBAAuB;AACvB;EACE,kBAAkB;AACpB","sourcesContent":["/*\n * Copyright (c) Saga Inc.\n * Distributed under the terms of the GNU Affero General Public License v3.0 License.\n */\n\n\n.file-list {\n list-style: none;\n padding-left: 0;\n margin: 0;\n}\n\n.file-list li {\n margin-bottom: 6px;\n}\n\n.modal-footer {\n margin-top: 16px;\n display: flex;\n justify-content: flex-end;\n}\n\n.files-selector-submit-button {\n margin-top: 8px;\n background-color: var(--purple-400) !important;\n color: var(--purple-700) !important;\n border: none !important;\n border-radius: 4px !important;\n font-weight: 500 !important;\n padding: 8px 16px !important;\n width: 100% !important;\n max-width: 100% !important;\n box-sizing: border-box !important;\n}\n\n.files-selector-submit-button:hover {\n background-color: var(--purple-500) !important;\n}\n\n.file-list input[type=\"checkbox\"],\n.select-all input[type=\"checkbox\"]{\n transform: scale(1.3);\n margin-right: 6px;\n accent-color: var(--purple-500);\n}\n\n.file-list svg {\n color: grey;\n margin-right: 4px;\n vertical-align: middle;\n}\n\n.select-all label {\n font-weight: 500;\n display: flex;\n align-items: center;\n gap: 6px;\n}\n\n/* Modal header */\n.modal-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n margin-bottom: 1px;\n}\n\n.modal-title {\n margin: 0;\n font-size: 1.2rem;\n}\n\n\n.modal-subtext {\n font-size: 0.7rem;\n color: #555;\n margin: 0;\n font-style: italic;\n}\n\n.modal-subheader{\n margin-bottom: 7px;\n}\n\n/* Checkbox label for spacing */\n.checkbox-label {\n display: flex;\n align-items: center;\n gap: 6px;\n}\n\n/* Scrollable file list */\n.file-list-scrollable {\n max-height: 300px;\n overflow-y: auto;\n margin-top: 8px;\n}\n\n/* File list items */\n.file-list {\n list-style: none;\n padding-left: 0;\n margin: 0;\n}\n\n/* Select all section */\n.files-selector-select-all {\n margin-bottom: 8px;\n}\n"],"sourceRoot":""}]);
15000
+ // Exports
15001
+ /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);
15002
+
15003
+
14620
15004
  /***/ }),
14621
15005
 
14622
15006
  /***/ "./node_modules/css-loader/dist/cjs.js!./style/IconButton.css":
@@ -17383,6 +17767,60 @@ var update = _node_modules_style_loader_dist_runtime_injectStylesIntoStyleTag_js
17383
17767
  /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (_node_modules_css_loader_dist_cjs_js_ErrorMimeRendererPlugin_css__WEBPACK_IMPORTED_MODULE_6__["default"] && _node_modules_css_loader_dist_cjs_js_ErrorMimeRendererPlugin_css__WEBPACK_IMPORTED_MODULE_6__["default"].locals ? _node_modules_css_loader_dist_cjs_js_ErrorMimeRendererPlugin_css__WEBPACK_IMPORTED_MODULE_6__["default"].locals : undefined);
17384
17768
 
17385
17769
 
17770
+ /***/ }),
17771
+
17772
+ /***/ "./style/FilesSelector.css":
17773
+ /*!*********************************!*\
17774
+ !*** ./style/FilesSelector.css ***!
17775
+ \*********************************/
17776
+ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
17777
+
17778
+ __webpack_require__.r(__webpack_exports__);
17779
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
17780
+ /* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__)
17781
+ /* harmony export */ });
17782
+ /* harmony import */ var _node_modules_style_loader_dist_runtime_injectStylesIntoStyleTag_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! !../node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js */ "./node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js");
17783
+ /* harmony import */ var _node_modules_style_loader_dist_runtime_injectStylesIntoStyleTag_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_node_modules_style_loader_dist_runtime_injectStylesIntoStyleTag_js__WEBPACK_IMPORTED_MODULE_0__);
17784
+ /* harmony import */ var _node_modules_style_loader_dist_runtime_styleDomAPI_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! !../node_modules/style-loader/dist/runtime/styleDomAPI.js */ "./node_modules/style-loader/dist/runtime/styleDomAPI.js");
17785
+ /* harmony import */ var _node_modules_style_loader_dist_runtime_styleDomAPI_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_node_modules_style_loader_dist_runtime_styleDomAPI_js__WEBPACK_IMPORTED_MODULE_1__);
17786
+ /* harmony import */ var _node_modules_style_loader_dist_runtime_insertBySelector_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! !../node_modules/style-loader/dist/runtime/insertBySelector.js */ "./node_modules/style-loader/dist/runtime/insertBySelector.js");
17787
+ /* harmony import */ var _node_modules_style_loader_dist_runtime_insertBySelector_js__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(_node_modules_style_loader_dist_runtime_insertBySelector_js__WEBPACK_IMPORTED_MODULE_2__);
17788
+ /* harmony import */ var _node_modules_style_loader_dist_runtime_setAttributesWithoutAttributes_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! !../node_modules/style-loader/dist/runtime/setAttributesWithoutAttributes.js */ "./node_modules/style-loader/dist/runtime/setAttributesWithoutAttributes.js");
17789
+ /* harmony import */ var _node_modules_style_loader_dist_runtime_setAttributesWithoutAttributes_js__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(_node_modules_style_loader_dist_runtime_setAttributesWithoutAttributes_js__WEBPACK_IMPORTED_MODULE_3__);
17790
+ /* harmony import */ var _node_modules_style_loader_dist_runtime_insertStyleElement_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! !../node_modules/style-loader/dist/runtime/insertStyleElement.js */ "./node_modules/style-loader/dist/runtime/insertStyleElement.js");
17791
+ /* harmony import */ var _node_modules_style_loader_dist_runtime_insertStyleElement_js__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(_node_modules_style_loader_dist_runtime_insertStyleElement_js__WEBPACK_IMPORTED_MODULE_4__);
17792
+ /* harmony import */ var _node_modules_style_loader_dist_runtime_styleTagTransform_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! !../node_modules/style-loader/dist/runtime/styleTagTransform.js */ "./node_modules/style-loader/dist/runtime/styleTagTransform.js");
17793
+ /* harmony import */ var _node_modules_style_loader_dist_runtime_styleTagTransform_js__WEBPACK_IMPORTED_MODULE_5___default = /*#__PURE__*/__webpack_require__.n(_node_modules_style_loader_dist_runtime_styleTagTransform_js__WEBPACK_IMPORTED_MODULE_5__);
17794
+ /* harmony import */ var _node_modules_css_loader_dist_cjs_js_FilesSelector_css__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! !!../node_modules/css-loader/dist/cjs.js!./FilesSelector.css */ "./node_modules/css-loader/dist/cjs.js!./style/FilesSelector.css");
17795
+
17796
+
17797
+
17798
+
17799
+
17800
+
17801
+
17802
+
17803
+
17804
+
17805
+
17806
+ var options = {};
17807
+
17808
+ options.styleTagTransform = (_node_modules_style_loader_dist_runtime_styleTagTransform_js__WEBPACK_IMPORTED_MODULE_5___default());
17809
+ options.setAttributes = (_node_modules_style_loader_dist_runtime_setAttributesWithoutAttributes_js__WEBPACK_IMPORTED_MODULE_3___default());
17810
+
17811
+ options.insert = _node_modules_style_loader_dist_runtime_insertBySelector_js__WEBPACK_IMPORTED_MODULE_2___default().bind(null, "head");
17812
+
17813
+ options.domAPI = (_node_modules_style_loader_dist_runtime_styleDomAPI_js__WEBPACK_IMPORTED_MODULE_1___default());
17814
+ options.insertStyleElement = (_node_modules_style_loader_dist_runtime_insertStyleElement_js__WEBPACK_IMPORTED_MODULE_4___default());
17815
+
17816
+ var update = _node_modules_style_loader_dist_runtime_injectStylesIntoStyleTag_js__WEBPACK_IMPORTED_MODULE_0___default()(_node_modules_css_loader_dist_cjs_js_FilesSelector_css__WEBPACK_IMPORTED_MODULE_6__["default"], options);
17817
+
17818
+
17819
+
17820
+
17821
+ /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (_node_modules_css_loader_dist_cjs_js_FilesSelector_css__WEBPACK_IMPORTED_MODULE_6__["default"] && _node_modules_css_loader_dist_cjs_js_FilesSelector_css__WEBPACK_IMPORTED_MODULE_6__["default"].locals ? _node_modules_css_loader_dist_cjs_js_FilesSelector_css__WEBPACK_IMPORTED_MODULE_6__["default"].locals : undefined);
17822
+
17823
+
17386
17824
  /***/ }),
17387
17825
 
17388
17826
  /***/ "./style/IconButton.css":
@@ -18250,4 +18688,4 @@ var update = _node_modules_style_loader_dist_runtime_injectStylesIntoStyleTag_js
18250
18688
  /***/ })
18251
18689
 
18252
18690
  }]);
18253
- //# sourceMappingURL=lib_index_js.cf2e3ad2797fbb53826b.js.map
18691
+ //# sourceMappingURL=lib_index_js.0c3368195d954d2ed033.js.map