gwchq-textjam 0.4.2 → 0.4.3

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
@@ -68435,8 +68435,8 @@ __webpack_require__.r(__webpack_exports__);
68435
68435
  /* harmony export */ __webpack_require__.d(__webpack_exports__, {
68436
68436
  /* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__)
68437
68437
  /* harmony export */ });
68438
- /* harmony import */ var _Users_valeriya_projects_gwc_gwchq_textjam_node_modules_babel_runtime_helpers_esm_objectSpread2_js__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(89379);
68439
- /* harmony import */ var _Users_valeriya_projects_gwc_gwchq_textjam_node_modules_babel_runtime_helpers_esm_objectWithoutProperties_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(53986);
68438
+ /* harmony import */ var _Users_ruslanmatkovskyi_projects_gwc_gwchq_textjam_node_modules_babel_runtime_helpers_esm_objectSpread2_js__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(89379);
68439
+ /* harmony import */ var _Users_ruslanmatkovskyi_projects_gwc_gwchq_textjam_node_modules_babel_runtime_helpers_esm_objectWithoutProperties_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(53986);
68440
68440
  /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(51649);
68441
68441
  /* harmony import */ var _hello_pangea_dnd__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(98850);
68442
68442
  /* harmony import */ var react_redux__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(14062);
@@ -68460,7 +68460,7 @@ var DraggableTab = _ref => {
68460
68460
  panelIndex,
68461
68461
  fileIndex
68462
68462
  } = _ref,
68463
- otherProps = (0,_Users_valeriya_projects_gwc_gwchq_textjam_node_modules_babel_runtime_helpers_esm_objectWithoutProperties_js__WEBPACK_IMPORTED_MODULE_5__/* ["default"] */ .A)(_ref, _excluded);
68463
+ otherProps = (0,_Users_ruslanmatkovskyi_projects_gwc_gwchq_textjam_node_modules_babel_runtime_helpers_esm_objectWithoutProperties_js__WEBPACK_IMPORTED_MODULE_5__/* ["default"] */ .A)(_ref, _excluded);
68464
68464
  var openFiles = (0,react_redux__WEBPACK_IMPORTED_MODULE_1__.useSelector)(state => state.editor.openedFiles);
68465
68465
  var openFilesCount = openFiles[panelIndex].length;
68466
68466
  var dispatch = (0,react_redux__WEBPACK_IMPORTED_MODULE_1__.useDispatch)();
@@ -68477,7 +68477,7 @@ var DraggableTab = _ref => {
68477
68477
  switchToFileTab(panelIndex, (fileIndex + openFilesCount - 1) % openFilesCount);
68478
68478
  }
68479
68479
  };
68480
- var InnerTab = () => /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsx)(react_tabs__WEBPACK_IMPORTED_MODULE_2__.Tab, (0,_Users_valeriya_projects_gwc_gwchq_textjam_node_modules_babel_runtime_helpers_esm_objectSpread2_js__WEBPACK_IMPORTED_MODULE_7__/* ["default"] */ .A)((0,_Users_valeriya_projects_gwc_gwchq_textjam_node_modules_babel_runtime_helpers_esm_objectSpread2_js__WEBPACK_IMPORTED_MODULE_7__/* ["default"] */ .A)({
68480
+ var InnerTab = () => /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsx)(react_tabs__WEBPACK_IMPORTED_MODULE_2__.Tab, (0,_Users_ruslanmatkovskyi_projects_gwc_gwchq_textjam_node_modules_babel_runtime_helpers_esm_objectSpread2_js__WEBPACK_IMPORTED_MODULE_7__/* ["default"] */ .A)((0,_Users_ruslanmatkovskyi_projects_gwc_gwchq_textjam_node_modules_babel_runtime_helpers_esm_objectSpread2_js__WEBPACK_IMPORTED_MODULE_7__/* ["default"] */ .A)({
68481
68481
  onClick: e => {
68482
68482
  e.stopPropagation();
68483
68483
  switchToFileTab(panelIndex, fileIndex);
@@ -68495,7 +68495,7 @@ var DraggableTab = _ref => {
68495
68495
  draggableProps,
68496
68496
  dragHandleProps
68497
68497
  } = _ref2;
68498
- return /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsx)("div", (0,_Users_valeriya_projects_gwc_gwchq_textjam_node_modules_babel_runtime_helpers_esm_objectSpread2_js__WEBPACK_IMPORTED_MODULE_7__/* ["default"] */ .A)((0,_Users_valeriya_projects_gwc_gwchq_textjam_node_modules_babel_runtime_helpers_esm_objectSpread2_js__WEBPACK_IMPORTED_MODULE_7__/* ["default"] */ .A)((0,_Users_valeriya_projects_gwc_gwchq_textjam_node_modules_babel_runtime_helpers_esm_objectSpread2_js__WEBPACK_IMPORTED_MODULE_7__/* ["default"] */ .A)({
68498
+ return /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsx)("div", (0,_Users_ruslanmatkovskyi_projects_gwc_gwchq_textjam_node_modules_babel_runtime_helpers_esm_objectSpread2_js__WEBPACK_IMPORTED_MODULE_7__/* ["default"] */ .A)((0,_Users_ruslanmatkovskyi_projects_gwc_gwchq_textjam_node_modules_babel_runtime_helpers_esm_objectSpread2_js__WEBPACK_IMPORTED_MODULE_7__/* ["default"] */ .A)((0,_Users_ruslanmatkovskyi_projects_gwc_gwchq_textjam_node_modules_babel_runtime_helpers_esm_objectSpread2_js__WEBPACK_IMPORTED_MODULE_7__/* ["default"] */ .A)({
68499
68499
  className: "draggable-tab",
68500
68500
  ref: innerRef
68501
68501
  }, draggableProps), dragHandleProps), {}, {
@@ -68517,8 +68517,8 @@ __webpack_require__.r(__webpack_exports__);
68517
68517
  /* harmony export */ __webpack_require__.d(__webpack_exports__, {
68518
68518
  /* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__)
68519
68519
  /* harmony export */ });
68520
- /* harmony import */ var _Users_valeriya_projects_gwc_gwchq_textjam_node_modules_babel_runtime_helpers_esm_objectSpread2_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(89379);
68521
- /* harmony import */ var _Users_valeriya_projects_gwc_gwchq_textjam_node_modules_babel_runtime_helpers_esm_objectWithoutProperties_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(53986);
68520
+ /* harmony import */ var _Users_ruslanmatkovskyi_projects_gwc_gwchq_textjam_node_modules_babel_runtime_helpers_esm_objectSpread2_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(89379);
68521
+ /* harmony import */ var _Users_ruslanmatkovskyi_projects_gwc_gwchq_textjam_node_modules_babel_runtime_helpers_esm_objectWithoutProperties_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(53986);
68522
68522
  /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(51649);
68523
68523
  /* harmony import */ var _hello_pangea_dnd__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(98850);
68524
68524
  /* harmony import */ var react_tabs__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(39243);
@@ -68537,8 +68537,8 @@ var DroppableTabList = _ref => {
68537
68537
  children: _children,
68538
68538
  index
68539
68539
  } = _ref,
68540
- otherProps = (0,_Users_valeriya_projects_gwc_gwchq_textjam_node_modules_babel_runtime_helpers_esm_objectWithoutProperties_js__WEBPACK_IMPORTED_MODULE_4__/* ["default"] */ .A)(_ref, _excluded);
68541
- return /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_3__.jsx)(react_tabs__WEBPACK_IMPORTED_MODULE_1__.TabList, (0,_Users_valeriya_projects_gwc_gwchq_textjam_node_modules_babel_runtime_helpers_esm_objectSpread2_js__WEBPACK_IMPORTED_MODULE_5__/* ["default"] */ .A)((0,_Users_valeriya_projects_gwc_gwchq_textjam_node_modules_babel_runtime_helpers_esm_objectSpread2_js__WEBPACK_IMPORTED_MODULE_5__/* ["default"] */ .A)({}, otherProps), {}, {
68540
+ otherProps = (0,_Users_ruslanmatkovskyi_projects_gwc_gwchq_textjam_node_modules_babel_runtime_helpers_esm_objectWithoutProperties_js__WEBPACK_IMPORTED_MODULE_4__/* ["default"] */ .A)(_ref, _excluded);
68541
+ return /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_3__.jsx)(react_tabs__WEBPACK_IMPORTED_MODULE_1__.TabList, (0,_Users_ruslanmatkovskyi_projects_gwc_gwchq_textjam_node_modules_babel_runtime_helpers_esm_objectSpread2_js__WEBPACK_IMPORTED_MODULE_5__/* ["default"] */ .A)((0,_Users_ruslanmatkovskyi_projects_gwc_gwchq_textjam_node_modules_babel_runtime_helpers_esm_objectSpread2_js__WEBPACK_IMPORTED_MODULE_5__/* ["default"] */ .A)({}, otherProps), {}, {
68542
68542
  children: /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_3__.jsx)(_hello_pangea_dnd__WEBPACK_IMPORTED_MODULE_6__.Droppable, {
68543
68543
  direction: "horizontal",
68544
68544
  droppableId: index.toString(),
@@ -68548,7 +68548,7 @@ var DroppableTabList = _ref => {
68548
68548
  droppableProps,
68549
68549
  placeholder
68550
68550
  } = _ref2;
68551
- return /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_3__.jsxs)("div", (0,_Users_valeriya_projects_gwc_gwchq_textjam_node_modules_babel_runtime_helpers_esm_objectSpread2_js__WEBPACK_IMPORTED_MODULE_5__/* ["default"] */ .A)((0,_Users_valeriya_projects_gwc_gwchq_textjam_node_modules_babel_runtime_helpers_esm_objectSpread2_js__WEBPACK_IMPORTED_MODULE_5__/* ["default"] */ .A)({
68551
+ return /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_3__.jsxs)("div", (0,_Users_ruslanmatkovskyi_projects_gwc_gwchq_textjam_node_modules_babel_runtime_helpers_esm_objectSpread2_js__WEBPACK_IMPORTED_MODULE_5__/* ["default"] */ .A)((0,_Users_ruslanmatkovskyi_projects_gwc_gwchq_textjam_node_modules_babel_runtime_helpers_esm_objectSpread2_js__WEBPACK_IMPORTED_MODULE_5__/* ["default"] */ .A)({
68552
68552
  className: "droppable-tab-list"
68553
68553
  }, droppableProps), {}, {
68554
68554
  ref: innerRef,
@@ -101966,7 +101966,13 @@ var EditorPanel = _ref => {
101966
101966
  });
101967
101967
  var startState = EditorState.create({
101968
101968
  doc: code,
101969
- extensions: [basicSetup, dist_keymap.of([dist_defaultKeymap, indentWithTab]), mode, label, onUpdate, editorTheme, indentationMarkers(), indentUnit.of(customIndentUnit), EditorView.editable.of(!readOnly), EditorView.lineWrapping, limitCharacters]
101969
+ extensions: [basicSetup,
101970
+ // Escape releases the Tab key so keyboard users can move focus out of
101971
+ // the editor instead of being trapped by indentWithTab (WCAG 2.1.2).
101972
+ dist_keymap.of([{
101973
+ key: "Escape",
101974
+ run: temporarilySetTabFocusMode
101975
+ }]), dist_keymap.of([dist_defaultKeymap, indentWithTab]), mode, label, onUpdate, editorTheme, indentationMarkers(), indentUnit.of(customIndentUnit), EditorView.editable.of(!readOnly), EditorView.lineWrapping, limitCharacters]
101970
101976
  });
101971
101977
  var view = new EditorView({
101972
101978
  state: startState,
@@ -102113,8 +102119,8 @@ var Sidebar_default = /*#__PURE__*/__webpack_require__.n(Sidebar);
102113
102119
  // EXTERNAL MODULE: ./src/components/Editor/EditorInput/EditorInput.tsx
102114
102120
  var EditorInput = __webpack_require__(99692);
102115
102121
  var EditorInput_default = /*#__PURE__*/__webpack_require__.n(EditorInput);
102116
- // EXTERNAL MODULE: ./src/utils/ResizableWithHandle.js + 3 modules
102117
- var ResizableWithHandle = __webpack_require__(51535);
102122
+ // EXTERNAL MODULE: ./src/utils/ResizableWithHandle.js + 4 modules
102123
+ var ResizableWithHandle = __webpack_require__(32694);
102118
102124
  ;// ./src/utils/containerQueries.js
102119
102125
  var projContainer = {
102120
102126
  "width-larger-than-720": {
@@ -102187,6 +102193,8 @@ var Project = props => {
102187
102193
  ref: containerRef,
102188
102194
  children: !loading && /*#__PURE__*/(0,jsx_runtime.jsxs)("div", {
102189
102195
  className: styles_module.projEditorWrapper,
102196
+ role: "group",
102197
+ "aria-label": "Code editor and output",
102190
102198
  children: [isCodeVisible && /*#__PURE__*/(0,jsx_runtime.jsx)(ResizableWithHandle["default"], {
102191
102199
  "data-testid": "proj-editor-container",
102192
102200
  className: styles_module.projEditorContainer,
@@ -102256,7 +102264,7 @@ __webpack_require__.r(__webpack_exports__);
102256
102264
  /* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__)
102257
102265
  /* harmony export */ });
102258
102266
  /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(51649);
102259
- /* harmony import */ var _utils_ResizableWithHandle__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(51535);
102267
+ /* harmony import */ var _utils_ResizableWithHandle__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(32694);
102260
102268
  /* harmony import */ var classnames__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(46942);
102261
102269
  /* harmony import */ var classnames__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(classnames__WEBPACK_IMPORTED_MODULE_1__);
102262
102270
  /* harmony import */ var _utils_mediaQueryBreakpoints__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(22554);
@@ -102280,7 +102288,8 @@ var SidebarPanel = props => {
102280
102288
  panelContentClassName,
102281
102289
  buttons = [],
102282
102290
  defaultWidth = "270px",
102283
- maxWidth = "600px"
102291
+ maxWidth = "600px",
102292
+ resizable = true
102284
102293
  } = props;
102285
102294
  var isMobile = (0,react_responsive__WEBPACK_IMPORTED_MODULE_4__.useMediaQuery)({
102286
102295
  query: _utils_mediaQueryBreakpoints__WEBPACK_IMPORTED_MODULE_5__.MOBILE_MEDIA_QUERY
@@ -102303,7 +102312,7 @@ var SidebarPanel = props => {
102303
102312
  children: /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_3__.jsx)(Footer, {})
102304
102313
  })]
102305
102314
  });
102306
- return isMobile ? /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_3__.jsx)("div", {
102315
+ return isMobile || !resizable ? /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_3__.jsx)("div", {
102307
102316
  "data-testid": "sidebar__panel",
102308
102317
  className: classnames__WEBPACK_IMPORTED_MODULE_1___default()(_styles_module_scss__WEBPACK_IMPORTED_MODULE_2__["default"].panel, className, Footer && _styles_module_scss__WEBPACK_IMPORTED_MODULE_2__["default"].panelWithFooter),
102309
102318
  children: panelContent
@@ -103136,7 +103145,7 @@ __webpack_require__.r(__webpack_exports__);
103136
103145
  /* harmony export */ __webpack_require__.d(__webpack_exports__, {
103137
103146
  /* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__)
103138
103147
  /* harmony export */ });
103139
- /* harmony import */ var _Users_valeriya_projects_gwc_gwchq_textjam_node_modules_babel_runtime_helpers_esm_objectSpread2_js__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(89379);
103148
+ /* harmony import */ var _Users_ruslanmatkovskyi_projects_gwc_gwchq_textjam_node_modules_babel_runtime_helpers_esm_objectSpread2_js__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(89379);
103140
103149
  /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(51649);
103141
103150
  /* harmony import */ var react_redux__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(14062);
103142
103151
  /* harmony import */ var _redux_EditorSlice__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(68512);
@@ -103250,7 +103259,7 @@ var ProjectName = _ref => {
103250
103259
  id: "project_name_label",
103251
103260
  className: _styles_module_scss__WEBPACK_IMPORTED_MODULE_3__["default"].projectLabel,
103252
103261
  children: "Project Name"
103253
- }), /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsxs)("div", (0,_Users_valeriya_projects_gwc_gwchq_textjam_node_modules_babel_runtime_helpers_esm_objectSpread2_js__WEBPACK_IMPORTED_MODULE_7__/* ["default"] */ .A)((0,_Users_valeriya_projects_gwc_gwchq_textjam_node_modules_babel_runtime_helpers_esm_objectSpread2_js__WEBPACK_IMPORTED_MODULE_7__/* ["default"] */ .A)({
103262
+ }), /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsxs)("div", (0,_Users_ruslanmatkovskyi_projects_gwc_gwchq_textjam_node_modules_babel_runtime_helpers_esm_objectSpread2_js__WEBPACK_IMPORTED_MODULE_7__/* ["default"] */ .A)((0,_Users_ruslanmatkovskyi_projects_gwc_gwchq_textjam_node_modules_babel_runtime_helpers_esm_objectSpread2_js__WEBPACK_IMPORTED_MODULE_7__/* ["default"] */ .A)({
103254
103263
  className: classnames__WEBPACK_IMPORTED_MODULE_2___default()(_styles_module_scss__WEBPACK_IMPORTED_MODULE_3__["default"].projectName, className)
103255
103264
  }, hoverProps), {}, {
103256
103265
  children: [/*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsx)((components_Tooltip_Tooltip__WEBPACK_IMPORTED_MODULE_8___default()), {
@@ -106407,7 +106416,7 @@ var defaultMZCriteria = {
106407
106416
 
106408
106417
  /***/ }),
106409
106418
 
106410
- /***/ 51535:
106419
+ /***/ 32694:
106411
106420
  /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
106412
106421
 
106413
106422
  // ESM COMPAT FLAG
@@ -107258,6 +107267,233 @@ var Resizable = /** @class */ (function (_super) {
107258
107267
  }(external_react_.PureComponent));
107259
107268
 
107260
107269
 
107270
+ ;// ./src/hooks/useResizableSeparator.js
107271
+
107272
+
107273
+
107274
+ // Minimum keyboard step, and how many steps span the whole resize range.
107275
+ var MIN_STEP_PX = 16;
107276
+ var STEPS_PER_RANGE = 20;
107277
+
107278
+ // The arrow that grows the panel points toward the edge the handle sits on
107279
+ // (dragging that edge outward enlarges the panel), so the keys depend on the
107280
+ // handle position, not just the axis.
107281
+ var ARROW_KEYS = {
107282
+ right: {
107283
+ increase: "ArrowRight",
107284
+ decrease: "ArrowLeft"
107285
+ },
107286
+ left: {
107287
+ increase: "ArrowLeft",
107288
+ decrease: "ArrowRight"
107289
+ },
107290
+ bottom: {
107291
+ increase: "ArrowDown",
107292
+ decrease: "ArrowUp"
107293
+ },
107294
+ top: {
107295
+ increase: "ArrowUp",
107296
+ decrease: "ArrowDown"
107297
+ }
107298
+ };
107299
+ var useResizableSeparator_clamp = (value, min, max) => Math.min(Math.max(value, min), max);
107300
+
107301
+ // Resolve a re-resizable size bound ("250px", "25%", 300, undefined) to pixels.
107302
+ // Percentages are relative to the parent along the resize axis; px/unitless are
107303
+ // taken as-is. Returns `fallback` when the value is absent or unparseable.
107304
+ var toPixels = (value, parentSize, fallback) => {
107305
+ if (value == null) return fallback;
107306
+ if (typeof value === "number") return value;
107307
+ var text = String(value).trim();
107308
+ var number = parseFloat(text);
107309
+ if (Number.isNaN(number)) return fallback;
107310
+ return text.endsWith("%") ? number / 100 * parentSize : number;
107311
+ };
107312
+
107313
+ // Owns the resize state for ResizableWithHandle and exposes an accessible,
107314
+ // keyboard-operable separator (WCAG 2.1.1). A vertical handle bar (right/left)
107315
+ // resizes width; a horizontal bar (top/bottom) resizes height. All resizing is
107316
+ // done in pixels against the consumer's own min/max bounds, so it works both
107317
+ // for percentage splits (code/output) and fixed panels (the file sidebar).
107318
+ //
107319
+ // Width can optionally be CONTROLLED by the consumer: pass `controlledWidth`
107320
+ // plus `onWidthChange` and the hook stops owning the width — it renders the
107321
+ // consumer's value and reports drag/keyboard changes through the callback. The
107322
+ // internal "keep the manual size across default changes" logic is bypassed for
107323
+ // that axis, so a parent that drives the width programmatically (e.g. an
107324
+ // expand/collapse toggle) always wins, even after a manual drag.
107325
+ var useResizableSeparator = _ref => {
107326
+ var {
107327
+ handleDirection,
107328
+ defaultWidth,
107329
+ defaultHeight,
107330
+ minWidth,
107331
+ maxWidth,
107332
+ minHeight,
107333
+ maxHeight,
107334
+ controlledWidth,
107335
+ onWidthChange
107336
+ } = _ref;
107337
+ var isWidthControlled = controlledWidth !== undefined;
107338
+ var [internalWidth, setInternalWidth] = (0,external_react_.useState)(defaultWidth !== null && defaultWidth !== void 0 ? defaultWidth : "auto");
107339
+ var [height, setHeight] = (0,external_react_.useState)(defaultHeight !== null && defaultHeight !== void 0 ? defaultHeight : "auto");
107340
+ // Position within the allowed range, 0 (min) – 100 (max), for aria-valuenow.
107341
+ var [valueNow, setValueNow] = (0,external_react_.useState)(50);
107342
+
107343
+ // Effective width and setter route to the consumer when width is controlled.
107344
+ var width = isWidthControlled ? controlledWidth : internalWidth;
107345
+ var setWidth = (0,external_react_.useCallback)(next => {
107346
+ if (isWidthControlled) {
107347
+ if (onWidthChange) onWidthChange(next);
107348
+ } else {
107349
+ setInternalWidth(next);
107350
+ }
107351
+ }, [isWidthControlled, onWidthChange]);
107352
+ var resizableRef = (0,external_react_.useRef)(null);
107353
+ var isVerticalHandle = ["right", "left"].includes(handleDirection);
107354
+
107355
+ // True once the user drags/keys the draggable axis; keeps their size on resize.
107356
+ var hasManuallyResizedRef = (0,external_react_.useRef)(false);
107357
+
107358
+ // Current pixel size plus the pixel bounds for the active axis.
107359
+ var getMetrics = (0,external_react_.useCallback)(() => {
107360
+ var _instance$resizable;
107361
+ var instance = resizableRef.current;
107362
+ var parent = instance === null || instance === void 0 || (_instance$resizable = instance.resizable) === null || _instance$resizable === void 0 ? void 0 : _instance$resizable.parentElement;
107363
+ if (!instance || !parent) return null;
107364
+ var parentSize = isVerticalHandle ? parent.clientWidth : parent.clientHeight;
107365
+ if (!parentSize) return null;
107366
+ var currentPx = isVerticalHandle ? instance.size.width : instance.size.height;
107367
+ var minPx = toPixels(isVerticalHandle ? minWidth : minHeight, parentSize, 0);
107368
+ var maxPx = toPixels(isVerticalHandle ? maxWidth : maxHeight, parentSize, parentSize);
107369
+ return {
107370
+ currentPx,
107371
+ minPx,
107372
+ maxPx
107373
+ };
107374
+ }, [isVerticalHandle, minWidth, maxWidth, minHeight, maxHeight]);
107375
+ var positionPercent = _ref2 => {
107376
+ var {
107377
+ currentPx,
107378
+ minPx,
107379
+ maxPx
107380
+ } = _ref2;
107381
+ return maxPx <= minPx ? 100 : useResizableSeparator_clamp(Math.round((currentPx - minPx) / (maxPx - minPx) * 100), 0, 100);
107382
+ };
107383
+
107384
+ // Keep aria-valuenow in sync whenever the rendered size changes (drag included).
107385
+ (0,external_react_.useEffect)(() => {
107386
+ var metrics = getMetrics();
107387
+ if (metrics) setValueNow(positionPercent(metrics));
107388
+ }, [getMetrics, width, height]);
107389
+ var applyPixels = (0,external_react_.useCallback)((px, metrics) => {
107390
+ var clamped = Math.round(useResizableSeparator_clamp(px, metrics.minPx, metrics.maxPx));
107391
+ if (isVerticalHandle) setWidth(clamped);else setHeight(clamped);
107392
+ setValueNow(positionPercent((0,objectSpread2/* default */.A)((0,objectSpread2/* default */.A)({}, metrics), {}, {
107393
+ currentPx: clamped
107394
+ })));
107395
+ }, [isVerticalHandle, setWidth]);
107396
+
107397
+ // When the draggable axis flips (e.g. the editor's right/bottom handle at the
107398
+ // 720px breakpoint), forget the manual size so the new layout gets its default
107399
+ // split. Declared before the sync effects so the reset wins that same render.
107400
+ (0,external_react_.useEffect)(() => {
107401
+ hasManuallyResizedRef.current = false;
107402
+ }, [isVerticalHandle]);
107403
+
107404
+ // Sync each axis with its default when that default changes (breakpoint or
107405
+ // window resize). If the user has dragged the draggable axis, keep their size
107406
+ // and just re-clamp it; otherwise follow the default. Skipped for a controlled
107407
+ // width — the consumer owns it entirely.
107408
+ (0,external_react_.useEffect)(() => {
107409
+ if (isWidthControlled) return;
107410
+ if (isVerticalHandle && hasManuallyResizedRef.current) {
107411
+ var metrics = getMetrics();
107412
+ if (metrics) applyPixels(metrics.currentPx, metrics);
107413
+ } else {
107414
+ setInternalWidth(defaultWidth !== null && defaultWidth !== void 0 ? defaultWidth : "auto");
107415
+ }
107416
+ }, [defaultWidth, isVerticalHandle, getMetrics, applyPixels, isWidthControlled]);
107417
+ (0,external_react_.useEffect)(() => {
107418
+ if (!isVerticalHandle && hasManuallyResizedRef.current) {
107419
+ var metrics = getMetrics();
107420
+ if (metrics) applyPixels(metrics.currentPx, metrics);
107421
+ } else {
107422
+ setHeight(defaultHeight !== null && defaultHeight !== void 0 ? defaultHeight : "auto");
107423
+ }
107424
+ }, [defaultHeight, isVerticalHandle, getMetrics, applyPixels]);
107425
+
107426
+ // On drag end, persist the real rendered size (px) of the resized axis only,
107427
+ // leaving the other axis at its default (e.g. height "100%").
107428
+ var onResizeStop = () => {
107429
+ var metrics = getMetrics();
107430
+ if (!metrics) return;
107431
+ hasManuallyResizedRef.current = true;
107432
+ if (isVerticalHandle) setWidth(metrics.currentPx);else setHeight(metrics.currentPx);
107433
+ };
107434
+
107435
+ // Explicit reset (double-click / Enter / Space): back to default, resume tracking.
107436
+ var resetSize = () => {
107437
+ hasManuallyResizedRef.current = false;
107438
+ if (isVerticalHandle) setWidth(defaultWidth);else setHeight(defaultHeight);
107439
+ };
107440
+ var onKeyDown = event => {
107441
+ var _ARROW_KEYS$handleDir;
107442
+ var metrics = getMetrics();
107443
+ if (!metrics) return;
107444
+ var {
107445
+ increase,
107446
+ decrease
107447
+ } = (_ARROW_KEYS$handleDir = ARROW_KEYS[handleDirection]) !== null && _ARROW_KEYS$handleDir !== void 0 ? _ARROW_KEYS$handleDir : {};
107448
+ var step = Math.max(MIN_STEP_PX, Math.round((metrics.maxPx - metrics.minPx) / STEPS_PER_RANGE));
107449
+ var nextPx;
107450
+ switch (event.key) {
107451
+ case decrease:
107452
+ nextPx = metrics.currentPx - step;
107453
+ break;
107454
+ case increase:
107455
+ nextPx = metrics.currentPx + step;
107456
+ break;
107457
+ case "Home":
107458
+ nextPx = metrics.minPx;
107459
+ break;
107460
+ case "End":
107461
+ nextPx = metrics.maxPx;
107462
+ break;
107463
+ case "Enter":
107464
+ case " ":
107465
+ event.preventDefault();
107466
+ resetSize();
107467
+ return;
107468
+ default:
107469
+ return;
107470
+ }
107471
+ event.preventDefault();
107472
+ hasManuallyResizedRef.current = true;
107473
+ applyPixels(nextPx, metrics);
107474
+ };
107475
+ var separatorProps = {
107476
+ role: "separator",
107477
+ tabIndex: 0,
107478
+ "aria-label": "Resize panels",
107479
+ "aria-orientation": isVerticalHandle ? "vertical" : "horizontal",
107480
+ "aria-valuemin": 0,
107481
+ "aria-valuemax": 100,
107482
+ "aria-valuenow": valueNow,
107483
+ "aria-valuetext": "".concat(valueNow, "%"),
107484
+ onKeyDown,
107485
+ onDoubleClick: resetSize
107486
+ };
107487
+ return {
107488
+ resizableRef,
107489
+ size: {
107490
+ width,
107491
+ height
107492
+ },
107493
+ onResizeStop,
107494
+ separatorProps
107495
+ };
107496
+ };
107261
107497
  ;// ./src/assets/stylesheets/ResizableWithHandle.scss
107262
107498
  // extracted by mini-css-extract-plugin
107263
107499
 
@@ -107266,87 +107502,50 @@ var jsx_runtime = __webpack_require__(74848);
107266
107502
  ;// ./src/utils/ResizableWithHandle.js
107267
107503
 
107268
107504
 
107269
- var _excluded = ["children", "defaultWidth", "defaultHeight", "handleDirection", "handleClassName"];
107505
+ var _excluded = ["children", "defaultWidth", "defaultHeight", "handleDirection", "handleClassName", "width", "onWidthChange"];
107270
107506
 
107271
107507
 
107272
107508
 
107273
107509
 
107274
- var VerticalHandle = _ref => {
107275
- var {
107276
- className
107277
- } = _ref;
107278
- return /*#__PURE__*/(0,jsx_runtime.jsx)("svg", {
107279
- "data-testid": "verticalHandle",
107280
- width: "44",
107281
- height: "56",
107282
- viewBox: "0 0 44 56",
107283
- fill: "none",
107284
- xmlns: "http://www.w3.org/2000/svg",
107285
- className: className,
107286
- children: /*#__PURE__*/(0,jsx_runtime.jsx)("rect", {
107287
- x: "20",
107288
- width: "4",
107289
- height: "56",
107290
- rx: "2",
107291
- fill: "#616575"
107292
- })
107293
- });
107294
- };
107295
- var HorizontalHandle = _ref2 => {
107296
- var {
107297
- className
107298
- } = _ref2;
107299
- return /*#__PURE__*/(0,jsx_runtime.jsx)("svg", {
107300
- "data-testid": "horizontalHandle",
107301
- width: "56",
107302
- height: "44",
107303
- viewBox: "0 0 56 44",
107304
- fill: "none",
107305
- xmlns: "http://www.w3.org/2000/svg",
107306
- className: className,
107307
- children: /*#__PURE__*/(0,jsx_runtime.jsx)("rect", {
107308
- x: "56",
107309
- y: "20",
107310
- width: "4",
107311
- height: "56",
107312
- rx: "2",
107313
- transform: "rotate(90 56 20)",
107314
- fill: "#616575"
107315
- })
107316
- });
107317
- };
107318
107510
  var ResizableWithHandle = props => {
107319
107511
  var {
107320
107512
  children,
107321
107513
  defaultWidth,
107322
107514
  defaultHeight,
107323
107515
  handleDirection,
107324
- handleClassName
107516
+ handleClassName,
107517
+ // Optional controlled width: when `width` is provided the consumer owns it
107518
+ // and receives drag/keyboard changes via `onWidthChange`. Destructured out
107519
+ // so they never leak onto <Resizable> through ...rest.
107520
+ width: controlledWidth,
107521
+ onWidthChange
107325
107522
  } = props,
107326
107523
  rest = (0,objectWithoutProperties/* default */.A)(props, _excluded);
107327
- var [width, setWidth] = (0,external_react_.useState)("auto");
107328
- var [height, setHeight] = (0,external_react_.useState)("auto");
107329
- (0,external_react_.useMemo)(() => setWidth(defaultWidth), [defaultWidth]);
107330
- (0,external_react_.useMemo)(() => setHeight(defaultHeight), [defaultHeight]);
107331
- var onResizeStop = function onResizeStop() {
107332
- for (var _len = arguments.length, _ref3 = new Array(_len), _key = 0; _key < _len; _key++) {
107333
- _ref3[_key] = arguments[_key];
107334
- }
107335
- var [,,, d] = _ref3;
107336
- setWidth(width + d.width);
107337
- setHeight(height + d.height);
107338
- };
107339
- var handleComponent = ["right", "left"].includes(handleDirection) ? {
107340
- [handleDirection]: /*#__PURE__*/(0,jsx_runtime.jsx)(VerticalHandle, {
107341
- className: handleClassName
107342
- })
107343
- } : ["top", "bottom"].includes(handleDirection) ? {
107344
- [handleDirection]: /*#__PURE__*/(0,jsx_runtime.jsx)(HorizontalHandle, {
107345
- className: handleClassName
107346
- })
107347
- } : {};
107524
+ var {
107525
+ resizableRef,
107526
+ size,
107527
+ onResizeStop,
107528
+ separatorProps
107529
+ } = useResizableSeparator({
107530
+ handleDirection,
107531
+ defaultWidth,
107532
+ defaultHeight,
107533
+ // min/max bounds are also forwarded to <Resizable> via ...rest for drag.
107534
+ minWidth: rest.minWidth,
107535
+ maxWidth: rest.maxWidth,
107536
+ minHeight: rest.minHeight,
107537
+ maxHeight: rest.maxHeight,
107538
+ controlledWidth,
107539
+ onWidthChange
107540
+ });
107348
107541
  var handleWrapperClass = "resizable-with-handle__handle resizable-with-handle__handle--".concat(handleDirection);
107542
+ var handleComponent = {
107543
+ [handleDirection]: /*#__PURE__*/(0,jsx_runtime.jsx)("span", (0,objectSpread2/* default */.A)({
107544
+ className: "resizable-with-handle__bar resizable-with-handle__bar--".concat(handleDirection)
107545
+ }, separatorProps))
107546
+ };
107349
107547
  return /*#__PURE__*/(0,jsx_runtime.jsx)(Resizable, (0,objectSpread2/* default */.A)((0,objectSpread2/* default */.A)({
107548
+ ref: resizableRef,
107350
107549
  enable: (0,objectSpread2/* default */.A)({
107351
107550
  top: false,
107352
107551
  right: false,
@@ -107355,20 +107554,21 @@ var ResizableWithHandle = props => {
107355
107554
  }, {
107356
107555
  [handleDirection]: true
107357
107556
  }),
107358
- handleComponent: handleComponent,
107359
107557
  handleWrapperClass: handleWrapperClass,
107360
107558
  onResizeStop: onResizeStop,
107361
- size: {
107362
- width: width,
107363
- height: height
107364
- },
107559
+ size: size,
107365
107560
  handleStyles: {
107561
+ top: {
107562
+ top: "-10px"
107563
+ },
107366
107564
  right: {
107367
107565
  height: "96%",
107368
- top: "2%"
107566
+ top: "2%",
107567
+ marginRight: "-13px"
107369
107568
  }
107370
107569
  },
107371
- handleClasses: handleClassName
107570
+ handleClasses: handleClassName,
107571
+ handleComponent: handleComponent
107372
107572
  }, rest), {}, {
107373
107573
  children: children
107374
107574
  }));
@@ -143054,7 +143254,7 @@ __webpack_require__.r(__webpack_exports__);
143054
143254
  /* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__)
143055
143255
  /* harmony export */ });
143056
143256
  // extracted by mini-css-extract-plugin
143057
- /* 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","pythonrunnerContainer":"styles-module__pythonrunnerContainer---nPGz","htmlrunnerContainer":"styles-module__htmlrunnerContainer--KPi8D","resizeHandleContainer":"styles-module__resizeHandleContainer--FID6p","console":"styles-module__console--B8x1A","pythonGraphic":"styles-module__pythonGraphic--V+du6","pyodiderunner":"styles-module__pyodiderunner--DKTtB","active":"styles-module__active--bKVnT","visualOutput":"styles-module__visualOutput--WLaca","outputPanel":"styles-module__outputPanel--W1TnW","pythonConsoleLine":"styles-module__pythonConsoleLine--bwFQC","stderr":"styles-module__stderr--PIyhL","pythonrunnerInput":"styles-module__pythonrunnerInput--dOXEa","webConsole":"styles-module__webConsole--MTdnk","webConsoleLine":"styles-module__webConsoleLine--j31U1","consoleMessageContent":"styles-module__consoleMessageContent--vXp1a","consoleMessageInline":"styles-module__consoleMessageInline--5pNbC","consoleCount":"styles-module__consoleCount--JLMn5","string":"styles-module__string--YDxWc","stringInsideStructure":"styles-module__stringInsideStructure--KJtZp","number":"styles-module__number--7h5TG","boolean":"styles-module__boolean--WYb4I","null":"styles-module__null--BZCmR","undefined":"styles-module__undefined--nQkol","key":"styles-module__key--ZZTlD","error":"styles-module__error--RL5Lt","errorIcon":"styles-module__errorIcon--Gnn4-","warn":"styles-module__warn--B+MPR","warnIcon":"styles-module__warnIcon--Q82Y5","timestamp":"styles-module__timestamp--HDZxD","details":"styles-module__details--W1bGT","summary":"styles-module__summary--vms5d","nestedEntry":"styles-module__nestedEntry--OU25X","previewLink":"styles-module__previewLink--rWLII","previewHtml":"styles-module__previewHtml--oWG28","iframe":"styles-module__iframe--YfQ0V","codeHasBeenRun":"styles-module__codeHasBeenRun--fqiYL"});
143257
+ /* 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","pythonrunnerContainer":"styles-module__pythonrunnerContainer---nPGz","htmlrunnerContainer":"styles-module__htmlrunnerContainer--KPi8D","resizeHandleContainer":"styles-module__resizeHandleContainer--FID6p","console":"styles-module__console--B8x1A","pythonGraphic":"styles-module__pythonGraphic--V+du6","pyodiderunner":"styles-module__pyodiderunner--DKTtB","active":"styles-module__active--bKVnT","visualOutput":"styles-module__visualOutput--WLaca","outputPanel":"styles-module__outputPanel--W1TnW","pythonConsoleLine":"styles-module__pythonConsoleLine--bwFQC","stderr":"styles-module__stderr--PIyhL","pythonrunnerInput":"styles-module__pythonrunnerInput--dOXEa","webConsole":"styles-module__webConsole--MTdnk","webConsoleLine":"styles-module__webConsoleLine--j31U1","consoleMessageContent":"styles-module__consoleMessageContent--vXp1a","consoleMessageInline":"styles-module__consoleMessageInline--5pNbC","consoleCount":"styles-module__consoleCount--JLMn5","string":"styles-module__string--YDxWc","stringInsideStructure":"styles-module__stringInsideStructure--KJtZp","number":"styles-module__number--7h5TG","boolean":"styles-module__boolean--WYb4I","null":"styles-module__null--BZCmR","undefined":"styles-module__undefined--nQkol","key":"styles-module__key--ZZTlD","error":"styles-module__error--RL5Lt","errorIcon":"styles-module__errorIcon--Gnn4-","warn":"styles-module__warn--B+MPR","warnIcon":"styles-module__warnIcon--Q82Y5","timestamp":"styles-module__timestamp--HDZxD","details":"styles-module__details--W1bGT","summary":"styles-module__summary--vms5d","nestedEntry":"styles-module__nestedEntry--OU25X","previewLink":"styles-module__previewLink--rWLII","previewHtml":"styles-module__previewHtml--oWG28","iframe":"styles-module__iframe--YfQ0V","codeHasBeenRun":"styles-module__codeHasBeenRun--fqiYL","focused":"styles-module__focused--a+AT8"});
143058
143258
 
143059
143259
  /***/ }),
143060
143260
 
@@ -143126,7 +143326,7 @@ __webpack_require__.r(__webpack_exports__);
143126
143326
  /* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__)
143127
143327
  /* harmony export */ });
143128
143328
  // extracted by mini-css-extract-plugin
143129
- /* 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","row":"styles-module__row--XtAJr","treeItem":"styles-module__treeItem--kbyYk","itemContent":"styles-module__itemContent--663gn","itemButton":"styles-module__itemButton--oB91C","itemTitle":"styles-module__itemTitle--a6Liw","itemFocused":"styles-module__itemFocused--SmUbE","level-0":"styles-module__level-0--5PVOR","level-1":"styles-module__level-1--vTMYv","level-2":"styles-module__level-2--Xx8Ie","level-3":"styles-module__level-3--ySuP4","level-4":"styles-module__level-4--7uciy","level-5":"styles-module__level-5--AEspR","level-6":"styles-module__level-6--c28W9","level-7":"styles-module__level-7--gU++Q","level-8":"styles-module__level-8--dmg5P","level-9":"styles-module__level-9--X73V0","level-10":"styles-module__level-10--VAKPI","level-11":"styles-module__level-11--5rqim","level-12":"styles-module__level-12--8cLBy","level-13":"styles-module__level-13--qlzYN","level-14":"styles-module__level-14--Q7bCT","level-15":"styles-module__level-15---1KaW","level-16":"styles-module__level-16--BCkZb","level-17":"styles-module__level-17--tM3Ht","level-18":"styles-module__level-18--GkyjF","level-19":"styles-module__level-19--kxLhM","level-20":"styles-module__level-20--sIjDn","itemContentEdit":"styles-module__itemContentEdit--cNmeE","inputError":"styles-module__inputError--zb21a","errorMessage":"styles-module__errorMessage--IT3hQ"});
143329
+ /* 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","row":"styles-module__row--XtAJr","treeItem":"styles-module__treeItem--kbyYk","itemContent":"styles-module__itemContent--663gn","itemTitle":"styles-module__itemTitle--a6Liw","itemButton":"styles-module__itemButton--oB91C","itemFocused":"styles-module__itemFocused--SmUbE","level-0":"styles-module__level-0--5PVOR","level-1":"styles-module__level-1--vTMYv","level-2":"styles-module__level-2--Xx8Ie","level-3":"styles-module__level-3--ySuP4","level-4":"styles-module__level-4--7uciy","level-5":"styles-module__level-5--AEspR","level-6":"styles-module__level-6--c28W9","level-7":"styles-module__level-7--gU++Q","level-8":"styles-module__level-8--dmg5P","level-9":"styles-module__level-9--X73V0","level-10":"styles-module__level-10--VAKPI","level-11":"styles-module__level-11--5rqim","level-12":"styles-module__level-12--8cLBy","level-13":"styles-module__level-13--qlzYN","level-14":"styles-module__level-14--Q7bCT","level-15":"styles-module__level-15---1KaW","level-16":"styles-module__level-16--BCkZb","level-17":"styles-module__level-17--tM3Ht","level-18":"styles-module__level-18--GkyjF","level-19":"styles-module__level-19--kxLhM","level-20":"styles-module__level-20--sIjDn","itemContentEdit":"styles-module__itemContentEdit--cNmeE","inputError":"styles-module__inputError--zb21a","errorMessage":"styles-module__errorMessage--IT3hQ"});
143130
143330
 
143131
143331
  /***/ }),
143132
143332
 
@@ -143150,7 +143350,7 @@ __webpack_require__.r(__webpack_exports__);
143150
143350
  /* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__)
143151
143351
  /* harmony export */ });
143152
143352
  // extracted by mini-css-extract-plugin
143153
- /* 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","sidebar":"styles-module__sidebar--Fu5Qi","bar":"styles-module__bar--fOlLf","optionsTop":"styles-module__optionsTop--2jNbs","optionsBottom":"styles-module__optionsBottom--PtwF4","option":"styles-module__option--MOGCu","userOption":"styles-module__userOption--qGt5j","selected":"styles-module__selected--avjSK","panel":"styles-module__panel--bHhSv","panelWithFooter":"styles-module__panelWithFooter--5ItsY","panelHeading":"styles-module__panelHeading--GRNt0","panelHeader":"styles-module__panelHeader--bAHfN","panelContent":"styles-module__panelContent--X7kot","panelButtons":"styles-module__panelButtons---y4S-","panelFooter":"styles-module__panelFooter--T1y-s"});
143353
+ /* 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","sidebar":"styles-module__sidebar--Fu5Qi","bar":"styles-module__bar--fOlLf","optionsTop":"styles-module__optionsTop--2jNbs","optionsBottom":"styles-module__optionsBottom--PtwF4","option":"styles-module__option--MOGCu","userOption":"styles-module__userOption--qGt5j","selected":"styles-module__selected--avjSK","panel":"styles-module__panel--bHhSv","panelsColumn":"styles-module__panelsColumn--3qf+3","panelsColumnTop":"styles-module__panelsColumnTop--OKT+V","panelsColumnFill":"styles-module__panelsColumnFill--v+VWp","panelWithFooter":"styles-module__panelWithFooter--5ItsY","panelHeading":"styles-module__panelHeading--GRNt0","panelHeader":"styles-module__panelHeader--bAHfN","panelContent":"styles-module__panelContent--X7kot","panelButtons":"styles-module__panelButtons---y4S-","panelFooter":"styles-module__panelFooter--T1y-s"});
143154
143354
 
143155
143355
  /***/ }),
143156
143356
 
@@ -369222,11 +369422,33 @@ const EditorInput = () => {
369222
369422
  }));
369223
369423
  }
369224
369424
  };
369425
+ // Panel whose tab was just closed; used to restore keyboard focus to the
369426
+ // newly-selected tab after re-render so focus is never dropped to <body>
369427
+ // (WCAG 2.4.3 Focus Order).
369428
+ const pendingFocusPanel = (0, react_1.useRef)(null);
369225
369429
  const closeFileTab = (e, file) => {
369226
369430
  e.stopPropagation();
369431
+ pendingFocusPanel.current = openedFilesIdsPanels.findIndex((ids) => ids.includes(file.id));
369227
369432
  dispatch((0, EditorSlice_1.closeFile)({ id: file.id }));
369228
369433
  };
369229
369434
  const tabRefs = (0, react_1.useRef)(projectFiles.map(() => (0, react_1.createRef)()));
369435
+ // After a tab closes, move focus to the tab that becomes selected.
369436
+ (0, react_1.useEffect)(() => {
369437
+ const panelIndex = pendingFocusPanel.current;
369438
+ if (panelIndex === null || panelIndex < 0)
369439
+ return;
369440
+ pendingFocusPanel.current = null;
369441
+ const panel = openedFilesIdsPanels[panelIndex];
369442
+ if (!panel || panel.length === 0)
369443
+ return;
369444
+ const focusIndex = focussedFileIndices[panelIndex] ?? 0;
369445
+ const fileId = panel[focusIndex];
369446
+ if (!fileId)
369447
+ return;
369448
+ const refIndex = projectFiles.findIndex((file) => file.id === fileId);
369449
+ const tabElement = tabRefs.current[refIndex]?.current?.closest('[role="tab"]');
369450
+ tabElement?.focus();
369451
+ }, [openedFilesIdsPanels, focussedFileIndices, projectFiles]);
369230
369452
  (0, react_1.useEffect)(() => {
369231
369453
  if (!project?.components)
369232
369454
  return;
@@ -369265,8 +369487,8 @@ const EditorInput = () => {
369265
369487
  const readonlyTooltipTop = (readonlyRef.current?.offsetTop ?? 0) +
369266
369488
  (readonlyRef.current?.offsetHeight ?? 0);
369267
369489
  const readonlyTooltipRight = readonlyRef.current?.offsetLeft ?? 0;
369268
- return ((0, jsx_runtime_1.jsx)(dnd_1.DragDropContext, { onDragStart: (input) => onDragStart(input), onDragEnd: (result) => onDragEnd(result), children: (0, jsx_runtime_1.jsx)("div", { className: styles_module_scss_1.default.editorInput, children: openedFiles.map((panel, panelIndex) => ((0, jsx_runtime_1.jsxs)(react_tabs_1.Tabs, { selectedIndex: focussedFileIndices[panelIndex], children: [(0, jsx_runtime_1.jsxs)("div", { className: "tabContainer", children: [(0, jsx_runtime_1.jsx)("div", { className: styles_module_scss_1.default.scrollWrapper, children: (0, jsx_runtime_1.jsx)(DroppableTabList_1.default, { index: panelIndex, children: panel.map((file, fileIndex) => file && ((0, jsx_runtime_1.jsxs)(DraggableTab_1.default, { fileIndex: fileIndex, panelIndex: panelIndex, children: [(0, jsx_runtime_1.jsxs)(Text_1.Text, { size: 12, weight: "medium", className: "tabText", tooltip: file.fullName, ref: tabRefs.current[projectFiles.findIndex((projectFile) => projectFile.id === file.id)], children: [(0, jsx_runtime_1.jsx)(FileIcon_1.default, { ext: file.extension }), (0, projectHelpers_1.truncateFileName)(file.fullName)] }), defaultFileName &&
369269
- file.fullName !== defaultFileName && ((0, jsx_runtime_1.jsx)(Button_1.default, { className: styles_module_scss_1.default.closeBtn, label: "close", onClickHandler: (e) => closeFileTab(e, file), ButtonIcon: () => ((0, jsx_runtime_1.jsx)(CloseIcon_1.default, { scaleFactor: 0.85 })) }))] }, file.id))) }) }), isMobile && (0, jsx_runtime_1.jsx)(RunnerControls_1.default, {})] }), panel.map((file, i) => file ? ((0, jsx_runtime_1.jsxs)(react_tabs_1.TabPanel, { children: [readOnly && ((0, jsx_runtime_1.jsxs)("div", { className: styles_module_scss_1.default.readOnlyContainer, ...hoverProps, children: [(0, jsx_runtime_1.jsx)(Tooltip_1.default, { message: "View mode. Remix to edit.", visible: hovered, position: "fixed", style: {
369490
+ return ((0, jsx_runtime_1.jsx)(dnd_1.DragDropContext, { onDragStart: (input) => onDragStart(input), onDragEnd: (result) => onDragEnd(result), children: (0, jsx_runtime_1.jsx)("div", { className: styles_module_scss_1.default.editorInput, role: "region", "aria-label": "Code editor", children: openedFiles.map((panel, panelIndex) => ((0, jsx_runtime_1.jsxs)(react_tabs_1.Tabs, { selectedIndex: focussedFileIndices[panelIndex], children: [(0, jsx_runtime_1.jsxs)("div", { className: "tabContainer", children: [(0, jsx_runtime_1.jsx)("div", { className: styles_module_scss_1.default.scrollWrapper, children: (0, jsx_runtime_1.jsx)(DroppableTabList_1.default, { index: panelIndex, children: panel.map((file, fileIndex) => file && ((0, jsx_runtime_1.jsxs)(DraggableTab_1.default, { fileIndex: fileIndex, panelIndex: panelIndex, children: [(0, jsx_runtime_1.jsxs)(Text_1.Text, { size: 12, weight: "medium", className: "tabText", tooltip: file.fullName, ref: tabRefs.current[projectFiles.findIndex((projectFile) => projectFile.id === file.id)], children: [(0, jsx_runtime_1.jsx)(FileIcon_1.default, { ext: file.extension }), (0, projectHelpers_1.truncateFileName)(file.fullName)] }), defaultFileName &&
369491
+ file.fullName !== defaultFileName && ((0, jsx_runtime_1.jsx)(Button_1.default, { className: styles_module_scss_1.default.closeBtn, label: `Close ${file.fullName}`, onClickHandler: (e) => closeFileTab(e, file), ButtonIcon: () => ((0, jsx_runtime_1.jsx)(CloseIcon_1.default, { scaleFactor: 0.85 })) }))] }, file.id))) }) }), isMobile && (0, jsx_runtime_1.jsx)(RunnerControls_1.default, {})] }), panel.map((file, i) => file ? ((0, jsx_runtime_1.jsxs)(react_tabs_1.TabPanel, { children: [readOnly && ((0, jsx_runtime_1.jsxs)("div", { className: styles_module_scss_1.default.readOnlyContainer, ...hoverProps, children: [(0, jsx_runtime_1.jsx)(Tooltip_1.default, { message: "View mode. Remix to edit.", visible: hovered, position: "fixed", style: {
369270
369492
  top: readonlyTooltipTop,
369271
369493
  right: readonlyTooltipRight,
369272
369494
  } }), (0, jsx_runtime_1.jsxs)("div", { className: styles_module_scss_1.default.readOnlyBanner, ref: readonlyRef, children: [(0, jsx_runtime_1.jsx)(SvgIcon_1.SvgIcon, { size: 12, SvgElement: readonly_svg_1.default }), (0, jsx_runtime_1.jsx)("span", { children: "Read Only" })] })] })), (0, jsx_runtime_1.jsx)(EditorPanel_1.default, { file: file }, file.id)] }, file.id)) : null)] }, panelIndex))) }) }));
@@ -369350,7 +369572,7 @@ const ErrorMessage = () => {
369350
369572
  }, [error]);
369351
369573
  if (!error)
369352
369574
  return null;
369353
- return ((0, jsx_runtime_1.jsxs)("div", { className: styles_module_scss_1.default.container, children: [(0, jsx_runtime_1.jsx)("pre", { ref: ref, className: styles_module_scss_1.default.message }), (0, jsx_runtime_1.jsx)(ErrorDetails_1.ErrorDetails, {})] }));
369575
+ return ((0, jsx_runtime_1.jsxs)("div", { className: styles_module_scss_1.default.container, role: "alert", "aria-label": "Run error", children: [(0, jsx_runtime_1.jsx)("pre", { ref: ref, className: styles_module_scss_1.default.message }), (0, jsx_runtime_1.jsx)(ErrorDetails_1.ErrorDetails, {})] }));
369354
369576
  };
369355
369577
  exports["default"] = ErrorMessage;
369356
369578
 
@@ -369394,7 +369616,7 @@ const styles_module_scss_1 = __importDefault(__webpack_require__(39412));
369394
369616
  const stores_1 = __webpack_require__(32132);
369395
369617
  const Output = ({ packageApiUrl }) => {
369396
369618
  const project = (0, stores_1.useAppSelector)((state) => state.editor.project);
369397
- return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(ExternalFiles_1.default, {}), (0, jsx_runtime_1.jsx)("div", { className: styles_module_scss_1.default.projRunnerContainer, "data-testid": "output", children: (0, jsx_runtime_1.jsx)(RunnerFactory_1.default, { projectType: project.project_type, packageApiUrl: packageApiUrl }) })] }));
369619
+ return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(ExternalFiles_1.default, {}), (0, jsx_runtime_1.jsx)("div", { className: styles_module_scss_1.default.projRunnerContainer, "data-testid": "output", role: "region", "aria-label": "Output", children: (0, jsx_runtime_1.jsx)(RunnerFactory_1.default, { projectType: project.project_type, packageApiUrl: packageApiUrl }) })] }));
369398
369620
  };
369399
369621
  exports["default"] = Output;
369400
369622
 
@@ -369417,7 +369639,7 @@ const SvgIcon_1 = __webpack_require__(82917);
369417
369639
  const alert_svg_1 = __importDefault(__webpack_require__(11367));
369418
369640
  const alertTriangle_svg_1 = __importDefault(__webpack_require__(30103));
369419
369641
  function HtmlConsole({ consoleLogs, }) {
369420
- return ((0, jsx_runtime_1.jsx)("pre", { className: (0, classnames_1.default)(styles_module_scss_1.default.console, styles_module_scss_1.default.webConsole), children: consoleLogs.length === 0 ? ((0, jsx_runtime_1.jsx)("span", { children: "No logs received yet" })) : (consoleLogs.map((log, i) => {
369642
+ return ((0, jsx_runtime_1.jsx)("pre", { className: (0, classnames_1.default)(styles_module_scss_1.default.console, styles_module_scss_1.default.webConsole), role: "log", "aria-live": "polite", "aria-label": "Console output", children: consoleLogs.length === 0 ? ((0, jsx_runtime_1.jsx)("span", { children: "No logs received yet" })) : (consoleLogs.map((log, i) => {
369421
369643
  return ((0, jsx_runtime_1.jsxs)("div", { className: (0, classnames_1.default)(styles_module_scss_1.default.webConsoleLine, styles_module_scss_1.default[log.method]), children: [(0, jsx_runtime_1.jsxs)("span", { children: [(log.method === "error" || log.method === "warn") && ((0, jsx_runtime_1.jsx)(SvgIcon_1.SvgIcon, { SvgElement: log.method === "error" ? alert_svg_1.default : alertTriangle_svg_1.default, size: 12, className: log.method === "error"
369422
369644
  ? styles_module_scss_1.default.errorIcon
369423
369645
  : styles_module_scss_1.default.warnIcon })), log.timestamp && ((0, jsx_runtime_1.jsx)("span", { className: styles_module_scss_1.default.timestamp, children: log.timestamp }))] }), (0, jsx_runtime_1.jsx)("span", { className: styles_module_scss_1.default.consoleMessageContent, children: (0, jsx_runtime_1.jsxs)("span", { className: styles_module_scss_1.default.consoleMessageInline, children: [log.data?.map((node, idx) => ((0, jsx_runtime_1.jsxs)("span", { children: [(0, jsx_runtime_1.jsx)(LogRenderer_1.default, { node: node }), log.data && idx < log.data.length - 1 && " "] }, idx))), log.count && log.count > 1 && ((0, jsx_runtime_1.jsx)("span", { className: styles_module_scss_1.default.consoleCount, children: log.count }))] }) })] }, i));
@@ -369460,7 +369682,7 @@ const scripts_1 = __webpack_require__(57621);
369460
369682
  const Errors_1 = __webpack_require__(20339);
369461
369683
  const NotFoundPage_1 = __webpack_require__(87931);
369462
369684
  const HtmlConsole_1 = __importDefault(__webpack_require__(55339));
369463
- const ResizableWithHandle_1 = __importDefault(__webpack_require__(51535));
369685
+ const ResizableWithHandle_1 = __importDefault(__webpack_require__(32694));
369464
369686
  const BROADCAST_CHANNEL = "html_runner_channel";
369465
369687
  var BroadcastMessageType;
369466
369688
  (function (BroadcastMessageType) {
@@ -369494,6 +369716,9 @@ function HtmlRunner() {
369494
369716
  // back/forward/refresh buttons must drive page navigation.
369495
369717
  const shouldUseBrowserHistory = isPreviewMode || isPreviewUrl;
369496
369718
  const [iframeKey, setIframeKey] = (0, react_1.useState)(0);
369719
+ // Chrome does not apply `:focus` to an iframe element (focus is delegated
369720
+ // into its document), so track focus via events to show a visible ring.
369721
+ const [iframeFocused, setIframeFocused] = (0, react_1.useState)(false);
369497
369722
  (0, react_1.useEffect)(() => {
369498
369723
  shouldUseBrowserHistoryRef.current = shouldUseBrowserHistory;
369499
369724
  }, [shouldUseBrowserHistory]);
@@ -369723,8 +369948,9 @@ function HtmlRunner() {
369723
369948
  const openInNewTabLink = ((0, jsx_runtime_1.jsx)("div", { className: styles_module_scss_1.default.previewLink, onClick: () => openPreview(runningFilePath), children: (0, jsx_runtime_1.jsx)(SvgIcon_1.SvgIcon, { SvgElement: open_in_new_tab_svg_1.default, size: 16 }) }));
369724
369949
  const iframeClasses = (0, classnames_1.default)(styles_module_scss_1.default.iframe, {
369725
369950
  [styles_module_scss_1.default.codeHasBeenRun]: codeHasBeenRun,
369951
+ [styles_module_scss_1.default.focused]: iframeFocused,
369726
369952
  });
369727
- return ((0, jsx_runtime_1.jsxs)("div", { className: styles_module_scss_1.default.htmlrunnerContainer, children: [(0, jsx_runtime_1.jsx)(OutputTabPanel_1.OutputTabPanel, { title: "Preview", icon: preview_svg_1.default, readOnly: readOnly, extraTabContent: codeHasBeenRun && openInNewTabLink, tabPanelClassName: styles_module_scss_1.default.previewHtml, children: error ? ((0, jsx_runtime_1.jsx)("div", { className: iframeClasses, children: (0, jsx_runtime_1.jsx)(NotFoundPage_1.NotFoundPage, {}) })) : ((0, jsx_runtime_1.jsx)("iframe", { className: iframeClasses, sandbox: "allow-scripts allow-same-origin allow-modals allow-popups allow-popups-to-escape-sandbox", referrerPolicy: "strict-origin-when-cross-origin", allow: "\n accelerometer 'none';\n camera 'none';\n encrypted-media;\n fullscreen;\n picture-in-picture;\n geolocation 'none';\n gyroscope 'none';\n magnetometer 'none';\n microphone 'none';\n midi 'none';\n payment 'none';\n usb 'none';\n ", id: "output-frame", title: "HTML Output Preview", ref: output, onLoad: iframeReload, src: "about:blank" }, iframeKey)) }), !isPreviewMode && ((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: "Console", icon: console_svg_1.default, readOnly: readOnly, children: (0, jsx_runtime_1.jsx)(HtmlConsole_1.default, { consoleLogs: consoleLogs }) }) }))] }));
369953
+ return ((0, jsx_runtime_1.jsxs)("div", { className: styles_module_scss_1.default.htmlrunnerContainer, children: [(0, jsx_runtime_1.jsx)(OutputTabPanel_1.OutputTabPanel, { title: "Preview", icon: preview_svg_1.default, readOnly: readOnly, extraTabContent: codeHasBeenRun && openInNewTabLink, tabPanelClassName: styles_module_scss_1.default.previewHtml, children: error ? ((0, jsx_runtime_1.jsx)("div", { className: iframeClasses, children: (0, jsx_runtime_1.jsx)(NotFoundPage_1.NotFoundPage, {}) })) : ((0, jsx_runtime_1.jsx)("iframe", { className: iframeClasses, sandbox: "allow-scripts allow-same-origin allow-modals allow-popups allow-popups-to-escape-sandbox", referrerPolicy: "strict-origin-when-cross-origin", allow: "\n accelerometer 'none';\n camera 'none';\n encrypted-media;\n fullscreen;\n picture-in-picture;\n geolocation 'none';\n gyroscope 'none';\n magnetometer 'none';\n microphone 'none';\n midi 'none';\n payment 'none';\n usb 'none';\n ", id: "output-frame", title: "HTML Output Preview", ref: output, onLoad: iframeReload, onFocus: () => setIframeFocused(true), onBlur: () => setIframeFocused(false), src: "about:blank" }, iframeKey)) }), !isPreviewMode && ((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: "Console", icon: console_svg_1.default, readOnly: readOnly, children: (0, jsx_runtime_1.jsx)(HtmlConsole_1.default, { consoleLogs: consoleLogs }) }) }))] }));
369728
369954
  }
369729
369955
  exports["default"] = HtmlRunner;
369730
369956
 
@@ -370373,7 +370599,11 @@ const OutputTabPanel = ({ title, icon: Icon, readOnly, children, outputWrapperCl
370373
370599
  if (outputOnly) {
370374
370600
  return children;
370375
370601
  }
370376
- return ((0, jsx_runtime_1.jsx)("div", { className: (0, classnames_1.default)(styles_module_scss_1.default.outputPanel, outputWrapperClassName), children: (0, jsx_runtime_1.jsxs)(react_tabs_1.Tabs, { forceRenderTabPanel: true, children: [(0, jsx_runtime_1.jsx)("div", { className: (0, classnames_1.default)("tabContainer", { readOnly: readOnly }), children: (0, jsx_runtime_1.jsx)(react_tabs_1.TabList, { className: (0, classnames_1.default)("react-tabs__tab-list", { readOnly: readOnly }), children: (0, jsx_runtime_1.jsx)(react_tabs_1.Tab, { className: (0, classnames_1.default)("react-tabs__tab", { readOnly: readOnly }), children: (0, jsx_runtime_1.jsxs)(Text_1.Text, { size: 12, weight: "medium", className: "tabText", children: [(0, jsx_runtime_1.jsx)(SvgIcon_1.SvgIcon, { SvgElement: Icon, size: 18 }), title, extraTabContent] }) }, 0) }) }), (0, jsx_runtime_1.jsx)(react_tabs_1.TabPanel, { className: (0, classnames_1.default)("react-tabs__tab-panel", tabPanelClassName), children: children }, 0)] }) }));
370602
+ return ((0, jsx_runtime_1.jsx)("div", { className: (0, classnames_1.default)(styles_module_scss_1.default.outputPanel, outputWrapperClassName), children: (0, jsx_runtime_1.jsxs)(react_tabs_1.Tabs, { forceRenderTabPanel: true, children: [(0, jsx_runtime_1.jsx)("div", { className: (0, classnames_1.default)("tabContainer", { readOnly: readOnly }), children: (0, jsx_runtime_1.jsx)(react_tabs_1.TabList, { className: (0, classnames_1.default)("react-tabs__tab-list", { readOnly: readOnly }), children: (0, jsx_runtime_1.jsx)(react_tabs_1.Tab, { className: (0, classnames_1.default)("react-tabs__tab", { readOnly: readOnly }),
370603
+ // A single, non-switchable output tab is a label, not an
370604
+ // interactive control — keep it out of the keyboard tab order so
370605
+ // focus does not land on an unlabelled, invisible stop.
370606
+ tabIndex: "-1", children: (0, jsx_runtime_1.jsxs)(Text_1.Text, { size: 12, weight: "medium", className: "tabText", children: [(0, jsx_runtime_1.jsx)(SvgIcon_1.SvgIcon, { SvgElement: Icon, size: 18 }), title, extraTabContent] }) }, 0) }) }), (0, jsx_runtime_1.jsx)(react_tabs_1.TabPanel, { className: (0, classnames_1.default)("react-tabs__tab-panel", tabPanelClassName), children: children }, 0)] }) }));
370377
370607
  };
370378
370608
  exports.OutputTabPanel = OutputTabPanel;
370379
370609
 
@@ -370397,7 +370627,7 @@ const stores_1 = __webpack_require__(32132);
370397
370627
  const OutputTabPanel_1 = __webpack_require__(25931);
370398
370628
  const console_svg_1 = __importDefault(__webpack_require__(20602));
370399
370629
  const preview_svg_1 = __importDefault(__webpack_require__(80417));
370400
- const ResizableWithHandle_1 = __importDefault(__webpack_require__(51535));
370630
+ const ResizableWithHandle_1 = __importDefault(__webpack_require__(32694));
370401
370631
  const usePyodideRunner_1 = __webpack_require__(65587);
370402
370632
  const consoleInput_1 = __webpack_require__(35660);
370403
370633
  /**
@@ -370418,7 +370648,7 @@ const PyodideRunner = ({ active, packageApiUrl, }) => {
370418
370648
  console.warn("PyodideWorker is not initialized");
370419
370649
  return null;
370420
370650
  }
370421
- 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 }) }) })] }));
370651
+ 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, role: "log", "aria-live": "polite", "aria-label": "Program output terminal" })] }), (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 }) }) })] }));
370422
370652
  };
370423
370653
  exports["default"] = PyodideRunner;
370424
370654
 
@@ -370500,7 +370730,7 @@ const VisualOutputPane = ({ visuals, setVisuals }) => {
370500
370730
  setVisuals((visuals) => showVisuals(visuals, output));
370501
370731
  }
370502
370732
  }, [visuals, setVisuals, showVisuals]);
370503
- 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, {})) }));
370733
+ 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, role: "region", "aria-label": "Visual program output" })) : ((0, jsx_runtime_1.jsx)(VisualOutputPaneEmpty_1.default, {})) }));
370504
370734
  };
370505
370735
  const elementFromProps = (map) => {
370506
370736
  const tag = map.get("tag");
@@ -371966,27 +372196,13 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
371966
372196
  };
371967
372197
  Object.defineProperty(exports, "__esModule", ({ value: true }));
371968
372198
  const jsx_runtime_1 = __webpack_require__(74848);
371969
- const react_1 = __webpack_require__(51649);
371970
372199
  const SidebarPanel_1 = __importDefault(__webpack_require__(83617));
371971
372200
  const styles_module_scss_1 = __importDefault(__webpack_require__(99016));
371972
- const PANEL_FRACTION = 0.33;
371973
- const getPanelWidth = () => `${Math.round(window.innerWidth * PANEL_FRACTION)}px`;
371974
- const getMaxPanelWidth = () => `${Math.round(window.innerWidth * 2 * PANEL_FRACTION)}px`;
371975
372201
  const CurriculumPanel = ({ curriculumContent, }) => {
371976
- const [defaultWidth, setDefaultWidth] = (0, react_1.useState)(getPanelWidth);
371977
- const [maxWidth, setMaxWidth] = (0, react_1.useState)(getMaxPanelWidth);
371978
- (0, react_1.useEffect)(() => {
371979
- const update = () => {
371980
- setDefaultWidth(getPanelWidth());
371981
- setMaxWidth(getMaxPanelWidth());
371982
- };
371983
- window.addEventListener("resize", update);
371984
- return () => window.removeEventListener("resize", update);
371985
- }, []);
371986
372202
  if (!curriculumContent) {
371987
372203
  return null;
371988
372204
  }
371989
- return ((0, jsx_runtime_1.jsx)(SidebarPanel_1.default, { panelContentClassName: styles_module_scss_1.default.panelContent, className: styles_module_scss_1.default.curriculumPanel, defaultWidth: defaultWidth, maxWidth: maxWidth, children: curriculumContent }));
372205
+ return ((0, jsx_runtime_1.jsx)(SidebarPanel_1.default, { resizable: false, panelContentClassName: styles_module_scss_1.default.panelContent, className: styles_module_scss_1.default.curriculumPanel, children: curriculumContent }));
371990
372206
  };
371991
372207
  exports["default"] = CurriculumPanel;
371992
372208
 
@@ -372005,13 +372221,13 @@ const jsx_runtime_1 = __webpack_require__(74848);
372005
372221
  const SidebarPanel_1 = __importDefault(__webpack_require__(83617));
372006
372222
  const FileTree_1 = __webpack_require__(88675);
372007
372223
  const stores_1 = __webpack_require__(32132);
372008
- const FilePanel = () => {
372224
+ const FilePanel = ({ resizable = true, }) => {
372009
372225
  const project = (0, stores_1.useAppSelector)((state) => state.editor.project);
372010
372226
  const projectComponents = (0, stores_1.useAppSelector)((state) => state.editor.project.components);
372011
372227
  if (!project || !project.components) {
372012
372228
  return null;
372013
372229
  }
372014
- return ((0, jsx_runtime_1.jsx)(SidebarPanel_1.default, { children: projectComponents?.length > 0 && (0, jsx_runtime_1.jsx)(FileTree_1.FileTree, {}) }));
372230
+ return ((0, jsx_runtime_1.jsx)(SidebarPanel_1.default, { resizable: resizable, children: projectComponents?.length > 0 && (0, jsx_runtime_1.jsx)(FileTree_1.FileTree, {}) }));
372015
372231
  };
372016
372232
  exports["default"] = FilePanel;
372017
372233
 
@@ -372095,17 +372311,23 @@ const useHover_1 = __webpack_require__(78556);
372095
372311
  const button_dots_svg_1 = __importDefault(__webpack_require__(80054));
372096
372312
  const SvgIcon_1 = __webpack_require__(82917);
372097
372313
  const stores_1 = __webpack_require__(32132);
372098
- const BasicItemContent = ({ item, onContextMenu, }) => {
372314
+ const BasicItemContent = ({ item, onOpenMenu, isMenuOpen, triggerRef, }) => {
372099
372315
  const itemData = item.getItemData();
372100
372316
  const isSharedProject = (0, stores_1.useAppSelector)((state) => state.editor.isSharedProject);
372101
372317
  const { hovered, hoverProps } = (0, useHover_1.useHover)(useHover_1.TOOLTIP_SHOW_DELAY_MS);
372102
372318
  if (item.isRenaming()) {
372103
372319
  return (0, jsx_runtime_1.jsx)(ItemEdit_1.ItemEdit, { item: item });
372104
372320
  }
372105
- return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsxs)("div", { className: styles_module_scss_1.default.itemContent, onContextMenu: onContextMenu, children: [(0, jsx_runtime_1.jsx)(ItemIcon_1.ItemIcon, { itemData: itemData }), (0, jsx_runtime_1.jsx)(Text_1.Text, { className: styles_module_scss_1.default.itemTitle, size: 12, draggable: true, ...hoverProps, children: item.getItemName() }), !isSharedProject && ((0, jsx_runtime_1.jsx)(SvgIcon_1.SvgIcon, { className: styles_module_scss_1.default.itemButton, size: 16, SvgElement: button_dots_svg_1.default, onClick: (e) => {
372106
- onContextMenu(e);
372321
+ return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsxs)("div", { className: styles_module_scss_1.default.itemContent, onContextMenu: (e) => {
372322
+ e.preventDefault();
372323
+ onOpenMenu({ x: e.clientX, y: e.clientY });
372324
+ }, children: [(0, jsx_runtime_1.jsx)(ItemIcon_1.ItemIcon, { itemData: itemData }), (0, jsx_runtime_1.jsx)(Text_1.Text, { className: styles_module_scss_1.default.itemTitle, size: 12, draggable: true, ...hoverProps, children: item.getItemName() }), !isSharedProject && ((0, jsx_runtime_1.jsx)("button", { ref: triggerRef, type: "button", className: styles_module_scss_1.default.itemButton, "aria-label": `More actions for ${item.getItemName()}`, "aria-haspopup": "menu", "aria-expanded": isMenuOpen, onClick: (e) => {
372107
372325
  e.stopPropagation();
372108
- } }))] }), (0, jsx_runtime_1.jsx)(Tooltip_1.default, { message: item.getItemName(), visible: hovered, position: "bottom" })] }));
372326
+ // Anchor to the button's box, not the click point: a keyboard
372327
+ // activation reports clientX/Y of 0.
372328
+ const rect = e.currentTarget.getBoundingClientRect();
372329
+ onOpenMenu({ x: rect.left, y: rect.bottom });
372330
+ }, children: (0, jsx_runtime_1.jsx)(SvgIcon_1.SvgIcon, { size: 16, SvgElement: button_dots_svg_1.default, "aria-hidden": "true", focusable: "false" }) }))] }), (0, jsx_runtime_1.jsx)(Tooltip_1.default, { message: item.getItemName(), visible: hovered, position: "bottom" })] }));
372109
372331
  };
372110
372332
  exports.BasicItemContent = BasicItemContent;
372111
372333
 
@@ -372121,8 +372343,8 @@ exports.FileItem = void 0;
372121
372343
  const jsx_runtime_1 = __webpack_require__(74848);
372122
372344
  const BasicItemContent_1 = __webpack_require__(34071);
372123
372345
  const ItemContextMenu_1 = __webpack_require__(54035);
372124
- const FileItem = ({ item, onContextMenu, isContextMenuOpen, contextMenuPosition, onCloseContextMenu, }) => {
372125
- return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(BasicItemContent_1.BasicItemContent, { item: item, onContextMenu: onContextMenu }), (0, jsx_runtime_1.jsx)(ItemContextMenu_1.ItemContextMenu, { item: item, menuPosition: contextMenuPosition, isOpened: isContextMenuOpen, onClose: onCloseContextMenu })] }));
372346
+ const FileItem = ({ item, onOpenMenu, isContextMenuOpen, contextMenuPosition, onCloseContextMenu, triggerRef, }) => {
372347
+ return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(BasicItemContent_1.BasicItemContent, { item: item, onOpenMenu: onOpenMenu, isMenuOpen: isContextMenuOpen, triggerRef: triggerRef }), (0, jsx_runtime_1.jsx)(ItemContextMenu_1.ItemContextMenu, { item: item, menuPosition: contextMenuPosition, isOpened: isContextMenuOpen, onClose: onCloseContextMenu })] }));
372126
372348
  };
372127
372349
  exports.FileItem = FileItem;
372128
372350
 
@@ -372144,12 +372366,12 @@ const arrow_right_svg_1 = __importDefault(__webpack_require__(17297));
372144
372366
  const ItemContextMenu_1 = __webpack_require__(54035);
372145
372367
  const BasicItemContent_1 = __webpack_require__(34071);
372146
372368
  const SvgIcon_1 = __webpack_require__(82917);
372147
- const FolderItem = ({ item, onContextMenu, onCloseContextMenu, isContextMenuOpen, onImport, contextMenuPosition, }) => {
372148
- const arrowIcon = item.isExpanded() ? ((0, jsx_runtime_1.jsx)(SvgIcon_1.SvgIcon, { style: { rotate: "90deg" }, SvgElement: arrow_right_svg_1.default, size: 16, "aria-hidden": "true", focusable: "false" })) : ((0, jsx_runtime_1.jsx)(SvgIcon_1.SvgIcon, { SvgElement: arrow_right_svg_1.default, size: 16, "aria-hidden": "true", focusable: "false" }));
372369
+ const FolderItem = ({ item, onOpenMenu, onCloseContextMenu, isContextMenuOpen, onImport, contextMenuPosition, triggerRef, }) => {
372370
+ const arrowIcon = ((0, jsx_runtime_1.jsx)(SvgIcon_1.SvgIcon, { style: item.isExpanded() ? { rotate: "90deg" } : undefined, SvgElement: arrow_right_svg_1.default, size: 16, "aria-hidden": "true", focusable: "false" }));
372149
372371
  const handleFileImport = (files) => {
372150
372372
  onImport(files, item);
372151
372373
  };
372152
- return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [arrowIcon, (0, jsx_runtime_1.jsx)(BasicItemContent_1.BasicItemContent, { item: item, onContextMenu: onContextMenu }), (0, jsx_runtime_1.jsx)(FileImport_1.FileImport, { onFilesUpload: handleFileImport, allowMultiple: true, children: (openImportFileDialog) => ((0, jsx_runtime_1.jsx)(ItemContextMenu_1.ItemContextMenu, { item: item, menuPosition: contextMenuPosition, isOpened: isContextMenuOpen, onClose: onCloseContextMenu, openImportFileDialog: openImportFileDialog })) })] }));
372374
+ return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [arrowIcon, (0, jsx_runtime_1.jsx)(BasicItemContent_1.BasicItemContent, { item: item, onOpenMenu: onOpenMenu, isMenuOpen: isContextMenuOpen, triggerRef: triggerRef }), (0, jsx_runtime_1.jsx)(FileImport_1.FileImport, { onFilesUpload: handleFileImport, allowMultiple: true, children: (openImportFileDialog) => ((0, jsx_runtime_1.jsx)(ItemContextMenu_1.ItemContextMenu, { item: item, menuPosition: contextMenuPosition, isOpened: isContextMenuOpen, onClose: onCloseContextMenu, openImportFileDialog: openImportFileDialog })) })] }));
372153
372375
  };
372154
372376
  exports.FolderItem = FolderItem;
372155
372377
 
@@ -372319,6 +372541,7 @@ exports.ItemEdit = void 0;
372319
372541
  const jsx_runtime_1 = __webpack_require__(74848);
372320
372542
  const react_1 = __webpack_require__(51649);
372321
372543
  const classnames_1 = __importDefault(__webpack_require__(46942));
372544
+ const ProjectTypes_1 = __webpack_require__(27130);
372322
372545
  const styles_module_scss_1 = __importDefault(__webpack_require__(10614));
372323
372546
  const FileTreeContext_1 = __webpack_require__(5323);
372324
372547
  const SvgIcon_1 = __webpack_require__(82917);
@@ -372332,6 +372555,13 @@ const ItemEdit = ({ item }) => {
372332
372555
  const { id, type } = item.getItemData();
372333
372556
  const children = item.getParent()?.getChildren() || [];
372334
372557
  const { onChange, ...restTreeInputProps } = item.getRenameInputProps();
372558
+ const kind = type === ProjectTypes_1.ProjectComponentType.DIR ? "folder" : "file";
372559
+ const currentName = item.getItemName();
372560
+ // Empty name means this is a freshly created draft, not a rename.
372561
+ const inputLabel = currentName
372562
+ ? `Rename ${kind} ${currentName}`
372563
+ : `Name for new ${kind}`;
372564
+ const errorId = `rename-error-${id}`;
372335
372565
  const handleRenameSubmission = (value) => {
372336
372566
  const initialValue = item.getItemName();
372337
372567
  const hasValueChanged = initialValue !== value.trim();
@@ -372361,7 +372591,7 @@ const ItemEdit = ({ item }) => {
372361
372591
  };
372362
372592
  return ((0, jsx_runtime_1.jsxs)("div", { className: (0, classnames_1.default)(styles_module_scss_1.default.itemContentEdit, {
372363
372593
  [styles_module_scss_1.default.inputError]: !!error,
372364
- }), children: [(0, jsx_runtime_1.jsx)(ItemIcon_1.ItemIcon, { itemData: item.getItemData() }), (0, jsx_runtime_1.jsx)("input", { name: "Item name", "aria-label": "Rename file or folder", onChange: handleChange, onKeyDown: handleKeyDown, ...restTreeInputProps, onBlur: handleBlur }), error && ((0, jsx_runtime_1.jsxs)("div", { className: styles_module_scss_1.default.errorMessage, children: [(0, jsx_runtime_1.jsx)(SvgIcon_1.SvgIcon, { SvgElement: alert_svg_1.default, size: 16 }), (0, jsx_runtime_1.jsx)(Text_1.Text, { size: 12, children: error })] }))] }));
372594
+ }), children: [(0, jsx_runtime_1.jsx)(ItemIcon_1.ItemIcon, { itemData: item.getItemData() }), (0, jsx_runtime_1.jsx)("input", { name: "Item name", "aria-label": inputLabel, "aria-invalid": !!error, "aria-describedby": error ? errorId : undefined, onChange: handleChange, onKeyDown: handleKeyDown, ...restTreeInputProps, onBlur: handleBlur }), error && ((0, jsx_runtime_1.jsxs)("div", { id: errorId, role: "alert", className: styles_module_scss_1.default.errorMessage, children: [(0, jsx_runtime_1.jsx)(SvgIcon_1.SvgIcon, { SvgElement: alert_svg_1.default, size: 16, "aria-hidden": "true", focusable: "false" }), (0, jsx_runtime_1.jsx)(Text_1.Text, { size: 12, children: error })] }))] }));
372365
372595
  };
372366
372596
  exports.ItemEdit = ItemEdit;
372367
372597
 
@@ -372384,7 +372614,7 @@ const ProjectTypes_1 = __webpack_require__(27130);
372384
372614
  const SvgIcon_1 = __webpack_require__(82917);
372385
372615
  const ItemIcon = ({ itemData }) => {
372386
372616
  if (itemData.type === ProjectTypes_1.ProjectComponentType.DIR) {
372387
- return (0, jsx_runtime_1.jsx)(SvgIcon_1.SvgIcon, { SvgElement: folder_svg_1.default, size: 18 });
372617
+ return ((0, jsx_runtime_1.jsx)(SvgIcon_1.SvgIcon, { SvgElement: folder_svg_1.default, size: 18, "aria-hidden": "true", focusable: "false" }));
372388
372618
  }
372389
372619
  return (0, jsx_runtime_1.jsx)(FileIcon_1.default, { ext: itemData.extension });
372390
372620
  };
@@ -372416,16 +372646,24 @@ const FileTreeItem = ({ item, guides, activeLevel, isContextMenuAvailable, }) =>
372416
372646
  const { layers, positions, sizes } = (0, treeGuides_1.getItemGuidesStyles)(guides, activeLevel);
372417
372647
  const [isContextMenuOpen, setIsContextMenuOpen] = (0, react_1.useState)(false);
372418
372648
  const [contextMenuPosition, setContextMenuPosition] = (0, react_1.useState)({ x: 0, y: 0 });
372419
- const handleOpenContextMenu = (e) => {
372649
+ const triggerRef = (0, react_1.useRef)(null);
372650
+ const handleOpenContextMenu = (position) => {
372420
372651
  if (!isContextMenuAvailable)
372421
372652
  return;
372422
- e.preventDefault();
372423
372653
  setIsContextMenuOpen(true);
372424
- setContextMenuPosition({ x: e.clientX, y: e.clientY });
372654
+ setContextMenuPosition(position);
372425
372655
  };
372426
372656
  const handleCloseContextMenu = () => {
372427
372657
  setIsContextMenuOpen(false);
372428
372658
  setContextMenuPosition({ x: 0, y: 0 });
372659
+ // On close, return focus to the trigger button — but only if focus was
372660
+ // lost (e.g. Escape), not when an action moved it (rename input, modal).
372661
+ requestAnimationFrame(() => {
372662
+ const active = document.activeElement;
372663
+ if (!active || active === document.body) {
372664
+ triggerRef.current?.focus();
372665
+ }
372666
+ });
372429
372667
  };
372430
372668
  return ((0, jsx_runtime_1.jsx)("div", { ...item.getProps(), className: (0, classnames_1.default)(styles_module_scss_1.default.row, styles_module_scss_1.default[`level-${level}`]), style: {
372431
372669
  ["--guides-bg"]: layers.join(","),
@@ -372436,7 +372674,7 @@ const FileTreeItem = ({ item, guides, activeLevel, isContextMenuAvailable, }) =>
372436
372674
  [styles_module_scss_1.default.itemExpanded]: item.isExpanded(),
372437
372675
  [styles_module_scss_1.default.itemSelected]: item.isSelected(),
372438
372676
  [styles_module_scss_1.default.itemFolder]: item.isFolder(),
372439
- }), children: item.isFolder() ? ((0, jsx_runtime_1.jsx)(FolderItem_1.FolderItem, { item: item, onContextMenu: handleOpenContextMenu, onCloseContextMenu: handleCloseContextMenu, isContextMenuOpen: isContextMenuOpen, onImport: importFiles, contextMenuPosition: contextMenuPosition })) : ((0, jsx_runtime_1.jsx)(FileItem_1.FileItem, { item: item, onContextMenu: handleOpenContextMenu, isContextMenuOpen: isContextMenuOpen, contextMenuPosition: contextMenuPosition, onCloseContextMenu: handleCloseContextMenu })) }) }));
372677
+ }), children: item.isFolder() ? ((0, jsx_runtime_1.jsx)(FolderItem_1.FolderItem, { item: item, onOpenMenu: handleOpenContextMenu, onCloseContextMenu: handleCloseContextMenu, isContextMenuOpen: isContextMenuOpen, onImport: importFiles, contextMenuPosition: contextMenuPosition, triggerRef: triggerRef })) : ((0, jsx_runtime_1.jsx)(FileItem_1.FileItem, { item: item, onOpenMenu: handleOpenContextMenu, isContextMenuOpen: isContextMenuOpen, contextMenuPosition: contextMenuPosition, onCloseContextMenu: handleCloseContextMenu, triggerRef: triggerRef })) }) }));
372440
372678
  };
372441
372679
  exports.FileTreeItem = FileTreeItem;
372442
372680
 
@@ -372580,31 +372818,41 @@ const stub = (id) => ({
372580
372818
  */
372581
372819
  const useFileTree = ({ projectFiles, onRename, onDrop, onOpenFile, rootId, readOnly = false, }) => {
372582
372820
  const [focusedItem, setFocusedItem] = (0, react_2.useState)(null);
372821
+ // Shared activation logic for both mouse (click) and keyboard (Enter/Space).
372822
+ const handlePrimaryAction = (item) => {
372823
+ // handle files
372824
+ if (!item.isFolder() && !item.isRenaming()) {
372825
+ onOpenFile(item.getId());
372826
+ return;
372827
+ }
372828
+ // handle folders
372829
+ item.isExpanded() ? item.collapse() : item.expand();
372830
+ };
372831
+ // Only activate when a tree row holds focus, so Enter/Space aren't stolen
372832
+ // from focusable children like the "more actions" button.
372833
+ const isTreeItemFocused = () => document.activeElement?.getAttribute("role") === "treeitem";
372834
+ const canActivate = (tree) => isTreeItemFocused() && !tree.isRenamingItem() && !tree.getState().dnd;
372583
372835
  const customClickBehavior = {
372584
372836
  itemInstance: {
372585
- getProps: ({ tree, item, prev }) => ({
372586
- ...prev?.(),
372587
- onClick: (e) => {
372588
- item.primaryAction();
372589
- item.setFocused();
372590
- if (!item.isFolder() && !item.isRenaming()) {
372591
- onOpenFile(item.getId());
372592
- return;
372593
- }
372594
- if (item.isExpanded()) {
372595
- item.collapse();
372596
- }
372597
- else {
372598
- item.expand();
372599
- }
372600
- },
372601
- onDoubleClick: () => {
372602
- item.primaryAction();
372603
- if (!readOnly) {
372604
- item.startRenaming();
372605
- }
372606
- },
372607
- }),
372837
+ getProps: ({ item, prev }) => {
372838
+ const isFolder = item.isFolder();
372839
+ const state = item.isExpanded() ? "expanded" : "collapsed";
372840
+ return {
372841
+ ...prev?.(),
372842
+ ...(isFolder && {
372843
+ "aria-label": `${item.getItemName()}, ${state}`,
372844
+ }),
372845
+ onClick: () => {
372846
+ item.setFocused();
372847
+ item.primaryAction();
372848
+ },
372849
+ onDoubleClick: () => {
372850
+ if (!readOnly) {
372851
+ item.startRenaming();
372852
+ }
372853
+ },
372854
+ };
372855
+ },
372608
372856
  },
372609
372857
  };
372610
372858
  const tree = (0, react_1.useTree)({
@@ -372614,6 +372862,23 @@ const useFileTree = ({ projectFiles, onRename, onDrop, onOpenFile, rootId, readO
372614
372862
  getItemName: (item) => (0, projectHelpers_1.getFullComponentName)(item.getItemData()),
372615
372863
  isItemFolder: (item) => item.getItemData().type === ProjectTypes_1.ProjectComponentType.DIR,
372616
372864
  onRename,
372865
+ onPrimaryAction: handlePrimaryAction,
372866
+ ignoreHotkeysOnInputs: true,
372867
+ // Bind Enter/Space to primaryAction so keyboard users can open files and
372868
+ // expand/collapse folders. Guarded so they don't fire on renaming/dragging.
372869
+ hotkeys: {
372870
+ customActivateItem: {
372871
+ hotkey: "Enter",
372872
+ isEnabled: (tree) => canActivate(tree) && !!tree.getFocusedItem(),
372873
+ handler: (_e, tree) => tree.getFocusedItem()?.primaryAction(),
372874
+ },
372875
+ customActivateItemWithSpace: {
372876
+ hotkey: "Space",
372877
+ preventDefault: true,
372878
+ isEnabled: (tree) => canActivate(tree) && !!tree.getFocusedItem(),
372879
+ handler: (_e, tree) => tree.getFocusedItem()?.primaryAction(),
372880
+ },
372881
+ },
372617
372882
  dataLoader: {
372618
372883
  getItem: (itemId) => {
372619
372884
  const item = projectFiles.find((item) => item.id === itemId);
@@ -372633,6 +372898,7 @@ const useFileTree = ({ projectFiles, onRename, onDrop, onOpenFile, rootId, readO
372633
372898
  features: [
372634
372899
  core_1.syncDataLoaderFeature,
372635
372900
  core_1.selectionFeature,
372901
+ core_1.hotkeysCoreFeature,
372636
372902
  core_1.expandAllFeature,
372637
372903
  customClickBehavior,
372638
372904
  core_1.dragAndDropFeature,
@@ -372894,7 +373160,7 @@ const FileTree = () => {
372894
373160
  removeComponent,
372895
373161
  importFolder,
372896
373162
  };
372897
- return ((0, jsx_runtime_1.jsxs)(FileTreeContext_1.TreeContext.Provider, { value: treeCtx, children: [(0, jsx_runtime_1.jsx)(FileTreeActions_1.FileTreeActions, { hasExpandedNodes: Boolean(tree.getState().expandedItems.length) }), treeItems.length > 0 ? ((0, jsx_runtime_1.jsxs)("div", { ...tree.getContainerProps(), className: styles_module_scss_1.default.tree, children: [treeItems.map((item, i) => ((0, jsx_runtime_1.jsx)(FileTreeItem_1.FileTreeItem, { item: item, guides: guidesByIndex[i], activeLevel: activeLevel, isContextMenuAvailable: !isReadOnly }, item.getId()))), (0, jsx_runtime_1.jsx)("div", { style: tree.getDragLineStyle(), className: styles_module_scss_1.default.dragline })] })) : ((0, jsx_runtime_1.jsx)(EmptyStateMessage_1.EmptyStateMessage, {}))] }));
373163
+ return ((0, jsx_runtime_1.jsxs)(FileTreeContext_1.TreeContext.Provider, { value: treeCtx, children: [(0, jsx_runtime_1.jsx)(FileTreeActions_1.FileTreeActions, { hasExpandedNodes: Boolean(tree.getState().expandedItems.length) }), treeItems.length > 0 ? ((0, jsx_runtime_1.jsxs)("div", { ...tree.getContainerProps("Project files"), className: styles_module_scss_1.default.tree, children: [treeItems.map((item, i) => ((0, jsx_runtime_1.jsx)(FileTreeItem_1.FileTreeItem, { item: item, guides: guidesByIndex[i], activeLevel: activeLevel, isContextMenuAvailable: !isReadOnly }, item.getId()))), (0, jsx_runtime_1.jsx)("div", { style: tree.getDragLineStyle(), className: styles_module_scss_1.default.dragline })] })) : ((0, jsx_runtime_1.jsx)(EmptyStateMessage_1.EmptyStateMessage, {}))] }));
372898
373164
  };
372899
373165
  exports.FileTree = FileTree;
372900
373166
 
@@ -372915,6 +373181,7 @@ const stores_1 = __webpack_require__(32132);
372915
373181
  const classnames_1 = __importDefault(__webpack_require__(46942));
372916
373182
  const FilePanel_1 = __importDefault(__webpack_require__(7802));
372917
373183
  const SidebarBar_1 = __importDefault(__webpack_require__(26624));
373184
+ const SidebarPanelsColumn_1 = __importDefault(__webpack_require__(91592));
372918
373185
  const SvgIcon_1 = __webpack_require__(82917);
372919
373186
  const folder_svg_1 = __importDefault(__webpack_require__(97091));
372920
373187
  const home_svg_1 = __importDefault(__webpack_require__(69046));
@@ -372950,7 +373217,7 @@ const getSidebarOptions = (initials, homeAction, isCodeVisible, curriculumCompon
372950
373217
  icon: () => (0, jsx_runtime_1.jsx)(SvgIcon_1.SvgIcon, { SvgElement: folder_svg_1.default, size: 24 }),
372951
373218
  title: "Files",
372952
373219
  position: "top",
372953
- panel: FilePanel_1.default,
373220
+ panel: ({ resizable }) => ((0, jsx_runtime_1.jsx)(FilePanel_1.default, { resizable: resizable })),
372954
373221
  },
372955
373222
  ]
372956
373223
  : []),
@@ -372961,7 +373228,8 @@ const getSidebarOptions = (initials, homeAction, isCodeVisible, curriculumCompon
372961
373228
  icon: () => (0, jsx_runtime_1.jsx)(SvgIcon_1.SvgIcon, { SvgElement: activity_svg_1.default, size: 24 }),
372962
373229
  title: "Curriculum",
372963
373230
  position: "top",
372964
- panel: () => ((0, jsx_runtime_1.jsx)(CurriculumPanel_1.default, { curriculumContent: curriculumComponent })),
373231
+ fillsColumn: true,
373232
+ panel: (_layout) => ((0, jsx_runtime_1.jsx)(CurriculumPanel_1.default, { curriculumContent: curriculumComponent })),
372965
373233
  },
372966
373234
  ]
372967
373235
  : []),
@@ -372984,33 +373252,38 @@ const Sidebar = ({ options = [], curriculumComponent = null, }) => {
372984
373252
  const { onHome } = (0, AuthContext_1.useAuthActions)();
372985
373253
  const sideBarOptions = getSidebarOptions(initials, onHome, isCodeVisible, curriculumComponent);
372986
373254
  const menuOptions = sideBarOptions.filter((option) => options.includes(option.name));
372987
- const [option, setOption] = (0, react_1.useState)(curriculumDisplayMode !== types_1.CurriculumDisplayMode.HIDDEN
372988
- ? PanelNames.CURRICULUM
372989
- : null);
372990
- const toggleOption = (newOption) => {
372991
- // when toggle the same option, close panel
372992
- if (option === newOption) {
372993
- setOption(null);
372994
- // State of curriculum control must be set because it can be changed from contentSection option
372995
- if (newOption === PanelNames.CURRICULUM) {
372996
- dispatch((0, EditorSlice_1.setCurriculumDisplayMode)(types_1.CurriculumDisplayMode.HIDDEN));
372997
- }
373255
+ // Panels are multi-select: files and curriculum can be open at the same time
373256
+ const [openPanels, setOpenPanels] = (0, react_1.useState)(curriculumDisplayMode !== types_1.CurriculumDisplayMode.HIDDEN
373257
+ ? [PanelNames.CURRICULUM]
373258
+ : []);
373259
+ const toggleOption = (name) => {
373260
+ const option = menuOptions.find((opt) => opt.name === name);
373261
+ // only options with a panel can be toggled open
373262
+ if (!option?.panel) {
373263
+ return;
372998
373264
  }
372999
- else {
373000
- setOption(newOption);
373265
+ const panelName = option.name;
373266
+ setOpenPanels((prev) => {
373267
+ const isOpen = prev.includes(panelName);
373001
373268
  // State of curriculum control must be set because it can be changed from contentSection option
373002
- if (newOption === PanelNames.CURRICULUM) {
373003
- dispatch((0, EditorSlice_1.setCurriculumDisplayMode)(types_1.CurriculumDisplayMode.NORMAL));
373004
- }
373005
- }
373269
+ if (panelName === PanelNames.CURRICULUM) {
373270
+ dispatch((0, EditorSlice_1.setCurriculumDisplayMode)(isOpen
373271
+ ? types_1.CurriculumDisplayMode.HIDDEN
373272
+ : types_1.CurriculumDisplayMode.NORMAL));
373273
+ }
373274
+ return isOpen
373275
+ ? prev.filter((panel) => panel !== panelName)
373276
+ : [...prev, panelName];
373277
+ });
373006
373278
  };
373007
- const activeOption = menuOptions.find((opt) => opt.name === option);
373008
- // Use CustomSidebarPanel only for non-curriculum panels. The curriculum panel
373009
- // must be rendered as <CurriculumPanel> directly (a stable component type)
373010
- // rather than through an anonymous arrow function stored in getSidebarOptions.
373011
- // An anonymous function creates a NEW component type on every Sidebar render.
373012
- const CustomSidebarPanel = activeOption?.panel || (() => null);
373013
- return ((0, jsx_runtime_1.jsxs)("div", { className: (0, classnames_1.default)(styles_module_scss_1.default.sidebar), children: [(0, jsx_runtime_1.jsx)(SidebarBar_1.default, { menuOptions: menuOptions, option: option, toggleOption: toggleOption }), option === PanelNames.CURRICULUM ? ((0, jsx_runtime_1.jsx)(CurriculumPanel_1.default, { curriculumContent: curriculumComponent })) : (option && (0, jsx_runtime_1.jsx)(CustomSidebarPanel, {}))] }));
373279
+ const openPanelOptions = menuOptions.filter((opt) => opt.panel && openPanels.includes(opt.name));
373280
+ // A fillsColumn panel forces the column layout (others stack above it).
373281
+ const useColumn = openPanelOptions.some((opt) => opt.fillsColumn);
373282
+ const columnPanels = [
373283
+ ...openPanelOptions.filter((opt) => !opt.fillsColumn),
373284
+ ...openPanelOptions.filter((opt) => opt.fillsColumn),
373285
+ ];
373286
+ return ((0, jsx_runtime_1.jsxs)("div", { className: (0, classnames_1.default)(styles_module_scss_1.default.sidebar), children: [(0, jsx_runtime_1.jsx)(SidebarBar_1.default, { menuOptions: menuOptions, openPanels: openPanels, toggleOption: toggleOption }), useColumn ? ((0, jsx_runtime_1.jsx)(SidebarPanelsColumn_1.default, { expanded: curriculumDisplayMode === types_1.CurriculumDisplayMode.EXPANDED, panels: columnPanels.map((opt) => opt.panel?.({ resizable: false })) })) : (openPanelOptions[0]?.panel?.({ resizable: true }) ?? null)] }));
373014
373287
  };
373015
373288
  exports["default"] = Sidebar;
373016
373289
 
@@ -373029,10 +373302,10 @@ const jsx_runtime_1 = __webpack_require__(74848);
373029
373302
  const styles_module_scss_1 = __importDefault(__webpack_require__(21852));
373030
373303
  const SidebarBarOption_1 = __importDefault(__webpack_require__(19371));
373031
373304
  const SidebarBar = (props) => {
373032
- const { menuOptions, option, toggleOption } = props;
373305
+ const { menuOptions, openPanels = [], toggleOption } = props;
373033
373306
  const topMenuOptions = menuOptions.filter((menuOption) => menuOption.position === "top");
373034
373307
  const bottomMenuOptions = menuOptions.filter((menuOption) => menuOption.position === "bottom");
373035
- return ((0, jsx_runtime_1.jsxs)("div", { className: styles_module_scss_1.default.bar, children: [(0, jsx_runtime_1.jsx)("div", { className: styles_module_scss_1.default.optionsTop, children: topMenuOptions.map((menuOption, i) => ((0, jsx_runtime_1.jsx)(SidebarBarOption_1.default, { Icon: menuOption.icon, title: menuOption.title, isActive: option === menuOption.name, toggleOption: toggleOption, name: menuOption.name, action: menuOption.action }, i))) }), (0, jsx_runtime_1.jsx)("div", { className: styles_module_scss_1.default.optionsBottom, children: bottomMenuOptions.map((menuOption, i) => ((0, jsx_runtime_1.jsx)(SidebarBarOption_1.default, { Icon: menuOption.icon, title: menuOption.title, isActive: option === menuOption.name, toggleOption: toggleOption, name: menuOption.name, buttonText: menuOption.buttonText, isUser: menuOption.name === "user" }, i))) })] }));
373308
+ return ((0, jsx_runtime_1.jsxs)("div", { className: styles_module_scss_1.default.bar, children: [(0, jsx_runtime_1.jsx)("div", { className: styles_module_scss_1.default.optionsTop, children: topMenuOptions.map((menuOption, i) => ((0, jsx_runtime_1.jsx)(SidebarBarOption_1.default, { Icon: menuOption.icon, title: menuOption.title, isActive: openPanels.includes(menuOption.name), toggleOption: toggleOption, name: menuOption.name, action: menuOption.action }, i))) }), (0, jsx_runtime_1.jsx)("div", { className: styles_module_scss_1.default.optionsBottom, children: bottomMenuOptions.map((menuOption, i) => ((0, jsx_runtime_1.jsx)(SidebarBarOption_1.default, { Icon: menuOption.icon, title: menuOption.title, isActive: openPanels.includes(menuOption.name), toggleOption: toggleOption, name: menuOption.name, buttonText: menuOption.buttonText, isUser: menuOption.name === "user" }, i))) })] }));
373036
373309
  };
373037
373310
  exports["default"] = SidebarBar;
373038
373311
 
@@ -373102,6 +373375,59 @@ const SidebarBarOption = (props) => {
373102
373375
  exports["default"] = SidebarBarOption;
373103
373376
 
373104
373377
 
373378
+ /***/ }),
373379
+
373380
+ /***/ 91592:
373381
+ /***/ (function(__unused_webpack_module, exports, __webpack_require__) {
373382
+
373383
+
373384
+ var __importDefault = (this && this.__importDefault) || function (mod) {
373385
+ return (mod && mod.__esModule) ? mod : { "default": mod };
373386
+ };
373387
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
373388
+ const jsx_runtime_1 = __webpack_require__(74848);
373389
+ const react_1 = __webpack_require__(51649);
373390
+ const ResizableWithHandle_1 = __importDefault(__webpack_require__(32694));
373391
+ const styles_module_scss_1 = __importDefault(__webpack_require__(21852));
373392
+ const PANEL_FRACTION = 0.33;
373393
+ const getPanelWidth = () => `${Math.round(window.innerWidth * PANEL_FRACTION)}px`;
373394
+ const getMaxPanelWidth = () => `${Math.round(window.innerWidth * 2 * PANEL_FRACTION)}px`;
373395
+ const SidebarPanelsColumn = ({ panels, expanded = false, }) => {
373396
+ // The column owns its width so the expand/collapse toggle is always authoritative:
373397
+ // toggling forces the preset, dragging updates the same value, and a viewport
373398
+ // resize re-applies the preset unless the user has dragged. Keeping width here
373399
+ // (rather than inside ResizableWithHandle) means a prior manual drag can never
373400
+ // suppress a programmatic width change.
373401
+ const presetWidth = (0, react_1.useCallback)(() => (expanded ? getMaxPanelWidth() : getPanelWidth()), [expanded]);
373402
+ const [width, setWidth] = (0, react_1.useState)(presetWidth);
373403
+ const [maxWidth, setMaxWidth] = (0, react_1.useState)(getMaxPanelWidth);
373404
+ const manuallyResizedRef = (0, react_1.useRef)(false);
373405
+ // Expand/collapse is a deliberate mode switch: force the preset and drop any
373406
+ // manual size so the toggle always resizes the column.
373407
+ (0, react_1.useEffect)(() => {
373408
+ manuallyResizedRef.current = false;
373409
+ setWidth(presetWidth());
373410
+ }, [expanded, presetWidth]);
373411
+ // On viewport resize keep the presets current; re-apply to the column only
373412
+ // when the user has not manually dragged it.
373413
+ (0, react_1.useEffect)(() => {
373414
+ const onResize = () => {
373415
+ setMaxWidth(getMaxPanelWidth());
373416
+ if (!manuallyResizedRef.current)
373417
+ setWidth(presetWidth());
373418
+ };
373419
+ window.addEventListener("resize", onResize);
373420
+ return () => window.removeEventListener("resize", onResize);
373421
+ }, [presetWidth]);
373422
+ const handleWidthChange = (0, react_1.useCallback)((next) => {
373423
+ manuallyResizedRef.current = true;
373424
+ setWidth(next);
373425
+ }, []);
373426
+ return ((0, jsx_runtime_1.jsxs)(ResizableWithHandle_1.default, { "data-testid": "sidebar__panels-column", className: styles_module_scss_1.default.panelsColumn, defaultWidth: typeof width === "number" ? `${width}px` : width, defaultHeight: "100%", handleDirection: "right", minWidth: "250px", maxWidth: maxWidth, width: width, onWidthChange: handleWidthChange, children: [panels.slice(0, -1).map((panel, index) => ((0, jsx_runtime_1.jsx)(ResizableWithHandle_1.default, { className: styles_module_scss_1.default.panelsColumnTop, defaultWidth: "100%", defaultHeight: "35%", handleDirection: "bottom", minHeight: "150px", maxHeight: "75%", children: panel }, index))), (0, jsx_runtime_1.jsx)("div", { className: styles_module_scss_1.default.panelsColumnFill, children: panels[panels.length - 1] })] }));
373427
+ };
373428
+ exports["default"] = SidebarPanelsColumn;
373429
+
373430
+
373105
373431
  /***/ }),
373106
373432
 
373107
373433
  /***/ 2957:
@@ -373793,7 +374119,7 @@ const extToIconMap = {
373793
374119
  };
373794
374120
  const FileIcon = ({ ext = "" }) => {
373795
374121
  const Icon = extToIconMap[ext] ?? file_svg_1.default;
373796
- return (0, jsx_runtime_1.jsx)(SvgIcon_1.SvgIcon, { size: 18, SvgElement: Icon });
374122
+ return ((0, jsx_runtime_1.jsx)(SvgIcon_1.SvgIcon, { size: 18, SvgElement: Icon, "aria-hidden": "true", focusable: "false" }));
373797
374123
  };
373798
374124
  exports["default"] = FileIcon;
373799
374125
 
@@ -374671,6 +374997,44 @@ const validateProjectFilesCount = (components, newFilesCount) => {
374671
374997
  exports.validateProjectFilesCount = validateProjectFilesCount;
374672
374998
 
374673
374999
 
375000
+ /***/ }),
375001
+
375002
+ /***/ 87170:
375003
+ /***/ ((__unused_webpack_module, exports, __webpack_require__) => {
375004
+
375005
+ var __webpack_unused_export__;
375006
+
375007
+ __webpack_unused_export__ = ({ value: true });
375008
+ exports.s = void 0;
375009
+ const react_1 = __webpack_require__(51649);
375010
+ const react_redux_1 = __webpack_require__(14062);
375011
+ const stores_1 = __webpack_require__(32132);
375012
+ const EditorSlice_1 = __webpack_require__(68512);
375013
+ /**
375014
+ * Public hook for host apps to read and control the curriculum panel's display
375015
+ * mode. Intended for driving the width of the already-open panel — i.e.
375016
+ * toggling between `NORMAL` and `EXPANDED` from a control in the curriculum
375017
+ * header.
375018
+ *
375019
+ * The host renders its curriculum content inside this library's Redux Provider,
375020
+ * so calling this hook from within that content dispatches to the library store
375021
+ * without depending on the store's shape.
375022
+ *
375023
+ * Note: opening/closing the panel is owned by the sidebar's own curriculum
375024
+ * control. Setting `HIDDEN` through this hook updates the mode but does not
375025
+ * close an already-mounted panel, so prefer it for the NORMAL↔EXPANDED toggle.
375026
+ */
375027
+ const useCurriculumDisplayMode = () => {
375028
+ const dispatch = (0, react_redux_1.useDispatch)();
375029
+ const mode = (0, stores_1.useAppSelector)((state) => state.editor.curriculumDisplayMode);
375030
+ const setMode = (0, react_1.useCallback)((next) => {
375031
+ dispatch((0, EditorSlice_1.setCurriculumDisplayMode)(next));
375032
+ }, [dispatch]);
375033
+ return [mode, setMode];
375034
+ };
375035
+ exports.s = useCurriculumDisplayMode;
375036
+
375037
+
374674
375038
  /***/ }),
374675
375039
 
374676
375040
  /***/ 78556:
@@ -376464,6 +376828,7 @@ var CurriculumDisplayMode;
376464
376828
  (function (CurriculumDisplayMode) {
376465
376829
  CurriculumDisplayMode["HIDDEN"] = "hidden";
376466
376830
  CurriculumDisplayMode["NORMAL"] = "normal";
376831
+ CurriculumDisplayMode["EXPANDED"] = "expanded";
376467
376832
  })(CurriculumDisplayMode || (exports.CurriculumDisplayMode = CurriculumDisplayMode = {}));
376468
376833
 
376469
376834
 
@@ -395131,10 +395496,18 @@ module.exports = /*#__PURE__*/JSON.parse('{"application/1d-interleaved-parityfec
395131
395496
  /************************************************************************/
395132
395497
  var __webpack_exports__ = {};
395133
395498
  /* harmony export */ __webpack_require__.d(__webpack_exports__, {
395134
- /* harmony export */ $: () => (/* reexport safe */ _App_tsx__WEBPACK_IMPORTED_MODULE_0__.TextJamEditor)
395499
+ /* harmony export */ $C: () => (/* reexport safe */ _App_tsx__WEBPACK_IMPORTED_MODULE_0__.TextJamEditor),
395500
+ /* harmony export */ r8: () => (/* reexport safe */ _redux_types__WEBPACK_IMPORTED_MODULE_1__.CurriculumDisplayMode),
395501
+ /* harmony export */ sD: () => (/* reexport safe */ _hooks_useCurriculumDisplayMode__WEBPACK_IMPORTED_MODULE_2__.s)
395135
395502
  /* harmony export */ });
395136
395503
  /* harmony import */ var _App_tsx__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(78577);
395137
395504
  /* harmony import */ var _App_tsx__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_App_tsx__WEBPACK_IMPORTED_MODULE_0__);
395505
+ /* harmony import */ var _redux_types__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(92932);
395506
+ /* harmony import */ var _hooks_useCurriculumDisplayMode__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(87170);
395507
+
395508
+
395138
395509
 
395139
- var __webpack_exports__TextJamEditor = __webpack_exports__.$;
395140
- export { __webpack_exports__TextJamEditor as TextJamEditor };
395510
+ var __webpack_exports__CurriculumDisplayMode = __webpack_exports__.r8;
395511
+ var __webpack_exports__TextJamEditor = __webpack_exports__.$C;
395512
+ var __webpack_exports__useCurriculumDisplayMode = __webpack_exports__.sD;
395513
+ export { __webpack_exports__CurriculumDisplayMode as CurriculumDisplayMode, __webpack_exports__TextJamEditor as TextJamEditor, __webpack_exports__useCurriculumDisplayMode as useCurriculumDisplayMode };