custom-ui-page

command
v0.1.3 Latest Latest
Warning

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

Go to latest
Published: Oct 1, 2026 License: Apache-2.0 Imports: 9 Imported by: 0

README

The custom-ui-page example

Stock CandaceOS Core with one page added: a sidebar entry, and something of your own behind it.

This is the smallest useful shape of the operator UI's extension seams, and it is deliberately not a rebrand. The identity, the palette, the templates, and every string in the shipped pages are exactly Core's — the only difference an operator sees is a fifth entry in the sidebar.


Run it

A complete Core needs what Core needs: a PostgreSQL URL, a writable data directory and workspace, a Warden URL, and a harness selection. Nothing here adds a setting of its own.

go run ./examples/custom-ui-page

The suite needs none of that — it mounts the same two values on the same engine Core builds, with no database and no container:

go test ./examples/custom-ui-page

The two options

Three declarations are the whole extension. This is main.go and the two halves of runbooks.go it names, with the comments trimmed:

// the sidebar entry
var entry = webui.NavItem{Label: "Runbooks", Href: runbookPath, Glyph: "▤"}

// the service behind it: no fields, because it keeps no state
type runbooks struct{}

func (runbooks) Register(router gin.IRouter) {
	router.GET(runbookPath, func(c *gin.Context) {
		c.Data(http.StatusOK, "text/html; charset=utf-8", runbookPage)
	})
}

// the composition root
func main() {
	err := bootstrap.Run(version,
		bootstrap.WithNavItem(entry),
		bootstrap.WithHTTPService(runbooks{}),
	)
	...
}

WithNavItem appends one entry to the operator sidebar, after Core's Home, Apps, Fleet, and Activity. It renders with the same markup, keyboard behavior, and aria semantics as those four. Label and Href are required; Glyph is decorative and aria-hidden, so it carries nothing the label does not. Both strings are bounded, control-character free, and escaped, and a target the browser must not follow is neutralized by the template rather than emitted. An entry that cannot be rendered as one labeled link fails assembly before any infrastructure is opened.

View is left unset here, which makes this a plain link. The built-in entries each name a section the operator page renders and switch to it in place; there is no such section for a page served from somewhere else.

WithHTTPService mounts anything that can register routes on a gin.IRouter onto the one engine Core builds. That engine has already applied Core's security headers, so the handler adds a content type and its bytes and nothing else — a mounted page inherits Core's posture and does not get to loosen it.

Core's own routes, including the /claws/... paths, are untouched by either option.

Two details worth copying

One constant for the link and the route. The sidebar entry's Href and the registered path are the same constant, because nothing checks that they agree.

Link the brand stylesheet anyway. The page links app.css and the generated brand stylesheet, both through browserroutes rather than literal URLs. Under the stock identity that second stylesheet is empty, so linking it costs nothing — and it is what keeps this page in step on the day the product is rebranded.

Next

  • examples/custom-brand — the same two options plus WithBrand and WithUIOverlay, which between them replace the two brand-bearing names, the wordmark, the palette, and any presentation file the overlay names.
  • services/candaceos/webui — the package documentation is the contract: the overridable template blocks, the data each receives, and what a caller may rely on.

Documentation

Overview

Command custom-ui-page is stock CandaceOS Core with one page added.

It is the smallest useful shape of the operator UI's extension seams, and it is deliberately not a rebrand: the identity, the palette, the templates, and every string in the shipped pages are exactly Core's. Two options are all it takes to put a page of your own inside that shell.

bootstrap.WithNavItem      one entry in the operator sidebar
bootstrap.WithHTTPService  the page that entry links to

The entry renders with the same markup, keyboard behavior, and aria semantics as the shipped Home, Apps, Fleet, and Activity, and it lands after them. The page is mounted on the same Gin engine Core builds, so it inherits Core's security headers, and Core's own routes — including the /claws/... paths — are untouched.

If you also want a different name, mark, or palette, examples/custom-brand adds bootstrap.WithBrand and bootstrap.WithUIOverlay to these two.

Running it

This is a complete Core and needs what Core needs: a PostgreSQL URL, a writable data directory and workspace, a Warden URL, and a harness selection. Nothing here adds a setting of its own.

go run ./examples/custom-ui-page

Jump to

Keyboard shortcuts

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