Commands Plugin

The Commands Plugin implements the Command Pattern for your PDF viewer. It serves as a central registry where you define actions (like “Zoom In”, “Next Page”, or “Print”) along with their keyboard shortcuts, icons, and dynamic state logic (enabled/disabled/visible).

By decoupling the logic of an action from the UI component that triggers it, you can build consistent toolbars, menus, and keyboard shortcuts that stay perfectly in sync.

Installation

npm install @embedpdf/plugin-commands

Registration

Import CommandsPluginPackage and add it to your plugin list. You can optionally register your application’s commands immediately via the configuration object.

import { createPluginRegistration } from '@embedpdf/core'; import { CommandsPluginPackage } from '@embedpdf/plugin-commands/svelte'; const plugins = [ // ... other plugins createPluginRegistration(ViewportPluginPackage), createPluginRegistration(ScrollPluginPackage), // Register Commands Plugin createPluginRegistration(CommandsPluginPackage, { commands: myCommands, // See "Defining Commands" below }), ];

Usage

1. Type-Safe Command Definitions

To get TypeScript support/autocompletion inside your command’s disabled, visible, or active functions, you should define your application’s state type. This involves combining GlobalStoreState with the state interfaces of the plugins you are using.

import type { GlobalStoreState } from '@embedpdf/core'; import type { Command } from '@embedpdf/plugin-commands'; import { SCROLL_PLUGIN_ID, type ScrollState } from '@embedpdf/plugin-scroll'; // ... other plugin imports // 1. Define the Global State based on enabled plugins export type AppState = GlobalStoreState<{ [SCROLL_PLUGIN_ID]: ScrollState; // [ZOOM_PLUGIN_ID]: ZoomState; }>; // 2. Use Command<AppState> to type your definitions const myCommands: Record<string, Command<AppState>> = { 'nav.next': { id: 'nav.next', label: 'Next Page', shortcuts: ['arrowright', 'j'], action: ({ registry }) => { registry.getPlugin('scroll')?.provides()?.scrollToNextPage(); }, // The 'state' argument is now typed as AppState disabled: ({ state, documentId }) => { // Access specific document state safely const scrollState = state.plugins.scroll.documents[documentId]; return scrollState ? scrollState.currentPage >= scrollState.totalPages : true; }, }, };

2. Creating a Command-Aware Button

Instead of manually tracking state in every button, use the useCommand store. It returns the fully resolved command object, which automatically updates whenever the viewer state changes.

<script lang="ts"> import { useCommand } from '@embedpdf/plugin-commands/svelte'; let { commandId, documentId }: { commandId: string; documentId: string } = $props(); // Pass the command ID and the current document ID const resolved = useCommand(() => commandId, () => documentId); </script> {#if resolved.current?.visible} <button onclick={resolved.current.execute} disabled={resolved.current.disabled} title="{resolved.current.label} ({resolved.current.shortcuts?.[0] || ''})" > {resolved.current.label} </button> {/if}

Live Example

This example demonstrates command-driven navigation with keyboard shortcuts. Use the arrow buttons or press the keyboard shortcuts (← / K for previous, → / J for next) to navigate between pages. Press ⌘+I (Mac) or Ctrl+I (Windows/Linux) to show document info. Notice how buttons are automatically disabled when navigation isn’t possible (e.g., “Previous” on page 1).

API Reference

Configuration (CommandsPluginConfig)

OptionTypeDescription
commandsRecord<string, Command>A map of command definitions to register on initialization.

Store: useCommand(commandId, documentId)

Retrieves a command and subscribes to its state changes.

Parameters

ParameterTypeDescription
commandId() => stringA getter function that returns the command ID.
documentId() => stringA getter function that returns the document ID for state resolution.

Returns

PropertyTypeDescription
currentResolvedCommand | nullThe resolved command object. Access via .current property.

ResolvedCommand Properties

PropertyTypeDescription
execute() => voidThe function to trigger the command.
disabledbooleanWhether the command is currently disabled.
activebooleanWhether the command is currently active/toggled on.
visiblebooleanWhether the command should be visible in the UI.
labelstringThe display label (translated if i18n is available).
shortcutsstring[]Array of assigned keyboard shortcuts.

Store: useCommandsCapability()

Access the global commands API.

CommandsCapability Methods

MethodDescription
registerCommand(cmd)Dynamically register a new command.
execute(id)Execute a command by its ID.
getCommandByShortcut(key)Find a command matching a shortcut string.
getAllCommands()Returns a list of all registered resolved commands.

The Command Interface

When defining a command, you implement this interface:

interface Command<TState = any> { id: string; label?: string; icon?: string; shortcuts?: string[]; // The execution logic action: (context: { registry: PluginRegistry; state: TState; documentId: string }) => void; // Dynamic predicates (can be value or function) // Receives the typed global state and the current documentId disabled?: boolean | ((ctx: { state: TState; documentId: string }) => boolean); visible?: boolean | ((ctx: { state: TState; documentId: string }) => boolean); active?: boolean | ((ctx: { state: TState; documentId: string }) => boolean); }
Last updated on December 5, 2025

Need Help?

Join our community for support, discussions, and to contribute to EmbedPDF's development.