@rbxts/app-forge 0.7.1 → 0.7.2-alpha.2
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 +26 -13
- package/out/react/hooks/usePx.d.ts +15 -0
- package/out/react/hooks/usePx.luau +117 -0
- package/out/vide/classes/renders.d.ts +4 -0
- package/out/vide/classes/renders.luau +50 -45
- package/out/vide/classes/rules/exclusiveGroup.luau +22 -15
- package/out/vide/classes/rules/init.luau +17 -13
- package/out/vide/classes/rules/parent.luau +8 -4
- package/out/vide/context.d.ts +1 -1
- package/out/vide/debug/debugger.d.ts +18 -0
- package/out/vide/debug/debugger.luau +89 -0
- package/out/vide/debug/index.d.ts +3 -0
- package/out/vide/debug/init.luau +8 -0
- package/out/vide/debug/logger.d.ts +5 -0
- package/out/vide/debug/logger.luau +37 -0
- package/out/vide/decorator.d.ts +8 -0
- package/out/vide/decorator.luau +41 -4
- package/out/vide/hooks/useAppContext.d.ts +1 -1
- package/out/vide/hooks/useAppContext.luau +5 -1
- package/out/vide/hooks/useEventListener.d.ts +17 -0
- package/out/vide/hooks/useEventListener.luau +61 -0
- package/out/vide/hooks/usePx.d.ts +11 -0
- package/out/vide/hooks/usePx.luau +103 -0
- package/out/vide/index.d.ts +5 -2
- package/out/vide/init.luau +85 -24
- package/out/vide/types.d.ts +1 -1
- package/package.json +9 -3
package/README.md
CHANGED
|
@@ -32,14 +32,34 @@ If you’ve ever ended up with tangled UI state, duplicated visibility logic, or
|
|
|
32
32
|
|
|
33
33
|
## 📦 Installation
|
|
34
34
|
|
|
35
|
+
### Using **bun**
|
|
36
|
+
|
|
35
37
|
```bash
|
|
36
38
|
bun add @rbxts/app-forge
|
|
37
39
|
```
|
|
38
40
|
|
|
39
|
-
Peer dependencies:
|
|
41
|
+
Peer dependencies (choose one renderer):
|
|
40
42
|
|
|
41
43
|
```bash
|
|
42
|
-
bun add @rbxts/vide
|
|
44
|
+
bun add @rbxts/vide
|
|
45
|
+
# or
|
|
46
|
+
bun add @rbxts/react
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
### Using **npm**
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
npm install @rbxts/app-forge
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Peer dependencies (choose one renderer):
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
npm install @rbxts/vide
|
|
61
|
+
# or
|
|
62
|
+
npm install @rbxts/react
|
|
43
63
|
```
|
|
44
64
|
|
|
45
65
|
---
|
|
@@ -127,9 +147,7 @@ forge.mount(
|
|
|
127
147
|
ResetOnSpawn={false}
|
|
128
148
|
/>
|
|
129
149
|
),
|
|
130
|
-
|
|
131
|
-
props,
|
|
132
|
-
},
|
|
150
|
+
props,
|
|
133
151
|
Players.LocalPlayer.WaitForChild("PlayerGui"),
|
|
134
152
|
);
|
|
135
153
|
```
|
|
@@ -157,9 +175,7 @@ forge.mount(
|
|
|
157
175
|
ResetOnSpawn={false}
|
|
158
176
|
/>
|
|
159
177
|
),
|
|
160
|
-
|
|
161
|
-
props,
|
|
162
|
-
},
|
|
178
|
+
props,
|
|
163
179
|
Players.LocalPlayer.WaitForChild("PlayerGui"),
|
|
164
180
|
);
|
|
165
181
|
```
|
|
@@ -177,9 +193,7 @@ This:
|
|
|
177
193
|
```ts
|
|
178
194
|
forge.mount(
|
|
179
195
|
() => <screengui ResetOnSpawn={false} />,
|
|
180
|
-
|
|
181
|
-
props: {},
|
|
182
|
-
},
|
|
196
|
+
props,
|
|
183
197
|
playerGui,
|
|
184
198
|
);
|
|
185
199
|
```
|
|
@@ -352,7 +366,6 @@ AppForge provides `forge.story` for **isolated rendering**, commonly used with *
|
|
|
352
366
|
const forge = new CreateVideForge();
|
|
353
367
|
|
|
354
368
|
return forge.story({
|
|
355
|
-
forge,
|
|
356
369
|
props,
|
|
357
370
|
config: {
|
|
358
371
|
px: {
|
|
@@ -430,7 +443,7 @@ AppForge
|
|
|
430
443
|
|
|
431
444
|
## ⚛️ React Support (Planned)
|
|
432
445
|
|
|
433
|
-
AppForge is designed as a **renderer-agnostic App Manager**.
|
|
446
|
+
AppForge is designed as a **renderer-agnostic App Manager**.b
|
|
434
447
|
|
|
435
448
|
Currently:
|
|
436
449
|
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scaled pixel unit helper.
|
|
3
|
+
*/
|
|
4
|
+
export declare const px: ((value: number) => number) & {
|
|
5
|
+
scale: (value: number) => number;
|
|
6
|
+
even: (value: number) => number;
|
|
7
|
+
floor: (value: number) => number;
|
|
8
|
+
ceil: (value: number) => number;
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Initializes global px scaling.
|
|
12
|
+
*
|
|
13
|
+
* Should be called exactly once at app mount.
|
|
14
|
+
*/
|
|
15
|
+
export declare function usePx(target?: GuiObject | Camera, baseResolution?: Vector2, minScale?: number): void;
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local TS = _G[script]
|
|
3
|
+
-- Services
|
|
4
|
+
local Workspace = TS.import(script, TS.getModule(script, "@rbxts", "services")).Workspace
|
|
5
|
+
-- React
|
|
6
|
+
local useEffect = TS.import(script, TS.getModule(script, "@rbxts", "react")).useEffect
|
|
7
|
+
--* Default reference resolution for px calculations
|
|
8
|
+
local BASE_RESOLUTION = Vector2.new(1920, 1080)
|
|
9
|
+
--* Minimum allowed scale to prevent unreadable UI
|
|
10
|
+
local MIN_SCALE = 0.5
|
|
11
|
+
--[[
|
|
12
|
+
*
|
|
13
|
+
* Interpolates between width- and height-based scaling.
|
|
14
|
+
* 0 = width-driven, 1 = height-driven
|
|
15
|
+
|
|
16
|
+
]]
|
|
17
|
+
local DOMINANT_AXIS = 0.5
|
|
18
|
+
local TARGET = Workspace.CurrentCamera
|
|
19
|
+
local SCALE = 1
|
|
20
|
+
local GLOBAL_INITIALIZED = false
|
|
21
|
+
--[[
|
|
22
|
+
*
|
|
23
|
+
* Assigns a call signature to an object.
|
|
24
|
+
|
|
25
|
+
]]
|
|
26
|
+
local function callable(callback, object)
|
|
27
|
+
return setmetatable(object, {
|
|
28
|
+
__call = function(_, ...)
|
|
29
|
+
local args = { ... }
|
|
30
|
+
return callback(unpack(args))
|
|
31
|
+
end,
|
|
32
|
+
})
|
|
33
|
+
end
|
|
34
|
+
--[[
|
|
35
|
+
*
|
|
36
|
+
* Scaled pixel unit helper.
|
|
37
|
+
|
|
38
|
+
]]
|
|
39
|
+
local px = callable(function(value)
|
|
40
|
+
return math.round(value * SCALE)
|
|
41
|
+
end, {
|
|
42
|
+
scale = function(value)
|
|
43
|
+
return value * SCALE
|
|
44
|
+
end,
|
|
45
|
+
even = function(value)
|
|
46
|
+
return math.round(value * SCALE * 0.5) * 2
|
|
47
|
+
end,
|
|
48
|
+
floor = function(value)
|
|
49
|
+
return math.floor(value * SCALE)
|
|
50
|
+
end,
|
|
51
|
+
ceil = function(value)
|
|
52
|
+
return math.ceil(value * SCALE)
|
|
53
|
+
end,
|
|
54
|
+
})
|
|
55
|
+
--[[
|
|
56
|
+
*
|
|
57
|
+
* Recalculates the current scale factor.
|
|
58
|
+
|
|
59
|
+
]]
|
|
60
|
+
local function calculateScale()
|
|
61
|
+
local target = TARGET
|
|
62
|
+
if not target then
|
|
63
|
+
return nil
|
|
64
|
+
end
|
|
65
|
+
local size = if target:IsA("Camera") then target.ViewportSize elseif target:IsA("GuiObject") then target.AbsoluteSize else nil
|
|
66
|
+
if not size then
|
|
67
|
+
return nil
|
|
68
|
+
end
|
|
69
|
+
if BASE_RESOLUTION.X <= 0 or BASE_RESOLUTION.Y <= 0 then
|
|
70
|
+
return nil
|
|
71
|
+
end
|
|
72
|
+
local width = math.log(size.X / BASE_RESOLUTION.X, 2)
|
|
73
|
+
local height = math.log(size.Y / BASE_RESOLUTION.Y, 2)
|
|
74
|
+
local centered = width + (height - width) * DOMINANT_AXIS
|
|
75
|
+
local scale = 2 ^ centered
|
|
76
|
+
SCALE = math.max(scale, MIN_SCALE)
|
|
77
|
+
end
|
|
78
|
+
--[[
|
|
79
|
+
*
|
|
80
|
+
* Initializes global px scaling.
|
|
81
|
+
*
|
|
82
|
+
* Should be called exactly once at app mount.
|
|
83
|
+
|
|
84
|
+
]]
|
|
85
|
+
local function usePx(target, baseResolution, minScale)
|
|
86
|
+
useEffect(function()
|
|
87
|
+
if GLOBAL_INITIALIZED then
|
|
88
|
+
warn("usePx() may only be called once globally")
|
|
89
|
+
return nil
|
|
90
|
+
end
|
|
91
|
+
GLOBAL_INITIALIZED = true
|
|
92
|
+
if baseResolution then
|
|
93
|
+
BASE_RESOLUTION = baseResolution
|
|
94
|
+
end
|
|
95
|
+
if minScale ~= nil then
|
|
96
|
+
MIN_SCALE = minScale
|
|
97
|
+
end
|
|
98
|
+
if target then
|
|
99
|
+
TARGET = target
|
|
100
|
+
end
|
|
101
|
+
local resolvedTarget = TARGET
|
|
102
|
+
if not resolvedTarget then
|
|
103
|
+
warn("usePx(): no valid target to observe")
|
|
104
|
+
return nil
|
|
105
|
+
end
|
|
106
|
+
local signal = if resolvedTarget:IsA("Camera") then resolvedTarget:GetPropertyChangedSignal("ViewportSize") else resolvedTarget:GetPropertyChangedSignal("AbsoluteSize")
|
|
107
|
+
local connection = signal:Connect(calculateScale)
|
|
108
|
+
calculateScale()
|
|
109
|
+
return function()
|
|
110
|
+
connection:Disconnect()
|
|
111
|
+
end
|
|
112
|
+
end, {})
|
|
113
|
+
end
|
|
114
|
+
return {
|
|
115
|
+
usePx = usePx,
|
|
116
|
+
px = px,
|
|
117
|
+
}
|
|
@@ -8,6 +8,10 @@ export default class Renders extends Rules {
|
|
|
8
8
|
render: Vide.Node;
|
|
9
9
|
}>;
|
|
10
10
|
constructor();
|
|
11
|
+
/**
|
|
12
|
+
* Entry point for mounting renders.
|
|
13
|
+
* Decides render strategy based on props.
|
|
14
|
+
*/
|
|
11
15
|
protected renderMount(this: AppForge, props: Types.Props.Main): Vide.Node;
|
|
12
16
|
private renderNames;
|
|
13
17
|
private collectByGroup;
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
-- Compiled with roblox-ts v3.0.0
|
|
2
2
|
local TS = _G[script]
|
|
3
|
-
-- Services
|
|
4
3
|
-- Packages
|
|
5
|
-
local usePx = TS.import(script, TS.getModule(script, "@rbxts", "loners-pretty-vide-utils").out).usePx
|
|
6
4
|
local _vide = TS.import(script, TS.getModule(script, "@rbxts", "vide").src)
|
|
7
5
|
local apply = _vide.apply
|
|
8
6
|
local create = _vide.create
|
|
9
7
|
-- Types
|
|
10
8
|
-- Components
|
|
11
9
|
local AppRegistry = TS.import(script, script.Parent.Parent, "decorator").AppRegistry
|
|
10
|
+
-- Hooks
|
|
11
|
+
local usePx = TS.import(script, script.Parent.Parent, "hooks", "usePx").usePx
|
|
12
12
|
-- Classes
|
|
13
13
|
local Rules = TS.import(script, script.Parent, "rules").default
|
|
14
14
|
local Renders
|
|
@@ -35,22 +35,28 @@ do
|
|
|
35
35
|
local render = _binding.render
|
|
36
36
|
local forge = _binding.forge
|
|
37
37
|
if not forge.__px then
|
|
38
|
+
local _debug = forge.debug
|
|
38
39
|
local _result = config
|
|
39
40
|
if _result ~= nil then
|
|
40
|
-
_result = _result.px
|
|
41
|
+
_result = _result.px
|
|
41
42
|
end
|
|
43
|
+
_debug:logTag("px", "global", "Initializing px scaling", _result)
|
|
42
44
|
local _result_1 = config
|
|
43
45
|
if _result_1 ~= nil then
|
|
44
|
-
_result_1 = _result_1.px.
|
|
46
|
+
_result_1 = _result_1.px.target
|
|
45
47
|
end
|
|
46
48
|
local _result_2 = config
|
|
47
49
|
if _result_2 ~= nil then
|
|
48
|
-
_result_2 = _result_2.px.
|
|
50
|
+
_result_2 = _result_2.px.resolution
|
|
49
51
|
end
|
|
50
|
-
|
|
52
|
+
local _result_3 = config
|
|
53
|
+
if _result_3 ~= nil then
|
|
54
|
+
_result_3 = _result_3.px.minScale
|
|
55
|
+
end
|
|
56
|
+
usePx(_result_1, _result_2, _result_3)
|
|
51
57
|
forge.__px = true
|
|
52
58
|
else
|
|
53
|
-
|
|
59
|
+
forge.debug:logTag("px", "global", "Skipped duplicate px initialization")
|
|
54
60
|
end
|
|
55
61
|
if render then
|
|
56
62
|
local _condition = render.name
|
|
@@ -58,34 +64,35 @@ do
|
|
|
58
64
|
_condition = render.group
|
|
59
65
|
end
|
|
60
66
|
if _condition ~= "" and _condition then
|
|
67
|
+
forge.debug:logTag("render", "global", "Rendering group by name", render)
|
|
61
68
|
return forge:renderGroupByName(props)
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
end
|
|
69
|
+
end
|
|
70
|
+
local _value = render.names and render.group
|
|
71
|
+
if _value ~= "" and _value then
|
|
72
|
+
forge.debug:logTag("render", "global", "Rendering group by names", render)
|
|
73
|
+
return forge:renderGroupByNames(props)
|
|
74
|
+
end
|
|
75
|
+
local _value_1 = render.name
|
|
76
|
+
if _value_1 ~= "" and _value_1 then
|
|
77
|
+
forge.debug:logTag("render", render.name, "Rendering single app")
|
|
78
|
+
return forge:renderApp(props)
|
|
79
|
+
end
|
|
80
|
+
if render.names then
|
|
81
|
+
forge.debug:logTag("render", "global", "Rendering multiple apps", render.names)
|
|
82
|
+
return forge:renderApps(props)
|
|
83
|
+
end
|
|
84
|
+
local _value_2 = render.group
|
|
85
|
+
if _value_2 ~= "" and _value_2 then
|
|
86
|
+
forge.debug:logTag("render", "global", "Rendering group", render.group)
|
|
87
|
+
return forge:renderGroup(props)
|
|
82
88
|
end
|
|
83
89
|
end
|
|
90
|
+
forge.debug:logTag("render", "global", "Rendering all apps")
|
|
84
91
|
return self:renderAll(props)
|
|
85
92
|
end
|
|
86
93
|
function Renders:renderNames(props, names, forge)
|
|
87
94
|
if #names == 0 then
|
|
88
|
-
error("No app names provided to
|
|
95
|
+
error("No app names provided to render", 2)
|
|
89
96
|
end
|
|
90
97
|
-- ▼ ReadonlyArray.map ▼
|
|
91
98
|
local _newValue = table.create(#names)
|
|
@@ -141,22 +148,21 @@ do
|
|
|
141
148
|
return _result
|
|
142
149
|
end
|
|
143
150
|
function Renders:renderApp(props)
|
|
144
|
-
local
|
|
145
|
-
local forge = _binding.forge
|
|
146
|
-
local render = _binding.render
|
|
147
|
-
local _name = render
|
|
151
|
+
local _name = props.render
|
|
148
152
|
if _name ~= nil then
|
|
149
153
|
_name = _name.name
|
|
150
154
|
end
|
|
151
155
|
local name = _name
|
|
152
156
|
if not (name ~= "" and name) then
|
|
153
|
-
error("
|
|
157
|
+
error("renderApp requires an app name", 2)
|
|
154
158
|
end
|
|
155
159
|
local appClass = AppRegistry[name]
|
|
156
160
|
if not appClass then
|
|
157
|
-
error(`App "{name}" not registered
|
|
161
|
+
error(`App "{name}" not registered`, 2)
|
|
158
162
|
end
|
|
159
|
-
|
|
163
|
+
self.debug:time("render", name)
|
|
164
|
+
if not (self.loaded[name] ~= nil) then
|
|
165
|
+
self.debug:logTag("render", name, "Creating render instance")
|
|
160
166
|
local render = appClass.constructor.new(props, name):render()
|
|
161
167
|
apply(render)({
|
|
162
168
|
Name = "Render",
|
|
@@ -169,19 +175,18 @@ do
|
|
|
169
175
|
Size = UDim2.fromScale(1, 1),
|
|
170
176
|
[0] = render,
|
|
171
177
|
})
|
|
172
|
-
local _loaded =
|
|
178
|
+
local _loaded = self.loaded
|
|
173
179
|
local _arg1 = {
|
|
174
180
|
container = container,
|
|
175
181
|
render = render,
|
|
176
182
|
}
|
|
177
183
|
_loaded[name] = _arg1
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
if not element then
|
|
181
|
-
error(`Failed to create instance for app "{name}"`)
|
|
184
|
+
else
|
|
185
|
+
self.debug:logTag("render", name, "Reusing existing render instance")
|
|
182
186
|
end
|
|
183
187
|
self:renderRules(name, props)
|
|
184
|
-
|
|
188
|
+
self.debug:timeEnd("render", name)
|
|
189
|
+
return self.loaded[name].container
|
|
185
190
|
end
|
|
186
191
|
function Renders:renderApps(props)
|
|
187
192
|
local _names = props.render
|
|
@@ -190,7 +195,7 @@ do
|
|
|
190
195
|
end
|
|
191
196
|
local names = _names
|
|
192
197
|
if not names then
|
|
193
|
-
error("
|
|
198
|
+
error("renderApps requires app names", 2)
|
|
194
199
|
end
|
|
195
200
|
return self:renderNames(props, names, self)
|
|
196
201
|
end
|
|
@@ -201,7 +206,7 @@ do
|
|
|
201
206
|
end
|
|
202
207
|
local group = _group
|
|
203
208
|
if not (group ~= "" and group) then
|
|
204
|
-
error("
|
|
209
|
+
error("renderGroup requires a group", 2)
|
|
205
210
|
end
|
|
206
211
|
local groups = self:normalizeGroups(group)
|
|
207
212
|
return self:renderNames(props, self:collectByGroup(groups), self)
|
|
@@ -211,7 +216,7 @@ do
|
|
|
211
216
|
local group = _binding.group
|
|
212
217
|
local name = _binding.name
|
|
213
218
|
if not (group ~= "" and group) or not (name ~= "" and name) then
|
|
214
|
-
error("Invalid renderGroupByName")
|
|
219
|
+
error("Invalid renderGroupByName call", 2)
|
|
215
220
|
end
|
|
216
221
|
local groups = self:normalizeGroups(group)
|
|
217
222
|
return self:renderNames(props, self:collectByGroup(groups, function(n)
|
|
@@ -223,7 +228,7 @@ do
|
|
|
223
228
|
local group = _binding.group
|
|
224
229
|
local names = _binding.names
|
|
225
230
|
if not (group ~= "" and group) or not names then
|
|
226
|
-
error("Invalid renderGroupByNames")
|
|
231
|
+
error("Invalid renderGroupByNames call", 2)
|
|
227
232
|
end
|
|
228
233
|
local groups = self:normalizeGroups(group)
|
|
229
234
|
return self:renderNames(props, self:collectByGroup(groups, function(n)
|
|
@@ -6,36 +6,43 @@ local AppRegistry = TS.import(script, script.Parent.Parent.Parent, "decorator").
|
|
|
6
6
|
local function ExclusiveGroupRule(entry, forge)
|
|
7
7
|
local _entry = entry
|
|
8
8
|
local entryApp = AppRegistry[_entry]
|
|
9
|
-
local
|
|
10
|
-
if
|
|
11
|
-
|
|
12
|
-
if
|
|
13
|
-
|
|
9
|
+
local _group = entryApp
|
|
10
|
+
if _group ~= nil then
|
|
11
|
+
_group = _group.rules
|
|
12
|
+
if _group ~= nil then
|
|
13
|
+
_group = _group.exclusiveGroup
|
|
14
14
|
end
|
|
15
15
|
end
|
|
16
|
-
|
|
16
|
+
local group = _group
|
|
17
|
+
if not (group ~= "" and group) then
|
|
17
18
|
return nil
|
|
18
19
|
end
|
|
19
|
-
local
|
|
20
|
-
|
|
21
|
-
if not entrySource then
|
|
20
|
+
local entryVisible = forge:getSource(entry)()
|
|
21
|
+
if not entryVisible then
|
|
22
22
|
return nil
|
|
23
23
|
end
|
|
24
|
+
forge.debug:logTag("rules", entry, "Exclusive group activated", group)
|
|
24
25
|
-- ▼ ReadonlyMap.forEach ▼
|
|
25
26
|
local _callback = function(app, name)
|
|
26
27
|
if name == entry then
|
|
27
28
|
return nil
|
|
28
29
|
end
|
|
29
|
-
local
|
|
30
|
-
if
|
|
31
|
-
|
|
30
|
+
local _result = app.rules
|
|
31
|
+
if _result ~= nil then
|
|
32
|
+
_result = _result.exclusiveGroup
|
|
32
33
|
end
|
|
33
|
-
if
|
|
34
|
+
if _result ~= group then
|
|
34
35
|
return nil
|
|
35
36
|
end
|
|
36
|
-
|
|
37
|
-
|
|
37
|
+
local visible = forge:getSource(name)()
|
|
38
|
+
if not visible then
|
|
39
|
+
return nil
|
|
38
40
|
end
|
|
41
|
+
forge.debug:logTag("rules", entry, "Closing app due to exclusive group", {
|
|
42
|
+
closed = name,
|
|
43
|
+
group = group,
|
|
44
|
+
})
|
|
45
|
+
forge:close(name, false)
|
|
39
46
|
end
|
|
40
47
|
for _k, _v in AppRegistry do
|
|
41
48
|
_callback(_v, _k, AppRegistry)
|
|
@@ -25,36 +25,40 @@ do
|
|
|
25
25
|
local _name = name
|
|
26
26
|
local appClass = AppRegistry[_name]
|
|
27
27
|
if not appClass then
|
|
28
|
-
error(`
|
|
28
|
+
error(`renderRules: App "{name}" not registered`, 2)
|
|
29
29
|
end
|
|
30
|
-
local
|
|
31
|
-
if
|
|
32
|
-
|
|
30
|
+
local rules = appClass.rules
|
|
31
|
+
if not rules then
|
|
32
|
+
return nil
|
|
33
33
|
end
|
|
34
|
-
|
|
34
|
+
-- Parent Anchor
|
|
35
|
+
local _condition = rules.parent
|
|
35
36
|
if _condition ~= "" and _condition then
|
|
36
|
-
_condition = not
|
|
37
|
+
_condition = not rules.detach
|
|
37
38
|
end
|
|
38
39
|
if _condition ~= "" and _condition then
|
|
39
|
-
self:
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
_result_1 = _result_1.index
|
|
40
|
+
self.debug:logTag("rules", name, "Applying parent anchor", {
|
|
41
|
+
parent = rules.parent,
|
|
42
|
+
})
|
|
43
|
+
self:anchor(name, rules.parent, props)
|
|
44
44
|
end
|
|
45
|
-
|
|
46
|
-
|
|
45
|
+
-- Index
|
|
46
|
+
if rules.index ~= nil then
|
|
47
|
+
self.debug:logTag("rules", name, "Applying ZIndex", rules.index)
|
|
48
|
+
self:index(name, rules.index)
|
|
47
49
|
end
|
|
48
50
|
end
|
|
49
51
|
function Rules:checkRules(name)
|
|
50
52
|
local _processing = self.processing
|
|
51
53
|
local _name = name
|
|
52
54
|
if _processing[_name] ~= nil then
|
|
55
|
+
self.debug:logTag("rules", name, "Skipped rule processing (cycle detected)")
|
|
53
56
|
return nil
|
|
54
57
|
end
|
|
55
58
|
local _processing_1 = self.processing
|
|
56
59
|
local _name_1 = name
|
|
57
60
|
_processing_1[_name_1] = true
|
|
61
|
+
self.debug:logTag("rules", name, "Evaluating rules")
|
|
58
62
|
TS.try(function()
|
|
59
63
|
ParentRule(name, self)
|
|
60
64
|
ExclusiveGroupRule(name, self)
|
|
@@ -4,7 +4,7 @@ local TS = _G[script]
|
|
|
4
4
|
-- Components
|
|
5
5
|
local AppRegistry = TS.import(script, script.Parent.Parent.Parent, "decorator").AppRegistry
|
|
6
6
|
local function ParentRule(entry, forge)
|
|
7
|
-
local
|
|
7
|
+
local entryVisible = forge:getSource(entry)()
|
|
8
8
|
-- ▼ ReadonlyMap.forEach ▼
|
|
9
9
|
local _callback = function(app, name)
|
|
10
10
|
local rules = app.rules
|
|
@@ -14,10 +14,14 @@ local function ParentRule(entry, forge)
|
|
|
14
14
|
if name == entry then
|
|
15
15
|
return nil
|
|
16
16
|
end
|
|
17
|
-
local
|
|
18
|
-
if
|
|
19
|
-
|
|
17
|
+
local childVisible = forge:getSource(name)()
|
|
18
|
+
if entryVisible or not childVisible then
|
|
19
|
+
return nil
|
|
20
20
|
end
|
|
21
|
+
forge.debug:logTag("rules", entry, "Closing child app (parent closed)", {
|
|
22
|
+
child = name,
|
|
23
|
+
})
|
|
24
|
+
forge:close(name, false)
|
|
21
25
|
end
|
|
22
26
|
for _k, _v in AppRegistry do
|
|
23
27
|
_callback(_v, _k, AppRegistry)
|
package/out/vide/context.d.ts
CHANGED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export type DebugTag = "render" | "rules" | "state" | "px" | "lifecycle";
|
|
2
|
+
type LogFn = (level: "DEBUG" | "PERF", message: string, data?: unknown, traceback?: string) => void;
|
|
3
|
+
export default class Debugger {
|
|
4
|
+
private readonly log;
|
|
5
|
+
private timers;
|
|
6
|
+
private enabled;
|
|
7
|
+
private allEnabled;
|
|
8
|
+
constructor(log: LogFn);
|
|
9
|
+
enable(tag: DebugTag): void;
|
|
10
|
+
disable(tag: DebugTag): void;
|
|
11
|
+
enableAll(): void;
|
|
12
|
+
disableAll(): void;
|
|
13
|
+
isEnabled(tag: DebugTag): boolean;
|
|
14
|
+
logTag(tag: DebugTag, app: AppNames, message: string, data?: unknown): void;
|
|
15
|
+
time(tag: DebugTag, app: AppNames): void;
|
|
16
|
+
timeEnd(tag: DebugTag, app: AppNames): void;
|
|
17
|
+
}
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local TS = _G[script]
|
|
3
|
+
-- Services
|
|
4
|
+
local RunService = TS.import(script, TS.getModule(script, "@rbxts", "services")).RunService
|
|
5
|
+
local Debugger
|
|
6
|
+
do
|
|
7
|
+
Debugger = setmetatable({}, {
|
|
8
|
+
__tostring = function()
|
|
9
|
+
return "Debugger"
|
|
10
|
+
end,
|
|
11
|
+
})
|
|
12
|
+
Debugger.__index = Debugger
|
|
13
|
+
function Debugger.new(...)
|
|
14
|
+
local self = setmetatable({}, Debugger)
|
|
15
|
+
return self:constructor(...) or self
|
|
16
|
+
end
|
|
17
|
+
function Debugger:constructor(log)
|
|
18
|
+
self.log = log
|
|
19
|
+
self.timers = {}
|
|
20
|
+
self.enabled = {}
|
|
21
|
+
self.allEnabled = false
|
|
22
|
+
end
|
|
23
|
+
function Debugger:enable(tag)
|
|
24
|
+
local _enabled = self.enabled
|
|
25
|
+
local _tag = tag
|
|
26
|
+
_enabled[_tag] = true
|
|
27
|
+
end
|
|
28
|
+
function Debugger:disable(tag)
|
|
29
|
+
local _enabled = self.enabled
|
|
30
|
+
local _tag = tag
|
|
31
|
+
_enabled[_tag] = nil
|
|
32
|
+
end
|
|
33
|
+
function Debugger:enableAll()
|
|
34
|
+
self.allEnabled = true
|
|
35
|
+
end
|
|
36
|
+
function Debugger:disableAll()
|
|
37
|
+
self.allEnabled = false
|
|
38
|
+
table.clear(self.enabled)
|
|
39
|
+
end
|
|
40
|
+
function Debugger:isEnabled(tag)
|
|
41
|
+
local _condition = self.allEnabled
|
|
42
|
+
if not _condition then
|
|
43
|
+
local _enabled = self.enabled
|
|
44
|
+
local _tag = tag
|
|
45
|
+
_condition = _enabled[_tag] ~= nil
|
|
46
|
+
end
|
|
47
|
+
return _condition
|
|
48
|
+
end
|
|
49
|
+
function Debugger:logTag(tag, app, message, data)
|
|
50
|
+
if not RunService:IsStudio() then
|
|
51
|
+
return nil
|
|
52
|
+
end
|
|
53
|
+
if not self:isEnabled(tag) then
|
|
54
|
+
return nil
|
|
55
|
+
end
|
|
56
|
+
self.log("DEBUG", `[{tag}][{app}] {message}`, data, debug.traceback(nil, 3))
|
|
57
|
+
end
|
|
58
|
+
function Debugger:time(tag, app)
|
|
59
|
+
if not RunService:IsStudio() then
|
|
60
|
+
return nil
|
|
61
|
+
end
|
|
62
|
+
if not self:isEnabled(tag) then
|
|
63
|
+
return nil
|
|
64
|
+
end
|
|
65
|
+
local _timers = self.timers
|
|
66
|
+
local _arg0 = `{tag}:{app}`
|
|
67
|
+
local _arg1 = os.clock()
|
|
68
|
+
_timers[_arg0] = _arg1
|
|
69
|
+
end
|
|
70
|
+
function Debugger:timeEnd(tag, app)
|
|
71
|
+
if not RunService:IsStudio() then
|
|
72
|
+
return nil
|
|
73
|
+
end
|
|
74
|
+
if not self:isEnabled(tag) then
|
|
75
|
+
return nil
|
|
76
|
+
end
|
|
77
|
+
local key = `{tag}:{app}`
|
|
78
|
+
local start = self.timers[key]
|
|
79
|
+
if start == nil then
|
|
80
|
+
return nil
|
|
81
|
+
end
|
|
82
|
+
self.timers[key] = nil
|
|
83
|
+
local elapsed = os.clock() - start
|
|
84
|
+
self.log("PERF", `[{tag}][{app}] {string.format("%.3fms", elapsed * 1000)}`)
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
return {
|
|
88
|
+
default = Debugger,
|
|
89
|
+
}
|