This file documents the runtime surface around the command system: manifest settings, storage keys, cache strategy, export UI, and library indexing.
From manifest.json:
- plugin name:
Keyboard Commands - API version:
1.0.0 - main entry:
dist/index.js - editor type:
figma - document access:
dynamic-page - permissions:
teamlibrary - UI file:
ui.html - exposed parameter:
command - allowed network domain:
https://cdnjs.cloudflare.com
Why those choices matter:
dynamic-pageis required because the plugin walks pages and imports library content at runtimeteamlibraryis required for theme toggling and library-backed variable/component workflowscdnjsis only used by the hidden export UI to loadJSZip
From package.json:
- build:
esbuild src/index.ts --bundle --outfile=dist/index.js --target=es6 --minify - prebuild runs all tests
- tests cover input parsing, command registry integrity, utility behavior, and persistence
Primary test files:
tests/command-input.test.jstests/command-registry.test.jstests/utils.test.jstests/persistence.test.js
The plugin uses four persistent client-storage buckets.
- key:
KB_COMMANDS_LIBRARY_DATA - file owner:
src/storage.ts - payload: compressed JSON via
lz-string
Stored data shape:
Record<LibraryName, LibraryItem[]>
Library items can represent:
- paint styles
- text styles
- effect styles
- grid styles
- components
- color variables
- float variables
- string variables
- boolean variables
- key:
KB_COMMANDS_ACTIVE_LIBRARIES - payload: string array of enabled library names
- key:
KB_COMMANDS_HISTORY - max length:
10
- key:
KB_COMMANDS_RECENT_VALUES - max entries per command:
5
The plugin caches several expensive lookups in memory.
src/storage.ts keeps in-memory copies of:
- stored library data
- active-library list
src/utils.ts caches local styles and variables for 5 minutes.
Cached groups:
- local paint styles
- local text styles
- local effect styles
- local grid styles
- local variables
- local variable collections
Important nuance:
- variable colors are resolved lazily only for final visible suggestion rows
- this avoids resolving large variable graphs for off-screen results
src/implementations/instance.ts keeps a WeakMap from instance node to main component to avoid repeated getMainComponentAsync() calls.
plib runs publishLibrary() and indexes the current file into plugin storage.
What gets indexed:
- all local paint styles
- all local text styles
- all local effect styles
- all supported local variables: color, float, string, boolean
- components from all pages
Component-set nuance:
- a component set is stored by the key of its default variant, not the set itself
- that makes later imports compatible with
importComponentByKeyAsync()
Color metadata nuance:
- paint styles store either a representative hex color or
IMAGE:<hash> - color variables resolve aliases recursively, up to a safety limit
Activation nuance:
- publishing automatically enables the library in the active-library list
The plugin has a local concept of "libraries" that is separate from Figma's native library UI.
Core behaviors:
- only active libraries participate in binding search
- toggles are stored locally, not globally in Figma
- library suggestions show checkbox state and indexed item count
- search is substring based on library name
Formatting examples:
[x] Core (216 items)[ ] Marketing (84 items)
Those formatted strings are also accepted as input for toggle/remove commands.
The binding system resolves user-facing strings into executable references.
resolvePaintValue() supports:
- literal hex colors
- paint style references
- color variable references
resolveStyleValue() supports:
- any style type used by the command
- variable references when the command accepts them
- literal hex as a fallback for paint-like commands
resolveNumberValue() supports:
- numeric literals
- percentages
- numeric expressions
- float variable references
Export commands call exportAs() in src/implementations/export.ts.
Runtime flow:
- export each selected node with Figma
exportAsync() - open
ui.htmlinvisibly - post binary export results to the UI
- let the UI download a single file directly or ZIP multiple files with
JSZip - wait for a UI completion message or a
10second timeout
Important behavior:
- single-node export downloads one file with the node name
- multi-node export downloads
export-N-files.zip - the plugin allows SVG, PDF, PNG, and JPG
- PNG and JPG can export by scale, width, or height
The selection-color subsystem recursively traverses the selection subtree and extracts colors from:
- fills
- strokes
- shadow effects
It distinguishes binding origin as:
- style
- variable
- literal
That is why cs? can offer meaningful source choices even when two visually identical colors come from different bindings.
t scans both local and team-library variable collections whose names include:
themeappearancepalette
It only considers collections with both:
- a light mode
- a dark mode
Auto-mode nuance:
- auto-named modes are ignored when looking for the explicit light/dark pair
- if a node currently has an explicit mode and auto resolution would already yield the opposite, the toggle clears the explicit mode instead of writing another explicit override
This means theme toggling tries to preserve inheritance where possible instead of always hard-coding the next mode.
The suggestion system can attach icons to results.
- solid colors get SVG swatches
- gradient styles use the first visible stop as the preview color
- image paint styles get a dedicated image-style icon
- selection-color swaps can show a dual swatch when exactly two colors are available
tests/command-registry.test.js asserts:
- every command definition is present in
COMMANDS - command names are unique
- aliases are unique across the entire registry
- commands expose the correct execution function shape
- value-format commands declare a value format
- lookup by alias and name works
- top-level suggestions cover the full registry
This is important because the docs in this folder assume the registry is the source of truth.