TypeScript Types
Complete list of exported types from @fcannizzaro/streamdeck-react.
All types are exported from the main @fcannizzaro/streamdeck-react package.
Plugin & Action Configuration
interface PluginConfig {
/** Stream Deck adapter. Defaults to physicalDevice() (Elgato SDK). */
adapter?: StreamDeckAdapter;
fonts: FontConfig[];
actions: ActionDefinition[];
wrapper?: WrapperComponent;
/**
* Plugin manifest metadata. Optional — used for runtime documentation.
* For manifest generation, provide plugin-level info via the bundler plugin's `manifest` option.
*/
info?: PluginManifestInfo;
/**
* Takumi renderer backend.
* - `"native-binding"` — uses `@takumi-rs/core` (native Rust NAPI addon).
* - `"wasm"` — uses `@takumi-rs/wasm` (WebAssembly build). WOFF fonts are
* not supported in this mode — use TTF/OTF only. Workers are force-disabled.
* @default "native-binding"
*/
takumi?: TakumiBackend;
imageFormat?: "png" | "webp";
caching?: boolean;
devicePixelRatio?: number;
onActionError?: (uuid: string, actionId: string, error: Error) => void;
devtools?: boolean;
/** Enable performance diagnostics (render counters, duplicate detection, depth warnings). @default NODE_ENV !== 'production' */
debug?: boolean;
/** Maximum image cache size in bytes for key/dial renders. Set to 0 to disable. @default 16777216 (16 MB) */
imageCacheMaxBytes?: number;
/** Maximum TouchStrip raw buffer cache size in bytes. Set to 0 to disable. @default 8388608 (8 MB) */
touchStripCacheMaxBytes?: number;
/** Offload Takumi rendering to a worker thread. Auto-detected: enabled only if any action defines a `touchStrip` component. Automatically disabled when `takumi` is `"wasm"`. @default auto-detect */
useWorker?: boolean;
/**
* Enable the Action Coordinator for cross-action communication.
* When enabled, actions can use `useChannel()` for shared state
* and `useActionPresence()` to observe which actions are visible.
* @default false
*/
coordinator?: boolean;
/**
* CSS theme definition. Tokens are injected as CSS custom properties
* on every root's container, making them available via Tailwind v4
* arbitrary values: `bg-[var(--color-primary)]`.
* Create a theme with `defineTheme()`.
*/
theme?: ThemeDefinition;
/**
* CSS stylesheets to pass to the Takumi renderer. Enables full
* Tailwind v4 support including `@theme` blocks, custom utilities,
* and any standard CSS. Import your CSS file with `?inline` to get
* the compiled stylesheet as a string at build time.
*/
stylesheets?: string[];
}
interface FontConfig {
name: string;
data: ArrayBuffer | Buffer;
weight: 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900;
style: "normal" | "italic";
}
interface GoogleFontVariant {
weight?: 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900;
style?: "normal" | "italic";
}
interface ActionConfig<S extends JsonObject = JsonObject> {
uuid: string;
key?: ComponentType;
dial?: ComponentType;
touchStrip?: ComponentType;
dialLayout?: EncoderLayout;
wrapper?: WrapperComponent;
defaultSettings?: Partial<S>;
}
interface ActionDefinition<S extends JsonObject = JsonObject> {
uuid: string;
key?: ComponentType;
dial?: ComponentType;
touchStrip?: ComponentType;
dialLayout?: EncoderLayout;
wrapper?: WrapperComponent;
defaultSettings: Partial<S>;
}
type EncoderLayout = string | TouchStripLayout;
interface TouchStripLayout {
$schema?: string;
id: string;
items: TouchStripLayoutItem[];
}
type TouchStripLayoutItem =
| TouchStripBarItem
| TouchStripGBarItem
| TouchStripPixmapItem
| TouchStripTextItem;
interface TouchStripBarItem {
type: "bar";
key: string;
rect: [x: number, y: number, width: number, height: number];
value: number;
background?: string;
enabled?: boolean;
opacity?: TouchStripOpacity;
zOrder?: number;
bar_bg_c?: string;
bar_border_c?: string;
bar_fill_c?: string;
border_w?: number;
range?: { min: number; max: number };
subtype?: 0 | 1 | 2 | 3 | 4;
}
interface TouchStripGBarItem {
type: "gbar";
key: string;
rect: [x: number, y: number, width: number, height: number];
value: number;
bar_h?: number;
background?: string;
enabled?: boolean;
opacity?: TouchStripOpacity;
zOrder?: number;
bar_bg_c?: string;
bar_border_c?: string;
bar_fill_c?: string;
border_w?: number;
range?: { min: number; max: number };
subtype?: 0 | 1 | 2 | 3 | 4;
}
interface TouchStripPixmapItem {
type: "pixmap";
key: string;
rect: [x: number, y: number, width: number, height: number];
value?: string;
background?: string;
enabled?: boolean;
opacity?: TouchStripOpacity;
zOrder?: number;
}
interface TouchStripTextItem {
type: "text";
key: string;
rect: [x: number, y: number, width: number, height: number];
value?: string;
alignment?: "center" | "left" | "right";
color?: string;
font?: { size?: number; weight?: number };
"text-overflow"?: "clip" | "ellipsis" | "fade";
background?: string;
enabled?: boolean;
opacity?: TouchStripOpacity;
zOrder?: number;
}
type TouchStripOpacity = 0 | 0.1 | 0.2 | 0.3 | 0.4 | 0.5 | 0.6 | 0.7 | 0.8 | 0.9 | 1;
type WrapperComponent = ComponentType<{ children?: ReactNode }>;Render Pipeline Types
/** Per-render timing and diagnostic data exposed via the onProfile callback. */
interface RenderProfile {
/** Time to convert VNode tree to Takumi node tree (ms). */
vnodeConversionMs: number;
takumiRenderMs: number;
hashMs: number;
base64Ms: number;
totalMs: number;
skipped: boolean;
/** Whether this render was served from the image cache. */
cacheHit: boolean;
treeDepth: number;
nodeCount: number;
/** Image cache statistics at the time of this render. */
cacheStats: CacheStats | null;
}
/** Image cache statistics. */
interface CacheStats {
/** Number of entries currently in the cache. */
entries: number;
/** Current memory usage in bytes. */
bytes: number;
/** Maximum memory budget in bytes. */
maxBytes: number;
/** Total cache hits since creation or last reset. */
hits: number;
/** Total cache misses since creation or last reset. */
misses: number;
}
/** Rolling-window render pipeline statistics. */
interface RenderMetrics {
/** Total flush() calls (render attempts). */
flushCount: number;
/** Flushes that reached the Takumi renderer. */
renderCount: number;
/** Tree hash cache hits (Phase 2). */
cacheHitCount: number;
/** Skipped due to clean tree — dirty flag check (Phase 1). */
dirtySkipCount: number;
/** Skipped due to identical output — post-render xxHash output dedup (Phase 4). */
hashDedupCount: number;
/** Average Takumi render time in milliseconds. */
avgRenderMs: number;
/** Peak (worst-case) render time in milliseconds. */
peakRenderMs: number;
/** Image cache memory usage in bytes. */
imageCacheBytes: number;
/** TouchStrip cache memory usage in bytes. */
touchStripCacheBytes: number;
}Size Utility Types
/** Size helper with percentage-based and proportional calculations. */
interface SizeHelper {
/** Raw canvas width in pixels. */
readonly width: number;
/** Raw canvas height in pixels. */
readonly height: number;
/** Minimum of width and height. Useful for circular elements. */
readonly min: number;
/** Maximum of width and height. */
readonly max: number;
/** True if width equals height (e.g., key surfaces: 144×144). */
readonly square: boolean;
/** True if width > height (e.g., dials: 200×100). */
readonly landscape: boolean;
/** True if height > width. */
readonly portrait: boolean;
/** Aspect ratio (width / height). */
readonly aspectRatio: number;
/** Calculate a percentage of the canvas width, rounded. */
w(percent: number): number;
/** Calculate a percentage of the canvas height, rounded. */
h(percent: number): number;
/** Calculate a percentage of the minimum dimension, rounded. */
minP(percent: number): number;
/** Calculate a percentage of the maximum dimension, rounded. */
maxP(percent: number): number;
/**
* Scale a base pixel value proportionally to canvas size.
* Uses the minimum dimension as reference (default 144).
*/
scale(basePx: number, referenceSize?: number): number;
}Coordinator Types
/** Information about a currently visible action instance. */
interface ActionPresenceInfo {
/** Unique action instance ID (contextId). */
id: string;
/** Action definition UUID (e.g., "com.example.plugin.volume"). */
uuid: string;
/** Surface type this instance is rendered on. */
surface: "key" | "dial" | "touch";
/** Grid coordinates on the device (undefined for touch strip). */
coordinates?: { column: number; row: number };
/** Device ID this instance belongs to. */
deviceId: string;
}
/** Snapshot of all presence data. */
interface ActionPresenceSnapshot {
/** All currently visible action instances. */
readonly all: readonly ActionPresenceInfo[];
/** Filter instances by action UUID. */
byUuid(uuid: string): readonly ActionPresenceInfo[];
/** Total count of visible action instances. */
readonly count: number;
}Theme Types
/** Flat map of CSS variable names to their values. */
type ThemeVariables = Record<string, string>;
/** Input shape for defineTheme — organized by category. */
interface ThemeInput {
colors?: Record<string, string>;
spacing?: Record<string, string>;
fontSize?: Record<string, string>;
borderRadius?: Record<string, string>;
/** Arbitrary category. Keys become `--{category}-{key}`. */
[category: string]: Record<string, string> | undefined;
}
/** Resolved theme definition with flattened CSS variables. */
interface ThemeDefinition {
/** Flat map of CSS variable names (e.g. `"--color-primary"`) to values. */
readonly variables: ThemeVariables;
}Manifest Types
These types support the manifest generation system. Action metadata is defined via defineAction({ info }) and auto-extracted at build time.
/** Per-action manifest metadata provided via defineAction({ info }). */
interface ActionManifestInfo {
name: string;
icon: string;
disabled?: boolean;
tooltip?: string;
states?: ManifestStateInfo[];
encoder?: ManifestEncoderInfo;
controllers?: [ManifestController, ManifestController?];
// ... additional optional fields
}
/** Plugin-level manifest metadata provided via streamDeckReact({ manifest }). */
interface PluginManifestInfo {
uuid: string;
name: string;
author: string;
description: string;
icon: string;
version: string;
category?: string;
// ... additional optional fields with sensible defaults
}Takumi Backend
/** Takumi renderer backend selection. */
type TakumiBackend = "native-binding" | "wasm";Bundler Plugin Types
These types are exported from @fcannizzaro/streamdeck-react/vite.
NativeModuleConfig
Configuration for a native .node module to be lazy-loaded or copied at build time. The built-in Takumi binding (@takumi-rs/core) is always registered automatically — use this interface to add additional native modules.
interface NativeModuleConfig {
/** The npm import specifier to intercept (e.g., "@mypkg/native-core"). */
importSpecifier: string;
/**
* The npm scope for platform-specific packages.
* Auto-inferred from scoped importSpecifier when omitted
* (e.g., "@mypkg/foo" → "@mypkg").
*/
scope?: string;
/**
* Platform → binding map. Keys are "<platform>-<arch>" strings
* (e.g., "darwin-arm64", "win32-x64").
*/
bindings: Record<string, { pkg: string; file: string }>;
/** Named exports to re-export from the loaded binding. */
exports: string[];
/** Explicit version (auto-resolved from package.json when omitted). */
version?: string;
}StreamDeckReactOptions
interface StreamDeckReactOptions {
targets?: StreamDeckTarget[];
takumi?: TakumiBackend;
nativeBindings?: "lazy" | "copy";
/** Additional native modules with the same lazy/copy treatment as Takumi. */
nativeModules?: NativeModuleConfig[];
uuid?: string;
manifest?: PluginManifestInfo;
}Library-Owned Primitives
/** Controller surface type. */
type Controller = "Keypad" | "Encoder";
/** Grid coordinates for a key or encoder on a device. */
interface Coordinates {
column: number;
row: number;
}
/** Device grid size (number of key columns and rows). */
interface Size {
columns: number;
rows: number;
}Context Types
interface DeviceInfo {
id: string;
/** Numeric device type matching Elgato DeviceType enum values (e.g. 7 = StreamDeckPlus). */
type: number;
size: { columns: number; rows: number };
name: string;
}
interface ActionInfo {
id: string;
uuid: string;
controller: "Keypad" | "Encoder";
coordinates?: { row: number; column: number };
isInMultiAction: boolean;
}
interface CanvasInfo {
width: number;
height: number;
type: "key" | "dial" | "touch";
}
interface StreamDeckAccess {
action: AdapterActionHandle;
adapter: StreamDeckAdapter;
}Event Payloads
interface KeyDownPayload {
settings: JsonObject;
isInMultiAction: boolean;
state?: number;
userDesiredState?: number;
}
interface KeyUpPayload {
settings: JsonObject;
isInMultiAction: boolean;
state?: number;
userDesiredState?: number;
}
interface DialRotatePayload {
ticks: number;
pressed: boolean;
settings: JsonObject;
}
interface DialPressPayload {
settings: JsonObject;
controller: "Encoder";
}
interface TouchTapPayload {
tapPos: [x: number, y: number];
hold: boolean;
settings: JsonObject;
}
interface DialHints {
rotate?: string;
press?: string;
touch?: string;
longTouch?: string;
}Touch Bar Types
interface TouchStripInfo {
/** Full render width in pixels (e.g., 800 for 4 encoders). */
width: number;
/** Strip height in pixels (always 100). */
height: number;
/** Sorted list of active encoder columns, e.g., [0, 1, 3]. */
columns: number[];
/** Width of each encoder segment in pixels (always 200). */
segmentWidth: number;
}
interface TouchStripTapPayload {
/** Absolute tap position across the full strip width. */
tapPos: [x: number, y: number];
hold: boolean;
/** The encoder column that was touched. */
column: number;
}
interface TouchStripDialRotatePayload {
column: number;
ticks: number;
pressed: boolean;
}
interface TouchStripDialPressPayload {
column: number;
}Gesture Options
interface TapOptions {
/** Timeout override for the gated delay (inherits from useDoubleTap when omitted). */
timeout?: number;
}
interface LongPressOptions {
/** Milliseconds the key must be held before firing. @default 500 */
timeout?: number;
}
interface DoubleTapOptions {
/** Max milliseconds between two key-up events. @default 250 */
timeout?: number;
}Component Props
interface BoxProps {
className?: string;
center?: boolean;
padding?: number;
background?: string;
borderRadius?: number;
gap?: number;
direction?: "row" | "column";
style?: CSSProperties;
children?: ReactNode;
}
interface TextProps {
className?: string;
size?: number;
color?: string;
weight?: number;
align?: "left" | "center" | "right";
font?: string;
lineHeight?: number;
style?: CSSProperties;
children?: ReactNode;
}
interface ImageProps {
className?: string;
src: string;
width: number;
height: number;
fit?: "contain" | "cover";
borderRadius?: number;
style?: CSSProperties;
}
interface IconProps {
className?: string;
path: string;
size?: number;
color?: string;
viewBox?: string;
style?: CSSProperties;
}
interface ProgressBarProps {
className?: string;
value: number;
max?: number;
height?: number;
color?: string;
background?: string;
borderRadius?: number;
style?: CSSProperties;
}
interface CircularGaugeProps {
className?: string;
value: number;
max?: number;
size?: number;
strokeWidth?: number;
color?: string;
background?: string;
style?: CSSProperties;
}
interface ErrorBoundaryProps {
fallback?: ReactNode;
onError?: (error: Error, errorInfo: ErrorInfo) => void;
children: ReactNode;
}Utility Types
type JsonPrimitive = boolean | number | string | null | undefined;
type JsonValue = JsonObject | JsonPrimitive | JsonValue[];
type JsonObject = { [key: string]: JsonValue };Animation Types
/** A single number or a flat object of named numbers. */
type AnimationTarget = number | Record<string, number>;
/** Maps an AnimationTarget shape to its animated output shape. */
type AnimatedValue<T extends AnimationTarget> = T extends number
? number
: { [K in keyof T]: number };
interface SpringConfig {
/** Stiffness coefficient (force per unit displacement). @default 170 */
tension: number;
/** Damping coefficient (force per unit velocity). @default 26 */
friction: number;
/** Mass of the simulated object. @default 1 */
mass: number;
/** Absolute velocity threshold below which the spring settles. @default 0.01 */
velocityThreshold: number;
/** Absolute displacement threshold below which the spring settles. @default 0.005 */
displacementThreshold: number;
/** Clamp output to target (no overshoot). @default false */
clamp: boolean;
}
interface SpringResult<T extends AnimationTarget> {
value: AnimatedValue<T>;
isAnimating: boolean;
set: (target: T) => void;
jump: (target: T) => void;
}
type EasingName =
| "linear"
| "easeIn"
| "easeOut"
| "easeInOut"
| "easeInCubic"
| "easeOutCubic"
| "easeInOutCubic"
| "easeInBack"
| "easeOutBack"
| "easeOutBounce";
type EasingFn = (t: number) => number;
interface TweenConfig {
/** Duration in milliseconds. @default 300 */
duration: number;
/** Easing function name or custom function. @default "easeOut" */
easing: EasingName | EasingFn;
/** Target FPS for the animation tick loop. @default 30 */
fps: number;
}
interface TweenResult<T extends AnimationTarget> {
value: AnimatedValue<T>;
progress: number;
isAnimating: boolean;
set: (target: T) => void;
jump: (target: T) => void;
}