@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,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
|