@rbxts/app-forge 0.2.2 → 0.2.5
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 +99 -152
- package/out/decorator.d.ts +1 -1
- package/out/decorator.luau +1 -1
- package/package.json +1 -1
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
|
-
|
|
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
|
-
>
|
|
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
|
|
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
|
|
52
|
+
const forge = new AppForge();
|
|
62
53
|
const root = createRoot(new Instance("Folder"));
|
|
63
54
|
|
|
64
|
-
const
|
|
55
|
+
const target = Workspace.CurrentCamera!
|
|
56
|
+
|
|
57
|
+
const props = {
|
|
65
58
|
player: Players.LocalPlayer!,
|
|
66
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
92
|
+
---
|
|
106
93
|
|
|
107
|
-
|
|
94
|
+
# 📦 Props & `Args` Features
|
|
95
|
+
|
|
96
|
+
Inside a decorated App:
|
|
108
97
|
|
|
109
98
|
```ts
|
|
110
|
-
this.
|
|
111
|
-
this.name
|
|
112
|
-
this.props
|
|
113
|
-
this.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
|
-
|
|
116
|
-
const { px } = this.props
|
|
105
|
+
Example:
|
|
117
106
|
|
|
118
|
-
|
|
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
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
const
|
|
132
|
-
const bind =
|
|
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()`
|
|
126
|
+
# 📐 Using `getBind()` inside React
|
|
138
127
|
|
|
139
128
|
```tsx
|
|
140
129
|
return (
|
|
141
|
-
<frame Visible={
|
|
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
|
-
|
|
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
|
|
151
|
+
Rules control app visibility behavior.
|
|
165
152
|
|
|
166
|
-
| Rule
|
|
167
|
-
|
|
168
|
-
| blockedBy | Prevents opening if another
|
|
169
|
-
| blocks
|
|
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
|
|
172
|
-
|
|
|
158
|
+
| groups | Non-conflicting coexistence grouping |
|
|
159
|
+
| Core | Always allowed — never auto-closed |
|
|
160
|
+
| layer | (Reserved – future rendering priority) |
|
|
173
161
|
|
|
174
162
|
---
|
|
175
163
|
|
|
176
|
-
## `
|
|
177
|
-
|
|
178
|
-
App cannot open if the listed apps are open.
|
|
164
|
+
## `groups`
|
|
179
165
|
|
|
180
166
|
```ts
|
|
181
|
-
@App({ name: "
|
|
167
|
+
@App({ name: "HUD", rules: { groups: "HUD" } })
|
|
168
|
+
@App({ name: "Crosshair", rules: { groups: "HUD" } })
|
|
182
169
|
```
|
|
183
170
|
|
|
184
|
-
|
|
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
|
-
## `
|
|
193
|
-
|
|
194
|
-
Opening the app will hide other apps.
|
|
175
|
+
## `Core`
|
|
195
176
|
|
|
196
177
|
```ts
|
|
197
|
-
@App({ name: "
|
|
178
|
+
@App({ name: "FPSCounter", rules: { groups: "Core" } })
|
|
198
179
|
```
|
|
199
180
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
```ts
|
|
203
|
-
@App({ name: "Shop", rules: { blocks: ["Inventory", "TeamUI"] } })
|
|
204
|
-
```
|
|
181
|
+
Never closed by rules.
|
|
205
182
|
|
|
206
183
|
---
|
|
207
184
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
For fullscreen apps:
|
|
185
|
+
# 🧪 Modern App Example (from your snippet)
|
|
211
186
|
|
|
212
187
|
```ts
|
|
213
|
-
@App({ name: "
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
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
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
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
|
-
|
|
239
|
-
|
|
240
|
-
Core apps are ALWAYS allowed and never auto-hidden.
|
|
204
|
+
# 🧠 Using AppForge from Flamework
|
|
241
205
|
|
|
242
206
|
```ts
|
|
243
|
-
|
|
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
|
-
|
|
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
|
-
|
|
232
|
+
You can manually choose which apps render:
|
|
256
233
|
|
|
257
|
-
```
|
|
258
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
302
|
-
✔ Use `
|
|
303
|
-
✔ Use `
|
|
304
|
-
✔ Use `
|
|
305
|
-
✔ Use `Core` for never-
|
|
306
|
-
✔
|
|
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
|
-
|
|
259
|
+
* [ ] UI layering & depth priority
|
|
313
260
|
|
|
314
261
|
---
|
|
315
262
|
|
|
316
263
|
# ❤️ Contributing
|
|
317
264
|
|
|
318
|
-
|
|
265
|
+
PRs and suggestions welcome!
|
|
319
266
|
|
|
320
267
|
---
|
|
321
268
|
|
package/out/decorator.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ import type AppForge from ".";
|
|
|
5
5
|
export declare const AppRegistry: Map<string, Types.AppRegistry>;
|
|
6
6
|
export declare function App(props: Types.AppRegistryProps): <T extends new (props: Types.MainProps) => Args>(constructor: T) => T;
|
|
7
7
|
export declare abstract class Args {
|
|
8
|
-
readonly
|
|
8
|
+
readonly forge: AppForge;
|
|
9
9
|
readonly props: AppProps & {
|
|
10
10
|
px: ReturnType<typeof usePx>;
|
|
11
11
|
};
|
package/out/decorator.luau
CHANGED