中文
Background services and panels

Background services and panels

Provide continuous plugin state and user interactions

Create a project with --with-panel to include a working counter panel. The installed package descriptor contributes the panel; workflow nodes operate existing panels rather than registering new definitions.

Parts of the example

  • internal/paneldefinition declares stable IDs, a number field, its display and an increment button with bilingual labels.
  • cmd/companion owns in-memory state and HTTP snapshot/event endpoints.
  • cmd/package/panel.go includes the executable, panel definition and endpoint paths in the signed package.

Adding a panel to a node-only project requires all three parts, not just setting plugin.json.panel=true.

Components and fields

Use group for hierarchy; text, number and status for values; timer and progress for time and progress; log for bounded records; and button, select, toggle, input for interactions. Fields use string, number or boolean values. Keep field and component IDs stable when changing labels. A select declares string choices, and interaction components declare event names.

The panel is a declarative tree, not an embedded HTML, JavaScript or Vue application. New component kinds require a corresponding public contract and host renderer.

Provider protocol

The counter template uses these relative paths, declared in its descriptor:

Method and path Result
GET /health 200 JSON with matching protocol and status
GET /v1/panel panel.Snapshot
POST /v1/events Accepts panel.Event, returns panel.Result
POST /stop 204, then completes the request and shuts down

Health returns {"protocol":"yotta.panel-provider/v1","status":"ready"}; an empty 204 does not satisfy startup verification. plugin.json.panelPort controls the packaged launch arguments and loopback origin. Repackage to change it instead of silently selecting an undeclared port.

A snapshot carries protocol, sessionId, revision, status, field values, controlRevisions and records. Generate a new sessionId on each start and monotonically increase revision. Status values are ready, waiting, stale, unavailable and ended.

An event carries sessionId, eventId, componentId, event name, control revision and a typed value: null for buttons, boolean for toggles, string for inputs and selects. A result returns the same eventId and current snapshot.

Handle retries and lifecycle

The template remembers the latest 1024 event requests and results. Identical retries return the original result without applying the operation twice; a reused ID with different content returns 409. Old sessions and stale control revisions also return 409. A lost response is not a reason to retry an effect with a new ID.

Data revision and control revision have different meanings. Telemetry updates should not invalidate an interaction being filled in or generate user events. Persist receipts when the business requires replay across restarts; the counter example loses its in-memory state on restart and rejects old-session events.

The host manages companion startup, health and shutdown. Closing a panel must not stop a provider still used by another workflow or collection task. Keep continuous collection in the companion and finish individual node calls promptly. Use the public WorldPosition type for world positions when applicable.

Workflow panel nodes can select, show, read, write, append logs and wait for interactions. Provider-owned telemetry is not directly writable by workflows; controls change through events. A value write is not a user click.

Verify discovery after installation, stable references when reopening, exactly-once button handling, rejection of old sessions after restart, unavailable/recovery states and lifecycle behavior with multiple users. Template httptest coverage does not replace an isolated App journey.

Settings tabs

The panel title-bar gear supports multiple settings tabs. Keep size, opacity, focus and click-through in Window settings. Put business options such as calibration and display rules in a plugin settings tab. Settings tabs do not create standalone panels or extra content tabs; avoid a separate website settings dialog.

A Website contribution can declare up to eight settingsPanels. Each entry is a standard panel.Contribution, with companionId, snapshotPath, eventPath and definition. Its definition uses yotta.panel/v1, a unique ID within the Website, translated titleKey, typed fields and standard components. For example, an input bound to a string factor field sends a set-factor event. Include the translation keys in both locales and declare the referenced companion. The host renders the controls; website content still receives no host bridge.

Use the regular snapshot/event protocol described above. Inputs send strings, buttons send null; the provider validates values, deduplicates events and keeps control revisions separate from live telemetry revisions. A declared position dependency adds the host source picker to the first plugin settings tab, or to a dedicated Data source tab when no custom tab exists. The source is shared by the package; applying it restarts the consuming provider and resets session statistics.

Pass ${plugin-data}/settings.json as a companion argument for persistent settings. The host expands ${plugin-data} to a stable package-specific directory in the current profile. Package updates retain the path; companions should use separate filenames. Providers own validation, atomic writes and backup/recovery of corrupt data. Migrate existing website localStorage preferences once when upgrading.

Verify small-window layout, reopening and persistence, absence of extra regular panels, source changes and provider restart behavior. This development feature requires a matching SDK and host with settingsPanels and ${plugin-data} support.

Companion pages and dynamic ports

For a HUD served by your own companion, declare companionId: "capture" and path: "/hud" on the Website instead of url. The ID references a companion in the same package. Yotta resolves its configured origin, including dynamic ports, and prepares it and its dependencies before opening the page. The two address forms are mutually exclusive. Settings still use settingsPanels; website content receives no host bridge.