mbeditor 0.13.1 → 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 (58) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +91 -0
  3. data/app/assets/javascripts/mbeditor/application.js +3 -0
  4. data/app/assets/javascripts/mbeditor/audit_log.js +165 -0
  5. data/app/assets/javascripts/mbeditor/collaboration_service.js +248 -26
  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 +429 -247
  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/LogPanel.js +3 -44
  17. data/app/assets/javascripts/mbeditor/components/MbeditorApp.js +1168 -1243
  18. data/app/assets/javascripts/mbeditor/components/ModelGraph.js +94 -37
  19. data/app/assets/javascripts/mbeditor/components/ProblemsPanel.js +47 -65
  20. data/app/assets/javascripts/mbeditor/components/QuickOpenDialog.js +26 -8
  21. data/app/assets/javascripts/mbeditor/components/SettingsModal.js +342 -0
  22. data/app/assets/javascripts/mbeditor/components/ShortcutHelp.js +2 -1
  23. data/app/assets/javascripts/mbeditor/components/TabBar.js +67 -98
  24. data/app/assets/javascripts/mbeditor/editor_plugins.js +687 -305
  25. data/app/assets/javascripts/mbeditor/file_import.js +13 -15
  26. data/app/assets/javascripts/mbeditor/file_service.js +38 -39
  27. data/app/assets/javascripts/mbeditor/git_service.js +15 -1
  28. data/app/assets/javascripts/mbeditor/history_service.js +5 -13
  29. data/app/assets/javascripts/mbeditor/search_service.js +9 -0
  30. data/app/assets/javascripts/mbeditor/tab_manager.js +127 -49
  31. data/app/assets/javascripts/mbeditor/websocket_service.js +13 -5
  32. data/app/assets/stylesheets/mbeditor/application.css +6 -1
  33. data/app/assets/stylesheets/mbeditor/editor.css +642 -248
  34. data/app/assets/stylesheets/mbeditor/glass.css +163 -0
  35. data/app/assets/stylesheets/mbeditor/themes.css +90 -30
  36. data/app/channels/mbeditor/collaboration_channel.rb +17 -4
  37. data/app/controllers/mbeditor/application_controller.rb +26 -3
  38. data/app/controllers/mbeditor/editors_controller.rb +117 -439
  39. data/app/services/mbeditor/archive_service.rb +137 -0
  40. data/app/services/mbeditor/collaboration_doc_store.rb +143 -14
  41. data/app/services/mbeditor/editor_state_service.rb +14 -51
  42. data/app/services/mbeditor/file_history_service.rb +222 -0
  43. data/app/services/mbeditor/git_info_service.rb +6 -0
  44. data/app/services/mbeditor/js_syntax_check_service.rb +42 -16
  45. data/app/services/mbeditor/lint_service.rb +137 -0
  46. data/app/services/mbeditor/locked_json_file.rb +67 -0
  47. data/app/services/mbeditor/process_runner.rb +32 -0
  48. data/app/services/mbeditor/ruby_lsp_result_translator.rb +226 -0
  49. data/app/services/mbeditor/search_replace_service.rb +8 -0
  50. data/app/views/layouts/mbeditor/application.html.erb +1 -1
  51. data/lib/mbeditor/audit_log.rb +203 -0
  52. data/lib/mbeditor/configuration.rb +6 -1
  53. data/lib/mbeditor/rack/pending_migration_bypass.rb +15 -9
  54. data/lib/mbeditor/route_map.rb +4 -0
  55. data/lib/mbeditor/ruby_lsp_client.rb +82 -14
  56. data/lib/mbeditor/version.rb +1 -1
  57. data/lib/mbeditor.rb +1 -0
  58. metadata +12 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fe5dce956e01ff66387b9ccf99fc8b163ce131a226158eda7b6cf2c8513eaa09
4
- data.tar.gz: 37e4f12f4ccd1ca7d209e7fe134a25d813bbe175862d53a470a5042aff9d7797
3
+ metadata.gz: 89b467923bfe67c35d68e57b667357c96c4217c39fff779f5ff98911eed59f25
4
+ data.tar.gz: e1b82cc6eddeaec6d31f00f83eff3b3e56abba423411304c9025b68dd9efbd81
5
5
  SHA512:
6
- metadata.gz: 60c74bb23b92719d4273d720af42813e2ecd3d639281901d47037e332697819cbe58c338eea9aeff334f3533d48d2e62c994b2122fb36fbded4bef94bd225b36
7
- data.tar.gz: 1451f3b2effb5d9cffd9811b41ea72b07d174702af9e6dc9993d5fbc92d990109dbf834e957c1eced22e8d77b815173d14bf6465b51072002a0e1c482745dc69
6
+ metadata.gz: 1e15d0215eba919681e9a435de8aeadcd3d24bbc1c9dc72f95f9036c2a39a89e39b28461d2acce844b4a9b5c2b1507318d84da3cdae62b08162f6412daa4dd8c
7
+ data.tar.gz: 611f9efe9f06536eb81ecf2b8f09d2da282958a17131b56baca6b47c07d10c2508a7797cba2f5469df457c2647b052a26f97a281fa8f44d7e574a2d89fe12d83
data/CHANGELOG.md CHANGED
@@ -5,6 +5,97 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.14.0] - 2026-09-21
9
+
10
+ ### Added
11
+ - **Liquid Glass chrome, opt-in** (Settings → Appearance). Translucent, blurred
12
+ panels layered over whichever theme is active — it is a layer, not a twelfth
13
+ theme, so every colour is derived from the existing `--ide-*` tokens. Monaco
14
+ itself stays solid: legibility beats the effect, and a blurred backdrop under
15
+ a text editor costs a re-composite per repaint. The default chrome is the new
16
+ Modern one, matching VS Code's Dark Modern.
17
+ - **Drag-to-open gutters.** One `Gutter` component owns every draggable gap.
18
+ Drag the strip above the status bar to open Problems, the right edge to open
19
+ the git panel, and the collapsed sidebar's gutter rightwards to snap the
20
+ explorer back open.
21
+ - **Settings is a searchable modal**, not an editor tab, so it no longer takes a
22
+ pane or persists into your tab list.
23
+ - **The model graph opens as its own view**, not a tab: the activity-bar icon
24
+ toggles it, it takes the whole centre column, and both side panels hide with
25
+ it. A layered graph is inherently wide and the tab gave it whatever was left
26
+ beside the explorer.
27
+ - **A downloadable audit log** (Settings → Diagnostics) that structurally cannot
28
+ carry a file name, path or URL — every field is a number, boolean or authored
29
+ Symbol.
30
+ - **Quick Open as a frosted palette** over the title bar, with a chevron
31
+ breadcrumb and VS Code-style Open Editors rows.
32
+ - Auto-close tags in ERB and multi-line JSX, linked tag pairs, and `props.x`
33
+ completion.
34
+ - Multi-select download in the explorer.
35
+ - A `toolbarLabels` preference: toolbar button labels are now opt-in.
36
+ - Accessibility: a focus-visible ring throughout, a real tab-close button, a
37
+ higher-contrast version chip, and the offline pulse respects reduced-motion.
38
+
39
+ ### Changed
40
+ - **The pending-migration warning is no longer a banner.** It sat above
41
+ everything and pushed the editor down the page every time it appeared. The
42
+ status bar turns amber instead and carries a "Migrations pending" item. Both
43
+ halves of that colour are per-theme, because `--ide-warning` is authored as a
44
+ foreground colour and several themes pick an ochre that is unreadable as a
45
+ background; measured contrast is 4.87:1 to 14.97:1 across the eleven themes.
46
+ - **Peer presence is an avatar stack**, not one chip per peer. Eight people used
47
+ to mean eight buttons across the title bar; it is now three overlapping dots
48
+ and a `+N`, fixed width whatever the roster size. Hovering a dot opens the
49
+ usual card, clicking follows or unfollows, and the `+N` opens a list of
50
+ everyone else with a follow toggle per row.
51
+ - The title bar says "Mbeditor" rather than the full window title; the host and
52
+ port are in the browser's own address bar.
53
+ - The file-tree section header has no chevron. Collapsing it left an empty
54
+ sidebar, so there was nothing to collapse to.
55
+ - Tabs are rounded chips on the strip, the Problems panel wears VS Code chrome,
56
+ and problem counts are neutral rather than alarming.
57
+ - `.scss` files open as scss and `.less` files as less, instead of css and
58
+ plaintext.
59
+ - Audit recording moved off the subprocess critical path.
60
+
61
+ ### Fixed
62
+ - **The pending-migration warning stuck after `rails db:migrate`**, and
63
+ flickered before that. The header announcing it was only *set* when a
64
+ migration was pending, and those responses are conditional-GET cacheable: a
65
+ 304 updates the stored headers of the cached 200 but never removes one it
66
+ omits, so the browser replayed "pending" for as long as the cache entry
67
+ lived. It is now always sent, `1` or `0`, and an absent header counts as no
68
+ evidence either way.
69
+ - **The model graph was unusable on a large app.** The cost was in click and
70
+ hover, not pan and zoom: `focused` was a dependency of the scene memo, so
71
+ every click-to-centre rebuilt every SVG element inside the click handler;
72
+ node hover scanned the whole scene per mouseenter; and the hover card and
73
+ edge tooltip each read `getBoundingClientRect` per pointer move, right after
74
+ hover had dirtied the style, forcing a full-scene layout. Measured on a
75
+ 420-model, 1,249-association app: click-to-centre 20.4 ms → 7.9 ms median,
76
+ hover 45.7 ms → 33.2 ms.
77
+ - Collaboration data-loss paths, and the per-keystroke buffer work that went
78
+ with them.
79
+ - Undo tracking starts after the file load, not before, so the first undo no
80
+ longer empties a freshly opened file.
81
+ - Ruby hover skips comments and the route-hint decoration space, and
82
+ go-to-definition starts the Ripper fallback alongside ruby-lsp rather than
83
+ after it.
84
+ - Closing a markdown file closes its preview, and source and preview scroll in
85
+ sync.
86
+ - Markdown anchor links, upload UX, the conflict chip and the cursor readout.
87
+ - `RubyLspClient` performs its handshake outside the state lock, bounds the
88
+ document-lock wait, and keeps an LRU of documents.
89
+ - `JsSyntaxCheckService` remembers a broken babel bundle instead of retrying it,
90
+ and bounds its lock wait.
91
+ - The test-result `localStorage` cache is capped, and failed draft backups are
92
+ surfaced rather than silently dropped.
93
+ - `json` is pinned below 3 so the suite runs.
94
+
95
+ ### Removed
96
+ - Stale agent docs, the dead `build_js` rake task and a set of duplicated
97
+ helpers — about 1,100 lines.
98
+
8
99
  ## [0.13.1] - 2026-08-26
9
100
 
10
101
  ### Removed
@@ -1,4 +1,5 @@
1
1
  //= require mbeditor/application_iife_head
2
+ //= require mbeditor/audit_log
2
3
  //= require mbeditor/editor_store
3
4
  //= require mbeditor/file_icon
4
5
  //= require mbeditor/file_service
@@ -17,6 +18,8 @@
17
18
  //= require mbeditor/ruby_outline
18
19
  //= require mbeditor/js_outline
19
20
  //= require mbeditor/components/CollapsibleSection
21
+ //= require mbeditor/components/SettingsModal
22
+ //= require mbeditor/components/Gutter
20
23
  //= require mbeditor/components/ShortcutHelp
21
24
  //= require mbeditor/components/DiffViewer
22
25
  //= require mbeditor/components/CombinedDiffViewer
@@ -0,0 +1,165 @@
1
+ // Audit/telemetry ring the developer can download and hand to an AI to analyse.
2
+ //
3
+ // The privacy guarantee is structural, not a scrubber: rec() is the only way
4
+ // in, and it drops any argument whose typeof is not 'number'. A path, a file
5
+ // name, a URL or a line of source cannot be represented at all. Enumerated
6
+ // dimensions (file extension, search backend tier, LSP method, collab phase)
7
+ // travel as small integers whose meaning lives in LEGEND. LEGEND stays here and
8
+ // is merged into the file at download time: sending it to the server would be
9
+ // the one path in this module that carries an arbitrary string, which is
10
+ // exactly the property the rest of it exists to deny.
11
+ window.MbeditorAudit = (function () {
12
+ var CAPACITY = 512;
13
+ var FLUSH_AT = CAPACITY / 2;
14
+
15
+ var EV = Object.freeze({
16
+ BOOT: 1,
17
+ OPEN: 2,
18
+ SAVE: 3,
19
+ SEARCH: 4,
20
+ TREE_POLL: 5,
21
+ GIT_POLL: 6,
22
+ LINT: 7,
23
+ FORMAT: 8,
24
+ LSP: 9,
25
+ COLLAB: 10,
26
+ LONGTASK: 11,
27
+ ERR: 12
28
+ });
29
+
30
+ var LEGEND = {
31
+ ev: {},
32
+ // What a, b and c mean per event. A field named after one of the
33
+ // enumerated dimensions below carries an index into that table.
34
+ fields: {
35
+ BOOT: ['msToReady', '', ''],
36
+ OPEN: ['ext', 'bytes', 'ms'],
37
+ SAVE: ['ext', 'bytes', 'ms'],
38
+ SEARCH: ['searchBackend', 'ms', 'hits'],
39
+ TREE_POLL: ['ms', 'changed', ''],
40
+ GIT_POLL: ['ms', 'changedFiles', ''],
41
+ LINT: ['ext', 'ms', 'markers'],
42
+ FORMAT: ['ext', 'ms', 'bytes'],
43
+ LSP: ['lspMethod', 'ms', 'ok'],
44
+ COLLAB: ['collabPhase', 'peers', 'ms'],
45
+ LONGTASK: ['ms', '', ''],
46
+ ERR: ['ev', '', '']
47
+ },
48
+ // Index 0 is always "other", so an unrecognised name still records.
49
+ ext: ['other', 'rb', 'js', 'jsx', 'erb', 'haml', 'css', 'scss', 'html', 'md', 'json', 'yml', 'ts', 'tsx'],
50
+ searchBackend: ['other', 'rg', 'git', 'grep'],
51
+ lspMethod: ['other', 'definition', 'hover', 'completion', 'diagnostics', 'references', 'rename',
52
+ 'format', 'signatureHelp', 'documentSymbol', 'foldingRange', 'documentHighlight',
53
+ 'selectionRange', 'codeAction'],
54
+ collabPhase: ['other', 'join', 'seed', 'attach', 'defer', 'update', 'reconnect', 'leave', 'degrade']
55
+ };
56
+ Object.keys(EV).forEach(function (name) { LEGEND.ev[EV[name]] = name; });
57
+
58
+ // Preallocated: rec() reuses the slot, so recording allocates nothing.
59
+ var ring = new Array(CAPACITY);
60
+ for (var i = 0; i < CAPACITY; i++) ring[i] = [0, 0, 0, 0, 0];
61
+
62
+ var write = 0;
63
+ var count = 0;
64
+ var enabled = false;
65
+ var scheduled = false;
66
+ var observing = false;
67
+
68
+ // The whole privacy guarantee in one line.
69
+ function num(v) { return typeof v === 'number' && isFinite(v) ? v : 0; }
70
+
71
+ function rec(ev, a, b, c) {
72
+ if (!enabled) return;
73
+ var e = ring[write];
74
+ e[0] = performance.now() | 0;
75
+ e[1] = num(ev);
76
+ e[2] = num(a);
77
+ e[3] = num(b);
78
+ e[4] = num(c);
79
+ write = write + 1 === CAPACITY ? 0 : write + 1;
80
+ if (count < CAPACITY) count++;
81
+ if (count >= FLUSH_AT) schedule();
82
+ }
83
+
84
+ function code(dimension, name) {
85
+ var table = LEGEND[dimension];
86
+ var index = table ? table.indexOf(name) : -1;
87
+ return index < 0 ? 0 : index;
88
+ }
89
+
90
+ function drain() {
91
+ var out = new Array(count);
92
+ var start = (write - count + CAPACITY) % CAPACITY;
93
+ for (var i = 0; i < count; i++) {
94
+ var e = ring[(start + i) % CAPACITY];
95
+ out[i] = [e[0], e[1], e[2], e[3], e[4]];
96
+ }
97
+ count = 0;
98
+ write = 0;
99
+ return out;
100
+ }
101
+
102
+ // keepalive rather than navigator.sendBeacon: the CSRF guard needs the
103
+ // X-Mbeditor-Client header and sendBeacon cannot set one.
104
+ function flush() {
105
+ scheduled = false;
106
+ if (!enabled || count === 0) return Promise.resolve();
107
+ var body = JSON.stringify({ events: drain() });
108
+ return fetch(window.mbeditorBasePath() + '/audit_log', {
109
+ method: 'POST',
110
+ keepalive: true,
111
+ headers: { 'X-Mbeditor-Client': '1', 'Content-Type': 'application/json' },
112
+ body: body
113
+ })['catch'](function () {});
114
+ }
115
+
116
+ function schedule() {
117
+ if (scheduled) return;
118
+ scheduled = true;
119
+ if (window.requestIdleCallback) {
120
+ window.requestIdleCallback(function () { flush(); }, { timeout: 2000 });
121
+ } else {
122
+ setTimeout(function () { flush(); }, 0);
123
+ }
124
+ }
125
+
126
+ function observe() {
127
+ if (observing) return;
128
+ observing = true;
129
+ try {
130
+ new PerformanceObserver(function (list) {
131
+ var items = list.getEntries();
132
+ for (var i = 0; i < items.length; i++) rec(EV.LONGTASK, items[i].duration | 0);
133
+ }).observe({ type: 'longtask', buffered: true });
134
+ } catch (e) {
135
+ // Safari has no longtask entry type. Everything else keeps recording.
136
+ }
137
+ }
138
+
139
+ function clear() { count = 0; write = 0; }
140
+
141
+ function setEnabled(on) {
142
+ on = !!on;
143
+ if (on === enabled) return;
144
+ enabled = on;
145
+ // Disabled means nothing recorded, including what was buffered before the
146
+ // preference arrived from the server.
147
+ if (on) observe(); else clear();
148
+ }
149
+
150
+ window.addEventListener('pagehide', function () { flush(); });
151
+ document.addEventListener('visibilitychange', function () {
152
+ if (document.visibilityState === 'hidden') flush();
153
+ });
154
+
155
+ return {
156
+ EV: EV,
157
+ LEGEND: LEGEND,
158
+ rec: rec,
159
+ code: code,
160
+ flush: flush,
161
+ clear: clear,
162
+ setEnabled: setEnabled,
163
+ buffered: function () { return count; }
164
+ };
165
+ })();