@rbxts/centurion-ui 1.0.8 → 2.0.0-rc.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/README.md +60 -15
- package/centurion.luau +10 -0
- package/package.json +13 -23
- package/src/analyse/cursor.luau +25 -0
- package/src/analyse/highlight.luau +91 -0
- package/src/analyse/init.luau +318 -0
- package/src/analyse/leventine.luau +298 -0
- package/src/analyse/match.luau +39 -0
- package/src/analyse/suggest.luau +41 -0
- package/src/app/header.luau +128 -0
- package/src/app/history.luau +258 -0
- package/src/app/init.luau +699 -0
- package/src/app/suggestions.luau +419 -0
- package/src/button.luau +13 -0
- package/src/components/background.luau +37 -0
- package/src/components/container.luau +33 -0
- package/src/components/corner.luau +11 -0
- package/src/components/divider.luau +28 -0
- package/src/components/flex.luau +108 -0
- package/src/components/gap.luau +25 -0
- package/src/components/image.luau +33 -0
- package/src/components/padding.luau +59 -0
- package/src/components/stroke.luau +20 -0
- package/src/components/text.luau +57 -0
- package/src/components/textbox.luau +186 -0
- package/src/fade.luau +23 -0
- package/src/init.luau +142 -0
- package/src/input.luau +16 -0
- package/src/px.luau +68 -0
- package/src/rich.luau +14 -0
- package/src/state.luau +33 -0
- package/src/theme.luau +70 -0
- package/src/ts/index.d.ts +60 -0
- package/src/ts/init.luau +5 -0
- package/src/types.luau +85 -0
- package/vide/init.luau +8 -0
- package/vide/src/action.luau +25 -0
- package/vide/src/apply.luau +162 -0
- package/vide/src/batch.luau +23 -0
- package/vide/src/branch.luau +40 -0
- package/vide/src/changed.luau +18 -0
- package/vide/src/cleanup.luau +43 -0
- package/vide/src/context.luau +74 -0
- package/vide/src/create.luau +107 -0
- package/vide/src/defaults.luau +113 -0
- package/vide/src/derive.luau +18 -0
- package/vide/src/effect.luau +12 -0
- package/vide/src/flags.luau +12 -0
- package/vide/src/graph.luau +321 -0
- package/vide/src/implicit_effect.luau +125 -0
- package/vide/src/indexes.luau +121 -0
- package/vide/src/init.luau +10 -0
- package/vide/src/lib.luau +120 -0
- package/vide/src/mount.luau +11 -0
- package/vide/src/read.luau +5 -0
- package/vide/src/root.luau +35 -0
- package/vide/src/show.luau +36 -0
- package/vide/src/source.luau +31 -0
- package/vide/src/spring.luau +328 -0
- package/vide/src/switch.luau +114 -0
- package/vide/src/timeout.luau +27 -0
- package/vide/src/untrack.luau +25 -0
- package/vide/src/values.luau +131 -0
- package/out/app/centurion-app.d.ts +0 -3
- package/out/app/centurion-app.luau +0 -86
- package/out/app/index.d.ts +0 -32
- package/out/app/init.luau +0 -105
- package/out/components/history/history-line.d.ts +0 -9
- package/out/components/history/history-line.luau +0 -88
- package/out/components/history/history-list.d.ts +0 -11
- package/out/components/history/history-list.luau +0 -67
- package/out/components/history/index.d.ts +0 -1
- package/out/components/history/init.luau +0 -7
- package/out/components/index.d.ts +0 -2
- package/out/components/init.luau +0 -10
- package/out/components/suggestions/badge.d.ts +0 -16
- package/out/components/suggestions/badge.luau +0 -40
- package/out/components/suggestions/index.d.ts +0 -1
- package/out/components/suggestions/init.luau +0 -7
- package/out/components/suggestions/main-suggestion.d.ts +0 -9
- package/out/components/suggestions/main-suggestion.luau +0 -232
- package/out/components/suggestions/suggestion-list.d.ts +0 -8
- package/out/components/suggestions/suggestion-list.luau +0 -96
- package/out/components/suggestions/suggestions.d.ts +0 -2
- package/out/components/suggestions/suggestions.luau +0 -64
- package/out/components/suggestions/types.d.ts +0 -6
- package/out/components/suggestions/types.luau +0 -2
- package/out/components/suggestions/util.d.ts +0 -1
- package/out/components/suggestions/util.luau +0 -22
- package/out/components/terminal/command.d.ts +0 -6
- package/out/components/terminal/command.luau +0 -74
- package/out/components/terminal/index.d.ts +0 -1
- package/out/components/terminal/init.luau +0 -7
- package/out/components/terminal/suggestion.d.ts +0 -26
- package/out/components/terminal/suggestion.luau +0 -205
- package/out/components/terminal/terminal-text-field/index.d.ts +0 -1
- package/out/components/terminal/terminal-text-field/init.luau +0 -7
- package/out/components/terminal/terminal-text-field/suggestion.d.ts +0 -5
- package/out/components/terminal/terminal-text-field/suggestion.luau +0 -124
- package/out/components/terminal/terminal-text-field/terminal-text-field.d.ts +0 -13
- package/out/components/terminal/terminal-text-field/terminal-text-field.luau +0 -278
- package/out/components/terminal/terminal.d.ts +0 -2
- package/out/components/terminal/terminal.luau +0 -304
- package/out/components/ui/frame.d.ts +0 -17
- package/out/components/ui/frame.luau +0 -32
- package/out/components/ui/group.d.ts +0 -15
- package/out/components/ui/group.luau +0 -29
- package/out/components/ui/layer.d.ts +0 -8
- package/out/components/ui/layer.luau +0 -26
- package/out/components/ui/outline.d.ts +0 -15
- package/out/components/ui/outline.luau +0 -116
- package/out/components/ui/padding.d.ts +0 -10
- package/out/components/ui/padding.luau +0 -19
- package/out/components/ui/scrolling-frame.d.ts +0 -14
- package/out/components/ui/scrolling-frame.luau +0 -44
- package/out/components/ui/text-field.d.ts +0 -12
- package/out/components/ui/text-field.luau +0 -60
- package/out/components/ui/text.d.ts +0 -17
- package/out/components/ui/text.luau +0 -61
- package/out/constants/options.d.ts +0 -2
- package/out/constants/options.luau +0 -27
- package/out/constants/text.d.ts +0 -3
- package/out/constants/text.luau +0 -9
- package/out/constants/util.d.ts +0 -1
- package/out/constants/util.luau +0 -7
- package/out/hooks/use-client.d.ts +0 -2
- package/out/hooks/use-client.luau +0 -17
- package/out/hooks/use-event.d.ts +0 -14
- package/out/hooks/use-event.luau +0 -58
- package/out/hooks/use-px.d.ts +0 -19
- package/out/hooks/use-px.luau +0 -78
- package/out/index.d.ts +0 -2
- package/out/init.luau +0 -10
- package/out/palette.d.ts +0 -47
- package/out/palette.luau +0 -42
- package/out/store.d.ts +0 -12
- package/out/store.luau +0 -26
- package/out/types.d.ts +0 -44
- package/out/types.luau +0 -2
- package/out/utils/string.d.ts +0 -5
- package/out/utils/string.luau +0 -25
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { Response } from "@rbxts/centurion";
|
|
2
|
+
|
|
3
|
+
export interface Options {
|
|
4
|
+
hide_on_lost_focus: boolean;
|
|
5
|
+
activation_keys: Enum.KeyCode[];
|
|
6
|
+
theme: Theme;
|
|
7
|
+
font: Font;
|
|
8
|
+
icon: string;
|
|
9
|
+
transparency: number;
|
|
10
|
+
format_date: (log: Log) => string;
|
|
11
|
+
max_suggestions: number;
|
|
12
|
+
|
|
13
|
+
text_size: number;
|
|
14
|
+
subtext_size: number;
|
|
15
|
+
|
|
16
|
+
anchor: Vector2;
|
|
17
|
+
width: UDim;
|
|
18
|
+
position: UDim2;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface Theme {
|
|
22
|
+
background: Color3;
|
|
23
|
+
surface: Color3;
|
|
24
|
+
text: Color3;
|
|
25
|
+
subtext: Color3;
|
|
26
|
+
highlight: Color3;
|
|
27
|
+
success: Color3;
|
|
28
|
+
error: Color3;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface Log {
|
|
32
|
+
command: string;
|
|
33
|
+
response: Response;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export namespace CenturionUI {
|
|
37
|
+
export function mount(keys?: Enum.KeyCode[]): void;
|
|
38
|
+
|
|
39
|
+
export function configure(options: Partial<Options>): void;
|
|
40
|
+
|
|
41
|
+
export function register_commands(options?: { exclude?: string[]; prefix?: string }): void;
|
|
42
|
+
|
|
43
|
+
export function clear(): void;
|
|
44
|
+
|
|
45
|
+
export function open(): void;
|
|
46
|
+
|
|
47
|
+
export function close(): void;
|
|
48
|
+
|
|
49
|
+
export function toggle(): void;
|
|
50
|
+
|
|
51
|
+
export function is_open(): boolean;
|
|
52
|
+
|
|
53
|
+
export const themes: {
|
|
54
|
+
frappe: Theme;
|
|
55
|
+
macchiato: Theme;
|
|
56
|
+
latte: Theme;
|
|
57
|
+
mocha: Theme;
|
|
58
|
+
roblox: Theme;
|
|
59
|
+
};
|
|
60
|
+
}
|
package/src/ts/init.luau
ADDED
package/src/types.luau
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
local centurion = require("../centurion")
|
|
2
|
+
export type Suggestion = {
|
|
3
|
+
name: string,
|
|
4
|
+
type: string,
|
|
5
|
+
description: string?,
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export type Options = {
|
|
9
|
+
hide_on_lost_focus: boolean,
|
|
10
|
+
activation_keys: { Enum.KeyCode },
|
|
11
|
+
theme: Theme,
|
|
12
|
+
font: Font,
|
|
13
|
+
icon: string,
|
|
14
|
+
transparency: number,
|
|
15
|
+
format_date: (log: Log) -> string,
|
|
16
|
+
max_suggestions: number,
|
|
17
|
+
|
|
18
|
+
text_size: number,
|
|
19
|
+
subtext_size: number,
|
|
20
|
+
|
|
21
|
+
anchor: Vector2,
|
|
22
|
+
width: UDim,
|
|
23
|
+
position: UDim2,
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export type OptionsPartial = {
|
|
27
|
+
hide_on_lost_focus: boolean?,
|
|
28
|
+
activation_keys: { Enum.KeyCode }?,
|
|
29
|
+
theme: Theme?,
|
|
30
|
+
font: Font?,
|
|
31
|
+
icon: string?,
|
|
32
|
+
transparency: number?,
|
|
33
|
+
format_date: ((log: Log) -> string)?,
|
|
34
|
+
max_suggestions: number?,
|
|
35
|
+
|
|
36
|
+
text_size: number?,
|
|
37
|
+
subtext_size: number?,
|
|
38
|
+
|
|
39
|
+
anchor: Vector2?,
|
|
40
|
+
width: UDim?,
|
|
41
|
+
position: UDim2?,
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export type Theme = {
|
|
45
|
+
background: Color3,
|
|
46
|
+
surface: Color3,
|
|
47
|
+
text: Color3,
|
|
48
|
+
subtext: Color3,
|
|
49
|
+
highlight: Color3,
|
|
50
|
+
success: Color3,
|
|
51
|
+
error: Color3,
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export type AnalysisSuggestion = {
|
|
55
|
+
kind: "command" | "argument",
|
|
56
|
+
name: string,
|
|
57
|
+
description: string?,
|
|
58
|
+
-- ranges of `name` that match the input
|
|
59
|
+
matches: { vector },
|
|
60
|
+
|
|
61
|
+
span: vector,
|
|
62
|
+
replace: string,
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export type AnalysisArgument = {
|
|
66
|
+
index: number,
|
|
67
|
+
type: string,
|
|
68
|
+
kind: "single" | "list",
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export type AnalysisResult = {
|
|
72
|
+
result: centurion.Result<unknown>,
|
|
73
|
+
command: string?,
|
|
74
|
+
argument: AnalysisArgument?,
|
|
75
|
+
suggestions: { AnalysisSuggestion },
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export type Log = {
|
|
79
|
+
command: string,
|
|
80
|
+
response: centurion.Response,
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export type TextStyle = "normal" | "error" | "subtext" | "bg" | "highlight"
|
|
84
|
+
|
|
85
|
+
return nil
|
package/vide/init.luau
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
type Action = {
|
|
2
|
+
priority: number,
|
|
3
|
+
callback: (Instance) -> ()
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
local ActionMT = table.freeze {}
|
|
7
|
+
|
|
8
|
+
local function is_action(v: any)
|
|
9
|
+
return getmetatable(v) == ActionMT
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
local function action(callback: (Instance) -> (), priority: number?): Action
|
|
13
|
+
local a = {
|
|
14
|
+
priority = priority or 1,
|
|
15
|
+
callback = callback
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
setmetatable(a :: any, ActionMT)
|
|
19
|
+
|
|
20
|
+
return table.freeze(a)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
return function()
|
|
24
|
+
return action, is_action
|
|
25
|
+
end
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
local typeof = game and typeof or require "../test/mock".typeof :: never
|
|
2
|
+
|
|
3
|
+
local flags = require "./flags"
|
|
4
|
+
local implicit_effect = require "./implicit_effect"
|
|
5
|
+
local _, is_action = require "./action"()
|
|
6
|
+
local graph = require "./graph"
|
|
7
|
+
type Node<T> = graph.Node<T>
|
|
8
|
+
|
|
9
|
+
type Array<V> = { V }
|
|
10
|
+
type ArrayOrV<V> = {ArrayOrV<V>} | V
|
|
11
|
+
type Map<K, V> = { [K]: V }
|
|
12
|
+
|
|
13
|
+
type Cache = {
|
|
14
|
+
-- event listeners to connect after properties are set
|
|
15
|
+
events: Array<
|
|
16
|
+
| string -- 1. event name
|
|
17
|
+
| () -> () -- 2. listener
|
|
18
|
+
>,
|
|
19
|
+
|
|
20
|
+
-- actions to run after events are connected
|
|
21
|
+
actions: Map<
|
|
22
|
+
number, -- priority
|
|
23
|
+
Array<(Instance) -> ()> -- action callbacks
|
|
24
|
+
>,
|
|
25
|
+
|
|
26
|
+
-- what to parent the instance to after running actions
|
|
27
|
+
parent: unknown,
|
|
28
|
+
|
|
29
|
+
-- cache to detect duplicate property setting at same nesting depth
|
|
30
|
+
nested_debug: Map<
|
|
31
|
+
number, -- depth
|
|
32
|
+
Map<string, true> -- set of property names
|
|
33
|
+
>,
|
|
34
|
+
|
|
35
|
+
-- each nested layer occupies two indexes: 1. table ref 2. nested depth
|
|
36
|
+
-- e.g. { t1 = { t3 = {} }, t2 = {} } -> { t1, 1, t2, 1, t3, 2 }
|
|
37
|
+
nested_stack: { {} | number }
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
local free_cache: Cache?
|
|
41
|
+
|
|
42
|
+
local function borrow_cache(): Cache
|
|
43
|
+
if free_cache then
|
|
44
|
+
local cache = free_cache
|
|
45
|
+
free_cache = nil
|
|
46
|
+
return cache
|
|
47
|
+
else
|
|
48
|
+
return {
|
|
49
|
+
events = {},
|
|
50
|
+
actions = setmetatable({} :: any, { -- lazy init
|
|
51
|
+
__index = function(self, i) self[i] = {}; return self[i] end
|
|
52
|
+
}),
|
|
53
|
+
parent = nil,
|
|
54
|
+
nested_debug = setmetatable({} :: any, {
|
|
55
|
+
__index = function(self, i: number) self[i] = {}; return self[i] end
|
|
56
|
+
}),
|
|
57
|
+
nested_stack = {}
|
|
58
|
+
}
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
local function return_cache(cache: Cache )
|
|
63
|
+
free_cache = cache
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
local function process_properties(properties: Map<unknown, unknown>, instance: Instance, cache: Cache, depth: number)
|
|
67
|
+
for property, value in properties do
|
|
68
|
+
if type(property) == "string" then
|
|
69
|
+
if flags.strict then -- check for duplicate property assignment at nesting depth
|
|
70
|
+
if cache.nested_debug[depth][property] then
|
|
71
|
+
error(`duplicate property {property} at depth {depth}`, 0)
|
|
72
|
+
end
|
|
73
|
+
cache.nested_debug[depth][property] = true
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
if property == "Parent" then
|
|
77
|
+
cache.parent = value
|
|
78
|
+
continue
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
if type(value) == "function" then
|
|
82
|
+
if typeof((instance :: any)[property]) == "RBXScriptSignal" then
|
|
83
|
+
table.insert(cache.events, property) -- add event name to buffer
|
|
84
|
+
table.insert(cache.events, value :: () -> ()) -- add event listener to buffer
|
|
85
|
+
else
|
|
86
|
+
implicit_effect.property(instance, property, value :: () -> ()) -- create implicit effect for property
|
|
87
|
+
end
|
|
88
|
+
else
|
|
89
|
+
(instance :: any)[property] = value -- set property
|
|
90
|
+
end
|
|
91
|
+
elseif type(property) == "number" then
|
|
92
|
+
if type(value) == "function" then
|
|
93
|
+
implicit_effect.children(instance, value :: () -> ArrayOrV<Instance>) -- bind children
|
|
94
|
+
elseif type(value) == "table" then
|
|
95
|
+
if is_action(value) then
|
|
96
|
+
table.insert(cache.actions[(value :: any).priority], (value :: any).callback :: () -> ()) -- add action to buffer
|
|
97
|
+
elseif flags.defer_nested_properties then
|
|
98
|
+
table.insert(cache.nested_stack, value :: {})
|
|
99
|
+
table.insert(cache.nested_stack, depth + 1) -- push table to stack for later processing
|
|
100
|
+
else
|
|
101
|
+
process_properties(value :: Map<unknown, unknown>, instance, cache, depth + 1)
|
|
102
|
+
end
|
|
103
|
+
elseif type(value) == "userdata" then
|
|
104
|
+
(value :: Instance).Parent = instance -- parent child
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
-- applies table of nested properties to an instance using full vide semantics
|
|
111
|
+
local function apply<T>(instance: T & Instance, properties: { [unknown]: unknown }): T
|
|
112
|
+
if not properties then
|
|
113
|
+
error "attempt to call a constructor returned by create() with no properties"
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
local caches = borrow_cache()
|
|
117
|
+
local events = caches.events
|
|
118
|
+
local actions = caches.actions
|
|
119
|
+
local nested_debug = caches.nested_debug
|
|
120
|
+
local nested_stack = caches.nested_stack
|
|
121
|
+
|
|
122
|
+
-- process all properties
|
|
123
|
+
local depth = 1
|
|
124
|
+
repeat
|
|
125
|
+
process_properties(properties, instance, caches, depth)
|
|
126
|
+
depth = table.remove(nested_stack) :: number
|
|
127
|
+
properties = table.remove(nested_stack) :: {}
|
|
128
|
+
until not properties
|
|
129
|
+
|
|
130
|
+
for i = 1, #events, 2 do
|
|
131
|
+
local event_name = events[i]
|
|
132
|
+
local event_listener = events[i + 1]
|
|
133
|
+
;(instance :: any)[event_name]:Connect(event_listener)
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
for _, queued in actions do
|
|
137
|
+
for _, callback in queued do
|
|
138
|
+
callback(instance)
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
local parent = caches.parent
|
|
143
|
+
if parent then
|
|
144
|
+
if type(parent) == "function" then
|
|
145
|
+
implicit_effect.parent(instance, parent :: () -> Instance)
|
|
146
|
+
else
|
|
147
|
+
instance.Parent = parent :: Instance
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
table.clear(events)
|
|
152
|
+
for _, queued in actions do table.clear(queued) end
|
|
153
|
+
caches.parent = nil
|
|
154
|
+
if flags.strict then table.clear(nested_debug) end
|
|
155
|
+
table.clear(nested_stack)
|
|
156
|
+
|
|
157
|
+
return_cache(caches)
|
|
158
|
+
|
|
159
|
+
return instance
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
return apply
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
local flags = require "./flags"
|
|
2
|
+
local graph = require "./graph"
|
|
3
|
+
|
|
4
|
+
local function batch(setter: () -> ())
|
|
5
|
+
local already_batching = flags.batch
|
|
6
|
+
local from
|
|
7
|
+
|
|
8
|
+
if not already_batching then
|
|
9
|
+
flags.batch = true
|
|
10
|
+
from = graph.get_update_queue_length()
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
local ok, err: string? = xpcall(setter, debug.traceback)
|
|
14
|
+
|
|
15
|
+
if not already_batching then
|
|
16
|
+
flags.batch = false
|
|
17
|
+
graph.flush_update_queue(from)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
if not ok then error(`error occured while batching updates: {err}`, 0) end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
return batch
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
local graph = require "./graph"
|
|
2
|
+
type Node<T> = graph.Node<T>
|
|
3
|
+
local create_node = graph.create_node
|
|
4
|
+
local push_scope = graph.push_scope
|
|
5
|
+
local pop_scope = graph.pop_scope
|
|
6
|
+
local destroy = graph.destroy
|
|
7
|
+
local get_scope = graph.get_scope
|
|
8
|
+
|
|
9
|
+
local function branch<T>(fn: () -> T): (() -> (), T)
|
|
10
|
+
local current = get_scope()
|
|
11
|
+
if not current then
|
|
12
|
+
error(`cannot use branch() outside a stable or reactive scope`, 0)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
local parent = current.owner
|
|
16
|
+
if not parent then
|
|
17
|
+
error(`current scope is not owned by a scope`, 0)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
local node = create_node(parent, false, false)
|
|
21
|
+
|
|
22
|
+
local destroy = function()
|
|
23
|
+
destroy(node)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
push_scope(node)
|
|
27
|
+
|
|
28
|
+
local ok, result = xpcall(fn, debug.traceback)
|
|
29
|
+
|
|
30
|
+
pop_scope()
|
|
31
|
+
|
|
32
|
+
if not ok then
|
|
33
|
+
destroy()
|
|
34
|
+
error(`error while running branch():\n\n{result}`, 0)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
return destroy, result
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
return branch
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
local action = require "./action"()
|
|
2
|
+
local cleanup = require "./cleanup"
|
|
3
|
+
|
|
4
|
+
local function changed<T>(property: string, callback: (T) -> ())
|
|
5
|
+
return action(function(instance)
|
|
6
|
+
local con = instance:GetPropertyChangedSignal(property):Connect(function()
|
|
7
|
+
callback((instance :: any)[property])
|
|
8
|
+
end)
|
|
9
|
+
|
|
10
|
+
cleanup(function()
|
|
11
|
+
con:Disconnect()
|
|
12
|
+
end)
|
|
13
|
+
|
|
14
|
+
callback((instance :: any)[property])
|
|
15
|
+
end)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
return changed
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
local typeof = game and typeof or require "../test/mock".typeof :: never
|
|
2
|
+
|
|
3
|
+
local graph = require "./graph"
|
|
4
|
+
local get_scope = graph.get_scope
|
|
5
|
+
local push_cleanup = graph.push_cleanup
|
|
6
|
+
|
|
7
|
+
local function helper(obj: any)
|
|
8
|
+
return
|
|
9
|
+
if typeof(obj) == "RBXScriptConnection" then function() obj:Disconnect() end
|
|
10
|
+
elseif type(obj) == "thread" then function() task.cancel(obj) end
|
|
11
|
+
elseif typeof(obj) == "Instance" then function() obj:Destroy() end
|
|
12
|
+
elseif obj.destroy then function() obj:destroy() end
|
|
13
|
+
elseif obj.disconnect then function() obj:disconnect() end
|
|
14
|
+
elseif obj.Destroy then function() obj:Destroy() end
|
|
15
|
+
elseif obj.Disconnect then function() obj:Disconnect() end
|
|
16
|
+
else error "cannot cleanup given object"
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
local function cleanup(value: unknown)
|
|
20
|
+
local scope = get_scope()
|
|
21
|
+
|
|
22
|
+
if not scope then
|
|
23
|
+
error "cannot cleanup outside a stable or reactive scope"
|
|
24
|
+
end; assert(scope)
|
|
25
|
+
|
|
26
|
+
if type(value) == "function" then
|
|
27
|
+
push_cleanup(scope, value :: () -> ())
|
|
28
|
+
else
|
|
29
|
+
push_cleanup(scope, helper(value))
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
type Destroyable = { destroy: (any) -> () } | { Destroy: (any) -> () }
|
|
34
|
+
type Disconnectable = { disconnect: (any) -> () } | { Disconnect: (any) -> () }
|
|
35
|
+
|
|
36
|
+
return cleanup ::
|
|
37
|
+
( (callback: () -> ()) -> () ) &
|
|
38
|
+
( (thread: thread) -> () ) &
|
|
39
|
+
( (instance: Destroyable) -> () ) &
|
|
40
|
+
( (connection: Disconnectable) -> () ) &
|
|
41
|
+
( (instance: Instance) -> () ) &
|
|
42
|
+
( (connection: RBXScriptConnection) -> () )
|
|
43
|
+
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
local graph = require "./graph"
|
|
2
|
+
type Node<T> = graph.Node<T>
|
|
3
|
+
local create_node = graph.create_node
|
|
4
|
+
local get_scope = graph.get_scope
|
|
5
|
+
local push_scope = graph.push_scope
|
|
6
|
+
local pop_scope = graph.pop_scope
|
|
7
|
+
local set_context = graph.set_context
|
|
8
|
+
|
|
9
|
+
export type Context<T> = (() -> T) & (<U>(T, () -> U) -> U)
|
|
10
|
+
|
|
11
|
+
local nil_symbol = newproxy()
|
|
12
|
+
local count = 0
|
|
13
|
+
|
|
14
|
+
local function context<T>(...: T): Context<T>
|
|
15
|
+
count += 1
|
|
16
|
+
local id = count
|
|
17
|
+
|
|
18
|
+
local has_default = select("#", ...) > 0
|
|
19
|
+
local default_value = ...
|
|
20
|
+
|
|
21
|
+
return function<T>(...): any -- todo: fix type error
|
|
22
|
+
local scope: Node<unknown>? | false = get_scope()
|
|
23
|
+
|
|
24
|
+
if select("#", ...) == 0 then -- get
|
|
25
|
+
while scope do
|
|
26
|
+
local ctx = scope.context
|
|
27
|
+
|
|
28
|
+
if not ctx then
|
|
29
|
+
scope = scope.owner
|
|
30
|
+
continue
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
local value = (ctx :: { unknown })[id]
|
|
34
|
+
|
|
35
|
+
if value == nil then
|
|
36
|
+
scope = scope.owner
|
|
37
|
+
continue
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
return (if value ~= nil_symbol then value else nil) :: T
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
if has_default ~= nil then
|
|
44
|
+
return default_value
|
|
45
|
+
else
|
|
46
|
+
error("attempt to get context when no context is set and no default context is set", 0)
|
|
47
|
+
end
|
|
48
|
+
else -- set
|
|
49
|
+
if not scope then return error("attempt to set context outside of a vide scope", 0) end
|
|
50
|
+
|
|
51
|
+
local value, component = ...
|
|
52
|
+
|
|
53
|
+
local new_scope = create_node(scope, false, false)
|
|
54
|
+
set_context(new_scope, id, if value == nil then nil_symbol else value)
|
|
55
|
+
|
|
56
|
+
push_scope(new_scope)
|
|
57
|
+
|
|
58
|
+
local function efn(err: string) return debug.traceback(err, 3) end
|
|
59
|
+
local ok, result = xpcall(component, efn)
|
|
60
|
+
|
|
61
|
+
pop_scope()
|
|
62
|
+
|
|
63
|
+
if not ok then
|
|
64
|
+
error(`error while running context:\n\n{result}`, 0)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
return result
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
return nil :: any
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
return context
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
local typeof = game and typeof or require "../test/mock".typeof :: never
|
|
2
|
+
local Instance = game and Instance or require "../test/mock".Instance :: never
|
|
3
|
+
|
|
4
|
+
local defaults = require "./defaults"
|
|
5
|
+
local apply = require "./apply"
|
|
6
|
+
local flags = require "./flags"
|
|
7
|
+
|
|
8
|
+
local function create_constructor_for_class(class: string): ({ [unknown]: unknown }) -> Instance
|
|
9
|
+
local function constructor(properties: { [unknown]: unknown }): Instance
|
|
10
|
+
local ok, instance: Instance = pcall(Instance.new, class :: any)
|
|
11
|
+
if not ok then error(`invalid class name {class}`, 0) end
|
|
12
|
+
|
|
13
|
+
if flags.defaults then
|
|
14
|
+
local default: { [string]: unknown }? = defaults[class]
|
|
15
|
+
if default then
|
|
16
|
+
for i, v in default do
|
|
17
|
+
(instance :: any)[i] = v
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
return apply(instance, properties)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
return constructor
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
local constructor_cache = {} :: { [string]: ({ [unknown]: unknown }) -> Instance }
|
|
29
|
+
|
|
30
|
+
-- todo: remove support for different overloads
|
|
31
|
+
local function create(class_or_instance: string|Instance, properties: { [unknown]: unknown }?): unknown
|
|
32
|
+
if type(class_or_instance) ~= "string" and typeof(class_or_instance) ~= "Instance" then
|
|
33
|
+
error("bad argument #1, expected string or instance, got " .. typeof(class_or_instance), 0)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
local constructor: ({ [unknown]: unknown }) -> Instance
|
|
37
|
+
if type(class_or_instance) == "string" then
|
|
38
|
+
constructor = constructor_cache[class_or_instance]
|
|
39
|
+
if not constructor then
|
|
40
|
+
constructor = create_constructor_for_class(class_or_instance)
|
|
41
|
+
constructor_cache[class_or_instance] = constructor
|
|
42
|
+
end
|
|
43
|
+
else
|
|
44
|
+
constructor = function(props)
|
|
45
|
+
local clone = assert(class_or_instance:Clone(), "attempt to clone a non-archivable instance")
|
|
46
|
+
return apply(clone, props)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
return if properties
|
|
51
|
+
then constructor(properties)
|
|
52
|
+
else constructor
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
type Instances = {
|
|
56
|
+
Folder: Folder,
|
|
57
|
+
BillboardGui: BillboardGui,
|
|
58
|
+
CanvasGroup: CanvasGroup,
|
|
59
|
+
Frame: Frame,
|
|
60
|
+
ImageButton: ImageButton,
|
|
61
|
+
ImageLabel: ImageLabel,
|
|
62
|
+
ScreenGui: ScreenGui,
|
|
63
|
+
ScrollingFrame: ScrollingFrame,
|
|
64
|
+
SurfaceGui: SurfaceGui,
|
|
65
|
+
TextBox: TextBox,
|
|
66
|
+
TextButton: TextButton,
|
|
67
|
+
TextLabel: TextLabel,
|
|
68
|
+
UIAspectRatioConstraint: UIAspectRatioConstraint,
|
|
69
|
+
UICorner: UICorner,
|
|
70
|
+
UIGradient: UIGradient,
|
|
71
|
+
UIGridLayout: UIGridLayout,
|
|
72
|
+
UIListLayout: UIListLayout,
|
|
73
|
+
Camera: Camera,
|
|
74
|
+
WorldModel: WorldModel,
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
type function Properties(instance: type?)
|
|
78
|
+
local properties = types.newtable()
|
|
79
|
+
|
|
80
|
+
while instance do
|
|
81
|
+
for i, v in instance:properties() do
|
|
82
|
+
local connector = v.read and v.read.tag == "table" and v.read:readproperty(types.singleton("Connect"))
|
|
83
|
+
if connector then
|
|
84
|
+
local params = connector:parameters().head
|
|
85
|
+
if not params then continue end
|
|
86
|
+
local listener = params[2]
|
|
87
|
+
if not listener then continue end
|
|
88
|
+
properties:setproperty(i, types.optional(listener))
|
|
89
|
+
elseif v.write then
|
|
90
|
+
properties:setproperty(i, types.optional(types.unionof(
|
|
91
|
+
v.write,
|
|
92
|
+
types.newfunction({}, { head = { v.write } })
|
|
93
|
+
)))
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
instance = instance:readparent()
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
properties:setindexer(types.number, types.any)
|
|
101
|
+
|
|
102
|
+
return properties
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
type Create = <Name>(Name|keyof<Instances>|"") -> (Properties<index<Instances, Name>>) -> index<Instances, Name>
|
|
106
|
+
|
|
107
|
+
return create :: Create
|