mbeditor 0.2.2 → 0.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 141c77f551fd195ca9003f28b527f22f29d735fa70e1a1f80f638ad5739edf2d
4
- data.tar.gz: 479f09a85f6c11c76f014ec85f269d5597bee72eb9e10135e65df275046a4fac
3
+ metadata.gz: 2a6414a87f25b938891d8e0de50d55442fe0b416450105f5b5b9b01b882798ec
4
+ data.tar.gz: f4b440dfe943a96e448cf88646443b8618d36fd3e1ee8957864d77458c2464cc
5
5
  SHA512:
6
- metadata.gz: d1015d5ca772fd35e20c83837ca54d4751ff3b09f0c1978a0d5e55bbb7ba392107e461bb7656ae7ac8158babb55289579afe15d9772f07024229ce6f77b1cb62
7
- data.tar.gz: 4194a9575055264e59b6f1922bfb7fa225c64242e9de8c406ca43a4c1995397b711204471994651d9b94da6b4ae0a02958f9d48833436af7e5f4dddb083990c9
6
+ metadata.gz: d1331d96e131f19e34d72919edce2b3d5e868e2a37776fa3230cd4a825c60f966b4aeae881e5624b87d2ddbd57971fa785e890e6f67f64bdf32e9e740892aa54
7
+ data.tar.gz: 7d3c20115f90d5ca2ede013ab230b3ead7a19352a582f68157edf288b9c9302c46620ee432b69e188ad2c65f0424b53c8c8b1085502b16aa811ec7f823fa2add
data/CHANGELOG.md CHANGED
@@ -5,10 +5,23 @@ 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.2.2] - 2026-04-02
8
+ ## [0.2.4] - 2026-04-02
9
+
10
+ ### Added
11
+ - **Global service exposure** — `SearchService` and `GitService` are now attached to the `window` object so host-app scripts can call them directly.
12
+ - **Loading indicator** — editor now shows a loading state while assets initialise.
13
+
14
+ ### Changed
15
+ - **Service worker** — simplified to a minimal implementation; removed the caching layer that was causing stale-asset issues.
16
+ - **Monaco Handlebars language** — updated language definition for improved syntax coverage and structure.
17
+
18
+ ### Fixed
19
+ - Test suite clean-up following recent service and SW changes.
20
+
21
+ ## [0.2.3] - 2026-04-02
9
22
 
10
23
  ### Changed
11
- - Bumped the gem version for the next tagged release.
24
+ - Released the current mainline commit as the latest tagged version.
12
25
 
13
26
  ## [0.2.1] - 2026-04-01
14
27
 
@@ -1,3 +1,4 @@
1
+ //= require mbeditor/application_iife_head
1
2
  //= require mbeditor/editor_store
2
3
  //= require mbeditor/file_icon
3
4
  //= require mbeditor/file_service
@@ -19,3 +20,4 @@
19
20
  //= require mbeditor/components/QuickOpenDialog
20
21
  //= require mbeditor/components/TabBar
21
22
  //= require mbeditor/components/MbeditorApp
23
+ //= require mbeditor/application_iife_tail
@@ -0,0 +1,4 @@
1
+ // Mbeditor app IIFE — React and ReactDOM are injected from window.MbeditorRuntime
2
+ // so that mbeditor never reads from or writes to window.React / window.ReactDOM,
3
+ // protecting the host application's globals.
4
+ (function(React, ReactDOM) {
@@ -0,0 +1,3 @@
1
+ window.SearchService = SearchService;
2
+ window.GitService = GitService;
3
+ })(window.MbeditorRuntime.React, window.MbeditorRuntime.ReactDOM);
@@ -475,14 +475,14 @@ var EditorPanel = function EditorPanel(_ref) {
475
475
  formatOnType: true
476
476
  });
477
477
 
478
- monacoRef.current = editor;
479
- window.__mbeditorActiveEditor = editor;
480
- setEditorReady(true);
481
-
482
478
  if (tab.viewState) {
483
479
  editor.restoreViewState(tab.viewState);
484
480
  }
485
481
 
482
+ monacoRef.current = editor;
483
+ window.__mbeditorActiveEditor = editor;
484
+ setEditorReady(true);
485
+
486
486
  // Stash the workspace-relative path on the model so code-action providers
487
487
  // can identify which file they are operating on without needing React state.
488
488
  var modelObj = editor.getModel();
@@ -503,11 +503,14 @@ var EditorPanel = function EditorPanel(_ref) {
503
503
  editorPluginDisposable = window.MbeditorEditorPlugins.attachEditorFeatures(editor, language);
504
504
  }
505
505
 
506
- // Column selection only when Alt is held during drag.
507
- // We toggle Monaco's columnSelection option on Alt+mousedown and reset on mouseup.
506
+ // Column selection only when Ctrl/Cmd is held during drag.
507
+ // We toggle Monaco's columnSelection option on Ctrl/Cmd+mousedown and reset on mouseup.
508
+ // Alt+mousedown without Ctrl/Cmd is suppressed (preventDefault stops Monaco's built-in Alt+drag).
508
509
  var onColumnMouseDown = function(ev) {
509
- if (ev.altKey && !ev.ctrlKey && !ev.metaKey) {
510
+ if (ev.ctrlKey || ev.metaKey) {
510
511
  editor.updateOptions({ columnSelection: true });
512
+ } else if (ev.altKey) {
513
+ ev.preventDefault();
511
514
  }
512
515
  };
513
516
  var onColumnMouseUp = function() {
@@ -903,8 +906,7 @@ var EditorPanel = function EditorPanel(_ref) {
903
906
  // Map a test method name to the best-matching line in the source file.
904
907
  // Extracts keywords from the test name and scores each source line.
905
908
  var mapTestToSourceLine = function(testName, sourceContent) {
906
- // Strip Minitest-style "ClassName#" prefix before the usual transformations
907
- var name = (testName || '').replace(/^\w+#/, '').replace(/^test_/, '').replace(/^(it |should )/, '');
909
+ var name = (testName || '').replace(/^test_/, '').replace(/^(it |should )/, '');
908
910
  var tokens = name.split('_').filter(function(t) { return t.length > 1; });
909
911
  if (tokens.length === 0) return 1;
910
912
 
@@ -385,7 +385,8 @@
385
385
  indentationRules: {
386
386
  increaseIndentPattern: /^\s*(def|class|module|if|unless|case|while|until|for|begin|elsif|else|rescue|ensure|when)\b/,
387
387
  decreaseIndentPattern: /^\s*(end|elsif|else|rescue|ensure|when)\b/
388
- }
388
+ },
389
+ wordPattern: /[a-zA-Z_]\w*[!?]?/
389
390
  });
390
391
 
391
392
  // Override Monaco's built-in Ruby tokenizer with a comprehensive Monarch grammar
@@ -695,7 +696,7 @@
695
696
  // Calls the backend /definition endpoint (Ripper-based) and renders
696
697
  // the method signature and any preceding # comments as hover markdown.
697
698
  monaco.languages.registerHoverProvider('ruby', {
698
- provideHover: function provideHover(model, position) {
699
+ provideHover: function provideHover(model, position, token) {
699
700
  var wordInfo = model.getWordAtPosition(position);
700
701
  if (!wordInfo) return null;
701
702
 
@@ -707,6 +708,11 @@
707
708
  var currentFile = model._mbeditorPath || null;
708
709
 
709
710
  return FileService.getDefinition(word, 'ruby').then(function(data) {
711
+ // If the hover was cancelled while the request was in flight (e.g. the
712
+ // user moved the mouse away), return null so Monaco's CancelablePromise
713
+ // wrapper resolves cleanly instead of throwing "Canceled".
714
+ if (token && token.isCancellationRequested) return null;
715
+
710
716
  var results = data && Array.isArray(data.results) ? data.results : [];
711
717
  // Filter out results that are in the file currently being edited —
712
718
  // showing a hover for a method you can already see adds no value.
@@ -845,4 +845,29 @@
845
845
  .cdiff-hunk { background: rgba(86,156,214,0.08); color: #569cd6; font-style: italic; }
846
846
  .cdiff-meta { color: #666; font-size: 11px; }
847
847
  .cdiff-file-header { color: #9db0c7; }
848
+
849
+ /* ── Loading indicator ───────────────────────────────────────────────── */
850
+ #mbeditor-loading {
851
+ display: flex;
852
+ flex-direction: column;
853
+ align-items: center;
854
+ justify-content: center;
855
+ height: 100vh;
856
+ background: #1e1e1e;
857
+ color: #858585;
858
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
859
+ font-size: 13px;
860
+ gap: 14px;
861
+ }
862
+ .mbeditor-spinner {
863
+ width: 28px;
864
+ height: 28px;
865
+ border: 2px solid rgba(255,255,255,0.08);
866
+ border-top-color: #569cd6;
867
+ border-radius: 50%;
868
+ animation: mbeditor-spin 0.75s linear infinite;
869
+ }
870
+ @keyframes mbeditor-spin {
871
+ to { transform: rotate(360deg); }
872
+ }
848
873
  .cdiff-ctx { color: #9d9d9d; }
@@ -207,7 +207,7 @@ module Mbeditor
207
207
  language = params[:language].to_s.strip
208
208
 
209
209
  return render json: { results: [] } if symbol.blank?
210
- return render json: { error: "Invalid symbol" }, status: :bad_request unless symbol.match?(/\A[a-zA-Z0-9_]{1,60}\z/)
210
+ return render json: { error: "Invalid symbol" }, status: :bad_request unless symbol.match?(/\A[a-zA-Z_]\w{0,59}[!?]?\z/)
211
211
 
212
212
  results = case language
213
213
  when "ruby"
@@ -17,40 +17,40 @@
17
17
  <title>Mbeditor — <%= Rails.root.basename %></title>
18
18
 
19
19
  <!-- ── Fonts and Styles ──────────────────────────────── -->
20
- <%= stylesheet_link_tag "fontawesome.min", media: "all" %>
21
- <%= stylesheet_link_tag "mbeditor/application", media: "all" %>
20
+ <%= stylesheet_link_tag "fontawesome.min", media: "all", preload_links_header: false %>
21
+ <%= stylesheet_link_tag "mbeditor/application", media: "all", preload_links_header: false %>
22
22
 
23
- <!-- ── Vendor JS (order matters) ─────────────────────── -->
24
- <script src="<%= asset_path('react.min.js') %>"></script>
25
- <script src="<%= asset_path('react-dom.min.js') %>"></script>
26
- <script src="<%= asset_path('axios.min.js') %>"></script>
27
- <script src="<%= asset_path('lodash.min.js') %>"></script>
28
- <script src="<%= asset_path('minisearch.min.js') %>"></script>
29
- <script src="<%= asset_path('marked.min.js') %>"></script>
30
- <!-- ── Monaco loader ─────────────────────────────────── -->
23
+ <!-- ── Vendor JS (deferred — only needed inside Monaco callback) ── -->
24
+ <script defer src="<%= asset_path('react.min.js') %>"></script>
25
+ <script defer src="<%= asset_path('react-dom.min.js') %>"></script>
26
+ <script defer src="<%= asset_path('axios.min.js') %>"></script>
27
+ <script defer src="<%= asset_path('lodash.min.js') %>"></script>
28
+ <script defer src="<%= asset_path('minisearch.min.js') %>"></script>
29
+ <script defer src="<%= asset_path('marked.min.js') %>"></script>
30
+ <!-- ── Monaco loader (sync — inline body script calls require()) ── -->
31
31
  <% base_path = request.script_name.to_s.sub(%r{/$}, '') %>
32
- <script>var require = { paths: { vs: '<%= json_escape("#{base_path}/monaco-editor/vs") %>', 'monaco-vim': '<%= json_escape(asset_path("monaco-vim.js").sub(/\.js$/, "")) %>', 'monaco-editor/esm/vs': '<%= json_escape("#{base_path}/monaco-editor/vs") %>' } };</script>
32
+ <script>var require = { paths: { vs: '<%= json_escape("#{base_path}/monaco-editor/vs") %>', 'monaco-editor/esm/vs': '<%= json_escape("#{base_path}/monaco-editor/vs") %>', 'monaco-vim': '<%= json_escape(asset_path("monaco-vim.js").sub(/\.js$/, "")) %>' } };</script>
33
33
  <script src="<%= "#{base_path}/monaco-editor/vs/loader.js" %>"></script>
34
- <!-- ── Emmet + Extra themes (non-AMD, load before app) ── -->
35
- <script src="<%= asset_path('emmet.js') %>"></script>
36
- <script src="<%= asset_path('monaco-themes-bundle.js') %>"></script>
34
+ <!-- ── Emmet + Extra themes (non-AMD, deferred used inside Monaco callback) ── -->
35
+ <script defer src="<%= asset_path('emmet.js') %>"></script>
36
+ <script defer src="<%= asset_path('monaco-themes-bundle.js') %>"></script>
37
37
  </head>
38
38
  <body data-rails-root="<%= Rails.root.to_s %>" data-mbeditor-version="<%= Mbeditor::VERSION %>">
39
39
  <script>
40
40
  window.MBEDITOR_BASE_PATH = "<%= json_escape(base_path) %>";
41
-
42
- // Monaco's restoreViewState cancels its own internal rendering promises and
43
- // those cancellations surface as "Canceled: Canceled" unhandled rejections.
44
- // This is a known Monaco quirk the cancellation is intentional and benign.
45
- // Suppress it here so it doesn't pollute the console.
46
- window.addEventListener('unhandledrejection', function(event) {
47
- if (event.reason && event.reason.name === 'Canceled' && event.reason.message === 'Canceled') {
48
- event.preventDefault();
49
- }
50
- });
41
+ // Snapshot any React the host app may already have before our deferred scripts run.
42
+ window._mbeditorHostReact = window.React;
43
+ window._mbeditorHostReactDOM = window.ReactDOM;
44
+ // Set by DOMContentLoaded, which fires after all deferred scripts have executed.
45
+ window._mbeditorDOMReady = false;
46
+ document.addEventListener('DOMContentLoaded', function() { window._mbeditorDOMReady = true; }, { once: true });
51
47
  </script>
52
48
 
53
49
  <div id="mbeditor-root">
50
+ <div id="mbeditor-loading">
51
+ <div class="mbeditor-spinner"></div>
52
+ <div class="mbeditor-loading-text">Loading editor&hellip;</div>
53
+ </div>
54
54
  <%= yield %>
55
55
  </div>
56
56
 
@@ -65,7 +65,7 @@
65
65
  }
66
66
  };
67
67
 
68
- // Load Prettier scripts sequentially with define temporarily hidden so their
68
+ // Load Prettier scripts in parallel with define temporarily hidden so their
69
69
  // UMD wrapper sets window.prettier / window.prettierPlugins instead of
70
70
  // registering as AMD modules via Monaco's loader. Then load Monaco and the
71
71
  // app once all Prettier scripts are ready.
@@ -79,40 +79,63 @@
79
79
  '<%= asset_path("prettier-plugin-markdown.js") %>'
80
80
  ];
81
81
 
82
- function loadSequential(srcs, done) {
83
- if (!srcs.length) return done();
84
- var s = document.createElement('script');
85
- s.src = srcs[0];
86
- s.onload = function() { loadSequential(srcs.slice(1), done); };
87
- document.head.appendChild(s);
88
- }
89
-
90
82
  var _define = window.define;
91
83
  window.define = undefined;
84
+ var pending = prettierScripts.length;
85
+
86
+ function onAllPrettierLoaded() {
87
+ // Deferred vendor scripts (React, ReactDOM, etc.) run before DOMContentLoaded.
88
+ // Restoring window.define before they execute would cause them to register
89
+ // as AMD modules instead of setting window globals, so wait for DOMContentLoaded first.
90
+ function proceed() {
91
+ // Store mbeditor's React/ReactDOM under a private namespace so the
92
+ // app bundle can use them without touching window.React / window.ReactDOM.
93
+ window.MbeditorRuntime = {
94
+ React: window.React,
95
+ ReactDOM: window.ReactDOM
96
+ };
97
+ // Restore whatever the host app had before our deferred scripts ran.
98
+ window.React = window._mbeditorHostReact;
99
+ window.ReactDOM = window._mbeditorHostReactDOM;
92
100
 
93
- loadSequential(prettierScripts, function() {
94
- window.define = _define;
101
+ window.define = _define;
95
102
 
96
- // Wait for Monaco to load before initializing application scripts
97
- require(['vs/editor/editor.main'], function() {
98
- // Register custom themes from the vendored bundle
99
- if (window.MBEDITOR_CUSTOM_THEMES && window.monaco) {
100
- Object.keys(window.MBEDITOR_CUSTOM_THEMES).forEach(function(id) {
101
- window.monaco.editor.defineTheme(id, window.MBEDITOR_CUSTOM_THEMES[id]);
102
- });
103
- }
104
- var appScript = document.createElement('script');
105
- appScript.src = '<%= asset_path("mbeditor/application.js") %>';
106
- appScript.onload = function() {
107
- var root = document.getElementById('mbeditor-root');
108
- if (window.MbeditorApp && window.ReactDOM) {
109
- window.ReactDOM.render(React.createElement(window.MbeditorApp), root);
110
- } else {
111
- console.error("Failed to mount: MbeditorApp or ReactDOM is undefined.");
103
+ // Wait for Monaco to load before initializing application scripts
104
+ require(['vs/editor/editor.main'], function() {
105
+ // Register custom themes from the vendored bundle
106
+ if (window.MBEDITOR_CUSTOM_THEMES && window.monaco) {
107
+ Object.keys(window.MBEDITOR_CUSTOM_THEMES).forEach(function(id) {
108
+ window.monaco.editor.defineTheme(id, window.MBEDITOR_CUSTOM_THEMES[id]);
109
+ });
112
110
  }
113
- };
114
- document.body.appendChild(appScript);
115
- });
111
+ var appScript = document.createElement('script');
112
+ appScript.src = '<%= asset_path("mbeditor/application.js") %>';
113
+ appScript.onload = function() {
114
+ var root = document.getElementById('mbeditor-root');
115
+ var _R = window.MbeditorRuntime.React;
116
+ var _RD = window.MbeditorRuntime.ReactDOM;
117
+ if (window.MbeditorApp && _R && _RD) {
118
+ _RD.render(_R.createElement(window.MbeditorApp), root);
119
+ } else {
120
+ console.error("Failed to mount: MbeditorApp or MbeditorRuntime is undefined.");
121
+ }
122
+ };
123
+ document.body.appendChild(appScript);
124
+ });
125
+ }
126
+
127
+ if (window._mbeditorDOMReady) {
128
+ proceed();
129
+ } else {
130
+ document.addEventListener('DOMContentLoaded', proceed, { once: true });
131
+ }
132
+ }
133
+
134
+ prettierScripts.forEach(function(src) {
135
+ var s = document.createElement('script');
136
+ s.src = src;
137
+ s.onload = function() { if (--pending === 0) onAllPrettierLoaded(); };
138
+ document.head.appendChild(s);
116
139
  });
117
140
  })();
118
141
  </script>
@@ -1,3 +1,3 @@
1
1
  module Mbeditor
2
- VERSION = "0.2.2"
2
+ VERSION = "0.2.4"
3
3
  end
@@ -4,437 +4,209 @@
4
4
  * Released under the MIT license
5
5
  * https://github.com/microsoft/monaco-editor/blob/main/LICENSE.txt
6
6
  *-----------------------------------------------------------------------------*/
7
+ define("vs/basic-languages/handlebars/handlebars", ["require", "require"], (require) => {
8
+ "use strict";
9
+ var moduleExports = (() => {
10
+ var monaco = require("vs/editor/editor.api");
7
11
 
8
- var __defProp = Object.defineProperty;
9
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
10
- var __getOwnPropNames = Object.getOwnPropertyNames;
11
- var __hasOwnProp = Object.prototype.hasOwnProperty;
12
- var __copyProps = (to, from, except, desc) => {
13
- if (from && typeof from === "object" || typeof from === "function") {
14
- for (let key of __getOwnPropNames(from))
15
- if (!__hasOwnProp.call(to, key) && key !== except)
16
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
- }
18
- return to;
19
- };
20
- var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
12
+ var EMPTY_ELEMENTS = ["area", "base", "br", "col", "embed", "hr", "img", "input", "keygen", "link", "menuitem", "meta", "param", "source", "track", "wbr"];
21
13
 
22
- // src/fillers/monaco-editor-core.ts
23
- var monaco_editor_core_exports = {};
24
- __reExport(monaco_editor_core_exports, monaco_editor_core_star);
25
- import * as monaco_editor_core_star from "../../editor/editor.api.js";
26
-
27
- // src/basic-languages/handlebars/handlebars.ts
28
- var EMPTY_ELEMENTS = [
29
- "area",
30
- "base",
31
- "br",
32
- "col",
33
- "embed",
34
- "hr",
35
- "img",
36
- "input",
37
- "keygen",
38
- "link",
39
- "menuitem",
40
- "meta",
41
- "param",
42
- "source",
43
- "track",
44
- "wbr"
45
- ];
46
- var conf = {
47
- wordPattern: /(-?\d*\.\d\w*)|([^\`\~\!\@\$\^\&\*\(\)\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\s]+)/g,
48
- comments: {
49
- blockComment: ["{{!--", "--}}"]
50
- },
51
- brackets: [
52
- ["<!--", "-->"],
53
- ["<", ">"],
54
- ["{{", "}}"],
55
- ["{", "}"],
56
- ["(", ")"]
57
- ],
58
- autoClosingPairs: [
59
- { open: "{", close: "}" },
60
- { open: "[", close: "]" },
61
- { open: "(", close: ")" },
62
- { open: '"', close: '"' },
63
- { open: "'", close: "'" }
64
- ],
65
- surroundingPairs: [
66
- { open: "<", close: ">" },
67
- { open: '"', close: '"' },
68
- { open: "'", close: "'" }
69
- ],
70
- onEnterRules: [
71
- {
72
- beforeText: new RegExp(
73
- `<(?!(?:${EMPTY_ELEMENTS.join("|")}))(\\w[\\w\\d]*)([^/>]*(?!/)>)[^<]*$`,
74
- "i"
75
- ),
76
- afterText: /^<\/(\w[\w\d]*)\s*>$/i,
77
- action: {
78
- indentAction: monaco_editor_core_exports.languages.IndentAction.IndentOutdent
79
- }
14
+ var conf = {
15
+ wordPattern: /(-?\d*\.\d\w*)|([^\`\~\!\@\$\^\&\*\(\)\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\s]+)/g,
16
+ comments: {
17
+ blockComment: ["{{!--", "--}}"]
80
18
  },
81
- {
82
- beforeText: new RegExp(
83
- `<(?!(?:${EMPTY_ELEMENTS.join("|")}))(\\w[\\w\\d]*)([^/>]*(?!/)>)[^<]*$`,
84
- "i"
85
- ),
86
- action: { indentAction: monaco_editor_core_exports.languages.IndentAction.Indent }
87
- }
88
- ]
89
- };
90
- var language = {
91
- defaultToken: "",
92
- tokenPostfix: "",
93
- // ignoreCase: true,
94
- // The main tokenizer for our languages
95
- tokenizer: {
96
- root: [
97
- [/\{\{!--/, "comment.block.start.handlebars", "@commentBlock"],
98
- [/\{\{!/, "comment.start.handlebars", "@comment"],
99
- [/\{\{/, { token: "@rematch", switchTo: "@handlebarsInSimpleState.root" }],
100
- [/<!DOCTYPE/, "metatag.html", "@doctype"],
101
- [/<!--/, "comment.html", "@commentHtml"],
102
- [/(<)(\w+)(\/>)/, ["delimiter.html", "tag.html", "delimiter.html"]],
103
- [/(<)(script)/, ["delimiter.html", { token: "tag.html", next: "@script" }]],
104
- [/(<)(style)/, ["delimiter.html", { token: "tag.html", next: "@style" }]],
105
- [/(<)([:\w]+)/, ["delimiter.html", { token: "tag.html", next: "@otherTag" }]],
106
- [/(<\/)(\w+)/, ["delimiter.html", { token: "tag.html", next: "@otherTag" }]],
107
- [/</, "delimiter.html"],
108
- [/\{/, "delimiter.html"],
109
- [/[^<{]+/]
110
- // text
111
- ],
112
- doctype: [
113
- [
114
- /\{\{/,
115
- {
116
- token: "@rematch",
117
- switchTo: "@handlebarsInSimpleState.comment"
118
- }
119
- ],
120
- [/[^>]+/, "metatag.content.html"],
121
- [/>/, "metatag.html", "@pop"]
122
- ],
123
- comment: [
124
- [/\}\}/, "comment.end.handlebars", "@pop"],
125
- [/./, "comment.content.handlebars"]
126
- ],
127
- commentBlock: [
128
- [/--\}\}/, "comment.block.end.handlebars", "@pop"],
129
- [/./, "comment.content.handlebars"]
130
- ],
131
- commentHtml: [
132
- [
133
- /\{\{/,
134
- {
135
- token: "@rematch",
136
- switchTo: "@handlebarsInSimpleState.comment"
137
- }
138
- ],
139
- [/-->/, "comment.html", "@pop"],
140
- [/[^-]+/, "comment.content.html"],
141
- [/./, "comment.content.html"]
142
- ],
143
- otherTag: [
144
- [
145
- /\{\{/,
146
- {
147
- token: "@rematch",
148
- switchTo: "@handlebarsInSimpleState.otherTag"
149
- }
150
- ],
151
- [/\/?>/, "delimiter.html", "@pop"],
152
- [/"([^"]*)"/, "attribute.value"],
153
- [/'([^']*)'/, "attribute.value"],
154
- [/[\w\-]+/, "attribute.name"],
155
- [/=/, "delimiter"],
156
- [/[ \t\r\n]+/]
157
- // whitespace
158
- ],
159
- // -- BEGIN <script> tags handling
160
- // After <script
161
- script: [
162
- [
163
- /\{\{/,
164
- {
165
- token: "@rematch",
166
- switchTo: "@handlebarsInSimpleState.script"
167
- }
168
- ],
169
- [/type/, "attribute.name", "@scriptAfterType"],
170
- [/"([^"]*)"/, "attribute.value"],
171
- [/'([^']*)'/, "attribute.value"],
172
- [/[\w\-]+/, "attribute.name"],
173
- [/=/, "delimiter"],
174
- [
175
- />/,
176
- {
177
- token: "delimiter.html",
178
- next: "@scriptEmbedded.text/javascript",
179
- nextEmbedded: "text/javascript"
180
- }
181
- ],
182
- [/[ \t\r\n]+/],
183
- // whitespace
184
- [
185
- /(<\/)(script\s*)(>)/,
186
- ["delimiter.html", "tag.html", { token: "delimiter.html", next: "@pop" }]
187
- ]
188
- ],
189
- // After <script ... type
190
- scriptAfterType: [
191
- [
192
- /\{\{/,
193
- {
194
- token: "@rematch",
195
- switchTo: "@handlebarsInSimpleState.scriptAfterType"
196
- }
197
- ],
198
- [/=/, "delimiter", "@scriptAfterTypeEquals"],
199
- [
200
- />/,
201
- {
202
- token: "delimiter.html",
203
- next: "@scriptEmbedded.text/javascript",
204
- nextEmbedded: "text/javascript"
205
- }
206
- ],
207
- // cover invalid e.g. <script type>
208
- [/[ \t\r\n]+/],
209
- // whitespace
210
- [/<\/script\s*>/, { token: "@rematch", next: "@pop" }]
211
- ],
212
- // After <script ... type =
213
- scriptAfterTypeEquals: [
214
- [
215
- /\{\{/,
216
- {
217
- token: "@rematch",
218
- switchTo: "@handlebarsInSimpleState.scriptAfterTypeEquals"
219
- }
220
- ],
221
- [
222
- /"([^"]*)"/,
223
- {
224
- token: "attribute.value",
225
- switchTo: "@scriptWithCustomType.$1"
226
- }
227
- ],
228
- [
229
- /'([^']*)'/,
230
- {
231
- token: "attribute.value",
232
- switchTo: "@scriptWithCustomType.$1"
233
- }
234
- ],
235
- [
236
- />/,
237
- {
238
- token: "delimiter.html",
239
- next: "@scriptEmbedded.text/javascript",
240
- nextEmbedded: "text/javascript"
241
- }
242
- ],
243
- // cover invalid e.g. <script type=>
244
- [/[ \t\r\n]+/],
245
- // whitespace
246
- [/<\/script\s*>/, { token: "@rematch", next: "@pop" }]
247
- ],
248
- // After <script ... type = $S2
249
- scriptWithCustomType: [
250
- [
251
- /\{\{/,
252
- {
253
- token: "@rematch",
254
- switchTo: "@handlebarsInSimpleState.scriptWithCustomType.$S2"
255
- }
256
- ],
257
- [
258
- />/,
259
- {
260
- token: "delimiter.html",
261
- next: "@scriptEmbedded.$S2",
262
- nextEmbedded: "$S2"
263
- }
264
- ],
265
- [/"([^"]*)"/, "attribute.value"],
266
- [/'([^']*)'/, "attribute.value"],
267
- [/[\w\-]+/, "attribute.name"],
268
- [/=/, "delimiter"],
269
- [/[ \t\r\n]+/],
270
- // whitespace
271
- [/<\/script\s*>/, { token: "@rematch", next: "@pop" }]
272
- ],
273
- scriptEmbedded: [
274
- [
275
- /\{\{/,
276
- {
277
- token: "@rematch",
278
- switchTo: "@handlebarsInEmbeddedState.scriptEmbedded.$S2",
279
- nextEmbedded: "@pop"
280
- }
281
- ],
282
- [/<\/script/, { token: "@rematch", next: "@pop", nextEmbedded: "@pop" }]
283
- ],
284
- // -- END <script> tags handling
285
- // -- BEGIN <style> tags handling
286
- // After <style
287
- style: [
288
- [
289
- /\{\{/,
290
- {
291
- token: "@rematch",
292
- switchTo: "@handlebarsInSimpleState.style"
293
- }
294
- ],
295
- [/type/, "attribute.name", "@styleAfterType"],
296
- [/"([^"]*)"/, "attribute.value"],
297
- [/'([^']*)'/, "attribute.value"],
298
- [/[\w\-]+/, "attribute.name"],
299
- [/=/, "delimiter"],
300
- [
301
- />/,
302
- {
303
- token: "delimiter.html",
304
- next: "@styleEmbedded.text/css",
305
- nextEmbedded: "text/css"
306
- }
307
- ],
308
- [/[ \t\r\n]+/],
309
- // whitespace
310
- [
311
- /(<\/)(style\s*)(>)/,
312
- ["delimiter.html", "tag.html", { token: "delimiter.html", next: "@pop" }]
313
- ]
314
- ],
315
- // After <style ... type
316
- styleAfterType: [
317
- [
318
- /\{\{/,
319
- {
320
- token: "@rematch",
321
- switchTo: "@handlebarsInSimpleState.styleAfterType"
322
- }
323
- ],
324
- [/=/, "delimiter", "@styleAfterTypeEquals"],
325
- [
326
- />/,
327
- {
328
- token: "delimiter.html",
329
- next: "@styleEmbedded.text/css",
330
- nextEmbedded: "text/css"
331
- }
332
- ],
333
- // cover invalid e.g. <style type>
334
- [/[ \t\r\n]+/],
335
- // whitespace
336
- [/<\/style\s*>/, { token: "@rematch", next: "@pop" }]
337
- ],
338
- // After <style ... type =
339
- styleAfterTypeEquals: [
340
- [
341
- /\{\{/,
342
- {
343
- token: "@rematch",
344
- switchTo: "@handlebarsInSimpleState.styleAfterTypeEquals"
345
- }
346
- ],
347
- [
348
- /"([^"]*)"/,
349
- {
350
- token: "attribute.value",
351
- switchTo: "@styleWithCustomType.$1"
352
- }
353
- ],
354
- [
355
- /'([^']*)'/,
356
- {
357
- token: "attribute.value",
358
- switchTo: "@styleWithCustomType.$1"
359
- }
360
- ],
361
- [
362
- />/,
363
- {
364
- token: "delimiter.html",
365
- next: "@styleEmbedded.text/css",
366
- nextEmbedded: "text/css"
367
- }
368
- ],
369
- // cover invalid e.g. <style type=>
370
- [/[ \t\r\n]+/],
371
- // whitespace
372
- [/<\/style\s*>/, { token: "@rematch", next: "@pop" }]
373
- ],
374
- // After <style ... type = $S2
375
- styleWithCustomType: [
376
- [
377
- /\{\{/,
378
- {
379
- token: "@rematch",
380
- switchTo: "@handlebarsInSimpleState.styleWithCustomType.$S2"
381
- }
382
- ],
383
- [
384
- />/,
385
- {
386
- token: "delimiter.html",
387
- next: "@styleEmbedded.$S2",
388
- nextEmbedded: "$S2"
389
- }
390
- ],
391
- [/"([^"]*)"/, "attribute.value"],
392
- [/'([^']*)'/, "attribute.value"],
393
- [/[\w\-]+/, "attribute.name"],
394
- [/=/, "delimiter"],
395
- [/[ \t\r\n]+/],
396
- // whitespace
397
- [/<\/style\s*>/, { token: "@rematch", next: "@pop" }]
398
- ],
399
- styleEmbedded: [
400
- [
401
- /\{\{/,
402
- {
403
- token: "@rematch",
404
- switchTo: "@handlebarsInEmbeddedState.styleEmbedded.$S2",
405
- nextEmbedded: "@pop"
406
- }
407
- ],
408
- [/<\/style/, { token: "@rematch", next: "@pop", nextEmbedded: "@pop" }]
409
- ],
410
- // -- END <style> tags handling
411
- handlebarsInSimpleState: [
412
- [/\{\{\{?/, "delimiter.handlebars"],
413
- [/\}\}\}?/, { token: "delimiter.handlebars", switchTo: "@$S2.$S3" }],
414
- { include: "handlebarsRoot" }
415
- ],
416
- handlebarsInEmbeddedState: [
417
- [/\{\{\{?/, "delimiter.handlebars"],
418
- [
419
- /\}\}\}?/,
420
- {
421
- token: "delimiter.handlebars",
422
- switchTo: "@$S2.$S3",
423
- nextEmbedded: "$S3"
424
- }
425
- ],
426
- { include: "handlebarsRoot" }
427
- ],
428
- handlebarsRoot: [
429
- [/"[^"]*"/, "string.handlebars"],
430
- [/[#/][^\s}]+/, "keyword.helper.handlebars"],
431
- [/else\b/, "keyword.helper.handlebars"],
432
- [/[\s]+/],
433
- [/[^}]/, "variable.parameter.handlebars"]
19
+ brackets: [
20
+ ["<!--", "-->"],
21
+ ["<", ">"],
22
+ ["{{", "}}"],
23
+ ["{", "}"],
24
+ ["(", ")"]
25
+ ],
26
+ autoClosingPairs: [
27
+ { open: "{", close: "}" },
28
+ { open: "[", close: "]" },
29
+ { open: "(", close: ")" },
30
+ { open: '"', close: '"' },
31
+ { open: "'", close: "'" }
32
+ ],
33
+ surroundingPairs: [
34
+ { open: "<", close: ">" },
35
+ { open: '"', close: '"' },
36
+ { open: "'", close: "'" }
37
+ ],
38
+ onEnterRules: [
39
+ {
40
+ beforeText: new RegExp(
41
+ "<(?!(?:" + EMPTY_ELEMENTS.join("|") + "))(\\w[\\w\\d]*)([^/>]*(?!/)>)[^<]*$",
42
+ "i"
43
+ ),
44
+ afterText: /^<\/(\w[\w\d]*)\s*>$/i,
45
+ action: {
46
+ indentAction: monaco.languages.IndentAction.IndentOutdent
47
+ }
48
+ },
49
+ {
50
+ beforeText: new RegExp(
51
+ "<(?!(?:" + EMPTY_ELEMENTS.join("|") + "))(\\w[\\w\\d]*)([^/>]*(?!/)>)[^<]*$",
52
+ "i"
53
+ ),
54
+ action: { indentAction: monaco.languages.IndentAction.Indent }
55
+ }
434
56
  ]
435
- }
436
- };
437
- export {
438
- conf,
439
- language
440
- };
57
+ };
58
+
59
+ var language = {
60
+ defaultToken: "",
61
+ tokenPostfix: "",
62
+ tokenizer: {
63
+ root: [
64
+ [/\{\{!--/, "comment.block.start.handlebars", "@commentBlock"],
65
+ [/\{\{!/, "comment.start.handlebars", "@comment"],
66
+ [/\{\{/, { token: "@rematch", switchTo: "@handlebarsInSimpleState.root" }],
67
+ [/<!DOCTYPE/, "metatag.html", "@doctype"],
68
+ [/<!--/, "comment.html", "@commentHtml"],
69
+ [/(<)(\w+)(\/>)/, ["delimiter.html", "tag.html", "delimiter.html"]],
70
+ [/(<)(script)/, ["delimiter.html", { token: "tag.html", next: "@script" }]],
71
+ [/(<)(style)/, ["delimiter.html", { token: "tag.html", next: "@style" }]],
72
+ [/(<)([:\w]+)/, ["delimiter.html", { token: "tag.html", next: "@otherTag" }]],
73
+ [/(<\/)(\w+)/, ["delimiter.html", { token: "tag.html", next: "@otherTag" }]],
74
+ [/</, "delimiter.html"],
75
+ [/\{/, "delimiter.html"],
76
+ [/[^<{]+/]
77
+ ],
78
+ doctype: [
79
+ [/\{\{/, { token: "@rematch", switchTo: "@handlebarsInSimpleState.comment" }],
80
+ [/[^>]+/, "metatag.content.html"],
81
+ [/>/, "metatag.html", "@pop"]
82
+ ],
83
+ comment: [
84
+ [/\}\}/, "comment.end.handlebars", "@pop"],
85
+ [/./, "comment.content.handlebars"]
86
+ ],
87
+ commentBlock: [
88
+ [/--\}\}/, "comment.block.end.handlebars", "@pop"],
89
+ [/./, "comment.content.handlebars"]
90
+ ],
91
+ commentHtml: [
92
+ [/\{\{/, { token: "@rematch", switchTo: "@handlebarsInSimpleState.comment" }],
93
+ [/-->/, "comment.html", "@pop"],
94
+ [/[^-]+/, "comment.content.html"],
95
+ [/./, "comment.content.html"]
96
+ ],
97
+ otherTag: [
98
+ [/\{\{/, { token: "@rematch", switchTo: "@handlebarsInSimpleState.otherTag" }],
99
+ [/\/?>/, "delimiter.html", "@pop"],
100
+ [/"([^"]*)"/, "attribute.value"],
101
+ [/'([^']*)'/, "attribute.value"],
102
+ [/[\w\-]+/, "attribute.name"],
103
+ [/=/, "delimiter"],
104
+ [/[ \t\r\n]+/]
105
+ ],
106
+ script: [
107
+ [/\{\{/, { token: "@rematch", switchTo: "@handlebarsInSimpleState.script" }],
108
+ [/type/, "attribute.name", "@scriptAfterType"],
109
+ [/"([^"]*)"/, "attribute.value"],
110
+ [/'([^']*)'/, "attribute.value"],
111
+ [/[\w\-]+/, "attribute.name"],
112
+ [/=/, "delimiter"],
113
+ [/>/, { token: "delimiter.html", next: "@scriptEmbedded.text/javascript", nextEmbedded: "text/javascript" }],
114
+ [/[ \t\r\n]+/],
115
+ [/(<\/)(script\s*)(>)/, ["delimiter.html", "tag.html", { token: "delimiter.html", next: "@pop" }]]
116
+ ],
117
+ scriptAfterType: [
118
+ [/\{\{/, { token: "@rematch", switchTo: "@handlebarsInSimpleState.scriptAfterType" }],
119
+ [/=/, "delimiter", "@scriptAfterTypeEquals"],
120
+ [/>/, { token: "delimiter.html", next: "@scriptEmbedded.text/javascript", nextEmbedded: "text/javascript" }],
121
+ [/[ \t\r\n]+/],
122
+ [/<\/script\s*>/, { token: "@rematch", next: "@pop" }]
123
+ ],
124
+ scriptAfterTypeEquals: [
125
+ [/\{\{/, { token: "@rematch", switchTo: "@handlebarsInSimpleState.scriptAfterTypeEquals" }],
126
+ [/"([^"]*)"/, { token: "attribute.value", switchTo: "@scriptWithCustomType.$1" }],
127
+ [/'([^']*)'/, { token: "attribute.value", switchTo: "@scriptWithCustomType.$1" }],
128
+ [/>/, { token: "delimiter.html", next: "@scriptEmbedded.text/javascript", nextEmbedded: "text/javascript" }],
129
+ [/[ \t\r\n]+/],
130
+ [/<\/script\s*>/, { token: "@rematch", next: "@pop" }]
131
+ ],
132
+ scriptWithCustomType: [
133
+ [/\{\{/, { token: "@rematch", switchTo: "@handlebarsInSimpleState.scriptWithCustomType.$S2" }],
134
+ [/>/, { token: "delimiter.html", next: "@scriptEmbedded.$S2", nextEmbedded: "$S2" }],
135
+ [/"([^"]*)"/, "attribute.value"],
136
+ [/'([^']*)'/, "attribute.value"],
137
+ [/[\w\-]+/, "attribute.name"],
138
+ [/=/, "delimiter"],
139
+ [/[ \t\r\n]+/],
140
+ [/<\/script\s*>/, { token: "@rematch", next: "@pop" }]
141
+ ],
142
+ scriptEmbedded: [
143
+ [/\{\{/, { token: "@rematch", switchTo: "@handlebarsInEmbeddedState.scriptEmbedded.$S2", nextEmbedded: "@pop" }],
144
+ [/<\/script/, { token: "@rematch", next: "@pop", nextEmbedded: "@pop" }]
145
+ ],
146
+ style: [
147
+ [/\{\{/, { token: "@rematch", switchTo: "@handlebarsInSimpleState.style" }],
148
+ [/type/, "attribute.name", "@styleAfterType"],
149
+ [/"([^"]*)"/, "attribute.value"],
150
+ [/'([^']*)'/, "attribute.value"],
151
+ [/[\w\-]+/, "attribute.name"],
152
+ [/=/, "delimiter"],
153
+ [/>/, { token: "delimiter.html", next: "@styleEmbedded.text/css", nextEmbedded: "text/css" }],
154
+ [/[ \t\r\n]+/],
155
+ [/(<\/)(style\s*)(>)/, ["delimiter.html", "tag.html", { token: "delimiter.html", next: "@pop" }]]
156
+ ],
157
+ styleAfterType: [
158
+ [/\{\{/, { token: "@rematch", switchTo: "@handlebarsInSimpleState.styleAfterType" }],
159
+ [/=/, "delimiter", "@styleAfterTypeEquals"],
160
+ [/>/, { token: "delimiter.html", next: "@styleEmbedded.text/css", nextEmbedded: "text/css" }],
161
+ [/[ \t\r\n]+/],
162
+ [/<\/style\s*>/, { token: "@rematch", next: "@pop" }]
163
+ ],
164
+ styleAfterTypeEquals: [
165
+ [/\{\{/, { token: "@rematch", switchTo: "@handlebarsInSimpleState.styleAfterTypeEquals" }],
166
+ [/"([^"]*)"/, { token: "attribute.value", switchTo: "@styleWithCustomType.$1" }],
167
+ [/'([^']*)'/, { token: "attribute.value", switchTo: "@styleWithCustomType.$1" }],
168
+ [/>/, { token: "delimiter.html", next: "@styleEmbedded.text/css", nextEmbedded: "text/css" }],
169
+ [/[ \t\r\n]+/],
170
+ [/<\/style\s*>/, { token: "@rematch", next: "@pop" }]
171
+ ],
172
+ styleWithCustomType: [
173
+ [/\{\{/, { token: "@rematch", switchTo: "@handlebarsInSimpleState.styleWithCustomType.$S2" }],
174
+ [/>/, { token: "delimiter.html", next: "@styleEmbedded.$S2", nextEmbedded: "$S2" }],
175
+ [/"([^"]*)"/, "attribute.value"],
176
+ [/'([^']*)'/, "attribute.value"],
177
+ [/[\w\-]+/, "attribute.name"],
178
+ [/=/, "delimiter"],
179
+ [/[ \t\r\n]+/],
180
+ [/<\/style\s*>/, { token: "@rematch", next: "@pop" }]
181
+ ],
182
+ styleEmbedded: [
183
+ [/\{\{/, { token: "@rematch", switchTo: "@handlebarsInEmbeddedState.styleEmbedded.$S2", nextEmbedded: "@pop" }],
184
+ [/<\/style/, { token: "@rematch", next: "@pop", nextEmbedded: "@pop" }]
185
+ ],
186
+ handlebarsInSimpleState: [
187
+ [/\{\{\{?/, "delimiter.handlebars"],
188
+ [/\}\}\}?/, { token: "delimiter.handlebars", switchTo: "@$S2.$S3" }],
189
+ { include: "handlebarsRoot" }
190
+ ],
191
+ handlebarsInEmbeddedState: [
192
+ [/\{\{\{?/, "delimiter.handlebars"],
193
+ [/\}\}\}?/, { token: "delimiter.handlebars", switchTo: "@$S2.$S3", nextEmbedded: "$S3" }],
194
+ { include: "handlebarsRoot" }
195
+ ],
196
+ handlebarsRoot: [
197
+ [/"[^"]*"/, "string.handlebars"],
198
+ [/[#/][^\s}]+/, "keyword.helper.handlebars"],
199
+ [/else\b/, "keyword.helper.handlebars"],
200
+ [/[\s]+/],
201
+ [/[^}]/, "variable.parameter.handlebars"]
202
+ ]
203
+ }
204
+ };
205
+
206
+ var result = {};
207
+ result.conf = conf;
208
+ result.language = language;
209
+ return result;
210
+ })();
211
+ return moduleExports;
212
+ });
data/public/sw.js CHANGED
@@ -1,8 +1,10 @@
1
- // Minimal service worker for installability without intercepting requests.
2
- self.addEventListener('install', function() {
3
- self.skipWaiting();
1
+ // Minimal service worker for Mbeditor. Registers the SW scope without
2
+ // intercepting fetch requests — the browser handles all network traffic normally.
3
+
4
+ self.addEventListener('install', function(event) {
5
+ self.skipWaiting();
4
6
  });
5
7
 
6
8
  self.addEventListener('activate', function(event) {
7
- event.waitUntil(self.clients.claim());
9
+ event.waitUntil(self.clients.claim());
8
10
  });
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mbeditor
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.2
4
+ version: 0.2.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Oliver Noonan
@@ -55,6 +55,8 @@ files:
55
55
  - CHANGELOG.md
56
56
  - README.md
57
57
  - app/assets/javascripts/mbeditor/application.js
58
+ - app/assets/javascripts/mbeditor/application_iife_head.js
59
+ - app/assets/javascripts/mbeditor/application_iife_tail.js
58
60
  - app/assets/javascripts/mbeditor/components/CodeReviewPanel.js
59
61
  - app/assets/javascripts/mbeditor/components/CollapsibleSection.js
60
62
  - app/assets/javascripts/mbeditor/components/CombinedDiffViewer.js