pulse-rb 1.2.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/README.md +225 -0
  2. package/adapters/linoria.lua +233 -0
  3. package/adapters/windui.llms.txt +366 -0
  4. package/adapters/windui.lua +505 -0
  5. package/bin/rb.js +2 -0
  6. package/dist/index.js +3285 -0
  7. package/package.json +59 -0
  8. package/pulse/dev/debuggui.lua +1206 -0
  9. package/pulse/dev/devconfig.lua +81 -0
  10. package/pulse/dev/ui/9_DevPanel.lua +384 -0
  11. package/pulse/helpers/aim.lua +193 -0
  12. package/pulse/helpers/cache.lua +68 -0
  13. package/pulse/helpers/cleaner.lua +110 -0
  14. package/pulse/helpers/conn.lua +33 -0
  15. package/pulse/helpers/cooldown.lua +47 -0
  16. package/pulse/helpers/draw.lua +122 -0
  17. package/pulse/helpers/hitbox.lua +63 -0
  18. package/pulse/helpers/input.lua +24 -0
  19. package/pulse/helpers/log.lua +228 -0
  20. package/pulse/helpers/loop.lua +58 -0
  21. package/pulse/helpers/memory.lua +48 -0
  22. package/pulse/helpers/narrate.lua +160 -0
  23. package/pulse/helpers/notify.lua +51 -0
  24. package/pulse/helpers/perf.lua +48 -0
  25. package/pulse/helpers/remote.lua +128 -0
  26. package/pulse/helpers/restore.lua +59 -0
  27. package/pulse/helpers/store.lua +39 -0
  28. package/pulse/helpers/team.lua +83 -0
  29. package/pulse/helpers/testmode.lua +80 -0
  30. package/pulse/helpers/trace.lua +111 -0
  31. package/pulse/helpers/track.lua +85 -0
  32. package/pulse/helpers/vec.lua +52 -0
  33. package/pulse/helpers/world.lua +51 -0
  34. package/pulse/runtime.lua +343 -0
  35. package/pulse/ui/linoria_settings.lua +55 -0
  36. package/pulse/ui/windui_settings.lua +87 -0
  37. package/templates/AGENTS.md +177 -0
  38. package/templates/CLAUDE.md +424 -0
  39. package/templates/deploy_config.example +17 -0
  40. package/templates/example_esp.rblua +69 -0
  41. package/templates/example_fov.rblua +20 -0
  42. package/templates/example_speed.rblua +25 -0
  43. package/templates/gitignore +4 -0
  44. package/templates/globals.lua +66 -0
  45. package/templates/layout.rblua +28 -0
  46. package/templates/module.lua +7 -0
  47. package/templates/module.rblua +32 -0
  48. package/templates/page_home.rblua +9 -0
  49. package/templates/remote.lua +6 -0
  50. package/templates/remotes.lua +14 -0
  51. package/vscode/language-configuration.json +35 -0
  52. package/vscode/package.json +35 -0
  53. package/vscode/src/extension.js +397 -0
  54. package/vscode/syntaxes/rblua.tmLanguage.json +126 -0
package/README.md ADDED
@@ -0,0 +1,225 @@
1
+ # Pulse — Reactive Component Framework for Roblox Lua
2
+
3
+ <p align="center">
4
+ <a href="https://pulse-rb.vercel.app"><strong>pulse-rb.vercel.app</strong></a> &nbsp;·&nbsp;
5
+ <a href="https://pulse-rb.vercel.app/docs/intro">Docs</a> &nbsp;·&nbsp;
6
+ <a href="https://pulse-rb.vercel.app/docs/quick-start">Quick Start</a> &nbsp;·&nbsp;
7
+ <a href="https://pulse-rb.vercel.app/changelog">Changelog</a> &nbsp;·&nbsp;
8
+ <a href="https://discord.gg/xBtWjXRfe">Discord</a>
9
+ </p>
10
+
11
+ <p align="center">
12
+ <img src="https://img.shields.io/badge/version-1.2.16-7c3aed?style=flat-square" alt="version" />
13
+ <img src="https://img.shields.io/badge/platform-Windows-0ea5e9?style=flat-square" alt="platform" />
14
+ <img src="https://img.shields.io/badge/language-Lua%20%2F%20Python-f59e0b?style=flat-square" alt="language" />
15
+ <img src="https://img.shields.io/badge/UI-WindUI%20%7C%20Linoria-a78bfa?style=flat-square" alt="ui" />
16
+ </p>
17
+
18
+ ---
19
+
20
+ **Pulse** is a reactive component framework for Roblox Lua. Write your script as clean, separated `.rblua` component files. The `rb` CLI compiles, checks, and bundles everything into one deployable Lua file.
21
+
22
+ ```rblua
23
+ component {
24
+ signal enabled = false
25
+ signal speed = 50
26
+
27
+ init {
28
+ Pulse.Notify.onToggle(SpeedHack.enabled, "Speed Hack")
29
+ }
30
+
31
+ ui {
32
+ toggle "Speed Hack" -> enabled tip="Override walk speed"
33
+ slider "Walk Speed" -> speed [16, 250]
34
+ }
35
+
36
+ on CharacterAdded {
37
+ guard h = humanoid
38
+ h.WalkSpeed = enabled and speed or 16
39
+ }
40
+
41
+ on enabled, speed {
42
+ guard h = humanoid
43
+ h.WalkSpeed = enabled and speed or 16
44
+ }
45
+ }
46
+ ```
47
+
48
+ One file. No `require()`. No runtime overhead. State, UI, and events all in one self-contained component.
49
+
50
+ ---
51
+
52
+ ## Why Pulse?
53
+
54
+ Roblox Lua projects grow fast. A single monolithic script becomes impossible to maintain, debug, or extend. Pulse gives you:
55
+
56
+ - **Reactive signals** — declare state once; the UI, event handlers, and other components update automatically
57
+ - **Component model** — one `.rblua` file = one feature; rename, move, or delete without touching anything else
58
+ - **Flat compilation** — all files concatenate into one Lua chunk at build time; no module system overhead at runtime
59
+ - **Built-in helpers** — `Pulse.Loop`, `Pulse.Draw`, `Pulse.Aim`, `Pulse.Hitbox`, `Pulse.Notify`, `Pulse.Log` and more
60
+ - **Live dev overlay** — inject with `--dev` to get a draggable log viewer, feature toggles, and a live monitor bar
61
+ - **One-command build** — `rb build` compiles, syntax-checks, and obfuscates in one step
62
+
63
+ ---
64
+
65
+ ## Install
66
+
67
+ ```powershell
68
+ irm https://pulse-rb.vercel.app/install.ps1 | iex
69
+ ```
70
+
71
+ Installs `rb` globally and adds it to PATH. Requires **Python 3.10+** and **Git**.
72
+
73
+ ---
74
+
75
+ ## Quick Start
76
+
77
+ ```powershell
78
+ rb init my-project # scaffold project + git repo
79
+ cd my-project
80
+ rb build # compile → check → obfuscate
81
+ rb watch # auto-rebuild on save
82
+ ```
83
+
84
+ A new project includes example components, a CLAUDE.md reference for AI assistants, and full WindUI layout configuration out of the box.
85
+
86
+ ---
87
+
88
+ ## Project Structure
89
+
90
+ ```
91
+ my-project/
92
+ ├── src/
93
+ │ ├── misc/helpers/globals.lua ← compiled first — func table, shared state
94
+ │ ├── misc/remotes.lua ← RemoteEvent/Function wrappers
95
+ │ ├── player/
96
+ │ │ └── SpeedHack.rblua ← Pulse component
97
+ │ ├── visuals/
98
+ │ │ └── PlayerESP.rblua
99
+ │ └── ui/
100
+ │ ├── layout.rblua ← window config (title, theme, size, ...)
101
+ │ └── pages/
102
+ │ └── 1_Home.rblua ← tab layout — mounts component widgets
103
+ └── build/
104
+ ├── script.lua ← compiled output
105
+ └── script.obf.lua ← obfuscated output
106
+ ```
107
+
108
+ ---
109
+
110
+ ## CLI Reference
111
+
112
+ | Command | Description |
113
+ |---|---|
114
+ | `rb init <name>` | Scaffold a new project |
115
+ | `rb build` | Compile → check → obfuscate |
116
+ | `rb build --no-obfuscate` | Compile and check only |
117
+ | `rb watch` | Auto-rebuild on file change |
118
+ | `rb copy` | Compile and copy output to clipboard |
119
+ | `rb new module <path>` | Add a new component |
120
+ | `rb new remote <Name>` | Add a remote wrapper stub |
121
+ | `rb lint` | Static analysis — dead signals, ghost refs |
122
+ | `rb deploy` | Push build output to GitHub |
123
+ | `rb docs` | Generate `PULSE_DOCS.md` full reference |
124
+
125
+ Full CLI reference → [pulse-rb.vercel.app/docs/cli](https://pulse-rb.vercel.app/docs/cli)
126
+
127
+ ---
128
+
129
+ ## Pulse Helpers
130
+
131
+ | Helper | Purpose |
132
+ |---|---|
133
+ | `Pulse.Loop` | Managed repeating task with safe start/stop |
134
+ | `Pulse.Notify` | In-game toast notifications |
135
+ | `Pulse.Draw` | Highlights, circles, selection boxes |
136
+ | `Pulse.Aim` | FOV check, camera snap, nearest-entity search |
137
+ | `Pulse.Hitbox` | BasePart size expansion with clean restore |
138
+ | `Pulse.Cooldown` | Rate limiting — per-call or per-entity |
139
+ | `Pulse.Store` | Cross-component reactive key-value state |
140
+ | `Pulse.Track` | Entity lifecycle tracking with auto-cleanup |
141
+ | `Pulse.Cache` | Timed function result caching |
142
+ | `Pulse.Conn` | Named connection manager |
143
+ | `Pulse.Log` | Structured dev logging (zero cost in prod) |
144
+ | `Pulse.Monitor` | Live key-value dashboard in the dev overlay |
145
+ | `Pulse.Memory` | Executor capability checks |
146
+ | `Pulse.Team` | Configurable friend/enemy resolver |
147
+ | `Pulse.Perf` | Rolling frame-time sampler |
148
+
149
+ Full helper reference → [pulse-rb.vercel.app/docs/helpers](https://pulse-rb.vercel.app/docs/helpers)
150
+
151
+ ---
152
+
153
+ ## Component Syntax
154
+
155
+ ```rblua
156
+ component {
157
+ signal enabled = false
158
+ signal radius = 400
159
+
160
+ ui {
161
+ toggle "Enable" -> enabled default=true tip="Tooltip"
162
+ slider "Radius" -> radius [50, 800]
163
+ button "Reset" -> Reset()
164
+ }
165
+
166
+ init {
167
+ -- private locals and helpers
168
+ local _loop = Pulse.Loop.new(0.5, function()
169
+ -- runs every 0.5 s while the loop is active
170
+ end)
171
+ Pulse.Notify.onToggle(MyComponent.enabled, "My Feature")
172
+ }
173
+
174
+ on enabled {
175
+ if v then _loop:start() else _loop:stop() end
176
+ }
177
+
178
+ on Heartbeat when enabled every 0.1 {
179
+ guard hrp
180
+ -- throttled to at most once per 0.1 s
181
+ }
182
+
183
+ on CharacterAdded { ... }
184
+
185
+ on InputBegan(input, gpe) when enabled debounce 0.2 {
186
+ if gpe then return end
187
+ -- ...
188
+ }
189
+
190
+ func Toggle() {
191
+ enabled(not enabled())
192
+ }
193
+ }
194
+ ```
195
+
196
+ Full framework reference → [pulse-rb.vercel.app/docs/intro](https://pulse-rb.vercel.app/docs/intro)
197
+
198
+ ---
199
+
200
+ ## Documentation
201
+
202
+ Full documentation, scripting school, and helper reference at **[pulse-rb.vercel.app](https://pulse-rb.vercel.app)**.
203
+
204
+ - [Introduction](https://pulse-rb.vercel.app/docs/intro)
205
+ - [Quick Start](https://pulse-rb.vercel.app/docs/quick-start)
206
+ - [Core Concepts](https://pulse-rb.vercel.app/docs/core/your-first-feature)
207
+ - [Helpers (Pulse.*)](https://pulse-rb.vercel.app/docs/helpers)
208
+ - [CLI Reference](https://pulse-rb.vercel.app/docs/cli)
209
+ - [Changelog](https://pulse-rb.vercel.app/changelog)
210
+
211
+ ---
212
+
213
+ ## Requirements
214
+
215
+ | Tool | Purpose | Auto-installed |
216
+ |---|---|---|
217
+ | Python 3.10+ | Running `rb` | No |
218
+ | Lua 5.1 (`luac`) | Syntax checking + obfuscation | Yes (via winget) |
219
+ | Git | Version control, Prometheus download | No |
220
+
221
+ ---
222
+
223
+ <p align="center">
224
+ Built with <a href="https://pulse-rb.vercel.app">Pulse</a> · <a href="https://discord.gg/xBtWjXRfe">Discord</a>
225
+ </p>
@@ -0,0 +1,233 @@
1
+ -- [adapters/linoria.lua]
2
+ -- Linoria UI adapter for Pulse components.
3
+ -- Injected by the compiler when src/ui/ exists. Provides _UIAdapter to layout
4
+ -- and page files. _Library / _ThemeManager / _SaveManager are available as
5
+ -- upvalues to all subsequent files (Settings.lua, etc.) via flat compilation.
6
+
7
+ local _LIB_URL = "https://raw.githubusercontent.com/violin-suzutsuki/LinoriaLib/main/Library.lua"
8
+ local _THEME_URL = "https://raw.githubusercontent.com/violin-suzutsuki/LinoriaLib/main/addons/ThemeManager.lua"
9
+ local _SAVE_URL = "https://raw.githubusercontent.com/violin-suzutsuki/LinoriaLib/main/addons/SaveManager.lua"
10
+
11
+ -- Safe filesystem wrappers — some executors error instead of returning nil
12
+ do
13
+ local _mf = makefolder or function() end
14
+ makefolder = function(p) pcall(_mf, p) end
15
+
16
+ local _wf = writefile or function() end
17
+ writefile = function(p, d) pcall(_wf, p, d) end
18
+
19
+ local _rf = readfile or function() return "" end
20
+ readfile = function(p)
21
+ local ok, r = pcall(_rf, p)
22
+ return ok and r or ""
23
+ end
24
+
25
+ local _iff = isfolder or function() return false end
26
+ isfolder = function(p)
27
+ local ok, r = pcall(_iff, p)
28
+ return ok and r or false
29
+ end
30
+
31
+ local _isf = isfile or function() return false end
32
+ isfile = function(p)
33
+ local ok, r = pcall(_isf, p)
34
+ return ok and r or false
35
+ end
36
+
37
+ local _df = delfile or function() end
38
+ delfile = function(p) pcall(_df, p) end
39
+ end
40
+
41
+ local function _loadLib(url, name)
42
+ local ok, src = pcall(game.HttpGet, game, url)
43
+ if not ok then error("[AOT-R] HTTP failed for " .. name .. ": " .. tostring(src)) end
44
+ if type(src) ~= "string" or #src < 50 then
45
+ error("[AOT-R] Empty response for " .. name)
46
+ end
47
+ local fn, err = loadstring(src)
48
+ if not fn then error("[AOT-R] Compile failed for " .. name .. ": " .. tostring(err)) end
49
+ local ok2, result = pcall(fn)
50
+ if not ok2 then error("[AOT-R] Init failed for " .. name .. ": " .. tostring(result)) end
51
+ return result
52
+ end
53
+
54
+ local _Library = _loadLib(_LIB_URL, "Library")
55
+ local _ThemeManager = _loadLib(_THEME_URL, "ThemeManager")
56
+ local _SaveManager = _loadLib(_SAVE_URL, "SaveManager")
57
+
58
+ -- ── Adapter ───────────────────────────────────────────────────────────────────
59
+
60
+ local _UIS = game:GetService("UserInputService")
61
+ local _UIAdapter = {}
62
+
63
+ function _UIAdapter:CreateWindow(title, w, h, opts)
64
+ -- opts table from layout (theme/folder/notify_title) — Linoria handles
65
+ -- folder via ThemeManager/SaveManager in the settings page, not here.
66
+ return _Library:CreateWindow({
67
+ Title = title,
68
+ Center = true,
69
+ AutoShow = true,
70
+ TabPadding = 8,
71
+ MenuFadeTime = 0.2,
72
+ })
73
+ end
74
+
75
+ function _UIAdapter:RegisterTheme(_t) end -- no-op for Linoria builds
76
+
77
+ function _UIAdapter:GetThemeNames() return nil end -- Linoria manages its own themes
78
+
79
+ function _UIAdapter:SetToggleKey(keyCode)
80
+ _Library.ToggleKey = keyCode
81
+ end
82
+
83
+ -- ── Widget constructors ───────────────────────────────────────────────────────
84
+
85
+ function _UIAdapter:addToggle(gb, id, opts)
86
+ -- opts: { label, signal, tip }
87
+ gb:AddToggle(id, {
88
+ Text = opts.label,
89
+ Default = opts.signal(),
90
+ Tooltip = opts.tip or "",
91
+ Callback = function(v) opts.signal(v) end,
92
+ })
93
+ -- reverse: signal changes → update Linoria widget
94
+ opts.signal:onChange(function(v)
95
+ local t = Toggles and Toggles[id]
96
+ if t then t:SetValue(v) end
97
+ end)
98
+ end
99
+
100
+ function _UIAdapter:addSlider(gb, id, opts)
101
+ -- opts: { label, signal, min, max, tip, rounding? }
102
+ gb:AddSlider(id, {
103
+ Text = opts.label,
104
+ Default = opts.signal(),
105
+ Min = opts.min,
106
+ Max = opts.max,
107
+ Rounding = opts.rounding or 1,
108
+ Compact = false,
109
+ Tooltip = opts.tip or "",
110
+ Callback = function(v) opts.signal(v) end,
111
+ })
112
+ opts.signal:onChange(function(v)
113
+ local t = Options and Options[id]
114
+ if t then t:SetValue(v) end
115
+ end)
116
+ end
117
+
118
+ function _UIAdapter:addDropdown(gb, id, opts)
119
+ -- opts: { label, signal, options, tip }
120
+ gb:AddDropdown(id, {
121
+ Text = opts.label,
122
+ Values = opts.options,
123
+ Default = opts.signal(),
124
+ Tooltip = opts.tip or "",
125
+ Callback = function(v) opts.signal(v) end,
126
+ })
127
+ opts.signal:onChange(function(v)
128
+ local t = Options and Options[id]
129
+ if t then t:SetValue(v) end
130
+ end)
131
+ end
132
+
133
+ function _UIAdapter:addMultiDropdown(gb, id, opts)
134
+ -- opts: { label, signal, options, tip }
135
+ gb:AddDropdown(id, {
136
+ Text = opts.label,
137
+ Values = opts.options,
138
+ Default = {},
139
+ Multi = true,
140
+ Tooltip = opts.tip or "",
141
+ Callback = function(v) opts.signal(v) end,
142
+ })
143
+ opts.signal:onChange(function(v)
144
+ local t = Options and Options[id]
145
+ if t then t:SetValue(v) end
146
+ end)
147
+ end
148
+
149
+ function _UIAdapter:addButton(gb, opts)
150
+ -- opts: { label, action, tip? }
151
+ gb:AddButton(opts.label, opts.action)
152
+ end
153
+
154
+ function _UIAdapter:addKeybind(gb, id, opts)
155
+ -- opts: { label, key, action }
156
+ gb:AddLabel(opts.label):AddKeyPicker(id, {
157
+ Default = opts.key,
158
+ SyncToggleState = false,
159
+ Mode = "Toggle",
160
+ Text = opts.label,
161
+ Callback = function()
162
+ -- Skip if the player is typing in any TextBox (chat, search, etc.)
163
+ if _UIS:GetFocusedTextBox() then return end
164
+ opts.action()
165
+ end,
166
+ })
167
+ end
168
+
169
+ function _UIAdapter:addLabel(gb, text)
170
+ gb:AddLabel(text)
171
+ end
172
+
173
+ function _UIAdapter:addParagraph(gb, title, desc)
174
+ gb:AddLabel(title)
175
+ if desc and desc ~= "" then
176
+ gb:AddLabel(desc)
177
+ end
178
+ end
179
+
180
+ function _UIAdapter:addSeparator(gb)
181
+ gb:AddBlank(6)
182
+ end
183
+
184
+ -- Notify — adapter-agnostic function called by the defaults runner.
185
+ _PulseNotify = function(msg, duration)
186
+ pcall(function() _Library:Notify(msg, duration or 5) end)
187
+ end
188
+
189
+ -- Register Linoria teardown with the Pulse destroy system.
190
+ -- _PulseDestroy() will call this before unmounting components.
191
+ _PulseOnDestroy(function()
192
+ pcall(function()
193
+ if _Library.Unload then
194
+ _Library:Unload()
195
+ elseif _Library.GUI then
196
+ _Library.GUI:Destroy()
197
+ end
198
+ end)
199
+ end)
200
+
201
+ -- ── Mount a component's _ui table into a groupbox ────────────────────────────
202
+
203
+ function _UIAdapter:mount(component, gb)
204
+ if not component then return end
205
+ local ui = component._ui
206
+ if not ui then return end
207
+ for _, w in ipairs(ui) do
208
+ local t = w.type
209
+ local sig = component[w.signal] -- Signal callable (nil for button/label/separator)
210
+ if t == "toggle" then
211
+ self:addToggle(gb, w.id, { label = w.label, signal = sig, tip = w.tip })
212
+ elseif t == "slider" then
213
+ self:addSlider(gb, w.id, { label = w.label, signal = sig,
214
+ min = w.min, max = w.max, tip = w.tip })
215
+ elseif t == "dropdown" then
216
+ self:addDropdown(gb, w.id, { label = w.label, signal = sig,
217
+ options = w.options, tip = w.tip })
218
+ elseif t == "multidropdown" then
219
+ self:addMultiDropdown(gb, w.id, { label = w.label, signal = sig,
220
+ options = w.options, tip = w.tip })
221
+ elseif t == "button" then
222
+ self:addButton(gb, { label = w.label, action = w.action })
223
+ elseif t == "keybind" then
224
+ self:addKeybind(gb, w.id, { label = w.label, key = w.key, action = w.action })
225
+ elseif t == "label" then
226
+ self:addLabel(gb, w.label)
227
+ elseif t == "paragraph" then
228
+ self:addParagraph(gb, w.title, w.desc)
229
+ elseif t == "separator" then
230
+ self:addSeparator(gb)
231
+ end
232
+ end
233
+ end