This document describes the internal architecture of TSC Bridge for contributors who want to understand the codebase, fix bugs, or write drivers.
TSC Bridge is a single Go binary that embeds an HTML dashboard and runs three subsystems concurrently:
- HTTP Server -- serves the API and dashboard on localhost
- System Tray -- native OS tray icon with context menu
- Native Window -- platform-specific dashboard window (WKWebView on macOS, webview_go on Windows/Linux, browser fallback)
graph TD
M[main.go] --> T[tray.go<br/>System tray lifecycle]
M --> H[HTTP Server<br/>net/http on 127.0.0.1:PORT]
M --> W[webview_*.go<br/>Native window]
M --> CFG[config.go<br/>Configuration]
M --> AUTH[auth.go<br/>Backend authentication]
H --> S[/status]
H --> P[/printers]
H --> PR[/print]
H --> BP[/batch-pdf]
H --> BT[/batch-tspl]
H --> D[/dashboard]
H --> O[/output/]
H --> TM[/templates]
H --> ETC[... 40+ endpoints]
The project uses a flat package structure. All Go files are in package main.
Platform-specific code is separated by build tags.
| File | Responsibility |
|---|---|
main.go |
Entry point, HTTP router, server lifecycle |
config.go |
Read/write configuration, environment detection |
auth.go |
Token management, backend authentication |
api_client.go |
HTTP client for the backend API |
network.go |
Network utilities, device discovery |
tls.go |
Self-signed certificate generation |
crypto.go |
AES-256 encryption for stored credentials |
| File | Responsibility |
|---|---|
pdf_renderer.go |
Renders label templates to PDF pages |
tspl_renderer.go |
Renders label templates to TSPL2 commands |
label_template.go |
Label template data structures and parsing |
presets.go |
Built-in label size presets |
batch.go |
Batch job management (Excel/CSV to labels) |
excel.go |
Excel file parsing |
| File | Platforms | Responsibility |
|---|---|---|
webview_darwin.go |
macOS | Native WKWebView via CGO |
webview.go |
Windows, Linux | webview_go library |
webview_stub.go |
Cross-build | Browser-only fallback |
printer_windows.go |
Windows | Win32 raw printing |
printer_other.go |
macOS, Linux | libusb + CUPS printing |
printer_crossbuild.go |
Cross-build | CUPS-only (no libusb) |
driver_darwin.go |
macOS | TSC driver detection via IOKit |
driver_windows.go |
Windows | TSC driver detection via Registry |
driver_other.go |
Linux | Stub |
dpi_darwin.go |
macOS | DPI query via CUPS PPD |
dpi_windows.go |
Windows | DPI query via WMI |
dpi_other.go |
Linux | Stub |
autostart_darwin.go |
macOS | LaunchAgent plist |
autostart_windows.go |
Windows | Registry run key |
autostart_other.go |
Linux | Stub |
icon_darwin.go |
macOS | Dock icon via NSImage |
icon_windows.go |
Windows | Taskbar icon |
icon_other.go |
Linux | No-op |
The file dashboard.html (embedded via go:embed) contains the entire
dashboard UI: HTML, CSS, and JavaScript in a single file. It uses Bootstrap 5
and vanilla JavaScript.
The dashboard has five tabs:
- Dashboard -- printer status, quick print, connection info
- Designer -- interactive label designer with drag-and-drop
- Batch -- import Excel, map columns, bulk print
- Templates -- manage local and server-side templates
- Settings -- printer selection, DPI, backend connection
| Tag | Purpose |
|---|---|
darwin |
macOS-specific code (CGO required) |
windows |
Windows-specific code |
!darwin && !windows |
Linux/other platforms |
crossbuild |
Cross-compilation without platform SDK headers |
The crossbuild tag disables libusb and webview_go, producing a binary that
uses CUPS for printing and the system browser for the dashboard.
Configuration lives in ~/.tsc-bridge/config.json. The bridge creates this
directory on first run. Key settings:
port-- HTTP server port (default: 9638)printer-- default printer namedpi-- printer DPI (auto-detected or manual)backend-- backend API URL for template syncwhitelabel-- custom brandingtls-- TLS certificate settingscors-- allowed CORS origins
When a print job arrives:
graph TD
REQ[JSON request] --> PARSE[Parse template<br/>label_template.go]
PARSE --> VARS[Resolve variables<br/>enrichRowForVariables]
VARS --> PDF[PDF path<br/>RenderBulkPDF → gopdf → .pdf]
VARS --> TSPL[TSPL path<br/>RenderBulkTSPL → TSPL2 → rawPrint]
VARS --> ZPL[Future: ZPL path<br/>RenderBulkZPL → ZPL → rawPrint]
Label templates use field names like {nombre} or {product_code}. When
rendering, the bridge matches these variables against the row data using:
- Exact match:
row["nombre"] - Suffix match:
row["gafete_nombre"]matches variablenombre - Normalized match: dots and underscores are interchangeable
This is implemented in enrichRowForVariables() and
enrichRowForPlaceholders() in pdf_renderer.go.
The current renderers (tspl_renderer.go, pdf_renderer.go) are tightly
coupled to the main package. The planned driver architecture will extract a
clean interface:
// Driver renders labels in a printer-specific language.
type Driver interface {
// Name returns the driver identifier (e.g., "tspl", "zpl", "epl").
Name() string
// Languages returns the label languages this driver supports.
Languages() []string
// Render converts a parsed label template and row data into
// printer-ready commands.
Render(template *LabelTemplate, row map[string]string, opts RenderOpts) ([]byte, error)
// Capabilities returns what this driver supports.
Capabilities() DriverCapabilities
}
type DriverCapabilities struct {
Barcodes []string // Supported barcode types
QRCode bool
Images bool
TrueType bool // TrueType font embedding
Rotation []int // Supported rotation angles
MaxDPI int
}
type RenderOpts struct {
DPI int
Copies int
Mode string // "print", "preview", "raster"
}See docs/DRIVERS.md for the full driver development guide.
Tests are in *_test.go files alongside the code they test:
go test ./...Key test files:
config_test.go-- configuration read/write, DPI round-tripauth_test.go-- authentication state managementdpi_test.go-- DPI detection parsing
When writing a driver, use the test harness described in docs/DRIVERS.md to validate output without a physical printer.