Help us shape the future of Go! Take the 2026 Go Developer Survey

playground

package module
v0.0.0-...-260f28f Latest Latest
Warning

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

Go to latest
Published: Nov 1, 2026 License: BSD-3-Clause Imports: 34 Imported by: 0

Documentation

Overview

Package playground is the go-tex WebAssembly LaTeX playground rendered as a single go-widgets/toolkit canvas application: a toolbar (syntax colour-scheme picker, minimap toggle), a horizontal split of a CodeEditor (line numbers, LaTeX syntax colours, current-line highlight) with an optional code minimap on the left and, on the right, a small Rendered│Log tab strip over either the shared toolkit.PagedView showing the pure-Go gotex render (continuous / paginated modes, zoom, and full wheel + keyboard page navigation, all owned by the widget) or a diagnostics Log, and a status bar (caret position, encoding, page count, issue count).

The whole View + logic lives here as a plain, tagless package so a native `go test` exercises construction, layout, compile, rasterize, event dispatch (including pointer drag: divider resize + scrollbar thumbs) and draw against an RGBA buffer via go-widgets/painter — no browser needed. The js/wasm canvas driver (cmd/playground-wasm) is the only build-tagged file; it forwards DOM input (mousedown/move/up, wheel, keys) into these handlers, applies the HiDPI device-pixel scale, and blits Draw's buffer.

Index

Constants

View Source
const DefaultRemoteURL = "https://sources.mesocentre.plateau-de-saclay.net/go-tex/examples.git"

DefaultRemoteURL is the repository the playground opens with: the go-tex sample documents, on the org's own Forgejo.

It has to be that host and not GitHub. A clone from a browser is an ordinary fetch, so the server must send the CORS headers for this origin, and GitHub does not: reaching a GitHub repository from here needs a proxy, and the one this app was written against answers nothing. The Forgejo does send them — access-control-allow-origin: https://go-tex.github.io on the git endpoints — so the clone works with no intermediary at all.

View Source
const SampleLaTeX = `` /* 713-byte string literal not displayed */

SampleLaTeX is the document the playground opens with — the same article the legacy textarea playground shipped, so the two render identically.

Variables

This section is empty.

Functions

func CanvasClipPath

func CanvasClipPath(overlays []CanvasOverlay, dpr float64) string

CanvasClipPath builds the CSS clip-path that punches every overlay out of the page container: the container's own box, then one subpath per overlay. A square-cornered overlay punches a rectangle; a rounded one punches a rounded rectangle (arcs at each corner) so the page is cut to the window's exact shape and no black notch shows in a rounded corner. dpr converts the device-pixel geometry to the CSS-pixel user space a path() reads. Returns "" when there is nothing to punch (the caller sets clip-path: none).

The container box + one subpath per hole, filled evenodd, is two SUBPATHS per hole rather than one polygon threading from the outer ring to each hole and back — that journey is drawn and leaves a wedge cut out of the page.

func SetupText

func SetupText(scale float64)

SetupText installs the toolkit's anti-aliased text and metric scale for a device-pixel ratio: every box metric is scaled, the text face with them, so the UI is crisp on a HiDPI panel and byte-identical at scale 1. The driver calls it (with window.devicePixelRatio) before building the State; tests call SetupText(1).

⛔⛔ The SIZE HERE IS LOGICAL. SetMetricScale scales the text too — "The text follows", says its own comment, and it calls rescaleText() — so multiplying the size by the scale as well applied the device ratio TWICE to everything a reader reads, while the padding around it was scaled once. Measured with the toolkit:

metricScale 1, size 16   TextWidth("Source") =  50
metricScale 1, size 32   TextWidth("Source") =  98
metricScale 2, size 16   TextWidth("Source") =  98   ← correct at dpr 2
metricScale 2, size 32   TextWidth("Source") = 195   ← what this did

On a Retina panel the four workspace tabs came out 60–73% too wide for their labels — a real browser at deviceScaleFactor 1 against 2 measured sourceTab at 92 against 159 CSS pixels, while their HEIGHTS matched exactly, which is what pins it on the text and not the boxes.

It is the shape of a dependency that grew a feature: the toolkit did not rescale text when this was written, so the application did it, and when the toolkit started doing it the application's compensation became the defect.

Types

type CanvasOverlay

type CanvasOverlay struct {
	Rect   toolkit.Rect
	Radius int
}

CanvasOverlay is one rectangle the canvas paints ON TOP of the DOM page, carrying the corner radius of the window drawn there (0 for a square-cornered panel). Radius is in the surface's device pixels, matching Rect.

type CollabDecoration

type CollabDecoration struct {
	Label     string // the peer's display name
	ColorHex  string // the peer's caret colour as "#rrggbb"
	Line, Col int    // the peer's caret position (rune coordinates)
}

CollabDecoration is one remote participant's caret as it is painted into the local editor — the read-back a headless test asserts on to prove a peer's caret and colour crossed the wire. It mirrors one toolkit.Decoration.

type GitCommitInfo

type GitCommitInfo struct {
	Hash    string
	Subject string
	Author  string
}

GitCommitInfo is one log line surfaced to the host (and the headless proof): the short hash, the subject and the author.

type ICEServer

type ICEServer struct {
	URL        string
	Username   string
	Credential string
}

ICEServer is one STUN or TURN server the WebRTC peers use to discover an address both browsers can reach. URL is the server ("stun:host:port" or "turn:host:port"); Username and Credential are the long-term credentials a TURN relay requires and are empty for a STUN server. It mirrors one entry of an RTCPeerConnection's iceServers, so the Collaborate config shape can express a credentialed TURN relay — the sovereign coturn planned for the EU host — and not only credential-free STUN.

type PageRender

type PageRender struct {
	// Page is 1-based, matching PagedView.
	Page int
	// SVG is the whole themed page.
	SVG string
	// Natural is the page's un-zoomed size in device pixels — what the SVG's
	// viewBox maps onto, and the space [State.SetLineBands] reports in.
	Natural toolkit.Rect
	// Rect is where the card is drawn and Clip the part the pane still shows,
	// both in the surface's DEVICE pixels: a host laying out in CSS pixels
	// divides by the device pixel ratio.
	Rect, Clip toolkit.Rect
}

PageRender is one page's SVG and where to put it. The host reads it from State.PageRenders and turns it into DOM.

type State

type State struct {

	// OnCompileNeeded schedules a debounced Compile after an edit; OnEdit
	// persists the buffer independent of the compile path. Nil in tests.
	OnCompileNeeded func()
	OnEdit          func(text string)
	// contains filtered or unexported fields
}

State is the whole playground: the widget tree, the current theme, the last compile result and host bookkeeping. Created with NewState, driven through its Handle* / Draw methods.

func NewState

func NewState(w, h int, dark bool) *State

NewState builds the playground at w×h DEVICE pixels, dark or light, compiles the sample document once and returns the ready scene. The caller installs the text/scale first via SetupText.

func (*State) ActiveEditorTab

func (s *State) ActiveEditorTab() int

ActiveEditorTab is the selected editor view index (0 = Source, 1 = WYSIWYG), derived from the mode boolean. Host/headless introspection.

func (*State) ActiveTab

func (s *State) ActiveTab() int

func (*State) AssetLoading

func (s *State) AssetLoading() string

AssetLoading is what the topZone is currently announcing (host introspection).

func (*State) AssetProgress

func (s *State) AssetProgress() float64

AssetProgress is the fraction downloaded of the asset named by State.AssetLoading, or -1 when it is not known.

func (*State) BootClone

func (s *State) BootClone(done func(err error))

BootClone opens DefaultRemoteURL in the workspace as the application starts, and shows the workspace sidebar once it lands.

It does NOT block the start. The app comes up on its built-in document immediately and the repository arrives when the network delivers it, so a slow or unreachable Forgejo costs a reader nothing but the samples — where waiting for the clone would have made every start as slow as the worst network, and a failed one leave a blank window.

A reader who begins typing before the clone lands keeps what they wrote: the buffer at boot is remembered and the repository's primary .tex is only loaded if it is still untouched.

It is a no-op with no backend (a headless test), with a repository already open, or when a clone is already in flight.

func (*State) BootNotice

func (s *State) BootNotice() string

BootNotice is what the topZone is currently reporting (host introspection).

func (s *State) BottomZoneHoveredLink() int

BottomZoneHoveredLink is the index (into BottomZoneLinkURLs / BottomZoneLinkRects) of the footer link currently showing its hover underline, or -1 when none is hovered. The render proof reads it to assert a pointer-move over a link rect raised exactly that link's underline.

func (*State) BottomZoneLinkRects

func (s *State) BottomZoneLinkRects() [][4]int

func (*State) BottomZoneLinkURLs

func (s *State) BottomZoneLinkURLs() []string

BottomZoneLinkURLs / BottomZoneLinkRects are the target url and device-pixel [x,y,w,h] rect of every bottomZone link, in the same order — so a harness can click each rect and know which url it should drive.

func (*State) BottomZoneRect

func (s *State) BottomZoneRect() [4]int

func (*State) CanvasOverlays

func (s *State) CanvasOverlays() []CanvasOverlay

CanvasOverlays are the windows the canvas paints ON TOP of everything — the find-and-replace panel, the Git panel, the Collaborate panel — in the surface's device pixels. Empty when none is open.

A host that draws page content as DOM MUST punch these out of it. The canvas and the overlay are siblings in the page, and a DOM element is unconditionally above a canvas: a window painted into the canvas cannot come forward over an element, however the application stacks it internally. Dragging the find modal onto the render pane sliced it off at the page's edge until this existed.

Each overlay carries the corner radius of the window drawn there, so the host punches a hole that follows the window's rounded corners. The find modal is a rounded toolkit.Dialog; a SQUARE hole behind it cut the page out at the corners while the canvas left them transparent, and the void between the two read as black notches in the rounded corners over the preview. The Git and Collaborate panels are square-cornered Backdrops, so their radius is 0.

func (*State) CaretPixel

func (s *State) CaretPixel(line, col int) (int, int)

CaretPixel returns the DEVICE-pixel point just inside the cell of the given 0-based (line, col) caret position — so a headless harness can click there and assert the caret round-trips exactly (proving the click→column mapping is accurate). It DELEGATES to the toolkit's toolkit.TextView.CaretPixel (promoted through the embedded *TextView) so the gutter/advance/line-pitch geometry lives in exactly one place: duplicating it here drifted the moment the gutter padding widened. CaretPixel returns the cell's top-left, so a couple of in-cell pixels (one across, half a glyph down) land the click squarely inside the cell for caretAt to map back.

func (*State) ClearDirty

func (s *State) ClearDirty()

func (*State) Cloning

func (s *State) Cloning() bool

Cloning reports whether a repository clone is in flight with no repo open yet — the state the workspace sidebar shows a loading spinner for. The host drives an animation frame loop while this is true (see SubscribeCloning + Tick).

func (*State) CollabAcceptAnswer

func (s *State) CollabAcceptAnswer(answer string, done func(err error))

CollabAcceptAnswer completes the host's handshake with the guest's pasted reply: it decodes the guest's identity envelope (capturing WHO is joining, for the panel), and hands the raw SDP inside it to the backend. The live connection follows (reported via the change hook). An empty field or a blob that is not a valid envelope is refused before the backend is touched, with a clear message in the errMsg lane.

func (*State) CollabActive

func (s *State) CollabActive() bool

CollabActive reports whether the panel is open.

func (*State) CollabAnswer

func (s *State) CollabAnswer() string

func (*State) CollabApplySignalFragment

func (s *State) CollabApplySignalFragment(fragment string) (kind string, ok bool)

CollabApplySignalFragment consumes a scan-to-connect URL fragment on load: an "#invite=…" opens the Collaborate panel and joins the host as the decoded invitation (exactly as if the guest had pasted it), an "#answer=…" hands the reply to the waiting host. The decoded envelope is dropped into the visible paste field first, so the flow is identical to a manual paste, then driven through State.CollabJoin / State.CollabAcceptAnswer. A malformed or non-signalling fragment is ignored (ok false, panel untouched) so a junk hash never disrupts a normal load. kind is [fragInvite]/[fragAnswer] on success.

func (*State) CollabButtonRects

func (s *State) CollabButtonRects() map[string][4]int

CollabButtonRects returns the device-pixel [x,y,w,h] rectangles of the Collaborate launcher and every currently-visible panel control, keyed by a stable name ("launcher", "name", and one per button role), so a headless two-tab harness can dispatch REAL pointer clicks at the actual buttons — the same handleClick → dispatch path a user drives — rather than calling the handlers directly. Host-facing introspection only.

func (*State) CollabColorHex

func (s *State) CollabColorHex() string

CollabColorHex is this participant's caret colour as "#rrggbb".

func (*State) CollabConnected

func (s *State) CollabConnected() bool

CollabConnected reports whether a live peer session is up.

func (*State) CollabConnecting

func (s *State) CollabConnecting() bool

CollabConnecting reports whether a Connect step was accepted and the panel is showing the "Connecting…" acknowledgement while the peer channel opens — the signal that a pasted blob was taken and a connection is being attempted.

func (*State) CollabDisconnect

func (s *State) CollabDisconnect()

CollabDisconnect tears the session down and returns the panel to its idle choice.

func (*State) CollabErrorMessage

func (s *State) CollabErrorMessage() string

CollabErrorMessage is the panel's error lane — the last error a collab step reported, the text rendered next to the ⚠ and cleared when a step starts. It is distinct from State.CollabFailureMessage, which is the fixed TURN hint the failed phase shows: this one carries what actually went wrong, in any phase, including a setup error that returned the panel to idle. A headless proof reads it so a failure names itself rather than being inferred from a phase that has already been reset.

func (*State) CollabFailureMessage

func (s *State) CollabFailureMessage() string

CollabFailureMessage is the panel's failure guidance when the connection could not be established ([phaseFailed]) — the TURN-server hint the user needs — and the empty string in every other phase. It is what the failure-path test and a gotex* debug hook read to prove the message is surfaced rather than the panel hanging silently.

func (*State) CollabHost

func (s *State) CollabHost(done func(offer string, err error))

CollabHost starts a hosting session: it seeds the shared document with the current editor source, binds the editor to it and makes the offer blob to hand to the peer. The raw SDP the backend produces is wrapped in an identity envelope (see [encodeEnvelope]) so the peer's panel can name who invited it; that envelope is what v.offer holds and "Copy invitation" puts on the clipboard. done (optional) receives the envelope or the error; the panel passes nil and reads v.offer, the headless proof passes a chained handler that relays it to the peer.

func (*State) CollabICEConfig

func (s *State) CollabICEConfig() []ICEServer

CollabICEConfig is the configured ICE servers, credentials included — the browser backend reads this to build each peer's RTCPeerConnection.

func (*State) CollabICEServers

func (s *State) CollabICEServers() []string

CollabICEServers is the configured ICE server URLs, for the config field's current value and headless introspection.

func (*State) CollabICEText

func (s *State) CollabICEText() string

CollabICEText is the current text of the panel's ICE-servers field, for the DOM host and headless introspection (the visible, editable value).

func (*State) CollabJoin

func (s *State) CollabJoin(offer string, done func(answer string, err error))

CollabJoin joins a hosting peer from its pasted invitation: it decodes the host's identity envelope (capturing WHO invited it, for the panel), binds the editor to the shared document and makes the answer blob to hand back. The raw SDP inside the envelope is what the backend receives; the answer it produces is itself wrapped in this client's envelope before it lands in v.answer. The live connection follows once the host accepts (reported via the change hook). An empty field or a blob that is not a valid envelope (an old-format or garbage paste) is refused before the backend is touched, with a clear message in the errMsg lane.

func (*State) CollabLocalConnect

func (s *State) CollabLocalConnect(done func(err error))

CollabLocalConnect starts the zero-config "in this browser" session: it hands the backend this participant's identity and lets it elect host or client on the shared BroadcastChannel bus and bind the editor. There is nothing to copy, scan or configure — every tab of this deployment is already on the bus — so the panel goes straight to a brief [phaseLocalConnecting] acknowledgement and the live session is reported through the change hook ([collabView.onBackendChange] advances it to [phaseConnected]). A setup failure (no BroadcastChannel, a bind error) surfaces in the errMsg lane and returns the panel to idle. done (optional) receives the setup error; the panel passes nil, the headless proof a handler.

func (*State) CollabLocalID

func (s *State) CollabLocalID() string

CollabLocalID is this participant's stable short client id (shown in the identity row next to the name), for the DOM host and headless introspection.

func (*State) CollabName

func (s *State) CollabName() string

CollabName / SetCollabName read and set this participant's display name.

func (*State) CollabOffer

func (s *State) CollabOffer() string

CollabOffer / CollabAnswer expose the current signalling blobs (for a DOM host that shows them in a real textarea, or a test).

func (*State) CollabPasteText

func (s *State) CollabPasteText() string

CollabPasteText is the current text of the visible signalling-blob paste field (the host's invitation on the guest side, the peer's reply on the host side), for the DOM host and headless introspection.

func (*State) CollabPeerColorHex

func (s *State) CollabPeerColorHex() string

func (*State) CollabPeerCount

func (s *State) CollabPeerCount() int

CollabPeerCount is how many remote participants are in the document.

func (*State) CollabPeerID

func (s *State) CollabPeerID() string

CollabPeerID / CollabPeerName / CollabPeerColorHex expose the identity decoded from the peer's pasted envelope once a valid invitation/reply has been consumed — who this client is connecting to. They are empty before a peer is known.

func (*State) CollabPeerName

func (s *State) CollabPeerName() string

func (*State) CollabPhase

func (s *State) CollabPhase() int

CollabPhase is the session phase as an int (0 idle … 4 connected, 5 failed), for a headless assertion.

func (*State) CollabRemoteDecorations

func (s *State) CollabRemoteDecorations() []CollabDecoration

CollabRemoteDecorations returns the remote carets currently painted into the editor — the proof that a peer's caret, colour and name crossed the wire. It reads the editor's live Decorations, which toolkit.CollabText rebuilt from the session's peers.

func (*State) Compile

func (s *State) Compile()

Compile runs the engine, feeds the freshly rasterized page bitmaps to the render pane's PagedView, updates the diagnostics Log and the status bar. A hard error (or an empty document) yields no bitmaps, which clears the viewer.

func (*State) CompilePending

func (s *State) CompilePending() bool

TakePendingCompile drains the edit latch (once). CompilePending compiles once iff a compile is pending, and reports whether it did. The host calls it after wiring the time provider so the boot compile's Log entries carry the same clock format as every later one.

func (*State) CursorCol

func (s *State) CursorCol() int

func (*State) CursorLine

func (s *State) CursorLine() int

func (*State) DebugRects

func (s *State) DebugRects() map[string][4]int

DebugRects returns the DEVICE-pixel [x,y,w,h] rectangles of the interactive targets a headless verification harness needs to click precisely: the colour scheme picker and its open popover, the two right-pane tabs, the whole render pane (the PagedView, which owns its own toolbar + scroll — the harness drives paging with real wheel/key events over this rect) and the render content area below the PagedView's toolbar strip. It is host-facing introspection only.

func (*State) Dirty

func (s *State) Dirty() bool

Dirty/ClearDirty/Theme accessors.

func (*State) DividerX

func (s *State) DividerX() int

DividerX is the surface x of the resize grip's leading edge.

func (*State) Draw

func (s *State) Draw(buf []byte)

Draw paints the whole scene onto buf (RGBA, row-major, w*h*4).

func (*State) DrawnPages

func (s *State) DrawnPages() int

func (*State) EditorCaretVisible

func (s *State) EditorCaretVisible() bool

EditorCaretVisible reports whether the editor caret is currently shown (host/harness introspection).

func (*State) EditorTabRect

func (s *State) EditorTabRect(i int) [4]int

EditorTabRect is the device rectangle (surface coordinates) of the Source⇄WYSIWYG control, so a headless harness can click it precisely. The view is now one toolbar toggle button, so both indices return that button's rect.

func (*State) EditorWidth

func (s *State) EditorWidth() int

Read-only introspection for the host/verification harness: the editor pane width, whether the Log is shown, the active tab index, the render zoom percentage (read from the PagedView's Zoom observable) and the selected colour scheme index. They let a headless test assert real state changes after a pointer interaction.

func (*State) FindCountText

func (s *State) FindCountText() string

func (*State) FindCurrent

func (s *State) FindCurrent() int

func (*State) FindInvalid

func (s *State) FindInvalid() bool

func (*State) FindMatchPoints

func (s *State) FindMatchPoints() [][2]int

FindMatchPoints returns a device-pixel point just inside each current match's highlight band (its start cell), in the active editor's match order.

func (*State) FindTotal

func (s *State) FindTotal() int

Find-and-replace host introspection for the headless proof: the modal's count state and a device-pixel point inside each match's highlight band, so the browser harness can read the count AND sample the canvas to prove the highlights land on the matches. They read the host's own match state (and the ACTIVE editor's highlight geometry), so they report on whichever editor — Source or WYSIWYG — the search is targeting.

func (*State) FindVisible

func (s *State) FindVisible() bool

FindVisible reports whether the find modal is open — the wasm driver reads it to decide whether a Shift+Enter should reach the modal (previous match) rather than the editor.

func (*State) GitActive

func (s *State) GitActive() bool

GitActive reports whether the panel is open.

func (*State) GitActiveTabIndex

func (s *State) GitActiveTabIndex() int

GitActiveTabIndex is the index of the active file within GitOpenTabs, or -1 when no open tab is active (no repo, or the editor was cleared).

func (*State) GitBranch

func (s *State) GitBranch() string

func (*State) GitBufferDirty

func (s *State) GitBufferDirty(path string) bool

GitBufferDirty reports whether path's edit buffer differs from its committed / indexed content — the per-file dirtiness the sidebar badges each row from.

func (*State) GitBufferPaths

func (s *State) GitBufferPaths() []string

GitBufferPaths returns, sorted, every path that currently has an edit buffer (host / proof introspection for the multi-buffer model).

func (*State) GitBusy

func (s *State) GitBusy() bool

GitBusy reports whether a network op is in flight (buttons are disabled).

func (*State) GitClone

func (s *State) GitClone(done func(err error))

GitClone clones the configured remote into memory and loads its primary .tex into the source editor. done (optional) receives the error; the panel passes nil, the headless proof passes a handler.

func (*State) GitCloseTab

func (s *State) GitCloseTab(path string)

GitCloseTab closes the editor tab for path (the file-tab strip's × drives it), activating a neighbour when the active tab is closed. A no-op while a network op is in flight. It keeps the file's edit buffer, so re-opening restores edits.

func (*State) GitCommit

func (s *State) GitCommit(done func(err error))

GitCommit flushes EVERY dirty edit buffer to the working tree, then commits. browsergit's Commit stages every change (git add -A) before committing, so the flush is what makes a commit capture edits to files other than the active one; the active file's own write is still carried by the backend Commit call. After a successful commit the flushed files' buffers match the committed content, so their dirty flags clear.

func (*State) GitCommitMessage

func (s *State) GitCommitMessage() string

func (*State) GitError

func (s *State) GitError() string

GitError / GitNotice expose the last error and the last success line.

func (*State) GitForget

func (s *State) GitForget(done func())

GitForget drops this browser's saved copy of the open repository, so the next visit clones afresh instead of reopening it.

It deliberately does NOT touch what is open. A reader asking to forget a stored copy is asking about the future, not asking to lose the document they are editing — and a workspace that emptied itself on this button would be a destructive action wearing a harmless label.

func (*State) GitLoadedPath

func (s *State) GitLoadedPath() string

GitLoadedPath is the working-tree path currently shown in the editor ("" none).

func (*State) GitLog

func (s *State) GitLog() []GitCommitInfo

GitLog returns the recent commits shown in the status area.

func (*State) GitNotice

func (s *State) GitNotice() string

func (*State) GitOpenFile

func (s *State) GitOpenFile(path string) bool

GitOpenFile opens working-tree path into the source editor (the sidebar's file-row click drives it), preserving the previously-open file's unsaved edits in its own buffer. It reads the cached content the last clone/pull filled — so any pre-cached text file (.tex/.sty/.bib/…), not just the primary .tex, opens. A no-op while a network op is in flight or before a clone; reports whether the file is the one now active.

func (*State) GitOpenTabs

func (s *State) GitOpenTabs() []string

GitOpenTabs is the ordered list of files open as editor tabs (the file-tab strip reads it). Empty with no repo / nothing opened.

func (*State) GitPrimaryPath

func (s *State) GitPrimaryPath() string

GitPrimaryPath is the .tex the render pane compiles ("" with no repo / no .tex).

func (*State) GitProgressFraction

func (s *State) GitProgressFraction() float64

GitProgressFraction is the parsed [0,1] completion of the current clone/pull's progress, or -1 when it is unknown / nothing is in flight. The busy empty-state bar reads it to run determinate (a real object count) rather than indeterminate.

func (*State) GitProgressLine

func (s *State) GitProgressLine() string

GitProgressLine is the remote's latest clone/pull progress phase line, or "" when none has arrived for the op in flight.

func (*State) GitPull

func (s *State) GitPull(done func(err error))

GitPull fast-forwards the open repo, then reloads the file in the editor.

func (*State) GitPush

func (s *State) GitPush(done func(err error))

GitPush pushes the open branch to origin.

func (*State) GitStage

func (s *State) GitStage(done func(err error))

GitStage flushes EVERY dirty edit buffer to the working tree, then stages them (git add) WITHOUT committing, so each edited file's sidebar badge flips to "staged". It mirrors GitCommit's guards + flush; the sidebar's Stage button drives it.

func (*State) GitStatusLine

func (s *State) GitStatusLine() string

GitStatusLine formats the status area's headline: the branch and, when the repo has enough history, its ahead/behind divergence plus dirty-file count.

func (*State) GitTeXFiles

func (s *State) GitTeXFiles() []string

GitTeXFiles are the .tex paths the picker offers.

func (*State) GitURL

func (s *State) GitURL() string

func (*State) HandleChar

func (s *State) HandleChar(code string) bool

HandleChar routes a printable character to the editor. Typing ends any keyboard selection in progress (the inserted text replaces it if present). When the render pane holds focus instead, a space bar pages the viewer and no character reaches the (unfocused) editor.

func (*State) HandleClick

func (s *State) HandleClick(x, y int) bool

HandleClick routes a pointer press and captures it for a following drag.

func (*State) HandleCompositionCancel

func (s *State) HandleCompositionCancel() bool

HandleCompositionCancel drops a pending preview: the composition was abandoned (Escape, a click elsewhere) and nothing is to be inserted.

func (*State) HandleCompositionCommit

func (s *State) HandleCompositionCommit(text string) bool

HandleCompositionCommit inserts what a composition finally produced.

It goes in one character at a time through the same door as a keystroke, so every field the playground has — the editor, the find bar, the git and collab panels — takes it the way it takes typing, and a selection is replaced exactly once. A commit can be several characters long (a CJK candidate, or ^ followed by a letter that does not combine with it).

func (*State) HandleCompositionPreview

func (s *State) HandleCompositionPreview(text string) bool

HandleCompositionPreview shows an IME's pending text where it will land — the ^ of a dead key, a half-typed CJK candidate — WITHOUT putting it in the document. The document changes only when the composition commits, so this deliberately leaves the dirty latch alone: a pending accent must not start a recompile.

func (*State) HandleCopy

func (s *State) HandleCopy() bool

HandleCopy copies the editor selection to the clipboard. No-op (returns false) when the editor is unfocused.

func (*State) HandleCut

func (s *State) HandleCut() bool

HandleCut cuts the editor selection to the clipboard (an edit, so it schedules a recompile via the editor's OnChange). No-op when unfocused.

func (*State) HandleKeyDown

func (s *State) HandleKeyDown(code string) bool

HandleKeyDown routes a named key. When the render pane holds keyboard focus it receives the key (PageDown / ArrowUp / Home / End flip pages) and the editor is left untouched. Otherwise it goes to the editor: a "Shift+"-prefixed navigation key extends the selection from an anchor; a plain navigation key moves the caret and collapses any selection; every other key is forwarded as-is.

func (*State) HandleMove

func (s *State) HandleMove(x, y int) bool

HandleMove routes a captured drag to its target. This is what makes the Paned divider and the scrollbar thumbs draggable (previously a no-op, so move/up were discarded and no widget ever received EventMouseDrag).

func (*State) HandlePaste

func (s *State) HandlePaste(text string) bool

HandlePaste inserts text at the caret (replacing any selection). The host reads the OS clipboard asynchronously and passes the text here. A paste into a focused Collaborate panel field (the signalling-blob paste field, the ICE or the name field) goes there instead, so ⌘V/Ctrl+V drops the invitation/reply into the visible field — the immediate proof it was taken. No-op when nothing is focused.

func (*State) HandleRelease

func (s *State) HandleRelease(x, y int) bool

HandleRelease ends a captured drag, delivering EventMouseUp to the target.

func (*State) HandleScroll

func (s *State) HandleScroll(x, y, dx, dy int) bool

HandleScroll forwards a wheel/two-finger scroll to the pane under the pointer. dx/dy are in ROWS (a horizontal swipe carries dx); the render pane routes both axes so a horizontal swipe moves its horizontal scrollbar, while the editor and minimap scroll vertically only.

func (*State) HandleSelectAll

func (s *State) HandleSelectAll() bool

HandleSelectAll selects the whole buffer. No-op when unfocused.

func (*State) HasSelection

func (s *State) HasSelection() bool

func (*State) LineToRenderPage

func (s *State) LineToRenderPage(line int) int

func (*State) LogEntryCount

func (s *State) LogEntryCount() int

LogEntryCount returns how many entries the diagnostics Log has accumulated (proving the history builds up across compiles, newest at the bottom, rather than being cleared each compile). Host introspection for the headless harness.

func (*State) PageCount

func (s *State) PageCount() int

PageCount is the engine's logical page count; DrawnPages the number actually rasterized and fed to the render pane. They let a headless test assert a multi-page document reached the viewer.

func (*State) PageRenders

func (s *State) PageRenders() []PageRender

PageRenders returns every page currently ON SCREEN in the render pane, with where to put it. The host creates one element per entry and removes the rest.

It is empty whenever nothing should be shown: the Log tab is in front, the pane has no room, or the compile produced no page. Reading it every frame is what keeps each page glued to its card through a scroll, a zoom, a page flip or a pane resize — the placement comes from toolkit.PagedView.PageRect, the same layout the pane paints its paper from, so the two cannot drift.

func (*State) ReadyDotRGB

func (s *State) ReadyDotRGB() [3]int

ReadyDotRGB is the topZone ready indicator's colour as [r,g,b], so the render proof can look for that exact hue in the painted band.

func (*State) RenderCurrentPage

func (s *State) RenderCurrentPage() int

func (*State) RenderFocused

func (s *State) RenderFocused() bool

RenderFocused reports whether the render pane holds keyboard focus (so nav keys drive the page viewer, not the editor). Host introspection.

func (*State) RenderLineAt

func (s *State) RenderLineAt(x, y int) int

Read-only introspection for the headless verification harness of the source↔render linking. RenderLineAt maps a device-pixel render-pane point to the source line drawn there (0 = none), LineToRenderPage the inverse page lookup (0 = nothing rendered for the line), RenderScrollY the render pane's vertical scroll offset. SetRenderPaginated flips the viewer's mode so a caret-driven scroll shows up crisply as a current-page change.

func (*State) RenderMode

func (s *State) RenderMode() int

RenderMode / RenderCurrentPage / RenderVisiblePages report the render pane's continuous-vs-paginated mode, the current 1-based page and how many pages are shown at once (1 paginated, all continuous) — all read from the PagedView's MVVM observables. CursorLine/CursorCol expose the editor caret for click-accuracy assertions. HasSelection / SelectionText expose the current text selection.

func (*State) RenderScrollY

func (s *State) RenderScrollY() int

func (*State) RenderVisiblePages

func (s *State) RenderVisiblePages() int

func (*State) Resize

func (s *State) Resize(w, h int)

Resize re-lays out to a new surface size and repaints.

func (*State) RichBlockCount

func (s *State) RichBlockCount() int

RichBlockCount is the number of top-level blocks in the RichEditor document.

func (*State) RichCurrentBlockKind

func (s *State) RichCurrentBlockKind() int

RichCurrentBlockKind is the caret block's kind as the toolkit BlockKind int (BlockParagraph, BlockH1.., BlockCodeKind, BlockQuoteKind), so a headless test can assert a block button changed the caret block.

func (*State) RichFirstHeading

func (s *State) RichFirstHeading() string

RichFirstHeading is the plain text of the document's first Heading block ("" if none), for a headless structure assertion.

func (*State) RichFirstHeadingID

func (s *State) RichFirstHeadingID() string

RichFirstHeadingID is the ID (LaTeX \label anchor) of the document's first Heading block ("" if none / no anchor) — the v0.2.0 Heading.ID, so a headless test can prove `\section{...}\label{...}` folded the label into the heading.

func (*State) RichHasBold

func (s *State) RichHasBold() bool

RichHasBold reports whether any Strong (bold) inline exists anywhere in the document, so a headless test can assert a parsed \textbf survived.

func (*State) RichPlainText

func (s *State) RichPlainText() string

RichPlainText is the document's plain text, for a headless content assertion.

func (*State) RichSelectBlock

func (s *State) RichSelectBlock(bi int)

RichSelectBlock selects block bi's whole editable content and parks the caret at its end, so a headless test can apply an inline verb to a real selection.

func (*State) RichToggleStrong

func (s *State) RichToggleStrong()

RichToggleStrong applies bold over the current RichEditor selection.

func (*State) RichToolbarButtonCount

func (s *State) RichToolbarButtonCount() int

RichToolbarButtonCount is the number of formatting buttons (12: 4 inline, 6 block, 2 list).

func (*State) RichToolbarButtonPressed

func (s *State) RichToolbarButtonPressed(i int) bool

RichToolbarButtonPressed reports whether button i shows its active (pressed) pill — the toolbar's reflection of the formatting in force at the caret / selection. An out-of-range index reports false.

func (*State) RichToolbarButtonRects

func (s *State) RichToolbarButtonRects() [][4]int

RichToolbarButtonRects is the device rectangle of every formatting button, in grouped order (see the rtb* constants), so a headless harness can dispatch a real pointer click at a button's centre.

func (*State) RichToolbarRect

func (s *State) RichToolbarRect() [4]int

RichToolbarRect is the device rectangle of the formatting-toolbar strip (zero height while it is hidden), for the headless harness.

func (*State) RichToolbarVisible

func (s *State) RichToolbarVisible() bool

RichToolbarVisible reports whether the formatting toolbar is currently shown (the WYSIWYG tab is active). Host / headless introspection.

func (*State) SelectedScheme

func (s *State) SelectedScheme() int

func (*State) SelectionText

func (s *State) SelectionText() string

func (*State) SetAssetLoading

func (s *State) SetAssetLoading(name string)

SetAssetLoading names the background asset currently downloading ("" clears it). The topZone shows it in place of the ready line, so the reader can see which wasm is still on its way.

func (*State) SetAssetProgress

func (s *State) SetAssetProgress(frac float64)

SetAssetProgress records how far the named background asset has downloaded. frac is clamped to [0,1]; pass a negative value for "cannot tell", which the workspace renders as an indeterminate bar rather than a dishonest 0%.

func (*State) SetBootNotice

func (s *State) SetBootNotice(msg string)

SetBootNotice records why the sample repository is not there ("" clears it). The topZone shows it in place of the ready line, so a reader is told rather than left with a workspace that never opened.

func (*State) SetBuildInfo

func (s *State) SetBuildInfo(version, buildTime string)

SetBuildInfo stamps the build identity into the last status-bar segment: a git short SHA (version) and a UTC build timestamp, both baked into the wasm at deploy time via `-ldflags -X`. The wasm shell (cmd/playground-wasm) calls it ONCE at startup with those injected values; a native/dev build never calls it and keeps the defaultBuildInfo placeholder. Because it targets the segment updateStatus leaves alone, the stamp survives every later compile — so a SHA visible on screen corresponds exactly to the wasm actually running (a new SHA after a deploy proves the fresh binary loaded past any Pages/CDN cache).

It is an immutable set-once value, not a per-frame cross-boundary copy, so it respects the MVVM boundary (no reactive Observable is warranted for a constant stamped in at init).

func (*State) SetClipboardWriter

func (s *State) SetClipboardWriter(w func(string))

SetClipboardWriter installs the host hook that mirrors every copy/cut to the OS clipboard (the wasm driver wires it to navigator.clipboard.writeText).

func (*State) SetCollabBaseURL

func (s *State) SetCollabBaseURL(base string)

SetCollabBaseURL sets the origin+path a scan-to-connect QR/URL primes. The js layer passes the live [location] at startup so a scanned code opens the exact deployment it was generated on; an empty string is ignored, keeping the built-in default ([signalBaseURL]).

func (*State) SetCollabICEServers

func (s *State) SetCollabICEServers(csv string)

SetCollabICEServers reconfigures the WebRTC ICE (STUN/TURN) servers from a config string (see [parseICEServers]); it takes effect on the next Host/Join. An empty or all-blank string restores host-candidate-only signalling. The panel field is kept in sync with the resulting configuration, so a programmatic reconfiguration (a restored setting, the gotexSetICEServers hook) shows up in the visible field.

func (*State) SetCollabName

func (s *State) SetCollabName(n string)

func (*State) SetCollabOpen

func (s *State) SetCollabOpen(open bool)

SetCollabOpen opens or closes the Collaborate panel (host / headless-proof control; the user toggles it with the launcher button).

func (*State) SetEditorCaretVisible

func (s *State) SetEditorCaretVisible(v bool)

SetEditorCaretVisible shows or hides the source editor's insertion caret and repaints. The host drives it on a timer to blink the caret (so it is easier to spot), resetting it visible on input; a hidden caret leaves focus, selection and the IME preview untouched (see go-widgets/toolkit TextView.SetCaretVisible).

func (*State) SetEditorTab

func (s *State) SetEditorTab(idx int)

SetEditorTab selects the editor view (0 = Source, 1 = WYSIWYG) directly, driving the same enter/leave path the toolbar toggle takes — the host/headless hook for the reactive view state. Any non-WYSIWYG index means Source.

func (*State) SetFindShortcut

func (s *State) SetFindShortcut(label string)

SetFindShortcut sets the keyboard-shortcut hint shown on the Find button (the wasm shell passes the platform-correct label, "⌘F" on macOS else "Ctrl+F"), through the toolkit Button's optional MVVM Shortcut adornment.

func (*State) SetGitAuthor

func (s *State) SetGitAuthor(a string)

func (*State) SetGitBranch

func (s *State) SetGitBranch(b string)

func (*State) SetGitCommitMessage

func (s *State) SetGitCommitMessage(m string)

func (*State) SetGitEmail

func (s *State) SetGitEmail(e string)

func (*State) SetGitOpen

func (s *State) SetGitOpen(open bool)

SetGitOpen opens or closes the panel (host / headless-proof control). Opening prewarms the worker so git-worker.wasm streams in before the first clone.

func (*State) SetGitProgress

func (s *State) SetGitProgress(line string, frac float64)

SetGitProgress records the remote's latest clone/pull progress (the worker forwards it off the sideband; see git_js.go): line is the raw phase text and frac its parsed [0,1] completion (or negative for "cannot tell"). It is best-effort — an update that arrives with no op in flight is dropped rather than left to linger — and repaints so the sidebar's bar/phase tracks the object count as it climbs. A no-op that leaves nothing changed skips the repaint so a flat-lined percentage does not churn frames.

func (*State) SetGitRemember

func (s *State) SetGitRemember(on bool)

SetGitRemember reflects a host-side "remember" state into the panel WITHOUT re-triggering the hook (used at startup when a token was restored from the credential store): it only flips the switch's visual/opt-in state.

func (*State) SetGitRememberHook

func (s *State) SetGitRememberHook(f func(store bool, token string))

SetGitRememberHook installs the host callback the panel calls when the "remember on this device" opt-in changes, or when the token changes while it is on: store==true asks the host to persist the given token in the browser's credential store, store==false asks it to drop any persisted token. This hook is the ONLY path by which the token leaves the in-memory Observable — there is deliberately no GitToken() getter (see [[gotex-playground-ui-architecture]]).

func (*State) SetGitToken

func (s *State) SetGitToken(t string)

func (*State) SetGitURL

func (s *State) SetGitURL(u string)

Config setters/getters (the host wires these to the DOM form / persistence; tests drive them directly). Every setter routes through the observable so the panel repaints.

func (*State) SetLineBands

func (s *State) SetLineBands(page int, lines, tops, bots []int)

SetLineBands records where a page's source lines were actually drawn, MEASURED FROM THE DOM by the host.

The bands used to come from the rasteriser's per-<g> bounds. With no rasteriser there is nothing in Go that knows where a line ended up — the browser laid the text out, so the browser is what measures it. The host reads every <g data-l="N"> element's box, converts it to this page's natural pixels, and reports it here; everything downstream (click to caret, caret to scroll) is unchanged, because this is the space it always worked in.

page is 1-based. lines, tops and bots are parallel; a short or ragged set is ignored rather than half-applied, so a bad measurement cannot half-break the linking.

func (*State) SetNavigate

func (s *State) SetNavigate(f func(url string))

SetNavigate installs the host navigation hook a bottomZone link click drives. The wasm driver wires it to window.location.href = url; a native build leaves it nil, so a link click is a no-op there (the whole affordance stays testable off a browser).

func (*State) SetRenderPaginated

func (s *State) SetRenderPaginated(on bool)

func (*State) SetSidebarOpen

func (s *State) SetSidebarOpen(open bool)

SetSidebarOpen sets the sidebar open state explicitly (host / proof control).

func (*State) SetSiteRoot

func (s *State) SetSiteRoot(base string)

SetSiteRoot sets the "Back to go-tex" link's target — the deployment's own site root. The js layer passes location.origin + "/" at startup so the link points at the exact site the app is served from; an empty string is ignored, keeping the built-in default ([defaultSiteRoot]).

func (*State) SetSource

func (s *State) SetSource(text string)

func (*State) SetSourceCursor

func (s *State) SetSourceCursor(text string, line, col, scroll int)

SetSourceCursor replaces the editor buffer AND restores a saved caret + scroll position, without firing OnEdit. It is how switching back to a file re-opens its independent edit buffer (git.go's openFile) so the unsaved edits AND the place the user left off both come back. Like SetSource it recompiles.

func (*State) SetTheme

func (s *State) SetTheme(dark bool)

SetTheme switches the palette and recompiles so the render pane's paper/ink follow the new theme.

func (*State) SetThemeApply

func (s *State) SetThemeApply(f func(mode string))

SetThemeApply installs the host hook the in-app theme button drives to put the chosen mode on the page (set data-theme + persist). The host's resulting recolour flows back through SetTheme, so this hook only carries the selection outward. A native build leaves it nil, so the button still cycles its own label and stays testable off a browser.

func (*State) SetThemeMode

func (s *State) SetThemeMode(mode string)

SetThemeMode seeds the button's current selection from the host (the value the page restored from localStorage at startup), without driving a re-apply.

func (*State) SetTimeProvider

func (s *State) SetTimeProvider(now func() string)

SetTimeProvider installs the host clock the compile Log stamps its entries with. The wasm driver wires it to the browser's new Date().toLocaleTimeString() so entries carry the viewer's local time; a nil hook is ignored (the Go wall-clock default stays in place).

func (*State) ShowLog

func (s *State) ShowLog() bool

func (*State) SidebarDetail

func (s *State) SidebarDetail() string

SidebarDetail is the detail strip's current text (a clicked commit's detail, or the latest git notice/error).

func (*State) SidebarFileRows

func (s *State) SidebarFileRows() []string

SidebarFileRows returns the flattened visible file/dir rows as "name" strings with their badge glyph appended (" M"/" A"/…), so a headless proof can assert the tree + per-file status without pixel-reading. Directories carry no badge.

func (*State) SidebarOpen

func (s *State) SidebarOpen() bool

SidebarOpen reports whether the workspace sidebar is showing.

func (*State) SidebarRect

func (s *State) SidebarRect() [4]int

SidebarRect is the sidebar column's device-pixel [x,y,w,h] bounds.

func (*State) SidebarTimelineTitles

func (s *State) SidebarTimelineTitles() []string

SidebarTimelineTitles returns the commit-timeline event titles, newest first.

func (*State) SidebarWidth

func (s *State) SidebarWidth() int

SidebarWidth is the column's reserved device width (0 when closed).

func (*State) Size

func (s *State) Size() (int, int)

Size returns the current surface size.

func (*State) Source

func (s *State) Source() string

Source returns the current editor buffer.

func (*State) SubscribeCloning

func (s *State) SubscribeCloning(cb func())

SubscribeCloning registers cb to run whenever the clone-in-flight state may have changed, so the host can start its animation frame loop when a clone begins. cb should check Cloning and (re)start the loop when it becomes true.

func (*State) SubscribeGitBusy

func (s *State) SubscribeGitBusy(cb func())

SubscribeGitBusy registers cb to run whenever ANY git operation starts or ends, so the host can start its animation frame loop for a pull/push/commit too — not only the initial clone. cb should check GitBusy and (re)start the loop when it becomes true.

func (*State) TakePendingCompile

func (s *State) TakePendingCompile() bool

func (*State) Theme

func (s *State) Theme() *toolkit.Theme

func (*State) ThemeMode

func (s *State) ThemeMode() string

ThemeMode is the current theme selection (host/harness introspection).

func (*State) Tick

func (s *State) Tick(dt float64)

Tick advances time-based animations (the workspace loading spinner) by dt seconds and accumulates the in-flight git operation's elapsed time. The host calls it once per animation frame while a git operation is in flight (GitBusy).

func (*State) ToggleFindReplace

func (s *State) ToggleFindReplace() bool

ToggleFindReplace shows or hides the regex find-and-replace modal over the active editor and relays out. It is the host hook the wasm driver wires to the ⌘F/Ctrl+F keydown (the toolbar Find button drives the same fr.toggle). It always reports true so the driver re-renders after the toggle.

func (*State) ToggleSidebar

func (s *State) ToggleSidebar()

ToggleSidebar flips the sidebar and relays out (host toolbar / proof control).

func (*State) ToggleWysiwyg

func (s *State) ToggleWysiwyg()

ToggleWysiwyg flips the editor view (Source <-> WYSIWYG) from the host (the wasm driver and the headless harness), driving the same enter/leave path the toolbar toggle button takes.

func (*State) TopZoneRect

func (s *State) TopZoneRect() [4]int

TopZoneRect / BottomZoneRect are the two bands' device-pixel [x,y,w,h] bounds.

func (*State) TopZoneStatusText

func (s *State) TopZoneStatusText() string

TopZoneStatusText is the topZone's status line.

func (*State) WysiwygActive

func (s *State) WysiwygActive() bool

WysiwygActive reports whether the RichEditor (the WYSIWYG tab) is currently shown.

func (*State) WysiwygAnchorCount

func (s *State) WysiwygAnchorCount() int

func (*State) WysiwygCrossRefCount

func (s *State) WysiwygCrossRefCount() int

func (*State) WysiwygFootnoteCount

func (s *State) WysiwygFootnoteCount() int

WysiwygFootnoteCount / WysiwygCrossRefCount / WysiwygAnchorCount count the v0.2.0 reference inlines the RichEditor holds — the marker the RichEditor paints as a superscript (Footnote), the accent run it paints for an inline-less CrossRef (\ref / \cite), and any point Anchor (\label). A headless test asserts on these to prove Parse yielded the v0.2 nodes and the editor adopted them.

func (*State) WysiwygParseError

func (s *State) WysiwygParseError() string

WysiwygParseError is the last parse error message ("" when none).

func (*State) ZoomPercent

func (s *State) ZoomPercent() int

Directories

Path Synopsis
cmd
collab-failtest command
Command collab-failtest is the real-browser proof that a WebRTC connection which cannot find a path to the peer surfaces a CLEAR failure in the Collaborate panel — the TURN-server guidance — instead of hanging silently on a "waiting…" line.
Command collab-failtest is the real-browser proof that a WebRTC connection which cannot find a path to the peer surfaces a CLEAR failure in the Collaborate panel — the TURN-server guidance — instead of hanging silently on a "waiting…" line.
collab-icetest command
git-worker command
Command git-worker is the playground's OFF-THREAD remote-git client.
Command git-worker is the playground's OFF-THREAD remote-git client.
playground-wasm command
internal
browsergit
Package browsergit is an in-browser (Go/wasm) remote-git client for the go-tex playground.
Package browsergit is an in-browser (Go/wasm) remote-git client for the go-tex playground.
gitrpc
Package gitrpc is the message protocol shared by the playground's main app and its off-thread git worker.
Package gitrpc is the message protocol shared by the playground's main app and its off-thread git worker.

Jump to

Keyboard shortcuts

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