Documentation
¶
Overview ¶
Package plugin is the hope plugin SDK: build a small JSON-RPC server that hope discovers across a fleet and renders in its UI. A plugin is your container, your language, your endpoint — hope dials in. Extend hope without joining it.
Minimal plugin:
p := plugin.New("badge-directory", "1.0.0").Icon("database")
p.View("counts", "Counts", plugin.KV, func(ctx context.Context) (any, error) {
return map[string]any{"users": 1402301, "badges": 88123}, nil
})
log.Fatal(p.ListenAndServe(":8080")) // serves JSON-RPC 2.0 at /__hope
The container then declares the labels hope scans for:
hope.plugin=true hope.plugin.port=8080 hope.plugin.path=/__hope
Index ¶
- Constants
- Variables
- func Input(ctx context.Context) string
- func NewError(code int, msg string) error
- func Params(ctx context.Context, v any) error
- func SearchQuery(ctx context.Context) string
- type ActionDesc
- type ActionFunc
- type ActionOpt
- type Capabilities
- type Card
- type CardField
- type CardsData
- type Cell
- func Badge(value, tone string) Cell
- func Code(value string) Cell
- func DetailLink(value, pageID, arg string) Cell
- func ExternalLink(value, href string) Cell
- func Image(src, alt string, opts ...ImageOpt) Cell
- func Link(value, to string) Cell
- func Number(n any, unit string) Cell
- func Progress(frac float64) Cell
- func Time(unix int64) Cell
- type ChartData
- type ChartSeries
- type Comp
- func Box(children ...*Comp) *Comp
- func CCard(title string, body ...*Comp) *Comp
- func CCell(cell Cell) *Comp
- func CGrid(children ...*Comp) *Comp
- func CIcon(name string) *Comp
- func CRow(children ...*Comp) *Comp
- func CTable(data *TableData) *Comp
- func CText(s string) *Comp
- func CTree(nodes ...TreeNode) *Comp
- func Divider() *Comp
- func Heading(text string, level int) *Comp
- func KeyVal(label string, value any) *Comp
- func Spacer(px int) *Comp
- func Sparkline(vals ...float64) *Comp
- func Stack(children ...*Comp) *Comp
- func TStep(label string, body ...*Comp) *Comp
- func Timeline(steps ...*Comp) *Comp
- func (c *Comp) At(value any) *Comp
- func (c *Comp) Current() *Comp
- func (c *Comp) Done() *Comp
- func (c *Comp) Gapped(px int) *Comp
- func (c *Comp) Help(text string, pos ...TipPos) *Comp
- func (c *Comp) Ico(name string) *Comp
- func (c *Comp) Pending() *Comp
- func (c *Comp) Sub(text string) *Comp
- func (c *Comp) Toned(tone string) *Comp
- func (c *Comp) Weight(n int) *Comp
- type CompKind
- type ConfirmFunc
- type ConfirmResult
- type Contribution
- type Crumb
- type EmitFunc
- type EmptyOpt
- type EmptyState
- type Event
- type EventFunc
- type Facet
- type Field
- type FieldError
- type FieldType
- type FieldsFunc
- type GraphData
- type GraphEdge
- type GraphMenuItem
- type GraphNode
- func (n *GraphNode) Act(actions ...RowAction) *GraphNode
- func (n *GraphNode) At(x, y float64) *GraphNode
- func (n *GraphNode) Form(fields ...Field) *GraphNode
- func (n *GraphNode) Ico(name string) *GraphNode
- func (n *GraphNode) InPorts(ports ...Port) *GraphNode
- func (n *GraphNode) OutPorts(ports ...Port) *GraphNode
- func (n *GraphNode) Stated(s string) *GraphNode
- func (n *GraphNode) Toned(tone string) *GraphNode
- func (n *GraphNode) Typed(t string) *GraphNode
- func (n *GraphNode) Wide(px int) *GraphNode
- func (n *GraphNode) With(data map[string]any) *GraphNode
- func (n *GraphNode) WithMeta(label, value string) *GraphNode
- type GraphOpt
- func GraphAdd(method string) GraphOpt
- func GraphConfig(method string) GraphOpt
- func GraphConnect(method string) GraphOpt
- func GraphDelete(method string) GraphOpt
- func GraphDirected() GraphOpt
- func GraphDisconnect(method string) GraphOpt
- func GraphMenu(method string) GraphOpt
- func GraphMove(method string) GraphOpt
- func GraphNodeFlyout(method string) GraphOpt
- func GraphPalette(method string) GraphOpt
- func GraphRun(streamMethod string) GraphOpt
- func GraphSelect(method string) GraphOpt
- func GraphSidebar(method string) GraphOpt
- func GraphSidebarActions(methods ...string) GraphOpt
- func GraphSnap(px int) GraphOpt
- func GraphToolbar(method string) GraphOpt
- func GraphTypeInfo(method string) GraphOpt
- func GraphValidateConnect(method string) GraphOpt
- func StaticPalette(types ...NodeType) GraphOpt
- type Hope
- type ImageOpt
- type InitContext
- type Item
- type KVData
- type Layout
- type Match
- type Node
- type NodeKind
- type NodeMeta
- type NodeType
- type Option
- type OptionsFunc
- type Page
- type PageItem
- type Permission
- type Plugin
- func (p *Plugin) Action(method, label string, fields []Field, fn ActionFunc, opts ...ActionOpt) *Plugin
- func (p *Plugin) Alert(ctx context.Context, severity, title, detail, dedupeKey string) error
- func (p *Plugin) Breadcrumbs(crumbs ...Crumb) *Plugin
- func (p *Plugin) CardsView(method, label string, fn ViewFunc, opts ...ViewOpt) *Plugin
- func (p *Plugin) ChartView(method, label string, fn ViewFunc, opts ...ViewOpt) *Plugin
- func (p *Plugin) ComponentView(method, label string, fn ViewFunc, opts ...ViewOpt) *Plugin
- func (p *Plugin) Confirm(method string, fn ConfirmFunc) *Plugin
- func (p *Plugin) ContainerPanel(title string, match *Match, node *Node) *Plugin
- func (p *Plugin) Contribute(c Contribution) *Plugin
- func (p *Plugin) DangerAction(method, label string, fields []Field, fn ActionFunc, opts ...ActionOpt) *Plugin
- func (p *Plugin) DashboardWidget(title string, node *Node) *Plugin
- func (p *Plugin) Description(s string) *Plugin
- func (p *Plugin) DetailPage(id, title, paramKey string, node *Node) *Plugin
- func (p *Plugin) DynamicPage(title string, node *Node, items []PageItem) *Plugin
- func (p *Plugin) DynamicPageFunc(title string, node *Node, fn func(ctx context.Context) []PageItem) *Plugin
- func (p *Plugin) Fields(method string, fn FieldsFunc) *Plugin
- func (p *Plugin) GraphView(method, label string, fn ViewFunc, opts ...GraphOpt) *Plugin
- func (p *Plugin) Handler() http.Handler
- func (p *Plugin) HeaderActions(refs ...string) *Plugin
- func (p *Plugin) Hope() *Hope
- func (p *Plugin) Icon(name string) *Plugin
- func (p *Plugin) Icons(m map[string]string) *Plugin
- func (p *Plugin) ListenAndServe(addr string) error
- func (p *Plugin) MaxBodyBytes(n int64) *Plugin
- func (p *Plugin) OnEvent(fn EventFunc) *Plugin
- func (p *Plugin) OnInit(fn func(ctx context.Context, in InitContext) error) *Plugin
- func (p *Plugin) OnStatus(fn func(ctx context.Context) StatusReport) *Plugin
- func (p *Plugin) Options(method string, fn OptionsFunc) *Plugin
- func (p *Plugin) Page(title string, node *Node) *Plugin
- func (p *Plugin) PageID(id string) *Plugin
- func (p *Plugin) PagedView(method, label string, fn ViewFunc, opts ...ViewOpt) *Plugin
- func (p *Plugin) Path(path string) *Plugin
- func (p *Plugin) Publish(ctx context.Context, e Event) error
- func (p *Plugin) QueryView(method, label, lang, def string, fn ViewFunc, opts ...ViewOpt) *Plugin
- func (p *Plugin) RequestPermission(ctx context.Context, scope, reason string) error
- func (p *Plugin) RequirePermission(scope, reason string) *Plugin
- func (p *Plugin) Resolve(method string, fn ResolveFunc) *Plugin
- func (p *Plugin) ResolveAlert(ctx context.Context, title, dedupeKey string) error
- func (p *Plugin) SearchView(method, label string, fn ViewFunc) *Plugin
- func (p *Plugin) Setting(s Setting) *Plugin
- func (p *Plugin) SettingValue(key string) string
- func (p *Plugin) StackWidget(title string, match *Match, node *Node) *Plugin
- func (p *Plugin) StatView(method, label string, fn ViewFunc, opts ...ViewOpt) *Plugin
- func (p *Plugin) Storage() *Storage
- func (p *Plugin) Stream(method, label string, kind StreamKind, fn StreamFunc) *Plugin
- func (p *Plugin) Subtitle(s string) *Plugin
- func (p *Plugin) TableView(method, label string, fn ViewFunc, opts ...TableOpt) *Plugin
- func (p *Plugin) TextView(method, label string, fn ViewFunc, opts ...ViewOpt) *Plugin
- func (p *Plugin) Token(token string) *Plugin
- func (p *Plugin) Validate(method string, fn ValidateFunc) *Plugin
- func (p *Plugin) View(method, label string, kind ViewKind, fn ViewFunc, opts ...ViewOpt) *Plugin
- type Port
- type ResolveFunc
- type RowAction
- type Schema
- type SearchData
- type SearchItem
- type Setting
- type SettingKind
- type SortSpec
- type StatBlock
- type StatData
- type StatusReport
- type Storage
- type StreamDesc
- type StreamFunc
- type StreamKind
- type Surface
- type TableData
- type TableOpt
- func DefaultSort(column, dir string) TableOpt
- func Editable(method string, columns ...string) TableOpt
- func Facets(facets ...Facet) TableOpt
- func HScroll() TableOpt
- func NoFilter() TableOpt
- func NoSort() TableOpt
- func PageSize(n int) TableOpt
- func RowActions(actions ...RowAction) TableOpt
- func RowDetail(method string) TableOpt
- func RowDetailButton(method string) TableOpt
- func RowFlyout(method string) TableOpt
- func RowFlyoutRefresh(seconds int) TableOpt
- func RowFlyoutWidth(width string) TableOpt
- func ServerSide() TableOpt
- type TableQuery
- type TableSort
- type TextData
- type TipPos
- type Tooltip
- type TreeData
- type TreeNode
- type ValidateFunc
- type ViewDesc
- type ViewFunc
- type ViewKind
- type ViewOpt
- type WizardStep
Constants ¶
const ( ToneOK = "ok" ToneWarn = "warn" ToneBad = "bad" ToneInfo = "info" )
Tone names a semantic color for a Badge (matches hope's ok/warn/bad/info).
const ( StatusOK = "ok" StatusInfo = "info" StatusWarn = "warn" StatusError = "error" )
Advisory status levels for StatusReport.Level. hope colors the reported status by this and otherwise doesn't interpret the report.
const ( ScopeEventsSubscribe = "events:subscribe" // receive hope events via OnEvent ScopeEventsPublish = "events:publish" // publish events onto hope's bus ScopeStorage = "storage" // durable per-install KV (p.Storage) ScopeSpecLabel = "spec:label" // add/update a service label in the plugin's own stack (p.Hope().AddServiceLabel) )
Reverse-capability scopes a plugin may request. Plain strings so older/newer peers interoperate — an unknown scope is simply never granted. Additive: publish/storage/ action scopes land with their phases.
const ProtocolVersion = 1
ProtocolVersion is the plugin-protocol version this SDK speaks. hope sends its own; a mismatch degrades gracefully (unknown surfaces/kinds are skipped) rather than breaking, so a new plugin still works against an older hope and vice-versa.
Variables ¶
var ErrNoReverseChannel = errors.New("hope reverse channel unavailable (no callback URL from hope.init)")
ErrNoReverseChannel is returned by Publish/Storage when hope hasn't provided a callback URL (an older hope, or one without [plugins] callback_url configured). The plugin still runs; it just can't call back into hope.
Functions ¶
func Input ¶
Input returns the "input" string from a Query view's params (the text the user typed into the query box), or "" if absent.
func NewError ¶
NewError builds an *rpcError a handler can return to control the JSON-RPC error code sent to hope (e.g. invalid query -> codeInvalidArgs).
func Params ¶
Params unmarshals the current call's JSON-RPC params into v. Use inside an action/view/stream handler to read structured input.
func SearchQuery ¶
SearchQuery reads the text a Search (autocomplete) view was called with (the "q" param). Empty when there's no query yet — return no items for that.
Types ¶
type ActionDesc ¶
type ActionDesc struct {
Method string `json:"method"`
Label string `json:"label"`
Icon string `json:"icon,omitempty"`
Danger bool `json:"danger,omitempty"`
Fields []Field `json:"fields,omitempty"`
// Steps turns the action's form into a WIZARD: hope renders each step's fields in
// order with Back/Next/Finish and a stepper. Values accumulate across steps, so a
// later step's cascading options / conditional fields read earlier answers. Set via
// ActionSteps; leave Fields empty when using Steps. The action receives the merged
// values from every step, exactly like a flat form.
Steps []WizardStep `json:"steps,omitempty"`
Tip *Tooltip `json:"tip,omitempty"` // hover tooltip on the action button (set with ActionTip)
// Prefill seeds the form's initial values by field Key, merged OVER the invoking
// context (the clicked row's columns + the page params) — so commanding from a row
// pre-selects its target and an explicit Prefill can override or add to that. The
// operator can still edit; the submitted value wins. Set with ActionPrefill.
Prefill map[string]string `json:"prefill,omitempty"`
// ValidateMethod names a Validate provider (registered with Plugin.Validate). hope
// calls it with the current values as they change; it returns per-field errors that
// render inline and disable Run until the form is valid. Set with ActionValidate.
ValidateMethod string `json:"validateMethod,omitempty"`
// ConfirmMethod names a Confirm provider (registered with Plugin.Confirm). After the
// form (and any Danger gate), hope calls it with the entered values to compute a
// go/no-go impact confirmation; the operator must approve before the action runs. Set
// with ActionConfirm.
ConfirmMethod string `json:"confirmMethod,omitempty"`
}
ActionDesc describes an invocable action (a mutation). Danger flags a destructive action so hope confirms before running it.
type ActionFunc ¶
ActionFunc runs an action with the UI-collected field values.
type ActionOpt ¶ added in v0.0.3
type ActionOpt func(*ActionDesc)
ActionOpt configures a registered action beyond its label (its icon, …). Pass these to Action/DangerAction; the danger tone is set by DangerAction itself.
func ActionConfirm ¶ added in v0.5.0
ActionConfirm points the action at a Confirm provider (registered with Plugin.Confirm) (#6). hope shows the computed impact confirmation after the form and runs the action only if the operator approves. Combine with DangerAction for a destructive-styled gate.
func ActionIcon ¶ added in v0.0.3
ActionIcon sets the action button's icon — a hope built-in icon name or one of this plugin's Icons keys. e.g. plugin.ActionIcon("rotate").
func ActionPrefill ¶ added in v0.5.0
ActionPrefill seeds the form's initial values by field Key, merged OVER the invoking context (the clicked row's columns + the page params) (#3). Commanding from a row already prefills matching keys; use this to add or override. The operator can still edit.
func ActionSteps ¶ added in v0.5.0
func ActionSteps(steps ...WizardStep) ActionOpt
ActionSteps turns an action's form into a multi-step WIZARD (see ActionDesc.Steps). Pass nil for the action's flat fields when using steps. Values accumulate across steps — a later step's OptionsMethod/DependsOn reads the earlier answers via Params(ctx).
func ActionTip ¶ added in v0.0.4
ActionTip sets a hover tooltip on the action button explaining what it does, with an optional placement (see Tip). e.g. plugin.ActionTip("Refresh stats", plugin.TipBottom).
func ActionValidate ¶ added in v0.5.0
ActionValidate points the action at a Validate provider (registered with Plugin.Validate) (#4). hope calls it as the values change and disables Run while it returns field errors.
type Capabilities ¶ added in v0.0.6
type Capabilities struct {
ViewKinds []string `json:"view_kinds"`
Features []string `json:"features"`
}
Capabilities is what the connected hope advertised: the supported view kinds (kv/table/…/component) and feature flags (static, empty, flyout, status). Read it with Caps and query it with Supports.
func Caps ¶ added in v0.0.6
func Caps(ctx context.Context) Capabilities
Caps returns the capabilities the connected hope advertised for the current call. Use it to adapt output across hope versions (see the package example above). Outside a hope-driven call (no headers) it returns an empty Capabilities.
func (Capabilities) Supports ¶ added in v0.0.6
func (c Capabilities) Supports(name string) bool
Supports reports whether hope advertised the named capability — a view kind ("component") or a feature ("static", "empty"). False when hope sent no capabilities (an older build), so guard optional output and keep a baseline fallback.
type Card ¶
type Card struct {
Title string `json:"title"`
Subtitle string `json:"subtitle,omitempty"`
Icon string `json:"icon,omitempty"`
Tone string `json:"tone,omitempty"` // ok|warn|bad|info accent
To string `json:"to,omitempty"`
Image string `json:"image,omitempty"` // absolute http(s) URL -> a hero image at the card top
Fields []CardField `json:"fields,omitempty"`
}
Card is one tile in a Cards view. Fields render as a small label/value list (the values may be rich cells — Badge/Number/…). A non-empty To makes the card navigate on click (plugin-relative, like a Link cell — see DetailLink).
type CardsData ¶
type CardsData struct {
Items []Card `json:"items"`
}
CardsData is what a Cards view returns: a grid of cards.
type Cell ¶ added in v0.0.6
Cell is a rich table/field cell — a typed shape hope renders specially (a pill, a link, a progress bar, …). It's a type *alias* for map[string]any, so it stays a plain JSON object on the wire and every builder below (and any hand-built map) satisfies it with no conversion; the name just documents intent where a cell is expected (e.g. a Comp's Cell field, or CCell). Build one with Badge/Link/Number/Time/Progress/Code/Image.
func Badge ¶
Badge renders value as a colored pill. tone is one of the Tone* constants ("" = neutral).
func DetailLink ¶
DetailLink renders value as a link to one of this plugin's DetailPage ids, passing arg as the page's ParamKey — a master-detail link that needs no knowledge of the plugin's hope key. e.g. DetailLink("alice", "user", "42") -> the "user" detail page with param {<paramKey>: "42"}.
func ExternalLink ¶
ExternalLink renders value as a link that opens href in a new tab.
func Image ¶
Image renders src as an image (click opens the full image in a new tab). alt is the hover/accessible label. Unlike RPC calls, hope does NOT proxy image bytes — the browser loads src directly, so src MUST be an absolute http(s) URL reachable from the browser (e.g. a public on-demand webp/avif image proxy). A non-http(s) src renders as its alt text. Usable as a table cell or a stat/card/cards field value.
With no opts it's a small inline thumbnail. Control the render box with opts:
Image(u, alt) // default thumbnail
Image(u, alt, ImgW(240)) // 240px wide, height auto (keeps aspect)
Image(u, alt, ImgBox(110, 110)) // fixed 110×110 box, image centered, contained
Image(u, alt, ImgBox(110,110), ImgFit("cover")) // fill the box, cropping overflow
func Link ¶
Link renders value as an in-app link that navigates to a hope route `to` (e.g. a master-detail page). Use ExternalLink for an off-site URL.
func Number ¶
Number renders n right-formatted with thousands separators; unit ("" = none) is appended (e.g. "MB", "reqs").
type ChartData ¶
type ChartData struct {
Type string `json:"type,omitempty"`
Labels []string `json:"labels"`
Series []ChartSeries `json:"series"`
}
ChartData is what a Chart view returns: categorical labels on the x-axis and one or more named series of values. Type is "bar" (default) or "line". A line chart with one series and many points is a good time-series-at-rest view (use a stream for live). hope draws the axes, gridlines, legend, and scaling.
type ChartSeries ¶
ChartSeries is one named line/bar series; Values aligns with ChartData.Labels.
type Comp ¶ added in v0.0.6
type Comp struct {
Kind CompKind `json:"kind"`
Children []*Comp `json:"children,omitempty"` // containers
Text string `json:"text,omitempty"` // text/heading content
Label string `json:"label,omitempty"` // keyval label
Cell Cell `json:"cell,omitempty"` // cell primitive: a rich Cell
Value any `json:"value,omitempty"` // keyval value (a scalar or a rich Cell)
Level int `json:"level,omitempty"` // heading level 1..4
Tone string `json:"tone,omitempty"` // ok|warn|bad|info accent (text/heading/keyval/box)
Icon string `json:"icon,omitempty"` // icon primitive
Values []float64 `json:"values,omitempty"` // sparkline points
Gap int `json:"gap,omitempty"` // container child gap, px
Size int `json:"size,omitempty"` // row/grid child weight | spacer height px
Table *TableData `json:"table,omitempty"` // embedded table (CompTable)
Tree []TreeNode `json:"tree,omitempty"` // embedded node tree (CTree) — folders + navigable leaves
// Tip is an optional hover tooltip (an info icon) on a labeled node — a Heading, a
// KeyVal (by its key), or a Card title. Set with .Help(text). Lets a plugin attach
// explanatory help ANYWHERE it renders a custom surface, the same way table headers use
// ColumnTips and fields use Help.
Tip *Tooltip `json:"tip,omitempty"`
// State is a timeline step's progress: "done" | "current" | "pending" (default pending) —
// drives the dot fill and the row's emphasis. Set with .Done()/.Current()/.Pending().
State string `json:"state,omitempty"`
}
Comp is one node in a Component tree. Build nodes with the constructors below (Box/Stack/CRow/CGrid for containers; Heading/CText/KeyVal/CIcon/Sparkline/CCell for terminals) rather than filling this struct by hand. Unknown kinds are skipped by hope, never fatal, so a newer primitive degrades gracefully on an older hope.
func CCard ¶ added in v0.5.0
CCard builds a bordered card: a header (icon/title/subtitle + a tone stripe) over a body of child components — the good default "item" to render in a grid or a paged collection. C-prefixed (Card is already the data-card type). Chain .Ico/.Sub/.Toned for the header.
func CCell ¶ added in v0.0.6
CCell wraps any rich Cell (Badge/Link/Number/Time/Progress/Code/Image) as a standalone primitive, so the whole cell vocabulary works inside a Component tree.
func CIcon ¶ added in v0.0.6
CIcon builds a single icon (a hope built-in name or one of this plugin's Icons keys). C-prefixed to leave room for future top-level icon helpers.
func CRow ¶ added in v0.0.6
CRow builds a horizontal container (children laid out left-to-right). Named CRow (not Row) because Row already builds a layout Node; a Comp tree uses CRow.
func CTable ¶ added in v0.0.10
CTable embeds a full table inside a Component tree — column headers, aligned cells, ellipsis, DetailLink/Image cells, all rendered by hope's table renderer. Build the data with plugin.Table(...). Ideal for a compact list-with-structure inside a flyout/panel (e.g. the badges on a canvas) instead of hand-stacking rows.
func CText ¶ added in v0.0.6
CText builds a line/paragraph of text (auto-escaped by hope). C-prefixed because Text is already a ViewKind constant.
func CTree ¶ added in v0.6.0
CTree embeds a collapsible node tree inside a Component surface — folders with children and navigable leaves (a TreeNode with To navigates; To:"graph:<id>" selects a DAG in a graph sidebar). Ideal for a plugin-controlled navigator (the graph sidebar's DAG/folder browser).
func Divider ¶ added in v0.0.6
func Divider() *Comp
Divider builds a horizontal rule between sections of a tile.
func Heading ¶ added in v0.0.6
Heading builds a heading; level is clamped to 1..4 by hope (1 = largest).
func KeyVal ¶ added in v0.0.6
KeyVal builds a label + value line. value may be a plain scalar or a rich Cell (Badge/Number/…); the `any` mirrors Number's signature — hope renders a Cell specially and stringifies a scalar.
func Stack ¶ added in v0.0.6
Stack builds a tight vertical container — good for a run of KeyVal lines.
func TStep ¶ added in v0.5.2
TStep is one timeline row: Text is the label, and any body components render indented under it (in the content column, past the rail) — so a step can hold a detail box, a keyval, a table, whatever. Chain .Sub(detail), .At(value) for the right-aligned value (a timestamp, a Cell, any scalar), .Done()/.Current()/.Pending() for the state (dot fill + emphasis), and .Toned(tone) to override the dot/value color.
Custom states beyond done/current/pending are just a tone + an optional icon marker: the step reads as reached (emphasized) and the dot takes the tone color, or an icon replaces the dot entirely. The icon is a hope built-in name OR one of the plugin's OWN loaded SVGs (an Icons key), so a step's marker can be fully custom. e.g. a failure or a skip:
plugin.TStep("aborted").Ico("alert").Toned(plugin.ToneBad).At(plugin.Time(ts))
plugin.TStep("scanning").Ico("beaker").Toned(plugin.ToneInfo) // beaker = a plugin Icons key
plugin.TStep("skipped").Toned(plugin.ToneWarn)
func Timeline ¶ added in v0.5.2
Timeline builds a vertical timeline — a dot-and-connector rail of TStep rows, each with a label, optional detail, a right-aligned value, and a done/current/pending state. Ideal for a lifecycle / progress / audit trail (an order's queued -> uplinked -> completed).
plugin.Timeline(
plugin.TStep("queued").Current().At(plugin.Time(ts)),
plugin.TStep("uplinked").Pending(),
plugin.TStep("tasked").Sub("executing over target").Pending(),
)
func (*Comp) At ¶ added in v0.5.2
At sets a timeline step's right-aligned value — a timestamp (plugin.Time), a rich Cell (Badge/Link), or any scalar. Empty renders a dim placeholder.
func (*Comp) Done ¶ added in v0.5.2
Done / Current / Pending set a timeline step's state (default pending).
func (*Comp) Help ¶ added in v0.5.1
Help attaches a hover tooltip (an info icon) to this node — works on Heading, KeyVal, and Card-title nodes. Chainable: plugin.Heading("Throughput", 4).Help("bytes/sec over the last minute"). Optional placement like Tip.
func (*Comp) Ico ¶ added in v0.5.0
Ico sets a card's (or any node's) leading icon — a hope built-in name or an Icons key.
type CompKind ¶ added in v0.0.6
type CompKind string
CompKind names one primitive in a Component tree. Containers hold Children; terminals carry their own inline payload (text, a cell, values, an icon).
const ( CompBox CompKind = "box" // vertical container (a card/tile body) CompStack CompKind = "stack" // vertical container, tighter — labeled rows CompRow CompKind = "row" // horizontal container (children side by side) CompGrid CompKind = "grid" // responsive grid of children CompText CompKind = "text" // a line/paragraph of text CompHeading CompKind = "heading" // a heading (Level 1..4) CompDivider CompKind = "divider" // a horizontal rule CompSpacer CompKind = "spacer" // vertical gap of Size px CompKeyval CompKind = "keyval" // a Label + Value line (value may be a rich Cell) CompIcon CompKind = "icon" // a single icon (built-in name or an Icons key) CompSparkline CompKind = "sparkline" // a tiny inline line chart from Values CompCell CompKind = "cell" // any rich Cell (Badge/Link/Number/Time/Progress/Image) CompTable CompKind = "table" // an embedded table (rich cells, alignment, ellipsis) CompCard CompKind = "card" // a bordered card: header (icon/title/subtitle/tone stripe) + body children CompTimeline CompKind = "timeline" // a vertical timeline of TStep children (dots + connector + right value) CompTStep CompKind = "tstep" // one timeline step (Text label, Sub detail, .At value, state, tone) CompTree CompKind = "tree" // a collapsible node tree (folders); nodes navigate via TreeNode.To )
type ConfirmFunc ¶ added in v0.5.0
type ConfirmFunc func(ctx context.Context) (ConfirmResult, error)
ConfirmFunc computes an impact confirmation from the entered values (Params) (#6). hope calls it after the form (and any Danger gate) and blocks the action until the operator approves the returned Message.
type ConfirmResult ¶ added in v0.5.0
type ConfirmResult struct {
Title string `json:"title,omitempty"`
Message string `json:"message"`
Danger bool `json:"danger,omitempty"`
ConfirmLabel string `json:"confirmLabel,omitempty"`
}
ConfirmResult is the go/no-go payload a Confirm provider returns (#6): hope shows Message (with an optional Title, Danger styling, and ConfirmLabel) and runs the action only if the operator approves. An empty Message skips the gate.
type Contribution ¶
type Contribution struct {
Surface Surface `json:"surface"`
Title string `json:"title,omitempty"` // tab/page title
Icon string `json:"icon,omitempty"`
Match *Match `json:"match,omitempty"`
Pages []PageItem `json:"pages,omitempty"`
Node *Node `json:"node"`
// Actions are method names of registered actions shown as a toolbar at the top of
// this surface (page/panel/dashboard header) — page-level actions distinct from
// leaf actions inside the layout. hope collects fields, confirms danger, audits.
Actions []string `json:"actions,omitempty"`
// ID is a stable address for a page contribution, so links can target it by name
// (a plugin does NOT know its hope key or a page's positional path). A DetailPage
// sets ID + ParamKey and is Hidden from the rail; a Link/DetailLink navigates to
// it plugin-relative, and hope passes the URL arg as param[ParamKey].
ID string `json:"id,omitempty"`
Hidden bool `json:"hidden,omitempty"` // not listed in the rail (a link/detail target)
ParamKey string `json:"param_key,omitempty"` // detail pages: the URL arg becomes param[ParamKey]
// Subtitle sets the page header's sub/meta line (page surfaces) — e.g. a record
// count or a connection string. {param} placeholders are filled from the page
// param. Empty => hope shows the plugin name.
Subtitle string `json:"subtitle,omitempty"`
// Breadcrumbs render above the page heading (page surfaces). Each Crumb's Label
// and To may contain {param} placeholders hope fills from the page param — e.g.
// on a user detail page, [{Users, users}, {"user {id}"}].
Breadcrumbs []Crumb `json:"breadcrumbs,omitempty"`
}
Contribution mounts one layout tree onto a Surface. For container/stack surfaces, Match decides which containers it applies to. For page surfaces, Pages (optional) turns one node into MANY rail entries that share the layout but each pass a distinct Param.
type Crumb ¶
Crumb is one breadcrumb. To (optional) is a plugin-relative navigation target (like a Link cell / DetailLink); the last crumb is usually the current page and has no To. {param} placeholders in Label/To are filled from the page param.
type EmptyOpt ¶ added in v0.0.6
type EmptyOpt func(*EmptyState)
EmptyOpt configures a view's EmptyState (its icon, secondary text, or a custom Comp).
func EmptyComp ¶ added in v0.0.6
EmptyComp sets a fully custom empty state (a Comp tree — see component.go), overriding the icon/title/text — e.g. an icon plus a "create one" Link.
type EmptyState ¶ added in v0.0.6
type EmptyState struct {
Icon string `json:"icon,omitempty"` // leading icon (a built-in name or an Icons key)
Title string `json:"title,omitempty"` // the headline, e.g. "No slow queries 🎉"
Text string `json:"text,omitempty"` // a dim secondary line
Comp *Comp `json:"comp,omitempty"` // optional custom empty state (overrides Icon/Title/Text)
}
EmptyState is what hope shows when a view resolves to no data (an empty table, a tree with no nodes, a stat with no blocks) instead of the generic "empty" text. The plain Icon/Title/Text cover the common case; set Comp for a fully custom empty state (an icon + heading + a "create one" Link). Build it with plugin.EmptyView.
type Event ¶ added in v0.1.0
type Event struct {
Seq uint64 `json:"seq,omitempty"`
Kind string `json:"kind"`
Host string `json:"host,omitempty"`
Project string `json:"project,omitempty"`
IDs []string `json:"ids,omitempty"`
Source string `json:"source,omitempty"`
Ts int64 `json:"ts,omitempty"`
Data json.RawMessage `json:"data,omitempty"`
}
Event is one hope event delivered to an OnEvent handler. Mirrors hope's wire event; Data is kind-specific JSON (may be empty). Delivered only when the plugin holds the events:subscribe grant.
type EventFunc ¶ added in v0.1.0
EventFunc handles a hope event pushed to the plugin (one unary hope.event call per event). It should return quickly; hope treats a slow/erroring handler as a missed delivery and moves on. Requires the events:subscribe permission.
type Facet ¶
type Facet struct {
Key string `json:"key"`
Label string `json:"label"`
Options []Option `json:"options"`
}
Facet is one dropdown filter on a server table: a key, a label, and the choices.
type Field ¶
type Field struct {
Key string `json:"key"`
Label string `json:"label"`
Type FieldType `json:"type,omitempty"` // one of the Field* consts (default FieldText)
Placeholder string `json:"placeholder,omitempty"`
Hint string `json:"hint,omitempty"` // a dim line UNDER the field (always visible)
// Help is a longer explanation shown in a tooltip off a small info icon next to the
// label — for detail that would clutter the form as an always-visible Hint. Both may
// be set: Hint for the one-liner, Help for the "what is this / why" on hover.
Help string `json:"help,omitempty"`
Value string `json:"value,omitempty"`
Optional bool `json:"optional,omitempty"`
Options []Option `json:"options,omitempty"`
// Number-field bounds/step/unit (Type:"number"). Unit renders as a suffix on the input.
Min float64 `json:"min,omitempty"`
Max float64 `json:"max,omitempty"`
Step float64 `json:"step,omitempty"`
Unit string `json:"unit,omitempty"`
// AllowCustom, on a Type:"combobox" field, lets the operator commit a typed value that
// isn't in Options — a freeform entry with suggestions. Without it a combobox is a
// type-ahead select restricted to the list. Ignored by other field types.
AllowCustom bool `json:"allowCustom,omitempty"`
// OptionsMethod names an Options provider (registered with Plugin.Options); when
// set, hope fetches this select's choices from that method live as it renders the
// form, instead of using the static Options above. The JSON key is camelCase to
// match hope's PromptField, which the fields map onto directly.
OptionsMethod string `json:"optionsMethod,omitempty"`
// RefreshEvery, with OptionsMethod, re-fetches this select's options every N seconds
// while the form is open — so a live label ("in contact / queues next pass", a changing
// count) stays current without closing the modal. 0 (default) fetches once. camelCase to
// match hope's PromptField.
RefreshEvery int `json:"refreshEvery,omitempty"`
// ResolveMethod names a Resolve provider (registered with Plugin.Resolve). When the
// form's values change, hope calls it with the current values and renders the
// returned component node inline below the fields — a selection can drive a live
// preview, confirmation, or result. camelCase for the same PromptField reason.
ResolveMethod string `json:"resolveMethod,omitempty"`
// Fields is the per-item sub-schema of a Type:"group" field — a repeatable
// array-of-objects the operator adds/removes rows of (a forms-builder). The action
// receives the group's value as a []map[string]any, one map per row. AddLabel names
// the "+ add" button.
Fields []Field `json:"fields,omitempty"`
AddLabel string `json:"addLabel,omitempty"`
// DependsOn conditionally shows this field: it renders ONLY when the field named by
// DependsOn currently equals DependsValue — or, when DependsValue is empty, when that
// field is non-empty/truthy. Combine with OptionsMethod (which reads Params for the
// current values) for cascading selects: choose A, and B's options refetch narrowed by
// A — and B can stay hidden until A is set. camelCase to match hope's PromptField.
DependsOn string `json:"dependsOn,omitempty"`
DependsValue string `json:"dependsValue,omitempty"`
// FieldsMethod names a Fields provider (registered with Plugin.Fields). On this field's
// change hope calls it with the current values (via Params) and renders the returned
// []Field inline as a sub-form below this field — so a selection produces real, typed
// inputs rather than a free-form box. The sub-fields may carry their own OptionsMethod /
// DependsOn. camelCase to match hope's PromptField.
FieldsMethod string `json:"fieldsMethod,omitempty"`
}
type FieldError ¶ added in v0.5.0
FieldError is one field-level validation error returned by a Validate provider (#4). hope renders Error inline under the field named by Key and disables Run while any exist.
type FieldType ¶ added in v0.5.0
type FieldType string
Field mirrors hope's PromptField: the UI collects these before invoking an action, then passes the values map to the action handler. FieldType is the input kind of an action/wizard Field. Use the Field* consts for typo-safe forms; a bare string still works, since the const values are these strings.
const ( FieldText FieldType = "text" // single-line text (the default) FieldTextarea FieldType = "textarea" // multi-line text FieldSelect FieldType = "select" // one choice from Options / OptionsMethod (dropdown) FieldToggle FieldType = "toggle" // on/off; the value is "true"/"false" FieldKV FieldType = "kv" // a key/value editor; the value is a JSON object FieldGroup FieldType = "group" // a repeatable sub-form (Fields); the value is a JSON array of rows FieldNumber FieldType = "number" // numeric input with Min/Max/Step/Unit FieldMultiselect FieldType = "multiselect" // multi choice as a checkbox dropdown; the value is a JSON array FieldChips FieldType = "chips" // multi choice as inline toggle pills; the value is a JSON array FieldCombobox FieldType = "combobox" // single editable type-ahead; set AllowCustom for freeform entry )
type FieldsFunc ¶ added in v0.5.0
FieldsFunc returns a dynamic sub-form for a Field whose FieldsMethod names it (#1). hope calls it on that field's change with the current values (Params) and renders the returned []Field inline below it — so a selection produces real, typed inputs, not a free-form box. The returned fields may themselves carry OptionsMethod/DependsOn.
type GraphData ¶ added in v0.6.0
type GraphData struct {
Nodes []*GraphNode `json:"nodes"`
Edges []GraphEdge `json:"edges,omitempty"`
Directed bool `json:"directed,omitempty"`
// Active is the id of the DAG this data is for — set it so hope shows/highlights the
// active DAG (in the sidebar, toolbar) and threads it to the chrome regions and run.
Active string `json:"active,omitempty"`
}
GraphData is what a Graph view returns: nodes positioned at x/y and edges between their ports. Build the nodes/edges with GNode/GEdge. hope renders the canvas; the plugin owns what each node contains and all persistence.
type GraphEdge ¶ added in v0.6.0
type GraphEdge struct {
From string `json:"from"`
To string `json:"to"`
Tone string `json:"tone,omitempty"`
Label string `json:"label,omitempty"`
}
GraphEdge connects one node's out-port to another's in-port. From/To are "nodeID:portID".
func GEdge ¶ added in v0.6.0
GEdge builds an edge between "nodeID:portID" endpoints. Chain .Toned / .Labeled.
type GraphMenuItem ¶ added in v0.6.0
type GraphMenuItem struct {
Label string `json:"label,omitempty"`
Icon string `json:"icon,omitempty"`
Method string `json:"method,omitempty"`
Danger bool `json:"danger,omitempty"`
Divider bool `json:"divider,omitempty"` // a separator (Label ignored)
}
GraphMenuItem is one entry in the canvas right-click menu, returned by the GraphMenu method for the right-clicked target ({kind:"node"|"edge"|"canvas", id/from/to}). Clicking it calls Method with the target's row ({id}/{from,to}/{x,y}, plus the active graph). hope prepends its own built-ins (Configure / Delete node / Disconnect) when the matching methods are wired.
type GraphNode ¶ added in v0.6.0
type GraphNode struct {
ID string `json:"id"`
Type string `json:"type,omitempty"`
X float64 `json:"x"`
Y float64 `json:"y"`
W int `json:"w,omitempty"` // node width px (0 => hope default ~190)
Title string `json:"title,omitempty"`
Icon string `json:"icon,omitempty"` // built-in name OR a plugin Icons key
Tone string `json:"tone,omitempty"` // node accent
// Meta is a clean, ordered key/value strip hope renders on the node face (its config /
// params) — set it from the node's settings so populated params show without hand-building
// a Body. Build with GNode(...).Meta(label, value). Body (below) is still available for
// fully custom node content.
Meta []NodeMeta `json:"meta,omitempty"`
Body *Comp `json:"body,omitempty"`
In []Port `json:"in,omitempty"`
Out []Port `json:"out,omitempty"`
State string `json:"state,omitempty"` // run overlay: idle|running|done|error (drives the node glow)
Data map[string]any `json:"data,omitempty"` // opaque; echoed back to mutation methods
// Fields is an optional per-node config form. When set (and the view has GraphConfig),
// clicking the node opens these Fields — the SAME dynamic form as an action, so a node
// gets real inputs: a select populated from an OptionsMethod, number, multiselect, etc.
// The form prefills from Data (matching keys); submitting calls the GraphConfig method
// with {id, ...values}. Build with GNode(...).Form(...).
Fields []Field `json:"fields,omitempty"`
// Actions render as a button bar at the bottom of the node card (Info / Logs / …). Each
// runs its Method with {id, ...Data}; the result can steer the UI (a flyout/navigate
// directive), so an "Info" action can open a modal explaining what the node does. Reuses
// RowAction (Icon/Danger/Fields/Tip). Build with GNode(...).Act(...).
Actions []RowAction `json:"actions,omitempty"`
}
GraphNode is one node on the canvas. hope ships no node types — Type is the plugin's own label for it (metadata hope never interprets); Body is an arbitrary Comp tree rendered in the node card; In/Out are the connectable ports. Build with GNode.
func GNode ¶ added in v0.6.0
GNode builds a node: an id + title, with any body components rendered in the node card. Chain .At/.Typed/.Ico/.Toned/.Wide/.InPorts/.OutPorts/.Stated/.With.
func (*GraphNode) Act ¶ added in v0.6.0
Act adds action buttons to the node's bottom bar (Info / Logs / …). Each runs its Method with {id, ...Data} and its result can open a flyout / navigate (an "Info" action that shows a modal of what the node does). Reuses RowAction. Chainable.
func (*GraphNode) Form ¶ added in v0.6.0
Form gives the node a config form — the same dynamic Fields as an action (a select with OptionsMethod, number, multiselect, ...). Clicking the node opens it, prefilled from Data; saving calls the view's GraphConfig method with {id, ...values}.
func (*GraphNode) Ico ¶ added in v0.6.0
Ico sets the node's leading icon (a built-in name or a plugin Icons key).
func (*GraphNode) InPorts ¶ added in v0.6.0
InPorts sets the node's input ports (rendered down the left edge).
func (*GraphNode) OutPorts ¶ added in v0.6.0
OutPorts sets the node's output ports (rendered down the right edge).
func (*GraphNode) Stated ¶ added in v0.6.0
Stated sets the node's run state (idle|running|done|error) — the run glow.
func (*GraphNode) Typed ¶ added in v0.6.0
Typed sets the node's plugin-defined type name (metadata; drives nothing in hope).
type GraphOpt ¶ added in v0.6.0
type GraphOpt = TableOpt
GraphOpt configures a Graph view's descriptor (a functional option, composes with ViewOpt).
func GraphAdd ¶ added in v0.6.0
GraphAdd names the method hope calls to create a node — with the picked palette {type, x, y}, or {type:"", x, y} from a double-click on empty canvas (your default node).
func GraphConfig ¶ added in v0.6.0
GraphConfig names the method hope calls to persist a node's config Form: {id, ...values}. Required for GNode(...).Form(...) node inputs to save.
func GraphConnect ¶ added in v0.6.0
GraphConnect names the method hope calls to add an edge: {from:"nodeID:portID", to:"nodeID:portID"}.
func GraphDelete ¶ added in v0.6.0
GraphDelete names the method hope calls to delete a node: {id}. Confirm it with a DangerAction.
func GraphDirected ¶ added in v0.6.0
func GraphDirected() GraphOpt
GraphDirected draws arrowheads on edges (a directed graph).
func GraphDisconnect ¶ added in v0.6.0
GraphDisconnect names the method hope calls to remove an edge: {from, to}.
func GraphMenu ¶ added in v0.6.0
GraphMenu names the method hope calls on right-click to build a custom context menu. It receives the target ({kind:"node"|"edge"|"canvas", id/from/to}) and returns []GraphMenuItem; hope shows them under its own built-ins (Configure / Delete node / Disconnect). Each item's Method is invoked with the target's row.
func GraphMove ¶ added in v0.6.0
GraphMove names the method hope calls to persist a dragged node's position: {id, x, y}.
func GraphNodeFlyout ¶ added in v0.6.0
GraphNodeFlyout names the method hope calls when a node is clicked: {id} -> a Comp body it renders in the right-side drawer. (A node with a config Form opens that form instead.)
func GraphPalette ¶ added in v0.6.0
GraphPalette names a method returning the []NodeType catalog — the node types the operator can drag onto the canvas. For a fixed catalog use StaticPalette instead.
func GraphRun ¶ added in v0.6.0
GraphRun names a StreamComponent-kind stream that emits GraphData frames while a run is active — the plugin advances each node's State so hope highlights progress live.
func GraphSelect ¶ added in v0.6.0
GraphSelect names the method hope calls when a sidebar entry is chosen: {id}. The canvas then re-fetches with {graph: id} so selecting a DAG swaps the canvas in place.
func GraphSidebar ¶ added in v0.6.0
GraphSidebar names a method returning a Comp for the left rail — the natural home for a browsable list of the plugin's DAGs. Pair with GraphSelect to switch the active graph.
func GraphSidebarActions ¶ added in v0.6.0
GraphSidebarActions renders the given action methods as buttons atop the sidebar — the home for "New pipeline" / "New folder" and the like. Each runs like any action (fields, confirm); on success hope re-fetches the sidebar + the graph.
func GraphSnap ¶ added in v0.6.0
GraphSnap snaps dragged nodes to an N-px grid (0 = free positioning).
func GraphToolbar ¶ added in v0.6.0
GraphToolbar names a method returning a Comp for the strip above the canvas.
func GraphTypeInfo ¶ added in v0.6.0
GraphTypeInfo names a method that returns a Comp describing a node TYPE ({type}). hope shows an info button on each palette entry that opens it in the drawer — so an operator can read what a node does before dragging it onto the canvas.
func GraphValidateConnect ¶ added in v0.6.0
GraphValidateConnect names an optional pre-connect gate: {from, to} -> a ConfirmResult-ish {ok bool, reason string}. hope only commits the connection when ok.
func StaticPalette ¶ added in v0.6.0
StaticPalette sets a fixed node-type catalog on the view (alternative to GraphPalette).
type Hope ¶ added in v0.1.0
type Hope struct {
// contains filtered or unexported fields
}
Hope is a handle to the operator ACTIONS a plugin may perform on hope — the operator pattern (watch events via OnEvent, then reconcile by mutating hope). Each action requires its own operator-granted scope and hope's reverse channel; without the grant hope rejects the call, and every action is audited. Actions target the plugin's OWN stack only.
func (*Hope) AddServiceLabel ¶ added in v0.1.0
AddServiceLabel adds (or updates) a label on a service in the plugin's own stack and hope re-applies the stack, so the label PERSISTS across future redeploys. The classic use is a plugin that auto-adds Prometheus scrape labels on deploy. service is a service name in this plugin's stack; the plugin can't target another stack. Requires the spec:label permission.
type ImageOpt ¶
ImageOpt configures an Image cell's render box (size + fit).
func ImgBox ¶
ImgBox fixes both dimensions: the image is centered in a w×h box and, by default, contained (scaled to fit, no crop). Add ImgFit("cover") to fill and crop instead.
func ImgCache ¶ added in v0.0.9
func ImgCache() ImageOpt
ImgCache opts THIS image into hope's durable local byte cache. hope caches the flagged image client-side via a service worker (the CORS-free way — it caches the <img>'s own request/response, so the image host needs no CORS/cooperation) and serves it from cache on later views, surviving reloads and sessions. Opt-in per image: only images you flag are cached — nothing else. Prefer it on immutable, content-addressed art (a badge/canvas keyed by hash) that's expensive to re-fetch from a remote host.
func ImgFallback ¶
ImgFallback sets an image shown when src fails to load (broken/404/blocked). It must also be an absolute http(s) URL. Tried once; if it too fails, the cell renders blank (no browser broken-image icon).
func ImgFit ¶
ImgFit sets object-fit: "contain" (default — whole image, letterboxed) or "cover" (fill the box, cropping overflow).
func ImgLightbox ¶
func ImgLightbox() ImageOpt
ImgLightbox makes clicking the image open it in an in-app lightbox (a dimmed full-screen overlay, closed by backdrop click / Esc / the close button) instead of a new browser tab — nicer for viewing badge/avatar art in place.
type InitContext ¶ added in v0.0.7
type InitContext struct {
Settings map[string]string
Protocol int
Caps Capabilities
}
InitContext is what hope hands a plugin at initialization (hope.init): the settings the operator configured (so the plugin can set up WITH them), plus the hope build's protocol version and capabilities.
type Item ¶ added in v0.5.0
type Item struct {
Type string `json:"type,omitempty"`
Data map[string]any `json:"data,omitempty"`
Comp *Comp `json:"comp,omitempty"`
}
Item is one entry in a paged collection (see ViewKind Paged). Resolution per item:
- Comp set (A): hope renders that component tree directly — a one-off / special item.
- else Type set (B): hope binds Data into the view's ItemTemplate for that Type.
- else: Data renders as key/values (a sensible default so an untyped item still shows).
type KVData ¶
KVData is what a KV view returns: a flat map of label -> value. A value may be a plain scalar or a rich cell (Badge/Link/Image/…). It's a named alias so a KV handler reads as returning KVData, while a map literal still satisfies it.
type Layout ¶
type Layout struct {
ProtocolVersion int `json:"protocolVersion"`
Contributions []Contribution `json:"contributions"`
}
Layout is the result of the fixed hope.layout method: the plugin's UI contributions, versioned for graceful cross-version degradation.
type Match ¶
type Match struct {
Always bool `json:"always,omitempty"` // every container
Images []string `json:"images,omitempty"` // image-ref globs, e.g. "postgres*"
Labels map[string]string `json:"labels,omitempty"` // label == value
Services []string `json:"services,omitempty"` // compose service names
}
Match decides which containers a container/stack contribution applies to. The plugin declares this (hope does not map containers to plugins). A nil/empty Match means "the plugin's own container" — the trivial self-describing case. Semantics: set clauses are AND-ed; values within a clause are OR-ed.
type Node ¶
type Node struct {
Kind NodeKind `json:"kind"`
Title string `json:"title,omitempty"`
Ref string `json:"ref,omitempty"` // leaf: method name of a view/action/stream
Size int `json:"size,omitempty"` // optional row/grid weight
Fill bool `json:"fill,omitempty"` // grow to fill the remaining height (e.g. a table)
// Collapsible makes a titled section fold on a title click; Collapsed starts it
// closed. For dense pages where not everything needs to be open at once.
Collapsible bool `json:"collapsible,omitempty"`
Collapsed bool `json:"collapsed,omitempty"`
Children []*Node `json:"children,omitempty"`
// Comp carries an inline Component tree when Kind is NodeComponent (see Component).
Comp *Comp `json:"comp,omitempty"`
}
Node is one node in the surface-agnostic layout tree. The same tree drives a container panel now and a full page later; hope's renderer walks it without caring which surface hosts it.
func Buttons ¶ added in v0.0.3
Buttons builds a horizontal group of action buttons from action method refs. The buttons size to content and sit together (a toolbar), unlike Row's stretched columns — e.g. Buttons("analyze", "vacuum") for a maintenance section.
func Component ¶ added in v0.0.6
Component builds an inline component node: a Comp tree (see component.go) rendered straight from the layout, with no per-view round-trip. Use it for a small static tile — e.g. plugin.Component(plugin.Box(plugin.Heading("Fleet", 3), …)).
func Row ¶
Row builds a horizontal row from children — equal-width columns (each child gets an equal flex share). For a group of action buttons use Buttons instead, so they size to content and don't spread across the row.
func (*Node) Collapse ¶
Collapse makes a titled section fold on a title click. Pass collapsed=true to start it closed.
func (*Node) Filled ¶
Filled marks a node to grow and fill the remaining height (and propagates up its ancestors when rendered) — e.g. a table that should fill the page.
func (*Node) Weight ¶
Weight sets a child's flex weight inside a Row (or Grid) — the WIDTH proportion it takes relative to its siblings. Default (0) means equal share. e.g. in a two-column Row, Weight(1) beside Weight(2) makes the second column twice as wide:
plugin.Row(
plugin.Section("Overview", plugin.Leaf("head")).Weight(1),
plugin.Section("Fields", plugin.Leaf("fields")).Weight(2),
)
type NodeKind ¶
type NodeKind string
NodeKind enumerates layout-tree node types. Containers hold Children; a leaf references a registered view/action/stream by method name.
const ( NodeSection NodeKind = "section" // titled group NodeTabs NodeKind = "tabs" // tabbed children NodeRow NodeKind = "row" // horizontal arrangement (equal-width columns) NodeButtons NodeKind = "buttons" // horizontal group of action buttons, sized to content NodeGrid NodeKind = "grid" // grid arrangement NodeLeaf NodeKind = "leaf" // a single view/action/stream // NodeComponent carries an inline Comp tree (see component.go) rendered directly // from the layout — no ref, no per-view round-trip. Build it with Component(). NodeComponent NodeKind = "component" )
type NodeMeta ¶ added in v0.6.0
type NodeMeta struct {
Label string `json:"label"`
Value string `json:"value"`
Tone string `json:"tone,omitempty"`
}
NodeMeta is one line in a node's clean key/value meta strip (its params/config).
type NodeType ¶ added in v0.6.0
type NodeType struct {
Type string `json:"type"`
Label string `json:"label,omitempty"`
Icon string `json:"icon,omitempty"`
Tone string `json:"tone,omitempty"`
Category string `json:"category,omitempty"` // groups the palette
Desc string `json:"desc,omitempty"` // tooltip
}
NodeType is one entry in the palette — a kind of node the plugin offers to place. Dragging it onto the canvas calls the GraphAdd method with {type,x,y}. Return these from GraphPalette (or set a static Palette on the view).
type OptionsFunc ¶ added in v0.3.0
OptionsFunc returns the choices for an RPC-populated select field. hope calls it (proxied like any method) to fill a Field whose OptionsMethod names it, instead of the author hard-coding a static list. The current partial form values are available via Params(ctx), so a later cascading select can narrow its options by an earlier field.
type Page ¶ added in v0.5.0
type Page struct {
Items []Item `json:"items"`
Total int `json:"total,omitempty"`
NextCursor string `json:"nextCursor,omitempty"`
}
Page is one page of a Paged view, returned by its method. The method reads the requested page from Params(ctx): "offset"+"limit" (offset paging) or "cursor" (cursor paging), plus "filter". Return Total for offset paging (drives the pager) OR NextCursor for cursor paging (drives infinite scroll); an empty NextCursor means the last page.
type PageItem ¶
type PageItem struct {
Title string `json:"title"`
Icon string `json:"icon,omitempty"`
Param map[string]any `json:"param,omitempty"`
Children []PageItem `json:"children,omitempty"`
}
PageItem is one dynamic subpage: it shares the contribution's Node but carries its own Param, which hope merges into every call the page makes — so a plugin can list e.g. every DB table as a rail entry that renders the same view with a different argument. Read it in a handler with plugin.Params(ctx, &v).
Children nests items one level (a group node), so e.g. three databases each listing their tables become nested rail entries. A node with Children is a group (not itself a page); a leaf node (no Children) is the navigable page.
type Permission ¶ added in v0.1.0
Permission is a reverse capability the plugin REQUESTS from hope. Least privilege: a plugin gets NOTHING on the plugin->hope direction unless it declares the scope here (via Plugin.RequirePermission) AND the operator consents when enabling it. hope records the granted subset and gates every reverse call on it; the plugin's token authenticates identity, the grant set authorizes. Reason is shown on the operator's consent prompt.
type Plugin ¶
type Plugin struct {
// contains filtered or unexported fields
}
Plugin is a hope plugin server. Construct with New, register capabilities, then ListenAndServe. Registration is not safe for concurrent use; serving is.
func New ¶
New creates a plugin with the given name and semantic version. The endpoint path defaults to /__hope and the request body cap to 4 MiB. If the environment sets HOPE_PLUGIN_TOKEN, calls must present it as a bearer token.
func (*Plugin) Action ¶
func (p *Plugin) Action(method, label string, fields []Field, fn ActionFunc, opts ...ActionOpt) *Plugin
Action registers an invocable action. The UI collects fields, then calls fn with the values. Mark destructive actions with DangerAction.
func (*Plugin) Alert ¶ added in v0.1.0
Alert is a convenience over Publish: it publishes an event whose Data is {severity, title, detail, dedupeKey}. hope namespaces the kind to plugin.<identity>.alert. Requires events:publish.
func (*Plugin) Breadcrumbs ¶
Breadcrumbs attaches a breadcrumb trail to the most recently added page contribution (call right after Page/DetailPage). {param} placeholders in a crumb's label/to are filled from the page param.
func (*Plugin) CardsView ¶
CardsView registers a Cards view; the handler returns CardsData (a grid of cards / a gallery — e.g. badges, users). Cards with a To navigate on click. opts (Static/EmptyView/…) are optional.
func (*Plugin) ChartView ¶
ChartView registers a Chart view; the handler returns ChartData (bar or line, one or more named series over categorical labels). hope draws axes + legend. opts (Static/EmptyView/…) are optional.
func (*Plugin) ComponentView ¶ added in v0.0.6
ComponentView registers a Component view — the escape hatch. The handler returns a *Comp: a tree of safe primitives (box/row/heading/keyval/sparkline/cell/…) hope composes into a custom widget the built-in kinds don't cover (see component.go). Add plugin.Static() to cache it or plugin.EmptyView(...) to customize its empty state. For a small STATIC tile, prefer an inline plugin.Component node in the layout instead — it needs no per-view round-trip.
func (*Plugin) Confirm ¶ added in v0.5.0
func (p *Plugin) Confirm(method string, fn ConfirmFunc) *Plugin
Confirm registers an impact-confirmation provider under method. Point an action's ConfirmMethod at it (via ActionConfirm). Shares the plugin's method namespace.
func (*Plugin) ContainerPanel ¶
ContainerPanel is a shorthand for a container-surface contribution with the given title, match, and layout node.
func (*Plugin) Contribute ¶
func (p *Plugin) Contribute(c Contribution) *Plugin
Contribute adds an explicit UI contribution. Without any, the plugin auto-generates a single container contribution (see layout) listing every registered capability — so a minimal plugin needs no layout code.
func (*Plugin) DangerAction ¶
func (p *Plugin) DangerAction(method, label string, fields []Field, fn ActionFunc, opts ...ActionOpt) *Plugin
DangerAction is like Action but flags the action destructive, so hope confirms before running it and audit-logs the invocation.
func (*Plugin) DashboardWidget ¶
DashboardWidget contributes a widget to hope's fleet/host dashboard (the `dashboard` surface): the node renders as a compact panel alongside hope's own dashboard cards. Keep it small — a couple of kv/counter/series leaves.
func (*Plugin) Description ¶
Description sets a one-line description shown in hope.
func (*Plugin) DetailPage ¶
DetailPage contributes a hidden master-detail page addressed by a stable id (not shown in the rail). A Link/DetailLink navigates to it plugin-relative, and hope passes the URL arg as param[paramKey] — read it in a handler with plugin.Params. e.g. DetailPage("user", "User", "id", node) rendered at .../user/42 => {id:"42"}.
func (*Plugin) DynamicPage ¶
DynamicPage contributes MANY rail pages that share one layout node but each pass a distinct Param (merged into every call the page makes). items may nest one level (groups of pages) — e.g. databases -> tables. Read the param in a handler with plugin.Params. Regenerate the items in getLayout to reflect live state.
func (*Plugin) DynamicPageFunc ¶ added in v0.0.2
func (p *Plugin) DynamicPageFunc(title string, node *Node, fn func(ctx context.Context) []PageItem) *Plugin
DynamicPageFunc is DynamicPage with LIVE items: fn runs on every hope.layout to produce the rail entries, so the set reflects current state (a database's tables, a broker's topics) instead of a snapshot frozen at startup — the answer to "my pages depend on data I don't have until runtime." Keep fn fast: hope fetches the layout per surface, so cache if it hits a slow backend.
func (*Plugin) Fields ¶ added in v0.5.0
func (p *Plugin) Fields(method string, fn FieldsFunc) *Plugin
Fields registers a selection->sub-form provider under method. Point a Field's FieldsMethod at it. The method shares the plugin's method namespace (like views/actions/options), so keep it distinct.
func (*Plugin) GraphView ¶ added in v0.6.0
GraphView registers a Graph view (clone of ChartView with Kind: Graph). The function returns a *GraphData. Pair with the Graph* opts to wire editing, a node-click flyout, a run stream, and the plugin-controlled chrome (sidebar / toolbar / palette). A bare GraphView with no mutation opts is a read-only canvas.
func (*Plugin) Handler ¶
Handler returns the http.Handler that serves the JSON-RPC endpoint at the plugin's path. Mount it yourself, or use ListenAndServe.
func (*Plugin) HeaderActions ¶
HeaderActions attaches action buttons to the most recently added contribution — a page/panel/dashboard toolbar (distinct from leaf actions inside the layout). Each ref names a registered Action; hope collects its fields, confirms danger, and audits. Call it right after Page/ContainerPanel/DashboardWidget.
func (*Plugin) Icon ¶
Icon sets the plugin's default icon — a hope built-in name or one of the Icons keys registered with Icons.
func (*Plugin) Icons ¶
Icons registers plugin-scoped icons: name -> inner SVG markup (path/circle/rect elements only, NOT a full <svg>), 24x24 stroke to match hope's icon set. hope resolves and sanitizes these in a per-plugin namespace, so they can't collide with other plugins or shadow hope's built-ins.
func (*Plugin) ListenAndServe ¶
ListenAndServe serves the plugin on addr (e.g. ":8080"). Blocks. A ReadHeaderTimeout guards against a slow-header (Slowloris) client holding a goroutine; no WriteTimeout is set because stream handlers write indefinitely.
func (*Plugin) MaxBodyBytes ¶
MaxBodyBytes overrides the request body cap (default 4 MiB).
func (*Plugin) OnEvent ¶ added in v0.1.0
OnEvent registers a handler for hope events (stack/container/image/plugin/agent changes, and plugin-published events). hope pushes each relevant event as a unary hope.event call. Registering a handler auto-declares the events:subscribe permission, so the operator is asked to consent on enable; without the grant hope never calls it. Call RequirePermission(ScopeEventsSubscribe, "<reason>") first to set a custom consent reason.
func (*Plugin) OnInit ¶ added in v0.0.7
OnInit registers a handler hope calls once the plugin is reachable and being initialized (hope.init) — and again if the plugin restarts. It receives the current settings, so a plugin that needs its config at startup (a pool sized by a setting, a mode flag) can initialize with them instead of booting on defaults and waiting for a later hope.settings push. Optional: without it, settings are still applied so SettingValue works immediately. Errors are returned to hope (the install surfaces them). Register before ListenAndServe.
func (*Plugin) OnStatus ¶ added in v0.3.0
func (p *Plugin) OnStatus(fn func(ctx context.Context) StatusReport) *Plugin
OnStatus registers a handler hope calls (via the reserved hope.status method) to read the plugin's ADVISORY self-reported health — domain state only the plugin knows (replica count, replication lag, upstream reachability). Pull-only: hope asks, the plugin answers, so no reverse channel or grant is involved. An older hope that lacks the "status" feature simply never calls it — check Caps(ctx).Supports("status") if the plugin wants to know whether this hope will ask.
func (*Plugin) Options ¶ added in v0.3.0
func (p *Plugin) Options(method string, fn OptionsFunc) *Plugin
Options registers an RPC-populated select provider under method. Reference it from a form Field with OptionsMethod: method, and hope fetches the choices live when it renders the form. The method name shares the plugin's method namespace (like views and actions), so keep it distinct.
func (*Plugin) Page ¶
Page contributes a full custom nav page (the `page` surface). hope lists it in the rail under the plugin and renders the node tree as a full page.
func (*Plugin) PageID ¶
PageID gives the most recently added page contribution a stable id so links and breadcrumbs can target it by name (a plugin doesn't know positional page paths). Call right after Page.
func (*Plugin) PagedView ¶ added in v0.5.0
PagedView registers a SERVER-PAGED collection view. The method reads the requested page from Params(ctx) — "offset"+"limit" (offset paging) or "cursor" (cursor paging), plus an optional "filter" — and returns a *Page. Each item renders via its Comp (A) or an ItemTemplate for its Type (B). Pair with Layout / Infinite / ItemTemplate / PageSize.
func (*Plugin) Path ¶
Path overrides the endpoint path (default /__hope). Must match the hope.plugin.path label.
func (*Plugin) Publish ¶ added in v0.1.0
Publish emits an event onto hope's bus. Requires (a) the events:publish permission granted by the operator and (b) hope's reverse channel configured. hope stamps the Source and namespaces the Kind (plugin.<identity>.<kind>), so only Kind + Data here are meaningful — a plugin can never spoof another's events or a core hope kind.
func (*Plugin) QueryView ¶
QueryView registers a Query view whose input editor syntax-highlights lang and prepopulates with def (a template where {param} placeholders are filled from the page's param, e.g. "select * from {table}"). Read the input with plugin.Input.
func (*Plugin) RequestPermission ¶ added in v0.1.0
RequestPermission asks the operator, at runtime, to grant a scope this plugin doesn't yet hold (the Android runtime-permission model). It does not grant anything — hope raises a consent prompt; the plugin gains the capability only if the operator allows it. Safe to call repeatedly: an already-granted/denied/pending scope is a no-op on hope's side. Declaring the scope up front via RequirePermission (so it's asked at enable) is usually better; use this when a capability is only needed conditionally at runtime.
func (*Plugin) RequirePermission ¶ added in v0.1.0
RequirePermission declares that the plugin wants a reverse capability (an events:*/storage/... scope). It appears in hope.schema; the operator consents when enabling the plugin, and hope gates the capability on the grant. Idempotent per scope — a later call only updates the reason if one is given.
func (*Plugin) Resolve ¶ added in v0.3.0
func (p *Plugin) Resolve(method string, fn ResolveFunc) *Plugin
Resolve registers a selector->surface provider under method. Point a form Field's ResolveMethod at it: when the form's values change, hope calls this with the current values and renders the returned component node inline. The method shares the plugin's method namespace (like views/actions/options), so keep it distinct.
func (*Plugin) ResolveAlert ¶ added in v0.1.0
ResolveAlert clears a previously-raised alert (matched by dedupeKey): hope surfaces it as a "resolved" confirmation and drops the alert from any active view. Use it when a monitored condition recovers. Requires events:publish.
func (*Plugin) SearchView ¶
SearchView registers a Search (autocomplete) view: hope calls fn with {q: <text>} as the user types and renders the returned SearchData as a live dropdown; selecting a SearchItem navigates to its To. Read the query with plugin.SearchQuery(ctx). Great for a "go to <entity>" jump box.
func (*Plugin) Setting ¶
Setting declares an operator-managed configuration field. hope renders these in the plugin inspector, persists the values (encrypted), and pushes them to the plugin; read a value in any handler with SettingValue. Settings are config the operator SETS — distinct from the panel the plugin SHOWS.
func (*Plugin) SettingValue ¶
SettingValue returns the current value of an operator-managed setting (the value hope last pushed), falling back to the declared default. Safe for concurrent use.
func (*Plugin) StackWidget ¶ added in v0.0.2
StackWidget contributes a widget to the stack view (the `stack` surface): the node renders as a panel on a stack's page, for stacks whose containers the Match selects. It's the container panel's whole-stack analog — same Match semantics (images/labels/services/always), but evaluated against the stack's set of containers rather than one. Keep it focused: a stack-scoped overview or action.
func (*Plugin) StatView ¶
StatView registers a Stat view: the handler returns StatData (one or more big-number blocks — counts, totals, sizes). Add plugin.Refreshable() for a manual refresh button (e.g. "count rows in my table" on demand).
func (*Plugin) Stream ¶
func (p *Plugin) Stream(method, label string, kind StreamKind, fn StreamFunc) *Plugin
Stream registers a live stream emitted as NDJSON frames.
func (*Plugin) Subtitle ¶
Subtitle sets the page header's sub/meta line for the most recently added page contribution ({param} placeholders filled from the page param). Call after Page.
func (*Plugin) TableView ¶
TableView registers a Table view. Add RowDetail/RowActions opts to make rows interactive — e.g. TableView("rows","Rows",fn, plugin.RowDetail("inspect"), plugin.RowActions(plugin.RowAction{Method:"del",Label:"Delete",Danger:true})).
func (*Plugin) TextView ¶
TextView registers a Text view: the handler returns {text: "…"} (or a raw string) rendered as a monospace scrollable block — logs, config, command output. opts (Static/EmptyView/…) are optional.
func (*Plugin) Token ¶
Token pins the shared secret hope must present as a bearer token, overriding HOPE_PLUGIN_TOKEN. Use when you configure the token in code rather than env.
type Port ¶ added in v0.6.0
type Port struct {
ID string `json:"id"`
Label string `json:"label,omitempty"`
Kind string `json:"kind,omitempty"`
Tone string `json:"tone,omitempty"`
}
Port is a connectable point on a node's edge (input on the left, output on the right). Kind is the plugin's typing used by GraphValidateConnect to accept/reject a connection.
func GPort ¶ added in v0.6.0
GPort builds a port. Chain .Kinded (typing for connect-validation) / .Toned.
type ResolveFunc ¶ added in v0.3.0
ResolveFunc returns a component node (built with the Box/KeyVal/Heading/Badge/Text primitives) for a selector->surface field. hope renders whatever it returns inline in the form, below the fields, and re-calls it whenever the form values change — so a selection can drive a live preview, a confirmation, or a result. Read the current values with Params(ctx) to render for the chosen option.
type RowAction ¶
type RowAction struct {
Method string `json:"method"`
Label string `json:"label"`
Icon string `json:"icon,omitempty"`
Danger bool `json:"danger,omitempty"` // hope confirms before running and audit-logs it
Fields []Field `json:"fields,omitempty"` // optional input collected before the call
Tip *Tooltip `json:"tip,omitempty"` // hover tooltip on the row action button (build with Tip)
// ShowWhenKey/ShowWhenValue gate the button PER ROW against that row's cells: the
// button shows only when the row's ShowWhenKey cell equals ShowWhenValue (or, when
// ShowWhenValue is empty, when that cell is non-empty/truthy). Mirrors Field.DependsOn/
// DependsValue. Applies to the inline row button AND the RowFlyout footer. Empty
// ShowWhenKey = always shown (unchanged). A Badge/Code/Link cell compares by its text.
ShowWhenKey string `json:"showWhenKey,omitempty"`
ShowWhenValue string `json:"showWhenValue,omitempty"`
// DisableInsteadOfHide renders the button disabled (greyed, Tip as the reason) instead
// of hidden when the predicate fails — so the operator sees it exists but can't use it.
DisableInsteadOfHide bool `json:"disableInsteadOfHide,omitempty"`
}
RowAction is one author-declared action bound to a table row. hope calls Method with {row: {column: value}} (plus the page param); use for row-scoped mutations. If Fields is set, hope collects them first and merges the values into the call params alongside row — e.g. a "Rename" action with a new-name field.
type Schema ¶
type Schema struct {
ProtocolVersion int `json:"protocolVersion"`
Name string `json:"name"`
Version string `json:"version"`
Description string `json:"description,omitempty"`
Icon string `json:"icon,omitempty"` // default icon: a hope built-in OR an Icons key
Icons map[string]string `json:"icons,omitempty"` // plugin-scoped {name: inner-svg-markup}
Actions []ActionDesc `json:"actions"`
Views []ViewDesc `json:"views"`
Streams []StreamDesc `json:"streams"`
Settings []Setting `json:"settings,omitempty"` // operator-managed config (see Setting)
Permissions []Permission `json:"permissions,omitempty"` // reverse-capability requests (see Permission)
}
Schema is the plugin's identity + capability manifest — the result of the fixed hope.schema method. It is the only method hope calls before the operator enables the plugin (discovery), so it must be safe to expose unauthenticated.
type SearchData ¶
type SearchData struct {
Items []SearchItem `json:"items"`
}
SearchData is what a Search view returns for a query: the current suggestion list.
type SearchItem ¶
type SearchItem struct {
Label string `json:"label"`
Sub string `json:"sub,omitempty"`
Image string `json:"image,omitempty"`
To string `json:"to"`
}
SearchItem is one autocomplete suggestion. Label is the primary text; Sub is a dim secondary line (e.g. an id); Image is an optional thumbnail (absolute http(s) URL); To is where selecting it navigates — plugin-relative like a Link/DetailLink cell (e.g. "creator/42").
type Setting ¶
type Setting struct {
Key string `json:"key"`
Label string `json:"label"`
Kind SettingKind `json:"kind,omitempty"` // default text
Default string `json:"default,omitempty"`
Hint string `json:"hint,omitempty"`
Options []Option `json:"options,omitempty"` // for kind=select
}
Setting is one operator-managed configuration field the plugin exposes. Unlike an action's Fields (per-invocation input), settings are configured once in the plugin inspector, persisted by hope (encrypted at rest), and pushed to the plugin via the reserved hope.settings method — read them in a handler with plugin.SettingValue(key). This is distinct from the plugin's rendered panel & metrics, which live on the container inspector: settings are what the operator CONFIGURES, the panel is what the plugin SHOWS.
type SettingKind ¶
type SettingKind string
SettingKind enumerates the input type for an operator-managed plugin setting.
const ( SettingText SettingKind = "text" SettingTextarea SettingKind = "textarea" SettingSelect SettingKind = "select" SettingToggle SettingKind = "toggle" SettingNumber SettingKind = "number" SettingSecret SettingKind = "secret" // masked input; hope stores it encrypted, never renders it back )
type StatBlock ¶
type StatBlock struct {
Label string `json:"label"`
Value any `json:"value"`
Unit string `json:"unit,omitempty"`
Sub string `json:"sub,omitempty"`
Tone string `json:"tone,omitempty"` // ok|warn|bad|info
Icon string `json:"icon,omitempty"`
Tip *Tooltip `json:"tip,omitempty"` // hover tooltip explaining the metric (build with Tip)
}
StatBlock is one stat: a big Value with a Label, optional Unit, a Sub line (e.g. a delta or context), a semantic Tone, and an optional Icon.
type StatData ¶
type StatData struct {
Stats []StatBlock `json:"stats"`
}
StatData is what a Stat view returns: one or more big-number stat blocks (counts, totals, sizes). Return {stats: [...]} for a row, or a single Stat.
type StatusReport ¶ added in v0.3.0
type StatusReport struct {
Status string `json:"status"` // short human status ("primary", "3/3 replicas", "lag 4s")
Level string `json:"level"` // ok | info | warn | error
Detail string `json:"detail,omitempty"` // optional longer explanation
}
StatusReport is a plugin's self-reported health, returned from the OnStatus handler when hope calls the reserved hope.status method. hope owns liveness (whether the plugin is reachable at all) and treats this as ADVISORY domain health it has no knowledge of: it renders Status/Detail and colors by Level, nothing more. hope caps the string lengths and clamps Level to the known set.
type Storage ¶ added in v0.1.0
type Storage struct {
// contains filtered or unexported fields
}
Storage is a handle to this plugin install's durable key/value store, persisted by hope. Values are opaque JSON hope never interprets, namespaced to this install's stable identity (two installs of the same image, or the same install on different hosts, are isolated; replicas of one install share it — last write wins). Requires the storage permission and hope's reverse channel; every op returns ErrNoReverseChannel until hope.init delivers a callback URL.
Use it for config a stateless plugin has nowhere else to keep — e.g. alert rules the operator defined. It's for small config, not bulk data (hope caps value size).
func (*Storage) Get ¶ added in v0.1.0
Get decodes the value at key into out. Returns (false, nil) when the key is absent.
type StreamDesc ¶
type StreamDesc struct {
Method string `json:"method"`
Label string `json:"label"`
Kind StreamKind `json:"kind"`
Icon string `json:"icon,omitempty"`
}
StreamDesc describes a live stream and how to render it.
type StreamFunc ¶
StreamFunc emits frames until it returns or ctx is cancelled (which hope does the moment the UI disconnects — so a dropped viewer never leaves you emitting forever). Always select on ctx.Done() in long loops.
type StreamKind ¶
type StreamKind string
StreamKind tells hope how to render a live NDJSON stream.
const ( Counter StreamKind = "counter" // number(s) ticking -> stat Log StreamKind = "log" // append-only lines Series StreamKind = "series" // time series -> sparkline StreamComponent StreamKind = "component" // each frame is a component tree (Box/KeyVal/...) hope renders live — full custom output )
type Surface ¶
type Surface string
Surface names a mount point in the hope UI. The schema defines them all so the wire is forward-compatible; hope renders the surfaces it knows and silently ignores any it doesn't, so a newer plugin degrades gracefully on older hope.
const ( SurfaceContainer Surface = "container" // panel/tab in the container inspector SurfacePage Surface = "page" // full custom nav page (+ dynamic nested pages) SurfaceRail Surface = "rail" // rail/nav entry + actions SurfaceDashboard Surface = "dashboard" // fleet/host dashboard widget SurfaceStack Surface = "stack" // stack-view widget (matched to the stack's containers) SurfaceCommand Surface = "command" // command-palette entry )
type TableData ¶
type TableData struct {
Columns []string `json:"columns"`
Rows [][]any `json:"rows"`
Total int `json:"total"`
Hidden []string `json:"hidden,omitempty"`
// ColumnTips maps a column name to a hover tooltip on its header — for clarifying
// a terse or computed column (e.g. "health": Tip("seq-scan / bloat state")).
ColumnTips map[string]*Tooltip `json:"column_tips,omitempty"`
// RowMethod makes rows clickable, opening the row-detail modal via that method —
// the data-side equivalent of the RowDetail table option (for views without opts,
// like a QueryView).
RowMethod string `json:"row_method,omitempty"`
// RowFlyout makes rows clickable into a right-side DRAWER instead of a modal. The
// method receives the clicked {row} and returns a component tree (plugin.Box(...)) hope
// renders in the flyout. Data-side equivalent of the RowFlyout table option. When both
// RowFlyout and RowMethod are set, the flyout wins.
RowFlyout string `json:"row_flyout,omitempty"`
// RowFlyoutWidth sizes the drawer the flyout opens in: a preset ("small" | "large" |
// "xlarge") or an explicit px value ("680"). Empty = the default (~460px).
RowFlyoutWidth string `json:"row_flyout_width,omitempty"`
// RowFlyoutRefresh, when > 0, makes an OPEN flyout re-invoke its method every N seconds
// with the same row and swap the body in place (scroll preserved) — so a live drawer
// (e.g. a lifecycle Timeline) follows along without reopening. 0 = one-shot (default).
RowFlyoutRefresh int `json:"row_flyout_refresh,omitempty"`
// Flush renders the table full-height with no toolbar (filter/pager) and no inner scroll
// box — it flows and the containing panel/flyout scrolls instead. Meant for an embedded
// CTable showing a bounded list (e.g. the badges on a canvas), not a big paged view.
Flush bool `json:"flush,omitempty"`
// Scroll lets a WIDE table (many columns) scroll HORIZONTALLY — each column keeps its
// natural width and a scrollbar appears — instead of cramming every column into the
// container so nothing feels squeezed. Set with HScroll.
Scroll bool `json:"scroll,omitempty"`
}
TableData is what a Table view returns: the column names, the rows (each a slice of cells — plain scalars or rich cells like Badge/Link/Image), the total row count (for the pager; for a server table this is the full count, not len(Rows)), and optional hidden column names (kept in each row for row-detail/actions but not rendered). Build it with plugin.Table(...).
type TableOpt ¶
type TableOpt func(*ViewDesc)
TableOpt configures an interactive Table view (row-click detail, row actions).
func DefaultSort ¶
DefaultSort sets the sort a server table applies on first load (before the user touches a column header) — e.g. DefaultSort("indexed", "desc") for newest-first. dir is "asc" or "desc"; column must be one your handler's sort map accepts.
func Editable ¶
Editable makes cells editable: editing one calls method with {row, column, value}. Pass column names to limit which are editable (none => every column).
func Facets ¶
Facets adds dropdown filters to a server table; the selected values arrive in the query as filters[key] (read with ReadTableQuery). Apply them in your store.
func HScroll ¶ added in v0.5.0
func HScroll() TableOpt
HScroll lets a wide table VIEW scroll horizontally (columns keep their natural width and a scrollbar appears) instead of cramming every column into the container. For an embedded CTable, set TableData.Scroll directly (or use CScroll).
func NoFilter ¶
func NoFilter() TableOpt
NoFilter hides a table's search box — for a plain paged list with no user search.
func NoSort ¶
func NoSort() TableOpt
NoSort makes a table's column headers non-interactive — the order is fixed by your handler (e.g. always newest-first), not user-sortable. Pair with NoFilter for a pure paged list.
func RowActions ¶
RowActions adds per-row action buttons; each click calls the action's Method with {row: {column: value}}. Use for row-scoped mutations like "delete row".
func RowDetail ¶
RowDetail makes table rows clickable: a click calls method with {row: {column: value}} and shows the returned kv/table in a modal (an author-controlled RPC).
func RowDetailButton ¶
RowDetailButton is like RowDetail but triggers from a per-row button instead of a whole-row click — use it when the row is also inline-editable so the click to edit a cell and the click to open the detail don't collide.
func RowFlyout ¶ added in v0.0.8
RowFlyout makes table rows clickable into a right-side DRAWER (not a modal): a click calls method with {row: {column: value}} and hope renders the returned component tree (plugin.Box(...) — image, key/values, Buttons bound to actions) in the flyout. Richer than RowDetail's modal; good for a compact record shown beside the table. If both RowFlyout and RowDetail are set, the flyout wins.
func RowFlyoutRefresh ¶ added in v0.5.3
RowFlyoutRefresh makes an OPEN row flyout re-invoke its method every `seconds` with the same row and re-render the returned tree in place (scroll preserved) — so a live drawer (a lifecycle Timeline, a progress view) follows along without the operator reopening it. 0 = one-shot (today's behavior). Pair with RowFlyout. Feature-gated: hope advertises "flyout-refresh" (check Caps(ctx).Supports); older hope ignores the option.
func RowFlyoutWidth ¶ added in v0.4.0
RowFlyoutWidth sizes the row-flyout drawer: a preset ("small" | "large" | "xlarge") or an explicit px value ("680"). Empty = the default (~460px). Pair with RowFlyout.
func ServerSide ¶
func ServerSide() TableOpt
ServerSide marks a table server-driven: hope sends the query state each call and expects one page + a total back, so a table too large to ship whole still works. Read the query with plugin.ReadTableQuery and return {columns, rows, total}.
type TableQuery ¶
type TableQuery struct {
Page int `json:"page"`
PageSize int `json:"page_size"`
Sort TableSort `json:"sort"`
Filter string `json:"filter"`
Filters map[string]string `json:"filters"`
}
TableQuery is hope's per-call query state for a ServerSide table: which page (0- based) of what size, an optional sort, the free-text filter, and any Facet selections (filters[key] = chosen value). Use it to run just that slice of your data and return {columns, rows, total}.
func ReadTableQuery ¶
func ReadTableQuery(ctx context.Context) (q TableQuery, ok bool)
ReadTableQuery reads the server-table query state hope sends (under "_q"). ok is false when the call carried none (e.g. a non-server table) — treat that as page 0.
type TableSort ¶
TableSort is the sort request for a server-driven table: which column, and the direction (1 ascending, -1 descending; 0 = unsorted).
type TextData ¶
type TextData struct {
Text string `json:"text"`
}
TextData is what a Text view returns: a block of monospace text (logs, config, command output). A Text handler may also return a raw string.
type TipPos ¶ added in v0.0.4
type TipPos string
TipPos is where a tooltip points relative to its target.
type Tooltip ¶ added in v0.0.4
Tooltip is hover help with an optional placement. Build one with Tip("text") or Tip("text", plugin.TipBottom). Empty Pos renders at the top.
type TreeData ¶ added in v0.0.5
type TreeData struct {
Nodes []TreeNode `json:"nodes"`
}
TreeData is what a Tree view returns: a hierarchy of nodes. Return it (or a bare map literal) from a Tree handler.
type TreeNode ¶ added in v0.0.5
type TreeNode struct {
Label string `json:"label"`
Icon string `json:"icon,omitempty"` // built-in icon name or an Icons key
Tone string `json:"tone,omitempty"` // ok|warn|bad|info dot
To string `json:"to,omitempty"` // plugin-relative nav target (like a Link cell)
Collapsed bool `json:"collapsed,omitempty"`
Tip *Tooltip `json:"tip,omitempty"`
Children []TreeNode `json:"children,omitempty"`
// Actions are per-node buttons (rename/delete/…), shown on hover — reuses RowAction, so
// Icon/Danger/Fields/Tip all work. Clicking one runs its Method with Args as the {row}.
Actions []RowAction `json:"actions,omitempty"`
Args map[string]any `json:"args,omitempty"` // the {row} context passed to this node's Actions
}
TreeNode is one node in a Tree view. Children make it a collapsible group (Collapsed starts it closed). A non-empty To makes the label a plugin-relative link (like a Link/DetailLink cell), so a tree can navigate — e.g. To: "table/public.users". Icon and Tone add a leading icon and a status dot; Tip adds a hover tooltip. A node can be both a group and a link: the caret toggles children, the label navigates.
type ValidateFunc ¶ added in v0.5.0
type ValidateFunc func(ctx context.Context) ([]FieldError, error)
ValidateFunc validates the current form values (Params) and returns per-field errors (#4). hope calls it as the values change; an empty slice means the form is valid and Run enables.
type ViewDesc ¶
type ViewDesc struct {
Method string `json:"method"`
Label string `json:"label"`
Kind ViewKind `json:"kind"`
Icon string `json:"icon,omitempty"`
// Empty customizes the "no data" state (see EmptyState); unset => hope's generic text.
Empty *EmptyState `json:"empty,omitempty"`
Lang string `json:"lang,omitempty"` // query views: syntax-highlight language (sql, json, …)
Default string `json:"default,omitempty"` // query views: initial text; {param} placeholders are filled from the page param
// RowMethod (table/query views): a method hope calls to open a row-detail modal,
// with params {row: {column: value}}. The result (kv or table) is shown in a
// modal — a fully author-controlled row-detail RPC.
RowMethod string `json:"row_method,omitempty"`
// RowFlyout (table/query views): like RowMethod but opens a right-side DRAWER instead
// of a modal. The method receives {row} and returns a component tree hope renders in
// the flyout. When both are set, the flyout wins.
RowFlyout string `json:"row_flyout,omitempty"`
// RowFlyoutWidth sizes the drawer: a preset ("small" | "large" | "xlarge") or an
// explicit px value ("680"). Empty = the default (~460px).
RowFlyoutWidth string `json:"row_flyout_width,omitempty"`
// RowFlyoutRefresh re-invokes an OPEN flyout's method every N seconds (same row) and
// swaps the body in place — a live drawer. 0 = one-shot (default). Set with RowFlyoutRefresh.
RowFlyoutRefresh int `json:"row_flyout_refresh,omitempty"`
// Scroll lets a WIDE table view (many columns) scroll HORIZONTALLY — columns keep their
// natural width and a scrollbar appears — instead of cramming them into the container.
// Set with HScroll.
Scroll bool `json:"scroll,omitempty"`
// --- Paged view (ViewKind Paged) ---
// ItemTemplates maps an Item's Type -> a component template (B). Placeholders {field} in
// the template's text/values are bound to the item's Data. Register with ItemTemplate.
ItemTemplates map[string]*Comp `json:"item_templates,omitempty"`
// Layout arranges a Paged view's items: "list" (default) | "grid" | "flow". Set with Layout.
Layout string `json:"layout,omitempty"`
// Infinite renders a Paged view with load-more-on-scroll instead of a pager. Set with Infinite.
Infinite bool `json:"infinite,omitempty"`
// RowDetailButton triggers RowMethod from a dedicated per-row button instead of a
// whole-row click. Use when the row body is otherwise interactive (e.g. inline-
// editable cells) so the two don't fight.
RowDetailButton bool `json:"row_detail_button,omitempty"`
// RowActions (table/query views): per-row action buttons. hope renders each in a
// trailing column (and in the row-detail modal); clicking one calls its Method
// with {row: {column: value}} — an author-controlled mutation like "delete row".
// A Danger action is confirmed first; on success hope refetches the table.
RowActions []RowAction `json:"row_actions,omitempty"`
// PageSize (table/query views) sets how many rows hope shows per page — the
// author knows the shape of their data, so paging is plugin-level. 0 => hope's
// default.
PageSize int `json:"page_size,omitempty"`
// EditMethod (table/query views): editing a cell calls this method with
// {row: {column: value}, column, value}. Return ok (and optionally a message).
// EditColumns limits which columns are editable (empty => all). An
// author-controlled inline-edit RPC — hope refetches the table on success.
EditMethod string `json:"edit_method,omitempty"`
EditColumns []string `json:"edit_columns,omitempty"`
// Server marks a table server-driven: hope does NOT ship every row and page in
// the browser. Instead it sends the query state ({_q: {page, page_size, sort,
// filter}}) on each call and expects one page back plus a total row count
// ({columns, rows, total}). Required for tables too large to send whole — read
// the query with plugin.ReadTableQuery.
Server bool `json:"server,omitempty"`
// Refresh adds a manual refresh button to the view header that re-fetches it —
// e.g. a stat/counter you want to recompute on demand.
Refresh bool `json:"refresh,omitempty"`
// RefreshInterval auto-refetches the view every N seconds (0 = off) — a live-ish
// view without a stream. hope stops the timer when the view leaves the DOM.
RefreshInterval int `json:"refresh_interval,omitempty"`
// Static marks a view's data as fixed for the life of the surface: hope fetches it
// once and serves the cached result on tab re-entry / re-navigation instead of
// re-calling the plugin — cutting round-trips and easing the per-plugin rate limit.
// A manual Refresh() button still forces a re-fetch, so Static()+Refresh() = "load
// once, refresh on demand". Don't combine with RefreshInterval.
Static bool `json:"static,omitempty"`
// Facets (server tables) are dropdown filters hope renders in the toolbar; the
// selected values arrive in the query as filters[key] (see TableQuery.Filters),
// which you apply in your store. Distinct from the free-text search box.
Facets []Facet `json:"facets,omitempty"`
// DefaultSort (server tables) is the sort hope applies on FIRST load, before the
// user clicks any column header — e.g. newest-indexed first. hope seeds the query
// state with it (so it arrives in TableQuery.Sort) and shows the arrow on that
// column. The user can still re-sort. Column must be one your handler accepts.
DefaultSort *SortSpec `json:"default_sort,omitempty"`
// NoFilter hides the search box and NoSort makes column headers non-interactive —
// for a plain paged list (a fixed order your handler controls, no user search/sort).
NoFilter bool `json:"no_filter,omitempty"`
NoSort bool `json:"no_sort,omitempty"`
// --- Graph view (ViewKind Graph) — all optional method refs; a bare GraphView is just
// the canvas. Set with the Graph* opts. hope calls each mutation with {row:{...}} (like a
// RowAction) and re-fetches the graph on success. ---
GraphMove string `json:"graph_move,omitempty"` // persist a dragged node's position {id,x,y}
GraphConnect string `json:"graph_connect,omitempty"` // add an edge {from:"n:p", to:"n:p"}
GraphDisconnect string `json:"graph_disconnect,omitempty"` // remove an edge {from,to}
GraphDelete string `json:"graph_delete,omitempty"` // delete a node {id}
GraphAdd string `json:"graph_add,omitempty"` // create a node {type,x,y} (palette drop / dbl-click)
GraphNodeFlyout string `json:"graph_node_flyout,omitempty"` // node click -> a Comp body in the drawer {id}
GraphConfig string `json:"graph_config,omitempty"` // persist a node's config form: {id, ...field values}
GraphMenu string `json:"graph_menu,omitempty"` // right-click -> []GraphMenuItem for {kind, id/from/to}
GraphValidateConnect string `json:"graph_validate_connect,omitempty"` // optional pre-connect gate {from,to} -> {ok,reason}
GraphRun string `json:"graph_run,omitempty"` // a StreamComponent-kind stream emitting GraphData frames (run highlight)
GraphSidebar string `json:"graph_sidebar,omitempty"` // left rail: a Comp (browse the plugin's DAGs) {}
GraphSidebarActions []string `json:"graph_sidebar_actions,omitempty"` // action-method refs rendered as buttons atop the sidebar (New, ...)
GraphToolbar string `json:"graph_toolbar,omitempty"` // top strip: a Comp (title/run/filters) {}
GraphSelect string `json:"graph_select,omitempty"` // sidebar row click -> set active graph {id}; canvas refetches with {graph:id}
GraphPalette string `json:"graph_palette,omitempty"` // the node-TYPE catalog: returns []NodeType
GraphTypeInfo string `json:"graph_type_info,omitempty"` // palette info button -> a Comp about a node TYPE {type} (view before adding)
Palette []NodeType `json:"palette,omitempty"` // a STATIC node-type catalog (alternative to GraphPalette)
GraphDirected bool `json:"graph_directed,omitempty"` // draw arrowheads on edges
GraphSnap int `json:"graph_snap,omitempty"` // snap dragged nodes to an N-px grid (0 = free)
}
ViewDesc describes a read-only data view and how to render it.
type ViewFunc ¶
ViewFunc returns the data for a view; hope renders it per the view's ViewKind. For a Query view, the user's input arrives in the request params — read it with plugin.Input(ctx) (or plugin.Params for structured input).
type ViewKind ¶
type ViewKind string
ViewKind tells hope how to render a view's returned data.
const ( KV ViewKind = "kv" // flat key/value map -> hope-kvlist Table ViewKind = "table" // {columns, rows} -> paginated grid Query ViewKind = "query" // user-edited input -> tabular result grid (e.g. a SQL box) Tree ViewKind = "tree" // hierarchy -> tree browser (e.g. a schema) Chart ViewKind = "chart" // {type, labels, series} -> bar/line chart (see ChartData) Cards ViewKind = "cards" // {items:[Card]} -> a responsive grid of cards (a gallery) Stat ViewKind = "stat" // {stats:[Stat]} (or one Stat) -> big-number stat blocks (see StatData) Text ViewKind = "text" // {text:"…"} (or a raw string) -> a monospace scrollable block (logs, config, output) Paged ViewKind = "paged" // a Page{Items,...} -> a server-paged collection; each item renders via its Comp (A) or a per-type ItemTemplate (B). Layout list|grid|flow. // Search is an autocomplete box: hope calls the method with {q: <typed text>} as the // user types (debounced) and renders the returned SearchItems as a live dropdown. // Selecting one navigates to its To (a DetailPage/link target) — a "go to X" jump. Search ViewKind = "search" // CompView is the escape hatch: the handler returns a *Comp — a tree of safe // primitives (box/row/heading/keyval/sparkline/cell/…) hope composes into a custom // widget the built-in kinds don't cover. See component.go. Also usable inline in a // layout via plugin.Component (no per-view round-trip). CompView ViewKind = "component" // Graph is the spatial / blueprint surface: the handler returns a GraphData of nodes at // x/y with edges between ports. hope ships NO node types — the plugin defines each node's // title/ports/body (an arbitrary Comp) and owns persistence + execution; hope is the // editor (pan/zoom, drag, connect) and calls the plugin's mutation methods, then re-fetches. // See GraphData + p.GraphView. Graph ViewKind = "graph" )
type ViewOpt ¶
type ViewOpt = TableOpt
ViewOpt configures a view (Refreshable, PageSize on tables, …). TableOpt is the same underlying type, so table options compose here too.
func EmptyView ¶ added in v0.0.6
EmptyView customizes the "no data" state shown when a view resolves empty (an empty table, a stat with no blocks) instead of hope's generic text — a title plus optional icon/text (or a custom Comp). Attach it wherever view opts are accepted (TableView, StatView, ComponentView), e.g.
p.TableView("slow", "Slow queries", fn,
plugin.EmptyView("No slow queries 🎉", plugin.EmptyIcon("check")))
func Infinite ¶ added in v0.5.0
func Infinite() ViewOpt
Infinite renders a Paged view with load-more-on-scroll instead of a pager.
func ItemTemplate ¶ added in v0.5.0
ItemTemplate registers a per-Type component template for a Paged view (B): hope binds an item's Data into it, substituting {field} placeholders in text/values. Register one per item Type; an item that instead carries its own Comp (A) uses that; an item with neither falls back to a key/value render.
func PageLayout ¶ added in v0.5.0
PageLayout arranges a Paged view's items: "list" (default) | "grid" | "flow".
func RefreshEvery ¶
RefreshEvery auto-refetches the view every n seconds (a live-ish view without a stream). hope stops the timer when the view leaves the DOM.
func Refreshable ¶
func Refreshable() ViewOpt
Refreshable adds a manual refresh button to the view header (re-fetches on click).
func Static ¶ added in v0.0.6
func Static() ViewOpt
Static marks a view's data fixed for the life of the surface: hope fetches it once and reuses the cached result on tab re-entry / re-navigation instead of re-calling the plugin — fewer round-trips, less rate-limit pressure. Pair with Refreshable() for a "load once, refresh on demand" view. Don't combine with RefreshEvery.
type WizardStep ¶ added in v0.5.0
type WizardStep struct {
Title string `json:"title,omitempty"`
Hint string `json:"hint,omitempty"`
Fields []Field `json:"fields"`
}
WizardStep is one page of a stepped action form — a title (+ optional hint) over a set of fields the modal renders on its own step. Build with Step / StepHint.
func Step ¶ added in v0.5.0
func Step(title string, fields ...Field) WizardStep
Step builds one wizard step: a titled set of fields. Pass steps to ActionSteps.
func StepHint ¶ added in v0.5.0
func StepHint(title, hint string, fields ...Field) WizardStep
StepHint is Step with a helper line shown under the step title.