@rbxts/app-forge 0.2.1 → 0.2.3

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
@@ -8,7 +8,7 @@ AppForge is a UI/app orchestration system for React in Roblox, enabling:
8
8
  ✔ Exclusive UI modes
9
9
  ✔ Rule-based interactions
10
10
  ✔ Priority & layering
11
- ✔ React-friendly state bindings
11
+ ✔ React-friendly state bindings & animations
12
12
 
13
13
  ---
14
14
 
@@ -28,58 +28,47 @@ bun i @rbxts/app-forge
28
28
 
29
29
  ```ts
30
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 {
31
+ type AppProps = {
39
32
  player: Player;
40
- target: GuiObject | Camera;
41
- playerData: any;
42
- events: any;
43
- appManager: import("@rbxts/app-forge").default;
44
33
  }
45
-
46
34
  export {};
47
35
  }
48
36
  ```
49
37
 
50
- > AppForge will now use your AppNames & AppProps as typed globals.
38
+ > App names are now autodetected via decorators no need to define `AppNames` globally.
51
39
 
52
40
  ---
53
41
 
54
42
  # 🚀 Initializing AppForge
55
43
 
56
44
  ```ts
57
- import AppManager, { Render } from "@rbxts/app-forge";
45
+ import { Workspace } from "@rbxts/services"
46
+
58
47
  import { createPortal, createRoot } from "@rbxts/react-roblox";
48
+ import { Players, Workspace } from "@rbxts/services";
49
+ import AppForge, { Render } from "@rbxts/app-forge";
59
50
  import React from "@rbxts/react";
60
51
 
61
- const manager = new AppManager();
52
+ const forge = new AppForge();
62
53
  const root = createRoot(new Instance("Folder"));
63
54
 
64
- const props: AppProps = {
55
+ const target = Workspace.CurrentCamera!
56
+
57
+ const props = {
65
58
  player: Players.LocalPlayer!,
66
- target: new Instance("Folder"),
67
- playerData: {},
68
- events: {},
69
- appManager: manager,
70
- };
59
+ } as const satisfies AppProps;
71
60
 
72
61
  root.render(
73
62
  createPortal(
74
63
  <screengui ResetOnSpawn={false} ZIndexBehavior="Sibling">
75
- <Render props={props} manager={manager} />
64
+ <Render {...{ props, forge, root, target }} />
76
65
  </screengui>,
77
- Players.LocalPlayer!.WaitForChild("PlayerGui"),
66
+ Players.LocalPlayer!.WaitForChild("PlayerGui")!,
78
67
  ),
79
68
  );
80
69
  ```
81
70
 
82
- > `Render` will automatically render ALL registered apps.
71
+ > `Render` will control all decorated apps.
83
72
 
84
73
  ---
85
74
 
@@ -94,58 +83,56 @@ import React from "@rbxts/react";
94
83
  visible: true,
95
84
  })
96
85
  export default class SideButtons extends Args {
97
- render() {
98
- return (
99
- <frame Size={UDim2.fromScale(1, 1)}>Side Buttons UI</frame>
100
- );
86
+ public render() {
87
+ return <frame Size={UDim2.fromScale(1, 1)}>Side Buttons UI</frame>;
101
88
  }
102
89
  }
103
90
  ```
104
91
 
105
- ### ❗ Note
92
+ ---
106
93
 
107
- `Args` automatically injects AppProps and more into `this`, such as:
94
+ # 📦 Props & `Args` Features
95
+
96
+ Inside a decorated App:
108
97
 
109
98
  ```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
- this.bind // This is the visible Bind
99
+ this.Forge // AppForge instance
100
+ this.name // App Name
101
+ this.props // AppProps
102
+ this.bind // visible state bind
103
+ ```
114
104
 
115
- // Also I added px into the this.props table for you just requires my other lib so you can do this
116
- const { px } = this.props
105
+ Example:
117
106
 
118
- px.map((s) => (1 * s))
107
+ ```ts
108
+ const { px } = this.props;
109
+ const scale2px = px.map((s) => s * 2);
119
110
  ```
120
111
 
121
- You never manually `new` apps — AppForge constructs them automatically.
122
-
123
112
  ---
124
113
 
125
114
  # 🕹 App Manager API
126
115
 
127
116
  ```ts
128
- appManager.toggle("Inventory");
129
- appManager.set("Shop", true);
130
- appManager.set("HUD", false);
131
- const isShown = appManager.getState("Pause");
132
- const bind = appManager.getBind("Inventory");
117
+ forge.toggle("Inventory");
118
+ forge.set("Shop", true);
119
+ forge.set("HUD", false);
120
+ const shown = forge.getState("Pause");
121
+ const bind = forge.getBind("Inventory");
133
122
  ```
134
123
 
135
124
  ---
136
125
 
137
- # 📐 Using `getBind()` in React
126
+ # 📐 Using `getBind()` inside React
138
127
 
139
128
  ```tsx
140
129
  return (
141
- <frame Visible={props.appManager.getBind("Inventory")}>
130
+ <frame Visible={forge.getBind("Inventory")}>
142
131
  Items…
143
132
  </frame>
144
133
  );
145
134
  ```
146
135
 
147
- Binds re-render automatically on visibility change.
148
-
149
136
  ---
150
137
 
151
138
  # ⌨️ Hotkey Toggle Example
@@ -153,7 +140,7 @@ Binds re-render automatically on visibility change.
153
140
  ```ts
154
141
  UserInputService.InputBegan.Connect((input) => {
155
142
  if (input.KeyCode === Enum.KeyCode.I)
156
- appManager.toggle("Inventory");
143
+ forge.toggle("Inventory");
157
144
  });
158
145
  ```
159
146
 
@@ -161,161 +148,121 @@ UserInputService.InputBegan.Connect((input) => {
161
148
 
162
149
  # ⚖️ APP RULES SYSTEM
163
150
 
164
- Rules control UI interaction & visibility behavior.
151
+ Rules control app visibility behavior.
165
152
 
166
- | Rule | Effect |
167
- |---|---|
168
- | blockedBy | Prevents opening if another app is active |
169
- | blocks | Auto-closes another app when opened |
153
+ | Rule | Effect |
154
+ | --------- | --------------------------------------- |
155
+ | blockedBy | Prevents opening if another is open |
156
+ | blocks | Closes another app when opened |
170
157
  | exclusive | Closes ALL other apps except same group |
171
- | groups | Logical grouping categories |
172
- | layer | (Coming sooncurrently not functional) |
158
+ | groups | Non-conflicting coexistence grouping |
159
+ | Core | Always allowednever auto-closed |
160
+ | layer | (Reserved – future rendering priority) |
173
161
 
174
162
  ---
175
163
 
176
- ## `blockedBy`
177
-
178
- App cannot open if the listed apps are open.
164
+ ## `groups`
179
165
 
180
166
  ```ts
181
- @App({ name: "Inventory", rules: { blockedBy: "Shop" } })
167
+ @App({ name: "HUD", rules: { groups: "HUD" } })
168
+ @App({ name: "Crosshair", rules: { groups: "HUD" } })
182
169
  ```
183
170
 
184
- Multiple:
185
-
186
- ```ts
187
- @App({ name: "Inventory", rules: { blockedBy: ["Shop", "Trade"] } })
188
- ```
171
+ Both may open at the same time.
189
172
 
190
173
  ---
191
174
 
192
- ## `blocks`
193
-
194
- Opening the app will hide other apps.
175
+ ## `Core`
195
176
 
196
177
  ```ts
197
- @App({ name: "Shop", rules: { blocks: "Inventory" } })
178
+ @App({ name: "FPSCounter", rules: { groups: "Core" } })
198
179
  ```
199
180
 
200
- Multiple:
201
-
202
- ```ts
203
- @App({ name: "Shop", rules: { blocks: ["Inventory", "TeamUI"] } })
204
- ```
181
+ Never closed by rules.
205
182
 
206
183
  ---
207
184
 
208
- ## `exclusive`
209
-
210
- For fullscreen apps:
185
+ # 🧪 Modern App Example (from your snippet)
211
186
 
212
187
  ```ts
213
- @App({ name: "Pause", rules: { exclusive: true } })
214
- ```
215
-
216
- When Pause opens:
217
-
218
- ✔ Inventory closes
219
- ✔ HUD closes
220
- ✔ SideButtons closes
221
- ✔ Everything except Core
188
+ @App({ name: "TestApp", visible: true, rules: { groups: "Core" } })
189
+ export default class TestApp extends Args {
190
+ public render() {
191
+ const { px } = this.props;
222
192
 
223
- ---
224
-
225
- ## `groups`
226
-
227
- Apps inside the same group DO NOT block each other.
228
-
229
- ```ts
230
- @App({ name: "HUD", rules: { groups: ["HUD"] } })
231
- @App({ name: "Crosshair", rules: { groups: ["HUD"] } })
193
+ return (
194
+ <frame AnchorPoint={new Vector2(0.5, 1)}>
195
+ UI Stuff…
196
+ </frame>
197
+ );
198
+ }
199
+ }
232
200
  ```
233
201
 
234
- These can both be open at the same time.
235
-
236
202
  ---
237
203
 
238
- ## `Core` Group
239
-
240
- Core apps are ALWAYS allowed and never auto-hidden.
204
+ # 🧠 Using AppForge from Flamework
241
205
 
242
206
  ```ts
243
- @App({ name: "FPSCounter", rules: { groups: "Core" } })
207
+ import AppForge, { Render } from "@rbxts/app-forge";
208
+
209
+ @Controller()
210
+ export default class AppController implements OnInit {
211
+ onInit() {
212
+ const props = this.createProps(player);
213
+ const forge = new AppForge();
214
+ const root = createRoot(new Instance("Folder"));
215
+
216
+ root.render(
217
+ createPortal(
218
+ <screengui ResetOnSpawn={false}>
219
+ <Render {...{ props, forge, root, target: props.target }} />
220
+ </screengui>,
221
+ player.WaitForChild("PlayerGui"),
222
+ ),
223
+ );
224
+ }
225
+ }
244
226
  ```
245
227
 
246
- This app will NEVER be closed by rules.
247
-
248
228
  ---
249
229
 
250
- ## `layer` *(not implemented yet)*
251
-
252
- > The `layer` property is part of the upcoming UI priority system.
253
- > Setting it currently does not affect rendering order.
230
+ # 🧠 Using with Storybook/Setup
254
231
 
255
- Example (for future readiness):
232
+ You can manually choose which apps render:
256
233
 
257
- ```ts
258
- @App({ name: "DebugUI", rules: { layer: 999 } })
259
- ```
260
-
261
- ---
262
-
263
- # 🧪 Full Rules Example
264
-
265
- ```ts
266
- @App({ name: "HUD", rules: { groups: ["HUD"] } })
267
- @App({ name: "Inventory", rules: { blockedBy: "Shop", groups: ["Panel"] } })
268
- @App({ name: "Shop", rules: { blocks: "Inventory", groups: ["Panel"] } })
269
- @App({ name: "Pause", rules: { exclusive: true } })
270
- @App({ name: "FPSCounter", rules: { groups: "Core" } })
234
+ ```tsx
235
+ <Render {...{ props, forge, target, name: "TestApp" }} />
271
236
  ```
272
237
 
273
- Behavior:
274
-
275
- | Action | Result |
276
- | -------------- | -------------------------------- |
277
- | Open Inventory | HUD + FPS stay |
278
- | Open Shop | Inventory closes |
279
- | Open Pause | ALL apps close except Core |
280
- | Open HUD | Never conflicts with SideButtons |
281
- | FPSCounter | Always visible |
282
-
283
- ---
284
-
285
- # 🧠 Using AppForge in UI Components
238
+ or:
286
239
 
287
240
  ```tsx
288
- function MenuUI(props: AppProps) {
289
- return (
290
- <frame Visible={props.appManager.getBind("Shop")}>
291
- Shop UI…
292
- </frame>
293
- );
294
- }
241
+ <Render {...{ props, forge, target, names: ["HUD", "Shop"] }} />
295
242
  ```
296
243
 
297
244
  ---
298
245
 
299
246
  # ❗ Best Practices
300
247
 
301
- ✔ Use `groups` for UI coexistence
302
- ✔ Use `exclusive` for fullscreen menus
303
- ✔ Use `blockedBy` for preventing conflicts
304
- ✔ Use `blocks` for auto-closing logic
305
- ✔ Use `Core` for never-closed apps
306
- Use `layer` for future-proofing (not active yet)
248
+ ✔ Use `groups` for compatible UIs
249
+ ✔ Use `blockedBy` to avoid interruptions
250
+ ✔ Use `blocks` for mutual exclusion
251
+ ✔ Use `exclusive` for fullscreen control
252
+ ✔ Use `"Core"` for never-hidden persistent UI
253
+ Avoid manually instantiating apps
307
254
 
308
255
  ---
309
256
 
310
257
  # 🛠 Future Roadmap
311
258
 
312
- - [ ] layer / priority rendering
259
+ * [ ] UI layering & depth priority
313
260
 
314
261
  ---
315
262
 
316
263
  # ❤️ Contributing
317
264
 
318
- Feel free to submit PRs, suggestions, or feature requests.
265
+ PRs and suggestions welcome!
319
266
 
320
267
  ---
321
268
 
@@ -2,15 +2,13 @@ import { usePx } from "@rbxts/loners-pretty-react-hooks";
2
2
  import React from "@rbxts/react";
3
3
  import type Types from "./types";
4
4
  import type AppForge from ".";
5
- import ReactRoblox from "@rbxts/react-roblox";
6
5
  export declare const AppRegistry: Map<string, Types.AppRegistry>;
7
6
  export declare function App(props: Types.AppRegistryProps): <T extends new (props: Types.MainProps) => Args>(constructor: T) => T;
8
7
  export declare abstract class Args {
9
- readonly Forge: AppForge;
8
+ readonly forge: AppForge;
10
9
  readonly props: AppProps & {
11
10
  px: ReturnType<typeof usePx>;
12
11
  };
13
- readonly root: ReactRoblox.Root | undefined;
14
12
  readonly bind: React.Binding<boolean>;
15
13
  readonly name: AppNames[number];
16
14
  constructor(props: Types.MainProps);
@@ -26,7 +26,6 @@ do
26
26
  Args = {}
27
27
  function Args:constructor(props)
28
28
  local _binding = props
29
- local root = _binding.root
30
29
  local target = _binding.target
31
30
  local forge = _binding.forge
32
31
  local name = _binding.name
@@ -34,8 +33,7 @@ do
34
33
  error("App name is required in Args constructor")
35
34
  end
36
35
  local px = usePx(target)
37
- self.Forge = forge
38
- self.root = root
36
+ self.forge = forge
39
37
  local _object = table.clone(props)
40
38
  setmetatable(_object, nil)
41
39
  _object.px = px
package/out/types.d.ts CHANGED
@@ -16,7 +16,6 @@ declare namespace Types {
16
16
  props: AppProps;
17
17
  forge: AppForge;
18
18
  target?: GuiObject | Camera;
19
- root?: ReactRoblox.Root;
20
19
  };
21
20
 
22
21
  type AppRegistry = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rbxts/app-forge",
3
- "version": "0.2.1",
3
+ "version": "0.2.3",
4
4
  "description": "An App Manager for react",
5
5
  "main": "out/init.lua",
6
6
  "packageManager": "bun@1.3.1",