mbeditor 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +116 -0
  3. data/README.md +180 -0
  4. data/app/assets/javascripts/mbeditor/application.js +21 -0
  5. data/app/assets/javascripts/mbeditor/components/CodeReviewPanel.js +202 -0
  6. data/app/assets/javascripts/mbeditor/components/CollapsibleSection.js +71 -0
  7. data/app/assets/javascripts/mbeditor/components/CombinedDiffViewer.js +139 -0
  8. data/app/assets/javascripts/mbeditor/components/CommitGraph.js +65 -0
  9. data/app/assets/javascripts/mbeditor/components/DiffViewer.js +166 -0
  10. data/app/assets/javascripts/mbeditor/components/EditorPanel.js +1139 -0
  11. data/app/assets/javascripts/mbeditor/components/FileHistoryPanel.js +117 -0
  12. data/app/assets/javascripts/mbeditor/components/FileTree.js +339 -0
  13. data/app/assets/javascripts/mbeditor/components/GitPanel.js +501 -0
  14. data/app/assets/javascripts/mbeditor/components/MbeditorApp.js +3108 -0
  15. data/app/assets/javascripts/mbeditor/components/QuickOpenDialog.js +272 -0
  16. data/app/assets/javascripts/mbeditor/components/ShortcutHelp.js +186 -0
  17. data/app/assets/javascripts/mbeditor/components/TabBar.js +238 -0
  18. data/app/assets/javascripts/mbeditor/components/TestResultsPanel.js +150 -0
  19. data/app/assets/javascripts/mbeditor/editor_plugins.js +758 -0
  20. data/app/assets/javascripts/mbeditor/editor_store.js +69 -0
  21. data/app/assets/javascripts/mbeditor/file_icon.js +30 -0
  22. data/app/assets/javascripts/mbeditor/file_service.js +96 -0
  23. data/app/assets/javascripts/mbeditor/git_service.js +104 -0
  24. data/app/assets/javascripts/mbeditor/search_service.js +63 -0
  25. data/app/assets/javascripts/mbeditor/tab_manager.js +485 -0
  26. data/app/assets/stylesheets/mbeditor/application.css +848 -0
  27. data/app/assets/stylesheets/mbeditor/editor.css +2061 -0
  28. data/app/controllers/mbeditor/application_controller.rb +70 -0
  29. data/app/controllers/mbeditor/editors_controller.rb +996 -0
  30. data/app/controllers/mbeditor/git_controller.rb +234 -0
  31. data/app/services/mbeditor/git_blame_service.rb +98 -0
  32. data/app/services/mbeditor/git_commit_graph_service.rb +60 -0
  33. data/app/services/mbeditor/git_diff_service.rb +74 -0
  34. data/app/services/mbeditor/git_file_history_service.rb +42 -0
  35. data/app/services/mbeditor/git_service.rb +95 -0
  36. data/app/services/mbeditor/redmine_service.rb +86 -0
  37. data/app/services/mbeditor/ruby_definition_service.rb +168 -0
  38. data/app/services/mbeditor/test_runner_service.rb +286 -0
  39. data/app/views/layouts/mbeditor/application.html.erb +120 -0
  40. data/app/views/mbeditor/editors/index.html.erb +1 -0
  41. data/config/initializers/assets.rb +9 -0
  42. data/config/routes.rb +44 -0
  43. data/lib/mbeditor/configuration.rb +22 -0
  44. data/lib/mbeditor/engine.rb +37 -0
  45. data/lib/mbeditor/rack/silence_ping_request.rb +56 -0
  46. data/lib/mbeditor/version.rb +3 -0
  47. data/lib/mbeditor.rb +19 -0
  48. data/mbeditor.gemspec +31 -0
  49. data/public/mbeditor-icon.svg +4 -0
  50. data/public/min-maps/vs/base/worker/workerMain.js.map +1 -0
  51. data/public/monaco-editor/vs/base/browser/ui/codicons/codicon/codicon.ttf +0 -0
  52. data/public/monaco-editor/vs/base/worker/workerMain.js +31 -0
  53. data/public/monaco-editor/vs/basic-languages/cameligo/cameligo.js +10 -0
  54. data/public/monaco-editor/vs/basic-languages/css/css.js +12 -0
  55. data/public/monaco-editor/vs/basic-languages/dart/dart.js +10 -0
  56. data/public/monaco-editor/vs/basic-languages/flow9/flow9.js +10 -0
  57. data/public/monaco-editor/vs/basic-languages/go/go.js +10 -0
  58. data/public/monaco-editor/vs/basic-languages/handlebars/handlebars.js +440 -0
  59. data/public/monaco-editor/vs/basic-languages/javascript/javascript.js +10 -0
  60. data/public/monaco-editor/vs/basic-languages/markdown/markdown.js +10 -0
  61. data/public/monaco-editor/vs/basic-languages/msdax/msdax.js +10 -0
  62. data/public/monaco-editor/vs/basic-languages/postiats/postiats.js +10 -0
  63. data/public/monaco-editor/vs/basic-languages/pug/pug.js +412 -0
  64. data/public/monaco-editor/vs/basic-languages/restructuredtext/restructuredtext.js +10 -0
  65. data/public/monaco-editor/vs/basic-languages/ruby/ruby.js +10 -0
  66. data/public/monaco-editor/vs/basic-languages/sb/sb.js +10 -0
  67. data/public/monaco-editor/vs/basic-languages/shell/shell.js +41 -0
  68. data/public/monaco-editor/vs/basic-languages/typescript/typescript.js +10 -0
  69. data/public/monaco-editor/vs/basic-languages/typespec/typespec.js +10 -0
  70. data/public/monaco-editor/vs/basic-languages/yaml/yaml.js +10 -0
  71. data/public/monaco-editor/vs/editor/editor.api.js +6 -0
  72. data/public/monaco-editor/vs/editor/editor.main.css +8 -0
  73. data/public/monaco-editor/vs/editor/editor.main.js +797 -0
  74. data/public/monaco-editor/vs/language/typescript/tsMode.js +20 -0
  75. data/public/monaco-editor/vs/language/typescript/tsWorker.js +51328 -0
  76. data/public/monaco-editor/vs/loader.js +10 -0
  77. data/public/monaco-editor/vs/nls.messages.de.js +20 -0
  78. data/public/monaco-editor/vs/nls.messages.es.js +20 -0
  79. data/public/monaco-editor/vs/nls.messages.fr.js +18 -0
  80. data/public/monaco-editor/vs/nls.messages.it.js +18 -0
  81. data/public/monaco-editor/vs/nls.messages.ja.js +20 -0
  82. data/public/monaco-editor/vs/nls.messages.ko.js +18 -0
  83. data/public/monaco-editor/vs/nls.messages.ru.js +20 -0
  84. data/public/monaco-editor/vs/nls.messages.zh-cn.js +20 -0
  85. data/public/monaco-editor/vs/nls.messages.zh-tw.js +18 -0
  86. data/public/monaco_worker.js +5 -0
  87. data/public/sw.js +8 -0
  88. data/public/ts_worker.js +5 -0
  89. data/vendor/assets/javascripts/axios.min.js +5 -0
  90. data/vendor/assets/javascripts/emmet.js +5452 -0
  91. data/vendor/assets/javascripts/lodash.min.js +136 -0
  92. data/vendor/assets/javascripts/marked.min.js +6 -0
  93. data/vendor/assets/javascripts/minisearch.min.js +2044 -0
  94. data/vendor/assets/javascripts/monaco-themes-bundle.js +10 -0
  95. data/vendor/assets/javascripts/monaco-vim.js +9867 -0
  96. data/vendor/assets/javascripts/prettier-plugin-babel.js +16 -0
  97. data/vendor/assets/javascripts/prettier-plugin-estree.js +35 -0
  98. data/vendor/assets/javascripts/prettier-plugin-html.js +19 -0
  99. data/vendor/assets/javascripts/prettier-plugin-markdown.js +59 -0
  100. data/vendor/assets/javascripts/prettier-plugin-postcss.js +52 -0
  101. data/vendor/assets/javascripts/prettier-standalone.js +37 -0
  102. data/vendor/assets/javascripts/react-dom.min.js +267 -0
  103. data/vendor/assets/javascripts/react.min.js +31 -0
  104. data/vendor/assets/stylesheets/fontawesome.min.css.erb +9 -0
  105. data/vendor/assets/webfonts/fa-brands-400.woff2 +0 -0
  106. data/vendor/assets/webfonts/fa-regular-400.woff2 +0 -0
  107. data/vendor/assets/webfonts/fa-solid-900.woff2 +0 -0
  108. metadata +188 -0
@@ -0,0 +1,3108 @@
1
+ "use strict";
2
+
3
+ var _slicedToArray = (function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; })();
4
+
5
+ var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
6
+
7
+ function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
8
+
9
+ var _React = React;
10
+ var useState = _React.useState;
11
+ var useEffect = _React.useEffect;
12
+ var useRef = _React.useRef;
13
+
14
+ var SIDEBAR_MIN_WIDTH = 280;
15
+ var SIDEBAR_MAX_WIDTH = 560;
16
+ var EDITOR_MIN_WIDTH = 320;
17
+ var GIT_PANEL_MIN_WIDTH = 280;
18
+ var PANE_MIN_WIDTH_PERCENT = 20;
19
+ var PANE_MAX_WIDTH_PERCENT = 80;
20
+ var SIDEBAR_COLLAPSED_WIDTH = 48;
21
+ var SUPPORTED_PRETTIER_EXTS = ['js', 'jsx', 'json', 'css', 'scss', 'html', 'md'];
22
+
23
+ var DEFAULT_EDITOR_PREFS = {
24
+ theme: 'vs-dark',
25
+ fontSize: 13,
26
+ fontFamily: "'JetBrains Mono', 'Fira Code', Consolas, 'Courier New', monospace",
27
+ tabSize: 4,
28
+ insertSpaces: false,
29
+ wordWrap: 'off',
30
+ lineNumbers: 'on',
31
+ renderWhitespace: 'none',
32
+ scrollBeyondLastLine: false,
33
+ minimap: false,
34
+ bracketPairColorization: true,
35
+ autoRevealInExplorer: true,
36
+ toolbarIconOnly: false,
37
+ rubocopLintEnabled: true,
38
+ prettierPrintWidth: 80,
39
+ prettierTabWidth: 2,
40
+ prettierUseTabs: false,
41
+ prettierSemi: true,
42
+ prettierSingleQuote: false,
43
+ prettierTrailingComma: 'all',
44
+ prettierBracketSpacing: true,
45
+ vimMode: false
46
+ };
47
+
48
+ var SidebarActionButton = function SidebarActionButton(_ref) {
49
+ var title = _ref.title;
50
+ var iconClass = _ref.iconClass;
51
+ var onClick = _ref.onClick;
52
+ var _ref$disabled = _ref.disabled;
53
+ var disabled = _ref$disabled === undefined ? false : _ref$disabled;
54
+ var _ref$danger = _ref.danger;
55
+ var danger = _ref$danger === undefined ? false : _ref$danger;
56
+ var _ref$ariaLabel = _ref.ariaLabel;
57
+ var ariaLabel = _ref$ariaLabel === undefined ? null : _ref$ariaLabel;
58
+
59
+ return React.createElement(
60
+ "button",
61
+ {
62
+ type: "button",
63
+ className: "project-action-btn" + (danger ? " danger" : ""),
64
+ title: title,
65
+ "aria-label": ariaLabel || title,
66
+ onClick: onClick,
67
+ disabled: !!disabled
68
+ },
69
+ React.createElement("i", { className: iconClass })
70
+ );
71
+ };
72
+
73
+ var SectionActionGroup = function SectionActionGroup(_ref2) {
74
+ var ariaLabel = _ref2.ariaLabel;
75
+ var children = _ref2.children;
76
+ var _ref2$className = _ref2.className;
77
+ var className = _ref2$className === undefined ? "" : _ref2$className;
78
+
79
+ return React.createElement(
80
+ "div",
81
+ {
82
+ className: "project-actions" + (className ? " " + className : ""),
83
+ role: "toolbar",
84
+ "aria-label": ariaLabel
85
+ },
86
+ children
87
+ );
88
+ };
89
+
90
+ var MbeditorApp = function MbeditorApp() {
91
+ var _useState = useState(EditorStore.getState());
92
+
93
+ var _useState2 = _slicedToArray(_useState, 2);
94
+
95
+ var state = _useState2[0];
96
+ var setState = _useState2[1];
97
+
98
+ var _useState21 = useState(null);
99
+ var _useState22 = _slicedToArray(_useState21, 2);
100
+ var historyPanelPath = _useState22[0];
101
+ var setHistoryPanelPath = _useState22[1];
102
+
103
+ var _useState23 = useState(false);
104
+ var _useState24 = _slicedToArray(_useState23, 2);
105
+ var isNavigating = _useState24[0];
106
+ var setIsNavigating = _useState24[1];
107
+
108
+ var _useState25 = useState(false);
109
+ var _useState26 = _slicedToArray(_useState25, 2);
110
+ var isReviewOpen = _useState26[0];
111
+ var setIsReviewOpen = _useState26[1];
112
+
113
+ var _useState27 = useState(null);
114
+ var _useState28 = _slicedToArray(_useState27, 2);
115
+ var selectedCommit = _useState28[0];
116
+ var setSelectedCommit = _useState28[1];
117
+
118
+ var _useState29 = useState(null);
119
+ var _useState30 = _slicedToArray(_useState29, 2);
120
+ var commitDetailFiles = _useState30[0];
121
+ var setCommitDetailFiles = _useState30[1];
122
+
123
+ var _useState4 = useState([]);
124
+
125
+ var _useState42 = _slicedToArray(_useState4, 2);
126
+
127
+ var treeData = _useState42[0];
128
+ var setTreeData = _useState42[1];
129
+
130
+ var _useState5 = useState("");
131
+
132
+ var _useState52 = _slicedToArray(_useState5, 2);
133
+
134
+ var projectRootName = _useState52[0];
135
+ var setProjectRootName = _useState52[1];
136
+
137
+ var _useState6 = useState(null);
138
+
139
+ var _useState62 = _slicedToArray(_useState6, 2);
140
+
141
+ var selectedTreeNode = _useState62[0];
142
+ var setSelectedTreeNode = _useState62[1];
143
+
144
+ var _useState7 = useState("");
145
+
146
+ var _useState72 = _slicedToArray(_useState7, 2);
147
+
148
+ var searchQuery = _useState72[0];
149
+ var setSearchQuery = _useState72[1];
150
+
151
+ var _useState33 = useState(false);
152
+ var _useState332 = _slicedToArray(_useState33, 2);
153
+ var searchLoading = _useState332[0];
154
+ var setSearchLoading = _useState332[1];
155
+
156
+ var searchRequestIdRef = useRef(0);
157
+
158
+ var _useState8 = useState("explorer");
159
+
160
+ var _useState82 = _slicedToArray(_useState8, 2);
161
+
162
+ var activeSidebarTab = _useState82[0];
163
+ var setActiveSidebarTab = _useState82[1];
164
+
165
+ var _useStateSC = useState(false);
166
+ var _useStateSC2 = _slicedToArray(_useStateSC, 2);
167
+ var sidebarCollapsed = _useStateSC2[0];
168
+ var setSidebarCollapsed = _useStateSC2[1];
169
+
170
+ var _useState9 = useState({});
171
+
172
+ var _useState92 = _slicedToArray(_useState9, 2);
173
+
174
+ var markers = _useState92[0];
175
+ var setMarkers = _useState92[1];
176
+ // { tabId: [] }
177
+
178
+ var _useState10 = useState({});
179
+
180
+ var _useState102 = _slicedToArray(_useState10, 2);
181
+
182
+ var loading = _useState102[0];
183
+ var setLoading = _useState102[1];
184
+
185
+ var _useState11 = useState(null);
186
+
187
+ var _useState112 = _slicedToArray(_useState11, 2);
188
+
189
+ var closingTabId = _useState112[0];
190
+ var setClosingTabId = _useState112[1];
191
+
192
+ var _useState12 = useState(null);
193
+
194
+ var _useState122 = _slicedToArray(_useState12, 2);
195
+
196
+ var closingPaneId = _useState122[0];
197
+ var setClosingPaneId = _useState122[1];
198
+
199
+ var _useState13 = useState(SIDEBAR_MIN_WIDTH);
200
+
201
+ var _useState132 = _slicedToArray(_useState13, 2);
202
+
203
+ var sidebarWidth = _useState132[0];
204
+ var setSidebarWidth = _useState132[1];
205
+
206
+ var _useState14 = useState(50);
207
+
208
+ var _useState142 = _slicedToArray(_useState14, 2);
209
+
210
+ var pane1Width = _useState142[0];
211
+ var setPane1Width = _useState142[1];
212
+ // percentage
213
+
214
+ var dragSplitWidthRef = useRef(pane1Width);
215
+
216
+ var _useState15 = useState(null);
217
+
218
+ var _useState152 = _slicedToArray(_useState15, 2);
219
+
220
+ var activeResizeMode = _useState152[0];
221
+ var setActiveResizeMode = _useState152[1];
222
+
223
+ var _useState16 = useState(null);
224
+
225
+ var _useState162 = _slicedToArray(_useState16, 2);
226
+
227
+ var draggedTab = _useState162[0];
228
+ var setDraggedTab = _useState162[1];
229
+
230
+ var _useState17 = useState(null);
231
+
232
+ var _useState172 = _slicedToArray(_useState17, 2);
233
+
234
+ var dragOverPaneId = _useState172[0];
235
+ var setDragOverPaneId = _useState172[1];
236
+
237
+ var _useState18 = useState(false);
238
+ var _useState182 = _slicedToArray(_useState18, 2);
239
+ var showGitPanel = _useState182[0];
240
+ var setShowGitPanel = _useState182[1];
241
+ var showGitPanelRef = useRef(showGitPanel);
242
+ showGitPanelRef.current = showGitPanel;
243
+
244
+ var _useState18g = useState(320);
245
+ var _useState18g2 = _slicedToArray(_useState18g, 2);
246
+ var gitPanelWidth = _useState18g2[0];
247
+ var setGitPanelWidth = _useState18g2[1];
248
+ var gitPanelWidthRef = useRef(gitPanelWidth);
249
+ gitPanelWidthRef.current = gitPanelWidth;
250
+
251
+ var _useState18h = useState(false);
252
+
253
+ var _useState18h2 = _slicedToArray(_useState18h, 2);
254
+
255
+ var showHelp = _useState18h2[0];
256
+ var setShowHelp = _useState18h2[1];
257
+
258
+ var _useStatePwa = useState(null);
259
+ var _useStatePwa2 = _slicedToArray(_useStatePwa, 2);
260
+ var pwaInstallPrompt = _useStatePwa2[0];
261
+ var setPwaInstallPrompt = _useStatePwa2[1];
262
+
263
+ var _useState18b = useState(true);
264
+
265
+ var _useState18b2 = _slicedToArray(_useState18b, 2);
266
+
267
+ var serverOnline = _useState18b2[0];
268
+ var setServerOnline = _useState18b2[1];
269
+
270
+ var _useState18c = useState(false);
271
+
272
+ var _useState18c2 = _slicedToArray(_useState18c, 2);
273
+
274
+ var rubocopAvailable = _useState18c2[0];
275
+ var setRubocopAvailable = _useState18c2[1];
276
+
277
+ var _useState18d = useState(false);
278
+
279
+ var _useState18d2 = _slicedToArray(_useState18d, 2);
280
+
281
+ var hamlLintAvailable = _useState18d2[0];
282
+ var setHamlLintAvailable = _useState18d2[1];
283
+
284
+ var _useState18e = useState(false);
285
+ var _useState18e2 = _slicedToArray(_useState18e, 2);
286
+ var gitAvailable = _useState18e2[0];
287
+ var setGitAvailable = _useState18e2[1];
288
+
289
+ var _useState18f = useState(false);
290
+ var _useState18f2 = _slicedToArray(_useState18f, 2);
291
+ var redmineEnabled = _useState18f2[0];
292
+ var setRedmineEnabled = _useState18f2[1];
293
+
294
+ var _useState18rc = useState(null);
295
+ var _useState18rc2 = _slicedToArray(_useState18rc, 2);
296
+ var rubocopConfigPath = _useState18rc2[0];
297
+ var setRubocopConfigPath = _useState18rc2[1];
298
+
299
+ var _useState18t = useState(false);
300
+ var _useState18t2 = _slicedToArray(_useState18t, 2);
301
+ var testAvailable = _useState18t2[0];
302
+ var setTestAvailable = _useState18t2[1];
303
+
304
+ var _useState18u = useState(null);
305
+ var _useState18u2 = _slicedToArray(_useState18u, 2);
306
+ var testResult = _useState18u2[0];
307
+ var setTestResult = _useState18u2[1];
308
+
309
+ var _useState18v = useState(false);
310
+ var _useState18v2 = _slicedToArray(_useState18v, 2);
311
+ var testLoading = _useState18v2[0];
312
+ var setTestLoading = _useState18v2[1];
313
+
314
+ var _useState18w = useState(true);
315
+ var _useState18w2 = _slicedToArray(_useState18w, 2);
316
+ var testInlineVisible = _useState18w2[0];
317
+ var setTestInlineVisible = _useState18w2[1];
318
+
319
+ var _useState18x = useState(null);
320
+ var _useState18x2 = _slicedToArray(_useState18x, 2);
321
+ var testPanelFile = _useState18x2[0];
322
+ var setTestPanelFile = _useState18x2[1];
323
+
324
+ var _useState18y = useState(false);
325
+ var _useState18y2 = _slicedToArray(_useState18y, 2);
326
+ var testPanelOpen = _useState18y2[0];
327
+ var setTestPanelOpen = _useState18y2[1];
328
+
329
+ var _useState18p = useState(DEFAULT_EDITOR_PREFS);
330
+ var _useState18p2 = _slicedToArray(_useState18p, 2);
331
+ var editorPrefs = _useState18p2[0];
332
+ var setEditorPrefs = _useState18p2[1];
333
+
334
+ var _useState19 = useState({
335
+ openEditors: false,
336
+ projects: false
337
+ });
338
+
339
+ var _useState192 = _slicedToArray(_useState19, 2);
340
+
341
+ var collapsedSections = _useState192[0];
342
+ var setCollapsedSections = _useState192[1];
343
+
344
+ var _useState20 = useState({});
345
+
346
+ var _useState202 = _slicedToArray(_useState20, 2);
347
+
348
+ var expandedDirs = _useState202[0];
349
+ var setExpandedDirs = _useState202[1];
350
+
351
+ var _useState21 = useState(null);
352
+
353
+ var _useState212 = _slicedToArray(_useState21, 2);
354
+
355
+ var pendingCreate = _useState212[0];
356
+ var setPendingCreate = _useState212[1];
357
+
358
+ var _useState22 = useState(null);
359
+
360
+ var _useState222 = _slicedToArray(_useState22, 2);
361
+
362
+ var pendingRename = _useState222[0];
363
+ var setPendingRename = _useState222[1];
364
+
365
+ var _useState23 = useState(null);
366
+
367
+ var _useState232 = _slicedToArray(_useState23, 2);
368
+
369
+ var contextMenu = _useState232[0];
370
+ var setContextMenu = _useState232[1];
371
+
372
+ var resizeSessionRef = useRef(null);
373
+ var resizeRafRef = useRef(null);
374
+
375
+ var clamp = function clamp(value, min, max) {
376
+ return Math.min(max, Math.max(min, value));
377
+ };
378
+
379
+ var normalizeRelativePath = function normalizeRelativePath(input) {
380
+ return (input || "").replace(/\\/g, "/").trim().replace(/^\/+/, "").replace(/\/+$/, "").replace(/\/+/g, "/");
381
+ };
382
+
383
+ var parentDir = function parentDir(path) {
384
+ if (!path) return "";
385
+ var idx = path.lastIndexOf("/");
386
+ return idx > 0 ? path.slice(0, idx) : "";
387
+ };
388
+
389
+ var deriveProjectRootName = function deriveProjectRootName() {
390
+ if (projectRootName) return projectRootName;
391
+ var railsRoot = document && document.body && document.body.dataset ? document.body.dataset.railsRoot : "";
392
+ if (!railsRoot) return "PROJECT";
393
+ var parts = railsRoot.split("/").filter(Boolean);
394
+ return parts.length ? parts[parts.length - 1] : "PROJECT";
395
+ };
396
+
397
+ var refreshProjectTree = function refreshProjectTree() {
398
+ return FileService.getTree().then(function (data) {
399
+ setTreeData(data || []);
400
+ SearchService.buildIndex(data || []);
401
+ return data || [];
402
+ })["catch"](function (err) {
403
+ EditorStore.setStatus("Failed to refresh files: " + (err && err.message || "Unknown error"), "error");
404
+ return [];
405
+ });
406
+ };
407
+
408
+ var isRubyPath = function isRubyPath(path) {
409
+ return path && (path.endsWith('.rb') || path.endsWith('.gemspec') || path.endsWith('Rakefile') || path.endsWith('Gemfile'));
410
+ };
411
+
412
+ var applyMarkersForTab = function applyMarkersForTab(paneId, tabId, nextMarkers) {
413
+ var currentPane = EditorStore.getState().panes.find(function (p) {
414
+ return p.id === paneId;
415
+ });
416
+ var current = currentPane ? currentPane.tabs.find(function (t) {
417
+ return t.id === tabId;
418
+ }) : null;
419
+ if (current) current.markers = nextMarkers;
420
+ setMarkers(function (prev) {
421
+ return _extends({}, prev, _defineProperty({}, tabId, nextMarkers));
422
+ });
423
+ };
424
+
425
+ var runRubyLint = function runRubyLint(tab, paneId) {
426
+ var options = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2];
427
+
428
+ if (!tab || (!isRubyPath(tab.path) && !tab.path.endsWith('.haml'))) return Promise.resolve(null);
429
+
430
+ if (options.showLoading) {
431
+ setLoading(function (prev) {
432
+ return _extends({}, prev, { lint: true });
433
+ });
434
+ }
435
+ if (options.showStatus) {
436
+ EditorStore.setStatus('Linting...', 'info');
437
+ }
438
+
439
+ return FileService.lintFile(tab.path, tab.content).then(function (res) {
440
+ var nextMarkers = res.markers || [];
441
+ applyMarkersForTab(paneId, tab.id, nextMarkers);
442
+
443
+ if (options.showStatus) {
444
+ var count = res.summary && res.summary.offense_count || 0;
445
+ EditorStore.setStatus(count === 0 ? 'No RuboCop offenses!' : "Found " + count + " offenses", count === 0 ? 'success' : 'warning');
446
+ }
447
+
448
+ return res;
449
+ })["catch"](function (err) {
450
+ if (options.showStatus) {
451
+ EditorStore.setStatus('Lint failed: ' + err.message, 'error');
452
+ }
453
+ return null;
454
+ })["finally"](function () {
455
+ if (options.showLoading) {
456
+ setLoading(function (prev) {
457
+ return _extends({}, prev, { lint: false });
458
+ });
459
+ }
460
+ });
461
+ };
462
+
463
+ var _debouncedAutoLint = useRef(window._.debounce(function (tab, paneId) {
464
+ if (!tab) return;
465
+ if (isRubyPath(tab.path)) {
466
+ runRubyLint(tab, paneId);
467
+ return;
468
+ }
469
+ if (tab.path.endsWith('.haml')) {
470
+ runRubyLint(tab, paneId);
471
+ return;
472
+ }
473
+
474
+ var ext = tab.path.split('.').pop().toLowerCase();
475
+ var formatMap = {
476
+ 'js': 'babel', 'jsx': 'babel',
477
+ 'json': 'json',
478
+ 'css': 'css', 'scss': 'scss',
479
+ 'html': 'html', 'md': 'markdown'
480
+ };
481
+ var parserName = formatMap[ext];
482
+
483
+ if (parserName && window.prettier && window.prettierPlugins) {
484
+ var prefs = EditorStore.getState().editorPrefs || DEFAULT_EDITOR_PREFS;
485
+ window.prettier.format(tab.content, {
486
+ parser: parserName,
487
+ plugins: Object.values(window.prettierPlugins),
488
+ printWidth: prefs.prettierPrintWidth != null ? prefs.prettierPrintWidth : 80,
489
+ tabWidth: prefs.prettierTabWidth != null ? prefs.prettierTabWidth : 2,
490
+ useTabs: !!prefs.prettierUseTabs,
491
+ semi: prefs.prettierSemi !== false,
492
+ singleQuote: !!prefs.prettierSingleQuote,
493
+ trailingComma: prefs.prettierTrailingComma || 'all',
494
+ bracketSpacing: prefs.prettierBracketSpacing !== false
495
+ }).then(function () {
496
+ var currentPane = EditorStore.getState().panes.find(function (p) {
497
+ return p.id === paneId;
498
+ });
499
+ var current = currentPane ? currentPane.tabs.find(function (t) {
500
+ return t.id === tab.id;
501
+ }) : null;
502
+ if (current) current.markers = [];
503
+ setMarkers(function (prev) {
504
+ return _extends({}, prev, _defineProperty({}, tab.id, []));
505
+ });
506
+ })["catch"](function (err) {
507
+ var newMarkers = [];
508
+ if (err && err.loc) {
509
+ // Prettier 3 (Babel parser) raises errors with err.loc = { line, column }
510
+ // Older Prettier used err.loc = { start: { line, column }, end: {...} }
511
+ var loc = err.loc.start ? err.loc.start : err.loc;
512
+ var endLoc = err.loc.end || null;
513
+ newMarkers.push({
514
+ severity: "error",
515
+ message: err.message.split("\n")[0] || "Syntax error",
516
+ startLine: loc.line,
517
+ startCol: loc.column,
518
+ endLine: endLoc ? endLoc.line : loc.line,
519
+ endCol: endLoc ? endLoc.column : loc.column + 1
520
+ });
521
+ }
522
+ var currentPane = EditorStore.getState().panes.find(function (p) {
523
+ return p.id === paneId;
524
+ });
525
+ var current = currentPane ? currentPane.tabs.find(function (t) {
526
+ return t.id === tab.id;
527
+ }) : null;
528
+ if (current) current.markers = newMarkers;
529
+ setMarkers(function (prev) {
530
+ return _extends({}, prev, _defineProperty({}, tab.id, newMarkers));
531
+ });
532
+ });
533
+ }
534
+ }, 600)).current;
535
+
536
+ var setQuickOpen = function setQuickOpen(visible) {
537
+ EditorStore.setState({ isQuickOpenVisible: visible });
538
+ };
539
+
540
+ // Persist state when openTabs or activeTabId changes
541
+ useEffect(function () {
542
+ // Subscribe to EditorStore
543
+ var unsubscribe = EditorStore.subscribe(setState);
544
+
545
+ // Initial load
546
+ Promise.all([FileService.getWorkspace()["catch"](function () {
547
+ return null;
548
+ }), refreshProjectTree()]).then(function (_ref) {
549
+ var _ref2 = _slicedToArray(_ref, 1);
550
+
551
+ var workspace = _ref2[0];
552
+
553
+ if (workspace && workspace.rootName) {
554
+ setProjectRootName(workspace.rootName);
555
+ }
556
+ if (workspace && typeof workspace.rubocopAvailable === 'boolean') {
557
+ setRubocopAvailable(workspace.rubocopAvailable);
558
+ window.MBEDITOR_RUBOCOP_AVAILABLE = workspace.rubocopAvailable;
559
+ }
560
+ if (workspace && workspace.rubocopConfigPath) {
561
+ setRubocopConfigPath(workspace.rubocopConfigPath);
562
+ }
563
+ if (workspace && typeof workspace.hamlLintAvailable === 'boolean') {
564
+ setHamlLintAvailable(workspace.hamlLintAvailable);
565
+ }
566
+ if (workspace && typeof workspace.gitAvailable === 'boolean') {
567
+ setGitAvailable(workspace.gitAvailable);
568
+ }
569
+ if (workspace && typeof workspace.redmineEnabled === 'boolean') {
570
+ setRedmineEnabled(workspace.redmineEnabled);
571
+ }
572
+ if (workspace && typeof workspace.testAvailable === 'boolean') {
573
+ setTestAvailable(workspace.testAvailable);
574
+ }
575
+ });
576
+ GitService.fetchStatus();
577
+
578
+ // Load persisted state
579
+ FileService.getState().then(function (savedState) {
580
+ var panesToLoad = savedState && savedState.panes;
581
+ if (savedState && savedState.openTabs) {
582
+ panesToLoad = [{ id: 1, tabs: savedState.openTabs, activeTabId: savedState.activeTabId }, { id: 2, tabs: [], activeTabId: null }];
583
+ }
584
+ if (savedState && savedState.editorPrefs && typeof savedState.editorPrefs === 'object') {
585
+ setEditorPrefs(Object.assign({}, DEFAULT_EDITOR_PREFS, savedState.editorPrefs));
586
+ }
587
+ if (savedState && savedState.activeSidebarTab) {
588
+ setActiveSidebarTab(savedState.activeSidebarTab);
589
+ }
590
+ if (savedState && savedState.sidebarCollapsed) {
591
+ setSidebarCollapsed(true);
592
+ }
593
+ if (panesToLoad && panesToLoad.length > 0) {
594
+ var allTabs = panesToLoad.flatMap(function (p) {
595
+ return p.tabs;
596
+ });
597
+ Promise.all(allTabs.map(function (t) {
598
+ if (t.isSettings || t.path === '__settings__') {
599
+ return Promise.resolve({ content: '' });
600
+ }
601
+ if (t.isDiff && t.repoPath) {
602
+ return GitService.fetchDiff(t.repoPath, t.diffBaseSha, t.diffHeadSha)
603
+ .then(function (d) { return { content: 'Diff loaded', diffOriginal: d.original || '', diffModified: d.modified || '', _isDiffResult: true }; })
604
+ ["catch"](function () { return { content: '', diffOriginal: '', diffModified: '', _isDiffResult: true }; });
605
+ }
606
+ if (t.isCombinedDiff || (t.path || '').startsWith('combined-diff://') || (t.path || '').startsWith('diff://')) {
607
+ return Promise.resolve({ content: '' });
608
+ }
609
+ var sourcePath = t.isPreview || /::preview$/.test(t.path || '') ? t.previewFor || (t.path || '').replace(/::preview$/, '') : t.path;
610
+ return FileService.getFile(sourcePath, { allowMissing: true }).then(function (data) {
611
+ return {
612
+ content: typeof data.content === 'string' ? data.content : '',
613
+ fileNotFound: data && data.missing === true,
614
+ image: data && data.image === true
615
+ };
616
+ })["catch"](function () {
617
+ return { content: '', fileNotFound: false };
618
+ });
619
+ })).then(function (results) {
620
+ var resIdx = 0;
621
+ var restoredPanes = panesToLoad.map(function (p) {
622
+ return _extends({}, p, {
623
+ tabs: p.tabs.map(function (t) {
624
+ var res = results[resIdx++];
625
+ return _extends({}, t, {
626
+ content: res.content,
627
+ externalContentVersion: (t.externalContentVersion || 0) + 1
628
+ }, res._isDiffResult ? {
629
+ diffOriginal: res.diffOriginal,
630
+ diffModified: res.diffModified
631
+ } : {}, typeof res.fileNotFound === 'boolean' ? {
632
+ fileNotFound: res.fileNotFound,
633
+ dirty: res.fileNotFound ? false : t.dirty
634
+ } : {}, res.image === true ? { isImage: true } : {});
635
+ })
636
+ });
637
+ });
638
+ EditorStore.setState(_extends({}, savedState, { panes: restoredPanes, openTabs: undefined }));
639
+ // Restore collapsedSections UI state
640
+ if (savedState.collapsedSections) {
641
+ setCollapsedSections(savedState.collapsedSections);
642
+ }
643
+ if (savedState.expandedDirs) {
644
+ setExpandedDirs(savedState.expandedDirs);
645
+ }
646
+ if (typeof savedState.showGitPanel === 'boolean') {
647
+ setShowGitPanel(savedState.showGitPanel);
648
+ if (savedState.showGitPanel) {
649
+ GitService.fetchInfo();
650
+ }
651
+ }
652
+ if (typeof savedState.gitPanelWidth === 'number') {
653
+ setGitPanelWidth(savedState.gitPanelWidth);
654
+ }
655
+ });
656
+ }
657
+ });
658
+
659
+ // Hotkeys setup
660
+ var onKeyDown = function onKeyDown(e) {
661
+ if ((e.ctrlKey || e.metaKey) && e.key === 'p') {
662
+ e.preventDefault();
663
+ setQuickOpen(true);
664
+ }
665
+ if ((e.ctrlKey || e.metaKey) && e.key === 's') {
666
+ (function () {
667
+ e.preventDefault();
668
+ var st = EditorStore.getState();
669
+ var focusedPane = st.panes.find(function (p) {
670
+ return p.id === st.focusedPaneId;
671
+ }) || st.panes[0];
672
+ if (focusedPane && focusedPane.activeTabId) {
673
+ var tab = focusedPane.tabs.find(function (t) {
674
+ return t.id === focusedPane.activeTabId;
675
+ });
676
+ if (tab && tab.dirty) handleSave(focusedPane.id, tab);
677
+ }
678
+ })();
679
+ }
680
+ if ((e.ctrlKey || e.metaKey) && e.shiftKey && e.key === 'S') {
681
+ e.preventDefault();
682
+ handleSaveAll();
683
+ }
684
+ if (e.altKey && e.shiftKey && e.key === 'F') {
685
+ e.preventDefault();
686
+ handleFormat();
687
+ }
688
+ if ((e.ctrlKey || e.metaKey) && e.shiftKey && e.key === 'G') {
689
+ e.preventDefault();
690
+ toggleGitPanel();
691
+ }
692
+ if (e.key === 'Escape') {
693
+ setContextMenu(null);
694
+ setShowHelp(false);
695
+ }
696
+ };
697
+
698
+ var handleMouseMove = function handleMouseMove(e) {
699
+ var session = resizeSessionRef.current;
700
+ if (!session) return;
701
+
702
+ // Throttle via rAF — skip if a frame is already queued to avoid paint thrashing
703
+ if (resizeRafRef.current) return;
704
+ var clientX = e.clientX;
705
+ resizeRafRef.current = requestAnimationFrame(function () {
706
+ resizeRafRef.current = null;
707
+ var s = resizeSessionRef.current;
708
+ if (!s) return;
709
+
710
+ if (s.mode === 'pane') {
711
+ var container = document.getElementById('ide-main-split-container');
712
+ if (!container) return;
713
+
714
+ var rect = container.getBoundingClientRect();
715
+ var nextWidth = (clientX - rect.left) / rect.width * 100;
716
+ setPane1Width(clamp(nextWidth, PANE_MIN_WIDTH_PERCENT, PANE_MAX_WIDTH_PERCENT));
717
+ }
718
+
719
+ if (s.mode === 'sidebar') {
720
+ var body = document.getElementById('ide-body-container');
721
+ if (!body) return;
722
+
723
+ var rect = body.getBoundingClientRect();
724
+ var reservedRight = EDITOR_MIN_WIDTH + (showGitPanelRef.current ? gitPanelWidthRef.current : 0);
725
+ var maxSidebarWidth = Math.min(SIDEBAR_MAX_WIDTH, Math.max(SIDEBAR_MIN_WIDTH, rect.width - reservedRight));
726
+ var nextWidth = clientX - rect.left;
727
+ setSidebarWidth(clamp(nextWidth, SIDEBAR_MIN_WIDTH, maxSidebarWidth));
728
+ }
729
+
730
+ if (s.mode === 'gitpanel') {
731
+ var body = document.getElementById('ide-body-container');
732
+ if (!body) return;
733
+
734
+ var rect = body.getBoundingClientRect();
735
+ var nextWidth = rect.right - clientX;
736
+ setGitPanelWidth(clamp(nextWidth, GIT_PANEL_MIN_WIDTH, 600));
737
+ }
738
+ });
739
+ };
740
+
741
+ var handleMouseUp = function handleMouseUp() {
742
+ if (!resizeSessionRef.current) return;
743
+
744
+ if (resizeRafRef.current) {
745
+ cancelAnimationFrame(resizeRafRef.current);
746
+ resizeRafRef.current = null;
747
+ }
748
+ resizeSessionRef.current = null;
749
+ setActiveResizeMode(null);
750
+ document.body.style.cursor = '';
751
+ document.body.style.userSelect = '';
752
+ };
753
+
754
+ window.addEventListener('keydown', onKeyDown);
755
+ window.addEventListener('mousemove', handleMouseMove);
756
+ window.addEventListener('mouseup', handleMouseUp);
757
+ return function () {
758
+ unsubscribe();
759
+ if (resizeRafRef.current) {
760
+ cancelAnimationFrame(resizeRafRef.current);
761
+ resizeRafRef.current = null;
762
+ }
763
+ window.removeEventListener('keydown', onKeyDown);
764
+ window.removeEventListener('mousemove', handleMouseMove);
765
+ window.removeEventListener('mouseup', handleMouseUp);
766
+ document.body.style.cursor = '';
767
+ document.body.style.userSelect = '';
768
+ };
769
+ }, []);
770
+
771
+ // Heartbeat — adaptive poll: 30s when connected, 5s when trying to reconnect.
772
+ // Skipped entirely while the tab is hidden (Page Visibility API).
773
+ useEffect(function () {
774
+ var wasOnline = true;
775
+ var timeoutId = null;
776
+
777
+ function schedule() {
778
+ var delay = wasOnline ? 30000 : 5000;
779
+ timeoutId = setTimeout(tick, delay);
780
+ }
781
+
782
+ function tick() {
783
+ if (document.hidden) {
784
+ // Tab is backgrounded — skip this cycle and reschedule at the normal
785
+ // online interval so we resume quickly once the tab becomes visible again.
786
+ schedule();
787
+ return;
788
+ }
789
+ FileService.ping().then(function () {
790
+ if (!wasOnline) {
791
+ wasOnline = true;
792
+ setServerOnline(true);
793
+ }
794
+ schedule();
795
+ }).catch(function () {
796
+ if (wasOnline) {
797
+ wasOnline = false;
798
+ setServerOnline(false);
799
+ }
800
+ schedule();
801
+ });
802
+ }
803
+
804
+ schedule();
805
+ return function () { clearTimeout(timeoutId); };
806
+ }, []);
807
+
808
+ var handleSelectFile = function handleSelectFile(path, name, line) {
809
+ TabManager.openTab(path, name, line);
810
+ setSelectedTreeNode({ path: path, name: name || path.split('/').pop(), type: 'file' });
811
+ setQuickOpen(false);
812
+ };
813
+
814
+ // Single-click in explorer: soft (preview) open — replaces any existing soft tab
815
+ var handleSoftOpenFile = function handleSoftOpenFile(path, name) {
816
+ TabManager.openTab(path, name, null, null, true);
817
+ setSelectedTreeNode({ path: path, name: name || path.split('/').pop(), type: 'file' });
818
+ };
819
+
820
+ // Double-click in explorer or on tab: harden the tab (remove italic/preview)
821
+ var handleHardOpenFile = function handleHardOpenFile(path, name) {
822
+ var st = EditorStore.getState();
823
+ var targetPane = st.panes.find(function (p) {
824
+ return p.tabs.some(function (t) {
825
+ return t.path === path;
826
+ });
827
+ });
828
+ if (targetPane) {
829
+ TabManager.hardenTab(targetPane.id, path);
830
+ TabManager.switchTab(targetPane.id, path);
831
+ } else {
832
+ TabManager.openTab(path, name, null, null, false);
833
+ }
834
+ setSelectedTreeNode({ path: path, name: name || path.split('/').pop(), type: 'file' });
835
+ };
836
+
837
+ var requestCloseTab = function requestCloseTab(paneId, id) {
838
+ var pane = state.panes.find(function (p) {
839
+ return p.id === paneId;
840
+ }) || state.panes[0];
841
+ var tab = pane.tabs.find(function (t) {
842
+ return t.id === id;
843
+ });
844
+ if (tab && tab.dirty) {
845
+ setClosingPaneId(paneId);
846
+ setClosingTabId(id);
847
+ } else {
848
+ TabManager.closeTab(paneId, id);
849
+ }
850
+ };
851
+
852
+ var confirmCloseTab = function confirmCloseTab(save) {
853
+ var pane = state.panes.find(function (p) {
854
+ return p.id === closingPaneId;
855
+ });
856
+ var tab = pane ? pane.tabs.find(function (t) {
857
+ return t.id === closingTabId;
858
+ }) : null;
859
+ if (!tab) {
860
+ setClosingTabId(null);
861
+ setClosingPaneId(null);
862
+ return;
863
+ }
864
+
865
+ if (save) {
866
+ setLoading(function (prev) {
867
+ return _extends({}, prev, { save: true });
868
+ });
869
+ EditorStore.setStatus("Saving " + tab.name + "...", "info");
870
+ FileService.saveFile(tab.path, tab.content).then(function () {
871
+ EditorStore.setStatus("Saved", "success");
872
+ GitService.fetchStatus();
873
+ TabManager.closeTab(closingPaneId, tab.id);
874
+ })["catch"](function (err) {
875
+ EditorStore.setStatus("Save failed: " + err.message, "error");
876
+ })["finally"](function () {
877
+ setLoading(function (prev) {
878
+ return _extends({}, prev, { save: false });
879
+ });
880
+ setClosingTabId(null);
881
+ setClosingPaneId(null);
882
+ });
883
+ } else {
884
+ TabManager.closeTab(closingPaneId, tab.id);
885
+ setClosingTabId(null);
886
+ setClosingPaneId(null);
887
+ }
888
+ };
889
+
890
+ var confirmBulkClose = function confirmBulkClose(tabs, scopeLabel) {
891
+ var dirtyCount = tabs.filter(function (tab) {
892
+ return tab.dirty;
893
+ }).length;
894
+
895
+ if (dirtyCount === 0) return true;
896
+
897
+ var dirtyLabel = dirtyCount === 1 ? "1 unsaved editor has changes." : dirtyCount + " unsaved editors have changes.";
898
+ return window.confirm(dirtyLabel + " Close " + scopeLabel + " without saving?");
899
+ };
900
+
901
+ var handleCloseAllEditors = function handleCloseAllEditors() {
902
+ var allTabs = state.panes.flatMap(function (pane) {
903
+ return pane.tabs;
904
+ });
905
+ if (allTabs.length === 0) return;
906
+ if (!confirmBulkClose(allTabs, "all editors")) return;
907
+
908
+ TabManager.closeAllTabs();
909
+ setClosingTabId(null);
910
+ setClosingPaneId(null);
911
+ EditorStore.setStatus("Closed " + allTabs.length + " editor" + (allTabs.length === 1 ? "" : "s"), "info");
912
+ };
913
+
914
+ var handleCloseEditorsInGroup = function handleCloseEditorsInGroup(paneId) {
915
+ var pane = state.panes.find(function (p) {
916
+ return p.id === paneId;
917
+ });
918
+ if (!pane || pane.tabs.length === 0) return;
919
+ if (!confirmBulkClose(pane.tabs, "all editors in Group " + paneId)) return;
920
+
921
+ TabManager.closeAllTabsInPane(paneId);
922
+ setClosingTabId(null);
923
+ setClosingPaneId(null);
924
+ EditorStore.setStatus("Closed " + pane.tabs.length + " editor" + (pane.tabs.length === 1 ? "" : "s") + " in Group " + paneId, "info");
925
+ };
926
+
927
+ // Persist state when panes, focusedPaneId, or collapsedSections changes
928
+ useEffect(function () {
929
+ // debounce explicitly using setTimeout to avoid spamming the backend
930
+ var timeoutId = setTimeout(function () {
931
+ var st = EditorStore.getState();
932
+ var lightweightPanes = st.panes.map(function (p) {
933
+ return {
934
+ id: p.id,
935
+ activeTabId: p.activeTabId,
936
+ tabs: p.tabs.filter(function(t) { return !t.isCombinedDiff; }).map(function (t) {
937
+ return {
938
+ id: t.id,
939
+ path: t.path,
940
+ name: t.name,
941
+ dirty: t.dirty,
942
+ viewState: t.viewState,
943
+ isSettings: !!t.isSettings,
944
+ isPreview: !!t.isPreview,
945
+ previewFor: t.previewFor || null,
946
+ isDiff: !!t.isDiff,
947
+ diffBaseSha: t.diffBaseSha || null,
948
+ diffHeadSha: t.diffHeadSha || null,
949
+ repoPath: t.repoPath || null
950
+ };
951
+ })
952
+ };
953
+ });
954
+ FileService.saveState({ panes: lightweightPanes, focusedPaneId: st.focusedPaneId, collapsedSections: collapsedSections, expandedDirs: expandedDirs, showGitPanel: showGitPanel, gitPanelWidth: gitPanelWidth, editorPrefs: editorPrefs, activeSidebarTab: activeSidebarTab, sidebarCollapsed: sidebarCollapsed });
955
+ }, 1000);
956
+ return function () {
957
+ return clearTimeout(timeoutId);
958
+ };
959
+ }, [state.panes, state.focusedPaneId, collapsedSections, expandedDirs, showGitPanel, gitPanelWidth, editorPrefs, activeSidebarTab, sidebarCollapsed]);
960
+
961
+ useEffect(function() {
962
+ document.documentElement.setAttribute('data-theme', editorPrefs.theme || 'vs-dark');
963
+ }, [editorPrefs.theme]);
964
+
965
+ useEffect(function() {
966
+ EditorStore.setState({ editorPrefs: editorPrefs });
967
+ }, [editorPrefs]);
968
+
969
+ useEffect(function() {
970
+ var handler = function(e) {
971
+ e.preventDefault();
972
+ setPwaInstallPrompt(e);
973
+ };
974
+ window.addEventListener('beforeinstallprompt', handler);
975
+ return function() { window.removeEventListener('beforeinstallprompt', handler); };
976
+ }, []);
977
+
978
+ var focusedPane = state.panes.find(function (p) {
979
+ return p.id === state.focusedPaneId;
980
+ }) || state.panes[0] || null;
981
+ var activeTab = focusedPane && focusedPane.tabs.find(function (t) {
982
+ return t.id === focusedPane.activeTabId;
983
+ });
984
+
985
+ // Phase 7: Per-file last-commit info shown in the status bar
986
+ var _useState31 = useState(null);
987
+ var _useState32 = _slicedToArray(_useState31, 2);
988
+ var activeFileCommit = _useState32[0];
989
+ var setActiveFileCommit = _useState32[1];
990
+
991
+ useEffect(function () {
992
+ if (!gitAvailable || !activeTab || activeTab.isDiff || activeTab.isCombinedDiff || activeTab.isCommitGraph || !activeTab.path || activeTab.path.indexOf('diff://') === 0 || activeTab.path.indexOf('combined-diff://') === 0) {
993
+ setActiveFileCommit(null);
994
+ return;
995
+ }
996
+ var currentPath = activeTab.path;
997
+ GitService.fetchFileHistory(currentPath).then(function(data) {
998
+ var first = data && data.history && data.history[0];
999
+ if (first) {
1000
+ setActiveFileCommit({ hash: first.hash, title: first.title, author: first.author, date: first.date });
1001
+ } else {
1002
+ setActiveFileCommit(null);
1003
+ }
1004
+ }).catch(function() {
1005
+ setActiveFileCommit(null);
1006
+ });
1007
+ }, [activeTab ? activeTab.id : null, gitAvailable]);
1008
+
1009
+ useEffect(function () {
1010
+ if (!activeTab || typeof activeTab.content !== 'string') return;
1011
+ if (activeTab.isDiff || activeTab.isCombinedDiff || activeTab.isCommitGraph) return;
1012
+ if (isRubyPath(activeTab.path) && !rubocopAvailable) return;
1013
+ if (activeTab.path.endsWith('.haml') && !hamlLintAvailable) return;
1014
+
1015
+ // Clear markers and skip auto-lint when RuboCop linting is disabled
1016
+ if (isRubyPath(activeTab.path) && editorPrefs.rubocopLintEnabled === false) {
1017
+ setMarkers(function(prev) { return _extends({}, prev, _defineProperty({}, activeTab.id, [])); });
1018
+ return;
1019
+ }
1020
+
1021
+ _debouncedAutoLint(activeTab, focusedPane ? focusedPane.id : null);
1022
+
1023
+ return function () {
1024
+ _debouncedAutoLint.cancel();
1025
+ };
1026
+ }, [focusedPane ? focusedPane.id : null, activeTab ? activeTab.id : null, activeTab ? activeTab.content : null, rubocopAvailable, hamlLintAvailable, editorPrefs.rubocopLintEnabled]);
1027
+
1028
+ var handleOpenCommitGraph = function handleOpenCommitGraph() {
1029
+ var paneId = state.focusedPaneId || 1;
1030
+ var tabId = 'mbeditor://commit-graph';
1031
+
1032
+ var pane = state.panes.find(function(p) { return p.id === paneId; });
1033
+ var existing = pane && pane.tabs.find(function(t) { return t.id === tabId; });
1034
+ if (existing) {
1035
+ TabManager.switchTab(paneId, tabId);
1036
+ return;
1037
+ }
1038
+
1039
+ var newTab = {
1040
+ id: tabId,
1041
+ path: tabId,
1042
+ name: 'Commit Graph',
1043
+ dirty: false,
1044
+ content: '', // not used
1045
+ isCommitGraph: true
1046
+ };
1047
+
1048
+ var newPanes = state.panes.map(function(p) {
1049
+ if (p.id === paneId) {
1050
+ return Object.assign({}, p, { tabs: p.tabs.concat(newTab), activeTabId: tabId });
1051
+ }
1052
+ return p;
1053
+ });
1054
+
1055
+ EditorStore.setState({ panes: newPanes, focusedPaneId: paneId, activeTabId: tabId });
1056
+
1057
+ // Fetch data asynchronously
1058
+ GitService.fetchCommitGraph().then(function(data) {
1059
+ var s = EditorStore.getState();
1060
+ var p = s.panes.find(function(p) { return p.id === paneId; });
1061
+ if (!p) return;
1062
+ var t = p.tabs.find(function(t) { return t.id === tabId; });
1063
+ if (t) {
1064
+ var newPanes2 = s.panes.map(function(p2) {
1065
+ if (p2.id === paneId) {
1066
+ var newTabs = p2.tabs.map(function(t2) {
1067
+ return t2.id === tabId ? Object.assign({}, t2, { commits: data.commits }) : t2;
1068
+ });
1069
+ return Object.assign({}, p2, { tabs: newTabs });
1070
+ }
1071
+ return p2;
1072
+ });
1073
+ EditorStore.setState({ panes: newPanes2 });
1074
+ }
1075
+ });
1076
+ };
1077
+
1078
+ var handleSave = function handleSave(paneId, tab) {
1079
+ setLoading(function (prev) {
1080
+ return _extends({}, prev, { save: true });
1081
+ });
1082
+ EditorStore.setStatus("Saving " + tab.name + "...", "info");
1083
+ FileService.saveFile(tab.path, tab.content).then(function () {
1084
+ var newPanes = EditorStore.getState().panes.map(function (p) {
1085
+ if (p.id === paneId) {
1086
+ return _extends({}, p, { tabs: p.tabs.map(function (t) {
1087
+ return t.id === tab.id ? _extends({}, t, { dirty: false, cleanContent: tab.content }) : t;
1088
+ }) });
1089
+ }
1090
+ return p;
1091
+ });
1092
+ EditorStore.setState({ panes: newPanes });
1093
+ EditorStore.setStatus("Saved", "success");
1094
+
1095
+ // Hot reload for Markdown: sync preview tab after save
1096
+ if (/\.(md|markdown)$/i.test(tab.path)) {
1097
+ TabManager.syncMarkdownPreview(tab.path, tab.content);
1098
+ }
1099
+
1100
+ GitService.fetchStatus();
1101
+ })["catch"](function (err) {
1102
+ EditorStore.setStatus("Save failed: " + err.message, "error");
1103
+ })["finally"](function () {
1104
+ return setLoading(function (prev) {
1105
+ return _extends({}, prev, { save: false });
1106
+ });
1107
+ });
1108
+ };
1109
+
1110
+ var handleSaveAll = function handleSaveAll() {
1111
+ var dirtyTabs = state.panes.flatMap(function (p) {
1112
+ return p.tabs;
1113
+ }).filter(function (t) {
1114
+ return t.dirty;
1115
+ });
1116
+ if (dirtyTabs.length === 0) return;
1117
+
1118
+ setLoading(function (prev) {
1119
+ return _extends({}, prev, { saveAll: true });
1120
+ });
1121
+ EditorStore.setStatus("Saving " + dirtyTabs.length + " files...", "info");
1122
+ var promises = dirtyTabs.map(function (tab) {
1123
+ return FileService.saveFile(tab.path, tab.content);
1124
+ });
1125
+ Promise.all(promises).then(function () {
1126
+ var newPanes = EditorStore.getState().panes.map(function (p) {
1127
+ return _extends({}, p, { tabs: p.tabs.map(function (t) {
1128
+ return _extends({}, t, { dirty: false, cleanContent: t.content });
1129
+ })
1130
+ });
1131
+ });
1132
+ EditorStore.setState({ panes: newPanes });
1133
+ EditorStore.setStatus("All files saved", "success");
1134
+ GitService.fetchStatus();
1135
+ })["catch"](function (err) {
1136
+ EditorStore.setStatus("Failed to save some files", "error");
1137
+ })["finally"](function () {
1138
+ return setLoading(function (prev) {
1139
+ return _extends({}, prev, { saveAll: false });
1140
+ });
1141
+ });
1142
+ };
1143
+
1144
+ var handleTabDragStart = function handleTabDragStart(sourcePaneId, tabId) {
1145
+ var pane2 = EditorStore.getState().panes.find(function (p) {
1146
+ return p.id === 2;
1147
+ });
1148
+ if (!pane2 || pane2.tabs.length === 0) {
1149
+ dragSplitWidthRef.current = 50;
1150
+ setPane1Width(50);
1151
+ } else {
1152
+ dragSplitWidthRef.current = pane1Width;
1153
+ }
1154
+ setDraggedTab({ sourcePaneId: sourcePaneId, tabId: tabId });
1155
+ };
1156
+
1157
+ var clearDragState = function clearDragState() {
1158
+ setDraggedTab(null);
1159
+ setDragOverPaneId(null);
1160
+ };
1161
+
1162
+ var moveDraggedTabToPane = function moveDraggedTabToPane(targetPaneId) {
1163
+ if (!draggedTab) return;
1164
+ TabManager.moveTabToPane(draggedTab.sourcePaneId, targetPaneId, draggedTab.tabId);
1165
+ clearDragState();
1166
+ };
1167
+
1168
+ var handleFormat = function handleFormat() {
1169
+ if (!activeTab) return;
1170
+
1171
+ var isRubyLang = activeTab.path.endsWith('.rb') || activeTab.path.endsWith('.gemspec') || activeTab.path.endsWith("Rakefile") || activeTab.path.endsWith("Gemfile");
1172
+
1173
+ if (isRubyLang && !rubocopAvailable) {
1174
+ EditorStore.setStatus("RuboCop is not available for this workspace.", "warning");
1175
+ return;
1176
+ }
1177
+
1178
+ if (isRubyLang) {
1179
+ setLoading(function (prev) {
1180
+ return _extends({}, prev, { format: true });
1181
+ });
1182
+ EditorStore.setStatus("Formatting...", "info");
1183
+ FileService.formatFile(activeTab.path, activeTab.content).then(function (res) {
1184
+ if (res.content) {
1185
+ // Update content and mark dirty — user decides when to save.
1186
+ // The executeEdits path in EditorPanel preserves the undo stack.
1187
+ var newPanes = EditorStore.getState().panes.map(function (p) {
1188
+ if (p.id === focusedPane.id) return _extends({}, p, { tabs: p.tabs.map(function (t) {
1189
+ return t.id === activeTab.id ? _extends({}, t, { content: res.content, dirty: true, externalContentVersion: (t.externalContentVersion || 0) + 1 }) : t;
1190
+ }) });
1191
+ return p;
1192
+ });
1193
+ EditorStore.setState({ panes: newPanes });
1194
+ }
1195
+ EditorStore.setStatus("Formatted (Unsaved)", "success");
1196
+ GitService.fetchStatus();
1197
+ })["catch"](function (err) {
1198
+ return EditorStore.setStatus("Format failed: " + err.message, "error");
1199
+ })["finally"](function () {
1200
+ return setLoading(function (prev) {
1201
+ return _extends({}, prev, { format: false });
1202
+ });
1203
+ });
1204
+ return;
1205
+ }
1206
+
1207
+ // Attempt Prettier Formatting
1208
+ var ext = activeTab.path.split('.').pop().toLowerCase();
1209
+ var formatMap = {
1210
+ 'js': 'babel', 'jsx': 'babel',
1211
+ 'json': 'json',
1212
+ 'css': 'css', 'scss': 'scss',
1213
+ 'html': 'html', 'md': 'markdown'
1214
+ };
1215
+ var parserName = formatMap[ext];
1216
+
1217
+ if (parserName && window.prettier && window.prettierPlugins) {
1218
+ setLoading(function (prev) {
1219
+ return _extends({}, prev, { format: true });
1220
+ });
1221
+ EditorStore.setStatus("Formatting with Prettier...", "info");
1222
+ window.prettier.format(activeTab.content, {
1223
+ parser: parserName,
1224
+ plugins: Object.values(window.prettierPlugins),
1225
+ printWidth: editorPrefs.prettierPrintWidth != null ? editorPrefs.prettierPrintWidth : 80,
1226
+ tabWidth: editorPrefs.prettierTabWidth != null ? editorPrefs.prettierTabWidth : 2,
1227
+ useTabs: !!editorPrefs.prettierUseTabs,
1228
+ semi: editorPrefs.prettierSemi !== false,
1229
+ singleQuote: !!editorPrefs.prettierSingleQuote,
1230
+ trailingComma: editorPrefs.prettierTrailingComma || 'all',
1231
+ bracketSpacing: editorPrefs.prettierBracketSpacing !== false
1232
+ }).then(function (formatted) {
1233
+ var newPanes = EditorStore.getState().panes.map(function (p) {
1234
+ if (p.id === focusedPane.id) return _extends({}, p, { tabs: p.tabs.map(function (t) {
1235
+ return t.id === activeTab.id ? _extends({}, t, { content: formatted, dirty: true, externalContentVersion: (t.externalContentVersion || 0) + 1 }) : t;
1236
+ }) });
1237
+ return p;
1238
+ });
1239
+ EditorStore.setState({ panes: newPanes });
1240
+ EditorStore.setStatus("Formatted (Unsaved)", "success");
1241
+ GitService.fetchStatus();
1242
+ })["catch"](function (err) {
1243
+ EditorStore.setStatus("Prettier Formatter failed: " + err.message, "error");
1244
+ })["finally"](function () {
1245
+ setLoading(function (prev) {
1246
+ return _extends({}, prev, { format: false });
1247
+ });
1248
+ });
1249
+ return;
1250
+ }
1251
+
1252
+ // Fallback: Monaco re-indent using the editor's configured tabSize/insertSpaces
1253
+ var monacoEditor = window.__mbeditorActiveEditor;
1254
+ if (monacoEditor) {
1255
+ var reindentAction = monacoEditor.getAction('editor.action.reindentLines');
1256
+ if (reindentAction) {
1257
+ reindentAction.run().then(function () {
1258
+ EditorStore.setStatus("Formatted (Unsaved)", "success");
1259
+ });
1260
+ }
1261
+ }
1262
+ };
1263
+
1264
+ var handleRunTest = function handleRunTest() {
1265
+ if (!activeTab || !activeTab.path) return;
1266
+ if (testLoading) return;
1267
+
1268
+ setTestLoading(true);
1269
+ EditorStore.setStatus('Running tests...', 'info');
1270
+
1271
+ FileService.runTests(activeTab.path).then(function (res) {
1272
+ setTestResult(res);
1273
+ setTestPanelFile(res.testFile || activeTab.path);
1274
+ setTestPanelOpen(true);
1275
+ if (res.ok) {
1276
+ var s = res.summary || {};
1277
+ var failCount = (s.failed || 0) + (s.errored || 0);
1278
+ if (failCount === 0) {
1279
+ EditorStore.setStatus('All ' + (s.total || 0) + ' tests passed', 'success');
1280
+ } else {
1281
+ EditorStore.setStatus(failCount + ' test' + (failCount === 1 ? '' : 's') + ' failed out of ' + (s.total || 0), 'warning');
1282
+ }
1283
+ } else {
1284
+ EditorStore.setStatus('Test run failed: ' + (res.error || 'unknown error'), 'error');
1285
+ }
1286
+ })["catch"](function (err) {
1287
+ var msg = err.response && err.response.data && err.response.data.error || err.message;
1288
+ setTestResult({ ok: false, error: msg, tests: [], summary: null });
1289
+ setTestPanelFile(activeTab.path);
1290
+ setTestPanelOpen(true);
1291
+ EditorStore.setStatus('Test run failed: ' + msg, 'error');
1292
+ })["finally"](function () {
1293
+ setTestLoading(false);
1294
+ });
1295
+ };
1296
+
1297
+ var onFormatRef = useRef(handleFormat);
1298
+ onFormatRef.current = handleFormat;
1299
+
1300
+ var _debouncedSearch = useRef(window._.debounce(function (q) {
1301
+ if (!q.trim()) {
1302
+ searchRequestIdRef.current += 1;
1303
+ setSearchLoading(false);
1304
+ EditorStore.setState({ searchResults: [] });
1305
+ return;
1306
+ }
1307
+ var requestId = ++searchRequestIdRef.current;
1308
+ setSearchLoading(true);
1309
+ EditorStore.setStatus("Searching project...", "info");
1310
+ SearchService.projectSearch(q).then(function (res) {
1311
+ if (searchRequestIdRef.current === requestId) {
1312
+ var count = res && res.results ? res.results.length : (Array.isArray(res) ? res.length : 0);
1313
+ EditorStore.setStatus("Found " + count + " result" + (count !== 1 ? "s" : ""), "success");
1314
+ }
1315
+ }).finally(function () {
1316
+ if (searchRequestIdRef.current === requestId) {
1317
+ setSearchLoading(false);
1318
+ }
1319
+ });
1320
+ }, 400)).current;
1321
+
1322
+ var handleSearchChange = function handleSearchChange(e) {
1323
+ var val = e.target.value;
1324
+ setSearchQuery(val);
1325
+ _debouncedSearch(val);
1326
+ };
1327
+
1328
+ var clearSearch = function clearSearch() {
1329
+ searchRequestIdRef.current += 1;
1330
+ if (_debouncedSearch.cancel) _debouncedSearch.cancel();
1331
+ setSearchQuery("");
1332
+ setSearchLoading(false);
1333
+ EditorStore.setState({ searchResults: [] });
1334
+ };
1335
+
1336
+ var execSearch = function execSearch(e) {
1337
+ e.preventDefault();
1338
+ _debouncedSearch(searchQuery);
1339
+ };
1340
+
1341
+ var toggleGitPanel = function toggleGitPanel() {
1342
+ setShowGitPanel(function (prev) {
1343
+ if (!prev) GitService.fetchInfo();
1344
+ return !prev;
1345
+ });
1346
+ };
1347
+
1348
+ var startGitPanelResize = function startGitPanelResize(e) {
1349
+ e.preventDefault();
1350
+ resizeSessionRef.current = { mode: 'gitpanel' };
1351
+ setActiveResizeMode('gitpanel');
1352
+ document.body.style.cursor = 'col-resize';
1353
+ document.body.style.userSelect = 'none';
1354
+ };
1355
+
1356
+ var handleSelectCommit = function handleSelectCommit(commit) {
1357
+ setSelectedCommit(commit);
1358
+ setCommitDetailFiles(null);
1359
+ GitService.fetchCommitDetail(commit.hash).then(function (data) {
1360
+ setCommitDetailFiles(data.files || []);
1361
+ }).catch(function () {
1362
+ setCommitDetailFiles([]);
1363
+ });
1364
+ };
1365
+
1366
+ var handleToggleSection = function handleToggleSection(sectionKey, isCollapsed) {
1367
+ setCollapsedSections(function (prev) {
1368
+ return _extends({}, prev, _defineProperty({}, sectionKey, isCollapsed));
1369
+ });
1370
+ };
1371
+
1372
+ var handleCollapseAll = function handleCollapseAll() {
1373
+ return setExpandedDirs({});
1374
+ };
1375
+
1376
+ var openContextMenu = function openContextMenu(e, node) {
1377
+ setContextMenu({ x: e.clientX, y: e.clientY, node: node });
1378
+ setSelectedTreeNode(node);
1379
+ };
1380
+
1381
+ var closeContextMenu = function closeContextMenu() {
1382
+ return setContextMenu(null);
1383
+ };
1384
+
1385
+ var handleContextMenuAction = function handleContextMenuAction(action) {
1386
+ var node = contextMenu && contextMenu.node;
1387
+ closeContextMenu();
1388
+ if (action === 'open' && node) {
1389
+ handleHardOpenFile(node.path, node.name);return;
1390
+ }
1391
+ if (action === 'newFile') {
1392
+ handleCreateFile(node);return;
1393
+ }
1394
+ if (action === 'newFolder') {
1395
+ handleCreateDir(node);return;
1396
+ }
1397
+ if (action === 'rename') {
1398
+ handleRenamePath(node);return;
1399
+ }
1400
+ if (action === 'delete') {
1401
+ handleDeletePath(node);return;
1402
+ }
1403
+ if (action === 'copyPath' && node) {
1404
+ if (navigator.clipboard) {
1405
+ navigator.clipboard.writeText(node.path)["catch"](function () {});
1406
+ }
1407
+ EditorStore.setStatus('Copied: ' + node.path, 'info');
1408
+ }
1409
+ };
1410
+
1411
+ var startPaneResize = function startPaneResize(e) {
1412
+ e.preventDefault();
1413
+ resizeSessionRef.current = { mode: 'pane' };
1414
+ setActiveResizeMode('pane');
1415
+ document.body.style.cursor = 'col-resize';
1416
+ document.body.style.userSelect = 'none';
1417
+ };
1418
+
1419
+ var startSidebarResize = function startSidebarResize(e) {
1420
+ if (sidebarCollapsed) return;
1421
+ e.preventDefault();
1422
+ resizeSessionRef.current = { mode: 'sidebar' };
1423
+ setActiveResizeMode('sidebar');
1424
+ document.body.style.cursor = 'col-resize';
1425
+ document.body.style.userSelect = 'none';
1426
+ };
1427
+
1428
+ var toggleSidebarCollapsed = function toggleSidebarCollapsed() {
1429
+ setSidebarCollapsed(function (prev) { return !prev; });
1430
+ };
1431
+
1432
+ var expandSidebarTo = function expandSidebarTo(tab) {
1433
+ setActiveSidebarTab(tab);
1434
+ setSidebarCollapsed(false);
1435
+ };
1436
+
1437
+ var openFileFromGitPanel = function openFileFromGitPanel(path, name) {
1438
+ if (!path) return;
1439
+ handleSelectFile(path, name || path.split('/').pop());
1440
+ };
1441
+
1442
+ var pathMatchesNodeOrDescendant = function pathMatchesNodeOrDescendant(value, targetPath) {
1443
+ if (!value || !targetPath) return false;
1444
+ return value === targetPath || value.indexOf(targetPath + '/') === 0 || value.indexOf(targetPath + '::preview') === 0;
1445
+ };
1446
+
1447
+ var rewritePathAfterRename = function rewritePathAfterRename(value, oldPath, newPath) {
1448
+ if (!value || !oldPath || !newPath) return value;
1449
+ if (value === oldPath) return newPath;
1450
+ if (value === oldPath + '::preview') return newPath + '::preview';
1451
+ if (value.indexOf(oldPath + '/') === 0) return newPath + value.slice(oldPath.length);
1452
+ return value;
1453
+ };
1454
+
1455
+ var applyRenameToOpenTabs = function applyRenameToOpenTabs(oldPath, newPath) {
1456
+ var currentState = EditorStore.getState();
1457
+ var newPanes = currentState.panes.map(function (pane) {
1458
+ var renamedTabs = pane.tabs.map(function (tab) {
1459
+ var nextPath = rewritePathAfterRename(tab.path, oldPath, newPath);
1460
+ var nextPreviewFor = rewritePathAfterRename(tab.previewFor, oldPath, newPath);
1461
+ if (nextPath === tab.path && nextPreviewFor === tab.previewFor) return tab;
1462
+
1463
+ var defaultName = nextPath.split('/').pop();
1464
+ var previewSourceName = nextPreviewFor ? nextPreviewFor.split('/').pop() : defaultName;
1465
+ return _extends({}, tab, {
1466
+ id: nextPath,
1467
+ path: nextPath,
1468
+ name: tab.isPreview ? previewSourceName + '-preview' : defaultName,
1469
+ previewFor: nextPreviewFor
1470
+ });
1471
+ });
1472
+
1473
+ return _extends({}, pane, {
1474
+ tabs: renamedTabs,
1475
+ activeTabId: rewritePathAfterRename(pane.activeTabId, oldPath, newPath)
1476
+ });
1477
+ });
1478
+
1479
+ EditorStore.setState({
1480
+ panes: newPanes,
1481
+ activeTabId: rewritePathAfterRename(currentState.activeTabId, oldPath, newPath)
1482
+ });
1483
+ };
1484
+
1485
+ var removeDeletedPathFromOpenTabs = function removeDeletedPathFromOpenTabs(targetPath) {
1486
+ var currentState = EditorStore.getState();
1487
+ var removedTabIds = [];
1488
+
1489
+ var newPanes = currentState.panes.map(function (pane) {
1490
+ var keptTabs = pane.tabs.filter(function (tab) {
1491
+ var removeTab = pathMatchesNodeOrDescendant(tab.path, targetPath) || pathMatchesNodeOrDescendant(tab.previewFor, targetPath);
1492
+ if (removeTab) {
1493
+ removedTabIds.push(tab.id);
1494
+ }
1495
+ return !removeTab;
1496
+ });
1497
+
1498
+ var nextActiveTabId = pane.activeTabId;
1499
+ var activeStillExists = keptTabs.some(function (tab) {
1500
+ return tab.id === nextActiveTabId;
1501
+ });
1502
+ if (!activeStillExists) {
1503
+ nextActiveTabId = keptTabs.length ? keptTabs[keptTabs.length - 1].id : null;
1504
+ }
1505
+
1506
+ return _extends({}, pane, {
1507
+ tabs: keptTabs,
1508
+ activeTabId: nextActiveTabId
1509
+ });
1510
+ });
1511
+
1512
+ var nextFocusedPaneId = currentState.focusedPaneId;
1513
+ var focusedPane = newPanes.find(function (pane) {
1514
+ return pane.id === nextFocusedPaneId;
1515
+ });
1516
+ if (!focusedPane || focusedPane.tabs.length === 0) {
1517
+ var paneWithTabs = newPanes.find(function (pane) {
1518
+ return pane.tabs.length > 0;
1519
+ });
1520
+ nextFocusedPaneId = paneWithTabs ? paneWithTabs.id : 1;
1521
+ }
1522
+
1523
+ var activePane = newPanes.find(function (pane) {
1524
+ return pane.id === nextFocusedPaneId;
1525
+ });
1526
+ EditorStore.setState({
1527
+ panes: newPanes,
1528
+ focusedPaneId: nextFocusedPaneId,
1529
+ activeTabId: activePane ? activePane.activeTabId : null
1530
+ });
1531
+
1532
+ if (removedTabIds.length) {
1533
+ setMarkers(function (prev) {
1534
+ var next = _extends({}, prev);
1535
+ removedTabIds.forEach(function (tabId) {
1536
+ return delete next[tabId];
1537
+ });
1538
+ return next;
1539
+ });
1540
+ }
1541
+ };
1542
+
1543
+ var handleCreateFile = function handleCreateFile(targetNode) {
1544
+ var node = targetNode !== undefined ? targetNode : selectedTreeNode;
1545
+ var baseDir = node ? node.type === 'folder' ? node.path : parentDir(node.path) : '';
1546
+ // Ensure the target folder is expanded so the inline row is visible
1547
+ if (baseDir) setExpandedDirs(function (prev) {
1548
+ return Object.assign({}, prev, _defineProperty({}, baseDir, true));
1549
+ });
1550
+ setPendingRename(null);
1551
+ setPendingCreate({ type: 'file', parentPath: baseDir });
1552
+ };
1553
+
1554
+ var handleCreateDir = function handleCreateDir(targetNode) {
1555
+ var node = targetNode !== undefined ? targetNode : selectedTreeNode;
1556
+ var baseDir = node ? node.type === 'folder' ? node.path : parentDir(node.path) : '';
1557
+ if (baseDir) setExpandedDirs(function (prev) {
1558
+ return Object.assign({}, prev, _defineProperty({}, baseDir, true));
1559
+ });
1560
+ setPendingRename(null);
1561
+ setPendingCreate({ type: 'folder', parentPath: baseDir });
1562
+ };
1563
+
1564
+ var handleCreateConfirm = function handleCreateConfirm(name) {
1565
+ if (!pendingCreate || !name) return;
1566
+ var type = pendingCreate.type;
1567
+ var parentPath = pendingCreate.parentPath;
1568
+
1569
+ var path = normalizeRelativePath(parentPath ? parentPath + '/' + name : name);
1570
+ setPendingCreate(null);
1571
+
1572
+ if (type === 'file') {
1573
+ setLoading(function (prev) {
1574
+ return _extends({}, prev, { createFile: true });
1575
+ });
1576
+ FileService.createFile(path, '').then(function (res) {
1577
+ var createdPath = res && res.path || path;
1578
+ var createdName = createdPath.split('/').pop();
1579
+ setSelectedTreeNode({ path: createdPath, name: createdName, type: 'file' });
1580
+ EditorStore.setStatus('Created file: ' + createdName, 'success');
1581
+ return refreshProjectTree().then(function () {
1582
+ handleSelectFile(createdPath, createdName);
1583
+ GitService.fetchStatus();
1584
+ });
1585
+ })["catch"](function (err) {
1586
+ var message = err && err.response && err.response.data && err.response.data.error || err.message;
1587
+ EditorStore.setStatus('Create file failed: ' + message, 'error');
1588
+ })["finally"](function () {
1589
+ return setLoading(function (prev) {
1590
+ return _extends({}, prev, { createFile: false });
1591
+ });
1592
+ });
1593
+ } else {
1594
+ setLoading(function (prev) {
1595
+ return _extends({}, prev, { createDir: true });
1596
+ });
1597
+ FileService.createDir(path).then(function (res) {
1598
+ var createdPath = res && res.path || path;
1599
+ setSelectedTreeNode({ path: createdPath, name: createdPath.split('/').pop(), type: 'folder' });
1600
+ EditorStore.setStatus('Created folder: ' + createdPath, 'success');
1601
+ return refreshProjectTree().then(function () {
1602
+ return GitService.fetchStatus();
1603
+ });
1604
+ })["catch"](function (err) {
1605
+ var message = err && err.response && err.response.data && err.response.data.error || err.message;
1606
+ EditorStore.setStatus('Create folder failed: ' + message, 'error');
1607
+ })["finally"](function () {
1608
+ return setLoading(function (prev) {
1609
+ return _extends({}, prev, { createDir: false });
1610
+ });
1611
+ });
1612
+ }
1613
+ };
1614
+
1615
+ var handleCreateCancel = function handleCreateCancel() {
1616
+ return setPendingCreate(null);
1617
+ };
1618
+
1619
+ var handleRenamePath = function handleRenamePath(targetNode) {
1620
+ var node = targetNode !== undefined ? targetNode : selectedTreeNode;
1621
+ if (!node || !node.path) {
1622
+ EditorStore.setStatus('Select a file or folder to rename first.', 'warning');
1623
+ return;
1624
+ }
1625
+
1626
+ var itemPath = node.path;
1627
+
1628
+ // Expand all ancestor folders so the rename inline input is always visible
1629
+ var parts = itemPath.split('/');
1630
+ if (parts.length > 1) {
1631
+ var ancestors = {};
1632
+ for (var i = 1; i < parts.length; i++) {
1633
+ ancestors[parts.slice(0, i).join('/')] = true;
1634
+ }
1635
+ setExpandedDirs(function (prev) {
1636
+ return Object.assign({}, prev, ancestors);
1637
+ });
1638
+ }
1639
+
1640
+ setPendingCreate(null);
1641
+ setPendingRename({
1642
+ path: itemPath,
1643
+ parentPath: parentDir(itemPath),
1644
+ type: node.type,
1645
+ currentName: node.name || itemPath.split('/').pop()
1646
+ });
1647
+ };
1648
+
1649
+ var handleRenameConfirm = function handleRenameConfirm(name, renameTarget) {
1650
+ var target = renameTarget || pendingRename;
1651
+ if (!target || !name) return;
1652
+
1653
+ var oldPath = target.path;
1654
+ var currentName = target.currentName || oldPath.split('/').pop();
1655
+ var nextName = name.trim();
1656
+ setPendingRename(null);
1657
+
1658
+ if (!nextName || nextName === currentName) return;
1659
+
1660
+ var nextPath = normalizeRelativePath(parentDir(oldPath) ? parentDir(oldPath) + '/' + nextName : nextName);
1661
+ if (!nextPath || nextPath === oldPath) return;
1662
+
1663
+ setLoading(function (prev) {
1664
+ return _extends({}, prev, { renamePath: true });
1665
+ });
1666
+ FileService.renamePath(oldPath, nextPath).then(function (res) {
1667
+ var renamedPath = res && res.path || nextPath;
1668
+ applyRenameToOpenTabs(oldPath, renamedPath);
1669
+ setSelectedTreeNode(function (prev) {
1670
+ return prev ? _extends({}, prev, { path: renamedPath, name: renamedPath.split('/').pop() }) : prev;
1671
+ });
1672
+ EditorStore.setStatus('Renamed to: ' + renamedPath, 'success');
1673
+ return refreshProjectTree().then(function () {
1674
+ GitService.fetchStatus();
1675
+ });
1676
+ })["catch"](function (err) {
1677
+ var message = err && err.response && err.response.data && err.response.data.error || err.message;
1678
+ EditorStore.setStatus('Rename failed: ' + message, 'error');
1679
+ })["finally"](function () {
1680
+ setLoading(function (prev) {
1681
+ return _extends({}, prev, { renamePath: false });
1682
+ });
1683
+ });
1684
+ };
1685
+
1686
+ var handleRenameCancel = function handleRenameCancel() {
1687
+ return setPendingRename(null);
1688
+ };
1689
+
1690
+ var handleDeletePath = function handleDeletePath(targetNode) {
1691
+ var node = targetNode !== undefined ? targetNode : selectedTreeNode;
1692
+ if (!node || !node.path) {
1693
+ EditorStore.setStatus('Select a file or folder to delete first.', 'warning');
1694
+ return;
1695
+ }
1696
+
1697
+ var targetPath = node.path;
1698
+ var confirmed = window.confirm('Delete ' + targetPath + '? This cannot be undone.');
1699
+ if (!confirmed) return;
1700
+
1701
+ setLoading(function (prev) {
1702
+ return _extends({}, prev, { deletePath: true });
1703
+ });
1704
+ FileService.deletePath(targetPath).then(function () {
1705
+ removeDeletedPathFromOpenTabs(targetPath);
1706
+ setSelectedTreeNode(null);
1707
+ EditorStore.setStatus('Deleted: ' + targetPath, 'success');
1708
+ return refreshProjectTree().then(function () {
1709
+ GitService.fetchStatus();
1710
+ });
1711
+ })["catch"](function (err) {
1712
+ var message = err && err.response && err.response.data && err.response.data.error || err.message;
1713
+ EditorStore.setStatus('Delete failed: ' + message, 'error');
1714
+ })["finally"](function () {
1715
+ setLoading(function (prev) {
1716
+ return _extends({}, prev, { deletePath: false });
1717
+ });
1718
+ });
1719
+ };
1720
+
1721
+ var projectSectionTitle = deriveProjectRootName().toUpperCase();
1722
+ var selectedTreePath = selectedTreeNode ? selectedTreeNode.path : null;
1723
+ var isRuby = activeTab && isRubyPath(activeTab.path);
1724
+ var isHaml = activeTab && activeTab.path.endsWith('.haml');
1725
+ var isPrettierable = activeTab && SUPPORTED_PRETTIER_EXTS.includes(activeTab.path.split('.').pop().toLowerCase());
1726
+ var rubocopLintOn = editorPrefs.rubocopLintEnabled !== false;
1727
+ var canLintAndFormat = !!activeTab;
1728
+ var hasGitBranch = !!(state.gitBranch && state.gitBranch.trim());
1729
+
1730
+ var renderTabBar = function renderTabBar(paneId, tabs, activeId) {
1731
+ return React.createElement(TabBar, {
1732
+ tabs: tabs,
1733
+ activeId: activeId,
1734
+ paneId: paneId,
1735
+ onSelect: function (id) {
1736
+ return TabManager.switchTab(paneId, id);
1737
+ },
1738
+ onClose: function (id) {
1739
+ return requestCloseTab(paneId, id);
1740
+ },
1741
+ onTabDragStart: function (id) {
1742
+ return handleTabDragStart(paneId, id);
1743
+ },
1744
+ onTabDragEnd: clearDragState,
1745
+ onHardenTab: function (tabId) {
1746
+ return TabManager.hardenTab(paneId, tabId);
1747
+ },
1748
+ onShowHistory: function (path) {
1749
+ setHistoryPanelPath(path);
1750
+ },
1751
+ onRevealInExplorer: function (path) {
1752
+ setActiveSidebarTab('explorer');
1753
+ setSelectedTreeNode({ path: path, name: path.split('/').pop(), type: 'file' });
1754
+ setExpandedDirs(function (prev) {
1755
+ var parts = path.split('/');
1756
+ var updates = {};
1757
+ for (var i = 0; i < parts.length - 1; i++) {
1758
+ updates[parts.slice(0, i + 1).join('/')] = true;
1759
+ }
1760
+ return Object.assign({}, prev, updates);
1761
+ });
1762
+ }
1763
+ });
1764
+ };
1765
+
1766
+ function openSettingsTab() {
1767
+ var st = EditorStore.getState();
1768
+ var foundPaneId = null;
1769
+ var foundTab = null;
1770
+ st.panes.forEach(function(p) {
1771
+ if (!foundTab) {
1772
+ var t = p.tabs.find(function(tab) { return tab.path === '__settings__'; });
1773
+ if (t) { foundTab = t; foundPaneId = p.id; }
1774
+ }
1775
+ });
1776
+ if (foundTab) {
1777
+ var newPanes = st.panes.map(function(p) {
1778
+ if (p.id === foundPaneId) return Object.assign({}, p, { activeTabId: '__settings__' });
1779
+ return p;
1780
+ });
1781
+ EditorStore.setState({ panes: newPanes, focusedPaneId: foundPaneId, activeTabId: '__settings__' });
1782
+ return;
1783
+ }
1784
+ var paneId = st.focusedPaneId;
1785
+ var pane = st.panes.find(function(p) { return p.id === paneId; }) || st.panes[0];
1786
+ if (!pane) return;
1787
+ paneId = pane.id;
1788
+ var newTab = { id: '__settings__', path: '__settings__', name: 'Settings', dirty: false, content: '', isSettings: true };
1789
+ var newPanes2 = st.panes.map(function(p) {
1790
+ if (p.id === paneId) return Object.assign({}, p, { tabs: p.tabs.concat(newTab), activeTabId: '__settings__' });
1791
+ return p;
1792
+ });
1793
+ EditorStore.setState({ panes: newPanes2, focusedPaneId: paneId, activeTabId: '__settings__' });
1794
+ }
1795
+
1796
+ return React.createElement(
1797
+ "div",
1798
+ { className: "ide-shell" },
1799
+ React.createElement(
1800
+ "div",
1801
+ { className: "ide-titlebar" },
1802
+ React.createElement("i", { className: "fas fa-layer-group ide-titlebar-icon" }),
1803
+ React.createElement(
1804
+ "div",
1805
+ { className: "ide-titlebar-title" },
1806
+ "Mini Browser Editor — ",
1807
+ window.location.host
1808
+ ),
1809
+ React.createElement(
1810
+ "div",
1811
+ { style: { marginLeft: "auto", display: "flex", gap: "4px", height: "100%", alignItems: "center" } },
1812
+ React.createElement(
1813
+ "button",
1814
+ { className: "statusbar-btn", onClick: function () {
1815
+ return activeTab && handleSave(focusedPane.id, activeTab);
1816
+ }, disabled: loading.save || !activeTab || !activeTab.dirty },
1817
+ React.createElement("i", { className: loading.save ? "fas fa-spinner fa-spin" : "fas fa-save" }),
1818
+ !editorPrefs.toolbarIconOnly && " Save",
1819
+ activeTab && activeTab.dirty ? " ●" : ""
1820
+ ),
1821
+ React.createElement(
1822
+ "button",
1823
+ { className: "statusbar-btn", onClick: handleSaveAll, disabled: loading.saveAll || !state.panes.flatMap(function (p) {
1824
+ return p.tabs;
1825
+ }).some(function (t) {
1826
+ return t.dirty;
1827
+ }) },
1828
+ React.createElement(
1829
+ "i",
1830
+ { className: loading.saveAll ? "fas fa-spinner fa-spin" : "fas fa-save", style: loading.saveAll ? {} : { position: 'relative' } },
1831
+ !loading.saveAll && React.createElement("i", { className: "fas fa-save", style: { position: 'absolute', top: '-2px', left: '3px', fontSize: '9px', opacity: 0.8 } })
1832
+ ),
1833
+ !editorPrefs.toolbarIconOnly && " Save All"
1834
+ ),
1835
+ React.createElement("div", { className: "statusbar-sep" }),
1836
+ React.createElement(
1837
+ "button",
1838
+ { className: "statusbar-btn", onClick: handleFormat, disabled: loading.format || !canLintAndFormat },
1839
+ React.createElement("i", { className: loading.format ? "fas fa-spinner fa-spin" : "fas fa-magic" }),
1840
+ !editorPrefs.toolbarIconOnly && " Format"
1841
+ ),
1842
+ hasGitBranch && React.createElement(
1843
+ React.Fragment,
1844
+ null,
1845
+ React.createElement("div", { className: "statusbar-sep" }),
1846
+ React.createElement(
1847
+ "button",
1848
+ { type: "button", className: "statusbar-btn", onClick: toggleGitPanel },
1849
+ React.createElement("i", { className: "fas fa-code-branch" }),
1850
+ !editorPrefs.toolbarIconOnly && " Git"
1851
+ )
1852
+ ),
1853
+ React.createElement("div", { className: "statusbar-sep" }),
1854
+ React.createElement(
1855
+ "button",
1856
+ { type: "button", className: "statusbar-btn", onClick: function () { return setShowHelp(true); }, title: "Keyboard shortcuts & help" },
1857
+ React.createElement("i", { className: "fas fa-keyboard" }),
1858
+ !editorPrefs.toolbarIconOnly && " Help"
1859
+ ),
1860
+ pwaInstallPrompt && React.createElement(
1861
+ React.Fragment,
1862
+ null,
1863
+ React.createElement("div", { className: "statusbar-sep" }),
1864
+ React.createElement(
1865
+ "button",
1866
+ {
1867
+ type: "button",
1868
+ className: "statusbar-btn",
1869
+ title: "Install as app",
1870
+ onClick: function() {
1871
+ pwaInstallPrompt.prompt();
1872
+ pwaInstallPrompt.userChoice.then(function() { setPwaInstallPrompt(null); });
1873
+ }
1874
+ },
1875
+ React.createElement("i", { className: "fas fa-download" }),
1876
+ !editorPrefs.toolbarIconOnly && " Install"
1877
+ )
1878
+ )
1879
+ )
1880
+ ),
1881
+ showHelp && React.createElement(ShortcutHelp, { onClose: function () { return setShowHelp(false); } }),
1882
+ React.createElement(
1883
+ "div",
1884
+ { className: "ide-body", id: "ide-body-container" },
1885
+ React.createElement(
1886
+ "div",
1887
+ { className: "ide-sidebar" + (sidebarCollapsed ? " ide-sidebar-collapsed" : ""), style: { width: (sidebarCollapsed ? SIDEBAR_COLLAPSED_WIDTH : sidebarWidth) + "px" } },
1888
+ sidebarCollapsed
1889
+ ? React.createElement(
1890
+ "div",
1891
+ { className: "sidebar-icon-strip" },
1892
+ React.createElement(
1893
+ "div",
1894
+ { className: "sidebar-strip-top" },
1895
+ React.createElement(
1896
+ "button",
1897
+ { type: "button", className: "sidebar-strip-btn " + (activeSidebarTab === 'explorer' ? 'active' : ''), title: "Explorer", onClick: function () { return expandSidebarTo('explorer'); } },
1898
+ React.createElement("i", { className: "far fa-folder" })
1899
+ ),
1900
+ React.createElement(
1901
+ "button",
1902
+ { type: "button", className: "sidebar-strip-btn " + (activeSidebarTab === 'search' ? 'active' : ''), title: "Search", onClick: function () { return expandSidebarTo('search'); } },
1903
+ React.createElement("i", { className: "fas fa-search" })
1904
+ )
1905
+ ),
1906
+ React.createElement(
1907
+ "div",
1908
+ { className: "sidebar-strip-bottom" },
1909
+ React.createElement(
1910
+ "button",
1911
+ { type: "button", className: "sidebar-strip-btn", title: "Editor Preferences", onClick: openSettingsTab },
1912
+ React.createElement("i", { className: "fas fa-cog" })
1913
+ ),
1914
+ React.createElement(
1915
+ "button",
1916
+ { type: "button", className: "sidebar-strip-btn", title: "Expand sidebar", onClick: toggleSidebarCollapsed },
1917
+ React.createElement("i", { className: "fas fa-chevron-right" })
1918
+ )
1919
+ )
1920
+ )
1921
+ : React.createElement(
1922
+ React.Fragment,
1923
+ null,
1924
+ React.createElement(
1925
+ "div",
1926
+ { className: "ide-sidebar-tabs" },
1927
+ React.createElement(
1928
+ "button",
1929
+ { type: "button", className: "ide-sidebar-tab " + (activeSidebarTab === 'explorer' ? 'active' : ''), onClick: function () { return setActiveSidebarTab('explorer'); } },
1930
+ "EXPLORER"
1931
+ ),
1932
+ React.createElement(
1933
+ "button",
1934
+ { type: "button", className: "ide-sidebar-tab " + (activeSidebarTab === 'search' ? 'active' : ''), onClick: function () { return setActiveSidebarTab('search'); } },
1935
+ "SEARCH"
1936
+ ),
1937
+ React.createElement(
1938
+ "button",
1939
+ { type: "button", className: "ide-sidebar-tab ide-sidebar-tab-icon", title: "Editor Preferences", onClick: openSettingsTab },
1940
+ React.createElement("i", { className: "fas fa-cog" })
1941
+ ),
1942
+ React.createElement(
1943
+ "button",
1944
+ { type: "button", className: "sidebar-strip-btn", title: "Collapse sidebar", onClick: toggleSidebarCollapsed },
1945
+ React.createElement("i", { className: "fas fa-chevron-left" })
1946
+ )
1947
+ ),
1948
+ activeSidebarTab === 'explorer' && React.createElement(
1949
+ "div",
1950
+ { className: "ide-sidebar-content" },
1951
+ React.createElement(
1952
+ "div",
1953
+ { className: "ide-sidebar-fixed" },
1954
+ state.panes.flatMap(function (p) {
1955
+ return p.tabs;
1956
+ }).length > 0 && React.createElement(
1957
+ CollapsibleSection,
1958
+ {
1959
+ title: "OPEN EDITORS",
1960
+ isCollapsed: collapsedSections.openEditors,
1961
+ onToggle: function (isCollapsed) {
1962
+ return handleToggleSection('openEditors', isCollapsed);
1963
+ },
1964
+ actions: React.createElement(
1965
+ SectionActionGroup,
1966
+ { ariaLabel: "Open editor actions" },
1967
+ React.createElement(SidebarActionButton, {
1968
+ title: "Close all editors",
1969
+ ariaLabel: "Close all open editors",
1970
+ iconClass: "far fa-window-close",
1971
+ onClick: handleCloseAllEditors
1972
+ })
1973
+ )
1974
+ },
1975
+ React.createElement(
1976
+ "div",
1977
+ { style: { marginBottom: "12px" } },
1978
+ state.panes.map(function (pane) {
1979
+ if (pane.tabs.length === 0) return null;
1980
+ var isPane2 = pane.id === 2;
1981
+ return React.createElement(
1982
+ "div",
1983
+ {
1984
+ key: pane.id,
1985
+ className: "open-editors-group",
1986
+ style: { marginBottom: pane.id === 1 && state.panes[1].tabs.length > 0 ? "10px" : "0" }
1987
+ },
1988
+ React.createElement(
1989
+ "div",
1990
+ { className: "ide-sidebar-header open-editors-group-header" },
1991
+ React.createElement(
1992
+ "span",
1993
+ { className: "open-editors-group-title" },
1994
+ "GROUP ",
1995
+ pane.id
1996
+ ),
1997
+ React.createElement(
1998
+ SectionActionGroup,
1999
+ { ariaLabel: "Group " + pane.id + " actions", className: "collapsible-actions open-editors-group-actions" },
2000
+ React.createElement(SidebarActionButton, {
2001
+ title: "Close all editors in Group " + pane.id,
2002
+ ariaLabel: "Close all editors in Group " + pane.id,
2003
+ iconClass: "far fa-window-close",
2004
+ onClick: function (e) {
2005
+ e.stopPropagation();handleCloseEditorsInGroup(pane.id);
2006
+ }
2007
+ })
2008
+ )
2009
+ ),
2010
+ React.createElement(
2011
+ "div",
2012
+ { className: "file-tree" },
2013
+ pane.tabs.map(function (tab) {
2014
+ return React.createElement(
2015
+ "div",
2016
+ {
2017
+ key: tab.id,
2018
+ className: "tree-item " + (pane.activeTabId === tab.id && state.focusedPaneId === pane.id ? "active" : ""),
2019
+ onClick: function () {
2020
+ TabManager.focusPane(pane.id);TabManager.switchTab(pane.id, tab.id);
2021
+ }
2022
+ },
2023
+ React.createElement("i", { className: "tree-item-icon " + (window.getFileIcon ? window.getFileIcon(tab.name) : 'far fa-file-code') + " tree-file-icon" }),
2024
+ React.createElement(
2025
+ "div",
2026
+ { className: "tree-item-name", style: { display: 'flex', alignItems: 'center' } },
2027
+ React.createElement(
2028
+ "span",
2029
+ { style: { overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' } },
2030
+ tab.name
2031
+ ),
2032
+ tab.dirty && React.createElement("i", { className: "fas fa-circle", style: { fontSize: '5px', color: '#e3d286', marginLeft: '6px', marginTop: '1px' } })
2033
+ ),
2034
+ React.createElement(
2035
+ "div",
2036
+ { className: "tab-actions", style: { display: 'flex', position: 'absolute', right: '4px', top: 0, height: '100%', alignItems: 'center' } },
2037
+ React.createElement(
2038
+ "div",
2039
+ { className: "tab-split", onClick: function (e) {
2040
+ e.stopPropagation();TabManager.moveTabToPane(pane.id, pane.id === 1 ? 2 : 1, tab.id);
2041
+ }, style: { padding: '0 4px', cursor: 'pointer', opacity: 0.6 }, title: "Move to Group " + (pane.id === 1 ? 2 : 1) },
2042
+ React.createElement("i", { className: isPane2 ? "fas fa-chevron-left" : "fas fa-chevron-right" })
2043
+ ),
2044
+ React.createElement(
2045
+ "div",
2046
+ { className: "tab-close", onClick: function (e) {
2047
+ e.stopPropagation();requestCloseTab(pane.id, tab.id);
2048
+ }, style: { padding: '0 4px', cursor: 'pointer', opacity: 0.6 } },
2049
+ React.createElement("i", { className: "fas fa-times" })
2050
+ )
2051
+ )
2052
+ );
2053
+ })
2054
+ )
2055
+ );
2056
+ })
2057
+ )
2058
+ )
2059
+ ),
2060
+ React.createElement(
2061
+ "div",
2062
+ { className: "ide-sidebar-scrollable" },
2063
+ React.createElement(
2064
+ CollapsibleSection,
2065
+ {
2066
+ title: projectSectionTitle,
2067
+ isCollapsed: collapsedSections.projects,
2068
+ onToggle: function (isCollapsed) {
2069
+ return handleToggleSection('projects', isCollapsed);
2070
+ },
2071
+ actions: React.createElement(
2072
+ SectionActionGroup,
2073
+ { ariaLabel: "Project actions" },
2074
+ React.createElement(SidebarActionButton, {
2075
+ title: "Collapse all folders",
2076
+ iconClass: "fas fa-compress-alt",
2077
+ onClick: handleCollapseAll
2078
+ }),
2079
+ React.createElement(SidebarActionButton, {
2080
+ title: "New file",
2081
+ iconClass: loading.createFile ? 'fas fa-spinner fa-spin' : 'far fa-file',
2082
+ onClick: function () {
2083
+ return handleCreateFile();
2084
+ },
2085
+ disabled: !!loading.createFile
2086
+ }),
2087
+ React.createElement(SidebarActionButton, {
2088
+ title: "New folder",
2089
+ iconClass: loading.createDir ? 'fas fa-spinner fa-spin' : 'far fa-folder',
2090
+ onClick: function () {
2091
+ return handleCreateDir();
2092
+ },
2093
+ disabled: !!loading.createDir
2094
+ }),
2095
+ React.createElement(SidebarActionButton, {
2096
+ title: "Rename selected",
2097
+ iconClass: loading.renamePath ? 'fas fa-spinner fa-spin' : 'fas fa-pen',
2098
+ onClick: function () {
2099
+ return handleRenamePath();
2100
+ },
2101
+ disabled: !!loading.renamePath || !selectedTreePath
2102
+ }),
2103
+ React.createElement(SidebarActionButton, {
2104
+ title: "Delete selected",
2105
+ iconClass: loading.deletePath ? 'fas fa-spinner fa-spin' : 'far fa-trash-alt',
2106
+ onClick: function () {
2107
+ return handleDeletePath();
2108
+ },
2109
+ disabled: !!loading.deletePath || !selectedTreePath,
2110
+ danger: true
2111
+ })
2112
+ )
2113
+ },
2114
+ React.createElement(FileTree, {
2115
+ items: treeData,
2116
+ onSelect: handleSoftOpenFile,
2117
+ activePath: editorPrefs.autoRevealInExplorer !== false ? (activeTab && activeTab.path) : null,
2118
+ selectedPath: selectedTreePath,
2119
+ onNodeSelect: setSelectedTreeNode,
2120
+ gitFiles: state.gitFiles,
2121
+ expandedDirs: expandedDirs,
2122
+ onExpandedDirsChange: setExpandedDirs,
2123
+ onFileDoubleClick: handleHardOpenFile,
2124
+ onContextMenu: openContextMenu,
2125
+ pendingCreate: pendingCreate,
2126
+ onCreateConfirm: handleCreateConfirm,
2127
+ onCreateCancel: handleCreateCancel,
2128
+ pendingRename: pendingRename,
2129
+ onRenameConfirm: handleRenameConfirm,
2130
+ onRenameCancel: handleRenameCancel
2131
+ })
2132
+ )
2133
+ )
2134
+ ),
2135
+ activeSidebarTab === 'search' && React.createElement(
2136
+ "form",
2137
+ { className: "search-panel", onSubmit: execSearch },
2138
+ React.createElement(
2139
+ "div",
2140
+ { className: "search-input-wrap" },
2141
+ React.createElement(
2142
+ "div",
2143
+ { className: "search-input-shell" },
2144
+ React.createElement("input", {
2145
+ className: "search-input",
2146
+ placeholder: "Find in files...",
2147
+ value: searchQuery,
2148
+ onChange: handleSearchChange
2149
+ }),
2150
+ searchQuery && React.createElement(
2151
+ "button",
2152
+ {
2153
+ type: "button",
2154
+ className: "search-clear-btn",
2155
+ onClick: clearSearch,
2156
+ title: "Clear search",
2157
+ "aria-label": "Clear search"
2158
+ },
2159
+ React.createElement("i", { className: "fas fa-times" })
2160
+ )
2161
+ ),
2162
+ React.createElement(
2163
+ "button",
2164
+ { type: "submit", className: "search-btn", disabled: searchLoading, title: searchLoading ? "Searching..." : "Search" },
2165
+ React.createElement("i", { className: searchLoading ? "fas fa-spinner fa-spin" : "fas fa-search" })
2166
+ )
2167
+ ),
2168
+ React.createElement(
2169
+ "div",
2170
+ { className: "search-results" },
2171
+ searchQuery && state.searchResults.length > 0 && React.createElement(
2172
+ "div",
2173
+ { className: "search-results-meta" },
2174
+ state.searchResults.length,
2175
+ " result" + (state.searchResults.length !== 1 ? "s" : ""),
2176
+ state.searchCapped && React.createElement(
2177
+ "span",
2178
+ { className: "search-results-capped" },
2179
+ " — refine query to see more"
2180
+ )
2181
+ ),
2182
+ searchQuery && state.searchResults.length === 0 && React.createElement(
2183
+ "div",
2184
+ { className: "search-results-empty" },
2185
+ "No results"
2186
+ ),
2187
+ state.searchResults.map(function (res, i) {
2188
+ var fileName = res.file.split('/').pop();
2189
+ return React.createElement(
2190
+ "div",
2191
+ { key: i, className: "search-result-item", onClick: function () {
2192
+ return handleSelectFile(res.file, res.file.split('/').pop(), res.line);
2193
+ } },
2194
+ React.createElement("i", { className: (window.getFileIcon ? window.getFileIcon(fileName) : 'far fa-file-code') + " search-result-icon" }),
2195
+ React.createElement(
2196
+ "div",
2197
+ { className: "search-result-body" },
2198
+ React.createElement(
2199
+ "div",
2200
+ { className: "search-result-file" },
2201
+ fileName,
2202
+ React.createElement(
2203
+ "span",
2204
+ { className: "search-result-line-num" },
2205
+ " ",
2206
+ res.file,
2207
+ ":",
2208
+ res.line
2209
+ )
2210
+ ),
2211
+ React.createElement(
2212
+ "div",
2213
+ { className: "search-result-text" },
2214
+ res.text
2215
+ )
2216
+ ),
2217
+ );
2218
+ })
2219
+ )
2220
+ )
2221
+ )
2222
+ ),
2223
+ React.createElement("div", {
2224
+ className: "panel-divider sidebar-divider " + (activeResizeMode === 'sidebar' ? 'active' : ''),
2225
+ onMouseDown: startSidebarResize,
2226
+ role: "separator",
2227
+ "aria-orientation": "vertical",
2228
+ "aria-label": "Resize explorer panel"
2229
+ }),
2230
+ React.createElement(
2231
+ "div",
2232
+ {
2233
+ id: "ide-main-split-container",
2234
+ className: "ide-main",
2235
+ style: { display: 'flex', flexDirection: 'row', width: '100%', height: '100%', cursor: activeResizeMode === 'pane' ? 'col-resize' : 'default', userSelect: activeResizeMode ? 'none' : 'auto' },
2236
+ onDragOverCapture: function (e) {
2237
+ if (!draggedTab) return;
2238
+ e.preventDefault();
2239
+
2240
+ var rect = e.currentTarget.getBoundingClientRect();
2241
+ var splitAtX = rect.left + rect.width * (dragSplitWidthRef.current / 100);
2242
+ var hoverPaneId = e.clientX >= splitAtX ? 2 : 1;
2243
+ var nextDropPane = hoverPaneId === draggedTab.sourcePaneId ? null : hoverPaneId;
2244
+
2245
+ e.dataTransfer.dropEffect = nextDropPane ? 'move' : 'none';
2246
+ if (dragOverPaneId !== nextDropPane) setDragOverPaneId(nextDropPane);
2247
+ },
2248
+ onDropCapture: function (e) {
2249
+ if (!draggedTab) return;
2250
+ e.preventDefault();
2251
+
2252
+ var rect = e.currentTarget.getBoundingClientRect();
2253
+ var splitAtX = rect.left + rect.width * (dragSplitWidthRef.current / 100);
2254
+ var targetPaneId = e.clientX >= splitAtX ? 2 : 1;
2255
+
2256
+ if (targetPaneId !== draggedTab.sourcePaneId) {
2257
+ moveDraggedTabToPane(targetPaneId);
2258
+ } else {
2259
+ clearDragState();
2260
+ }
2261
+ }
2262
+ },
2263
+ state.panes.map(function (pane, idx) {
2264
+ if (pane.id === 2 && pane.tabs.length === 0 && !draggedTab) return null; // Show pane 2 while dragging to allow drop-to-split
2265
+
2266
+ // Dynamic width distribution
2267
+ var isSplit = state.panes[1].tabs.length > 0 || !!draggedTab;
2268
+ var flexBasis = '100%';
2269
+ if (isSplit) flexBasis = pane.id === 1 ? pane1Width + "%" : 100 - pane1Width + "%";
2270
+
2271
+ var isFocused = state.focusedPaneId === pane.id;
2272
+ var pActiveTab = pane.tabs.find(function (t) {
2273
+ return t.id === pane.activeTabId;
2274
+ });
2275
+ var canAcceptDrop = !!draggedTab && draggedTab.sourcePaneId !== pane.id;
2276
+ var isDropTarget = canAcceptDrop && dragOverPaneId === pane.id;
2277
+
2278
+ var content;
2279
+ if (pane.tabs.length === 0) {
2280
+ content = React.createElement(
2281
+ 'div',
2282
+ { className: 'ide-empty-pane' },
2283
+ React.createElement('i', { className: 'fas fa-code ide-empty-icon' }),
2284
+ React.createElement(
2285
+ 'p',
2286
+ null,
2287
+ 'Ctrl+P to open files'
2288
+ )
2289
+ );
2290
+ } else if (pActiveTab) {
2291
+ if (pActiveTab.isCommitGraph) {
2292
+ content = React.createElement(window.CommitGraph || CommitGraph, {
2293
+ commits: pActiveTab.commits || [],
2294
+ onSelectCommit: handleSelectCommit
2295
+ });
2296
+ } else if (pActiveTab.isSettings) {
2297
+ content = React.createElement(
2298
+ 'div',
2299
+ { className: 'ide-settings-tab-content' },
2300
+ React.createElement(
2301
+ 'div',
2302
+ { className: 'ide-settings-body' },
2303
+ React.createElement(
2304
+ 'label', { className: 'ide-settings-row' },
2305
+ React.createElement('span', { className: 'ide-settings-label' }, 'Theme'),
2306
+ React.createElement(
2307
+ 'select', {
2308
+ className: 'ide-settings-select',
2309
+ value: editorPrefs.theme || 'vs-dark',
2310
+ onChange: function(e) { setEditorPrefs(function(p) { return Object.assign({}, p, { theme: e.target.value }); }); }
2311
+ },
2312
+ React.createElement('option', { value: 'vs-dark' }, 'Dark (vs-dark)'),
2313
+ React.createElement('option', { value: 'vs' }, 'Light (vs)'),
2314
+ React.createElement('option', { value: 'hc-black' }, 'High Contrast Dark'),
2315
+ React.createElement('option', { value: 'hc-light' }, 'High Contrast Light'),
2316
+ React.createElement('option', { value: 'dracula' }, 'Dracula'),
2317
+ React.createElement('option', { value: 'night-owl' }, 'Night Owl'),
2318
+ React.createElement('option', { value: 'monokai' }, 'Monokai'),
2319
+ React.createElement('option', { value: 'nord' }, 'Nord'),
2320
+ React.createElement('option', { value: 'github-dark' }, 'GitHub Dark'),
2321
+ React.createElement('option', { value: 'tomorrow-night' }, 'Tomorrow Night'),
2322
+ React.createElement('option', { value: 'github-light' }, 'GitHub Light')
2323
+ )
2324
+ ),
2325
+ React.createElement(
2326
+ 'label', { className: 'ide-settings-row' },
2327
+ React.createElement('span', { className: 'ide-settings-label' }, 'Font size'),
2328
+ React.createElement('input', {
2329
+ type: 'number', min: '8', max: '32', step: '1',
2330
+ className: 'ide-settings-input',
2331
+ value: editorPrefs.fontSize || 13,
2332
+ onChange: function(e) {
2333
+ var v = parseInt(e.target.value, 10);
2334
+ if (v >= 8 && v <= 32) setEditorPrefs(function(p) { return Object.assign({}, p, { fontSize: v }); });
2335
+ }
2336
+ })
2337
+ ),
2338
+ React.createElement(
2339
+ 'label', { className: 'ide-settings-row' },
2340
+ React.createElement('span', { className: 'ide-settings-label' }, 'Tab size'),
2341
+ React.createElement('input', {
2342
+ type: 'number', min: '1', max: '8', step: '1',
2343
+ className: 'ide-settings-input',
2344
+ value: editorPrefs.tabSize || 1,
2345
+ onChange: function(e) {
2346
+ var v = parseInt(e.target.value, 10);
2347
+ if (v >= 1 && v <= 8) setEditorPrefs(function(p) { return Object.assign({}, p, { tabSize: v }); });
2348
+ }
2349
+ })
2350
+ ),
2351
+ React.createElement(
2352
+ 'label', { className: 'ide-settings-row ide-settings-row-check' },
2353
+ React.createElement('span', { className: 'ide-settings-label' }, 'Use spaces'),
2354
+ React.createElement('input', {
2355
+ type: 'checkbox',
2356
+ className: 'ide-settings-checkbox',
2357
+ checked: !!(editorPrefs.insertSpaces),
2358
+ onChange: function(e) { var v = e.target.checked; setEditorPrefs(function(p) { return Object.assign({}, p, { insertSpaces: v }); }); }
2359
+ })
2360
+ ),
2361
+ React.createElement(
2362
+ 'label', { className: 'ide-settings-row' },
2363
+ React.createElement('span', { className: 'ide-settings-label' }, 'Font family'),
2364
+ React.createElement('input', {
2365
+ type: 'text',
2366
+ className: 'ide-settings-input ide-settings-input-wide',
2367
+ value: editorPrefs.fontFamily || "'JetBrains Mono', 'Fira Code', Consolas, 'Courier New', monospace",
2368
+ onChange: function(e) { setEditorPrefs(function(p) { return Object.assign({}, p, { fontFamily: e.target.value }); }); }
2369
+ })
2370
+ ),
2371
+ React.createElement(
2372
+ 'label', { className: 'ide-settings-row' },
2373
+ React.createElement('span', { className: 'ide-settings-label' }, 'Word wrap'),
2374
+ React.createElement(
2375
+ 'select', {
2376
+ className: 'ide-settings-select',
2377
+ value: editorPrefs.wordWrap || 'off',
2378
+ onChange: function(e) { setEditorPrefs(function(p) { return Object.assign({}, p, { wordWrap: e.target.value }); }); }
2379
+ },
2380
+ React.createElement('option', { value: 'off' }, 'Off'),
2381
+ React.createElement('option', { value: 'on' }, 'On'),
2382
+ React.createElement('option', { value: 'wordWrapColumn' }, 'Column')
2383
+ )
2384
+ ),
2385
+ React.createElement(
2386
+ 'label', { className: 'ide-settings-row' },
2387
+ React.createElement('span', { className: 'ide-settings-label' }, 'Line numbers'),
2388
+ React.createElement(
2389
+ 'select', {
2390
+ className: 'ide-settings-select',
2391
+ value: editorPrefs.lineNumbers || 'on',
2392
+ onChange: function(e) { setEditorPrefs(function(p) { return Object.assign({}, p, { lineNumbers: e.target.value }); }); }
2393
+ },
2394
+ React.createElement('option', { value: 'on' }, 'On'),
2395
+ React.createElement('option', { value: 'off' }, 'Off'),
2396
+ React.createElement('option', { value: 'relative' }, 'Relative')
2397
+ )
2398
+ ),
2399
+ React.createElement(
2400
+ 'label', { className: 'ide-settings-row' },
2401
+ React.createElement('span', { className: 'ide-settings-label' }, 'Render whitespace'),
2402
+ React.createElement(
2403
+ 'select', {
2404
+ className: 'ide-settings-select',
2405
+ value: editorPrefs.renderWhitespace || 'none',
2406
+ onChange: function(e) { setEditorPrefs(function(p) { return Object.assign({}, p, { renderWhitespace: e.target.value }); }); }
2407
+ },
2408
+ React.createElement('option', { value: 'none' }, 'None'),
2409
+ React.createElement('option', { value: 'selection' }, 'Selection only'),
2410
+ React.createElement('option', { value: 'boundary' }, 'Boundary'),
2411
+ React.createElement('option', { value: 'all' }, 'All')
2412
+ )
2413
+ ),
2414
+ React.createElement(
2415
+ 'label', { className: 'ide-settings-row ide-settings-row-check' },
2416
+ React.createElement('span', { className: 'ide-settings-label' }, 'Minimap'),
2417
+ React.createElement('input', {
2418
+ type: 'checkbox',
2419
+ className: 'ide-settings-checkbox',
2420
+ checked: !!(editorPrefs.minimap),
2421
+ onChange: function(e) { var v = e.target.checked; setEditorPrefs(function(p) { return Object.assign({}, p, { minimap: v }); }); }
2422
+ })
2423
+ ),
2424
+ React.createElement(
2425
+ 'label', { className: 'ide-settings-row ide-settings-row-check' },
2426
+ React.createElement('span', { className: 'ide-settings-label' }, 'Scroll beyond last line'),
2427
+ React.createElement('input', {
2428
+ type: 'checkbox',
2429
+ className: 'ide-settings-checkbox',
2430
+ checked: !!(editorPrefs.scrollBeyondLastLine),
2431
+ onChange: function(e) { var v = e.target.checked; setEditorPrefs(function(p) { return Object.assign({}, p, { scrollBeyondLastLine: v }); }); }
2432
+ })
2433
+ ),
2434
+ React.createElement(
2435
+ 'label', { className: 'ide-settings-row ide-settings-row-check' },
2436
+ React.createElement('span', { className: 'ide-settings-label' }, 'Bracket colorization'),
2437
+ React.createElement('input', {
2438
+ type: 'checkbox',
2439
+ className: 'ide-settings-checkbox',
2440
+ checked: !!(editorPrefs.bracketPairColorization),
2441
+ onChange: function(e) { var v = e.target.checked; setEditorPrefs(function(p) { return Object.assign({}, p, { bracketPairColorization: v }); }); }
2442
+ })
2443
+ ),
2444
+ React.createElement(
2445
+ 'label', { className: 'ide-settings-row ide-settings-row-check' },
2446
+ React.createElement('span', { className: 'ide-settings-label' }, 'Vim mode'),
2447
+ React.createElement('input', {
2448
+ type: 'checkbox',
2449
+ className: 'ide-settings-checkbox',
2450
+ checked: !!(editorPrefs.vimMode),
2451
+ onChange: function(e) { var v = e.target.checked; setEditorPrefs(function(p) { return Object.assign({}, p, { vimMode: v }); }); }
2452
+ })
2453
+ ),
2454
+ React.createElement(
2455
+ 'label', { className: 'ide-settings-row ide-settings-row-check' },
2456
+ React.createElement('span', { className: 'ide-settings-label' }, 'Explorer follows active file'),
2457
+ React.createElement('input', {
2458
+ type: 'checkbox',
2459
+ className: 'ide-settings-checkbox',
2460
+ checked: !!(editorPrefs.autoRevealInExplorer),
2461
+ onChange: function(e) { var v = e.target.checked; setEditorPrefs(function(p) { return Object.assign({}, p, { autoRevealInExplorer: v }); }); }
2462
+ })
2463
+ ),
2464
+ React.createElement(
2465
+ 'label', { className: 'ide-settings-row ide-settings-row-check' },
2466
+ React.createElement('span', { className: 'ide-settings-label' }, 'Toolbar: icons only'),
2467
+ React.createElement('input', {
2468
+ type: 'checkbox',
2469
+ className: 'ide-settings-checkbox',
2470
+ checked: !!(editorPrefs.toolbarIconOnly),
2471
+ onChange: function(e) { var v = e.target.checked; setEditorPrefs(function(p) { return Object.assign({}, p, { toolbarIconOnly: v }); }); }
2472
+ })
2473
+ ),
2474
+ React.createElement('div', { className: 'ide-settings-section-header' }, 'Prettier'),
2475
+ React.createElement(
2476
+ 'label', { className: 'ide-settings-row' },
2477
+ React.createElement('span', { className: 'ide-settings-label' }, 'Print width'),
2478
+ React.createElement('input', {
2479
+ type: 'number', min: '40', max: '200', step: '1',
2480
+ className: 'ide-settings-input',
2481
+ value: editorPrefs.prettierPrintWidth != null ? editorPrefs.prettierPrintWidth : 80,
2482
+ onChange: function(e) {
2483
+ var v = parseInt(e.target.value, 10);
2484
+ if (v >= 40 && v <= 200) setEditorPrefs(function(p) { return Object.assign({}, p, { prettierPrintWidth: v }); });
2485
+ }
2486
+ })
2487
+ ),
2488
+ React.createElement(
2489
+ 'label', { className: 'ide-settings-row' },
2490
+ React.createElement('span', { className: 'ide-settings-label' }, 'Tab width'),
2491
+ React.createElement('input', {
2492
+ type: 'number', min: '1', max: '8', step: '1',
2493
+ className: 'ide-settings-input',
2494
+ value: editorPrefs.prettierTabWidth != null ? editorPrefs.prettierTabWidth : 2,
2495
+ onChange: function(e) {
2496
+ var v = parseInt(e.target.value, 10);
2497
+ if (v >= 1 && v <= 8) setEditorPrefs(function(p) { return Object.assign({}, p, { prettierTabWidth: v }); });
2498
+ }
2499
+ })
2500
+ ),
2501
+ React.createElement(
2502
+ 'label', { className: 'ide-settings-row ide-settings-row-check' },
2503
+ React.createElement('span', { className: 'ide-settings-label' }, 'Use tabs'),
2504
+ React.createElement('input', {
2505
+ type: 'checkbox',
2506
+ className: 'ide-settings-checkbox',
2507
+ checked: !!editorPrefs.prettierUseTabs,
2508
+ onChange: function(e) { var v = e.target.checked; setEditorPrefs(function(p) { return Object.assign({}, p, { prettierUseTabs: v }); }); }
2509
+ })
2510
+ ),
2511
+ React.createElement(
2512
+ 'label', { className: 'ide-settings-row ide-settings-row-check' },
2513
+ React.createElement('span', { className: 'ide-settings-label' }, 'Semicolons'),
2514
+ React.createElement('input', {
2515
+ type: 'checkbox',
2516
+ className: 'ide-settings-checkbox',
2517
+ checked: editorPrefs.prettierSemi !== false,
2518
+ onChange: function(e) { var v = e.target.checked; setEditorPrefs(function(p) { return Object.assign({}, p, { prettierSemi: v }); }); }
2519
+ })
2520
+ ),
2521
+ React.createElement(
2522
+ 'label', { className: 'ide-settings-row ide-settings-row-check' },
2523
+ React.createElement('span', { className: 'ide-settings-label' }, 'Single quotes'),
2524
+ React.createElement('input', {
2525
+ type: 'checkbox',
2526
+ className: 'ide-settings-checkbox',
2527
+ checked: !!editorPrefs.prettierSingleQuote,
2528
+ onChange: function(e) { var v = e.target.checked; setEditorPrefs(function(p) { return Object.assign({}, p, { prettierSingleQuote: v }); }); }
2529
+ })
2530
+ ),
2531
+ React.createElement(
2532
+ 'label', { className: 'ide-settings-row' },
2533
+ React.createElement('span', { className: 'ide-settings-label' }, 'Trailing commas'),
2534
+ React.createElement(
2535
+ 'select', {
2536
+ className: 'ide-settings-select',
2537
+ value: editorPrefs.prettierTrailingComma || 'all',
2538
+ onChange: function(e) { setEditorPrefs(function(p) { return Object.assign({}, p, { prettierTrailingComma: e.target.value }); }); }
2539
+ },
2540
+ React.createElement('option', { value: 'all' }, 'All'),
2541
+ React.createElement('option', { value: 'es5' }, 'ES5'),
2542
+ React.createElement('option', { value: 'none' }, 'None')
2543
+ )
2544
+ ),
2545
+ React.createElement(
2546
+ 'label', { className: 'ide-settings-row ide-settings-row-check' },
2547
+ React.createElement('span', { className: 'ide-settings-label' }, 'Bracket spacing'),
2548
+ React.createElement('input', {
2549
+ type: 'checkbox',
2550
+ className: 'ide-settings-checkbox',
2551
+ checked: editorPrefs.prettierBracketSpacing !== false,
2552
+ onChange: function(e) { var v = e.target.checked; setEditorPrefs(function(p) { return Object.assign({}, p, { prettierBracketSpacing: v }); }); }
2553
+ })
2554
+ ),
2555
+ React.createElement('div', { className: 'ide-settings-section-header' }, 'RuboCop'),
2556
+ React.createElement(
2557
+ 'label', { className: 'ide-settings-row ide-settings-row-check' },
2558
+ React.createElement('span', { className: 'ide-settings-label' }, 'Enable RuboCop linting'),
2559
+ React.createElement('input', {
2560
+ type: 'checkbox',
2561
+ className: 'ide-settings-checkbox',
2562
+ checked: editorPrefs.rubocopLintEnabled !== false,
2563
+ onChange: function(e) { var v = e.target.checked; setEditorPrefs(function(p) { return Object.assign({}, p, { rubocopLintEnabled: v }); }); }
2564
+ })
2565
+ ),
2566
+ rubocopAvailable && rubocopConfigPath ? React.createElement(
2567
+ 'div', { className: 'ide-settings-row ide-settings-row-link' },
2568
+ React.createElement('span', { className: 'ide-settings-label' }, 'Config file'),
2569
+ React.createElement(
2570
+ 'button', {
2571
+ type: 'button',
2572
+ className: 'ide-settings-config-link',
2573
+ title: 'Open ' + rubocopConfigPath,
2574
+ onClick: function() { handleSelectFile(rubocopConfigPath, rubocopConfigPath.split('/').pop()); }
2575
+ },
2576
+ React.createElement('i', { className: 'fas fa-file-alt', style: { marginRight: 5 } }),
2577
+ rubocopConfigPath
2578
+ )
2579
+ ) : null,
2580
+ React.createElement(
2581
+ 'button',
2582
+ {
2583
+ className: 'ide-settings-reset-btn',
2584
+ type: 'button',
2585
+ onClick: function() { setEditorPrefs(Object.assign({}, DEFAULT_EDITOR_PREFS)); }
2586
+ },
2587
+ React.createElement('i', { className: 'fas fa-undo', style: { marginRight: 6 } }),
2588
+ 'Reset to defaults'
2589
+ )
2590
+ )
2591
+ );
2592
+ } else if (pActiveTab.isDiff) {
2593
+ var _t = editorPrefs.theme || 'vs-dark';
2594
+ var isDiffDark = _t !== 'vs' && _t !== 'hc-light' && _t !== 'github-light';
2595
+ content = React.createElement(window.DiffViewer || DiffViewer, {
2596
+ key: pActiveTab.id,
2597
+ path: pActiveTab.path,
2598
+ original: pActiveTab.diffOriginal || '',
2599
+ modified: pActiveTab.diffModified || '',
2600
+ isDark: isDiffDark,
2601
+ editorPrefs: editorPrefs,
2602
+ onClose: function() { requestCloseTab(pane.id, pActiveTab.id); }
2603
+ });
2604
+ } else {
2605
+ content = React.createElement(window.EditorPanel || EditorPanel, {
2606
+ key: pActiveTab.id,
2607
+ tab: pActiveTab,
2608
+ paneId: pane.id,
2609
+ markers: markers[pActiveTab.id] || [],
2610
+ gitAvailable: gitAvailable,
2611
+ testAvailable: testAvailable,
2612
+ treeData: treeData,
2613
+ testResult: testResult,
2614
+ testPanelFile: testPanelFile,
2615
+ testLoading: testLoading,
2616
+ testInlineVisible: testInlineVisible,
2617
+ editorPrefs: editorPrefs,
2618
+ onFormat: function() { onFormatRef.current(); },
2619
+ onSave: function() { handleSave(pane.id, pActiveTab); },
2620
+ onRunTest: handleRunTest,
2621
+ onShowHistory: function(path) { setHistoryPanelPath(path); },
2622
+ onContentChange: function onContentChange(val) {
2623
+ var st = EditorStore.getState();
2624
+ var cp = st.panes.find(function(p) { return p.id === pane.id; });
2625
+ var ct = cp && cp.tabs.find(function(t) { return t.id === pActiveTab.id; });
2626
+ var cleanNorm = ((ct && ct.cleanContent) || '').replace(/\r\n/g, '\n');
2627
+ var valNorm = val.replace(/\r\n/g, '\n');
2628
+ if (valNorm === cleanNorm) {
2629
+ TabManager.markClean(pane.id, pActiveTab.id, val);
2630
+ } else {
2631
+ TabManager.markDirty(pane.id, pActiveTab.id, val);
2632
+ }
2633
+ }
2634
+ });
2635
+ }
2636
+ }
2637
+
2638
+ return React.createElement(
2639
+ React.Fragment,
2640
+ { key: pane.id },
2641
+ idx === 1 && isSplit && React.createElement("div", {
2642
+ className: "panel-divider pane-divider " + (activeResizeMode === 'pane' ? 'active' : ''),
2643
+ onMouseDown: startPaneResize
2644
+ }),
2645
+ React.createElement(
2646
+ "div",
2647
+ {
2648
+ className: "ide-pane " + (isFocused ? 'focused' : '') + " " + (isDropTarget ? 'drop-target' : ''),
2649
+ style: { flexBasis: flexBasis, flexShrink: 0, flexGrow: 0, display: 'flex', flexDirection: 'column', minWidth: 0 },
2650
+ onClickCapture: function (e) {
2651
+ // Do not steal click events from controls inside the Settings tab.
2652
+ // Focusing the pane in capture phase can rerender before checkbox
2653
+ // change events are processed, making toggles appear stuck.
2654
+ if (e.target && e.target.closest && e.target.closest('.ide-settings-tab-content')) return;
2655
+ return TabManager.focusPane(pane.id);
2656
+ },
2657
+ onDragOver: function (e) {
2658
+ if (!canAcceptDrop) return;
2659
+ e.preventDefault();
2660
+ e.dataTransfer.dropEffect = 'move';
2661
+ if (dragOverPaneId !== pane.id) setDragOverPaneId(pane.id);
2662
+ },
2663
+ onDragEnter: function (e) {
2664
+ if (!canAcceptDrop) return;
2665
+ e.preventDefault();
2666
+ if (dragOverPaneId !== pane.id) setDragOverPaneId(pane.id);
2667
+ },
2668
+ onDragLeave: function (e) {
2669
+ if (dragOverPaneId !== pane.id) return;
2670
+ if (!e.currentTarget.contains(e.relatedTarget)) {
2671
+ setDragOverPaneId(null);
2672
+ }
2673
+ },
2674
+ onDrop: function (e) {
2675
+ if (!canAcceptDrop) return;
2676
+ e.preventDefault();
2677
+ moveDraggedTabToPane(pane.id);
2678
+ }
2679
+ },
2680
+ pane.tabs.length > 0 ? React.createElement(
2681
+ React.Fragment,
2682
+ null,
2683
+ renderTabBar(pane.id, pane.tabs, pane.activeTabId),
2684
+ React.createElement(
2685
+ "div",
2686
+ { style: { flex: 1, minHeight: 0, display: 'flex', flexDirection: 'column', visibility: activeResizeMode === 'pane' ? 'hidden' : 'visible' } },
2687
+ content
2688
+ )
2689
+ ) : React.createElement(
2690
+ "div",
2691
+ { className: "tab-welcome" },
2692
+ canAcceptDrop ? React.createElement(
2693
+ React.Fragment,
2694
+ null,
2695
+ React.createElement("i", { className: "fas fa-columns" }),
2696
+ React.createElement(
2697
+ "h2",
2698
+ null,
2699
+ "Drop Tab Here"
2700
+ ),
2701
+ React.createElement(
2702
+ "p",
2703
+ null,
2704
+ "Release to move this file into Group ",
2705
+ pane.id,
2706
+ "."
2707
+ )
2708
+ ) : pane.id === 1 ? React.createElement(
2709
+ React.Fragment,
2710
+ null,
2711
+ React.createElement("i", { className: "fas fa-code" }),
2712
+ React.createElement("h2", null, "Mini Browser Editor"),
2713
+ React.createElement("p", { className: "welcome-intro" }, "Open a file from the explorer to start editing."),
2714
+ React.createElement(
2715
+ "div",
2716
+ { className: "welcome-shortcuts" },
2717
+ React.createElement(
2718
+ "div",
2719
+ { className: "welcome-section" },
2720
+ React.createElement("h3", null, "Keyboard shortcuts"),
2721
+ React.createElement(
2722
+ "table",
2723
+ { className: "shortcut-table" },
2724
+ React.createElement(
2725
+ "tbody",
2726
+ null,
2727
+ React.createElement("tr", null,
2728
+ React.createElement("td", null, React.createElement("kbd", null, "Ctrl+P")),
2729
+ React.createElement("td", null, "Quick-open any file by name")
2730
+ ),
2731
+ React.createElement("tr", null,
2732
+ React.createElement("td", null, React.createElement("kbd", null, "Ctrl+S")),
2733
+ React.createElement("td", null, "Save the active file")
2734
+ ),
2735
+ React.createElement("tr", null,
2736
+ React.createElement("td", null, React.createElement("kbd", null, "Ctrl+Shift+S")),
2737
+ React.createElement("td", null, "Save all dirty files")
2738
+ ),
2739
+ React.createElement("tr", null,
2740
+ React.createElement("td", null, React.createElement("kbd", null, "Alt+Shift+F")),
2741
+ React.createElement("td", null, "Format the active file")
2742
+ ),
2743
+ React.createElement("tr", null,
2744
+ React.createElement("td", null, React.createElement("kbd", null, "Ctrl+Shift+G")),
2745
+ React.createElement("td", null, "Toggle git panel")
2746
+ ),
2747
+ React.createElement("tr", null,
2748
+ React.createElement("td", null, React.createElement("kbd", null, "Ctrl+Z\u00a0/\u00a0Ctrl+Y")),
2749
+ React.createElement("td", null, "Undo / Redo")
2750
+ )
2751
+ )
2752
+ )
2753
+ ),
2754
+ React.createElement(
2755
+ "div",
2756
+ { className: "welcome-section" },
2757
+ React.createElement("h3", null, "Sidebar panels"),
2758
+ React.createElement(
2759
+ "ul",
2760
+ { className: "welcome-tips" },
2761
+ React.createElement("li", null, React.createElement("i", { className: "fas fa-folder-open" }), "\u00a0Explorer \u2014 browse and manage project files"),
2762
+ React.createElement("li", null, React.createElement("i", { className: "fas fa-search" }), "\u00a0Search \u2014 full-text search across all files"),
2763
+ React.createElement("li", null, React.createElement("i", { className: "fas fa-code-branch" }), "\u00a0Git panel \u2014 branch status and changed files (top-right icon)")
2764
+ )
2765
+ ),
2766
+ React.createElement(
2767
+ "div",
2768
+ { className: "welcome-section" },
2769
+ React.createElement("h3", null, "Editor tips"),
2770
+ React.createElement(
2771
+ "ul",
2772
+ { className: "welcome-tips" },
2773
+ React.createElement("li", null, "Drag any tab to the right half to open a split pane"),
2774
+ React.createElement("li", null, "Right-click a file in the explorer to rename or delete it"),
2775
+ React.createElement("li", null, "Ruby files auto-lint with RuboCop when installed"),
2776
+ React.createElement("li", null, "JS, CSS, HTML and Markdown auto-format with Prettier")
2777
+ )
2778
+ )
2779
+ )
2780
+ ) : null
2781
+ )
2782
+ )
2783
+ );
2784
+ })
2785
+ ),
2786
+
2787
+ // Right-side Git panel (children of ide-body, alongside sidebar and ide-main)
2788
+ showGitPanel && React.createElement("div", {
2789
+ className: "panel-divider gitpanel-divider " + (activeResizeMode === 'gitpanel' ? 'active' : ''),
2790
+ onMouseDown: startGitPanelResize,
2791
+ role: "separator",
2792
+ "aria-orientation": "vertical",
2793
+ "aria-label": "Resize git panel"
2794
+ }),
2795
+ showGitPanel && React.createElement(
2796
+ "div",
2797
+ { className: "ide-git-right-panel", style: { width: gitPanelWidth + "px" } },
2798
+ React.createElement(window.GitPanel || GitPanel, {
2799
+ gitInfo: state.gitInfo,
2800
+ error: state.gitInfoError,
2801
+ redmineEnabled: redmineEnabled,
2802
+ onRefresh: function () { return GitService.fetchInfo(); },
2803
+ onClose: function () { return setShowGitPanel(false); },
2804
+ onOpenFile: openFileFromGitPanel,
2805
+ onOpenDiff: TabManager.openDiffTab,
2806
+ onOpenAllChanges: function(scope, label) { TabManager.openCombinedDiffTab(scope, label); },
2807
+ onSelectCommit: handleSelectCommit
2808
+ })
2809
+ )
2810
+ ),
2811
+ React.createElement(
2812
+ "div",
2813
+ { className: "ide-statusbar" },
2814
+ hasGitBranch && React.createElement(
2815
+ "div",
2816
+ { className: "statusbar-branch" },
2817
+ React.createElement("i", { className: "fas fa-code-branch" }),
2818
+ " ",
2819
+ state.gitBranch,
2820
+ state.gitInfo && state.gitInfo.ahead > 0 && React.createElement(
2821
+ "span",
2822
+ { className: "statusbar-aheadbehind", title: state.gitInfo.ahead + " commit(s) ahead of upstream" },
2823
+ " \u2191",
2824
+ state.gitInfo.ahead
2825
+ ),
2826
+ state.gitInfo && state.gitInfo.behind > 0 && React.createElement(
2827
+ "span",
2828
+ { className: "statusbar-aheadbehind statusbar-behind", title: state.gitInfo.behind + " commit(s) behind upstream" },
2829
+ " \u2193",
2830
+ state.gitInfo.behind
2831
+ )
2832
+ ),
2833
+ !serverOnline && React.createElement(
2834
+ "div",
2835
+ { className: "statusbar-offline" },
2836
+ React.createElement("i", { className: "fas fa-exclamation-triangle" }),
2837
+ " Server offline"
2838
+ ),
2839
+ activeFileCommit && React.createElement(
2840
+ "div",
2841
+ { className: "statusbar-file-commit", title: activeFileCommit.title + " — " + activeFileCommit.author },
2842
+ React.createElement("i", { className: "fas fa-history", style: { marginRight: "4px", opacity: 0.7 } }),
2843
+ React.createElement("span", { className: "commit-hash" }, activeFileCommit.hash.slice(0, 7)),
2844
+ " ",
2845
+ activeFileCommit.author
2846
+ ),
2847
+ React.createElement(
2848
+ "div",
2849
+ { className: "statusbar-msg " + state.statusMessage.kind },
2850
+ state.statusMessage.text
2851
+ ),
2852
+ React.createElement(
2853
+ "div",
2854
+ { className: "statusbar-version" },
2855
+ "v" + (document.body.dataset.mbeditorVersion || "")
2856
+ )
2857
+ ),
2858
+
2859
+ // File History Panel overlay
2860
+ historyPanelPath && React.createElement(
2861
+ React.Fragment,
2862
+ null,
2863
+ React.createElement("div", {
2864
+ style: { position: 'fixed', inset: 0, zIndex: 9800, background: 'rgba(0,0,0,0.55)' },
2865
+ onClick: function() { setHistoryPanelPath(null); }
2866
+ }),
2867
+ React.createElement(window.FileHistoryPanel || FileHistoryPanel, {
2868
+ path: historyPanelPath,
2869
+ onClose: function () { return setHistoryPanelPath(null); },
2870
+ onSelectCommit: function (hash, path) {
2871
+ TabManager.openDiffTab(path, path.split('/').pop(), hash + '^', hash, null);
2872
+ }
2873
+ })
2874
+ ),
2875
+
2876
+ // Test Results Panel overlay — closing hides the dialog but keeps testResult for inline markers
2877
+ (testPanelOpen || testLoading) && React.createElement(window.TestResultsPanel || TestResultsPanel, {
2878
+ result: testResult,
2879
+ testFile: testPanelFile,
2880
+ isLoading: testLoading,
2881
+ showInline: testInlineVisible,
2882
+ onToggleInline: function () { setTestInlineVisible(function (prev) { return !prev; }); },
2883
+ onClose: function () { setTestPanelOpen(false); },
2884
+ onOpenTestFile: testPanelFile ? function () {
2885
+ var fileName = testPanelFile.split('/').pop();
2886
+ TabManager.openTab(testPanelFile, fileName);
2887
+ setTestPanelOpen(false);
2888
+ } : null
2889
+ }),
2890
+
2891
+ // Commit Detail overlay (shown when a commit row is clicked in CommitGraph)
2892
+ selectedCommit && React.createElement(
2893
+ React.Fragment,
2894
+ null,
2895
+ React.createElement("div", {
2896
+ style: { position: 'fixed', inset: 0, zIndex: 9000, background: 'rgba(0,0,0,0.45)' },
2897
+ onClick: function() { setSelectedCommit(null); setCommitDetailFiles(null); }
2898
+ }),
2899
+ React.createElement(
2900
+ "div",
2901
+ { className: "ide-commit-detail-panel" },
2902
+ React.createElement(
2903
+ "div",
2904
+ { className: "ide-commit-detail-header" },
2905
+ React.createElement(
2906
+ "div",
2907
+ null,
2908
+ React.createElement("div", { className: "ide-commit-detail-title" }, selectedCommit.title),
2909
+ React.createElement(
2910
+ "div",
2911
+ { className: "ide-commit-detail-meta" },
2912
+ React.createElement("span", { className: "commit-hash" }, selectedCommit.hash.slice(0, 7)),
2913
+ " \xB7 ",
2914
+ selectedCommit.author,
2915
+ " \xB7 ",
2916
+ selectedCommit.date ? new Date(selectedCommit.date).toLocaleString() : ""
2917
+ )
2918
+ ),
2919
+ React.createElement(
2920
+ "button",
2921
+ { className: "git-header-btn", onClick: function() { setSelectedCommit(null); setCommitDetailFiles(null); }, title: "Close" },
2922
+ React.createElement("i", { className: "fas fa-times" })
2923
+ )
2924
+ ),
2925
+ commitDetailFiles === null
2926
+ ? React.createElement("div", { className: "git-empty" }, React.createElement("i", { className: "fas fa-spinner fa-spin" }), " Loading...")
2927
+ : commitDetailFiles.length === 0
2928
+ ? React.createElement("div", { className: "git-empty" }, "No file changes found.")
2929
+ : React.createElement(
2930
+ "div",
2931
+ { className: "git-list" },
2932
+ commitDetailFiles.map(function(f, i) {
2933
+ var name = (f.path || '').split('/').pop() || f.path;
2934
+ return React.createElement(
2935
+ "div",
2936
+ { key: i, className: "git-file-item" },
2937
+ React.createElement(
2938
+ "div",
2939
+ { className: "git-file-info", onClick: function() { openFileFromGitPanel(f.path, name); } },
2940
+ React.createElement("span", { className: "git-status-badge git-" + (f.status || 'M'), title: f.status }, f.status),
2941
+ React.createElement("span", { className: "git-file-path", title: f.path }, f.path)
2942
+ ),
2943
+ React.createElement(
2944
+ "div",
2945
+ { className: "git-file-actions" },
2946
+ React.createElement(
2947
+ "button",
2948
+ {
2949
+ className: "git-action-btn",
2950
+ title: "View Diff",
2951
+ onClick: function(e) {
2952
+ e.stopPropagation();
2953
+ TabManager.openDiffTab(f.path, name, selectedCommit.hash + '^', selectedCommit.hash, null);
2954
+ }
2955
+ },
2956
+ React.createElement("i", { className: "fas fa-exchange-alt" })
2957
+ )
2958
+ )
2959
+ );
2960
+ })
2961
+ )
2962
+ )
2963
+ ),
2964
+
2965
+ // Modals & Panels
2966
+ state.isQuickOpenVisible && React.createElement(window.QuickOpenDialog || QuickOpenDialog, { onSelect: handleSelectFile, onClose: function () {
2967
+ return setQuickOpen(false);
2968
+ } }),
2969
+ contextMenu && React.createElement(
2970
+ React.Fragment,
2971
+ null,
2972
+ React.createElement("div", {
2973
+ style: { position: 'fixed', inset: 0, zIndex: 9998 },
2974
+ onClick: closeContextMenu,
2975
+ onContextMenu: function (e) {
2976
+ e.preventDefault();closeContextMenu();
2977
+ }
2978
+ }),
2979
+ React.createElement(
2980
+ "div",
2981
+ {
2982
+ className: "context-menu",
2983
+ style: { left: contextMenu.x, top: contextMenu.y },
2984
+ onClick: function (e) {
2985
+ return e.stopPropagation();
2986
+ }
2987
+ },
2988
+ contextMenu.node && contextMenu.node.type === 'file' && React.createElement(
2989
+ "div",
2990
+ { className: "context-menu-item", onClick: function () {
2991
+ return handleContextMenuAction('open');
2992
+ } },
2993
+ React.createElement("i", { className: "far fa-file-code context-menu-icon" }),
2994
+ " Open"
2995
+ ),
2996
+ React.createElement(
2997
+ "div",
2998
+ { className: "context-menu-item", onClick: function () {
2999
+ return handleContextMenuAction('newFile');
3000
+ } },
3001
+ React.createElement("i", { className: "far fa-file context-menu-icon" }),
3002
+ " New File"
3003
+ ),
3004
+ React.createElement(
3005
+ "div",
3006
+ { className: "context-menu-item", onClick: function () {
3007
+ return handleContextMenuAction('newFolder');
3008
+ } },
3009
+ React.createElement("i", { className: "far fa-folder context-menu-icon" }),
3010
+ " New Folder"
3011
+ ),
3012
+ React.createElement("div", { className: "context-menu-divider" }),
3013
+ React.createElement(
3014
+ "div",
3015
+ { className: "context-menu-item", onClick: function () {
3016
+ return handleContextMenuAction('rename');
3017
+ } },
3018
+ React.createElement("i", { className: "fas fa-pen context-menu-icon" }),
3019
+ " Rename"
3020
+ ),
3021
+ React.createElement(
3022
+ "div",
3023
+ { className: "context-menu-item context-menu-item-danger", onClick: function () {
3024
+ return handleContextMenuAction('delete');
3025
+ } },
3026
+ React.createElement("i", { className: "far fa-trash-alt context-menu-icon" }),
3027
+ " Delete"
3028
+ ),
3029
+ React.createElement("div", { className: "context-menu-divider" }),
3030
+ React.createElement(
3031
+ "div",
3032
+ { className: "context-menu-item", onClick: function () {
3033
+ return handleContextMenuAction('copyPath');
3034
+ } },
3035
+ React.createElement("i", { className: "fas fa-copy context-menu-icon" }),
3036
+ " Copy Path"
3037
+ )
3038
+ )
3039
+ ),
3040
+ closingTabId && React.createElement(
3041
+ "div",
3042
+ { className: "quick-open-overlay", style: { zIndex: 10001 } },
3043
+ React.createElement(
3044
+ "div",
3045
+ { className: "quick-open-box", style: { width: '400px', padding: '20px', background: '#252526', border: '1px solid #454545' } },
3046
+ React.createElement(
3047
+ "h3",
3048
+ { style: { marginTop: 0, fontSize: '14px', color: '#fff' } },
3049
+ "Unsaved Changes"
3050
+ ),
3051
+ React.createElement(
3052
+ "p",
3053
+ { style: { color: '#ccc', margin: '16px 0', fontSize: '13px' } },
3054
+ "Do you want to save the changes you made to ",
3055
+ React.createElement(
3056
+ "strong",
3057
+ null,
3058
+ (state.panes.flatMap(function (p) {
3059
+ return p.tabs;
3060
+ }).find(function (t) {
3061
+ return t.id === closingTabId;
3062
+ }) || {}).name
3063
+ ),
3064
+ "?"
3065
+ ),
3066
+ React.createElement(
3067
+ "p",
3068
+ { style: { color: '#888', marginBottom: '24px', fontSize: '12px' } },
3069
+ "Your changes will be lost if you don't save them."
3070
+ ),
3071
+ React.createElement(
3072
+ "div",
3073
+ { style: { display: 'flex', gap: '8px', justifyContent: 'flex-end' } },
3074
+ React.createElement(
3075
+ "button",
3076
+ {
3077
+ onClick: function () {
3078
+ return confirmCloseTab(true);
3079
+ },
3080
+ style: { padding: '6px 16px', background: '#0e639c', color: '#fff', border: 'none', borderRadius: '4px', cursor: 'pointer' } },
3081
+ "Save"
3082
+ ),
3083
+ React.createElement(
3084
+ "button",
3085
+ {
3086
+ onClick: function () {
3087
+ return confirmCloseTab(false);
3088
+ },
3089
+ style: { padding: '6px 16px', background: 'transparent', color: '#ccc', border: '1px solid #666', borderRadius: '4px', cursor: 'pointer' } },
3090
+ "Don't Save"
3091
+ ),
3092
+ React.createElement(
3093
+ "button",
3094
+ {
3095
+ onClick: function () {
3096
+ return setClosingTabId(null);
3097
+ },
3098
+ style: { padding: '6px 16px', background: 'transparent', color: '#888', border: 'none', cursor: 'pointer' } },
3099
+ "Cancel"
3100
+ )
3101
+ )
3102
+ )
3103
+ )
3104
+ );
3105
+ };
3106
+
3107
+ window.MbeditorApp = MbeditorApp;
3108
+ /* TITLE BAR */ /* SIDEBAR */ /* EDITOR AREA */ /* STATUS BAR */ /* Right-click context menu */