@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.
Files changed (141) hide show
  1. package/README.md +60 -15
  2. package/centurion.luau +10 -0
  3. package/package.json +13 -23
  4. package/src/analyse/cursor.luau +25 -0
  5. package/src/analyse/highlight.luau +91 -0
  6. package/src/analyse/init.luau +318 -0
  7. package/src/analyse/leventine.luau +298 -0
  8. package/src/analyse/match.luau +39 -0
  9. package/src/analyse/suggest.luau +41 -0
  10. package/src/app/header.luau +128 -0
  11. package/src/app/history.luau +258 -0
  12. package/src/app/init.luau +699 -0
  13. package/src/app/suggestions.luau +419 -0
  14. package/src/button.luau +13 -0
  15. package/src/components/background.luau +37 -0
  16. package/src/components/container.luau +33 -0
  17. package/src/components/corner.luau +11 -0
  18. package/src/components/divider.luau +28 -0
  19. package/src/components/flex.luau +108 -0
  20. package/src/components/gap.luau +25 -0
  21. package/src/components/image.luau +33 -0
  22. package/src/components/padding.luau +59 -0
  23. package/src/components/stroke.luau +20 -0
  24. package/src/components/text.luau +57 -0
  25. package/src/components/textbox.luau +186 -0
  26. package/src/fade.luau +23 -0
  27. package/src/init.luau +142 -0
  28. package/src/input.luau +16 -0
  29. package/src/px.luau +68 -0
  30. package/src/rich.luau +14 -0
  31. package/src/state.luau +33 -0
  32. package/src/theme.luau +70 -0
  33. package/src/ts/index.d.ts +60 -0
  34. package/src/ts/init.luau +5 -0
  35. package/src/types.luau +85 -0
  36. package/vide/init.luau +8 -0
  37. package/vide/src/action.luau +25 -0
  38. package/vide/src/apply.luau +162 -0
  39. package/vide/src/batch.luau +23 -0
  40. package/vide/src/branch.luau +40 -0
  41. package/vide/src/changed.luau +18 -0
  42. package/vide/src/cleanup.luau +43 -0
  43. package/vide/src/context.luau +74 -0
  44. package/vide/src/create.luau +107 -0
  45. package/vide/src/defaults.luau +113 -0
  46. package/vide/src/derive.luau +18 -0
  47. package/vide/src/effect.luau +12 -0
  48. package/vide/src/flags.luau +12 -0
  49. package/vide/src/graph.luau +321 -0
  50. package/vide/src/implicit_effect.luau +125 -0
  51. package/vide/src/indexes.luau +121 -0
  52. package/vide/src/init.luau +10 -0
  53. package/vide/src/lib.luau +120 -0
  54. package/vide/src/mount.luau +11 -0
  55. package/vide/src/read.luau +5 -0
  56. package/vide/src/root.luau +35 -0
  57. package/vide/src/show.luau +36 -0
  58. package/vide/src/source.luau +31 -0
  59. package/vide/src/spring.luau +328 -0
  60. package/vide/src/switch.luau +114 -0
  61. package/vide/src/timeout.luau +27 -0
  62. package/vide/src/untrack.luau +25 -0
  63. package/vide/src/values.luau +131 -0
  64. package/out/app/centurion-app.d.ts +0 -3
  65. package/out/app/centurion-app.luau +0 -86
  66. package/out/app/index.d.ts +0 -32
  67. package/out/app/init.luau +0 -105
  68. package/out/components/history/history-line.d.ts +0 -9
  69. package/out/components/history/history-line.luau +0 -88
  70. package/out/components/history/history-list.d.ts +0 -11
  71. package/out/components/history/history-list.luau +0 -67
  72. package/out/components/history/index.d.ts +0 -1
  73. package/out/components/history/init.luau +0 -7
  74. package/out/components/index.d.ts +0 -2
  75. package/out/components/init.luau +0 -10
  76. package/out/components/suggestions/badge.d.ts +0 -16
  77. package/out/components/suggestions/badge.luau +0 -40
  78. package/out/components/suggestions/index.d.ts +0 -1
  79. package/out/components/suggestions/init.luau +0 -7
  80. package/out/components/suggestions/main-suggestion.d.ts +0 -9
  81. package/out/components/suggestions/main-suggestion.luau +0 -232
  82. package/out/components/suggestions/suggestion-list.d.ts +0 -8
  83. package/out/components/suggestions/suggestion-list.luau +0 -96
  84. package/out/components/suggestions/suggestions.d.ts +0 -2
  85. package/out/components/suggestions/suggestions.luau +0 -64
  86. package/out/components/suggestions/types.d.ts +0 -6
  87. package/out/components/suggestions/types.luau +0 -2
  88. package/out/components/suggestions/util.d.ts +0 -1
  89. package/out/components/suggestions/util.luau +0 -22
  90. package/out/components/terminal/command.d.ts +0 -6
  91. package/out/components/terminal/command.luau +0 -74
  92. package/out/components/terminal/index.d.ts +0 -1
  93. package/out/components/terminal/init.luau +0 -7
  94. package/out/components/terminal/suggestion.d.ts +0 -26
  95. package/out/components/terminal/suggestion.luau +0 -205
  96. package/out/components/terminal/terminal-text-field/index.d.ts +0 -1
  97. package/out/components/terminal/terminal-text-field/init.luau +0 -7
  98. package/out/components/terminal/terminal-text-field/suggestion.d.ts +0 -5
  99. package/out/components/terminal/terminal-text-field/suggestion.luau +0 -124
  100. package/out/components/terminal/terminal-text-field/terminal-text-field.d.ts +0 -13
  101. package/out/components/terminal/terminal-text-field/terminal-text-field.luau +0 -278
  102. package/out/components/terminal/terminal.d.ts +0 -2
  103. package/out/components/terminal/terminal.luau +0 -304
  104. package/out/components/ui/frame.d.ts +0 -17
  105. package/out/components/ui/frame.luau +0 -32
  106. package/out/components/ui/group.d.ts +0 -15
  107. package/out/components/ui/group.luau +0 -29
  108. package/out/components/ui/layer.d.ts +0 -8
  109. package/out/components/ui/layer.luau +0 -26
  110. package/out/components/ui/outline.d.ts +0 -15
  111. package/out/components/ui/outline.luau +0 -116
  112. package/out/components/ui/padding.d.ts +0 -10
  113. package/out/components/ui/padding.luau +0 -19
  114. package/out/components/ui/scrolling-frame.d.ts +0 -14
  115. package/out/components/ui/scrolling-frame.luau +0 -44
  116. package/out/components/ui/text-field.d.ts +0 -12
  117. package/out/components/ui/text-field.luau +0 -60
  118. package/out/components/ui/text.d.ts +0 -17
  119. package/out/components/ui/text.luau +0 -61
  120. package/out/constants/options.d.ts +0 -2
  121. package/out/constants/options.luau +0 -27
  122. package/out/constants/text.d.ts +0 -3
  123. package/out/constants/text.luau +0 -9
  124. package/out/constants/util.d.ts +0 -1
  125. package/out/constants/util.luau +0 -7
  126. package/out/hooks/use-client.d.ts +0 -2
  127. package/out/hooks/use-client.luau +0 -17
  128. package/out/hooks/use-event.d.ts +0 -14
  129. package/out/hooks/use-event.luau +0 -58
  130. package/out/hooks/use-px.d.ts +0 -19
  131. package/out/hooks/use-px.luau +0 -78
  132. package/out/index.d.ts +0 -2
  133. package/out/init.luau +0 -10
  134. package/out/palette.d.ts +0 -47
  135. package/out/palette.luau +0 -42
  136. package/out/store.d.ts +0 -12
  137. package/out/store.luau +0 -26
  138. package/out/types.d.ts +0 -44
  139. package/out/types.luau +0 -2
  140. package/out/utils/string.d.ts +0 -5
  141. 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
+ }
@@ -0,0 +1,5 @@
1
+ local ui = require("./.")
2
+
3
+ return {
4
+ CenturionUI = ui,
5
+ }
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,8 @@
1
+ local vide = require "@self/src/lib"
2
+
3
+ export type source<T> = vide.source<T>
4
+ export type Source<T> = vide.Source<T>
5
+ export type context<T> = vide.context<T>
6
+ export type Context<T> = vide.Context<T>
7
+
8
+ return vide
@@ -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