@rbxts/app-forge 0.0.3 → 0.0.6

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 CHANGED
@@ -0,0 +1,318 @@
1
+ # AppForge — React App & Window Manager for roblox-ts
2
+
3
+ AppForge is a UI/app orchestration system for React in Roblox, enabling:
4
+
5
+ ✔ App registration
6
+ ✔ Visibility & state control
7
+ ✔ App grouping
8
+ ✔ Exclusive UI modes
9
+ ✔ Rule-based interactions
10
+ ✔ Priority & layering
11
+ ✔ React-friendly state bindings
12
+
13
+ ---
14
+
15
+ # 📦 Installation
16
+
17
+ ```bash
18
+ npm i @rbxts/app-forge
19
+ # or
20
+ bun i @rbxts/app-forge
21
+ ```
22
+
23
+ ---
24
+
25
+ # 🧩 Setup — REQUIRED
26
+
27
+ ### Create `global.d.ts`
28
+
29
+ ```ts
30
+ declare global {
31
+ // All registered Apps by name
32
+ type AppNames = readonly ["HUD", "Inventory", "Shop", "SideButtons", "Pause"];
33
+
34
+ // Logical groupings of apps
35
+ type AppGroups = readonly ["HUD", "Panel", "Overlay"];
36
+
37
+ // Props that are injected into each App
38
+ interface AppProps {
39
+ player: Player;
40
+ target: GuiObject | Camera;
41
+ playerData: any;
42
+ events: any;
43
+ appManager: import("@rbxts/app-forge").default;
44
+ }
45
+
46
+ export {};
47
+ }
48
+ ```
49
+
50
+ > AppForge will now use your AppNames & AppProps as typed globals.
51
+
52
+ ---
53
+
54
+ # 🚀 Initializing AppForge
55
+
56
+ ```ts
57
+ import AppManager, { Render } from "@rbxts/app-forge";
58
+ import { createPortal, createRoot } from "@rbxts/react-roblox";
59
+ import React from "@rbxts/react";
60
+
61
+ const manager = new AppManager();
62
+ const root = createRoot(new Instance("Folder"));
63
+
64
+ const props: AppProps = {
65
+ player: Players.LocalPlayer!,
66
+ target: new Instance("Folder"),
67
+ playerData: {},
68
+ events: {},
69
+ appManager: manager,
70
+ };
71
+
72
+ root.render(
73
+ createPortal(
74
+ <screengui ResetOnSpawn={false} ZIndexBehavior="Sibling">
75
+ <Render props={props} manager={manager} />
76
+ </screengui>,
77
+ Players.LocalPlayer!.WaitForChild("PlayerGui"),
78
+ ),
79
+ );
80
+ ```
81
+
82
+ > `Render` will automatically render ALL registered apps.
83
+
84
+ ---
85
+
86
+ # 🧱 Creating an App
87
+
88
+ ```ts
89
+ import { App, Args } from "@rbxts/app-forge";
90
+ import React from "@rbxts/react";
91
+
92
+ @App({
93
+ name: "SideButtons",
94
+ visible: true,
95
+ })
96
+ export default class SideButtons extends Args {
97
+ render() {
98
+ return (
99
+ <frame Size={UDim2.fromScale(1, 1)}>Side Buttons UI</frame>
100
+ );
101
+ }
102
+ }
103
+ ```
104
+
105
+ ### ❗ Note
106
+
107
+ `Args` automatically injects AppProps and more into `this`, such as:
108
+
109
+ ```ts
110
+ this.Forge // This is the AppForge Manager Created
111
+ this.name // The Name given on Element Creation
112
+ this.props // Props passed through Render
113
+ ```
114
+
115
+ You never manually `new` apps — AppForge constructs them automatically.
116
+
117
+ ---
118
+
119
+ # 🕹 App Manager API
120
+
121
+ ```ts
122
+ appManager.toggle("Inventory");
123
+ appManager.set("Shop", true);
124
+ appManager.set("HUD", false);
125
+ const isShown = appManager.getState("Pause");
126
+ const bind = appManager.getBind("Inventory");
127
+ ```
128
+
129
+ ---
130
+
131
+ # 📐 Using `getBind()` in React
132
+
133
+ ```tsx
134
+ return (
135
+ <frame Visible={props.appManager.getBind("Inventory")}>
136
+ Items…
137
+ </frame>
138
+ );
139
+ ```
140
+
141
+ Binds re-render automatically on visibility change.
142
+
143
+ ---
144
+
145
+ # ⌨️ Hotkey Toggle Example
146
+
147
+ ```ts
148
+ UserInputService.InputBegan.Connect((input) => {
149
+ if (input.KeyCode === Enum.KeyCode.I)
150
+ appManager.toggle("Inventory");
151
+ });
152
+ ```
153
+
154
+ ---
155
+
156
+ # ⚖️ APP RULES SYSTEM
157
+
158
+ Rules control UI interaction & visibility behavior.
159
+
160
+ | Rule | Effect |
161
+ |---|---|
162
+ | blockedBy | Prevents opening if another app is active |
163
+ | blocks | Auto-closes another app when opened |
164
+ | exclusive | Closes ALL other apps except same group |
165
+ | groups | Logical grouping categories |
166
+ | layer | (Coming soon — currently not functional) |
167
+
168
+ ---
169
+
170
+ ## `blockedBy`
171
+
172
+ App cannot open if the listed apps are open.
173
+
174
+ ```ts
175
+ @App({ name: "Inventory", rules: { blockedBy: "Shop" } })
176
+ ```
177
+
178
+ Multiple:
179
+
180
+ ```ts
181
+ @App({ name: "Inventory", rules: { blockedBy: ["Shop", "Trade"] } })
182
+ ```
183
+
184
+ ---
185
+
186
+ ## `blocks`
187
+
188
+ Opening the app will hide other apps.
189
+
190
+ ```ts
191
+ @App({ name: "Shop", rules: { blocks: "Inventory" } })
192
+ ```
193
+
194
+ Multiple:
195
+
196
+ ```ts
197
+ @App({ name: "Shop", rules: { blocks: ["Inventory", "TeamUI"] } })
198
+ ```
199
+
200
+ ---
201
+
202
+ ## `exclusive`
203
+
204
+ For fullscreen apps:
205
+
206
+ ```ts
207
+ @App({ name: "Pause", rules: { exclusive: true } })
208
+ ```
209
+
210
+ When Pause opens:
211
+
212
+ ✔ Inventory closes
213
+ ✔ HUD closes
214
+ ✔ SideButtons closes
215
+ ✔ Everything except Core
216
+
217
+ ---
218
+
219
+ ## `groups`
220
+
221
+ Apps inside the same group DO NOT block each other.
222
+
223
+ ```ts
224
+ @App({ name: "HUD", rules: { groups: ["HUD"] } })
225
+ @App({ name: "Crosshair", rules: { groups: ["HUD"] } })
226
+ ```
227
+
228
+ These can both be open at the same time.
229
+
230
+ ---
231
+
232
+ ## `Core` Group
233
+
234
+ Core apps are ALWAYS allowed and never auto-hidden.
235
+
236
+ ```ts
237
+ @App({ name: "FPSCounter", rules: { groups: "Core" } })
238
+ ```
239
+
240
+ This app will NEVER be closed by rules.
241
+
242
+ ---
243
+
244
+ ## `layer` *(not implemented yet)*
245
+
246
+ > The `layer` property is part of the upcoming UI priority system.
247
+ > Setting it currently does not affect rendering order.
248
+
249
+ Example (for future readiness):
250
+
251
+ ```ts
252
+ @App({ name: "DebugUI", rules: { layer: 999 } })
253
+ ```
254
+
255
+ ---
256
+
257
+ # 🧪 Full Rules Example
258
+
259
+ ```ts
260
+ @App({ name: "HUD", rules: { groups: ["HUD"] } })
261
+ @App({ name: "Inventory", rules: { blockedBy: "Shop", groups: ["Panel"] } })
262
+ @App({ name: "Shop", rules: { blocks: "Inventory", groups: ["Panel"] } })
263
+ @App({ name: "Pause", rules: { exclusive: true } })
264
+ @App({ name: "FPSCounter", rules: { groups: "Core" } })
265
+ ```
266
+
267
+ Behavior:
268
+
269
+ | Action | Result |
270
+ | -------------- | -------------------------------- |
271
+ | Open Inventory | HUD + FPS stay |
272
+ | Open Shop | Inventory closes |
273
+ | Open Pause | ALL apps close except Core |
274
+ | Open HUD | Never conflicts with SideButtons |
275
+ | FPSCounter | Always visible |
276
+
277
+ ---
278
+
279
+ # 🧠 Using AppForge in UI Components
280
+
281
+ ```tsx
282
+ function MenuUI(props: AppProps) {
283
+ return (
284
+ <frame Visible={props.appManager.getBind("Shop")}>
285
+ Shop UI…
286
+ </frame>
287
+ );
288
+ }
289
+ ```
290
+
291
+ ---
292
+
293
+ # ❗ Best Practices
294
+
295
+ ✔ Use `groups` for UI coexistence
296
+ ✔ Use `exclusive` for fullscreen menus
297
+ ✔ Use `blockedBy` for preventing conflicts
298
+ ✔ Use `blocks` for auto-closing logic
299
+ ✔ Use `Core` for never-closed apps
300
+ ✔ Use `layer` for future-proofing (not active yet)
301
+
302
+ ---
303
+
304
+ # 🛠 Future Roadmap
305
+
306
+ - [ ] layer / priority rendering
307
+
308
+ ---
309
+
310
+ # ❤️ Contributing
311
+
312
+ Feel free to submit PRs, suggestions, or feature requests.
313
+
314
+ ---
315
+
316
+ # 📄 License
317
+
318
+ MIT
@@ -1,5 +1,5 @@
1
- import type AppManager from ".";
1
+ import type AppForge from ".";
2
2
  export declare function AppContainer(props: AppProps & {
3
3
  name: AppNames[number];
4
- manager: AppManager;
4
+ manager: AppForge;
5
5
  }): JSX.Element;
@@ -8,13 +8,54 @@ local useBindingListener = _loners_pretty_react_hooks.useBindingListener
8
8
  local useSpring = _loners_pretty_react_hooks.useSpring
9
9
  local _react = TS.import(script, TS.getModule(script, "@rbxts", "react"))
10
10
  local React = _react
11
+ local cloneElement = _react.cloneElement
12
+ local useBinding = _react.useBinding
11
13
  local useState = _react.useState
12
14
  -- Types
15
+ -- Components
16
+ local AppRegistry = TS.import(script, script.Parent, "decorator").AppRegistry
17
+ local function createBinding(name, manager)
18
+ local _name = name
19
+ local app = AppRegistry[_name]
20
+ if not app then
21
+ error(`App "{name}" not registered`)
22
+ end
23
+ local _condition = app.visible
24
+ if _condition == nil then
25
+ _condition = false
26
+ end
27
+ local binding = { useBinding(_condition) }
28
+ local _binds = manager.binds
29
+ local _name_1 = name
30
+ _binds[_name_1] = binding
31
+ return unpack(binding)
32
+ end
33
+ local function createInstance(props, name, manager)
34
+ local _name = name
35
+ local appClass = AppRegistry[_name]
36
+ if not appClass then
37
+ error(`App "{name}" not registered`)
38
+ end
39
+ local _loaded = manager.loaded
40
+ local _name_1 = name
41
+ if not (_loaded[_name_1] ~= nil) then
42
+ local instance = appClass.constructor.new(props, manager, name)
43
+ local element = cloneElement(instance:render(), {
44
+ key = "Main",
45
+ })
46
+ local _loaded_1 = manager.loaded
47
+ local _name_2 = name
48
+ _loaded_1[_name_2] = element
49
+ end
50
+ local _loaded_1 = manager.loaded
51
+ local _name_2 = name
52
+ return _loaded_1[_name_2]
53
+ end
13
54
  local function AppContainer(props)
14
55
  local _binding = props
15
56
  local name = _binding.name
16
57
  local manager = _binding.manager
17
- local binding, _ = manager:createBinding(name)
58
+ local binding, _ = createBinding(name, manager)
18
59
  local spring = useSpring(binding:map(function(v)
19
60
  return if v then 0 else 1
20
61
  end), {
@@ -22,7 +63,7 @@ local function AppContainer(props)
22
63
  damping = 0.8,
23
64
  })
24
65
  local _isVisible, setisVisible = useState(binding:getValue())
25
- local element = manager:createInstance(props, name)
66
+ local element = createInstance(props, name, manager)
26
67
  if not element then
27
68
  error(`Failed to create instance for app "{name}"`)
28
69
  end
@@ -1,9 +1,12 @@
1
1
  import React from "@rbxts/react";
2
2
  import type Types from "./types";
3
+ import type AppForge from ".";
3
4
  export declare const AppRegistry: Map<string, Types.AppRegistry>;
4
- export declare function App(props: Types.AppRegistryProps): <T extends new (props: AppProps) => Args>(constructor: T) => T;
5
+ export declare function App(props: Types.AppRegistryProps): <T extends new (props: AppProps, forge: AppForge, name: string) => Args>(constructor: T) => T;
5
6
  export declare abstract class Args {
7
+ readonly Forge: AppForge;
6
8
  readonly props: AppProps;
7
- constructor(props: AppProps);
9
+ readonly name: AppNames[number];
10
+ constructor(props: AppProps, forge: AppForge, name: AppNames[number]);
8
11
  abstract render(): React.Element;
9
12
  }
@@ -21,8 +21,10 @@ end
21
21
  local Args
22
22
  do
23
23
  Args = {}
24
- function Args:constructor(props)
24
+ function Args:constructor(props, forge, name)
25
25
  self.props = props
26
+ self.Forge = forge
27
+ self.name = name
26
28
  end
27
29
  end
28
30
  return {
package/out/helpers.d.ts CHANGED
@@ -1,6 +1,6 @@
1
- import type AppManager from ".";
1
+ import type AppForge from ".";
2
2
  export declare function Render({ props, manager, names, }: {
3
3
  props: AppProps;
4
- manager: AppManager;
4
+ manager: AppForge;
5
5
  names?: AppNames[number] | AppNames[number][];
6
6
  }): JSX.Element | JSX.Element[];
package/out/index.d.ts CHANGED
@@ -1,12 +1,9 @@
1
1
  import React from "@rbxts/react";
2
2
  import { Args, App } from "./decorator";
3
- export default class AppManager {
4
- private binds;
5
- private loaded;
3
+ export default class AppForge {
4
+ binds: Map<string, [React.Binding<boolean>, (T: boolean) => void]>;
5
+ loaded: Map<string, React.Element<any, string | React.JSXElementConstructor<any>>>;
6
6
  private rulesManager;
7
- private getAllNames;
8
- createBinding(name: AppNames[number]): LuaTuple<[React.Binding<boolean>, (newValue: boolean) => void]>;
9
- createInstance(props: AppProps, name: AppNames[number]): React.Element<any, string | React.JSXElementConstructor<any>>;
10
7
  getBind(name: AppNames[number]): React.Binding<boolean>;
11
8
  getState(name: AppNames[number]): boolean;
12
9
  set(name: AppNames[number], value: boolean): void;
package/out/init.luau CHANGED
@@ -2,76 +2,33 @@
2
2
  local TS = _G[script]
3
3
  local exports = {}
4
4
  -- Packages
5
- local _react = TS.import(script, TS.getModule(script, "@rbxts", "react"))
6
- local React = _react
7
- local cloneElement = _react.cloneElement
8
- local useBinding = _react.useBinding
9
- local Object = TS.import(script, TS.getModule(script, "@rbxts", "object-utils"))
5
+ local React = TS.import(script, TS.getModule(script, "@rbxts", "react"))
10
6
  -- Components
11
7
  local _decorator = TS.import(script, script, "decorator")
12
8
  local AppRegistry = _decorator.AppRegistry
13
9
  local Args = _decorator.Args
14
10
  local App = _decorator.App
15
11
  local AppContainer = TS.import(script, script, "container").AppContainer
12
+ -- Classes
16
13
  local RulesManager = TS.import(script, script, "rules").default
17
- local AppManager
14
+ local AppForge
18
15
  do
19
- AppManager = setmetatable({}, {
16
+ AppForge = setmetatable({}, {
20
17
  __tostring = function()
21
- return "AppManager"
18
+ return "AppForge"
22
19
  end,
23
20
  })
24
- AppManager.__index = AppManager
25
- function AppManager.new(...)
26
- local self = setmetatable({}, AppManager)
21
+ AppForge.__index = AppForge
22
+ function AppForge.new(...)
23
+ local self = setmetatable({}, AppForge)
27
24
  return self:constructor(...) or self
28
25
  end
29
- function AppManager:constructor()
26
+ function AppForge:constructor()
30
27
  self.binds = {}
31
28
  self.loaded = {}
32
29
  self.rulesManager = RulesManager.new(self)
33
30
  end
34
- function AppManager:getAllNames()
35
- return Object.keys(AppRegistry)
36
- end
37
- function AppManager:createBinding(name)
38
- local _name = name
39
- local app = AppRegistry[_name]
40
- if not app then
41
- error(`App "{name}" not registered`)
42
- end
43
- local _condition = app.visible
44
- if _condition == nil then
45
- _condition = false
46
- end
47
- local binding = { useBinding(_condition) }
48
- local _binds = self.binds
49
- local _name_1 = name
50
- _binds[_name_1] = binding
51
- return unpack(binding)
52
- end
53
- function AppManager:createInstance(props, name)
54
- local _name = name
55
- local appClass = AppRegistry[_name]
56
- if not appClass then
57
- error(`App "{name}" not registered`)
58
- end
59
- local _loaded = self.loaded
60
- local _name_1 = name
61
- if not (_loaded[_name_1] ~= nil) then
62
- local instance = appClass.constructor.new(props)
63
- local element = cloneElement(instance:render(), {
64
- key = "Main",
65
- })
66
- local _loaded_1 = self.loaded
67
- local _name_2 = name
68
- _loaded_1[_name_2] = element
69
- end
70
- local _loaded_1 = self.loaded
71
- local _name_2 = name
72
- return _loaded_1[_name_2]
73
- end
74
- function AppManager:getBind(name)
31
+ function AppForge:getBind(name)
75
32
  local _binds = self.binds
76
33
  local _name = name
77
34
  if not (_binds[_name] ~= nil) then
@@ -81,10 +38,10 @@ do
81
38
  local _name_1 = name
82
39
  return _binds_1[_name_1][1]
83
40
  end
84
- function AppManager:getState(name)
41
+ function AppForge:getState(name)
85
42
  return self:getBind(name):getValue()
86
43
  end
87
- function AppManager:set(name, value)
44
+ function AppForge:set(name, value)
88
45
  if not self.rulesManager:applyRules(name, value) then
89
46
  return nil
90
47
  end
@@ -95,16 +52,16 @@ do
95
52
  local setBinding = _binding[2]
96
53
  setBinding(value)
97
54
  end
98
- function AppManager:open(name)
55
+ function AppForge:open(name)
99
56
  self:set(name, true)
100
57
  end
101
- function AppManager:close(name)
58
+ function AppForge:close(name)
102
59
  self:set(name, false)
103
60
  end
104
- function AppManager:toggle(name)
61
+ function AppForge:toggle(name)
105
62
  self:set(name, not self:getState(name))
106
63
  end
107
- function AppManager:renderApp(props, name)
64
+ function AppForge:renderApp(props, name)
108
65
  local _attributes = {
109
66
  key = `{name}_Container`,
110
67
  name = name,
@@ -115,7 +72,7 @@ do
115
72
  end
116
73
  return React.createElement(AppContainer, _attributes)
117
74
  end
118
- function AppManager:renderApps(props, names)
75
+ function AppForge:renderApps(props, names)
119
76
  -- ▼ ReadonlyArray.map ▼
120
77
  local _newValue = table.create(#names)
121
78
  local _callback = function(n)
@@ -127,12 +84,22 @@ do
127
84
  -- ▲ ReadonlyArray.map ▲
128
85
  return _newValue
129
86
  end
130
- function AppManager:renderAll(props)
131
- return self:renderApps(props, self:getAllNames())
87
+ function AppForge:renderAll(props)
88
+ local keys = {}
89
+ -- ▼ ReadonlyMap.forEach ▼
90
+ local _callback = function(_, name)
91
+ local _name = name
92
+ table.insert(keys, _name)
93
+ end
94
+ for _k, _v in AppRegistry do
95
+ _callback(_v, _k, AppRegistry)
96
+ end
97
+ -- ▲ ReadonlyMap.forEach ▲
98
+ return self:renderApps(props, keys)
132
99
  end
133
100
  end
134
101
  exports.Render = TS.import(script, script, "helpers").Render
135
- exports.default = AppManager
102
+ exports.default = AppForge
136
103
  exports.App = App
137
104
  exports.Args = Args
138
105
  return exports
package/out/types.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  // Types
2
2
  import type { Args } from "./decorator";
3
+ import type AppForge from ".";
3
4
 
4
5
  declare namespace Types {
5
6
  type AppRegistryProps = {
@@ -9,7 +10,7 @@ declare namespace Types {
9
10
  };
10
11
 
11
12
  type AppRegistry = {
12
- constructor: new (props: AppProps) => Args;
13
+ constructor: new (props: AppProps, forge: AppForge, name: string) => Args;
13
14
  visible?: boolean;
14
15
  rules?: Rules.All;
15
16
  };
package/package.json CHANGED
@@ -1,59 +1,58 @@
1
1
  {
2
- "name": "@rbxts/app-forge",
3
- "version": "0.0.3",
4
- "description": "An App Manager for react",
5
- "main": "out/init.lua",
6
- "packageManager": "bun@1.3.1",
7
- "scripts": {
8
- "build": "rbxtsc",
9
- "dev": "rbxtsc -w --type game --rojo test.project.json",
10
- "login": "bunx npm login --auth-type=legacy"
11
-
12
- },
13
- "keywords": [
14
- "roblox-ts",
15
- "react"
16
- ],
17
- "author": "loner1536",
18
- "license": "MIT",
19
- "repository": {
20
- "type": "git",
21
- "url": "https://github.com/Loner1536/AppForge"
22
- },
23
- "bugs": {
24
- "url": "https://github.com/Loner1536/AppForge/issues"
25
- },
26
- "types": "out/index.d.ts",
27
- "files": [
28
- "out",
29
- "!**/*.tsbuildinfo",
30
- "!**/*.spec.lua",
31
- "!**/*.spec.d.ts"
32
- ],
33
- "publishConfig": {
34
- "access": "public"
35
- },
36
- "devDependencies": {
37
- "@rbxts/compiler-types": "3.0.0-types.0",
38
- "@rbxts/react": "17.3.0-alpha.1",
39
- "@rbxts/react-roblox": "17.3.0-alpha.1",
40
- "@rbxts/types": "^1.0.891",
41
- "@typescript-eslint/eslint-plugin": "^8.46.4",
42
- "@typescript-eslint/parser": "^8.46.4",
43
- "roblox-ts": "3.0.0",
44
- "typescript": "^5.9.3"
45
- },
46
- "dependencies": {
47
- "@rbxts/charm": "^0.10.0",
48
- "@rbxts/loners-pretty-react-hooks": "^0.0.3",
49
- "@rbxts/react-charm": "^0.3.0",
50
- "@rbxts/ripple": "^0.9.3",
51
- "@rbxts/services": "^1.6.0",
52
- "@rbxts/set-timeout": "^1.1.2"
53
- },
54
- "peerDependencies": {
55
- "@rbxts/object-utils": "*",
56
- "@rbxts/react-roblox": "*",
57
- "@rbxts/react": "*"
58
- }
2
+ "name": "@rbxts/app-forge",
3
+ "version": "0.0.6",
4
+ "description": "An App Manager for react",
5
+ "main": "out/init.lua",
6
+ "packageManager": "bun@1.3.1",
7
+ "scripts": {
8
+ "build": "rbxtsc",
9
+ "dev": "rbxtsc -w --type game --rojo test.project.json",
10
+ "login": "bunx npm login --auth-type=legacy"
11
+ },
12
+ "keywords": [
13
+ "roblox-ts",
14
+ "react"
15
+ ],
16
+ "author": "loner1536",
17
+ "license": "MIT",
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "https://github.com/Loner1536/AppForge"
21
+ },
22
+ "bugs": {
23
+ "url": "https://github.com/Loner1536/AppForge/issues"
24
+ },
25
+ "types": "out/index.d.ts",
26
+ "files": [
27
+ "out",
28
+ "!**/*.tsbuildinfo",
29
+ "!**/*.spec.lua",
30
+ "!**/*.spec.d.ts"
31
+ ],
32
+ "publishConfig": {
33
+ "access": "public"
34
+ },
35
+ "devDependencies": {
36
+ "@biomejs/biome": "^2.3.7",
37
+ "@rbxts/compiler-types": "3.0.0-types.0",
38
+ "@rbxts/react": "17.3.0-alpha.1",
39
+ "@rbxts/react-roblox": "17.3.0-alpha.1",
40
+ "@rbxts/types": "^1.0.891",
41
+ "@typescript-eslint/eslint-plugin": "^8.46.4",
42
+ "@typescript-eslint/parser": "^8.46.4",
43
+ "roblox-ts": "3.0.0",
44
+ "typescript": "^5.9.3"
45
+ },
46
+ "dependencies": {
47
+ "@rbxts/charm": "^0.10.0",
48
+ "@rbxts/loners-pretty-react-hooks": "^0.0.3",
49
+ "@rbxts/react-charm": "^0.3.0",
50
+ "@rbxts/ripple": "^0.9.3",
51
+ "@rbxts/services": "^1.6.0",
52
+ "@rbxts/set-timeout": "^1.1.2"
53
+ },
54
+ "peerDependencies": {
55
+ "@rbxts/react-roblox": "*",
56
+ "@rbxts/react": "*"
57
+ }
59
58
  }