Skip to content

Commit

Permalink
improved viewer
Browse files Browse the repository at this point in the history
  • Loading branch information
Daniel Menet committed Sep 18, 2023
1 parent 335f2c0 commit f524f59
Show file tree
Hide file tree
Showing 17 changed files with 3,235 additions and 412 deletions.
2 changes: 0 additions & 2 deletions cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,8 +150,6 @@ func (a *App) serveCmd(cmd *cobra.Command, args []string) {

s, err := NewServer(
a.flags.serve.listener,
a.flags.serve.renderer,
a.flags.configfile,
a.flags.base,
a.flags.glob,
a.flags.serve.cacheTimeout,
Expand Down
75 changes: 37 additions & 38 deletions server.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,43 +12,57 @@ import (
"sysdoc/internal/persistence"
"text/template"
"time"

"gopkg.in/yaml.v3"
)

//go:embed server/templates/*
//go:embed server/private/*
var templateFS embed.FS

//go:embed server/static/*
var staticFS embed.FS

type server struct {
indexTemplate *template.Template
listener string
defaultRenderer string
configfile string
base string
glob string
cache cache.Cache
persistence persistence.Persistence
renderer Renderer
indexTemplate *template.Template
listener string
base string
glob string
cache cache.Cache
persistence persistence.Persistence
rendererConfig renderConfig
renderer Renderer
}

func NewServer(listener, defaultRenderer, configfile, base, glob, cacheTimeout string, p persistence.Persistence, r Renderer) (*server, error) {
func NewServer(listener, base, glob, cacheTimeout string, p persistence.Persistence, r Renderer) (*server, error) {
durr, err := time.ParseDuration(cacheTimeout)
if err != nil {
return nil, err
}

s := &server{
listener: listener,
defaultRenderer: defaultRenderer,
configfile: configfile,
base: base,
glob: glob,
cache: *cache.New(durr),
persistence: p,
renderer: r,
listener: listener,
base: base,
glob: glob,
cache: *cache.New(durr),
persistence: p,
renderer: r,
}

rendererConfig, err := templateFS.ReadFile("server/private/renderer.yaml")
if err != nil {
return nil, err
}
err = yaml.Unmarshal(rendererConfig, &s.rendererConfig)
if err != nil {
return nil, err
}

s.indexTemplate, err = template.ParseFS(templateFS, "server/private/index.html.tmpl")
if err != nil {
return nil, err
}
s.indexTemplate, err = template.ParseFS(templateFS, "server/templates/index.html.tmpl")
return s, err

return s, nil
}

func (s *server) Run() error {
Expand Down Expand Up @@ -112,35 +126,20 @@ func (s *server) HandleSVG(w http.ResponseWriter, r *http.Request) {
}
}

rendererName := r.URL.Query().Get("renderer")
if rendererName == "" {
rendererName = s.defaultRenderer
}

cfg, err := NewConfig(s.configfile, s.persistence.Filesystem())
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
_, _ = w.Write([]byte(err.Error()))
return
}
// build system
sys, errs := New(s.base, s.glob, focus, s.persistence)
if len(errs) > 0 {
w.WriteHeader(http.StatusInternalServerError)
out := ""
for _, err = range errs {
for _, err := range errs {
out += fmt.Sprintf("%s\n", err.Error())
}
_, _ = w.Write([]byte(out))
return
}

// render template
renderer, ok := cfg.Renderer[rendererName]
if !ok {
exitOnErr(fmt.Errorf("renderer %s not specified in %s", rendererName, s.configfile))
}
img, err := s.renderer.Do(sys, renderer, false)
img, err := s.renderer.Do(sys, s.rendererConfig, false)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
_, _ = w.Write([]byte(err.Error()))
Expand Down
153 changes: 153 additions & 0 deletions server/private/index.html.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>sysdoc</title>
<link href="https://fonts.googleapis.com/css?family=Karla:400,700" rel="stylesheet">
<link rel="stylesheet" href="static/style.css">
</head>
<body>

<div id="alert-box"></div>

<div id="placeholder">
<svg id="loader" version="1.1" viewBox="0 0 100 100">
<path fill="#095f91" d="M31.6,3.5C5.9,13.6-6.6,42.7,3.5,68.4c10.1,25.7,39.2,38.3,64.9,28.1l-3.1-7.9c-21.3,8.4-45.4-2-53.8-23.3
c-8.4-21.3,2-45.4,23.3-53.8L31.6,3.5z">
<animateTransform
attributeName="transform"
attributeType="XML"
type="rotate"
dur="2s"
from="0 50 50"
to="360 50 50"
repeatCount="indefinite" />
</path>
<path fill="#095f91" d="M42.3,39.6c5.7-4.3,13.9-3.1,18.1,2.7c4.3,5.7,3.1,13.9-2.7,18.1l4.1,5.5c8.8-6.5,10.6-19,4.1-27.7
c-6.5-8.8-19-10.6-27.7-4.1L42.3,39.6z">
<animateTransform
attributeName="transform"
attributeType="XML"
type="rotate"
dur="1s"
from="0 50 50"
to="-360 50 50"
repeatCount="indefinite" />
</path>
<path fill="#095f91" d="M82,35.7C74.1,18,53.4,10.1,35.7,18S10.1,46.6,18,64.3l7.6-3.4c-6-13.5,0-29.3,13.5-35.3s29.3,0,35.3,13.5
L82,35.7z">
<animateTransform
attributeName="transform"
attributeType="XML"
type="rotate"
dur="2s"
from="0 50 50"
to="360 50 50"
repeatCount="indefinite" />
</path>
</svg>
</div>

<nav class="float-container">
<div class="logo float-child">
sysdoc
</div>

<div class="controls float-child">
<form>
<select name="branches" id="branches"></select>
<form>
</div>

</div>
<div class="shortcuts float-child">
<a onclick="updateQueryParam('focus', '');">Overview</a>
</div>
</nav>

<script type="text/javascript" src="static/alert.js"></script>
<script type="text/javascript" src="static/hover.js"></script>
<script type="text/javascript" src="static/svg-pan-zoom.js"></script>
<script>
// load svg
var url = window.location.origin + "/svg/" + window.location.search;
let xhr = new XMLHttpRequest();
xhr.open('GET', url);
xhr.send();
xhr.onload = function() {
if (xhr.status != 200) {
console.log(`Error ${xhr.status}: ${xhr.response}`);
displayAlert(`Error ${xhr.status}: ${xhr.response}`);
} else {
document.getElementById('placeholder').outerHTML = xhr.response;
var panZoomTiger = svgPanZoom('#svg');
initHover();
addOnClick();
}
};

xhr.onerror = function() {
console.log("Request failed");
displayAlert("Request failed");
};

// list branches
var branches = document.getElementById('branches');
async function loadBranches() {
const response = await fetch(window.location.origin + "/branches.json");
const branchlist = await response.json();
let searchParams = new URLSearchParams(window.location.search);
let branch = searchParams.get("branch");
for (var i = 0; i < branchlist.length; i++) {
let newOption = new Option(branchlist[i],branchlist[i]);
if (branch == branchlist[i]) {
newOption.selected = true
}
branches.add(newOption, undefined)
}
branches.addEventListener("change", function(){
console.log(branches.value);
var params = new URLSearchParams(window.location.search)
params.set("branch", branches.value)
window.location.href = '//' + location.host + location.pathname + "?" + params.toString();
});
};

loadBranches();

// create links

function addOnClick() {
var elems = getElementsWithID("svg", "element");
for (let i = 0; i < elems.length; i++) {
elems[i].setAttribute("onclick", "updateQueryParam('focus', '"+elems[i].id+"');");
}
}

function updateQueryParam(a, b) {
var searchParams = new URLSearchParams(window.location.search);
if (b != '') {
searchParams.set(a, b);
} else {
searchParams.delete(a);
}
window.location.search = searchParams.toString();
}

function getElementsWithID(container, className) {
var items = [];
var elems = document.getElementById(container).getElementsByClassName(className);
for (var i = 0; i < elems.length; i++) {
if (elems[i].id != "") {
items.push(elems[i]);
}
}
return items;
}


</script>
</body>
</html>
88 changes: 88 additions & 0 deletions server/private/renderer.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
---
postprocessor:
name: d2
templates:
element: |
{{if eq .Fragment "root"}}
{{range $child := .Children}}
{{$child}}
{{- end}}
{{- else}}
{{.Fragment}}: "{{if .Name}}{{.Name}}{{else}}{{.ID "."}}{{end}}{{if (index .Tags "external")}} (external){{end}}{{if (index .Tags "obsolete")}} (obsolete){{end}}" {
style: {
border-radius: 14
stroke-width: 0
}
class: element
{{if (index .Tags "focussed")}}
style: {
fill: "#22bbbb"
}
{{- end}}
{{if eq (index .Tags "type") "user"}}
shape: person
{{- end}}
{{if (index .Tags "link")}}
tooltip: Documentation at "{{index .Tags "link"}}"
{{- end}}
{{if (index .Tags "external")}}
style: {
opacity: 0.4
}
{{- end}}
{{if (index .Tags "obsolete")}}
style: {
fill: "#cc7745"
opacity: 0.8
}
{{- end}}
{{range $child := .Children}}
{{$child}}
{{- end}}
{{range $interf := .Interfaces}}
{{$interf.Fragment}}{{if $interf.Name}}: "{{$interf.Name}}"{{end}} {
shape: diamond
{{- if (index .Tags "link")}}
link: "{{index .Tags "link"}}"
{{- end}}
tooltip: "{{$.ID "."}}.{{$interf.Fragment}}"
style: {
stroke-width: 0
fill: "#8e89c4"
}
}
{{- end}}
{{range $prop := .Propagations}}
"{{$prop.Fragment}}" {
shape: circle
style: {
stroke-width: 0
fill: "#8e89c4"
}
}
{{- end}}
}
{{- end}}
dependency: |
{{.BelongsToID "."}} -> {{.ViaPropagation "."}}{{if .Description}}: {{.Description}}{{end}}{{if (index .Tags "manual")}} (manual) {
style: {
stroke-dash: 3
}
}{{end}}
propagation: |
{{.ID "."}} -> {{.PropagatesID "."}}: propagates
global: |
direction: right
# Elements
{{.Elements}}
# Dependencies
{{.Dependencies}}
# Propagations
{{.Propagations}}
7 changes: 7 additions & 0 deletions server/static/alert.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
function displayAlert(msg) {
const node = document.createElement('div');
node.classList.add('alert');
const textnode = document.createTextNode(msg);
node.appendChild(textnode);
document.getElementById('alert-box').appendChild(node);
}
Loading

0 comments on commit f524f59

Please sign in to comment.