mbeditor 0.5.3 → 0.7.1
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 +77 -0
- data/README.md +7 -0
- data/app/assets/javascripts/mbeditor/application.js +3 -0
- data/app/assets/javascripts/mbeditor/components/ChangelogView.js +145 -0
- data/app/assets/javascripts/mbeditor/components/DiffViewer.js +1 -1
- data/app/assets/javascripts/mbeditor/components/EditorPanel.js +359 -31
- data/app/assets/javascripts/mbeditor/components/FileTree.js +177 -116
- data/app/assets/javascripts/mbeditor/components/MbeditorApp.js +952 -143
- data/app/assets/javascripts/mbeditor/components/TabBar.js +9 -0
- data/app/assets/javascripts/mbeditor/conflict_parser.js +48 -0
- data/app/assets/javascripts/mbeditor/editor_plugins.js +420 -67
- data/app/assets/javascripts/mbeditor/editor_store.js +1 -0
- data/app/assets/javascripts/mbeditor/file_service.js +34 -6
- data/app/assets/javascripts/mbeditor/git_service.js +2 -1
- data/app/assets/javascripts/mbeditor/history_service.js +177 -0
- data/app/assets/javascripts/mbeditor/search_service.js +1 -0
- data/app/assets/javascripts/mbeditor/tab_manager.js +8 -5
- data/app/assets/stylesheets/mbeditor/application.css +112 -0
- data/app/assets/stylesheets/mbeditor/editor.css +443 -78
- data/app/channels/mbeditor/editor_channel.rb +5 -41
- data/app/controllers/mbeditor/application_controller.rb +8 -1
- data/app/controllers/mbeditor/editors_controller.rb +276 -654
- data/app/controllers/mbeditor/git_controller.rb +2 -61
- data/app/services/mbeditor/availability_probe.rb +83 -0
- data/app/services/mbeditor/code_search_service.rb +42 -0
- data/app/services/mbeditor/editor_state_service.rb +91 -0
- data/app/services/mbeditor/exclusion_matcher.rb +23 -0
- data/app/services/mbeditor/file_operation_service.rb +68 -0
- data/app/services/mbeditor/file_tree_service.rb +69 -0
- data/app/services/mbeditor/git_combined_diff_service.rb +43 -0
- data/app/services/mbeditor/git_commit_detail_service.rb +46 -0
- data/app/services/mbeditor/git_info_service.rb +151 -0
- data/app/services/mbeditor/git_service.rb +36 -26
- data/app/services/mbeditor/js_definition_service.rb +59 -0
- data/app/services/mbeditor/js_members_service.rb +62 -0
- data/app/services/mbeditor/process_runner.rb +48 -0
- data/app/services/mbeditor/rails_related_files_service.rb +282 -0
- data/app/services/mbeditor/ruby_definition_service.rb +77 -101
- data/app/services/mbeditor/schema_service.rb +270 -0
- data/app/services/mbeditor/search_replace_service.rb +184 -0
- data/app/services/mbeditor/test_runner_service.rb +5 -27
- data/app/views/layouts/mbeditor/application.html.erb +2 -2
- data/config/routes.rb +8 -1
- data/lib/mbeditor/configuration.rb +4 -2
- data/lib/mbeditor/version.rb +1 -1
- data/public/monaco-editor/vs/language/css/cssMode.js +13 -0
- data/public/monaco-editor/vs/language/css/cssWorker.js +77 -0
- data/public/monaco-editor/vs/language/html/htmlMode.js +13 -0
- data/public/monaco-editor/vs/language/html/htmlWorker.js +454 -0
- data/public/monaco-editor/vs/language/json/jsonMode.js +19 -0
- data/public/monaco-editor/vs/language/json/jsonWorker.js +42 -0
- metadata +26 -3
- data/app/services/mbeditor/unused_methods_service.rb +0 -139
|
@@ -95,7 +95,7 @@ var FileService = (function () {
|
|
|
95
95
|
}
|
|
96
96
|
|
|
97
97
|
function runTests(path) {
|
|
98
|
-
return axios.post(window.mbeditorBasePath() + '/test', { path: path }).then(function(res) { return res.data; });
|
|
98
|
+
return axios.post(window.mbeditorBasePath() + '/test', { path: path }, { timeout: 120000 }).then(function(res) { return res.data; });
|
|
99
99
|
}
|
|
100
100
|
|
|
101
101
|
function ping() {
|
|
@@ -177,7 +177,6 @@ var FileService = (function () {
|
|
|
177
177
|
prefetchCache.delete(path);
|
|
178
178
|
return null;
|
|
179
179
|
}
|
|
180
|
-
prefetchCache.delete(path);
|
|
181
180
|
return entry.promise;
|
|
182
181
|
}
|
|
183
182
|
|
|
@@ -189,6 +188,16 @@ var FileService = (function () {
|
|
|
189
188
|
prefetchCache.delete(path);
|
|
190
189
|
}
|
|
191
190
|
|
|
191
|
+
function getJsDefinition(symbol, extraOptions) {
|
|
192
|
+
var config = Object.assign({ params: { symbol: symbol }, timeout: 5000 }, extraOptions || {});
|
|
193
|
+
return axios.get(window.mbeditorBasePath() + '/js_definition', config).then(function(res) { return res.data; });
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function getJsMembers(symbol, extraOptions) {
|
|
197
|
+
var config = Object.assign({ params: { symbol: symbol }, timeout: 5000 }, extraOptions || {});
|
|
198
|
+
return axios.get(window.mbeditorBasePath() + '/js_members', config).then(function(res) { return res.data; });
|
|
199
|
+
}
|
|
200
|
+
|
|
192
201
|
function getModuleMembers(name, extraOptions) {
|
|
193
202
|
var config = Object.assign({ params: { name: name }, timeout: 8000 }, extraOptions || {});
|
|
194
203
|
return axios.get(window.mbeditorBasePath() + '/module_members', config).then(function(res) { return res.data; });
|
|
@@ -199,9 +208,23 @@ var FileService = (function () {
|
|
|
199
208
|
return axios.get(window.mbeditorBasePath() + '/file_includes', config).then(function(res) { return res.data; });
|
|
200
209
|
}
|
|
201
210
|
|
|
202
|
-
function
|
|
203
|
-
|
|
204
|
-
|
|
211
|
+
function getClientConfig() {
|
|
212
|
+
return axios.get(window.mbeditorBasePath() + '/client_config').then(function(res) { return res.data; });
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function getRelatedFiles(path) {
|
|
216
|
+
return axios.get(window.mbeditorBasePath() + '/related_files', { params: { path: path } })
|
|
217
|
+
.then(function(res) { return res.data; });
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function getChangelog() {
|
|
221
|
+
return axios.get(window.mbeditorBasePath() + '/changelog')
|
|
222
|
+
.then(function(res) { return res.data; });
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function getModelSchema(model) {
|
|
226
|
+
return axios.get(window.mbeditorBasePath() + '/model_schema', { params: { model: model } })
|
|
227
|
+
.then(function(res) { return res.data; });
|
|
205
228
|
}
|
|
206
229
|
|
|
207
230
|
return {
|
|
@@ -225,11 +248,16 @@ var FileService = (function () {
|
|
|
225
248
|
saveBranchState: saveBranchState,
|
|
226
249
|
pruneBranchStates: pruneBranchStates,
|
|
227
250
|
getDefinition: getDefinition,
|
|
251
|
+
getJsDefinition: getJsDefinition,
|
|
252
|
+
getJsMembers: getJsMembers,
|
|
228
253
|
prefetch: prefetch,
|
|
229
254
|
getPrefetched: getPrefetched,
|
|
230
255
|
cancelPrefetch: cancelPrefetch,
|
|
231
256
|
getModuleMembers: getModuleMembers,
|
|
232
257
|
getFileIncludes: getFileIncludes,
|
|
233
|
-
|
|
258
|
+
getClientConfig: getClientConfig,
|
|
259
|
+
getRelatedFiles: getRelatedFiles,
|
|
260
|
+
getModelSchema: getModelSchema,
|
|
261
|
+
getChangelog: getChangelog
|
|
234
262
|
};
|
|
235
263
|
})();
|
|
@@ -7,7 +7,8 @@ var GitService = (function () {
|
|
|
7
7
|
gitInfo: data,
|
|
8
8
|
gitInfoError: null
|
|
9
9
|
};
|
|
10
|
-
|
|
10
|
+
var gitSig = function(arr) { return arr.map(function(f) { return f.path + '\x00' + f.status; }).join('\x01'); };
|
|
11
|
+
if (gitSig(files) !== gitSig(current)) {
|
|
11
12
|
stateUpdate.gitFiles = files;
|
|
12
13
|
}
|
|
13
14
|
EditorStore.setState(stateUpdate);
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var HistoryService = (function () {
|
|
4
|
+
// _tracking[filePath] = { branch }
|
|
5
|
+
var _tracking = {};
|
|
6
|
+
// _pending["branch:filePath"] = [[sl,sc,el,ec,text], ...]
|
|
7
|
+
var _pending = {};
|
|
8
|
+
// _bases["branch:filePath"] = "original content" — cleared after first flush
|
|
9
|
+
var _bases = {};
|
|
10
|
+
// _replayingPaths: Set of filePaths currently undergoing Phase 2 replay
|
|
11
|
+
var _replayingPaths = {};
|
|
12
|
+
// _idleTimers["branch:filePath"] = timerHandle
|
|
13
|
+
var _idleTimers = {};
|
|
14
|
+
|
|
15
|
+
var IDLE_MS = 30000;
|
|
16
|
+
|
|
17
|
+
function _key(branch, filePath) {
|
|
18
|
+
return branch + ':' + filePath;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function beginTracking(branch, filePath, baseContent) {
|
|
22
|
+
_tracking[filePath] = { branch: branch };
|
|
23
|
+
var k = _key(branch, filePath);
|
|
24
|
+
_pending[k] = _pending[k] || [];
|
|
25
|
+
_bases[k] = baseContent;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function resumeTracking(branch, filePath) {
|
|
29
|
+
_tracking[filePath] = { branch: branch };
|
|
30
|
+
var k = _key(branch, filePath);
|
|
31
|
+
_pending[k] = _pending[k] || [];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function stopTracking(filePath) {
|
|
35
|
+
var rec = _tracking[filePath];
|
|
36
|
+
if (!rec) return;
|
|
37
|
+
var k = _key(rec.branch, filePath);
|
|
38
|
+
clearTimeout(_idleTimers[k]);
|
|
39
|
+
delete _idleTimers[k];
|
|
40
|
+
flush(rec.branch, filePath);
|
|
41
|
+
delete _tracking[filePath];
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function setReplayInProgress(filePath, inProgress) {
|
|
45
|
+
if (inProgress) {
|
|
46
|
+
_replayingPaths[filePath] = true;
|
|
47
|
+
} else {
|
|
48
|
+
delete _replayingPaths[filePath];
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function recordOps(filePath, changes) {
|
|
53
|
+
if (_replayingPaths[filePath]) return;
|
|
54
|
+
var rec = _tracking[filePath];
|
|
55
|
+
if (!rec) return;
|
|
56
|
+
var k = _key(rec.branch, filePath);
|
|
57
|
+
var bucket = _pending[k] = _pending[k] || [];
|
|
58
|
+
for (var i = 0; i < changes.length; i++) {
|
|
59
|
+
var c = changes[i];
|
|
60
|
+
bucket.push([
|
|
61
|
+
c.range.startLineNumber,
|
|
62
|
+
c.range.startColumn,
|
|
63
|
+
c.range.endLineNumber,
|
|
64
|
+
c.range.endColumn,
|
|
65
|
+
c.text
|
|
66
|
+
]);
|
|
67
|
+
}
|
|
68
|
+
clearTimeout(_idleTimers[k]);
|
|
69
|
+
_idleTimers[k] = setTimeout(function () { flush(rec.branch, filePath); }, IDLE_MS);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function flush(branch, filePath) {
|
|
73
|
+
var k = _key(branch, filePath);
|
|
74
|
+
var ops = _pending[k];
|
|
75
|
+
if (!ops || ops.length === 0) return;
|
|
76
|
+
_pending[k] = [];
|
|
77
|
+
clearTimeout(_idleTimers[k]);
|
|
78
|
+
delete _idleTimers[k];
|
|
79
|
+
|
|
80
|
+
var body = { branch: branch, path: filePath, ops: ops };
|
|
81
|
+
if (_bases.hasOwnProperty(k)) {
|
|
82
|
+
body.base = _bases[k];
|
|
83
|
+
delete _bases[k];
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
try {
|
|
87
|
+
fetch(window.mbeditorBasePath() + '/file_history', {
|
|
88
|
+
method: 'POST',
|
|
89
|
+
headers: {
|
|
90
|
+
'Content-Type': 'application/json',
|
|
91
|
+
'X-Mbeditor-Client': '1'
|
|
92
|
+
},
|
|
93
|
+
body: JSON.stringify(body)
|
|
94
|
+
}).catch(function () {
|
|
95
|
+
// On failure, put ops back so next flush retries
|
|
96
|
+
var existing = _pending[k] || [];
|
|
97
|
+
_pending[k] = ops.concat(existing);
|
|
98
|
+
if (body.base !== undefined && !_bases.hasOwnProperty(k)) {
|
|
99
|
+
_bases[k] = body.base;
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
} catch (e) {}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function flushAll(options) {
|
|
106
|
+
var useKeepalive = options && options.keepalive;
|
|
107
|
+
Object.keys(_tracking).forEach(function (filePath) {
|
|
108
|
+
var rec = _tracking[filePath];
|
|
109
|
+
var k = _key(rec.branch, filePath);
|
|
110
|
+
var ops = _pending[k];
|
|
111
|
+
if (!ops || ops.length === 0) return;
|
|
112
|
+
var remaining = ops.slice();
|
|
113
|
+
_pending[k] = [];
|
|
114
|
+
clearTimeout(_idleTimers[k]);
|
|
115
|
+
delete _idleTimers[k];
|
|
116
|
+
|
|
117
|
+
var body = { branch: rec.branch, path: filePath, ops: remaining };
|
|
118
|
+
if (_bases.hasOwnProperty(k)) {
|
|
119
|
+
body.base = _bases[k];
|
|
120
|
+
delete _bases[k];
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
try {
|
|
124
|
+
fetch(window.mbeditorBasePath() + '/file_history', {
|
|
125
|
+
method: 'POST',
|
|
126
|
+
headers: {
|
|
127
|
+
'Content-Type': 'application/json',
|
|
128
|
+
'X-Mbeditor-Client': '1'
|
|
129
|
+
},
|
|
130
|
+
keepalive: useKeepalive === true,
|
|
131
|
+
body: JSON.stringify(body)
|
|
132
|
+
});
|
|
133
|
+
} catch (e) {}
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function fetchHistory(branch, filePath) {
|
|
138
|
+
return fetch(
|
|
139
|
+
window.mbeditorBasePath() + '/file_history' +
|
|
140
|
+
'?branch=' + encodeURIComponent(branch) +
|
|
141
|
+
'&path=' + encodeURIComponent(filePath),
|
|
142
|
+
{ headers: { 'X-Mbeditor-Client': '1' } }
|
|
143
|
+
).then(function (res) {
|
|
144
|
+
if (!res.ok) return null;
|
|
145
|
+
return res.json();
|
|
146
|
+
}).then(function (data) {
|
|
147
|
+
if (!data || !data.ops || data.ops.length === 0) return null;
|
|
148
|
+
return data;
|
|
149
|
+
}).catch(function () { return null; });
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Global flush triggers
|
|
153
|
+
document.addEventListener('visibilitychange', function () {
|
|
154
|
+
if (document.visibilityState === 'hidden') flushAll({});
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
window.addEventListener('beforeunload', function () {
|
|
158
|
+
flushAll({ keepalive: true });
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
function flushForPath(filePath) {
|
|
162
|
+
var rec = _tracking[filePath];
|
|
163
|
+
if (rec) flush(rec.branch, filePath);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return {
|
|
167
|
+
beginTracking: beginTracking,
|
|
168
|
+
resumeTracking: resumeTracking,
|
|
169
|
+
stopTracking: stopTracking,
|
|
170
|
+
setReplayInProgress: setReplayInProgress,
|
|
171
|
+
recordOps: recordOps,
|
|
172
|
+
flush: flush,
|
|
173
|
+
flushAll: flushAll,
|
|
174
|
+
flushForPath: flushForPath,
|
|
175
|
+
fetchHistory: fetchHistory
|
|
176
|
+
};
|
|
177
|
+
})();
|
|
@@ -61,6 +61,7 @@ var SearchService = (function () {
|
|
|
61
61
|
|
|
62
62
|
function traverse(nodes) {
|
|
63
63
|
nodes.forEach(function(n) {
|
|
64
|
+
if (n.excluded) return; // skip excluded paths and their descendants
|
|
64
65
|
if (n.type === 'file') {
|
|
65
66
|
docs.push({ id: idCounter++, path: n.path, name: n.name, type: 'file' });
|
|
66
67
|
} else if (n.type === 'folder') {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
var TabManager = (function () {
|
|
2
|
-
var MAX_MODELS =
|
|
2
|
+
var MAX_MODELS = 25;
|
|
3
3
|
|
|
4
4
|
// Evict the least-recently-used Monaco model that is not currently open in
|
|
5
5
|
// any pane. Call this before creating a new model entry.
|
|
@@ -118,7 +118,7 @@ var TabManager = (function () {
|
|
|
118
118
|
});
|
|
119
119
|
}
|
|
120
120
|
|
|
121
|
-
function openTab(path, name, line, forcePaneId, isSoftOpen) {
|
|
121
|
+
function openTab(path, name, line, forcePaneId, isSoftOpen, col) {
|
|
122
122
|
var state = EditorStore.getState();
|
|
123
123
|
var paneId = forcePaneId || state.focusedPaneId;
|
|
124
124
|
var pane = state.panes.find(function(p) { return p.id === paneId; });
|
|
@@ -137,7 +137,7 @@ var TabManager = (function () {
|
|
|
137
137
|
var existing = pane.tabs.find(function(t) { return t.path === path; });
|
|
138
138
|
|
|
139
139
|
if (existing) {
|
|
140
|
-
if (line) _updateTab(paneId, path, { gotoLine: line });
|
|
140
|
+
if (line) _updateTab(paneId, path, { gotoLine: line, gotoCol: col || null });
|
|
141
141
|
switchTab(paneId, path);
|
|
142
142
|
if (_isMarkdownPath(path)) {
|
|
143
143
|
_ensureMarkdownPreview(paneId, path, existing.name || name, existing.content || "");
|
|
@@ -163,7 +163,7 @@ var TabManager = (function () {
|
|
|
163
163
|
isSoftOpen: isSoftOpen ? true : false,
|
|
164
164
|
loading: true
|
|
165
165
|
};
|
|
166
|
-
if (line) newTab.gotoLine = line;
|
|
166
|
+
if (line) { newTab.gotoLine = line; newTab.gotoCol = col || null; }
|
|
167
167
|
|
|
168
168
|
var newPanes = state.panes.map(function(p) {
|
|
169
169
|
if (p.id === paneId) {
|
|
@@ -349,6 +349,9 @@ var TabManager = (function () {
|
|
|
349
349
|
}
|
|
350
350
|
|
|
351
351
|
function closeTab(paneId, path) {
|
|
352
|
+
if (typeof HistoryService !== 'undefined') {
|
|
353
|
+
HistoryService.flushForPath(path);
|
|
354
|
+
}
|
|
352
355
|
var state = EditorStore.getState();
|
|
353
356
|
var newPanes = state.panes.map(function(pane) {
|
|
354
357
|
if (pane.id === paneId) {
|
|
@@ -553,7 +556,7 @@ var TabManager = (function () {
|
|
|
553
556
|
}
|
|
554
557
|
|
|
555
558
|
function clearGotoLine(paneId, path) {
|
|
556
|
-
_updateTab(paneId, path, { gotoLine: null });
|
|
559
|
+
_updateTab(paneId, path, { gotoLine: null, gotoCol: null });
|
|
557
560
|
}
|
|
558
561
|
|
|
559
562
|
return {
|
|
@@ -861,3 +861,115 @@
|
|
|
861
861
|
}
|
|
862
862
|
|
|
863
863
|
.cdiff-ctx { color: var(--ide-text-muted); }
|
|
864
|
+
|
|
865
|
+
/* ── File Reload Banner ─────────────────────────────────────────── */
|
|
866
|
+
.mb-file-reload-banner {
|
|
867
|
+
display: flex;
|
|
868
|
+
flex-direction: column;
|
|
869
|
+
gap: 0;
|
|
870
|
+
flex-shrink: 0;
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
.mb-file-reload-item {
|
|
874
|
+
display: flex;
|
|
875
|
+
align-items: center;
|
|
876
|
+
justify-content: space-between;
|
|
877
|
+
padding: 5px 12px;
|
|
878
|
+
background: #2a2600;
|
|
879
|
+
border-bottom: 1px solid #5a5000;
|
|
880
|
+
font-size: 12px;
|
|
881
|
+
color: #e3d286;
|
|
882
|
+
gap: 12px;
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
.mb-file-reload-msg {
|
|
886
|
+
display: flex;
|
|
887
|
+
align-items: center;
|
|
888
|
+
gap: 6px;
|
|
889
|
+
flex: 1;
|
|
890
|
+
min-width: 0;
|
|
891
|
+
overflow: hidden;
|
|
892
|
+
text-overflow: ellipsis;
|
|
893
|
+
white-space: nowrap;
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
.mb-file-reload-actions {
|
|
897
|
+
display: flex;
|
|
898
|
+
gap: 5px;
|
|
899
|
+
flex-shrink: 0;
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
.mb-btn-warning {
|
|
903
|
+
background: #6b4500;
|
|
904
|
+
color: #ffd88a;
|
|
905
|
+
border: 1px solid #8a5900;
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
.mb-btn-warning:hover {
|
|
909
|
+
background: #8a5900;
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
/* ── Merge Conflict Decorations ─────────────────────────────────── */
|
|
913
|
+
.mb-conflict-marker-line {
|
|
914
|
+
background: rgba(120, 80, 0, 0.35) !important;
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
.mb-conflict-head {
|
|
918
|
+
background: rgba(180, 60, 60, 0.18) !important;
|
|
919
|
+
border-left: 2px solid rgba(200, 80, 80, 0.6);
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
.mb-conflict-incoming {
|
|
923
|
+
background: rgba(60, 130, 60, 0.18) !important;
|
|
924
|
+
border-left: 2px solid rgba(80, 160, 80, 0.6);
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
/* ── Conflict Banner ─────────────────────────────────────────────── */
|
|
928
|
+
.mb-conflict-banner {
|
|
929
|
+
display: flex;
|
|
930
|
+
align-items: center;
|
|
931
|
+
gap: 10px;
|
|
932
|
+
padding: 5px 12px;
|
|
933
|
+
background: #1e1a00;
|
|
934
|
+
border-bottom: 1px solid #5a5000;
|
|
935
|
+
font-size: 12px;
|
|
936
|
+
color: #e3d286;
|
|
937
|
+
flex-shrink: 0;
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
.mb-conflict-count {
|
|
941
|
+
font-weight: bold;
|
|
942
|
+
color: #f5a623;
|
|
943
|
+
white-space: nowrap;
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
.mb-conflict-nav {
|
|
947
|
+
display: flex;
|
|
948
|
+
gap: 4px;
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
.mb-conflict-actions {
|
|
952
|
+
display: flex;
|
|
953
|
+
gap: 5px;
|
|
954
|
+
margin-left: auto;
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
.mb-btn-success {
|
|
958
|
+
background: #1a5c2a;
|
|
959
|
+
color: #aef;
|
|
960
|
+
border: 1px solid #2a7a3a;
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
.mb-btn-success:hover {
|
|
964
|
+
background: #1e7a38;
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
.mb-btn-incoming {
|
|
968
|
+
background: #1a3060;
|
|
969
|
+
color: #aef;
|
|
970
|
+
border: 1px solid #1a4080;
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
.mb-btn-incoming:hover {
|
|
974
|
+
background: #1a4080;
|
|
975
|
+
}
|