gwchq-textjam 0.3.1 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -64833,6 +64833,35 @@ function SvgButtonDots(props) {
64833
64833
 
64834
64834
  /***/ }),
64835
64835
 
64836
+ /***/ 62284:
64837
+ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
64838
+
64839
+ __webpack_require__.r(__webpack_exports__);
64840
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
64841
+ /* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__)
64842
+ /* harmony export */ });
64843
+ /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(51649);
64844
+ var _path;
64845
+ function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); }
64846
+
64847
+ function SvgCheckbox(props) {
64848
+ return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__.createElement("svg", _extends({
64849
+ width: 8,
64850
+ height: 6,
64851
+ fill: "none",
64852
+ xmlns: "http://www.w3.org/2000/svg"
64853
+ }, props), _path || (_path = /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__.createElement("path", {
64854
+ d: "M.75 2.75l2 2 4-4",
64855
+ stroke: "#33625E",
64856
+ strokeWidth: 1.5,
64857
+ strokeLinecap: "round",
64858
+ strokeLinejoin: "round"
64859
+ })));
64860
+ }
64861
+ /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (SvgCheckbox);
64862
+
64863
+ /***/ }),
64864
+
64836
64865
  /***/ 16882:
64837
64866
  /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
64838
64867
 
@@ -65494,6 +65523,35 @@ function SvgMinusCircle(props) {
65494
65523
 
65495
65524
  /***/ }),
65496
65525
 
65526
+ /***/ 36311:
65527
+ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
65528
+
65529
+ __webpack_require__.r(__webpack_exports__);
65530
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
65531
+ /* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__)
65532
+ /* harmony export */ });
65533
+ /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(51649);
65534
+ var _path;
65535
+ function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); }
65536
+
65537
+ function SvgMonitor(props) {
65538
+ return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__.createElement("svg", _extends({
65539
+ width: 32,
65540
+ height: 32,
65541
+ fill: "none",
65542
+ xmlns: "http://www.w3.org/2000/svg"
65543
+ }, props), _path || (_path = /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__.createElement("path", {
65544
+ d: "M10.8 28h10.4M16 22.667V28M5.6 4h20.8C27.836 4 29 5.194 29 6.667V20c0 1.473-1.164 2.667-2.6 2.667H5.6C4.164 22.667 3 21.473 3 20V6.667C3 5.194 4.164 4 5.6 4z",
65545
+ stroke: "#003046",
65546
+ strokeWidth: 2,
65547
+ strokeLinecap: "round",
65548
+ strokeLinejoin: "round"
65549
+ })));
65550
+ }
65551
+ /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (SvgMonitor);
65552
+
65553
+ /***/ }),
65554
+
65497
65555
  /***/ 97512:
65498
65556
  /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
65499
65557
 
@@ -68348,8 +68406,8 @@ __webpack_require__.r(__webpack_exports__);
68348
68406
  /* harmony export */ __webpack_require__.d(__webpack_exports__, {
68349
68407
  /* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__)
68350
68408
  /* harmony export */ });
68351
- /* harmony import */ var D_gwc_gwchq_textjam_node_modules_babel_runtime_helpers_esm_objectSpread2_js__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(89379);
68352
- /* harmony import */ var D_gwc_gwchq_textjam_node_modules_babel_runtime_helpers_esm_objectWithoutProperties_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(53986);
68409
+ /* harmony import */ var C_Project_source_gwchq_textjam_node_modules_babel_runtime_helpers_esm_objectSpread2_js__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(89379);
68410
+ /* harmony import */ var C_Project_source_gwchq_textjam_node_modules_babel_runtime_helpers_esm_objectWithoutProperties_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(53986);
68353
68411
  /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(51649);
68354
68412
  /* harmony import */ var _hello_pangea_dnd__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(98850);
68355
68413
  /* harmony import */ var react_redux__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(14062);
@@ -68373,7 +68431,7 @@ var DraggableTab = _ref => {
68373
68431
  panelIndex,
68374
68432
  fileIndex
68375
68433
  } = _ref,
68376
- otherProps = (0,D_gwc_gwchq_textjam_node_modules_babel_runtime_helpers_esm_objectWithoutProperties_js__WEBPACK_IMPORTED_MODULE_5__/* ["default"] */ .A)(_ref, _excluded);
68434
+ otherProps = (0,C_Project_source_gwchq_textjam_node_modules_babel_runtime_helpers_esm_objectWithoutProperties_js__WEBPACK_IMPORTED_MODULE_5__/* ["default"] */ .A)(_ref, _excluded);
68377
68435
  var openFiles = (0,react_redux__WEBPACK_IMPORTED_MODULE_1__.useSelector)(state => state.editor.openedFiles);
68378
68436
  var openFilesCount = openFiles[panelIndex].length;
68379
68437
  var dispatch = (0,react_redux__WEBPACK_IMPORTED_MODULE_1__.useDispatch)();
@@ -68390,7 +68448,7 @@ var DraggableTab = _ref => {
68390
68448
  switchToFileTab(panelIndex, (fileIndex + openFilesCount - 1) % openFilesCount);
68391
68449
  }
68392
68450
  };
68393
- var InnerTab = () => /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsx)(react_tabs__WEBPACK_IMPORTED_MODULE_2__.Tab, (0,D_gwc_gwchq_textjam_node_modules_babel_runtime_helpers_esm_objectSpread2_js__WEBPACK_IMPORTED_MODULE_7__/* ["default"] */ .A)((0,D_gwc_gwchq_textjam_node_modules_babel_runtime_helpers_esm_objectSpread2_js__WEBPACK_IMPORTED_MODULE_7__/* ["default"] */ .A)({
68451
+ var InnerTab = () => /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsx)(react_tabs__WEBPACK_IMPORTED_MODULE_2__.Tab, (0,C_Project_source_gwchq_textjam_node_modules_babel_runtime_helpers_esm_objectSpread2_js__WEBPACK_IMPORTED_MODULE_7__/* ["default"] */ .A)((0,C_Project_source_gwchq_textjam_node_modules_babel_runtime_helpers_esm_objectSpread2_js__WEBPACK_IMPORTED_MODULE_7__/* ["default"] */ .A)({
68394
68452
  onClick: e => {
68395
68453
  e.stopPropagation();
68396
68454
  switchToFileTab(panelIndex, fileIndex);
@@ -68408,7 +68466,7 @@ var DraggableTab = _ref => {
68408
68466
  draggableProps,
68409
68467
  dragHandleProps
68410
68468
  } = _ref2;
68411
- return /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsx)("div", (0,D_gwc_gwchq_textjam_node_modules_babel_runtime_helpers_esm_objectSpread2_js__WEBPACK_IMPORTED_MODULE_7__/* ["default"] */ .A)((0,D_gwc_gwchq_textjam_node_modules_babel_runtime_helpers_esm_objectSpread2_js__WEBPACK_IMPORTED_MODULE_7__/* ["default"] */ .A)((0,D_gwc_gwchq_textjam_node_modules_babel_runtime_helpers_esm_objectSpread2_js__WEBPACK_IMPORTED_MODULE_7__/* ["default"] */ .A)({
68469
+ return /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsx)("div", (0,C_Project_source_gwchq_textjam_node_modules_babel_runtime_helpers_esm_objectSpread2_js__WEBPACK_IMPORTED_MODULE_7__/* ["default"] */ .A)((0,C_Project_source_gwchq_textjam_node_modules_babel_runtime_helpers_esm_objectSpread2_js__WEBPACK_IMPORTED_MODULE_7__/* ["default"] */ .A)((0,C_Project_source_gwchq_textjam_node_modules_babel_runtime_helpers_esm_objectSpread2_js__WEBPACK_IMPORTED_MODULE_7__/* ["default"] */ .A)({
68412
68470
  className: "draggable-tab",
68413
68471
  ref: innerRef
68414
68472
  }, draggableProps), dragHandleProps), {}, {
@@ -68430,8 +68488,8 @@ __webpack_require__.r(__webpack_exports__);
68430
68488
  /* harmony export */ __webpack_require__.d(__webpack_exports__, {
68431
68489
  /* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__)
68432
68490
  /* harmony export */ });
68433
- /* harmony import */ var D_gwc_gwchq_textjam_node_modules_babel_runtime_helpers_esm_objectSpread2_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(89379);
68434
- /* harmony import */ var D_gwc_gwchq_textjam_node_modules_babel_runtime_helpers_esm_objectWithoutProperties_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(53986);
68491
+ /* harmony import */ var C_Project_source_gwchq_textjam_node_modules_babel_runtime_helpers_esm_objectSpread2_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(89379);
68492
+ /* harmony import */ var C_Project_source_gwchq_textjam_node_modules_babel_runtime_helpers_esm_objectWithoutProperties_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(53986);
68435
68493
  /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(51649);
68436
68494
  /* harmony import */ var _hello_pangea_dnd__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(98850);
68437
68495
  /* harmony import */ var react_tabs__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(39243);
@@ -68450,8 +68508,8 @@ var DroppableTabList = _ref => {
68450
68508
  children: _children,
68451
68509
  index
68452
68510
  } = _ref,
68453
- otherProps = (0,D_gwc_gwchq_textjam_node_modules_babel_runtime_helpers_esm_objectWithoutProperties_js__WEBPACK_IMPORTED_MODULE_4__/* ["default"] */ .A)(_ref, _excluded);
68454
- return /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_3__.jsx)(react_tabs__WEBPACK_IMPORTED_MODULE_1__.TabList, (0,D_gwc_gwchq_textjam_node_modules_babel_runtime_helpers_esm_objectSpread2_js__WEBPACK_IMPORTED_MODULE_5__/* ["default"] */ .A)((0,D_gwc_gwchq_textjam_node_modules_babel_runtime_helpers_esm_objectSpread2_js__WEBPACK_IMPORTED_MODULE_5__/* ["default"] */ .A)({}, otherProps), {}, {
68511
+ otherProps = (0,C_Project_source_gwchq_textjam_node_modules_babel_runtime_helpers_esm_objectWithoutProperties_js__WEBPACK_IMPORTED_MODULE_4__/* ["default"] */ .A)(_ref, _excluded);
68512
+ return /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_3__.jsx)(react_tabs__WEBPACK_IMPORTED_MODULE_1__.TabList, (0,C_Project_source_gwchq_textjam_node_modules_babel_runtime_helpers_esm_objectSpread2_js__WEBPACK_IMPORTED_MODULE_5__/* ["default"] */ .A)((0,C_Project_source_gwchq_textjam_node_modules_babel_runtime_helpers_esm_objectSpread2_js__WEBPACK_IMPORTED_MODULE_5__/* ["default"] */ .A)({}, otherProps), {}, {
68455
68513
  children: /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_3__.jsx)(_hello_pangea_dnd__WEBPACK_IMPORTED_MODULE_6__.Droppable, {
68456
68514
  direction: "horizontal",
68457
68515
  droppableId: index.toString(),
@@ -68461,7 +68519,7 @@ var DroppableTabList = _ref => {
68461
68519
  droppableProps,
68462
68520
  placeholder
68463
68521
  } = _ref2;
68464
- return /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_3__.jsxs)("div", (0,D_gwc_gwchq_textjam_node_modules_babel_runtime_helpers_esm_objectSpread2_js__WEBPACK_IMPORTED_MODULE_5__/* ["default"] */ .A)((0,D_gwc_gwchq_textjam_node_modules_babel_runtime_helpers_esm_objectSpread2_js__WEBPACK_IMPORTED_MODULE_5__/* ["default"] */ .A)({
68522
+ return /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_3__.jsxs)("div", (0,C_Project_source_gwchq_textjam_node_modules_babel_runtime_helpers_esm_objectSpread2_js__WEBPACK_IMPORTED_MODULE_5__/* ["default"] */ .A)((0,C_Project_source_gwchq_textjam_node_modules_babel_runtime_helpers_esm_objectSpread2_js__WEBPACK_IMPORTED_MODULE_5__/* ["default"] */ .A)({
68465
68523
  className: "droppable-tab-list"
68466
68524
  }, droppableProps), {}, {
68467
68525
  ref: innerRef,
@@ -101805,8 +101863,10 @@ var editorLightTheme = EditorView.theme({
101805
101863
  var utils_settings = __webpack_require__(62161);
101806
101864
  // EXTERNAL MODULE: ./src/redux/stores/index.ts
101807
101865
  var stores = __webpack_require__(32132);
101808
- // EXTERNAL MODULE: ./src/components/AssetPreview/index.tsx
101809
- var AssetPreview = __webpack_require__(91078);
101866
+ // EXTERNAL MODULE: ./src/components/Editor/EditorStatistic/EditorStatistic.tsx
101867
+ var EditorStatistic = __webpack_require__(47272);
101868
+ // EXTERNAL MODULE: ./src/components/AssetPreviewPanel/index.tsx
101869
+ var AssetPreviewPanel = __webpack_require__(66360);
101810
101870
  ;// ./src/components/Editor/EditorPanel/EditorPanel.jsx
101811
101871
 
101812
101872
  /* eslint-disable react-hooks/exhaustive-deps */
@@ -101829,6 +101889,8 @@ var AssetPreview = __webpack_require__(91078);
101829
101889
 
101830
101890
 
101831
101891
 
101892
+
101893
+
101832
101894
 
101833
101895
 
101834
101896
 
@@ -101847,6 +101909,8 @@ var EditorPanel = _ref => {
101847
101909
  } = (0,es.useTranslation)();
101848
101910
  var settings = (0,external_react_.useContext)(utils_settings.SettingsContext);
101849
101911
  var [characterLimitExceeded, setCharacterLimitExceeded] = (0,external_react_.useState)(false);
101912
+ var [line, setLine] = (0,external_react_.useState)(0);
101913
+ var [position, setPosition] = (0,external_react_.useState)(0);
101850
101914
  var updateStoredProject = c(content => {
101851
101915
  dispatch((0,EditorSlice.updateProjectComponent)((0,objectSpread2/* default */.A)((0,objectSpread2/* default */.A)({}, file), {}, {
101852
101916
  content
@@ -101912,7 +101976,7 @@ var EditorPanel = _ref => {
101912
101976
  }, [file, cascadeUpdate, editorViewRef]);
101913
101977
  if (!file) return null;
101914
101978
  if (file.isBinary) {
101915
- return /*#__PURE__*/(0,jsx_runtime.jsx)(AssetPreview/* AssetPreview */.n, {
101979
+ return /*#__PURE__*/(0,jsx_runtime.jsx)(AssetPreviewPanel.AssetPreviewPanel, {
101916
101980
  file: file
101917
101981
  });
101918
101982
  }
@@ -101924,20 +101988,31 @@ var EditorPanel = _ref => {
101924
101988
  updateStoredProject(viewUpdate.state.doc.toString());
101925
101989
  }
101926
101990
  });
101927
- var getMode = () => {
101928
- switch (file.extension) {
101929
- case "html":
101930
- return html();
101931
- case "css":
101932
- return css();
101933
- case "py":
101934
- return python();
101935
- case "js":
101936
- return javascript();
101937
- default:
101938
- return html();
101991
+ var fileModes = {
101992
+ html: {
101993
+ title: "HTML",
101994
+ renderer: html()
101995
+ },
101996
+ css: {
101997
+ title: "CSS",
101998
+ renderer: css()
101999
+ },
102000
+ py: {
102001
+ title: "Python",
102002
+ renderer: python()
102003
+ },
102004
+ js: {
102005
+ title: "JavaScript",
102006
+ renderer: javascript()
101939
102007
  }
101940
102008
  };
102009
+ var getMode = () => {
102010
+ return !!fileModes[file.extension] ? fileModes[file.extension].renderer : fileModes["html"].renderer;
102011
+ };
102012
+ var getModeTitle = () => {
102013
+ return !!fileModes[file.extension] ? fileModes[file.extension].title : file.extension;
102014
+ };
102015
+
101941
102016
  // const isDarkMode =
101942
102017
  // cookies.theme === "dark" ||
101943
102018
  // (!cookies.theme &&
@@ -101946,11 +102021,24 @@ var EditorPanel = _ref => {
101946
102021
 
101947
102022
  // setting hardcode light theme for TextJam phase 1
101948
102023
  var editorTheme = editorLightTheme;
102024
+ var getCaretPosition = () => {
102025
+ var _editorViewRef$curren, _editorViewRef$curren2;
102026
+ var pos = (_editorViewRef$curren = editorViewRef.current) === null || _editorViewRef$curren === void 0 || (_editorViewRef$curren = _editorViewRef$curren.state) === null || _editorViewRef$curren === void 0 || (_editorViewRef$curren = _editorViewRef$curren.selection) === null || _editorViewRef$curren === void 0 || (_editorViewRef$curren = _editorViewRef$curren.main) === null || _editorViewRef$curren === void 0 ? void 0 : _editorViewRef$curren.head;
102027
+ var line = (_editorViewRef$curren2 = editorViewRef.current) === null || _editorViewRef$curren2 === void 0 ? void 0 : _editorViewRef$curren2.state.doc.lineAt(pos);
102028
+ setLine(line.number);
102029
+ setPosition(pos - line.from + 1);
102030
+ };
101949
102031
  return /*#__PURE__*/(0,jsx_runtime.jsxs)("div", {
101950
102032
  className: "editor-wrapper",
101951
102033
  children: [/*#__PURE__*/(0,jsx_runtime.jsx)("div", {
101952
102034
  className: "editor editor--".concat(settings.fontSize),
101953
- ref: editor
102035
+ ref: editor,
102036
+ onKeyUp: getCaretPosition,
102037
+ onClick: getCaretPosition
102038
+ }), /*#__PURE__*/(0,jsx_runtime.jsx)(EditorStatistic.EditorStatistic, {
102039
+ line: line,
102040
+ position: position,
102041
+ title: getModeTitle()
101954
102042
  }), characterLimitExceeded && /*#__PURE__*/(0,jsx_runtime.jsx)(er, {
101955
102043
  title: t("editorPanel.characterLimitError"),
101956
102044
  type: "error",
@@ -103015,7 +103103,7 @@ __webpack_require__.r(__webpack_exports__);
103015
103103
  /* harmony export */ __webpack_require__.d(__webpack_exports__, {
103016
103104
  /* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__)
103017
103105
  /* harmony export */ });
103018
- /* harmony import */ var D_gwc_gwchq_textjam_node_modules_babel_runtime_helpers_esm_objectSpread2_js__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(89379);
103106
+ /* harmony import */ var C_Project_source_gwchq_textjam_node_modules_babel_runtime_helpers_esm_objectSpread2_js__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(89379);
103019
103107
  /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(51649);
103020
103108
  /* harmony import */ var react_redux__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(14062);
103021
103109
  /* harmony import */ var _redux_EditorSlice__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(68512);
@@ -103129,7 +103217,7 @@ var ProjectName = _ref => {
103129
103217
  id: "project_name_label",
103130
103218
  className: _styles_module_scss__WEBPACK_IMPORTED_MODULE_3__["default"].projectLabel,
103131
103219
  children: "Project Name"
103132
- }), /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsxs)("div", (0,D_gwc_gwchq_textjam_node_modules_babel_runtime_helpers_esm_objectSpread2_js__WEBPACK_IMPORTED_MODULE_7__/* ["default"] */ .A)((0,D_gwc_gwchq_textjam_node_modules_babel_runtime_helpers_esm_objectSpread2_js__WEBPACK_IMPORTED_MODULE_7__/* ["default"] */ .A)({
103220
+ }), /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsxs)("div", (0,C_Project_source_gwchq_textjam_node_modules_babel_runtime_helpers_esm_objectSpread2_js__WEBPACK_IMPORTED_MODULE_7__/* ["default"] */ .A)((0,C_Project_source_gwchq_textjam_node_modules_babel_runtime_helpers_esm_objectSpread2_js__WEBPACK_IMPORTED_MODULE_7__/* ["default"] */ .A)({
103133
103221
  className: classnames__WEBPACK_IMPORTED_MODULE_2___default()(_styles_module_scss__WEBPACK_IMPORTED_MODULE_3__["default"].projectName, className)
103134
103222
  }, hoverProps), {}, {
103135
103223
  children: [/*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsx)((components_Tooltip_Tooltip__WEBPACK_IMPORTED_MODULE_8___default()), {
@@ -103147,6 +103235,7 @@ var ProjectName = _ref => {
103147
103235
  onKeyUp: handleKeyUp,
103148
103236
  value: name,
103149
103237
  readOnly: !isEditing,
103238
+ maxLength: 255,
103150
103239
  onChange: handleOnChange,
103151
103240
  onBlur: handleOnBlur,
103152
103241
  className: classnames__WEBPACK_IMPORTED_MODULE_2___default()(_styles_module_scss__WEBPACK_IMPORTED_MODULE_3__["default"].projectInput, {
@@ -103213,6 +103302,11 @@ var Button = __webpack_require__(79428);
103213
103302
  var external_react_redux_ = __webpack_require__(14062);
103214
103303
  // EXTERNAL MODULE: ./src/redux/EditorSlice.ts
103215
103304
  var EditorSlice = __webpack_require__(68512);
103305
+ // EXTERNAL MODULE: ./src/components/Tooltip/Tooltip.tsx
103306
+ var Tooltip = __webpack_require__(26982);
103307
+ var Tooltip_default = /*#__PURE__*/__webpack_require__.n(Tooltip);
103308
+ // EXTERNAL MODULE: ./src/hooks/useHover.ts
103309
+ var useHover = __webpack_require__(78556);
103216
103310
  // EXTERNAL MODULE: ./node_modules/react/jsx-runtime.js
103217
103311
  var jsx_runtime = __webpack_require__(74848);
103218
103312
  ;// ./src/components/RunButton/RunButton.jsx
@@ -103224,6 +103318,8 @@ var _excluded = ["className"];
103224
103318
 
103225
103319
 
103226
103320
 
103321
+
103322
+
103227
103323
  var RunButton = _ref => {
103228
103324
  var {
103229
103325
  className
@@ -103233,15 +103329,26 @@ var RunButton = _ref => {
103233
103329
  var activeRunner = (0,external_react_redux_.useSelector)(state => state.editor.activeRunner);
103234
103330
  var loadedRunner = (0,external_react_redux_.useSelector)(state => state.editor.loadedRunner);
103235
103331
  var dispatch = (0,external_react_redux_.useDispatch)();
103332
+ var {
103333
+ hovered,
103334
+ hoverProps
103335
+ } = (0,useHover.useHover)();
103236
103336
  var onClickRun = () => {
103237
103337
  dispatch((0,EditorSlice.triggerCodeRun)());
103238
103338
  // reset page when running code to ensure it starts from the beginning
103239
103339
  dispatch((0,EditorSlice.setPage)(null));
103240
103340
  };
103241
- return /*#__PURE__*/(0,jsx_runtime.jsx)(Button["default"], (0,objectSpread2/* default */.A)({
103242
- disabled: !activeRunner || activeRunner !== loadedRunner || codeRunLoading,
103243
- onClickHandler: onClickRun
103244
- }, props));
103341
+ var isButtonDisabled = !activeRunner || activeRunner !== loadedRunner || codeRunLoading;
103342
+ return /*#__PURE__*/(0,jsx_runtime.jsxs)("div", (0,objectSpread2/* default */.A)((0,objectSpread2/* default */.A)({}, hoverProps), {}, {
103343
+ children: [/*#__PURE__*/(0,jsx_runtime.jsx)((Tooltip_default()), {
103344
+ message: "We\u2019re still loading what you need to run your code. Try again in a moment!",
103345
+ visible: hovered && isButtonDisabled,
103346
+ position: "bottom"
103347
+ }), /*#__PURE__*/(0,jsx_runtime.jsx)(Button["default"], (0,objectSpread2/* default */.A)({
103348
+ disabled: isButtonDisabled,
103349
+ onClickHandler: onClickRun
103350
+ }, props))]
103351
+ }));
103245
103352
  };
103246
103353
  /* harmony default export */ const RunButton_RunButton = (RunButton);
103247
103354
  // EXTERNAL MODULE: ./node_modules/classnames/index.js
@@ -103460,6 +103567,7 @@ var jsx_runtime = __webpack_require__(74848);
103460
103567
 
103461
103568
 
103462
103569
  var NO_CHANGES_TO_SAVE_MESSAGE = "No new changes to save!";
103570
+ var DEFAULT_TOOLTIP = "Save project";
103463
103571
  var SaveButton = props => {
103464
103572
  var dispatch = (0,external_react_redux_.useDispatch)();
103465
103573
  var {
@@ -103489,8 +103597,8 @@ var SaveButton = props => {
103489
103597
  className: styles_module.wrapper
103490
103598
  }, hoverProps), {}, {
103491
103599
  children: [/*#__PURE__*/(0,jsx_runtime.jsx)((Tooltip_default()), {
103492
- message: NO_CHANGES_TO_SAVE_MESSAGE,
103493
- visible: !hasChangesToSave && hovered,
103600
+ message: DEFAULT_TOOLTIP + (hasChangesToSave ? "" : "\n" + NO_CHANGES_TO_SAVE_MESSAGE),
103601
+ visible: hovered,
103494
103602
  position: "bottom"
103495
103603
  }), /*#__PURE__*/(0,jsx_runtime.jsx)(Button["default"], (0,objectSpread2/* default */.A)({
103496
103604
  buttonText: buttonText,
@@ -106198,6 +106306,14 @@ instance.use(es.initReactI18next)
106198
106306
 
106199
106307
  interpolation: {
106200
106308
  escapeValue: false // not needed for react!!
106309
+ },
106310
+ // Disable Suspense integration in react-i18next so that translation
106311
+ // loads (initial + i18n.changeLanguage) never throw promises that would
106312
+ // bubble to the root <Suspense fallback={<Loading />}> in App.tsx and
106313
+ // remount the Loading component (restarting the GIF / dot animation).
106314
+ // The visible loading state is driven by redux in WebComponentLoader.
106315
+ react: {
106316
+ useSuspense: false
106201
106317
  }
106202
106318
  });
106203
106319
  /* harmony default export */ const i18n = (instance);
@@ -142811,6 +142927,18 @@ __webpack_require__.r(__webpack_exports__);
142811
142927
  // extracted by mini-css-extract-plugin
142812
142928
 
142813
142929
 
142930
+ /***/ }),
142931
+
142932
+ /***/ 22401:
142933
+ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
142934
+
142935
+ __webpack_require__.r(__webpack_exports__);
142936
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
142937
+ /* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__)
142938
+ /* harmony export */ });
142939
+ // extracted by mini-css-extract-plugin
142940
+ /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ({"previewContainer":"styles-module__previewContainer--gHB-N"});
142941
+
142814
142942
  /***/ }),
142815
142943
 
142816
142944
  /***/ 63724:
@@ -142837,6 +142965,18 @@ __webpack_require__.r(__webpack_exports__);
142837
142965
 
142838
142966
  /***/ }),
142839
142967
 
142968
+ /***/ 89464:
142969
+ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
142970
+
142971
+ __webpack_require__.r(__webpack_exports__);
142972
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
142973
+ /* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__)
142974
+ /* harmony export */ });
142975
+ // extracted by mini-css-extract-plugin
142976
+ /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ({"grey-rpi-grey-15":"#d5d7dc","grey-rpi-grey-40":"#9497a4","grey-rpi-grey-5":"#f1f2f3","grey-rpi-grey-70":"#4a4d59","grey-rpf-white":"#fff","editorStatistic":"styles-module__editorStatistic--EyK0S","contentType":"styles-module__contentType--Sb4eE"});
142977
+
142978
+ /***/ }),
142979
+
142840
142980
  /***/ 71244:
142841
142981
  /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
142842
142982
 
@@ -142861,6 +143001,18 @@ __webpack_require__.r(__webpack_exports__);
142861
143001
 
142862
143002
  /***/ }),
142863
143003
 
143004
+ /***/ 38231:
143005
+ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
143006
+
143007
+ __webpack_require__.r(__webpack_exports__);
143008
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
143009
+ /* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__)
143010
+ /* harmony export */ });
143011
+ // extracted by mini-css-extract-plugin
143012
+ /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ({"grey-rpi-grey-15":"#d5d7dc","grey-rpi-grey-40":"#9497a4","grey-rpi-grey-5":"#f1f2f3","grey-rpi-grey-70":"#4a4d59","grey-rpf-white":"#fff","container":"styles-module__container--cs4+2","title":"styles-module__title--7NEra"});
143013
+
143014
+ /***/ }),
143015
+
142864
143016
  /***/ 12914:
142865
143017
  /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
142866
143018
 
@@ -142941,7 +143093,7 @@ __webpack_require__.r(__webpack_exports__);
142941
143093
  /* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__)
142942
143094
  /* harmony export */ });
142943
143095
  // extracted by mini-css-extract-plugin
142944
- /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ({"grey-rpi-grey-15":"#d5d7dc","grey-rpi-grey-40":"#9497a4","grey-rpi-grey-5":"#f1f2f3","grey-rpi-grey-70":"#4a4d59","grey-rpf-white":"#fff","tree":"styles-module__tree--KziAJ","fileTreeActions":"styles-module__fileTreeActions--k+Wsd","fileTreeActionsLabel":"styles-module__fileTreeActionsLabel--dPZp4","importButton":"styles-module__importButton--rcLUm","importArrowIcon":"styles-module__importArrowIcon--3eOLN","label":"styles-module__label--WY51f","iconButton":"styles-module__iconButton--6UIVe","iconButtonFilled":"styles-module__iconButtonFilled--w2vJC","dragline":"styles-module__dragline--RXmTT","emptyStateContainer":"styles-module__emptyStateContainer--AnTAm","emptyStateIcon":"styles-module__emptyStateIcon--sDlxK","emptyStateMessage":"styles-module__emptyStateMessage--OkPax"});
143096
+ /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ({"grey-rpi-grey-15":"#d5d7dc","grey-rpi-grey-40":"#9497a4","grey-rpi-grey-5":"#f1f2f3","grey-rpi-grey-70":"#4a4d59","grey-rpf-white":"#fff","tree":"styles-module__tree--KziAJ","fileTreeActions":"styles-module__fileTreeActions--k+Wsd","tooltipWrapper":"styles-module__tooltipWrapper--wltXE","fileTreeActionsLabel":"styles-module__fileTreeActionsLabel--dPZp4","importButton":"styles-module__importButton--rcLUm","importArrowIcon":"styles-module__importArrowIcon--3eOLN","label":"styles-module__label--WY51f","iconButton":"styles-module__iconButton--6UIVe","iconButtonFilled":"styles-module__iconButtonFilled--w2vJC","dragline":"styles-module__dragline--RXmTT","emptyStateContainer":"styles-module__emptyStateContainer--AnTAm","emptyStateIcon":"styles-module__emptyStateIcon--sDlxK","emptyStateMessage":"styles-module__emptyStateMessage--OkPax"});
142945
143097
 
142946
143098
  /***/ }),
142947
143099
 
@@ -368684,15 +368836,34 @@ const TextJamEditor = ({ onLogoutClick = () => { }, onViewProfileClick = () => {
368684
368836
  exports.TextJamEditor = TextJamEditor;
368685
368837
 
368686
368838
 
368839
+ /***/ }),
368840
+
368841
+ /***/ 66360:
368842
+ /***/ (function(__unused_webpack_module, exports, __webpack_require__) {
368843
+
368844
+
368845
+ var __importDefault = (this && this.__importDefault) || function (mod) {
368846
+ return (mod && mod.__esModule) ? mod : { "default": mod };
368847
+ };
368848
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
368849
+ exports.AssetPreviewPanel = void 0;
368850
+ const jsx_runtime_1 = __webpack_require__(74848);
368851
+ const styles_module_scss_1 = __importDefault(__webpack_require__(22401));
368852
+ const AssetPreview_1 = __webpack_require__(91078);
368853
+ const AssetPreviewPanel = ({ file, }) => {
368854
+ return ((0, jsx_runtime_1.jsx)("div", { className: styles_module_scss_1.default.previewContainer, children: (0, jsx_runtime_1.jsx)(AssetPreview_1.AssetPreview, { file: file }) }));
368855
+ };
368856
+ exports.AssetPreviewPanel = AssetPreviewPanel;
368857
+
368858
+
368687
368859
  /***/ }),
368688
368860
 
368689
368861
  /***/ 91078:
368690
368862
  /***/ ((__unused_webpack_module, exports, __webpack_require__) => {
368691
368863
 
368692
- var __webpack_unused_export__;
368693
368864
 
368694
- __webpack_unused_export__ = ({ value: true });
368695
- exports.n = void 0;
368865
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
368866
+ exports.AssetPreview = void 0;
368696
368867
  const jsx_runtime_1 = __webpack_require__(74848);
368697
368868
  const react_1 = __webpack_require__(51649);
368698
368869
  const binaryStore_1 = __webpack_require__(5060);
@@ -368719,7 +368890,7 @@ const AssetPreview = ({ file }) => {
368719
368890
  if (!url)
368720
368891
  return null;
368721
368892
  if (file.mimeType?.startsWith("image/")) {
368722
- return ((0, jsx_runtime_1.jsx)("img", { src: url, alt: file.name || "Asset preview", style: { maxWidth: "100%" } }));
368893
+ return ((0, jsx_runtime_1.jsx)("img", { src: url, alt: file.name || "Asset preview", style: { width: "fit-content", maxWidth: "100%" } }));
368723
368894
  }
368724
368895
  if (file.mimeType?.startsWith("audio/")) {
368725
368896
  return (0, jsx_runtime_1.jsx)("audio", { src: url, controls: true, style: { margin: 20 } });
@@ -368732,7 +368903,7 @@ const AssetPreview = ({ file }) => {
368732
368903
  }
368733
368904
  return (0, jsx_runtime_1.jsxs)("div", { children: ["File: ", file.name] });
368734
368905
  };
368735
- exports.n = AssetPreview;
368906
+ exports.AssetPreview = AssetPreview;
368736
368907
 
368737
368908
 
368738
368909
  /***/ }),
@@ -368804,7 +368975,7 @@ const HistoryButton = ({ className }) => {
368804
368975
  const handleClick = () => {
368805
368976
  setIsContextOpen((prev) => !prev);
368806
368977
  };
368807
- return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsxs)("div", { className: styles_module_scss_1.default.btnContainer, ...hoverProps, children: [(0, jsx_runtime_1.jsx)(Tooltip_1.default, { message: "Nothing here yet! Once you save your work, versions will appear here so you can restore them.", visible: hovered && !commits?.length, position: "bottom" }), (0, jsx_runtime_1.jsx)(Button_1.default, { buttonRef: ref, className: (0, classnames_1.default)(styles_module_scss_1.default.historyButton, className, {
368978
+ return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsxs)("div", { className: styles_module_scss_1.default.btnContainer, ...hoverProps, children: [(0, jsx_runtime_1.jsx)(Tooltip_1.default, { message: "Nothing here yet! Once you save your work, versions will appear here so you can restore them.", visible: hovered && !commits?.length, position: "bottom" }), (0, jsx_runtime_1.jsx)(Tooltip_1.default, { message: "Load previous versions", visible: hovered && !!commits?.length, position: "bottom" }), (0, jsx_runtime_1.jsx)(Button_1.default, { buttonRef: ref, className: (0, classnames_1.default)(styles_module_scss_1.default.historyButton, className, {
368808
368979
  [styles_module_scss_1.default.historyActive]: isContextOpen,
368809
368980
  }), variant: "tertiary", onClickHandler: handleClick, ButtonIcon: () => (0, SvgIcon_1.SvgIcon)({ SvgElement: history_svg_1.default, size: 24 }), disabled: !commits?.length })] }), (0, jsx_runtime_1.jsx)(CommitHistoryMenu_1.CommitHistoryMenu, { isOpen: isContextOpen, onClose: () => setIsContextOpen(false), commits: commits, btnRef: ref, activeCommitId: project.commitId ?? undefined })] }));
368810
368981
  };
@@ -368851,13 +369022,16 @@ const stores_1 = __webpack_require__(32132);
368851
369022
  const SvgIcon_1 = __webpack_require__(82917);
368852
369023
  const Button_1 = __importDefault(__webpack_require__(79428));
368853
369024
  const createProjectArchive_1 = __webpack_require__(46514);
369025
+ const Tooltip_1 = __importDefault(__webpack_require__(26982));
369026
+ const useHover_1 = __webpack_require__(78556);
368854
369027
  const DownloadButton = (props) => {
368855
369028
  const project = (0, stores_1.useAppSelector)((state) => state.editor.project);
369029
+ const { hovered, hoverProps } = (0, useHover_1.useHover)();
368856
369030
  const onClickDownload = async () => {
368857
369031
  const { zipBlob: content } = await (0, createProjectArchive_1.createProjectArchive)(project);
368858
369032
  file_saver_1.default.saveAs(content, `${(0, js_convert_case_1.toSnakeCase)(project.name || "untitled_project")}.zip`);
368859
369033
  };
368860
- return ((0, jsx_runtime_1.jsx)(Button_1.default, { variant: "tertiary", ButtonIcon: () => (0, jsx_runtime_1.jsx)(SvgIcon_1.SvgIcon, { SvgElement: download_svg_1.default, size: 24 }), onClickHandler: onClickDownload, ...props }));
369034
+ return ((0, jsx_runtime_1.jsxs)("div", { ...hoverProps, children: [(0, jsx_runtime_1.jsx)(Button_1.default, { variant: "tertiary", ButtonIcon: () => (0, jsx_runtime_1.jsx)(SvgIcon_1.SvgIcon, { SvgElement: download_svg_1.default, size: 24 }), onClickHandler: onClickDownload, ...props }), (0, jsx_runtime_1.jsx)(Tooltip_1.default, { message: "Download project", visible: hovered, position: "bottom" })] }));
368861
369035
  };
368862
369036
  exports["default"] = DownloadButton;
368863
369037
 
@@ -369048,6 +369222,28 @@ const EditorInput = () => {
369048
369222
  exports["default"] = EditorInput;
369049
369223
 
369050
369224
 
369225
+ /***/ }),
369226
+
369227
+ /***/ 47272:
369228
+ /***/ (function(__unused_webpack_module, exports, __webpack_require__) {
369229
+
369230
+
369231
+ var __importDefault = (this && this.__importDefault) || function (mod) {
369232
+ return (mod && mod.__esModule) ? mod : { "default": mod };
369233
+ };
369234
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
369235
+ exports.EditorStatistic = void 0;
369236
+ const jsx_runtime_1 = __webpack_require__(74848);
369237
+ const SvgIcon_1 = __webpack_require__(82917);
369238
+ const checkbox_svg_1 = __importDefault(__webpack_require__(62284));
369239
+ const styles_module_scss_1 = __importDefault(__webpack_require__(89464));
369240
+ const EditorStatistic = (props) => {
369241
+ const TAB_SIZE = 2;
369242
+ return ((0, jsx_runtime_1.jsxs)("div", { className: styles_module_scss_1.default.editorStatistic, children: [(0, jsx_runtime_1.jsxs)("div", { className: styles_module_scss_1.default.contentType, children: ["{", (0, jsx_runtime_1.jsx)(SvgIcon_1.SvgIcon, { size: 6, SvgElement: checkbox_svg_1.default }), "} ", props.title] }), (0, jsx_runtime_1.jsxs)("div", { className: styles_module_scss_1.default.contentStatistic, children: ["Ln ", props.line, ", Col ", props.position, " * Spaces ", TAB_SIZE] })] }));
369243
+ };
369244
+ exports.EditorStatistic = EditorStatistic;
369245
+
369246
+
369051
369247
  /***/ }),
369052
369248
 
369053
369249
  /***/ 80335:
@@ -370164,7 +370360,7 @@ const PyodideRunner = ({ active, packageApiUrl, }) => {
370164
370360
  console.warn("PyodideWorker is not initialized");
370165
370361
  return null;
370166
370362
  }
370167
- return ((0, jsx_runtime_1.jsxs)("div", { className: (0, classnames_1.default)(styles_module_scss_1.default.pythonrunnerContainer, styles_module_scss_1.default.pyodiderunner, active && styles_module_scss_1.default.active), children: [(0, jsx_runtime_1.jsxs)(OutputTabPanel_1.OutputTabPanel, { title: "Terminal", icon: console_svg_1.default, readOnly: readOnly, children: [(0, jsx_runtime_1.jsx)(ErrorMessage_1.default, {}), (0, jsx_runtime_1.jsx)("pre", { className: styles_module_scss_1.default.console, onClick: consoleInput_1.shiftFocusToInput, ref: output })] }), (0, jsx_runtime_1.jsx)(ResizableWithHandle_1.default, { "data-testid": "proj-console-container", handleDirection: "top", defaultHeight: "50%", className: styles_module_scss_1.default.resizeContainer, handleClassName: styles_module_scss_1.default.resizeHandleContainer, children: (0, jsx_runtime_1.jsx)(OutputTabPanel_1.OutputTabPanel, { title: "Visual output", icon: preview_svg_1.default, readOnly: readOnly, children: (0, jsx_runtime_1.jsx)(VisualOutputPane_1.default, { visuals: visuals, setVisuals: setVisuals }) }) })] }));
370363
+ return ((0, jsx_runtime_1.jsxs)("div", { className: (0, classnames_1.default)(styles_module_scss_1.default.pythonrunnerContainer, styles_module_scss_1.default.pyodiderunner, active && styles_module_scss_1.default.active), children: [(0, jsx_runtime_1.jsxs)(OutputTabPanel_1.OutputTabPanel, { title: "Terminal", icon: console_svg_1.default, readOnly: readOnly, children: [(0, jsx_runtime_1.jsx)(ErrorMessage_1.default, {}), (0, jsx_runtime_1.jsx)("pre", { className: styles_module_scss_1.default.console, onClick: consoleInput_1.shiftFocusToInput, ref: output })] }), (0, jsx_runtime_1.jsx)(ResizableWithHandle_1.default, { "data-testid": "proj-console-container", handleDirection: "top", defaultHeight: "50%", className: styles_module_scss_1.default.resizeContainer, handleClassName: styles_module_scss_1.default.resizeHandleContainer, children: (0, jsx_runtime_1.jsx)(OutputTabPanel_1.OutputTabPanel, { title: "VNC", icon: preview_svg_1.default, readOnly: readOnly, children: (0, jsx_runtime_1.jsx)(VisualOutputPane_1.default, { visuals: visuals, setVisuals: setVisuals }) }) })] }));
370168
370364
  };
370169
370365
  exports["default"] = PyodideRunner;
370170
370366
 
@@ -370184,6 +370380,7 @@ const react_1 = __webpack_require__(51649);
370184
370380
  const highcharts_1 = __importDefault(__webpack_require__(14783));
370185
370381
  const plotly_js_1 = __importDefault(__webpack_require__(28850));
370186
370382
  const styles_module_scss_1 = __importDefault(__webpack_require__(12914));
370383
+ const VisualOutputPaneEmpty_1 = __importDefault(__webpack_require__(93290));
370187
370384
  const VisualOutputPane = ({ visuals, setVisuals }) => {
370188
370385
  const output = (0, react_1.useRef)(null);
370189
370386
  const showVisual = (0, react_1.useCallback)((visual, output) => {
@@ -370245,7 +370442,7 @@ const VisualOutputPane = ({ visuals, setVisuals }) => {
370245
370442
  setVisuals((visuals) => showVisuals(visuals, output));
370246
370443
  }
370247
370444
  }, [visuals, setVisuals, showVisuals]);
370248
- return ((0, jsx_runtime_1.jsx)("div", { className: styles_module_scss_1.default.visualOutput, children: (0, jsx_runtime_1.jsx)("div", { ref: output, className: styles_module_scss_1.default.pythonGraphic }) }));
370445
+ return ((0, jsx_runtime_1.jsx)("div", { className: styles_module_scss_1.default.visualOutput, children: visuals.length !== 0 ? ((0, jsx_runtime_1.jsx)("div", { ref: output, className: styles_module_scss_1.default.pythonGraphic })) : ((0, jsx_runtime_1.jsx)(VisualOutputPaneEmpty_1.default, {})) }));
370249
370446
  };
370250
370447
  const elementFromProps = (map) => {
370251
370448
  const tag = map.get("tag");
@@ -370264,6 +370461,27 @@ const elementFromProps = (map) => {
370264
370461
  exports["default"] = VisualOutputPane;
370265
370462
 
370266
370463
 
370464
+ /***/ }),
370465
+
370466
+ /***/ 93290:
370467
+ /***/ (function(__unused_webpack_module, exports, __webpack_require__) {
370468
+
370469
+
370470
+ var __importDefault = (this && this.__importDefault) || function (mod) {
370471
+ return (mod && mod.__esModule) ? mod : { "default": mod };
370472
+ };
370473
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
370474
+ const jsx_runtime_1 = __webpack_require__(74848);
370475
+ const styles_module_scss_1 = __importDefault(__webpack_require__(38231));
370476
+ const monitor_svg_1 = __importDefault(__webpack_require__(36311));
370477
+ const SvgIcon_1 = __webpack_require__(82917);
370478
+ const Text_1 = __webpack_require__(82803);
370479
+ const VisualOutputPaneEmpty = () => {
370480
+ return ((0, jsx_runtime_1.jsxs)("div", { className: styles_module_scss_1.default.container, children: [(0, jsx_runtime_1.jsx)(SvgIcon_1.SvgIcon, { SvgElement: monitor_svg_1.default, size: 32, "aria-hidden": "true", focusable: "false" }), (0, jsx_runtime_1.jsx)(Text_1.Text, { size: 20, className: styles_module_scss_1.default.title, weight: "bold", children: "View your app\u2019s desktop screen output" }), (0, jsx_runtime_1.jsx)(Text_1.Text, { size: 12, children: "No VNC browser is currently running with compatible ports available to view." })] }));
370481
+ };
370482
+ exports["default"] = VisualOutputPaneEmpty;
370483
+
370484
+
370267
370485
  /***/ }),
370268
370486
 
370269
370487
  /***/ 85799:
@@ -371666,12 +371884,15 @@ const SvgIcon_1 = __webpack_require__(82917);
371666
371884
  const arrow_right_svg_1 = __importDefault(__webpack_require__(17297));
371667
371885
  const styles_module_scss_1 = __importDefault(__webpack_require__(51191));
371668
371886
  const ContextMenu_1 = __importDefault(__webpack_require__(39179));
371887
+ const Tooltip_1 = __importDefault(__webpack_require__(26982));
371888
+ const useHover_1 = __webpack_require__(78556);
371669
371889
  const Dropdown = (props) => {
371670
371890
  const { ButtonIcon, buttonClassname, menuOptions, menuPosition, ariaLabel } = props;
371671
371891
  const { direction = "bottom", align = "center" } = menuPosition || {};
371672
371892
  const [isOpen, setOpen] = (0, react_1.useState)(false);
371673
371893
  const buttonRef = (0, react_1.useRef)(null);
371674
- return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsxs)("button", { type: "button", "aria-label": ariaLabel, className: (0, classnames_1.default)(buttonClassname, isOpen && styles_module_scss_1.default.buttonActive), onClick: () => setOpen((prev) => !prev), ref: buttonRef, children: [ButtonIcon && ((0, jsx_runtime_1.jsx)(SvgIcon_1.SvgIcon, { SvgElement: ButtonIcon, size: 16, "aria-hidden": "true", focusable: "false" })), (0, jsx_runtime_1.jsx)(SvgIcon_1.SvgIcon, { className: (0, classnames_1.default)(styles_module_scss_1.default.arrowIcon, isOpen && styles_module_scss_1.default.arrowIconOpen), style: { rotate: `${isOpen ? "-" : ""}90deg` }, SvgElement: arrow_right_svg_1.default, size: 8, "aria-hidden": "true", focusable: "false" })] }), (0, jsx_runtime_1.jsx)(ContextMenu_1.default, { anchorRef: buttonRef, menuOptions: menuOptions, opened: isOpen, onClose: () => setOpen(false), align: align, direction: direction, gap: 4 })] }));
371894
+ const { hovered, hoverProps } = (0, useHover_1.useHover)();
371895
+ return ((0, jsx_runtime_1.jsxs)("div", { ...hoverProps, children: [(0, jsx_runtime_1.jsxs)("button", { type: "button", "aria-label": ariaLabel, className: (0, classnames_1.default)(buttonClassname, isOpen && styles_module_scss_1.default.buttonActive), onClick: () => setOpen((prev) => !prev), ref: buttonRef, children: [ButtonIcon && ((0, jsx_runtime_1.jsx)(SvgIcon_1.SvgIcon, { SvgElement: ButtonIcon, size: 16, "aria-hidden": "true", focusable: "false" })), (0, jsx_runtime_1.jsx)(SvgIcon_1.SvgIcon, { className: (0, classnames_1.default)(styles_module_scss_1.default.arrowIcon, isOpen && styles_module_scss_1.default.arrowIconOpen), style: { rotate: `${isOpen ? "-" : ""}90deg` }, SvgElement: arrow_right_svg_1.default, size: 8, "aria-hidden": "true", focusable: "false" })] }), (0, jsx_runtime_1.jsx)(Tooltip_1.default, { message: "Upload files", visible: hovered && !isOpen, position: "fixed" }), (0, jsx_runtime_1.jsx)(ContextMenu_1.default, { anchorRef: buttonRef, menuOptions: menuOptions, opened: isOpen, onClose: () => setOpen(false), align: align, direction: direction, gap: 4 })] }));
371675
371896
  };
371676
371897
  exports["default"] = Dropdown;
371677
371898
 
@@ -371746,10 +371967,15 @@ const FileTreeContext_1 = __webpack_require__(5323);
371746
371967
  const ProjectTypes_1 = __webpack_require__(27130);
371747
371968
  const Text_1 = __webpack_require__(82803);
371748
371969
  const stores_1 = __webpack_require__(32132);
371970
+ const Tooltip_1 = __importDefault(__webpack_require__(26982));
371971
+ const useHover_1 = __webpack_require__(78556);
371749
371972
  const FileTreeActions = ({ hasExpandedNodes, }) => {
371750
371973
  const { expandAll, collapseAll, createComponent, importFiles, importFolder } = (0, react_1.useContext)(FileTreeContext_1.TreeContext);
371751
371974
  const isReadOnly = (0, stores_1.useAppSelector)((state) => state.editor.readOnly);
371752
- return ((0, jsx_runtime_1.jsxs)("div", { className: styles_module_scss_1.default.fileTreeActions, children: [hasExpandedNodes ? ((0, jsx_runtime_1.jsx)("button", { type: "button", "aria-label": "Collapse all folders", onClick: collapseAll, className: styles_module_scss_1.default.iconButton, children: (0, jsx_runtime_1.jsx)(minus_circle_svg_1.default, { "aria-hidden": "true", focusable: "false" }) })) : ((0, jsx_runtime_1.jsx)("button", { type: "button", "aria-label": "Expand all folders", onClick: expandAll, className: styles_module_scss_1.default.iconButton, children: (0, jsx_runtime_1.jsx)(plus_circle_svg_1.default, { "aria-hidden": "true", focusable: "false" }) })), (0, jsx_runtime_1.jsx)(Text_1.Text, { size: 12, weight: "medium", className: styles_module_scss_1.default.fileTreeActionsLabel, children: "Project files" }), !isReadOnly && ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("button", { type: "button", "aria-label": "Add folder", onClick: () => createComponent(ProjectTypes_1.ProjectComponentType.DIR), className: (0, classnames_1.default)([styles_module_scss_1.default.iconButtonFilled, styles_module_scss_1.default.iconButton]), children: (0, jsx_runtime_1.jsx)(add_folder_svg_1.default, { "aria-hidden": "true", focusable: "false" }) }), (0, jsx_runtime_1.jsx)("button", { type: "button", "aria-label": "Add file", onClick: () => createComponent(ProjectTypes_1.ProjectComponentType.FILE), className: (0, classnames_1.default)([styles_module_scss_1.default.iconButtonFilled, styles_module_scss_1.default.iconButton]), children: (0, jsx_runtime_1.jsx)(add_file_svg_1.default, { "aria-hidden": "true", focusable: "false" }) }), (0, jsx_runtime_1.jsx)(ImportButton_1.ImportButton, { importFiles: importFiles, importFolder: importFolder })] }))] }));
371975
+ const { hovered: toggleHovered, hoverProps: toggleHoverProps } = (0, useHover_1.useHover)();
371976
+ const { hovered: addFolderHovered, hoverProps: addFolderHoverProps } = (0, useHover_1.useHover)();
371977
+ const { hovered: addFileHovered, hoverProps: addFileHoverProps } = (0, useHover_1.useHover)();
371978
+ return ((0, jsx_runtime_1.jsxs)("div", { className: styles_module_scss_1.default.fileTreeActions, children: [hasExpandedNodes ? ((0, jsx_runtime_1.jsxs)("div", { className: styles_module_scss_1.default.tooltipWrapper, ...toggleHoverProps, children: [(0, jsx_runtime_1.jsx)("button", { type: "button", "aria-label": "Collapse all folders", onClick: collapseAll, className: styles_module_scss_1.default.iconButton, children: (0, jsx_runtime_1.jsx)(minus_circle_svg_1.default, { "aria-hidden": "true", focusable: "false" }) }), (0, jsx_runtime_1.jsx)(Tooltip_1.default, { message: "Collapse all folders", visible: toggleHovered, position: "fixed" })] })) : ((0, jsx_runtime_1.jsxs)("div", { className: styles_module_scss_1.default.tooltipWrapper, ...toggleHoverProps, children: [(0, jsx_runtime_1.jsx)("button", { type: "button", "aria-label": "Expand all folders", onClick: expandAll, className: styles_module_scss_1.default.iconButton, children: (0, jsx_runtime_1.jsx)(plus_circle_svg_1.default, { "aria-hidden": "true", focusable: "false" }) }), (0, jsx_runtime_1.jsx)(Tooltip_1.default, { message: "Expand all folders", visible: toggleHovered, position: "fixed" })] })), (0, jsx_runtime_1.jsx)(Text_1.Text, { size: 12, weight: "medium", className: styles_module_scss_1.default.fileTreeActionsLabel, children: "Project files" }), !isReadOnly && ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsxs)("div", { className: styles_module_scss_1.default.tooltipWrapper, ...addFolderHoverProps, children: [(0, jsx_runtime_1.jsx)("button", { type: "button", "aria-label": "Add folder", onClick: () => createComponent(ProjectTypes_1.ProjectComponentType.DIR), className: (0, classnames_1.default)([styles_module_scss_1.default.iconButtonFilled, styles_module_scss_1.default.iconButton]), children: (0, jsx_runtime_1.jsx)(add_folder_svg_1.default, { "aria-hidden": "true", focusable: "false" }) }), (0, jsx_runtime_1.jsx)(Tooltip_1.default, { message: "Add folder", visible: addFolderHovered, position: "fixed" })] }), (0, jsx_runtime_1.jsxs)("div", { className: styles_module_scss_1.default.tooltipWrapper, ...addFileHoverProps, children: [(0, jsx_runtime_1.jsx)("button", { type: "button", "aria-label": "Add file", onClick: () => createComponent(ProjectTypes_1.ProjectComponentType.FILE), className: (0, classnames_1.default)([styles_module_scss_1.default.iconButtonFilled, styles_module_scss_1.default.iconButton]), children: (0, jsx_runtime_1.jsx)(add_file_svg_1.default, { "aria-hidden": "true", focusable: "false" }) }), (0, jsx_runtime_1.jsx)(Tooltip_1.default, { message: "Add file", visible: addFileHovered, position: "fixed" })] }), (0, jsx_runtime_1.jsx)(ImportButton_1.ImportButton, { importFiles: importFiles, importFolder: importFolder })] }))] }));
371753
371979
  };
371754
371980
  exports.FileTreeActions = FileTreeActions;
371755
371981
 
@@ -371894,9 +372120,15 @@ const DeleteOption_1 = __webpack_require__(50185);
371894
372120
  const react_redux_1 = __webpack_require__(14062);
371895
372121
  const EditorSlice_1 = __webpack_require__(68512);
371896
372122
  const types_1 = __webpack_require__(92932);
372123
+ const createProjectArchive_1 = __webpack_require__(46514);
372124
+ const js_convert_case_1 = __webpack_require__(61647);
372125
+ const stores_1 = __webpack_require__(32132);
372126
+ const file_saver_1 = __importDefault(__webpack_require__(4213));
372127
+ const sendToast_1 = __webpack_require__(50068);
371897
372128
  const ItemContextMenu = ({ item, menuPosition, isOpened, onClose, openImportFileDialog, }) => {
371898
372129
  const dispatch = (0, react_redux_1.useDispatch)();
371899
372130
  const { startRenaming, createComponent, downloadFile, removeComponent } = (0, react_1.useContext)(FileTreeContext_1.TreeContext);
372131
+ const project = (0, stores_1.useAppSelector)((state) => state.editor.project);
371900
372132
  const handleDelete = ({ isFolder }) => {
371901
372133
  dispatch((0, EditorSlice_1.showModal)({
371902
372134
  modal: types_1.ModalType.REMOVE_ITEM,
@@ -371930,6 +372162,25 @@ const ItemContextMenu = ({ item, menuPosition, isOpened, onClose, openImportFile
371930
372162
  icon: (0, jsx_runtime_1.jsx)(OptionIcon_1.OptionIcon, { icon: upload_file_svg_1.default }),
371931
372163
  action: openImportFileDialog,
371932
372164
  },
372165
+ {
372166
+ text: "Download Folder",
372167
+ icon: (0, jsx_runtime_1.jsx)(OptionIcon_1.OptionIcon, { icon: download_svg_1.default }),
372168
+ // Sync action that fires-and-handles its own async work. `ContextMenu`
372169
+ // calls `option.action?.()` without awaiting, so we must catch errors
372170
+ // here ourselves to avoid an unhandled promise rejection.
372171
+ action: () => {
372172
+ const folder = item.getItemData();
372173
+ const fileName = `${(0, js_convert_case_1.toSnakeCase)(project.name || "untitled_project")}_${(0, js_convert_case_1.toSnakeCase)(folder.name || "folder")}.zip`;
372174
+ const downloadFolder = async () => {
372175
+ const { zipBlob: content } = await (0, createProjectArchive_1.createProjectArchive)(project, folder);
372176
+ file_saver_1.default.saveAs(content, fileName);
372177
+ };
372178
+ downloadFolder().catch((err) => {
372179
+ console.error("Download Folder failed:", err);
372180
+ (0, sendToast_1.showError)(`Failed to download folder "${folder.name}".`);
372181
+ });
372182
+ },
372183
+ },
371933
372184
  {
371934
372185
  text: (0, jsx_runtime_1.jsx)(DeleteOption_1.DeleteOption, {}),
371935
372186
  action: () => handleDelete({ isFolder: true }),
@@ -372674,20 +372925,27 @@ const classnames_1 = __importDefault(__webpack_require__(46942));
372674
372925
  const styles_module_scss_1 = __importDefault(__webpack_require__(21852));
372675
372926
  const UserMenu_1 = __importDefault(__webpack_require__(2957));
372676
372927
  const backgroundColors = [
372677
- 'D7F9F4', '43D6B9',
372678
- '0D9C90', 'E5F8FF',
372679
- 'FEEFEE', 'F37C6F',
372680
- 'FFF3E8', 'F9B88C',
372681
- 'FA7815', 'FCE688'
372928
+ "D7F9F4",
372929
+ "43D6B9",
372930
+ "0D9C90",
372931
+ "E5F8FF",
372932
+ "FEEFEE",
372933
+ "F37C6F",
372934
+ "FFF3E8",
372935
+ "F9B88C",
372936
+ "FA7815",
372937
+ "FCE688",
372682
372938
  ];
372683
372939
  const SidebarBarOption = (props) => {
372684
- const { Icon, isActive, name, title, toggleOption, buttonText, isUser, action } = props;
372940
+ const { Icon, isActive, name, title, toggleOption, buttonText, isUser, action, } = props;
372685
372941
  const userId = (0, stores_1.useAppSelector)((state) => state.user.userId);
372686
372942
  const isFacilitator = (0, stores_1.useAppSelector)((state) => state.user.isFacilitator);
372687
372943
  const avatarSrc = (0, stores_1.useAppSelector)((state) => state.user.avatarSrc);
372688
372944
  const avatarStyle = isUser
372689
372945
  ? isFacilitator
372690
- ? { background: 'linear-gradient(90deg,#d7f9f4,#fff9e2,#fff3e8,#e5f8ff,#d7f9f4)' }
372946
+ ? {
372947
+ background: "linear-gradient(90deg,#d7f9f4,#fff9e2,#fff3e8,#e5f8ff,#d7f9f4)",
372948
+ }
372691
372949
  : { backgroundColor: `#${backgroundColors[(userId ?? 0) % 10]}` }
372692
372950
  : null;
372693
372951
  const [isMenuOpen, setIsMenuOpen] = (0, react_1.useState)(false);
@@ -373604,15 +373862,8 @@ const WebComponentLoader = (props) => {
373604
373862
  }, children: [(0, jsx_runtime_1.jsx)(ToastMessages_1.ToastMessagesProvider, {}), (0, jsx_runtime_1.jsxs)("div", { id: "textjam-root", className: `editor-shell --${themeDefault}`, children: [(0, jsx_runtime_1.jsx)(WebComponentProject_1.default, { nameEditable: projectNameEditable, sidebarOptions: sidebarOptions, packageApiUrl: packageApiUrl }), errorModalShowing && (0, jsx_runtime_1.jsx)(ErrorModal_1.default, {}), modal, shouldBlockNavigation && (0, jsx_runtime_1.jsx)(SaveBeforeLeave_1.SaveBeforeLeaveModal, {})] })] }) }));
373605
373863
  const renderFailedState = () => ((0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: (0, jsx_runtime_1.jsx)("p", { children: t("webComponent.failed") }) }));
373606
373864
  const renderLoadingState = () => (0, jsx_runtime_1.jsx)(Loading_1.default, {});
373607
- if (loading === types_1.LoadingState.SUCCESS) {
373608
- return renderSuccessState();
373609
- }
373610
- else if (loading === types_1.LoadingState.FAILED) {
373611
- return renderFailedState();
373612
- }
373613
- else {
373614
- return renderLoadingState();
373615
- }
373865
+ const isLoadingState = ![types_1.LoadingState.SUCCESS, types_1.LoadingState.FAILED].includes(loading);
373866
+ return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [loading === types_1.LoadingState.SUCCESS && renderSuccessState(), loading === types_1.LoadingState.FAILED && renderFailedState(), isLoadingState && renderLoadingState()] }));
373616
373867
  };
373617
373868
  exports["default"] = WebComponentLoader;
373618
373869
 
@@ -376500,20 +376751,39 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
376500
376751
  };
376501
376752
  Object.defineProperty(exports, "__esModule", ({ value: true }));
376502
376753
  exports.createProjectArchive = createProjectArchive;
376503
- const jszip_1 = __importDefault(__webpack_require__(71710));
376504
376754
  const ProjectTypes_1 = __webpack_require__(27130);
376755
+ const jszip_1 = __importDefault(__webpack_require__(71710));
376505
376756
  const binaryStore_1 = __webpack_require__(5060);
376506
- async function createProjectArchive(project) {
376757
+ const projectPath_1 = __webpack_require__(95506);
376758
+ async function createProjectArchive(project, folder) {
376507
376759
  let unzipSize = 0;
376508
376760
  const zip = new jszip_1.default();
376509
376761
  await Promise.all(project.components.map(async (component) => {
376510
376762
  if (component.type !== ProjectTypes_1.ProjectComponentType.FILE)
376511
376763
  return;
376512
376764
  const file = component;
376513
- // remove leading slash for windows compatibility
376514
- const safePath = file.path.startsWith("/")
376515
- ? file.path.slice(1)
376516
- : file.path;
376765
+ // Normalize once; we decide between "everything" and "scope to a
376766
+ // subfolder" by inspecting the normalized folder path.
376767
+ const normalizedFile = (0, projectPath_1.normalizePath)(file.path);
376768
+ const normalizedFolder = folder ? (0, projectPath_1.normalizePath)(folder.path) : null;
376769
+ let safePath;
376770
+ if (normalizedFolder && normalizedFolder !== "/") {
376771
+ // Scoped to a non-root subfolder: include only files within it,
376772
+ // with paths relative to that folder. `stripTopFolder` returns null
376773
+ // when the file is outside the folder and "" when the file path
376774
+ // equals the folder path (skip both).
376775
+ const relative = (0, projectPath_1.stripTopFolder)(normalizedFile, normalizedFolder);
376776
+ if (relative === null || relative === "")
376777
+ return;
376778
+ safePath = relative;
376779
+ }
376780
+ else {
376781
+ // Whole project (no folder, or the root folder "/"). Emit
376782
+ // Windows-friendly entries without a leading "/".
376783
+ if (normalizedFile === "/")
376784
+ return;
376785
+ safePath = normalizedFile.slice(1);
376786
+ }
376517
376787
  const blob = (await binaryStore_1.binaryStore.get(file.id)) ??
376518
376788
  new Blob([file.content], { type: file.mimeType || "text/plain" });
376519
376789
  unzipSize += blob.size;