@rbxts/centurion-ui 1.0.7 → 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 -273
  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,298 @@
1
+ --!optimize 2
2
+ --!native
3
+
4
+ --[[
5
+ levintine
6
+ a port of fastest-levinstein to luau, with added functions for convenience
7
+ https://github.com/ka-weihe/fastest-levenshtein
8
+ ]]
9
+
10
+ type MapInfo = {
11
+ case_sensitive: boolean?,
12
+ min_score: number?,
13
+ }
14
+
15
+ local PEQ = table.create(0x10000, 0)
16
+ local DEFAULT_MIN_SCORE = -math.huge
17
+ local TCREATE = table.create
18
+ local RSHIFT = bit32.rshift
19
+ local LSHIFT = bit32.lshift
20
+ local SPLIT = string.split
21
+ local LOWER = string.lower
22
+ local FIND = string.find
23
+ local BYTE = string.byte
24
+ local SORT = table.sort
25
+ local CEIL = math.ceil
26
+ local XOR = bit32.bxor
27
+ local AND = bit32.band
28
+ local NOT = bit32.bnot
29
+ local OR = bit32.bor
30
+ local MIN = math.min
31
+
32
+ local function APPLY_CASE_SENSITIVITY(is_case_sensitive: boolean?, a: string, b: string): (string, string)
33
+ if is_case_sensitive then
34
+ return a, b
35
+ else
36
+ return LOWER(a), LOWER(b)
37
+ end
38
+ end
39
+
40
+ local function GET_BYTES(s: string, len: number): { number }
41
+ return { BYTE(s, 1, len) }
42
+ end
43
+
44
+ local function myers_32(a: string, b: string, a_len: number, b_len: number): number
45
+ local lst = LSHIFT(1, a_len - 1)
46
+ local a_bytes = GET_BYTES(a, a_len)
47
+ local b_bytes = GET_BYTES(b, b_len)
48
+ local sc = a_len
49
+ local pv = -1
50
+ local mv = 0
51
+
52
+ -- Set the bits for characters in string a
53
+ for j, a_byte in a_bytes do
54
+ PEQ[a_byte] = OR(PEQ[a_byte], LSHIFT(1, j))
55
+ end
56
+
57
+ for _, byte in b_bytes do
58
+ local eq = PEQ[byte]
59
+ local xv = OR(eq, mv)
60
+ local new_eq = OR(eq, XOR(AND(eq, pv) + pv, pv))
61
+ local mv2 = OR(mv, NOT(OR(new_eq, pv)))
62
+ local pv2 = AND(pv, new_eq)
63
+
64
+ if AND(mv2, lst) ~= 0 then
65
+ sc += 1
66
+ end
67
+
68
+ if AND(pv2, lst) ~= 0 then
69
+ sc -= 1
70
+ end
71
+
72
+ local mv3 = OR(LSHIFT(mv2, 1), 1)
73
+ pv = OR(LSHIFT(pv2, 1), NOT(OR(xv, mv3)))
74
+ mv = AND(mv3, xv)
75
+ end
76
+
77
+ -- Reset the PEQ array
78
+ for _, a_byte in a_bytes do
79
+ PEQ[a_byte] = 0
80
+ end
81
+
82
+ return sc
83
+ end
84
+
85
+ local function myers_x(b: string, a: string, a_len: number, b_len: number): number
86
+ local h_size = CEIL(a_len / 32)
87
+ local v_size = CEIL(b_len / 32)
88
+ local phc = TCREATE(h_size, -1)
89
+ local mhc = TCREATE(h_size, 0)
90
+ local a_bytes = GET_BYTES(a, a_len)
91
+ local b_bytes = GET_BYTES(b, b_len)
92
+ local v_min = MIN(32, b_len)
93
+ local j_outside = 1
94
+
95
+ for j = 1, v_size - 1 do
96
+ local previous_j = j - 1
97
+ local v_len = (v_min + previous_j) * 32
98
+ local start = previous_j * 32
99
+ local pv = -1
100
+ local mv = 0
101
+ j_outside += 1
102
+
103
+ -- Set the bits for characters in string b
104
+ for k = start + 1, v_len, -start do
105
+ local b_byte = b_bytes[k]
106
+ PEQ[b_byte] = OR(PEQ[b_byte], LSHIFT(1, k - start))
107
+ end
108
+
109
+ for index, byte in a_bytes do
110
+ local index_32floor = (index // 32) + 1
111
+ local index_32modulo = index % 32
112
+
113
+ local phc_value = phc[index_32floor]
114
+ local mhc_value = mhc[index_32floor]
115
+ local eq = PEQ[byte]
116
+ local pb = AND(RSHIFT(phc_value, index_32modulo), 1)
117
+ local mb = AND(RSHIFT(mhc_value, index_32modulo), 1)
118
+ local xv = OR(eq, mv)
119
+ local xh = OR(XOR(AND(OR(OR(eq, mb), pv), pv) + pv, pv), eq, mb)
120
+ local ph = OR(mv, NOT(OR(xh, pv)))
121
+ local mh = AND(pv, xh)
122
+ if XOR(RSHIFT(ph, 31), pb) == 1 then
123
+ phc[index_32floor] = XOR(phc_value, LSHIFT(1, index_32modulo))
124
+ end
125
+ if XOR(RSHIFT(mh, 31), mb) == 1 then
126
+ mhc[index_32floor] = XOR(mhc_value, LSHIFT(1, index_32modulo))
127
+ end
128
+
129
+ pv = OR(OR(LSHIFT(mh, 1), mb), NOT(OR(xv, ph)))
130
+ mv = AND(OR(LSHIFT(ph, 1), pb), xv)
131
+ end
132
+
133
+ -- Reset the PEQ array for the current block
134
+ for k = start + 1, v_len do
135
+ PEQ[b_bytes[k]] = 0
136
+ end
137
+ end
138
+
139
+ local start = (j_outside - 1) * 32
140
+ local v_len = MIN(32, b_len - start) + start
141
+ local pv = -1
142
+ local mv = 0
143
+
144
+ -- Set the bits for characters in string b
145
+ for k = start + 1, v_len, -start do
146
+ local byte = b_bytes[k]
147
+ PEQ[byte] = OR(PEQ[byte], LSHIFT(1, k))
148
+ end
149
+
150
+ local score = b_len
151
+
152
+ for index, byte in a_bytes do
153
+ local index_32floor = (index // 32) + 1
154
+ local index_32modulo = index % 32
155
+
156
+ local phc_value = phc[index_32floor]
157
+ local mhc_value = mhc[index_32floor]
158
+ local eq = PEQ[byte]
159
+ local pb = AND(RSHIFT(phc_value, index_32modulo), 1)
160
+ local mb = AND(RSHIFT(mhc_value, index_32modulo), 1)
161
+ local xv = OR(eq, mv)
162
+ local xh = OR(XOR(AND(OR(OR(eq, mb), pv), pv) + pv, pv), eq, mb)
163
+ local ph = OR(mv, NOT(OR(xh, pv)))
164
+ local mh = AND(pv, xh)
165
+ local b_subbed = b_len - 1
166
+ score += AND(RSHIFT(ph, b_subbed), 1) - AND(RSHIFT(mh, b_subbed), 1)
167
+
168
+ if XOR(RSHIFT(ph, 31), pb) == 1 then
169
+ phc[index_32floor] = XOR(phc_value, LSHIFT(1, index_32modulo))
170
+ end
171
+
172
+ if XOR(RSHIFT(mh, 31), mb) == 1 then
173
+ mhc[index_32floor] = XOR(mhc_value, LSHIFT(1, index_32modulo))
174
+ end
175
+
176
+ pv = OR(OR(LSHIFT(mh, 1), mb), NOT(OR(xv, ph)))
177
+ mv = AND(OR(LSHIFT(ph, 1), pb), xv)
178
+ end
179
+
180
+ -- Reset the PEQ array for the last block
181
+ for k = start + 1, v_len do
182
+ PEQ[b_bytes[k]] = 0
183
+ end
184
+
185
+ return score
186
+ end
187
+
188
+ --[[
189
+ Scores `a` and `b` using levenstine distance to compare the strings
190
+
191
+ ```luau
192
+ local score = leventine.score("meow", "mrrp")
193
+ print(score) --> 4
194
+ ```
195
+ ]]
196
+ local function score(a: string, b: string, case_sensitive: boolean?): number
197
+ a, b = APPLY_CASE_SENSITIVITY(case_sensitive, a, b)
198
+ local a_len = #a
199
+ local b_len = #b
200
+
201
+ if a_len < b_len then
202
+ a_len, b_len = b_len, a_len
203
+ a, b = b, a
204
+ end
205
+
206
+ if b_len == 0 then
207
+ return a_len
208
+ elseif a_len <= 32 then
209
+ return myers_32(a, b, a_len, b_len)
210
+ else
211
+ return myers_x(a, b, a_len, b_len)
212
+ end
213
+ end
214
+
215
+ local leventine = {
216
+ score = score,
217
+ }
218
+
219
+ --[[
220
+ Gives a copy of `t` sorted based on highest to lowest score when scored aginst `s`
221
+
222
+ ```luau
223
+ local mapped = leventine.map("meow", { "mrrp", "mrow", "miaow" })
224
+ print(table.concat(mapped, ", ")) --> "mrow, mrrp, miaow"
225
+ ```
226
+ ]]
227
+ function leventine.map(s: string, t: { string }, info: MapInfo?): { string }
228
+ local min_score = info and info.min_score or DEFAULT_MIN_SCORE
229
+ local case_sensitive = info and info.case_sensitive or false
230
+ local mapped = TCREATE(#t) :: { { string | number } }
231
+
232
+ for index, ts in t do
233
+ local score_val = (score(s, ts, case_sensitive))
234
+
235
+ if score_val > min_score then
236
+ mapped[index] = { score_val, ts } :: any
237
+ end
238
+ end
239
+
240
+ SORT(mapped, function(a, b)
241
+ return a[1] < b[1]
242
+ end)
243
+
244
+ for index, val in mapped do
245
+ mapped[index] = val[2] :: any
246
+ end
247
+
248
+ return mapped :: any
249
+ end
250
+
251
+ --[[
252
+ Checks if `b` is a subsequence of `a`
253
+
254
+ ```luau
255
+ print(leventine.has_match("meow", "mw")) --> true
256
+ print(leventine.has_match("meow", "mrrp")) --> false
257
+ ```
258
+ ]]
259
+ function leventine.has_match(a: string, b: string, case_sensitive: boolean?): boolean
260
+ a, b = APPLY_CASE_SENSITIVITY(case_sensitive, a, b)
261
+ local char_index: number? = 1
262
+
263
+ for _, character in SPLIT(b, "") do
264
+ char_index = FIND(a, character, char_index, true)
265
+
266
+ if char_index then
267
+ char_index += 1
268
+ else
269
+ return false
270
+ end
271
+ end
272
+ return true
273
+ end
274
+
275
+ --[[
276
+ Gives the closest string from `t` to `s`
277
+
278
+ ```luau
279
+ local closest = leventine.closest("meow", { "mrrp", "mrow", "miaow" })
280
+ print(closest) --> "mrow"
281
+ ```
282
+ ]]
283
+ function leventine.closest(s: string, t: { string }, case_sensitive: boolean?): (string, number)
284
+ local closest_score = math.huge
285
+ local closest_string: string
286
+
287
+ for _, ts in t do
288
+ local score_val = score(s, ts, case_sensitive)
289
+
290
+ if score_val < closest_score then
291
+ closest_string = ts
292
+ closest_score = score_val
293
+ end
294
+ end
295
+ return closest_string, closest_score
296
+ end
297
+
298
+ return table.freeze(leventine)
@@ -0,0 +1,39 @@
1
+ --!strict
2
+
3
+ -- finds the characters of `candidate` that match `query`, as inclusive ranges
4
+ local function match(query: string, candidate: string): { vector }
5
+ if #query == 0 then
6
+ return {}
7
+ end
8
+
9
+ local normalized_query = query:lower()
10
+ local normalized_candidate = candidate:lower()
11
+
12
+ local start = string.find(normalized_candidate, normalized_query, 1, true)
13
+ if start then
14
+ return { vector.create(start, start + #query - 1, 0) }
15
+ end
16
+
17
+ -- otherwise match each character in order, e.g. "tst" in "test"
18
+ local ranges: { vector } = {}
19
+ local pos = 1
20
+ for i = 1, #normalized_query do
21
+ local found = string.find(normalized_candidate, normalized_query:sub(i, i), pos, true)
22
+ if not found then
23
+ -- fuzzy matches (typos) don't line up with the input, so don't highlight anything
24
+ return {}
25
+ end
26
+
27
+ local last = ranges[#ranges]
28
+ if last and last.y == found - 1 then
29
+ ranges[#ranges] = vector.create(last.x, found, 0)
30
+ else
31
+ table.insert(ranges, vector.create(found, found, 0))
32
+ end
33
+ pos = found + 1
34
+ end
35
+
36
+ return ranges
37
+ end
38
+
39
+ return match
@@ -0,0 +1,41 @@
1
+ local leventine = require("./leventine")
2
+
3
+ local MIN_SCORE = -math.huge
4
+
5
+ local function suggest(input: string, candidates: { string }, max_suggestions: number?): { string }
6
+ if #input == 0 then
7
+ return candidates
8
+ end
9
+
10
+ local normalized = input:lower()
11
+ local scored = {}
12
+ for _, candidate in candidates do
13
+ local score = 0
14
+
15
+ if candidate:sub(1, #normalized):lower() == normalized then
16
+ score = 1000 - (#candidate - #normalized)
17
+ else
18
+ score = leventine.score(normalized, candidate, false)
19
+ score = (1 - score / math.max(#normalized, #candidate)) * 100
20
+ if leventine.has_match(candidate, normalized, false) then
21
+ score += 100
22
+ end
23
+ end
24
+
25
+ if score > MIN_SCORE then
26
+ table.insert(scored, { text = candidate, score = score })
27
+ end
28
+ end
29
+
30
+ table.sort(scored, function(a, b)
31
+ return a.score > b.score
32
+ end)
33
+
34
+ local mapped = {}
35
+ for i = 1, math.min(max_suggestions or #scored, #scored) do
36
+ table.insert(mapped, scored[i].text)
37
+ end
38
+ return mapped
39
+ end
40
+
41
+ return suggest
@@ -0,0 +1,128 @@
1
+ local background = require("../components/background")
2
+ local button_name = require("../button")
3
+ local container = require("../components/container")
4
+ local corner = require("../components/corner")
5
+ local flex = require("../components/flex")
6
+ local image = require("../components/image")
7
+ local padding = require("../components/padding")
8
+ local preferred_input = require("../input")
9
+ local px = require("../px")
10
+ local state = require("../state")
11
+ local text = require("../components/text")
12
+ local vide = require("../../vide")
13
+
14
+ local show = vide.show
15
+
16
+ type Header = {
17
+ read order: number?,
18
+ }
19
+
20
+ -- KeyCode names that aren't what's printed on the key
21
+ local key_labels: { [string]: string } = {
22
+ Backquote = "`",
23
+ Tilde = "~",
24
+ Semicolon = ";",
25
+ Quote = "'",
26
+ Slash = "/",
27
+ BackSlash = "\\",
28
+ Comma = ",",
29
+ Period = ".",
30
+ Minus = "-",
31
+ Equals = "=",
32
+ LeftBracket = "[",
33
+ RightBracket = "]",
34
+ }
35
+
36
+ local function key_label(key: Enum.KeyCode): string
37
+ return key_labels[key.Name] or key.Name
38
+ end
39
+
40
+ local function header(props: Header)
41
+ local input = preferred_input()
42
+
43
+ return container {
44
+ size = px(UDim2.new(1, 0, 0, 34)),
45
+ order = props.order,
46
+
47
+ padding { x = px(12) },
48
+ flex():column():vertical("center"):between("horizontal"),
49
+
50
+ container {
51
+ auto = "XY",
52
+ order = 1,
53
+
54
+ flex():column():vertical("center"):gap(8),
55
+
56
+ image {
57
+ image = state.icon,
58
+ size = px(function()
59
+ return UDim2.fromOffset(state.subtext_size(), state.subtext_size())
60
+ end),
61
+ color = function()
62
+ return state.theme().highlight
63
+ end,
64
+ visible = function()
65
+ return state.icon() ~= ""
66
+ end,
67
+ order = 1,
68
+ },
69
+
70
+ text {
71
+ auto = "XY",
72
+ order = 2,
73
+ text = state.title,
74
+ text_size = px(state.subtext_size),
75
+ text_style = "subtext",
76
+ weight = Enum.FontWeight.Bold,
77
+ },
78
+ },
79
+
80
+ show(function()
81
+ if input() == Enum.PreferredInput.Gamepad then
82
+ return true
83
+ end
84
+ return input() == Enum.PreferredInput.KeyboardAndMouse and #state.activation_keys() > 0
85
+ end, function()
86
+ return container {
87
+ auto = "XY",
88
+ order = 2,
89
+
90
+ flex():column():vertical("center"):gap(6),
91
+
92
+ background {
93
+ auto = "XY",
94
+ order = 1,
95
+ color = function()
96
+ return state.theme().surface
97
+ end,
98
+
99
+ corner(px(4)),
100
+ padding { x = px(6), y = px(2) },
101
+
102
+ text {
103
+ auto = "XY",
104
+ text = function()
105
+ -- gamepads close with B rather than an activation key
106
+ if input() == Enum.PreferredInput.Gamepad then
107
+ return button_name(Enum.KeyCode.ButtonB)
108
+ end
109
+ return key_label(state.activation_keys()[1])
110
+ end,
111
+ text_size = px(state.subtext_size),
112
+ weight = Enum.FontWeight.Bold,
113
+ },
114
+ },
115
+
116
+ text {
117
+ auto = "XY",
118
+ order = 2,
119
+ text = "to close",
120
+ text_size = px(state.subtext_size),
121
+ text_style = "subtext",
122
+ },
123
+ }
124
+ end),
125
+ }
126
+ end
127
+
128
+ return header