Draft: First draft of new fiber based server #7
5 changed files with 89 additions and 60 deletions
|
@ -51,8 +51,8 @@ var (
|
||||||
hub := events.NewEventHub(
|
hub := events.NewEventHub(
|
||||||
wdfs,
|
wdfs,
|
||||||
fnv.New32a(),
|
fnv.New32a(),
|
||||||
events.FileNameTrigger(args[0]),
|
events.MutationReloadForFile(args[0]),
|
||||||
events.FileNameTrigger(filepath.Base(cfg.ConfigFileInUse)),
|
events.MutationConfigReloadForFile(filepath.Base(cfg.ConfigFileInUse)),
|
||||||
)
|
)
|
||||||
|
|
||||||
api.NoCache(app)
|
api.NoCache(app)
|
||||||
|
|
|
@ -3,9 +3,10 @@ package config
|
||||||
var (
|
var (
|
||||||
defaults = map[string]interface{}{
|
defaults = map[string]interface{}{
|
||||||
"mermaid.theme": "forest",
|
"mermaid.theme": "forest",
|
||||||
"reveal.theme": "beige",
|
"theme": "beige",
|
||||||
"codeTheme": "monokai",
|
"codeTheme": "monokai",
|
||||||
"transition": TransitionNone,
|
"transition": TransitionNone,
|
||||||
|
"controlsLayout": ControlsLayoutEdges,
|
||||||
"controls": true,
|
"controls": true,
|
||||||
"progress": true,
|
"progress": true,
|
||||||
"history": true,
|
"history": true,
|
||||||
|
@ -23,11 +24,15 @@ const (
|
||||||
TransitionConvex Transition = "convex"
|
TransitionConvex Transition = "convex"
|
||||||
TransitionConcave Transition = "concave"
|
TransitionConcave Transition = "concave"
|
||||||
TransitionZoom Transition = "zoom"
|
TransitionZoom Transition = "zoom"
|
||||||
|
|
||||||
|
ControlsLayoutBottomRight ControlsLayout = "bottom-right"
|
||||||
|
ControlsLayoutEdges ControlsLayout = "edges"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
Transition string
|
Transition string
|
||||||
Mermaid struct {
|
ControlsLayout string
|
||||||
|
Mermaid struct {
|
||||||
Theme string `json:"theme"`
|
Theme string `json:"theme"`
|
||||||
}
|
}
|
||||||
Rendering struct {
|
Rendering struct {
|
||||||
|
@ -36,15 +41,16 @@ type (
|
||||||
Stylesheets []string
|
Stylesheets []string
|
||||||
}
|
}
|
||||||
Reveal struct {
|
Reveal struct {
|
||||||
Theme string `json:"theme"`
|
Theme string `json:"theme"`
|
||||||
CodeTheme string `json:"codeTheme"`
|
CodeTheme string `json:"codeTheme"`
|
||||||
Transition Transition `json:"transition"`
|
Transition Transition `json:"transition"`
|
||||||
Controls bool `json:"controls"`
|
Controls bool `json:"controls"`
|
||||||
Progress bool `json:"progress"`
|
ControlsLayout ControlsLayout `json:"controlsLayout"`
|
||||||
History bool `json:"history"`
|
Progress bool `json:"progress"`
|
||||||
Center bool `json:"center"`
|
History bool `json:"history"`
|
||||||
SlideNumber bool `json:"slideNumber"`
|
Center bool `json:"center"`
|
||||||
Menu struct {
|
SlideNumber bool `json:"slideNumber"`
|
||||||
|
Menu struct {
|
||||||
Numbers bool `json:"numbers"`
|
Numbers bool `json:"numbers"`
|
||||||
UseTextContentForMissingTitles bool `json:"useTextContentForMissingTitles"`
|
UseTextContentForMissingTitles bool `json:"useTextContentForMissingTitles"`
|
||||||
Transitions bool
|
Transitions bool
|
||||||
|
|
|
@ -21,14 +21,15 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
ReloadTrigger interface {
|
EventMutation interface {
|
||||||
Triggers(ev fs.Event) bool
|
OnEvent(in ContentEvent, ev fs.Event) (out ContentEvent)
|
||||||
}
|
}
|
||||||
ContentEvent struct {
|
ContentEvent struct {
|
||||||
File string `json:"file"`
|
File string `json:"file"`
|
||||||
FileNameHash string `json:"fileNameHash"`
|
FileNameHash string `json:"fileNameHash"`
|
||||||
Timestamp string `json:"ts"`
|
Timestamp string `json:"ts"`
|
||||||
ForceReload bool `json:"forceReload"`
|
ForceReload bool `json:"forceReload"`
|
||||||
|
ReloadConfig bool `json:"reloadConfig"`
|
||||||
}
|
}
|
||||||
EventSource interface {
|
EventSource interface {
|
||||||
io.Closer
|
io.Closer
|
||||||
|
@ -43,30 +44,35 @@ type (
|
||||||
EventHandler
|
EventHandler
|
||||||
OnError chan error
|
OnError chan error
|
||||||
}
|
}
|
||||||
FileNameTrigger string
|
MutationReloadForFile string
|
||||||
FileSuffixTrigger string
|
MutationConfigReloadForFile string
|
||||||
)
|
)
|
||||||
|
|
||||||
func (t FileNameTrigger) Triggers(ev fs.Event) bool {
|
func (t MutationReloadForFile) OnEvent(in ContentEvent, ev fs.Event) (out ContentEvent) {
|
||||||
fileBase := filepath.Base(ev.File)
|
if strings.EqualFold(filepath.Base(ev.File), string(t)) {
|
||||||
return strings.EqualFold(fileBase, string(t))
|
in.ForceReload = true
|
||||||
|
}
|
||||||
|
return in
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t FileSuffixTrigger) Triggers(ev fs.Event) bool {
|
func (t MutationConfigReloadForFile) OnEvent(in ContentEvent, ev fs.Event) (out ContentEvent) {
|
||||||
return strings.HasSuffix(strings.ToLower(filepath.Base(ev.File)), strings.ToLower(string(t)))
|
if strings.EqualFold(filepath.Base(ev.File), string(t)) {
|
||||||
|
in.ReloadConfig = true
|
||||||
|
}
|
||||||
|
return in
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f EventHandlerFunc) OnEvent(ev ContentEvent) error {
|
func (f EventHandlerFunc) OnEvent(ev ContentEvent) error {
|
||||||
return f(ev)
|
return f(ev)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewEventHub(eventSource EventSource, fileNameHash hash.Hash, triggers ...ReloadTrigger) *EventHub {
|
func NewEventHub(eventSource EventSource, fileNameHash hash.Hash, mutations ...EventMutation) *EventHub {
|
||||||
hub := &EventHub{
|
hub := &EventHub{
|
||||||
FileNameHash: fileNameHash,
|
FileNameHash: fileNameHash,
|
||||||
reloadTriggers: triggers,
|
mutations: mutations,
|
||||||
source: eventSource,
|
source: eventSource,
|
||||||
subscriptions: make(map[uuid.UUID]*subscription),
|
subscriptions: make(map[uuid.UUID]*subscription),
|
||||||
done: make(chan struct{}),
|
done: make(chan struct{}),
|
||||||
}
|
}
|
||||||
|
|
||||||
go hub.processEvents()
|
go hub.processEvents()
|
||||||
|
@ -75,12 +81,12 @@ func NewEventHub(eventSource EventSource, fileNameHash hash.Hash, triggers ...Re
|
||||||
}
|
}
|
||||||
|
|
||||||
type EventHub struct {
|
type EventHub struct {
|
||||||
FileNameHash hash.Hash
|
FileNameHash hash.Hash
|
||||||
reloadTriggers []ReloadTrigger
|
mutations []EventMutation
|
||||||
lock sync.RWMutex
|
lock sync.RWMutex
|
||||||
done chan struct{}
|
done chan struct{}
|
||||||
source EventSource
|
source EventSource
|
||||||
subscriptions map[uuid.UUID]*subscription
|
subscriptions map[uuid.UUID]*subscription
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *EventHub) Subscribe(handler EventHandler) (id uuid.UUID, onError <-chan error) {
|
func (h *EventHub) Subscribe(handler EventHandler) (id uuid.UUID, onError <-chan error) {
|
||||||
|
@ -129,20 +135,16 @@ func (h *EventHub) notifySubscribers(ev fs.Event) {
|
||||||
h.lock.RLock()
|
h.lock.RLock()
|
||||||
defer h.lock.RUnlock()
|
defer h.lock.RUnlock()
|
||||||
|
|
||||||
var triggerReload bool
|
|
||||||
for idx := range h.reloadTriggers {
|
|
||||||
if triggerReload = h.reloadTriggers[idx].Triggers(ev); triggerReload {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ce := ContentEvent{
|
ce := ContentEvent{
|
||||||
File: fmt.Sprintf("/%s", ev.File),
|
File: fmt.Sprintf("/%s", ev.File),
|
||||||
Timestamp: strconv.FormatInt(ev.Timestamp.Unix(), baseDecimal),
|
Timestamp: strconv.FormatInt(ev.Timestamp.Unix(), baseDecimal),
|
||||||
ForceReload: triggerReload,
|
|
||||||
FileNameHash: hex.EncodeToString(h.FileNameHash.Sum([]byte(path.Base(ev.File)))),
|
FileNameHash: hex.EncodeToString(h.FileNameHash.Sum([]byte(path.Base(ev.File)))),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for idx := range h.mutations {
|
||||||
|
ce = h.mutations[idx].OnEvent(ce, ev)
|
||||||
|
}
|
||||||
|
|
||||||
for _, handler := range h.subscriptions {
|
for _, handler := range h.subscriptions {
|
||||||
if err := handler.OnEvent(ce); err != nil {
|
if err := handler.OnEvent(ce); err != nil {
|
||||||
handler.OnError <- err
|
handler.OnError <- err
|
||||||
|
|
|
@ -3,6 +3,16 @@ codeTheme: monokai
|
||||||
horizontalSeparator: ===
|
horizontalSeparator: ===
|
||||||
verticalSeparator: ---
|
verticalSeparator: ---
|
||||||
transition: convex
|
transition: convex
|
||||||
menu.numbers: false
|
controlsLayout: edges
|
||||||
|
controls: true
|
||||||
|
progress: true
|
||||||
|
history: true
|
||||||
|
center: true
|
||||||
|
slideNumber: true
|
||||||
|
menu:
|
||||||
|
numbers: false
|
||||||
|
useTextContentForMissingTitles: true
|
||||||
|
mermaid:
|
||||||
|
theme: forest
|
||||||
stylesheets:
|
stylesheets:
|
||||||
- custom.css
|
- custom.css
|
|
@ -25,9 +25,13 @@ async function setSlidesContent() {
|
||||||
document.getElementById("content-root").innerHTML = contentDocument.documentElement.innerHTML
|
document.getElementById("content-root").innerHTML = contentDocument.documentElement.innerHTML
|
||||||
}
|
}
|
||||||
|
|
||||||
async function initReveal() {
|
async function getRevealConfig() {
|
||||||
let resp = await fetch('/api/v1/config/reveal')
|
let resp = await fetch('/api/v1/config/reveal')
|
||||||
let cfg = await resp.json()
|
return await resp.json()
|
||||||
|
}
|
||||||
|
|
||||||
|
async function initReveal() {
|
||||||
|
let cfg = await getRevealConfig()
|
||||||
Reveal.initialize({
|
Reveal.initialize({
|
||||||
controls: cfg.controls,
|
controls: cfg.controls,
|
||||||
progress: cfg.progress,
|
progress: cfg.progress,
|
||||||
|
@ -97,20 +101,27 @@ function subscribeToEvents() {
|
||||||
source.onmessage = (ev => {
|
source.onmessage = (ev => {
|
||||||
let obj = JSON.parse(ev.data);
|
let obj = JSON.parse(ev.data);
|
||||||
console.log(obj);
|
console.log(obj);
|
||||||
if (obj.forceReload) {
|
switch (true) {
|
||||||
window.location.reload()
|
case obj.forceReload:
|
||||||
} else {
|
window.location.reload()
|
||||||
switch (true) {
|
break
|
||||||
case obj.file.endsWith(".css"):
|
case obj.reloadConfig:
|
||||||
let cssLink = document.querySelector(`link[rel=stylesheet][id="${obj.fileNameHash}"]`);
|
getRevealConfig().then(cfg => {
|
||||||
cssLink.href = `${obj.file}?ts=${obj.ts}`
|
Reveal.configure(cfg)
|
||||||
break
|
})
|
||||||
default:
|
break
|
||||||
let elem = document.getElementById(obj.fileNameHash);
|
default:
|
||||||
if (elem !== null) {
|
switch (true) {
|
||||||
elem.src = `${obj.file}?ts=${obj.ts}`
|
case obj.file.endsWith(".css"):
|
||||||
}
|
let cssLink = document.querySelector(`link[rel=stylesheet][id="${obj.fileNameHash}"]`);
|
||||||
}
|
cssLink.href = `${obj.file}?ts=${obj.ts}`
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
let elem = document.getElementById(obj.fileNameHash);
|
||||||
|
if (elem !== null) {
|
||||||
|
elem.src = `${obj.file}?ts=${obj.ts}`
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
Loading…
Reference in a new issue