mbeditor 0.1.4 → 0.1.6
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/README.md +16 -1
- data/app/assets/javascripts/mbeditor/application.js +1 -0
- data/app/assets/javascripts/mbeditor/components/DiffViewer.js +3 -1
- data/app/assets/javascripts/mbeditor/components/EditorPanel.js +66 -13
- data/app/assets/javascripts/mbeditor/components/FileTree.js +0 -17
- data/app/assets/javascripts/mbeditor/components/GitPanel.js +115 -32
- data/app/assets/javascripts/mbeditor/components/MbeditorApp.js +253 -30
- data/app/assets/javascripts/mbeditor/components/QuickOpenDialog.js +50 -18
- data/app/assets/javascripts/mbeditor/components/TabBar.js +2 -2
- data/app/assets/javascripts/mbeditor/editor_plugins.js +49 -0
- data/app/assets/javascripts/mbeditor/editor_store.js +1 -0
- data/app/assets/javascripts/mbeditor/file_icon.js +30 -0
- data/app/assets/javascripts/mbeditor/file_service.js +6 -1
- data/app/assets/javascripts/mbeditor/search_service.js +8 -4
- data/app/assets/stylesheets/mbeditor/application.css +51 -0
- data/app/assets/stylesheets/mbeditor/editor.css +379 -5
- data/app/controllers/mbeditor/editors_controller.rb +121 -11
- data/app/controllers/mbeditor/git_controller.rb +14 -4
- data/app/services/mbeditor/git_service.rb +11 -1
- data/app/views/layouts/mbeditor/application.html.erb +5 -1
- data/config/routes.rb +1 -0
- data/lib/mbeditor/rack/silence_ping_request.rb +20 -6
- data/lib/mbeditor/version.rb +1 -1
- 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/ts_worker.js +5 -0
- metadata +6 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 706d2307424a25e3a1b19683326d62de74d98568f755971d6eeb592ad5bae1a4
|
|
4
|
+
data.tar.gz: 8cfb289f2cc0fd18eb2e8c55ae3a2f766712d68231e695b2e02a1e7778207c2d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8b691b32511249206e9af0e7ceeb6b26f9cf5bf63cd4b035e8b7fa9411ba05480b4c8795cdcf7a20ffefd536803a61e79c84edb7874fad7d7308aa4dca622b2f
|
|
7
|
+
data.tar.gz: 41c25d7ad6113629fd50267ceff4e9f07490627f9fc2a8eba2fc2804743e8ba7e3c3c6bedc30c4a225e6a177c3354b1282a88df874086bf2e969bced2d20ea65
|
data/README.md
CHANGED
|
@@ -47,7 +47,7 @@ mount Mbeditor::Engine, at: "/mbeditor"
|
|
|
47
47
|
Use a single initializer to set the engine options you need:
|
|
48
48
|
|
|
49
49
|
```ruby
|
|
50
|
-
|
|
50
|
+
Mbeditor.configure do |config|
|
|
51
51
|
config.allowed_environments = [:development]
|
|
52
52
|
# config.workspace_root = Rails.root
|
|
53
53
|
config.excluded_paths = %w[.git tmp log node_modules .bundle coverage vendor/bundle]
|
|
@@ -70,6 +70,17 @@ Available options:
|
|
|
70
70
|
- `redmine_url` sets the Redmine base URL. Required when `redmine_enabled` is `true`.
|
|
71
71
|
- `redmine_api_key` sets the Redmine API key. Required when `redmine_enabled` is `true`.
|
|
72
72
|
|
|
73
|
+
## Keyboard Shortcuts
|
|
74
|
+
|
|
75
|
+
| Shortcut | Action |
|
|
76
|
+
|----------|--------|
|
|
77
|
+
| `Ctrl+P` | Quick-open file by name |
|
|
78
|
+
| `Ctrl+S` | Save the active file |
|
|
79
|
+
| `Ctrl+Shift+S` | Save all dirty files |
|
|
80
|
+
| `Alt+Shift+F` | Format the active file |
|
|
81
|
+
| `Ctrl+Shift+G` | Toggle the git panel |
|
|
82
|
+
| `Ctrl+Z` / `Ctrl+Y` | Undo / Redo (Monaco built-in) |
|
|
83
|
+
|
|
73
84
|
## Host Requirements (Optional)
|
|
74
85
|
The gem keeps host/tooling responsibilities in the host app:
|
|
75
86
|
- `rubocop` and `rubocop-rails` gems (optional, required for Ruby lint/format endpoints)
|
|
@@ -99,6 +110,10 @@ The gem includes syntax highlighting for common Rails and React development file
|
|
|
99
110
|
|
|
100
111
|
These language modules are packaged locally with the gem for true offline operation. No network fallback is needed—all highlighting works without internet connectivity.
|
|
101
112
|
|
|
113
|
+
## Asset Pipeline
|
|
114
|
+
|
|
115
|
+
Mbeditor requires **Sprockets** (`sprockets-rails >= 3.4`). Host apps using **Propshaft** as their asset pipeline are not supported — the engine depends on Sprockets directives to load its JavaScript and CSS assets.
|
|
116
|
+
|
|
102
117
|
## Development
|
|
103
118
|
|
|
104
119
|
A minimal dummy Rails app is included for local development and testing:
|
|
@@ -83,10 +83,12 @@ var DiffViewer = function DiffViewer(_ref) {
|
|
|
83
83
|
|
|
84
84
|
function getLanguageForPath(filePath) {
|
|
85
85
|
if (!filePath) return 'plaintext';
|
|
86
|
+
var fileName = filePath.split('/').pop().toLowerCase();
|
|
87
|
+
if (fileName === 'gemfile' || fileName === 'gemfile.lock' || fileName === 'rakefile') return 'ruby';
|
|
86
88
|
var ext = filePath.split('.').pop().toLowerCase();
|
|
87
89
|
var map = {
|
|
88
90
|
'rb': 'ruby', 'js': 'javascript', 'jsx': 'javascript',
|
|
89
|
-
'ts': '
|
|
91
|
+
'ts': 'typescript', 'tsx': 'typescript',
|
|
90
92
|
'json': 'json', 'yml': 'yaml', 'yaml': 'yaml',
|
|
91
93
|
'css': 'css', 'scss': 'scss', 'html': 'html',
|
|
92
94
|
'xml': 'xml', 'md': 'markdown', 'sh': 'shell'
|
|
@@ -15,9 +15,12 @@ var EditorPanel = function EditorPanel(_ref) {
|
|
|
15
15
|
var onContentChange = _ref.onContentChange;
|
|
16
16
|
var markers = _ref.markers;
|
|
17
17
|
var gitAvailable = _ref.gitAvailable === true;
|
|
18
|
+
var onFormat = _ref.onFormat;
|
|
19
|
+
var editorPrefs = _ref.editorPrefs || {};
|
|
18
20
|
|
|
19
21
|
var editorRef = useRef(null);
|
|
20
22
|
var monacoRef = useRef(null);
|
|
23
|
+
var latestContentRef = useRef('');
|
|
21
24
|
|
|
22
25
|
var _useState = useState('');
|
|
23
26
|
var _useState2 = _slicedToArray(_useState, 2);
|
|
@@ -42,6 +45,9 @@ var EditorPanel = function EditorPanel(_ref) {
|
|
|
42
45
|
var blameDecorationsRef = useRef([]);
|
|
43
46
|
var blameZoneIdsRef = useRef([]);
|
|
44
47
|
|
|
48
|
+
var onFormatRef = useRef(onFormat);
|
|
49
|
+
onFormatRef.current = onFormat;
|
|
50
|
+
|
|
45
51
|
var clearBlameZones = function clearBlameZones(editor) {
|
|
46
52
|
if (!editor) return;
|
|
47
53
|
if (blameZoneIdsRef.current.length === 0) return;
|
|
@@ -75,16 +81,23 @@ var EditorPanel = function EditorPanel(_ref) {
|
|
|
75
81
|
window.MbeditorEditorPlugins.registerGlobalExtensions(window.monaco);
|
|
76
82
|
}
|
|
77
83
|
|
|
78
|
-
var
|
|
84
|
+
var fileName = tab.path.split('/').pop() || '';
|
|
85
|
+
var parts = fileName.split('.');
|
|
79
86
|
var extension = parts.length > 1 ? parts.pop().toLowerCase() : '';
|
|
80
87
|
var language = 'plaintext';
|
|
81
|
-
switch (
|
|
82
|
-
case '
|
|
88
|
+
switch (fileName.toLowerCase()) {
|
|
89
|
+
case 'gemfile':
|
|
90
|
+
case 'gemfile.lock':
|
|
91
|
+
case 'rakefile':
|
|
83
92
|
language = 'ruby';break;
|
|
93
|
+
default:
|
|
94
|
+
switch (extension) {
|
|
95
|
+
case 'rb':case 'ruby':case 'gemspec':
|
|
96
|
+
language = 'ruby';break;
|
|
84
97
|
case 'js':case 'jsx':
|
|
85
98
|
language = 'javascript';break;
|
|
86
99
|
case 'ts':case 'tsx':
|
|
87
|
-
language = '
|
|
100
|
+
language = 'typescript';break;
|
|
88
101
|
case 'css':case 'scss':case 'sass':
|
|
89
102
|
language = 'css';break;
|
|
90
103
|
case 'html':case 'erb':
|
|
@@ -101,6 +114,7 @@ var EditorPanel = function EditorPanel(_ref) {
|
|
|
101
114
|
language = 'shell';break;
|
|
102
115
|
case 'png':case 'jpg':case 'jpeg':case 'gif':case 'svg':case 'ico':case 'webp':case 'bmp':case 'avif':
|
|
103
116
|
language = 'image';break;
|
|
117
|
+
}
|
|
104
118
|
}
|
|
105
119
|
|
|
106
120
|
if (language === 'image') return;
|
|
@@ -108,15 +122,15 @@ var EditorPanel = function EditorPanel(_ref) {
|
|
|
108
122
|
var editor = window.monaco.editor.create(editorRef.current, {
|
|
109
123
|
value: tab.content,
|
|
110
124
|
language: language,
|
|
111
|
-
theme: 'vs-dark',
|
|
125
|
+
theme: editorPrefs.theme || 'vs-dark',
|
|
112
126
|
automaticLayout: true,
|
|
113
127
|
minimap: { enabled: false },
|
|
114
128
|
renderLineHighlight: 'none',
|
|
115
129
|
bracketPairColorization: { enabled: true },
|
|
116
|
-
fontFamily: "'JetBrains Mono', 'Fira Code', Consolas, 'Courier New', monospace",
|
|
117
|
-
fontSize: 13,
|
|
118
|
-
tabSize: 4,
|
|
119
|
-
insertSpaces:
|
|
130
|
+
fontFamily: editorPrefs.fontFamily || "'JetBrains Mono', 'Fira Code', Consolas, 'Courier New', monospace",
|
|
131
|
+
fontSize: editorPrefs.fontSize || 13,
|
|
132
|
+
tabSize: editorPrefs.tabSize || 4,
|
|
133
|
+
insertSpaces: typeof editorPrefs.insertSpaces === 'boolean' ? editorPrefs.insertSpaces : false,
|
|
120
134
|
wordWrap: 'on',
|
|
121
135
|
linkedEditing: true, // Enables Auto-Rename Tag natively!
|
|
122
136
|
fixedOverflowWidgets: true,
|
|
@@ -135,7 +149,20 @@ var EditorPanel = function EditorPanel(_ref) {
|
|
|
135
149
|
monacoRef.current = editor;
|
|
136
150
|
window.__mbeditorActiveEditor = editor;
|
|
137
151
|
|
|
152
|
+
// Stash the workspace-relative path on the model so code-action providers
|
|
153
|
+
// can identify which file they are operating on without needing React state.
|
|
138
154
|
var modelObj = editor.getModel();
|
|
155
|
+
if (modelObj) modelObj._mbeditorPath = tab.path;
|
|
156
|
+
|
|
157
|
+
var formatActionDisposable = editor.addAction({
|
|
158
|
+
id: 'mbeditor.formatDocument',
|
|
159
|
+
label: 'Format Document',
|
|
160
|
+
contextMenuGroupId: '1_modification',
|
|
161
|
+
contextMenuOrder: 1.5,
|
|
162
|
+
run: function() {
|
|
163
|
+
if (onFormatRef.current) onFormatRef.current();
|
|
164
|
+
}
|
|
165
|
+
});
|
|
139
166
|
|
|
140
167
|
var editorPluginDisposable = null;
|
|
141
168
|
if (window.MbeditorEditorPlugins && window.MbeditorEditorPlugins.attachEditorFeatures) {
|
|
@@ -145,7 +172,7 @@ var EditorPanel = function EditorPanel(_ref) {
|
|
|
145
172
|
// Change listener
|
|
146
173
|
var contentDisposable = modelObj.onDidChangeContent(function (e) {
|
|
147
174
|
var val = editor.getValue();
|
|
148
|
-
var currentContent =
|
|
175
|
+
var currentContent = latestContentRef.current;
|
|
149
176
|
|
|
150
177
|
// Normalize before comparing to prevent false positive dirty edits
|
|
151
178
|
var vNorm = val.replace(/\r\n/g, '\n');
|
|
@@ -163,6 +190,7 @@ var EditorPanel = function EditorPanel(_ref) {
|
|
|
163
190
|
window.__mbeditorActiveEditor = null;
|
|
164
191
|
}
|
|
165
192
|
if (editorPluginDisposable) editorPluginDisposable.dispose();
|
|
193
|
+
formatActionDisposable.dispose();
|
|
166
194
|
contentDisposable.dispose();
|
|
167
195
|
editor.dispose();
|
|
168
196
|
};
|
|
@@ -171,7 +199,7 @@ var EditorPanel = function EditorPanel(_ref) {
|
|
|
171
199
|
// Listen for external content changes (e.g. after Format/Save/Load)
|
|
172
200
|
useEffect(function () {
|
|
173
201
|
var editor = monacoRef.current;
|
|
174
|
-
if (editor)
|
|
202
|
+
if (editor) latestContentRef.current = tab.content; // keep ref in sync for closure
|
|
175
203
|
|
|
176
204
|
if (editor && editor.getValue() !== tab.content) {
|
|
177
205
|
if (typeof tab.content !== 'string') return;
|
|
@@ -199,6 +227,21 @@ var EditorPanel = function EditorPanel(_ref) {
|
|
|
199
227
|
}
|
|
200
228
|
}, [tab.content]);
|
|
201
229
|
|
|
230
|
+
// Apply editorPrefs changes to a running editor without remounting
|
|
231
|
+
useEffect(function () {
|
|
232
|
+
if (!window.monaco) return;
|
|
233
|
+
var theme = editorPrefs.theme || 'vs-dark';
|
|
234
|
+
window.monaco.editor.setTheme(theme);
|
|
235
|
+
if (monacoRef.current) {
|
|
236
|
+
monacoRef.current.updateOptions({
|
|
237
|
+
fontSize: editorPrefs.fontSize || 13,
|
|
238
|
+
fontFamily: editorPrefs.fontFamily || "'JetBrains Mono', 'Fira Code', Consolas, 'Courier New', monospace",
|
|
239
|
+
tabSize: editorPrefs.tabSize || 4,
|
|
240
|
+
insertSpaces: typeof editorPrefs.insertSpaces === 'boolean' ? editorPrefs.insertSpaces : false
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
}, [editorPrefs]);
|
|
244
|
+
|
|
202
245
|
// Jump to line if specified
|
|
203
246
|
useEffect(function () {
|
|
204
247
|
if (tab.gotoLine && monacoRef.current) {
|
|
@@ -222,8 +265,13 @@ var EditorPanel = function EditorPanel(_ref) {
|
|
|
222
265
|
var model = monacoRef.current.getModel();
|
|
223
266
|
if (model) {
|
|
224
267
|
var monacoMarkers = markers.map(function (m) {
|
|
268
|
+
var sev = m.severity === 'error'
|
|
269
|
+
? window.monaco.MarkerSeverity.Error
|
|
270
|
+
: window.monaco.MarkerSeverity.Warning;
|
|
225
271
|
return {
|
|
226
|
-
severity:
|
|
272
|
+
severity: sev,
|
|
273
|
+
source: 'rubocop',
|
|
274
|
+
code: m.copName || '',
|
|
227
275
|
message: m.message,
|
|
228
276
|
startLineNumber: m.startLine,
|
|
229
277
|
startColumn: m.startCol,
|
|
@@ -232,6 +280,11 @@ var EditorPanel = function EditorPanel(_ref) {
|
|
|
232
280
|
};
|
|
233
281
|
});
|
|
234
282
|
window.monaco.editor.setModelMarkers(model, 'rubocop', monacoMarkers);
|
|
283
|
+
// Track which cops are autocorrectable so the quick-fix provider can
|
|
284
|
+
// skip lightbulbs for cops that can never be machine-fixed.
|
|
285
|
+
model._mbeditorCorrectableCops = new Set(
|
|
286
|
+
markers.filter(function(m) { return m.correctable && m.copName; }).map(function(m) { return m.copName; })
|
|
287
|
+
);
|
|
235
288
|
}
|
|
236
289
|
}
|
|
237
290
|
}, [markers, tab.id]);
|
|
@@ -385,7 +438,7 @@ var EditorPanel = function EditorPanel(_ref) {
|
|
|
385
438
|
}, [markdownContent, isMarkdown]);
|
|
386
439
|
|
|
387
440
|
if (tab.isDiff) {
|
|
388
|
-
var isDiffDark =
|
|
441
|
+
var isDiffDark = (editorPrefs.theme || 'vs-dark') !== 'vs' && (editorPrefs.theme || 'vs-dark') !== 'hc-light';
|
|
389
442
|
return React.createElement(window.DiffViewer || DiffViewer, {
|
|
390
443
|
path: tab.repoPath || tab.path,
|
|
391
444
|
original: tab.diffOriginal || "",
|
|
@@ -109,23 +109,6 @@ var FileTree = function FileTree(_ref) {
|
|
|
109
109
|
return { badge: key || '?', cssKey: key || 'Q', title: titleMap[key] || 'Status' };
|
|
110
110
|
};
|
|
111
111
|
|
|
112
|
-
window.getFileIcon = function (name) {
|
|
113
|
-
var ext = name.split('.').pop().toLowerCase();
|
|
114
|
-
var lName = name.toLowerCase();
|
|
115
|
-
if (lName === 'gemfile' || ext === 'gemspec' || ext === 'lock') return 'fas fa-gem ruby-icon';
|
|
116
|
-
if (ext === 'rb' || ext === 'rake' || lName === 'rakefile') return 'far fa-gem ruby-icon';
|
|
117
|
-
if (ext === 'jsx' || name.endsWith('.js.jsx')) return 'fas fa-atom react-icon';
|
|
118
|
-
if (ext === 'js' || ext === 'mjs' || ext === 'cjs') return 'fa-brands fa-js js-icon';
|
|
119
|
-
if (ext === 'html') return 'fa-brands fa-html5 html-icon';
|
|
120
|
-
if (ext === 'erb') return 'fa-brands fa-html5 erb-icon';
|
|
121
|
-
if (ext === 'css' || ext === 'scss' || ext === 'sass') return 'fa-brands fa-css3-alt css-icon';
|
|
122
|
-
if (['png', 'jpg', 'jpeg', 'gif', 'svg', 'ico', 'webp', 'bmp', 'avif'].includes(ext)) return 'far fa-file-image image-icon';
|
|
123
|
-
if (ext === 'json') return 'fas fa-code json-icon';
|
|
124
|
-
if (ext === 'md' || ext === 'txt') return 'fas fa-file-alt md-icon';
|
|
125
|
-
if (ext === 'yml' || ext === 'yaml') return 'fas fa-cogs yml-icon';
|
|
126
|
-
return 'far fa-file-code';
|
|
127
|
-
};
|
|
128
|
-
|
|
129
112
|
var handleInlineKeyDown = function handleInlineKeyDown(e) {
|
|
130
113
|
var isRename = !!pendingRename;
|
|
131
114
|
if (e.key === 'Enter') {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
var GitPanel = function GitPanel(_ref) {
|
|
2
2
|
var gitInfo = _ref.gitInfo;
|
|
3
3
|
var error = _ref.error;
|
|
4
|
+
var redmineEnabled = _ref.redmineEnabled;
|
|
4
5
|
var onOpenFile = _ref.onOpenFile;
|
|
5
6
|
var onOpenDiff = _ref.onOpenDiff;
|
|
6
7
|
var onOpenAllChanges = _ref.onOpenAllChanges;
|
|
@@ -8,6 +9,7 @@ var GitPanel = function GitPanel(_ref) {
|
|
|
8
9
|
var onClose = _ref.onClose;
|
|
9
10
|
|
|
10
11
|
var useState = React.useState;
|
|
12
|
+
var useEffect = React.useEffect;
|
|
11
13
|
|
|
12
14
|
var _s1 = useState(true); var localExpanded = _s1[0]; var setLocalExpanded = _s1[1];
|
|
13
15
|
var _s2 = useState(true); var branchExpanded = _s2[0]; var setBranchExpanded = _s2[1];
|
|
@@ -16,6 +18,13 @@ var GitPanel = function GitPanel(_ref) {
|
|
|
16
18
|
var _s4 = useState({}); var expandedCommits = _s4[0]; var setExpandedCommits = _s4[1];
|
|
17
19
|
// { [hash]: { loading, files: [{status,path}], error } }
|
|
18
20
|
var _s5 = useState({}); var commitFiles = _s5[0]; var setCommitFiles = _s5[1];
|
|
21
|
+
var _s6 = useState(false); var refreshing = _s6[0]; var setRefreshing = _s6[1];
|
|
22
|
+
|
|
23
|
+
// ── Redmine state ───────────────────────────────────────────────────────────
|
|
24
|
+
var _sr1 = useState(null); var redmineIssue = _sr1[0]; var setRedmineIssue = _sr1[1];
|
|
25
|
+
var _sr2 = useState(null); var redmineError = _sr2[0]; var setRedmineError = _sr2[1];
|
|
26
|
+
var _sr3 = useState(false); var redmineLoading = _sr3[0]; var setRedmineLoading = _sr3[1];
|
|
27
|
+
var _sr4 = useState(true); var redmineExpanded = _sr4[0]; var setRedmineExpanded = _sr4[1];
|
|
19
28
|
|
|
20
29
|
var workingTree = gitInfo && gitInfo.workingTree || [];
|
|
21
30
|
var unpushedFiles = gitInfo && gitInfo.unpushedFiles || [];
|
|
@@ -31,6 +40,39 @@ var GitPanel = function GitPanel(_ref) {
|
|
|
31
40
|
return Object.assign({}, c, { isLocal: !!localHashes[c.hash] });
|
|
32
41
|
});
|
|
33
42
|
|
|
43
|
+
// Extract the first Redmine ticket ID (#123) from recent branch commit messages
|
|
44
|
+
var redmineTicketId = null;
|
|
45
|
+
if (redmineEnabled) {
|
|
46
|
+
for (var ci = 0; ci < branchCommits.length; ci++) {
|
|
47
|
+
var titleMatch = branchCommits[ci] && branchCommits[ci].title && branchCommits[ci].title.match(/#(\d+)/);
|
|
48
|
+
if (titleMatch) { redmineTicketId = titleMatch[1]; break; }
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Fetch Redmine issue whenever the ticket ID changes
|
|
53
|
+
useEffect(function () {
|
|
54
|
+
if (!redmineEnabled) return;
|
|
55
|
+
if (!redmineTicketId) {
|
|
56
|
+
setRedmineIssue(null);
|
|
57
|
+
setRedmineError(null);
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
setRedmineLoading(true);
|
|
61
|
+
setRedmineIssue(null);
|
|
62
|
+
setRedmineError(null);
|
|
63
|
+
var basePath = (window.MBEDITOR_BASE_PATH || '/mbeditor').replace(/\/$/, '');
|
|
64
|
+
axios.get(basePath + '/redmine/issue/' + redmineTicketId)
|
|
65
|
+
.then(function (res) {
|
|
66
|
+
setRedmineIssue(res.data);
|
|
67
|
+
setRedmineLoading(false);
|
|
68
|
+
})
|
|
69
|
+
.catch(function (err) {
|
|
70
|
+
var msg = (err.response && err.response.data && err.response.data.error) || err.message || 'Failed to load Redmine issue.';
|
|
71
|
+
setRedmineError(msg);
|
|
72
|
+
setRedmineLoading(false);
|
|
73
|
+
});
|
|
74
|
+
}, [redmineEnabled, redmineTicketId]);
|
|
75
|
+
|
|
34
76
|
var statusMeta = function statusMeta(rawStatus) {
|
|
35
77
|
var raw = (rawStatus || '').trim();
|
|
36
78
|
if (raw === '??') return { badge: 'NEW', cssKey: 'A', description: 'Untracked' };
|
|
@@ -46,36 +88,7 @@ var GitPanel = function GitPanel(_ref) {
|
|
|
46
88
|
};
|
|
47
89
|
|
|
48
90
|
var fileIcon = function fileIcon(filename) {
|
|
49
|
-
|
|
50
|
-
var iconMap = {
|
|
51
|
-
'rb': { cls: 'fas fa-gem', color: '#cc342d' },
|
|
52
|
-
'gemspec': { cls: 'fas fa-gem', color: '#cc342d' },
|
|
53
|
-
'js': { cls: 'fab fa-js', color: '#f0db4f' },
|
|
54
|
-
'ts': { cls: 'fas fa-code', color: '#3178c6' },
|
|
55
|
-
'jsx': { cls: 'fab fa-js', color: '#61dafb' },
|
|
56
|
-
'tsx': { cls: 'fas fa-code', color: '#61dafb' },
|
|
57
|
-
'html': { cls: 'fab fa-html5', color: '#e34f26' },
|
|
58
|
-
'htm': { cls: 'fab fa-html5', color: '#e34f26' },
|
|
59
|
-
'erb': { cls: 'fab fa-html5', color: '#cc342d' },
|
|
60
|
-
'css': { cls: 'fab fa-css3-alt', color: '#1572b6' },
|
|
61
|
-
'scss': { cls: 'fab fa-sass', color: '#cc6699' },
|
|
62
|
-
'sass': { cls: 'fab fa-sass', color: '#cc6699' },
|
|
63
|
-
'md': { cls: 'fab fa-markdown', color: '#7f8b97' },
|
|
64
|
-
'json': { cls: 'fas fa-code', color: '#ffe082' },
|
|
65
|
-
'yml': { cls: 'fas fa-cog', color: '#888' },
|
|
66
|
-
'yaml': { cls: 'fas fa-cog', color: '#888' },
|
|
67
|
-
'png': { cls: 'fas fa-image', color: '#aaa' },
|
|
68
|
-
'jpg': { cls: 'fas fa-image', color: '#aaa' },
|
|
69
|
-
'jpeg': { cls: 'fas fa-image', color: '#aaa' },
|
|
70
|
-
'gif': { cls: 'fas fa-image', color: '#aaa' },
|
|
71
|
-
'svg': { cls: 'fas fa-image', color: '#aaa' },
|
|
72
|
-
'txt': { cls: 'fas fa-file-alt', color: '#888' },
|
|
73
|
-
'sh': { cls: 'fas fa-terminal', color: '#89d185' },
|
|
74
|
-
'lock': { cls: 'fas fa-lock', color: '#888' },
|
|
75
|
-
'pdf': { cls: 'fas fa-file-pdf', color: '#f48771' },
|
|
76
|
-
};
|
|
77
|
-
var icon = iconMap[ext] || { cls: 'fas fa-file-code', color: '#7f8b97' };
|
|
78
|
-
return React.createElement('i', { className: icon.cls + ' git-file-type-icon', style: { color: icon.color } });
|
|
91
|
+
return React.createElement('i', { className: (window.getFileIcon ? window.getFileIcon(filename) : 'far fa-file-code') + ' git-file-type-icon' });
|
|
79
92
|
};
|
|
80
93
|
|
|
81
94
|
// Renders a file row used in Local Changes and Changes in Branch sections
|
|
@@ -148,6 +161,23 @@ var GitPanel = function GitPanel(_ref) {
|
|
|
148
161
|
}
|
|
149
162
|
};
|
|
150
163
|
|
|
164
|
+
var handleRefresh = function handleRefresh() {
|
|
165
|
+
if (!onRefresh || refreshing) return;
|
|
166
|
+
|
|
167
|
+
var result;
|
|
168
|
+
try {
|
|
169
|
+
setRefreshing(true);
|
|
170
|
+
result = onRefresh();
|
|
171
|
+
} catch (err) {
|
|
172
|
+
setRefreshing(false);
|
|
173
|
+
throw err;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return Promise.resolve(result).finally(function () {
|
|
177
|
+
setRefreshing(false);
|
|
178
|
+
});
|
|
179
|
+
};
|
|
180
|
+
|
|
151
181
|
var renderCommit = function renderCommit(commit, idx) {
|
|
152
182
|
var isFirst = idx === 0;
|
|
153
183
|
var isLast = idx === branchCommits.length - 1;
|
|
@@ -305,8 +335,8 @@ var GitPanel = function GitPanel(_ref) {
|
|
|
305
335
|
{ className: 'ide-git-panel-actions' },
|
|
306
336
|
onRefresh && React.createElement(
|
|
307
337
|
'button',
|
|
308
|
-
{ className: 'git-header-btn', onClick:
|
|
309
|
-
React.createElement('i', { className: 'fas fa-sync-alt' })
|
|
338
|
+
{ className: 'git-header-btn', onClick: handleRefresh, title: 'Refresh', disabled: refreshing, 'aria-busy': refreshing },
|
|
339
|
+
React.createElement('i', { className: 'fas fa-sync-alt' + (refreshing ? ' fa-spin' : '') })
|
|
310
340
|
),
|
|
311
341
|
onClose && React.createElement(
|
|
312
342
|
'button',
|
|
@@ -318,6 +348,59 @@ var GitPanel = function GitPanel(_ref) {
|
|
|
318
348
|
|
|
319
349
|
error && React.createElement('div', { className: 'git-error' }, error),
|
|
320
350
|
|
|
351
|
+
// ── Redmine Issue Section ───────────────────────────────────────────────
|
|
352
|
+
redmineEnabled && React.createElement(
|
|
353
|
+
'div',
|
|
354
|
+
{ className: 'git-section ' + (redmineExpanded ? 'expanded' : '') + ' git-section--redmine' },
|
|
355
|
+
React.createElement(
|
|
356
|
+
'div',
|
|
357
|
+
{ className: 'git-section-title' },
|
|
358
|
+
React.createElement(
|
|
359
|
+
'div',
|
|
360
|
+
{ className: 'git-section-title-main hoverable', onClick: function () { setRedmineExpanded(!redmineExpanded); } },
|
|
361
|
+
React.createElement(
|
|
362
|
+
'span',
|
|
363
|
+
{ className: 'git-redmine-section-label' },
|
|
364
|
+
React.createElement('i', { className: 'fas ' + (redmineExpanded ? 'fa-chevron-down' : 'fa-chevron-right'), style: { width: '14px', fontSize: '10px' } }),
|
|
365
|
+
'\u2002Redmine',
|
|
366
|
+
redmineTicketId && React.createElement('span', { className: 'git-section-count', style: { marginLeft: '4px' } }, '#' + redmineTicketId)
|
|
367
|
+
),
|
|
368
|
+
redmineIssue && React.createElement('span', { className: 'redmine-badge redmine-badge--section' }, redmineIssue.status)
|
|
369
|
+
)
|
|
370
|
+
),
|
|
371
|
+
redmineExpanded && React.createElement(
|
|
372
|
+
'div',
|
|
373
|
+
{ className: 'git-redmine-content' },
|
|
374
|
+
redmineLoading
|
|
375
|
+
? React.createElement('div', { className: 'git-empty' },
|
|
376
|
+
React.createElement('i', { className: 'fas fa-spinner fa-spin', style: { marginRight: '6px' } }),
|
|
377
|
+
'Loading issue\u2026'
|
|
378
|
+
)
|
|
379
|
+
: !redmineTicketId
|
|
380
|
+
? React.createElement('div', { className: 'git-empty' }, 'No matching ticket found in branch commits.')
|
|
381
|
+
: redmineError
|
|
382
|
+
? React.createElement('div', { className: 'git-redmine-error' },
|
|
383
|
+
React.createElement('i', { className: 'fas fa-exclamation-circle', style: { marginRight: '6px', color: '#f48771' } }),
|
|
384
|
+
redmineError
|
|
385
|
+
)
|
|
386
|
+
: redmineIssue
|
|
387
|
+
? React.createElement(
|
|
388
|
+
'div',
|
|
389
|
+
{ className: 'git-redmine-issue' },
|
|
390
|
+
React.createElement('div', { className: 'git-redmine-title' }, redmineIssue.title),
|
|
391
|
+
redmineIssue.description
|
|
392
|
+
? React.createElement('div', { className: 'git-redmine-desc' }, redmineIssue.description)
|
|
393
|
+
: null,
|
|
394
|
+
React.createElement(
|
|
395
|
+
'div',
|
|
396
|
+
{ className: 'git-redmine-footer' },
|
|
397
|
+
redmineIssue.author || ''
|
|
398
|
+
)
|
|
399
|
+
)
|
|
400
|
+
: null
|
|
401
|
+
)
|
|
402
|
+
),
|
|
403
|
+
|
|
321
404
|
// ── Section 1: Local Changes ────────────────────────────────────────────
|
|
322
405
|
React.createElement(
|
|
323
406
|
'div',
|