1.3.0 — TypeScript-first architecture
This is a major release. The .rblua DSL still works, but all new projects are TypeScript-first.
TypeScript replaces .rblua
Components, pages, and layout are now written in TypeScript. The compiler runs typescript-to-lua (TSTL) on every .ts file and appends the Lua output directly into the build pipeline — no extra build step needed.
src/
layout.ts ← window config
pages/
1_Home.ts ← tab layout (Next.js-style)
combat/
SpeedHack.ts ← component (SolidJS-style)
misc/
globals.lua ← plain Lua still works
SolidJS-style reactive signals
// Like SolidJS createSignal — read by calling, write with .set()
const enabled = signal(false)
const speed = signal(16)
enabled() // → false
enabled.set(true) // → triggers all watchers
SolidJS-style event hooks via on.*
// Like createEffect scoped to RunService.Heartbeat
on.heartbeat({ when: enabled }, () => {
const h = _PulseGetHumanoid()
if (h) h.WalkSpeed = speed()
})
on.signal(enabled, (v) => {
// fires whenever enabled changes
})
on.respawn(() => {
// CharacterAdded
})
Next.js-style file-based pages
// src/pages/1_Home.ts
definePage('Home', { icon: 'house' }, () => [
groupbox('left', 'Player', { icon: 'person', mount: 'SpeedHack' }),
groupbox('right', 'Visuals', { icon: 'eye', mount: 'PlayerESP' }),
])
Pages are auto-registered in filename order. No routing, no URL params — just tabs.
Fluent widget builders
defineComponent('SpeedHack', () => {
const enabled = signal(false)
const speed = signal(16)
on.heartbeat({ when: enabled }, () => {
const h = _PulseGetHumanoid()
if (h) h.WalkSpeed = speed()
})
return [
toggle('Speed Hack').bind(enabled),
slider('Walk Speed', { min: 16, max: 250 }).bind(speed),
]
})
One layout.ts for all window config
// src/layout.ts
export default {
title: 'MyScript',
toggleKey: 'RightControl',
size: [850, 560] as [number, number],
uiLibrary: 'windui' as 'windui' | 'linoria',
theme: 'Indigo',
icon: 'code-2',
folder: 'MyScript',
acrylic: true,
transparency: 0.8,
} satisfies LayoutConfig
CDN-only runtime
The Pulse runtime and adapter are always loaded from CDN. They are never inlined into the output. The compiled script is just the re-execution guard + two loadstrings + your code:
if _G.__AOT_R_DESTROY then pcall(_G.__AOT_R_DESTROY) end
local _P=loadstring(game:HttpGet("https://cdn.pulse-rb.dev/v1.3.0/bundle.lua"))()
local _A=loadstring(game:HttpGet("https://cdn.pulse-rb.dev/v1.3.0/adapters/windui.lua"))()
-- your compiled TypeScript below
defineComponent("SpeedHack", function() ... end)
Previously, the entire runtime (~90 KB) was inlined. Now it's ~5 KB of user code.
Obfuscator: Ironbrew2 via .NET CLI
The obfuscator now clones github.com/Trollicus/ironbrew-2 and runs the pre-built IronBrew2 CLI.exe via dotnet --roll-forward Major. No Python Lua runtime needed. Stale/broken clone directories are detected and wiped automatically.
rb init generates TypeScript projects
rb init my-script now scaffolds a full TypeScript project with:
src/layout.ts— window configsrc/pages/1_Home.ts— home tabsrc/combat/SpeedHack.ts— example componentsrc/visuals/PlayerESP.ts— example componenttsconfig.json— TSTL-compatible configCLAUDE.md/AGENTS.md/PULSE_DOCS.md— AI context files
.rblua projects still build without changes.