@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,59 @@
1
+ local vide = require("../../vide")
2
+
3
+ local create = vide.create
4
+ local read = vide.read
5
+
6
+ type Can<T> = T | () -> T
7
+ type Padding = {
8
+ read padding: Can<number>?,
9
+ read x: Can<number>?,
10
+ read y: Can<number>?,
11
+ read left: Can<number>?,
12
+ read right: Can<number>?,
13
+ read top: Can<number>?,
14
+ read bottom: Can<number>?,
15
+ }
16
+
17
+ local function padding(props: number | () -> number | Padding)
18
+ if typeof(props) == "number" or typeof(props) == "function" then
19
+ return create "UIPadding" {
20
+ PaddingLeft = function()
21
+ return UDim.new(0, read(props))
22
+ end,
23
+ PaddingRight = function()
24
+ return UDim.new(0, read(props))
25
+ end,
26
+ PaddingTop = function()
27
+ return UDim.new(0, read(props))
28
+ end,
29
+ PaddingBottom = function()
30
+ return UDim.new(0, read(props))
31
+ end,
32
+ }
33
+ end
34
+
35
+ local pad = props.padding or 0
36
+ local x = props.x or pad
37
+ local y = props.y or pad
38
+ local left = props.left or x
39
+ local right = props.right or x
40
+ local top = props.top or y
41
+ local bottom = props.bottom or y
42
+
43
+ return create "UIPadding" {
44
+ PaddingLeft = function()
45
+ return UDim.new(0, read(left))
46
+ end,
47
+ PaddingRight = function()
48
+ return UDim.new(0, read(right))
49
+ end,
50
+ PaddingTop = function()
51
+ return UDim.new(0, read(top))
52
+ end,
53
+ PaddingBottom = function()
54
+ return UDim.new(0, read(bottom))
55
+ end,
56
+ }
57
+ end
58
+
59
+ return padding
@@ -0,0 +1,20 @@
1
+ local fade = require("../fade")
2
+ local vide = require("../../vide")
3
+
4
+ local create = vide.create
5
+
6
+ type Can<T> = T | () -> T
7
+ type Stroke = {
8
+ read color: Can<Color3>?,
9
+ read thickness: Can<number>?,
10
+ read transparency: Can<number>?,
11
+ }
12
+
13
+ return function(props: Stroke)
14
+ return create "UIStroke" {
15
+ ApplyStrokeMode = Enum.ApplyStrokeMode.Border,
16
+ Color = props.color,
17
+ Thickness = props.thickness or 1,
18
+ Transparency = fade.fade(props.transparency),
19
+ }
20
+ end
@@ -0,0 +1,57 @@
1
+ local fade = require("../fade")
2
+ local px = require("../px")
3
+ local state = require("../state")
4
+ local theme = require("../theme")
5
+ local types = require("../types")
6
+ local vide = require("../../vide")
7
+
8
+ local create = vide.create
9
+ local read = vide.read :: (<T>(() -> T) -> T) & (<T>(T) -> T)
10
+
11
+ type Can<T> = T | () -> T
12
+ type Text = {
13
+ read size: Can<UDim2>?,
14
+ read auto: Can<Enum.AutomaticSize | "X" | "Y" | "XY">?,
15
+ read order: Can<number>?,
16
+
17
+ read text: Can<string>,
18
+ read weight: Can<Enum.FontWeight>?,
19
+ read text_size: Can<number>?,
20
+ read text_style: Can<types.TextStyle>?,
21
+ read xalignment: Can<Enum.TextXAlignment>?,
22
+ read wrapped: Can<boolean>?,
23
+ read rich_text: Can<boolean>?,
24
+ read zindex: Can<number>?,
25
+ read transparency: Can<number>?,
26
+
27
+ [number]: unknown,
28
+ }
29
+
30
+ return function(props: Text)
31
+ return create "TextLabel" {
32
+ Size = props.size,
33
+ AutomaticSize = props.auto,
34
+ LayoutOrder = props.order,
35
+
36
+ Text = props.text,
37
+ TextSize = props.text_size or px(state.text_size),
38
+ TextTransparency = fade.fade(props.transparency),
39
+ TextColor3 = function()
40
+ return theme.text_color(state.theme(), props.text_style and read(props.text_style) or "normal")
41
+ end,
42
+ FontFace = function()
43
+ local f: Font = state.font()
44
+ local weight = read(props.weight) or Enum.FontWeight.Regular
45
+ return Font.new(f.Family, weight)
46
+ end,
47
+ TextWrapped = props.wrapped,
48
+ TextXAlignment = props.xalignment,
49
+ RichText = props.rich_text,
50
+ ZIndex = props.zindex,
51
+
52
+ AutoLocalize = false,
53
+ BackgroundTransparency = 1,
54
+
55
+ unpack(props),
56
+ }
57
+ end
@@ -0,0 +1,186 @@
1
+ local GuiService = game:GetService("GuiService")
2
+ local UserInputService = game:GetService("UserInputService")
3
+
4
+ local fade = require("../fade")
5
+ local px = require("../px")
6
+ local state = require("../state")
7
+ local theme = require("../theme")
8
+ local types = require("../types")
9
+ local vide = require("../../vide")
10
+
11
+ local create = vide.create
12
+ local changed = vide.changed
13
+ local source = vide.source
14
+ local effect = vide.effect
15
+ local read = vide.read :: (<T>(() -> T) -> T) & (<T>(T) -> T)
16
+
17
+ type Can<T> = T | () -> T
18
+ type TextBoxProps = {
19
+ read width: Can<number>?,
20
+ read height: Can<number>?,
21
+
22
+ read text: Can<string>,
23
+ read text_size: Can<number>?,
24
+ read text_style: Can<types.TextStyle>?,
25
+ read wrapped: Can<boolean>?,
26
+ read weight: Can<Enum.FontWeight>?,
27
+ read rich_text: Can<boolean>?,
28
+ read background_color: Can<Color3>?,
29
+ read background_transparency: Can<number>?,
30
+ read placeholder: Can<string>?,
31
+ read focused: Can<boolean>?,
32
+ read xalignment: Can<Enum.TextXAlignment>?,
33
+ read multiline: Can<boolean>?,
34
+ read editable: Can<boolean>?,
35
+ read transparency: Can<number>?,
36
+ -- whether confirming the console keyboard submits, rather than only committing the text
37
+ read submit_on_keyboard_return: boolean?,
38
+ -- receives a function that opens the keyboard, even if the textbox is already focused without it
39
+ read bind_open_keyboard: ((open: () -> ()) -> ())?,
40
+
41
+ read update_text: ((string) -> ())?,
42
+ read update_focused: ((boolean) -> ())?,
43
+ read enter: (string) -> (),
44
+
45
+ [number]: unknown,
46
+ }
47
+
48
+ return function(props: TextBoxProps)
49
+ local raw_text = source ""
50
+ -- set while focus is briefly released to reopen the touch keyboard
51
+ local reattaching = false
52
+
53
+ local function submit()
54
+ if not props.focused then
55
+ return
56
+ end
57
+ props.enter(raw_text())
58
+ end
59
+
60
+ local textbox: TextBox
61
+ textbox = create "TextBox" {
62
+ Size = function()
63
+ return UDim2.fromOffset(read(props.width) or 0, read(props.height) or 0)
64
+ end,
65
+ AutomaticSize = Enum.AutomaticSize.XY,
66
+
67
+ Text = props.text,
68
+ PlaceholderText = props.placeholder,
69
+ TextSize = props.text_size or px(state.text_size),
70
+ TextTransparency = fade.fade(props.transparency),
71
+ TextColor3 = function()
72
+ return theme.text_color(state.theme(), props.text_style and read(props.text_style) or "normal")
73
+ end,
74
+ TextXAlignment = props.xalignment,
75
+ TextEditable = props.editable,
76
+ TextWrapped = props.wrapped,
77
+ MultiLine = props.multiline,
78
+ FontFace = function()
79
+ local f: Font = state.font()
80
+ local weight = read(props.weight) or Enum.FontWeight.Regular
81
+ return Font.new(f.Family, weight)
82
+ end,
83
+ RichText = props.rich_text,
84
+ PlaceholderColor3 = function()
85
+ return state.theme().subtext
86
+ end,
87
+
88
+ BackgroundColor3 = props.background_color,
89
+ BackgroundTransparency = props.background_transparency or 1,
90
+ AutoLocalize = false,
91
+
92
+ if props.update_text then changed("Text", props.update_text) else nil,
93
+ changed("Text", raw_text),
94
+
95
+ Focused = function()
96
+ if not props.update_focused then
97
+ return
98
+ end
99
+ props.update_focused(true)
100
+ end,
101
+
102
+ FocusLost = function(enter)
103
+ if reattaching then
104
+ return
105
+ end
106
+ if props.update_focused then
107
+ props.update_focused(false)
108
+ end
109
+ if not enter then
110
+ return
111
+ end
112
+ submit()
113
+ end,
114
+
115
+ -- consoles don't fire enter through FocusLost, so this workaround is required
116
+ -- Roblox have shown some interest in fixing this, so it should be removed in that case to prevent duplicate submissions
117
+ ReturnPressedFromOnScreenKeyboard = if GuiService:IsTenFootInterface()
118
+ then function()
119
+ if props.submit_on_keyboard_return ~= false then
120
+ submit()
121
+ else
122
+ -- the textbox can stay focused once the keyboard closes, so release it explicitly
123
+ textbox:ReleaseFocus(false)
124
+ end
125
+ end
126
+ else nil,
127
+
128
+ unpack(props),
129
+ }
130
+
131
+ -- releases focus without reporting it, so that it can be captured again
132
+ local function detach()
133
+ reattaching = true
134
+ textbox:ReleaseFocus(false)
135
+ task.wait()
136
+ reattaching = false
137
+ end
138
+
139
+ local function capture()
140
+ if textbox:IsFocused() then
141
+ -- tapping a button on touch devices can leave the textbox focused
142
+ -- without the keyboard open, so release it and capture it again
143
+ if
144
+ UserInputService.OnScreenKeyboardVisible
145
+ or UserInputService.PreferredInput ~= Enum.PreferredInput.Touch
146
+ then
147
+ return
148
+ end
149
+
150
+ detach()
151
+ end
152
+
153
+ if read(props.focused) then
154
+ textbox:CaptureFocus()
155
+ end
156
+ end
157
+
158
+ if props.bind_open_keyboard then
159
+ props.bind_open_keyboard(function()
160
+ task.defer(function()
161
+ if textbox:IsFocused() then
162
+ detach()
163
+ end
164
+ textbox:CaptureFocus()
165
+ end)
166
+ end)
167
+ end
168
+
169
+ if props.focused ~= nil then
170
+ effect(function()
171
+ local value = read(props.focused)
172
+ if textbox:IsFocused() == value then
173
+ return
174
+ end
175
+ task.defer(function()
176
+ if value then
177
+ capture()
178
+ else
179
+ textbox:ReleaseFocus(false)
180
+ end
181
+ end)
182
+ end)
183
+ end
184
+
185
+ return textbox
186
+ end
package/src/fade.luau ADDED
@@ -0,0 +1,23 @@
1
+ local vide = require("../vide")
2
+
3
+ local read = vide.read
4
+
5
+ -- how visible the current subtree is, from 0 (hidden) to 1 (shown). Lets the
6
+ -- console fade as a whole without a CanvasGroup, which would soften text
7
+ local opacity = vide.context(function()
8
+ return 1
9
+ end :: () -> number)
10
+
11
+ -- combines a transparency with the surrounding opacity
12
+ local function fade(transparency: (number | () -> number)?): () -> number
13
+ local current = opacity()
14
+ return function()
15
+ local value = read(transparency) or 0
16
+ return 1 - (1 - value) * current()
17
+ end
18
+ end
19
+
20
+ return {
21
+ opacity = opacity,
22
+ fade = fade,
23
+ }
package/src/init.luau ADDED
@@ -0,0 +1,142 @@
1
+ local Players = game:GetService("Players")
2
+
3
+ local app = require("@self/app")
4
+ local centurion = require("./centurion")
5
+ local px = require("@self/px")
6
+ local state = require("@self/state")
7
+ local theme = require("@self/theme")
8
+ local types = require("@self/types")
9
+ local vide = require("./vide")
10
+
11
+ local mounted = false
12
+
13
+ local function mount(keys: { Enum.KeyCode }?)
14
+ if mounted then
15
+ return
16
+ end
17
+ mounted = true
18
+
19
+ state.activation_keys(keys or { Enum.KeyCode.F2 })
20
+
21
+ vide.mount(function()
22
+ px.mount()
23
+ return vide.create "ScreenGui" {
24
+ Name = "Centurion",
25
+ ResetOnSpawn = false,
26
+ ZIndexBehavior = Enum.ZIndexBehavior.Sibling,
27
+
28
+ app(),
29
+ }
30
+ end, Players.LocalPlayer:WaitForChild("PlayerGui"))
31
+ end
32
+
33
+ local function set_opt<T>(source: () -> T, value: T?)
34
+ if value == nil then
35
+ return
36
+ end
37
+ source(value)
38
+ end
39
+
40
+ local function configure(options: types.OptionsPartial)
41
+ set_opt(state.hide_on_lost_focus, options.hide_on_lost_focus)
42
+ set_opt(state.activation_keys, options.activation_keys)
43
+ set_opt(state.theme, options.theme)
44
+ set_opt(state.icon, options.icon)
45
+ set_opt(state.font, options.font)
46
+ set_opt(state.transparency, options.transparency)
47
+
48
+ set_opt(state.text_size, options.text_size)
49
+ set_opt(state.subtext_size, options.subtext_size)
50
+
51
+ set_opt(state.anchor, options.anchor)
52
+ set_opt(state.position, options.position)
53
+ set_opt(state.width, options.width)
54
+
55
+ set_opt(state.max_suggestions, options.max_suggestions)
56
+ set_opt(state.format_date, options.format_date)
57
+ end
58
+
59
+ local function clear()
60
+ state.logs({})
61
+ end
62
+
63
+ local function open()
64
+ state.opened(true)
65
+ state.focused(true)
66
+ end
67
+
68
+ local function close()
69
+ state.opened(false)
70
+ state.focused(false)
71
+ end
72
+
73
+ local function toggle()
74
+ if state.opened() then
75
+ close()
76
+ else
77
+ open()
78
+ end
79
+ end
80
+
81
+ local function is_open(): boolean
82
+ return state.opened()
83
+ end
84
+
85
+ local function register_commands(options: { exclude: { string }?, prefix: string? }?)
86
+ options = options or {}
87
+
88
+ local cmd = function<T...>(name: string, cmd_data: centurion.Command<T...>)
89
+ if options.exclude and table.find(options.exclude, name) then
90
+ return
91
+ end
92
+
93
+ local cmd_name = name
94
+ if options.prefix then
95
+ cmd_name = options.prefix .. " " .. cmd_name
96
+ end
97
+ centurion.register_command(cmd_name, cmd_data)
98
+ end
99
+
100
+ cmd("clear", {
101
+ description = "Clear the command history",
102
+ callback = function()
103
+ clear()
104
+ return ""
105
+ end,
106
+ })
107
+
108
+ local theme_names: { types.Theme } = {}
109
+ for name in pairs(theme.themes) do
110
+ table.insert(theme_names, name)
111
+ end
112
+
113
+ local centurion_theme = centurion.register_type("centurion-theme", centurion.create_enum(theme_names))
114
+ cmd("theme", {
115
+ description = "Change the terminal theme",
116
+ arguments = function()
117
+ return centurion_theme("theme", "The name of the theme to switch to")
118
+ end,
119
+ callback = function(ctx, theme_name)
120
+ local data = theme.themes[theme_name]
121
+ if not data then
122
+ ctx.error("Theme '" .. theme_name .. "' does not exist.")
123
+ return
124
+ end
125
+
126
+ state.theme(data)
127
+ return `Theme changed to {theme_name}`
128
+ end,
129
+ })
130
+ end
131
+
132
+ return {
133
+ mount = mount,
134
+ configure = configure,
135
+ register_commands = register_commands,
136
+ clear = clear,
137
+ open = open,
138
+ close = close,
139
+ toggle = toggle,
140
+ is_open = is_open,
141
+ themes = theme.themes,
142
+ }
package/src/input.luau ADDED
@@ -0,0 +1,16 @@
1
+ local UserInputService = game:GetService("UserInputService")
2
+
3
+ local vide = require("../vide")
4
+
5
+ local cleanup = vide.cleanup
6
+ local source = vide.source
7
+
8
+ local function preferred_input(): () -> Enum.PreferredInput
9
+ local preferred = source(UserInputService.PreferredInput)
10
+ cleanup(UserInputService:GetPropertyChangedSignal("PreferredInput"):Connect(function()
11
+ preferred(UserInputService.PreferredInput)
12
+ end))
13
+ return preferred
14
+ end
15
+
16
+ return preferred_input
package/src/px.luau ADDED
@@ -0,0 +1,68 @@
1
+ local vide = require("../vide")
2
+
3
+ local source = vide.source
4
+ local read = vide.read
5
+ local cleanup = vide.cleanup
6
+
7
+ export type Mode = "normal" | "even" | "scale" | "floor" | "ceil"
8
+
9
+ local BASE_RESOLUTION = Vector2.new(1920, 1080)
10
+ local MIN_SCALE = 0.5
11
+ local DOMINANT_AXIS = 0.5
12
+
13
+ local scale = source(1)
14
+
15
+ local function convert(value: number, mode: Mode?): number
16
+ return if mode == "even"
17
+ then math.round(value * scale() * 0.5) * 2
18
+ elseif mode == "scale" then value * scale()
19
+ elseif mode == "floor" then math.floor(value * scale())
20
+ elseif mode == "ceil" then math.ceil(value * scale())
21
+ else math.round(value * scale())
22
+ end
23
+
24
+ local function px_raw<T>(value: T | () -> T, mode: Mode? | () -> Mode?): T
25
+ local value_raw, mode_raw = read(value), read(mode)
26
+
27
+ if typeof(value_raw) == "number" then
28
+ return convert(value_raw, mode_raw) :: any
29
+ elseif typeof(value_raw) == "UDim" then
30
+ return UDim.new(value_raw.Scale, convert(value_raw.Offset, mode_raw)) :: any
31
+ elseif typeof(value_raw) == "UDim2" then
32
+ return UDim2.new(
33
+ value_raw.X.Scale,
34
+ convert(value_raw.X.Offset, mode_raw),
35
+ value_raw.Y.Scale,
36
+ convert(value_raw.Y.Offset, mode_raw)
37
+ ) :: any
38
+ else
39
+ error("px: unsupported type")
40
+ end
41
+ end
42
+
43
+ local function px<T>(value: T | () -> T, mode: Mode? | () -> Mode?): () -> T
44
+ return function()
45
+ return px_raw(value, mode)
46
+ end
47
+ end
48
+
49
+ local function mount()
50
+ local camera = workspace.CurrentCamera
51
+
52
+ local function update()
53
+ local width = math.log(camera.ViewportSize.X / BASE_RESOLUTION.X, 2)
54
+ local height = math.log(camera.ViewportSize.Y / BASE_RESOLUTION.Y, 2)
55
+ local centered = width + (height - width) * DOMINANT_AXIS
56
+ scale(math.max(2 ^ centered, MIN_SCALE))
57
+ end
58
+
59
+ update()
60
+
61
+ cleanup(camera:GetPropertyChangedSignal("ViewportSize"):Connect(update))
62
+ end
63
+
64
+ return setmetatable({ mount = mount, convert = convert, get = px_raw }, {
65
+ __call = function<T>(_, value: T | () -> T, mode: Mode? | () -> Mode?): () -> T
66
+ return px(value, mode)
67
+ end,
68
+ })
package/src/rich.luau ADDED
@@ -0,0 +1,14 @@
1
+ -- helpers for building RichText strings
2
+
3
+ local function escape(text: string): string
4
+ return (text:gsub("&", "&amp;"):gsub("<", "&lt;"):gsub(">", "&gt;"):gsub('"', "&quot;"):gsub("'", "&apos;"))
5
+ end
6
+
7
+ local function color(text: string, value: Color3): string
8
+ return `<font color="#{value:ToHex()}">{text}</font>`
9
+ end
10
+
11
+ return {
12
+ escape = escape,
13
+ color = color,
14
+ }
package/src/state.luau ADDED
@@ -0,0 +1,33 @@
1
+ local theme = require("./theme")
2
+ local types = require("./types")
3
+ local vide = require("../vide")
4
+
5
+ local source = vide.source
6
+
7
+ return {
8
+ opened = source(false),
9
+ focused = source(false),
10
+ logs = source({} :: { types.Log }),
11
+
12
+ -- options
13
+ activation_keys = source({}),
14
+ hide_on_lost_focus = source(true),
15
+ theme = source(theme.themes.mocha),
16
+ font = source(Font.fromId(16658246179)),
17
+ title = source("Centurion"),
18
+ icon = source("rbxassetid://115292290730914"),
19
+ transparency = source(0.2),
20
+ format_date = source(function(log: types.Log): string
21
+ local dateTime = DateTime.fromUnixTimestamp(log.response.timestamp):FormatLocalTime("LT", "en-us")
22
+ local dateParts = dateTime:split(" ")
23
+ return `<b>{dateParts[1]}</b> {dateParts[2]}`
24
+ end),
25
+ max_suggestions = source(5),
26
+
27
+ text_size = source(28),
28
+ subtext_size = source(24),
29
+
30
+ anchor = source(Vector2.zero),
31
+ position = source(UDim2.new()),
32
+ width = source(UDim.new(1, 0)),
33
+ }
package/src/theme.luau ADDED
@@ -0,0 +1,70 @@
1
+ local types = require("./types")
2
+
3
+ local function text_color(theme: types.Theme, style: types.TextStyle)
4
+ return if style == "normal"
5
+ then theme.text
6
+ elseif style == "error" then theme.error
7
+ elseif style == "subtext" then theme.subtext
8
+ elseif style == "bg" then theme.background
9
+ elseif style == "highlight" then theme.highlight
10
+ else theme.text
11
+ end
12
+
13
+ -- subtle outline color derived from the theme, so custom themes don't need an extra field
14
+ local function border(theme: types.Theme)
15
+ return theme.surface:Lerp(theme.text, 0.15)
16
+ end
17
+
18
+ local themes = {
19
+ mocha = {
20
+ background = Color3.fromRGB(30, 30, 46),
21
+ surface = Color3.fromRGB(49, 50, 68),
22
+ text = Color3.fromRGB(205, 214, 244),
23
+ subtext = Color3.fromRGB(166, 173, 200),
24
+ highlight = Color3.fromRGB(137, 180, 250),
25
+ success = Color3.fromRGB(166, 227, 161),
26
+ error = Color3.fromRGB(243, 139, 168),
27
+ } :: types.Theme,
28
+ macchiato = {
29
+ background = Color3.fromRGB(36, 39, 58),
30
+ surface = Color3.fromRGB(54, 58, 79),
31
+ text = Color3.fromRGB(202, 211, 245),
32
+ subtext = Color3.fromRGB(165, 173, 203),
33
+ highlight = Color3.fromRGB(138, 173, 244),
34
+ success = Color3.fromRGB(166, 218, 149),
35
+ error = Color3.fromRGB(237, 135, 150),
36
+ } :: types.Theme,
37
+ frappe = {
38
+ background = Color3.fromRGB(48, 52, 70),
39
+ surface = Color3.fromRGB(65, 69, 89),
40
+ text = Color3.fromRGB(198, 208, 245),
41
+ subtext = Color3.fromRGB(165, 173, 206),
42
+ highlight = Color3.fromRGB(140, 170, 238),
43
+ success = Color3.fromRGB(166, 209, 137),
44
+ error = Color3.fromRGB(231, 130, 132),
45
+ } :: types.Theme,
46
+ latte = {
47
+ background = Color3.fromRGB(230, 233, 239),
48
+ surface = Color3.fromRGB(220, 224, 232),
49
+ text = Color3.fromRGB(76, 79, 105),
50
+ subtext = Color3.fromRGB(92, 95, 119),
51
+ highlight = Color3.fromRGB(30, 102, 245),
52
+ success = Color3.fromRGB(64, 160, 43),
53
+ error = Color3.fromRGB(210, 15, 57),
54
+ } :: types.Theme,
55
+ roblox = {
56
+ background = Color3.fromRGB(17, 18, 22),
57
+ surface = Color3.fromRGB(31, 32, 36),
58
+ text = Color3.fromRGB(255, 255, 255),
59
+ subtext = Color3.fromRGB(187, 188, 190),
60
+ highlight = Color3.fromRGB(51, 95, 255),
61
+ success = Color3.fromRGB(57, 197, 130),
62
+ error = Color3.fromRGB(240, 68, 51),
63
+ } :: types.Theme,
64
+ }
65
+
66
+ return {
67
+ text_color = text_color,
68
+ border = border,
69
+ themes = themes,
70
+ }