pulse-rb 1.2.24 → 1.3.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pulse-rb",
3
- "version": "1.2.24",
3
+ "version": "1.3.1",
4
4
  "description": "rb CLI — Pulse framework build tool for Roblox script projects",
5
5
  "bin": {
6
6
  "rb": "./bin/rb.js"
@@ -28,6 +28,7 @@
28
28
  "ofetch": "^1.3.4",
29
29
  "pathe": "^1.1.2",
30
30
  "picocolors": "^1.1.0",
31
+ "typescript-to-lua": "^1.30.0",
31
32
  "wasmoon": "^1.16.0",
32
33
  "zod": "^4.4.3"
33
34
  },
@@ -37,7 +38,7 @@
37
38
  "@types/node": "^22.0.0",
38
39
  "tsup": "^8.3.0",
39
40
  "tsx": "^4.19.0",
40
- "typescript": "^5.7.0",
41
+ "typescript": "^5.8.3",
41
42
  "vitest": "^2.1.0"
42
43
  },
43
44
  "engines": {
package/pulse/runtime.lua CHANGED
@@ -341,3 +341,157 @@ _PulseDestroy = _PulseDestroyAll
341
341
  -- Ordered list of { type, id, value } entries populated by transpiler-generated code.
342
342
  -- Consumed by the compiler-generated defaults runner at end of script.
343
343
  _PULSE_DEFAULTS = {}
344
+
345
+ -- ── TypeScript-compatible API ─────────────────────────────────────────────────
346
+ -- SolidJS-style signals + Next.js-style pages compiled from TypeScript via TSTL.
347
+ -- These globals are available in .ts source files without any imports.
348
+
349
+ local _currentComponent = nil
350
+ local _sigCounter = 0
351
+ _Pages = {}
352
+
353
+ local function _signal(default)
354
+ _sigCounter = _sigCounter + 1
355
+ local s = Signal(default)
356
+ s._id = ((_currentComponent and _currentComponent._name) or "g") .. "_s" .. _sigCounter
357
+ s.set = function(_, v) s:set(v) end
358
+ s.toggle = function(_) s:set(not s:get()) end
359
+ s.watch = function(_, fn) return s:onChange(fn) end
360
+ s.update = function(_, fn) s:set(fn(s:get())) end
361
+ return s
362
+ end
363
+
364
+ local function _computed(fn) return Computed(fn) end
365
+
366
+ local function _widgetBase(t)
367
+ function t:bind(sig)
368
+ self.signal = sig
369
+ self.id = sig and (sig._id or "?") or nil
370
+ return self
371
+ end
372
+ function t:withTip(text) self.tip = text; return self end
373
+ function t:withDefault(v) self.default = v; return self end
374
+ return t
375
+ end
376
+
377
+ local function _toggle(lbl, opts)
378
+ return _widgetBase({ type="toggle", label=lbl, tip=(opts and opts.tip) or "" })
379
+ end
380
+ local function _slider(lbl, opts)
381
+ return _widgetBase({ type="slider", label=lbl,
382
+ min=(opts and opts.min) or 0, max=(opts and opts.max) or 100,
383
+ tip=(opts and opts.tip) or "", suffix=(opts and opts.suffix) or "" })
384
+ end
385
+ local function _dropdown(lbl, opts)
386
+ return _widgetBase({ type="dropdown", label=lbl,
387
+ options=(opts and opts.options) or {}, tip=(opts and opts.tip) or "" })
388
+ end
389
+ local function _multidropdown(lbl, opts)
390
+ return _widgetBase({ type="multidropdown", label=lbl,
391
+ options=(opts and opts.options) or {}, tip=(opts and opts.tip) or "" })
392
+ end
393
+ local function _button(lbl, fnOrOpts)
394
+ local fn, tip
395
+ if type(fnOrOpts) == "function" then fn = fnOrOpts
396
+ elseif type(fnOrOpts) == "table" then tip = fnOrOpts.tip end
397
+ local t = { type="button", label=lbl, tip=tip or "", action=fn }
398
+ function t:onClick(cb) self.action = cb; return self end
399
+ function t:bind() return self end
400
+ return t
401
+ end
402
+ local function _keybind(lbl, opts)
403
+ return _widgetBase({ type="keybind", label=lbl,
404
+ key=(opts and opts.key) or "None", tip=(opts and opts.tip) or "" })
405
+ end
406
+ local function _label(text)
407
+ local t = { type="label", label=text }; function t:bind() return self end; return t
408
+ end
409
+ local function _separator()
410
+ local t = { type="separator" }; function t:bind() return self end; return t
411
+ end
412
+
413
+ local function _needComp(name)
414
+ assert(_currentComponent, "on." .. name .. " must be called inside defineComponent()")
415
+ return _currentComponent
416
+ end
417
+
418
+ local function _uid() return tostring(math.random(100000,999999)) end
419
+
420
+ local function _hbBind(svc, optsOrFn, fn)
421
+ local comp = _needComp("event")
422
+ local opts, cb
423
+ if type(optsOrFn) == "function" then cb=optsOrFn; opts={}
424
+ else opts=optsOrFn; cb=fn end
425
+ local when=opts.when; local every=opts.every; local last=0
426
+ comp:bind("ev_".._uid(), svc:Connect(function(a,b)
427
+ if when and not when() then return end
428
+ if every then
429
+ local t=type(every)=="table" and every() or every
430
+ local now=tick(); if (now-last)<t then return end; last=now
431
+ end
432
+ local ok,err=pcall(cb,a,b)
433
+ if not ok then warn("[Pulse] "..comp._name.." event error: "..tostring(err)) end
434
+ end))
435
+ end
436
+
437
+ local _on = {}
438
+ _on.heartbeat = function(a,b) _hbBind(_PulseRS.Heartbeat, a,b) end
439
+ _on.renderStepped = function(a,b) _hbBind(_PulseRS.RenderStepped, a,b) end
440
+ _on.stepped = function(a,b) _hbBind(_PulseRS.Stepped, a,b) end
441
+ _on.inputBegan = function(fn) local c=_needComp("inputBegan"); c:bind("ib_".._uid(), _PulseUIS.InputBegan:Connect(fn)) end
442
+ _on.inputEnded = function(fn) local c=_needComp("inputEnded"); c:bind("ie_".._uid(), _PulseUIS.InputEnded:Connect(fn)) end
443
+ _on.characterAdded = function(fn) local c=_needComp("characterAdded"); c:bind("ca_".._uid(), _LocalPlayer.CharacterAdded:Connect(fn)) end
444
+ _on.characterRemoving = function(fn) local c=_needComp("characterRemoving"); c:bind("cr_".._uid(), _LocalPlayer.CharacterRemoving:Connect(fn)) end
445
+ _on.respawn = function(fn) _needComp("respawn"):onRespawn(fn) end
446
+ _on.signal = function(sig,fn) _needComp("signal"):watch(sig,fn) end
447
+ _on.after = function(s,fn) _needComp("after"):task(s,fn) end
448
+
449
+ local function _defineComponent(name, setup)
450
+ local comp = Component(name)
451
+ _currentComponent = comp
452
+ local ok, widgets = pcall(setup)
453
+ _currentComponent = nil
454
+ if not ok then warn("[Pulse] defineComponent('"..name.."') error: "..tostring(widgets)); widgets={} end
455
+ widgets = widgets or {}
456
+ comp._ui = widgets
457
+ for _, w in ipairs(widgets) do
458
+ if w.default ~= nil and w.signal then
459
+ local kind = w.type == "toggle" and "toggle" or "option"
460
+ _PULSE_DEFAULTS[#_PULSE_DEFAULTS+1] = {
461
+ type=kind, id=w.id or (name.."_?"), value=w.default,
462
+ set=function(v) if w.signal then w.signal.set(nil,v) end end
463
+ }
464
+ end
465
+ end
466
+ return comp
467
+ end
468
+
469
+ local function _groupbox(side, title, opts)
470
+ return { type="groupbox", side=side, title=title,
471
+ icon=(opts and opts.icon) or "",
472
+ mount=(opts and opts.mount) or nil,
473
+ widgets=(opts and opts.widgets) or {} }
474
+ end
475
+
476
+ local function _definePage(title, opts, layoutFn)
477
+ local page = { title=title, icon=(opts and opts.icon) or "circle",
478
+ layout=layoutFn and layoutFn() or {} }
479
+ table.insert(_Pages, page)
480
+ return page
481
+ end
482
+
483
+ -- Expose as globals (available in TypeScript-compiled Lua without imports)
484
+ signal = _signal
485
+ computed = _computed
486
+ defineComponent = _defineComponent
487
+ on = _on
488
+ toggle = _toggle
489
+ slider = _slider
490
+ dropdown = _dropdown
491
+ multidropdown = _multidropdown
492
+ button = _button
493
+ keybind = _keybind
494
+ label = _label
495
+ separator = _separator
496
+ groupbox = _groupbox
497
+ definePage = _definePage
@@ -0,0 +1,15 @@
1
+ defineComponent('PlayerESP', (): WidgetDef[] => {
2
+ const enabled = signal<boolean>(false)
3
+ const showName = signal<boolean>(true)
4
+ const showBox = signal<boolean>(true)
5
+
6
+ on.heartbeat({ when: enabled, every: 0.05 }, (): void => {
7
+ // Draw ESP — use Pulse.UI helpers or drawing APIs here
8
+ })
9
+
10
+ return [
11
+ toggle('Player ESP').bind(enabled),
12
+ toggle('Show Names').bind(showName),
13
+ toggle('Show Boxes').bind(showBox),
14
+ ]
15
+ })
@@ -0,0 +1,17 @@
1
+ defineComponent('FOVChanger', (): WidgetDef[] => {
2
+ const enabled = signal<boolean>(false)
3
+ const fov = signal<number>(70)
4
+
5
+ const applyFov = (): void => {
6
+ const cam = (workspace as any).CurrentCamera
7
+ if (cam) cam.FieldOfView = enabled() ? fov() : 70
8
+ }
9
+
10
+ on.signal(enabled, applyFov)
11
+ on.signal(fov, applyFov)
12
+
13
+ return [
14
+ toggle('FOV Changer').bind(enabled),
15
+ slider('Field of View', { min: 1, max: 120, suffix: '°' }).bind(fov),
16
+ ]
17
+ })
@@ -0,0 +1,19 @@
1
+ defineComponent('SpeedHack', (): WidgetDef[] => {
2
+ const enabled = signal<boolean>(false)
3
+ const speed = signal<number>(16)
4
+
5
+ on.heartbeat({ when: enabled }, (): void => {
6
+ const h = _PulseGetHumanoid() as any
7
+ if (h) h.WalkSpeed = speed()
8
+ })
9
+
10
+ on.respawn((): void => {
11
+ const h = _PulseGetHumanoid() as any
12
+ if (h) h.WalkSpeed = enabled() ? speed() : 16
13
+ })
14
+
15
+ return [
16
+ toggle('Speed Hack').bind(enabled),
17
+ slider('Walk Speed', { min: 16, max: 250 }).bind(speed),
18
+ ]
19
+ })
@@ -1,4 +1,4 @@
1
+ node_modules/
1
2
  build/
2
3
  *.log
3
- __pycache__/
4
4
  .rb-deploy
@@ -0,0 +1,20 @@
1
+ // Pulse layout — configures the script window.
2
+ // Based on Next.js root layout convention.
3
+ // uiLibrary: 'windui' | 'linoria'
4
+
5
+ export default {
6
+ title: '{NAME}',
7
+ author: '',
8
+ toggleKey: 'RightControl',
9
+ size: [850, 560] as [number, number],
10
+ uiLibrary: 'windui' as 'windui' | 'linoria',
11
+ theme: 'Indigo',
12
+ icon: 'code-2',
13
+ folder: '{NAME}',
14
+ acrylic: true,
15
+ transparency: 0.8,
16
+ openButtonMobileOnly: true,
17
+ openButtonIcon: 'code-2',
18
+ themes: [] as LayoutConfig['themes'],
19
+ compatExclude: [] as string[],
20
+ } satisfies LayoutConfig
@@ -0,0 +1,8 @@
1
+ // Home page tab — Next.js-style file-based routing.
2
+ // Filename prefix (1_) determines tab order.
3
+
4
+ definePage('Home', { icon: 'house' }, () => [
5
+ groupbox('left', 'Player', { icon: 'person', mount: 'SpeedHack' }),
6
+ groupbox('left', 'Player', { icon: 'person', mount: 'FOVChanger' }),
7
+ groupbox('right', 'Visuals', { icon: 'eye', mount: 'PlayerESP' }),
8
+ ])
@@ -1,233 +0,0 @@
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
@@ -1,55 +0,0 @@
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
@@ -1,69 +0,0 @@
1
- -- PlayerESP — example component. Customize or delete this file.
2
- component {
3
- signal enabled = false
4
-
5
- init {
6
- Pulse.Notify.onToggle(PlayerESP.enabled, "Player ESP")
7
-
8
- local _CoreGui = game:GetService("CoreGui")
9
- local _Players = game:GetService("Players")
10
- local _highlights = {}
11
-
12
- local function _addHighlight(player)
13
- if not player.Character then return end
14
- if _highlights[player] then return end
15
- local hl = Instance.new("Highlight")
16
- hl.DepthMode = Enum.HighlightDepthMode.AlwaysOnTop
17
- hl.FillColor = Color3.fromRGB(255, 60, 60)
18
- hl.FillTransparency = 0.4
19
- hl.OutlineColor = Color3.fromRGB(255, 60, 60)
20
- hl.Adornee = player.Character
21
- hl.Parent = _CoreGui
22
- _highlights[player] = hl
23
- Pulse.Log.trace("PlayerESP", "highlighted", { name = player.Name })
24
- end
25
-
26
- local function _removeHighlight(player)
27
- if _highlights[player] then
28
- _highlights[player]:Destroy()
29
- _highlights[player] = nil
30
- end
31
- end
32
-
33
- local function _clearAll()
34
- for _, hl in pairs(_highlights) do hl:Destroy() end
35
- _highlights = {}
36
- end
37
-
38
- local _loop = Pulse.Loop.new(1.0, function()
39
- for _, player in ipairs(func.GetCachedPlayers()) do
40
- if player ~= _Players.LocalPlayer then
41
- _addHighlight(player)
42
- end
43
- end
44
- end)
45
-
46
- local function espLoop(value)
47
- if value then
48
- _loop:start()
49
- Pulse.Log.info("PlayerESP", "started")
50
- else
51
- _loop:stop()
52
- _clearAll()
53
- Pulse.Log.info("PlayerESP", "stopped")
54
- end
55
- end
56
- }
57
-
58
- on enabled { espLoop(v) }
59
-
60
- on CharacterAdded {
61
- if not enabled then return end
62
- _clearAll()
63
- _loop:start()
64
- }
65
-
66
- ui {
67
- toggle "Player ESP" -> enabled tip="Highlight all players through walls"
68
- }
69
- }
@@ -1,20 +0,0 @@
1
- -- FOVChanger — example component. Customize or delete this file.
2
- component {
3
- signal enabled = false
4
- signal fov = 90
5
-
6
- init {
7
- Pulse.Notify.onToggle(FOVChanger.enabled, "Custom FOV")
8
- }
9
-
10
- ui {
11
- toggle "Custom FOV" -> enabled
12
- slider "Field of View" -> fov [30, 120]
13
- }
14
-
15
- on enabled, fov {
16
- guard cam = camera
17
- cam.FieldOfView = enabled and fov or 70
18
- Pulse.Log.info("FOVChanger", "fov set", { fov = cam.FieldOfView })
19
- }
20
- }
@@ -1,25 +0,0 @@
1
- -- SpeedHack — example component. Customize or delete this file.
2
- component {
3
- signal enabled = false
4
- signal speed = 50
5
-
6
- init {
7
- Pulse.Notify.onToggle(SpeedHack.enabled, "Speed Hack")
8
- }
9
-
10
- ui {
11
- toggle "Speed Hack" -> enabled tip="Override character walk speed"
12
- slider "Walk Speed" -> speed [16, 250]
13
- }
14
-
15
- on CharacterAdded {
16
- guard h = humanoid
17
- h.WalkSpeed = enabled and speed or 16
18
- }
19
-
20
- on enabled, speed {
21
- guard h = humanoid
22
- h.WalkSpeed = enabled and speed or 16
23
- Pulse.Log.info("SpeedHack", "walk speed set", { speed = h.WalkSpeed })
24
- }
25
- }