@fcannizzaro/streamdeck-react

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;
}

On this page