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.
- package/README.md +225 -0
- package/adapters/linoria.lua +233 -0
- package/adapters/windui.llms.txt +366 -0
- package/adapters/windui.lua +505 -0
- package/bin/rb.js +2 -0
- package/dist/index.js +3285 -0
- package/package.json +59 -0
- package/pulse/dev/debuggui.lua +1206 -0
- package/pulse/dev/devconfig.lua +81 -0
- package/pulse/dev/ui/9_DevPanel.lua +384 -0
- package/pulse/helpers/aim.lua +193 -0
- package/pulse/helpers/cache.lua +68 -0
- package/pulse/helpers/cleaner.lua +110 -0
- package/pulse/helpers/conn.lua +33 -0
- package/pulse/helpers/cooldown.lua +47 -0
- package/pulse/helpers/draw.lua +122 -0
- package/pulse/helpers/hitbox.lua +63 -0
- package/pulse/helpers/input.lua +24 -0
- package/pulse/helpers/log.lua +228 -0
- package/pulse/helpers/loop.lua +58 -0
- package/pulse/helpers/memory.lua +48 -0
- package/pulse/helpers/narrate.lua +160 -0
- package/pulse/helpers/notify.lua +51 -0
- package/pulse/helpers/perf.lua +48 -0
- package/pulse/helpers/remote.lua +128 -0
- package/pulse/helpers/restore.lua +59 -0
- package/pulse/helpers/store.lua +39 -0
- package/pulse/helpers/team.lua +83 -0
- package/pulse/helpers/testmode.lua +80 -0
- package/pulse/helpers/trace.lua +111 -0
- package/pulse/helpers/track.lua +85 -0
- package/pulse/helpers/vec.lua +52 -0
- package/pulse/helpers/world.lua +51 -0
- package/pulse/runtime.lua +343 -0
- package/pulse/ui/linoria_settings.lua +55 -0
- package/pulse/ui/windui_settings.lua +87 -0
- package/templates/AGENTS.md +177 -0
- package/templates/CLAUDE.md +424 -0
- package/templates/deploy_config.example +17 -0
- package/templates/example_esp.rblua +69 -0
- package/templates/example_fov.rblua +20 -0
- package/templates/example_speed.rblua +25 -0
- package/templates/gitignore +4 -0
- package/templates/globals.lua +66 -0
- package/templates/layout.rblua +28 -0
- package/templates/module.lua +7 -0
- package/templates/module.rblua +32 -0
- package/templates/page_home.rblua +9 -0
- package/templates/remote.lua +6 -0
- package/templates/remotes.lua +14 -0
- package/vscode/language-configuration.json +35 -0
- package/vscode/package.json +35 -0
- package/vscode/src/extension.js +397 -0
- package/vscode/syntaxes/rblua.tmLanguage.json +126 -0
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
-- ── Pulse.World ──────────────────────────────────────────────────────────────
|
|
2
|
+
-- Safe workspace/service navigation — no nil-index crashes.
|
|
3
|
+
-- Usage:
|
|
4
|
+
-- local folder = Pulse.World.find("Titans", "Alive")
|
|
5
|
+
-- local list = Pulse.World.children("Titans", "Alive")
|
|
6
|
+
-- local svc = Pulse.World.service("TweenService")
|
|
7
|
+
-- local child = Pulse.World.await(model, "HumanoidRootPart", 5)
|
|
8
|
+
|
|
9
|
+
Pulse.World = (function()
|
|
10
|
+
local W = {}
|
|
11
|
+
|
|
12
|
+
-- Navigate workspace by a sequence of child names.
|
|
13
|
+
-- Returns the final Instance, or nil if any step is missing.
|
|
14
|
+
function W.find(...)
|
|
15
|
+
local node = workspace
|
|
16
|
+
for _, name in ipairs({...}) do
|
|
17
|
+
if not node then return nil end
|
|
18
|
+
local ok, child = pcall(function() return node:FindFirstChild(name) end)
|
|
19
|
+
if not ok or not child then return nil end
|
|
20
|
+
node = child
|
|
21
|
+
end
|
|
22
|
+
return node
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
-- Like find(), but returns the children of the final node.
|
|
26
|
+
-- Returns {} if the path doesn't exist.
|
|
27
|
+
function W.children(...)
|
|
28
|
+
local node = W.find(...)
|
|
29
|
+
if not node then return {} end
|
|
30
|
+
local ok, list = pcall(function() return node:GetChildren() end)
|
|
31
|
+
return ok and list or {}
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
-- Safely get a game service by name.
|
|
35
|
+
function W.service(name)
|
|
36
|
+
local ok, svc = pcall(game.GetService, game, name)
|
|
37
|
+
return ok and svc or nil
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
-- Wait for a named child to appear (wraps WaitForChild with a timeout).
|
|
41
|
+
-- Returns the child Instance or nil on timeout / error.
|
|
42
|
+
function W.await(parent, name, timeout)
|
|
43
|
+
if typeof(parent) ~= "Instance" then return nil end
|
|
44
|
+
local ok, result = pcall(function()
|
|
45
|
+
return parent:WaitForChild(name, timeout or 10)
|
|
46
|
+
end)
|
|
47
|
+
return ok and result or nil
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
return W
|
|
51
|
+
end)()
|
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
-- ============================================================
|
|
2
|
+
-- Pulse Runtime v0.1.0
|
|
3
|
+
-- Injected automatically by rb build before all user modules.
|
|
4
|
+
-- Do not require() this file — the compiler handles injection.
|
|
5
|
+
-- Pulse.* helpers are injected from rb/pulse/helpers/ after this.
|
|
6
|
+
-- ============================================================
|
|
7
|
+
|
|
8
|
+
-- ── Signal ───────────────────────────────────────────────────────────────────
|
|
9
|
+
-- Reactive atom. Call with no args to read, one arg to write.
|
|
10
|
+
-- signal:onChange(fn) → returns unsubscribe function.
|
|
11
|
+
|
|
12
|
+
local function Signal(default)
|
|
13
|
+
local _value = default
|
|
14
|
+
local _subs = {}
|
|
15
|
+
|
|
16
|
+
local s = {}
|
|
17
|
+
|
|
18
|
+
function s:get() return _value end
|
|
19
|
+
|
|
20
|
+
function s:set(new)
|
|
21
|
+
if new == _value then return end
|
|
22
|
+
_value = new
|
|
23
|
+
for i = #_subs, 1, -1 do
|
|
24
|
+
local ok, err = pcall(_subs[i], new)
|
|
25
|
+
if not ok then
|
|
26
|
+
local msg = "signal subscriber error: " .. tostring(err)
|
|
27
|
+
if Pulse.Log then
|
|
28
|
+
Pulse.Log.error("signal", msg, { signal = s._name or "?" })
|
|
29
|
+
else
|
|
30
|
+
warn("[Pulse] " .. msg)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
function s:onChange(fn)
|
|
37
|
+
table.insert(_subs, fn)
|
|
38
|
+
return function()
|
|
39
|
+
for i, sub in ipairs(_subs) do
|
|
40
|
+
if sub == fn then table.remove(_subs, i) return end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
setmetatable(s, {
|
|
46
|
+
__call = function(_, ...)
|
|
47
|
+
if select("#", ...) == 0 then return s:get() end
|
|
48
|
+
s:set(...)
|
|
49
|
+
end
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
return s
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
-- ── Computed ─────────────────────────────────────────────────────────────────
|
|
56
|
+
-- Derived cached value. Re-evaluates when :invalidate() is called,
|
|
57
|
+
-- or when any Signal passed via :track(signal) changes.
|
|
58
|
+
|
|
59
|
+
local function Computed(fn)
|
|
60
|
+
local _cache = nil
|
|
61
|
+
local _dirty = true
|
|
62
|
+
local _unsubs = {}
|
|
63
|
+
|
|
64
|
+
local c = {}
|
|
65
|
+
|
|
66
|
+
function c:get()
|
|
67
|
+
if _dirty then
|
|
68
|
+
_cache = fn()
|
|
69
|
+
_dirty = false
|
|
70
|
+
end
|
|
71
|
+
return _cache
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
function c:track(signal)
|
|
75
|
+
table.insert(_unsubs, signal:onChange(function()
|
|
76
|
+
_dirty = true
|
|
77
|
+
end))
|
|
78
|
+
return c
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
function c:destroy()
|
|
82
|
+
for _, unsub in ipairs(_unsubs) do pcall(unsub) end
|
|
83
|
+
_unsubs = {}
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
setmetatable(c, { __call = function() return c:get() end })
|
|
87
|
+
return c
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
-- ── PulseEvent ───────────────────────────────────────────────────────────────
|
|
91
|
+
-- Lightweight event bus. fire(...) notifies all listeners.
|
|
92
|
+
-- connect(fn) → returns disconnect function.
|
|
93
|
+
|
|
94
|
+
local function PulseEvent(name)
|
|
95
|
+
local _listeners = {}
|
|
96
|
+
local e = {}
|
|
97
|
+
e._isPulseEvent = true
|
|
98
|
+
e._name = name or "?"
|
|
99
|
+
|
|
100
|
+
function e:connect(fn)
|
|
101
|
+
table.insert(_listeners, fn)
|
|
102
|
+
return function()
|
|
103
|
+
for i, l in ipairs(_listeners) do
|
|
104
|
+
if l == fn then table.remove(_listeners, i) return end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
function e:fire(...)
|
|
110
|
+
for i = #_listeners, 1, -1 do
|
|
111
|
+
local ok, err = pcall(_listeners[i], ...)
|
|
112
|
+
if not ok then
|
|
113
|
+
local msg = "event listener error: " .. tostring(err)
|
|
114
|
+
if Pulse.Log then
|
|
115
|
+
Pulse.Log.error("event", msg, { event = e._name })
|
|
116
|
+
else
|
|
117
|
+
warn("[Pulse] event '" .. e._name .. "' error: " .. tostring(err))
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
setmetatable(e, { __call = function(_, ...) e:fire(...) end })
|
|
124
|
+
return e
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
-- ── Component ────────────────────────────────────────────────────────────────
|
|
128
|
+
-- Behavior service with lifecycle, connection tracking, and task management.
|
|
129
|
+
|
|
130
|
+
local _Registry = {} -- flat: { "Aimbot" = component, ... }
|
|
131
|
+
local _NamespaceReg = {} -- nested: { "Shifter" = { "Combat" = component } }
|
|
132
|
+
|
|
133
|
+
local function Component(name)
|
|
134
|
+
local c = {}
|
|
135
|
+
c._isComponent = true
|
|
136
|
+
c._name = name
|
|
137
|
+
c._connections = {}
|
|
138
|
+
c._tasks = {}
|
|
139
|
+
c._watchers = {}
|
|
140
|
+
c._mounted = false
|
|
141
|
+
|
|
142
|
+
-- Connection management
|
|
143
|
+
function c:bind(key, connection)
|
|
144
|
+
if self._connections[key] then
|
|
145
|
+
pcall(function() self._connections[key]:Disconnect() end)
|
|
146
|
+
end
|
|
147
|
+
self._connections[key] = connection
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
function c:unbind(key)
|
|
151
|
+
if self._connections[key] then
|
|
152
|
+
pcall(function() self._connections[key]:Disconnect() end)
|
|
153
|
+
self._connections[key] = nil
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
function c:unbindAll()
|
|
158
|
+
for _, conn in pairs(self._connections) do
|
|
159
|
+
pcall(function() conn:Disconnect() end)
|
|
160
|
+
end
|
|
161
|
+
self._connections = {}
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
-- Signal watching (auto-cleaned on unmount)
|
|
165
|
+
function c:watch(signal, fn)
|
|
166
|
+
local unsub = signal:onChange(fn)
|
|
167
|
+
table.insert(self._watchers, unsub)
|
|
168
|
+
return unsub
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
-- Delayed tasks (auto-cancelled on unmount)
|
|
172
|
+
function c:task(delay, fn)
|
|
173
|
+
local cancelled = false
|
|
174
|
+
table.insert(self._tasks, function() cancelled = true end)
|
|
175
|
+
task.delay(delay, function()
|
|
176
|
+
if cancelled then return end
|
|
177
|
+
local ok, err = pcall(fn)
|
|
178
|
+
if not ok then
|
|
179
|
+
local msg = "after-block error: " .. tostring(err)
|
|
180
|
+
if Pulse.Log then
|
|
181
|
+
Pulse.Log.error("component", msg, { component = name })
|
|
182
|
+
else
|
|
183
|
+
warn("[Pulse] " .. name .. " " .. msg)
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
end)
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
-- Respawn listener (bound to connection tracker)
|
|
190
|
+
function c:onRespawn(fn)
|
|
191
|
+
local key = "__respawn_" .. name
|
|
192
|
+
local existing = self._connections[key]
|
|
193
|
+
if existing then
|
|
194
|
+
pcall(function() existing:Disconnect() end)
|
|
195
|
+
end
|
|
196
|
+
self._connections[key] = game:GetService("Players").LocalPlayer
|
|
197
|
+
.CharacterAdded:Connect(fn)
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
-- Lifecycle
|
|
201
|
+
function c:mount()
|
|
202
|
+
if self._mounted then return end
|
|
203
|
+
self._mounted = true
|
|
204
|
+
if Pulse.Log then Pulse.Log.trace("component", name .. " mounted") end
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
function c:unmount()
|
|
208
|
+
if not self._mounted then return end
|
|
209
|
+
self._mounted = false
|
|
210
|
+
self:unbindAll()
|
|
211
|
+
for _, unsub in ipairs(self._watchers) do pcall(unsub) end
|
|
212
|
+
self._watchers = {}
|
|
213
|
+
for _, cancel in ipairs(self._tasks) do pcall(cancel) end
|
|
214
|
+
self._tasks = {}
|
|
215
|
+
if Pulse.Log then Pulse.Log.trace("component", name .. " unmounted") end
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
-- Register: support "Shifter.Combat" style namespace
|
|
219
|
+
local parts = {}
|
|
220
|
+
for part in name:gmatch("[^%.]+") do table.insert(parts, part) end
|
|
221
|
+
|
|
222
|
+
_Registry[name] = c
|
|
223
|
+
|
|
224
|
+
local node = _NamespaceReg
|
|
225
|
+
for i, part in ipairs(parts) do
|
|
226
|
+
if i == #parts then
|
|
227
|
+
node[part] = c
|
|
228
|
+
else
|
|
229
|
+
node[part] = node[part] or {}
|
|
230
|
+
node = node[part]
|
|
231
|
+
end
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
return c
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
-- ── Store ────────────────────────────────────────────────────────────────────
|
|
238
|
+
-- Shared state with signals + actions.
|
|
239
|
+
-- Built by transpiler-generated setup calls; this is the constructor.
|
|
240
|
+
|
|
241
|
+
local _Stores = {}
|
|
242
|
+
|
|
243
|
+
local function Store(name)
|
|
244
|
+
local s = {}
|
|
245
|
+
s._isStore = true
|
|
246
|
+
s._name = name
|
|
247
|
+
s._signals = {}
|
|
248
|
+
s._actions = {}
|
|
249
|
+
|
|
250
|
+
-- Add a signal slice
|
|
251
|
+
function s:addSignal(key, default)
|
|
252
|
+
local sig = Signal(default)
|
|
253
|
+
sig._name = name .. "." .. key
|
|
254
|
+
self[key] = sig
|
|
255
|
+
self._signals[key] = sig
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
-- Register an action (only way to write store state from outside)
|
|
259
|
+
function s:addAction(key, fn)
|
|
260
|
+
self[key] = fn
|
|
261
|
+
self._actions[key] = fn
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
_Stores[name] = s
|
|
265
|
+
return s
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
-- ── Framework character globals ──────────────────────────────────────────────
|
|
269
|
+
-- Always available in on-blocks via transpiler-generated local mirrors.
|
|
270
|
+
-- Live lookups — never cached, always correct across respawn.
|
|
271
|
+
|
|
272
|
+
local _Players = game:GetService("Players")
|
|
273
|
+
local _RunService = game:GetService("RunService")
|
|
274
|
+
local _UIS = game:GetService("UserInputService")
|
|
275
|
+
local _VIM = game:GetService("VirtualInputManager")
|
|
276
|
+
local _LocalPlayer = _Players.LocalPlayer
|
|
277
|
+
|
|
278
|
+
-- ── Destroy ──────────────────────────────────────────────────────────────────
|
|
279
|
+
-- _PulseDestroy() — stop all components, tear down UI, clear _G entry.
|
|
280
|
+
-- _PulseOnDestroy(fn) — register a teardown callback (UI adapter uses this).
|
|
281
|
+
|
|
282
|
+
local _destroyCallbacks = {}
|
|
283
|
+
|
|
284
|
+
local function _PulseRegisterDestroy(fn)
|
|
285
|
+
table.insert(_destroyCallbacks, fn)
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
local function _PulseDestroyAll()
|
|
289
|
+
-- Teardown callbacks first (UI must be destroyed before connections drop)
|
|
290
|
+
for _, cb in ipairs(_destroyCallbacks) do pcall(cb) end
|
|
291
|
+
_destroyCallbacks = {}
|
|
292
|
+
-- Unmount every component: disconnects all Roblox connections + cancels tasks
|
|
293
|
+
for _, comp in pairs(_Registry) do
|
|
294
|
+
pcall(function() comp:unmount() end)
|
|
295
|
+
end
|
|
296
|
+
_G.__AOT_R_DESTROY = nil
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
-- ── Pulse namespace ──────────────────────────────────────────────────────────
|
|
300
|
+
-- Pulse.* helpers are injected from rb/pulse/helpers/ immediately after this.
|
|
301
|
+
|
|
302
|
+
local Pulse = {}
|
|
303
|
+
|
|
304
|
+
-- ── Global exports ───────────────────────────────────────────────────────────
|
|
305
|
+
|
|
306
|
+
Signal = Signal
|
|
307
|
+
Computed = Computed
|
|
308
|
+
PulseEvent = PulseEvent
|
|
309
|
+
Component = Component
|
|
310
|
+
Store = Store
|
|
311
|
+
Components = _NamespaceReg -- Components.Aimbot.enabled()
|
|
312
|
+
Pulse = Pulse -- Pulse.Log / .Draw / .Cache / .Track / .World / .Aim / .Memory / …
|
|
313
|
+
|
|
314
|
+
-- Internal accessors used by transpiler-generated code.
|
|
315
|
+
-- Read _LocalPlayer.Character directly every call — no stale cache after respawn.
|
|
316
|
+
_PulseGetChar = function()
|
|
317
|
+
return _LocalPlayer.Character
|
|
318
|
+
end
|
|
319
|
+
_PulseGetHRP = function()
|
|
320
|
+
local c = _LocalPlayer.Character
|
|
321
|
+
return c and c:FindFirstChild("HumanoidRootPart")
|
|
322
|
+
end
|
|
323
|
+
_PulseGetHumanoid = function()
|
|
324
|
+
local c = _LocalPlayer.Character
|
|
325
|
+
return c and c:FindFirstChild("Humanoid")
|
|
326
|
+
end
|
|
327
|
+
_PulseGetAlive = function()
|
|
328
|
+
local c = _LocalPlayer.Character
|
|
329
|
+
local h = c and c:FindFirstChild("Humanoid")
|
|
330
|
+
return h ~= nil and h.Health > 0
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
-- RS / UIS shortcuts used in transpiler-generated bind calls
|
|
334
|
+
_PulseRS = _RunService
|
|
335
|
+
_PulseUIS = _UIS
|
|
336
|
+
|
|
337
|
+
-- Destroy API — used by Settings page button and re-execution guard
|
|
338
|
+
_PulseOnDestroy = _PulseRegisterDestroy
|
|
339
|
+
_PulseDestroy = _PulseDestroyAll
|
|
340
|
+
|
|
341
|
+
-- Ordered list of { type, id, value } entries populated by transpiler-generated code.
|
|
342
|
+
-- Consumed by the compiler-generated defaults runner at end of script.
|
|
343
|
+
_PULSE_DEFAULTS = {}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
-- [pulse/ui/linoria_settings.lua]
|
|
2
|
+
-- Framework-managed Settings tab for Linoria builds.
|
|
3
|
+
-- Injected automatically by the compiler when ui_library = "linoria".
|
|
4
|
+
-- Do not copy this file into src/ — edit it here in rb/pulse/ui/.
|
|
5
|
+
do
|
|
6
|
+
local SettingsTab = _GetTab("Settings")
|
|
7
|
+
|
|
8
|
+
_ThemeManager:SetLibrary(_Library)
|
|
9
|
+
_SaveManager:SetLibrary(_Library)
|
|
10
|
+
|
|
11
|
+
_SaveManager:IgnoreThemeSettings()
|
|
12
|
+
_SaveManager:SetIgnoreIndexes({ "MenuKeybind" })
|
|
13
|
+
|
|
14
|
+
-- Folder names come from folder = "..." in layout.rblua
|
|
15
|
+
_ThemeManager:SetFolder(_PULSE_FOLDER)
|
|
16
|
+
_SaveManager:SetFolder(_PULSE_FOLDER .. "/configs")
|
|
17
|
+
|
|
18
|
+
_ThemeManager:ApplyToTab(SettingsTab)
|
|
19
|
+
_SaveManager:BuildConfigSection(SettingsTab)
|
|
20
|
+
|
|
21
|
+
local gb_menu = SettingsTab:AddLeftGroupbox("Menu Settings")
|
|
22
|
+
gb_menu:AddButton("Destroy UI", function() _PulseDestroy() end)
|
|
23
|
+
gb_menu:AddLabel("Menu bind"):AddKeyPicker("MenuKeybind", {
|
|
24
|
+
Default = _PULSE_TOGGLE_KEY,
|
|
25
|
+
NoUI = false,
|
|
26
|
+
Text = "Menu keybind",
|
|
27
|
+
})
|
|
28
|
+
_Library.ToggleKeybind = Options.MenuKeybind
|
|
29
|
+
|
|
30
|
+
-- Performance — rendered directly (multi-dropdown requires a direct Linoria call)
|
|
31
|
+
local gb_perf = SettingsTab:AddRightGroupbox("Performance")
|
|
32
|
+
gb_perf:AddLabel("Removes objects every 1 second.")
|
|
33
|
+
gb_perf:AddLabel("Safe to toggle mid-session.")
|
|
34
|
+
gb_perf:AddBlank(4)
|
|
35
|
+
gb_perf:AddToggle("Perf_enabled", {
|
|
36
|
+
Text = "Enabled",
|
|
37
|
+
Default = false,
|
|
38
|
+
Tooltip = "Start/stop all selected cleanups",
|
|
39
|
+
Callback = function(v) Performance.enabled(v) end,
|
|
40
|
+
})
|
|
41
|
+
Performance.enabled:onChange(function(v)
|
|
42
|
+
local t = Toggles and Toggles["Perf_enabled"]
|
|
43
|
+
if t then t:SetValue(v) end
|
|
44
|
+
end)
|
|
45
|
+
gb_perf:AddDropdown("Perf_selected", {
|
|
46
|
+
Text = "Remove",
|
|
47
|
+
Values = {"Dead Titans","Decor","HQ Decor","Rocks","Grass","Trees","Civilians","Buildings"},
|
|
48
|
+
Default = {"Dead Titans"},
|
|
49
|
+
Multi = true,
|
|
50
|
+
Tooltip = "Objects removed every second while Enabled is on",
|
|
51
|
+
Callback = function(v) Performance.selected(v) end,
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
_SaveManager:LoadAutoloadConfig()
|
|
55
|
+
end
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
-- [pulse/ui/windui_settings.lua]
|
|
2
|
+
-- Framework-managed Settings tab for Wind UI builds.
|
|
3
|
+
-- Injected automatically by the compiler when ui_library = "windui".
|
|
4
|
+
-- Do not copy this file into src/ — edit it here in rb/pulse/ui/.
|
|
5
|
+
do
|
|
6
|
+
-- ── Appearance ────────────────────────────────────────────────────────────
|
|
7
|
+
local gb_appear = _GetGroupbox("Settings", "left", "Appearance")
|
|
8
|
+
|
|
9
|
+
-- Theme list: live from WindUI (picks up custom themes from layout too),
|
|
10
|
+
-- with a static fallback for all 16 built-in names if GetThemes() is unavailable.
|
|
11
|
+
local _themeValues = _UIAdapter:GetThemeNames()
|
|
12
|
+
or { "Amber","CottonCandy","Crimson","Dark","Emerald","Indigo","Light",
|
|
13
|
+
"Mellowsi","Midnight","MonokaiPro","Plant","Rainbow","Red","Rose","Sky","Violet" }
|
|
14
|
+
gb_appear._container:Dropdown({
|
|
15
|
+
Title = "Theme",
|
|
16
|
+
Values = _themeValues,
|
|
17
|
+
Value = _PULSE_DEFAULT_THEME,
|
|
18
|
+
Flag = "Settings_theme",
|
|
19
|
+
Callback = function(v) _UIAdapter:SetTheme(v) end,
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
-- ── Config ────────────────────────────────────────────────────────────────
|
|
23
|
+
local gb_cfg = _GetGroupbox("Settings", "left", "Configuration")
|
|
24
|
+
|
|
25
|
+
_UIAdapter:addParagraph(gb_cfg,
|
|
26
|
+
"Config is stored in your executor's filesystem.",
|
|
27
|
+
"Save writes the current state. Load restores the last saved state."
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
gb_cfg._container:Button({
|
|
31
|
+
Title = "Save Config",
|
|
32
|
+
Icon = "save",
|
|
33
|
+
Callback = function()
|
|
34
|
+
if _UIAdapter.Config then
|
|
35
|
+
local ok, err = pcall(function() _UIAdapter.Config:Save() end)
|
|
36
|
+
_PulseNotify(ok and "Config saved." or ("Save failed: " .. tostring(err)), 3)
|
|
37
|
+
else
|
|
38
|
+
_PulseNotify("Config manager unavailable.", 3)
|
|
39
|
+
end
|
|
40
|
+
end,
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
gb_cfg._container:Button({
|
|
44
|
+
Title = "Load Config",
|
|
45
|
+
Icon = "folder-open",
|
|
46
|
+
Callback = function()
|
|
47
|
+
if _UIAdapter.Config then
|
|
48
|
+
local ok, err = pcall(function() _UIAdapter.Config:Load() end)
|
|
49
|
+
_PulseNotify(ok and "Config loaded." or ("Load failed: " .. tostring(err)), 3)
|
|
50
|
+
else
|
|
51
|
+
_PulseNotify("Config manager unavailable.", 3)
|
|
52
|
+
end
|
|
53
|
+
end,
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
-- ── Menu Settings ─────────────────────────────────────────────────────────
|
|
57
|
+
local gb_menu = _GetGroupbox("Settings", "right", "Menu Settings")
|
|
58
|
+
|
|
59
|
+
_UIAdapter:addButton(gb_menu, {
|
|
60
|
+
label = "Destroy UI",
|
|
61
|
+
action = function() _PulseDestroy() end,
|
|
62
|
+
tip = "Unloads the script and removes all UI",
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
_UIAdapter:addKeybind(gb_menu, "Settings_MenuKeybind", {
|
|
66
|
+
label = "Menu Toggle",
|
|
67
|
+
key = _PULSE_TOGGLE_KEY,
|
|
68
|
+
action = function() _UIAdapter:ToggleWindow() end,
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
-- ── Auto-load saved config on startup ─────────────────────────────────────
|
|
72
|
+
task.spawn(function()
|
|
73
|
+
task.wait(1.5)
|
|
74
|
+
if _UIAdapter.Config then
|
|
75
|
+
local ok = pcall(function() _UIAdapter.Config:Load() end)
|
|
76
|
+
if ok then Pulse.Log.info("settings", "config auto-loaded") end
|
|
77
|
+
end
|
|
78
|
+
end)
|
|
79
|
+
|
|
80
|
+
-- ── Re-select first tab (Home) — Settings is injected last so WindUI
|
|
81
|
+
-- leaves it active; defer one frame so all tabs are fully built.
|
|
82
|
+
task.defer(function()
|
|
83
|
+
if _UIAdapter._firstTab then
|
|
84
|
+
pcall(function() _UIAdapter._firstTab:Select() end)
|
|
85
|
+
end
|
|
86
|
+
end)
|
|
87
|
+
end
|