Namespace Ofs
Classes
- AppScoped<T>
A value of type
Tthat lives in this plugin's global settings: it is loaded once when created and saved back whenever it changes. Get it once in OnLoad() via AppScoped<T>(string), then read and mutate Value directly (its fields/properties bind straight into Ui widgets) — the host flushes edits once per frame and again on app close, so there is nothing to call.Unlike ProjectScoped<T>, this is global to all projects (it does not reset or reload when a different project is opened); it is persisted in this plugin's own file under the app's settings directory.
Tis JSON-serialized including public fields, so a plain settings class with public fields works without attributes. Main-thread only.
- Axes
The set of funscript axes. Reads are cheap and direct through the indexer; writes go through a buffered Edit() finished with Commit().
- Axis
A read-only snapshot of one axis for the current frame: its sorted actions, selection and O(log n) lookup helpers. Begin a write with Edit().
- AxisEdit
A buffered, mutable working copy of an axis. Mutators are fluent and accumulate locally — nothing is applied to the project until Commit(). One Commit is one undo step. An uncommitted edit is a clean no-op.
- Commands
Command registration. Handlers are supplied inline — there is no central dispatch switch. Call Register(string, string, Action, bool, bool) from OnLoad.
- Dialogs
Native file/folder/message dialogs, backed by the host's native dialog integration. Every call is NON-BLOCKING: it queues the dialog and returns a Task that completes — on the main thread, on a later frame — once the user answers.
awaitit from a command or button handler; the frame loop keeps rendering while the dialog is open. Main-thread only (call, not the await).
- DiscreteWriter
Write sink for a discrete node callback.
- Editing
Registration of alternate edit modes. A mode owns how the user's editing gestures resolve — it can observe, drop, or replace each EditIntent. Registering only publishes the mode; the user activates it from the footer (there is no plugin-callable setter). Call RegisterMode(string, string, EditIntentHandler, Action?, Action?, Action<Ui>?) from OnLoad().
- Navigation
Registration of alternate navigators. A navigator owns what the prev/next-step keys do — given a step, it decides where the playhead goes (next action, a custom grid, …). It does not move the playhead any other way and cannot edit actions. Registering only publishes the navigator; the user activates it from the footer (no plugin-callable setter). Call RegisterMode(string, string, NavStepHandler, Action?, Action?, Action<Ui>?) from OnLoad().
- Node
A capture-safe handle to a plugin node, obtained from Node() inside the node's NodeUi<TState> callback. A
TStatevalue cannot be captured byrefacross anawait, so async work captures this reference instead and calls Update<TState>(StateMutator<TState>) when it completes. The mutation is queued and applied on the main thread on the node's next UI pass — replace reference fields, don't mutate them in place.
- Nodes
Registration of custom processing nodes. A node declares a NodeShape (N named input pins, M named output pins) and one eval delegate; the signal kind (functional vs discrete) is chosen by the delegate shape.
- OfsPlugin
Base class for every ofs-ng plugin. Derive from it and override only what you need. The single required member is Name.
- Player
Playback state, commands and events. State is exposed as properties; commands are methods; notifications are events. All members must be touched on the main thread.
- Project
Access to project-level state: unsaved status, metadata, chapters, bookmarks and processing regions (all read-only snapshots), plus this plugin's own per-project data store (Scoped<T>(string)), which is persisted inside the .ofp and round-trips with save/load. All members are main-thread only and read fresh each call — the returned lists are snapshots, not live views.
- ProjectScoped<T>
A value of type
Tthat lives in the current project: it is loaded from the project file on creation, automatically reloaded whenever a different project is opened, and saved back when it changes. Get it once in OnLoad() via Scoped<T>(string), then read and mutate Value directly (its fields/properties bind straight into Ui widgets) — the host flushes edits once per frame, so there is nothing to call. A project with nothing stored under the key resets Value to a freshnew T(), so settings never bleed between projects.Tis JSON-serialized including public fields, so a plain settings class with public fields works without attributes. Main-thread only.
- Selection
Registration of alternate selection modes. A mode owns which actions a selection gesture selects — given the region the user gestured over, it decides what becomes selected (only the peaks, only actions above a threshold, every Nth, …). It cannot edit actions, only select existing ones. Registering only publishes the mode; the user activates it from the footer's Select selector (no plugin-callable setter). Call RegisterMode(string, string, SelectHandler, Action?, Action?, Action<Ui>?) from OnLoad().
- StandardAxisExtensions
Helpers for StandardAxis.
- Ui
Immediate-mode widgets, drawn each frame inside OnRenderUi(Ui). Boolean-returning widgets report "changed this frame"; value widgets bind through ref.
Conventions: widget width is host-owned — there is no pixel-size parameter; a widget fills the available width, and widgets inside Row(string, Action) split it evenly, so plugin UI is automatically font-, DPI- and translation-safe (use Section(string, Action)/Row(string, Action) for layout). Labels and titles are rendered verbatim — localizing them is the plugin's job.
maxBytesparameters count UTF-8 bytes, not characters (a CJK glyph is 3 bytes, an emoji 4), so size text buffers generously for non-ASCII input.
Structs
- DiscreteReader
Read-only view of a discrete node input.
- EditIntent
A single edit gesture an active edit mode observes and may transform. The host hands one of these to
onIntent; only the fields named for Kind are meaningful. To replace a gesture, build new intents with the static factory methods and return Replace(params EditIntent[]).
- EditResult
What an edit mode decides for an intent: apply it unchanged, drop it, or replace it with other intents.
- Nav
A navigator's answer to a NavStep: seek to a time, do nothing, or pass the step to the native resolution for its granularity.
- NavStep
A step request handed to an active navigator: which way, how many steps, which channel.
- NodeContext
Evaluation context passed to every node callback. Pure value — safe on any thread. The params buffer is valid only for the duration of the call; do not store it.
- NodeShape
A node's pin shape: the input and output pin names. Each array's length is that direction's pin count. Pin names double as labels (and, for scripts, injected locals). Construct with named pins, e.g.
new NodeShape(inputs: ["a", "b"], outputs: ["mix"]).
- ProjectBookmark
A point-in-time bookmark.
- ProjectChapter
A named chapter: a time range with a color.
- ProjectRegion
A processing region: a named time range with its own node graph.
- ScriptAction
A point in a funscript: a timestamp (At, seconds) and a position (Pos, 0–100). Value type with value equality, deconstruction and
with-expressions. The sequential layout keeps it blittable so it can cross the native ABI without marshaling.
- SelectRequest
One selection gesture handed to an active selection mode, once per editable axis (the host fans out per-axis). The mode reads the region and axis, enumerates candidates itself through
Host.Axes[req.Axis], and returns the actions it keeps.
- SelectResult
What a selection mode decides for a request: select the native candidates, select nothing, or select exactly the named actions instead.
Interfaces
- IOfsHost
The host surface available to a plugin, organized into cohesive sub-objects.
Enums
- ConfirmKind
The button set for Confirm(string, string, ConfirmKind, CancellationToken).
- EditIntentKind
The kind of an EditIntent — what authoring gesture the user made. Mirrors the host's edit-intent vocabulary.
- FunscriptVersion
The funscript on-disk format version selectable in FunscriptJson(FunscriptVersion, params StandardAxis[]).
- LogLevel
Severity of a host log message written via Log(LogLevel, string).
- NavGranularity
Which step a NavStep targets. A navigator may redefine one channel and Pass the rest back to the native (overlay / adjacent-action) resolution.
- NodeIcon
The glyph a node shows in the add-node palette and on its title bar — a curated subset of the host's icons, chosen by value so a plugin never needs the host font's codepoints. Pass one to AddNode<TState>(string, string, NodeShape, EvalFunctional<TState>, NodeUi<TState>?, string?, NodeIcon, string?). Default lets the host pick by arity (Generate / Modify / Combine). The list is append-only; an older host that doesn't know a newer value falls back to Default.
- NotifyLevel
Severity of a user-facing toast raised via Notify(NotifyLevel, string).
- SelectGesture
Which selection-authoring gesture a SelectRequest carries. A mode reinterprets what the gesture means; the native resolution selects every candidate it covers.
- StandardAxis
A funscript axis role, including the trailing Count sentinel. The values are part of the frozen plugin ABI and never change.
- StepDirection
A step / nudge direction shared by Direction and Direction. None is an EditIntent-only state: a position nudge rather than a time nudge (Pos carries the delta). A NavStep only ever carries Backward or Forward.
Delegates
- EditIntentHandler
Handles one edit intent for an active mode. Runs on the main thread.
- EvalDiscrete
Discrete eval (worker thread) for a stateless node: read the N input action lists and write the M output action lists once per region.
- EvalDiscrete<TState>
Discrete eval (worker thread): read the N input action lists and write the M output action lists once per region.
- EvalFunctional
Functional eval (worker thread) for a stateless node: given time
t, read the N input values ininsand write the M output values (0..100) intoouts.
- EvalFunctional<TState>
Functional eval (worker thread): given time
t, read the N input values ininsand write the M output values (0..100) intoouts.
- FunctionalSample
The per-sample closure a functional factory returns: read the N input values, write the M output values. Built once per region eval, sampled per output sample.
- NodeUi<TState>
The node's body UI (main thread). Draws the node's widgets — sliders/checkboxes for its
TStatefields, buttons, file pickers, status labels.sis the live state byref: mutate it synchronously, replacing reference fields with new arrays/lists rather than mutating in place. For a deferred/async write (across anawait) grab a capture-safe handle with Node() and call Update<TState>(StateMutator<TState>) when the work completes — a value type cannot be captured byrefacross an await.
- PrepareFunctional
Functional factory (worker thread) for a stateless node: runs once per region eval and returns the per-sample closure.
- PrepareFunctional<TState>
Functional factory (worker thread): runs once per region eval and returns the per-sample closure. Build a non-serializable artifact (LUT, interpolator, loaded dataset) here rather than rebuilding it every sample.
- SelectHandler
Resolves one SelectRequest into a SelectResult. Runs on the main thread.
- StateMutator<TState>
A deferred mutation applied on the main thread to a node's live state, via Update<TState>(StateMutator<TState>).