Documentation
¶
Index ¶
- Constants
- func AddClass(id, className string)
- func AddEventListener(el JSValue, event string, fn func())
- func AddEventListenerWithEvent(el JSValue, event string, fn func(JSValue))
- func AppendChild(parent, child JSValue)
- func BeginBatch()
- func BroadcastTopicEncoded(keyName, encoded string)
- func BroadcastTopicEncodedField(keyName, fieldName, encoded string)
- func BroadcastTopicOnline(keyName, encoded string)
- func CaptureScope() string
- func ClickElement(el JSValue)
- func ConsoleLog(args ...any)
- func CookieDelete(key string)
- func CookieGet(key string) string
- func CookieSet(key, value string, opts ...CookieOptions)
- func CopyBytesToGo(dst []byte, src JSValue) int
- func CopyBytesToJS(dst JSValue, src []byte) int
- func CreateTopic[T any](zero T, cfg TopicConfig) func() interface{}
- func CreateWasmBoolFunc(name string, fn func(bool))
- func CreateWasmFunc(name string, fn func())
- func CreateWasmStringFunc(name string, fn func(string))
- func DurableKey() string
- func DurableObserve[T any](field string, obs *Observable[T], encode func(T) string, decode func(string) T)
- func EndBatch()
- func ExecJS(script string)
- func Fetch(url string, config ...FetchConfig) (string, error)
- func FetchBytes(url string, config ...FetchConfig) ([]byte, error)
- func GetFileBytes(id string) []byte
- func GetValue(id string) string
- func GoBack()
- func GothicHaltChan() <-chan struct{}
- func GothicRegisterSchema(key, schemaID, descriptor string)
- func GothicRegisterScope(body func())
- func GothicRegisterWithCore(scopeID, schemaID, schema string)
- func HexDecode(s string) []byte
- func HexEncode(src []byte) string
- func ListenTopicCoreOnline(key string, fn func())
- func ListenTopicEvent(keyName string, fn func(string))
- func ListenTopicEventField(keyName, fieldName string, fn func([]byte))
- func ListenTopicOnline(keyName string, fn func(string))
- func ListenTopicPing(keyName string, fn func())
- func ListenTopicSetReq(keyName string, fn func(string))
- func ListenTopicSetReqField(keyName, fieldName string, fn func(string))
- func LocalStorageGet(key string) string
- func LocalStorageRemove(key string)
- func LocalStorageSet(key, value string)
- func Navigate(url string)
- func OnUnmount(fn func())
- func PingTopicManager(keyName string)
- func PingUntilOnline(keyName string, isOnline func() bool)
- func PushState(url, title string)
- func ReadTopicStore(keyName string) (string, bool)
- func RegisterTopicWithCore(key string, fields []string)
- func Reload()
- func RemoveClass(id, className string)
- func RemoveElement(el JSValue)
- func RequestTopicSet(keyName, encoded string)
- func RequestTopicSetField(keyName, fieldName, encoded string)
- func RequestTopicSetFieldBytes(keyName, fieldName string, b []byte)
- func RunInScope(id string, fn func())
- func SessionStorageGet(key string) string
- func SessionStorageRemove(key string)
- func SessionStorageSet(key, value string)
- func SetAttr(id, attr, value string)
- func SetHTML(id, html string)
- func SetStyle(id, property, value string)
- func SetText(id, value string)
- func SetValue(id, value string)
- func ToggleClass(id, className string)
- func TriggerDownload(filename string, data []byte, mimeType string)
- func UpdateTopicOnlineStore(keyName string, encoded []byte)
- func WriteClipboard(text string)
- type Compression
- type CookieOptions
- type Decoder
- func (d *Decoder) Bool() bool
- func (d *Decoder) Bytes() []byte
- func (d *Decoder) F32() float32
- func (d *Decoder) F64() float64
- func (d *Decoder) I32() int32
- func (d *Decoder) I64() int64
- func (d *Decoder) String() string
- func (d *Decoder) U8() uint8
- func (d *Decoder) U16() uint16
- func (d *Decoder) U32() uint32
- func (d *Decoder) U64() uint64
- type Encoder
- func (e *Encoder) Bool(v bool)
- func (e *Encoder) Bytes(v []byte)
- func (e *Encoder) F32(v float32)
- func (e *Encoder) F64(v float64)
- func (e *Encoder) I32(v int32)
- func (e *Encoder) I64(v int64)
- func (e *Encoder) String(v string)
- func (e *Encoder) U8(v uint8)
- func (e *Encoder) U16(v uint16)
- func (e *Encoder) U32(v uint32)
- func (e *Encoder) U64(v uint64)
- type FetchConfig
- type JSValue
- func CreateElement(tag string) JSValue
- func CreateWasmFuncWithReturn(name string, fn func(this JSValue, args []JSValue) any) JSValue
- func Document() JSValue
- func GetElementById(id string) JSValue
- func JS() JSValue
- func QuerySelector(sel string) JSValue
- func QuerySelectorAll(sel string) JSValue
- func Window() JSValue
- func (v JSValue) Bool() bool
- func (v JSValue) Call(method string, args ...any) JSValue
- func (v JSValue) Float() float64
- func (v JSValue) Get(key string) JSValue
- func (v JSValue) Index(i int) JSValue
- func (v JSValue) Int() int
- func (v JSValue) IsNull() bool
- func (v JSValue) IsUndefined() bool
- func (v JSValue) Length() int
- func (v JSValue) New(args ...any) JSValue
- func (v JSValue) Set(key string, val any)
- func (v JSValue) SetIndex(i int, val any)
- func (v JSValue) String() string
- func (v JSValue) Truthy() bool
- type Observable
- type ObservableField
- type SharedTopicObservable
- type Subscription
- type TopicConfig
- type TopicKey
- func AutoKey[T any](name string) TopicKey[T]
- func BinaryKey[T any](name string, encode func(T, *Encoder), decode func(*Decoder) T) TopicKey[T]
- func BoolKey(name string) TopicKey[bool]
- func ByteKey(name string) TopicKey[byte]
- func CustomKey[T any](name string, encode func(T) string, decode func(string) T) TopicKey[T]
- func Float32Key(name string) TopicKey[float32]
- func Float64Key(name string) TopicKey[float64]
- func Int8Key(name string) TopicKey[int8]
- func Int16Key(name string) TopicKey[int16]
- func Int32Key(name string) TopicKey[int32]
- func Int64Key(name string) TopicKey[int64]
- func IntKey(name string) TopicKey[int]
- func RuneKey(name string) TopicKey[rune]
- func StringKey(name string) TopicKey[string]
- func Uint8Key(name string) TopicKey[uint8]
- func Uint16Key(name string) TopicKey[uint16]
- func Uint32Key(name string) TopicKey[uint32]
- func Uint64Key(name string) TopicKey[uint64]
- func UintKey(name string) TopicKey[uint]
- type WasmCompiler
Constants ¶
const WireVersion byte = 1
WireVersion is the codec's frame-format version. It is written as byte 0 of every top-level frame by NewEncoder and validated by NewDecoder. Bumping it is an intentional, irreversible wire break: a decoder built for one version rejects frames carrying any other. Nested encodes (_encode_X, _encode_sliceX) receive an already-opened *Encoder and therefore never re-emit this byte — only the frame's opening carries it.
Variables ¶
This section is empty.
Functions ¶
func AddEventListener ¶
AddEventListener attaches a persistent event listener to el for the given event name. fn is called with no arguments each time the event fires. The js.Func is retained in keep and never released — persistent listeners must stay alive for the lifetime of the page; releasing them would panic on the next event.
func AddEventListenerWithEvent ¶
AddEventListenerWithEvent attaches a persistent event listener to el for the given event name. fn receives the browser Event object as a JSValue, giving access to event properties and methods such as preventDefault(), stopPropagation(), event.target, event.key, event.detail, etc. The js.Func is retained in keep and never released — persistent listeners must stay alive for the lifetime of the page; releasing them would panic on the next event.
func BeginBatch ¶
func BeginBatch()
BeginBatch defers observer notifications until the matching EndBatch. Nested calls are supported; effects only flush when depth returns to 0. Each pending Subscription is deduplicated and runs at most once per batch.
func BroadcastTopicEncoded ¶
func BroadcastTopicEncoded(keyName, encoded string)
BroadcastTopicEncoded writes encoded to the JS store and dispatches a CustomEvent.
func BroadcastTopicEncodedField ¶
func BroadcastTopicEncodedField(keyName, fieldName, encoded string)
BroadcastTopicEncodedField broadcasts an already-encoded single field value. Event name: "gothic:topic:<keyName>:<fieldName>"
func BroadcastTopicOnline ¶
func BroadcastTopicOnline(keyName, encoded string)
BroadcastTopicOnline dispatches the online ack to all consumer WASMs for this key.
func CaptureScope ¶
func CaptureScope() string
CaptureScope returns the scope active at the call site. Capture it before spawning a goroutine or scheduling async work whose body touches scoped DOM helpers, then re-establish it with RunInScope when that work runs — a goroutine does not inherit the carrier across a suspension point.
Keep any blocking call (a fetch, a channel receive) OUTSIDE RunInScope and wrap only the scoped, non-blocking work — mirroring how PingUntilOnline keeps time.Sleep outside RunInScope:
scope := CaptureScope()
go func() {
body, _ := Fetch(url) // blocks — outside RunInScope
RunInScope(scope, func() { SetText("out", body) }) // scoped, non-blocking
}()
func ClickElement ¶
func ClickElement(el JSValue)
func ConsoleLog ¶
func ConsoleLog(args ...any)
func CookieGet ¶
CookieGet reads a cookie value from document.cookie. Returns "" for missing or HttpOnly cookies.
func CookieSet ¶
func CookieSet(key, value string, opts ...CookieOptions)
CookieSet writes a cookie to document.cookie.
func CopyBytesToGo ¶
func CopyBytesToJS ¶
func CreateTopic ¶
func CreateTopic[T any](zero T, cfg TopicConfig) func() interface{}
CreateTopic declares a topic. The CLI AST scanner detects this call and generates the concrete typed accessor. At runtime this returns a no-op.
func CreateWasmBoolFunc ¶
func CreateWasmFunc ¶
func CreateWasmFunc(name string, fn func())
CreateWasmFunc registers a no-arg user callback under name. The Go closure becomes a js.Func stored in __gothic_registry[scope][name] (registerLocal), keyed by the active scope. That registry js.Func is the SOLE dispatch target: the GLOBAL proxy window[name] — the function HTML onclick/onchange invokes — is pure, instance-agnostic JS installed once per name by __gothicInstallProxy (see pkg/helpers/gothiccore/core.go). On each click that proxy resolves the clicked element's scope off the LIVE __gothic_registry and calls this scope's js.Func, so tearing down a sibling instance (which deletes its registry entry) can never leave the proxy pointing at halted Go code.
func CreateWasmStringFunc ¶
func DurableKey ¶
func DurableKey() string
DurableKey returns the STABLE durable key declared on the active scope's wrapper (data-gothic-durable-key), or "" when the placement is not durable. The DOM read lives in JS (window.__gothicDurableKey in gothic-core.js) and returns a plain string so no per-mount MouseEvent/element js.Value is boxed into TinyGo's slot table (the same _values[]-leak avoidance as findScope). Empty string is the default and means "durability off" — callers treat it as opt-out.
func DurableObserve ¶
func DurableObserve[T any](field string, obs *Observable[T], encode func(T) string, decode func(string) T)
DurableObserve binds obs to the core's durable KV as `field` under this placement's durable key. It rehydrates obs from the core BEFORE the component goes live, then persists every subsequent change for the page session. When the placement has NO durable key it is a NO-OP and obs behaves exactly as today (OPT-IN; default off). encode/decode are the field's string codec (same shape as CustomKey — reuse strconv for primitives, or a binary Encoder/Decoder for a struct field); they exist in both build worlds so the same ClientSideState block compiles server-side (no-op) and under TinyGo.
count := CreateObservable(0)
DurableObserve("count", count, strconv.Itoa,
func(s string) int { n, _ := strconv.Atoi(s); return n })
Observe(func() { SetText("out", strconv.Itoa(count.Get())) }, count)
func EndBatch ¶
func EndBatch()
EndBatch decrements the batch depth and, when it reaches zero, runs every Subscription that was queued during the batch (deduplicated).
func ExecJS ¶
func ExecJS(script string)
ExecJS executes the given script string in the global scope.
func Fetch ¶
func Fetch(url string, config ...FetchConfig) (string, error)
Fetch makes an HTTP request using the browser's fetch API and blocks until complete.
func FetchBytes ¶
func FetchBytes(url string, config ...FetchConfig) ([]byte, error)
FetchBytes makes an HTTP request and returns the response as raw bytes. Use for binary responses (images, PDFs, ZIPs) where Fetch's text() decoding would corrupt data.
func GetFileBytes ¶
GetFileBytes reads the contents of the first selected file from a <input type="file"> element. Blocks until the FileReader completes. Returns nil on error or if no file is selected.
func GothicHaltChan ¶
func GothicHaltChan() <-chan struct{}
GothicHaltChan returns the keep-alive sentinel channel. The generated WASM main() selects on it so the module returns when the bootstrap's per-scope teardown invokes this instance's __gothic_halt callback.
func GothicRegisterSchema ¶
func GothicRegisterSchema(key, schemaID, descriptor string)
GothicRegisterSchema records a topic/component type's compact schema descriptor under its content-hash id at the point the type registers. This is the Phase 15 SCHEMA SEAM: a reserved, additive control-plane slot for a future generic wire interpreter (Phase 16's core stores it opaquely). NOTHING interprets it in v3.0 — it is written once, off the data-plane, and never read back by any 3.0 consumer. The descriptor is deposited on window.__gothicSchemas (keyed by schemaID) so the core can later pick it up without a wire change.
Generated code only (like the Broadcast*/Listen* helpers); it is never hand-written in a ClientSideState block, so it has no user-facing stub-parity obligation.
func GothicRegisterScope ¶
func GothicRegisterScope(body func())
GothicRegisterScope wires a Multiplexed page's ClientSideState body into the per-scope registration system. The generated main() of a Multiplexed route calls it with the ClientSideState body wrapped in a closure, instead of running that body inline. It:
Registers the instance's own mount scope immediately by running body under runInScope(bootstrapScope). This makes the first placement behave exactly like a non-multiplexed instance: body runs under the bootstrap scope, so its observables/callbacks/listeners land in __gothic_registry[bootstrapScope] — byte-identical to the non-multiplexed registration path.
Publishes a per-instance __gothic_register_scope(id) callback onto this module's window.__gothicInstances[<bootstrapScope>] slot (the same slot the Phase-12 __halt lives on). The bootstrap JS invokes it for every SUBSEQUENT placement of the same component type, running the SAME body under runInScope(id). Each invocation re-runs body, creating FRESH observables and callbacks via new closures — this is why Phase 13's scope refactor is the gate for multiplexing: one instance hosts N independent scopes, each with its own state, all resolved per active scope.
Publishing onto the per-instance slot (not a window global) mirrors __halt: portable across the GothicTinyGo/LocalTinyGo/Golang compilers AND correct on a multi-instance page. main() runs synchronously up to its keep-alive select during go.run, so the callback is set before the bootstrap flushes its pending queue.
func GothicRegisterWithCore ¶
func GothicRegisterWithCore(scopeID, schemaID, schema string)
GothicRegisterWithCore performs the component→core registration RPC against the Phase-16 full-Go static core over the `document` control-plane bus. It hands the core an OPAQUE schema descriptor keyed by (scopeID, schemaID): the core records {scopeId, schemaId, schema} verbatim and acks — it never interprets the descriptor (the generic interpreter is DEFERRED).
Ordering mirrors the topic online/ping handshake and handles both startup races WITHOUT a goroutine:
- core already up → the immediate send is received and acked.
- core comes up later → the core announces `gothic:core:online` on boot; this module's online listener re-sends the registration until it is acked.
Asyncify safety is a TWO-SIDED contract. This side (outbound register) wraps every dispatch in queueMicrotask (like the topic bus's __gothicDispatchAsync) so the register fires from a clean call stack, never from inside this module's running scheduler turn. The RETURN side (the core's ack + online announce) is symmetric: the full-Go core schedules those on its own microtask (see pkg/wasm/core-runtime scheduleDispatch), so the ack that lands here does NOT re-enter this component's asyncify turn.
Generated code only (like GothicRegisterSchema / the Broadcast*/Listen* helpers); it is never hand-written in a ClientSideState block, so it carries no user-facing stub-parity obligation (its host no-op lives in events_stub.go).
func ListenTopicCoreOnline ¶
func ListenTopicCoreOnline(key string, fn func())
ListenTopicCoreOnline registers a handler for the core's per-key online ack (gothic:core:topic-online:<key>). The core dispatches it AFTER replaying the topic's current per-field state to this consumer, so by the time the handler runs the per-field ListenTopicEventField handlers have already applied the replayed values. The generated consumer uses it to flip _online and flush any pending whole-struct Set (fanned out per-field).
Like the other topic listeners it re-establishes the registering scope (scopedListener) because the ack fires from an async document-event turn where findScope() cannot see the component's [data-gothic-scope], and routes through addScopedDocListener so the per-scope teardown can remove it on unmount.
func ListenTopicEvent ¶
ListenTopicEvent registers a cross-module listener for topic updates.
The listener re-establishes the scope that registered it (see scopedListener) because the topic CustomEvent fires from an async turn where findScope() cannot see the component's [data-gothic-scope]; fn typically drives scoped DOM helpers via ApplyExternal → Observe, which must resolve to this scope.
func ListenTopicEventField ¶
ListenTopicEventField subscribes to per-field broadcasts from the core hub.
fn receives the raw frame as []byte (not string): this is the HOT consumer data-plane path (one call per broadcast per subscribed field). Passing []byte lets the generated consumer feed the pooled scratch buffer straight into NewDecoder, eliminating the string(dst)→[]byte(detail) double copy the old func(string) signature forced. Combined with the per-listener scratch reuse in topicViewInto, a stable-payload rebroadcast now allocates ZERO transient frame bytes on the receive side — the fix for the multi-MB linear-memory ratchet on TinyGo's no-shrink conservative heap. The []byte is only valid for the duration of fn (it aliases the reused scratch); the decoder copies out its own values.
func ListenTopicOnline ¶
ListenTopicOnline registers a handler that receives the manager's online ack with current state. Fires once on manager startup and on every ping response.
func ListenTopicPing ¶
func ListenTopicPing(keyName string, fn func())
ListenTopicPing registers a handler for incoming pings on the topic manager WASM.
func ListenTopicSetReq ¶
ListenTopicSetReq registers a handler for incoming set-requests on a topic manager WASM.
func ListenTopicSetReqField ¶
ListenTopicSetReqField subscribes to per-field set-requests (used by the manager).
func LocalStorageGet ¶
func LocalStorageRemove ¶
func LocalStorageRemove(key string)
func OnUnmount ¶
func OnUnmount(fn func())
OnUnmount registers a cleanup callback invoked by the bootstrap's per-scope teardown when this component's [data-gothic-scope] element is removed from the DOM. Use it to release things created outside the component's own subtree (persistent document listeners attached directly, timers, topic mounts). The callback is stored at __gothic_registry[<scope>].__onUnmount and retained in keep so TinyGo's GC won't reclaim it before teardown fires.
func PingTopicManager ¶
func PingTopicManager(keyName string)
PingTopicManager dispatches a ping to the topic manager asking for an online ack.
func PingUntilOnline ¶
PingUntilOnline retries PingTopicManager every 50 ms until isOnline returns true. Runs in its own goroutine so it doesn't block the caller.
A goroutine does not inherit the scope carrier across suspension points, so we CaptureScope() at spawn and RunInScope() each iteration to re-establish the caller's scope (isOnline may read scoped state). For a single-scope instance the captured scope is bootstrapScope, so behaviour is unchanged.
func ReadTopicStore ¶
ReadTopicStore reads the encoded topic value from the shared JS store.
func RegisterTopicWithCore ¶
RegisterTopicWithCore performs the topic → core control-plane registration. The CustomEvent detail is a plain JS object {key, fields:[...]} — control-plane JSON/values, NEVER binary. It hands the core the topic's wire key and the ORDERED field-name list so the core can subscribe to each gothic:topic-req:<key>:<field> and replay the current per-field state back. The field names are ROUTING metadata; the core still never interprets a payload byte (the generic interpreter is DEFERRED), so this keeps the core opaque.
Startup races are handled the same way as GothicRegisterWithCore, with no goroutine:
- core already up → the register is received; the core replays this topic's stored per-field frames and announces the per-key online ack.
- core comes up later → it announces gothic:core:online on boot; this re-fires the register until the per-key online ack lands (acked short-circuits, so at most one extra send per core boot).
Every dispatch is wrapped in queueMicrotask so it leaves a clean call stack and never fires from inside this module's running asyncify turn (the RETURN side — the core's replay + ack — is symmetric: the full-Go core microtask-schedules those, so nothing re-enters this component's scheduler).
func RemoveClass ¶
func RemoveClass(id, className string)
func RemoveElement ¶
func RemoveElement(el JSValue)
func RequestTopicSet ¶
func RequestTopicSet(keyName, encoded string)
RequestTopicSet dispatches a set-request to the topic manager WASM for this key. The manager is the sole writer: it applies the update and broadcasts back.
func RequestTopicSetField ¶
func RequestTopicSetField(keyName, fieldName, encoded string)
RequestTopicSetField sends a per-field set-request to the manager. Event name: "gothic:topic-req:<keyName>:<fieldName>"
func RequestTopicSetFieldBytes ¶
RequestTopicSetFieldBytes is RequestTopicSetField for callers that already hold the encoded frame as []byte (the generated consumer's _broadcastAll). It hands the bytes straight to dispatchDirect — which copies them into dispatchHold — skipping the string(encoded)→[]byte(encoded) round-trip the string form forces on every per-field send. Event name is identical: "gothic:topic-req:<keyName>:<fieldName>".
func RunInScope ¶
func RunInScope(id string, fn func())
RunInScope runs fn with the given scope active, restoring the previous scope afterwards. Pair it with CaptureScope to carry a scope into a goroutine or deferred callback. Nesting is supported.
func SessionStorageGet ¶
func SessionStorageRemove ¶
func SessionStorageRemove(key string)
func ToggleClass ¶
func ToggleClass(id, className string)
func TriggerDownload ¶
TriggerDownload prompts the browser to download `data` as a file named `filename` with the given MIME type.
func UpdateTopicOnlineStore ¶
UpdateTopicOnlineStore updates the JS-side topic store so that late-joining consumers see fresh data via ReadTopicStore, WITHOUT dispatching the gothic:topic-online event. Use this from ListenTopicSetReq to fix the startup race (T5) without triggering ListenTopicOnline scans in already-running consumers.
func WriteClipboard ¶
func WriteClipboard(text string)
WriteClipboard writes the given text to the system clipboard via navigator.clipboard.writeText.
Types ¶
type Compression ¶
type Compression int
Compression is the compression algorithm used for a topic's WASM payload.
const ( GZIP Compression = iota // default BROTLI Compression = iota )
type CookieOptions ¶
type CookieOptions struct {
MaxAge int // seconds; 0 = session cookie
Path string // defaults to "/"
SameSite string // "Strict", "Lax", or "None"
Secure bool
}
CookieOptions configures CookieSet behaviour.
type Decoder ¶
Decoder reads a little-endian binary stream. Err accumulates the first decode error — check it once at the end rather than per call.
func NewDecoder ¶
NewDecoder opens a frame produced by NewEncoder. It validates the WireVersion header byte at position 0 and positions Pos immediately after it, so the first typed read returns the frame's first field. On an empty buffer or a version mismatch it sets Err (leaving Pos at 0) and never panics — need() short- circuits all subsequent reads. Use this at every site that decodes a complete wire frame; per-field capture helpers that walk an already-opened Decoder keep using the raw &Decoder{} form.
type Encoder ¶
type Encoder struct{ Buf []byte }
Encoder writes a little-endian binary stream into Buf. Preallocate with NewEncoder to avoid repeated append growth.
func NewEncoder ¶
NewEncoder opens a new frame: it returns an *Encoder whose buffer already contains the single WireVersion header byte at position 0. Every subsequent write appends after it. Because nested encoders reuse this same *Encoder, the version byte appears exactly once per frame — at the frame boundary.
type FetchConfig ¶
type FetchConfig struct {
Method string
Headers map[string]string
Body string
BodyBytes []byte
Query map[string]string
}
FetchConfig configures an HTTP request made via Fetch.
type JSValue ¶
type JSValue struct {
// contains filtered or unexported fields
}
JSValue wraps js.Value. Never create as a literal; use JS(), Document(), etc.
func CreateElement ¶
func CreateWasmFuncWithReturn ¶
CreateWasmFuncWithReturn registers a named global JS function that can return a value back to JS. Wraps syscall/js.FuncOf. The returned JSValue holds the js.Func so it can be passed directly to JS object properties (chart configs, map renderers, etc.) that require a synchronous return value. The js.Func is retained in keep and never released — it must stay alive for the lifetime of the page; releasing it would panic on the next invocation.
func GetElementById ¶
func QuerySelector ¶
func QuerySelectorAll ¶
func (JSValue) IsUndefined ¶
type Observable ¶
type Observable[T any] struct { // contains filtered or unexported fields }
Observable is a reactive value container. Reading it inside an Observe callback automatically subscribes that callback to future updates; calling Set notifies all subscribers synchronously.
Create one with CreateObservable:
count := CreateObservable(0)
name := CreateObservable("Alice")
Similar to useState in React, but the value is held in the Observable itself rather than being destructured into a [value, setter] pair.
func CreateObservable ¶
func CreateObservable[T any](initial T) *Observable[T]
CreateObservable creates a new Observable with the given initial value. It is the Gothic equivalent of React's useState hook.
Example:
count := CreateObservable(0)
label := CreateObservable("hello")
// Read the current value:
fmt.Println(count.Get())
// Update the value (triggers all Observe callbacks that depend on count):
count.Set(count.Get() + 1)
func (*Observable[T]) Get ¶
func (s *Observable[T]) Get() T
func (*Observable[T]) Set ¶
func (s *Observable[T]) Set(v T)
type ObservableField ¶
type ObservableField[T any] struct { // contains filtered or unexported fields }
ObservableField is a reactive Observable bound to one field of a shared topic struct. It behaves like *Observable[T] but Set also broadcasts the full topic to other modules. Pass *ObservableField as a dep in Observe to react to individual property changes.
func NewObservableField ¶
func NewObservableField[T any](initial T) *ObservableField[T]
NewObservableField creates an ObservableField with the given initial value.
func (*ObservableField[T]) ApplyExternal ¶
func (f *ObservableField[T]) ApplyExternal(v T)
ApplyExternal updates value and notifies subscribers without triggering broadcast. Used by generated topic listeners and Set-all methods to avoid redundant events.
func (*ObservableField[T]) Get ¶
func (f *ObservableField[T]) Get() T
Get returns the current value, auto-registering as a dep of any running effect.
func (*ObservableField[T]) Peek ¶
func (f *ObservableField[T]) Peek() T
Peek returns the current value without registering as an effect dependency. Used internally by broadcast closures to read sibling field values safely.
func (*ObservableField[T]) Set ¶
func (f *ObservableField[T]) Set(v T)
Set sends a set-request to the topic manager WASM. The local value is silently updated so Peek() returns the correct value during encoding, but subscribers are NOT notified until the manager broadcasts back.
func (*ObservableField[T]) SetBroadcast ¶
func (f *ObservableField[T]) SetBroadcast(fn func())
SetBroadcast wires the broadcast callback called whenever Set updates this field.
type SharedTopicObservable ¶
type SharedTopicObservable[T any] struct { // contains filtered or unexported fields }
SharedTopicObservable is a reactive Observable bound to a shared topic key. Get/Set work like a regular Observable, but Set also broadcasts the new value to every other WASM module sharing the same key. Used internally by the auto-generated topic constructors (e.g. PageTopic()).
func (*SharedTopicObservable[T]) Get ¶
func (s *SharedTopicObservable[T]) Get() T
func (*SharedTopicObservable[T]) Set ¶
func (s *SharedTopicObservable[T]) Set(v T)
type Subscription ¶
type Subscription struct {
// contains filtered or unexported fields
}
func Observe ¶
func Observe(fn func(), deps ...any) *Subscription
Observe runs fn immediately and re-runs it whenever a listed dep changes. Pass no deps to run fn exactly once with no reactive subscription. It is the Gothic equivalent of React's useEffect hook.
Example — update the DOM whenever count changes:
count := CreateObservable(0)
Observe(func() {
document.GetElementById("counter").SetInnerHTML(fmt.Sprintf("%d", count.Get()))
}, count)
// Later, updating count re-runs the callback automatically:
count.Set(count.Get() + 1)
Pass multiple deps to react to any of them:
Observe(func() { ... }, a, b, c)
Pass no deps to run once on mount with no subscription:
Observe(func() { fmt.Println("mounted") })
func ObserveWithCleanup ¶
func ObserveWithCleanup(fn func() func(), deps ...any) *Subscription
ObserveWithCleanup is like Observe but fn may return a cleanup function that runs before each re-execution and when Stop() is called.
Example — add and remove an event listener reactively:
ObserveWithCleanup(func() func() {
el := document.GetElementById("btn")
handler := el.AddEventListener("click", func() { ... })
return func() { el.RemoveEventListener("click", handler) }
}, someObservable)
func (*Subscription) Stop ¶
func (e *Subscription) Stop()
type TopicConfig ¶
type TopicConfig struct {
Name string
Compression Compression // GZIP (default) or BROTLI
Compiler WasmCompiler // GothicTinyGo (default), LocalTinyGo, or Golang
SubscriberFnName string // overrides generated accessor func name (default: <StructName>Topic)
}
TopicConfig holds per-topic configuration.
type TopicKey ¶
TopicKey is a typed topic identifier that carries its own codec. T encodes the value type — provider and consumer must use the same key. Construct via the factory functions (IntKey, StringKey, BinaryKey, etc.), not as a struct literal.
func AutoKey ¶
AutoKey is rewritten to BinaryKey by the CLI before TinyGo compiles. This stub exists so server-side code compiles; WASM code never calls it directly.
func BinaryKey ¶
BinaryKey returns a TopicKey that serializes T using a compact little-endian binary codec. No reflection, no encoding/json — just typed Encoder/Decoder calls. The encode function writes fields onto e; the decode function reads them back and returns T. Field order must match between encode and decode.
Example:
BinaryKey[Page]("page",
func(v Page, e *Encoder) {
e.I64(int64(v.Pings))
e.String(v.Label)
e.String(v.Theme)
},
func(d *Decoder) PageCtx {
return PageCtx{Pings: int(d.I32()), Label: d.String(), Theme: d.String()}
},
)
func Float32Key ¶
func Float64Key ¶
type WasmCompiler ¶
type WasmCompiler int
WasmCompiler selects the WASM build toolchain for a topic.
const ( GothicTinyGo WasmCompiler = iota // default: embedded TinyGo binary LocalTinyGo // system tinygo binary in PATH Golang // GOOS=js GOARCH=wasm standard Go compiler )