@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.
@@ -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 effect = _vide.effect
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 scrollingEnabled = source(false)
21
- local canvasSize = source(UDim2.new())
22
- local canvasPos = source(Vector2.new())
23
- effect(function()
24
- local height = read(data).height - px(8)
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 = canvasSize,
35
- canvasPosition = canvasPos,
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 scrollingEnabled() then 10 else 0
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 _result_1 = arg.type.suggestions
53
- if _result_1 ~= nil then
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
- _result_1 = _result_1(_condition, Players.LocalPlayer)
70
+ _result_2 = _result_2(_condition, Players.LocalPlayer)
59
71
  end
60
- _result = _result_1
72
+ _result_1 = _result_2
61
73
  else
62
- local _result_1 = arg.type.suggestions
63
- if _result_1 ~= nil then
64
- _result_1 = _result_1(arg.input, Players.LocalPlayer)
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
- _result = _result_1
78
+ _result_1 = _result_2
67
79
  end
68
- local typeSuggestions = _result
80
+ local typeSuggestions = _result_1
69
81
  if typeSuggestions ~= nil then
70
82
  for _, text in typeSuggestions do
71
- table.insert(suggestions, text)
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 _result_1
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
- _result_1 = _type.transform(_condition, Players.LocalPlayer)
97
+ _result_2 = _type.transform(_condition, Players.LocalPlayer)
85
98
  else
86
- _result_1 = arg.type.transform(arg.input, Players.LocalPlayer)
99
+ _result_2 = arg.type.transform(arg.input, Players.LocalPlayer)
87
100
  end
88
- local transformResult = _result_1
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,7 @@
1
+ -- Compiled with roblox-ts v3.0.0
2
+ local TS = _G[script]
3
+ local exports = {}
4
+ for _k, _v in TS.import(script, script, "terminal-text-field") or {} do
5
+ exports[_k] = _v
6
+ end
7
+ return exports
@@ -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 _command = TS.import(script, script.Parent, "command")
31
- local formatPartsAsPath = _command.formatPartsAsPath
32
- local getArgumentNames = _command.getArgumentNames
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
- effect(function()
52
- if visible() then
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
- textBox.Text = ""
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
- local newText = history[newIndex + 1]
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
- if suggestion == nil then
97
- suggestionText("")
98
- return nil
99
- end
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 currentText = _currentText
111
- if currentText == nil then
112
- return nil
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(newText)
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
- -- Command suggestions
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
- local suggestionTitle = suggestion.title
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 _commandArgs = client.registry:getCommand(commandPath)
226
- if _commandArgs ~= nil then
227
- _commandArgs = _commandArgs.options.arguments
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 == 0
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
- if _condition_1 then
243
- suggestionText("")
244
- local newText = `{currentText} `
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
- -- Argument suggestions
250
- if #suggestion.others == 0 then
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
- local argIndex = terminalArgIndex()
254
- if argIndex == nil or commandArgs == nil then
155
+ if #suggestion.others == 0 then
156
+ setText(currentText)
255
157
  return nil
256
158
  end
257
- local newText = currentText
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 atNextPart = string.sub(text, -1) == " "
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 (atNextPart and #missing == 1))
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 lastPart = parts[#parts]
140
- local textPart = nil
141
- if string.sub(lastPart, 0, 1) == '"' then
142
- textPart = string.sub(lastPart, 2)
143
- elseif not atNextPart then
144
- textPart = lastPart
145
- end
146
- if textPart ~= nil then
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 atNextPart then 0 else 1) else -1
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 atNextPart then 0 else 1))) else nil
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 = textPart,
221
- }, textPart)
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 lastPart = if not (#textParts == 0) then textParts[#textParts] else nil
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 lastPart else nil
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(terminalTextParts(), commandPath:size())
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.0-dev-ecb0e98",
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-dev-ecb0e98",
35
+ "@rbxts/centurion": "^1.0.0",
36
36
  "@rbxts/compiler-types": "3.0.0-types.0",
37
- "@rbxts/types": "^1.0.799",
37
+ "@rbxts/types": "^1.0.809",
38
38
  "roblox-ts": "3.0.0",
39
39
  "shx": "^0.3.4",
40
- "typescript": "~5.5.4"
40
+ "typescript": "=5.5.3"
41
41
  },
42
42
  "dependencies": {
43
- "@rbxts/charm": "^0.6.0",
44
- "@rbxts/services": "^1.5.4",
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.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
  }