mbeditor 0.13.0 → 0.14.0

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.
Files changed (66) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +157 -1
  3. data/app/assets/javascripts/mbeditor/application.js +3 -1
  4. data/app/assets/javascripts/mbeditor/audit_log.js +165 -0
  5. data/app/assets/javascripts/mbeditor/collaboration_service.js +264 -22
  6. data/app/assets/javascripts/mbeditor/components/ChangelogView.js +89 -94
  7. data/app/assets/javascripts/mbeditor/components/CodeReviewPanel.js +6 -9
  8. data/app/assets/javascripts/mbeditor/components/CollapsibleSection.js +12 -7
  9. data/app/assets/javascripts/mbeditor/components/CombinedDiffViewer.js +20 -0
  10. data/app/assets/javascripts/mbeditor/components/DiffViewer.js +1 -1
  11. data/app/assets/javascripts/mbeditor/components/EditorPanel.js +440 -334
  12. data/app/assets/javascripts/mbeditor/components/FileHistoryPanel.js +6 -9
  13. data/app/assets/javascripts/mbeditor/components/FileTree.js +26 -12
  14. data/app/assets/javascripts/mbeditor/components/GitPanel.js +3 -0
  15. data/app/assets/javascripts/mbeditor/components/Gutter.js +51 -0
  16. data/app/assets/javascripts/mbeditor/components/ImportDialog.js +9 -1
  17. data/app/assets/javascripts/mbeditor/components/LogPanel.js +3 -44
  18. data/app/assets/javascripts/mbeditor/components/MbeditorApp.js +1322 -1431
  19. data/app/assets/javascripts/mbeditor/components/ModelGraph.js +94 -37
  20. data/app/assets/javascripts/mbeditor/components/ProblemsPanel.js +82 -72
  21. data/app/assets/javascripts/mbeditor/components/QuickOpenDialog.js +121 -81
  22. data/app/assets/javascripts/mbeditor/components/SettingsModal.js +342 -0
  23. data/app/assets/javascripts/mbeditor/components/ShortcutHelp.js +2 -1
  24. data/app/assets/javascripts/mbeditor/components/TabBar.js +67 -98
  25. data/app/assets/javascripts/mbeditor/editor_plugins.js +694 -306
  26. data/app/assets/javascripts/mbeditor/file_import.js +13 -15
  27. data/app/assets/javascripts/mbeditor/file_service.js +46 -57
  28. data/app/assets/javascripts/mbeditor/git_service.js +15 -1
  29. data/app/assets/javascripts/mbeditor/history_service.js +5 -13
  30. data/app/assets/javascripts/mbeditor/search_service.js +29 -2
  31. data/app/assets/javascripts/mbeditor/tab_manager.js +135 -54
  32. data/app/assets/javascripts/mbeditor/websocket_service.js +13 -5
  33. data/app/assets/stylesheets/mbeditor/application.css +6 -1
  34. data/app/assets/stylesheets/mbeditor/editor.css +762 -297
  35. data/app/assets/stylesheets/mbeditor/glass.css +163 -0
  36. data/app/assets/stylesheets/mbeditor/themes.css +90 -30
  37. data/app/channels/mbeditor/collaboration_channel.rb +25 -6
  38. data/app/controllers/mbeditor/application_controller.rb +26 -3
  39. data/app/controllers/mbeditor/editors_controller.rb +125 -465
  40. data/app/services/mbeditor/archive_service.rb +137 -0
  41. data/app/services/mbeditor/collaboration_doc_store.rb +180 -12
  42. data/app/services/mbeditor/duplicate_content_scanner.rb +105 -0
  43. data/app/services/mbeditor/editor_state_service.rb +14 -51
  44. data/app/services/mbeditor/file_history_service.rb +222 -0
  45. data/app/services/mbeditor/git_info_service.rb +6 -0
  46. data/app/services/mbeditor/js_globals_service.rb +12 -1
  47. data/app/services/mbeditor/js_syntax_check_service.rb +42 -16
  48. data/app/services/mbeditor/lint_service.rb +137 -0
  49. data/app/services/mbeditor/locked_json_file.rb +67 -0
  50. data/app/services/mbeditor/process_runner.rb +32 -0
  51. data/app/services/mbeditor/rubocop_run_service.rb +17 -5
  52. data/app/services/mbeditor/ruby_lsp_result_translator.rb +226 -0
  53. data/app/services/mbeditor/schema_service.rb +8 -2
  54. data/app/services/mbeditor/search_replace_service.rb +19 -2
  55. data/app/services/mbeditor/test_runner_service.rb +3 -56
  56. data/app/views/layouts/mbeditor/application.html.erb +1 -1
  57. data/lib/mbeditor/audit_log.rb +203 -0
  58. data/lib/mbeditor/configuration.rb +6 -9
  59. data/lib/mbeditor/rack/pending_migration_bypass.rb +15 -9
  60. data/lib/mbeditor/route_map.rb +4 -1
  61. data/lib/mbeditor/ruby_lsp_client.rb +82 -14
  62. data/lib/mbeditor/version.rb +1 -1
  63. data/lib/mbeditor.rb +1 -0
  64. data/lib/tasks/mbeditor.rake +23 -0
  65. metadata +14 -3
  66. data/app/assets/javascripts/mbeditor/components/TestRunPanel.js +0 -312
@@ -1,12 +1,46 @@
1
1
  'use strict';
2
2
 
3
- var _slicedToArray = (function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i['return']) _i['return'](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError('Invalid attempt to destructure non-iterable instance'); } }; })();
4
3
 
5
4
  var _React = React;
6
5
  var useState = _React.useState;
7
6
  var useEffect = _React.useEffect;
8
7
  var useRef = _React.useRef;
9
8
 
9
+ var menuItem = function menuItem(icon, label, onClick) {
10
+ return React.createElement(
11
+ 'div',
12
+ { className: 'ide-tab-context-menu-item', onClick: onClick },
13
+ React.createElement('i', { className: icon }),
14
+ label
15
+ );
16
+ };
17
+
18
+ // Clamps a position:fixed, content-sized popup menu into the viewport.
19
+ // The menu is already rendered at its raw (possibly off-screen) cursor
20
+ // position via inline style, so its size can only be known by measuring
21
+ // the real element — nudge it back on-screen if it overflows. Shared by
22
+ // the tab-strip menu here and the explorer context menu in MbeditorApp.js.
23
+ window.clampMenuIntoView = function clampMenuIntoView(el) {
24
+ if (!el) return;
25
+ var MARGIN = 4;
26
+ var rect = el.getBoundingClientRect();
27
+ var left = rect.left;
28
+ var top = rect.top;
29
+
30
+ if (rect.right > window.innerWidth - MARGIN) {
31
+ left = Math.max(MARGIN, window.innerWidth - MARGIN - rect.width);
32
+ }
33
+ if (rect.bottom > window.innerHeight - MARGIN) {
34
+ var above = rect.top - rect.height;
35
+ top = above >= MARGIN ? above : MARGIN;
36
+ }
37
+ if (left < MARGIN) left = MARGIN;
38
+ if (top < MARGIN) top = MARGIN;
39
+
40
+ if (left !== rect.left) el.style.left = left + 'px';
41
+ if (top !== rect.top) el.style.top = top + 'px';
42
+ };
43
+
10
44
  var TabBar = function TabBar(_ref) {
11
45
  var tabs = _ref.tabs;
12
46
  var activeId = _ref.activeId;
@@ -29,27 +63,24 @@ var TabBar = function TabBar(_ref) {
29
63
 
30
64
  var _useState = useState(null);
31
65
 
32
- var _useState2 = _slicedToArray(_useState, 2);
33
66
 
34
- var draggingTabId = _useState2[0];
35
- var setDraggingTabId = _useState2[1];
67
+ var draggingTabId = _useState[0];
68
+ var setDraggingTabId = _useState[1];
36
69
 
37
70
  var _useState3 = useState(null);
38
71
 
39
- var _useState4 = _slicedToArray(_useState3, 2);
40
72
 
41
- var tabContextMenu = _useState4[0];
42
- var setTabContextMenu = _useState4[1];
73
+ var tabContextMenu = _useState3[0];
74
+ var setTabContextMenu = _useState3[1];
75
+ var tabContextMenuRef = useRef(null);
43
76
 
44
77
  var _useState5 = useState(null);
45
- var _useState6 = _slicedToArray(_useState5, 2);
46
- var dropTargetTabId = _useState6[0];
47
- var setDropTargetTabId = _useState6[1];
78
+ var dropTargetTabId = _useState5[0];
79
+ var setDropTargetTabId = _useState5[1];
48
80
 
49
81
  var _useState7 = useState(null);
50
- var _useState8 = _slicedToArray(_useState7, 2);
51
- var dropTargetSide = _useState8[0];
52
- var setDropTargetSide = _useState8[1];
82
+ var dropTargetSide = _useState7[0];
83
+ var setDropTargetSide = _useState7[1];
53
84
 
54
85
  var getTabMarkerClass = function getTabMarkerClass(tab) {
55
86
  var tabMarkers = markers[tab.id] || [];
@@ -93,6 +124,11 @@ var TabBar = function TabBar(_ref) {
93
124
  return function () { document.removeEventListener('mousedown', handler); };
94
125
  }, [tabContextMenu]);
95
126
 
127
+ // Clamp into view whenever the menu opens at a new position.
128
+ useEffect(function () {
129
+ if (tabContextMenu) window.clampMenuIntoView(tabContextMenuRef.current);
130
+ }, [tabContextMenu]);
131
+
96
132
  return React.createElement(
97
133
  React.Fragment,
98
134
  null,
@@ -104,7 +140,7 @@ var TabBar = function TabBar(_ref) {
104
140
  }
105
141
  } },
106
142
  tabs.map(function (tab) {
107
- var isSpecial = tab.isCommitGraph || tab.isDiff || tab.isPreview || tab.isSettings;
143
+ var isSpecial = tab.isCommitGraph || tab.isDiff || tab.isPreview;
108
144
  return React.createElement(
109
145
  'div',
110
146
  {
@@ -178,7 +214,7 @@ var TabBar = function TabBar(_ref) {
178
214
  }
179
215
  }
180
216
  },
181
- React.createElement('i', { className: 'tab-item-icon ' + (tab.isSettings ? 'fas fa-cog' : (window.getFileIcon ? window.getFileIcon(tab.name) : 'far fa-file-code')) }),
217
+ React.createElement('i', { className: 'tab-item-icon ' + (window.getFileIcon ? window.getFileIcon(tab.name) : 'far fa-file-code') }),
182
218
  React.createElement(
183
219
  'div',
184
220
  { className: 'tab-item-name' },
@@ -190,14 +226,12 @@ var TabBar = function TabBar(_ref) {
190
226
  '●'
191
227
  ),
192
228
  React.createElement(
193
- 'div',
229
+ 'button',
194
230
  {
231
+ type: 'button',
195
232
  className: 'tab-close',
196
- // No role="button" here: the host app's Pico CSS skins
197
- // [role=button] with a primary background and form padding, which
198
- // turns this into a blue square. It is a plain div with a tooltip,
199
- // as it was — Ctrl+W is the keyboard path.
200
233
  title: 'Close ' + tab.name + (tab.dirty ? ' (unsaved changes)' : ''),
234
+ 'aria-label': 'Close ' + tab.name + (tab.dirty ? ' (unsaved changes)' : ''),
201
235
  onClick: function (e) {
202
236
  e.stopPropagation();
203
237
  onClose(tab.id);
@@ -222,6 +256,7 @@ var TabBar = function TabBar(_ref) {
222
256
  'div',
223
257
  {
224
258
  className: 'ide-tab-context-menu',
259
+ ref: tabContextMenuRef,
225
260
  style: {
226
261
  position: 'fixed',
227
262
  top: tabContextMenu.y,
@@ -236,85 +271,19 @@ var TabBar = function TabBar(_ref) {
236
271
  },
237
272
  onMouseDown: function(e) { e.stopPropagation(); }
238
273
  },
239
- React.createElement(
240
- 'div',
241
- {
242
- className: 'ide-tab-context-menu-item',
243
- style: { padding: '6px 14px', cursor: 'pointer', color: '#ccc', fontSize: '12px', display: 'flex', alignItems: 'center', gap: '8px' },
244
- onMouseEnter: function(e) { e.currentTarget.style.background = '#094771'; },
245
- onMouseLeave: function(e) { e.currentTarget.style.background = 'transparent'; },
246
- onClick: function() { setTabContextMenu(null); onClose(tabContextMenu.tab.id); }
247
- },
248
- React.createElement('i', { className: 'fas fa-times', style: { width: '14px', textAlign: 'center' } }),
249
- 'Close'
250
- ),
251
- onCloseOthers && React.createElement(
252
- 'div',
253
- {
254
- className: 'ide-tab-context-menu-item',
255
- style: { padding: '6px 14px', cursor: 'pointer', color: '#ccc', fontSize: '12px', display: 'flex', alignItems: 'center', gap: '8px' },
256
- onMouseEnter: function(e) { e.currentTarget.style.background = '#094771'; },
257
- onMouseLeave: function(e) { e.currentTarget.style.background = 'transparent'; },
258
- onClick: function() { setTabContextMenu(null); onCloseOthers(tabContextMenu.tab.id); }
259
- },
260
- React.createElement('i', { className: 'fas fa-times-circle', style: { width: '14px', textAlign: 'center' } }),
261
- 'Close Others'
262
- ),
263
- onCloseSaved && React.createElement(
264
- 'div',
265
- {
266
- className: 'ide-tab-context-menu-item',
267
- style: { padding: '6px 14px', cursor: 'pointer', color: '#ccc', fontSize: '12px', display: 'flex', alignItems: 'center', gap: '8px' },
268
- onMouseEnter: function(e) { e.currentTarget.style.background = '#094771'; },
269
- onMouseLeave: function(e) { e.currentTarget.style.background = 'transparent'; },
270
- onClick: function() { setTabContextMenu(null); onCloseSaved(); }
271
- },
272
- React.createElement('i', { className: 'fas fa-check', style: { width: '14px', textAlign: 'center' } }),
273
- 'Close Saved'
274
- ),
275
- onCloseAll && React.createElement(
276
- 'div',
277
- {
278
- className: 'ide-tab-context-menu-item',
279
- style: { padding: '6px 14px', cursor: 'pointer', color: '#ccc', fontSize: '12px', display: 'flex', alignItems: 'center', gap: '8px' },
280
- onMouseEnter: function(e) { e.currentTarget.style.background = '#094771'; },
281
- onMouseLeave: function(e) { e.currentTarget.style.background = 'transparent'; },
282
- onClick: function() { setTabContextMenu(null); onCloseAll(); }
283
- },
284
- React.createElement('i', { className: 'fas fa-times', style: { width: '14px', textAlign: 'center' } }),
285
- 'Close All'
286
- ),
274
+ menuItem('fas fa-times', 'Close', function() { setTabContextMenu(null); onClose(tabContextMenu.tab.id); }),
275
+ onCloseOthers && menuItem('fas fa-times-circle', 'Close Others', function() { setTabContextMenu(null); onCloseOthers(tabContextMenu.tab.id); }),
276
+ onCloseSaved && menuItem('fas fa-check', 'Close Saved', function() { setTabContextMenu(null); onCloseSaved(); }),
277
+ onCloseAll && menuItem('fas fa-times', 'Close All', function() { setTabContextMenu(null); onCloseAll(); }),
287
278
  React.createElement('div', { style: { height: '1px', background: '#454545', margin: '4px 0' } }),
288
- onShowHistory && React.createElement(
289
- 'div',
290
- {
291
- className: 'ide-tab-context-menu-item',
292
- style: { padding: '6px 14px', cursor: 'pointer', color: '#ccc', fontSize: '12px', display: 'flex', alignItems: 'center', gap: '8px' },
293
- onMouseEnter: function(e) { e.currentTarget.style.background = '#094771'; },
294
- onMouseLeave: function(e) { e.currentTarget.style.background = 'transparent'; },
295
- onClick: function() {
296
- setTabContextMenu(null);
297
- onShowHistory(tabContextMenu.tab.path);
298
- }
299
- },
300
- React.createElement('i', { className: 'fas fa-history', style: { width: '14px', textAlign: 'center' } }),
301
- 'File History'
302
- ),
303
- onRevealInExplorer && React.createElement(
304
- 'div',
305
- {
306
- className: 'ide-tab-context-menu-item',
307
- style: { padding: '6px 14px', cursor: 'pointer', color: '#ccc', fontSize: '12px', display: 'flex', alignItems: 'center', gap: '8px' },
308
- onMouseEnter: function(e) { e.currentTarget.style.background = '#094771'; },
309
- onMouseLeave: function(e) { e.currentTarget.style.background = 'transparent'; },
310
- onClick: function() {
311
- setTabContextMenu(null);
312
- onRevealInExplorer(tabContextMenu.tab.path);
313
- }
314
- },
315
- React.createElement('i', { className: 'fas fa-sitemap', style: { width: '14px', textAlign: 'center' } }),
316
- 'Find in Explorer'
317
- )
279
+ onShowHistory && menuItem('fas fa-history', 'File History', function() {
280
+ setTabContextMenu(null);
281
+ onShowHistory(tabContextMenu.tab.path);
282
+ }),
283
+ onRevealInExplorer && menuItem('fas fa-sitemap', 'Find in Explorer', function() {
284
+ setTabContextMenu(null);
285
+ onRevealInExplorer(tabContextMenu.tab.path);
286
+ })
318
287
  )
319
288
  );
320
289
  };