Settings & Property Inspector
Bi-directional settings sync and Property Inspector communication.
Settings Sync Architecture
┌──────────────┐ setSettings() ┌──────────────────┐
│ React Tree │ ────────────────────────> │ Stream Deck SDK │
│ (useState) │ │ (persisted) │
│ │ <──────────────────────── │ │
└──────────────┘ onDidReceiveSettings └──────────────────┘
↕
┌──────────────────┐
│ Property Inspector│
│ (HTML/JS UI) │
└──────────────────┘- React to SDK: calling
setSettings({ count: 5 })updates the React state (triggers re-render) and callsaction.setSettings()to persist. - SDK to React: when the Property Inspector changes settings,
onDidReceiveSettingsfires and the React state updates. - Conflict resolution: the SDK is the source of truth (last-write-wins).
Per-Action Settings
type MySettings = { color: string; brightness: number };
function MyKey() {
const [settings, setSettings] = useSettings<MySettings>();
useKeyDown(() => {
setSettings({ brightness: Math.min(100, settings.brightness + 10) });
});
// settings.color, settings.brightness are reactive
}setSettings uses shallow merge ({ ...current, ...partial }), matching the SDK behavior.
Global Settings
type GlobalConfig = { apiKey: string; theme: 'light' | 'dark' };
function MyKey() {
const [global, setGlobal] = useGlobalSettings<GlobalConfig>();
// Shared across all action instances
}Property Inspector Communication
Beyond settings, you can send arbitrary messages between the plugin and the Property Inspector:
Sending to PI
function MyKey() {
const sendToPI = useSendToPI();
useKeyDown(() => {
sendToPI({ currentState: 'active', timestamp: Date.now() });
});
}Receiving from PI
function MyKey() {
usePropertyInspector<{ action: string }>((msg) => {
if (msg.action === 'refresh') {
// Handle PI message
}
});
}Default Settings
Set default values in defineAction so new instances start with sensible state:
export const myAction = defineAction<MySettings>({
uuid: 'com.example.plugin.my-action',
key: MyKey,
defaultSettings: { color: '#000', brightness: 50 },
});