@rbxts/centurion-ui 1.0.0-dev-ecb0e98 → 1.0.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/out/components/history/history-list.luau +16 -16
- package/out/components/terminal/command.luau +2 -2
- package/out/components/terminal/suggestion.luau +34 -21
- package/out/components/terminal/terminal-text-field/index.d.ts +1 -0
- package/out/components/terminal/terminal-text-field/init.luau +7 -0
- package/out/components/terminal/terminal-text-field/suggestion.d.ts +5 -0
- package/out/components/terminal/terminal-text-field/suggestion.luau +128 -0
- package/out/components/terminal/{terminal-text-field.luau → terminal-text-field/terminal-text-field.luau} +50 -162
- package/out/components/terminal/terminal.luau +25 -23
- package/out/store.d.ts +1 -0
- package/out/store.luau +20 -1
- package/package.json +9 -10
- /package/out/components/terminal/{terminal-text-field.d.ts → terminal-text-field/terminal-text-field.d.ts} +0 -0
|
@@ -2,10 +2,9 @@
|
|
|
2
2
|
local TS = _G[script]
|
|
3
3
|
local _vide = TS.import(script, TS.getModule(script, "@rbxts", "vide").src)
|
|
4
4
|
local Vide = _vide
|
|
5
|
-
local
|
|
5
|
+
local derive = _vide.derive
|
|
6
6
|
local For = _vide.For
|
|
7
7
|
local read = _vide.read
|
|
8
|
-
local source = _vide.source
|
|
9
8
|
local useAtom = TS.import(script, script.Parent.Parent.Parent, "hooks", "use-atom").useAtom
|
|
10
9
|
local px = TS.import(script, script.Parent.Parent.Parent, "hooks", "use-px").px
|
|
11
10
|
local interfaceOptions = TS.import(script, script.Parent.Parent.Parent, "store").interfaceOptions
|
|
@@ -17,29 +16,30 @@ local function HistoryList(_param)
|
|
|
17
16
|
local position = _param.position
|
|
18
17
|
local maxHeight = _param.maxHeight
|
|
19
18
|
local options = useAtom(interfaceOptions)
|
|
20
|
-
local
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
canvasSize(UDim2.new(0, 0, 0, height))
|
|
26
|
-
canvasPos(Vector2.new(0, height))
|
|
27
|
-
if maxHeight ~= nil then
|
|
28
|
-
scrollingEnabled(height > read(maxHeight))
|
|
29
|
-
end
|
|
19
|
+
local height = derive(function()
|
|
20
|
+
return read(data).height - px(8)
|
|
21
|
+
end)
|
|
22
|
+
local exceedsMaxHeight = derive(function()
|
|
23
|
+
return maxHeight ~= nil and height() > read(maxHeight)
|
|
30
24
|
end)
|
|
31
25
|
return Vide.jsx(ScrollingFrame, {
|
|
32
26
|
size = size,
|
|
33
27
|
position = position,
|
|
34
|
-
canvasSize =
|
|
35
|
-
|
|
28
|
+
canvasSize = function()
|
|
29
|
+
return UDim2.fromOffset(0, height())
|
|
30
|
+
end,
|
|
31
|
+
canvasPosition = function()
|
|
32
|
+
return Vector2.new(0, height())
|
|
33
|
+
end,
|
|
36
34
|
scrollBarColor = function()
|
|
37
35
|
return options().palette.subtext
|
|
38
36
|
end,
|
|
39
37
|
scrollBarThickness = function()
|
|
40
|
-
return if
|
|
38
|
+
return if exceedsMaxHeight() then 10 else 0
|
|
39
|
+
end,
|
|
40
|
+
scrollingEnabled = function()
|
|
41
|
+
return exceedsMaxHeight()
|
|
41
42
|
end,
|
|
42
|
-
scrollingEnabled = scrollingEnabled,
|
|
43
43
|
}, Vide.jsx(For, {
|
|
44
44
|
each = function()
|
|
45
45
|
return read(data).lines
|
|
@@ -5,7 +5,7 @@ local ImmutableRegistryPath = _centurion.ImmutableRegistryPath
|
|
|
5
5
|
local RegistryPath = _centurion.RegistryPath
|
|
6
6
|
local IS_EDIT = TS.import(script, script.Parent.Parent.Parent, "constants", "util").IS_EDIT
|
|
7
7
|
local function formatPartsAsPath(parts)
|
|
8
|
-
return table.concat(parts, "/")
|
|
8
|
+
return string.lower(table.concat(parts, "/"))
|
|
9
9
|
end
|
|
10
10
|
local function getValidPath(registry, text)
|
|
11
11
|
if #text == 0 then
|
|
@@ -17,7 +17,7 @@ local function getValidPath(registry, text)
|
|
|
17
17
|
end
|
|
18
18
|
local path = RegistryPath:empty()
|
|
19
19
|
for _, part in text do
|
|
20
|
-
path:append(part)
|
|
20
|
+
path:append(string.lower(part))
|
|
21
21
|
if registry:getCommand(path) == nil and registry:getGroup(path) == nil and #registry:getChildPaths(path) == 0 then
|
|
22
22
|
path:remove(path:size() - 1)
|
|
23
23
|
break
|
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
local TS = _G[script]
|
|
3
3
|
local Players = TS.import(script, TS.getModule(script, "@rbxts", "services")).Players
|
|
4
4
|
local MAX_OTHER_SUGGESTIONS = 3
|
|
5
|
+
local function formatText(text)
|
|
6
|
+
return if #{ string.match(text, "%s") } == 0 then text else `"{text}"`
|
|
7
|
+
end
|
|
5
8
|
local function getMatches(strings, text)
|
|
6
9
|
if text == nil then
|
|
7
10
|
table.sort(strings)
|
|
@@ -41,51 +44,61 @@ local function getMatches(strings, text)
|
|
|
41
44
|
return _newValue
|
|
42
45
|
end
|
|
43
46
|
local function getArgumentSuggestion(arg, textPart)
|
|
44
|
-
local _array = {}
|
|
45
|
-
local _length = #_array
|
|
46
|
-
local _array_1 = (arg.options.suggestions or {})
|
|
47
|
-
table.move(_array_1, 1, #_array_1, _length + 1, _array)
|
|
48
|
-
local suggestions = _array
|
|
49
|
-
local singleArg = arg.kind == "single"
|
|
50
47
|
local _result
|
|
48
|
+
if arg.options.suggestions ~= nil then
|
|
49
|
+
local _exp = arg.options.suggestions
|
|
50
|
+
-- ▼ ReadonlyArray.map ▼
|
|
51
|
+
local _newValue = table.create(#_exp)
|
|
52
|
+
for _k, _v in _exp do
|
|
53
|
+
_newValue[_k] = formatText(_v, _k - 1, _exp)
|
|
54
|
+
end
|
|
55
|
+
-- ▲ ReadonlyArray.map ▲
|
|
56
|
+
_result = _newValue
|
|
57
|
+
else
|
|
58
|
+
_result = {}
|
|
59
|
+
end
|
|
60
|
+
local suggestions = _result
|
|
61
|
+
local singleArg = arg.kind == "single"
|
|
62
|
+
local _result_1
|
|
51
63
|
if singleArg then
|
|
52
|
-
local
|
|
53
|
-
if
|
|
64
|
+
local _result_2 = arg.type.suggestions
|
|
65
|
+
if _result_2 ~= nil then
|
|
54
66
|
local _condition = arg.input
|
|
55
67
|
if _condition == nil then
|
|
56
68
|
_condition = ""
|
|
57
69
|
end
|
|
58
|
-
|
|
70
|
+
_result_2 = _result_2(_condition, Players.LocalPlayer)
|
|
59
71
|
end
|
|
60
|
-
|
|
72
|
+
_result_1 = _result_2
|
|
61
73
|
else
|
|
62
|
-
local
|
|
63
|
-
if
|
|
64
|
-
|
|
74
|
+
local _result_2 = arg.type.suggestions
|
|
75
|
+
if _result_2 ~= nil then
|
|
76
|
+
_result_2 = _result_2(arg.input, Players.LocalPlayer)
|
|
65
77
|
end
|
|
66
|
-
|
|
78
|
+
_result_1 = _result_2
|
|
67
79
|
end
|
|
68
|
-
local typeSuggestions =
|
|
80
|
+
local typeSuggestions = _result_1
|
|
69
81
|
if typeSuggestions ~= nil then
|
|
70
82
|
for _, text in typeSuggestions do
|
|
71
|
-
|
|
83
|
+
local _arg0 = formatText(text)
|
|
84
|
+
table.insert(suggestions, _arg0)
|
|
72
85
|
end
|
|
73
86
|
end
|
|
74
87
|
local errorText
|
|
75
88
|
if not arg.type.expensive then
|
|
76
89
|
local success, err = pcall(function()
|
|
77
|
-
local
|
|
90
|
+
local _result_2
|
|
78
91
|
if singleArg then
|
|
79
92
|
local _type = arg.type
|
|
80
93
|
local _condition = arg.input
|
|
81
94
|
if _condition == nil then
|
|
82
95
|
_condition = ""
|
|
83
96
|
end
|
|
84
|
-
|
|
97
|
+
_result_2 = _type.transform(_condition, Players.LocalPlayer)
|
|
85
98
|
else
|
|
86
|
-
|
|
99
|
+
_result_2 = arg.type.transform(arg.input, Players.LocalPlayer)
|
|
87
100
|
end
|
|
88
|
-
local transformResult =
|
|
101
|
+
local transformResult = _result_2
|
|
89
102
|
if transformResult.ok then
|
|
90
103
|
return nil
|
|
91
104
|
end
|
|
@@ -170,7 +183,7 @@ local function getCommandSuggestion(registry, parentPath, text)
|
|
|
170
183
|
local _newValue_1 = table.create(#sortedPaths)
|
|
171
184
|
local _callback_1 = function(_param)
|
|
172
185
|
local str = _param[2]
|
|
173
|
-
return str
|
|
186
|
+
return formatText(str)
|
|
174
187
|
end
|
|
175
188
|
for _k, _v in sortedPaths do
|
|
176
189
|
_newValue_1[_k] = _callback_1(_v, _k - 1, sortedPaths)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./terminal-text-field";
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { BaseRegistry } from "@rbxts/centurion";
|
|
2
|
+
import { Suggestion } from "../../../types";
|
|
3
|
+
export declare function completeCommand(registry: BaseRegistry, text: string, suggestion: string): string;
|
|
4
|
+
export declare function completeArgument(text: string, suggestion: string, commandArgCount: number): string;
|
|
5
|
+
export declare function getSuggestedText(registry: BaseRegistry, text: string, suggestion?: Suggestion): string;
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local TS = _G[script]
|
|
3
|
+
local _store = TS.import(script, script.Parent.Parent.Parent.Parent, "store")
|
|
4
|
+
local atNextPart = _store.atNextPart
|
|
5
|
+
local commandArgIndex = _store.commandArgIndex
|
|
6
|
+
local currentCommandPath = _store.currentCommandPath
|
|
7
|
+
local currentTextPart = _store.currentTextPart
|
|
8
|
+
local terminalArgIndex = _store.terminalArgIndex
|
|
9
|
+
local terminalTextParts = _store.terminalTextParts
|
|
10
|
+
local _command = TS.import(script, script.Parent.Parent, "command")
|
|
11
|
+
local formatPartsAsPath = _command.formatPartsAsPath
|
|
12
|
+
local getArgumentNames = _command.getArgumentNames
|
|
13
|
+
local function replaceTextPart(text, ...)
|
|
14
|
+
local suggestions = { ... }
|
|
15
|
+
local currentPart = currentTextPart()
|
|
16
|
+
local _result = currentPart
|
|
17
|
+
if _result ~= nil then
|
|
18
|
+
_result = #_result
|
|
19
|
+
end
|
|
20
|
+
local _condition = _result
|
|
21
|
+
if _condition == nil then
|
|
22
|
+
_condition = 0
|
|
23
|
+
end
|
|
24
|
+
local currentPartSize = _condition
|
|
25
|
+
local _text = text
|
|
26
|
+
local _arg1 = #text - currentPartSize
|
|
27
|
+
local result = string.sub(_text, 0, _arg1)
|
|
28
|
+
result ..= table.concat(suggestions, " ")
|
|
29
|
+
return result
|
|
30
|
+
end
|
|
31
|
+
local function completeCommand(registry, text, suggestion)
|
|
32
|
+
local textParts = terminalTextParts()
|
|
33
|
+
local atNextPart = string.sub(text, -1) == " "
|
|
34
|
+
local _array = {}
|
|
35
|
+
local _length = #_array
|
|
36
|
+
table.move(textParts, 1, #textParts, _length + 1, _array)
|
|
37
|
+
local pathParts = _array
|
|
38
|
+
if not atNextPart then
|
|
39
|
+
local _arg0 = #textParts - 1
|
|
40
|
+
table.remove(pathParts, _arg0 + 1)
|
|
41
|
+
end
|
|
42
|
+
local _suggestion = suggestion
|
|
43
|
+
table.insert(pathParts, _suggestion)
|
|
44
|
+
local _command_1 = registry:getCommandByString(formatPartsAsPath(pathParts))
|
|
45
|
+
if _command_1 ~= nil then
|
|
46
|
+
_command_1 = _command_1.options
|
|
47
|
+
end
|
|
48
|
+
local command = _command_1
|
|
49
|
+
local result = replaceTextPart(text, suggestion)
|
|
50
|
+
-- Add a space if this path isn't a command (meaning it's a group) or is a command with arguments
|
|
51
|
+
local _condition = command == nil
|
|
52
|
+
if not _condition then
|
|
53
|
+
local _result = command.arguments
|
|
54
|
+
if _result ~= nil then
|
|
55
|
+
_result = #_result == 0
|
|
56
|
+
end
|
|
57
|
+
local _condition_1 = _result
|
|
58
|
+
if _condition_1 == nil then
|
|
59
|
+
_condition_1 = true
|
|
60
|
+
end
|
|
61
|
+
_condition = not _condition_1
|
|
62
|
+
end
|
|
63
|
+
if _condition then
|
|
64
|
+
result ..= " "
|
|
65
|
+
end
|
|
66
|
+
return result
|
|
67
|
+
end
|
|
68
|
+
local function completeArgument(text, suggestion, commandArgCount)
|
|
69
|
+
local argIndex = terminalArgIndex()
|
|
70
|
+
if argIndex == nil or commandArgCount == 0 then
|
|
71
|
+
return text
|
|
72
|
+
end
|
|
73
|
+
local result = replaceTextPart(text, suggestion)
|
|
74
|
+
if argIndex < commandArgCount - 1 then
|
|
75
|
+
result ..= " "
|
|
76
|
+
end
|
|
77
|
+
return result
|
|
78
|
+
end
|
|
79
|
+
local function getSuggestedText(registry, text, suggestion)
|
|
80
|
+
if suggestion == nil then
|
|
81
|
+
return ""
|
|
82
|
+
end
|
|
83
|
+
local suggestionText
|
|
84
|
+
local parts = terminalTextParts()
|
|
85
|
+
if #parts == 0 or suggestion.type == "command" then
|
|
86
|
+
suggestionText = suggestion.title
|
|
87
|
+
else
|
|
88
|
+
local command = currentCommandPath()
|
|
89
|
+
local argIndex = terminalArgIndex()
|
|
90
|
+
if command == nil or argIndex == nil then
|
|
91
|
+
return ""
|
|
92
|
+
end
|
|
93
|
+
if atNextPart() and argIndex == commandArgIndex() then
|
|
94
|
+
suggestionText = suggestion.title
|
|
95
|
+
elseif not (#suggestion.others == 0) then
|
|
96
|
+
suggestionText = suggestion.others[1]
|
|
97
|
+
else
|
|
98
|
+
local _condition = currentTextPart()
|
|
99
|
+
if _condition == nil then
|
|
100
|
+
_condition = ""
|
|
101
|
+
end
|
|
102
|
+
suggestionText = _condition
|
|
103
|
+
end
|
|
104
|
+
local argNames = getArgumentNames(registry, command)
|
|
105
|
+
for i = argIndex + 1, #argNames - 1 do
|
|
106
|
+
suggestionText ..= ` {argNames[i + 1]}`
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
local _result = currentTextPart()
|
|
110
|
+
if _result ~= nil then
|
|
111
|
+
_result = #_result
|
|
112
|
+
end
|
|
113
|
+
local _condition = _result
|
|
114
|
+
if _condition == nil then
|
|
115
|
+
_condition = 0
|
|
116
|
+
end
|
|
117
|
+
local partSize = _condition
|
|
118
|
+
local result = text
|
|
119
|
+
local _suggestionText = suggestionText
|
|
120
|
+
local _arg0 = partSize + 1
|
|
121
|
+
result ..= string.sub(_suggestionText, _arg0)
|
|
122
|
+
return result
|
|
123
|
+
end
|
|
124
|
+
return {
|
|
125
|
+
completeCommand = completeCommand,
|
|
126
|
+
completeArgument = completeArgument,
|
|
127
|
+
getSuggestedText = getSuggestedText,
|
|
128
|
+
}
|
|
@@ -1,35 +1,29 @@
|
|
|
1
1
|
-- Compiled with roblox-ts v3.0.0
|
|
2
2
|
local TS = _G[script]
|
|
3
|
-
local splitString = TS.import(script, TS.getModule(script, "@rbxts", "centurion").out.shared.util.string).splitString
|
|
4
3
|
local subscribe = TS.import(script, TS.getModule(script, "@rbxts", "charm").src).subscribe
|
|
5
4
|
local UserInputService = TS.import(script, TS.getModule(script, "@rbxts", "services")).UserInputService
|
|
6
5
|
local _vide = TS.import(script, TS.getModule(script, "@rbxts", "vide").src)
|
|
7
6
|
local Vide = _vide
|
|
8
7
|
local cleanup = _vide.cleanup
|
|
9
|
-
local effect = _vide.effect
|
|
10
8
|
local source = _vide.source
|
|
11
|
-
local useAtom = TS.import(script, script.Parent.Parent.Parent, "hooks", "use-atom").useAtom
|
|
12
|
-
local useClient = TS.import(script, script.Parent.Parent.Parent, "hooks", "use-client").useClient
|
|
13
|
-
local useEvent = TS.import(script, script.Parent.Parent.Parent, "hooks", "use-event").useEvent
|
|
14
|
-
local px = TS.import(script, script.Parent.Parent.Parent, "hooks", "use-px").px
|
|
15
|
-
local _store = TS.import(script, script.Parent.Parent.Parent, "store")
|
|
16
|
-
local commandArgIndex = _store.commandArgIndex
|
|
9
|
+
local useAtom = TS.import(script, script.Parent.Parent.Parent.Parent, "hooks", "use-atom").useAtom
|
|
10
|
+
local useClient = TS.import(script, script.Parent.Parent.Parent.Parent, "hooks", "use-client").useClient
|
|
11
|
+
local useEvent = TS.import(script, script.Parent.Parent.Parent.Parent, "hooks", "use-event").useEvent
|
|
12
|
+
local px = TS.import(script, script.Parent.Parent.Parent.Parent, "hooks", "use-px").px
|
|
13
|
+
local _store = TS.import(script, script.Parent.Parent.Parent.Parent, "store")
|
|
17
14
|
local currentCommandPath = _store.currentCommandPath
|
|
18
15
|
local currentSuggestion = _store.currentSuggestion
|
|
19
|
-
local currentTextPart = _store.currentTextPart
|
|
20
16
|
local interfaceOptions = _store.interfaceOptions
|
|
21
17
|
local interfaceVisible = _store.interfaceVisible
|
|
22
|
-
local terminalArgIndex = _store.terminalArgIndex
|
|
23
|
-
local terminalText = _store.terminalText
|
|
24
|
-
local terminalTextParts = _store.terminalTextParts
|
|
25
18
|
local terminalTextValid = _store.terminalTextValid
|
|
26
|
-
local Frame = TS.import(script, script.Parent.Parent, "ui", "frame").Frame
|
|
27
|
-
local Padding = TS.import(script, script.Parent.Parent, "ui", "padding").Padding
|
|
28
|
-
local Text = TS.import(script, script.Parent.Parent, "ui", "text").Text
|
|
29
|
-
local TextField = TS.import(script, script.Parent.Parent, "ui", "text-field").TextField
|
|
30
|
-
local
|
|
31
|
-
local
|
|
32
|
-
local
|
|
19
|
+
local Frame = TS.import(script, script.Parent.Parent.Parent, "ui", "frame").Frame
|
|
20
|
+
local Padding = TS.import(script, script.Parent.Parent.Parent, "ui", "padding").Padding
|
|
21
|
+
local Text = TS.import(script, script.Parent.Parent.Parent, "ui", "text").Text
|
|
22
|
+
local TextField = TS.import(script, script.Parent.Parent.Parent, "ui", "text-field").TextField
|
|
23
|
+
local _suggestion = TS.import(script, script.Parent, "suggestion")
|
|
24
|
+
local completeArgument = _suggestion.completeArgument
|
|
25
|
+
local completeCommand = _suggestion.completeCommand
|
|
26
|
+
local getSuggestedText = _suggestion.getSuggestedText
|
|
33
27
|
local TEXT_SIZE = 22
|
|
34
28
|
local function TerminalTextField(_param)
|
|
35
29
|
local anchor = _param.anchor
|
|
@@ -40,7 +34,6 @@ local function TerminalTextField(_param)
|
|
|
40
34
|
local onSubmit = _param.onSubmit
|
|
41
35
|
local client = useClient()
|
|
42
36
|
local options = useAtom(interfaceOptions)
|
|
43
|
-
local visible = useAtom(interfaceVisible)
|
|
44
37
|
local valid = useAtom(terminalTextValid)
|
|
45
38
|
local ref = source()
|
|
46
39
|
local commandHistory = source({})
|
|
@@ -48,8 +41,8 @@ local function TerminalTextField(_param)
|
|
|
48
41
|
local suggestionText = source("")
|
|
49
42
|
local currentTextValue = ""
|
|
50
43
|
-- Focus text field when terminal becomes visible
|
|
51
|
-
|
|
52
|
-
if visible
|
|
44
|
+
local visibleConnection = subscribe(interfaceVisible, function(visible)
|
|
45
|
+
if visible then
|
|
53
46
|
local _result = ref()
|
|
54
47
|
if _result ~= nil then
|
|
55
48
|
_result:CaptureFocus()
|
|
@@ -61,6 +54,15 @@ local function TerminalTextField(_param)
|
|
|
61
54
|
end
|
|
62
55
|
end
|
|
63
56
|
end)
|
|
57
|
+
cleanup(visibleConnection)
|
|
58
|
+
local setText = function(text)
|
|
59
|
+
local textBox = ref()
|
|
60
|
+
if textBox == nil then
|
|
61
|
+
return nil
|
|
62
|
+
end
|
|
63
|
+
textBox.Text = text
|
|
64
|
+
textBox.CursorPosition = #text + 1
|
|
65
|
+
end
|
|
64
66
|
local traverseHistory = function(up)
|
|
65
67
|
local history = commandHistory()
|
|
66
68
|
if #history == 0 then
|
|
@@ -76,8 +78,7 @@ local function TerminalTextField(_param)
|
|
|
76
78
|
end
|
|
77
79
|
local lastIndex = #history - 1
|
|
78
80
|
if not up and historyIndex == lastIndex then
|
|
79
|
-
|
|
80
|
-
suggestionText("")
|
|
81
|
+
setText("")
|
|
81
82
|
commandHistoryIndex(nil)
|
|
82
83
|
return nil
|
|
83
84
|
end
|
|
@@ -86,63 +87,20 @@ local function TerminalTextField(_param)
|
|
|
86
87
|
_condition = #history
|
|
87
88
|
end
|
|
88
89
|
local newIndex = math.clamp(_condition + (if up then -1 else 1), 0, lastIndex)
|
|
89
|
-
|
|
90
|
-
textBox.Text = newText
|
|
91
|
-
textBox.CursorPosition = #newText + 1
|
|
92
|
-
suggestionText("")
|
|
90
|
+
setText(history[newIndex + 1])
|
|
93
91
|
commandHistoryIndex(newIndex)
|
|
94
92
|
end
|
|
95
93
|
local suggestionConnection = subscribe(currentSuggestion, function(suggestion)
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
local atNextPart = string.sub(terminalText(), -1) == " "
|
|
101
|
-
local textParts = terminalTextParts()
|
|
102
|
-
if #textParts == 0 then
|
|
103
|
-
suggestionText(suggestion.title)
|
|
104
|
-
return nil
|
|
105
|
-
end
|
|
106
|
-
local _currentText = ref()
|
|
107
|
-
if _currentText ~= nil then
|
|
108
|
-
_currentText = _currentText.Text
|
|
94
|
+
local _exp = client.registry
|
|
95
|
+
local _result = ref()
|
|
96
|
+
if _result ~= nil then
|
|
97
|
+
_result = _result.Text
|
|
109
98
|
end
|
|
110
|
-
local
|
|
111
|
-
if
|
|
112
|
-
|
|
113
|
-
end
|
|
114
|
-
-- Command suggestions
|
|
115
|
-
if suggestion.type == "command" then
|
|
116
|
-
local suggestionStartIndex = (if not atNextPart then #textParts[#textParts] else 0) + 1
|
|
117
|
-
suggestionText(currentText .. string.sub(suggestion.title, suggestionStartIndex))
|
|
118
|
-
return nil
|
|
119
|
-
end
|
|
120
|
-
-- Argument suggestions
|
|
121
|
-
local command = currentCommandPath()
|
|
122
|
-
local argIndex = terminalArgIndex()
|
|
123
|
-
if suggestion.type ~= "argument" or command == nil or argIndex == nil then
|
|
124
|
-
return nil
|
|
125
|
-
end
|
|
126
|
-
local newText = currentText
|
|
127
|
-
if atNextPart and argIndex == commandArgIndex() then
|
|
128
|
-
newText ..= suggestion.title
|
|
129
|
-
elseif not (#suggestion.others == 0) then
|
|
130
|
-
local _exp = suggestion.others[1]
|
|
131
|
-
local _result = currentTextPart()
|
|
132
|
-
if _result ~= nil then
|
|
133
|
-
_result = #_result
|
|
134
|
-
end
|
|
135
|
-
local _condition = _result
|
|
136
|
-
if _condition == nil then
|
|
137
|
-
_condition = 0
|
|
138
|
-
end
|
|
139
|
-
newText ..= string.sub(_exp, _condition + 1)
|
|
140
|
-
end
|
|
141
|
-
local argNames = getArgumentNames(client.registry, command)
|
|
142
|
-
for i = argIndex + 1, #argNames - 1 do
|
|
143
|
-
newText = `{newText} {argNames[i + 1]}`
|
|
99
|
+
local _condition = _result
|
|
100
|
+
if _condition == nil then
|
|
101
|
+
_condition = ""
|
|
144
102
|
end
|
|
145
|
-
suggestionText(
|
|
103
|
+
suggestionText(getSuggestedText(_exp, _condition, suggestion))
|
|
146
104
|
end)
|
|
147
105
|
cleanup(suggestionConnection)
|
|
148
106
|
useEvent(UserInputService.InputBegan, function(input)
|
|
@@ -158,6 +116,7 @@ local function TerminalTextField(_param)
|
|
|
158
116
|
if _condition then
|
|
159
117
|
return nil
|
|
160
118
|
end
|
|
119
|
+
-- Handle history traversal
|
|
161
120
|
if input.KeyCode == Enum.KeyCode.Up then
|
|
162
121
|
traverseHistory(true)
|
|
163
122
|
elseif input.KeyCode == Enum.KeyCode.Down then
|
|
@@ -166,109 +125,38 @@ local function TerminalTextField(_param)
|
|
|
166
125
|
if input.KeyCode ~= Enum.KeyCode.Tab then
|
|
167
126
|
return nil
|
|
168
127
|
end
|
|
169
|
-
--
|
|
128
|
+
-- Handle autocompletion
|
|
170
129
|
local commandPath = currentCommandPath()
|
|
171
130
|
local suggestion = currentSuggestion()
|
|
172
131
|
if suggestion == nil then
|
|
173
132
|
return nil
|
|
174
133
|
end
|
|
175
134
|
local currentText = textBox.Text
|
|
176
|
-
local textParts = terminalTextParts()
|
|
177
|
-
local atNextPart = string.sub(currentText, -1) == " "
|
|
178
|
-
local lastPart = if not atNextPart then textParts[#textParts] else nil
|
|
179
135
|
if commandPath == nil then
|
|
180
|
-
|
|
181
|
-
if #textParts == 0 then
|
|
182
|
-
return nil
|
|
183
|
-
end
|
|
184
|
-
local _array = {}
|
|
185
|
-
local _length = #_array
|
|
186
|
-
table.move(textParts, 1, #textParts, _length + 1, _array)
|
|
187
|
-
local pathParts = _array
|
|
188
|
-
local newText = currentText
|
|
189
|
-
if atNextPart then
|
|
190
|
-
newText ..= suggestionTitle
|
|
191
|
-
table.insert(pathParts, suggestionTitle)
|
|
192
|
-
elseif lastPart ~= nil then
|
|
193
|
-
local _newText = newText
|
|
194
|
-
local _arg1 = #newText - #lastPart
|
|
195
|
-
newText = string.sub(_newText, 0, _arg1) .. suggestionTitle
|
|
196
|
-
local _arg0 = #textParts - 1
|
|
197
|
-
table.remove(pathParts, _arg0 + 1)
|
|
198
|
-
table.insert(pathParts, suggestionTitle)
|
|
199
|
-
end
|
|
200
|
-
local _nextCommand = client.registry:getCommandByString(formatPartsAsPath(pathParts))
|
|
201
|
-
if _nextCommand ~= nil then
|
|
202
|
-
_nextCommand = _nextCommand.options
|
|
203
|
-
end
|
|
204
|
-
local nextCommand = _nextCommand
|
|
205
|
-
local _condition_1 = nextCommand == nil
|
|
206
|
-
if not _condition_1 then
|
|
207
|
-
local _result = nextCommand.arguments
|
|
208
|
-
if _result ~= nil then
|
|
209
|
-
_result = #_result == 0
|
|
210
|
-
end
|
|
211
|
-
local _condition_2 = _result
|
|
212
|
-
if _condition_2 == nil then
|
|
213
|
-
_condition_2 = true
|
|
214
|
-
end
|
|
215
|
-
_condition_1 = not _condition_2
|
|
216
|
-
end
|
|
217
|
-
if _condition_1 then
|
|
218
|
-
newText ..= " "
|
|
219
|
-
end
|
|
220
|
-
suggestionText("")
|
|
221
|
-
textBox.Text = newText
|
|
222
|
-
textBox.CursorPosition = #newText + 1
|
|
136
|
+
setText(completeCommand(client.registry, textBox.Text, suggestion.title))
|
|
223
137
|
return nil
|
|
224
138
|
end
|
|
225
|
-
local
|
|
226
|
-
if
|
|
227
|
-
|
|
228
|
-
end
|
|
229
|
-
local commandArgs = _commandArgs
|
|
230
|
-
local _condition_1 = suggestion.type == "command" and not atNextPart
|
|
231
|
-
if _condition_1 then
|
|
232
|
-
local _result = commandArgs
|
|
139
|
+
local _result = client.registry:getCommand(commandPath)
|
|
140
|
+
if _result ~= nil then
|
|
141
|
+
_result = _result.options.arguments
|
|
233
142
|
if _result ~= nil then
|
|
234
|
-
_result = #_result
|
|
235
|
-
end
|
|
236
|
-
local _condition_2 = _result
|
|
237
|
-
if _condition_2 == nil then
|
|
238
|
-
_condition_2 = true
|
|
143
|
+
_result = #_result
|
|
239
144
|
end
|
|
240
|
-
_condition_1 = not _condition_2
|
|
241
145
|
end
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
textBox.Text = newText
|
|
246
|
-
textBox.CursorPosition = #newText + 1
|
|
247
|
-
return nil
|
|
146
|
+
local _condition_1 = _result
|
|
147
|
+
if _condition_1 == nil then
|
|
148
|
+
_condition_1 = 0
|
|
248
149
|
end
|
|
249
|
-
|
|
250
|
-
if
|
|
150
|
+
local argCount = _condition_1
|
|
151
|
+
if suggestion.type == "command" and argCount ~= 0 then
|
|
152
|
+
setText(`{currentText} `)
|
|
251
153
|
return nil
|
|
252
154
|
end
|
|
253
|
-
|
|
254
|
-
|
|
155
|
+
if #suggestion.others == 0 then
|
|
156
|
+
setText(currentText)
|
|
255
157
|
return nil
|
|
256
158
|
end
|
|
257
|
-
|
|
258
|
-
local parts = splitString(newText, " ", true)
|
|
259
|
-
local currentPart = parts[#parts]
|
|
260
|
-
local currentPartSize = if currentTextPart() ~= nil then #currentPart else 0
|
|
261
|
-
local _newText = newText
|
|
262
|
-
local _arg1 = #newText - currentPartSize
|
|
263
|
-
newText = string.sub(_newText, 0, _arg1)
|
|
264
|
-
local otherSuggestion = suggestion.others[1]
|
|
265
|
-
newText ..= if #{ string.match(otherSuggestion, "%s") } == 0 then otherSuggestion else `"{otherSuggestion}"`
|
|
266
|
-
if argIndex < #commandArgs - 1 then
|
|
267
|
-
newText ..= " "
|
|
268
|
-
end
|
|
269
|
-
suggestionText("")
|
|
270
|
-
textBox.Text = newText
|
|
271
|
-
textBox.CursorPosition = #newText + 1
|
|
159
|
+
setText(completeArgument(currentText, suggestion.others[1], argCount))
|
|
272
160
|
end)
|
|
273
161
|
return Vide.jsx(Frame, {
|
|
274
162
|
anchor = anchor,
|
|
@@ -14,6 +14,7 @@ local useClient = TS.import(script, script.Parent.Parent.Parent, "hooks", "use-c
|
|
|
14
14
|
local useHistory = TS.import(script, script.Parent.Parent.Parent, "hooks", "use-history").useHistory
|
|
15
15
|
local px = TS.import(script, script.Parent.Parent.Parent, "hooks", "use-px").px
|
|
16
16
|
local _store = TS.import(script, script.Parent.Parent.Parent, "store")
|
|
17
|
+
local atNextPart = _store.atNextPart
|
|
17
18
|
local commandArgIndex = _store.commandArgIndex
|
|
18
19
|
local currentCommandPath = _store.currentCommandPath
|
|
19
20
|
local currentSuggestion = _store.currentSuggestion
|
|
@@ -36,6 +37,8 @@ local getCommandSuggestion = _suggestion.getCommandSuggestion
|
|
|
36
37
|
local TerminalTextField = TS.import(script, script.Parent, "terminal-text-field").TerminalTextField
|
|
37
38
|
local MAX_HEIGHT = HISTORY_TEXT_SIZE * 10
|
|
38
39
|
local TEXT_FIELD_HEIGHT = 40
|
|
40
|
+
local START_QUOTE_PATTERN = `^(['"])`
|
|
41
|
+
local TRAILING_SPACE_PATTERN = "(%s+)$"
|
|
39
42
|
local function Terminal()
|
|
40
43
|
local client = useClient()
|
|
41
44
|
local options = useAtom(interfaceOptions)
|
|
@@ -114,7 +117,7 @@ local function Terminal()
|
|
|
114
117
|
end
|
|
115
118
|
-- If the text ends in a space, we want to count that as having traversed
|
|
116
119
|
-- to the next "part" of the text.
|
|
117
|
-
local
|
|
120
|
+
local endsInSpace = string.sub(text, -1) == " "
|
|
118
121
|
local path = getValidPath(client.registry, parts)
|
|
119
122
|
local command = if path ~= nil then client.registry:getCommand(path) else nil
|
|
120
123
|
if command ~= nil then
|
|
@@ -131,43 +134,42 @@ local function Terminal()
|
|
|
131
134
|
local noArgs = _condition
|
|
132
135
|
currentCommandPath(path)
|
|
133
136
|
missingArgs(missing)
|
|
134
|
-
terminalTextValid(noArgs or #missing == 0 or (
|
|
137
|
+
terminalTextValid(noArgs or #missing == 0 or (endsInSpace and #missing == 1))
|
|
135
138
|
else
|
|
136
139
|
currentCommandPath(nil)
|
|
137
140
|
terminalTextValid(false)
|
|
138
141
|
end
|
|
139
|
-
local
|
|
140
|
-
local
|
|
141
|
-
if
|
|
142
|
-
textPart =
|
|
143
|
-
elseif
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
local trailingSpaces = (string.match(text, "(%s+)$"))
|
|
148
|
-
if trailingSpaces ~= nil then
|
|
149
|
-
textPart ..= tostring(trailingSpaces)
|
|
142
|
+
local textPart = parts[#parts]
|
|
143
|
+
local atNext = atNextPart()
|
|
144
|
+
if atNext then
|
|
145
|
+
textPart = nil
|
|
146
|
+
elseif textPart ~= nil then
|
|
147
|
+
local _condition = (string.match(text, TRAILING_SPACE_PATTERN))
|
|
148
|
+
if _condition == nil then
|
|
149
|
+
_condition = ""
|
|
150
150
|
end
|
|
151
|
+
local spaces = _condition
|
|
152
|
+
textPart ..= tostring(spaces)
|
|
151
153
|
end
|
|
152
|
-
local argIndex = if path ~= nil then #parts - path:size() - (if
|
|
154
|
+
local argIndex = if path ~= nil then #parts - path:size() - (if atNext then 0 else 1) else -1
|
|
153
155
|
terminalArgIndex(argIndex)
|
|
154
156
|
if command == nil or argIndex == -1 then
|
|
155
157
|
-- Get command suggestions
|
|
156
|
-
local parentPath = if not (#parts == 0) then RegistryPath.new(ArrayUtil.slice(parts, 0, #parts - (if
|
|
158
|
+
local parentPath = if not (#parts == 0) then RegistryPath.new(ArrayUtil.slice(parts, 0, #parts - (if atNext then 0 else 1))) else nil
|
|
159
|
+
currentTextPart(textPart)
|
|
157
160
|
local _exp = client.registry
|
|
158
161
|
local _result = parentPath
|
|
159
162
|
if _result ~= nil then
|
|
160
163
|
_result = _result:isEmpty()
|
|
161
164
|
end
|
|
162
165
|
currentSuggestion(getCommandSuggestion(_exp, if not _result then parentPath else nil, textPart))
|
|
163
|
-
currentTextPart(textPart)
|
|
164
166
|
return nil
|
|
165
167
|
end
|
|
166
168
|
-- Handle arguments
|
|
167
169
|
local args = command.options.arguments
|
|
168
170
|
if args == nil then
|
|
169
|
-
currentSuggestion(nil)
|
|
170
171
|
currentTextPart(nil)
|
|
172
|
+
currentSuggestion(nil)
|
|
171
173
|
commandArgIndex(nil)
|
|
172
174
|
return nil
|
|
173
175
|
end
|
|
@@ -217,20 +219,20 @@ local function Terminal()
|
|
|
217
219
|
kind = "single",
|
|
218
220
|
options = currentArg,
|
|
219
221
|
type = argType,
|
|
220
|
-
input =
|
|
221
|
-
},
|
|
222
|
+
input = argTextPart,
|
|
223
|
+
}, argTextPart)
|
|
222
224
|
else
|
|
223
225
|
local _condition_1 = textPart
|
|
224
226
|
if _condition_1 == nil then
|
|
225
227
|
_condition_1 = ""
|
|
226
228
|
end
|
|
227
|
-
local textParts = splitString(_condition_1, ",")
|
|
228
|
-
local
|
|
229
|
+
local textParts = splitString(_condition_1, ",", true)
|
|
230
|
+
local lastText = if not (#textParts == 0) then textParts[#textParts] else nil
|
|
229
231
|
local _result_1 = textPart
|
|
230
232
|
if _result_1 ~= nil then
|
|
231
233
|
_result_1 = string.sub(_result_1, -1)
|
|
232
234
|
end
|
|
233
|
-
argTextPart = if _result_1 ~= "," then
|
|
235
|
+
argTextPart = if _result_1 ~= "," then lastText else nil
|
|
234
236
|
suggestion = getArgumentSuggestion({
|
|
235
237
|
kind = "list",
|
|
236
238
|
options = currentArg,
|
|
@@ -268,7 +270,7 @@ local function Terminal()
|
|
|
268
270
|
})
|
|
269
271
|
return nil
|
|
270
272
|
end
|
|
271
|
-
local args = ArrayUtil.slice(
|
|
273
|
+
local args = ArrayUtil.slice(splitString(terminalText(), " "), commandPath:size())
|
|
272
274
|
client.dispatcher:run(commandPath, args)
|
|
273
275
|
end,
|
|
274
276
|
}))
|
package/out/store.d.ts
CHANGED
|
@@ -9,5 +9,6 @@ export declare const terminalArgIndex: import("@rbxts/charm").Atom<number | unde
|
|
|
9
9
|
export declare const currentSuggestion: import("@rbxts/charm").Atom<Suggestion | undefined>;
|
|
10
10
|
export declare const terminalText: import("@rbxts/charm").Atom<string>;
|
|
11
11
|
export declare const terminalTextParts: import("@rbxts/charm").Molecule<string[]>;
|
|
12
|
+
export declare const atNextPart: import("@rbxts/charm").Molecule<boolean>;
|
|
12
13
|
export declare const terminalTextValid: import("@rbxts/charm").Atom<boolean>;
|
|
13
14
|
export declare const currentTextPart: import("@rbxts/charm").Atom<string | undefined>;
|
package/out/store.luau
CHANGED
|
@@ -14,7 +14,25 @@ local terminalArgIndex = atom(nil)
|
|
|
14
14
|
local currentSuggestion = atom(nil)
|
|
15
15
|
local terminalText = atom("")
|
|
16
16
|
local terminalTextParts = computed(function()
|
|
17
|
-
return splitString(terminalText(), " ")
|
|
17
|
+
return splitString(terminalText(), " ", true)
|
|
18
|
+
end)
|
|
19
|
+
local atNextPart = computed(function()
|
|
20
|
+
local parts = terminalTextParts()
|
|
21
|
+
local textPart = parts[#parts]
|
|
22
|
+
local _startQuote = textPart
|
|
23
|
+
if _startQuote ~= nil then
|
|
24
|
+
local _arg0 = `^(['"])`
|
|
25
|
+
_startQuote = string.match(_startQuote, _arg0)
|
|
26
|
+
end
|
|
27
|
+
local startQuote = _startQuote
|
|
28
|
+
local _endQuote = textPart
|
|
29
|
+
if _endQuote ~= nil then
|
|
30
|
+
local _arg0 = `(['"])%s*$`
|
|
31
|
+
_endQuote = string.match(_endQuote, _arg0)
|
|
32
|
+
end
|
|
33
|
+
local endQuote = _endQuote
|
|
34
|
+
local quoted = startQuote ~= nil and endQuote == nil
|
|
35
|
+
return string.sub(terminalText(), -1) == " " and not quoted
|
|
18
36
|
end)
|
|
19
37
|
local terminalTextValid = atom(false)
|
|
20
38
|
local currentTextPart = atom(nil)
|
|
@@ -28,6 +46,7 @@ return {
|
|
|
28
46
|
currentSuggestion = currentSuggestion,
|
|
29
47
|
terminalText = terminalText,
|
|
30
48
|
terminalTextParts = terminalTextParts,
|
|
49
|
+
atNextPart = atNextPart,
|
|
31
50
|
terminalTextValid = terminalTextValid,
|
|
32
51
|
currentTextPart = currentTextPart,
|
|
33
52
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rbxts/centurion-ui",
|
|
3
3
|
"description": "Terminal UI for the Centurion command framework",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.1",
|
|
5
5
|
"author": "paradoxuum",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"main": "out/init.lua",
|
|
@@ -32,21 +32,20 @@
|
|
|
32
32
|
"dev": "shx rm -rf out && rbxtsc -w"
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
35
|
-
"@rbxts/centurion": "^1.0.0
|
|
35
|
+
"@rbxts/centurion": "^1.0.0",
|
|
36
36
|
"@rbxts/compiler-types": "3.0.0-types.0",
|
|
37
|
-
"@rbxts/types": "^1.0.
|
|
37
|
+
"@rbxts/types": "^1.0.809",
|
|
38
38
|
"roblox-ts": "3.0.0",
|
|
39
39
|
"shx": "^0.3.4",
|
|
40
|
-
"typescript": "
|
|
40
|
+
"typescript": "=5.5.3"
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
|
-
"@rbxts/charm": "^0.
|
|
44
|
-
"@rbxts/services": "^1.5.
|
|
43
|
+
"@rbxts/charm": "^0.7.5",
|
|
44
|
+
"@rbxts/services": "^1.5.5",
|
|
45
45
|
"@rbxts/set-timeout": "^1.1.2",
|
|
46
|
-
"@rbxts/vide": "^0.4.
|
|
46
|
+
"@rbxts/vide": "^0.4.6"
|
|
47
47
|
},
|
|
48
48
|
"peerDependencies": {
|
|
49
|
-
"@rbxts/centurion": "
|
|
50
|
-
}
|
|
51
|
-
"stableVersion": "1.0.0"
|
|
49
|
+
"@rbxts/centurion": "^1.0.0"
|
|
50
|
+
}
|
|
52
51
|
}
|
|
File without changes
|