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