Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
Gemfile.lock

/cache
/coverage
/dev
/doc
/gems
.gems
/spec/status.txt
/tasks/debug.runfile
/tmp
Expand All @@ -17,3 +16,4 @@ madness.log
nohup.out
toc.txt
toc.html
.envrc
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ $ alias madness='docker run --rm -it -v $PWD:/docs -p 3000:3000 dannyben/madness
- Optional support for `[[Short Link]]` syntax.
- Optional support for Mermaid diagrams.
- Optional basic authentication.
- Live reload - automatically refreshes the browser when files change.
- Support for extended markdown syntax, such as footnotes and syntax
highlighting.

Expand Down Expand Up @@ -200,6 +201,9 @@ auth_zone: Restricted Documentation
# expose_extensions: pdf,docx,xlsx,txt
expose_extensions: ~

# enable live reload (auto-refresh on file changes)
live_reload: true

# exclude directories that match these regular expressions
# note that this is an array
exclude: ['^[a-z_\-0-9]+$']
Expand Down
61 changes: 61 additions & 0 deletions app/public/js/live_reload.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
var baseUri = document.body.dataset.baseUri || "";
var wsProtocol = location.protocol === "https:" ? "wss:" : "ws:";
var wsUrl = wsProtocol + "//" + location.host + baseUri + "/_live_reload";
var socket;

function connect() {
socket = new WebSocket(wsUrl);

socket.onopen = function() {
console.log("[LiveReload] connected");
};

socket.onmessage = function(event) {
console.log("[LiveReload] changed:", event.data);
reloadContent();
};

socket.onclose = function() {
console.log("[LiveReload] disconnected, reconnecting...");
setTimeout(connect, 1000);
};
}

function reloadContent() {
fetch(location.href, { cache: "no-store" })
.then(function(response) { return response.text(); })
.then(function(html) {
var parsed = new DOMParser().parseFromString(html, "text/html");
swapElement(parsed, ".main");
swapElement(parsed, "nav");
updateTitle(parsed);
reinitMermaid();
console.log("[LiveReload] content swapped");
})
.catch(function(error) {
console.error("[LiveReload] fetch failed:", error);
});
}

function updateTitle(parsed) {
var newTitle = parsed.querySelector("title");
if (newTitle) document.title = newTitle.textContent;
}

function reinitMermaid() {
if (typeof mermaid !== "undefined") mermaid.init();
}

function swapElement(parsed, selector) {
var current = document.querySelector(selector);
var updated = parsed.querySelector(selector);
if (current && updated) {
current.innerHTML = updated.innerHTML;
}
}

window.addEventListener("beforeunload", function() {
socket.close();
});

connect();
5 changes: 4 additions & 1 deletion app/views/layout.slim
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,8 @@ html
javascript:
mermaid.initialize({startOnLoad:true});

body
- if config.live_reload
script src="#{config.base_uri}/js/live_reload.js" defer=true

body data-base-uri="#{config.base_uri}"
== yield
2 changes: 2 additions & 0 deletions lib/madness/commands/server.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ class Server < Base
option '--auth USER:PASS', 'Enable basic authentication'
option '--auth-zone NAME', 'The basic authentication realm (default: Restricted Documentation)'
option '--theme FOLDER', 'Use a custom theme (either absolute or relative to the main documentation path)'
option '--no-live-reload', 'Disable live reload (enabled by default)'

example 'madness server'
example 'madness server docs'
Expand Down Expand Up @@ -59,6 +60,7 @@ def override_config(args)
config.auth_zone = args['--auth-zone'] if args['--auth-zone']
config.open = true if args['--open']
config.theme = File.expand_path(args['--theme'], config.path) if args['--theme']
config.live_reload = false if args['--no-live-reload']
end

def show_status
Expand Down
49 changes: 49 additions & 0 deletions lib/madness/live_reload.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
require 'listen'
require 'faye/websocket'

module Madness
class LiveReload
class << self
def watch(path)
@clients = []
@listener = Listen.to(path, &method(:on_change))
@listener.start
end

def stop
@listener&.stop
end

def add_client(ws)
@clients << ws
ws.on(:close) { @clients.delete(ws) }
end

def broadcast(files)
@clients.each { |ws| ws.send(files.join(",")) }
end

def websocket?(env)
Faye::WebSocket.websocket?(env)
end

def handle(env)
ws = Faye::WebSocket.new(env)
add_client(ws)
ws.rack_response
end

private

def on_change(modified, added, removed)
files = (modified + added + removed).uniq
files.each { |file| log_change(file) }
broadcast(files)
end

def log_change(file)
$stderr.puts "LiveReload: #{file}"
end
end
end
end
23 changes: 23 additions & 0 deletions lib/madness/middleware/live_reload.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
module Madness
module Middleware
class LiveReload
def initialize(app)
@app = app
end

def call(env)
if websocket_upgrade?(env)
Madness::LiveReload.handle(env)
else
@app.call(env)
end
end

private

def websocket_upgrade?(env)
Madness::LiveReload.websocket?(env) && env['PATH_INFO'] =~ %r{/_live_reload$}
end
end
end
end
14 changes: 9 additions & 5 deletions lib/madness/server_base.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# require 'sinatra/reloader'
require 'sinatra/base'
require 'slim'
require 'madness/live_reload'
require 'madness/middleware/live_reload'

module Madness
# The base class for the sinatra server.
Expand All @@ -15,11 +17,6 @@ class ServerBase < Sinatra::Application
set :server, :puma
set :static, false

# Since we cannot use any config values in the main body of the class,
# since they will be updated later, we need to set anything that relies
# on the config values just before running the server.
# The CommandLine class and the test suite should both call
# `Server.prepare` before calling Server.run!
class << self
include ServerHelper

Expand All @@ -29,6 +26,13 @@ def prepare
set :views, theme.views_path

set_basic_auth if config.auth
setup_live_reload if config.live_reload
end

def setup_live_reload
use Madness::Middleware::LiveReload
Madness::LiveReload.watch(File.expand_path(config.path, Dir.pwd))
at_exit { Madness::LiveReload.stop }
end

def set_basic_auth
Expand Down
1 change: 1 addition & 0 deletions lib/madness/settings.rb
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ def defaults
auth: false,
auth_zone: 'Restricted Documentation',
expose_extensions: nil,
live_reload: true,
exclude: ['^[a-z_\-0-9]+$'],
}
end
Expand Down
3 changes: 3 additions & 0 deletions lib/madness/templates/madness.yml
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,9 @@ source_link_label: Page Source
# source_link_pos: top
source_link_pos: bottom

# automatically reload the browser when files change
live_reload: true

# open the server URL in the browser
open: false

Expand Down
2 changes: 2 additions & 0 deletions madness.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ Gem::Specification.new do |s|
s.add_dependency 'colsole', '~> 1.0'
s.add_dependency 'extended_yaml', '~> 0.2'
s.add_dependency 'mister_bin', '~> 0.7'
s.add_dependency 'faye-websocket', '~> 0.11'
s.add_dependency 'listen', '~> 3.0'
s.add_dependency 'naturally', '~> 2.2'
s.add_dependency 'os', '~> 1.0'
s.add_dependency 'pandoc-ruby', '~> 2.1'
Expand Down
1 change: 1 addition & 0 deletions spec/approvals/cli/config/show
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,6 @@
auth: ~
auth_zone: Restricted Documentation
expose_extensions: ~
live_reload: true
exclude: ["^[a-z_\\-0-9]+$"]

1 change: 1 addition & 0 deletions spec/approvals/cli/config/show-non-default
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
auth: ~
auth_zone: Restricted Documentation
expose_extensions: ~
live_reload: true
exclude: ["^[a-z_\\-0-9]+$"]
invalid_key: with some value

Expand Down
6 changes: 4 additions & 2 deletions spec/approvals/cli/server/help
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@ Options:
The basic authentication realm (default: Restricted Documentation)

--theme FOLDER
Use a custom theme (either absolute or relative to the main documentation
path)
Use a custom theme (either absolute or relative to the main documentation path)

--no-live-reload
Disable live reload (enabled by default)

-h --help
Show this help
Expand Down
1 change: 1 addition & 0 deletions spec/approvals/cli/theme/theme-ls
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
- tmp/theme-test/public/font/fontello.woff2
- tmp/theme-test/public/js
- tmp/theme-test/public/js/clipboard.js
- tmp/theme-test/public/js/live_reload.js
- tmp/theme-test/public/js/vendor
- tmp/theme-test/public/js/vendor/clipboard.min.js
- tmp/theme-test/public/js/vendor/jquery.min.js
Expand Down
3 changes: 3 additions & 0 deletions spec/madness/commands/server_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
before do
config.reset
config.path = 'spec/fixtures/docroot'
config.live_reload = false
end

context 'with --help' do
Expand Down Expand Up @@ -78,6 +79,7 @@

it 'does considers the config values' do
expect(server).to receive :run!
allow(Madness::LiveReload).to receive(:watch)

Dir.chdir 'spec/fixtures/docroot-with-config' do
expect { subject.execute %w[server] }
Expand All @@ -94,6 +96,7 @@

it 'calls the TOC builder' do
expect(server).to receive :run!
allow(Madness::LiveReload).to receive(:watch)

Dir.chdir 'spec/fixtures/docroot-with-config' do
expect(TableOfContents).to receive(:new).and_return(toc_double)
Expand Down
Loading
Loading