sink

package
v1.0.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 31, 2026 License: Apache-2.0 Imports: 12 Imported by: 0

Documentation

Overview

Package sink provides output format renderers for tower visualizations.

Overview

A "sink" transforms a computed layout.Layout into a final output format. This package provides renderers for:

  • SVG: Scalable vector graphics with interactivity
  • PDF: Print-ready output (requires rsvg-convert)
  • PNG: Raster image output (requires rsvg-convert)

SVG Output

RenderSVG produces interactive SVG with:

  • Visual styles (hand-drawn XKCD-style or clean simple style)
  • Hover highlighting of related blocks
  • Optional popups showing package metadata
  • Optional "Nebraska guy" ranking panel
  • Optional dependency edge visualization

Basic usage:

svg := sink.RenderSVG(layout,
    sink.WithGraph(g),
    sink.WithStyle(handdrawn.New(seed)),
    sink.WithPopups(),
)

SVG Options

PDF and PNG Output

RenderPDF and RenderPNG render the layout as PDF/PNG by first generating SVG, then converting via render.ToPDF and render.ToPNG:

pdf, err := sink.RenderPDF(layout, opts...)
png, err := sink.RenderPNG(layout, sink.WithScale(2), opts...)

These require librsvg to be installed:

  • macOS: brew install librsvg
  • Linux: apt install librsvg2-bin

The conversion functions are shared with [nodelink] so both visualization types can export to PDF/PNG.

render.ToPDF: github.com/matzehuels/stacktower/pkg/core/render.ToPDF render.ToPNG: github.com/matzehuels/stacktower/pkg/core/render.ToPNG [nodelink]: github.com/matzehuels/stacktower/pkg/core/render/nodelink

Adding New Formats

To add a new output format:

  1. Create a renderer function: func RenderFoo(l layout.Layout, opts ...FooOption) ([]byte, error)
  2. Define option types for configuration
  3. Access l.Blocks for positioned blocks, l.RowOrders for orderings
  4. Register in internal/cli/render.go for CLI support

The existing sinks provide examples: svg.go for full-featured output, pdf.go/png.go for format conversion wrappers.

layout.Layout: github.com/matzehuels/stacktower/pkg/core/render/tower/layout.Layout styles.Simple: github.com/matzehuels/stacktower/pkg/core/render/tower/styles.Simple [handdrawn.New]: github.com/matzehuels/stacktower/pkg/core/render/tower/styles/handdrawn.New

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func RenderPDF

func RenderPDF(l layout.Layout, opts ...PDFOption) ([]byte, error)

RenderPDF renders the layout as PDF via SVG conversion. Requires librsvg: brew install librsvg (macOS), apt install librsvg2-bin (Linux).

func RenderPNG

func RenderPNG(l layout.Layout, opts ...PNGOption) ([]byte, error)

RenderPNG renders the layout as PNG via SVG conversion. Requires librsvg: brew install librsvg (macOS), apt install librsvg2-bin (Linux).

func RenderSVG

func RenderSVG(l layout.Layout, opts ...SVGOption) []byte
Example
package main

import (
	"fmt"
	"strings"

	"github.com/matzehuels/stacktower/pkg/core/dag"
	"github.com/matzehuels/stacktower/pkg/core/render/tower/layout"
	"github.com/matzehuels/stacktower/pkg/core/render/tower/sink"
)

func main() {
	// Build a simple dependency graph
	g := dag.New(nil)
	_ = g.AddNode(dag.Node{ID: "app", Row: 0})
	_ = g.AddNode(dag.Node{ID: "lib", Row: 1})
	_ = g.AddNode(dag.Node{ID: "core", Row: 2})
	_ = g.AddEdge(dag.Edge{From: "app", To: "lib"})
	_ = g.AddEdge(dag.Edge{From: "lib", To: "core"})

	// Compute layout
	l := layout.Build(g, 400, 300)

	// Render to SVG
	svg := sink.RenderSVG(l, sink.WithGraph(g))

	fmt.Println("SVG starts with:", string(svg[:4]))
	fmt.Println("Contains viewBox:", strings.Contains(string(svg), "viewBox"))
}
Output:
SVG starts with: <svg
Contains viewBox: true
Example (WithEdges)
package main

import (
	"fmt"

	"github.com/matzehuels/stacktower/pkg/core/dag"
	"github.com/matzehuels/stacktower/pkg/core/render/tower/layout"
	"github.com/matzehuels/stacktower/pkg/core/render/tower/sink"
)

func main() {
	g := dag.New(nil)
	_ = g.AddNode(dag.Node{ID: "app", Row: 0})
	_ = g.AddNode(dag.Node{ID: "auth", Row: 1})
	_ = g.AddNode(dag.Node{ID: "cache", Row: 1})
	_ = g.AddEdge(dag.Edge{From: "app", To: "auth"})
	_ = g.AddEdge(dag.Edge{From: "app", To: "cache"})

	l := layout.Build(g, 400, 300)

	// Enable edge rendering to show dependency lines
	svg := sink.RenderSVG(l,
		sink.WithGraph(g),
		sink.WithEdges(),
	)

	// Edges are rendered as dashed lines
	fmt.Println("Has content:", len(svg) > 100)
}
Output:
Has content: true
Example (WithPopups)
package main

import (
	"fmt"
	"strings"

	"github.com/matzehuels/stacktower/pkg/core/dag"
	"github.com/matzehuels/stacktower/pkg/core/render/tower/layout"
	"github.com/matzehuels/stacktower/pkg/core/render/tower/sink"
)

func main() {
	g := dag.New(nil)
	_ = g.AddNode(dag.Node{
		ID:  "fastapi",
		Row: 0,
		Meta: dag.Metadata{
			"version":          "0.100.0",
			"repo_description": "FastAPI framework",
			"repo_stars":       70000,
		},
	})
	_ = g.AddNode(dag.Node{ID: "starlette", Row: 1})
	_ = g.AddEdge(dag.Edge{From: "fastapi", To: "starlette"})

	l := layout.Build(g, 400, 300)

	// Enable hover popups showing package metadata
	svg := sink.RenderSVG(l,
		sink.WithGraph(g),
		sink.WithPopups(),
	)

	// Popups include metadata from nodes
	fmt.Println("Has popup content:", strings.Contains(string(svg), "popup"))
}
Output:
Has popup content: true
Example (WithStyle)
package main

import (
	"fmt"

	"github.com/matzehuels/stacktower/pkg/core/dag"
	"github.com/matzehuels/stacktower/pkg/core/render/tower/layout"
	"github.com/matzehuels/stacktower/pkg/core/render/tower/sink"
	"github.com/matzehuels/stacktower/pkg/core/render/tower/styles"
)

func main() {
	g := dag.New(nil)
	_ = g.AddNode(dag.Node{ID: "app", Row: 0})
	_ = g.AddNode(dag.Node{ID: "lib", Row: 1})
	_ = g.AddEdge(dag.Edge{From: "app", To: "lib"})

	l := layout.Build(g, 400, 300)

	// Use simple style (clean, minimal appearance)
	svg := sink.RenderSVG(l,
		sink.WithGraph(g),
		sink.WithStyle(styles.Simple{}),
	)

	fmt.Println("Generated SVG length:", len(svg) > 0)
}
Output:
Generated SVG length: true

Types

type PDFOption

type PDFOption func(*pdfRenderer)

PDFOption configures PDF rendering.

func WithPDFSVGOptions

func WithPDFSVGOptions(opts ...SVGOption) PDFOption

WithPDFSVGOptions passes options through to the underlying SVG renderer.

type PNGOption

type PNGOption func(*pngRenderer)

PNGOption configures PNG rendering.

func WithPNGSVGOptions

func WithPNGSVGOptions(opts ...SVGOption) PNGOption

WithPNGSVGOptions passes options through to the underlying SVG renderer.

func WithScale

func WithScale(s float64) PNGOption

WithScale sets the PNG scale factor (default 2.0 for 2x resolution).

type SVGOption

type SVGOption func(*svgRenderer)

func WithEdges

func WithEdges() SVGOption

func WithGraph

func WithGraph(g *dag.DAG) SVGOption

func WithMerged

func WithMerged() SVGOption

func WithNebraska

func WithNebraska(rankings []feature.NebraskaRanking) SVGOption

func WithPopups

func WithPopups() SVGOption

func WithStyle

func WithStyle(s styles.Style) SVGOption

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL