@rbxts/toucan-debugger 0.0.0-stage → 1.0.1
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/LICENSE +21 -0
- package/README.md +28 -2
- package/out/collapsible.d.ts +7 -0
- package/out/collapsible.luau +47 -0
- package/out/drawEntityEntries.d.ts +4 -0
- package/out/drawEntityEntries.luau +97 -0
- package/out/index.d.ts +18 -0
- package/out/init.luau +52 -0
- package/out/instance.d.ts +63 -0
- package/out/instance.luau +138 -0
- package/out/themes/black.d.ts +3 -0
- package/out/themes/black.luau +50 -0
- package/out/themes/index.d.ts +49 -0
- package/out/themes/init.luau +13 -0
- package/out/themes/white.d.ts +3 -0
- package/out/themes/white.luau +50 -0
- package/out/ui/explorer/dashboard.d.ts +3 -0
- package/out/ui/explorer/dashboard.luau +14 -0
- package/out/ui/explorer/entityEntry.d.ts +5 -0
- package/out/ui/explorer/entityEntry.luau +182 -0
- package/out/ui/explorer/entityList.d.ts +4 -0
- package/out/ui/explorer/entityList.luau +36 -0
- package/out/ui/explorer/index.d.ts +3 -0
- package/out/ui/explorer/init.luau +22 -0
- package/out/ui/footer.d.ts +3 -0
- package/out/ui/footer.luau +15 -0
- package/out/ui/hero.d.ts +3 -0
- package/out/ui/hero.luau +28 -0
- package/out/ui/index.d.ts +2 -0
- package/out/ui/init.luau +29 -0
- package/out/updateRuntimeLabel.d.ts +2 -0
- package/out/updateRuntimeLabel.luau +37 -0
- package/package.json +39 -5
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Over
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,29 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @rbxts/toucan-debugger
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A visual debugger plugin for [Toucan](https://github.com/OverlineJunior/toucan).
|
|
4
|
+
|
|
5
|
+
It is currently very early in development and cannot observe the server. It can however:
|
|
6
|
+
|
|
7
|
+
- List every entity-like along its id and type icon;
|
|
8
|
+
- Display `ChildOf` hierarchies;
|
|
9
|
+
- Measure system runtimes (they're under schedules).
|
|
10
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
|
|
13
|
+
```sh
|
|
14
|
+
npm install @rbxts/toucan-debugger
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
> [!IMPORTANT]
|
|
18
|
+
> The version of Toucan your game uses must be the same as the one this debugger plugin uses. This package currently targets `@rbxts/toucan` `0.6.0`.
|
|
19
|
+
|
|
20
|
+
## Usage
|
|
21
|
+
|
|
22
|
+
Pass the plugin to your scheduler before calling `run()`. It must be used on the client.
|
|
23
|
+
|
|
24
|
+
```ts
|
|
25
|
+
import toucanDebugger, { themes } from "@rbxts/toucan-debugger";
|
|
26
|
+
|
|
27
|
+
// Passing a theme is optional — defaults to black.
|
|
28
|
+
scheduler().usePlugin(toucanDebugger, themes.black).run();
|
|
29
|
+
```
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local TS = _G[script]
|
|
3
|
+
local _toucan = TS.import(script, TS.getModule(script, "@rbxts", "toucan").out)
|
|
4
|
+
local component = _toucan.component
|
|
5
|
+
local query = _toucan.query
|
|
6
|
+
local Collapsible = component("Collapsible")
|
|
7
|
+
-- TODO: This is temporary - use `useEvent` or another approach.
|
|
8
|
+
local Done = component("Done")
|
|
9
|
+
local function updateCollapsible(theme)
|
|
10
|
+
query(Collapsible):forEach(function(e, _param)
|
|
11
|
+
local toggleButton = _param.toggleButton
|
|
12
|
+
local chevron = _param.chevron
|
|
13
|
+
local body = _param.body
|
|
14
|
+
local _exp = body:GetChildren()
|
|
15
|
+
-- ▼ ReadonlyArray.some ▼
|
|
16
|
+
local _result = false
|
|
17
|
+
local _callback = function(c)
|
|
18
|
+
return c:IsA("GuiObject")
|
|
19
|
+
end
|
|
20
|
+
for _k, _v in _exp do
|
|
21
|
+
if _callback(_v, _k - 1, _exp) then
|
|
22
|
+
_result = true
|
|
23
|
+
break
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
-- ▲ ReadonlyArray.some ▲
|
|
27
|
+
local hasContent = _result
|
|
28
|
+
chevron.Visible = hasContent
|
|
29
|
+
body.Visible = if hasContent then body.Visible else false
|
|
30
|
+
if not e:has(Done) then
|
|
31
|
+
-- Start closed.
|
|
32
|
+
body.Visible = false
|
|
33
|
+
toggleButton.MouseButton1Click:Connect(function()
|
|
34
|
+
if not hasContent then
|
|
35
|
+
return nil
|
|
36
|
+
end
|
|
37
|
+
body.Visible = not body.Visible
|
|
38
|
+
chevron.Image = if body.Visible then theme.icons.chevronDown else theme.icons.chevronRight
|
|
39
|
+
end)
|
|
40
|
+
e:set(Done)
|
|
41
|
+
end
|
|
42
|
+
end)
|
|
43
|
+
end
|
|
44
|
+
return {
|
|
45
|
+
updateCollapsible = updateCollapsible,
|
|
46
|
+
Collapsible = Collapsible,
|
|
47
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local TS = _G[script]
|
|
3
|
+
local _toucan = TS.import(script, TS.getModule(script, "@rbxts", "toucan").out)
|
|
4
|
+
local Builtin = _toucan.Builtin
|
|
5
|
+
local component = _toucan.component
|
|
6
|
+
local pair = _toucan.pair
|
|
7
|
+
local query = _toucan.query
|
|
8
|
+
local _instance = TS.import(script, script.Parent, "instance")
|
|
9
|
+
local Frame = _instance.Frame
|
|
10
|
+
local ScrollingFrame = _instance.ScrollingFrame
|
|
11
|
+
local _entityEntry = TS.import(script, script.Parent, "ui", "explorer", "entityEntry")
|
|
12
|
+
local entityEntry = _entityEntry.default
|
|
13
|
+
local NoEntry = _entityEntry.NoEntry
|
|
14
|
+
local EntityList = TS.import(script, script.Parent, "ui", "explorer", "entityList").EntityList
|
|
15
|
+
local EntryFor = component("EntryFor")
|
|
16
|
+
local seen = {}
|
|
17
|
+
local function canPlaceEntry(e)
|
|
18
|
+
local _condition = not e:has(NoEntry)
|
|
19
|
+
if _condition then
|
|
20
|
+
local _id = e.id
|
|
21
|
+
_condition = not (seen[_id] ~= nil)
|
|
22
|
+
end
|
|
23
|
+
return _condition
|
|
24
|
+
end
|
|
25
|
+
local function drawEntityEntries(theme)
|
|
26
|
+
local placeEntriesDeep
|
|
27
|
+
placeEntriesDeep = function(e, _param)
|
|
28
|
+
local parentEntt = _param[1]
|
|
29
|
+
local parentInst = _param[2]
|
|
30
|
+
local _binding = entityEntry(e, theme)
|
|
31
|
+
local _binding_1 = _binding[1]
|
|
32
|
+
local entryEntt = _binding_1[1]
|
|
33
|
+
local entryInst = _binding_1[2]
|
|
34
|
+
local body = _binding[2]
|
|
35
|
+
entryEntt:set(pair(Builtin.ChildOf, parentEntt))
|
|
36
|
+
entryEntt:set(pair(EntryFor, e))
|
|
37
|
+
entryInst.Parent = parentInst
|
|
38
|
+
local _id = e.id
|
|
39
|
+
seen[_id] = true
|
|
40
|
+
local _exp = e:children()
|
|
41
|
+
-- ▼ ReadonlyArray.filter ▼
|
|
42
|
+
local _newValue = {}
|
|
43
|
+
local _callback = function(c)
|
|
44
|
+
return canPlaceEntry(c)
|
|
45
|
+
end
|
|
46
|
+
local _length = 0
|
|
47
|
+
for _k, _v in _exp do
|
|
48
|
+
if _callback(_v, _k - 1, _exp) == true then
|
|
49
|
+
_length += 1
|
|
50
|
+
_newValue[_length] = _v
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
-- ▲ ReadonlyArray.filter ▲
|
|
54
|
+
-- ▼ ReadonlyArray.forEach ▼
|
|
55
|
+
local _callback_1 = function(c)
|
|
56
|
+
return placeEntriesDeep(c, { entryEntt, body })
|
|
57
|
+
end
|
|
58
|
+
for _k, _v in _newValue do
|
|
59
|
+
_callback_1(_v, _k - 1, _newValue)
|
|
60
|
+
end
|
|
61
|
+
-- ▲ ReadonlyArray.forEach ▲
|
|
62
|
+
end
|
|
63
|
+
local _binding = query(ScrollingFrame):with(EntityList):collect()[1]
|
|
64
|
+
local containerEntt = _binding[1]
|
|
65
|
+
local containerInst = _binding[2]
|
|
66
|
+
local enttKinds = { Builtin.Entity, Builtin.Component, Builtin.Resource }
|
|
67
|
+
-- ▼ ReadonlyArray.forEach ▼
|
|
68
|
+
local _callback = function(enttKind)
|
|
69
|
+
query(enttKind):filter(function(e)
|
|
70
|
+
return canPlaceEntry(e) and e:parent() == nil
|
|
71
|
+
end):forEach(function(e)
|
|
72
|
+
return placeEntriesDeep(e, { containerEntt, containerInst })
|
|
73
|
+
end)
|
|
74
|
+
end
|
|
75
|
+
for _k, _v in enttKinds do
|
|
76
|
+
_callback(_v, _k - 1, enttKinds)
|
|
77
|
+
end
|
|
78
|
+
-- ▲ ReadonlyArray.forEach ▲
|
|
79
|
+
end
|
|
80
|
+
local function bindEntityEntryCleanup()
|
|
81
|
+
EntryFor:onRemoved(function(entry, _v, despawned)
|
|
82
|
+
if despawned then
|
|
83
|
+
return nil
|
|
84
|
+
end
|
|
85
|
+
local frame = entry:get(Frame)
|
|
86
|
+
if not frame then
|
|
87
|
+
return nil
|
|
88
|
+
end
|
|
89
|
+
frame:Destroy()
|
|
90
|
+
entry:despawn()
|
|
91
|
+
end)
|
|
92
|
+
end
|
|
93
|
+
return {
|
|
94
|
+
drawEntityEntries = drawEntityEntries,
|
|
95
|
+
bindEntityEntryCleanup = bindEntityEntryCleanup,
|
|
96
|
+
EntryFor = EntryFor,
|
|
97
|
+
}
|
package/out/index.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Scheduler } from '@rbxts/toucan';
|
|
2
|
+
import { type Theme } from './themes';
|
|
3
|
+
export { type Theme, themes } from './themes';
|
|
4
|
+
/**
|
|
5
|
+
* Spawns a visual debugger.
|
|
6
|
+
*
|
|
7
|
+
* @remarks
|
|
8
|
+
* Throws if used on the server.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```ts
|
|
12
|
+
* import toucanDebugger, { themes } from '@rbxts/toucan-debugger'
|
|
13
|
+
*
|
|
14
|
+
* // Passing a theme is optional — defaults to black.
|
|
15
|
+
* scheduler().usePlugin(toucanDebugger, themes.black).run()
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
export default function toucanDebugger(scheduler: Scheduler, theme?: Theme): void;
|
package/out/init.luau
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local TS = _G[script]
|
|
3
|
+
local exports = {}
|
|
4
|
+
local _services = TS.import(script, TS.getModule(script, "@rbxts", "services"))
|
|
5
|
+
local Players = _services.Players
|
|
6
|
+
local RunService = _services.RunService
|
|
7
|
+
local updateCollapsible = TS.import(script, script, "collapsible").updateCollapsible
|
|
8
|
+
local _drawEntityEntries = TS.import(script, script, "drawEntityEntries")
|
|
9
|
+
local bindEntityEntryCleanup = _drawEntityEntries.bindEntityEntryCleanup
|
|
10
|
+
local drawEntityEntries = _drawEntityEntries.drawEntityEntries
|
|
11
|
+
local themes = TS.import(script, script, "themes").themes
|
|
12
|
+
local explorerPanel = TS.import(script, script, "ui").default
|
|
13
|
+
local updateRuntimeLabel = TS.import(script, script, "updateRuntimeLabel").updateRuntimeLabel
|
|
14
|
+
exports.themes = TS.import(script, script, "themes").themes
|
|
15
|
+
local function setupDebuggerPanel(parent, theme)
|
|
16
|
+
explorerPanel(parent, theme)
|
|
17
|
+
end
|
|
18
|
+
--[[
|
|
19
|
+
*
|
|
20
|
+
* Spawns a visual debugger.
|
|
21
|
+
*
|
|
22
|
+
* @remarks
|
|
23
|
+
* Throws if used on the server.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```ts
|
|
27
|
+
* import toucanDebugger, { themes } from '@rbxts/toucan-debugger'
|
|
28
|
+
*
|
|
29
|
+
* // Passing a theme is optional — defaults to black.
|
|
30
|
+
* scheduler().usePlugin(toucanDebugger, themes.black).run()
|
|
31
|
+
* ```
|
|
32
|
+
|
|
33
|
+
]]
|
|
34
|
+
local function toucanDebugger(scheduler, theme)
|
|
35
|
+
if theme == nil then
|
|
36
|
+
theme = themes.black
|
|
37
|
+
end
|
|
38
|
+
if RunService:IsServer() then
|
|
39
|
+
error("Toucan's debugger can only be used on the client")
|
|
40
|
+
end
|
|
41
|
+
local client = Players.LocalPlayer
|
|
42
|
+
local playerGui = client:WaitForChild("PlayerGui")
|
|
43
|
+
scheduler:useSystem("preStartup", setupDebuggerPanel, {
|
|
44
|
+
args = { playerGui, theme },
|
|
45
|
+
}):useSystem("preStartup", bindEntityEntryCleanup):useSystemChain("last", { { drawEntityEntries, {
|
|
46
|
+
args = { theme },
|
|
47
|
+
} }, { updateCollapsible, {
|
|
48
|
+
args = { theme },
|
|
49
|
+
} }, updateRuntimeLabel })
|
|
50
|
+
end
|
|
51
|
+
exports.default = toucanDebugger
|
|
52
|
+
return exports
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { type Component, type Entity } from '@rbxts/toucan';
|
|
2
|
+
export declare const Frame: Component<Frame>;
|
|
3
|
+
export declare const ScreenGui: Component<ScreenGui>;
|
|
4
|
+
export declare const ScrollingFrame: Component<ScrollingFrame>;
|
|
5
|
+
export declare const ImageButton: Component<ImageButton>;
|
|
6
|
+
export declare const ImageLabel: Component<ImageLabel>;
|
|
7
|
+
export declare const TextLabel: Component<TextLabel>;
|
|
8
|
+
export declare const UIPadding: Component<UIPadding>;
|
|
9
|
+
export declare const UIListLayout: Component<UIListLayout>;
|
|
10
|
+
export declare const UIFlexItem: Component<UIFlexItem>;
|
|
11
|
+
export declare const UISizeConstraint: Component<UISizeConstraint>;
|
|
12
|
+
export declare const UIAspectRatioConstraint: Component<UIAspectRatioConstraint>;
|
|
13
|
+
export declare const UICorner: Component<UICorner>;
|
|
14
|
+
export declare const UIStroke: Component<UIStroke>;
|
|
15
|
+
declare const SUPPORTED_INSTANCES: {
|
|
16
|
+
Frame: Component<Frame>;
|
|
17
|
+
ScreenGui: Component<ScreenGui>;
|
|
18
|
+
ScrollingFrame: Component<ScrollingFrame>;
|
|
19
|
+
ImageButton: Component<ImageButton>;
|
|
20
|
+
ImageLabel: Component<ImageLabel>;
|
|
21
|
+
TextLabel: Component<TextLabel>;
|
|
22
|
+
UIPadding: Component<UIPadding>;
|
|
23
|
+
UIListLayout: Component<UIListLayout>;
|
|
24
|
+
UIFlexItem: Component<UIFlexItem>;
|
|
25
|
+
UISizeConstraint: Component<UISizeConstraint>;
|
|
26
|
+
UIAspectRatioConstraint: Component<UIAspectRatioConstraint>;
|
|
27
|
+
UICorner: Component<UICorner>;
|
|
28
|
+
UIStroke: Component<UIStroke>;
|
|
29
|
+
};
|
|
30
|
+
type SupportedInstances = typeof SUPPORTED_INSTANCES;
|
|
31
|
+
export type EntityInstance<I extends Instance = Instance> = [Entity, I];
|
|
32
|
+
type Child = EntityInstance | Entity | false | undefined;
|
|
33
|
+
type InstanceProperties<T extends Instance> = Partial<WritableInstanceProperties<T>> & {
|
|
34
|
+
child?: Child;
|
|
35
|
+
children?: Child[];
|
|
36
|
+
callback?: (entity: Entity, instance: T) => void;
|
|
37
|
+
Name?: string;
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Spawns an entity paired with a new instance of the given `className`, automatically setting the corresponding component and parenting any children.
|
|
41
|
+
*
|
|
42
|
+
* Giving the instance a `Name` will also give its entity a `Label` of the same name.
|
|
43
|
+
*
|
|
44
|
+
* @remarks
|
|
45
|
+
* Not every instance is supported, as there needs to be a corresponding component for it. If there's an instance you need that isn't supported, consider contributing.
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* let textLabel: TextLabel
|
|
49
|
+
*
|
|
50
|
+
* const [btnEntt, btnInst] = entityFor('ImageButton', {
|
|
51
|
+
* Size: new UDim2(0, 100, 0, 40),
|
|
52
|
+
* Parent: playerGui,
|
|
53
|
+
* child: entityFor('TextLabel', {
|
|
54
|
+
* Text: 'Click me',
|
|
55
|
+
* callback: (entt, inst) => {
|
|
56
|
+
* entt.set(SpecialLabel)
|
|
57
|
+
* textLabel = inst
|
|
58
|
+
* },
|
|
59
|
+
* }),
|
|
60
|
+
* })
|
|
61
|
+
*/
|
|
62
|
+
export declare function entityFor<T extends keyof SupportedInstances>(className: T, properties?: InstanceProperties<CreatableInstances[T]>): EntityInstance<CreatableInstances[T]>;
|
|
63
|
+
export {};
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local TS = _G[script]
|
|
3
|
+
local _toucan = TS.import(script, TS.getModule(script, "@rbxts", "toucan").out)
|
|
4
|
+
local Builtin = _toucan.Builtin
|
|
5
|
+
local component = _toucan.component
|
|
6
|
+
local EntityLike = _toucan.EntityLike
|
|
7
|
+
local entity = _toucan.entity
|
|
8
|
+
local pair = _toucan.pair
|
|
9
|
+
local Frame = component("Frame")
|
|
10
|
+
local ScreenGui = component("ScreenGui")
|
|
11
|
+
local ScrollingFrame = component("ScrollingFrame")
|
|
12
|
+
local ImageButton = component("ImageButton")
|
|
13
|
+
local ImageLabel = component("ImageLabel")
|
|
14
|
+
local TextLabel = component("TextLabel")
|
|
15
|
+
local UIPadding = component("UIPadding")
|
|
16
|
+
local UIListLayout = component("UIListLayout")
|
|
17
|
+
local UIFlexItem = component("UIFlexItem")
|
|
18
|
+
local UISizeConstraint = component("UISizeConstraint")
|
|
19
|
+
local UIAspectRatioConstraint = component("UIAspectRatioConstraint")
|
|
20
|
+
local UICorner = component("UICorner")
|
|
21
|
+
local UIStroke = component("UIStroke")
|
|
22
|
+
local SUPPORTED_INSTANCES = {
|
|
23
|
+
Frame = Frame,
|
|
24
|
+
ScreenGui = ScreenGui,
|
|
25
|
+
ScrollingFrame = ScrollingFrame,
|
|
26
|
+
ImageButton = ImageButton,
|
|
27
|
+
ImageLabel = ImageLabel,
|
|
28
|
+
TextLabel = TextLabel,
|
|
29
|
+
UIPadding = UIPadding,
|
|
30
|
+
UIListLayout = UIListLayout,
|
|
31
|
+
UIFlexItem = UIFlexItem,
|
|
32
|
+
UISizeConstraint = UISizeConstraint,
|
|
33
|
+
UIAspectRatioConstraint = UIAspectRatioConstraint,
|
|
34
|
+
UICorner = UICorner,
|
|
35
|
+
UIStroke = UIStroke,
|
|
36
|
+
}
|
|
37
|
+
local RESERVED_PROPERTIES = { "child", "children", "callback" }
|
|
38
|
+
--[[
|
|
39
|
+
*
|
|
40
|
+
* Spawns an entity paired with a new instance of the given `className`, automatically setting the corresponding component and parenting any children.
|
|
41
|
+
*
|
|
42
|
+
* Giving the instance a `Name` will also give its entity a `Label` of the same name.
|
|
43
|
+
*
|
|
44
|
+
* @remarks
|
|
45
|
+
* Not every instance is supported, as there needs to be a corresponding component for it. If there's an instance you need that isn't supported, consider contributing.
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* let textLabel: TextLabel
|
|
49
|
+
*
|
|
50
|
+
* const [btnEntt, btnInst] = entityFor('ImageButton', {
|
|
51
|
+
* Size: new UDim2(0, 100, 0, 40),
|
|
52
|
+
* Parent: playerGui,
|
|
53
|
+
* child: entityFor('TextLabel', {
|
|
54
|
+
* Text: 'Click me',
|
|
55
|
+
* callback: (entt, inst) => {
|
|
56
|
+
* entt.set(SpecialLabel)
|
|
57
|
+
* textLabel = inst
|
|
58
|
+
* },
|
|
59
|
+
* }),
|
|
60
|
+
* })
|
|
61
|
+
|
|
62
|
+
]]
|
|
63
|
+
local function entityFor(className, properties)
|
|
64
|
+
if properties == nil then
|
|
65
|
+
properties = {}
|
|
66
|
+
end
|
|
67
|
+
local _result = properties
|
|
68
|
+
if _result ~= nil then
|
|
69
|
+
_result = _result.Name
|
|
70
|
+
end
|
|
71
|
+
local _condition = _result
|
|
72
|
+
if _condition == nil then
|
|
73
|
+
_condition = className
|
|
74
|
+
end
|
|
75
|
+
local entt = entity(_condition)
|
|
76
|
+
local inst = Instance.new(className)
|
|
77
|
+
entt:set(SUPPORTED_INSTANCES[className], inst)
|
|
78
|
+
for k, v in pairs(properties) do
|
|
79
|
+
if table.find(RESERVED_PROPERTIES, k) ~= nil then
|
|
80
|
+
continue
|
|
81
|
+
end
|
|
82
|
+
inst[k] = v
|
|
83
|
+
end
|
|
84
|
+
local parentChild = function(child, entt, inst)
|
|
85
|
+
if not child then
|
|
86
|
+
return nil
|
|
87
|
+
end
|
|
88
|
+
if TS.instanceof(child, EntityLike) then
|
|
89
|
+
child:set(pair(Builtin.ChildOf, entt))
|
|
90
|
+
else
|
|
91
|
+
local _binding = child
|
|
92
|
+
local childEntt = _binding[1]
|
|
93
|
+
local childInst = _binding[2]
|
|
94
|
+
childEntt:set(pair(Builtin.ChildOf, entt))
|
|
95
|
+
childInst.Parent = inst
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
local _result_1 = properties
|
|
99
|
+
if _result_1 ~= nil then
|
|
100
|
+
_result_1 = _result_1.child
|
|
101
|
+
end
|
|
102
|
+
parentChild(_result_1, entt, inst)
|
|
103
|
+
local _result_2 = properties
|
|
104
|
+
if _result_2 ~= nil then
|
|
105
|
+
_result_2 = _result_2.children
|
|
106
|
+
end
|
|
107
|
+
local _condition_1 = _result_2
|
|
108
|
+
if _condition_1 == nil then
|
|
109
|
+
_condition_1 = {}
|
|
110
|
+
end
|
|
111
|
+
for _, child in _condition_1 do
|
|
112
|
+
parentChild(child, entt, inst)
|
|
113
|
+
end
|
|
114
|
+
local _result_3 = properties
|
|
115
|
+
if _result_3 ~= nil then
|
|
116
|
+
_result_3 = _result_3.callback
|
|
117
|
+
if _result_3 ~= nil then
|
|
118
|
+
_result_3(entt, inst)
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
return { entt, inst }
|
|
122
|
+
end
|
|
123
|
+
return {
|
|
124
|
+
entityFor = entityFor,
|
|
125
|
+
Frame = Frame,
|
|
126
|
+
ScreenGui = ScreenGui,
|
|
127
|
+
ScrollingFrame = ScrollingFrame,
|
|
128
|
+
ImageButton = ImageButton,
|
|
129
|
+
ImageLabel = ImageLabel,
|
|
130
|
+
TextLabel = TextLabel,
|
|
131
|
+
UIPadding = UIPadding,
|
|
132
|
+
UIListLayout = UIListLayout,
|
|
133
|
+
UIFlexItem = UIFlexItem,
|
|
134
|
+
UISizeConstraint = UISizeConstraint,
|
|
135
|
+
UIAspectRatioConstraint = UIAspectRatioConstraint,
|
|
136
|
+
UICorner = UICorner,
|
|
137
|
+
UIStroke = UIStroke,
|
|
138
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local FONT_INTER = "rbxassetid://12187365364"
|
|
3
|
+
local blackTheme = {
|
|
4
|
+
colors = {
|
|
5
|
+
content = Color3.fromRGB(255, 255, 255),
|
|
6
|
+
contentMuted = Color3.fromRGB(130, 130, 130),
|
|
7
|
+
contentInverse = Color3.fromRGB(0, 0, 0),
|
|
8
|
+
surface1 = Color3.fromRGB(0, 0, 0),
|
|
9
|
+
surface2 = Color3.fromRGB(16, 16, 16),
|
|
10
|
+
surface3 = Color3.fromRGB(24, 24, 24),
|
|
11
|
+
border = Color3.fromRGB(64, 64, 64),
|
|
12
|
+
borderMuted = Color3.fromRGB(32, 32, 32),
|
|
13
|
+
client = Color3.fromRGB(40, 140, 255),
|
|
14
|
+
server = Color3.fromRGB(40, 255, 55),
|
|
15
|
+
err = Color3.fromRGB(255, 40, 40),
|
|
16
|
+
},
|
|
17
|
+
icons = {
|
|
18
|
+
chevronRight = "rbxassetid://84839472552140",
|
|
19
|
+
chevronRightBold = "rbxassetid://78446529333772",
|
|
20
|
+
chevronDown = "rbxassetid://123273356220210",
|
|
21
|
+
chevronDownBold = "rbxassetid://71570100619926",
|
|
22
|
+
entity = "rbxassetid://117106064006931",
|
|
23
|
+
component = "rbxassetid://132097048540989",
|
|
24
|
+
system = "rbxassetid://104146467001055",
|
|
25
|
+
schedule = "rbxassetid://73049254764861",
|
|
26
|
+
plugin = "rbxassetid://85799610981034",
|
|
27
|
+
},
|
|
28
|
+
fonts = {
|
|
29
|
+
black = Font.new(FONT_INTER, Enum.FontWeight.Heavy),
|
|
30
|
+
bold = Font.new(FONT_INTER, Enum.FontWeight.Bold),
|
|
31
|
+
regular = Font.new(FONT_INTER, Enum.FontWeight.Regular),
|
|
32
|
+
extraLightItalic = Font.new(FONT_INTER, Enum.FontWeight.ExtraLight, Enum.FontStyle.Italic),
|
|
33
|
+
},
|
|
34
|
+
fontSizes = {
|
|
35
|
+
tiny = 10,
|
|
36
|
+
small = 12,
|
|
37
|
+
large = 16,
|
|
38
|
+
huge = 36,
|
|
39
|
+
},
|
|
40
|
+
paddingSizes = {
|
|
41
|
+
small = UDim.new(nil, 3),
|
|
42
|
+
medium = UDim.new(nil, 6),
|
|
43
|
+
large = UDim.new(nil, 9),
|
|
44
|
+
},
|
|
45
|
+
explorerIndent = 20,
|
|
46
|
+
}
|
|
47
|
+
local default = blackTheme
|
|
48
|
+
return {
|
|
49
|
+
default = default,
|
|
50
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
export declare const ROBLOX_TOPBAR_HEIGHT = 58;
|
|
2
|
+
export interface Theme {
|
|
3
|
+
colors: {
|
|
4
|
+
content: Color3;
|
|
5
|
+
contentMuted: Color3;
|
|
6
|
+
contentInverse: Color3;
|
|
7
|
+
surface1: Color3;
|
|
8
|
+
surface2: Color3;
|
|
9
|
+
surface3: Color3;
|
|
10
|
+
border: Color3;
|
|
11
|
+
borderMuted: Color3;
|
|
12
|
+
client: Color3;
|
|
13
|
+
server: Color3;
|
|
14
|
+
err: Color3;
|
|
15
|
+
};
|
|
16
|
+
icons: {
|
|
17
|
+
chevronRight: string;
|
|
18
|
+
chevronRightBold: string;
|
|
19
|
+
chevronDown: string;
|
|
20
|
+
chevronDownBold: string;
|
|
21
|
+
entity: string;
|
|
22
|
+
component: string;
|
|
23
|
+
system: string;
|
|
24
|
+
schedule: string;
|
|
25
|
+
plugin: string;
|
|
26
|
+
};
|
|
27
|
+
fonts: {
|
|
28
|
+
black: Font;
|
|
29
|
+
bold: Font;
|
|
30
|
+
regular: Font;
|
|
31
|
+
extraLightItalic: Font;
|
|
32
|
+
};
|
|
33
|
+
fontSizes: {
|
|
34
|
+
tiny: number;
|
|
35
|
+
small: number;
|
|
36
|
+
large: number;
|
|
37
|
+
huge: number;
|
|
38
|
+
};
|
|
39
|
+
paddingSizes: {
|
|
40
|
+
small: UDim;
|
|
41
|
+
medium: UDim;
|
|
42
|
+
large: UDim;
|
|
43
|
+
};
|
|
44
|
+
explorerIndent: number;
|
|
45
|
+
}
|
|
46
|
+
export declare const themes: {
|
|
47
|
+
black: Theme;
|
|
48
|
+
white: Theme;
|
|
49
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local TS = _G[script]
|
|
3
|
+
local blackTheme = TS.import(script, script, "black").default
|
|
4
|
+
local whiteTheme = TS.import(script, script, "white").default
|
|
5
|
+
local ROBLOX_TOPBAR_HEIGHT = 58
|
|
6
|
+
local themes = {
|
|
7
|
+
black = blackTheme,
|
|
8
|
+
white = whiteTheme,
|
|
9
|
+
}
|
|
10
|
+
return {
|
|
11
|
+
ROBLOX_TOPBAR_HEIGHT = ROBLOX_TOPBAR_HEIGHT,
|
|
12
|
+
themes = themes,
|
|
13
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local FONT_INTER = "rbxassetid://12187365364"
|
|
3
|
+
local whiteTheme = {
|
|
4
|
+
colors = {
|
|
5
|
+
content = Color3.fromRGB(0, 0, 0),
|
|
6
|
+
contentMuted = Color3.fromRGB(125, 125, 125),
|
|
7
|
+
contentInverse = Color3.fromRGB(255, 255, 255),
|
|
8
|
+
surface1 = Color3.fromRGB(255, 255, 255),
|
|
9
|
+
surface2 = Color3.fromRGB(240, 240, 240),
|
|
10
|
+
surface3 = Color3.fromRGB(230, 230, 230),
|
|
11
|
+
border = Color3.fromRGB(190, 190, 190),
|
|
12
|
+
borderMuted = Color3.fromRGB(223, 223, 223),
|
|
13
|
+
client = Color3.fromRGB(0, 110, 230),
|
|
14
|
+
server = Color3.fromRGB(0, 170, 30),
|
|
15
|
+
err = Color3.fromRGB(220, 30, 30),
|
|
16
|
+
},
|
|
17
|
+
icons = {
|
|
18
|
+
chevronRight = "rbxassetid://84839472552140",
|
|
19
|
+
chevronRightBold = "rbxassetid://78446529333772",
|
|
20
|
+
chevronDown = "rbxassetid://123273356220210",
|
|
21
|
+
chevronDownBold = "rbxassetid://71570100619926",
|
|
22
|
+
entity = "rbxassetid://117106064006931",
|
|
23
|
+
component = "rbxassetid://132097048540989",
|
|
24
|
+
system = "rbxassetid://104146467001055",
|
|
25
|
+
schedule = "rbxassetid://73049254764861",
|
|
26
|
+
plugin = "rbxassetid://85799610981034",
|
|
27
|
+
},
|
|
28
|
+
fonts = {
|
|
29
|
+
black = Font.new(FONT_INTER, Enum.FontWeight.Heavy),
|
|
30
|
+
bold = Font.new(FONT_INTER, Enum.FontWeight.Bold),
|
|
31
|
+
regular = Font.new(FONT_INTER, Enum.FontWeight.Regular),
|
|
32
|
+
extraLightItalic = Font.new(FONT_INTER, Enum.FontWeight.ExtraLight, Enum.FontStyle.Italic),
|
|
33
|
+
},
|
|
34
|
+
fontSizes = {
|
|
35
|
+
tiny = 10,
|
|
36
|
+
small = 12,
|
|
37
|
+
large = 16,
|
|
38
|
+
huge = 36,
|
|
39
|
+
},
|
|
40
|
+
paddingSizes = {
|
|
41
|
+
small = UDim.new(nil, 3),
|
|
42
|
+
medium = UDim.new(nil, 6),
|
|
43
|
+
large = UDim.new(nil, 9),
|
|
44
|
+
},
|
|
45
|
+
explorerIndent = 20,
|
|
46
|
+
}
|
|
47
|
+
local default = whiteTheme
|
|
48
|
+
return {
|
|
49
|
+
default = default,
|
|
50
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local TS = _G[script]
|
|
3
|
+
local entityFor = TS.import(script, script.Parent.Parent.Parent, "instance").entityFor
|
|
4
|
+
local function dashboard(theme)
|
|
5
|
+
return entityFor("Frame", {
|
|
6
|
+
Name = "dashboard",
|
|
7
|
+
AutomaticSize = Enum.AutomaticSize.Y,
|
|
8
|
+
BackgroundColor3 = theme.colors.surface1,
|
|
9
|
+
LayoutOrder = 0,
|
|
10
|
+
})
|
|
11
|
+
end
|
|
12
|
+
return {
|
|
13
|
+
default = dashboard,
|
|
14
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { type EntityLike } from '@rbxts/toucan';
|
|
2
|
+
import { type EntityInstance } from '../../instance';
|
|
3
|
+
import type { Theme } from '../../themes';
|
|
4
|
+
export declare const NoEntry: import("@rbxts/toucan").Component<undefined>;
|
|
5
|
+
export default function entityEntry(entity: EntityLike, theme: Theme): [EntityInstance<Frame>, Frame];
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local TS = _G[script]
|
|
3
|
+
local _toucan = TS.import(script, TS.getModule(script, "@rbxts", "toucan").out)
|
|
4
|
+
local Builtin = _toucan.Builtin
|
|
5
|
+
local component = _toucan.component
|
|
6
|
+
local pair = _toucan.pair
|
|
7
|
+
local Collapsible = TS.import(script, script.Parent.Parent.Parent, "collapsible").Collapsible
|
|
8
|
+
local entityFor = TS.import(script, script.Parent.Parent.Parent, "instance").entityFor
|
|
9
|
+
local RuntimeLabelFor = TS.import(script, script.Parent.Parent.Parent, "updateRuntimeLabel").RuntimeLabelFor
|
|
10
|
+
local HEADER_HEIGHT = 20
|
|
11
|
+
local NoEntry = component("NoEntry")
|
|
12
|
+
local function getEntityIcon(entity, theme)
|
|
13
|
+
if entity:has(Builtin.Component) then
|
|
14
|
+
return theme.icons.component
|
|
15
|
+
end
|
|
16
|
+
if entity:has(Builtin.System) then
|
|
17
|
+
return theme.icons.system
|
|
18
|
+
end
|
|
19
|
+
if entity:has(Builtin.Schedule) then
|
|
20
|
+
return theme.icons.schedule
|
|
21
|
+
end
|
|
22
|
+
if entity:has(Builtin.Plugin) then
|
|
23
|
+
return theme.icons.plugin
|
|
24
|
+
end
|
|
25
|
+
return theme.icons.entity
|
|
26
|
+
end
|
|
27
|
+
local function makeHeader(entity, theme, callback)
|
|
28
|
+
local chevron
|
|
29
|
+
local header = entityFor("ImageButton", {
|
|
30
|
+
Name = "header",
|
|
31
|
+
Size = UDim2.new(1, 0, 0, HEADER_HEIGHT),
|
|
32
|
+
BackgroundColor3 = theme.colors.surface2,
|
|
33
|
+
LayoutOrder = 0,
|
|
34
|
+
children = { entityFor("UIListLayout", {
|
|
35
|
+
FillDirection = Enum.FillDirection.Horizontal,
|
|
36
|
+
SortOrder = Enum.SortOrder.LayoutOrder,
|
|
37
|
+
Padding = theme.paddingSizes.medium,
|
|
38
|
+
}), entityFor("UIPadding", {
|
|
39
|
+
PaddingBottom = theme.paddingSizes.small,
|
|
40
|
+
PaddingTop = theme.paddingSizes.small,
|
|
41
|
+
PaddingLeft = theme.paddingSizes.small,
|
|
42
|
+
PaddingRight = theme.paddingSizes.small,
|
|
43
|
+
}), entityFor("UICorner", {
|
|
44
|
+
CornerRadius = theme.paddingSizes.medium,
|
|
45
|
+
}), entityFor("UIStroke", {
|
|
46
|
+
Color = theme.colors.border,
|
|
47
|
+
}), entityFor("Frame", {
|
|
48
|
+
Name = "left",
|
|
49
|
+
Size = UDim2.fromScale(0, 1),
|
|
50
|
+
BackgroundTransparency = 1,
|
|
51
|
+
children = { entityFor("UIListLayout", {
|
|
52
|
+
FillDirection = Enum.FillDirection.Horizontal,
|
|
53
|
+
SortOrder = Enum.SortOrder.LayoutOrder,
|
|
54
|
+
Padding = theme.paddingSizes.medium,
|
|
55
|
+
}), entityFor("UIFlexItem", {
|
|
56
|
+
FlexMode = Enum.UIFlexMode.Fill,
|
|
57
|
+
}), entityFor("ImageLabel", {
|
|
58
|
+
Name = "icon",
|
|
59
|
+
Image = getEntityIcon(entity, theme),
|
|
60
|
+
ImageColor3 = theme.colors.contentMuted,
|
|
61
|
+
Size = UDim2.fromScale(0, 1),
|
|
62
|
+
BackgroundTransparency = 1,
|
|
63
|
+
LayoutOrder = 0,
|
|
64
|
+
child = entityFor("UIAspectRatioConstraint", {
|
|
65
|
+
AspectType = Enum.AspectType.ScaleWithParentSize,
|
|
66
|
+
DominantAxis = Enum.DominantAxis.Height,
|
|
67
|
+
}),
|
|
68
|
+
}), entityFor("TextLabel", {
|
|
69
|
+
Name = "id",
|
|
70
|
+
Text = `#{entity.id}`,
|
|
71
|
+
FontFace = theme.fonts.regular,
|
|
72
|
+
TextSize = theme.fontSizes.small,
|
|
73
|
+
TextXAlignment = Enum.TextXAlignment.Left,
|
|
74
|
+
TextColor3 = theme.colors.contentMuted,
|
|
75
|
+
Size = UDim2.new(0, 33.5, 1, 0),
|
|
76
|
+
BackgroundTransparency = 1,
|
|
77
|
+
LayoutOrder = 1,
|
|
78
|
+
}), entityFor("TextLabel", {
|
|
79
|
+
Name = "label",
|
|
80
|
+
Text = entity:toString(),
|
|
81
|
+
FontFace = theme.fonts.regular,
|
|
82
|
+
TextSize = theme.fontSizes.small,
|
|
83
|
+
TextXAlignment = Enum.TextXAlignment.Left,
|
|
84
|
+
TextColor3 = theme.colors.content,
|
|
85
|
+
Size = UDim2.new(0, 40, 1, 0),
|
|
86
|
+
AutomaticSize = Enum.AutomaticSize.X,
|
|
87
|
+
BackgroundTransparency = 1,
|
|
88
|
+
LayoutOrder = 2,
|
|
89
|
+
}), entity:has(Builtin.System) and entityFor("TextLabel", {
|
|
90
|
+
Name = "runtime",
|
|
91
|
+
Text = "?",
|
|
92
|
+
FontFace = theme.fonts.extraLightItalic,
|
|
93
|
+
TextSize = theme.fontSizes.tiny,
|
|
94
|
+
TextXAlignment = Enum.TextXAlignment.Left,
|
|
95
|
+
TextYAlignment = Enum.TextYAlignment.Bottom,
|
|
96
|
+
TextColor3 = theme.colors.contentMuted,
|
|
97
|
+
Size = UDim2.new(0, 35, 1, 0),
|
|
98
|
+
BackgroundTransparency = 1,
|
|
99
|
+
LayoutOrder = 3,
|
|
100
|
+
callback = function(e)
|
|
101
|
+
return e:set(pair(RuntimeLabelFor, entity))
|
|
102
|
+
end,
|
|
103
|
+
}) },
|
|
104
|
+
}), entityFor("ImageLabel", {
|
|
105
|
+
Name = "chevron",
|
|
106
|
+
Size = UDim2.fromScale(0, 1),
|
|
107
|
+
Position = UDim2.fromScale(0.8, 0),
|
|
108
|
+
BackgroundTransparency = 1,
|
|
109
|
+
Image = theme.icons.chevronRight,
|
|
110
|
+
callback = function(_e, inst)
|
|
111
|
+
chevron = inst
|
|
112
|
+
end,
|
|
113
|
+
child = entityFor("UIAspectRatioConstraint", {
|
|
114
|
+
AspectType = Enum.AspectType.ScaleWithParentSize,
|
|
115
|
+
DominantAxis = Enum.DominantAxis.Height,
|
|
116
|
+
}),
|
|
117
|
+
}) },
|
|
118
|
+
})
|
|
119
|
+
callback(header[2], chevron)
|
|
120
|
+
return header
|
|
121
|
+
end
|
|
122
|
+
local function entityEntry(entity, theme)
|
|
123
|
+
local header
|
|
124
|
+
local chevron
|
|
125
|
+
local body
|
|
126
|
+
local entry = entityFor("Frame", {
|
|
127
|
+
Name = `entityEntry: {entity}`,
|
|
128
|
+
Size = UDim2.fromScale(0, 0),
|
|
129
|
+
AutomaticSize = Enum.AutomaticSize.Y,
|
|
130
|
+
Transparency = 1,
|
|
131
|
+
children = { entityFor("UIListLayout", {
|
|
132
|
+
FillDirection = Enum.FillDirection.Vertical,
|
|
133
|
+
SortOrder = Enum.SortOrder.LayoutOrder,
|
|
134
|
+
Padding = theme.paddingSizes.medium,
|
|
135
|
+
}), makeHeader(entity, theme, function(hdr, chv)
|
|
136
|
+
header = hdr
|
|
137
|
+
chevron = chv
|
|
138
|
+
end), entityFor("Frame", {
|
|
139
|
+
Name = "body",
|
|
140
|
+
Size = UDim2.fromScale(1, 0),
|
|
141
|
+
AutomaticSize = Enum.AutomaticSize.Y,
|
|
142
|
+
Transparency = 1,
|
|
143
|
+
LayoutOrder = 1,
|
|
144
|
+
callback = function(_e, inst)
|
|
145
|
+
body = inst
|
|
146
|
+
end,
|
|
147
|
+
children = { entityFor("UIListLayout", {
|
|
148
|
+
FillDirection = Enum.FillDirection.Vertical,
|
|
149
|
+
HorizontalFlex = Enum.UIFlexAlignment.Fill,
|
|
150
|
+
SortOrder = Enum.SortOrder.LayoutOrder,
|
|
151
|
+
Padding = theme.paddingSizes.medium,
|
|
152
|
+
}), entityFor("UIPadding", {
|
|
153
|
+
PaddingLeft = UDim.new(0, theme.explorerIndent),
|
|
154
|
+
}) },
|
|
155
|
+
}) },
|
|
156
|
+
})
|
|
157
|
+
entry[1]:set(Collapsible, {
|
|
158
|
+
toggleButton = header,
|
|
159
|
+
chevron = chevron,
|
|
160
|
+
body = body,
|
|
161
|
+
})
|
|
162
|
+
-- Without this, `drawEntities` would add entries infinitely, crashing the game.
|
|
163
|
+
local addNoEntryRecursive
|
|
164
|
+
addNoEntryRecursive = function(e)
|
|
165
|
+
e:set(NoEntry)
|
|
166
|
+
local _exp = e:children()
|
|
167
|
+
-- ▼ ReadonlyArray.forEach ▼
|
|
168
|
+
local _callback = function(c)
|
|
169
|
+
return addNoEntryRecursive(c)
|
|
170
|
+
end
|
|
171
|
+
for _k, _v in _exp do
|
|
172
|
+
_callback(_v, _k - 1, _exp)
|
|
173
|
+
end
|
|
174
|
+
-- ▲ ReadonlyArray.forEach ▲
|
|
175
|
+
end
|
|
176
|
+
addNoEntryRecursive(entry[1])
|
|
177
|
+
return { entry, body }
|
|
178
|
+
end
|
|
179
|
+
return {
|
|
180
|
+
default = entityEntry,
|
|
181
|
+
NoEntry = NoEntry,
|
|
182
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local TS = _G[script]
|
|
3
|
+
local component = TS.import(script, TS.getModule(script, "@rbxts", "toucan").out).component
|
|
4
|
+
local entityFor = TS.import(script, script.Parent.Parent.Parent, "instance").entityFor
|
|
5
|
+
local EntityList = component("EntityList")
|
|
6
|
+
local function entityList(theme)
|
|
7
|
+
return entityFor("ScrollingFrame", {
|
|
8
|
+
Name = "entityList",
|
|
9
|
+
CanvasSize = UDim2.fromScale(0, 0),
|
|
10
|
+
AutomaticCanvasSize = Enum.AutomaticSize.Y,
|
|
11
|
+
ScrollBarThickness = 0,
|
|
12
|
+
BackgroundColor3 = theme.colors.surface1,
|
|
13
|
+
BorderColor3 = theme.colors.border,
|
|
14
|
+
LayoutOrder = 1,
|
|
15
|
+
callback = function(e)
|
|
16
|
+
return e:set(EntityList)
|
|
17
|
+
end,
|
|
18
|
+
children = { entityFor("UIFlexItem", {
|
|
19
|
+
FlexMode = Enum.UIFlexMode.Fill,
|
|
20
|
+
}), entityFor("UIListLayout", {
|
|
21
|
+
FillDirection = Enum.FillDirection.Vertical,
|
|
22
|
+
HorizontalFlex = Enum.UIFlexAlignment.Fill,
|
|
23
|
+
SortOrder = Enum.SortOrder.LayoutOrder,
|
|
24
|
+
Padding = theme.paddingSizes.medium,
|
|
25
|
+
}), entityFor("UIPadding", {
|
|
26
|
+
PaddingLeft = theme.paddingSizes.medium,
|
|
27
|
+
PaddingRight = theme.paddingSizes.medium,
|
|
28
|
+
PaddingTop = theme.paddingSizes.large,
|
|
29
|
+
PaddingBottom = theme.paddingSizes.large,
|
|
30
|
+
}) },
|
|
31
|
+
})
|
|
32
|
+
end
|
|
33
|
+
return {
|
|
34
|
+
default = entityList,
|
|
35
|
+
EntityList = EntityList,
|
|
36
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local TS = _G[script]
|
|
3
|
+
local entityFor = TS.import(script, script.Parent.Parent, "instance").entityFor
|
|
4
|
+
local dashboard = TS.import(script, script, "dashboard").default
|
|
5
|
+
local entityList = TS.import(script, script, "entityList").default
|
|
6
|
+
local function explorer(theme)
|
|
7
|
+
return entityFor("Frame", {
|
|
8
|
+
Name = "explorer",
|
|
9
|
+
Size = UDim2.new(1, 0, 1, -35),
|
|
10
|
+
Transparency = 1,
|
|
11
|
+
LayoutOrder = 1,
|
|
12
|
+
children = { entityFor("UIFlexItem", {
|
|
13
|
+
FlexMode = Enum.UIFlexMode.Fill,
|
|
14
|
+
}), entityFor("UIListLayout", {
|
|
15
|
+
HorizontalFlex = Enum.UIFlexAlignment.Fill,
|
|
16
|
+
SortOrder = Enum.SortOrder.LayoutOrder,
|
|
17
|
+
}), dashboard(theme), entityList(theme) },
|
|
18
|
+
})
|
|
19
|
+
end
|
|
20
|
+
return {
|
|
21
|
+
default = explorer,
|
|
22
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local TS = _G[script]
|
|
3
|
+
local entityFor = TS.import(script, script.Parent.Parent, "instance").entityFor
|
|
4
|
+
local function footer(theme)
|
|
5
|
+
return entityFor("Frame", {
|
|
6
|
+
Name = "footer",
|
|
7
|
+
Size = UDim2.new(1, 0, 0, 25),
|
|
8
|
+
BackgroundColor3 = theme.colors.surface1,
|
|
9
|
+
BorderColor3 = theme.colors.border,
|
|
10
|
+
LayoutOrder = 2,
|
|
11
|
+
})
|
|
12
|
+
end
|
|
13
|
+
return {
|
|
14
|
+
default = footer,
|
|
15
|
+
}
|
package/out/ui/hero.d.ts
ADDED
package/out/ui/hero.luau
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local TS = _G[script]
|
|
3
|
+
local entityFor = TS.import(script, script.Parent.Parent, "instance").entityFor
|
|
4
|
+
local ROBLOX_TOPBAR_HEIGHT = TS.import(script, script.Parent.Parent, "themes").ROBLOX_TOPBAR_HEIGHT
|
|
5
|
+
local function hero(theme)
|
|
6
|
+
return entityFor("Frame", {
|
|
7
|
+
Name = "hero",
|
|
8
|
+
Size = UDim2.new(1, 0, 0, ROBLOX_TOPBAR_HEIGHT),
|
|
9
|
+
BackgroundTransparency = 1,
|
|
10
|
+
LayoutOrder = 0,
|
|
11
|
+
children = { entityFor("UIPadding", {
|
|
12
|
+
PaddingRight = theme.paddingSizes.medium,
|
|
13
|
+
}), entityFor("TextLabel", {
|
|
14
|
+
Name = "title",
|
|
15
|
+
Text = "Toucan",
|
|
16
|
+
FontFace = theme.fonts.black,
|
|
17
|
+
Size = UDim2.new(1, 0, 1, 0),
|
|
18
|
+
TextXAlignment = Enum.TextXAlignment.Right,
|
|
19
|
+
TextYAlignment = Enum.TextYAlignment.Bottom,
|
|
20
|
+
TextColor3 = theme.colors.contentInverse,
|
|
21
|
+
TextSize = theme.fontSizes.huge,
|
|
22
|
+
BackgroundTransparency = 1,
|
|
23
|
+
}) },
|
|
24
|
+
})
|
|
25
|
+
end
|
|
26
|
+
return {
|
|
27
|
+
default = hero,
|
|
28
|
+
}
|
package/out/ui/init.luau
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local TS = _G[script]
|
|
3
|
+
local entityFor = TS.import(script, script.Parent, "instance").entityFor
|
|
4
|
+
local explorer = TS.import(script, script, "explorer").default
|
|
5
|
+
local footer = TS.import(script, script, "footer").default
|
|
6
|
+
local hero = TS.import(script, script, "hero").default
|
|
7
|
+
local function explorerPanel(parent, theme)
|
|
8
|
+
entityFor("ScreenGui", {
|
|
9
|
+
Name = "toucanExplorer",
|
|
10
|
+
Parent = parent,
|
|
11
|
+
IgnoreGuiInset = true,
|
|
12
|
+
children = { entityFor("Frame", {
|
|
13
|
+
Name = "panel",
|
|
14
|
+
Position = UDim2.fromScale(0, 0),
|
|
15
|
+
Size = UDim2.new(0, 300, 1, 0),
|
|
16
|
+
BackgroundColor3 = theme.colors.surface2,
|
|
17
|
+
BorderColor3 = theme.colors.border,
|
|
18
|
+
BackgroundTransparency = 0.15,
|
|
19
|
+
children = { entityFor("UIListLayout", {
|
|
20
|
+
FillDirection = Enum.FillDirection.Vertical,
|
|
21
|
+
HorizontalFlex = Enum.UIFlexAlignment.Fill,
|
|
22
|
+
SortOrder = Enum.SortOrder.LayoutOrder,
|
|
23
|
+
}), hero(theme), explorer(theme), footer(theme) },
|
|
24
|
+
}) },
|
|
25
|
+
})
|
|
26
|
+
end
|
|
27
|
+
return {
|
|
28
|
+
default = explorerPanel,
|
|
29
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local TS = _G[script]
|
|
3
|
+
local _toucan = TS.import(script, TS.getModule(script, "@rbxts", "toucan").out)
|
|
4
|
+
local Builtin = _toucan.Builtin
|
|
5
|
+
local component = _toucan.component
|
|
6
|
+
local pair = _toucan.pair
|
|
7
|
+
local query = _toucan.query
|
|
8
|
+
local TextLabel = TS.import(script, script.Parent, "instance").TextLabel
|
|
9
|
+
local UPDATE_INTERVAL = 0.5
|
|
10
|
+
local RuntimeLabelFor = component("RuntimeLabelFor")
|
|
11
|
+
local lastRun = 0
|
|
12
|
+
local function formatRuntime(ms)
|
|
13
|
+
if ms >= 1 then
|
|
14
|
+
return `{math.round(ms * 10) / 10}ms`
|
|
15
|
+
end
|
|
16
|
+
local us = ms * 1000
|
|
17
|
+
local usRounded = math.round(us * 10) / 10
|
|
18
|
+
local showDecimal = usRounded < 10
|
|
19
|
+
return if showDecimal then `{usRounded}µs` else `{math.round(us)}µs`
|
|
20
|
+
end
|
|
21
|
+
local function updateRuntimeLabel()
|
|
22
|
+
local now = os.clock()
|
|
23
|
+
-- TODO: use something like `useThrottle` once its plugin gets published.
|
|
24
|
+
if now - lastRun < UPDATE_INTERVAL then
|
|
25
|
+
return nil
|
|
26
|
+
end
|
|
27
|
+
lastRun = now
|
|
28
|
+
query(TextLabel):with(pair(RuntimeLabelFor, Builtin.Wildcard)):forEach(function(e, label)
|
|
29
|
+
local systemEntt = e:targetOf(RuntimeLabelFor)
|
|
30
|
+
local system = systemEntt:get(Builtin.System)
|
|
31
|
+
label.Text = formatRuntime(system.runtimeMs)
|
|
32
|
+
end)
|
|
33
|
+
end
|
|
34
|
+
return {
|
|
35
|
+
updateRuntimeLabel = updateRuntimeLabel,
|
|
36
|
+
RuntimeLabelFor = RuntimeLabelFor,
|
|
37
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,40 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
2
|
+
"name": "@rbxts/toucan-debugger",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "",
|
|
5
|
+
"main": "out/init.lua",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"setup": "bun i && bun i --cwd test && rokit install",
|
|
8
|
+
"build": "rbxtsc",
|
|
9
|
+
"dev": "concurrently -n \"LIB,TEST,ROJO\" -c \"blue,magenta,yellow\" \"rbxtsc -w\" \"rbxtsc -p test -w\" \"rojo serve test/default.project.json\"",
|
|
10
|
+
"prepublishOnly": "npm run build",
|
|
11
|
+
"lint": "biome check --write .",
|
|
12
|
+
"format": "biome format --write .",
|
|
13
|
+
"predev": "rbxtsc && rbxtsc -p test",
|
|
14
|
+
"preinstall": "npx only-allow bun"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [],
|
|
17
|
+
"author": "",
|
|
18
|
+
"license": "ISC",
|
|
19
|
+
"type": "commonjs",
|
|
20
|
+
"types": "out/index.d.ts",
|
|
21
|
+
"files": [
|
|
22
|
+
"out",
|
|
23
|
+
"!**/*.tsbuildinfo"
|
|
24
|
+
],
|
|
25
|
+
"publishConfig": {
|
|
26
|
+
"access": "public"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@biomejs/biome": "2.5.0",
|
|
30
|
+
"@rbxts/compiler-types": "^3.0.0-types.0",
|
|
31
|
+
"@rbxts/types": "^1.0.959",
|
|
32
|
+
"concurrently": "^10.0.3",
|
|
33
|
+
"roblox-ts": "^3.0.0",
|
|
34
|
+
"typescript": "^5.9.3"
|
|
35
|
+
},
|
|
36
|
+
"dependencies": {
|
|
37
|
+
"@rbxts/services": "^1.6.0",
|
|
38
|
+
"@rbxts/toucan": "0.6.0"
|
|
39
|
+
}
|
|
40
|
+
}
|