@rb-pulse/core 1.2.24 → 1.3.0

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.
Files changed (2) hide show
  1. package/index.ts +296 -345
  2. package/package.json +1 -1
package/index.ts CHANGED
@@ -1,399 +1,350 @@
1
1
  /**
2
- * @pulse/core — Component, signal, and event API for Pulse framework scripts.
2
+ * @pulse/core — Global ambient declarations for Pulse framework TypeScript scripts.
3
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.
4
+ * Mental model:
5
+ * - SolidJS-style reactive signals: `signal(default)` returns a PulseSignal<T>.
6
+ * Call it to read, `.set()` to write, `.watch()` to subscribe — just like
7
+ * SolidJS `createSignal` but without the getter/setter tuple split.
8
+ * - Next.js-style file-based pages: place `src/pages/1_Home.ts`, `2_Combat.ts`…
9
+ * Each file calls `definePage(...)` at the top level — tab order is determined
10
+ * by the numeric filename prefix, just like Next.js routes.
11
+ * - Components are SolidJS-style setup functions: call `defineComponent(name, setup)`
12
+ * where `setup` declares signals, registers `on.*` handlers, and returns widgets.
13
+ * - All APIs are Lua globals — no imports needed. TypeScript sees them via this
14
+ * `declare global {}` block. TSTL will NOT emit any `require()` calls.
6
15
  *
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.
16
+ * Compiled to Lua via typescript-to-lua (TSTL): `rb build` handles this for you.
150
17
  *
151
18
  * @example
152
- * export const SpeedHack = defineComponent(() => {
19
+ * // src/combat/SpeedHack.ts
20
+ * defineComponent('SpeedHack', () => {
153
21
  * const enabled = signal(false)
154
22
  * const speed = signal(16)
155
- *
156
23
  * on.heartbeat({ when: enabled }, () => {
157
- * // manipulate character walkspeed
158
- * })
159
- *
160
- * on.respawn(() => {
161
- * if (enabled()) applySpeed(speed())
24
+ * const h = _PulseGetHumanoid() as any
25
+ * if (h) h.WalkSpeed = speed()
162
26
  * })
163
- *
164
27
  * return [
165
- * toggle('Speed hack').bind(enabled),
166
- * slider('Speed', { min: 1, max: 250, suffix: ' studs/s' }).bind(speed),
28
+ * toggle('Speed Hack').bind(enabled),
29
+ * slider('Walk Speed', { min: 16, max: 250 }).bind(speed),
167
30
  * ]
168
31
  * })
169
32
  */
170
- export declare function defineComponent<S extends ComponentSetup>(setup: S): Component<S>
171
33
 
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>
34
+ declare global {
190
35
 
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>
36
+ // ── Signal ────────────────────────────────────────────────────────────────────
206
37
 
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
38
  /**
223
- * RunService.Heartbeat fires every frame.
224
- *
225
- * Compiles to: `on Heartbeat { when = ..., every = ... }` in .rblua.
39
+ * Reactive state cell returned by `signal()`.
226
40
  *
227
- * @param opts Optional `when` guard and `every` throttle.
228
- * @param fn Callback invoked each (throttled) tick. Receives delta-time.
41
+ * Call it to read the current value. Changes propagate to UI widgets,
42
+ * event guards, and watchers automatically.
229
43
  *
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))
44
+ * Like SolidJS `createSignal` but without the getter/setter tuple split.
238
45
  */
239
- heartbeat(opts: HandlerOpts, fn: (dt: number) => void): void
240
- heartbeat(fn: (dt: number) => void): void
46
+ interface PulseSignal<T> {
47
+ /** Read the current value. */
48
+ (): T
49
+ /** Write a new value. Triggers UI, handler, and watcher updates. */
50
+ set(this: PulseSignal<T>, value: T): void
51
+ /**
52
+ * Toggle the signal value (boolean signals only).
53
+ * Equivalent to `signal.set(!signal())`.
54
+ */
55
+ toggle(this: PulseSignal<boolean>): void
56
+ /**
57
+ * Subscribe to value changes. Returns an unsubscribe function.
58
+ * Fires immediately with the current value, then on every change.
59
+ */
60
+ watch(this: PulseSignal<T>, fn: (value: T) => void): () => void
61
+ /**
62
+ * Apply a function to the current value and set the result.
63
+ * @example speed.update(v => v + 10)
64
+ */
65
+ update(this: PulseSignal<T>, fn: (current: T) => T): void
66
+ }
67
+
68
+ // ── Event options ─────────────────────────────────────────────────────────────
241
69
 
242
70
  /**
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))
71
+ * Options for periodic event handlers (`on.heartbeat`, `on.renderStepped`, `on.stepped`).
72
+ * Both fields are optional omit entirely to run every frame with no guard.
249
73
  */
250
- renderStepped(opts: HandlerOpts, fn: (dt: number) => void): void
251
- renderStepped(fn: (dt: number) => void): void
74
+ interface HandlerOpts {
75
+ /**
76
+ * Gate: skip the callback while this signal is `false`.
77
+ * The event connection stays alive but the callback is skipped.
78
+ */
79
+ when?: PulseSignal<boolean>
80
+ /**
81
+ * Throttle: minimum seconds between callback invocations.
82
+ * Accepts a number or a reactive `PulseSignal<number>` for live adjustment.
83
+ * @default 0 (every frame)
84
+ */
85
+ every?: number | PulseSignal<number>
86
+ }
87
+
88
+ // ── Widget definitions ────────────────────────────────────────────────────────
89
+
90
+ interface ToggleDef {
91
+ bind(this: ToggleDef, sig: PulseSignal<boolean>): ToggleDef
92
+ withTip(this: ToggleDef, text: string): ToggleDef
93
+ withDefault(this: ToggleDef, v: boolean): ToggleDef
94
+ }
95
+
96
+ interface SliderDef {
97
+ bind(this: SliderDef, sig: PulseSignal<number>): SliderDef
98
+ withTip(this: SliderDef, text: string): SliderDef
99
+ withDefault(this: SliderDef, v: number): SliderDef
100
+ }
101
+
102
+ interface DropdownDef {
103
+ bind(this: DropdownDef, sig: PulseSignal<string>): DropdownDef
104
+ withTip(this: DropdownDef, text: string): DropdownDef
105
+ withDefault(this: DropdownDef, v: string): DropdownDef
106
+ }
107
+
108
+ interface ButtonDef {
109
+ onClick(this: ButtonDef, fn: () => void): ButtonDef
110
+ bind(this: ButtonDef): ButtonDef
111
+ }
112
+
113
+ type WidgetDef = ToggleDef | SliderDef | DropdownDef | ButtonDef | {
114
+ bind(this: any): any
115
+ }
116
+
117
+ // ── Component / Page / Layout ────────────────────────────────────────────────
118
+
119
+ /** Opaque handle returned by `defineComponent`. */
120
+ interface ComponentRef {
121
+ readonly _name: string
122
+ readonly _ui: WidgetDef[]
123
+ }
124
+
125
+ interface GroupboxDef {
126
+ readonly type: 'groupbox'
127
+ readonly side: 'left' | 'right'
128
+ readonly title: string
129
+ readonly icon: string
130
+ readonly mount?: string
131
+ }
132
+
133
+ interface PageDef {
134
+ readonly title: string
135
+ readonly icon: string
136
+ readonly layout: GroupboxDef[]
137
+ }
138
+
139
+ interface LayoutConfig {
140
+ title: string
141
+ author?: string
142
+ toggleKey?: string
143
+ size?: [number, number]
144
+ uiLibrary?: 'windui' | 'linoria'
145
+ theme?: string
146
+ icon?: string
147
+ folder?: string
148
+ acrylic?: boolean
149
+ transparency?: number
150
+ openButtonMobileOnly?: boolean
151
+ openButtonIcon?: string
152
+ themes?: Array<{ name: string; values: Record<string, string> }>
153
+ compatExclude?: string[]
154
+ }
155
+
156
+ // ── Core API — all are Lua globals, no require() ──────────────────────────────
252
157
 
253
158
  /**
254
- * RunService.Stepped fires at the physics step rate.
159
+ * SolidJS-style reactive state see createSignal.
255
160
  *
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.
161
+ * Creates a reactive state cell with an initial value.
162
+ * Compiles to `Signal(initialValue)` in Lua.
264
163
  *
265
164
  * @example
266
- * on.inputBegan((input, gpe) => {
267
- * if (gpe) return
268
- * if (input.KeyCode === Enum.KeyCode.X) enabled.toggle()
269
- * })
165
+ * const enabled = signal(false) // PulseSignal<boolean>
166
+ * const speed = signal(16) // PulseSignal<number>
167
+ * enabled() // read
168
+ * enabled.set(true) // write
169
+ * enabled.toggle() // flip boolean
270
170
  */
271
- inputBegan(fn: (input: InputObject, gameProcessed: boolean) => void): void
171
+ function signal<T>(initialValue: T): PulseSignal<T>
272
172
 
273
173
  /**
274
- * UserInputService.InputEnded.
275
- *
276
- * @example
277
- * on.inputEnded(input => {
278
- * if (input.UserInputType === Enum.UserInputType.MouseButton1) release()
279
- * })
174
+ * Create a derived value that recomputes when its dependencies change.
175
+ * Compiles to `Computed(fn)` in Lua.
280
176
  */
281
- inputEnded(fn: (input: InputObject, gameProcessed: boolean) => void): void
177
+ function computed<T>(fn: () => T): () => T
282
178
 
283
179
  /**
284
- * Fires when the player's character respawns.
285
- * Use this to re-apply effects that attach to the character.
180
+ * SolidJS-style component setup function.
286
181
  *
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.
182
+ * The setup function runs once on init. Declare signals, register `on.*`
183
+ * handlers, then return an array of widget definitions. All connections are
184
+ * auto-disconnected when the component is destroyed.
296
185
  *
297
186
  * @example
298
- * on.characterAdded(char => {
299
- * const hrp = char.WaitForChild('HumanoidRootPart') as BasePart
187
+ * defineComponent('SpeedHack', () => {
188
+ * const enabled = signal(false)
189
+ * on.heartbeat({ when: enabled }, () => { ... })
190
+ * return [toggle('Speed Hack').bind(enabled)]
300
191
  * })
301
192
  */
302
- characterAdded(fn: (character: Model) => void): void
193
+ function defineComponent(name: string, setup: () => WidgetDef[]): ComponentRef
303
194
 
304
195
  /**
305
- * Player.CharacterRemoving fires just before the character is removed.
306
- *
307
- * @example
308
- * on.characterRemoving(() => cleanup())
196
+ * Event subscription namespace.
197
+ * All connections are automatically cleaned up when the component is destroyed.
198
+ * Must be called inside a `defineComponent` setup function.
309
199
  */
310
- characterRemoving(fn: (character: Model) => void): void
200
+ const on: {
201
+ /**
202
+ * RunService.Heartbeat — fires every frame.
203
+ * Like SolidJS createEffect scoped to RunService.Heartbeat.
204
+ *
205
+ * @example
206
+ * on.heartbeat({ when: enabled, every: 0.1 }, dt => { doWork() })
207
+ * on.heartbeat(dt => updateDisplay(dt))
208
+ */
209
+ heartbeat(opts: HandlerOpts, fn: (dt: number) => void): void
210
+ heartbeat(fn: (dt: number) => void): void
211
+
212
+ /** RunService.RenderStepped — fires before every frame render (client only). */
213
+ renderStepped(opts: HandlerOpts, fn: (dt: number) => void): void
214
+ renderStepped(fn: (dt: number) => void): void
215
+
216
+ /** RunService.Stepped — fires at the physics step rate. */
217
+ stepped(opts: HandlerOpts, fn: (time: number, dt: number) => void): void
218
+ stepped(fn: (time: number, dt: number) => void): void
219
+
220
+ /** UserInputService.InputBegan. */
221
+ inputBegan(fn: (input: any, gameProcessed: boolean) => void): void
222
+
223
+ /** UserInputService.InputEnded. */
224
+ inputEnded(fn: (input: any, gameProcessed: boolean) => void): void
225
+
226
+ /** Fires when the player's character respawns. */
227
+ respawn(fn: () => void): void
228
+
229
+ /** Player.CharacterAdded — fires when a new character model is added. */
230
+ characterAdded(fn: (character: any) => void): void
231
+
232
+ /** Player.CharacterRemoving — fires just before the character is removed. */
233
+ characterRemoving(fn: (character: any) => void): void
234
+
235
+ /**
236
+ * Subscribe to changes on a signal.
237
+ * Fires immediately with the current value, then on every change.
238
+ */
239
+ signal<T>(sig: PulseSignal<T>, fn: (val: T) => void): void
240
+
241
+ /**
242
+ * Run a one-shot callback after a delay (seconds).
243
+ * @example on.after(0.5, () => init())
244
+ */
245
+ after(seconds: number, fn: () => void): void
246
+ }
247
+
248
+ // ── Widget factory globals ─────────────────────────────────────────────────────
249
+
250
+ /** Toggle widget — checkbox bound to a `PulseSignal<boolean>`. */
251
+ function toggle(label: string, opts?: { tip?: string; default?: boolean }): ToggleDef
252
+
253
+ /** Slider widget — numeric range input. */
254
+ function slider(label: string, opts?: {
255
+ min?: number; max?: number; step?: number; default?: number;
256
+ suffix?: string; tip?: string
257
+ }): SliderDef
258
+
259
+ /** Dropdown widget — selectable list of string options. */
260
+ function dropdown(label: string, opts?: {
261
+ options?: string[]; default?: string; tip?: string
262
+ }): DropdownDef
263
+
264
+ /** Multi-select dropdown widget. */
265
+ function multidropdown(label: string, opts?: {
266
+ options?: string[]; default?: string[]; tip?: string
267
+ }): DropdownDef
268
+
269
+ /** Button widget — triggers a callback when clicked. */
270
+ function button(label: string, fn: () => void, opts?: { tip?: string }): ButtonDef
271
+ function button(label: string, opts?: { tip?: string }): ButtonDef
272
+
273
+ /** Keybind widget — lets the user rebind a hotkey. */
274
+ function keybind(label: string, opts?: { key?: string; tip?: string }): WidgetDef
275
+
276
+ /** Label widget — static non-interactive text. */
277
+ function label(text: string): WidgetDef
278
+
279
+ /** Separator widget — horizontal divider line. */
280
+ function separator(): WidgetDef
311
281
 
312
282
  /**
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))
283
+ * Declare a groupbox column in a page.
284
+ * @param side 'left' or 'right' column
285
+ * @param title Groupbox header text
286
+ * @param opts icon, mount (component name string), widgets
327
287
  */
328
- signal<T>(sig: Signal<T>, fn: (val: T) => void): void
288
+ function groupbox(side: 'left' | 'right', title: string, opts?: {
289
+ icon?: string; mount?: string; widgets?: WidgetDef[]
290
+ }): GroupboxDef
329
291
 
330
292
  /**
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.
293
+ * Next.js-style file-based page tab place in src/pages/N_Name.ts.
334
294
  *
335
- * @param seconds Delay in seconds (0.5 is typical for post-mount init).
336
- * @param fn Callback to run once.
295
+ * Each file calls `definePage` at the top level. Tab order is determined
296
+ * by the numeric prefix of the filename (1_Home.ts, 2_Combat.ts, …).
337
297
  *
338
298
  * @example
339
- * on.after(0.5, () => {
340
- * // safe to read UI state here everything is mounted
341
- * Pulse.UI.paragraph('Info', 'Loaded!')
342
- * })
299
+ * // src/pages/1_Home.ts
300
+ * definePage('Home', { icon: 'house' }, () => [
301
+ * groupbox('left', 'Player', { icon: 'person', mount: 'SpeedHack' }),
302
+ * ])
343
303
  */
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
- }
304
+ function definePage(
305
+ title: string,
306
+ opts: { icon?: string },
307
+ factory: () => GroupboxDef[],
308
+ ): PageDef
309
+
310
+ // ── Roblox globals (minimal stubs) ────────────────────────────────────────────
311
+
312
+ const game: {
313
+ GetService(name: string): any
314
+ HttpGet(url: string): string
315
+ }
316
+
317
+ const workspace: {
318
+ CurrentCamera: any
319
+ [key: string]: any
320
+ }
321
+
322
+ const Enum: Record<string, Record<string, any>>
323
+
324
+ const Color3: {
325
+ new(r: number, g: number, b: number): any
326
+ fromRGB(r: number, g: number, b: number): any
327
+ }
328
+
329
+ const Vector3: {
330
+ new(x: number, y: number, z: number): any
331
+ }
332
+
333
+ const CFrame: {
334
+ new(x: number, y: number, z: number): any
335
+ }
336
+
337
+ // ── Pulse character helpers ───────────────────────────────────────────────────
338
+
339
+ /** Returns `LocalPlayer.Character` or nil. */
340
+ function _PulseGetChar(): any
341
+ /** Returns `HumanoidRootPart` from the character, or nil. */
342
+ function _PulseGetHRP(): any
343
+ /** Returns `Humanoid` from the character, or nil. */
344
+ function _PulseGetHumanoid(): any
345
+ /** Returns true if the local player's humanoid is alive. */
346
+ function _PulseGetAlive(): boolean
357
347
 
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
348
  }
365
349
 
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
- }
350
+ export {}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rb-pulse/core",
3
- "version": "1.2.24",
3
+ "version": "1.3.0",
4
4
  "description": "Pulse framework — component, signal, and event TypeScript API",
5
5
  "main": "./index.ts",
6
6
  "types": "./index.ts",