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
@@ -0,0 +1,228 @@
1
+ -- ── Pulse.Log + Pulse.Monitor ─────────────────────────────────────────────────
2
+ -- Structured dev logging with levels, tag filtering, throttling, and assertions.
3
+ -- Active only when _PULSE_DEV = true (injected by compiler in --dev builds).
4
+ -- In prod every call is a single boolean check — zero overhead.
5
+ --
6
+ -- Usage:
7
+ -- Pulse.Log.info("Aimbot", "locked", { name = t.Name, dist = d })
8
+ -- Pulse.Log.warn("globals", "folder missing")
9
+ -- Pulse.Log.error("Cutter", "pcall failed", { err = tostring(e) })
10
+ -- Pulse.Log.assert(x ~= nil, "Cutter", "x must not be nil here")
11
+ -- Pulse.Log.throttle("NapeSize", 5, "debug", "scanning " .. n .. " titans")
12
+ -- Pulse.Log.configure({ level="debug", file="dev.log", tags={"Aimbot"} })
13
+ --
14
+ -- Monitors (live key-value dashboard):
15
+ -- Pulse.Monitor.set("titans", 3)
16
+ -- Pulse.Monitor.getAll() → { titans=3, shifters=1, ... }
17
+
18
+ do
19
+ -- ── Shared state ─────────────────────────────────────────────────────────
20
+
21
+ local _active = (_PULSE_DEV == true)
22
+ local _minN = 2 -- 0=trace 1=debug 2=info 3=warn 4=error
23
+ local _tagWL = nil -- nil=all; table=whitelist
24
+ local _tagBL = nil -- blacklist (only when _tagWL is nil)
25
+ local _disabledTags = {} -- per-tag disable (runtime toggle)
26
+ local _console = true
27
+ local _file = nil
28
+ local _buf = {} -- ring buffer of structured entries
29
+ local _bufMax = 600
30
+ local _seq = 0 -- monotonic entry ID for GUI polling
31
+ local _timers = {} -- throttle: key → last tick
32
+ local _knownTags = {} -- set of all tags ever seen
33
+ local _subs = {} -- subscribers: fn(entry) — used by Pulse.Narrate etc.
34
+
35
+ local _N = { trace=0, debug=1, info=2, warn=3, error=4 }
36
+ local _LBL = { [0]="TRC", [1]="DBG", [2]="INF", [3]="WRN", [4]="ERR" }
37
+ local _HS = game:GetService("HttpService")
38
+
39
+ -- ── Helpers ───────────────────────────────────────────────────────────────
40
+
41
+ local function _ts()
42
+ local ok, s = pcall(os.date, "%H:%M:%S")
43
+ return (ok and type(s) == "string") and s
44
+ or string.format("%d", math.floor(tick() % 86400))
45
+ end
46
+
47
+ local function _fmtData(data)
48
+ if data == nil then return "" end
49
+ if type(data) ~= "table" then return " " .. tostring(data) end
50
+ local ok, s = pcall(function() return _HS:JSONEncode(data) end)
51
+ return ok and (" " .. s) or ""
52
+ end
53
+
54
+ local function _tagAllowed(tag)
55
+ if _disabledTags[tag] then return false end
56
+ if _tagWL then return _tagWL[tag] == true end
57
+ if _tagBL then return _tagBL[tag] ~= true end
58
+ return true
59
+ end
60
+
61
+ local function _emit(tag, n, msg, data)
62
+ if not _active then return end
63
+ if n < _minN then return end
64
+ if not _tagAllowed(tag) then return end
65
+
66
+ _knownTags[tag] = true
67
+ local ts = _ts()
68
+ local line = string.format("[%s] %s [%s] %s%s", ts, _LBL[n], tag, tostring(msg), _fmtData(data))
69
+
70
+ _seq = _seq + 1
71
+ local entry = { id=_seq, ts=ts, n=n, lbl=_LBL[n], tag=tag, msg=tostring(msg), data=data, line=line }
72
+ _buf[#_buf + 1] = entry
73
+ if #_buf > _bufMax then table.remove(_buf, 1) end
74
+
75
+ if _console then
76
+ if n >= 3 then warn(line) else print(line) end
77
+ end
78
+ for _, fn in ipairs(_subs) do pcall(fn, entry) end
79
+ if _file then
80
+ pcall(function()
81
+ if type(rawget(_G, "appendfile")) == "function" then
82
+ appendfile(_file, line .. "\n")
83
+ end
84
+ end)
85
+ end
86
+ end
87
+
88
+ -- ── Public API ────────────────────────────────────────────────────────────
89
+
90
+ Pulse.Log = {}
91
+
92
+ -- Configure (call from rb/pulse/dev/devconfig.lua).
93
+ -- opts: level, console, file, tags (whitelist array), except (blacklist array)
94
+ function Pulse.Log.configure(opts)
95
+ if not _active then return end
96
+ if opts.level ~= nil then _minN = _N[opts.level] or _minN end
97
+ if opts.console ~= nil then _console = opts.console end
98
+ if opts.file ~= nil then _file = opts.file
99
+ -- Start fresh log file for this session.
100
+ if _file then
101
+ pcall(function()
102
+ if type(rawget(_G, "writefile")) == "function" then
103
+ writefile(_file, string.format("=== Pulse Dev %s ===\n", _ts()))
104
+ end
105
+ end)
106
+ end
107
+ end
108
+ if opts.tags ~= nil then
109
+ _tagWL = {}; _tagBL = nil
110
+ for _, t in ipairs(opts.tags) do _tagWL[t] = true end
111
+ elseif opts.except ~= nil then
112
+ _tagBL = {}; _tagWL = nil
113
+ for _, t in ipairs(opts.except) do _tagBL[t] = true end
114
+ end
115
+ end
116
+
117
+ -- Per-tag enable/disable at runtime (used by debug UI toggles).
118
+ function Pulse.Log.setTagEnabled(tag, enabled)
119
+ _disabledTags[tag] = not enabled
120
+ end
121
+
122
+ -- Returns a copy of all tags ever seen (for the debug UI tag list).
123
+ function Pulse.Log.getKnownTags()
124
+ local t = {}
125
+ for tag in pairs(_knownTags) do t[#t+1] = tag end
126
+ table.sort(t)
127
+ return t
128
+ end
129
+
130
+ -- Poll for entries newer than `afterId` (0 = all). Used by the debug GUI.
131
+ function Pulse.Log.getNewEntries(afterId)
132
+ local result = {}
133
+ for _, e in ipairs(_buf) do
134
+ if e.id > afterId then result[#result+1] = e end
135
+ end
136
+ return result
137
+ end
138
+
139
+ -- Log levels.
140
+ function Pulse.Log.trace(tag, msg, data) _emit(tag, 0, msg, data) end
141
+ function Pulse.Log.debug(tag, msg, data) _emit(tag, 1, msg, data) end
142
+ function Pulse.Log.info (tag, msg, data) _emit(tag, 2, msg, data) end
143
+ function Pulse.Log.warn (tag, msg, data) _emit(tag, 3, msg, data) end
144
+ function Pulse.Log.error(tag, msg, data) _emit(tag, 4, msg, data) end
145
+
146
+ -- Assertion — logs ERROR if condition is false; never throws.
147
+ function Pulse.Log.assert(cond, tag, msg, data)
148
+ if not cond then _emit(tag, 4, "ASSERT: " .. tostring(msg), data) end
149
+ end
150
+
151
+ -- Throttled log — safe for Heartbeat loops; at most once per `interval` s.
152
+ function Pulse.Log.throttle(tag, interval, level, msg, data)
153
+ if not _active then return end
154
+ local key = tag .. "|" .. tostring(msg)
155
+ local now = tick()
156
+ if _timers[key] and (now - _timers[key]) < interval then return end
157
+ _timers[key] = now
158
+ _emit(tag, _N[level] or 2, msg, data)
159
+ end
160
+
161
+ -- Dump a table as a debug entry.
162
+ function Pulse.Log.snapshot(tag, label, tbl)
163
+ _emit(tag, 1, "snapshot: " .. tostring(label), tbl)
164
+ end
165
+
166
+ -- Subscribe to all emitted entries (used by Pulse.Narrate for auto-narration).
167
+ function Pulse.Log.subscribe(fn)
168
+ _subs[#_subs + 1] = fn
169
+ end
170
+
171
+ -- Watch a Signal for value changes — logs every time it fires.
172
+ -- Essential for "feature randomly stops working": you'll see exactly when
173
+ -- and to what value the signal was set.
174
+ -- Usage: Pulse.Log.watchSignal(Components.Aimbot.enabled, "aimbot", "enabled")
175
+ function Pulse.Log.watchSignal(signal, tag, name)
176
+ if not _active then return end
177
+ signal:onChange(function(v)
178
+ _emit(tag, 2, "signal change: " .. tostring(name or "?") .. " → " .. tostring(v))
179
+ end)
180
+ end
181
+
182
+ -- Compat / utility.
183
+ function Pulse.Log.enable() _active = true end
184
+ function Pulse.Log.disable() _active = false end
185
+ function Pulse.Log.clear() _buf = {}; _timers = {}; _seq = 0 end
186
+ function Pulse.Log.dump() local t={}; for _,e in ipairs(_buf) do t[#t+1]=e.line end; return table.concat(t,"\n") end
187
+ function Pulse.Log.save(path)
188
+ path = path or "pulse_log.txt"
189
+ if type(rawget(_G, "writefile")) == "function" then
190
+ pcall(writefile, path, Pulse.Log.dump())
191
+ return true
192
+ end
193
+ return false
194
+ end
195
+
196
+ if _active then
197
+ print("[Pulse.Log] dev mode — configure via Pulse.Log.configure({...})")
198
+ end
199
+ end
200
+
201
+ -- ── Pulse.Monitor ─────────────────────────────────────────────────────────────
202
+ -- Lightweight live key-value store for the debug UI monitor bar.
203
+ -- Pulse.Monitor.set("titans", 3) anywhere; debug GUI reads it every frame.
204
+
205
+ do
206
+ local _vals = {}
207
+ local _subs = {}
208
+
209
+ Pulse.Monitor = {}
210
+
211
+ function Pulse.Monitor.set(key, value)
212
+ _vals[key] = value
213
+ for _, fn in ipairs(_subs) do pcall(fn, key, value) end
214
+ end
215
+
216
+ -- Increment a named counter — use in loop bodies to prove execution.
217
+ -- Put Pulse.Monitor.tick("aimbot") inside a Heartbeat loop and watch
218
+ -- the monitor bar: if the count stops increasing, the loop stopped.
219
+ function Pulse.Monitor.tick(key)
220
+ local v = (_vals[key] or 0) + 1
221
+ _vals[key] = v
222
+ for _, fn in ipairs(_subs) do pcall(fn, key, v) end
223
+ end
224
+
225
+ function Pulse.Monitor.get(key) return _vals[key] end
226
+ function Pulse.Monitor.getAll() local t={}; for k,v in pairs(_vals) do t[k]=v end; return t end
227
+ function Pulse.Monitor.subscribe(fn) _subs[#_subs+1] = fn end
228
+ end
@@ -0,0 +1,58 @@
1
+ -- ── Pulse.Loop ───────────────────────────────────────────────────────────────
2
+ -- A managed task loop with lifecycle logging and spam-safe error handling.
3
+ -- Usage:
4
+ -- local loop = Pulse.Loop.new(1.0, function() ... end, "Aimbot")
5
+ --
6
+ -- loop:start() -- begin (restarts if already running)
7
+ -- loop:stop() -- cancel (safe even if not running)
8
+ -- loop:running() → bool
9
+ -- loop:setInterval(n) -- change interval while running
10
+
11
+ Pulse.Loop = (function()
12
+ local L = {}
13
+
14
+ -- interval: seconds between ticks
15
+ -- fn: body; return false to self-stop
16
+ -- tag: log tag for this loop (optional, defaults to "loop")
17
+ function L.new(interval, fn, tag)
18
+ local _tag = tag or "loop"
19
+ local self = { _id = nil, _interval = interval, _fn = fn }
20
+
21
+ function self:start()
22
+ self:stop() -- stop previous run if any (silent — _id already nil after stop)
23
+ local id = {}
24
+ self._id = id
25
+ Pulse.Log.trace(_tag, "loop started", { interval = self._interval })
26
+ task.spawn(function()
27
+ while self._id == id do
28
+ local ok, result = pcall(self._fn)
29
+ if not ok then
30
+ -- Throttle to once every 5 s per unique error so loops can't spam.
31
+ Pulse.Log.throttle(_tag, 5, "warn", "loop error", { err = tostring(result) })
32
+ elseif result == false then
33
+ Pulse.Log.trace(_tag, "loop self-stopped")
34
+ break
35
+ end
36
+ if self._id == id then
37
+ task.wait(self._interval)
38
+ end
39
+ end
40
+ if self._id == id then self._id = nil end
41
+ end)
42
+ end
43
+
44
+ function self:stop()
45
+ if self._id ~= nil then
46
+ Pulse.Log.trace(_tag, "loop stopped")
47
+ end
48
+ self._id = nil
49
+ end
50
+
51
+ function self:running() return self._id ~= nil end
52
+ function self:setInterval(n) self._interval = n end
53
+
54
+ return self
55
+ end
56
+
57
+ return L
58
+ end)()
@@ -0,0 +1,48 @@
1
+ -- ── Pulse.Memory ─────────────────────────────────────────────────────────────
2
+ -- Executor capability checks and safe module resolution.
3
+ -- Usage:
4
+ -- if Pulse.Memory.supports("writefile") then Pulse.Log.save() end
5
+ -- local mem = Pulse.Memory.requireFrom("ODMG", "Client", "Memory")
6
+
7
+ Pulse.Memory = (function()
8
+ local M = {}
9
+
10
+ -- True if the named global function exists (e.g. "writefile", "readfile").
11
+ function M.supports(fnName)
12
+ return type(rawget(_G, fnName)) == "function"
13
+ end
14
+
15
+ -- Test whether a table field is actually writable on this executor.
16
+ -- Returns true / false.
17
+ function M.testWritable(tbl, key)
18
+ if not tbl then return false end
19
+ local orig
20
+ if not pcall(function() orig = tbl[key] end) then return false end
21
+ local test = type(orig) == "number" and (orig + 9999.1) or 9999.1
22
+ if not pcall(function() tbl[key] = test end) then return false end
23
+ local readback
24
+ if not pcall(function() readback = tbl[key] end) then
25
+ pcall(function() tbl[key] = orig end); return false
26
+ end
27
+ pcall(function() tbl[key] = orig end)
28
+ return readback == test
29
+ end
30
+
31
+ -- Traverse child names from Player.Character and require() the final Instance.
32
+ -- Returns the module value or nil if any step fails.
33
+ function M.requireFrom(...)
34
+ local node = _LocalPlayer.Character
35
+ if not node then return nil end
36
+ for _, name in ipairs({...}) do
37
+ if not node then return nil end
38
+ local ok, child = pcall(function() return node:FindFirstChild(name) end)
39
+ if not ok or not child then return nil end
40
+ node = child
41
+ end
42
+ if typeof(node) ~= "Instance" then return nil end
43
+ local ok, result = pcall(require, node)
44
+ return ok and result or nil
45
+ end
46
+
47
+ return M
48
+ end)()
@@ -0,0 +1,160 @@
1
+ -- Pulse.Narrate — on-screen toast + sound narration for dev builds.
2
+ -- Active only when _PULSE_DEV = true. In prod every call is a no-op.
3
+ --
4
+ -- Usage:
5
+ -- Pulse.Narrate.say("locking titan", "warn") -- explicit toast
6
+ -- Pulse.Narrate.configure({ autoLevel = "warn", sound = true, volume = 0.5 })
7
+ -- -- After configure(), Pulse.Log entries at or above autoLevel are auto-narrated.
8
+
9
+ do
10
+ local _active = (_PULSE_DEV == true)
11
+ local _sound = true
12
+ local _volume = 0.5
13
+ local _autoN = 3 -- auto-narrate warn (3) and error (4) by default
14
+ local _N = { trace=0, debug=1, info=2, warn=3, error=4 }
15
+
16
+ -- Level colors matching the log palette
17
+ local _colors = {
18
+ [0] = Color3.fromRGB(72, 72, 90),
19
+ [1] = Color3.fromRGB(70, 175, 155),
20
+ [2] = Color3.fromRGB(170, 195, 220),
21
+ [3] = Color3.fromRGB(215, 165, 45),
22
+ [4] = Color3.fromRGB(215, 65, 65),
23
+ }
24
+
25
+ -- ── Lazy GUI (created on first call) ─────────────────────────────────────
26
+ local _gui = nil
27
+ local _frame = nil
28
+ local _label = nil
29
+ local _TS = nil
30
+ pcall(function() _TS = game:GetService("TweenService") end)
31
+
32
+ local function _ensureGui()
33
+ if _gui then return end
34
+ local parent
35
+ pcall(function() parent = game:GetService("CoreGui") end)
36
+ if not parent then
37
+ pcall(function() parent = game:GetService("Players").LocalPlayer.PlayerGui end)
38
+ end
39
+ if not parent then return end
40
+
41
+ _gui = Instance.new("ScreenGui")
42
+ _gui.Name = "PulseNarrate"
43
+ _gui.ResetOnSpawn = false
44
+ _gui.IgnoreGuiInset = true
45
+ _gui.DisplayOrder = 1001
46
+ pcall(function() _gui.Parent = parent end)
47
+
48
+ _frame = Instance.new("Frame")
49
+ _frame.Name = "Toast"
50
+ _frame.Size = UDim2.new(0, 520, 0, 52)
51
+ _frame.Position = UDim2.new(0.5, -260, 0, 110)
52
+ _frame.BackgroundColor3 = Color3.fromRGB(10, 10, 18)
53
+ _frame.BackgroundTransparency = 0.15
54
+ _frame.BorderSizePixel = 0
55
+ _frame.Visible = false
56
+ _frame.Parent = _gui
57
+ Instance.new("UICorner", _frame).CornerRadius = UDim.new(0, 8)
58
+ local stroke = Instance.new("UIStroke", _frame)
59
+ stroke.Color = Color3.fromRGB(60, 60, 90)
60
+ stroke.Thickness = 1
61
+
62
+ _label = Instance.new("TextLabel")
63
+ _label.Size = UDim2.new(1, -20, 1, 0)
64
+ _label.Position = UDim2.new(0, 10, 0, 0)
65
+ _label.BackgroundTransparency = 1
66
+ _label.Font = Enum.Font.GothamBold
67
+ _label.TextSize = 15
68
+ _label.TextColor3 = Color3.fromRGB(200, 200, 220)
69
+ _label.TextXAlignment = Enum.TextXAlignment.Center
70
+ _label.TextYAlignment = Enum.TextYAlignment.Center
71
+ _label.TextWrapped = true
72
+ _label.RichText = true
73
+ _label.Text = ""
74
+ _label.Parent = _frame
75
+ end
76
+
77
+ -- ── Sound ─────────────────────────────────────────────────────────────────
78
+ local _snd = nil
79
+ local _pitches = { [0]=0.5, [1]=0.7, [2]=1.0, [3]=1.35, [4]=1.7 }
80
+
81
+ local function _playSound(n)
82
+ if not _sound then return end
83
+ pcall(function()
84
+ if not _snd then
85
+ _snd = Instance.new("Sound")
86
+ _snd.SoundId = "rbxasset://sounds/electronicpingshort.wav"
87
+ _snd.Parent = game:GetService("SoundService")
88
+ end
89
+ _snd.Volume = _volume
90
+ _snd.PlaybackSpeed = _pitches[n] or 1.0
91
+ _snd:Play()
92
+ end)
93
+ end
94
+
95
+ -- ── Toast display ─────────────────────────────────────────────────────────
96
+ local _toastActive = false
97
+
98
+ local function _showToast(msg, n)
99
+ _ensureGui()
100
+ if not (_frame and _frame.Parent) then return end
101
+ local color = _colors[n] or _colors[2]
102
+
103
+ _label.Text = tostring(msg)
104
+ _label.TextColor3 = color
105
+ _label.TextTransparency = 0
106
+ _frame.BackgroundTransparency = 0.15
107
+ _frame.Visible = true
108
+ _toastActive = true
109
+
110
+ -- Fade out after 2.8 s
111
+ task.delay(2.3, function()
112
+ if not (_frame and _frame.Parent) then return end
113
+ if _TS then
114
+ local ti = TweenInfo.new(0.5, Enum.EasingStyle.Quad, Enum.EasingDirection.In)
115
+ _TS:Create(_frame, ti, { BackgroundTransparency = 1 }):Play()
116
+ _TS:Create(_label, ti, { TextTransparency = 1 }):Play()
117
+ task.wait(0.55)
118
+ end
119
+ if _frame and _frame.Parent then
120
+ _frame.Visible = false
121
+ _toastActive = false
122
+ if _label then _label.TextTransparency = 0 end
123
+ end
124
+ end)
125
+ end
126
+
127
+ -- ── Public API ────────────────────────────────────────────────────────────
128
+
129
+ Pulse.Narrate = {}
130
+
131
+ function Pulse.Narrate.configure(opts)
132
+ if not _active then return end
133
+ if opts.sound ~= nil then _sound = opts.sound end
134
+ if opts.volume ~= nil then _volume = opts.volume end
135
+ if opts.autoLevel ~= nil then
136
+ _autoN = _N[opts.autoLevel] or _autoN
137
+ end
138
+ end
139
+
140
+ -- Explicitly announce a message.
141
+ -- levelName: "trace" | "debug" | "info" | "warn" | "error" (default "info")
142
+ function Pulse.Narrate.say(msg, levelName)
143
+ if not _active then return end
144
+ local n = _N[levelName] or 2
145
+ local prefix = levelName and ("[" .. levelName:upper():sub(1,3) .. "] ") or ""
146
+ _showToast(prefix .. tostring(msg), n)
147
+ _playSound(n)
148
+ end
149
+
150
+ -- Subscribe to Pulse.Log so entries at/above autoLevel get auto-narrated.
151
+ -- This runs immediately after narrate.lua is loaded (Pulse.Log is already defined).
152
+ if _active and Pulse.Log and Pulse.Log.subscribe then
153
+ Pulse.Log.subscribe(function(e)
154
+ if e.n >= _autoN then
155
+ _showToast(e.tag .. ": " .. e.msg, e.n)
156
+ _playSound(e.n)
157
+ end
158
+ end)
159
+ end
160
+ end
@@ -0,0 +1,51 @@
1
+ -- ── Pulse.Notify ─────────────────────────────────────────────────────────────
2
+ -- In-game notification helpers.
3
+ --
4
+ -- Instant toast:
5
+ -- Pulse.Notify("Aimbot locked on target")
6
+ -- Pulse.Notify("Speed boost active", 5) -- 5-second toast
7
+ --
8
+ -- Auto-notify when a boolean signal changes:
9
+ -- Pulse.Notify.onToggle(self.enabled, "Aimbot")
10
+ -- -- fires "Aimbot on" when signal → true, "Aimbot off" when → false
11
+ --
12
+ -- Pulse.Notify.onToggle(self.enabled, "AutoFarm", {
13
+ -- on = "AutoFarm started", -- custom on message
14
+ -- off = "AutoFarm stopped", -- custom off message
15
+ -- duration = 5, -- toast duration in seconds (default 3)
16
+ -- notify_on = true, -- set false to skip the ON notification
17
+ -- notify_off = true, -- set false to skip the OFF notification
18
+ -- })
19
+ --
20
+ -- Returns the unsubscribe function so you can stop watching:
21
+ -- local stop = Pulse.Notify.onToggle(sig, "Name")
22
+ -- stop() -- disconnect
23
+
24
+ do
25
+ Pulse.Notify = {}
26
+
27
+ -- Make Pulse.Notify(...) callable as a direct shorthand for _PulseNotify.
28
+ setmetatable(Pulse.Notify, {
29
+ __call = function(_, msg, duration)
30
+ pcall(_PulseNotify, tostring(msg), duration or 3)
31
+ end,
32
+ })
33
+
34
+ -- Watch a boolean signal and fire a toast each time it changes.
35
+ -- Returns the unsubscribe function (useful inside component:watch).
36
+ function Pulse.Notify.onToggle(signal, name, opts)
37
+ opts = opts or {}
38
+ local dur = opts.duration or 3
39
+ return signal:onChange(function(v)
40
+ if v then
41
+ if opts.notify_on ~= false then
42
+ pcall(_PulseNotify, opts.on or (name .. " on"), dur)
43
+ end
44
+ else
45
+ if opts.notify_off ~= false then
46
+ pcall(_PulseNotify, opts.off or (name .. " off"), dur)
47
+ end
48
+ end
49
+ end)
50
+ end
51
+ end
@@ -0,0 +1,48 @@
1
+ -- ── Pulse.Perf ───────────────────────────────────────────────────────────────
2
+ -- Lightweight frame-time sampler with rolling average and optional warn threshold.
3
+ -- Usage:
4
+ -- Pulse.Perf.tick() -- call once per frame to sample
5
+ -- Pulse.Perf.avg() -- average frame time in seconds
6
+ -- Pulse.Perf.fps() -- estimated FPS (1 / avg)
7
+ -- Pulse.Perf.enabled -- false when auto-disabled on error
8
+ -- Pulse.Perf.setThreshold(ms) -- warn when avg > ms (default 50 ms)
9
+
10
+ Pulse.Perf = (function()
11
+ local P = {}
12
+ P.enabled = true
13
+ P._buf = {}
14
+ P._max = 60
15
+ P._last = 0
16
+ P._avg = 0
17
+ P._thresh = 0.05 -- 50 ms
18
+ P._lastWarn = 0
19
+ P._warnCD = 5
20
+
21
+ function P.tick()
22
+ if not P.enabled then return end
23
+ local ok = pcall(function()
24
+ local now = tick()
25
+ if P._last > 0 then
26
+ local ft = now - P._last
27
+ P._buf[#P._buf + 1] = ft
28
+ if #P._buf > P._max then table.remove(P._buf, 1) end
29
+ local total = 0
30
+ for i = 1, #P._buf do total = total + P._buf[i] end
31
+ P._avg = total / #P._buf
32
+ if P._thresh > 0 and P._avg > P._thresh
33
+ and now - P._lastWarn > P._warnCD then
34
+ warn("[Pulse.Perf] avg frame time: " .. math.floor(P._avg * 1000) .. " ms")
35
+ P._lastWarn = now
36
+ end
37
+ end
38
+ P._last = now
39
+ end)
40
+ if not ok then P.enabled = false end
41
+ end
42
+
43
+ function P.avg() return P._avg end
44
+ function P.fps() return P._avg > 0 and (1 / P._avg) or 0 end
45
+ function P.setThreshold(ms) P._thresh = ms / 1000 end
46
+
47
+ return P
48
+ end)()