@rbxts/app-forge 0.0.3 → 0.0.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 +316 -0
- package/package.json +57 -57
package/README.md
CHANGED
|
@@ -0,0 +1,316 @@
|
|
|
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 into `this`, such as:
|
|
108
|
+
|
|
109
|
+
```ts
|
|
110
|
+
this.props
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
You never manually `new` apps — AppForge constructs them automatically.
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
# 🕹 App Manager API
|
|
118
|
+
|
|
119
|
+
```ts
|
|
120
|
+
appManager.toggle("Inventory");
|
|
121
|
+
appManager.set("Shop", true);
|
|
122
|
+
appManager.set("HUD", false);
|
|
123
|
+
const isShown = appManager.getState("Pause");
|
|
124
|
+
const bind = appManager.getBind("Inventory");
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
# 📐 Using `getBind()` in React
|
|
130
|
+
|
|
131
|
+
```tsx
|
|
132
|
+
return (
|
|
133
|
+
<frame Visible={props.appManager.getBind("Inventory")}>
|
|
134
|
+
Items…
|
|
135
|
+
</frame>
|
|
136
|
+
);
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Binds re-render automatically on visibility change.
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
# ⌨️ Hotkey Toggle Example
|
|
144
|
+
|
|
145
|
+
```ts
|
|
146
|
+
UserInputService.InputBegan.Connect((input) => {
|
|
147
|
+
if (input.KeyCode === Enum.KeyCode.I)
|
|
148
|
+
appManager.toggle("Inventory");
|
|
149
|
+
});
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
# ⚖️ APP RULES SYSTEM
|
|
155
|
+
|
|
156
|
+
Rules control UI interaction & visibility behavior.
|
|
157
|
+
|
|
158
|
+
| Rule | Effect |
|
|
159
|
+
|---|---|
|
|
160
|
+
| blockedBy | Prevents opening if another app is active |
|
|
161
|
+
| blocks | Auto-closes another app when opened |
|
|
162
|
+
| exclusive | Closes ALL other apps except same group |
|
|
163
|
+
| groups | Logical grouping categories |
|
|
164
|
+
| layer | (Coming soon — currently not functional) |
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
## `blockedBy`
|
|
169
|
+
|
|
170
|
+
App cannot open if the listed apps are open.
|
|
171
|
+
|
|
172
|
+
```ts
|
|
173
|
+
@App({ name: "Inventory", rules: { blockedBy: "Shop" } })
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Multiple:
|
|
177
|
+
|
|
178
|
+
```ts
|
|
179
|
+
@App({ name: "Inventory", rules: { blockedBy: ["Shop", "Trade"] } })
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
---
|
|
183
|
+
|
|
184
|
+
## `blocks`
|
|
185
|
+
|
|
186
|
+
Opening the app will hide other apps.
|
|
187
|
+
|
|
188
|
+
```ts
|
|
189
|
+
@App({ name: "Shop", rules: { blocks: "Inventory" } })
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
Multiple:
|
|
193
|
+
|
|
194
|
+
```ts
|
|
195
|
+
@App({ name: "Shop", rules: { blocks: ["Inventory", "TeamUI"] } })
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
---
|
|
199
|
+
|
|
200
|
+
## `exclusive`
|
|
201
|
+
|
|
202
|
+
For fullscreen apps:
|
|
203
|
+
|
|
204
|
+
```ts
|
|
205
|
+
@App({ name: "Pause", rules: { exclusive: true } })
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
When Pause opens:
|
|
209
|
+
|
|
210
|
+
✔ Inventory closes
|
|
211
|
+
✔ HUD closes
|
|
212
|
+
✔ SideButtons closes
|
|
213
|
+
✔ Everything except Core
|
|
214
|
+
|
|
215
|
+
---
|
|
216
|
+
|
|
217
|
+
## `groups`
|
|
218
|
+
|
|
219
|
+
Apps inside the same group DO NOT block each other.
|
|
220
|
+
|
|
221
|
+
```ts
|
|
222
|
+
@App({ name: "HUD", rules: { groups: ["HUD"] } })
|
|
223
|
+
@App({ name: "Crosshair", rules: { groups: ["HUD"] } })
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
These can both be open at the same time.
|
|
227
|
+
|
|
228
|
+
---
|
|
229
|
+
|
|
230
|
+
## `Core` Group
|
|
231
|
+
|
|
232
|
+
Core apps are ALWAYS allowed and never auto-hidden.
|
|
233
|
+
|
|
234
|
+
```ts
|
|
235
|
+
@App({ name: "FPSCounter", rules: { groups: "Core" } })
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
This app will NEVER be closed by rules.
|
|
239
|
+
|
|
240
|
+
---
|
|
241
|
+
|
|
242
|
+
## `layer` *(not implemented yet)*
|
|
243
|
+
|
|
244
|
+
> The `layer` property is part of the upcoming UI priority system.
|
|
245
|
+
> Setting it currently does not affect rendering order.
|
|
246
|
+
|
|
247
|
+
Example (for future readiness):
|
|
248
|
+
|
|
249
|
+
```ts
|
|
250
|
+
@App({ name: "DebugUI", rules: { layer: 999 } })
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
---
|
|
254
|
+
|
|
255
|
+
# 🧪 Full Rules Example
|
|
256
|
+
|
|
257
|
+
```ts
|
|
258
|
+
@App({ name: "HUD", rules: { groups: ["HUD"] } })
|
|
259
|
+
@App({ name: "Inventory", rules: { blockedBy: "Shop", groups: ["Panel"] } })
|
|
260
|
+
@App({ name: "Shop", rules: { blocks: "Inventory", groups: ["Panel"] } })
|
|
261
|
+
@App({ name: "Pause", rules: { exclusive: true } })
|
|
262
|
+
@App({ name: "FPSCounter", rules: { groups: "Core" } })
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
Behavior:
|
|
266
|
+
|
|
267
|
+
| Action | Result |
|
|
268
|
+
| -------------- | -------------------------------- |
|
|
269
|
+
| Open Inventory | HUD + FPS stay |
|
|
270
|
+
| Open Shop | Inventory closes |
|
|
271
|
+
| Open Pause | ALL apps close except Core |
|
|
272
|
+
| Open HUD | Never conflicts with SideButtons |
|
|
273
|
+
| FPSCounter | Always visible |
|
|
274
|
+
|
|
275
|
+
---
|
|
276
|
+
|
|
277
|
+
# 🧠 Using AppForge in UI Components
|
|
278
|
+
|
|
279
|
+
```tsx
|
|
280
|
+
function MenuUI(props: AppProps) {
|
|
281
|
+
return (
|
|
282
|
+
<frame Visible={props.appManager.getBind("Shop")}>
|
|
283
|
+
Shop UI…
|
|
284
|
+
</frame>
|
|
285
|
+
);
|
|
286
|
+
}
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
---
|
|
290
|
+
|
|
291
|
+
# ❗ Best Practices
|
|
292
|
+
|
|
293
|
+
✔ Use `groups` for UI coexistence
|
|
294
|
+
✔ Use `exclusive` for fullscreen menus
|
|
295
|
+
✔ Use `blockedBy` for preventing conflicts
|
|
296
|
+
✔ Use `blocks` for auto-closing logic
|
|
297
|
+
✔ Use `Core` for never-closed apps
|
|
298
|
+
✔ Use `layer` for future-proofing (not active yet)
|
|
299
|
+
|
|
300
|
+
---
|
|
301
|
+
|
|
302
|
+
# 🛠 Future Roadmap
|
|
303
|
+
|
|
304
|
+
- [ ] layer / priority rendering
|
|
305
|
+
|
|
306
|
+
---
|
|
307
|
+
|
|
308
|
+
# ❤️ Contributing
|
|
309
|
+
|
|
310
|
+
Feel free to submit PRs, suggestions, or feature requests.
|
|
311
|
+
|
|
312
|
+
---
|
|
313
|
+
|
|
314
|
+
# 📄 License
|
|
315
|
+
|
|
316
|
+
MIT
|
package/package.json
CHANGED
|
@@ -1,59 +1,59 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
2
|
+
"name": "@rbxts/app-forge",
|
|
3
|
+
"version": "0.0.5",
|
|
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/object-utils": "*",
|
|
56
|
+
"@rbxts/react-roblox": "*",
|
|
57
|
+
"@rbxts/react": "*"
|
|
58
|
+
}
|
|
59
59
|
}
|