@rb-pulse/core 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/index.ts +399 -0
- package/package.json +18 -0
package/index.ts
ADDED
|
@@ -0,0 +1,399 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @pulse/core — Component, signal, and event API for Pulse framework scripts.
|
|
3
|
+
*
|
|
4
|
+
* Write game scripts in TypeScript; compile to Lua via `rb build`.
|
|
5
|
+
* Every API maps 1-to-1 to the Pulse Lua runtime.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* import { defineComponent, signal, on } from '@pulse/core'
|
|
10
|
+
* import { toggle, slider } from '@pulse/ui'
|
|
11
|
+
*
|
|
12
|
+
* export const KillAura = defineComponent(() => {
|
|
13
|
+
* const enabled = signal(false)
|
|
14
|
+
* const range = signal(50)
|
|
15
|
+
*
|
|
16
|
+
* on.heartbeat({ when: enabled, every: 0.1 }, dt => {
|
|
17
|
+
* // runs every 0.1 s while enabled === true
|
|
18
|
+
* })
|
|
19
|
+
*
|
|
20
|
+
* return [
|
|
21
|
+
* toggle('Kill Aura').bind(enabled),
|
|
22
|
+
* slider('Range', { min: 1, max: 500 }).bind(range),
|
|
23
|
+
* ]
|
|
24
|
+
* })
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
// ── Signal ─────────────────────────────────────────────────────────────────────
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Reactive state cell returned by `signal()`.
|
|
32
|
+
*
|
|
33
|
+
* Call it to read the current value; use `.set()` to write.
|
|
34
|
+
* Changes automatically propagate to UI widgets, event guards, and watchers.
|
|
35
|
+
*
|
|
36
|
+
* Compiles to: `Signal(initialValue)` in Lua.
|
|
37
|
+
*/
|
|
38
|
+
export interface Signal<T> {
|
|
39
|
+
/** Read the current value. Compiles to: `signalName()` */
|
|
40
|
+
(): T
|
|
41
|
+
/**
|
|
42
|
+
* Write a new value. Triggers UI, handler, and watcher updates.
|
|
43
|
+
* Compiles to: `signalName.set(value)`
|
|
44
|
+
*/
|
|
45
|
+
set(value: T): void
|
|
46
|
+
/**
|
|
47
|
+
* Toggle the signal value (boolean signals only).
|
|
48
|
+
* Equivalent to `signal.set(!signal())`.
|
|
49
|
+
* Compiles to: `signalName.toggle()`
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* const enabled = signal(false)
|
|
53
|
+
* button('Toggle').onClick(() => enabled.toggle())
|
|
54
|
+
*/
|
|
55
|
+
toggle(this: Signal<boolean>): void
|
|
56
|
+
/**
|
|
57
|
+
* Subscribe to value changes. The callback fires immediately with the
|
|
58
|
+
* current value, then on every subsequent change.
|
|
59
|
+
* Returns an unsubscribe function.
|
|
60
|
+
* Compiles to: `signalName.watch(fn)`
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* const unsubscribe = enabled.watch(v => {
|
|
64
|
+
* print('enabled changed to', v)
|
|
65
|
+
* })
|
|
66
|
+
* // later: unsubscribe()
|
|
67
|
+
*/
|
|
68
|
+
watch(fn: (value: T) => void): () => void
|
|
69
|
+
/**
|
|
70
|
+
* Mirror this signal's value into another signal on every change.
|
|
71
|
+
* Useful for propagating values across component boundaries.
|
|
72
|
+
* Compiles to: `signalName.mirror(target)`
|
|
73
|
+
*/
|
|
74
|
+
mirror(target: Signal<T>): void
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Derived signal — recomputes when any signal read inside `fn` changes.
|
|
79
|
+
* Read-only; no `.set()`.
|
|
80
|
+
*
|
|
81
|
+
* Compiles to: `Computed(fn)` in Lua.
|
|
82
|
+
*/
|
|
83
|
+
export interface Computed<T> {
|
|
84
|
+
(): T
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// ── Event options ──────────────────────────────────────────────────────────────
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Options for periodic event handlers (`on.heartbeat`, `on.renderStepped`, `on.stepped`).
|
|
91
|
+
* Both fields are optional — omit entirely to run every frame with no guard.
|
|
92
|
+
*/
|
|
93
|
+
export interface HandlerOpts {
|
|
94
|
+
/**
|
|
95
|
+
* Gate: skip the callback while this signal is `false`.
|
|
96
|
+
* The event connection stays alive but the callback is skipped.
|
|
97
|
+
* Compiles to: `when = signalName` in the event binding.
|
|
98
|
+
*/
|
|
99
|
+
when?: Signal<boolean>
|
|
100
|
+
/**
|
|
101
|
+
* Throttle: minimum seconds between callback invocations.
|
|
102
|
+
* Uses an internal accumulator — does not create extra connections.
|
|
103
|
+
* Accepts a number or a reactive `Signal<number>` for live adjustment.
|
|
104
|
+
* Compiles to: `every = value` in the event binding.
|
|
105
|
+
*
|
|
106
|
+
* @default 0 (every frame)
|
|
107
|
+
*/
|
|
108
|
+
every?: number | Signal<number>
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// ── Widget base ────────────────────────────────────────────────────────────────
|
|
112
|
+
|
|
113
|
+
/** Base interface shared by all widget definitions returned from `defineComponent`. */
|
|
114
|
+
export interface WidgetDef {
|
|
115
|
+
/**
|
|
116
|
+
* Bind this widget to a signal so their values stay in sync.
|
|
117
|
+
* The widget writes the signal on user interaction; the signal
|
|
118
|
+
* drives the widget when set programmatically.
|
|
119
|
+
*/
|
|
120
|
+
bind(signal: Signal<any>): this
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// ── Component ──────────────────────────────────────────────────────────────────
|
|
124
|
+
|
|
125
|
+
/** Setup function type for `defineComponent`. */
|
|
126
|
+
export type ComponentSetup = () => WidgetDef[]
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Opaque component handle returned by `defineComponent`.
|
|
130
|
+
* Pass it to `groupbox({ mount: MyComponent })` to embed its widgets.
|
|
131
|
+
*/
|
|
132
|
+
export interface Component<S extends ComponentSetup> {
|
|
133
|
+
readonly _type: 'component'
|
|
134
|
+
readonly _widgets: ReturnType<S>
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// ── Core API ───────────────────────────────────────────────────────────────────
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Define a Pulse component — the primary building block.
|
|
141
|
+
*
|
|
142
|
+
* The setup function runs once on initialisation.
|
|
143
|
+
* Declare signals, register event handlers with `on.*`, then return an array
|
|
144
|
+
* of widget definitions to mount into the component's groupbox.
|
|
145
|
+
* All `on.*` subscriptions are auto-disconnected when the component is destroyed.
|
|
146
|
+
*
|
|
147
|
+
* Compiles to a self-contained Lua IIFE with Signal/bind wiring.
|
|
148
|
+
*
|
|
149
|
+
* @param setup Called once at init. Returns UI widget array.
|
|
150
|
+
*
|
|
151
|
+
* @example
|
|
152
|
+
* export const SpeedHack = defineComponent(() => {
|
|
153
|
+
* const enabled = signal(false)
|
|
154
|
+
* const speed = signal(16)
|
|
155
|
+
*
|
|
156
|
+
* on.heartbeat({ when: enabled }, () => {
|
|
157
|
+
* // manipulate character walkspeed
|
|
158
|
+
* })
|
|
159
|
+
*
|
|
160
|
+
* on.respawn(() => {
|
|
161
|
+
* if (enabled()) applySpeed(speed())
|
|
162
|
+
* })
|
|
163
|
+
*
|
|
164
|
+
* return [
|
|
165
|
+
* toggle('Speed hack').bind(enabled),
|
|
166
|
+
* slider('Speed', { min: 1, max: 250, suffix: ' studs/s' }).bind(speed),
|
|
167
|
+
* ]
|
|
168
|
+
* })
|
|
169
|
+
*/
|
|
170
|
+
export declare function defineComponent<S extends ComponentSetup>(setup: S): Component<S>
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Create a reactive state cell with an initial value.
|
|
174
|
+
*
|
|
175
|
+
* Compiles to: `Signal(initialValue)` in Lua.
|
|
176
|
+
*
|
|
177
|
+
* @param initialValue The starting value.
|
|
178
|
+
*
|
|
179
|
+
* @example
|
|
180
|
+
* const enabled = signal(false) // Signal<boolean>
|
|
181
|
+
* const range = signal(50) // Signal<number>
|
|
182
|
+
* const mode = signal('Auto') // Signal<string>
|
|
183
|
+
*
|
|
184
|
+
* enabled() // read → false
|
|
185
|
+
* enabled.set(true) // write
|
|
186
|
+
* enabled.toggle() // flip boolean
|
|
187
|
+
* enabled.watch(v => print(v)) // subscribe
|
|
188
|
+
*/
|
|
189
|
+
export declare function signal<T>(initialValue: T): Signal<T>
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Create a derived signal that recomputes automatically when its dependencies change.
|
|
193
|
+
*
|
|
194
|
+
* Compiles to: `Computed(fn)` in Lua.
|
|
195
|
+
*
|
|
196
|
+
* @param fn Pure function reading one or more signals. Must not have side effects.
|
|
197
|
+
*
|
|
198
|
+
* @example
|
|
199
|
+
* const fov = signal(90)
|
|
200
|
+
* const label = computed(() => `FOV: ${fov()}`)
|
|
201
|
+
*
|
|
202
|
+
* // Use in a toggle tooltip or paragraph widget
|
|
203
|
+
* paragraph('Current FOV', label())
|
|
204
|
+
*/
|
|
205
|
+
export declare function computed<T>(fn: () => T): Computed<T>
|
|
206
|
+
|
|
207
|
+
// ── Event namespace ────────────────────────────────────────────────────────────
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Event subscription namespace.
|
|
211
|
+
* All connections are automatically cleaned up when the component is destroyed.
|
|
212
|
+
*
|
|
213
|
+
* @example
|
|
214
|
+
* on.heartbeat({ when: enabled, every: 0.1 }, dt => { ... })
|
|
215
|
+
* on.heartbeat(dt => updateDisplay(dt)) // no opts overload
|
|
216
|
+
* on.signal(range, v => applyRange(v)) // signal watcher
|
|
217
|
+
* on.inputBegan(input => handleKey(input))
|
|
218
|
+
* on.respawn(() => reapplyEffects())
|
|
219
|
+
* on.after(0.5, () => init()) // delayed one-shot
|
|
220
|
+
*/
|
|
221
|
+
export declare const on: {
|
|
222
|
+
/**
|
|
223
|
+
* RunService.Heartbeat — fires every frame.
|
|
224
|
+
*
|
|
225
|
+
* Compiles to: `on Heartbeat { when = ..., every = ... }` in .rblua.
|
|
226
|
+
*
|
|
227
|
+
* @param opts Optional `when` guard and `every` throttle.
|
|
228
|
+
* @param fn Callback invoked each (throttled) tick. Receives delta-time.
|
|
229
|
+
*
|
|
230
|
+
* @example
|
|
231
|
+
* // Throttled, gated
|
|
232
|
+
* on.heartbeat({ when: enabled, every: 0.1 }, dt => {
|
|
233
|
+
* doWork()
|
|
234
|
+
* })
|
|
235
|
+
*
|
|
236
|
+
* // Every frame, no guard
|
|
237
|
+
* on.heartbeat(dt => updateDisplay(dt))
|
|
238
|
+
*/
|
|
239
|
+
heartbeat(opts: HandlerOpts, fn: (dt: number) => void): void
|
|
240
|
+
heartbeat(fn: (dt: number) => void): void
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* RunService.RenderStepped — fires before every frame render (client only).
|
|
244
|
+
* Prefer `heartbeat` for game logic; use this for camera / visual work.
|
|
245
|
+
*
|
|
246
|
+
* @example
|
|
247
|
+
* on.renderStepped({ when: espEnabled }, dt => drawBoxes(dt))
|
|
248
|
+
* on.renderStepped(dt => updateCamera(dt))
|
|
249
|
+
*/
|
|
250
|
+
renderStepped(opts: HandlerOpts, fn: (dt: number) => void): void
|
|
251
|
+
renderStepped(fn: (dt: number) => void): void
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* RunService.Stepped — fires at the physics step rate.
|
|
255
|
+
*
|
|
256
|
+
* @example
|
|
257
|
+
* on.stepped({ when: enabled }, (t, dt) => applyVelocity(dt))
|
|
258
|
+
*/
|
|
259
|
+
stepped(opts: HandlerOpts, fn: (time: number, dt: number) => void): void
|
|
260
|
+
stepped(fn: (time: number, dt: number) => void): void
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* UserInputService.InputBegan.
|
|
264
|
+
*
|
|
265
|
+
* @example
|
|
266
|
+
* on.inputBegan((input, gpe) => {
|
|
267
|
+
* if (gpe) return
|
|
268
|
+
* if (input.KeyCode === Enum.KeyCode.X) enabled.toggle()
|
|
269
|
+
* })
|
|
270
|
+
*/
|
|
271
|
+
inputBegan(fn: (input: InputObject, gameProcessed: boolean) => void): void
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* UserInputService.InputEnded.
|
|
275
|
+
*
|
|
276
|
+
* @example
|
|
277
|
+
* on.inputEnded(input => {
|
|
278
|
+
* if (input.UserInputType === Enum.UserInputType.MouseButton1) release()
|
|
279
|
+
* })
|
|
280
|
+
*/
|
|
281
|
+
inputEnded(fn: (input: InputObject, gameProcessed: boolean) => void): void
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Fires when the player's character respawns.
|
|
285
|
+
* Use this to re-apply effects that attach to the character.
|
|
286
|
+
*
|
|
287
|
+
* @example
|
|
288
|
+
* on.respawn(() => {
|
|
289
|
+
* if (enabled()) applySpeed(speed())
|
|
290
|
+
* })
|
|
291
|
+
*/
|
|
292
|
+
respawn(fn: () => void): void
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Player.CharacterAdded — fires when a new character model is added.
|
|
296
|
+
*
|
|
297
|
+
* @example
|
|
298
|
+
* on.characterAdded(char => {
|
|
299
|
+
* const hrp = char.WaitForChild('HumanoidRootPart') as BasePart
|
|
300
|
+
* })
|
|
301
|
+
*/
|
|
302
|
+
characterAdded(fn: (character: Model) => void): void
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Player.CharacterRemoving — fires just before the character is removed.
|
|
306
|
+
*
|
|
307
|
+
* @example
|
|
308
|
+
* on.characterRemoving(() => cleanup())
|
|
309
|
+
*/
|
|
310
|
+
characterRemoving(fn: (character: Model) => void): void
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Subscribe to changes on a signal — fires immediately with the current value,
|
|
314
|
+
* then on every subsequent change. Part of component lifecycle (auto-disconnected).
|
|
315
|
+
*
|
|
316
|
+
* Compiles to: `on signalName { }` signal handler in .rblua.
|
|
317
|
+
*
|
|
318
|
+
* @param sig The signal to watch.
|
|
319
|
+
* @param fn Called with the new value on every change.
|
|
320
|
+
*
|
|
321
|
+
* @example
|
|
322
|
+
* on.signal(enabled, v => {
|
|
323
|
+
* if (v) activateESP() else deactivateESP()
|
|
324
|
+
* })
|
|
325
|
+
*
|
|
326
|
+
* on.signal(range, v => updateAimRadius(v))
|
|
327
|
+
*/
|
|
328
|
+
signal<T>(sig: Signal<T>, fn: (val: T) => void): void
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* Run a one-shot callback after a delay.
|
|
332
|
+
* Runs inside the component's init phase — useful for deferred setup.
|
|
333
|
+
* Compiles to: `task.delay(seconds, fn)` wrapped in component lifecycle.
|
|
334
|
+
*
|
|
335
|
+
* @param seconds Delay in seconds (0.5 is typical for post-mount init).
|
|
336
|
+
* @param fn Callback to run once.
|
|
337
|
+
*
|
|
338
|
+
* @example
|
|
339
|
+
* on.after(0.5, () => {
|
|
340
|
+
* // safe to read UI state here — everything is mounted
|
|
341
|
+
* Pulse.UI.paragraph('Info', 'Loaded!')
|
|
342
|
+
* })
|
|
343
|
+
*/
|
|
344
|
+
after(seconds: number, fn: () => void): void
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// ── Roblox type stubs ──────────────────────────────────────────────────────────
|
|
348
|
+
// Minimal stubs so editors don't error. Install @pulse/roblox for full types.
|
|
349
|
+
|
|
350
|
+
/** @see @pulse/roblox */
|
|
351
|
+
export interface InputObject {
|
|
352
|
+
KeyCode: { Value: number; Name: string }
|
|
353
|
+
UserInputType: { Value: number; Name: string }
|
|
354
|
+
Position: Vector3
|
|
355
|
+
Delta: Vector3
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/** @see @pulse/roblox */
|
|
359
|
+
export interface Model {
|
|
360
|
+
WaitForChild(name: string, timeout?: number): Instance
|
|
361
|
+
FindFirstChild(name: string): Instance | undefined
|
|
362
|
+
FindFirstChildOfClass(className: string): Instance | undefined
|
|
363
|
+
GetChildren(): Instance[]
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/** @see @pulse/roblox */
|
|
367
|
+
export interface Instance {
|
|
368
|
+
Name: string
|
|
369
|
+
Parent: Instance | undefined
|
|
370
|
+
Destroy(): void
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/** @see @pulse/roblox */
|
|
374
|
+
export interface BasePart extends Instance {
|
|
375
|
+
Position: Vector3
|
|
376
|
+
CFrame: CFrame
|
|
377
|
+
Size: Vector3
|
|
378
|
+
Anchored: boolean
|
|
379
|
+
CanCollide: boolean
|
|
380
|
+
Transparency: number
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
/** @see @pulse/roblox */
|
|
384
|
+
export interface Vector3 {
|
|
385
|
+
X: number; Y: number; Z: number
|
|
386
|
+
Magnitude: number
|
|
387
|
+
Unit: Vector3
|
|
388
|
+
add(other: Vector3): Vector3
|
|
389
|
+
sub(other: Vector3): Vector3
|
|
390
|
+
mul(scalar: number): Vector3
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
/** @see @pulse/roblox */
|
|
394
|
+
export interface CFrame {
|
|
395
|
+
Position: Vector3
|
|
396
|
+
LookVector: Vector3
|
|
397
|
+
RightVector: Vector3
|
|
398
|
+
UpVector: Vector3
|
|
399
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@rb-pulse/core",
|
|
3
|
+
"version": "1.2.24",
|
|
4
|
+
"description": "Pulse framework — component, signal, and event TypeScript API",
|
|
5
|
+
"main": "./index.ts",
|
|
6
|
+
"types": "./index.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"index.ts",
|
|
9
|
+
"*.d.ts"
|
|
10
|
+
],
|
|
11
|
+
"keywords": [
|
|
12
|
+
"roblox",
|
|
13
|
+
"pulse",
|
|
14
|
+
"lua",
|
|
15
|
+
"typescript-to-lua"
|
|
16
|
+
],
|
|
17
|
+
"license": "MIT"
|
|
18
|
+
}
|