mbeditor 0.5.2 → 0.5.4
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/app/assets/javascripts/mbeditor/application.js +1 -0
- data/app/assets/javascripts/mbeditor/components/EditorPanel.js +174 -1
- data/app/assets/javascripts/mbeditor/components/MbeditorApp.js +188 -0
- data/app/assets/javascripts/mbeditor/conflict_parser.js +48 -0
- data/app/assets/javascripts/mbeditor/editor_plugins.js +28 -19
- data/app/assets/javascripts/mbeditor/editor_store.js +1 -0
- data/app/assets/stylesheets/mbeditor/application.css +112 -0
- data/lib/mbeditor/rack/handle_pending_migrations.rb +72 -51
- data/lib/mbeditor/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6ce79d4cc314cce5975277d04c12a0d7d5fa13629fa0447d66cf742bf05aee3e
|
|
4
|
+
data.tar.gz: 820ef6cac234149c3f3e69069c5255598b27d161dea7c60939d06999d16d9c4a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 87cc5da11a46b54e9399ecb678c450a6f71053807a2f80bb22b788ef7c4c359b2cbcb2cc6bce4e1691763336ecd9c9ef4d9540f6a80f587f013188142a7d0b1c
|
|
7
|
+
data.tar.gz: 3cd29156eda196448f5f31eed27e5b8d7483e7a8f2f91d70ab6adc58a039a3e5cb19568a6b1a7bc94e38bee904cc4a495e9c698428117e8cd5aba4c09f6e9342
|
|
@@ -35,10 +35,18 @@ var EditorPanel = function EditorPanel(_ref) {
|
|
|
35
35
|
var editorRef = useRef(null);
|
|
36
36
|
var monacoRef = useRef(null);
|
|
37
37
|
var latestContentRef = useRef('');
|
|
38
|
-
var lastAppliedExternalVersionRef = useRef(
|
|
38
|
+
var lastAppliedExternalVersionRef = useRef(0);
|
|
39
|
+
var conflictDecorationsRef = React.useRef([]);
|
|
40
|
+
var conflictBlocksRef = React.useRef([]);
|
|
39
41
|
var aviBaseRef = useRef(0);
|
|
40
42
|
var aviMaxRef = useRef(0);
|
|
41
43
|
|
|
44
|
+
var _conflictState = React.useState(0);
|
|
45
|
+
var conflictCount = _conflictState[0];
|
|
46
|
+
var setConflictCount = _conflictState[1];
|
|
47
|
+
|
|
48
|
+
var currentConflictIndexRef = React.useRef(0);
|
|
49
|
+
|
|
42
50
|
var _useState = useState('');
|
|
43
51
|
var _useState2 = _slicedToArray(_useState, 2);
|
|
44
52
|
var markup = _useState2[0];
|
|
@@ -832,13 +840,68 @@ var EditorPanel = function EditorPanel(_ref) {
|
|
|
832
840
|
EditorStore.setState({ canUndo: false, canRedo: false });
|
|
833
841
|
} else {
|
|
834
842
|
// Keep undo stack for formats or replaces by using executeEdits
|
|
843
|
+
var _extEntry = window.__mbeditorModels && window.__mbeditorModels[tab.path];
|
|
844
|
+
if (_extEntry) _extEntry.cleanVersionId = null;
|
|
835
845
|
editor.pushUndoStop();
|
|
836
846
|
editor.executeEdits("external", [{
|
|
837
847
|
range: model.getFullModelRange(),
|
|
838
848
|
text: tab.content
|
|
839
849
|
}]);
|
|
840
850
|
editor.pushUndoStop();
|
|
851
|
+
// Re-anchor the clean baseline so onDidChangeContent doesn't mark this
|
|
852
|
+
// externally-applied content as a dirty edit.
|
|
853
|
+
var _newExtAvi = model.getAlternativeVersionId();
|
|
854
|
+
if (_extEntry) _extEntry.cleanVersionId = _newExtAvi;
|
|
855
|
+
aviBaseRef.current = _newExtAvi;
|
|
856
|
+
}
|
|
857
|
+
}, [tab.content, tab.externalContentVersion]);
|
|
858
|
+
|
|
859
|
+
useEffect(function () {
|
|
860
|
+
var editor = monacoRef.current;
|
|
861
|
+
if (!editor || !window.monaco || typeof tab.content !== 'string') return;
|
|
862
|
+
var model = editor.getModel();
|
|
863
|
+
if (!model) return;
|
|
864
|
+
|
|
865
|
+
if (!ConflictParser.hasConflicts(tab.content)) {
|
|
866
|
+
conflictDecorationsRef.current = model.deltaDecorations(conflictDecorationsRef.current, []);
|
|
867
|
+
conflictBlocksRef.current = [];
|
|
868
|
+
setConflictCount(0);
|
|
869
|
+
return;
|
|
841
870
|
}
|
|
871
|
+
|
|
872
|
+
var blocks = ConflictParser.parse(tab.content);
|
|
873
|
+
conflictBlocksRef.current = blocks;
|
|
874
|
+
setConflictCount(blocks.length);
|
|
875
|
+
|
|
876
|
+
var decorations = [];
|
|
877
|
+
blocks.forEach(function (block) {
|
|
878
|
+
decorations.push({
|
|
879
|
+
range: new window.monaco.Range(block.startLine + 1, 1, block.startLine + 1, 1),
|
|
880
|
+
options: { isWholeLine: true, className: 'mb-conflict-marker-line' }
|
|
881
|
+
});
|
|
882
|
+
if (block.headEnd > block.startLine + 1) {
|
|
883
|
+
decorations.push({
|
|
884
|
+
range: new window.monaco.Range(block.startLine + 2, 1, block.headEnd, 1),
|
|
885
|
+
options: { isWholeLine: true, className: 'mb-conflict-head' }
|
|
886
|
+
});
|
|
887
|
+
}
|
|
888
|
+
decorations.push({
|
|
889
|
+
range: new window.monaco.Range(block.dividerLine + 1, 1, block.dividerLine + 1, 1),
|
|
890
|
+
options: { isWholeLine: true, className: 'mb-conflict-marker-line' }
|
|
891
|
+
});
|
|
892
|
+
if (block.endLine > block.dividerLine + 1) {
|
|
893
|
+
decorations.push({
|
|
894
|
+
range: new window.monaco.Range(block.dividerLine + 2, 1, block.endLine, 1),
|
|
895
|
+
options: { isWholeLine: true, className: 'mb-conflict-incoming' }
|
|
896
|
+
});
|
|
897
|
+
}
|
|
898
|
+
decorations.push({
|
|
899
|
+
range: new window.monaco.Range(block.endLine + 1, 1, block.endLine + 1, 1),
|
|
900
|
+
options: { isWholeLine: true, className: 'mb-conflict-marker-line' }
|
|
901
|
+
});
|
|
902
|
+
});
|
|
903
|
+
|
|
904
|
+
conflictDecorationsRef.current = model.deltaDecorations(conflictDecorationsRef.current, decorations);
|
|
842
905
|
}, [tab.content, tab.externalContentVersion]);
|
|
843
906
|
|
|
844
907
|
// Apply editorPrefs changes to a running editor without remounting
|
|
@@ -1451,6 +1514,57 @@ var EditorPanel = function EditorPanel(_ref) {
|
|
|
1451
1514
|
);
|
|
1452
1515
|
}
|
|
1453
1516
|
|
|
1517
|
+
function resolveConflict(blockIndex, resolution) {
|
|
1518
|
+
var editor = monacoRef.current;
|
|
1519
|
+
if (!editor || !window.monaco) return;
|
|
1520
|
+
var blocks = conflictBlocksRef.current;
|
|
1521
|
+
if (blockIndex < 0 || blockIndex >= blocks.length) return;
|
|
1522
|
+
var block = blocks[blockIndex];
|
|
1523
|
+
var model = editor.getModel();
|
|
1524
|
+
if (!model) return;
|
|
1525
|
+
|
|
1526
|
+
var resolvedContent;
|
|
1527
|
+
if (resolution === 'head') {
|
|
1528
|
+
resolvedContent = block.headContent;
|
|
1529
|
+
} else if (resolution === 'incoming') {
|
|
1530
|
+
resolvedContent = block.incomingContent;
|
|
1531
|
+
} else {
|
|
1532
|
+
resolvedContent = block.headContent +
|
|
1533
|
+
(block.headContent && block.incomingContent ? '\n' : '') +
|
|
1534
|
+
block.incomingContent;
|
|
1535
|
+
}
|
|
1536
|
+
|
|
1537
|
+
editor.pushUndoStop();
|
|
1538
|
+
editor.executeEdits('conflict-resolve', [{
|
|
1539
|
+
range: new window.monaco.Range(
|
|
1540
|
+
block.startLine + 1, 1,
|
|
1541
|
+
block.endLine + 1,
|
|
1542
|
+
model.getLineMaxColumn(block.endLine + 1)
|
|
1543
|
+
),
|
|
1544
|
+
text: resolvedContent
|
|
1545
|
+
}]);
|
|
1546
|
+
editor.pushUndoStop();
|
|
1547
|
+
|
|
1548
|
+
var newContent = editor.getValue();
|
|
1549
|
+
EditorStore.setState({
|
|
1550
|
+
panes: EditorStore.getState().panes.map(function (p) {
|
|
1551
|
+
return Object.assign({}, p, {
|
|
1552
|
+
tabs: p.tabs.map(function (t) {
|
|
1553
|
+
if (t.path !== tab.path) return t;
|
|
1554
|
+
return Object.assign({}, t, {
|
|
1555
|
+
content: newContent,
|
|
1556
|
+
dirty: true,
|
|
1557
|
+
externalContentVersion: (t.externalContentVersion || 0) + 1
|
|
1558
|
+
});
|
|
1559
|
+
})
|
|
1560
|
+
});
|
|
1561
|
+
})
|
|
1562
|
+
});
|
|
1563
|
+
|
|
1564
|
+
var remaining = conflictBlocksRef.current.length - 1;
|
|
1565
|
+
currentConflictIndexRef.current = Math.min(currentConflictIndexRef.current, Math.max(0, remaining - 1));
|
|
1566
|
+
}
|
|
1567
|
+
|
|
1454
1568
|
// Always render the same wrapper structure so the editorRef div is never
|
|
1455
1569
|
// unmounted when gitAvailable changes (e.g. loaded async after workspace
|
|
1456
1570
|
// call returns). The toolbar is conditionally included inside the wrapper.
|
|
@@ -1525,6 +1639,65 @@ var EditorPanel = function EditorPanel(_ref) {
|
|
|
1525
1639
|
!editorPrefs.toolbarIconOnly && !testLoading && React.createElement('span', { className: 'ide-toolbar-label' }, 'Test')
|
|
1526
1640
|
)
|
|
1527
1641
|
),
|
|
1642
|
+
conflictCount > 0 && React.createElement(
|
|
1643
|
+
'div', { className: 'mb-conflict-banner' },
|
|
1644
|
+
React.createElement(
|
|
1645
|
+
'span', { className: 'mb-conflict-count' },
|
|
1646
|
+
React.createElement('i', { className: 'fas fa-code-merge' }),
|
|
1647
|
+
' ',
|
|
1648
|
+
conflictCount,
|
|
1649
|
+
' merge conflict',
|
|
1650
|
+
conflictCount !== 1 ? 's' : ''
|
|
1651
|
+
),
|
|
1652
|
+
React.createElement(
|
|
1653
|
+
'div', { className: 'mb-conflict-nav' },
|
|
1654
|
+
React.createElement('button', {
|
|
1655
|
+
className: 'mb-btn mb-btn-sm',
|
|
1656
|
+
title: 'Previous conflict',
|
|
1657
|
+
onClick: function () {
|
|
1658
|
+
var idx = Math.max(0, currentConflictIndexRef.current - 1);
|
|
1659
|
+
currentConflictIndexRef.current = idx;
|
|
1660
|
+
var b = conflictBlocksRef.current[idx];
|
|
1661
|
+
if (b && monacoRef.current) {
|
|
1662
|
+
monacoRef.current.revealLineInCenter(b.startLine + 1);
|
|
1663
|
+
monacoRef.current.setPosition({ lineNumber: b.startLine + 1, column: 1 });
|
|
1664
|
+
}
|
|
1665
|
+
}
|
|
1666
|
+
}, '↑ Prev'),
|
|
1667
|
+
React.createElement('button', {
|
|
1668
|
+
className: 'mb-btn mb-btn-sm',
|
|
1669
|
+
title: 'Next conflict',
|
|
1670
|
+
onClick: function () {
|
|
1671
|
+
var blocks = conflictBlocksRef.current;
|
|
1672
|
+
var idx = Math.min(blocks.length - 1, currentConflictIndexRef.current + 1);
|
|
1673
|
+
currentConflictIndexRef.current = idx;
|
|
1674
|
+
var b = blocks[idx];
|
|
1675
|
+
if (b && monacoRef.current) {
|
|
1676
|
+
monacoRef.current.revealLineInCenter(b.startLine + 1);
|
|
1677
|
+
monacoRef.current.setPosition({ lineNumber: b.startLine + 1, column: 1 });
|
|
1678
|
+
}
|
|
1679
|
+
}
|
|
1680
|
+
}, '↓ Next')
|
|
1681
|
+
),
|
|
1682
|
+
React.createElement(
|
|
1683
|
+
'div', { className: 'mb-conflict-actions' },
|
|
1684
|
+
React.createElement('button', {
|
|
1685
|
+
className: 'mb-btn mb-btn-sm mb-btn-success',
|
|
1686
|
+
title: 'Accept current (HEAD) — keep your local changes',
|
|
1687
|
+
onClick: function () { resolveConflict(currentConflictIndexRef.current, 'head'); }
|
|
1688
|
+
}, 'Accept Current'),
|
|
1689
|
+
React.createElement('button', {
|
|
1690
|
+
className: 'mb-btn mb-btn-sm mb-btn-incoming',
|
|
1691
|
+
title: 'Accept incoming — take the changes being merged in',
|
|
1692
|
+
onClick: function () { resolveConflict(currentConflictIndexRef.current, 'incoming'); }
|
|
1693
|
+
}, 'Accept Incoming'),
|
|
1694
|
+
React.createElement('button', {
|
|
1695
|
+
className: 'mb-btn mb-btn-sm',
|
|
1696
|
+
title: 'Accept both — keep current above incoming',
|
|
1697
|
+
onClick: function () { resolveConflict(currentConflictIndexRef.current, 'both'); }
|
|
1698
|
+
}, 'Accept Both')
|
|
1699
|
+
)
|
|
1700
|
+
),
|
|
1528
1701
|
tab.truncated && React.createElement(
|
|
1529
1702
|
'div',
|
|
1530
1703
|
{
|
|
@@ -111,6 +111,44 @@ var SectionActionGroup = function SectionActionGroup(_ref2) {
|
|
|
111
111
|
);
|
|
112
112
|
};
|
|
113
113
|
|
|
114
|
+
function FileReloadBanner(_ref) {
|
|
115
|
+
var pendingReloads = _ref.pendingReloads;
|
|
116
|
+
var onSaveAndReload = _ref.onSaveAndReload;
|
|
117
|
+
var onDiscardAndReload = _ref.onDiscardAndReload;
|
|
118
|
+
var onKeepMine = _ref.onKeepMine;
|
|
119
|
+
if (!pendingReloads || pendingReloads.length === 0) return null;
|
|
120
|
+
return React.createElement(
|
|
121
|
+
'div', { className: 'mb-file-reload-banner' },
|
|
122
|
+
pendingReloads.map(function (r) {
|
|
123
|
+
return React.createElement(
|
|
124
|
+
'div', { key: r.paneId + ':' + r.tabId, className: 'mb-file-reload-item' },
|
|
125
|
+
React.createElement(
|
|
126
|
+
'span', { className: 'mb-file-reload-msg' },
|
|
127
|
+
React.createElement('i', { className: 'fas fa-sync-alt' }),
|
|
128
|
+
' ',
|
|
129
|
+
React.createElement('strong', null, r.name),
|
|
130
|
+
' was updated externally'
|
|
131
|
+
),
|
|
132
|
+
React.createElement(
|
|
133
|
+
'div', { className: 'mb-file-reload-actions' },
|
|
134
|
+
React.createElement('button', {
|
|
135
|
+
className: 'mb-btn mb-btn-sm mb-btn-primary',
|
|
136
|
+
onClick: function () { onSaveAndReload(r); }
|
|
137
|
+
}, 'Save & Reload'),
|
|
138
|
+
React.createElement('button', {
|
|
139
|
+
className: 'mb-btn mb-btn-sm mb-btn-warning',
|
|
140
|
+
onClick: function () { onDiscardAndReload(r); }
|
|
141
|
+
}, 'Discard & Reload'),
|
|
142
|
+
React.createElement('button', {
|
|
143
|
+
className: 'mb-btn mb-btn-sm',
|
|
144
|
+
onClick: function () { onKeepMine(r); }
|
|
145
|
+
}, 'Keep Mine')
|
|
146
|
+
)
|
|
147
|
+
);
|
|
148
|
+
})
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
|
|
114
152
|
var MbeditorApp = function MbeditorApp() {
|
|
115
153
|
var _useState = useState(EditorStore.getState());
|
|
116
154
|
|
|
@@ -1099,12 +1137,77 @@ var MbeditorApp = function MbeditorApp() {
|
|
|
1099
1137
|
SearchService.buildIndex(newData);
|
|
1100
1138
|
return newData;
|
|
1101
1139
|
});
|
|
1140
|
+
checkOpenTabsForExternalChanges();
|
|
1102
1141
|
})["catch"](function () {});
|
|
1103
1142
|
}
|
|
1104
1143
|
WebSocketService.onFilesChanged(handleFilesChanged);
|
|
1105
1144
|
return function () { WebSocketService.offFilesChanged(handleFilesChanged); };
|
|
1106
1145
|
}, []);
|
|
1107
1146
|
|
|
1147
|
+
function checkOpenTabsForExternalChanges() {
|
|
1148
|
+
var st = EditorStore.getState();
|
|
1149
|
+
var allTabs = st.panes.reduce(function (acc, p) {
|
|
1150
|
+
return acc.concat(p.tabs.map(function (t) { return { paneId: p.id, tab: t }; }));
|
|
1151
|
+
}, []);
|
|
1152
|
+
var fileTabs = allTabs.filter(function (pt) {
|
|
1153
|
+
var path = pt.tab.path || '';
|
|
1154
|
+
return path &&
|
|
1155
|
+
!path.startsWith('mbeditor://') &&
|
|
1156
|
+
!path.startsWith('diff://') &&
|
|
1157
|
+
!path.startsWith('combined-diff://') &&
|
|
1158
|
+
!pt.tab.isCombinedDiff &&
|
|
1159
|
+
!pt.tab.isSettings &&
|
|
1160
|
+
!pt.tab.isImage &&
|
|
1161
|
+
!pt.tab.isDiff &&
|
|
1162
|
+
typeof pt.tab.content === 'string';
|
|
1163
|
+
});
|
|
1164
|
+
fileTabs.forEach(function (pt) {
|
|
1165
|
+
FileService.getFile(pt.tab.path, { allowMissing: true }).then(function (data) {
|
|
1166
|
+
if (!data || typeof data.content !== 'string') return;
|
|
1167
|
+
var serverNorm = data.content.replace(/\r\n/g, '\n');
|
|
1168
|
+
var tabNorm = (pt.tab.content || '').replace(/\r\n/g, '\n');
|
|
1169
|
+
if (serverNorm === tabNorm) return;
|
|
1170
|
+
if (!pt.tab.dirty) {
|
|
1171
|
+
EditorStore.setState({
|
|
1172
|
+
panes: EditorStore.getState().panes.map(function (p) {
|
|
1173
|
+
if (p.id !== pt.paneId) return p;
|
|
1174
|
+
return Object.assign({}, p, {
|
|
1175
|
+
tabs: p.tabs.map(function (t) {
|
|
1176
|
+
if (t.id !== pt.tab.id) return t;
|
|
1177
|
+
return Object.assign({}, t, {
|
|
1178
|
+
content: data.content,
|
|
1179
|
+
externalContentVersion: (t.externalContentVersion || 0) + 1
|
|
1180
|
+
});
|
|
1181
|
+
})
|
|
1182
|
+
});
|
|
1183
|
+
})
|
|
1184
|
+
});
|
|
1185
|
+
} else {
|
|
1186
|
+
// Re-verify the tab still exists before queuing
|
|
1187
|
+
var currentState = EditorStore.getState();
|
|
1188
|
+
var stillExists = currentState.panes.some(function (p) {
|
|
1189
|
+
return p.id === pt.paneId && p.tabs.some(function (t) { return t.id === pt.tab.id; });
|
|
1190
|
+
});
|
|
1191
|
+
if (!stillExists) return;
|
|
1192
|
+
var existing = EditorStore.getState().pendingReloads.find(function (r) {
|
|
1193
|
+
return r.paneId === pt.paneId && r.tabId === pt.tab.id;
|
|
1194
|
+
});
|
|
1195
|
+
if (!existing) {
|
|
1196
|
+
EditorStore.setState({
|
|
1197
|
+
pendingReloads: EditorStore.getState().pendingReloads.concat([{
|
|
1198
|
+
paneId: pt.paneId,
|
|
1199
|
+
tabId: pt.tab.id,
|
|
1200
|
+
path: pt.tab.path,
|
|
1201
|
+
name: pt.tab.name,
|
|
1202
|
+
serverContent: data.content
|
|
1203
|
+
}])
|
|
1204
|
+
});
|
|
1205
|
+
}
|
|
1206
|
+
}
|
|
1207
|
+
})["catch"](function () {});
|
|
1208
|
+
});
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1108
1211
|
// Auto-refresh the file tree every 10s to pick up external changes (new files, deletions, etc.)
|
|
1109
1212
|
// When an ActionCable WebSocket is connected this acts only as a safety-net fallback —
|
|
1110
1213
|
// the WebSocket push above handles immediate invalidation after mbeditor mutations.
|
|
@@ -1168,6 +1271,13 @@ var MbeditorApp = function MbeditorApp() {
|
|
|
1168
1271
|
setClosingTabId(id);
|
|
1169
1272
|
} else {
|
|
1170
1273
|
TabManager.closeTab(paneId, id);
|
|
1274
|
+
EditorStore.setState({
|
|
1275
|
+
pendingReloads: EditorStore.getState().pendingReloads.filter(function (r) {
|
|
1276
|
+
return EditorStore.getState().panes.some(function (p) {
|
|
1277
|
+
return p.tabs.some(function (t) { return t.id === r.tabId; });
|
|
1278
|
+
});
|
|
1279
|
+
})
|
|
1280
|
+
});
|
|
1171
1281
|
}
|
|
1172
1282
|
};
|
|
1173
1283
|
|
|
@@ -1199,6 +1309,13 @@ var MbeditorApp = function MbeditorApp() {
|
|
|
1199
1309
|
_closeEntry.cleanVersionId = _closeEntry.model.getAlternativeVersionId();
|
|
1200
1310
|
}
|
|
1201
1311
|
TabManager.closeTab(closingPaneId, tab.id);
|
|
1312
|
+
EditorStore.setState({
|
|
1313
|
+
pendingReloads: EditorStore.getState().pendingReloads.filter(function (r) {
|
|
1314
|
+
return EditorStore.getState().panes.some(function (p) {
|
|
1315
|
+
return p.tabs.some(function (t) { return t.id === r.tabId; });
|
|
1316
|
+
});
|
|
1317
|
+
})
|
|
1318
|
+
});
|
|
1202
1319
|
})["catch"](function (err) {
|
|
1203
1320
|
EditorStore.setStatus("Save failed: " + err.message, "error");
|
|
1204
1321
|
})["finally"](function () {
|
|
@@ -1210,6 +1327,13 @@ var MbeditorApp = function MbeditorApp() {
|
|
|
1210
1327
|
});
|
|
1211
1328
|
} else {
|
|
1212
1329
|
TabManager.closeTab(closingPaneId, tab.id);
|
|
1330
|
+
EditorStore.setState({
|
|
1331
|
+
pendingReloads: EditorStore.getState().pendingReloads.filter(function (r) {
|
|
1332
|
+
return EditorStore.getState().panes.some(function (p) {
|
|
1333
|
+
return p.tabs.some(function (t) { return t.id === r.tabId; });
|
|
1334
|
+
});
|
|
1335
|
+
})
|
|
1336
|
+
});
|
|
1213
1337
|
setClosingTabId(null);
|
|
1214
1338
|
setClosingPaneId(null);
|
|
1215
1339
|
}
|
|
@@ -1472,6 +1596,64 @@ var MbeditorApp = function MbeditorApp() {
|
|
|
1472
1596
|
});
|
|
1473
1597
|
};
|
|
1474
1598
|
|
|
1599
|
+
function dismissPendingReload(reload) {
|
|
1600
|
+
EditorStore.setState({
|
|
1601
|
+
pendingReloads: EditorStore.getState().pendingReloads.filter(function (r) {
|
|
1602
|
+
return !(r.paneId === reload.paneId && r.tabId === reload.tabId);
|
|
1603
|
+
})
|
|
1604
|
+
});
|
|
1605
|
+
}
|
|
1606
|
+
|
|
1607
|
+
function handleSaveAndReload(reload) {
|
|
1608
|
+
var st = EditorStore.getState();
|
|
1609
|
+
var pane = st.panes.find(function (p) { return p.id === reload.paneId; });
|
|
1610
|
+
var tab = pane && pane.tabs.find(function (t) { return t.id === reload.tabId; });
|
|
1611
|
+
if (!tab) { dismissPendingReload(reload); return; }
|
|
1612
|
+
FileService.saveFile(tab.path, tab.content).then(function () {
|
|
1613
|
+
EditorStore.setState({
|
|
1614
|
+
panes: EditorStore.getState().panes.map(function (p) {
|
|
1615
|
+
if (p.id !== reload.paneId) return p;
|
|
1616
|
+
return Object.assign({}, p, {
|
|
1617
|
+
tabs: p.tabs.map(function (t) {
|
|
1618
|
+
if (t.id !== reload.tabId) return t;
|
|
1619
|
+
return Object.assign({}, t, {
|
|
1620
|
+
content: tab.content,
|
|
1621
|
+
dirty: false,
|
|
1622
|
+
externalContentVersion: (t.externalContentVersion || 0) + 1
|
|
1623
|
+
});
|
|
1624
|
+
})
|
|
1625
|
+
});
|
|
1626
|
+
})
|
|
1627
|
+
});
|
|
1628
|
+
dismissPendingReload(reload);
|
|
1629
|
+
})["catch"](function () {
|
|
1630
|
+
EditorStore.setStatus('Save failed — cannot reload', 'error');
|
|
1631
|
+
});
|
|
1632
|
+
}
|
|
1633
|
+
|
|
1634
|
+
function handleDiscardAndReload(reload) {
|
|
1635
|
+
EditorStore.setState({
|
|
1636
|
+
panes: EditorStore.getState().panes.map(function (p) {
|
|
1637
|
+
if (p.id !== reload.paneId) return p;
|
|
1638
|
+
return Object.assign({}, p, {
|
|
1639
|
+
tabs: p.tabs.map(function (t) {
|
|
1640
|
+
if (t.id !== reload.tabId) return t;
|
|
1641
|
+
return Object.assign({}, t, {
|
|
1642
|
+
content: reload.serverContent,
|
|
1643
|
+
dirty: false,
|
|
1644
|
+
externalContentVersion: (t.externalContentVersion || 0) + 1
|
|
1645
|
+
});
|
|
1646
|
+
})
|
|
1647
|
+
});
|
|
1648
|
+
})
|
|
1649
|
+
});
|
|
1650
|
+
dismissPendingReload(reload);
|
|
1651
|
+
}
|
|
1652
|
+
|
|
1653
|
+
function handleKeepMine(reload) {
|
|
1654
|
+
dismissPendingReload(reload);
|
|
1655
|
+
}
|
|
1656
|
+
|
|
1475
1657
|
var handleSaveAll = function handleSaveAll() {
|
|
1476
1658
|
var dirtyTabs = state.panes.flatMap(function (p) {
|
|
1477
1659
|
return p.tabs;
|
|
@@ -3775,6 +3957,12 @@ var MbeditorApp = function MbeditorApp() {
|
|
|
3775
3957
|
React.Fragment,
|
|
3776
3958
|
null,
|
|
3777
3959
|
renderTabBar(pane.id, pane.tabs, pane.activeTabId),
|
|
3960
|
+
React.createElement(FileReloadBanner, {
|
|
3961
|
+
pendingReloads: (state.pendingReloads || []).filter(function (r) { return r.paneId === pane.id; }),
|
|
3962
|
+
onSaveAndReload: handleSaveAndReload,
|
|
3963
|
+
onDiscardAndReload: handleDiscardAndReload,
|
|
3964
|
+
onKeepMine: handleKeepMine
|
|
3965
|
+
}),
|
|
3778
3966
|
React.createElement(
|
|
3779
3967
|
"div",
|
|
3780
3968
|
{ style: { flex: 1, minHeight: 0, display: 'flex', flexDirection: 'column', visibility: activeResizeMode === 'pane' ? 'hidden' : 'visible' } },
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
var ConflictParser = (function () {
|
|
2
|
+
function hasConflicts(content) {
|
|
3
|
+
return /^<<<<<<< /m.test(content);
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
function parse(content) {
|
|
7
|
+
var blocks = [];
|
|
8
|
+
var lines = content.split('\n');
|
|
9
|
+
var state = null;
|
|
10
|
+
var headStart = -1, headEnd = -1, dividerLine = -1;
|
|
11
|
+
var headLines = [], incomingLines = [];
|
|
12
|
+
var marker = null;
|
|
13
|
+
|
|
14
|
+
for (var i = 0; i < lines.length; i++) {
|
|
15
|
+
var line = lines[i];
|
|
16
|
+
if (/^<<<<<<< /.test(line) && state === null) {
|
|
17
|
+
state = 'head';
|
|
18
|
+
headStart = i;
|
|
19
|
+
headLines = [];
|
|
20
|
+
marker = line;
|
|
21
|
+
} else if (/^=======\s*$/.test(line) && state === 'head') {
|
|
22
|
+
state = 'incoming';
|
|
23
|
+
headEnd = i;
|
|
24
|
+
dividerLine = i;
|
|
25
|
+
incomingLines = [];
|
|
26
|
+
} else if (/^>>>>>>> /.test(line) && state === 'incoming') {
|
|
27
|
+
blocks.push({
|
|
28
|
+
startLine: headStart,
|
|
29
|
+
headEnd: headEnd,
|
|
30
|
+
dividerLine: dividerLine,
|
|
31
|
+
endLine: i,
|
|
32
|
+
headContent: headLines.join('\n'),
|
|
33
|
+
incomingContent: incomingLines.join('\n'),
|
|
34
|
+
marker: marker,
|
|
35
|
+
endMarker: line
|
|
36
|
+
});
|
|
37
|
+
state = null;
|
|
38
|
+
} else if (state === 'head') {
|
|
39
|
+
headLines.push(line);
|
|
40
|
+
} else if (state === 'incoming') {
|
|
41
|
+
incomingLines.push(line);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return blocks;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return { hasConflicts: hasConflicts, parse: parse };
|
|
48
|
+
})();
|
|
@@ -68,9 +68,6 @@
|
|
|
68
68
|
var key = keys[i];
|
|
69
69
|
if (alreadyDeclared[key]) continue;
|
|
70
70
|
if (!/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(key)) continue;
|
|
71
|
-
var desc;
|
|
72
|
-
try { desc = Object.getOwnPropertyDescriptor(window, key); } catch (e) { continue; }
|
|
73
|
-
if (!desc || !desc.configurable || !desc.writable || desc.get) continue;
|
|
74
71
|
var value;
|
|
75
72
|
try { value = window[key]; } catch (e) { continue; }
|
|
76
73
|
if (value === null || value === undefined) continue;
|
|
@@ -488,11 +485,11 @@
|
|
|
488
485
|
});
|
|
489
486
|
}
|
|
490
487
|
|
|
491
|
-
// Declare globals that
|
|
492
|
-
//
|
|
493
|
-
//
|
|
494
|
-
//
|
|
495
|
-
//
|
|
488
|
+
// Declare globals that are injected at runtime so checkJs doesn't flag them
|
|
489
|
+
// as undefined. The buildWindowGlobalsShim() function automatically detects
|
|
490
|
+
// window globals from the host application. Common Sprockets globals
|
|
491
|
+
// (React, ReactDOM, etc.) are declared explicitly. For additional globals
|
|
492
|
+
// not auto-detected, add `/* global MyComponent */` at the top of the file.
|
|
496
493
|
if (monaco.languages.typescript && monaco.languages.typescript.javascriptDefaults) {
|
|
497
494
|
monaco.languages.typescript.javascriptDefaults.addExtraLib(
|
|
498
495
|
[
|
|
@@ -515,12 +512,18 @@
|
|
|
515
512
|
);
|
|
516
513
|
}
|
|
517
514
|
|
|
518
|
-
// Downgrade
|
|
519
|
-
// TypeScript has no built-in way to emit
|
|
515
|
+
// Downgrade certain TypeScript diagnostic codes from Error to Warning.
|
|
516
|
+
// TypeScript has no built-in way to emit these as warnings, so we intercept
|
|
520
517
|
// the marker set after the worker fires and re-apply with lower severity.
|
|
521
|
-
//
|
|
522
|
-
|
|
523
|
-
|
|
518
|
+
//
|
|
519
|
+
// Patch markers after the TypeScript worker fires:
|
|
520
|
+
// - Both: downgrade TS6133 ("declared but never read") from Error to Warning.
|
|
521
|
+
// Host-app globals are handled by the dynamic window shim and explicit
|
|
522
|
+
// addExtraLib declarations above — we do not suppress TS2304 globally so
|
|
523
|
+
// that genuinely undefined names are still flagged as errors.
|
|
524
|
+
var JS_SUPPRESS_CODES = {};
|
|
525
|
+
var JS_WARN_CODES = { '6133': true };
|
|
526
|
+
var TS_WARN_CODES = { '6133': true };
|
|
524
527
|
var _severityPatchActive = false;
|
|
525
528
|
monaco.editor.onDidChangeMarkers(function(uris) {
|
|
526
529
|
if (_severityPatchActive) return;
|
|
@@ -529,14 +532,20 @@
|
|
|
529
532
|
uris.forEach(function(uri) {
|
|
530
533
|
var model = monaco.editor.getModel(uri);
|
|
531
534
|
if (!model) return;
|
|
532
|
-
|
|
533
|
-
|
|
535
|
+
[
|
|
536
|
+
{ owner: 'javascript', suppress: JS_SUPPRESS_CODES, warn: JS_WARN_CODES },
|
|
537
|
+
{ owner: 'typescript', suppress: {}, warn: TS_WARN_CODES }
|
|
538
|
+
].forEach(function(entry) {
|
|
539
|
+
var markers = monaco.editor.getModelMarkers({ resource: uri, owner: entry.owner });
|
|
534
540
|
var needsPatch = markers.some(function(m) {
|
|
535
|
-
|
|
541
|
+
var code = String(m.code);
|
|
542
|
+
return (m.severity === monaco.MarkerSeverity.Error && (entry.suppress[code] || entry.warn[code]));
|
|
536
543
|
});
|
|
537
544
|
if (!needsPatch) return;
|
|
538
|
-
monaco.editor.setModelMarkers(model, owner, markers.
|
|
539
|
-
return
|
|
545
|
+
monaco.editor.setModelMarkers(model, entry.owner, markers.filter(function(m) {
|
|
546
|
+
return !entry.suppress[String(m.code)];
|
|
547
|
+
}).map(function(m) {
|
|
548
|
+
return (m.severity === monaco.MarkerSeverity.Error && entry.warn[String(m.code)])
|
|
540
549
|
? Object.assign({}, m, { severity: monaco.MarkerSeverity.Warning })
|
|
541
550
|
: m;
|
|
542
551
|
}));
|
|
@@ -548,7 +557,7 @@
|
|
|
548
557
|
});
|
|
549
558
|
}
|
|
550
559
|
|
|
551
|
-
// TypeScript: enable JSX for .tsx files
|
|
560
|
+
// TypeScript: enable JSX for .tsx files.
|
|
552
561
|
if (monaco.languages.typescript && monaco.languages.typescript.typescriptDefaults) {
|
|
553
562
|
monaco.languages.typescript.typescriptDefaults.setCompilerOptions({
|
|
554
563
|
target: monaco.languages.typescript.ScriptTarget.ES2020,
|
|
@@ -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
|
+
}
|
|
@@ -36,6 +36,15 @@ module Mbeditor
|
|
|
36
36
|
private
|
|
37
37
|
|
|
38
38
|
def editor_shell_html(base)
|
|
39
|
+
prettier_scripts = %w[
|
|
40
|
+
prettier-standalone.js
|
|
41
|
+
prettier-plugin-babel.js
|
|
42
|
+
prettier-plugin-estree.js
|
|
43
|
+
prettier-plugin-html.js
|
|
44
|
+
prettier-plugin-postcss.js
|
|
45
|
+
prettier-plugin-markdown.js
|
|
46
|
+
].map { |f| "#{base}/assets/#{f}" }.to_json
|
|
47
|
+
|
|
39
48
|
<<~HTML
|
|
40
49
|
<!DOCTYPE html>
|
|
41
50
|
<html lang="en">
|
|
@@ -45,6 +54,16 @@ module Mbeditor
|
|
|
45
54
|
<title>Mbeditor</title>
|
|
46
55
|
<link rel="stylesheet" href="#{base}/assets/fontawesome.min.css" />
|
|
47
56
|
<link rel="stylesheet" href="#{base}/assets/mbeditor/application.css" />
|
|
57
|
+
<script defer src="#{base}/assets/react.min.js"></script>
|
|
58
|
+
<script defer src="#{base}/assets/react-dom.min.js"></script>
|
|
59
|
+
<script defer src="#{base}/assets/axios.min.js"></script>
|
|
60
|
+
<script defer src="#{base}/assets/lodash.min.js"></script>
|
|
61
|
+
<script defer src="#{base}/assets/minisearch.min.js"></script>
|
|
62
|
+
<script defer src="#{base}/assets/marked.min.js"></script>
|
|
63
|
+
<script defer src="#{base}/assets/emmet.js"></script>
|
|
64
|
+
<script defer src="#{base}/assets/monaco-themes-bundle.js"></script>
|
|
65
|
+
<script>var require = { paths: { vs: '#{base}/monaco-editor/vs', 'monaco-editor/esm/vs': '#{base}/monaco-editor/vs', 'monaco-vim': '#{base}/assets/monaco-vim' } };</script>
|
|
66
|
+
<script src="#{base}/monaco-editor/vs/loader.js"></script>
|
|
48
67
|
</head>
|
|
49
68
|
<body>
|
|
50
69
|
<script>
|
|
@@ -60,68 +79,70 @@ module Mbeditor
|
|
|
60
79
|
<div class="mbeditor-loading-text">Loading editor…</div>
|
|
61
80
|
</div>
|
|
62
81
|
</div>
|
|
63
|
-
<script defer src="#{base}/assets/react.min.js"></script>
|
|
64
|
-
<script defer src="#{base}/assets/react-dom.min.js"></script>
|
|
65
|
-
<script defer src="#{base}/assets/axios.min.js"></script>
|
|
66
|
-
<script defer src="#{base}/assets/lodash.min.js"></script>
|
|
67
|
-
<script defer src="#{base}/assets/minisearch.min.js"></script>
|
|
68
|
-
<script defer src="#{base}/assets/marked.min.js"></script>
|
|
69
|
-
<script defer src="#{base}/assets/emmet.js"></script>
|
|
70
|
-
<script defer src="#{base}/assets/monaco-themes-bundle.js"></script>
|
|
71
82
|
<script>
|
|
72
83
|
window.MonacoEnvironment = {
|
|
73
84
|
getWorkerUrl: function(workerId, label) {
|
|
74
|
-
|
|
75
|
-
return '
|
|
85
|
+
var b = window.MBEDITOR_BASE_PATH || '';
|
|
86
|
+
if (label === 'typescript' || label === 'javascript') return b + '/ts_worker.js';
|
|
87
|
+
return b + '/monaco_worker.js';
|
|
76
88
|
}
|
|
77
89
|
};
|
|
78
|
-
var require = { paths: { vs: '#{base}/monaco-editor/vs', 'monaco-editor/esm/vs': '#{base}/monaco-editor/vs', 'monaco-vim': '#{base}/assets/monaco-vim' } };
|
|
79
|
-
</script>
|
|
80
|
-
<script src="#{base}/monaco-editor/vs/loader.js"></script>
|
|
81
|
-
<script>
|
|
82
90
|
(function() {
|
|
83
|
-
var prettierScripts = [
|
|
84
|
-
'#{base}/assets/prettier-standalone.js',
|
|
85
|
-
'#{base}/assets/prettier-plugin-babel.js',
|
|
86
|
-
'#{base}/assets/prettier-plugin-estree.js',
|
|
87
|
-
'#{base}/assets/prettier-plugin-html.js',
|
|
88
|
-
'#{base}/assets/prettier-plugin-postcss.js',
|
|
89
|
-
'#{base}/assets/prettier-plugin-markdown.js'
|
|
90
|
-
];
|
|
91
91
|
var _define = window.define;
|
|
92
92
|
window.define = undefined;
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
window.define =
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
Object.keys(window.MBEDITOR_CUSTOM_THEMES).forEach(function(id) {
|
|
103
|
-
window.monaco.editor.defineTheme(id, window.MBEDITOR_CUSTOM_THEMES[id]);
|
|
104
|
-
});
|
|
105
|
-
}
|
|
93
|
+
|
|
94
|
+
var prettierScripts = #{prettier_scripts};
|
|
95
|
+
window.loadPrettierPlugins = function() {
|
|
96
|
+
if (window._prettierLoadPromise) return window._prettierLoadPromise;
|
|
97
|
+
window._prettierLoadPromise = new Promise(function(resolve, reject) {
|
|
98
|
+
var savedDefine = window.define;
|
|
99
|
+
window.define = undefined;
|
|
100
|
+
var pending = prettierScripts.length;
|
|
101
|
+
prettierScripts.forEach(function(src) {
|
|
106
102
|
var s = document.createElement('script');
|
|
107
|
-
s.src =
|
|
108
|
-
s.onload = function() {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
if (window.MbeditorApp && _R && _RD) _RD.render(_R.createElement(window.MbeditorApp), root);
|
|
112
|
-
};
|
|
113
|
-
document.body.appendChild(s);
|
|
103
|
+
s.src = src;
|
|
104
|
+
s.onload = function() { if (--pending === 0) { window.define = savedDefine; resolve(); } };
|
|
105
|
+
s.onerror = function() { window.define = savedDefine; reject(new Error('Failed to load Prettier: ' + src)); };
|
|
106
|
+
document.head.appendChild(s);
|
|
114
107
|
});
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
|
|
108
|
+
});
|
|
109
|
+
return window._prettierLoadPromise;
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
function proceed() {
|
|
113
|
+
window.MbeditorRuntime = { React: window.React, ReactDOM: window.ReactDOM };
|
|
114
|
+
window.React = window._mbeditorHostReact;
|
|
115
|
+
window.ReactDOM = window._mbeditorHostReactDOM;
|
|
116
|
+
window.define = _define;
|
|
117
|
+
|
|
118
|
+
var _monacoResolve;
|
|
119
|
+
window.__monacoReady = new Promise(function(resolve) { _monacoResolve = resolve; });
|
|
120
|
+
|
|
121
|
+
var appScript = document.createElement('script');
|
|
122
|
+
appScript.src = '#{base}/assets/mbeditor/application.js';
|
|
123
|
+
appScript.onload = function() {
|
|
124
|
+
var root = document.getElementById('mbeditor-root');
|
|
125
|
+
var _R = window.MbeditorRuntime.React, _RD = window.MbeditorRuntime.ReactDOM;
|
|
126
|
+
if (window.MbeditorApp && _R && _RD) _RD.render(_R.createElement(window.MbeditorApp), root);
|
|
127
|
+
};
|
|
128
|
+
appScript.onerror = function() {
|
|
129
|
+
document.getElementById('mbeditor-root').innerHTML =
|
|
130
|
+
'<div style="padding:2rem;font-family:sans-serif;color:#c00">Editor failed to load. Please refresh the page.</div>';
|
|
131
|
+
};
|
|
132
|
+
document.body.appendChild(appScript);
|
|
133
|
+
|
|
134
|
+
require(['vs/editor/editor.main'], function() {
|
|
135
|
+
if (window.MBEDITOR_CUSTOM_THEMES && window.monaco) {
|
|
136
|
+
Object.keys(window.MBEDITOR_CUSTOM_THEMES).forEach(function(id) {
|
|
137
|
+
window.monaco.editor.defineTheme(id, window.MBEDITOR_CUSTOM_THEMES[id]);
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
_monacoResolve();
|
|
141
|
+
});
|
|
118
142
|
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
s.onload = function() { if (--pending === 0) onAllPrettierLoaded(); };
|
|
123
|
-
document.head.appendChild(s);
|
|
124
|
-
});
|
|
143
|
+
|
|
144
|
+
if (window._mbeditorDOMReady) proceed();
|
|
145
|
+
else document.addEventListener('DOMContentLoaded', proceed, { once: true });
|
|
125
146
|
})();
|
|
126
147
|
</script>
|
|
127
148
|
</body>
|
data/lib/mbeditor/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: mbeditor
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.5.
|
|
4
|
+
version: 0.5.4
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Oliver Noonan
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-04
|
|
11
|
+
date: 2026-05-04 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rails
|
|
@@ -71,6 +71,7 @@ files:
|
|
|
71
71
|
- app/assets/javascripts/mbeditor/components/ShortcutHelp.js
|
|
72
72
|
- app/assets/javascripts/mbeditor/components/TabBar.js
|
|
73
73
|
- app/assets/javascripts/mbeditor/components/TestResultsPanel.js
|
|
74
|
+
- app/assets/javascripts/mbeditor/conflict_parser.js
|
|
74
75
|
- app/assets/javascripts/mbeditor/editor_plugins.js
|
|
75
76
|
- app/assets/javascripts/mbeditor/editor_store.js
|
|
76
77
|
- app/assets/javascripts/mbeditor/file_icon.js
|