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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +157 -1
- data/app/assets/javascripts/mbeditor/application.js +3 -1
- data/app/assets/javascripts/mbeditor/audit_log.js +165 -0
- data/app/assets/javascripts/mbeditor/collaboration_service.js +264 -22
- data/app/assets/javascripts/mbeditor/components/ChangelogView.js +89 -94
- data/app/assets/javascripts/mbeditor/components/CodeReviewPanel.js +6 -9
- data/app/assets/javascripts/mbeditor/components/CollapsibleSection.js +12 -7
- data/app/assets/javascripts/mbeditor/components/CombinedDiffViewer.js +20 -0
- data/app/assets/javascripts/mbeditor/components/DiffViewer.js +1 -1
- data/app/assets/javascripts/mbeditor/components/EditorPanel.js +440 -334
- data/app/assets/javascripts/mbeditor/components/FileHistoryPanel.js +6 -9
- data/app/assets/javascripts/mbeditor/components/FileTree.js +26 -12
- data/app/assets/javascripts/mbeditor/components/GitPanel.js +3 -0
- data/app/assets/javascripts/mbeditor/components/Gutter.js +51 -0
- data/app/assets/javascripts/mbeditor/components/ImportDialog.js +9 -1
- data/app/assets/javascripts/mbeditor/components/LogPanel.js +3 -44
- data/app/assets/javascripts/mbeditor/components/MbeditorApp.js +1322 -1431
- data/app/assets/javascripts/mbeditor/components/ModelGraph.js +94 -37
- data/app/assets/javascripts/mbeditor/components/ProblemsPanel.js +82 -72
- data/app/assets/javascripts/mbeditor/components/QuickOpenDialog.js +121 -81
- data/app/assets/javascripts/mbeditor/components/SettingsModal.js +342 -0
- data/app/assets/javascripts/mbeditor/components/ShortcutHelp.js +2 -1
- data/app/assets/javascripts/mbeditor/components/TabBar.js +67 -98
- data/app/assets/javascripts/mbeditor/editor_plugins.js +694 -306
- data/app/assets/javascripts/mbeditor/file_import.js +13 -15
- data/app/assets/javascripts/mbeditor/file_service.js +46 -57
- data/app/assets/javascripts/mbeditor/git_service.js +15 -1
- data/app/assets/javascripts/mbeditor/history_service.js +5 -13
- data/app/assets/javascripts/mbeditor/search_service.js +29 -2
- data/app/assets/javascripts/mbeditor/tab_manager.js +135 -54
- data/app/assets/javascripts/mbeditor/websocket_service.js +13 -5
- data/app/assets/stylesheets/mbeditor/application.css +6 -1
- data/app/assets/stylesheets/mbeditor/editor.css +762 -297
- data/app/assets/stylesheets/mbeditor/glass.css +163 -0
- data/app/assets/stylesheets/mbeditor/themes.css +90 -30
- data/app/channels/mbeditor/collaboration_channel.rb +25 -6
- data/app/controllers/mbeditor/application_controller.rb +26 -3
- data/app/controllers/mbeditor/editors_controller.rb +125 -465
- data/app/services/mbeditor/archive_service.rb +137 -0
- data/app/services/mbeditor/collaboration_doc_store.rb +180 -12
- data/app/services/mbeditor/duplicate_content_scanner.rb +105 -0
- data/app/services/mbeditor/editor_state_service.rb +14 -51
- data/app/services/mbeditor/file_history_service.rb +222 -0
- data/app/services/mbeditor/git_info_service.rb +6 -0
- data/app/services/mbeditor/js_globals_service.rb +12 -1
- data/app/services/mbeditor/js_syntax_check_service.rb +42 -16
- data/app/services/mbeditor/lint_service.rb +137 -0
- data/app/services/mbeditor/locked_json_file.rb +67 -0
- data/app/services/mbeditor/process_runner.rb +32 -0
- data/app/services/mbeditor/rubocop_run_service.rb +17 -5
- data/app/services/mbeditor/ruby_lsp_result_translator.rb +226 -0
- data/app/services/mbeditor/schema_service.rb +8 -2
- data/app/services/mbeditor/search_replace_service.rb +19 -2
- data/app/services/mbeditor/test_runner_service.rb +3 -56
- data/app/views/layouts/mbeditor/application.html.erb +1 -1
- data/lib/mbeditor/audit_log.rb +203 -0
- data/lib/mbeditor/configuration.rb +6 -9
- data/lib/mbeditor/rack/pending_migration_bypass.rb +15 -9
- data/lib/mbeditor/route_map.rb +4 -1
- data/lib/mbeditor/ruby_lsp_client.rb +82 -14
- data/lib/mbeditor/version.rb +1 -1
- data/lib/mbeditor.rb +1 -0
- data/lib/tasks/mbeditor.rake +23 -0
- metadata +14 -3
- data/app/assets/javascripts/mbeditor/components/TestRunPanel.js +0 -312
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 89b467923bfe67c35d68e57b667357c96c4217c39fff779f5ff98911eed59f25
|
|
4
|
+
data.tar.gz: e1b82cc6eddeaec6d31f00f83eff3b3e56abba423411304c9025b68dd9efbd81
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1e15d0215eba919681e9a435de8aeadcd3d24bbc1c9dc72f95f9036c2a39a89e39b28461d2acce844b4a9b5c2b1507318d84da3cdae62b08162f6412daa4dd8c
|
|
7
|
+
data.tar.gz: 611f9efe9f06536eb81ecf2b8f09d2da282958a17131b56baca6b47c07d10c2508a7797cba2f5469df457c2647b052a26f97a281fa8f44d7e574a2d89fe12d83
|
data/CHANGELOG.md
CHANGED
|
@@ -5,7 +5,163 @@ 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
|
-
## [
|
|
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
|
+
|
|
99
|
+
## [0.13.1] - 2026-08-26
|
|
100
|
+
|
|
101
|
+
### Removed
|
|
102
|
+
- **The whole-suite test run is gone** — the status-bar "Tests" button, the
|
|
103
|
+
Test Run drawer, `POST /test_all`, and the `test_all_command` /
|
|
104
|
+
`test_all_timeout` settings. It was one blocking request holding a
|
|
105
|
+
development-server thread for the length of the suite, with no way to cancel
|
|
106
|
+
it and the runner's output buffered whole in the server process; on a real
|
|
107
|
+
host app that is twenty minutes of a frozen-looking button for a result the
|
|
108
|
+
terminal gives you faster and interruptibly. The per-file (and per-line) test
|
|
109
|
+
run stays — that one is scoped, finishes in seconds and is why the editor
|
|
110
|
+
runs tests at all. **A host app setting `test_all_command` or
|
|
111
|
+
`test_all_timeout` in its initializer must drop those lines.**
|
|
112
|
+
- **The ruby-lsp status-bar chip is gone**, along with its 10-second health
|
|
113
|
+
poll. ruby-lsp itself is untouched — definitions, hover, completion,
|
|
114
|
+
diagnostics and the rest all still run through it.
|
|
115
|
+
|
|
116
|
+
### Added
|
|
117
|
+
- `rake mbeditor:scan_duplicates` — scans the workspace for files that contain a
|
|
118
|
+
duplicated copy of themselves, for finding files damaged before the seeding fix below.
|
|
119
|
+
Set `MBEDITOR_WORKSPACE_ROOT` to scan a tree other than the resolved workspace.
|
|
120
|
+
|
|
121
|
+
### Fixed
|
|
122
|
+
- **Data loss: a saved file could contain two copies of itself.** When two editor
|
|
123
|
+
clients attached to a collaborative room the server had no state for — a fresh
|
|
124
|
+
room, or one evicted while both sat idle — each one seeded the shared document
|
|
125
|
+
from disk, and Yjs merged both inserts at offset 0 into two concatenated copies.
|
|
126
|
+
Saving then wrote the doubled buffer. The server now grants the right to seed a
|
|
127
|
+
room to exactly one client (`CollaborationDocStore.claim_seed`), never evicts a
|
|
128
|
+
room that still has subscribers, and a client whose cable reconnects re-publishes
|
|
129
|
+
its snapshot so a server that lost its state cannot hand out a second seed.
|
|
130
|
+
- **Status bar alignment.** Every item now sits in the same 8px cell instead of
|
|
131
|
+
carrying its own padding on top of the bar's (the branch, the offline chip
|
|
132
|
+
and the version each had a different one), the branch drops from 13px to the
|
|
133
|
+
bar's 11px, and `line-height: 1` on the bar *and* on its buttons puts an icon
|
|
134
|
+
and the number beside it in identical line boxes — Pico gives `<button>` its
|
|
135
|
+
own 1.5, so the problem counts sat in a 16.5px box next to an 11px icon.
|
|
136
|
+
- **The Problems panel's Fix button did nothing visible.** `rubocop -a` writes
|
|
137
|
+
its corrections to disk, but reports the offenses it just fixed alongside the
|
|
138
|
+
ones it left — each flagged both `corrected` and `correctable` — so the list
|
|
139
|
+
after a fix was byte-identical to the list before it and the button stayed
|
|
140
|
+
lit. Corrected offenses are now dropped from the response, open buffers are
|
|
141
|
+
re-read after the run, and clearing stale diagnostics now clears the React
|
|
142
|
+
marker map as well as Monaco's, which is what had left the panel, the tab
|
|
143
|
+
badges and the status-bar tallies disagreeing.
|
|
144
|
+
- **Quick Open matches loosely again.** MiniSearch was configured `fuzzy: false`,
|
|
145
|
+
so a single typo found nothing; fuzziness is now scaled per term. Folders sort
|
|
146
|
+
after files instead of ahead of every match, and the dialog and its text are
|
|
147
|
+
larger.
|
|
148
|
+
- **Search results read like VS Code's.** Hits group under a collapsible row per
|
|
149
|
+
file with a match count, the matched text is tinted with the accent colour and
|
|
150
|
+
is kept on screen at any panel width (eliding left and right around it), and
|
|
151
|
+
clicking a hit selects the match and leaves the cursor at its end.
|
|
152
|
+
- **Bulk import no longer opens on the previous import's folder** — the folder
|
|
153
|
+
you actually acted on is offered first — and it now reports what it wrote and
|
|
154
|
+
reveals the destination in the tree.
|
|
155
|
+
- `ReactRailsUJS` no longer reports as undefined; it ships inside the react-rails
|
|
156
|
+
gem, outside the workspace, so no static scan of the tree could ever find it.
|
|
157
|
+
- `<<~JSX` heredocs highlight as JavaScript (as do `<<~TSX`/`<<~TS`).
|
|
158
|
+
- The Open Editors list no longer runs its filenames underneath the split and
|
|
159
|
+
close buttons.
|
|
160
|
+
|
|
161
|
+
### Changed
|
|
162
|
+
- **The activity bar is a third narrower** (60px to 40px), and the editor
|
|
163
|
+
toolbar's per-file Test button is gone; running the test under the cursor is
|
|
164
|
+
unaffected.
|
|
9
165
|
|
|
10
166
|
## [0.13.0] - 2026-08-20
|
|
11
167
|
|
|
@@ -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
|
|
@@ -27,7 +30,6 @@
|
|
|
27
30
|
//= require mbeditor/components/TestResultsPanel
|
|
28
31
|
//= require mbeditor/components/LogPanel
|
|
29
32
|
//= require mbeditor/components/ProblemsPanel
|
|
30
|
-
//= require mbeditor/components/TestRunPanel
|
|
31
33
|
//= require mbeditor/components/CodeReviewPanel
|
|
32
34
|
//= require mbeditor/components/EditorPanel
|
|
33
35
|
//= require mbeditor/components/FileTree
|
|
@@ -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
|
+
})();
|