@rbxts/centurion-ui 1.0.0-dev-284510d
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/app/app.d.ts +6 -0
- package/out/app/app.lua +83 -0
- package/out/app/index.d.ts +1 -0
- package/out/app/init.lua +7 -0
- package/out/app/terminal-app.d.ts +1 -0
- package/out/app/terminal-app.lua +61 -0
- package/out/components/history/history-line.d.ts +10 -0
- package/out/components/history/history-line.lua +63 -0
- package/out/components/history/history-list.d.ts +15 -0
- package/out/components/history/history-list.lua +62 -0
- package/out/components/history/index.d.ts +1 -0
- package/out/components/history/init.lua +7 -0
- package/out/components/index.d.ts +1 -0
- package/out/components/init.lua +7 -0
- package/out/components/suggestions/badge.d.ts +16 -0
- package/out/components/suggestions/badge.lua +52 -0
- package/out/components/suggestions/index.d.ts +1 -0
- package/out/components/suggestions/init.lua +7 -0
- package/out/components/suggestions/main-suggestion.d.ts +12 -0
- package/out/components/suggestions/main-suggestion.lua +180 -0
- package/out/components/suggestions/suggestion-list.d.ts +8 -0
- package/out/components/suggestions/suggestion-list.lua +70 -0
- package/out/components/suggestions/suggestions.d.ts +1 -0
- package/out/components/suggestions/suggestions.lua +117 -0
- package/out/components/suggestions/types.d.ts +6 -0
- package/out/components/suggestions/types.lua +2 -0
- package/out/components/suggestions/util.d.ts +4 -0
- package/out/components/suggestions/util.lua +72 -0
- package/out/components/terminal-text-field.d.ts +11 -0
- package/out/components/terminal-text-field.lua +289 -0
- package/out/components/terminal-window.d.ts +1 -0
- package/out/components/terminal-window.lua +265 -0
- package/out/components/terminal.d.ts +1 -0
- package/out/components/terminal.lua +26 -0
- package/out/components/ui/frame.d.ts +20 -0
- package/out/components/ui/frame.lua +29 -0
- package/out/components/ui/group.d.ts +17 -0
- package/out/components/ui/group.lua +24 -0
- package/out/components/ui/layer.d.ts +7 -0
- package/out/components/ui/layer.lua +23 -0
- package/out/components/ui/outline.d.ts +14 -0
- package/out/components/ui/outline.lua +106 -0
- package/out/components/ui/padding.d.ts +10 -0
- package/out/components/ui/padding.lua +19 -0
- package/out/components/ui/scrolling-frame.d.ts +15 -0
- package/out/components/ui/scrolling-frame.lua +42 -0
- package/out/components/ui/shortcut-group.d.ts +6 -0
- package/out/components/ui/shortcut-group.lua +100 -0
- package/out/components/ui/text-field.d.ts +13 -0
- package/out/components/ui/text-field.lua +53 -0
- package/out/components/ui/text.d.ts +20 -0
- package/out/components/ui/text.lua +55 -0
- package/out/constants/options.d.ts +2 -0
- package/out/constants/options.lua +19 -0
- package/out/constants/springs.d.ts +19 -0
- package/out/constants/springs.lua +20 -0
- package/out/constants/text.d.ts +3 -0
- package/out/constants/text.lua +9 -0
- package/out/constants/util.d.ts +1 -0
- package/out/constants/util.lua +7 -0
- package/out/hooks/use-motion.d.ts +4 -0
- package/out/hooks/use-motion.lua +24 -0
- package/out/hooks/use-px.d.ts +24 -0
- package/out/hooks/use-px.lua +56 -0
- package/out/hooks/use-store.d.ts +3 -0
- package/out/hooks/use-store.lua +7 -0
- package/out/index.d.ts +2 -0
- package/out/init.lua +10 -0
- package/out/palette.d.ts +56 -0
- package/out/palette.lua +51 -0
- package/out/providers/api-provider.d.ts +8 -0
- package/out/providers/api-provider.lua +17 -0
- package/out/providers/options-provider.d.ts +13 -0
- package/out/providers/options-provider.lua +39 -0
- package/out/providers/root-provider.d.ts +11 -0
- package/out/providers/root-provider.lua +24 -0
- package/out/store/app/app-selectors.d.ts +2 -0
- package/out/store/app/app-selectors.lua +7 -0
- package/out/store/app/app-slice.d.ts +8 -0
- package/out/store/app/app-slice.lua +17 -0
- package/out/store/app/index.d.ts +2 -0
- package/out/store/app/init.lua +10 -0
- package/out/store/command/command-selectors.d.ts +2 -0
- package/out/store/command/command-selectors.lua +7 -0
- package/out/store/command/command-slice.d.ts +15 -0
- package/out/store/command/command-slice.lua +21 -0
- package/out/store/command/index.d.ts +2 -0
- package/out/store/command/init.lua +10 -0
- package/out/store/history/history-slice.d.ts +11 -0
- package/out/store/history/history-slice.lua +52 -0
- package/out/store/history/index.d.ts +1 -0
- package/out/store/history/init.lua +7 -0
- package/out/store/index.d.ts +46 -0
- package/out/store/init.lua +18 -0
- package/out/store/suggestion/index.d.ts +2 -0
- package/out/store/suggestion/init.lua +10 -0
- package/out/store/suggestion/suggestion-selectors.d.ts +2 -0
- package/out/store/suggestion/suggestion-selectors.lua +7 -0
- package/out/store/suggestion/suggestion-slice.d.ts +9 -0
- package/out/store/suggestion/suggestion-slice.lua +15 -0
- package/out/store/text/index.d.ts +2 -0
- package/out/store/text/init.lua +10 -0
- package/out/store/text/text-selectors.d.ts +3 -0
- package/out/store/text/text-selectors.lua +11 -0
- package/out/store/text/text-slice.d.ts +20 -0
- package/out/store/text/text-slice.lua +29 -0
- package/out/types.d.ts +40 -0
- package/out/types.lua +2 -0
- package/out/util/argument.d.ts +2 -0
- package/out/util/argument.lua +26 -0
- package/out/util/suggestion.d.ts +4 -0
- package/out/util/suggestion.lua +208 -0
- package/package.json +54 -0
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v2.3.0-dev-26ec859
|
|
2
|
+
local TS = _G[script]
|
|
3
|
+
local _react = TS.import(script, TS.getModule(script, "@rbxts", "react"))
|
|
4
|
+
local React = _react
|
|
5
|
+
local useContext = _react.useContext
|
|
6
|
+
local SUGGESTION_TEXT_SIZE = TS.import(script, script.Parent.Parent.Parent, "constants", "text").SUGGESTION_TEXT_SIZE
|
|
7
|
+
local usePx = TS.import(script, script.Parent.Parent.Parent, "hooks", "use-px").usePx
|
|
8
|
+
local OptionsContext = TS.import(script, script.Parent.Parent.Parent, "providers", "options-provider").OptionsContext
|
|
9
|
+
local Frame = TS.import(script, script.Parent.Parent, "ui", "frame").Frame
|
|
10
|
+
local Group = TS.import(script, script.Parent.Parent, "ui", "group").Group
|
|
11
|
+
local Padding = TS.import(script, script.Parent.Parent, "ui", "padding").Padding
|
|
12
|
+
local Text = TS.import(script, script.Parent.Parent, "ui", "text").Text
|
|
13
|
+
local highlightMatching = TS.import(script, script.Parent, "util").highlightMatching
|
|
14
|
+
local function SuggestionList(_param)
|
|
15
|
+
local suggestion = _param.suggestion
|
|
16
|
+
local currentText = _param.currentText
|
|
17
|
+
local size = _param.size
|
|
18
|
+
local px = usePx()
|
|
19
|
+
local options = useContext(OptionsContext)
|
|
20
|
+
local _exp = React.createElement("uilistlayout", {
|
|
21
|
+
SortOrder = "LayoutOrder",
|
|
22
|
+
Padding = UDim.new(0, px(8)),
|
|
23
|
+
})
|
|
24
|
+
local _result = suggestion
|
|
25
|
+
if _result ~= nil then
|
|
26
|
+
_result = _result.others
|
|
27
|
+
if _result ~= nil then
|
|
28
|
+
-- ▼ ReadonlyArray.map ▼
|
|
29
|
+
local _newValue = table.create(#_result)
|
|
30
|
+
local _callback = function(name, i)
|
|
31
|
+
return React.createElement(Frame, {
|
|
32
|
+
key = `{i}-{name}`,
|
|
33
|
+
size = UDim2.new(1, 0, 0, px(SUGGESTION_TEXT_SIZE + 6)),
|
|
34
|
+
backgroundColor = options.palette.background,
|
|
35
|
+
backgroundTransparency = options.backgroundTransparency,
|
|
36
|
+
cornerRadius = UDim.new(0, px(8)),
|
|
37
|
+
clipsDescendants = true,
|
|
38
|
+
}, React.createElement(Padding, {
|
|
39
|
+
all = UDim.new(0, px(4)),
|
|
40
|
+
}), React.createElement(Text, {
|
|
41
|
+
size = UDim2.new(1, 0, 1, 0),
|
|
42
|
+
text = highlightMatching(options.palette.highlight, name, currentText),
|
|
43
|
+
textColor = options.palette.text,
|
|
44
|
+
textSize = px(SUGGESTION_TEXT_SIZE),
|
|
45
|
+
textXAlignment = "Left",
|
|
46
|
+
richText = true,
|
|
47
|
+
}))
|
|
48
|
+
end
|
|
49
|
+
for _k, _v in _result do
|
|
50
|
+
_newValue[_k] = _callback(_v, _k - 1, _result)
|
|
51
|
+
end
|
|
52
|
+
-- ▲ ReadonlyArray.map ▲
|
|
53
|
+
_result = _newValue
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
return React.createElement(Group, {
|
|
57
|
+
size = size,
|
|
58
|
+
event = {
|
|
59
|
+
MouseEnter = function()
|
|
60
|
+
return options.setMouseOnGUI(true)
|
|
61
|
+
end,
|
|
62
|
+
MouseLeave = function()
|
|
63
|
+
return options.setMouseOnGUI(false)
|
|
64
|
+
end,
|
|
65
|
+
},
|
|
66
|
+
}, _exp, _result)
|
|
67
|
+
end
|
|
68
|
+
return {
|
|
69
|
+
SuggestionList = SuggestionList,
|
|
70
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function Suggestions(): JSX.Element;
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v2.3.0-dev-26ec859
|
|
2
|
+
local TS = _G[script]
|
|
3
|
+
local _react = TS.import(script, TS.getModule(script, "@rbxts", "react"))
|
|
4
|
+
local React = _react
|
|
5
|
+
local useBinding = _react.useBinding
|
|
6
|
+
local useContext = _react.useContext
|
|
7
|
+
local useEffect = _react.useEffect
|
|
8
|
+
local useMemo = _react.useMemo
|
|
9
|
+
local useSelector = TS.import(script, TS.getModule(script, "@rbxts", "react-reflex")).useSelector
|
|
10
|
+
local TextService = TS.import(script, TS.getModule(script, "@rbxts", "services")).TextService
|
|
11
|
+
local springs = TS.import(script, script.Parent.Parent.Parent, "constants", "springs").springs
|
|
12
|
+
local _text = TS.import(script, script.Parent.Parent.Parent, "constants", "text")
|
|
13
|
+
local SUGGESTION_TEXT_SIZE = _text.SUGGESTION_TEXT_SIZE
|
|
14
|
+
local SUGGESTION_TITLE_TEXT_SIZE = _text.SUGGESTION_TITLE_TEXT_SIZE
|
|
15
|
+
local useMotion = TS.import(script, script.Parent.Parent.Parent, "hooks", "use-motion").useMotion
|
|
16
|
+
local usePx = TS.import(script, script.Parent.Parent.Parent, "hooks", "use-px").usePx
|
|
17
|
+
local OptionsContext = TS.import(script, script.Parent.Parent.Parent, "providers", "options-provider").OptionsContext
|
|
18
|
+
local selectSuggestion = TS.import(script, script.Parent.Parent.Parent, "store", "suggestion").selectSuggestion
|
|
19
|
+
local selectText = TS.import(script, script.Parent.Parent.Parent, "store", "text").selectText
|
|
20
|
+
local Group = TS.import(script, script.Parent.Parent, "ui", "group").Group
|
|
21
|
+
local MainSuggestion = TS.import(script, script.Parent, "main-suggestion").MainSuggestion
|
|
22
|
+
local SuggestionList = TS.import(script, script.Parent, "suggestion-list").SuggestionList
|
|
23
|
+
local getSuggestionTextBounds = TS.import(script, script.Parent, "util").getSuggestionTextBounds
|
|
24
|
+
local MAX_SUGGESTION_WIDTH = 180
|
|
25
|
+
local MAX_BADGE_WIDTH = 80
|
|
26
|
+
local PADDING = 8
|
|
27
|
+
local function Suggestions()
|
|
28
|
+
local options = useContext(OptionsContext)
|
|
29
|
+
local px = usePx()
|
|
30
|
+
local terminalText = useSelector(selectText)
|
|
31
|
+
local currentTextPart = useMemo(function()
|
|
32
|
+
if terminalText.index == -1 or terminalText.index >= #terminalText.parts then
|
|
33
|
+
return nil
|
|
34
|
+
end
|
|
35
|
+
return terminalText.parts[terminalText.index + 1]
|
|
36
|
+
end, { terminalText })
|
|
37
|
+
local textBoundsParams = useMemo(function()
|
|
38
|
+
return Instance.new("GetTextBoundsParams")
|
|
39
|
+
end, {})
|
|
40
|
+
-- Suggestions
|
|
41
|
+
local currentSuggestion = useSelector(selectSuggestion)
|
|
42
|
+
local sizes, setSizes = useBinding({
|
|
43
|
+
title = UDim2.fromOffset(0, px(SUGGESTION_TITLE_TEXT_SIZE)),
|
|
44
|
+
description = UDim2.fromOffset(0, px(SUGGESTION_TEXT_SIZE)),
|
|
45
|
+
errorTextHeight = 0,
|
|
46
|
+
typeBadgeWidth = 0,
|
|
47
|
+
})
|
|
48
|
+
local suggestionSize, suggestionSizeMotion = useMotion(UDim2.new())
|
|
49
|
+
local otherSuggestionSize, otherSuggestionSizeMotion = useMotion(UDim2.new())
|
|
50
|
+
-- Resize window based on suggestions
|
|
51
|
+
useEffect(function()
|
|
52
|
+
if currentSuggestion == nil then
|
|
53
|
+
suggestionSizeMotion:spring(UDim2.new())
|
|
54
|
+
otherSuggestionSizeMotion:spring(UDim2.new())
|
|
55
|
+
return nil
|
|
56
|
+
end
|
|
57
|
+
local mainSuggestion = currentSuggestion.main
|
|
58
|
+
local otherSuggestions = currentSuggestion.others
|
|
59
|
+
if #otherSuggestions == 0 then
|
|
60
|
+
otherSuggestionSizeMotion:spring(UDim2.new())
|
|
61
|
+
end
|
|
62
|
+
local textBounds = getSuggestionTextBounds(options, mainSuggestion, px(SUGGESTION_TITLE_TEXT_SIZE), px(SUGGESTION_TEXT_SIZE), px(MAX_SUGGESTION_WIDTH), px(MAX_BADGE_WIDTH))
|
|
63
|
+
setSizes(textBounds)
|
|
64
|
+
local windowWidth = math.max(textBounds.title.X.Offset, textBounds.description.X.Offset) + px(PADDING * 2)
|
|
65
|
+
local windowHeight = textBounds.title.Y.Offset + textBounds.description.Y.Offset + textBounds.errorTextHeight + px(PADDING * 2)
|
|
66
|
+
if textBounds.typeBadgeWidth > 0 then
|
|
67
|
+
windowWidth += textBounds.typeBadgeWidth + px(16)
|
|
68
|
+
end
|
|
69
|
+
if textBounds.errorTextHeight > 0 then
|
|
70
|
+
windowHeight += px(8)
|
|
71
|
+
end
|
|
72
|
+
-- Calculate other suggestion sizes
|
|
73
|
+
local otherHeight = 0
|
|
74
|
+
if not (#otherSuggestions == 0) then
|
|
75
|
+
local maxSuggestionWidth = 0
|
|
76
|
+
textBoundsParams.Font = options.font.regular
|
|
77
|
+
textBoundsParams.Size = px(SUGGESTION_TEXT_SIZE)
|
|
78
|
+
textBoundsParams.Width = math.huge
|
|
79
|
+
for _, name in otherSuggestions do
|
|
80
|
+
textBoundsParams.Text = name
|
|
81
|
+
local suggestionBounds = TextService:GetTextBoundsAsync(textBoundsParams)
|
|
82
|
+
if suggestionBounds.X > maxSuggestionWidth then
|
|
83
|
+
maxSuggestionWidth = suggestionBounds.X
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
otherHeight = #otherSuggestions * px(SUGGESTION_TEXT_SIZE + 6) + (#otherSuggestions - 1) * px(4) + px(8)
|
|
87
|
+
windowWidth = math.max(windowWidth, maxSuggestionWidth)
|
|
88
|
+
otherSuggestionSizeMotion:spring(UDim2.fromOffset(windowWidth, otherHeight), springs.gentle)
|
|
89
|
+
end
|
|
90
|
+
suggestionSizeMotion:spring(UDim2.fromOffset(windowWidth, windowHeight), springs.responsive)
|
|
91
|
+
end, { currentSuggestion, px })
|
|
92
|
+
local _attributes = {
|
|
93
|
+
suggestion = currentSuggestion,
|
|
94
|
+
}
|
|
95
|
+
local _result = currentSuggestion
|
|
96
|
+
if _result ~= nil then
|
|
97
|
+
_result = _result.main.type
|
|
98
|
+
end
|
|
99
|
+
_attributes.argument = _result == "argument"
|
|
100
|
+
_attributes.currentText = currentTextPart
|
|
101
|
+
_attributes.size = suggestionSize
|
|
102
|
+
_attributes.sizes = sizes
|
|
103
|
+
return React.createElement(Group, {
|
|
104
|
+
size = UDim2.fromScale(1, 1),
|
|
105
|
+
visible = currentSuggestion ~= nil,
|
|
106
|
+
}, React.createElement(MainSuggestion, _attributes), React.createElement(SuggestionList, {
|
|
107
|
+
suggestion = currentSuggestion,
|
|
108
|
+
currentText = currentTextPart,
|
|
109
|
+
size = otherSuggestionSize,
|
|
110
|
+
}), React.createElement("uilistlayout", {
|
|
111
|
+
SortOrder = "LayoutOrder",
|
|
112
|
+
Padding = UDim.new(0, px(PADDING)),
|
|
113
|
+
}))
|
|
114
|
+
end
|
|
115
|
+
return {
|
|
116
|
+
Suggestions = Suggestions,
|
|
117
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { ArgumentSuggestion, CommandSuggestion, InterfaceOptions } from "../../types";
|
|
2
|
+
import { SuggestionTextBounds } from "./types";
|
|
3
|
+
export declare function highlightMatching(color: Color3, text?: string, terminalText?: string): string;
|
|
4
|
+
export declare function getSuggestionTextBounds(options: InterfaceOptions, suggestion: ArgumentSuggestion | CommandSuggestion, titleTextSize: number, textSize: number, maxWidth: number, maxBadgeWidth: number): SuggestionTextBounds;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v2.3.0-dev-26ec859
|
|
2
|
+
local TS = _G[script]
|
|
3
|
+
local TextService = TS.import(script, TS.getModule(script, "@rbxts", "services")).TextService
|
|
4
|
+
local TEXT_BOUNDS_PARAMS = Instance.new("GetTextBoundsParams")
|
|
5
|
+
local DEFAULT_BOUNDS = Vector2.new()
|
|
6
|
+
local prevColor
|
|
7
|
+
local prevColorHex
|
|
8
|
+
local function highlightMatching(color, text, terminalText)
|
|
9
|
+
if text == nil then
|
|
10
|
+
return ""
|
|
11
|
+
end
|
|
12
|
+
if terminalText == nil then
|
|
13
|
+
return text
|
|
14
|
+
end
|
|
15
|
+
local _text = text
|
|
16
|
+
local _arg1 = #terminalText
|
|
17
|
+
local subText = string.sub(_text, 0, _arg1)
|
|
18
|
+
if string.lower(terminalText) ~= string.lower(subText) then
|
|
19
|
+
return text
|
|
20
|
+
end
|
|
21
|
+
if prevColor ~= color then
|
|
22
|
+
prevColor = color
|
|
23
|
+
prevColorHex = color:ToHex()
|
|
24
|
+
end
|
|
25
|
+
local _text_1 = text
|
|
26
|
+
local _arg0 = #terminalText + 1
|
|
27
|
+
local unhighlightedText = string.sub(_text_1, _arg0)
|
|
28
|
+
return `<font color="#{prevColorHex}">{subText}</font>{unhighlightedText}`
|
|
29
|
+
end
|
|
30
|
+
local function getSuggestionTextBounds(options, suggestion, titleTextSize, textSize, maxWidth, maxBadgeWidth)
|
|
31
|
+
-- Get title text bounds
|
|
32
|
+
TEXT_BOUNDS_PARAMS.Text = suggestion.title
|
|
33
|
+
TEXT_BOUNDS_PARAMS.Size = titleTextSize
|
|
34
|
+
TEXT_BOUNDS_PARAMS.Font = options.font.bold
|
|
35
|
+
local titleBounds = TextService:GetTextBoundsAsync(TEXT_BOUNDS_PARAMS)
|
|
36
|
+
-- Get description text bounds
|
|
37
|
+
TEXT_BOUNDS_PARAMS.Width = maxWidth
|
|
38
|
+
TEXT_BOUNDS_PARAMS.Size = textSize
|
|
39
|
+
TEXT_BOUNDS_PARAMS.Font = options.font.regular
|
|
40
|
+
local descriptionBounds = DEFAULT_BOUNDS
|
|
41
|
+
if suggestion.description ~= nil then
|
|
42
|
+
TEXT_BOUNDS_PARAMS.Text = suggestion.description
|
|
43
|
+
descriptionBounds = TextService:GetTextBoundsAsync(TEXT_BOUNDS_PARAMS)
|
|
44
|
+
end
|
|
45
|
+
local errorTextHeight = 0
|
|
46
|
+
local typeBadgeWidth = 0
|
|
47
|
+
if suggestion.type == "argument" then
|
|
48
|
+
-- Get error text bounds
|
|
49
|
+
if suggestion.error ~= nil then
|
|
50
|
+
local _condition = suggestion.error
|
|
51
|
+
if _condition == nil then
|
|
52
|
+
_condition = ""
|
|
53
|
+
end
|
|
54
|
+
TEXT_BOUNDS_PARAMS.Text = _condition
|
|
55
|
+
errorTextHeight = TextService:GetTextBoundsAsync(TEXT_BOUNDS_PARAMS).Y
|
|
56
|
+
end
|
|
57
|
+
-- Get type badge bounds
|
|
58
|
+
TEXT_BOUNDS_PARAMS.Text = suggestion.dataType
|
|
59
|
+
TEXT_BOUNDS_PARAMS.Width = maxBadgeWidth
|
|
60
|
+
typeBadgeWidth = TextService:GetTextBoundsAsync(TEXT_BOUNDS_PARAMS).X
|
|
61
|
+
end
|
|
62
|
+
return {
|
|
63
|
+
title = UDim2.fromOffset(titleBounds.X, titleBounds.Y),
|
|
64
|
+
description = UDim2.fromOffset(descriptionBounds.X, descriptionBounds.Y),
|
|
65
|
+
errorTextHeight = errorTextHeight,
|
|
66
|
+
typeBadgeWidth = typeBadgeWidth,
|
|
67
|
+
}
|
|
68
|
+
end
|
|
69
|
+
return {
|
|
70
|
+
highlightMatching = highlightMatching,
|
|
71
|
+
getSuggestionTextBounds = getSuggestionTextBounds,
|
|
72
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { BindingOrValue } from "@rbxts/pretty-react-hooks";
|
|
2
|
+
interface TerminalTextFieldProps {
|
|
3
|
+
anchorPoint?: BindingOrValue<Vector2>;
|
|
4
|
+
size?: BindingOrValue<UDim2>;
|
|
5
|
+
position?: BindingOrValue<UDim2>;
|
|
6
|
+
backgroundTransparency?: BindingOrValue<number>;
|
|
7
|
+
onTextChange?: (text: string) => void;
|
|
8
|
+
onSubmit?: (text: string) => void;
|
|
9
|
+
}
|
|
10
|
+
export declare function TerminalTextField({ anchorPoint, size, position, backgroundTransparency, onTextChange, onSubmit, }: TerminalTextFieldProps): JSX.Element;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v2.3.0-dev-26ec859
|
|
2
|
+
local TS = _G[script]
|
|
3
|
+
local _string = TS.import(script, TS.getModule(script, "@rbxts", "centurion").out.shared.util.string)
|
|
4
|
+
local endsWithSpace = _string.endsWithSpace
|
|
5
|
+
local formatPartsAsPath = _string.formatPartsAsPath
|
|
6
|
+
local _pretty_react_hooks = TS.import(script, TS.getModule(script, "@rbxts", "pretty-react-hooks").out)
|
|
7
|
+
local getBindingValue = _pretty_react_hooks.getBindingValue
|
|
8
|
+
local useEventListener = _pretty_react_hooks.useEventListener
|
|
9
|
+
local useMountEffect = _pretty_react_hooks.useMountEffect
|
|
10
|
+
local _react = TS.import(script, TS.getModule(script, "@rbxts", "react"))
|
|
11
|
+
local React = _react
|
|
12
|
+
local useBinding = _react.useBinding
|
|
13
|
+
local useCallback = _react.useCallback
|
|
14
|
+
local useContext = _react.useContext
|
|
15
|
+
local useEffect = _react.useEffect
|
|
16
|
+
local useRef = _react.useRef
|
|
17
|
+
local useState = _react.useState
|
|
18
|
+
local useSelector = TS.import(script, TS.getModule(script, "@rbxts", "react-reflex")).useSelector
|
|
19
|
+
local UserInputService = TS.import(script, TS.getModule(script, "@rbxts", "services")).UserInputService
|
|
20
|
+
local usePx = TS.import(script, script.Parent.Parent, "hooks", "use-px").usePx
|
|
21
|
+
local useStore = TS.import(script, script.Parent.Parent, "hooks", "use-store").useStore
|
|
22
|
+
local ApiContext = TS.import(script, script.Parent.Parent, "providers", "api-provider").ApiContext
|
|
23
|
+
local OptionsContext = TS.import(script, script.Parent.Parent, "providers", "options-provider").OptionsContext
|
|
24
|
+
local selectVisible = TS.import(script, script.Parent.Parent, "store", "app").selectVisible
|
|
25
|
+
local selectCommand = TS.import(script, script.Parent.Parent, "store", "command").selectCommand
|
|
26
|
+
local selectSuggestion = TS.import(script, script.Parent.Parent, "store", "suggestion").selectSuggestion
|
|
27
|
+
local selectValid = TS.import(script, script.Parent.Parent, "store", "text").selectValid
|
|
28
|
+
local getArgumentNames = TS.import(script, script.Parent.Parent, "util", "argument").getArgumentNames
|
|
29
|
+
local Frame = TS.import(script, script.Parent, "ui", "frame").Frame
|
|
30
|
+
local Padding = TS.import(script, script.Parent, "ui", "padding").Padding
|
|
31
|
+
local Text = TS.import(script, script.Parent, "ui", "text").Text
|
|
32
|
+
local TextField = TS.import(script, script.Parent, "ui", "text-field").TextField
|
|
33
|
+
local TEXT_SIZE = 22
|
|
34
|
+
local function TerminalTextField(_param)
|
|
35
|
+
local anchorPoint = _param.anchorPoint
|
|
36
|
+
local size = _param.size
|
|
37
|
+
local position = _param.position
|
|
38
|
+
local backgroundTransparency = _param.backgroundTransparency
|
|
39
|
+
local onTextChange = _param.onTextChange
|
|
40
|
+
local onSubmit = _param.onSubmit
|
|
41
|
+
local px = usePx()
|
|
42
|
+
local ref = useRef()
|
|
43
|
+
local api = useContext(ApiContext)
|
|
44
|
+
local options = useContext(OptionsContext)
|
|
45
|
+
local store = useStore()
|
|
46
|
+
local appVisible = useSelector(selectVisible)
|
|
47
|
+
local currentSuggestion = useSelector(selectSuggestion)
|
|
48
|
+
local text, setText = useBinding("")
|
|
49
|
+
local suggestionText, setSuggestionText = useBinding("")
|
|
50
|
+
local valid, setValid = useState(false)
|
|
51
|
+
local traverseHistory = useCallback(function(up)
|
|
52
|
+
local history = store:getState().history.commandHistory
|
|
53
|
+
if #history == 0 then
|
|
54
|
+
return nil
|
|
55
|
+
end
|
|
56
|
+
local historyIndex = store:getState().history.commandHistoryIndex
|
|
57
|
+
if (up and historyIndex == 0) or (not up and historyIndex == -1) then
|
|
58
|
+
return nil
|
|
59
|
+
end
|
|
60
|
+
local newIndex
|
|
61
|
+
if up then
|
|
62
|
+
newIndex = if historyIndex == -1 then #history - 1 else historyIndex - 1
|
|
63
|
+
else
|
|
64
|
+
newIndex = if historyIndex ~= #history - 1 then historyIndex + 1 else -1
|
|
65
|
+
end
|
|
66
|
+
local newText = if newIndex ~= -1 then history[newIndex + 1] else ""
|
|
67
|
+
setText(newText)
|
|
68
|
+
setSuggestionText("")
|
|
69
|
+
store.setCommandHistoryIndex(newIndex)
|
|
70
|
+
if ref.current ~= nil then
|
|
71
|
+
ref.current.CursorPosition = #newText + 1
|
|
72
|
+
end
|
|
73
|
+
end, {})
|
|
74
|
+
useEffect(function()
|
|
75
|
+
if ref.current == nil then
|
|
76
|
+
return nil
|
|
77
|
+
end
|
|
78
|
+
if appVisible then
|
|
79
|
+
ref.current:CaptureFocus()
|
|
80
|
+
else
|
|
81
|
+
ref.current:ReleaseFocus()
|
|
82
|
+
end
|
|
83
|
+
end, { appVisible })
|
|
84
|
+
useMountEffect(function()
|
|
85
|
+
store:subscribe(selectValid, function(valid)
|
|
86
|
+
setValid(valid)
|
|
87
|
+
end)
|
|
88
|
+
store:subscribe(selectCommand, function(command)
|
|
89
|
+
if not store:getState().text.valid then
|
|
90
|
+
return nil
|
|
91
|
+
end
|
|
92
|
+
setValid(command ~= nil)
|
|
93
|
+
end)
|
|
94
|
+
end)
|
|
95
|
+
useEffect(function()
|
|
96
|
+
if currentSuggestion == nil then
|
|
97
|
+
setSuggestionText("")
|
|
98
|
+
return nil
|
|
99
|
+
end
|
|
100
|
+
local state = store:getState()
|
|
101
|
+
local atNextPart = endsWithSpace(state.text.value)
|
|
102
|
+
local suggestionStartIndex = if #state.text.parts > 0 then (if not atNextPart then #state.text.parts[#state.text.parts] else 0) + 1 else -1
|
|
103
|
+
local command = state.command.path
|
|
104
|
+
local argIndex = state.command.argIndex
|
|
105
|
+
if currentSuggestion.main.type == "command" and suggestionStartIndex > -1 then
|
|
106
|
+
setSuggestionText(getBindingValue(text) .. string.sub(currentSuggestion.main.title, suggestionStartIndex))
|
|
107
|
+
return nil
|
|
108
|
+
end
|
|
109
|
+
if currentSuggestion.main.type ~= "argument" or command == nil or argIndex == nil then
|
|
110
|
+
return nil
|
|
111
|
+
end
|
|
112
|
+
local newText = getBindingValue(text)
|
|
113
|
+
local argNames = getArgumentNames(api.registry, command)
|
|
114
|
+
for i = argIndex, #argNames - 1 do
|
|
115
|
+
if i == argIndex and not atNextPart then
|
|
116
|
+
if not (#currentSuggestion.others == 0) then
|
|
117
|
+
newText ..= string.sub(currentSuggestion.others[1], suggestionStartIndex)
|
|
118
|
+
end
|
|
119
|
+
newText ..= " "
|
|
120
|
+
continue
|
|
121
|
+
end
|
|
122
|
+
newText = `{newText}{argNames[i + 1]} `
|
|
123
|
+
end
|
|
124
|
+
setSuggestionText(newText)
|
|
125
|
+
end, { currentSuggestion })
|
|
126
|
+
useEventListener(UserInputService.InputBegan, function(input)
|
|
127
|
+
if ref.current == nil or not ref.current:IsFocused() then
|
|
128
|
+
return nil
|
|
129
|
+
end
|
|
130
|
+
if input.KeyCode == Enum.KeyCode.Up then
|
|
131
|
+
traverseHistory(true)
|
|
132
|
+
elseif input.KeyCode == Enum.KeyCode.Down then
|
|
133
|
+
traverseHistory(false)
|
|
134
|
+
end
|
|
135
|
+
if input.KeyCode ~= Enum.KeyCode.Tab then
|
|
136
|
+
return nil
|
|
137
|
+
end
|
|
138
|
+
local state = store:getState()
|
|
139
|
+
local commandPath = state.command.path
|
|
140
|
+
-- Handle command suggestions
|
|
141
|
+
if commandPath == nil then
|
|
142
|
+
local _suggestionTitle = currentSuggestion
|
|
143
|
+
if _suggestionTitle ~= nil then
|
|
144
|
+
_suggestionTitle = _suggestionTitle.main.title
|
|
145
|
+
end
|
|
146
|
+
local suggestionTitle = _suggestionTitle
|
|
147
|
+
if suggestionTitle == nil then
|
|
148
|
+
return nil
|
|
149
|
+
end
|
|
150
|
+
local currentText = text:getValue()
|
|
151
|
+
local newText = ""
|
|
152
|
+
if endsWithSpace(currentText) then
|
|
153
|
+
newText = currentText .. suggestionTitle
|
|
154
|
+
elseif not (#state.text.parts == 0) then
|
|
155
|
+
local textPartSize = #state.text.parts[#state.text.parts]
|
|
156
|
+
local _arg1 = #currentText - textPartSize
|
|
157
|
+
newText = string.sub(currentText, 0, _arg1) .. suggestionTitle
|
|
158
|
+
end
|
|
159
|
+
local suggestionTextParts = string.split((string.gsub(suggestionText:getValue(), "%s+", " ")), " ")
|
|
160
|
+
local _nextCommand = api.registry:getCommandByString(formatPartsAsPath(suggestionTextParts))
|
|
161
|
+
if _nextCommand ~= nil then
|
|
162
|
+
_nextCommand = _nextCommand.options
|
|
163
|
+
end
|
|
164
|
+
local nextCommand = _nextCommand
|
|
165
|
+
local _condition = nextCommand == nil
|
|
166
|
+
if not _condition then
|
|
167
|
+
local _result = nextCommand.arguments
|
|
168
|
+
if _result ~= nil then
|
|
169
|
+
_result = #_result
|
|
170
|
+
end
|
|
171
|
+
local _condition_1 = _result
|
|
172
|
+
if _condition_1 == nil then
|
|
173
|
+
_condition_1 = 0
|
|
174
|
+
end
|
|
175
|
+
_condition = _condition_1 > 0
|
|
176
|
+
end
|
|
177
|
+
if _condition then
|
|
178
|
+
newText ..= " "
|
|
179
|
+
end
|
|
180
|
+
setSuggestionText("")
|
|
181
|
+
setText(newText)
|
|
182
|
+
ref.current.CursorPosition = #newText + 1
|
|
183
|
+
return nil
|
|
184
|
+
end
|
|
185
|
+
-- Handle argument suggestions
|
|
186
|
+
if commandPath == nil or currentSuggestion == nil or #currentSuggestion.others == 0 then
|
|
187
|
+
return nil
|
|
188
|
+
end
|
|
189
|
+
local argIndex = state.command.argIndex
|
|
190
|
+
local _commandArgs = api.registry:getCommand(commandPath)
|
|
191
|
+
if _commandArgs ~= nil then
|
|
192
|
+
_commandArgs = _commandArgs.options.arguments
|
|
193
|
+
end
|
|
194
|
+
local commandArgs = _commandArgs
|
|
195
|
+
if argIndex == nil or commandArgs == nil then
|
|
196
|
+
return nil
|
|
197
|
+
end
|
|
198
|
+
local newText = getBindingValue(text)
|
|
199
|
+
local parts = state.text.parts
|
|
200
|
+
if not endsWithSpace(newText) and not (#parts == 0) then
|
|
201
|
+
local _newText = newText
|
|
202
|
+
local _arg1 = #newText - #parts[#parts]
|
|
203
|
+
newText = string.sub(_newText, 0, _arg1)
|
|
204
|
+
end
|
|
205
|
+
local suggestion = currentSuggestion.others[1]
|
|
206
|
+
if (string.match(suggestion, "%s")) ~= nil then
|
|
207
|
+
suggestion = `"{suggestion}"`
|
|
208
|
+
end
|
|
209
|
+
newText ..= suggestion
|
|
210
|
+
if argIndex < #commandArgs - 1 then
|
|
211
|
+
newText ..= " "
|
|
212
|
+
end
|
|
213
|
+
setSuggestionText("")
|
|
214
|
+
setText(newText)
|
|
215
|
+
ref.current.CursorPosition = #newText + 1
|
|
216
|
+
end)
|
|
217
|
+
return React.createElement(Frame, {
|
|
218
|
+
anchorPoint = anchorPoint,
|
|
219
|
+
size = size,
|
|
220
|
+
position = position,
|
|
221
|
+
backgroundColor = options.palette.surface,
|
|
222
|
+
backgroundTransparency = backgroundTransparency,
|
|
223
|
+
cornerRadius = UDim.new(0, px(4)),
|
|
224
|
+
}, React.createElement(Padding, {
|
|
225
|
+
all = UDim.new(0, px(8)),
|
|
226
|
+
}), React.createElement(TextField, {
|
|
227
|
+
size = UDim2.fromScale(1, 1),
|
|
228
|
+
placeholderText = "Enter command...",
|
|
229
|
+
text = text,
|
|
230
|
+
textSize = px(TEXT_SIZE),
|
|
231
|
+
textColor = if valid then options.palette.success else options.palette.error,
|
|
232
|
+
placeholderColor = options.palette.subtext,
|
|
233
|
+
textXAlignment = "Left",
|
|
234
|
+
clearTextOnFocus = false,
|
|
235
|
+
font = options.font.medium,
|
|
236
|
+
ref = ref,
|
|
237
|
+
event = {
|
|
238
|
+
FocusLost = function(rbx, enterPressed)
|
|
239
|
+
if not enterPressed then
|
|
240
|
+
return nil
|
|
241
|
+
end
|
|
242
|
+
store.addCommandHistory(rbx.Text, api.options.historyLength)
|
|
243
|
+
store.setCommandHistoryIndex(-1)
|
|
244
|
+
local _result = onSubmit
|
|
245
|
+
if _result ~= nil then
|
|
246
|
+
_result(rbx.Text)
|
|
247
|
+
end
|
|
248
|
+
local _result_1 = ref.current
|
|
249
|
+
if _result_1 ~= nil then
|
|
250
|
+
_result_1:CaptureFocus()
|
|
251
|
+
end
|
|
252
|
+
setText("")
|
|
253
|
+
end,
|
|
254
|
+
},
|
|
255
|
+
change = {
|
|
256
|
+
Text = function(rbx)
|
|
257
|
+
local newText = rbx.Text
|
|
258
|
+
-- Remove line breaks
|
|
259
|
+
if (string.match(newText, "[\n\r]")) ~= nil then
|
|
260
|
+
newText = (string.gsub(newText, "[\n\r]", ""))
|
|
261
|
+
end
|
|
262
|
+
-- Remove all tabs from text input - we use these for autocompletion
|
|
263
|
+
if (string.match(newText, "\t")) ~= nil then
|
|
264
|
+
newText = (string.gsub(newText, "\t", ""))
|
|
265
|
+
end
|
|
266
|
+
-- Reset command history index
|
|
267
|
+
if store:getState().history.commandHistoryIndex ~= -1 then
|
|
268
|
+
store.setCommandHistoryIndex(-1)
|
|
269
|
+
end
|
|
270
|
+
setText(newText)
|
|
271
|
+
local _result = onTextChange
|
|
272
|
+
if _result ~= nil then
|
|
273
|
+
_result(newText)
|
|
274
|
+
end
|
|
275
|
+
end,
|
|
276
|
+
},
|
|
277
|
+
zIndex = 2,
|
|
278
|
+
}), React.createElement(Text, {
|
|
279
|
+
size = UDim2.fromScale(1, 1),
|
|
280
|
+
text = suggestionText,
|
|
281
|
+
textColor = options.palette.subtext,
|
|
282
|
+
textSize = px(TEXT_SIZE),
|
|
283
|
+
textXAlignment = "Left",
|
|
284
|
+
font = options.font.medium,
|
|
285
|
+
}))
|
|
286
|
+
end
|
|
287
|
+
return {
|
|
288
|
+
TerminalTextField = TerminalTextField,
|
|
289
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function TerminalWindow(): JSX.Element;
|