- internal/graph package: pure-Go layered top-down DAG layout - LayerByLongestPath (multi-parent sits at max(parent-layer)+1) - OrderInLayer (slug-sort, deterministic) - Compute returns positions + edges + canvas size - cycle-safe (depth-cap) - web/graph.go handler: filter chips reused from tree_filter - dim mode default (opacity 0.15 on non-matches) - ?isolate=1 hides non-matches + prunes orphaned edges - ?download=svg serves raw SVG attachment - graph_svg.tmpl renders inline SVG: border colour by management (mai blue / self green / external orange / mixed dashed purple), opacity by status, tag pills, ×N multi-parent badge, click-navigate - nav adds "graph" link; design.md §"Graph view" documents the surface - 4 integration tests cover render, dim, isolate, SVG download - 6 layout unit tests cover layering, ordering, cycle-guard
50 lines
2.0 KiB
Cheetah
50 lines
2.0 KiB
Cheetah
{{define "content"}}
|
|
<h1>Graph <small class="muted">{{.Matched}} / {{.Total}} items</small></h1>
|
|
|
|
<section class="tagbar" id="graph-filterbar">
|
|
<form id="graph-filter" class="search"
|
|
hx-get="/graph"
|
|
hx-target="main"
|
|
hx-select="main"
|
|
hx-swap="outerHTML"
|
|
hx-trigger="change from:select, change from:input[type=checkbox], keyup changed delay:200ms from:input[name=q]"
|
|
hx-push-url="true">
|
|
<input type="search" name="q" value="{{.Filter.Q}}" placeholder="search…" autocomplete="off">
|
|
<label>tag
|
|
<select name="tag" multiple size="3">
|
|
{{$sel := .Filter.Tags}}
|
|
{{range .AllTags}}<option value="{{.}}" {{if contains $sel .}}selected{{end}}>{{.}}</option>{{end}}
|
|
</select>
|
|
</label>
|
|
<label>mgmt
|
|
<select name="mgmt" multiple size="4">
|
|
{{$selM := .Filter.Management}}
|
|
<option value="mai" {{if contains $selM "mai"}}selected{{end}}>mai</option>
|
|
<option value="self" {{if contains $selM "self"}}selected{{end}}>self</option>
|
|
<option value="external" {{if contains $selM "external"}}selected{{end}}>external</option>
|
|
<option value="unmanaged"{{if contains $selM "unmanaged"}}selected{{end}}>unmanaged</option>
|
|
</select>
|
|
</label>
|
|
<label class="checkbox">
|
|
<input type="checkbox" name="isolate" value="1" {{if .Isolate}}checked{{end}}>
|
|
isolate (hide non-matches)
|
|
</label>
|
|
{{if .Filter.Active}}<a class="clear" href="/graph">clear filters</a>{{end}}
|
|
<a class="download" href="/graph?download=svg">download SVG</a>
|
|
</form>
|
|
</section>
|
|
|
|
<section class="graph-canvas">
|
|
{{template "graph-svg" .P}}
|
|
</section>
|
|
|
|
<section class="graph-legend muted">
|
|
<span class="legend-key key-mai">mai</span>
|
|
<span class="legend-key key-self">self</span>
|
|
<span class="legend-key key-external">external</span>
|
|
<span class="legend-key key-mixed">mixed</span>
|
|
<span class="legend-key key-unmanaged">unmanaged</span>
|
|
· status opacity: active 1.0 · done 0.6 · archived 0.3
|
|
</section>
|
|
{{end}}
|