mbeditor 0.3.8 → 0.4.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 +4 -4
- data/CHANGELOG.md +35 -0
- data/app/assets/javascripts/mbeditor/application.js +1 -0
- data/app/assets/javascripts/mbeditor/application_iife_head.js +7 -0
- data/app/assets/javascripts/mbeditor/components/CodeReviewPanel.js +1 -1
- data/app/assets/javascripts/mbeditor/components/EditorPanel.js +213 -11
- data/app/assets/javascripts/mbeditor/components/GitPanel.js +14 -4
- data/app/assets/javascripts/mbeditor/components/MbeditorApp.js +673 -160
- data/app/assets/javascripts/mbeditor/components/QuickOpenDialog.js +41 -1
- data/app/assets/javascripts/mbeditor/components/TabBar.js +3 -2
- data/app/assets/javascripts/mbeditor/editor_plugins.js +21 -0
- data/app/assets/javascripts/mbeditor/editor_store.js +10 -2
- data/app/assets/javascripts/mbeditor/file_service.js +29 -23
- data/app/assets/javascripts/mbeditor/git_service.js +7 -11
- data/app/assets/javascripts/mbeditor/search_service.js +51 -14
- data/app/assets/javascripts/mbeditor/tab_manager.js +3 -3
- data/app/assets/javascripts/mbeditor/websocket_service.js +126 -0
- data/app/assets/stylesheets/mbeditor/editor.css +237 -15
- data/app/channels/mbeditor/editor_channel.rb +79 -0
- data/app/controllers/mbeditor/editors_controller.rb +177 -136
- data/app/controllers/mbeditor/git_controller.rb +5 -40
- data/app/services/mbeditor/git_blame_service.rb +6 -0
- data/app/services/mbeditor/git_commit_graph_service.rb +2 -0
- data/app/services/mbeditor/git_service.rb +97 -28
- data/app/services/mbeditor/redmine_service.rb +7 -0
- data/app/services/mbeditor/ruby_definition_service.rb +23 -2
- data/app/views/layouts/mbeditor/application.html.erb +4 -0
- data/lib/mbeditor/cable_log_filter.rb +28 -0
- data/lib/mbeditor/configuration.rb +7 -1
- data/lib/mbeditor/engine.rb +37 -0
- data/lib/mbeditor/rack/silence_ping_request.rb +4 -1
- data/lib/mbeditor/version.rb +3 -1
- data/lib/mbeditor.rb +2 -0
- metadata +5 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d378fdda8fc933a3c3d1f5b1fc5eb1622ca4ebee91db435378dd069827f708e2
|
|
4
|
+
data.tar.gz: 2c58b8db51c1df5e7f746af61550dc2ccf62ba33a515cb374c79931aefed1beb
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 43753349cc14d328dbb45c4d6413cb0a56b9ac49979e7cd0bafe884de0889ede353decdd8552da38d872c278fa3c70162a1730df5827f45d0f629aa0d44a1f6f
|
|
7
|
+
data.tar.gz: 3547eec20751a3c7279765e79429dda30398dfd6aa64c7b70d7e2f70cda6e8de6d9ae8c30a0c671a9750dcce1422d115d2b42f1a3d35b6679e81d795ba134c3e
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,41 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.4.2] - 2026-04-22
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
- Updated sidebar search system test selectors to match the current UI loading and clear controls, resolving CI matrix failures in GitHub Actions.
|
|
12
|
+
|
|
13
|
+
## [0.4.1] - 2026-04-22
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
- Real-time file update integration via Action Cable to improve collaborative and live-refresh editing workflows.
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
- Search functionality enhancements for improved result handling and responsiveness.
|
|
20
|
+
|
|
21
|
+
## [0.4.0] - 2026-04-22
|
|
22
|
+
|
|
23
|
+
### Added
|
|
24
|
+
- **Draft auto-save and restore** — unsaved edits are written to `localStorage` every 500 ms per file. On reconnect after a server outage, a dialog offers to restore any drafts that diverged from the in-memory tab content.
|
|
25
|
+
- **Tab bar layout setting** — new "Tab bar layout" preference (Scroll / Wrap multi-row) stored in editor prefs; `TabBar` receives a `tabDisplayMode` prop and applies the appropriate CSS class; horizontal wheel scroll is disabled in wrap mode.
|
|
26
|
+
|
|
27
|
+
### Fixed
|
|
28
|
+
- **Drag-to-split** — pane 2 drop zone is now only shown when the cursor actively hovers over the right-half content area, preventing the tab bar from collapsing during a drag. Dropping onto a tab bar element is no longer intercepted by the cross-pane drop handler, preserving same-pane reordering.
|
|
29
|
+
- **Quick Open sorting** — files consistently rank above directories; within the same priority tier results are sorted by match relevance (exact basename > prefix match > substring > other).
|
|
30
|
+
- **Search total count** — the count thread is joined with a 100 ms timeout; `total_count` is omitted from the response when the thread has not finished, so the first page is never blocked by the counting subprocess.
|
|
31
|
+
- **Status bar message colour** — removed `color-mix` transparency that was dimming the accent-text colour.
|
|
32
|
+
|
|
33
|
+
## [0.3.9] - 2026-04-21
|
|
34
|
+
|
|
35
|
+
### Added
|
|
36
|
+
- Search responses now include a total result count; background counting runs for the first page to populate it without blocking.
|
|
37
|
+
- Editor preferences expanded with line height, letter spacing, cursor style, and auto-closing bracket/quote settings.
|
|
38
|
+
|
|
39
|
+
### Fixed
|
|
40
|
+
- Multi-line tab indentation corrected; added regression test to prevent recurrence.
|
|
41
|
+
- 8 correctness, security, and reliability fixes surfaced by ultrareview.
|
|
42
|
+
|
|
8
43
|
## [0.3.8] - 2026-04-16
|
|
9
44
|
|
|
10
45
|
### Fixed
|
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
// Mbeditor app IIFE — React and ReactDOM are injected from window.MbeditorRuntime
|
|
2
2
|
// so that mbeditor never reads from or writes to window.React / window.ReactDOM,
|
|
3
3
|
// protecting the host application's globals.
|
|
4
|
+
|
|
5
|
+
// Shared helper — returns the engine mount path with no trailing slash.
|
|
6
|
+
// All service modules and components use this instead of inlining the lookup.
|
|
7
|
+
window.mbeditorBasePath = function() {
|
|
8
|
+
return (window.MBEDITOR_BASE_PATH || '/mbeditor').replace(/\/$/, '');
|
|
9
|
+
};
|
|
10
|
+
|
|
4
11
|
(function(React, ReactDOM) {
|
|
@@ -27,7 +27,7 @@ var CodeReviewPanel = function CodeReviewPanel(_ref) {
|
|
|
27
27
|
setIsRedmineLoading(true);
|
|
28
28
|
|
|
29
29
|
// Call the redmine endpoint. It will 503 if disabled via config.
|
|
30
|
-
axios.get(
|
|
30
|
+
axios.get(window.mbeditorBasePath() + '/redmine/issue/' + issueId)
|
|
31
31
|
.then(function (res) {
|
|
32
32
|
setRedmineIssue(res.data);
|
|
33
33
|
setIsRedmineLoading(false);
|
|
@@ -68,6 +68,23 @@ var EditorPanel = function EditorPanel(_ref) {
|
|
|
68
68
|
var editorReady = _useState10[0];
|
|
69
69
|
var setEditorReady = _useState10[1];
|
|
70
70
|
|
|
71
|
+
var _useState11 = useState(false);
|
|
72
|
+
var _useState12 = _slicedToArray(_useState11, 2);
|
|
73
|
+
var methodsOpen = _useState12[0];
|
|
74
|
+
var setMethodsOpen = _useState12[1];
|
|
75
|
+
|
|
76
|
+
var _useState13 = useState([]);
|
|
77
|
+
var _useState14 = _slicedToArray(_useState13, 2);
|
|
78
|
+
var methodsList = _useState14[0];
|
|
79
|
+
var setMethodsList = _useState14[1];
|
|
80
|
+
|
|
81
|
+
var _useState15 = useState(null);
|
|
82
|
+
var _useState16 = _slicedToArray(_useState15, 2);
|
|
83
|
+
var methodsDropdownPos = _useState16[0];
|
|
84
|
+
var setMethodsDropdownPos = _useState16[1];
|
|
85
|
+
|
|
86
|
+
var methodsBtnRef = useRef(null);
|
|
87
|
+
|
|
71
88
|
var onFormatRef = useRef(onFormat);
|
|
72
89
|
onFormatRef.current = onFormat;
|
|
73
90
|
|
|
@@ -480,30 +497,80 @@ var EditorPanel = function EditorPanel(_ref) {
|
|
|
480
497
|
theme: editorPrefs.theme || 'vs-dark',
|
|
481
498
|
automaticLayout: true,
|
|
482
499
|
minimap: { enabled: !!(editorPrefs.minimap) },
|
|
483
|
-
renderLineHighlight: 'none',
|
|
500
|
+
renderLineHighlight: editorPrefs.renderLineHighlight || 'none',
|
|
484
501
|
bracketPairColorization: { enabled: editorPrefs.bracketPairColorization !== false },
|
|
485
502
|
fontFamily: editorPrefs.fontFamily || "'JetBrains Mono', 'Fira Code', Consolas, 'Courier New', monospace",
|
|
486
503
|
fontSize: editorPrefs.fontSize || 13,
|
|
504
|
+
lineHeight: editorPrefs.lineHeight || 0,
|
|
505
|
+
letterSpacing: editorPrefs.letterSpacing || 0,
|
|
487
506
|
tabSize: editorPrefs.tabSize || 4,
|
|
488
507
|
insertSpaces: typeof editorPrefs.insertSpaces === 'boolean' ? editorPrefs.insertSpaces : false,
|
|
489
508
|
wordWrap: editorPrefs.wordWrap || 'off',
|
|
490
509
|
lineNumbers: editorPrefs.lineNumbers || 'on',
|
|
491
510
|
renderWhitespace: editorPrefs.renderWhitespace || 'none',
|
|
492
511
|
scrollBeyondLastLine: !!(editorPrefs.scrollBeyondLastLine),
|
|
493
|
-
|
|
512
|
+
cursorStyle: editorPrefs.cursorStyle || 'line',
|
|
513
|
+
cursorBlinking: editorPrefs.cursorBlinking || 'blink',
|
|
514
|
+
folding: editorPrefs.folding !== false,
|
|
515
|
+
smoothScrolling: !!(editorPrefs.smoothScrolling),
|
|
516
|
+
mouseWheelZoom: !!(editorPrefs.mouseWheelZoom),
|
|
517
|
+
autoClosingBrackets: editorPrefs.autoClosingBrackets || 'always',
|
|
518
|
+
autoClosingQuotes: editorPrefs.autoClosingQuotes || 'always',
|
|
519
|
+
autoIndent: editorPrefs.autoIndent || 'full',
|
|
520
|
+
formatOnPaste: editorPrefs.formatOnPaste !== false,
|
|
521
|
+
formatOnType: editorPrefs.formatOnType !== false,
|
|
522
|
+
quickSuggestions: editorPrefs.quickSuggestions !== false,
|
|
523
|
+
wordBasedSuggestions: editorPrefs.wordBasedSuggestions || 'matchingDocuments',
|
|
524
|
+
acceptSuggestionOnEnter: editorPrefs.acceptSuggestionOnEnter || 'on',
|
|
525
|
+
linkedEditing: true,
|
|
494
526
|
fixedOverflowWidgets: true,
|
|
495
|
-
hover: { above: false }
|
|
496
|
-
autoClosingBrackets: 'always',
|
|
497
|
-
autoClosingQuotes: 'always',
|
|
498
|
-
autoIndent: 'full',
|
|
499
|
-
formatOnPaste: true,
|
|
500
|
-
formatOnType: true
|
|
527
|
+
hover: { above: false }
|
|
501
528
|
});
|
|
502
529
|
|
|
503
530
|
if (tab.viewState) {
|
|
504
531
|
editor.restoreViewState(tab.viewState);
|
|
505
532
|
}
|
|
506
533
|
|
|
534
|
+
// Restore the find widget state from the previous editor (when persistFindState is on).
|
|
535
|
+
if (editorPrefs.persistFindState !== false && window.__mbeditorFindState && window.__mbeditorFindState.searchString) {
|
|
536
|
+
var _savedFind = window.__mbeditorFindState;
|
|
537
|
+
if (_savedFind.isRevealed) {
|
|
538
|
+
// Open the widget first (setTimeout so layout is ready), then re-apply the
|
|
539
|
+
// saved query — actions.find may seed from the selection and overwrite it.
|
|
540
|
+
setTimeout(function() {
|
|
541
|
+
try {
|
|
542
|
+
editor.trigger('', 'actions.find', null);
|
|
543
|
+
setTimeout(function() {
|
|
544
|
+
try {
|
|
545
|
+
var _fc0 = editor.getContribution('editor.contrib.findController');
|
|
546
|
+
if (_fc0 && _fc0.getState) {
|
|
547
|
+
_fc0.getState().change({
|
|
548
|
+
searchString: _savedFind.searchString,
|
|
549
|
+
isRegex: !!_savedFind.isRegex,
|
|
550
|
+
matchCase: !!_savedFind.matchCase,
|
|
551
|
+
wholeWord: !!_savedFind.wholeWord
|
|
552
|
+
}, false);
|
|
553
|
+
}
|
|
554
|
+
} catch (e) {}
|
|
555
|
+
}, 0);
|
|
556
|
+
} catch (e) {}
|
|
557
|
+
}, 0);
|
|
558
|
+
} else {
|
|
559
|
+
// Widget was closed — just seed the state silently so Ctrl+F pre-fills it.
|
|
560
|
+
try {
|
|
561
|
+
var _fc0 = editor.getContribution('editor.contrib.findController');
|
|
562
|
+
if (_fc0 && _fc0.getState) {
|
|
563
|
+
_fc0.getState().change({
|
|
564
|
+
searchString: _savedFind.searchString,
|
|
565
|
+
isRegex: !!_savedFind.isRegex,
|
|
566
|
+
matchCase: !!_savedFind.matchCase,
|
|
567
|
+
wholeWord: !!_savedFind.wholeWord
|
|
568
|
+
}, false);
|
|
569
|
+
}
|
|
570
|
+
} catch (e) {}
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
|
|
507
574
|
monacoRef.current = editor;
|
|
508
575
|
window.__mbeditorActiveEditor = editor;
|
|
509
576
|
setEditorReady(true);
|
|
@@ -634,6 +701,26 @@ var EditorPanel = function EditorPanel(_ref) {
|
|
|
634
701
|
_me.aviBase = aviBaseRef.current;
|
|
635
702
|
_me.aviMax = aviMaxRef.current;
|
|
636
703
|
}
|
|
704
|
+
// Save the current find widget state so it can be restored on next tab switch.
|
|
705
|
+
// Only overwrite the global state when there is an actual search string — this
|
|
706
|
+
// prevents a blank/fresh editor from clobbering the shared query.
|
|
707
|
+
if (editorPrefs.persistFindState !== false) {
|
|
708
|
+
try {
|
|
709
|
+
var _fc = editor.getContribution('editor.contrib.findController');
|
|
710
|
+
if (_fc && _fc.getState) {
|
|
711
|
+
var _fs = _fc.getState();
|
|
712
|
+
if (_fs.searchString) {
|
|
713
|
+
window.__mbeditorFindState = {
|
|
714
|
+
searchString: _fs.searchString,
|
|
715
|
+
isRegex: _fs.isRegex,
|
|
716
|
+
matchCase: _fs.matchCase,
|
|
717
|
+
wholeWord: _fs.wholeWord,
|
|
718
|
+
isRevealed: _fs.isRevealed
|
|
719
|
+
};
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
} catch (e) {}
|
|
723
|
+
}
|
|
637
724
|
if (window.__mbeditorActiveEditor === editor) {
|
|
638
725
|
window.__mbeditorActiveEditor = null;
|
|
639
726
|
}
|
|
@@ -700,6 +787,8 @@ var EditorPanel = function EditorPanel(_ref) {
|
|
|
700
787
|
monacoRef.current.updateOptions({
|
|
701
788
|
fontSize: editorPrefs.fontSize || 13,
|
|
702
789
|
fontFamily: editorPrefs.fontFamily || "'JetBrains Mono', 'Fira Code', Consolas, 'Courier New', monospace",
|
|
790
|
+
lineHeight: editorPrefs.lineHeight || 0,
|
|
791
|
+
letterSpacing: editorPrefs.letterSpacing || 0,
|
|
703
792
|
tabSize: editorPrefs.tabSize || 4,
|
|
704
793
|
insertSpaces: typeof editorPrefs.insertSpaces === 'boolean' ? editorPrefs.insertSpaces : false,
|
|
705
794
|
wordWrap: editorPrefs.wordWrap || 'off',
|
|
@@ -707,7 +796,21 @@ var EditorPanel = function EditorPanel(_ref) {
|
|
|
707
796
|
renderWhitespace: editorPrefs.renderWhitespace || 'none',
|
|
708
797
|
minimap: { enabled: !!(editorPrefs.minimap) },
|
|
709
798
|
scrollBeyondLastLine: !!(editorPrefs.scrollBeyondLastLine),
|
|
710
|
-
bracketPairColorization: { enabled: editorPrefs.bracketPairColorization !== false }
|
|
799
|
+
bracketPairColorization: { enabled: editorPrefs.bracketPairColorization !== false },
|
|
800
|
+
renderLineHighlight: editorPrefs.renderLineHighlight || 'none',
|
|
801
|
+
cursorStyle: editorPrefs.cursorStyle || 'line',
|
|
802
|
+
cursorBlinking: editorPrefs.cursorBlinking || 'blink',
|
|
803
|
+
folding: editorPrefs.folding !== false,
|
|
804
|
+
smoothScrolling: !!(editorPrefs.smoothScrolling),
|
|
805
|
+
mouseWheelZoom: !!(editorPrefs.mouseWheelZoom),
|
|
806
|
+
autoClosingBrackets: editorPrefs.autoClosingBrackets || 'always',
|
|
807
|
+
autoClosingQuotes: editorPrefs.autoClosingQuotes || 'always',
|
|
808
|
+
autoIndent: editorPrefs.autoIndent || 'full',
|
|
809
|
+
formatOnPaste: editorPrefs.formatOnPaste !== false,
|
|
810
|
+
formatOnType: editorPrefs.formatOnType !== false,
|
|
811
|
+
quickSuggestions: editorPrefs.quickSuggestions !== false,
|
|
812
|
+
wordBasedSuggestions: editorPrefs.wordBasedSuggestions || 'matchingDocuments',
|
|
813
|
+
acceptSuggestionOnEnter: editorPrefs.acceptSuggestionOnEnter || 'on'
|
|
711
814
|
});
|
|
712
815
|
}
|
|
713
816
|
}, [editorPrefs]);
|
|
@@ -1146,6 +1249,9 @@ var EditorPanel = function EditorPanel(_ref) {
|
|
|
1146
1249
|
var ext = parts.length > 1 ? parts.pop().toLowerCase() : '';
|
|
1147
1250
|
var isImage = tab.isImage || IMAGE_EXTENSIONS.includes(ext);
|
|
1148
1251
|
var isMarkdown = ['md', 'markdown'].includes(ext);
|
|
1252
|
+
var fileBaseName = (tab.path || '').split('/').pop().toLowerCase();
|
|
1253
|
+
var isRubyFile = ext === 'rb' || ext === 'ruby' || ext === 'gemspec' ||
|
|
1254
|
+
fileBaseName === 'gemfile' || fileBaseName === 'gemfile.lock' || fileBaseName === 'rakefile';
|
|
1149
1255
|
|
|
1150
1256
|
useEffect(function () {
|
|
1151
1257
|
if (isMarkdown && window.marked) {
|
|
@@ -1157,11 +1263,57 @@ var EditorPanel = function EditorPanel(_ref) {
|
|
|
1157
1263
|
renderer.html = function (token) {
|
|
1158
1264
|
return '<pre>' + escapeHtml(typeof token === 'object' ? token.text : token) + '</pre>';
|
|
1159
1265
|
};
|
|
1266
|
+
// Sanitize link/image hrefs to block javascript: and data: schemes.
|
|
1267
|
+
var _origLink = renderer.link.bind(renderer);
|
|
1268
|
+
var _origImage = renderer.image.bind(renderer);
|
|
1269
|
+
var SAFE_HREF_SCHEME = /^(https?:|mailto:|#|\/)/i;
|
|
1270
|
+
var safeHref = function(href) {
|
|
1271
|
+
if (!href) return href;
|
|
1272
|
+
return SAFE_HREF_SCHEME.test(href.trim()) ? href : '#';
|
|
1273
|
+
};
|
|
1274
|
+
renderer.link = function(token) {
|
|
1275
|
+
if (token && typeof token === 'object') { token = Object.assign({}, token, { href: safeHref(token.href) }); }
|
|
1276
|
+
return _origLink(token);
|
|
1277
|
+
};
|
|
1278
|
+
renderer.image = function(token) {
|
|
1279
|
+
if (token && typeof token === 'object') { token = Object.assign({}, token, { href: safeHref(token.href) }); }
|
|
1280
|
+
return _origImage(token);
|
|
1281
|
+
};
|
|
1160
1282
|
setMarkup(window.marked.parse(markdownContent, { renderer: renderer }));
|
|
1161
1283
|
})();
|
|
1162
1284
|
}
|
|
1163
1285
|
}, [markdownContent, isMarkdown]);
|
|
1164
1286
|
|
|
1287
|
+
// Click-outside handler to close the methods dropdown
|
|
1288
|
+
useEffect(function() {
|
|
1289
|
+
if (!methodsOpen) return;
|
|
1290
|
+
function handleClickOutside(e) {
|
|
1291
|
+
var btn = methodsBtnRef.current;
|
|
1292
|
+
// Close if click is not on the button (the dropdown uses onMouseDown with preventDefault)
|
|
1293
|
+
if (btn && !btn.contains(e.target)) {
|
|
1294
|
+
setMethodsOpen(false);
|
|
1295
|
+
}
|
|
1296
|
+
}
|
|
1297
|
+
document.addEventListener('mousedown', handleClickOutside);
|
|
1298
|
+
return function() { document.removeEventListener('mousedown', handleClickOutside); };
|
|
1299
|
+
}, [methodsOpen]);
|
|
1300
|
+
|
|
1301
|
+
// Parse all method definitions from the current Monaco model
|
|
1302
|
+
function parseRubyMethods(model) {
|
|
1303
|
+
var methods = [];
|
|
1304
|
+
var lineCount = model.getLineCount();
|
|
1305
|
+
var DEF_RE = /^\s*def\s+(self\.)?([a-zA-Z_][a-zA-Z0-9_?!=]*)/;
|
|
1306
|
+
for (var i = 1; i <= lineCount; i++) {
|
|
1307
|
+
var line = model.getLineContent(i);
|
|
1308
|
+
var m = DEF_RE.exec(line);
|
|
1309
|
+
if (m) {
|
|
1310
|
+
var selfPrefix = m[1] ? 'self.' : '';
|
|
1311
|
+
methods.push({ line: i, name: selfPrefix + m[2] });
|
|
1312
|
+
}
|
|
1313
|
+
}
|
|
1314
|
+
return methods;
|
|
1315
|
+
}
|
|
1316
|
+
|
|
1165
1317
|
if (tab.fileNotFound) {
|
|
1166
1318
|
return React.createElement(
|
|
1167
1319
|
'div',
|
|
@@ -1192,11 +1344,10 @@ var EditorPanel = function EditorPanel(_ref) {
|
|
|
1192
1344
|
}
|
|
1193
1345
|
|
|
1194
1346
|
if (isImage) {
|
|
1195
|
-
var basePath = (window.MBEDITOR_BASE_PATH || '/mbeditor').replace(/\/$/, '');
|
|
1196
1347
|
return React.createElement(
|
|
1197
1348
|
'div',
|
|
1198
1349
|
{ className: 'monaco-container', style: { display: 'flex', alignItems: 'center', justifyContent: 'center', background: '#1e1e1e' } },
|
|
1199
|
-
React.createElement('img', { src:
|
|
1350
|
+
React.createElement('img', { src: window.mbeditorBasePath() + '/raw?path=' + encodeURIComponent(tab.path), style: { maxWidth: '90%', maxHeight: '90%', objectFit: 'contain' }, alt: tab.name })
|
|
1200
1351
|
);
|
|
1201
1352
|
}
|
|
1202
1353
|
|
|
@@ -1241,6 +1392,28 @@ var EditorPanel = function EditorPanel(_ref) {
|
|
|
1241
1392
|
React.createElement('i', { className: 'fas fa-history', style: { marginRight: editorPrefs.toolbarIconOnly ? 0 : '5px', flexShrink: 0 } }),
|
|
1242
1393
|
!editorPrefs.toolbarIconOnly && React.createElement('span', { className: 'ide-toolbar-label' }, 'History')
|
|
1243
1394
|
),
|
|
1395
|
+
isRubyFile && React.createElement(
|
|
1396
|
+
'button',
|
|
1397
|
+
{
|
|
1398
|
+
ref: methodsBtnRef,
|
|
1399
|
+
className: 'ide-icon-btn' + (methodsOpen ? ' active' : ''),
|
|
1400
|
+
onClick: function() {
|
|
1401
|
+
var nextOpen = !methodsOpen;
|
|
1402
|
+
if (nextOpen) {
|
|
1403
|
+
var model = monacoRef.current && monacoRef.current.getModel();
|
|
1404
|
+
setMethodsList(model ? parseRubyMethods(model) : []);
|
|
1405
|
+
if (methodsBtnRef.current) {
|
|
1406
|
+
var rect = methodsBtnRef.current.getBoundingClientRect();
|
|
1407
|
+
setMethodsDropdownPos({ top: rect.bottom + 4, right: window.innerWidth - rect.right });
|
|
1408
|
+
}
|
|
1409
|
+
}
|
|
1410
|
+
setMethodsOpen(nextOpen);
|
|
1411
|
+
},
|
|
1412
|
+
title: 'Jump to Method'
|
|
1413
|
+
},
|
|
1414
|
+
React.createElement('i', { className: 'fas fa-list-ul', style: { marginRight: editorPrefs.toolbarIconOnly ? 0 : '5px', flexShrink: 0 } }),
|
|
1415
|
+
!editorPrefs.toolbarIconOnly && React.createElement('span', { className: 'ide-toolbar-label' }, 'Methods')
|
|
1416
|
+
),
|
|
1244
1417
|
gitAvailable && React.createElement(
|
|
1245
1418
|
'button',
|
|
1246
1419
|
{
|
|
@@ -1265,6 +1438,35 @@ var EditorPanel = function EditorPanel(_ref) {
|
|
|
1265
1438
|
)
|
|
1266
1439
|
),
|
|
1267
1440
|
React.createElement('div', { ref: editorRef, className: 'monaco-container', style: { flex: 1, minHeight: 0 } }),
|
|
1441
|
+
methodsOpen && methodsDropdownPos && React.createElement(
|
|
1442
|
+
'div',
|
|
1443
|
+
{
|
|
1444
|
+
className: 'ide-methods-dropdown',
|
|
1445
|
+
style: { position: 'fixed', top: methodsDropdownPos.top + 'px', right: methodsDropdownPos.right + 'px', left: 'auto', zIndex: 9900 }
|
|
1446
|
+
},
|
|
1447
|
+
methodsList.length === 0
|
|
1448
|
+
? React.createElement('div', { className: 'ide-methods-dropdown-empty' }, 'No methods found')
|
|
1449
|
+
: methodsList.map(function(m) {
|
|
1450
|
+
return React.createElement(
|
|
1451
|
+
'div',
|
|
1452
|
+
{
|
|
1453
|
+
key: m.line,
|
|
1454
|
+
className: 'ide-methods-dropdown-item',
|
|
1455
|
+
onMouseDown: function(e) {
|
|
1456
|
+
e.preventDefault();
|
|
1457
|
+
setMethodsOpen(false);
|
|
1458
|
+
if (monacoRef.current) {
|
|
1459
|
+
monacoRef.current.revealLineInCenter(m.line);
|
|
1460
|
+
monacoRef.current.setPosition({ lineNumber: m.line, column: 1 });
|
|
1461
|
+
monacoRef.current.focus();
|
|
1462
|
+
}
|
|
1463
|
+
}
|
|
1464
|
+
},
|
|
1465
|
+
React.createElement('span', { className: 'ide-methods-dropdown-line' }, m.line),
|
|
1466
|
+
m.name
|
|
1467
|
+
);
|
|
1468
|
+
})
|
|
1469
|
+
),
|
|
1268
1470
|
React.createElement('div', { ref: vimStatusRef, className: 'vim-statusbar', style: { display: editorPrefs.vimMode ? 'flex' : 'none', height: '22px', alignItems: 'center', padding: '0 10px', fontFamily: "'JetBrains Mono', 'Fira Code', Consolas, monospace", fontSize: '12px', background: 'var(--ide-statusbar-bg, #1e1e2e)', color: 'var(--ide-statusbar-fg, #9cdcfe)', borderTop: '1px solid var(--ide-border, #3e3e3e)', flexShrink: 0, userSelect: 'none', letterSpacing: '0.02em' } })
|
|
1269
1471
|
);
|
|
1270
1472
|
};
|
|
@@ -14,8 +14,19 @@ var GitPanel = function GitPanel(_ref) {
|
|
|
14
14
|
var _s1 = useState(true); var localExpanded = _s1[0]; var setLocalExpanded = _s1[1];
|
|
15
15
|
var _s2 = useState(true); var branchExpanded = _s2[0]; var setBranchExpanded = _s2[1];
|
|
16
16
|
var _s3 = useState(true); var historyExpanded= _s3[0]; var setHistoryExpanded= _s3[1];
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
|
|
18
|
+
// { [hash]: true|false } — persisted to localStorage keyed by root path so
|
|
19
|
+
// users don't need to re-expand commits on every page load.
|
|
20
|
+
var _lsKey = 'mbeditor_expanded_commits_' + (window.MBEDITOR_BASE_PATH || '');
|
|
21
|
+
var _lsInitial = (function() {
|
|
22
|
+
try { return JSON.parse(localStorage.getItem(_lsKey)) || {}; } catch(e) { return {}; }
|
|
23
|
+
})();
|
|
24
|
+
var _s4 = useState(_lsInitial); var expandedCommits = _s4[0]; var setExpandedCommits = _s4[1];
|
|
25
|
+
|
|
26
|
+
// Persist expandedCommits whenever it changes.
|
|
27
|
+
useEffect(function() {
|
|
28
|
+
try { localStorage.setItem(_lsKey, JSON.stringify(expandedCommits)); } catch(e) {}
|
|
29
|
+
}, [expandedCommits]);
|
|
19
30
|
// { [hash]: { loading, files: [{status,path}], error } }
|
|
20
31
|
var _s5 = useState({}); var commitFiles = _s5[0]; var setCommitFiles = _s5[1];
|
|
21
32
|
var _s6 = useState(false); var refreshing = _s6[0]; var setRefreshing = _s6[1];
|
|
@@ -55,8 +66,7 @@ var GitPanel = function GitPanel(_ref) {
|
|
|
55
66
|
setRedmineLoading(true);
|
|
56
67
|
setRedmineIssue(null);
|
|
57
68
|
setRedmineError(null);
|
|
58
|
-
|
|
59
|
-
axios.get(basePath + '/redmine/issue/' + redmineTicketId)
|
|
69
|
+
axios.get(window.mbeditorBasePath() + '/redmine/issue/' + redmineTicketId)
|
|
60
70
|
.then(function (res) {
|
|
61
71
|
setRedmineIssue(res.data);
|
|
62
72
|
setRedmineLoading(false);
|