mbeditor 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/README.md +127 -0
- data/app/assets/javascripts/mbeditor/application.js +19 -0
- data/app/assets/javascripts/mbeditor/components/CodeReviewPanel.js +202 -0
- data/app/assets/javascripts/mbeditor/components/CollapsibleSection.js +71 -0
- data/app/assets/javascripts/mbeditor/components/CombinedDiffViewer.js +139 -0
- data/app/assets/javascripts/mbeditor/components/CommitGraph.js +65 -0
- data/app/assets/javascripts/mbeditor/components/DiffViewer.js +142 -0
- data/app/assets/javascripts/mbeditor/components/EditorPanel.js +363 -0
- data/app/assets/javascripts/mbeditor/components/FileHistoryPanel.js +112 -0
- data/app/assets/javascripts/mbeditor/components/FileTree.js +304 -0
- data/app/assets/javascripts/mbeditor/components/GitPanel.js +416 -0
- data/app/assets/javascripts/mbeditor/components/MbeditorApp.js +2335 -0
- data/app/assets/javascripts/mbeditor/components/QuickOpenDialog.js +118 -0
- data/app/assets/javascripts/mbeditor/components/ShortcutHelp.js +186 -0
- data/app/assets/javascripts/mbeditor/components/TabBar.js +123 -0
- data/app/assets/javascripts/mbeditor/editor_plugins.js +282 -0
- data/app/assets/javascripts/mbeditor/editor_store.js +53 -0
- data/app/assets/javascripts/mbeditor/file_service.js +77 -0
- data/app/assets/javascripts/mbeditor/git_service.js +104 -0
- data/app/assets/javascripts/mbeditor/search_service.js +53 -0
- data/app/assets/javascripts/mbeditor/tab_manager.js +461 -0
- data/app/assets/stylesheets/mbeditor/application.css +705 -0
- data/app/assets/stylesheets/mbeditor/editor.css +1264 -0
- data/app/controllers/mbeditor/application_controller.rb +10 -0
- data/app/controllers/mbeditor/editors_controller.rb +695 -0
- data/app/controllers/mbeditor/git_controller.rb +188 -0
- data/app/services/mbeditor/git_blame_service.rb +98 -0
- data/app/services/mbeditor/git_commit_graph_service.rb +60 -0
- data/app/services/mbeditor/git_diff_service.rb +71 -0
- data/app/services/mbeditor/git_file_history_service.rb +42 -0
- data/app/services/mbeditor/git_service.rb +82 -0
- data/app/services/mbeditor/redmine_service.rb +86 -0
- data/app/views/layouts/mbeditor/application.html.erb +71 -0
- data/app/views/mbeditor/editors/index.html.erb +1 -0
- data/config/environments/development.rb +53 -0
- data/config/initializers/assets.rb +9 -0
- data/config/routes.rb +37 -0
- data/lib/mbeditor/configuration.rb +16 -0
- data/lib/mbeditor/engine.rb +28 -0
- data/lib/mbeditor/version.rb +3 -0
- data/lib/mbeditor.rb +19 -0
- data/mbeditor.gemspec +30 -0
- data/public/min-maps/vs/base/worker/workerMain.js.map +1 -0
- data/public/monaco-editor/vs/base/browser/ui/codicons/codicon/codicon.ttf +0 -0
- data/public/monaco-editor/vs/base/worker/workerMain.js +31 -0
- data/public/monaco-editor/vs/basic-languages/cameligo/cameligo.js +10 -0
- data/public/monaco-editor/vs/basic-languages/css/css.js +12 -0
- data/public/monaco-editor/vs/basic-languages/dart/dart.js +10 -0
- data/public/monaco-editor/vs/basic-languages/flow9/flow9.js +10 -0
- data/public/monaco-editor/vs/basic-languages/go/go.js +10 -0
- data/public/monaco-editor/vs/basic-languages/handlebars/handlebars.js +440 -0
- data/public/monaco-editor/vs/basic-languages/javascript/javascript.js +10 -0
- data/public/monaco-editor/vs/basic-languages/markdown/markdown.js +10 -0
- data/public/monaco-editor/vs/basic-languages/msdax/msdax.js +10 -0
- data/public/monaco-editor/vs/basic-languages/postiats/postiats.js +10 -0
- data/public/monaco-editor/vs/basic-languages/pug/pug.js +412 -0
- data/public/monaco-editor/vs/basic-languages/restructuredtext/restructuredtext.js +10 -0
- data/public/monaco-editor/vs/basic-languages/ruby/ruby.js +10 -0
- data/public/monaco-editor/vs/basic-languages/sb/sb.js +10 -0
- data/public/monaco-editor/vs/basic-languages/typespec/typespec.js +10 -0
- data/public/monaco-editor/vs/basic-languages/yaml/yaml.js +10 -0
- data/public/monaco-editor/vs/editor/editor.main.css +8 -0
- data/public/monaco-editor/vs/editor/editor.main.js +797 -0
- data/public/monaco-editor/vs/language/typescript/tsMode.js +20 -0
- data/public/monaco-editor/vs/language/typescript/tsWorker.js +51328 -0
- data/public/monaco-editor/vs/loader.js +10 -0
- data/public/monaco-editor/vs/nls.messages.de.js +20 -0
- data/public/monaco-editor/vs/nls.messages.es.js +20 -0
- data/public/monaco-editor/vs/nls.messages.fr.js +18 -0
- data/public/monaco-editor/vs/nls.messages.it.js +18 -0
- data/public/monaco-editor/vs/nls.messages.ja.js +20 -0
- data/public/monaco-editor/vs/nls.messages.ko.js +18 -0
- data/public/monaco-editor/vs/nls.messages.ru.js +20 -0
- data/public/monaco-editor/vs/nls.messages.zh-cn.js +20 -0
- data/public/monaco-editor/vs/nls.messages.zh-tw.js +18 -0
- data/public/monaco_worker.js +5 -0
- data/vendor/assets/javascripts/axios.min.js +2 -0
- data/vendor/assets/javascripts/lodash.min.js +140 -0
- data/vendor/assets/javascripts/marked.min.js +6 -0
- data/vendor/assets/javascripts/minisearch.min.js +2044 -0
- data/vendor/assets/javascripts/prettier-plugin-babel.js +16 -0
- data/vendor/assets/javascripts/prettier-plugin-estree.js +35 -0
- data/vendor/assets/javascripts/prettier-plugin-html.js +19 -0
- data/vendor/assets/javascripts/prettier-plugin-markdown.js +59 -0
- data/vendor/assets/javascripts/prettier-plugin-postcss.js +52 -0
- data/vendor/assets/javascripts/prettier-standalone.js +37 -0
- data/vendor/assets/javascripts/react-dom.min.js +267 -0
- data/vendor/assets/javascripts/react.min.js +31 -0
- data/vendor/assets/stylesheets/fontawesome.min.css +9 -0
- data/vendor/assets/webfonts/fa-brands-400.woff2 +0 -0
- data/vendor/assets/webfonts/fa-regular-400.woff2 +0 -0
- data/vendor/assets/webfonts/fa-solid-900.woff2 +0 -0
- metadata +173 -0
|
@@ -0,0 +1,2335 @@
|
|
|
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 = 240;
|
|
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
|
+
|
|
21
|
+
var SidebarActionButton = function SidebarActionButton(_ref) {
|
|
22
|
+
var title = _ref.title;
|
|
23
|
+
var iconClass = _ref.iconClass;
|
|
24
|
+
var onClick = _ref.onClick;
|
|
25
|
+
var _ref$disabled = _ref.disabled;
|
|
26
|
+
var disabled = _ref$disabled === undefined ? false : _ref$disabled;
|
|
27
|
+
var _ref$danger = _ref.danger;
|
|
28
|
+
var danger = _ref$danger === undefined ? false : _ref$danger;
|
|
29
|
+
var _ref$ariaLabel = _ref.ariaLabel;
|
|
30
|
+
var ariaLabel = _ref$ariaLabel === undefined ? null : _ref$ariaLabel;
|
|
31
|
+
|
|
32
|
+
return React.createElement(
|
|
33
|
+
"button",
|
|
34
|
+
{
|
|
35
|
+
type: "button",
|
|
36
|
+
className: "project-action-btn" + (danger ? " danger" : ""),
|
|
37
|
+
title: title,
|
|
38
|
+
"aria-label": ariaLabel || title,
|
|
39
|
+
onClick: onClick,
|
|
40
|
+
disabled: !!disabled
|
|
41
|
+
},
|
|
42
|
+
React.createElement("i", { className: iconClass })
|
|
43
|
+
);
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
var SectionActionGroup = function SectionActionGroup(_ref2) {
|
|
47
|
+
var ariaLabel = _ref2.ariaLabel;
|
|
48
|
+
var children = _ref2.children;
|
|
49
|
+
var _ref2$className = _ref2.className;
|
|
50
|
+
var className = _ref2$className === undefined ? "" : _ref2$className;
|
|
51
|
+
|
|
52
|
+
return React.createElement(
|
|
53
|
+
"div",
|
|
54
|
+
{
|
|
55
|
+
className: "project-actions" + (className ? " " + className : ""),
|
|
56
|
+
role: "toolbar",
|
|
57
|
+
"aria-label": ariaLabel
|
|
58
|
+
},
|
|
59
|
+
children
|
|
60
|
+
);
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
var MbeditorApp = function MbeditorApp() {
|
|
64
|
+
var _useState = useState(EditorStore.getState());
|
|
65
|
+
|
|
66
|
+
var _useState2 = _slicedToArray(_useState, 2);
|
|
67
|
+
|
|
68
|
+
var state = _useState2[0];
|
|
69
|
+
var setState = _useState2[1];
|
|
70
|
+
|
|
71
|
+
var _useState21 = useState(null);
|
|
72
|
+
var _useState22 = _slicedToArray(_useState21, 2);
|
|
73
|
+
var historyPanelPath = _useState22[0];
|
|
74
|
+
var setHistoryPanelPath = _useState22[1];
|
|
75
|
+
|
|
76
|
+
var _useState23 = useState(false);
|
|
77
|
+
var _useState24 = _slicedToArray(_useState23, 2);
|
|
78
|
+
var isNavigating = _useState24[0];
|
|
79
|
+
var setIsNavigating = _useState24[1];
|
|
80
|
+
|
|
81
|
+
var _useState25 = useState(false);
|
|
82
|
+
var _useState26 = _slicedToArray(_useState25, 2);
|
|
83
|
+
var isReviewOpen = _useState26[0];
|
|
84
|
+
var setIsReviewOpen = _useState26[1];
|
|
85
|
+
|
|
86
|
+
var _useState27 = useState(null);
|
|
87
|
+
var _useState28 = _slicedToArray(_useState27, 2);
|
|
88
|
+
var selectedCommit = _useState28[0];
|
|
89
|
+
var setSelectedCommit = _useState28[1];
|
|
90
|
+
|
|
91
|
+
var _useState29 = useState(null);
|
|
92
|
+
var _useState30 = _slicedToArray(_useState29, 2);
|
|
93
|
+
var commitDetailFiles = _useState30[0];
|
|
94
|
+
var setCommitDetailFiles = _useState30[1];
|
|
95
|
+
|
|
96
|
+
var _useState4 = useState([]);
|
|
97
|
+
|
|
98
|
+
var _useState42 = _slicedToArray(_useState4, 2);
|
|
99
|
+
|
|
100
|
+
var treeData = _useState42[0];
|
|
101
|
+
var setTreeData = _useState42[1];
|
|
102
|
+
|
|
103
|
+
var _useState5 = useState("");
|
|
104
|
+
|
|
105
|
+
var _useState52 = _slicedToArray(_useState5, 2);
|
|
106
|
+
|
|
107
|
+
var projectRootName = _useState52[0];
|
|
108
|
+
var setProjectRootName = _useState52[1];
|
|
109
|
+
|
|
110
|
+
var _useState6 = useState(null);
|
|
111
|
+
|
|
112
|
+
var _useState62 = _slicedToArray(_useState6, 2);
|
|
113
|
+
|
|
114
|
+
var selectedTreeNode = _useState62[0];
|
|
115
|
+
var setSelectedTreeNode = _useState62[1];
|
|
116
|
+
|
|
117
|
+
var _useState7 = useState("");
|
|
118
|
+
|
|
119
|
+
var _useState72 = _slicedToArray(_useState7, 2);
|
|
120
|
+
|
|
121
|
+
var searchQuery = _useState72[0];
|
|
122
|
+
var setSearchQuery = _useState72[1];
|
|
123
|
+
|
|
124
|
+
var _useState8 = useState("explorer");
|
|
125
|
+
|
|
126
|
+
var _useState82 = _slicedToArray(_useState8, 2);
|
|
127
|
+
|
|
128
|
+
var activeSidebarTab = _useState82[0];
|
|
129
|
+
var setActiveSidebarTab = _useState82[1];
|
|
130
|
+
|
|
131
|
+
var _useState9 = useState({});
|
|
132
|
+
|
|
133
|
+
var _useState92 = _slicedToArray(_useState9, 2);
|
|
134
|
+
|
|
135
|
+
var markers = _useState92[0];
|
|
136
|
+
var setMarkers = _useState92[1];
|
|
137
|
+
// { tabId: [] }
|
|
138
|
+
|
|
139
|
+
var _useState10 = useState({});
|
|
140
|
+
|
|
141
|
+
var _useState102 = _slicedToArray(_useState10, 2);
|
|
142
|
+
|
|
143
|
+
var loading = _useState102[0];
|
|
144
|
+
var setLoading = _useState102[1];
|
|
145
|
+
|
|
146
|
+
var _useState11 = useState(null);
|
|
147
|
+
|
|
148
|
+
var _useState112 = _slicedToArray(_useState11, 2);
|
|
149
|
+
|
|
150
|
+
var closingTabId = _useState112[0];
|
|
151
|
+
var setClosingTabId = _useState112[1];
|
|
152
|
+
|
|
153
|
+
var _useState12 = useState(null);
|
|
154
|
+
|
|
155
|
+
var _useState122 = _slicedToArray(_useState12, 2);
|
|
156
|
+
|
|
157
|
+
var closingPaneId = _useState122[0];
|
|
158
|
+
var setClosingPaneId = _useState122[1];
|
|
159
|
+
|
|
160
|
+
var _useState13 = useState(SIDEBAR_MIN_WIDTH);
|
|
161
|
+
|
|
162
|
+
var _useState132 = _slicedToArray(_useState13, 2);
|
|
163
|
+
|
|
164
|
+
var sidebarWidth = _useState132[0];
|
|
165
|
+
var setSidebarWidth = _useState132[1];
|
|
166
|
+
|
|
167
|
+
var _useState14 = useState(50);
|
|
168
|
+
|
|
169
|
+
var _useState142 = _slicedToArray(_useState14, 2);
|
|
170
|
+
|
|
171
|
+
var pane1Width = _useState142[0];
|
|
172
|
+
var setPane1Width = _useState142[1];
|
|
173
|
+
// percentage
|
|
174
|
+
|
|
175
|
+
var _useState15 = useState(null);
|
|
176
|
+
|
|
177
|
+
var _useState152 = _slicedToArray(_useState15, 2);
|
|
178
|
+
|
|
179
|
+
var activeResizeMode = _useState152[0];
|
|
180
|
+
var setActiveResizeMode = _useState152[1];
|
|
181
|
+
|
|
182
|
+
var _useState16 = useState(null);
|
|
183
|
+
|
|
184
|
+
var _useState162 = _slicedToArray(_useState16, 2);
|
|
185
|
+
|
|
186
|
+
var draggedTab = _useState162[0];
|
|
187
|
+
var setDraggedTab = _useState162[1];
|
|
188
|
+
|
|
189
|
+
var _useState17 = useState(null);
|
|
190
|
+
|
|
191
|
+
var _useState172 = _slicedToArray(_useState17, 2);
|
|
192
|
+
|
|
193
|
+
var dragOverPaneId = _useState172[0];
|
|
194
|
+
var setDragOverPaneId = _useState172[1];
|
|
195
|
+
|
|
196
|
+
var _useState18 = useState(false);
|
|
197
|
+
var _useState182 = _slicedToArray(_useState18, 2);
|
|
198
|
+
var showGitPanel = _useState182[0];
|
|
199
|
+
var setShowGitPanel = _useState182[1];
|
|
200
|
+
|
|
201
|
+
var _useState18g = useState(320);
|
|
202
|
+
var _useState18g2 = _slicedToArray(_useState18g, 2);
|
|
203
|
+
var gitPanelWidth = _useState18g2[0];
|
|
204
|
+
var setGitPanelWidth = _useState18g2[1];
|
|
205
|
+
|
|
206
|
+
var _useState18h = useState(false);
|
|
207
|
+
|
|
208
|
+
var _useState18h2 = _slicedToArray(_useState18h, 2);
|
|
209
|
+
|
|
210
|
+
var showHelp = _useState18h2[0];
|
|
211
|
+
var setShowHelp = _useState18h2[1];
|
|
212
|
+
|
|
213
|
+
var _useState18b = useState(true);
|
|
214
|
+
|
|
215
|
+
var _useState18b2 = _slicedToArray(_useState18b, 2);
|
|
216
|
+
|
|
217
|
+
var serverOnline = _useState18b2[0];
|
|
218
|
+
var setServerOnline = _useState18b2[1];
|
|
219
|
+
|
|
220
|
+
var _useState18c = useState(false);
|
|
221
|
+
|
|
222
|
+
var _useState18c2 = _slicedToArray(_useState18c, 2);
|
|
223
|
+
|
|
224
|
+
var rubocopAvailable = _useState18c2[0];
|
|
225
|
+
var setRubocopAvailable = _useState18c2[1];
|
|
226
|
+
|
|
227
|
+
var _useState18d = useState(false);
|
|
228
|
+
|
|
229
|
+
var _useState18d2 = _slicedToArray(_useState18d, 2);
|
|
230
|
+
|
|
231
|
+
var hamlLintAvailable = _useState18d2[0];
|
|
232
|
+
var setHamlLintAvailable = _useState18d2[1];
|
|
233
|
+
|
|
234
|
+
var _useState18e = useState(false);
|
|
235
|
+
var _useState18e2 = _slicedToArray(_useState18e, 2);
|
|
236
|
+
var gitAvailable = _useState18e2[0];
|
|
237
|
+
var setGitAvailable = _useState18e2[1];
|
|
238
|
+
|
|
239
|
+
var _useState19 = useState({
|
|
240
|
+
openEditors: false,
|
|
241
|
+
projects: false
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
var _useState192 = _slicedToArray(_useState19, 2);
|
|
245
|
+
|
|
246
|
+
var collapsedSections = _useState192[0];
|
|
247
|
+
var setCollapsedSections = _useState192[1];
|
|
248
|
+
|
|
249
|
+
var _useState20 = useState({});
|
|
250
|
+
|
|
251
|
+
var _useState202 = _slicedToArray(_useState20, 2);
|
|
252
|
+
|
|
253
|
+
var expandedDirs = _useState202[0];
|
|
254
|
+
var setExpandedDirs = _useState202[1];
|
|
255
|
+
|
|
256
|
+
var _useState21 = useState(null);
|
|
257
|
+
|
|
258
|
+
var _useState212 = _slicedToArray(_useState21, 2);
|
|
259
|
+
|
|
260
|
+
var pendingCreate = _useState212[0];
|
|
261
|
+
var setPendingCreate = _useState212[1];
|
|
262
|
+
|
|
263
|
+
var _useState22 = useState(null);
|
|
264
|
+
|
|
265
|
+
var _useState222 = _slicedToArray(_useState22, 2);
|
|
266
|
+
|
|
267
|
+
var pendingRename = _useState222[0];
|
|
268
|
+
var setPendingRename = _useState222[1];
|
|
269
|
+
|
|
270
|
+
var _useState23 = useState(null);
|
|
271
|
+
|
|
272
|
+
var _useState232 = _slicedToArray(_useState23, 2);
|
|
273
|
+
|
|
274
|
+
var contextMenu = _useState232[0];
|
|
275
|
+
var setContextMenu = _useState232[1];
|
|
276
|
+
|
|
277
|
+
var resizeSessionRef = useRef(null);
|
|
278
|
+
var resizeRafRef = useRef(null);
|
|
279
|
+
|
|
280
|
+
var clamp = function clamp(value, min, max) {
|
|
281
|
+
return Math.min(max, Math.max(min, value));
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
var normalizeRelativePath = function normalizeRelativePath(input) {
|
|
285
|
+
return (input || "").replace(/\\/g, "/").trim().replace(/^\/+/, "").replace(/\/+$/, "").replace(/\/+/g, "/");
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
var parentDir = function parentDir(path) {
|
|
289
|
+
if (!path) return "";
|
|
290
|
+
var idx = path.lastIndexOf("/");
|
|
291
|
+
return idx > 0 ? path.slice(0, idx) : "";
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
var deriveProjectRootName = function deriveProjectRootName() {
|
|
295
|
+
if (projectRootName) return projectRootName;
|
|
296
|
+
var railsRoot = document && document.body && document.body.dataset ? document.body.dataset.railsRoot : "";
|
|
297
|
+
if (!railsRoot) return "PROJECT";
|
|
298
|
+
var parts = railsRoot.split("/").filter(Boolean);
|
|
299
|
+
return parts.length ? parts[parts.length - 1] : "PROJECT";
|
|
300
|
+
};
|
|
301
|
+
|
|
302
|
+
var refreshProjectTree = function refreshProjectTree() {
|
|
303
|
+
return FileService.getTree().then(function (data) {
|
|
304
|
+
setTreeData(data || []);
|
|
305
|
+
SearchService.buildIndex(data || []);
|
|
306
|
+
return data || [];
|
|
307
|
+
})["catch"](function (err) {
|
|
308
|
+
EditorStore.setStatus("Failed to refresh files: " + (err && err.message || "Unknown error"), "error");
|
|
309
|
+
return [];
|
|
310
|
+
});
|
|
311
|
+
};
|
|
312
|
+
|
|
313
|
+
var isRubyPath = function isRubyPath(path) {
|
|
314
|
+
return path && (path.endsWith('.rb') || path.endsWith('.gemspec') || path.endsWith('Rakefile') || path.endsWith('Gemfile'));
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
var applyMarkersForTab = function applyMarkersForTab(paneId, tabId, nextMarkers) {
|
|
318
|
+
var currentPane = EditorStore.getState().panes.find(function (p) {
|
|
319
|
+
return p.id === paneId;
|
|
320
|
+
});
|
|
321
|
+
var current = currentPane ? currentPane.tabs.find(function (t) {
|
|
322
|
+
return t.id === tabId;
|
|
323
|
+
}) : null;
|
|
324
|
+
if (current) current.markers = nextMarkers;
|
|
325
|
+
setMarkers(function (prev) {
|
|
326
|
+
return _extends({}, prev, _defineProperty({}, tabId, nextMarkers));
|
|
327
|
+
});
|
|
328
|
+
};
|
|
329
|
+
|
|
330
|
+
var runRubyLint = function runRubyLint(tab, paneId) {
|
|
331
|
+
var options = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2];
|
|
332
|
+
|
|
333
|
+
if (!tab || (!isRubyPath(tab.path) && !tab.path.endsWith('.haml'))) return Promise.resolve(null);
|
|
334
|
+
|
|
335
|
+
if (options.showLoading) {
|
|
336
|
+
setLoading(function (prev) {
|
|
337
|
+
return _extends({}, prev, { lint: true });
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
if (options.showStatus) {
|
|
341
|
+
EditorStore.setStatus('Linting...', 'info');
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
return FileService.lintFile(tab.path, tab.content).then(function (res) {
|
|
345
|
+
var nextMarkers = res.markers || [];
|
|
346
|
+
applyMarkersForTab(paneId, tab.id, nextMarkers);
|
|
347
|
+
|
|
348
|
+
if (options.showStatus) {
|
|
349
|
+
var count = res.summary && res.summary.offense_count || 0;
|
|
350
|
+
EditorStore.setStatus(count === 0 ? 'No RuboCop offenses!' : "Found " + count + " offenses", count === 0 ? 'success' : 'warning');
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
return res;
|
|
354
|
+
})["catch"](function (err) {
|
|
355
|
+
if (options.showStatus) {
|
|
356
|
+
EditorStore.setStatus('Lint failed: ' + err.message, 'error');
|
|
357
|
+
}
|
|
358
|
+
return null;
|
|
359
|
+
})["finally"](function () {
|
|
360
|
+
if (options.showLoading) {
|
|
361
|
+
setLoading(function (prev) {
|
|
362
|
+
return _extends({}, prev, { lint: false });
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
});
|
|
366
|
+
};
|
|
367
|
+
|
|
368
|
+
var _debouncedAutoLint = useRef(window._.debounce(function (tab, paneId) {
|
|
369
|
+
if (!tab) return;
|
|
370
|
+
if (isRubyPath(tab.path)) {
|
|
371
|
+
runRubyLint(tab, paneId);
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
if (tab.path.endsWith('.haml')) {
|
|
375
|
+
runRubyLint(tab, paneId);
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
var ext = tab.path.split('.').pop().toLowerCase();
|
|
380
|
+
var formatMap = {
|
|
381
|
+
'js': 'babel', 'jsx': 'babel',
|
|
382
|
+
'json': 'json',
|
|
383
|
+
'css': 'css', 'scss': 'scss',
|
|
384
|
+
'html': 'html', 'md': 'markdown'
|
|
385
|
+
};
|
|
386
|
+
var parserName = formatMap[ext];
|
|
387
|
+
|
|
388
|
+
if (parserName && window.prettier && window.prettierPlugins) {
|
|
389
|
+
window.prettier.format(tab.content, {
|
|
390
|
+
parser: parserName,
|
|
391
|
+
plugins: Object.values(window.prettierPlugins),
|
|
392
|
+
tabWidth: 4,
|
|
393
|
+
useTabs: false
|
|
394
|
+
}).then(function () {}).then(function () {
|
|
395
|
+
var currentPane = EditorStore.getState().panes.find(function (p) {
|
|
396
|
+
return p.id === paneId;
|
|
397
|
+
});
|
|
398
|
+
var current = currentPane ? currentPane.tabs.find(function (t) {
|
|
399
|
+
return t.id === tab.id;
|
|
400
|
+
}) : null;
|
|
401
|
+
if (current) current.markers = [];
|
|
402
|
+
setMarkers(function (prev) {
|
|
403
|
+
return _extends({}, prev, _defineProperty({}, tab.id, []));
|
|
404
|
+
});
|
|
405
|
+
})["catch"](function (err) {
|
|
406
|
+
var newMarkers = [];
|
|
407
|
+
if (err && err.loc && err.loc.start) {
|
|
408
|
+
newMarkers.push({
|
|
409
|
+
severity: "error",
|
|
410
|
+
message: err.message.split("\n")[0] || "Syntax error",
|
|
411
|
+
startLine: err.loc.start.line,
|
|
412
|
+
startCol: err.loc.start.column,
|
|
413
|
+
endLine: err.loc.end ? err.loc.end.line : err.loc.start.line,
|
|
414
|
+
endCol: err.loc.end ? err.loc.end.column : err.loc.start.column + 1
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
var currentPane = EditorStore.getState().panes.find(function (p) {
|
|
418
|
+
return p.id === paneId;
|
|
419
|
+
});
|
|
420
|
+
var current = currentPane ? currentPane.tabs.find(function (t) {
|
|
421
|
+
return t.id === tab.id;
|
|
422
|
+
}) : null;
|
|
423
|
+
if (current) current.markers = newMarkers;
|
|
424
|
+
setMarkers(function (prev) {
|
|
425
|
+
return _extends({}, prev, _defineProperty({}, tab.id, newMarkers));
|
|
426
|
+
});
|
|
427
|
+
});
|
|
428
|
+
}
|
|
429
|
+
}, 600)).current;
|
|
430
|
+
|
|
431
|
+
var setQuickOpen = function setQuickOpen(visible) {
|
|
432
|
+
EditorStore.setState({ isQuickOpenVisible: visible });
|
|
433
|
+
};
|
|
434
|
+
|
|
435
|
+
// Persist state when openTabs or activeTabId changes
|
|
436
|
+
useEffect(function () {
|
|
437
|
+
// Subscribe to EditorStore
|
|
438
|
+
var unsubscribe = EditorStore.subscribe(setState);
|
|
439
|
+
|
|
440
|
+
// Initial load
|
|
441
|
+
Promise.all([FileService.getWorkspace()["catch"](function () {
|
|
442
|
+
return null;
|
|
443
|
+
}), refreshProjectTree()]).then(function (_ref) {
|
|
444
|
+
var _ref2 = _slicedToArray(_ref, 1);
|
|
445
|
+
|
|
446
|
+
var workspace = _ref2[0];
|
|
447
|
+
|
|
448
|
+
if (workspace && workspace.rootName) {
|
|
449
|
+
setProjectRootName(workspace.rootName);
|
|
450
|
+
}
|
|
451
|
+
if (workspace && typeof workspace.rubocopAvailable === 'boolean') {
|
|
452
|
+
setRubocopAvailable(workspace.rubocopAvailable);
|
|
453
|
+
}
|
|
454
|
+
if (workspace && typeof workspace.hamlLintAvailable === 'boolean') {
|
|
455
|
+
setHamlLintAvailable(workspace.hamlLintAvailable);
|
|
456
|
+
}
|
|
457
|
+
if (workspace && typeof workspace.gitAvailable === 'boolean') {
|
|
458
|
+
setGitAvailable(workspace.gitAvailable);
|
|
459
|
+
}
|
|
460
|
+
});
|
|
461
|
+
GitService.fetchStatus();
|
|
462
|
+
|
|
463
|
+
// Load persisted state
|
|
464
|
+
FileService.getState().then(function (savedState) {
|
|
465
|
+
var panesToLoad = savedState && savedState.panes;
|
|
466
|
+
if (savedState && savedState.openTabs) {
|
|
467
|
+
panesToLoad = [{ id: 1, tabs: savedState.openTabs, activeTabId: savedState.activeTabId }, { id: 2, tabs: [], activeTabId: null }];
|
|
468
|
+
}
|
|
469
|
+
if (panesToLoad && panesToLoad.length > 0) {
|
|
470
|
+
var allTabs = panesToLoad.flatMap(function (p) {
|
|
471
|
+
return p.tabs;
|
|
472
|
+
});
|
|
473
|
+
Promise.all(allTabs.map(function (t) {
|
|
474
|
+
if (t.isDiff && t.repoPath) {
|
|
475
|
+
return GitService.fetchDiff(t.repoPath, t.diffBaseSha, t.diffHeadSha)
|
|
476
|
+
.then(function (d) { return { content: 'Diff loaded', diffOriginal: d.original || '', diffModified: d.modified || '', _isDiffResult: true }; })
|
|
477
|
+
["catch"](function () { return { content: '', diffOriginal: '', diffModified: '', _isDiffResult: true }; });
|
|
478
|
+
}
|
|
479
|
+
if (t.isCombinedDiff || (t.path || '').startsWith('combined-diff://') || (t.path || '').startsWith('diff://')) {
|
|
480
|
+
return Promise.resolve({ content: '' });
|
|
481
|
+
}
|
|
482
|
+
var sourcePath = t.isPreview || /::preview$/.test(t.path || '') ? t.previewFor || (t.path || '').replace(/::preview$/, '') : t.path;
|
|
483
|
+
return FileService.getFile(sourcePath)["catch"](function () {
|
|
484
|
+
return { content: '' };
|
|
485
|
+
});
|
|
486
|
+
})).then(function (results) {
|
|
487
|
+
var resIdx = 0;
|
|
488
|
+
var restoredPanes = panesToLoad.map(function (p) {
|
|
489
|
+
return _extends({}, p, {
|
|
490
|
+
tabs: p.tabs.map(function (t) {
|
|
491
|
+
var res = results[resIdx++];
|
|
492
|
+
return _extends({}, t, { content: res.content }, res._isDiffResult ? { diffOriginal: res.diffOriginal, diffModified: res.diffModified } : {});
|
|
493
|
+
})
|
|
494
|
+
});
|
|
495
|
+
});
|
|
496
|
+
EditorStore.setState(_extends({}, savedState, { panes: restoredPanes, openTabs: undefined }));
|
|
497
|
+
// Restore collapsedSections UI state
|
|
498
|
+
if (savedState.collapsedSections) {
|
|
499
|
+
setCollapsedSections(savedState.collapsedSections);
|
|
500
|
+
}
|
|
501
|
+
if (savedState.expandedDirs) {
|
|
502
|
+
setExpandedDirs(savedState.expandedDirs);
|
|
503
|
+
}
|
|
504
|
+
if (typeof savedState.showGitPanel === 'boolean') {
|
|
505
|
+
setShowGitPanel(savedState.showGitPanel);
|
|
506
|
+
if (savedState.showGitPanel) {
|
|
507
|
+
GitService.fetchInfo();
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
if (typeof savedState.gitPanelWidth === 'number') {
|
|
511
|
+
setGitPanelWidth(savedState.gitPanelWidth);
|
|
512
|
+
}
|
|
513
|
+
});
|
|
514
|
+
}
|
|
515
|
+
});
|
|
516
|
+
|
|
517
|
+
// Hotkeys setup
|
|
518
|
+
var onKeyDown = function onKeyDown(e) {
|
|
519
|
+
if ((e.ctrlKey || e.metaKey) && e.key === 'p') {
|
|
520
|
+
e.preventDefault();
|
|
521
|
+
setQuickOpen(true);
|
|
522
|
+
}
|
|
523
|
+
if ((e.ctrlKey || e.metaKey) && e.key === 's') {
|
|
524
|
+
(function () {
|
|
525
|
+
e.preventDefault();
|
|
526
|
+
var st = EditorStore.getState();
|
|
527
|
+
var focusedPane = st.panes.find(function (p) {
|
|
528
|
+
return p.id === st.focusedPaneId;
|
|
529
|
+
}) || st.panes[0];
|
|
530
|
+
if (focusedPane && focusedPane.activeTabId) {
|
|
531
|
+
var tab = focusedPane.tabs.find(function (t) {
|
|
532
|
+
return t.id === focusedPane.activeTabId;
|
|
533
|
+
});
|
|
534
|
+
if (tab && tab.dirty) handleSave(focusedPane.id, tab);
|
|
535
|
+
}
|
|
536
|
+
})();
|
|
537
|
+
}
|
|
538
|
+
if (e.key === 'Escape') {
|
|
539
|
+
setContextMenu(null);
|
|
540
|
+
setShowHelp(false);
|
|
541
|
+
}
|
|
542
|
+
};
|
|
543
|
+
|
|
544
|
+
var handleMouseMove = function handleMouseMove(e) {
|
|
545
|
+
var session = resizeSessionRef.current;
|
|
546
|
+
if (!session) return;
|
|
547
|
+
|
|
548
|
+
// Throttle via rAF — skip if a frame is already queued to avoid paint thrashing
|
|
549
|
+
if (resizeRafRef.current) return;
|
|
550
|
+
var clientX = e.clientX;
|
|
551
|
+
resizeRafRef.current = requestAnimationFrame(function () {
|
|
552
|
+
resizeRafRef.current = null;
|
|
553
|
+
var s = resizeSessionRef.current;
|
|
554
|
+
if (!s) return;
|
|
555
|
+
|
|
556
|
+
if (s.mode === 'pane') {
|
|
557
|
+
var container = document.getElementById('ide-main-split-container');
|
|
558
|
+
if (!container) return;
|
|
559
|
+
|
|
560
|
+
var rect = container.getBoundingClientRect();
|
|
561
|
+
var nextWidth = (clientX - rect.left) / rect.width * 100;
|
|
562
|
+
setPane1Width(clamp(nextWidth, PANE_MIN_WIDTH_PERCENT, PANE_MAX_WIDTH_PERCENT));
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
if (s.mode === 'sidebar') {
|
|
566
|
+
var body = document.getElementById('ide-body-container');
|
|
567
|
+
if (!body) return;
|
|
568
|
+
|
|
569
|
+
var rect = body.getBoundingClientRect();
|
|
570
|
+
var reservedRight = EDITOR_MIN_WIDTH + (showGitPanel ? gitPanelWidth : 0);
|
|
571
|
+
var maxSidebarWidth = Math.min(SIDEBAR_MAX_WIDTH, Math.max(SIDEBAR_MIN_WIDTH, rect.width - reservedRight));
|
|
572
|
+
var nextWidth = clientX - rect.left;
|
|
573
|
+
setSidebarWidth(clamp(nextWidth, SIDEBAR_MIN_WIDTH, maxSidebarWidth));
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
if (s.mode === 'gitpanel') {
|
|
577
|
+
var body = document.getElementById('ide-body-container');
|
|
578
|
+
if (!body) return;
|
|
579
|
+
|
|
580
|
+
var rect = body.getBoundingClientRect();
|
|
581
|
+
var nextWidth = rect.right - clientX;
|
|
582
|
+
setGitPanelWidth(clamp(nextWidth, GIT_PANEL_MIN_WIDTH, 600));
|
|
583
|
+
}
|
|
584
|
+
});
|
|
585
|
+
};
|
|
586
|
+
|
|
587
|
+
var handleMouseUp = function handleMouseUp() {
|
|
588
|
+
if (!resizeSessionRef.current) return;
|
|
589
|
+
|
|
590
|
+
if (resizeRafRef.current) {
|
|
591
|
+
cancelAnimationFrame(resizeRafRef.current);
|
|
592
|
+
resizeRafRef.current = null;
|
|
593
|
+
}
|
|
594
|
+
resizeSessionRef.current = null;
|
|
595
|
+
setActiveResizeMode(null);
|
|
596
|
+
document.body.style.cursor = '';
|
|
597
|
+
document.body.style.userSelect = '';
|
|
598
|
+
};
|
|
599
|
+
|
|
600
|
+
window.addEventListener('keydown', onKeyDown);
|
|
601
|
+
window.addEventListener('mousemove', handleMouseMove);
|
|
602
|
+
window.addEventListener('mouseup', handleMouseUp);
|
|
603
|
+
return function () {
|
|
604
|
+
unsubscribe();
|
|
605
|
+
if (resizeRafRef.current) {
|
|
606
|
+
cancelAnimationFrame(resizeRafRef.current);
|
|
607
|
+
resizeRafRef.current = null;
|
|
608
|
+
}
|
|
609
|
+
window.removeEventListener('keydown', onKeyDown);
|
|
610
|
+
window.removeEventListener('mousemove', handleMouseMove);
|
|
611
|
+
window.removeEventListener('mouseup', handleMouseUp);
|
|
612
|
+
document.body.style.cursor = '';
|
|
613
|
+
document.body.style.userSelect = '';
|
|
614
|
+
};
|
|
615
|
+
}, []);
|
|
616
|
+
|
|
617
|
+
// Heartbeat — poll /ping every 5s and reflect connectivity in the status bar
|
|
618
|
+
useEffect(function () {
|
|
619
|
+
var wasOnline = true;
|
|
620
|
+
var interval = setInterval(function () {
|
|
621
|
+
FileService.ping().then(function () {
|
|
622
|
+
if (!wasOnline) {
|
|
623
|
+
wasOnline = true;
|
|
624
|
+
setServerOnline(true);
|
|
625
|
+
}
|
|
626
|
+
}).catch(function () {
|
|
627
|
+
if (wasOnline) {
|
|
628
|
+
wasOnline = false;
|
|
629
|
+
setServerOnline(false);
|
|
630
|
+
}
|
|
631
|
+
});
|
|
632
|
+
}, 5000);
|
|
633
|
+
return function () { clearInterval(interval); };
|
|
634
|
+
}, []);
|
|
635
|
+
|
|
636
|
+
var handleSelectFile = function handleSelectFile(path, name, line) {
|
|
637
|
+
TabManager.openTab(path, name, line);
|
|
638
|
+
setSelectedTreeNode({ path: path, name: name || path.split('/').pop(), type: 'file' });
|
|
639
|
+
setQuickOpen(false);
|
|
640
|
+
};
|
|
641
|
+
|
|
642
|
+
// Single-click in explorer: soft (preview) open — replaces any existing soft tab
|
|
643
|
+
var handleSoftOpenFile = function handleSoftOpenFile(path, name) {
|
|
644
|
+
TabManager.openTab(path, name, null, null, true);
|
|
645
|
+
setSelectedTreeNode({ path: path, name: name || path.split('/').pop(), type: 'file' });
|
|
646
|
+
};
|
|
647
|
+
|
|
648
|
+
// Double-click in explorer or on tab: harden the tab (remove italic/preview)
|
|
649
|
+
var handleHardOpenFile = function handleHardOpenFile(path, name) {
|
|
650
|
+
var st = EditorStore.getState();
|
|
651
|
+
var targetPane = st.panes.find(function (p) {
|
|
652
|
+
return p.tabs.some(function (t) {
|
|
653
|
+
return t.path === path;
|
|
654
|
+
});
|
|
655
|
+
});
|
|
656
|
+
if (targetPane) {
|
|
657
|
+
TabManager.hardenTab(targetPane.id, path);
|
|
658
|
+
TabManager.switchTab(targetPane.id, path);
|
|
659
|
+
} else {
|
|
660
|
+
TabManager.openTab(path, name, null, null, false);
|
|
661
|
+
}
|
|
662
|
+
setSelectedTreeNode({ path: path, name: name || path.split('/').pop(), type: 'file' });
|
|
663
|
+
};
|
|
664
|
+
|
|
665
|
+
var requestCloseTab = function requestCloseTab(paneId, id) {
|
|
666
|
+
var pane = state.panes.find(function (p) {
|
|
667
|
+
return p.id === paneId;
|
|
668
|
+
}) || state.panes[0];
|
|
669
|
+
var tab = pane.tabs.find(function (t) {
|
|
670
|
+
return t.id === id;
|
|
671
|
+
});
|
|
672
|
+
if (tab && tab.dirty) {
|
|
673
|
+
setClosingPaneId(paneId);
|
|
674
|
+
setClosingTabId(id);
|
|
675
|
+
} else {
|
|
676
|
+
TabManager.closeTab(paneId, id);
|
|
677
|
+
}
|
|
678
|
+
};
|
|
679
|
+
|
|
680
|
+
var confirmCloseTab = function confirmCloseTab(save) {
|
|
681
|
+
var pane = state.panes.find(function (p) {
|
|
682
|
+
return p.id === closingPaneId;
|
|
683
|
+
});
|
|
684
|
+
var tab = pane ? pane.tabs.find(function (t) {
|
|
685
|
+
return t.id === closingTabId;
|
|
686
|
+
}) : null;
|
|
687
|
+
if (!tab) {
|
|
688
|
+
setClosingTabId(null);
|
|
689
|
+
setClosingPaneId(null);
|
|
690
|
+
return;
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
if (save) {
|
|
694
|
+
setLoading(function (prev) {
|
|
695
|
+
return _extends({}, prev, { save: true });
|
|
696
|
+
});
|
|
697
|
+
EditorStore.setStatus("Saving " + tab.name + "...", "info");
|
|
698
|
+
FileService.saveFile(tab.path, tab.content).then(function () {
|
|
699
|
+
EditorStore.setStatus("Saved", "success");
|
|
700
|
+
GitService.fetchStatus();
|
|
701
|
+
TabManager.closeTab(closingPaneId, tab.id);
|
|
702
|
+
})["catch"](function (err) {
|
|
703
|
+
EditorStore.setStatus("Save failed: " + err.message, "error");
|
|
704
|
+
})["finally"](function () {
|
|
705
|
+
setLoading(function (prev) {
|
|
706
|
+
return _extends({}, prev, { save: false });
|
|
707
|
+
});
|
|
708
|
+
setClosingTabId(null);
|
|
709
|
+
setClosingPaneId(null);
|
|
710
|
+
});
|
|
711
|
+
} else {
|
|
712
|
+
TabManager.closeTab(closingPaneId, tab.id);
|
|
713
|
+
setClosingTabId(null);
|
|
714
|
+
setClosingPaneId(null);
|
|
715
|
+
}
|
|
716
|
+
};
|
|
717
|
+
|
|
718
|
+
var confirmBulkClose = function confirmBulkClose(tabs, scopeLabel) {
|
|
719
|
+
var dirtyCount = tabs.filter(function (tab) {
|
|
720
|
+
return tab.dirty;
|
|
721
|
+
}).length;
|
|
722
|
+
|
|
723
|
+
if (dirtyCount === 0) return true;
|
|
724
|
+
|
|
725
|
+
var dirtyLabel = dirtyCount === 1 ? "1 unsaved editor has changes." : dirtyCount + " unsaved editors have changes.";
|
|
726
|
+
return window.confirm(dirtyLabel + " Close " + scopeLabel + " without saving?");
|
|
727
|
+
};
|
|
728
|
+
|
|
729
|
+
var handleCloseAllEditors = function handleCloseAllEditors() {
|
|
730
|
+
var allTabs = state.panes.flatMap(function (pane) {
|
|
731
|
+
return pane.tabs;
|
|
732
|
+
});
|
|
733
|
+
if (allTabs.length === 0) return;
|
|
734
|
+
if (!confirmBulkClose(allTabs, "all editors")) return;
|
|
735
|
+
|
|
736
|
+
TabManager.closeAllTabs();
|
|
737
|
+
setClosingTabId(null);
|
|
738
|
+
setClosingPaneId(null);
|
|
739
|
+
EditorStore.setStatus("Closed " + allTabs.length + " editor" + (allTabs.length === 1 ? "" : "s"), "info");
|
|
740
|
+
};
|
|
741
|
+
|
|
742
|
+
var handleCloseEditorsInGroup = function handleCloseEditorsInGroup(paneId) {
|
|
743
|
+
var pane = state.panes.find(function (p) {
|
|
744
|
+
return p.id === paneId;
|
|
745
|
+
});
|
|
746
|
+
if (!pane || pane.tabs.length === 0) return;
|
|
747
|
+
if (!confirmBulkClose(pane.tabs, "all editors in Group " + paneId)) return;
|
|
748
|
+
|
|
749
|
+
TabManager.closeAllTabsInPane(paneId);
|
|
750
|
+
setClosingTabId(null);
|
|
751
|
+
setClosingPaneId(null);
|
|
752
|
+
EditorStore.setStatus("Closed " + pane.tabs.length + " editor" + (pane.tabs.length === 1 ? "" : "s") + " in Group " + paneId, "info");
|
|
753
|
+
};
|
|
754
|
+
|
|
755
|
+
// Persist state when panes, focusedPaneId, or collapsedSections changes
|
|
756
|
+
useEffect(function () {
|
|
757
|
+
// debounce explicitly using setTimeout to avoid spamming the backend
|
|
758
|
+
var timeoutId = setTimeout(function () {
|
|
759
|
+
var st = EditorStore.getState();
|
|
760
|
+
var lightweightPanes = st.panes.map(function (p) {
|
|
761
|
+
return {
|
|
762
|
+
id: p.id,
|
|
763
|
+
activeTabId: p.activeTabId,
|
|
764
|
+
tabs: p.tabs.filter(function(t) { return !t.isCombinedDiff; }).map(function (t) {
|
|
765
|
+
return {
|
|
766
|
+
id: t.id,
|
|
767
|
+
path: t.path,
|
|
768
|
+
name: t.name,
|
|
769
|
+
dirty: t.dirty,
|
|
770
|
+
viewState: t.viewState,
|
|
771
|
+
isPreview: !!t.isPreview,
|
|
772
|
+
previewFor: t.previewFor || null,
|
|
773
|
+
isDiff: !!t.isDiff,
|
|
774
|
+
diffBaseSha: t.diffBaseSha || null,
|
|
775
|
+
diffHeadSha: t.diffHeadSha || null,
|
|
776
|
+
repoPath: t.repoPath || null
|
|
777
|
+
};
|
|
778
|
+
})
|
|
779
|
+
};
|
|
780
|
+
});
|
|
781
|
+
FileService.saveState({ panes: lightweightPanes, focusedPaneId: st.focusedPaneId, collapsedSections: collapsedSections, expandedDirs: expandedDirs, showGitPanel: showGitPanel, gitPanelWidth: gitPanelWidth });
|
|
782
|
+
}, 1000);
|
|
783
|
+
return function () {
|
|
784
|
+
return clearTimeout(timeoutId);
|
|
785
|
+
};
|
|
786
|
+
}, [state.panes, state.focusedPaneId, collapsedSections, expandedDirs, showGitPanel, gitPanelWidth]);
|
|
787
|
+
|
|
788
|
+
var focusedPane = state.panes.find(function (p) {
|
|
789
|
+
return p.id === state.focusedPaneId;
|
|
790
|
+
}) || state.panes[0];
|
|
791
|
+
var activeTab = focusedPane.tabs.find(function (t) {
|
|
792
|
+
return t.id === focusedPane.activeTabId;
|
|
793
|
+
});
|
|
794
|
+
|
|
795
|
+
// Phase 7: Per-file last-commit info shown in the status bar
|
|
796
|
+
var _useState31 = useState(null);
|
|
797
|
+
var _useState32 = _slicedToArray(_useState31, 2);
|
|
798
|
+
var activeFileCommit = _useState32[0];
|
|
799
|
+
var setActiveFileCommit = _useState32[1];
|
|
800
|
+
|
|
801
|
+
useEffect(function () {
|
|
802
|
+
if (!gitAvailable || !activeTab || activeTab.isDiff || activeTab.isCombinedDiff || activeTab.isCommitGraph || !activeTab.path || activeTab.path.indexOf('diff://') === 0 || activeTab.path.indexOf('combined-diff://') === 0) {
|
|
803
|
+
setActiveFileCommit(null);
|
|
804
|
+
return;
|
|
805
|
+
}
|
|
806
|
+
var currentPath = activeTab.path;
|
|
807
|
+
GitService.fetchFileHistory(currentPath).then(function(data) {
|
|
808
|
+
var first = data && data.history && data.history[0];
|
|
809
|
+
if (first) {
|
|
810
|
+
setActiveFileCommit({ hash: first.hash, title: first.title, author: first.author, date: first.date });
|
|
811
|
+
} else {
|
|
812
|
+
setActiveFileCommit(null);
|
|
813
|
+
}
|
|
814
|
+
}).catch(function() {
|
|
815
|
+
setActiveFileCommit(null);
|
|
816
|
+
});
|
|
817
|
+
}, [activeTab ? activeTab.id : null, gitAvailable]);
|
|
818
|
+
|
|
819
|
+
useEffect(function () {
|
|
820
|
+
if (!activeTab || typeof activeTab.content !== 'string') return;
|
|
821
|
+
if (activeTab.isDiff || activeTab.isCombinedDiff || activeTab.isCommitGraph) return;
|
|
822
|
+
if (isRubyPath(activeTab.path) && !rubocopAvailable) return;
|
|
823
|
+
if (activeTab.path.endsWith('.haml') && !hamlLintAvailable) return;
|
|
824
|
+
|
|
825
|
+
_debouncedAutoLint(activeTab, focusedPane.id);
|
|
826
|
+
|
|
827
|
+
return function () {
|
|
828
|
+
_debouncedAutoLint.cancel();
|
|
829
|
+
};
|
|
830
|
+
}, [focusedPane.id, activeTab ? activeTab.id : null, activeTab ? activeTab.content : null, rubocopAvailable, hamlLintAvailable]);
|
|
831
|
+
|
|
832
|
+
var handleOpenCommitGraph = function handleOpenCommitGraph() {
|
|
833
|
+
var paneId = state.focusedPaneId || 1;
|
|
834
|
+
var tabId = 'mbeditor://commit-graph';
|
|
835
|
+
|
|
836
|
+
var pane = state.panes.find(function(p) { return p.id === paneId; });
|
|
837
|
+
var existing = pane && pane.tabs.find(function(t) { return t.id === tabId; });
|
|
838
|
+
if (existing) {
|
|
839
|
+
TabManager.switchTab(paneId, tabId);
|
|
840
|
+
return;
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
var newTab = {
|
|
844
|
+
id: tabId,
|
|
845
|
+
path: tabId,
|
|
846
|
+
name: 'Commit Graph',
|
|
847
|
+
dirty: false,
|
|
848
|
+
content: '', // not used
|
|
849
|
+
isCommitGraph: true
|
|
850
|
+
};
|
|
851
|
+
|
|
852
|
+
var newPanes = state.panes.map(function(p) {
|
|
853
|
+
if (p.id === paneId) {
|
|
854
|
+
return Object.assign({}, p, { tabs: p.tabs.concat(newTab), activeTabId: tabId });
|
|
855
|
+
}
|
|
856
|
+
return p;
|
|
857
|
+
});
|
|
858
|
+
|
|
859
|
+
EditorStore.setState({ panes: newPanes, focusedPaneId: paneId, activeTabId: tabId });
|
|
860
|
+
|
|
861
|
+
// Fetch data asynchronously
|
|
862
|
+
GitService.fetchCommitGraph().then(function(data) {
|
|
863
|
+
var s = EditorStore.getState();
|
|
864
|
+
var p = s.panes.find(function(p) { return p.id === paneId; });
|
|
865
|
+
if (!p) return;
|
|
866
|
+
var t = p.tabs.find(function(t) { return t.id === tabId; });
|
|
867
|
+
if (t) {
|
|
868
|
+
var newPanes2 = s.panes.map(function(p2) {
|
|
869
|
+
if (p2.id === paneId) {
|
|
870
|
+
var newTabs = p2.tabs.map(function(t2) {
|
|
871
|
+
return t2.id === tabId ? Object.assign({}, t2, { commits: data.commits }) : t2;
|
|
872
|
+
});
|
|
873
|
+
return Object.assign({}, p2, { tabs: newTabs });
|
|
874
|
+
}
|
|
875
|
+
return p2;
|
|
876
|
+
});
|
|
877
|
+
EditorStore.setState({ panes: newPanes2 });
|
|
878
|
+
}
|
|
879
|
+
});
|
|
880
|
+
};
|
|
881
|
+
|
|
882
|
+
var handleSave = function handleSave(paneId, tab) {
|
|
883
|
+
setLoading(function (prev) {
|
|
884
|
+
return _extends({}, prev, { save: true });
|
|
885
|
+
});
|
|
886
|
+
EditorStore.setStatus("Saving " + tab.name + "...", "info");
|
|
887
|
+
FileService.saveFile(tab.path, tab.content).then(function () {
|
|
888
|
+
var newPanes = EditorStore.getState().panes.map(function (p) {
|
|
889
|
+
if (p.id === paneId) {
|
|
890
|
+
return _extends({}, p, { tabs: p.tabs.map(function (t) {
|
|
891
|
+
return t.id === tab.id ? _extends({}, t, { dirty: false, cleanContent: tab.content }) : t;
|
|
892
|
+
}) });
|
|
893
|
+
}
|
|
894
|
+
return p;
|
|
895
|
+
});
|
|
896
|
+
EditorStore.setState({ panes: newPanes });
|
|
897
|
+
EditorStore.setStatus("Saved", "success");
|
|
898
|
+
GitService.fetchStatus();
|
|
899
|
+
})["catch"](function (err) {
|
|
900
|
+
EditorStore.setStatus("Save failed: " + err.message, "error");
|
|
901
|
+
})["finally"](function () {
|
|
902
|
+
return setLoading(function (prev) {
|
|
903
|
+
return _extends({}, prev, { save: false });
|
|
904
|
+
});
|
|
905
|
+
});
|
|
906
|
+
};
|
|
907
|
+
|
|
908
|
+
var handleSaveAll = function handleSaveAll() {
|
|
909
|
+
var dirtyTabs = state.panes.flatMap(function (p) {
|
|
910
|
+
return p.tabs;
|
|
911
|
+
}).filter(function (t) {
|
|
912
|
+
return t.dirty;
|
|
913
|
+
});
|
|
914
|
+
if (dirtyTabs.length === 0) return;
|
|
915
|
+
|
|
916
|
+
setLoading(function (prev) {
|
|
917
|
+
return _extends({}, prev, { saveAll: true });
|
|
918
|
+
});
|
|
919
|
+
EditorStore.setStatus("Saving " + dirtyTabs.length + " files...", "info");
|
|
920
|
+
var promises = dirtyTabs.map(function (tab) {
|
|
921
|
+
return FileService.saveFile(tab.path, tab.content);
|
|
922
|
+
});
|
|
923
|
+
Promise.all(promises).then(function () {
|
|
924
|
+
var newPanes = EditorStore.getState().panes.map(function (p) {
|
|
925
|
+
return _extends({}, p, { tabs: p.tabs.map(function (t) {
|
|
926
|
+
return _extends({}, t, { dirty: false, cleanContent: t.content });
|
|
927
|
+
})
|
|
928
|
+
});
|
|
929
|
+
});
|
|
930
|
+
EditorStore.setState({ panes: newPanes });
|
|
931
|
+
EditorStore.setStatus("All files saved", "success");
|
|
932
|
+
GitService.fetchStatus();
|
|
933
|
+
})["catch"](function (err) {
|
|
934
|
+
EditorStore.setStatus("Failed to save some files", "error");
|
|
935
|
+
})["finally"](function () {
|
|
936
|
+
return setLoading(function (prev) {
|
|
937
|
+
return _extends({}, prev, { saveAll: false });
|
|
938
|
+
});
|
|
939
|
+
});
|
|
940
|
+
};
|
|
941
|
+
|
|
942
|
+
var handleTabDragStart = function handleTabDragStart(sourcePaneId, tabId) {
|
|
943
|
+
var pane2 = EditorStore.getState().panes.find(function (p) {
|
|
944
|
+
return p.id === 2;
|
|
945
|
+
});
|
|
946
|
+
if (!pane2 || pane2.tabs.length === 0) {
|
|
947
|
+
setPane1Width(50);
|
|
948
|
+
}
|
|
949
|
+
setDraggedTab({ sourcePaneId: sourcePaneId, tabId: tabId });
|
|
950
|
+
};
|
|
951
|
+
|
|
952
|
+
var clearDragState = function clearDragState() {
|
|
953
|
+
setDraggedTab(null);
|
|
954
|
+
setDragOverPaneId(null);
|
|
955
|
+
};
|
|
956
|
+
|
|
957
|
+
var moveDraggedTabToPane = function moveDraggedTabToPane(targetPaneId) {
|
|
958
|
+
if (!draggedTab) return;
|
|
959
|
+
TabManager.moveTabToPane(draggedTab.sourcePaneId, targetPaneId, draggedTab.tabId);
|
|
960
|
+
clearDragState();
|
|
961
|
+
};
|
|
962
|
+
|
|
963
|
+
var handleFormat = function handleFormat() {
|
|
964
|
+
if (!activeTab) return;
|
|
965
|
+
|
|
966
|
+
var isRubyLang = activeTab.path.endsWith('.rb') || activeTab.path.endsWith('.gemspec') || activeTab.path.endsWith("Rakefile") || activeTab.path.endsWith("Gemfile");
|
|
967
|
+
|
|
968
|
+
if (isRubyLang && !rubocopAvailable) {
|
|
969
|
+
EditorStore.setStatus("RuboCop is not available for this workspace.", "warning");
|
|
970
|
+
return;
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
if (activeTab.dirty) handleSave(focusedPane.id, activeTab); // save first
|
|
974
|
+
|
|
975
|
+
if (isRubyLang) {
|
|
976
|
+
setLoading(function (prev) {
|
|
977
|
+
return _extends({}, prev, { format: true });
|
|
978
|
+
});
|
|
979
|
+
EditorStore.setStatus("Formatting...", "info");
|
|
980
|
+
FileService.formatFile(activeTab.path).then(function (res) {
|
|
981
|
+
if (res.content) {
|
|
982
|
+
// Update content without marking dirty
|
|
983
|
+
var newPanes = EditorStore.getState().panes.map(function (p) {
|
|
984
|
+
if (p.id === focusedPane.id) return _extends({}, p, { tabs: p.tabs.map(function (t) {
|
|
985
|
+
return t.id === activeTab.id ? _extends({}, t, { content: res.content, dirty: false }) : t;
|
|
986
|
+
}) });
|
|
987
|
+
return p;
|
|
988
|
+
});
|
|
989
|
+
EditorStore.setState({ panes: newPanes });
|
|
990
|
+
}
|
|
991
|
+
EditorStore.setStatus("Formatted successfully", "success");
|
|
992
|
+
setMarkers(function (prev) {
|
|
993
|
+
return _extends({}, prev, _defineProperty({}, activeTab.id, []));
|
|
994
|
+
}); // clear lint markers
|
|
995
|
+
GitService.fetchStatus();
|
|
996
|
+
})["catch"](function (err) {
|
|
997
|
+
return EditorStore.setStatus("Format failed: " + err.message, "error");
|
|
998
|
+
})["finally"](function () {
|
|
999
|
+
return setLoading(function (prev) {
|
|
1000
|
+
return _extends({}, prev, { format: false });
|
|
1001
|
+
});
|
|
1002
|
+
});
|
|
1003
|
+
return;
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
// Attempt Prettier Formatting
|
|
1007
|
+
var ext = activeTab.path.split('.').pop().toLowerCase();
|
|
1008
|
+
var formatMap = {
|
|
1009
|
+
'js': 'babel', 'jsx': 'babel',
|
|
1010
|
+
'json': 'json',
|
|
1011
|
+
'css': 'css', 'scss': 'scss',
|
|
1012
|
+
'html': 'html', 'md': 'markdown'
|
|
1013
|
+
};
|
|
1014
|
+
var parserName = formatMap[ext];
|
|
1015
|
+
|
|
1016
|
+
if (parserName && window.prettier && window.prettierPlugins) {
|
|
1017
|
+
setLoading(function (prev) {
|
|
1018
|
+
return _extends({}, prev, { format: true });
|
|
1019
|
+
});
|
|
1020
|
+
EditorStore.setStatus("Formatting with Prettier...", "info");
|
|
1021
|
+
window.prettier.format(activeTab.content, {
|
|
1022
|
+
parser: parserName,
|
|
1023
|
+
plugins: Object.values(window.prettierPlugins),
|
|
1024
|
+
tabWidth: 4,
|
|
1025
|
+
useTabs: false
|
|
1026
|
+
}).then(function (formatted) {
|
|
1027
|
+
var newPanes = EditorStore.getState().panes.map(function (p) {
|
|
1028
|
+
if (p.id === focusedPane.id) return _extends({}, p, { tabs: p.tabs.map(function (t) {
|
|
1029
|
+
return t.id === activeTab.id ? _extends({}, t, { content: formatted, dirty: true }) : t;
|
|
1030
|
+
}) });
|
|
1031
|
+
return p;
|
|
1032
|
+
});
|
|
1033
|
+
EditorStore.setState({ panes: newPanes });
|
|
1034
|
+
EditorStore.setStatus("Formatted (Unsaved)", "success");
|
|
1035
|
+
GitService.fetchStatus();
|
|
1036
|
+
})["catch"](function (err) {
|
|
1037
|
+
EditorStore.setStatus("Prettier Formatter failed: " + err.message, "error");
|
|
1038
|
+
})["finally"](function () {
|
|
1039
|
+
setLoading(function (prev) {
|
|
1040
|
+
return _extends({}, prev, { format: false });
|
|
1041
|
+
});
|
|
1042
|
+
});
|
|
1043
|
+
}
|
|
1044
|
+
};
|
|
1045
|
+
|
|
1046
|
+
var _debouncedSearch = useRef(window._.debounce(function (q) {
|
|
1047
|
+
if (!q.trim()) {
|
|
1048
|
+
EditorStore.setState({ searchResults: [] });
|
|
1049
|
+
return;
|
|
1050
|
+
}
|
|
1051
|
+
EditorStore.setStatus("Searching project...", "info");
|
|
1052
|
+
SearchService.projectSearch(q).then(function (res) {
|
|
1053
|
+
EditorStore.setStatus("Found " + res.length + " results", "success");
|
|
1054
|
+
});
|
|
1055
|
+
}, 400)).current;
|
|
1056
|
+
|
|
1057
|
+
var handleSearchChange = function handleSearchChange(e) {
|
|
1058
|
+
var val = e.target.value;
|
|
1059
|
+
setSearchQuery(val);
|
|
1060
|
+
_debouncedSearch(val);
|
|
1061
|
+
};
|
|
1062
|
+
|
|
1063
|
+
var execSearch = function execSearch(e) {
|
|
1064
|
+
e.preventDefault();
|
|
1065
|
+
_debouncedSearch(searchQuery);
|
|
1066
|
+
};
|
|
1067
|
+
|
|
1068
|
+
var toggleGitPanel = function toggleGitPanel() {
|
|
1069
|
+
setShowGitPanel(function (prev) {
|
|
1070
|
+
if (!prev) GitService.fetchInfo();
|
|
1071
|
+
return !prev;
|
|
1072
|
+
});
|
|
1073
|
+
};
|
|
1074
|
+
|
|
1075
|
+
var startGitPanelResize = function startGitPanelResize(e) {
|
|
1076
|
+
e.preventDefault();
|
|
1077
|
+
resizeSessionRef.current = { mode: 'gitpanel' };
|
|
1078
|
+
setActiveResizeMode('gitpanel');
|
|
1079
|
+
document.body.style.cursor = 'col-resize';
|
|
1080
|
+
document.body.style.userSelect = 'none';
|
|
1081
|
+
};
|
|
1082
|
+
|
|
1083
|
+
var handleSelectCommit = function handleSelectCommit(commit) {
|
|
1084
|
+
setSelectedCommit(commit);
|
|
1085
|
+
setCommitDetailFiles(null);
|
|
1086
|
+
GitService.fetchCommitDetail(commit.hash).then(function (data) {
|
|
1087
|
+
setCommitDetailFiles(data.files || []);
|
|
1088
|
+
}).catch(function () {
|
|
1089
|
+
setCommitDetailFiles([]);
|
|
1090
|
+
});
|
|
1091
|
+
};
|
|
1092
|
+
|
|
1093
|
+
var handleToggleSection = function handleToggleSection(sectionKey, isCollapsed) {
|
|
1094
|
+
setCollapsedSections(function (prev) {
|
|
1095
|
+
return _extends({}, prev, _defineProperty({}, sectionKey, isCollapsed));
|
|
1096
|
+
});
|
|
1097
|
+
};
|
|
1098
|
+
|
|
1099
|
+
var handleCollapseAll = function handleCollapseAll() {
|
|
1100
|
+
return setExpandedDirs({});
|
|
1101
|
+
};
|
|
1102
|
+
|
|
1103
|
+
var openContextMenu = function openContextMenu(e, node) {
|
|
1104
|
+
setContextMenu({ x: e.clientX, y: e.clientY, node: node });
|
|
1105
|
+
setSelectedTreeNode(node);
|
|
1106
|
+
};
|
|
1107
|
+
|
|
1108
|
+
var closeContextMenu = function closeContextMenu() {
|
|
1109
|
+
return setContextMenu(null);
|
|
1110
|
+
};
|
|
1111
|
+
|
|
1112
|
+
var handleContextMenuAction = function handleContextMenuAction(action) {
|
|
1113
|
+
var node = contextMenu && contextMenu.node;
|
|
1114
|
+
closeContextMenu();
|
|
1115
|
+
if (action === 'open' && node) {
|
|
1116
|
+
handleHardOpenFile(node.path, node.name);return;
|
|
1117
|
+
}
|
|
1118
|
+
if (action === 'newFile') {
|
|
1119
|
+
handleCreateFile(node);return;
|
|
1120
|
+
}
|
|
1121
|
+
if (action === 'newFolder') {
|
|
1122
|
+
handleCreateDir(node);return;
|
|
1123
|
+
}
|
|
1124
|
+
if (action === 'rename') {
|
|
1125
|
+
handleRenamePath(node);return;
|
|
1126
|
+
}
|
|
1127
|
+
if (action === 'delete') {
|
|
1128
|
+
handleDeletePath(node);return;
|
|
1129
|
+
}
|
|
1130
|
+
if (action === 'copyPath' && node) {
|
|
1131
|
+
if (navigator.clipboard) {
|
|
1132
|
+
navigator.clipboard.writeText(node.path)["catch"](function () {});
|
|
1133
|
+
}
|
|
1134
|
+
EditorStore.setStatus('Copied: ' + node.path, 'info');
|
|
1135
|
+
}
|
|
1136
|
+
};
|
|
1137
|
+
|
|
1138
|
+
var startPaneResize = function startPaneResize(e) {
|
|
1139
|
+
e.preventDefault();
|
|
1140
|
+
resizeSessionRef.current = { mode: 'pane' };
|
|
1141
|
+
setActiveResizeMode('pane');
|
|
1142
|
+
document.body.style.cursor = 'col-resize';
|
|
1143
|
+
document.body.style.userSelect = 'none';
|
|
1144
|
+
};
|
|
1145
|
+
|
|
1146
|
+
var startSidebarResize = function startSidebarResize(e) {
|
|
1147
|
+
e.preventDefault();
|
|
1148
|
+
resizeSessionRef.current = { mode: 'sidebar' };
|
|
1149
|
+
setActiveResizeMode('sidebar');
|
|
1150
|
+
document.body.style.cursor = 'col-resize';
|
|
1151
|
+
document.body.style.userSelect = 'none';
|
|
1152
|
+
};
|
|
1153
|
+
|
|
1154
|
+
var openFileFromGitPanel = function openFileFromGitPanel(path, name) {
|
|
1155
|
+
if (!path) return;
|
|
1156
|
+
handleSelectFile(path, name || path.split('/').pop());
|
|
1157
|
+
};
|
|
1158
|
+
|
|
1159
|
+
var pathMatchesNodeOrDescendant = function pathMatchesNodeOrDescendant(value, targetPath) {
|
|
1160
|
+
if (!value || !targetPath) return false;
|
|
1161
|
+
return value === targetPath || value.indexOf(targetPath + '/') === 0 || value.indexOf(targetPath + '::preview') === 0;
|
|
1162
|
+
};
|
|
1163
|
+
|
|
1164
|
+
var rewritePathAfterRename = function rewritePathAfterRename(value, oldPath, newPath) {
|
|
1165
|
+
if (!value || !oldPath || !newPath) return value;
|
|
1166
|
+
if (value === oldPath) return newPath;
|
|
1167
|
+
if (value === oldPath + '::preview') return newPath + '::preview';
|
|
1168
|
+
if (value.indexOf(oldPath + '/') === 0) return newPath + value.slice(oldPath.length);
|
|
1169
|
+
return value;
|
|
1170
|
+
};
|
|
1171
|
+
|
|
1172
|
+
var applyRenameToOpenTabs = function applyRenameToOpenTabs(oldPath, newPath) {
|
|
1173
|
+
var currentState = EditorStore.getState();
|
|
1174
|
+
var newPanes = currentState.panes.map(function (pane) {
|
|
1175
|
+
var renamedTabs = pane.tabs.map(function (tab) {
|
|
1176
|
+
var nextPath = rewritePathAfterRename(tab.path, oldPath, newPath);
|
|
1177
|
+
var nextPreviewFor = rewritePathAfterRename(tab.previewFor, oldPath, newPath);
|
|
1178
|
+
if (nextPath === tab.path && nextPreviewFor === tab.previewFor) return tab;
|
|
1179
|
+
|
|
1180
|
+
var defaultName = nextPath.split('/').pop();
|
|
1181
|
+
var previewSourceName = nextPreviewFor ? nextPreviewFor.split('/').pop() : defaultName;
|
|
1182
|
+
return _extends({}, tab, {
|
|
1183
|
+
id: nextPath,
|
|
1184
|
+
path: nextPath,
|
|
1185
|
+
name: tab.isPreview ? previewSourceName + '-preview' : defaultName,
|
|
1186
|
+
previewFor: nextPreviewFor
|
|
1187
|
+
});
|
|
1188
|
+
});
|
|
1189
|
+
|
|
1190
|
+
return _extends({}, pane, {
|
|
1191
|
+
tabs: renamedTabs,
|
|
1192
|
+
activeTabId: rewritePathAfterRename(pane.activeTabId, oldPath, newPath)
|
|
1193
|
+
});
|
|
1194
|
+
});
|
|
1195
|
+
|
|
1196
|
+
EditorStore.setState({
|
|
1197
|
+
panes: newPanes,
|
|
1198
|
+
activeTabId: rewritePathAfterRename(currentState.activeTabId, oldPath, newPath)
|
|
1199
|
+
});
|
|
1200
|
+
};
|
|
1201
|
+
|
|
1202
|
+
var removeDeletedPathFromOpenTabs = function removeDeletedPathFromOpenTabs(targetPath) {
|
|
1203
|
+
var currentState = EditorStore.getState();
|
|
1204
|
+
var removedTabIds = [];
|
|
1205
|
+
|
|
1206
|
+
var newPanes = currentState.panes.map(function (pane) {
|
|
1207
|
+
var keptTabs = pane.tabs.filter(function (tab) {
|
|
1208
|
+
var removeTab = pathMatchesNodeOrDescendant(tab.path, targetPath) || pathMatchesNodeOrDescendant(tab.previewFor, targetPath);
|
|
1209
|
+
if (removeTab) {
|
|
1210
|
+
removedTabIds.push(tab.id);
|
|
1211
|
+
}
|
|
1212
|
+
return !removeTab;
|
|
1213
|
+
});
|
|
1214
|
+
|
|
1215
|
+
var nextActiveTabId = pane.activeTabId;
|
|
1216
|
+
var activeStillExists = keptTabs.some(function (tab) {
|
|
1217
|
+
return tab.id === nextActiveTabId;
|
|
1218
|
+
});
|
|
1219
|
+
if (!activeStillExists) {
|
|
1220
|
+
nextActiveTabId = keptTabs.length ? keptTabs[keptTabs.length - 1].id : null;
|
|
1221
|
+
}
|
|
1222
|
+
|
|
1223
|
+
return _extends({}, pane, {
|
|
1224
|
+
tabs: keptTabs,
|
|
1225
|
+
activeTabId: nextActiveTabId
|
|
1226
|
+
});
|
|
1227
|
+
});
|
|
1228
|
+
|
|
1229
|
+
var nextFocusedPaneId = currentState.focusedPaneId;
|
|
1230
|
+
var focusedPane = newPanes.find(function (pane) {
|
|
1231
|
+
return pane.id === nextFocusedPaneId;
|
|
1232
|
+
});
|
|
1233
|
+
if (!focusedPane || focusedPane.tabs.length === 0) {
|
|
1234
|
+
var paneWithTabs = newPanes.find(function (pane) {
|
|
1235
|
+
return pane.tabs.length > 0;
|
|
1236
|
+
});
|
|
1237
|
+
nextFocusedPaneId = paneWithTabs ? paneWithTabs.id : 1;
|
|
1238
|
+
}
|
|
1239
|
+
|
|
1240
|
+
var activePane = newPanes.find(function (pane) {
|
|
1241
|
+
return pane.id === nextFocusedPaneId;
|
|
1242
|
+
});
|
|
1243
|
+
EditorStore.setState({
|
|
1244
|
+
panes: newPanes,
|
|
1245
|
+
focusedPaneId: nextFocusedPaneId,
|
|
1246
|
+
activeTabId: activePane ? activePane.activeTabId : null
|
|
1247
|
+
});
|
|
1248
|
+
|
|
1249
|
+
if (removedTabIds.length) {
|
|
1250
|
+
setMarkers(function (prev) {
|
|
1251
|
+
var next = _extends({}, prev);
|
|
1252
|
+
removedTabIds.forEach(function (tabId) {
|
|
1253
|
+
return delete next[tabId];
|
|
1254
|
+
});
|
|
1255
|
+
return next;
|
|
1256
|
+
});
|
|
1257
|
+
}
|
|
1258
|
+
};
|
|
1259
|
+
|
|
1260
|
+
var handleCreateFile = function handleCreateFile(targetNode) {
|
|
1261
|
+
var node = targetNode !== undefined ? targetNode : selectedTreeNode;
|
|
1262
|
+
var baseDir = node ? node.type === 'folder' ? node.path : parentDir(node.path) : '';
|
|
1263
|
+
// Ensure the target folder is expanded so the inline row is visible
|
|
1264
|
+
if (baseDir) setExpandedDirs(function (prev) {
|
|
1265
|
+
return Object.assign({}, prev, _defineProperty({}, baseDir, true));
|
|
1266
|
+
});
|
|
1267
|
+
setPendingRename(null);
|
|
1268
|
+
setPendingCreate({ type: 'file', parentPath: baseDir });
|
|
1269
|
+
};
|
|
1270
|
+
|
|
1271
|
+
var handleCreateDir = function handleCreateDir(targetNode) {
|
|
1272
|
+
var node = targetNode !== undefined ? targetNode : selectedTreeNode;
|
|
1273
|
+
var baseDir = node ? node.type === 'folder' ? node.path : parentDir(node.path) : '';
|
|
1274
|
+
if (baseDir) setExpandedDirs(function (prev) {
|
|
1275
|
+
return Object.assign({}, prev, _defineProperty({}, baseDir, true));
|
|
1276
|
+
});
|
|
1277
|
+
setPendingRename(null);
|
|
1278
|
+
setPendingCreate({ type: 'folder', parentPath: baseDir });
|
|
1279
|
+
};
|
|
1280
|
+
|
|
1281
|
+
var handleCreateConfirm = function handleCreateConfirm(name) {
|
|
1282
|
+
if (!pendingCreate || !name) return;
|
|
1283
|
+
var type = pendingCreate.type;
|
|
1284
|
+
var parentPath = pendingCreate.parentPath;
|
|
1285
|
+
|
|
1286
|
+
var path = normalizeRelativePath(parentPath ? parentPath + '/' + name : name);
|
|
1287
|
+
setPendingCreate(null);
|
|
1288
|
+
|
|
1289
|
+
if (type === 'file') {
|
|
1290
|
+
setLoading(function (prev) {
|
|
1291
|
+
return _extends({}, prev, { createFile: true });
|
|
1292
|
+
});
|
|
1293
|
+
FileService.createFile(path, '').then(function (res) {
|
|
1294
|
+
var createdPath = res && res.path || path;
|
|
1295
|
+
var createdName = createdPath.split('/').pop();
|
|
1296
|
+
setSelectedTreeNode({ path: createdPath, name: createdName, type: 'file' });
|
|
1297
|
+
EditorStore.setStatus('Created file: ' + createdName, 'success');
|
|
1298
|
+
return refreshProjectTree().then(function () {
|
|
1299
|
+
handleSelectFile(createdPath, createdName);
|
|
1300
|
+
GitService.fetchStatus();
|
|
1301
|
+
});
|
|
1302
|
+
})["catch"](function (err) {
|
|
1303
|
+
var message = err && err.response && err.response.data && err.response.data.error || err.message;
|
|
1304
|
+
EditorStore.setStatus('Create file failed: ' + message, 'error');
|
|
1305
|
+
})["finally"](function () {
|
|
1306
|
+
return setLoading(function (prev) {
|
|
1307
|
+
return _extends({}, prev, { createFile: false });
|
|
1308
|
+
});
|
|
1309
|
+
});
|
|
1310
|
+
} else {
|
|
1311
|
+
setLoading(function (prev) {
|
|
1312
|
+
return _extends({}, prev, { createDir: true });
|
|
1313
|
+
});
|
|
1314
|
+
FileService.createDir(path).then(function (res) {
|
|
1315
|
+
var createdPath = res && res.path || path;
|
|
1316
|
+
setSelectedTreeNode({ path: createdPath, name: createdPath.split('/').pop(), type: 'folder' });
|
|
1317
|
+
EditorStore.setStatus('Created folder: ' + createdPath, 'success');
|
|
1318
|
+
return refreshProjectTree().then(function () {
|
|
1319
|
+
return GitService.fetchStatus();
|
|
1320
|
+
});
|
|
1321
|
+
})["catch"](function (err) {
|
|
1322
|
+
var message = err && err.response && err.response.data && err.response.data.error || err.message;
|
|
1323
|
+
EditorStore.setStatus('Create folder failed: ' + message, 'error');
|
|
1324
|
+
})["finally"](function () {
|
|
1325
|
+
return setLoading(function (prev) {
|
|
1326
|
+
return _extends({}, prev, { createDir: false });
|
|
1327
|
+
});
|
|
1328
|
+
});
|
|
1329
|
+
}
|
|
1330
|
+
};
|
|
1331
|
+
|
|
1332
|
+
var handleCreateCancel = function handleCreateCancel() {
|
|
1333
|
+
return setPendingCreate(null);
|
|
1334
|
+
};
|
|
1335
|
+
|
|
1336
|
+
var handleRenamePath = function handleRenamePath(targetNode) {
|
|
1337
|
+
var node = targetNode !== undefined ? targetNode : selectedTreeNode;
|
|
1338
|
+
if (!node || !node.path) {
|
|
1339
|
+
EditorStore.setStatus('Select a file or folder to rename first.', 'warning');
|
|
1340
|
+
return;
|
|
1341
|
+
}
|
|
1342
|
+
|
|
1343
|
+
var itemPath = node.path;
|
|
1344
|
+
|
|
1345
|
+
// Expand all ancestor folders so the rename inline input is always visible
|
|
1346
|
+
var parts = itemPath.split('/');
|
|
1347
|
+
if (parts.length > 1) {
|
|
1348
|
+
var ancestors = {};
|
|
1349
|
+
for (var i = 1; i < parts.length; i++) {
|
|
1350
|
+
ancestors[parts.slice(0, i).join('/')] = true;
|
|
1351
|
+
}
|
|
1352
|
+
setExpandedDirs(function (prev) {
|
|
1353
|
+
return Object.assign({}, prev, ancestors);
|
|
1354
|
+
});
|
|
1355
|
+
}
|
|
1356
|
+
|
|
1357
|
+
setPendingCreate(null);
|
|
1358
|
+
setPendingRename({
|
|
1359
|
+
path: itemPath,
|
|
1360
|
+
parentPath: parentDir(itemPath),
|
|
1361
|
+
type: node.type,
|
|
1362
|
+
currentName: node.name || itemPath.split('/').pop()
|
|
1363
|
+
});
|
|
1364
|
+
};
|
|
1365
|
+
|
|
1366
|
+
var handleRenameConfirm = function handleRenameConfirm(name, renameTarget) {
|
|
1367
|
+
var target = renameTarget || pendingRename;
|
|
1368
|
+
if (!target || !name) return;
|
|
1369
|
+
|
|
1370
|
+
var oldPath = target.path;
|
|
1371
|
+
var currentName = target.currentName || oldPath.split('/').pop();
|
|
1372
|
+
var nextName = name.trim();
|
|
1373
|
+
setPendingRename(null);
|
|
1374
|
+
|
|
1375
|
+
if (!nextName || nextName === currentName) return;
|
|
1376
|
+
|
|
1377
|
+
var nextPath = normalizeRelativePath(parentDir(oldPath) ? parentDir(oldPath) + '/' + nextName : nextName);
|
|
1378
|
+
if (!nextPath || nextPath === oldPath) return;
|
|
1379
|
+
|
|
1380
|
+
setLoading(function (prev) {
|
|
1381
|
+
return _extends({}, prev, { renamePath: true });
|
|
1382
|
+
});
|
|
1383
|
+
FileService.renamePath(oldPath, nextPath).then(function (res) {
|
|
1384
|
+
var renamedPath = res && res.path || nextPath;
|
|
1385
|
+
applyRenameToOpenTabs(oldPath, renamedPath);
|
|
1386
|
+
setSelectedTreeNode(function (prev) {
|
|
1387
|
+
return prev ? _extends({}, prev, { path: renamedPath, name: renamedPath.split('/').pop() }) : prev;
|
|
1388
|
+
});
|
|
1389
|
+
EditorStore.setStatus('Renamed to: ' + renamedPath, 'success');
|
|
1390
|
+
return refreshProjectTree().then(function () {
|
|
1391
|
+
GitService.fetchStatus();
|
|
1392
|
+
});
|
|
1393
|
+
})["catch"](function (err) {
|
|
1394
|
+
var message = err && err.response && err.response.data && err.response.data.error || err.message;
|
|
1395
|
+
EditorStore.setStatus('Rename failed: ' + message, 'error');
|
|
1396
|
+
})["finally"](function () {
|
|
1397
|
+
setLoading(function (prev) {
|
|
1398
|
+
return _extends({}, prev, { renamePath: false });
|
|
1399
|
+
});
|
|
1400
|
+
});
|
|
1401
|
+
};
|
|
1402
|
+
|
|
1403
|
+
var handleRenameCancel = function handleRenameCancel() {
|
|
1404
|
+
return setPendingRename(null);
|
|
1405
|
+
};
|
|
1406
|
+
|
|
1407
|
+
var handleDeletePath = function handleDeletePath(targetNode) {
|
|
1408
|
+
var node = targetNode !== undefined ? targetNode : selectedTreeNode;
|
|
1409
|
+
if (!node || !node.path) {
|
|
1410
|
+
EditorStore.setStatus('Select a file or folder to delete first.', 'warning');
|
|
1411
|
+
return;
|
|
1412
|
+
}
|
|
1413
|
+
|
|
1414
|
+
var targetPath = node.path;
|
|
1415
|
+
var confirmed = window.confirm('Delete ' + targetPath + '? This cannot be undone.');
|
|
1416
|
+
if (!confirmed) return;
|
|
1417
|
+
|
|
1418
|
+
setLoading(function (prev) {
|
|
1419
|
+
return _extends({}, prev, { deletePath: true });
|
|
1420
|
+
});
|
|
1421
|
+
FileService.deletePath(targetPath).then(function () {
|
|
1422
|
+
removeDeletedPathFromOpenTabs(targetPath);
|
|
1423
|
+
setSelectedTreeNode(null);
|
|
1424
|
+
EditorStore.setStatus('Deleted: ' + targetPath, 'success');
|
|
1425
|
+
return refreshProjectTree().then(function () {
|
|
1426
|
+
GitService.fetchStatus();
|
|
1427
|
+
});
|
|
1428
|
+
})["catch"](function (err) {
|
|
1429
|
+
var message = err && err.response && err.response.data && err.response.data.error || err.message;
|
|
1430
|
+
EditorStore.setStatus('Delete failed: ' + message, 'error');
|
|
1431
|
+
})["finally"](function () {
|
|
1432
|
+
setLoading(function (prev) {
|
|
1433
|
+
return _extends({}, prev, { deletePath: false });
|
|
1434
|
+
});
|
|
1435
|
+
});
|
|
1436
|
+
};
|
|
1437
|
+
|
|
1438
|
+
var projectSectionTitle = deriveProjectRootName().toUpperCase();
|
|
1439
|
+
var selectedTreePath = selectedTreeNode ? selectedTreeNode.path : null;
|
|
1440
|
+
var isRuby = activeTab && isRubyPath(activeTab.path);
|
|
1441
|
+
var isHaml = activeTab && activeTab.path.endsWith('.haml');
|
|
1442
|
+
var supportedPrettierExts = ['js', 'jsx', 'json', 'css', 'scss', 'html', 'md'];
|
|
1443
|
+
var isPrettierable = activeTab && supportedPrettierExts.includes(activeTab.path.split('.').pop().toLowerCase());
|
|
1444
|
+
var canLintAndFormat = activeTab && (isPrettierable || isRuby && rubocopAvailable || isHaml && hamlLintAvailable);
|
|
1445
|
+
var hasGitBranch = !!(state.gitBranch && state.gitBranch.trim());
|
|
1446
|
+
|
|
1447
|
+
var renderTabBar = function renderTabBar(paneId, tabs, activeId) {
|
|
1448
|
+
return React.createElement(TabBar, {
|
|
1449
|
+
tabs: tabs,
|
|
1450
|
+
activeId: activeId,
|
|
1451
|
+
onSelect: function (id) {
|
|
1452
|
+
return TabManager.switchTab(paneId, id);
|
|
1453
|
+
},
|
|
1454
|
+
onClose: function (id) {
|
|
1455
|
+
return requestCloseTab(paneId, id);
|
|
1456
|
+
},
|
|
1457
|
+
onTabDragStart: function (id) {
|
|
1458
|
+
return handleTabDragStart(paneId, id);
|
|
1459
|
+
},
|
|
1460
|
+
onTabDragEnd: clearDragState,
|
|
1461
|
+
onHardenTab: function (tabId) {
|
|
1462
|
+
return TabManager.hardenTab(paneId, tabId);
|
|
1463
|
+
},
|
|
1464
|
+
onShowHistory: function (path) {
|
|
1465
|
+
setHistoryPanelPath(path);
|
|
1466
|
+
}
|
|
1467
|
+
});
|
|
1468
|
+
};
|
|
1469
|
+
|
|
1470
|
+
return React.createElement(
|
|
1471
|
+
"div",
|
|
1472
|
+
{ className: "ide-shell" },
|
|
1473
|
+
React.createElement(
|
|
1474
|
+
"div",
|
|
1475
|
+
{ className: "ide-titlebar" },
|
|
1476
|
+
React.createElement("i", { className: "fas fa-layer-group ide-titlebar-icon" }),
|
|
1477
|
+
React.createElement(
|
|
1478
|
+
"div",
|
|
1479
|
+
{ className: "ide-titlebar-title" },
|
|
1480
|
+
"Mini Browser Editor — ",
|
|
1481
|
+
window.location.host
|
|
1482
|
+
),
|
|
1483
|
+
React.createElement(
|
|
1484
|
+
"div",
|
|
1485
|
+
{ style: { marginLeft: "auto", display: "flex", gap: "4px", height: "100%", alignItems: "center" } },
|
|
1486
|
+
React.createElement(
|
|
1487
|
+
"button",
|
|
1488
|
+
{ className: "statusbar-btn", onClick: function () {
|
|
1489
|
+
return activeTab && handleSave(focusedPane.id, activeTab);
|
|
1490
|
+
}, disabled: loading.save || !activeTab || !activeTab.dirty },
|
|
1491
|
+
React.createElement("i", { className: loading.save ? "fas fa-spinner fa-spin" : "fas fa-save" }),
|
|
1492
|
+
" Save ",
|
|
1493
|
+
activeTab && activeTab.dirty ? "●" : ""
|
|
1494
|
+
),
|
|
1495
|
+
React.createElement(
|
|
1496
|
+
"button",
|
|
1497
|
+
{ className: "statusbar-btn", onClick: handleSaveAll, disabled: loading.saveAll || !state.panes.flatMap(function (p) {
|
|
1498
|
+
return p.tabs;
|
|
1499
|
+
}).some(function (t) {
|
|
1500
|
+
return t.dirty;
|
|
1501
|
+
}) },
|
|
1502
|
+
React.createElement(
|
|
1503
|
+
"i",
|
|
1504
|
+
{ className: loading.saveAll ? "fas fa-spinner fa-spin" : "fas fa-save", style: loading.saveAll ? {} : { position: 'relative' } },
|
|
1505
|
+
!loading.saveAll && React.createElement("i", { className: "fas fa-save", style: { position: 'absolute', top: '-2px', left: '3px', fontSize: '9px', opacity: 0.8 } })
|
|
1506
|
+
),
|
|
1507
|
+
" Save All"
|
|
1508
|
+
),
|
|
1509
|
+
React.createElement("div", { className: "statusbar-sep" }),
|
|
1510
|
+
React.createElement(
|
|
1511
|
+
"button",
|
|
1512
|
+
{ className: "statusbar-btn", onClick: handleFormat, disabled: loading.format || !canLintAndFormat },
|
|
1513
|
+
React.createElement("i", { className: loading.format ? "fas fa-spinner fa-spin" : "fas fa-magic" }),
|
|
1514
|
+
" Format"
|
|
1515
|
+
),
|
|
1516
|
+
hasGitBranch && React.createElement(
|
|
1517
|
+
React.Fragment,
|
|
1518
|
+
null,
|
|
1519
|
+
React.createElement("div", { className: "statusbar-sep" }),
|
|
1520
|
+
React.createElement(
|
|
1521
|
+
"button",
|
|
1522
|
+
{ type: "button", className: "statusbar-btn titlebar-git-btn", onClick: toggleGitPanel },
|
|
1523
|
+
React.createElement("i", { className: "fas fa-code-branch" }),
|
|
1524
|
+
" Git"
|
|
1525
|
+
)
|
|
1526
|
+
),
|
|
1527
|
+
React.createElement("div", { className: "statusbar-sep" }),
|
|
1528
|
+
React.createElement(
|
|
1529
|
+
"button",
|
|
1530
|
+
{ type: "button", className: "statusbar-btn", onClick: function () { return setShowHelp(true); }, title: "Keyboard shortcuts & help" },
|
|
1531
|
+
React.createElement("i", { className: "fas fa-keyboard" }),
|
|
1532
|
+
" Help"
|
|
1533
|
+
)
|
|
1534
|
+
)
|
|
1535
|
+
),
|
|
1536
|
+
showHelp && React.createElement(ShortcutHelp, { onClose: function () { return setShowHelp(false); } }),
|
|
1537
|
+
React.createElement(
|
|
1538
|
+
"div",
|
|
1539
|
+
{ className: "ide-body", id: "ide-body-container" },
|
|
1540
|
+
React.createElement(
|
|
1541
|
+
"div",
|
|
1542
|
+
{ className: "ide-sidebar", style: { width: sidebarWidth + "px" } },
|
|
1543
|
+
React.createElement(
|
|
1544
|
+
"div",
|
|
1545
|
+
{ className: "ide-sidebar-tabs" },
|
|
1546
|
+
React.createElement(
|
|
1547
|
+
"button",
|
|
1548
|
+
{ type: "button", className: "ide-sidebar-tab " + (activeSidebarTab === 'explorer' ? 'active' : ''), onClick: function () {
|
|
1549
|
+
return setActiveSidebarTab('explorer');
|
|
1550
|
+
} },
|
|
1551
|
+
"EXPLORER"
|
|
1552
|
+
),
|
|
1553
|
+
React.createElement(
|
|
1554
|
+
"button",
|
|
1555
|
+
{ type: "button", className: "ide-sidebar-tab " + (activeSidebarTab === 'search' ? 'active' : ''), onClick: function () {
|
|
1556
|
+
return setActiveSidebarTab('search');
|
|
1557
|
+
} },
|
|
1558
|
+
"SEARCH"
|
|
1559
|
+
)
|
|
1560
|
+
),
|
|
1561
|
+
activeSidebarTab === 'explorer' && React.createElement(
|
|
1562
|
+
"div",
|
|
1563
|
+
{ className: "ide-sidebar-content" },
|
|
1564
|
+
state.panes.flatMap(function (p) {
|
|
1565
|
+
return p.tabs;
|
|
1566
|
+
}).length > 0 && React.createElement(
|
|
1567
|
+
CollapsibleSection,
|
|
1568
|
+
{
|
|
1569
|
+
title: "OPEN EDITORS",
|
|
1570
|
+
isCollapsed: collapsedSections.openEditors,
|
|
1571
|
+
onToggle: function (isCollapsed) {
|
|
1572
|
+
return handleToggleSection('openEditors', isCollapsed);
|
|
1573
|
+
},
|
|
1574
|
+
actions: React.createElement(
|
|
1575
|
+
SectionActionGroup,
|
|
1576
|
+
{ ariaLabel: "Open editor actions" },
|
|
1577
|
+
React.createElement(SidebarActionButton, {
|
|
1578
|
+
title: "Close all editors",
|
|
1579
|
+
ariaLabel: "Close all open editors",
|
|
1580
|
+
iconClass: "far fa-window-close",
|
|
1581
|
+
onClick: handleCloseAllEditors
|
|
1582
|
+
})
|
|
1583
|
+
)
|
|
1584
|
+
},
|
|
1585
|
+
React.createElement(
|
|
1586
|
+
"div",
|
|
1587
|
+
{ style: { marginBottom: "12px" } },
|
|
1588
|
+
state.panes.map(function (pane) {
|
|
1589
|
+
if (pane.tabs.length === 0) return null;
|
|
1590
|
+
var isPane2 = pane.id === 2;
|
|
1591
|
+
return React.createElement(
|
|
1592
|
+
"div",
|
|
1593
|
+
{
|
|
1594
|
+
key: pane.id,
|
|
1595
|
+
className: "open-editors-group",
|
|
1596
|
+
style: { marginBottom: pane.id === 1 && state.panes[1].tabs.length > 0 ? "10px" : "0" }
|
|
1597
|
+
},
|
|
1598
|
+
React.createElement(
|
|
1599
|
+
"div",
|
|
1600
|
+
{ className: "ide-sidebar-header open-editors-group-header" },
|
|
1601
|
+
React.createElement(
|
|
1602
|
+
"span",
|
|
1603
|
+
{ className: "open-editors-group-title" },
|
|
1604
|
+
"GROUP ",
|
|
1605
|
+
pane.id
|
|
1606
|
+
),
|
|
1607
|
+
React.createElement(
|
|
1608
|
+
SectionActionGroup,
|
|
1609
|
+
{ ariaLabel: "Group " + pane.id + " actions", className: "collapsible-actions open-editors-group-actions" },
|
|
1610
|
+
React.createElement(SidebarActionButton, {
|
|
1611
|
+
title: "Close all editors in Group " + pane.id,
|
|
1612
|
+
ariaLabel: "Close all editors in Group " + pane.id,
|
|
1613
|
+
iconClass: "far fa-window-close",
|
|
1614
|
+
onClick: function (e) {
|
|
1615
|
+
e.stopPropagation();handleCloseEditorsInGroup(pane.id);
|
|
1616
|
+
}
|
|
1617
|
+
})
|
|
1618
|
+
)
|
|
1619
|
+
),
|
|
1620
|
+
React.createElement(
|
|
1621
|
+
"div",
|
|
1622
|
+
{ className: "file-tree" },
|
|
1623
|
+
pane.tabs.map(function (tab) {
|
|
1624
|
+
return React.createElement(
|
|
1625
|
+
"div",
|
|
1626
|
+
{
|
|
1627
|
+
key: tab.id,
|
|
1628
|
+
className: "tree-item " + (pane.activeTabId === tab.id && state.focusedPaneId === pane.id ? "active" : ""),
|
|
1629
|
+
onClick: function () {
|
|
1630
|
+
TabManager.focusPane(pane.id);TabManager.switchTab(pane.id, tab.id);
|
|
1631
|
+
}
|
|
1632
|
+
},
|
|
1633
|
+
React.createElement("i", { className: "tree-item-icon " + (window.getFileIcon ? window.getFileIcon(tab.name) : 'far fa-file-code') + " tree-file-icon" }),
|
|
1634
|
+
React.createElement(
|
|
1635
|
+
"div",
|
|
1636
|
+
{ className: "tree-item-name", style: { display: 'flex', alignItems: 'center' } },
|
|
1637
|
+
React.createElement(
|
|
1638
|
+
"span",
|
|
1639
|
+
{ style: { overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' } },
|
|
1640
|
+
tab.name
|
|
1641
|
+
),
|
|
1642
|
+
tab.dirty && React.createElement("i", { className: "fas fa-circle", style: { fontSize: '5px', color: '#e3d286', marginLeft: '6px', marginTop: '1px' } })
|
|
1643
|
+
),
|
|
1644
|
+
React.createElement(
|
|
1645
|
+
"div",
|
|
1646
|
+
{ className: "tab-actions", style: { display: 'flex', position: 'absolute', right: '4px', top: 0, height: '100%', alignItems: 'center' } },
|
|
1647
|
+
React.createElement(
|
|
1648
|
+
"div",
|
|
1649
|
+
{ className: "tab-split", onClick: function (e) {
|
|
1650
|
+
e.stopPropagation();TabManager.moveTabToPane(pane.id, pane.id === 1 ? 2 : 1, tab.id);
|
|
1651
|
+
}, style: { padding: '0 4px', cursor: 'pointer', opacity: 0.6 }, title: "Move to Group " + (pane.id === 1 ? 2 : 1) },
|
|
1652
|
+
React.createElement("i", { className: isPane2 ? "fas fa-chevron-left" : "fas fa-chevron-right" })
|
|
1653
|
+
),
|
|
1654
|
+
React.createElement(
|
|
1655
|
+
"div",
|
|
1656
|
+
{ className: "tab-close", onClick: function (e) {
|
|
1657
|
+
e.stopPropagation();requestCloseTab(pane.id, tab.id);
|
|
1658
|
+
}, style: { padding: '0 4px', cursor: 'pointer', opacity: 0.6 } },
|
|
1659
|
+
React.createElement("i", { className: "fas fa-times" })
|
|
1660
|
+
)
|
|
1661
|
+
)
|
|
1662
|
+
);
|
|
1663
|
+
})
|
|
1664
|
+
)
|
|
1665
|
+
);
|
|
1666
|
+
})
|
|
1667
|
+
)
|
|
1668
|
+
),
|
|
1669
|
+
React.createElement(
|
|
1670
|
+
CollapsibleSection,
|
|
1671
|
+
{
|
|
1672
|
+
title: projectSectionTitle,
|
|
1673
|
+
isCollapsed: collapsedSections.projects,
|
|
1674
|
+
onToggle: function (isCollapsed) {
|
|
1675
|
+
return handleToggleSection('projects', isCollapsed);
|
|
1676
|
+
},
|
|
1677
|
+
actions: React.createElement(
|
|
1678
|
+
SectionActionGroup,
|
|
1679
|
+
{ ariaLabel: "Project actions" },
|
|
1680
|
+
React.createElement(SidebarActionButton, {
|
|
1681
|
+
title: "Collapse all folders",
|
|
1682
|
+
iconClass: "fas fa-compress-alt",
|
|
1683
|
+
onClick: handleCollapseAll
|
|
1684
|
+
}),
|
|
1685
|
+
React.createElement(SidebarActionButton, {
|
|
1686
|
+
title: "New file",
|
|
1687
|
+
iconClass: loading.createFile ? 'fas fa-spinner fa-spin' : 'far fa-file',
|
|
1688
|
+
onClick: function () {
|
|
1689
|
+
return handleCreateFile();
|
|
1690
|
+
},
|
|
1691
|
+
disabled: !!loading.createFile
|
|
1692
|
+
}),
|
|
1693
|
+
React.createElement(SidebarActionButton, {
|
|
1694
|
+
title: "New folder",
|
|
1695
|
+
iconClass: loading.createDir ? 'fas fa-spinner fa-spin' : 'far fa-folder',
|
|
1696
|
+
onClick: function () {
|
|
1697
|
+
return handleCreateDir();
|
|
1698
|
+
},
|
|
1699
|
+
disabled: !!loading.createDir
|
|
1700
|
+
}),
|
|
1701
|
+
React.createElement(SidebarActionButton, {
|
|
1702
|
+
title: "Rename selected",
|
|
1703
|
+
iconClass: loading.renamePath ? 'fas fa-spinner fa-spin' : 'fas fa-pen',
|
|
1704
|
+
onClick: function () {
|
|
1705
|
+
return handleRenamePath();
|
|
1706
|
+
},
|
|
1707
|
+
disabled: !!loading.renamePath || !selectedTreePath
|
|
1708
|
+
}),
|
|
1709
|
+
React.createElement(SidebarActionButton, {
|
|
1710
|
+
title: "Delete selected",
|
|
1711
|
+
iconClass: loading.deletePath ? 'fas fa-spinner fa-spin' : 'far fa-trash-alt',
|
|
1712
|
+
onClick: function () {
|
|
1713
|
+
return handleDeletePath();
|
|
1714
|
+
},
|
|
1715
|
+
disabled: !!loading.deletePath || !selectedTreePath,
|
|
1716
|
+
danger: true
|
|
1717
|
+
})
|
|
1718
|
+
)
|
|
1719
|
+
},
|
|
1720
|
+
React.createElement(FileTree, {
|
|
1721
|
+
items: treeData,
|
|
1722
|
+
onSelect: handleSoftOpenFile,
|
|
1723
|
+
activePath: activeTab && activeTab.path,
|
|
1724
|
+
selectedPath: selectedTreePath,
|
|
1725
|
+
onNodeSelect: setSelectedTreeNode,
|
|
1726
|
+
gitFiles: state.gitFiles,
|
|
1727
|
+
expandedDirs: expandedDirs,
|
|
1728
|
+
onExpandedDirsChange: setExpandedDirs,
|
|
1729
|
+
onFileDoubleClick: handleHardOpenFile,
|
|
1730
|
+
onContextMenu: openContextMenu,
|
|
1731
|
+
pendingCreate: pendingCreate,
|
|
1732
|
+
onCreateConfirm: handleCreateConfirm,
|
|
1733
|
+
onCreateCancel: handleCreateCancel,
|
|
1734
|
+
pendingRename: pendingRename,
|
|
1735
|
+
onRenameConfirm: handleRenameConfirm,
|
|
1736
|
+
onRenameCancel: handleRenameCancel
|
|
1737
|
+
})
|
|
1738
|
+
)
|
|
1739
|
+
),
|
|
1740
|
+
activeSidebarTab === 'search' && React.createElement(
|
|
1741
|
+
"form",
|
|
1742
|
+
{ className: "search-panel", onSubmit: execSearch },
|
|
1743
|
+
React.createElement(
|
|
1744
|
+
"div",
|
|
1745
|
+
{ className: "search-input-wrap" },
|
|
1746
|
+
React.createElement("input", {
|
|
1747
|
+
className: "search-input",
|
|
1748
|
+
placeholder: "Find in files...",
|
|
1749
|
+
value: searchQuery,
|
|
1750
|
+
onChange: handleSearchChange
|
|
1751
|
+
}),
|
|
1752
|
+
React.createElement(
|
|
1753
|
+
"button",
|
|
1754
|
+
{ type: "submit", className: "search-btn" },
|
|
1755
|
+
React.createElement("i", { className: "fas fa-search" })
|
|
1756
|
+
)
|
|
1757
|
+
),
|
|
1758
|
+
React.createElement(
|
|
1759
|
+
"div",
|
|
1760
|
+
{ className: "search-results" },
|
|
1761
|
+
searchQuery && state.searchResults.length > 0 && React.createElement(
|
|
1762
|
+
"div",
|
|
1763
|
+
{ className: "search-results-meta" },
|
|
1764
|
+
state.searchResults.length,
|
|
1765
|
+
" result" + (state.searchResults.length !== 1 ? "s" : ""),
|
|
1766
|
+
state.searchResults.length >= 30 && React.createElement(
|
|
1767
|
+
"span",
|
|
1768
|
+
{ className: "search-results-capped" },
|
|
1769
|
+
" — refine query to see more"
|
|
1770
|
+
)
|
|
1771
|
+
),
|
|
1772
|
+
searchQuery && state.searchResults.length === 0 && React.createElement(
|
|
1773
|
+
"div",
|
|
1774
|
+
{ className: "search-results-empty" },
|
|
1775
|
+
"No results"
|
|
1776
|
+
),
|
|
1777
|
+
state.searchResults.map(function (res, i) {
|
|
1778
|
+
return React.createElement(
|
|
1779
|
+
"div",
|
|
1780
|
+
{ key: i, className: "search-result-item", onClick: function () {
|
|
1781
|
+
return handleSelectFile(res.file, res.file.split('/').pop(), res.line);
|
|
1782
|
+
} },
|
|
1783
|
+
React.createElement(
|
|
1784
|
+
"div",
|
|
1785
|
+
{ className: "search-result-file" },
|
|
1786
|
+
res.file,
|
|
1787
|
+
" ",
|
|
1788
|
+
React.createElement(
|
|
1789
|
+
"span",
|
|
1790
|
+
{ className: "search-result-line-num" },
|
|
1791
|
+
":",
|
|
1792
|
+
res.line
|
|
1793
|
+
)
|
|
1794
|
+
),
|
|
1795
|
+
React.createElement(
|
|
1796
|
+
"div",
|
|
1797
|
+
{ className: "search-result-text" },
|
|
1798
|
+
res.text
|
|
1799
|
+
)
|
|
1800
|
+
);
|
|
1801
|
+
})
|
|
1802
|
+
)
|
|
1803
|
+
)
|
|
1804
|
+
),
|
|
1805
|
+
React.createElement("div", {
|
|
1806
|
+
className: "panel-divider sidebar-divider " + (activeResizeMode === 'sidebar' ? 'active' : ''),
|
|
1807
|
+
onMouseDown: startSidebarResize,
|
|
1808
|
+
role: "separator",
|
|
1809
|
+
"aria-orientation": "vertical",
|
|
1810
|
+
"aria-label": "Resize explorer panel"
|
|
1811
|
+
}),
|
|
1812
|
+
React.createElement(
|
|
1813
|
+
"div",
|
|
1814
|
+
{
|
|
1815
|
+
id: "ide-main-split-container",
|
|
1816
|
+
className: "ide-main",
|
|
1817
|
+
style: { display: 'flex', flexDirection: 'row', width: '100%', height: '100%', cursor: activeResizeMode === 'pane' ? 'col-resize' : 'default', userSelect: activeResizeMode ? 'none' : 'auto' },
|
|
1818
|
+
onDragOverCapture: function (e) {
|
|
1819
|
+
if (!draggedTab) return;
|
|
1820
|
+
e.preventDefault();
|
|
1821
|
+
|
|
1822
|
+
var rect = e.currentTarget.getBoundingClientRect();
|
|
1823
|
+
var splitAtX = rect.left + rect.width * (pane1Width / 100);
|
|
1824
|
+
var hoverPaneId = e.clientX >= splitAtX ? 2 : 1;
|
|
1825
|
+
var nextDropPane = hoverPaneId === draggedTab.sourcePaneId ? null : hoverPaneId;
|
|
1826
|
+
|
|
1827
|
+
e.dataTransfer.dropEffect = nextDropPane ? 'move' : 'none';
|
|
1828
|
+
if (dragOverPaneId !== nextDropPane) setDragOverPaneId(nextDropPane);
|
|
1829
|
+
},
|
|
1830
|
+
onDropCapture: function (e) {
|
|
1831
|
+
if (!draggedTab) return;
|
|
1832
|
+
e.preventDefault();
|
|
1833
|
+
|
|
1834
|
+
var rect = e.currentTarget.getBoundingClientRect();
|
|
1835
|
+
var splitAtX = rect.left + rect.width * (pane1Width / 100);
|
|
1836
|
+
var targetPaneId = e.clientX >= splitAtX ? 2 : 1;
|
|
1837
|
+
|
|
1838
|
+
if (targetPaneId !== draggedTab.sourcePaneId) {
|
|
1839
|
+
moveDraggedTabToPane(targetPaneId);
|
|
1840
|
+
} else {
|
|
1841
|
+
clearDragState();
|
|
1842
|
+
}
|
|
1843
|
+
}
|
|
1844
|
+
},
|
|
1845
|
+
state.panes.map(function (pane, idx) {
|
|
1846
|
+
if (pane.id === 2 && pane.tabs.length === 0 && !draggedTab) return null; // Show pane 2 while dragging to allow drop-to-split
|
|
1847
|
+
|
|
1848
|
+
// Dynamic width distribution
|
|
1849
|
+
var isSplit = state.panes[1].tabs.length > 0 || !!draggedTab;
|
|
1850
|
+
var flexBasis = '100%';
|
|
1851
|
+
if (isSplit) flexBasis = pane.id === 1 ? pane1Width + "%" : 100 - pane1Width + "%";
|
|
1852
|
+
|
|
1853
|
+
var isFocused = state.focusedPaneId === pane.id;
|
|
1854
|
+
var pActiveTab = pane.tabs.find(function (t) {
|
|
1855
|
+
return t.id === pane.activeTabId;
|
|
1856
|
+
});
|
|
1857
|
+
var canAcceptDrop = !!draggedTab && draggedTab.sourcePaneId !== pane.id;
|
|
1858
|
+
var isDropTarget = canAcceptDrop && dragOverPaneId === pane.id;
|
|
1859
|
+
|
|
1860
|
+
var content;
|
|
1861
|
+
if (pane.tabs.length === 0) {
|
|
1862
|
+
content = React.createElement(
|
|
1863
|
+
'div',
|
|
1864
|
+
{ className: 'ide-empty-pane' },
|
|
1865
|
+
React.createElement('i', { className: 'fas fa-code ide-empty-icon' }),
|
|
1866
|
+
React.createElement(
|
|
1867
|
+
'p',
|
|
1868
|
+
null,
|
|
1869
|
+
'Ctrl+P to open files'
|
|
1870
|
+
)
|
|
1871
|
+
);
|
|
1872
|
+
} else if (pActiveTab) {
|
|
1873
|
+
if (pActiveTab.isCommitGraph) {
|
|
1874
|
+
content = React.createElement(window.CommitGraph || CommitGraph, {
|
|
1875
|
+
commits: pActiveTab.commits || [],
|
|
1876
|
+
onSelectCommit: handleSelectCommit
|
|
1877
|
+
});
|
|
1878
|
+
} else if (pActiveTab.isDiff) {
|
|
1879
|
+
content = React.createElement(window.DiffViewer || DiffViewer, {
|
|
1880
|
+
key: pActiveTab.id,
|
|
1881
|
+
path: pActiveTab.path,
|
|
1882
|
+
original: pActiveTab.diffOriginal || '',
|
|
1883
|
+
modified: pActiveTab.diffModified || '',
|
|
1884
|
+
isDark: true,
|
|
1885
|
+
onClose: function() { requestCloseTab(pane.id, pActiveTab.id); }
|
|
1886
|
+
});
|
|
1887
|
+
} else {
|
|
1888
|
+
content = React.createElement(window.EditorPanel || EditorPanel, {
|
|
1889
|
+
key: pActiveTab.id,
|
|
1890
|
+
tab: pActiveTab,
|
|
1891
|
+
paneId: pane.id,
|
|
1892
|
+
markers: markers[pActiveTab.id] || [],
|
|
1893
|
+
gitAvailable: gitAvailable,
|
|
1894
|
+
onContentChange: function onContentChange(val) {
|
|
1895
|
+
var st = EditorStore.getState();
|
|
1896
|
+
var cp = st.panes.find(function(p) { return p.id === pane.id; });
|
|
1897
|
+
var ct = cp && cp.tabs.find(function(t) { return t.id === pActiveTab.id; });
|
|
1898
|
+
var cleanNorm = ((ct && ct.cleanContent) || '').replace(/\r\n/g, '\n');
|
|
1899
|
+
var valNorm = val.replace(/\r\n/g, '\n');
|
|
1900
|
+
if (valNorm === cleanNorm) {
|
|
1901
|
+
TabManager.markClean(pane.id, pActiveTab.id, val);
|
|
1902
|
+
} else {
|
|
1903
|
+
TabManager.markDirty(pane.id, pActiveTab.id, val);
|
|
1904
|
+
}
|
|
1905
|
+
}
|
|
1906
|
+
});
|
|
1907
|
+
}
|
|
1908
|
+
}
|
|
1909
|
+
|
|
1910
|
+
return React.createElement(
|
|
1911
|
+
React.Fragment,
|
|
1912
|
+
{ key: pane.id },
|
|
1913
|
+
idx === 1 && isSplit && React.createElement("div", {
|
|
1914
|
+
className: "panel-divider pane-divider " + (activeResizeMode === 'pane' ? 'active' : ''),
|
|
1915
|
+
onMouseDown: startPaneResize
|
|
1916
|
+
}),
|
|
1917
|
+
React.createElement(
|
|
1918
|
+
"div",
|
|
1919
|
+
{
|
|
1920
|
+
className: "ide-pane " + (isFocused ? 'focused' : '') + " " + (isDropTarget ? 'drop-target' : ''),
|
|
1921
|
+
style: { flexBasis: flexBasis, flexShrink: 0, flexGrow: 0, display: 'flex', flexDirection: 'column', minWidth: 0 },
|
|
1922
|
+
onClickCapture: function () {
|
|
1923
|
+
return TabManager.focusPane(pane.id);
|
|
1924
|
+
},
|
|
1925
|
+
onDragOver: function (e) {
|
|
1926
|
+
if (!canAcceptDrop) return;
|
|
1927
|
+
e.preventDefault();
|
|
1928
|
+
e.dataTransfer.dropEffect = 'move';
|
|
1929
|
+
if (dragOverPaneId !== pane.id) setDragOverPaneId(pane.id);
|
|
1930
|
+
},
|
|
1931
|
+
onDragEnter: function (e) {
|
|
1932
|
+
if (!canAcceptDrop) return;
|
|
1933
|
+
e.preventDefault();
|
|
1934
|
+
if (dragOverPaneId !== pane.id) setDragOverPaneId(pane.id);
|
|
1935
|
+
},
|
|
1936
|
+
onDragLeave: function (e) {
|
|
1937
|
+
if (dragOverPaneId !== pane.id) return;
|
|
1938
|
+
if (!e.currentTarget.contains(e.relatedTarget)) {
|
|
1939
|
+
setDragOverPaneId(null);
|
|
1940
|
+
}
|
|
1941
|
+
},
|
|
1942
|
+
onDrop: function (e) {
|
|
1943
|
+
if (!canAcceptDrop) return;
|
|
1944
|
+
e.preventDefault();
|
|
1945
|
+
moveDraggedTabToPane(pane.id);
|
|
1946
|
+
}
|
|
1947
|
+
},
|
|
1948
|
+
pane.tabs.length > 0 ? React.createElement(
|
|
1949
|
+
React.Fragment,
|
|
1950
|
+
null,
|
|
1951
|
+
renderTabBar(pane.id, pane.tabs, pane.activeTabId),
|
|
1952
|
+
React.createElement(
|
|
1953
|
+
"div",
|
|
1954
|
+
{ style: { flex: 1, minHeight: 0, display: 'flex', flexDirection: 'column', visibility: activeResizeMode === 'pane' ? 'hidden' : 'visible' } },
|
|
1955
|
+
content
|
|
1956
|
+
)
|
|
1957
|
+
) : React.createElement(
|
|
1958
|
+
"div",
|
|
1959
|
+
{ className: "tab-welcome" },
|
|
1960
|
+
canAcceptDrop ? React.createElement(
|
|
1961
|
+
React.Fragment,
|
|
1962
|
+
null,
|
|
1963
|
+
React.createElement("i", { className: "fas fa-columns" }),
|
|
1964
|
+
React.createElement(
|
|
1965
|
+
"h2",
|
|
1966
|
+
null,
|
|
1967
|
+
"Drop Tab Here"
|
|
1968
|
+
),
|
|
1969
|
+
React.createElement(
|
|
1970
|
+
"p",
|
|
1971
|
+
null,
|
|
1972
|
+
"Release to move this file into Group ",
|
|
1973
|
+
pane.id,
|
|
1974
|
+
"."
|
|
1975
|
+
)
|
|
1976
|
+
) : pane.id === 1 ? React.createElement(
|
|
1977
|
+
React.Fragment,
|
|
1978
|
+
null,
|
|
1979
|
+
React.createElement("i", { className: "fas fa-code" }),
|
|
1980
|
+
React.createElement("h2", null, "Mini Browser Editor"),
|
|
1981
|
+
React.createElement("p", { className: "welcome-intro" }, "Open a file from the explorer to start editing."),
|
|
1982
|
+
React.createElement(
|
|
1983
|
+
"div",
|
|
1984
|
+
{ className: "welcome-shortcuts" },
|
|
1985
|
+
React.createElement(
|
|
1986
|
+
"div",
|
|
1987
|
+
{ className: "welcome-section" },
|
|
1988
|
+
React.createElement("h3", null, "Keyboard shortcuts"),
|
|
1989
|
+
React.createElement(
|
|
1990
|
+
"table",
|
|
1991
|
+
{ className: "shortcut-table" },
|
|
1992
|
+
React.createElement(
|
|
1993
|
+
"tbody",
|
|
1994
|
+
null,
|
|
1995
|
+
React.createElement("tr", null,
|
|
1996
|
+
React.createElement("td", null, React.createElement("kbd", null, "Ctrl+P")),
|
|
1997
|
+
React.createElement("td", null, "Quick-open any file by name")
|
|
1998
|
+
),
|
|
1999
|
+
React.createElement("tr", null,
|
|
2000
|
+
React.createElement("td", null, React.createElement("kbd", null, "Ctrl+S")),
|
|
2001
|
+
React.createElement("td", null, "Save the active file")
|
|
2002
|
+
),
|
|
2003
|
+
React.createElement("tr", null,
|
|
2004
|
+
React.createElement("td", null, React.createElement("kbd", null, "Ctrl+Z\u00a0/\u00a0Ctrl+Y")),
|
|
2005
|
+
React.createElement("td", null, "Undo / Redo")
|
|
2006
|
+
)
|
|
2007
|
+
)
|
|
2008
|
+
)
|
|
2009
|
+
),
|
|
2010
|
+
React.createElement(
|
|
2011
|
+
"div",
|
|
2012
|
+
{ className: "welcome-section" },
|
|
2013
|
+
React.createElement("h3", null, "Sidebar panels"),
|
|
2014
|
+
React.createElement(
|
|
2015
|
+
"ul",
|
|
2016
|
+
{ className: "welcome-tips" },
|
|
2017
|
+
React.createElement("li", null, React.createElement("i", { className: "fas fa-folder-open" }), "\u00a0Explorer \u2014 browse and manage project files"),
|
|
2018
|
+
React.createElement("li", null, React.createElement("i", { className: "fas fa-search" }), "\u00a0Search \u2014 full-text search across all files"),
|
|
2019
|
+
React.createElement("li", null, React.createElement("i", { className: "fas fa-code-branch" }), "\u00a0Git panel \u2014 branch status and changed files (top-right icon)")
|
|
2020
|
+
)
|
|
2021
|
+
),
|
|
2022
|
+
React.createElement(
|
|
2023
|
+
"div",
|
|
2024
|
+
{ className: "welcome-section" },
|
|
2025
|
+
React.createElement("h3", null, "Editor tips"),
|
|
2026
|
+
React.createElement(
|
|
2027
|
+
"ul",
|
|
2028
|
+
{ className: "welcome-tips" },
|
|
2029
|
+
React.createElement("li", null, "Drag any tab to the right half to open a split pane"),
|
|
2030
|
+
React.createElement("li", null, "Right-click a file in the explorer to rename or delete it"),
|
|
2031
|
+
React.createElement("li", null, "Ruby files auto-lint with RuboCop when installed"),
|
|
2032
|
+
React.createElement("li", null, "JS, CSS, HTML and Markdown auto-format with Prettier")
|
|
2033
|
+
)
|
|
2034
|
+
)
|
|
2035
|
+
)
|
|
2036
|
+
) : null
|
|
2037
|
+
)
|
|
2038
|
+
)
|
|
2039
|
+
);
|
|
2040
|
+
})
|
|
2041
|
+
),
|
|
2042
|
+
|
|
2043
|
+
// Right-side Git panel (children of ide-body, alongside sidebar and ide-main)
|
|
2044
|
+
showGitPanel && React.createElement("div", {
|
|
2045
|
+
className: "panel-divider gitpanel-divider " + (activeResizeMode === 'gitpanel' ? 'active' : ''),
|
|
2046
|
+
onMouseDown: startGitPanelResize,
|
|
2047
|
+
role: "separator",
|
|
2048
|
+
"aria-orientation": "vertical",
|
|
2049
|
+
"aria-label": "Resize git panel"
|
|
2050
|
+
}),
|
|
2051
|
+
showGitPanel && React.createElement(
|
|
2052
|
+
"div",
|
|
2053
|
+
{ className: "ide-git-right-panel", style: { width: gitPanelWidth + "px" } },
|
|
2054
|
+
React.createElement(window.GitPanel || GitPanel, {
|
|
2055
|
+
gitInfo: state.gitInfo,
|
|
2056
|
+
error: state.gitInfoError,
|
|
2057
|
+
onRefresh: function () { return GitService.fetchInfo(); },
|
|
2058
|
+
onClose: function () { return setShowGitPanel(false); },
|
|
2059
|
+
onOpenFile: openFileFromGitPanel,
|
|
2060
|
+
onOpenDiff: TabManager.openDiffTab,
|
|
2061
|
+
onOpenAllChanges: function(scope, label) { TabManager.openCombinedDiffTab(scope, label); },
|
|
2062
|
+
onSelectCommit: handleSelectCommit
|
|
2063
|
+
})
|
|
2064
|
+
)
|
|
2065
|
+
),
|
|
2066
|
+
React.createElement(
|
|
2067
|
+
"div",
|
|
2068
|
+
{ className: "ide-statusbar" },
|
|
2069
|
+
hasGitBranch && React.createElement(
|
|
2070
|
+
"div",
|
|
2071
|
+
{ className: "statusbar-branch" },
|
|
2072
|
+
React.createElement("i", { className: "fas fa-code-branch" }),
|
|
2073
|
+
" ",
|
|
2074
|
+
state.gitBranch,
|
|
2075
|
+
state.gitInfo && state.gitInfo.ahead > 0 && React.createElement(
|
|
2076
|
+
"span",
|
|
2077
|
+
{ className: "statusbar-aheadbehind", title: state.gitInfo.ahead + " commit(s) ahead of upstream" },
|
|
2078
|
+
" \u2191",
|
|
2079
|
+
state.gitInfo.ahead
|
|
2080
|
+
),
|
|
2081
|
+
state.gitInfo && state.gitInfo.behind > 0 && React.createElement(
|
|
2082
|
+
"span",
|
|
2083
|
+
{ className: "statusbar-aheadbehind statusbar-behind", title: state.gitInfo.behind + " commit(s) behind upstream" },
|
|
2084
|
+
" \u2193",
|
|
2085
|
+
state.gitInfo.behind
|
|
2086
|
+
)
|
|
2087
|
+
),
|
|
2088
|
+
!serverOnline && React.createElement(
|
|
2089
|
+
"div",
|
|
2090
|
+
{ className: "statusbar-offline" },
|
|
2091
|
+
React.createElement("i", { className: "fas fa-exclamation-triangle" }),
|
|
2092
|
+
" Server offline"
|
|
2093
|
+
),
|
|
2094
|
+
activeFileCommit && React.createElement(
|
|
2095
|
+
"div",
|
|
2096
|
+
{ className: "statusbar-file-commit", title: activeFileCommit.title + " — " + activeFileCommit.author },
|
|
2097
|
+
React.createElement("i", { className: "fas fa-history", style: { marginRight: "4px", opacity: 0.7 } }),
|
|
2098
|
+
React.createElement("span", { className: "commit-hash" }, activeFileCommit.hash.slice(0, 7)),
|
|
2099
|
+
" ",
|
|
2100
|
+
activeFileCommit.author
|
|
2101
|
+
),
|
|
2102
|
+
React.createElement(
|
|
2103
|
+
"div",
|
|
2104
|
+
{ className: "statusbar-msg " + state.statusMessage.kind },
|
|
2105
|
+
state.statusMessage.text
|
|
2106
|
+
)
|
|
2107
|
+
),
|
|
2108
|
+
|
|
2109
|
+
// File History Panel overlay
|
|
2110
|
+
historyPanelPath && React.createElement(window.FileHistoryPanel || FileHistoryPanel, {
|
|
2111
|
+
path: historyPanelPath,
|
|
2112
|
+
onClose: function () { return setHistoryPanelPath(null); },
|
|
2113
|
+
onSelectCommit: function (hash, path) {
|
|
2114
|
+
TabManager.openDiffTab(path, path.split('/').pop(), hash + '^', hash, null);
|
|
2115
|
+
}
|
|
2116
|
+
}),
|
|
2117
|
+
|
|
2118
|
+
// Commit Detail overlay (shown when a commit row is clicked in CommitGraph)
|
|
2119
|
+
selectedCommit && React.createElement(
|
|
2120
|
+
React.Fragment,
|
|
2121
|
+
null,
|
|
2122
|
+
React.createElement("div", {
|
|
2123
|
+
style: { position: 'fixed', inset: 0, zIndex: 9000, background: 'rgba(0,0,0,0.45)' },
|
|
2124
|
+
onClick: function() { setSelectedCommit(null); setCommitDetailFiles(null); }
|
|
2125
|
+
}),
|
|
2126
|
+
React.createElement(
|
|
2127
|
+
"div",
|
|
2128
|
+
{ className: "ide-commit-detail-panel" },
|
|
2129
|
+
React.createElement(
|
|
2130
|
+
"div",
|
|
2131
|
+
{ className: "ide-commit-detail-header" },
|
|
2132
|
+
React.createElement(
|
|
2133
|
+
"div",
|
|
2134
|
+
null,
|
|
2135
|
+
React.createElement("div", { className: "ide-commit-detail-title" }, selectedCommit.title),
|
|
2136
|
+
React.createElement(
|
|
2137
|
+
"div",
|
|
2138
|
+
{ className: "ide-commit-detail-meta" },
|
|
2139
|
+
React.createElement("span", { className: "commit-hash" }, selectedCommit.hash.slice(0, 7)),
|
|
2140
|
+
" \xB7 ",
|
|
2141
|
+
selectedCommit.author,
|
|
2142
|
+
" \xB7 ",
|
|
2143
|
+
selectedCommit.date ? new Date(selectedCommit.date).toLocaleString() : ""
|
|
2144
|
+
)
|
|
2145
|
+
),
|
|
2146
|
+
React.createElement(
|
|
2147
|
+
"button",
|
|
2148
|
+
{ className: "git-header-btn", onClick: function() { setSelectedCommit(null); setCommitDetailFiles(null); }, title: "Close" },
|
|
2149
|
+
React.createElement("i", { className: "fas fa-times" })
|
|
2150
|
+
)
|
|
2151
|
+
),
|
|
2152
|
+
commitDetailFiles === null
|
|
2153
|
+
? React.createElement("div", { className: "git-empty" }, React.createElement("i", { className: "fas fa-spinner fa-spin" }), " Loading...")
|
|
2154
|
+
: commitDetailFiles.length === 0
|
|
2155
|
+
? React.createElement("div", { className: "git-empty" }, "No file changes found.")
|
|
2156
|
+
: React.createElement(
|
|
2157
|
+
"div",
|
|
2158
|
+
{ className: "git-list" },
|
|
2159
|
+
commitDetailFiles.map(function(f, i) {
|
|
2160
|
+
var name = (f.path || '').split('/').pop() || f.path;
|
|
2161
|
+
return React.createElement(
|
|
2162
|
+
"div",
|
|
2163
|
+
{ key: i, className: "git-file-item" },
|
|
2164
|
+
React.createElement(
|
|
2165
|
+
"div",
|
|
2166
|
+
{ className: "git-file-info", onClick: function() { openFileFromGitPanel(f.path, name); } },
|
|
2167
|
+
React.createElement("span", { className: "git-status-badge git-" + (f.status || 'M'), title: f.status }, f.status),
|
|
2168
|
+
React.createElement("span", { className: "git-file-path", title: f.path }, f.path)
|
|
2169
|
+
),
|
|
2170
|
+
React.createElement(
|
|
2171
|
+
"div",
|
|
2172
|
+
{ className: "git-file-actions" },
|
|
2173
|
+
React.createElement(
|
|
2174
|
+
"button",
|
|
2175
|
+
{
|
|
2176
|
+
className: "git-action-btn",
|
|
2177
|
+
title: "View Diff",
|
|
2178
|
+
onClick: function(e) {
|
|
2179
|
+
e.stopPropagation();
|
|
2180
|
+
TabManager.openDiffTab(f.path, name, selectedCommit.hash + '^', selectedCommit.hash, null);
|
|
2181
|
+
}
|
|
2182
|
+
},
|
|
2183
|
+
React.createElement("i", { className: "fas fa-exchange-alt" })
|
|
2184
|
+
)
|
|
2185
|
+
)
|
|
2186
|
+
);
|
|
2187
|
+
})
|
|
2188
|
+
)
|
|
2189
|
+
)
|
|
2190
|
+
),
|
|
2191
|
+
|
|
2192
|
+
// Modals & Panels
|
|
2193
|
+
state.isQuickOpenVisible && React.createElement(window.QuickOpenDialog || QuickOpenDialog, { onSelect: handleSelectFile, onClose: function () {
|
|
2194
|
+
return setQuickOpen(false);
|
|
2195
|
+
} }),
|
|
2196
|
+
contextMenu && React.createElement(
|
|
2197
|
+
React.Fragment,
|
|
2198
|
+
null,
|
|
2199
|
+
React.createElement("div", {
|
|
2200
|
+
style: { position: 'fixed', inset: 0, zIndex: 9998 },
|
|
2201
|
+
onClick: closeContextMenu,
|
|
2202
|
+
onContextMenu: function (e) {
|
|
2203
|
+
e.preventDefault();closeContextMenu();
|
|
2204
|
+
}
|
|
2205
|
+
}),
|
|
2206
|
+
React.createElement(
|
|
2207
|
+
"div",
|
|
2208
|
+
{
|
|
2209
|
+
className: "context-menu",
|
|
2210
|
+
style: { left: contextMenu.x, top: contextMenu.y },
|
|
2211
|
+
onClick: function (e) {
|
|
2212
|
+
return e.stopPropagation();
|
|
2213
|
+
}
|
|
2214
|
+
},
|
|
2215
|
+
contextMenu.node && contextMenu.node.type === 'file' && React.createElement(
|
|
2216
|
+
"div",
|
|
2217
|
+
{ className: "context-menu-item", onClick: function () {
|
|
2218
|
+
return handleContextMenuAction('open');
|
|
2219
|
+
} },
|
|
2220
|
+
React.createElement("i", { className: "far fa-file-code context-menu-icon" }),
|
|
2221
|
+
" Open"
|
|
2222
|
+
),
|
|
2223
|
+
React.createElement(
|
|
2224
|
+
"div",
|
|
2225
|
+
{ className: "context-menu-item", onClick: function () {
|
|
2226
|
+
return handleContextMenuAction('newFile');
|
|
2227
|
+
} },
|
|
2228
|
+
React.createElement("i", { className: "far fa-file context-menu-icon" }),
|
|
2229
|
+
" New File"
|
|
2230
|
+
),
|
|
2231
|
+
React.createElement(
|
|
2232
|
+
"div",
|
|
2233
|
+
{ className: "context-menu-item", onClick: function () {
|
|
2234
|
+
return handleContextMenuAction('newFolder');
|
|
2235
|
+
} },
|
|
2236
|
+
React.createElement("i", { className: "far fa-folder context-menu-icon" }),
|
|
2237
|
+
" New Folder"
|
|
2238
|
+
),
|
|
2239
|
+
React.createElement("div", { className: "context-menu-divider" }),
|
|
2240
|
+
React.createElement(
|
|
2241
|
+
"div",
|
|
2242
|
+
{ className: "context-menu-item", onClick: function () {
|
|
2243
|
+
return handleContextMenuAction('rename');
|
|
2244
|
+
} },
|
|
2245
|
+
React.createElement("i", { className: "fas fa-pen context-menu-icon" }),
|
|
2246
|
+
" Rename"
|
|
2247
|
+
),
|
|
2248
|
+
React.createElement(
|
|
2249
|
+
"div",
|
|
2250
|
+
{ className: "context-menu-item context-menu-item-danger", onClick: function () {
|
|
2251
|
+
return handleContextMenuAction('delete');
|
|
2252
|
+
} },
|
|
2253
|
+
React.createElement("i", { className: "far fa-trash-alt context-menu-icon" }),
|
|
2254
|
+
" Delete"
|
|
2255
|
+
),
|
|
2256
|
+
React.createElement("div", { className: "context-menu-divider" }),
|
|
2257
|
+
React.createElement(
|
|
2258
|
+
"div",
|
|
2259
|
+
{ className: "context-menu-item", onClick: function () {
|
|
2260
|
+
return handleContextMenuAction('copyPath');
|
|
2261
|
+
} },
|
|
2262
|
+
React.createElement("i", { className: "fas fa-copy context-menu-icon" }),
|
|
2263
|
+
" Copy Path"
|
|
2264
|
+
)
|
|
2265
|
+
)
|
|
2266
|
+
),
|
|
2267
|
+
closingTabId && React.createElement(
|
|
2268
|
+
"div",
|
|
2269
|
+
{ className: "quick-open-overlay", style: { zIndex: 10001 } },
|
|
2270
|
+
React.createElement(
|
|
2271
|
+
"div",
|
|
2272
|
+
{ className: "quick-open-box", style: { width: '400px', padding: '20px', background: '#252526', border: '1px solid #454545' } },
|
|
2273
|
+
React.createElement(
|
|
2274
|
+
"h3",
|
|
2275
|
+
{ style: { marginTop: 0, fontSize: '14px', color: '#fff' } },
|
|
2276
|
+
"Unsaved Changes"
|
|
2277
|
+
),
|
|
2278
|
+
React.createElement(
|
|
2279
|
+
"p",
|
|
2280
|
+
{ style: { color: '#ccc', margin: '16px 0', fontSize: '13px' } },
|
|
2281
|
+
"Do you want to save the changes you made to ",
|
|
2282
|
+
React.createElement(
|
|
2283
|
+
"strong",
|
|
2284
|
+
null,
|
|
2285
|
+
(state.panes.flatMap(function (p) {
|
|
2286
|
+
return p.tabs;
|
|
2287
|
+
}).find(function (t) {
|
|
2288
|
+
return t.id === closingTabId;
|
|
2289
|
+
}) || {}).name
|
|
2290
|
+
),
|
|
2291
|
+
"?"
|
|
2292
|
+
),
|
|
2293
|
+
React.createElement(
|
|
2294
|
+
"p",
|
|
2295
|
+
{ style: { color: '#888', marginBottom: '24px', fontSize: '12px' } },
|
|
2296
|
+
"Your changes will be lost if you don't save them."
|
|
2297
|
+
),
|
|
2298
|
+
React.createElement(
|
|
2299
|
+
"div",
|
|
2300
|
+
{ style: { display: 'flex', gap: '8px', justifyContent: 'flex-end' } },
|
|
2301
|
+
React.createElement(
|
|
2302
|
+
"button",
|
|
2303
|
+
{
|
|
2304
|
+
onClick: function () {
|
|
2305
|
+
return confirmCloseTab(true);
|
|
2306
|
+
},
|
|
2307
|
+
style: { padding: '6px 16px', background: '#0e639c', color: '#fff', border: 'none', borderRadius: '4px', cursor: 'pointer' } },
|
|
2308
|
+
"Save"
|
|
2309
|
+
),
|
|
2310
|
+
React.createElement(
|
|
2311
|
+
"button",
|
|
2312
|
+
{
|
|
2313
|
+
onClick: function () {
|
|
2314
|
+
return confirmCloseTab(false);
|
|
2315
|
+
},
|
|
2316
|
+
style: { padding: '6px 16px', background: 'transparent', color: '#ccc', border: '1px solid #666', borderRadius: '4px', cursor: 'pointer' } },
|
|
2317
|
+
"Don't Save"
|
|
2318
|
+
),
|
|
2319
|
+
React.createElement(
|
|
2320
|
+
"button",
|
|
2321
|
+
{
|
|
2322
|
+
onClick: function () {
|
|
2323
|
+
return setClosingTabId(null);
|
|
2324
|
+
},
|
|
2325
|
+
style: { padding: '6px 16px', background: 'transparent', color: '#888', border: 'none', cursor: 'pointer' } },
|
|
2326
|
+
"Cancel"
|
|
2327
|
+
)
|
|
2328
|
+
)
|
|
2329
|
+
)
|
|
2330
|
+
)
|
|
2331
|
+
);
|
|
2332
|
+
};
|
|
2333
|
+
|
|
2334
|
+
window.MbeditorApp = MbeditorApp;
|
|
2335
|
+
/* TITLE BAR */ /* SIDEBAR */ /* EDITOR AREA */ /* STATUS BAR */ /* Right-click context menu */
|