rojo/.cursor/rules/plugin.mdc
2026-02-02 12:08:47 -06:00

139 lines
4.5 KiB
Text

---
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<Ref>)
added = { ["id3"] = {...} }, -- Map: ID → Instance data
updated = { -- Array of updates (Vec<InstanceUpdate>)
{ 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.