mbeditor 0.3.9 → 0.4.3
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 +38 -0
- data/README.md +18 -0
- data/app/assets/javascripts/mbeditor/application.js +1 -0
- data/app/assets/javascripts/mbeditor/components/EditorPanel.js +161 -0
- data/app/assets/javascripts/mbeditor/components/MbeditorApp.js +392 -62
- data/app/assets/javascripts/mbeditor/components/QuickOpenDialog.js +20 -4
- data/app/assets/javascripts/mbeditor/components/TabBar.js +3 -2
- data/app/assets/javascripts/mbeditor/editor_plugins.js +21 -0
- data/app/assets/javascripts/mbeditor/file_service.js +6 -0
- data/app/assets/javascripts/mbeditor/search_service.js +7 -3
- data/app/assets/javascripts/mbeditor/websocket_service.js +195 -0
- data/app/assets/stylesheets/mbeditor/application.css +19 -0
- data/app/assets/stylesheets/mbeditor/editor.css +225 -10
- data/app/channels/mbeditor/editor_channel.rb +81 -0
- data/app/controllers/mbeditor/editors_controller.rb +65 -13
- data/app/views/layouts/mbeditor/application.html.erb +4 -0
- data/lib/mbeditor/cable_log_filter.rb +56 -0
- data/lib/mbeditor/engine.rb +10 -0
- data/lib/mbeditor/rack/silence_ping_request.rb +4 -1
- data/lib/mbeditor/version.rb +1 -1
- metadata +5 -2
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "fileutils"
|
|
4
|
+
require "pathname"
|
|
5
|
+
|
|
6
|
+
module Mbeditor
|
|
7
|
+
CableBaseClass = defined?(ActionCable::Channel::Base) ? ActionCable::Channel::Base : Object
|
|
8
|
+
|
|
9
|
+
class EditorChannel < CableBaseClass
|
|
10
|
+
STATE_MAX_BYTES = 1 * 1024 * 1024
|
|
11
|
+
SAFE_BRANCH_NAME = /\A[a-zA-Z0-9._\-\/]+\z/
|
|
12
|
+
|
|
13
|
+
def subscribed
|
|
14
|
+
stream_from "mbeditor_editor" if respond_to?(:stream_from)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def unsubscribed
|
|
18
|
+
# no-op
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Called via WebSocketService.perform('save_state', { state: ... })
|
|
22
|
+
def save_state(data)
|
|
23
|
+
Rails.logger.silence do
|
|
24
|
+
payload = (data["state"] || data).to_json
|
|
25
|
+
return if payload.bytesize > STATE_MAX_BYTES
|
|
26
|
+
|
|
27
|
+
root = workspace_root
|
|
28
|
+
path = root.join("tmp", "mbeditor_workspace.json")
|
|
29
|
+
FileUtils.mkdir_p(root.join("tmp"))
|
|
30
|
+
File.open(path, File::RDWR | File::CREAT) do |f|
|
|
31
|
+
f.flock(File::LOCK_EX)
|
|
32
|
+
f.truncate(0)
|
|
33
|
+
f.rewind
|
|
34
|
+
f.write(payload)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
rescue StandardError
|
|
38
|
+
# Never let a state-save failure crash the WebSocket connection
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Called via WebSocketService.perform('save_branch_state', { branch: ..., state: ... })
|
|
42
|
+
def save_branch_state(data)
|
|
43
|
+
Rails.logger.silence do
|
|
44
|
+
branch = data["branch"].to_s.strip
|
|
45
|
+
return unless branch.match?(SAFE_BRANCH_NAME)
|
|
46
|
+
|
|
47
|
+
state_data = data["state"]
|
|
48
|
+
payload_json = state_data.to_json
|
|
49
|
+
return if payload_json.bytesize > STATE_MAX_BYTES
|
|
50
|
+
|
|
51
|
+
root = workspace_root
|
|
52
|
+
path = root.join("tmp", "mbeditor_branch_states.json")
|
|
53
|
+
FileUtils.mkdir_p(root.join("tmp"))
|
|
54
|
+
File.open(path, File::RDWR | File::CREAT) do |f|
|
|
55
|
+
f.flock(File::LOCK_EX)
|
|
56
|
+
existing = f.size > 0 ? JSON.parse(f.read) : {}
|
|
57
|
+
existing[branch] = state_data
|
|
58
|
+
f.truncate(0)
|
|
59
|
+
f.rewind
|
|
60
|
+
f.write(existing.to_json)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
rescue StandardError
|
|
64
|
+
# Never let a state-save failure crash the WebSocket connection
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
private
|
|
68
|
+
|
|
69
|
+
def workspace_root
|
|
70
|
+
configured = Mbeditor.configuration.workspace_root
|
|
71
|
+
return Pathname.new(configured.to_s) if configured.present?
|
|
72
|
+
|
|
73
|
+
# Fall back to git root, same logic as ApplicationController
|
|
74
|
+
rails_root = Rails.root.to_s
|
|
75
|
+
out, _err, status = Open3.capture3("git", "-C", rails_root, "rev-parse", "--show-toplevel")
|
|
76
|
+
Pathname.new(status.success? && out.strip.present? ? out.strip : rails_root)
|
|
77
|
+
rescue StandardError
|
|
78
|
+
Rails.root
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
@@ -38,7 +38,8 @@ module Mbeditor
|
|
|
38
38
|
gitAvailable: git_available?,
|
|
39
39
|
blameAvailable: git_blame_available?,
|
|
40
40
|
redmineEnabled: Mbeditor.configuration.redmine_enabled == true,
|
|
41
|
-
testAvailable: test_available
|
|
41
|
+
testAvailable: test_available?,
|
|
42
|
+
actionCableEnabled: action_cable_enabled?
|
|
42
43
|
}
|
|
43
44
|
end
|
|
44
45
|
|
|
@@ -206,6 +207,7 @@ module Mbeditor
|
|
|
206
207
|
return render_file_too_large(content.bytesize) if content.bytesize > MAX_OPEN_FILE_SIZE_BYTES
|
|
207
208
|
|
|
208
209
|
File.write(path, content)
|
|
210
|
+
broadcast_files_changed
|
|
209
211
|
render json: { ok: true, path: relative_path(path) }
|
|
210
212
|
rescue StandardError => e
|
|
211
213
|
render json: { error: e.message }, status: :unprocessable_content
|
|
@@ -223,6 +225,7 @@ module Mbeditor
|
|
|
223
225
|
|
|
224
226
|
FileUtils.mkdir_p(File.dirname(path))
|
|
225
227
|
File.write(path, content)
|
|
228
|
+
broadcast_files_changed
|
|
226
229
|
|
|
227
230
|
render json: { ok: true, type: "file", path: relative_path(path), name: File.basename(path) }
|
|
228
231
|
rescue StandardError => e
|
|
@@ -237,6 +240,7 @@ module Mbeditor
|
|
|
237
240
|
return render json: { error: "Path already exists" }, status: :unprocessable_content if File.exist?(path)
|
|
238
241
|
|
|
239
242
|
FileUtils.mkdir_p(path)
|
|
243
|
+
broadcast_files_changed
|
|
240
244
|
render json: { ok: true, type: "folder", path: relative_path(path), name: File.basename(path) }
|
|
241
245
|
rescue StandardError => e
|
|
242
246
|
render json: { error: e.message }, status: :unprocessable_content
|
|
@@ -253,6 +257,7 @@ module Mbeditor
|
|
|
253
257
|
|
|
254
258
|
FileUtils.mkdir_p(File.dirname(new_path))
|
|
255
259
|
FileUtils.mv(old_path, new_path)
|
|
260
|
+
broadcast_files_changed
|
|
256
261
|
|
|
257
262
|
render json: {
|
|
258
263
|
ok: true,
|
|
@@ -274,9 +279,11 @@ module Mbeditor
|
|
|
274
279
|
|
|
275
280
|
if File.directory?(path)
|
|
276
281
|
FileUtils.rm_rf(path)
|
|
282
|
+
broadcast_files_changed
|
|
277
283
|
render json: { ok: true, type: "folder", path: relative_path(path) }
|
|
278
284
|
else
|
|
279
285
|
File.delete(path)
|
|
286
|
+
broadcast_files_changed
|
|
280
287
|
render json: { ok: true, type: "file", path: relative_path(path) }
|
|
281
288
|
end
|
|
282
289
|
rescue StandardError => e
|
|
@@ -312,23 +319,31 @@ module Mbeditor
|
|
|
312
319
|
render json: { error: e.message }, status: :unprocessable_content
|
|
313
320
|
end
|
|
314
321
|
|
|
315
|
-
# GET /mbeditor/search?q=...&offset=0&limit=50
|
|
322
|
+
# GET /mbeditor/search?q=...&offset=0&limit=50®ex=false&match_case=false&whole_word=false
|
|
316
323
|
def search
|
|
317
|
-
query
|
|
318
|
-
offset
|
|
319
|
-
limit
|
|
320
|
-
|
|
324
|
+
query = params[:q].to_s.strip
|
|
325
|
+
offset = [params[:offset].to_i, 0].max
|
|
326
|
+
limit = [[params[:limit].to_i > 0 ? params[:limit].to_i : 50, 200].min, 1].max
|
|
327
|
+
use_regex = params[:regex] == 'true'
|
|
328
|
+
match_case = params[:match_case] == 'true'
|
|
329
|
+
whole_word = params[:whole_word] == 'true'
|
|
330
|
+
needed = offset + limit + 1 # collect one extra to detect has_more
|
|
321
331
|
|
|
322
332
|
return render json: [] if query.blank?
|
|
323
333
|
return render json: { error: "Query too long" }, status: :bad_request if query.length > 500
|
|
324
334
|
|
|
325
335
|
# On first page, count total matches in parallel with fetching results.
|
|
326
|
-
count_thread = offset == 0 ? Thread.new { count_search_results(query) } : nil
|
|
336
|
+
count_thread = offset == 0 ? Thread.new { count_search_results(query, use_regex: use_regex, match_case: match_case, whole_word: whole_word) } : nil
|
|
327
337
|
|
|
328
|
-
results = stream_search_results(query, needed)
|
|
338
|
+
results = stream_search_results(query, needed, use_regex: use_regex, match_case: match_case, whole_word: whole_word)
|
|
329
339
|
has_more = results.length > offset + limit
|
|
330
340
|
response = { results: results[offset, limit] || [], has_more: has_more }
|
|
331
|
-
|
|
341
|
+
if count_thread
|
|
342
|
+
# Give the count thread up to 100 ms; omit total_count when it hasn't finished yet
|
|
343
|
+
# so the first page is never blocked by the counting subprocess.
|
|
344
|
+
count_thread.join(0.1)
|
|
345
|
+
response[:total_count] = count_thread.value unless count_thread.alive?
|
|
346
|
+
end
|
|
332
347
|
|
|
333
348
|
render json: response
|
|
334
349
|
rescue StandardError => e
|
|
@@ -661,6 +676,14 @@ module Mbeditor
|
|
|
661
676
|
|
|
662
677
|
private
|
|
663
678
|
|
|
679
|
+
def broadcast_files_changed
|
|
680
|
+
return unless defined?(ActionCable.server)
|
|
681
|
+
|
|
682
|
+
ActionCable.server.broadcast("mbeditor_editor", { type: "files_changed" })
|
|
683
|
+
rescue StandardError
|
|
684
|
+
# Never let a broadcast failure affect the HTTP response
|
|
685
|
+
end
|
|
686
|
+
|
|
664
687
|
def sanitize_branch_name(branch)
|
|
665
688
|
return nil if branch.blank?
|
|
666
689
|
str = branch.to_s.strip
|
|
@@ -668,6 +691,23 @@ module Mbeditor
|
|
|
668
691
|
str.match?(SAFE_BRANCH_NAME) ? str : nil
|
|
669
692
|
end
|
|
670
693
|
|
|
694
|
+
def action_cable_enabled?
|
|
695
|
+
return false unless defined?(ActionCable::Channel::Base)
|
|
696
|
+
|
|
697
|
+
mount_path = begin
|
|
698
|
+
ActionCable.server.config.mount_path
|
|
699
|
+
rescue StandardError
|
|
700
|
+
nil
|
|
701
|
+
end
|
|
702
|
+
mount_path = '/cable' if mount_path.blank?
|
|
703
|
+
|
|
704
|
+
Rails.application.routes.routes.any? do |route|
|
|
705
|
+
route.path.spec.to_s.start_with?(mount_path)
|
|
706
|
+
end
|
|
707
|
+
rescue StandardError
|
|
708
|
+
false
|
|
709
|
+
end
|
|
710
|
+
|
|
671
711
|
def allow_missing_file?
|
|
672
712
|
%w[1 true yes on].include?(params[:allow_missing].to_s.downcase)
|
|
673
713
|
end
|
|
@@ -696,11 +736,14 @@ module Mbeditor
|
|
|
696
736
|
# Stream search results using popen so we can stop reading early once we
|
|
697
737
|
# have collected `limit` matches (avoids buffering the entire rg/grep output
|
|
698
738
|
# in memory when searching large codebases for common tokens).
|
|
699
|
-
def stream_search_results(query, limit)
|
|
739
|
+
def stream_search_results(query, limit, use_regex: false, match_case: false, whole_word: false)
|
|
700
740
|
results = []
|
|
701
741
|
|
|
702
742
|
if RG_AVAILABLE
|
|
703
743
|
args = ["rg", "--json", "--no-ignore"]
|
|
744
|
+
args << "-F" unless use_regex
|
|
745
|
+
args << "--ignore-case" unless match_case
|
|
746
|
+
args << "--word-regexp" if whole_word
|
|
704
747
|
excluded_paths.each { |p| args << "--glob=!#{p}" }
|
|
705
748
|
args += ["--", query, workspace_root.to_s]
|
|
706
749
|
|
|
@@ -725,7 +768,10 @@ module Mbeditor
|
|
|
725
768
|
end
|
|
726
769
|
end
|
|
727
770
|
else
|
|
728
|
-
|
|
771
|
+
base_flags = use_regex ? "-E" : "-F"
|
|
772
|
+
args = ["grep", "-rn", base_flags]
|
|
773
|
+
args << "-i" unless match_case
|
|
774
|
+
args << "-w" if whole_word
|
|
729
775
|
excluded_dirnames.select { |d| d.match?(/\A[\w.\/-]+\z/) }.each { |d| args << "--exclude-dir=#{d}" }
|
|
730
776
|
args += [query, workspace_root.to_s]
|
|
731
777
|
|
|
@@ -756,17 +802,23 @@ module Mbeditor
|
|
|
756
802
|
|
|
757
803
|
# Count total matching lines across the workspace using rg --count (or grep -c).
|
|
758
804
|
# Fast: rg just counts without extracting context. Runs in a background thread.
|
|
759
|
-
def count_search_results(query)
|
|
805
|
+
def count_search_results(query, use_regex: false, match_case: false, whole_word: false)
|
|
760
806
|
total = 0
|
|
761
807
|
if RG_AVAILABLE
|
|
762
808
|
args = ["rg", "--count", "--no-ignore"]
|
|
809
|
+
args << "-F" unless use_regex
|
|
810
|
+
args << "--ignore-case" unless match_case
|
|
811
|
+
args << "--word-regexp" if whole_word
|
|
763
812
|
excluded_paths.each { |p| args << "--glob=!#{p}" }
|
|
764
813
|
args += ["--", query, workspace_root.to_s]
|
|
765
814
|
IO.popen(args, err: File::NULL) do |io|
|
|
766
815
|
io.each_line { |line| total += line.strip.split(":").last.to_i rescue 0 }
|
|
767
816
|
end
|
|
768
817
|
else
|
|
769
|
-
|
|
818
|
+
base_flags = use_regex ? "-E" : "-F"
|
|
819
|
+
args = ["grep", "-rc", base_flags]
|
|
820
|
+
args << "-i" unless match_case
|
|
821
|
+
args << "-w" if whole_word
|
|
770
822
|
excluded_dirnames.each { |d| args << "--exclude-dir=#{d}" }
|
|
771
823
|
args += [query, workspace_root.to_s]
|
|
772
824
|
IO.popen(args, err: File::NULL) do |io|
|
|
@@ -20,6 +20,10 @@
|
|
|
20
20
|
<%= stylesheet_link_tag "fontawesome.min", media: "all", preload_links_header: false %>
|
|
21
21
|
<%= stylesheet_link_tag "mbeditor/application", media: "all", preload_links_header: false %>
|
|
22
22
|
|
|
23
|
+
<!-- ── ActionCable (deferred — sets window.ActionCable for WebSocket service) ── -->
|
|
24
|
+
<% if defined?(ActionCable::Channel::Base) %>
|
|
25
|
+
<script defer src="<%= asset_path('actioncable.js') %>"></script>
|
|
26
|
+
<% end %>
|
|
23
27
|
<!-- ── Vendor JS (deferred — only needed inside Monaco callback) ── -->
|
|
24
28
|
<script defer src="<%= asset_path('react.min.js') %>"></script>
|
|
25
29
|
<script defer src="<%= asset_path('react-dom.min.js') %>"></script>
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Mbeditor
|
|
4
|
+
# Wraps the ActionCable logger and suppresses all log lines that mention
|
|
5
|
+
# Mbeditor channels so the development console stays readable.
|
|
6
|
+
# Non-Mbeditor ActionCable messages pass through unchanged.
|
|
7
|
+
class CableLogFilter < SimpleDelegator
|
|
8
|
+
SUPPRESS_PATTERN = /Mbeditor::|mbeditor_editor/
|
|
9
|
+
|
|
10
|
+
%w[debug info warn error fatal unknown].each do |level|
|
|
11
|
+
define_method(level) do |message = nil, &block|
|
|
12
|
+
msg = message.nil? && block ? block.call : message.to_s
|
|
13
|
+
return if msg.match?(SUPPRESS_PATTERN)
|
|
14
|
+
|
|
15
|
+
super(message, &block)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Tagged-logging compat — the block body still passes through the filter.
|
|
20
|
+
def tagged(*tags, &block)
|
|
21
|
+
if __getobj__.respond_to?(:tagged)
|
|
22
|
+
__getobj__.tagged(*tags, &block)
|
|
23
|
+
elsif block
|
|
24
|
+
block.call
|
|
25
|
+
else
|
|
26
|
+
self
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Rails/ActiveSupport logger compatibility. Some logger stacks call these
|
|
31
|
+
# methods even when the underlying logger is not TaggedLogging.
|
|
32
|
+
def current_tags
|
|
33
|
+
return __getobj__.current_tags if __getobj__.respond_to?(:current_tags)
|
|
34
|
+
|
|
35
|
+
[]
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def push_tags(*tags)
|
|
39
|
+
return __getobj__.push_tags(*tags) if __getobj__.respond_to?(:push_tags)
|
|
40
|
+
|
|
41
|
+
tags
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def pop_tags(count = 1)
|
|
45
|
+
return __getobj__.pop_tags(count) if __getobj__.respond_to?(:pop_tags)
|
|
46
|
+
|
|
47
|
+
[]
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def clear_tags!
|
|
51
|
+
return __getobj__.clear_tags! if __getobj__.respond_to?(:clear_tags!)
|
|
52
|
+
|
|
53
|
+
nil
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
data/lib/mbeditor/engine.rb
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require "mbeditor/rack/silence_ping_request"
|
|
4
4
|
require "mbeditor/rack/handle_pending_migrations"
|
|
5
|
+
require "mbeditor/cable_log_filter"
|
|
5
6
|
|
|
6
7
|
module Mbeditor
|
|
7
8
|
class Engine < ::Rails::Engine
|
|
@@ -24,6 +25,15 @@ module Mbeditor
|
|
|
24
25
|
end
|
|
25
26
|
|
|
26
27
|
config.after_initialize do
|
|
28
|
+
# Silence ActionCable framework logs for Mbeditor channels (subscription
|
|
29
|
+
# confirmations, streaming notices, action invocations, disconnect messages).
|
|
30
|
+
# We wrap the existing ActionCable logger in a filter proxy rather than
|
|
31
|
+
# replacing it, so the host app's non-Mbeditor channel logs are unaffected.
|
|
32
|
+
if defined?(ActionCable)
|
|
33
|
+
original_logger = ActionCable.server.config.logger || Rails.logger
|
|
34
|
+
ActionCable.server.config.logger = Mbeditor::CableLogFilter.new(original_logger)
|
|
35
|
+
end
|
|
36
|
+
|
|
27
37
|
Mbeditor::RubyDefinitionService.cache_path =
|
|
28
38
|
Rails.root.join("tmp", "mbeditor_ruby_defs.json").to_s
|
|
29
39
|
|
|
@@ -17,7 +17,7 @@ module Mbeditor
|
|
|
17
17
|
path = normalized_request_path(env)
|
|
18
18
|
if root_request?(env, path)
|
|
19
19
|
@app.call(env)
|
|
20
|
-
elsif mbeditor_request?(path) || editor_asset_request?(env, path)
|
|
20
|
+
elsif mbeditor_request?(path) || cable_request?(path) || editor_asset_request?(env, path)
|
|
21
21
|
Rails.logger.silence { @app.call(env) }
|
|
22
22
|
else
|
|
23
23
|
@app.call(env)
|
|
@@ -30,6 +30,9 @@ module Mbeditor
|
|
|
30
30
|
path.start_with?("/mbeditor/")
|
|
31
31
|
end
|
|
32
32
|
|
|
33
|
+
def cable_request?(path)
|
|
34
|
+
path == "/cable" || path.start_with?("/cable/")
|
|
35
|
+
end
|
|
33
36
|
# Silence asset pipeline requests that belong to the editor:
|
|
34
37
|
# - /assets/mbeditor/... is always an editor asset (CSS/JS bundle)
|
|
35
38
|
# - other /assets/... requests are silenced only when the browser is
|
data/lib/mbeditor/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: mbeditor
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.3
|
|
4
|
+
version: 0.4.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Oliver Noonan
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-04-
|
|
11
|
+
date: 2026-04-22 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rails
|
|
@@ -78,9 +78,11 @@ files:
|
|
|
78
78
|
- app/assets/javascripts/mbeditor/git_service.js
|
|
79
79
|
- app/assets/javascripts/mbeditor/search_service.js
|
|
80
80
|
- app/assets/javascripts/mbeditor/tab_manager.js
|
|
81
|
+
- app/assets/javascripts/mbeditor/websocket_service.js
|
|
81
82
|
- app/assets/stylesheets/mbeditor/application.css
|
|
82
83
|
- app/assets/stylesheets/mbeditor/editor.css
|
|
83
84
|
- app/assets/stylesheets/mbeditor/themes.css
|
|
85
|
+
- app/channels/mbeditor/editor_channel.rb
|
|
84
86
|
- app/controllers/mbeditor/application_controller.rb
|
|
85
87
|
- app/controllers/mbeditor/editors_controller.rb
|
|
86
88
|
- app/controllers/mbeditor/git_controller.rb
|
|
@@ -98,6 +100,7 @@ files:
|
|
|
98
100
|
- config/initializers/assets.rb
|
|
99
101
|
- config/routes.rb
|
|
100
102
|
- lib/mbeditor.rb
|
|
103
|
+
- lib/mbeditor/cable_log_filter.rb
|
|
101
104
|
- lib/mbeditor/configuration.rb
|
|
102
105
|
- lib/mbeditor/engine.rb
|
|
103
106
|
- lib/mbeditor/rack/handle_pending_migrations.rb
|