--- description: "Rojo Studio plugin specifics: Roact patterns, reconciler logic, WebSocket protocol, and plugin testing." globs: - "plugin/**/*.lua" - "plugin/**/*.luau" - "plugin/**/*.json" --- # Rojo Studio Plugin Built with Roact (React-like), handles WebSocket sync with server, patch application to Studio, and UI. **Entry:** `plugin/src/init.server.lua` | **Protocol:** WebSocket v5 | **Must match server MAJOR.MINOR** ## Architecture ### Key Components **1. UI Layer** (`App/`) - **App.lua** - Root component, manages connection state - **Components/** - Reusable UI components - `StatusPages/` - Connected, Connecting, Error, Settings screens - `PatchVisualizer/` - Shows changes before applying - `StringDiffVisualizer/` - Side-by-side script diffs - `TableDiffVisualizer/` - Attributes and Tags diffs - `Notifications/` - Toast notifications - **Theme.lua** - Studio-matched color palette - Uses Roact for declarative UI **2. Communication** (`ServeSession.lua`) - WebSocket connection to server (previously long-polling) - Handles patches, errors, reconnection - Protocol version checking **3. Reconciler** (`Reconciler/`) - **applyPatch.lua** - Applies server patches to Studio tree - **reify.lua** - Creates instances from snapshots - **hydrate.lua** - Updates existing instances - **diff.lua** - Generates patches (for two-way sync) - **decodeValue.lua** - Converts encoded values to Roblox types - **setProperty.lua** - Sets properties safely - **getProperty.lua** - Gets properties with fallbacks **4. State Management** - **Settings.lua** - Persistent plugin settings - **InstanceMap.lua** - Tracks server ID → Studio instance mapping - **PatchSet.lua** / **PatchTree.lua** - Patch representation **5. Testing** - Unit tests: `.spec.lua` files (TestEZ) - Integration tests: `integration/` directory - Stress tests: `.stress.spec.lua` files - Test runner: `runTests.lua` ## Key Configuration (`Config.lua`) ```lua { version = {7, 7, 0, "-rc.1"}, protocolVersion = 5, -- MUST match server ignoredClassNames = { TouchTransmitter = true, Camera = true } } ``` **When changing protocol:** Bump `protocolVersion` in both plugin AND server. ## WebSocket Protocol (v5) 1. Connect to `ws://host:port/api/rojo` 2. Server sends: protocol version, project name 3. Plugin sends: `{"type": "Subscribe"}` 4. Server sends patches on filesystem changes **Patch structure:** ```lua { type = "ApplyPatch", sessionId = "uuid", patch = { removed = {"id1", "id2"}, -- Array of IDs (Vec) added = { ["id3"] = {...} }, -- Map: ID → Instance data updated = { -- Array of updates (Vec) { id = "id4", changedName = "...", changedProperties = {...} } } } } ``` ## Reconciler (`Reconciler/`) **applyPatch.lua** - Main patch application: 1. Remove instances in `removed` 2. Create instances in `added` via `reify` 3. Update instances in `updated` via `hydrate` 4. Collect errors, show in UI **reify.lua** - Create instances (parent last - Roblox best practice) **hydrate.lua** - Update existing (only changed properties) **setProperty.lua** / **getProperty.lua** - Handle unscriptable properties ## UI Components (`App/`) **StatusPages:** NotConnected, Connecting, Connected, Error, Settings **PatchVisualizer:** Tree view of changes with property diffs **StringDiffVisualizer:** Side-by-side script diffs **TableDiffVisualizer:** Attributes and Tags diffs **Notifications:** Toast messages **Theme.lua:** Studio-matched colors (auto light/dark) ## InstanceMap Bidirectional Server ID ↔ Studio Instance mapping: ```lua instanceMap:insert(serverId, studioInstance) local instance = instanceMap:fromId(serverId) local serverId = instanceMap:fromInstance(instance) ``` Critical for patch application and avoiding duplicates. ## Two-Way Sync (Experimental) ⚠️ Watches Studio changes, sends to server. Has known issues - use with caution. ## Dependencies (`plugin/Packages/`) Roact, Flipper, Promise, TestEZ, t, Highlighter, msgpack-luau. **Don't edit vendored packages.** ## Common Issues **Plugin not reloading:** Enable Studio setting "Reload plugins on file change" **Connection fails:** Check server running, host/port match, protocol versions match **Properties not syncing:** Verify scriptable in rbx_reflection_database **Duplicates:** InstanceMap corruption - disconnect, restart Studio ## Debugging Settings → Log Level → "Trace" for verbose output. `ROJO_DEV_BUILD` instance enables auto-tests.