mbeditor 0.4.4 → 0.5.0

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.
@@ -62,7 +62,8 @@ var DEFAULT_EDITOR_PREFS = {
62
62
  fileTreeTypeahead: true,
63
63
  quickOpenShowFolders: false,
64
64
  tabDisplayMode: 'scroll',
65
- persistFindState: true
65
+ persistFindState: true,
66
+ showDotFiles: false
66
67
  };
67
68
 
68
69
  var SidebarActionButton = function SidebarActionButton(_ref) {
@@ -218,6 +219,21 @@ var MbeditorApp = function MbeditorApp() {
218
219
  var searchWholeWordRef = useRef(false);
219
220
  var searchResultsContainerRef = useRef(null);
220
221
 
222
+ var _useStateRM = useState(false);
223
+ var _useStateRM2 = _slicedToArray(_useStateRM, 2);
224
+ var replaceMode = _useStateRM2[0];
225
+ var setReplaceMode = _useStateRM2[1];
226
+
227
+ var _useStateRQ = useState('');
228
+ var _useStateRQ2 = _slicedToArray(_useStateRQ, 2);
229
+ var replaceQuery = _useStateRQ2[0];
230
+ var setReplaceQuery = _useStateRQ2[1];
231
+
232
+ var _useStateRL = useState(false);
233
+ var _useStateRL2 = _slicedToArray(_useStateRL, 2);
234
+ var replaceLoading = _useStateRL2[0];
235
+ var setReplaceLoading = _useStateRL2[1];
236
+
221
237
  var _useState8 = useState("explorer");
222
238
 
223
239
  var _useState82 = _slicedToArray(_useState8, 2);
@@ -432,10 +448,16 @@ var MbeditorApp = function MbeditorApp() {
432
448
  var contextMenu = _useState232[0];
433
449
  var setContextMenu = _useState232[1];
434
450
 
451
+ var _useState24 = useState(140);
452
+ var _useState242 = _slicedToArray(_useState24, 2);
453
+ var openEditorsHeight = _useState242[0];
454
+ var setOpenEditorsHeight = _useState242[1];
455
+
435
456
  var resizeSessionRef = useRef(null);
436
457
  var resizeRafRef = useRef(null);
437
458
  var prevGitBranchRef = useRef(null);
438
459
  var isSwitchingBranchRef = useRef(false);
460
+ var stateRestoredRef = useRef(false);
439
461
 
440
462
  // ── Draft backup helpers ─────────────────────────────────────────────────
441
463
  var draftWriteTimerRef = useRef({});
@@ -467,10 +489,26 @@ var MbeditorApp = function MbeditorApp() {
467
489
  var draftRestoreOffer = _useState_dro2[0];
468
490
  var setDraftRestoreOffer = _useState_dro2[1];
469
491
 
492
+ var _useStateMR = useState(false);
493
+ var _useStateMR2 = _slicedToArray(_useStateMR, 2);
494
+ var monacoReady = _useStateMR2[0];
495
+ var setMonacoReady = _useStateMR2[1];
496
+
497
+ var _useStateZen = useState(false);
498
+ var _useStateZen2 = _slicedToArray(_useStateZen, 2);
499
+ var zenMode = _useStateZen2[0];
500
+ var setZenMode = _useStateZen2[1];
501
+
470
502
  var clamp = function clamp(value, min, max) {
471
503
  return Math.min(max, Math.max(min, value));
472
504
  };
473
505
 
506
+ var filterDotFiles = function filterDotFiles(nodes) {
507
+ return nodes.filter(function(n) { return n.name[0] !== '.'; }).map(function(n) {
508
+ return n.children ? Object.assign({}, n, { children: filterDotFiles(n.children) }) : n;
509
+ });
510
+ };
511
+
474
512
  var normalizeRelativePath = function normalizeRelativePath(input) {
475
513
  return (input || "").replace(/\\/g, "/").trim().replace(/^\/+/, "").replace(/\/+$/, "").replace(/\/+/g, "/");
476
514
  };
@@ -654,6 +692,17 @@ var MbeditorApp = function MbeditorApp() {
654
692
  // Subscribe to EditorStore
655
693
  var unsubscribe = EditorStore.subscribe(setState);
656
694
 
695
+ // Resolve monacoReady when the __monacoReady promise settles.
696
+ // This lets EditorPanel defer monaco.editor.create() until Monaco is loaded
697
+ // while the rest of the UI (file tree, tabs, sidebar) renders immediately.
698
+ var _mrMounted = true;
699
+ if (window.__monacoReady && typeof window.__monacoReady.then === 'function') {
700
+ window.__monacoReady.then(function() { if (_mrMounted) setMonacoReady(true); });
701
+ } else {
702
+ // Fallback: Monaco was already loaded synchronously (e.g. tests / old path).
703
+ setMonacoReady(true);
704
+ }
705
+
657
706
  // Initial load
658
707
  Promise.all([FileService.getWorkspace()["catch"](function () {
659
708
  return null;
@@ -765,6 +814,10 @@ var MbeditorApp = function MbeditorApp() {
765
814
  if (typeof savedState.gitPanelWidth === 'number') {
766
815
  setGitPanelWidth(savedState.gitPanelWidth);
767
816
  }
817
+ if (typeof savedState.openEditorsHeight === 'number') {
818
+ setOpenEditorsHeight(savedState.openEditorsHeight);
819
+ }
820
+ stateRestoredRef.current = true;
768
821
 
769
822
  // Load pane state for current branch; fall back to legacy global state panes
770
823
  var branchStatePromise = branch
@@ -873,6 +926,7 @@ var MbeditorApp = function MbeditorApp() {
873
926
  e.preventDefault();
874
927
  toggleGitPanel();
875
928
  }
929
+ // Ctrl+Shift+Z is handled in capture phase below so Monaco cannot swallow it.
876
930
  if (e.key === 'Escape') {
877
931
  setContextMenu(null);
878
932
  setShowHelp(false);
@@ -886,6 +940,7 @@ var MbeditorApp = function MbeditorApp() {
886
940
  // Throttle via rAF — skip if a frame is already queued to avoid paint thrashing
887
941
  if (resizeRafRef.current) return;
888
942
  var clientX = e.clientX;
943
+ var clientY = e.clientY;
889
944
  resizeRafRef.current = requestAnimationFrame(function () {
890
945
  resizeRafRef.current = null;
891
946
  var s = resizeSessionRef.current;
@@ -919,6 +974,12 @@ var MbeditorApp = function MbeditorApp() {
919
974
  var nextWidth = rect.right - clientX;
920
975
  setGitPanelWidth(clamp(nextWidth, GIT_PANEL_MIN_WIDTH, 600));
921
976
  }
977
+
978
+ if (s.mode === 'openeditors') {
979
+ var delta = clientY - s.startY;
980
+ var nextHeight = Math.max(60, Math.min(400, s.startHeight + delta));
981
+ setOpenEditorsHeight(nextHeight);
982
+ }
922
983
  });
923
984
  };
924
985
 
@@ -935,10 +996,22 @@ var MbeditorApp = function MbeditorApp() {
935
996
  document.body.style.userSelect = '';
936
997
  };
937
998
 
999
+ // Capture-phase listener for Ctrl+Shift+Z so it fires before Monaco's
1000
+ // own keybinding handler (which intercepts in the bubble phase).
1001
+ var onZenCapture = function(e) {
1002
+ if (e.ctrlKey && !e.metaKey && e.shiftKey && e.key.toLowerCase() === 'z') {
1003
+ e.preventDefault();
1004
+ e.stopPropagation();
1005
+ toggleZenMode();
1006
+ }
1007
+ };
1008
+
938
1009
  window.addEventListener('keydown', onKeyDown);
1010
+ document.addEventListener('keydown', onZenCapture, true);
939
1011
  window.addEventListener('mousemove', handleMouseMove);
940
1012
  window.addEventListener('mouseup', handleMouseUp);
941
1013
  return function () {
1014
+ _mrMounted = false;
942
1015
  unsubscribe();
943
1016
  unsubBranch();
944
1017
  if (resizeRafRef.current) {
@@ -946,6 +1019,7 @@ var MbeditorApp = function MbeditorApp() {
946
1019
  resizeRafRef.current = null;
947
1020
  }
948
1021
  window.removeEventListener('keydown', onKeyDown);
1022
+ document.removeEventListener('keydown', onZenCapture, true);
949
1023
  window.removeEventListener('mousemove', handleMouseMove);
950
1024
  window.removeEventListener('mouseup', handleMouseUp);
951
1025
  document.body.style.cursor = '';
@@ -1117,7 +1191,13 @@ var MbeditorApp = function MbeditorApp() {
1117
1191
  EditorStore.setStatus("Saving " + tab.name + "...", "info");
1118
1192
  FileService.saveFile(tab.path, tab.content).then(function () {
1119
1193
  EditorStore.setStatus("Saved", "success");
1194
+ SearchService.invalidate();
1120
1195
  GitService.fetchStatus();
1196
+ // Reset the AVI clean baseline so undo past this save point shows dirty correctly.
1197
+ var _closeEntry = window.__mbeditorModels && window.__mbeditorModels[tab.path];
1198
+ if (_closeEntry && _closeEntry.model && !_closeEntry.model.isDisposed()) {
1199
+ _closeEntry.cleanVersionId = _closeEntry.model.getAlternativeVersionId();
1200
+ }
1121
1201
  TabManager.closeTab(closingPaneId, tab.id);
1122
1202
  })["catch"](function (err) {
1123
1203
  EditorStore.setStatus("Save failed: " + err.message, "error");
@@ -1174,6 +1254,8 @@ var MbeditorApp = function MbeditorApp() {
1174
1254
 
1175
1255
  // Persist state when panes, focusedPaneId, or collapsedSections changes
1176
1256
  useEffect(function () {
1257
+ // Don't overwrite server state with React defaults before the initial load completes.
1258
+ if (!stateRestoredRef.current) return;
1177
1259
  // debounce explicitly using setTimeout to avoid spamming the backend
1178
1260
  var timeoutId = setTimeout(function () {
1179
1261
  var st = EditorStore.getState();
@@ -1205,12 +1287,12 @@ var MbeditorApp = function MbeditorApp() {
1205
1287
  FileService.saveBranchState(currentBranch, { panes: lightweightPanes, focusedPaneId: st.focusedPaneId })["catch"](function () {});
1206
1288
  }
1207
1289
  // Also persist to global state (prefs + panes as legacy fallback)
1208
- FileService.saveState({ panes: lightweightPanes, focusedPaneId: st.focusedPaneId, collapsedSections: collapsedSections, expandedDirs: expandedDirs, showGitPanel: showGitPanel, gitPanelWidth: gitPanelWidth, editorPrefs: editorPrefs, activeSidebarTab: activeSidebarTab, sidebarCollapsed: sidebarCollapsed });
1290
+ FileService.saveState({ panes: lightweightPanes, focusedPaneId: st.focusedPaneId, collapsedSections: collapsedSections, expandedDirs: expandedDirs, showGitPanel: showGitPanel, gitPanelWidth: gitPanelWidth, editorPrefs: editorPrefs, activeSidebarTab: activeSidebarTab, sidebarCollapsed: sidebarCollapsed, openEditorsHeight: openEditorsHeight });
1209
1291
  }, 1000);
1210
1292
  return function () {
1211
1293
  return clearTimeout(timeoutId);
1212
1294
  };
1213
- }, [state.panes, state.focusedPaneId, collapsedSections, expandedDirs, showGitPanel, gitPanelWidth, editorPrefs, activeSidebarTab, sidebarCollapsed]);
1295
+ }, [state.panes, state.focusedPaneId, collapsedSections, expandedDirs, showGitPanel, gitPanelWidth, editorPrefs, activeSidebarTab, sidebarCollapsed, openEditorsHeight]);
1214
1296
 
1215
1297
  useEffect(function() {
1216
1298
  document.documentElement.setAttribute('data-theme', editorPrefs.theme || 'vs-dark');
@@ -1366,14 +1448,20 @@ var MbeditorApp = function MbeditorApp() {
1366
1448
  return p;
1367
1449
  });
1368
1450
  EditorStore.setState({ panes: newPanes });
1451
+ // Reset the AVI clean baseline so undo past this save point shows dirty correctly.
1452
+ var _modelEntry = window.__mbeditorModels && window.__mbeditorModels[tab.path];
1453
+ if (_modelEntry && _modelEntry.model && !_modelEntry.model.isDisposed()) {
1454
+ _modelEntry.cleanVersionId = _modelEntry.model.getAlternativeVersionId();
1455
+ }
1369
1456
  EditorStore.setStatus("Saved", "success");
1370
1457
  _clearDraft(tab.path);
1371
-
1458
+ SearchService.invalidate();
1459
+
1372
1460
  // Hot reload for Markdown: sync preview tab after save
1373
1461
  if (/\.(md|markdown)$/i.test(tab.path)) {
1374
1462
  TabManager.syncMarkdownPreview(tab.path, tab.content);
1375
1463
  }
1376
-
1464
+
1377
1465
  GitService.fetchStatus();
1378
1466
  })["catch"](function (err) {
1379
1467
  EditorStore.setStatus("Save failed: " + err.message, "error");
@@ -1407,7 +1495,15 @@ var MbeditorApp = function MbeditorApp() {
1407
1495
  });
1408
1496
  });
1409
1497
  EditorStore.setState({ panes: newPanes });
1498
+ // Reset AVI clean baselines for all saved files so undo past save shows dirty correctly.
1499
+ dirtyTabs.forEach(function(tab) {
1500
+ var _me = window.__mbeditorModels && window.__mbeditorModels[tab.path];
1501
+ if (_me && _me.model && !_me.model.isDisposed()) {
1502
+ _me.cleanVersionId = _me.model.getAlternativeVersionId();
1503
+ }
1504
+ });
1410
1505
  EditorStore.setStatus("All files saved", "success");
1506
+ SearchService.invalidate();
1411
1507
  GitService.fetchStatus();
1412
1508
  })["catch"](function (err) {
1413
1509
  EditorStore.setStatus("Failed to save some files", "error");
@@ -1511,38 +1607,51 @@ var MbeditorApp = function MbeditorApp() {
1511
1607
  };
1512
1608
  var parserName = formatMap[ext];
1513
1609
 
1514
- if (parserName && window.prettier && window.prettierPlugins) {
1610
+ if (parserName) {
1515
1611
  setLoading(function (prev) {
1516
1612
  return _extends({}, prev, { format: true });
1517
1613
  });
1518
1614
  EditorStore.setStatus("Formatting with Prettier...", "info");
1519
- window.prettier.format(activeTab.content, {
1520
- parser: parserName,
1521
- plugins: Object.values(window.prettierPlugins),
1522
- printWidth: editorPrefs.prettierPrintWidth != null ? editorPrefs.prettierPrintWidth : 80,
1523
- tabWidth: editorPrefs.prettierTabWidth != null ? editorPrefs.prettierTabWidth : 2,
1524
- useTabs: !!editorPrefs.prettierUseTabs,
1525
- semi: editorPrefs.prettierSemi !== false,
1526
- singleQuote: !!editorPrefs.prettierSingleQuote,
1527
- trailingComma: editorPrefs.prettierTrailingComma || 'all',
1528
- bracketSpacing: editorPrefs.prettierBracketSpacing !== false
1529
- }).then(function (formatted) {
1530
- var newPanes = EditorStore.getState().panes.map(function (p) {
1531
- if (p.id === focusedPane.id) return _extends({}, p, { tabs: p.tabs.map(function (t) {
1532
- return t.id === activeTab.id ? _extends({}, t, { content: formatted, dirty: true, externalContentVersion: (t.externalContentVersion || 0) + 1 }) : t;
1533
- }) });
1534
- return p;
1615
+ var doFormat = function doFormat() {
1616
+ return window.prettier.format(activeTab.content, {
1617
+ parser: parserName,
1618
+ plugins: Object.values(window.prettierPlugins),
1619
+ printWidth: editorPrefs.prettierPrintWidth != null ? editorPrefs.prettierPrintWidth : 80,
1620
+ tabWidth: editorPrefs.prettierTabWidth != null ? editorPrefs.prettierTabWidth : 2,
1621
+ useTabs: !!editorPrefs.prettierUseTabs,
1622
+ semi: editorPrefs.prettierSemi !== false,
1623
+ singleQuote: !!editorPrefs.prettierSingleQuote,
1624
+ trailingComma: editorPrefs.prettierTrailingComma || 'all',
1625
+ bracketSpacing: editorPrefs.prettierBracketSpacing !== false
1626
+ }).then(function (formatted) {
1627
+ var newPanes = EditorStore.getState().panes.map(function (p) {
1628
+ if (p.id === focusedPane.id) return _extends({}, p, { tabs: p.tabs.map(function (t) {
1629
+ return t.id === activeTab.id ? _extends({}, t, { content: formatted, dirty: true, externalContentVersion: (t.externalContentVersion || 0) + 1 }) : t;
1630
+ }) });
1631
+ return p;
1632
+ });
1633
+ EditorStore.setState({ panes: newPanes });
1634
+ EditorStore.setStatus("Formatted (Unsaved)", "success");
1635
+ GitService.fetchStatus();
1636
+ })["catch"](function (err) {
1637
+ EditorStore.setStatus("Prettier Formatter failed: " + err.message, "error");
1638
+ })["finally"](function () {
1639
+ setLoading(function (prev) {
1640
+ return _extends({}, prev, { format: false });
1641
+ });
1535
1642
  });
1536
- EditorStore.setState({ panes: newPanes });
1537
- EditorStore.setStatus("Formatted (Unsaved)", "success");
1538
- GitService.fetchStatus();
1539
- })["catch"](function (err) {
1540
- EditorStore.setStatus("Prettier Formatter failed: " + err.message, "error");
1541
- })["finally"](function () {
1542
- setLoading(function (prev) {
1543
- return _extends({}, prev, { format: false });
1643
+ };
1644
+ if (window.prettier && window.prettierPlugins) {
1645
+ doFormat();
1646
+ } else if (window.loadPrettierPlugins) {
1647
+ window.loadPrettierPlugins().then(doFormat)["catch"](function (err) {
1648
+ EditorStore.setStatus("Failed to load Prettier: " + err.message, "error");
1649
+ setLoading(function (prev) { return _extends({}, prev, { format: false }); });
1544
1650
  });
1545
- });
1651
+ } else {
1652
+ EditorStore.setStatus("Prettier is not available.", "warning");
1653
+ setLoading(function (prev) { return _extends({}, prev, { format: false }); });
1654
+ }
1546
1655
  return;
1547
1656
  }
1548
1657
 
@@ -1741,6 +1850,59 @@ var MbeditorApp = function MbeditorApp() {
1741
1850
  _debouncedSearch(searchQuery);
1742
1851
  };
1743
1852
 
1853
+ var handleReplaceAll = function handleReplaceAll() {
1854
+ if (!searchQuery.trim()) {
1855
+ EditorStore.setStatus("Enter a search query first", "error");
1856
+ return;
1857
+ }
1858
+ var matchCount = (state.searchResults || []).length;
1859
+ var confirmMsg = "Replace all occurrences of \"" + searchQuery + "\" with \"" + replaceQuery + "\"?";
1860
+ if (matchCount > 0) {
1861
+ confirmMsg += " (" + matchCount + (searchHasMore ? "+" : "") + " match" + (matchCount !== 1 ? "es" : "") + " across files)";
1862
+ }
1863
+ if (!window.confirm(confirmMsg)) return;
1864
+
1865
+ setReplaceLoading(true);
1866
+ EditorStore.setStatus("Replacing…", "info");
1867
+ SearchService.replaceInFiles(searchQuery, replaceQuery, {
1868
+ regex: searchUseRegexRef.current,
1869
+ matchCase: searchMatchCaseRef.current,
1870
+ wholeWord: searchWholeWordRef.current
1871
+ }).then(function(data) {
1872
+ var count = data.replaced_count || 0;
1873
+ var files = data.files_affected || [];
1874
+ var errors = data.errors || [];
1875
+ var msg = "Replaced " + count + " occurrence" + (count !== 1 ? "s" : "") + " in " + files.length + " file" + (files.length !== 1 ? "s" : "");
1876
+ if (errors.length) msg += " (" + errors.length + " error" + (errors.length !== 1 ? "s" : "") + ")";
1877
+ EditorStore.setStatus(msg, errors.length ? "warning" : "success");
1878
+
1879
+ // Invalidate search cache so next search reflects the new content.
1880
+ SearchService.invalidate();
1881
+
1882
+ // Update any open Monaco models whose content changed.
1883
+ if (files.length && window.__mbeditorModels) {
1884
+ files.forEach(function(relPath) {
1885
+ var model = window.__mbeditorModels[relPath];
1886
+ if (!model) return;
1887
+ FileService.getFile(relPath).then(function(res) {
1888
+ if (res && res.content != null && !model.isDisposed()) {
1889
+ model.setValue(res.content);
1890
+ }
1891
+ }).catch(function() {});
1892
+ });
1893
+ }
1894
+
1895
+ // Refresh search results to reflect replacements.
1896
+ if (searchQueryRef.current) {
1897
+ _debouncedSearch(searchQueryRef.current);
1898
+ }
1899
+ }).catch(function(err) {
1900
+ EditorStore.setStatus("Replace failed: " + (err.message || String(err)), "error");
1901
+ }).finally(function() {
1902
+ setReplaceLoading(false);
1903
+ });
1904
+ };
1905
+
1744
1906
  // Load more results when the user scrolls near the bottom of the list.
1745
1907
  var handleSearchResultsScroll = function handleSearchResultsScroll(e) {
1746
1908
  var el = e.currentTarget;
@@ -1756,6 +1918,22 @@ var MbeditorApp = function MbeditorApp() {
1756
1918
  });
1757
1919
  };
1758
1920
 
1921
+ var toggleZenMode = function toggleZenMode() {
1922
+ setZenMode(function (prev) {
1923
+ var next = !prev;
1924
+ // After React re-renders the new layout, call layout() on all visible Monaco editors
1925
+ // so they fill the reclaimed space correctly.
1926
+ setTimeout(function () {
1927
+ if (window.monaco && window.monaco.editor) {
1928
+ window.monaco.editor.getEditors().forEach(function(ed) {
1929
+ if (typeof ed.layout === 'function') ed.layout();
1930
+ });
1931
+ }
1932
+ }, 50);
1933
+ return next;
1934
+ });
1935
+ };
1936
+
1759
1937
  var startGitPanelResize = function startGitPanelResize(e) {
1760
1938
  e.preventDefault();
1761
1939
  resizeSessionRef.current = { mode: 'gitpanel' };
@@ -1874,6 +2052,15 @@ var MbeditorApp = function MbeditorApp() {
1874
2052
  document.body.style.userSelect = 'none';
1875
2053
  };
1876
2054
 
2055
+ var startOpenEditorsResize = function startOpenEditorsResize(e) {
2056
+ if (collapsedSections.openEditors) return;
2057
+ e.preventDefault();
2058
+ resizeSessionRef.current = { mode: 'openeditors', startY: e.clientY, startHeight: openEditorsHeight };
2059
+ setActiveResizeMode('openeditors');
2060
+ document.body.style.cursor = 'row-resize';
2061
+ document.body.style.userSelect = 'none';
2062
+ };
2063
+
1877
2064
  var toggleSidebarCollapsed = function toggleSidebarCollapsed() {
1878
2065
  setSidebarCollapsed(function (prev) { return !prev; });
1879
2066
  };
@@ -2404,7 +2591,7 @@ var MbeditorApp = function MbeditorApp() {
2404
2591
  { className: "ide-body", id: "ide-body-container" },
2405
2592
  React.createElement(
2406
2593
  "div",
2407
- { className: "ide-sidebar" + (sidebarCollapsed ? " ide-sidebar-collapsed" : ""), style: { width: (sidebarCollapsed ? SIDEBAR_COLLAPSED_WIDTH : sidebarWidth) + "px" } },
2594
+ { className: "ide-sidebar" + (sidebarCollapsed ? " ide-sidebar-collapsed" : ""), style: { width: (sidebarCollapsed ? SIDEBAR_COLLAPSED_WIDTH : sidebarWidth) + "px", display: zenMode ? 'none' : undefined } },
2408
2595
  sidebarCollapsed
2409
2596
  ? React.createElement(
2410
2597
  "div",
@@ -2474,7 +2661,7 @@ var MbeditorApp = function MbeditorApp() {
2474
2661
  { className: "ide-sidebar-content" },
2475
2662
  React.createElement(
2476
2663
  "div",
2477
- { className: "ide-sidebar-fixed" },
2664
+ { className: "ide-sidebar-fixed", style: { '--open-editors-height': openEditorsHeight + 'px' } },
2478
2665
  state.panes.flatMap(function (p) {
2479
2666
  return p.tabs;
2480
2667
  }).length > 0 && React.createElement(
@@ -2584,6 +2771,10 @@ var MbeditorApp = function MbeditorApp() {
2584
2771
  )
2585
2772
  )
2586
2773
  ),
2774
+ state.panes.flatMap(function (p) { return p.tabs; }).length > 0 && React.createElement(
2775
+ "div",
2776
+ { className: "open-editors-resize-handle", onMouseDown: startOpenEditorsResize }
2777
+ ),
2587
2778
  React.createElement(
2588
2779
  "div",
2589
2780
  { className: "ide-sidebar-scrollable" },
@@ -2643,7 +2834,7 @@ var MbeditorApp = function MbeditorApp() {
2643
2834
  )
2644
2835
  },
2645
2836
  React.createElement(FileTree, {
2646
- items: treeData,
2837
+ items: editorPrefs.showDotFiles ? treeData : filterDotFiles(treeData || []),
2647
2838
  onSelect: handleSoftOpenFile,
2648
2839
  activePath: editorPrefs.autoRevealInExplorer !== false ? (activeTab && activeTab.path) : null,
2649
2840
  selectedPaths: selectedPaths,
@@ -2712,6 +2903,16 @@ var MbeditorApp = function MbeditorApp() {
2712
2903
  },
2713
2904
  React.createElement("i", { className: "codicon codicon-regex" })
2714
2905
  ),
2906
+ React.createElement(
2907
+ "button",
2908
+ {
2909
+ type: "button",
2910
+ className: "search-adornment-btn" + (replaceMode ? " active" : ""),
2911
+ onClick: function() { setReplaceMode(function(p) { return !p; }); },
2912
+ title: "Toggle Replace"
2913
+ },
2914
+ React.createElement("i", { className: "codicon codicon-replace" })
2915
+ ),
2715
2916
  searchQuery && React.createElement(
2716
2917
  "button",
2717
2918
  {
@@ -2725,6 +2926,33 @@ var MbeditorApp = function MbeditorApp() {
2725
2926
  )
2726
2927
  )
2727
2928
  ),
2929
+ replaceMode && React.createElement(
2930
+ "div",
2931
+ { className: "search-replace-row" },
2932
+ React.createElement("input", {
2933
+ className: "search-input search-replace-input",
2934
+ placeholder: "Replace with…",
2935
+ value: replaceQuery,
2936
+ onChange: function(e) { setReplaceQuery(e.target.value); },
2937
+ disabled: replaceLoading
2938
+ }),
2939
+ React.createElement(
2940
+ "button",
2941
+ {
2942
+ type: "button",
2943
+ className: "search-replace-all-btn",
2944
+ onClick: handleReplaceAll,
2945
+ disabled: !searchQuery.trim() || replaceLoading,
2946
+ title: "Replace All"
2947
+ },
2948
+ replaceLoading
2949
+ ? React.createElement("i", { className: "fas fa-spinner fa-spin" })
2950
+ : React.createElement(React.Fragment, null,
2951
+ React.createElement("i", { className: "codicon codicon-replace-all" }),
2952
+ " Replace All"
2953
+ )
2954
+ )
2955
+ ),
2728
2956
  (function() {
2729
2957
  var allResults = state.searchResults || [];
2730
2958
  var loadedCount = allResults.length;
@@ -2923,12 +3151,17 @@ var MbeditorApp = function MbeditorApp() {
2923
3151
  'label', { className: 'ide-settings-row ide-settings-row-half' },
2924
3152
  React.createElement('span', { className: 'ide-settings-label' }, 'Font size'),
2925
3153
  React.createElement('input', {
3154
+ key: String(editorPrefs.fontSize || 13),
2926
3155
  type: 'number', min: '8', max: '32', step: '1',
2927
3156
  className: 'ide-settings-input',
2928
- value: editorPrefs.fontSize || 13,
3157
+ defaultValue: editorPrefs.fontSize || 13,
2929
3158
  onChange: function(e) {
2930
3159
  var v = parseInt(e.target.value, 10);
2931
3160
  if (v >= 8 && v <= 32) setEditorPrefs(function(p) { return Object.assign({}, p, { fontSize: v }); });
3161
+ },
3162
+ onBlur: function(e) {
3163
+ var v = parseInt(e.target.value, 10);
3164
+ if (isNaN(v) || v < 8 || v > 32) e.target.value = String(editorPrefs.fontSize || 13);
2932
3165
  }
2933
3166
  })
2934
3167
  ),
@@ -2946,12 +3179,17 @@ var MbeditorApp = function MbeditorApp() {
2946
3179
  'label', { className: 'ide-settings-row ide-settings-row-half', title: 'Row height in pixels. 0 = auto (roughly font size × 1.5)' },
2947
3180
  React.createElement('span', { className: 'ide-settings-label' }, 'Line height (0=auto)'),
2948
3181
  React.createElement('input', {
3182
+ key: String(editorPrefs.lineHeight != null ? editorPrefs.lineHeight : 0),
2949
3183
  type: 'number', min: '0', max: '100', step: '1',
2950
3184
  className: 'ide-settings-input',
2951
- value: editorPrefs.lineHeight != null ? editorPrefs.lineHeight : 0,
3185
+ defaultValue: editorPrefs.lineHeight != null ? editorPrefs.lineHeight : 0,
2952
3186
  onChange: function(e) {
2953
3187
  var v = parseInt(e.target.value, 10);
2954
3188
  if (!isNaN(v) && v >= 0 && v <= 100) setEditorPrefs(function(p) { return Object.assign({}, p, { lineHeight: v }); });
3189
+ },
3190
+ onBlur: function(e) {
3191
+ var v = parseInt(e.target.value, 10);
3192
+ if (isNaN(v) || v < 0 || v > 100) e.target.value = String(editorPrefs.lineHeight != null ? editorPrefs.lineHeight : 0);
2955
3193
  }
2956
3194
  })
2957
3195
  ),
@@ -2959,12 +3197,17 @@ var MbeditorApp = function MbeditorApp() {
2959
3197
  'label', { className: 'ide-settings-row ide-settings-row-half', title: 'Extra space between characters in pixels. 0 = default' },
2960
3198
  React.createElement('span', { className: 'ide-settings-label' }, 'Letter spacing (px)'),
2961
3199
  React.createElement('input', {
3200
+ key: String(editorPrefs.letterSpacing != null ? editorPrefs.letterSpacing : 0),
2962
3201
  type: 'number', min: '-5', max: '20', step: '0.5',
2963
3202
  className: 'ide-settings-input',
2964
- value: editorPrefs.letterSpacing != null ? editorPrefs.letterSpacing : 0,
3203
+ defaultValue: editorPrefs.letterSpacing != null ? editorPrefs.letterSpacing : 0,
2965
3204
  onChange: function(e) {
2966
3205
  var v = parseFloat(e.target.value);
2967
3206
  if (!isNaN(v) && v >= -5 && v <= 20) setEditorPrefs(function(p) { return Object.assign({}, p, { letterSpacing: v }); });
3207
+ },
3208
+ onBlur: function(e) {
3209
+ var v = parseFloat(e.target.value);
3210
+ if (isNaN(v) || v < -5 || v > 20) e.target.value = String(editorPrefs.letterSpacing != null ? editorPrefs.letterSpacing : 0);
2968
3211
  }
2969
3212
  })
2970
3213
  ),
@@ -2975,12 +3218,17 @@ var MbeditorApp = function MbeditorApp() {
2975
3218
  'label', { className: 'ide-settings-row ide-settings-row-half' },
2976
3219
  React.createElement('span', { className: 'ide-settings-label' }, 'Tab size'),
2977
3220
  React.createElement('input', {
3221
+ key: String(editorPrefs.tabSize || 4),
2978
3222
  type: 'number', min: '1', max: '8', step: '1',
2979
3223
  className: 'ide-settings-input',
2980
- value: editorPrefs.tabSize || 4,
3224
+ defaultValue: editorPrefs.tabSize || 4,
2981
3225
  onChange: function(e) {
2982
3226
  var v = parseInt(e.target.value, 10);
2983
3227
  if (v >= 1 && v <= 8) setEditorPrefs(function(p) { return Object.assign({}, p, { tabSize: v }); });
3228
+ },
3229
+ onBlur: function(e) {
3230
+ var v = parseInt(e.target.value, 10);
3231
+ if (isNaN(v) || v < 1 || v > 8) e.target.value = String(editorPrefs.tabSize || 4);
2984
3232
  }
2985
3233
  })
2986
3234
  ),
@@ -3262,12 +3510,17 @@ var MbeditorApp = function MbeditorApp() {
3262
3510
  'label', { className: 'ide-settings-row ide-settings-row-half' },
3263
3511
  React.createElement('span', { className: 'ide-settings-label' }, 'Print width'),
3264
3512
  React.createElement('input', {
3513
+ key: String(editorPrefs.prettierPrintWidth != null ? editorPrefs.prettierPrintWidth : 80),
3265
3514
  type: 'number', min: '40', max: '200', step: '1',
3266
3515
  className: 'ide-settings-input',
3267
- value: editorPrefs.prettierPrintWidth != null ? editorPrefs.prettierPrintWidth : 80,
3516
+ defaultValue: editorPrefs.prettierPrintWidth != null ? editorPrefs.prettierPrintWidth : 80,
3268
3517
  onChange: function(e) {
3269
3518
  var v = parseInt(e.target.value, 10);
3270
3519
  if (v >= 40 && v <= 200) setEditorPrefs(function(p) { return Object.assign({}, p, { prettierPrintWidth: v }); });
3520
+ },
3521
+ onBlur: function(e) {
3522
+ var v = parseInt(e.target.value, 10);
3523
+ if (isNaN(v) || v < 40 || v > 200) e.target.value = String(editorPrefs.prettierPrintWidth != null ? editorPrefs.prettierPrintWidth : 80);
3271
3524
  }
3272
3525
  })
3273
3526
  ),
@@ -3337,6 +3590,16 @@ var MbeditorApp = function MbeditorApp() {
3337
3590
  onChange: function(e) { var v = e.target.checked; setEditorPrefs(function(p) { return Object.assign({}, p, { fileTreeTypeahead: v }); }); }
3338
3591
  })
3339
3592
  ),
3593
+ React.createElement(
3594
+ 'label', { className: 'ide-settings-row ide-settings-row-check' },
3595
+ React.createElement('span', { className: 'ide-settings-label' }, 'Show dotfiles'),
3596
+ React.createElement('input', {
3597
+ type: 'checkbox',
3598
+ className: 'ide-settings-checkbox',
3599
+ checked: !!(editorPrefs.showDotFiles),
3600
+ onChange: function(e) { var v = e.target.checked; setEditorPrefs(function(p) { return Object.assign({}, p, { showDotFiles: v }); }); }
3601
+ })
3602
+ ),
3340
3603
  React.createElement(
3341
3604
  'label', { className: 'ide-settings-row ide-settings-row-half' },
3342
3605
  React.createElement('span', { className: 'ide-settings-label' }, 'Tab bar layout'),
@@ -3445,22 +3708,21 @@ var MbeditorApp = function MbeditorApp() {
3445
3708
  testLoading: testLoading,
3446
3709
  testInlineVisible: testInlineVisible,
3447
3710
  editorPrefs: editorPrefs,
3711
+ monacoReady: monacoReady,
3448
3712
  onFormat: function() { onFormatRef.current(); },
3449
3713
  onSave: function() { handleSave(pane.id, pActiveTab); },
3450
3714
  onRunTest: handleRunTest,
3451
3715
  onShowHistory: function(path) { setHistoryPanelPath(path); },
3452
3716
  onContentChange: function onContentChange(val) {
3717
+ // Dirty/clean state is now set in EditorPanel via AVI comparison.
3718
+ // onContentChange only needs to handle draft persistence.
3453
3719
  var st = EditorStore.getState();
3454
3720
  var cp = st.panes.find(function(p) { return p.id === pane.id; });
3455
3721
  var ct = cp && cp.tabs.find(function(t) { return t.id === pActiveTab.id; });
3456
- var cleanNorm = ((ct && ct.cleanContent) || '').replace(/\r\n/g, '\n');
3457
- var valNorm = val.replace(/\r\n/g, '\n');
3458
- if (valNorm === cleanNorm) {
3459
- TabManager.markClean(pane.id, pActiveTab.id, val);
3460
- _clearDraft(pActiveTab.path);
3461
- } else {
3462
- TabManager.markDirty(pane.id, pActiveTab.id, val);
3722
+ if (ct && ct.dirty) {
3463
3723
  _scheduleDraftWrite(pActiveTab.path, val);
3724
+ } else {
3725
+ _clearDraft(pActiveTab.path);
3464
3726
  }
3465
3727
  }
3466
3728
  });
@@ -3576,6 +3838,10 @@ var MbeditorApp = function MbeditorApp() {
3576
3838
  React.createElement("td", null, React.createElement("kbd", null, "Ctrl+Shift+G")),
3577
3839
  React.createElement("td", null, "Toggle git panel")
3578
3840
  ),
3841
+ React.createElement("tr", null,
3842
+ React.createElement("td", null, React.createElement("kbd", null, "Ctrl+Shift+Z")),
3843
+ React.createElement("td", null, "Toggle zen / focus mode")
3844
+ ),
3579
3845
  React.createElement("tr", null,
3580
3846
  React.createElement("td", null, React.createElement("kbd", null, "Ctrl+Z\u00a0/\u00a0Ctrl+Y")),
3581
3847
  React.createElement("td", null, "Undo / Redo")
@@ -3617,14 +3883,14 @@ var MbeditorApp = function MbeditorApp() {
3617
3883
  ),
3618
3884
 
3619
3885
  // Right-side Git panel (children of ide-body, alongside sidebar and ide-main)
3620
- showGitPanel && React.createElement("div", {
3886
+ showGitPanel && !zenMode && React.createElement("div", {
3621
3887
  className: "panel-divider gitpanel-divider " + (activeResizeMode === 'gitpanel' ? 'active' : ''),
3622
3888
  onMouseDown: startGitPanelResize,
3623
3889
  role: "separator",
3624
3890
  "aria-orientation": "vertical",
3625
3891
  "aria-label": "Resize git panel"
3626
3892
  }),
3627
- showGitPanel && React.createElement(
3893
+ showGitPanel && !zenMode && React.createElement(
3628
3894
  "div",
3629
3895
  { className: "ide-git-right-panel", style: { width: gitPanelWidth + "px" } },
3630
3896
  React.createElement(window.GitPanel || GitPanel, {
@@ -3701,6 +3967,16 @@ var MbeditorApp = function MbeditorApp() {
3701
3967
  },
3702
3968
  activeEOL
3703
3969
  ),
3970
+ zenMode && React.createElement(
3971
+ "button",
3972
+ {
3973
+ type: "button",
3974
+ className: "statusbar-btn statusbar-zen-btn",
3975
+ title: "Zen mode active — click or press Ctrl+Shift+Z to exit",
3976
+ onClick: toggleZenMode
3977
+ },
3978
+ "ZEN"
3979
+ ),
3704
3980
  React.createElement(
3705
3981
  "div",
3706
3982
  { className: "statusbar-version" },