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/adapters/windui.lua +8 -4
- package/dist/index.js +265 -221
- package/package.json +3 -2
- package/pulse/runtime.lua +154 -0
- package/templates/component_esp.ts +15 -0
- package/templates/component_fov.ts +17 -0
- package/templates/component_speed.ts +19 -0
- package/templates/gitignore +1 -1
- package/templates/layout.ts +20 -0
- package/templates/page_home.ts +8 -0
- package/adapters/linoria.lua +0 -233
- package/pulse/ui/linoria_settings.lua +0 -55
- package/templates/example_esp.rblua +0 -69
- package/templates/example_fov.rblua +0 -20
- package/templates/example_speed.rblua +0 -25
- package/templates/layout.rblua +0 -28
- package/templates/module.rblua +0 -32
- package/templates/page_home.rblua +0 -9
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pulse-rb",
|
|
3
|
-
"version": "1.
|
|
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.
|
|
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
|
+
})
|
package/templates/gitignore
CHANGED
|
@@ -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
|
+
])
|
package/adapters/linoria.lua
DELETED
|
@@ -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
|
-
}
|