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,505 @@
|
|
|
1
|
+
-- [adapters/windui.lua]
|
|
2
|
+
-- Wind UI adapter for Pulse components.
|
|
3
|
+
-- Injected by the compiler when ui_library = "windui" in layout.rblua.
|
|
4
|
+
-- Provides _UIAdapter, _PulseNotify, and Pulse.UI (dynamic element builder).
|
|
5
|
+
--
|
|
6
|
+
-- Activation: set ui_library = "windui" in src/ui/layout.rblua
|
|
7
|
+
-- Docs: footagesus.github.io/treehub-web/docs/windui
|
|
8
|
+
-- Version: 1.6.64-fix (pinned)
|
|
9
|
+
|
|
10
|
+
local _WINDUI_URL = "https://github.com/Footagesus/WindUI/releases/download/1.6.64-fix/main.lua"
|
|
11
|
+
local _PULSE_LOGO = "https://pulse-rb.vercel.app/img/logo.svg"
|
|
12
|
+
|
|
13
|
+
local _ok, _src = pcall(game.HttpGet, game, _WINDUI_URL)
|
|
14
|
+
if not _ok then error("[Pulse/WindUI] HTTP failed: " .. tostring(_src)) end
|
|
15
|
+
if type(_src) ~= "string" or #_src < 100 then
|
|
16
|
+
error("[Pulse/WindUI] Empty response from WindUI CDN")
|
|
17
|
+
end
|
|
18
|
+
local _loadFn, _loadErr = loadstring(_src)
|
|
19
|
+
if not _loadFn then error("[Pulse/WindUI] Compile error: " .. tostring(_loadErr)) end
|
|
20
|
+
local _ok2, WindUI = pcall(_loadFn)
|
|
21
|
+
if not _ok2 then error("[Pulse/WindUI] Init failed: " .. tostring(WindUI)) end
|
|
22
|
+
|
|
23
|
+
-- ── Shared state ──────────────────────────────────────────────────────────────
|
|
24
|
+
|
|
25
|
+
local _UIS = game:GetService("UserInputService")
|
|
26
|
+
local _widgets = {} -- [id] → wind element, for programmatic signal sync
|
|
27
|
+
local _mountedGbs = {} -- [component table ref] → groupbox proxy
|
|
28
|
+
local _windWindow = nil -- set in CreateWindow
|
|
29
|
+
local _windConfig = nil -- set in CreateWindow (ConfigManager handle)
|
|
30
|
+
local _folder = "Hub" -- overwritten in CreateWindow from layout opts
|
|
31
|
+
local _notifyTitle = "Hub" -- overwritten in CreateWindow from layout opts
|
|
32
|
+
local _toggleKeyName = "RightControl"
|
|
33
|
+
local _windowVisible = true
|
|
34
|
+
|
|
35
|
+
local _isMobile = _UIS.TouchEnabled and not _UIS.KeyboardEnabled
|
|
36
|
+
|
|
37
|
+
-- ── Adapter ───────────────────────────────────────────────────────────────────
|
|
38
|
+
|
|
39
|
+
local _UIAdapter = {}
|
|
40
|
+
|
|
41
|
+
function _UIAdapter:RegisterTheme(t)
|
|
42
|
+
pcall(function()
|
|
43
|
+
WindUI:AddTheme({
|
|
44
|
+
Name = t.name,
|
|
45
|
+
Accent = Color3.fromHex(t.accent or "#ffffff"),
|
|
46
|
+
Background = Color3.fromHex(t.background or "#1a1a2e"),
|
|
47
|
+
Outline = Color3.fromHex(t.outline or "#2a2a3e"),
|
|
48
|
+
Text = Color3.fromHex(t.text or "#ffffff"),
|
|
49
|
+
Placeholder = Color3.fromHex(t.placeholder or "#888888"),
|
|
50
|
+
Button = Color3.fromHex(t.button or "#2a2a3e"),
|
|
51
|
+
Icon = Color3.fromHex(t.icon or "#ffffff"),
|
|
52
|
+
})
|
|
53
|
+
end)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
function _UIAdapter:GetThemeNames()
|
|
57
|
+
local names = {}
|
|
58
|
+
pcall(function()
|
|
59
|
+
for name, _ in pairs(WindUI:GetThemes()) do
|
|
60
|
+
table.insert(names, name)
|
|
61
|
+
end
|
|
62
|
+
table.sort(names)
|
|
63
|
+
end)
|
|
64
|
+
return next(names) ~= nil and names or nil
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
-- ── Helpers ───────────────────────────────────────────────────────────────────
|
|
68
|
+
|
|
69
|
+
local function _getViewportWidth()
|
|
70
|
+
local ok, vp = pcall(function() return workspace.CurrentCamera.ViewportSize.X end)
|
|
71
|
+
return (ok and type(vp) == "number") and vp or 1920
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
local function _mkColumns(windTab)
|
|
75
|
+
local leftCol, rightCol
|
|
76
|
+
local vw = _getViewportWidth()
|
|
77
|
+
|
|
78
|
+
if not _isMobile and vw >= 960 then
|
|
79
|
+
pcall(function()
|
|
80
|
+
local hs = windTab:HStack({ AutoSpace = true })
|
|
81
|
+
local lv = hs:VStack({})
|
|
82
|
+
local rv = hs:VStack({})
|
|
83
|
+
if lv and rv then
|
|
84
|
+
leftCol = lv
|
|
85
|
+
rightCol = rv
|
|
86
|
+
end
|
|
87
|
+
end)
|
|
88
|
+
else
|
|
89
|
+
pcall(function()
|
|
90
|
+
local col = windTab:VStack({})
|
|
91
|
+
if col then
|
|
92
|
+
leftCol = col
|
|
93
|
+
rightCol = col
|
|
94
|
+
end
|
|
95
|
+
end)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
leftCol = leftCol or windTab
|
|
99
|
+
rightCol = rightCol or windTab
|
|
100
|
+
return leftCol, rightCol
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
-- ── Window proxy ──────────────────────────────────────────────────────────────
|
|
104
|
+
|
|
105
|
+
function _UIAdapter:CreateWindow(title, w, h, opts)
|
|
106
|
+
opts = opts or {}
|
|
107
|
+
_folder = opts.folder or _folder
|
|
108
|
+
_notifyTitle = opts.notify_title or _notifyTitle
|
|
109
|
+
|
|
110
|
+
local icon = (opts.icon and opts.icon ~= "") and opts.icon or _PULSE_LOGO
|
|
111
|
+
local author = (opts.author and opts.author ~= "") and opts.author or nil
|
|
112
|
+
local transparency = opts.transparency or 0
|
|
113
|
+
local acrylic = opts.acrylic == true
|
|
114
|
+
local openBtnMobileOnly = opts.open_btn_mobile_only ~= false
|
|
115
|
+
local openBtnIcon = (opts.open_btn_icon and opts.open_btn_icon ~= "") and opts.open_btn_icon or nil
|
|
116
|
+
|
|
117
|
+
_windWindow = WindUI:CreateWindow({
|
|
118
|
+
Title = title,
|
|
119
|
+
Icon = icon,
|
|
120
|
+
Author = author,
|
|
121
|
+
Folder = _folder,
|
|
122
|
+
Theme = opts.theme or "Dark",
|
|
123
|
+
Resizable = true,
|
|
124
|
+
Size = UDim2.fromOffset(w or 950, h or 600),
|
|
125
|
+
Transparent = acrylic or (transparency > 0),
|
|
126
|
+
BackgroundImageTransparency = acrylic and 0 or transparency,
|
|
127
|
+
Acrylic = acrylic,
|
|
128
|
+
|
|
129
|
+
OpenButton = {
|
|
130
|
+
Enabled = true,
|
|
131
|
+
Draggable = true,
|
|
132
|
+
OnlyMobile = openBtnMobileOnly,
|
|
133
|
+
Icon = openBtnIcon,
|
|
134
|
+
Scale = 0.5,
|
|
135
|
+
CornerRadius = UDim.new(1, 0),
|
|
136
|
+
StrokeThickness = 3,
|
|
137
|
+
},
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
pcall(function()
|
|
141
|
+
_windConfig = _windWindow.ConfigManager:Config(_folder)
|
|
142
|
+
end)
|
|
143
|
+
|
|
144
|
+
_UIAdapter.Window = _windWindow
|
|
145
|
+
_UIAdapter.Config = _windConfig
|
|
146
|
+
|
|
147
|
+
local winProxy = { _wind = _windWindow }
|
|
148
|
+
|
|
149
|
+
function winProxy:AddTab(name, icon)
|
|
150
|
+
local windTab = _windWindow:Tab({
|
|
151
|
+
Title = name,
|
|
152
|
+
Icon = (icon and icon ~= "") and icon or nil,
|
|
153
|
+
})
|
|
154
|
+
if not _UIAdapter._firstTab then
|
|
155
|
+
_UIAdapter._firstTab = windTab
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
local leftCol, rightCol = _mkColumns(windTab)
|
|
159
|
+
|
|
160
|
+
local function _mkSection(col, gbTitle, gbIcon)
|
|
161
|
+
local sect = col:Section({
|
|
162
|
+
Title = gbTitle,
|
|
163
|
+
Icon = (gbIcon and gbIcon ~= "") and gbIcon or nil,
|
|
164
|
+
Box = true,
|
|
165
|
+
BoxBorder = true,
|
|
166
|
+
Opened = true,
|
|
167
|
+
})
|
|
168
|
+
return { _container = sect }
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
local tabProxy = { _wind = windTab }
|
|
172
|
+
function tabProxy:AddLeftGroupbox(title, gbIcon) return _mkSection(leftCol, title, gbIcon) end
|
|
173
|
+
function tabProxy:AddRightGroupbox(title, gbIcon) return _mkSection(rightCol, title, gbIcon) end
|
|
174
|
+
return tabProxy
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
return winProxy
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
function _UIAdapter:ToggleWindow()
|
|
181
|
+
if not _windWindow then return end
|
|
182
|
+
if _windowVisible then
|
|
183
|
+
pcall(_PulseNotify, "Press " .. _toggleKeyName .. " to open", 4)
|
|
184
|
+
end
|
|
185
|
+
pcall(function() _windWindow:Toggle() end)
|
|
186
|
+
_windowVisible = not _windowVisible
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
function _UIAdapter:SetToggleKey(keyCode)
|
|
190
|
+
if keyCode then
|
|
191
|
+
pcall(function() _toggleKeyName = keyCode.Name end)
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
function _UIAdapter:SetTheme(name)
|
|
196
|
+
pcall(function() WindUI:SetTheme(name) end)
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
-- ── Widget helpers ────────────────────────────────────────────────────────────
|
|
200
|
+
|
|
201
|
+
function _UIAdapter:addToggle(gb, id, opts)
|
|
202
|
+
local elem = gb._container:Toggle({
|
|
203
|
+
Title = opts.label,
|
|
204
|
+
Desc = opts.tip or nil,
|
|
205
|
+
Value = opts.signal(),
|
|
206
|
+
Flag = id,
|
|
207
|
+
Callback = function(v) opts.signal(v) end,
|
|
208
|
+
})
|
|
209
|
+
_widgets[id] = elem
|
|
210
|
+
_G.Toggles = _G.Toggles or {}
|
|
211
|
+
_G.Toggles[id] = { Value = opts.signal(), SetValue = function(_, v) opts.signal(v) end }
|
|
212
|
+
opts.signal:onChange(function(v)
|
|
213
|
+
local w = _widgets[id]
|
|
214
|
+
if w then pcall(function() w:Set(v) end) end
|
|
215
|
+
if _G.Toggles and _G.Toggles[id] then _G.Toggles[id].Value = v end
|
|
216
|
+
end)
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
function _UIAdapter:addSlider(gb, id, opts)
|
|
220
|
+
local min = opts.min or 0
|
|
221
|
+
local max = opts.max or 100
|
|
222
|
+
local def = opts.signal()
|
|
223
|
+
local step
|
|
224
|
+
if opts.rounding and opts.rounding > 0 then
|
|
225
|
+
step = 1 / (10 ^ opts.rounding)
|
|
226
|
+
else
|
|
227
|
+
local isFloat = (min % 1 ~= 0) or (max % 1 ~= 0)
|
|
228
|
+
or (type(def) == "number" and def % 1 ~= 0)
|
|
229
|
+
step = isFloat and 0.01 or 1
|
|
230
|
+
end
|
|
231
|
+
local elem = gb._container:Slider({
|
|
232
|
+
Title = opts.label,
|
|
233
|
+
Desc = opts.tip or nil,
|
|
234
|
+
Step = step,
|
|
235
|
+
Value = { Min = min, Max = max, Default = def },
|
|
236
|
+
Flag = id,
|
|
237
|
+
Callback = function(v) opts.signal(v) end,
|
|
238
|
+
})
|
|
239
|
+
_widgets[id] = elem
|
|
240
|
+
_G.Options = _G.Options or {}
|
|
241
|
+
_G.Options[id] = { Value = opts.signal(), SetValue = function(_, v) opts.signal(v) end }
|
|
242
|
+
opts.signal:onChange(function(v)
|
|
243
|
+
local w = _widgets[id]
|
|
244
|
+
if w then pcall(function() w:Set(v) end) end
|
|
245
|
+
if _G.Options and _G.Options[id] then _G.Options[id].Value = v end
|
|
246
|
+
end)
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
function _UIAdapter:addDropdown(gb, id, opts)
|
|
250
|
+
local elem = gb._container:Dropdown({
|
|
251
|
+
Title = opts.label,
|
|
252
|
+
Desc = opts.tip or nil,
|
|
253
|
+
Values = opts.options,
|
|
254
|
+
Value = opts.signal(),
|
|
255
|
+
Flag = id,
|
|
256
|
+
Callback = function(v) opts.signal(v) end,
|
|
257
|
+
})
|
|
258
|
+
_widgets[id] = elem
|
|
259
|
+
_G.Options = _G.Options or {}
|
|
260
|
+
_G.Options[id] = { Value = opts.signal(), SetValue = function(_, v) opts.signal(v) end }
|
|
261
|
+
opts.signal:onChange(function(v)
|
|
262
|
+
local w = _widgets[id]
|
|
263
|
+
if w then pcall(function() w:Select(v) end) end
|
|
264
|
+
if _G.Options and _G.Options[id] then _G.Options[id].Value = v end
|
|
265
|
+
end)
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
function _UIAdapter:addMultiDropdown(gb, id, opts)
|
|
269
|
+
local current = opts.signal()
|
|
270
|
+
local elem = gb._container:Dropdown({
|
|
271
|
+
Title = opts.label,
|
|
272
|
+
Desc = opts.tip or nil,
|
|
273
|
+
Values = opts.options,
|
|
274
|
+
Value = (type(current) == "table" and next(current) ~= nil) and current or nil,
|
|
275
|
+
Multi = true,
|
|
276
|
+
AllowNone = true,
|
|
277
|
+
Flag = id,
|
|
278
|
+
Callback = function(v) opts.signal(v) end,
|
|
279
|
+
})
|
|
280
|
+
_widgets[id] = elem
|
|
281
|
+
opts.signal:onChange(function(v)
|
|
282
|
+
local w = _widgets[id]
|
|
283
|
+
if w then pcall(function() w:Select(v) end) end
|
|
284
|
+
end)
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
function _UIAdapter:addButton(gb, opts)
|
|
288
|
+
gb._container:Button({
|
|
289
|
+
Title = opts.label,
|
|
290
|
+
Desc = opts.tip or nil,
|
|
291
|
+
Callback = opts.action,
|
|
292
|
+
})
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
function _UIAdapter:addKeybind(gb, id, opts)
|
|
296
|
+
local elem = gb._container:Keybind({
|
|
297
|
+
Title = opts.label,
|
|
298
|
+
Value = opts.key,
|
|
299
|
+
Flag = id,
|
|
300
|
+
Callback = function(_v)
|
|
301
|
+
if _UIS:GetFocusedTextBox() then return end
|
|
302
|
+
opts.action()
|
|
303
|
+
end,
|
|
304
|
+
})
|
|
305
|
+
_widgets[id] = elem
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
function _UIAdapter:addLabel(gb, text)
|
|
309
|
+
if text == "" or text:match("^%s*$") then
|
|
310
|
+
gb._container:Space({ Columns = 1 })
|
|
311
|
+
else
|
|
312
|
+
gb._container:Paragraph({ Title = text, Desc = "" })
|
|
313
|
+
end
|
|
314
|
+
end
|
|
315
|
+
|
|
316
|
+
function _UIAdapter:addParagraph(gb, title, desc)
|
|
317
|
+
gb._container:Paragraph({ Title = title or "", Desc = desc or "" })
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
function _UIAdapter:addSeparator(gb)
|
|
321
|
+
gb._container:Divider()
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
-- ── Notify ────────────────────────────────────────────────────────────────────
|
|
325
|
+
|
|
326
|
+
_PulseNotify = function(msg, duration)
|
|
327
|
+
pcall(function()
|
|
328
|
+
WindUI:Notify({
|
|
329
|
+
Title = _notifyTitle,
|
|
330
|
+
Content = msg,
|
|
331
|
+
Duration = duration or 5,
|
|
332
|
+
})
|
|
333
|
+
end)
|
|
334
|
+
end
|
|
335
|
+
|
|
336
|
+
-- ── Destroy ───────────────────────────────────────────────────────────────────
|
|
337
|
+
|
|
338
|
+
_PulseOnDestroy(function()
|
|
339
|
+
pcall(function()
|
|
340
|
+
if _windWindow then _windWindow:Destroy() end
|
|
341
|
+
end)
|
|
342
|
+
end)
|
|
343
|
+
|
|
344
|
+
-- ── Mount ─────────────────────────────────────────────────────────────────────
|
|
345
|
+
|
|
346
|
+
function _UIAdapter:mount(component, gb)
|
|
347
|
+
if not component then return end
|
|
348
|
+
-- Store groupbox so Pulse.UI.gb(component) works in after {} blocks
|
|
349
|
+
_mountedGbs[component] = gb
|
|
350
|
+
local ui = component._ui
|
|
351
|
+
if not ui then return end
|
|
352
|
+
for _, w in ipairs(ui) do
|
|
353
|
+
local t = w.type
|
|
354
|
+
local sig = component[w.signal]
|
|
355
|
+
if t == "toggle" then
|
|
356
|
+
self:addToggle(gb, w.id, { label = w.label, signal = sig, tip = w.tip })
|
|
357
|
+
elseif t == "slider" then
|
|
358
|
+
self:addSlider(gb, w.id, { label = w.label, signal = sig,
|
|
359
|
+
min = w.min, max = w.max, tip = w.tip,
|
|
360
|
+
rounding = w.rounding })
|
|
361
|
+
elseif t == "dropdown" then
|
|
362
|
+
self:addDropdown(gb, w.id, { label = w.label, signal = sig,
|
|
363
|
+
options = w.options, tip = w.tip })
|
|
364
|
+
elseif t == "multidropdown" then
|
|
365
|
+
self:addMultiDropdown(gb, w.id, { label = w.label, signal = sig,
|
|
366
|
+
options = w.options, tip = w.tip })
|
|
367
|
+
elseif t == "button" then
|
|
368
|
+
self:addButton(gb, { label = w.label, action = w.action, tip = w.tip })
|
|
369
|
+
elseif t == "keybind" then
|
|
370
|
+
self:addKeybind(gb, w.id, { label = w.label, key = w.key, action = w.action })
|
|
371
|
+
elseif t == "label" then
|
|
372
|
+
self:addLabel(gb, w.label)
|
|
373
|
+
elseif t == "paragraph" then
|
|
374
|
+
self:addParagraph(gb, w.title, w.desc)
|
|
375
|
+
elseif t == "separator" then
|
|
376
|
+
self:addSeparator(gb)
|
|
377
|
+
end
|
|
378
|
+
end
|
|
379
|
+
end
|
|
380
|
+
|
|
381
|
+
-- ── Pulse.UI — imperative dynamic UI builder ──────────────────────────────────
|
|
382
|
+
-- Use in after {} blocks (runs after mount, so groupbox is available).
|
|
383
|
+
-- All functions return a handle with :set / :setTitle / :setDesc / :refresh /
|
|
384
|
+
-- :destroy methods. All mutations are nil-safe (pcall-wrapped).
|
|
385
|
+
--
|
|
386
|
+
-- Quick reference:
|
|
387
|
+
-- local gb = Pulse.UI.gb(MyComponent)
|
|
388
|
+
-- local par = Pulse.UI.paragraph(gb, "Title", "desc") par:setDesc("new")
|
|
389
|
+
-- local btn = Pulse.UI.button(gb, "Label", "desc", fn)
|
|
390
|
+
-- local tog = Pulse.UI.toggle(gb, "id", "Label", "desc", false, fn)
|
|
391
|
+
-- local sli = Pulse.UI.slider(gb, "id", "Label", 0, 100, 50, fn)
|
|
392
|
+
-- local drp = Pulse.UI.dropdown(gb, "id", "Label", {"A"}, nil, false, fn)
|
|
393
|
+
-- drp:refresh({"A","B"}) drp:set("B")
|
|
394
|
+
-- local sec = Pulse.UI.section(gb, "Title", "icon-name")
|
|
395
|
+
-- sec:paragraph(...) sec:button(...) etc.
|
|
396
|
+
|
|
397
|
+
Pulse.UI = {}
|
|
398
|
+
|
|
399
|
+
function Pulse.UI.gb(component)
|
|
400
|
+
return _mountedGbs[component]
|
|
401
|
+
end
|
|
402
|
+
|
|
403
|
+
function Pulse.UI.paragraph(container, title, desc)
|
|
404
|
+
local elem
|
|
405
|
+
pcall(function()
|
|
406
|
+
elem = container._container:Paragraph({ Title = title or "", Desc = desc or "" })
|
|
407
|
+
end)
|
|
408
|
+
local h = {}
|
|
409
|
+
function h:set(t, d)
|
|
410
|
+
pcall(function()
|
|
411
|
+
if t ~= nil then elem:SetTitle(t) end
|
|
412
|
+
if d ~= nil then elem:SetDesc(d) end
|
|
413
|
+
end)
|
|
414
|
+
end
|
|
415
|
+
function h:setTitle(v) pcall(function() elem:SetTitle(v) end) end
|
|
416
|
+
function h:setDesc(v) pcall(function() elem:SetDesc(v) end) end
|
|
417
|
+
function h:destroy() pcall(function() elem:Destroy() end) end
|
|
418
|
+
return h
|
|
419
|
+
end
|
|
420
|
+
|
|
421
|
+
function Pulse.UI.button(container, label, desc, fn)
|
|
422
|
+
local elem
|
|
423
|
+
pcall(function()
|
|
424
|
+
elem = container._container:Button({ Title = label, Desc = desc, Callback = fn })
|
|
425
|
+
end)
|
|
426
|
+
local h = {}
|
|
427
|
+
function h:setTitle(v) pcall(function() elem:SetTitle(v) end) end
|
|
428
|
+
function h:setDesc(v) pcall(function() elem:SetDesc(v) end) end
|
|
429
|
+
function h:destroy() pcall(function() elem:Destroy() end) end
|
|
430
|
+
return h
|
|
431
|
+
end
|
|
432
|
+
|
|
433
|
+
function Pulse.UI.toggle(container, id, label, desc, default, fn)
|
|
434
|
+
local elem
|
|
435
|
+
pcall(function()
|
|
436
|
+
elem = container._container:Toggle({
|
|
437
|
+
Title = label, Desc = desc, Value = default or false,
|
|
438
|
+
Flag = id, Callback = fn,
|
|
439
|
+
})
|
|
440
|
+
end)
|
|
441
|
+
local h = {}
|
|
442
|
+
function h:set(v) pcall(function() elem:Set(v) end) end
|
|
443
|
+
function h:setDesc(v) pcall(function() elem:SetDesc(v) end) end
|
|
444
|
+
function h:destroy() pcall(function() elem:Destroy() end) end
|
|
445
|
+
return h
|
|
446
|
+
end
|
|
447
|
+
|
|
448
|
+
function Pulse.UI.slider(container, id, label, min, max, default, fn)
|
|
449
|
+
local elem
|
|
450
|
+
pcall(function()
|
|
451
|
+
local mn, mx = min or 0, max or 100
|
|
452
|
+
elem = container._container:Slider({
|
|
453
|
+
Title = label, Step = 1,
|
|
454
|
+
Value = { Min = mn, Max = mx, Default = default or mn },
|
|
455
|
+
Flag = id, Callback = fn,
|
|
456
|
+
})
|
|
457
|
+
end)
|
|
458
|
+
local h = {}
|
|
459
|
+
function h:set(v) pcall(function() elem:Set(v) end) end
|
|
460
|
+
function h:setDesc(v) pcall(function() elem:SetDesc(v) end) end
|
|
461
|
+
function h:destroy() pcall(function() elem:Destroy() end) end
|
|
462
|
+
return h
|
|
463
|
+
end
|
|
464
|
+
|
|
465
|
+
function Pulse.UI.dropdown(container, id, label, values, default, multi, fn)
|
|
466
|
+
local elem
|
|
467
|
+
pcall(function()
|
|
468
|
+
elem = container._container:Dropdown({
|
|
469
|
+
Title = label, Values = values or {}, Value = default,
|
|
470
|
+
Multi = multi or false, AllowNone = multi or false,
|
|
471
|
+
Flag = id, Callback = fn,
|
|
472
|
+
})
|
|
473
|
+
end)
|
|
474
|
+
local h = {}
|
|
475
|
+
function h:set(v) pcall(function() elem:Select(v) end) end
|
|
476
|
+
function h:refresh(vs) pcall(function() elem:Refresh(vs) end) end
|
|
477
|
+
function h:setDesc(v) pcall(function() elem:SetDesc(v) end) end
|
|
478
|
+
function h:destroy() pcall(function() elem:Destroy() end) end
|
|
479
|
+
return h
|
|
480
|
+
end
|
|
481
|
+
|
|
482
|
+
function Pulse.UI.divider(container)
|
|
483
|
+
pcall(function() container._container:Divider() end)
|
|
484
|
+
end
|
|
485
|
+
|
|
486
|
+
-- Creates a nested section inside a groupbox. Returns a proxy with the same
|
|
487
|
+
-- methods as a groupbox so you can chain: sec:paragraph(), sec:button(), etc.
|
|
488
|
+
function Pulse.UI.section(container, title, icon, opened)
|
|
489
|
+
local sect
|
|
490
|
+
pcall(function()
|
|
491
|
+
sect = container._container:Section({
|
|
492
|
+
Title = title or "", Icon = (icon and icon ~= "") and icon or nil,
|
|
493
|
+
Box = true, BoxBorder = true, Opened = opened ~= false,
|
|
494
|
+
})
|
|
495
|
+
end)
|
|
496
|
+
local proxy = { _container = sect }
|
|
497
|
+
function proxy:paragraph(...) return Pulse.UI.paragraph(self, ...) end
|
|
498
|
+
function proxy:button(...) return Pulse.UI.button(self, ...) end
|
|
499
|
+
function proxy:toggle(...) return Pulse.UI.toggle(self, ...) end
|
|
500
|
+
function proxy:slider(...) return Pulse.UI.slider(self, ...) end
|
|
501
|
+
function proxy:dropdown(...) return Pulse.UI.dropdown(self, ...) end
|
|
502
|
+
function proxy:divider() Pulse.UI.divider(self) end
|
|
503
|
+
function proxy:section(...) return Pulse.UI.section(self, ...) end
|
|
504
|
+
return proxy
|
|
505
|
+
end
|
package/bin/rb.js
ADDED