mbeditor 0.13.1 → 0.14.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.
Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +132 -0
  3. data/app/assets/javascripts/mbeditor/application.js +5 -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/CombinedFileView.js +214 -0
  11. data/app/assets/javascripts/mbeditor/components/DiffViewer.js +1 -1
  12. data/app/assets/javascripts/mbeditor/components/EditorPanel.js +442 -248
  13. data/app/assets/javascripts/mbeditor/components/FileHistoryPanel.js +6 -9
  14. data/app/assets/javascripts/mbeditor/components/FileTree.js +26 -12
  15. data/app/assets/javascripts/mbeditor/components/GitPanel.js +3 -0
  16. data/app/assets/javascripts/mbeditor/components/Gutter.js +51 -0
  17. data/app/assets/javascripts/mbeditor/components/ImportConflictModal.js +60 -5
  18. data/app/assets/javascripts/mbeditor/components/LogPanel.js +3 -44
  19. data/app/assets/javascripts/mbeditor/components/MbeditorApp.js +1241 -1256
  20. data/app/assets/javascripts/mbeditor/components/ModelGraph.js +94 -37
  21. data/app/assets/javascripts/mbeditor/components/PastePlaceModal.js +329 -0
  22. data/app/assets/javascripts/mbeditor/components/ProblemsPanel.js +47 -65
  23. data/app/assets/javascripts/mbeditor/components/QuickOpenDialog.js +26 -8
  24. data/app/assets/javascripts/mbeditor/components/SettingsModal.js +342 -0
  25. data/app/assets/javascripts/mbeditor/components/ShortcutHelp.js +18 -1
  26. data/app/assets/javascripts/mbeditor/components/TabBar.js +67 -98
  27. data/app/assets/javascripts/mbeditor/editor_plugins.js +705 -323
  28. data/app/assets/javascripts/mbeditor/file_import.js +13 -15
  29. data/app/assets/javascripts/mbeditor/file_service.js +48 -39
  30. data/app/assets/javascripts/mbeditor/git_service.js +15 -1
  31. data/app/assets/javascripts/mbeditor/history_service.js +5 -13
  32. data/app/assets/javascripts/mbeditor/search_service.js +9 -0
  33. data/app/assets/javascripts/mbeditor/tab_manager.js +127 -49
  34. data/app/assets/javascripts/mbeditor/websocket_service.js +13 -5
  35. data/app/assets/stylesheets/mbeditor/application.css +6 -1
  36. data/app/assets/stylesheets/mbeditor/editor.css +672 -249
  37. data/app/assets/stylesheets/mbeditor/glass.css +174 -0
  38. data/app/assets/stylesheets/mbeditor/themes.css +93 -33
  39. data/app/channels/mbeditor/collaboration_channel.rb +17 -4
  40. data/app/controllers/mbeditor/application_controller.rb +26 -3
  41. data/app/controllers/mbeditor/editors_controller.rb +124 -439
  42. data/app/services/mbeditor/archive_service.rb +137 -0
  43. data/app/services/mbeditor/collaboration_doc_store.rb +143 -14
  44. data/app/services/mbeditor/editor_state_service.rb +14 -51
  45. data/app/services/mbeditor/file_history_service.rb +222 -0
  46. data/app/services/mbeditor/git_info_service.rb +6 -0
  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/ruby_lsp_result_translator.rb +226 -0
  52. data/app/services/mbeditor/ruby_method_ranges_service.rb +120 -0
  53. data/app/services/mbeditor/search_replace_service.rb +8 -0
  54. data/app/views/layouts/mbeditor/application.html.erb +1 -1
  55. data/lib/mbeditor/audit_log.rb +203 -0
  56. data/lib/mbeditor/configuration.rb +6 -1
  57. data/lib/mbeditor/rack/pending_migration_bypass.rb +31 -10
  58. data/lib/mbeditor/route_map.rb +5 -0
  59. data/lib/mbeditor/ruby_lsp_client.rb +82 -14
  60. data/lib/mbeditor/version.rb +1 -1
  61. data/lib/mbeditor.rb +1 -0
  62. data/public/monaco-editor/VERSIONS +1 -1
  63. data/public/monaco-editor/codicon.ttf +0 -0
  64. data/public/monaco-editor/css.worker.js +41 -37
  65. data/public/monaco-editor/editor.worker.js +19 -15
  66. data/public/monaco-editor/html.worker.js +53 -49
  67. data/public/monaco-editor/json.worker.js +36 -32
  68. data/public/monaco-editor/monaco.css +1 -1
  69. data/public/monaco-editor/monaco.js +388 -301
  70. data/public/monaco-editor/ts.worker.js +226 -222
  71. data/vendor/assets/javascripts/axios.min.js +3 -3
  72. metadata +15 -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: d6faf6632c3072f98fe34083c809069a4e37ed9e956fd6722fd2fb188ed13c55
4
+ data.tar.gz: 24e179af4efd250cef7ddeec7d81a8c033891af89cb286980ddbb3541f543239
5
5
  SHA512:
6
- metadata.gz: 60c74bb23b92719d4273d720af42813e2ecd3d639281901d47037e332697819cbe58c338eea9aeff334f3533d48d2e62c994b2122fb36fbded4bef94bd225b36
7
- data.tar.gz: 1451f3b2effb5d9cffd9811b41ea72b07d174702af9e6dc9993d5fbc92d990109dbf834e957c1eced22e8d77b815173d14bf6465b51072002a0e1c482745dc69
6
+ metadata.gz: 800cbc5afda678e3ae3298dc781f39054c32ecb0098dc82e68df9c6f0f8222ba4e5c06d0c6b424e50360dda0f02ed678169c4f4922353c432c8c0b446f28f428
7
+ data.tar.gz: 49bca37c5e4a696a4ba1ec4f5f01454899d006cb827f109e11a96e46feb65544ac7c85afdc55756076302618a2af30ced210e1b149b85690899b6801f5dcd37b
data/CHANGELOG.md CHANGED
@@ -5,6 +5,138 @@ 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.1] - 2026-10-19
9
+
10
+ ### Added
11
+ - **Combined View.** Ctrl/Cmd-select files or folders in the explorer, right-click,
12
+ "Open in Combined View": every file on one scrolling page under its path, with
13
+ Copy and Download per file. Folders expand recursively; capped at 200 files with
14
+ a visible note. The tab is virtual and is not restored on reload.
15
+ - **Method picking and Markdown export.** In Combined View, Ruby files get a
16
+ method checklist; "Download .md" / "Copy .md" bundle the view into one file,
17
+ method-limited files keeping their `class`/`module` skeleton with `# ...`
18
+ between pieces. Built for chat assistants that take only a few attachments.
19
+ - **Paste & Place** (editor context menu or F1, Ruby). Paste methods from a
20
+ chat reply and mbeditor finds where each goes: the active file, then a repo
21
+ search for `def name` narrowed by class. New methods go into the file that
22
+ holds the class, before `private`. Diff preview first; Apply is one undoable
23
+ edit in the buffer and nothing is saved.
24
+ - **Compare on upload conflicts.** When an upload or drop would overwrite a
25
+ file, each text file gets a "Compare" diff of disk vs incoming.
26
+ - `POST /ruby_methods`: Ripper-based method ranges (owner, singleton, doc
27
+ comment start) behind the two features above.
28
+ - A help-panel section describing all of the above.
29
+
30
+ ### Fixed
31
+ - **Unreadable status bar and buttons.** The Liquid Glass status bar and the
32
+ title-bar buttons painted "text on the accent fill" onto panel-coloured
33
+ surfaces; glass also hid the amber pending-migration background; Dracula,
34
+ Night Owl and Tomorrow Night had white text on light accents. 44 combinations
35
+ (11 themes × glass × pending) now all clear 4.5:1 on the status bar.
36
+ - **Flicker while migrations are pending.** `/cable` was not covered by the
37
+ pending-migration bypass, so every reconnect raised and the live channel
38
+ (presence, collaboration, file-change push) stayed dead.
39
+ - Paste & Place searches use POSIX classes, because the `git grep` search tier
40
+ is ERE and silently matches nothing for `\s`.
41
+
42
+ ### Changed
43
+ - **Monaco 0.57.0** (from 0.55.1). Its TypeScript API moved from
44
+ `monaco.languages.typescript` to `monaco.typescript`; every call site is
45
+ migrated. DOMPurify inside Monaco is still 3.4.15 (advisory rated low) until a
46
+ Monaco release ships 3.4.16+.
47
+ - **axios 1.20.0** (from 1.17.0 vendored), for new high-severity advisories.
48
+
49
+ ## [0.14.0] - 2026-09-21
50
+
51
+ ### Added
52
+ - **Liquid Glass chrome, opt-in** (Settings → Appearance). Translucent, blurred
53
+ panels layered over whichever theme is active — it is a layer, not a twelfth
54
+ theme, so every colour is derived from the existing `--ide-*` tokens. Monaco
55
+ itself stays solid: legibility beats the effect, and a blurred backdrop under
56
+ a text editor costs a re-composite per repaint. The default chrome is the new
57
+ Modern one, matching VS Code's Dark Modern.
58
+ - **Drag-to-open gutters.** One `Gutter` component owns every draggable gap.
59
+ Drag the strip above the status bar to open Problems, the right edge to open
60
+ the git panel, and the collapsed sidebar's gutter rightwards to snap the
61
+ explorer back open.
62
+ - **Settings is a searchable modal**, not an editor tab, so it no longer takes a
63
+ pane or persists into your tab list.
64
+ - **The model graph opens as its own view**, not a tab: the activity-bar icon
65
+ toggles it, it takes the whole centre column, and both side panels hide with
66
+ it. A layered graph is inherently wide and the tab gave it whatever was left
67
+ beside the explorer.
68
+ - **A downloadable audit log** (Settings → Diagnostics) that structurally cannot
69
+ carry a file name, path or URL — every field is a number, boolean or authored
70
+ Symbol.
71
+ - **Quick Open as a frosted palette** over the title bar, with a chevron
72
+ breadcrumb and VS Code-style Open Editors rows.
73
+ - Auto-close tags in ERB and multi-line JSX, linked tag pairs, and `props.x`
74
+ completion.
75
+ - Multi-select download in the explorer.
76
+ - A `toolbarLabels` preference: toolbar button labels are now opt-in.
77
+ - Accessibility: a focus-visible ring throughout, a real tab-close button, a
78
+ higher-contrast version chip, and the offline pulse respects reduced-motion.
79
+
80
+ ### Changed
81
+ - **The pending-migration warning is no longer a banner.** It sat above
82
+ everything and pushed the editor down the page every time it appeared. The
83
+ status bar turns amber instead and carries a "Migrations pending" item. Both
84
+ halves of that colour are per-theme, because `--ide-warning` is authored as a
85
+ foreground colour and several themes pick an ochre that is unreadable as a
86
+ background; measured contrast is 4.87:1 to 14.97:1 across the eleven themes.
87
+ - **Peer presence is an avatar stack**, not one chip per peer. Eight people used
88
+ to mean eight buttons across the title bar; it is now three overlapping dots
89
+ and a `+N`, fixed width whatever the roster size. Hovering a dot opens the
90
+ usual card, clicking follows or unfollows, and the `+N` opens a list of
91
+ everyone else with a follow toggle per row.
92
+ - The title bar says "Mbeditor" rather than the full window title; the host and
93
+ port are in the browser's own address bar.
94
+ - The file-tree section header has no chevron. Collapsing it left an empty
95
+ sidebar, so there was nothing to collapse to.
96
+ - Tabs are rounded chips on the strip, the Problems panel wears VS Code chrome,
97
+ and problem counts are neutral rather than alarming.
98
+ - `.scss` files open as scss and `.less` files as less, instead of css and
99
+ plaintext.
100
+ - Audit recording moved off the subprocess critical path.
101
+
102
+ ### Fixed
103
+ - **The pending-migration warning stuck after `rails db:migrate`**, and
104
+ flickered before that. The header announcing it was only *set* when a
105
+ migration was pending, and those responses are conditional-GET cacheable: a
106
+ 304 updates the stored headers of the cached 200 but never removes one it
107
+ omits, so the browser replayed "pending" for as long as the cache entry
108
+ lived. It is now always sent, `1` or `0`, and an absent header counts as no
109
+ evidence either way.
110
+ - **The model graph was unusable on a large app.** The cost was in click and
111
+ hover, not pan and zoom: `focused` was a dependency of the scene memo, so
112
+ every click-to-centre rebuilt every SVG element inside the click handler;
113
+ node hover scanned the whole scene per mouseenter; and the hover card and
114
+ edge tooltip each read `getBoundingClientRect` per pointer move, right after
115
+ hover had dirtied the style, forcing a full-scene layout. Measured on a
116
+ 420-model, 1,249-association app: click-to-centre 20.4 ms → 7.9 ms median,
117
+ hover 45.7 ms → 33.2 ms.
118
+ - Collaboration data-loss paths, and the per-keystroke buffer work that went
119
+ with them.
120
+ - Undo tracking starts after the file load, not before, so the first undo no
121
+ longer empties a freshly opened file.
122
+ - Ruby hover skips comments and the route-hint decoration space, and
123
+ go-to-definition starts the Ripper fallback alongside ruby-lsp rather than
124
+ after it.
125
+ - Closing a markdown file closes its preview, and source and preview scroll in
126
+ sync.
127
+ - Markdown anchor links, upload UX, the conflict chip and the cursor readout.
128
+ - `RubyLspClient` performs its handshake outside the state lock, bounds the
129
+ document-lock wait, and keeps an LRU of documents.
130
+ - `JsSyntaxCheckService` remembers a broken babel bundle instead of retrying it,
131
+ and bounds its lock wait.
132
+ - The test-result `localStorage` cache is capped, and failed draft backups are
133
+ surfaced rather than silently dropped.
134
+ - `json` is pinned below 3 so the suite runs.
135
+
136
+ ### Removed
137
+ - Stale agent docs, the dead `build_js` rake task and a set of duplicated
138
+ helpers — about 1,100 lines.
139
+
8
140
  ## [0.13.1] - 2026-08-26
9
141
 
10
142
  ### 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,12 +18,15 @@
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
23
26
  //= require mbeditor/components/CommitGraph
24
27
  //= require mbeditor/components/ModelGraph
25
28
  //= require mbeditor/components/ChangelogView
29
+ //= require mbeditor/components/CombinedFileView
26
30
  //= require mbeditor/components/FileHistoryPanel
27
31
  //= require mbeditor/components/TestResultsPanel
28
32
  //= require mbeditor/components/LogPanel
@@ -39,5 +43,6 @@
39
43
  // whole app down the first time it renders.
40
44
  //= require mbeditor/components/ImportConflictModal
41
45
  //= require mbeditor/components/ImportDialog
46
+ //= require mbeditor/components/PastePlaceModal
42
47
  //= require mbeditor/components/MbeditorApp
43
48
  //= require mbeditor/application_iife_tail
@@ -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
+ })();