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