diff --git a/.dir-locals.el b/.dir-locals.el new file mode 100644 index 00000000..25e8e6c1 --- /dev/null +++ b/.dir-locals.el @@ -0,0 +1,2 @@ +((nil . ((eglot-luau-rojo-project-path . "plugin.project.json") + (eglot-luau-rojo-sourcemap-enabled . 't)))) \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a1718858..1441c04a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -31,7 +31,7 @@ jobs: version: 'v0.3.0' - name: Build Plugin - run: rojo build plugin --output Rojo.rbxm + run: rojo build plugin.project.json --output Rojo.rbxm - name: Upload Plugin to Release env: diff --git a/.gitignore b/.gitignore index 88cdf551..b1a5efe9 100644 --- a/.gitignore +++ b/.gitignore @@ -10,8 +10,8 @@ /*.rbxl /*.rbxlx -# Test places for the Roblox Studio Plugin -/plugin/*.rbxlx +# Sourcemap for the Rojo plugin (for better intellisense) +/sourcemap.json # Roblox Studio holds 'lock' files on places *.rbxl.lock diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 00000000..6ffebea2 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,8 @@ +{ + "recommendations": [ + "JohnnyMorganz.luau-lsp", + "JohnnyMorganz.stylua", + "Kampfkarren.selene-vscode", + "rust-lang.rust-analyzer" + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..f6578386 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,4 @@ +{ + "luau-lsp.sourcemap.rojoProjectFile": "plugin.project.json", + "luau-lsp.sourcemap.autogenerate": true +} diff --git a/CHANGELOG.md b/CHANGELOG.md index ba9a0542..a5d0874c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,7 @@ * Improved settings UI ([#886]) * `Open Scripts Externally` option can now be changed while syncing ([#911]) * The sync reminder notification will now tell you what was last synced and when ([#987]) +* Fixed notification and tooltip text sometimes getting cut off ([#988]) * Projects may now specify rules for syncing files as if they had a different file extension. ([#813]) This is specified via a new field on project files, `syncRules`: @@ -92,6 +93,7 @@ [#915]: https://github.com/rojo-rbx/rojo/pull/915 [#974]: https://github.com/rojo-rbx/rojo/pull/974 [#987]: https://github.com/rojo-rbx/rojo/pull/987 +[#988]: https://github.com/rojo-rbx/rojo/pull/988 ## [7.4.3] - August 6th, 2024 * Fixed issue with building binary files introduced in 7.4.2 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8f3efd19..7eccb0ee 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -16,6 +16,23 @@ You'll want these tools to work on Rojo: * Latest stable Rust compiler * Latest stable [Rojo](https://github.com/rojo-rbx/rojo) * [Foreman](https://github.com/Roblox/foreman) +* [Luau Language Server](https://github.com/JohnnyMorganz/luau-lsp) (Only needed if working on the Studio plugin.) + +When working on the Studio plugin, we recommend using this command to automatically rebuild the plugin when you save a change: + +*(Make sure you've enabled the Studio setting to reload plugins on file change!)* + +```bash +bash scripts/watch-build-plugin.sh +``` + +You can also run the plugin's unit tests with the following: + +*(Make sure you have `run-in-roblox` installed first!)* + +```bash +bash scripts/unit-test-plugin.sh +``` ## Documentation Documentation impacts way more people than the individual lines of code we write. diff --git a/Cargo.lock b/Cargo.lock index 908ff793..3e61f7c0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1752,7 +1752,7 @@ dependencies = [ [[package]] name = "rojo" -version = "7.4.4" +version = "7.5.0-prealpha" dependencies = [ "anyhow", "backtrace", diff --git a/Cargo.toml b/Cargo.toml index 700f6e47..9798e670 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rojo" -version = "7.4.4" +version = "7.5.0-prealpha" rust-version = "1.70.0" authors = ["Lucien Greathouse "] description = "Enables professional-grade development tools for Roblox developers" diff --git a/build.rs b/build.rs index 9f9a606b..e423b61b 100644 --- a/build.rs +++ b/build.rs @@ -41,12 +41,12 @@ fn snapshot_from_fs_path(path: &Path) -> io::Result { fn main() -> Result<(), anyhow::Error> { let out_dir = env::var_os("OUT_DIR").unwrap(); - let root_dir = env::var_os("CARGO_MANIFEST_DIR").unwrap(); - let plugin_root = PathBuf::from(root_dir).join("plugin"); + let root_dir = PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").unwrap()); + let plugin_dir = root_dir.join("plugin"); let our_version = Version::parse(env::var_os("CARGO_PKG_VERSION").unwrap().to_str().unwrap())?; let plugin_version = - Version::parse(fs::read_to_string(plugin_root.join("Version.txt"))?.trim())?; + Version::parse(fs::read_to_string(plugin_dir.join("Version.txt"))?.trim())?; assert_eq!( our_version, plugin_version, @@ -54,14 +54,16 @@ fn main() -> Result<(), anyhow::Error> { ); let snapshot = VfsSnapshot::dir(hashmap! { - "default.project.json" => snapshot_from_fs_path(&plugin_root.join("default.project.json"))?, - "fmt" => snapshot_from_fs_path(&plugin_root.join("fmt"))?, - "http" => snapshot_from_fs_path(&plugin_root.join("http"))?, - "log" => snapshot_from_fs_path(&plugin_root.join("log"))?, - "rbx_dom_lua" => snapshot_from_fs_path(&plugin_root.join("rbx_dom_lua"))?, - "src" => snapshot_from_fs_path(&plugin_root.join("src"))?, - "Packages" => snapshot_from_fs_path(&plugin_root.join("Packages"))?, - "Version.txt" => snapshot_from_fs_path(&plugin_root.join("Version.txt"))?, + "default.project.json" => snapshot_from_fs_path(&root_dir.join("plugin.project.json"))?, + "plugin" => VfsSnapshot::dir(hashmap! { + "fmt" => snapshot_from_fs_path(&plugin_dir.join("fmt"))?, + "http" => snapshot_from_fs_path(&plugin_dir.join("http"))?, + "log" => snapshot_from_fs_path(&plugin_dir.join("log"))?, + "rbx_dom_lua" => snapshot_from_fs_path(&plugin_dir.join("rbx_dom_lua"))?, + "src" => snapshot_from_fs_path(&plugin_dir.join("src"))?, + "Packages" => snapshot_from_fs_path(&plugin_dir.join("Packages"))?, + "Version.txt" => snapshot_from_fs_path(&plugin_dir.join("Version.txt"))?, + }), }); let out_path = Path::new(&out_dir).join("plugin.bincode"); diff --git a/plugin/default.project.json b/plugin.project.json similarity index 50% rename from plugin/default.project.json rename to plugin.project.json index 424f107e..83265250 100644 --- a/plugin/default.project.json +++ b/plugin.project.json @@ -3,25 +3,25 @@ "tree": { "$className": "Folder", "Plugin": { - "$path": "src" + "$path": "plugin/src" }, "Packages": { - "$path": "Packages", + "$path": "plugin/Packages", "Log": { - "$path": "log" + "$path": "plugin/log" }, "Http": { - "$path": "http" + "$path": "plugin/http" }, "Fmt": { - "$path": "fmt" + "$path": "plugin/fmt" }, "RbxDom": { - "$path": "rbx_dom_lua" + "$path": "plugin/rbx_dom_lua" } }, "Version": { - "$path": "Version.txt" + "$path": "plugin/Version.txt" } } -} \ No newline at end of file +} diff --git a/plugin/Version.txt b/plugin/Version.txt index c74107f7..9b648123 100644 --- a/plugin/Version.txt +++ b/plugin/Version.txt @@ -1 +1 @@ -7.4.4 \ No newline at end of file +7.5.0-prealpha \ No newline at end of file diff --git a/plugin/run-tests.server.lua b/plugin/run-tests.server.lua index 1e2130f5..0ab3cc05 100644 --- a/plugin/run-tests.server.lua +++ b/plugin/run-tests.server.lua @@ -1,6 +1,6 @@ local ReplicatedStorage = game:GetService("ReplicatedStorage") -local TestEZ = require(ReplicatedStorage.Packages.TestEZ) +local TestEZ = require(ReplicatedStorage.Packages:WaitForChild("TestEZ", 10)) local Rojo = ReplicatedStorage.Rojo diff --git a/plugin/src/App/Components/Checkbox.lua b/plugin/src/App/Components/Checkbox.lua index 0d37fd86..c4572e93 100644 --- a/plugin/src/App/Components/Checkbox.lua +++ b/plugin/src/App/Components/Checkbox.lua @@ -32,7 +32,7 @@ end function Checkbox:render() return Theme.with(function(theme) - theme = theme.Checkbox + local checkboxTheme = theme.Checkbox local activeTransparency = Roact.joinBindings({ self.binding:map(function(value) @@ -63,14 +63,14 @@ function Checkbox:render() Active = e(SlicedImage, { slice = Assets.Slices.RoundedBackground, - color = theme.Active.BackgroundColor, + color = checkboxTheme.Active.BackgroundColor, transparency = activeTransparency, size = UDim2.new(1, 0, 1, 0), zIndex = 2, }, { Icon = e("ImageLabel", { Image = if self.props.locked then Assets.Images.Checkbox.Locked else Assets.Images.Checkbox.Active, - ImageColor3 = theme.Active.IconColor, + ImageColor3 = checkboxTheme.Active.IconColor, ImageTransparency = activeTransparency, Size = UDim2.new(0, 16, 0, 16), @@ -83,7 +83,7 @@ function Checkbox:render() Inactive = e(SlicedImage, { slice = Assets.Slices.RoundedBorder, - color = theme.Inactive.BorderColor, + color = checkboxTheme.Inactive.BorderColor, transparency = self.props.transparency, size = UDim2.new(1, 0, 1, 0), }, { @@ -91,7 +91,7 @@ function Checkbox:render() Image = if self.props.locked then Assets.Images.Checkbox.Locked else Assets.Images.Checkbox.Inactive, - ImageColor3 = theme.Inactive.IconColor, + ImageColor3 = checkboxTheme.Inactive.IconColor, ImageTransparency = self.props.transparency, Size = UDim2.new(0, 16, 0, 16), diff --git a/plugin/src/App/Components/CodeLabel.lua b/plugin/src/App/Components/CodeLabel.lua index 0cdba8e9..1951106b 100644 --- a/plugin/src/App/Components/CodeLabel.lua +++ b/plugin/src/App/Components/CodeLabel.lua @@ -1,4 +1,5 @@ local Rojo = script:FindFirstAncestor("Rojo") +local Plugin = Rojo.Plugin local Packages = Rojo.Packages local Roact = require(Packages.Roact) @@ -7,6 +8,8 @@ Highlighter.matchStudioSettings() local e = Roact.createElement +local Theme = require(Plugin.App.Theme) + local CodeLabel = Roact.PureComponent:extend("CodeLabel") function CodeLabel:init() @@ -40,22 +43,24 @@ function CodeLabel:updateHighlights() end function CodeLabel:render() - return e("TextLabel", { - Size = self.props.size, - Position = self.props.position, - Text = self.props.text, - BackgroundTransparency = 1, - Font = Enum.Font.RobotoMono, - TextSize = 16, - TextXAlignment = Enum.TextXAlignment.Left, - TextYAlignment = Enum.TextYAlignment.Top, - TextColor3 = Color3.fromRGB(255, 255, 255), - [Roact.Ref] = self.labelRef, - }, { - SyntaxHighlights = e("Folder", { - [Roact.Ref] = self.highlightsRef, - }), - }) + return Theme.with(function(theme) + return e("TextLabel", { + Size = self.props.size, + Position = self.props.position, + Text = self.props.text, + BackgroundTransparency = 1, + FontFace = theme.Font.Code, + TextSize = theme.TextSize.Code, + TextXAlignment = Enum.TextXAlignment.Left, + TextYAlignment = Enum.TextYAlignment.Top, + TextColor3 = Color3.fromRGB(255, 255, 255), + [Roact.Ref] = self.labelRef, + }, { + SyntaxHighlights = e("Folder", { + [Roact.Ref] = self.highlightsRef, + }), + }) + end) end return CodeLabel diff --git a/plugin/src/App/Components/Dropdown.lua b/plugin/src/App/Components/Dropdown.lua index a5dda6f2..72629c18 100644 --- a/plugin/src/App/Components/Dropdown.lua +++ b/plugin/src/App/Components/Dropdown.lua @@ -1,5 +1,3 @@ -local TextService = game:GetService("TextService") - local Rojo = script:FindFirstAncestor("Rojo") local Plugin = Rojo.Plugin local Packages = Rojo.Packages @@ -10,6 +8,7 @@ local Flipper = require(Packages.Flipper) local Assets = require(Plugin.Assets) local Theme = require(Plugin.App.Theme) local bindingUtil = require(Plugin.App.bindingUtil) +local getTextBoundsAsync = require(Plugin.App.getTextBoundsAsync) local SlicedImage = require(script.Parent.SlicedImage) local ScrollingFrame = require(script.Parent.ScrollingFrame) @@ -44,29 +43,29 @@ end function Dropdown:render() return Theme.with(function(theme) - theme = theme.Dropdown + local dropdownTheme = theme.Dropdown local optionButtons = {} local width = -1 for i, option in self.props.options do local text = tostring(option or "") - local textSize = TextService:GetTextSize(text, 15, Enum.Font.GothamMedium, Vector2.new(math.huge, 20)) - if textSize.X > width then - width = textSize.X + local textBounds = getTextBoundsAsync(text, theme.Font.Main, theme.TextSize.Body, math.huge) + if textBounds.X > width then + width = textBounds.X end optionButtons[text] = e("TextButton", { Text = text, LayoutOrder = i, Size = UDim2.new(1, 0, 0, 24), - BackgroundColor3 = theme.BackgroundColor, + BackgroundColor3 = dropdownTheme.BackgroundColor, TextTransparency = self.props.transparency, BackgroundTransparency = self.props.transparency, BorderSizePixel = 0, - TextColor3 = theme.TextColor, + TextColor3 = dropdownTheme.TextColor, TextXAlignment = Enum.TextXAlignment.Left, - TextSize = 15, - Font = Enum.Font.GothamMedium, + TextSize = theme.TextSize.Body, + FontFace = theme.Font.Main, [Roact.Event.Activated] = function() if self.props.locked then @@ -103,13 +102,13 @@ function Dropdown:render() }, { Border = e(SlicedImage, { slice = Assets.Slices.RoundedBorder, - color = theme.BorderColor, + color = dropdownTheme.BorderColor, transparency = self.props.transparency, size = UDim2.new(1, 0, 1, 0), }, { DropArrow = e("ImageLabel", { Image = if self.props.locked then Assets.Images.Dropdown.Locked else Assets.Images.Dropdown.Arrow, - ImageColor3 = theme.IconColor, + ImageColor3 = dropdownTheme.IconColor, ImageTransparency = self.props.transparency, Size = UDim2.new(0, 18, 0, 18), @@ -126,9 +125,9 @@ function Dropdown:render() Position = UDim2.new(0, 6, 0, 0), BackgroundTransparency = 1, Text = self.props.active, - Font = Enum.Font.GothamMedium, - TextSize = 15, - TextColor3 = theme.TextColor, + FontFace = theme.Font.Main, + TextSize = theme.TextSize.Body, + TextColor3 = dropdownTheme.TextColor, TextXAlignment = Enum.TextXAlignment.Left, TextTransparency = self.props.transparency, }), @@ -136,7 +135,7 @@ function Dropdown:render() Options = if self.state.open then e(SlicedImage, { slice = Assets.Slices.RoundedBackground, - color = theme.BackgroundColor, + color = dropdownTheme.BackgroundColor, position = UDim2.new(1, 0, 1, 3), size = self.openBinding:map(function(a) return UDim2.new(1, 0, a * math.min(3, #self.props.options), 0) @@ -145,7 +144,7 @@ function Dropdown:render() }, { Border = e(SlicedImage, { slice = Assets.Slices.RoundedBorder, - color = theme.BorderColor, + color = dropdownTheme.BorderColor, transparency = self.props.transparency, size = UDim2.new(1, 0, 1, 0), }), diff --git a/plugin/src/App/Components/Header.lua b/plugin/src/App/Components/Header.lua index ca6acecf..81e2f46a 100644 --- a/plugin/src/App/Components/Header.lua +++ b/plugin/src/App/Components/Header.lua @@ -31,13 +31,13 @@ local function Header(props) Version = e("TextLabel", { Text = Version.display(Config.version), - Font = Enum.Font.Gotham, - TextSize = 14, + FontFace = theme.Font.Thin, + TextSize = theme.TextSize.Body, TextColor3 = theme.Header.VersionColor, TextXAlignment = Enum.TextXAlignment.Left, TextTransparency = props.transparency, - Size = UDim2.new(1, 0, 0, 14), + Size = UDim2.new(1, 0, 0, theme.TextSize.Body), LayoutOrder = 2, BackgroundTransparency = 1, diff --git a/plugin/src/App/Components/PatchVisualizer/ChangeList.lua b/plugin/src/App/Components/PatchVisualizer/ChangeList.lua index 2f10d458..fd6f83cc 100644 --- a/plugin/src/App/Components/PatchVisualizer/ChangeList.lua +++ b/plugin/src/App/Components/PatchVisualizer/ChangeList.lua @@ -39,8 +39,8 @@ local function ViewDiffButton(props) Label = e("TextLabel", { Text = "View Diff", BackgroundTransparency = 1, - Font = Enum.Font.GothamMedium, - TextSize = 14, + FontFace = theme.Font.Main, + TextSize = theme.TextSize.Body, TextColor3 = theme.Settings.Setting.DescriptionColor, TextXAlignment = Enum.TextXAlignment.Left, TextTransparency = props.transparency, @@ -170,8 +170,8 @@ function ChangeList:render() ColumnA = e("TextLabel", { Text = tostring(headerRow[1]), BackgroundTransparency = 1, - Font = Enum.Font.GothamBold, - TextSize = 14, + FontFace = theme.Font.Bold, + TextSize = theme.TextSize.Body, TextColor3 = theme.TextColor, TextXAlignment = Enum.TextXAlignment.Left, TextTransparency = props.transparency, @@ -182,8 +182,8 @@ function ChangeList:render() ColumnB = e("TextLabel", { Text = tostring(headerRow[2]), BackgroundTransparency = 1, - Font = Enum.Font.GothamBold, - TextSize = 14, + FontFace = theme.Font.Bold, + TextSize = theme.TextSize.Body, TextColor3 = theme.TextColor, TextXAlignment = Enum.TextXAlignment.Left, TextTransparency = props.transparency, @@ -194,8 +194,8 @@ function ChangeList:render() ColumnC = e("TextLabel", { Text = tostring(headerRow[3]), BackgroundTransparency = 1, - Font = Enum.Font.GothamBold, - TextSize = 14, + FontFace = theme.Font.Bold, + TextSize = theme.TextSize.Body, TextColor3 = theme.TextColor, TextXAlignment = Enum.TextXAlignment.Left, TextTransparency = props.transparency, @@ -230,8 +230,8 @@ function ChangeList:render() ColumnA = e("TextLabel", { Text = (if isWarning then "⚠ " else "") .. tostring(values[1]), BackgroundTransparency = 1, - Font = Enum.Font.GothamMedium, - TextSize = 14, + FontFace = theme.Font.Main, + TextSize = theme.TextSize.Body, TextColor3 = if isWarning then theme.Diff.Warning else theme.TextColor, TextXAlignment = Enum.TextXAlignment.Left, TextTransparency = props.transparency, diff --git a/plugin/src/App/Components/PatchVisualizer/DisplayValue.lua b/plugin/src/App/Components/PatchVisualizer/DisplayValue.lua index f7e772b9..94106af6 100644 --- a/plugin/src/App/Components/PatchVisualizer/DisplayValue.lua +++ b/plugin/src/App/Components/PatchVisualizer/DisplayValue.lua @@ -32,8 +32,8 @@ local function DisplayValue(props) Label = e("TextLabel", { Text = string.format("%d, %d, %d", props.value.R * 255, props.value.G * 255, props.value.B * 255), BackgroundTransparency = 1, - Font = Enum.Font.GothamMedium, - TextSize = 14, + FontFace = theme.Font.Main, + TextSize = theme.TextSize.Body, TextColor3 = props.textColor, TextXAlignment = Enum.TextXAlignment.Left, TextTransparency = props.transparency, @@ -90,8 +90,8 @@ local function DisplayValue(props) return e("TextLabel", { Text = textRepresentation, BackgroundTransparency = 1, - Font = Enum.Font.GothamMedium, - TextSize = 14, + FontFace = theme.Font.Main, + TextSize = theme.TextSize.Body, TextColor3 = props.textColor, TextXAlignment = Enum.TextXAlignment.Left, TextTransparency = props.transparency, @@ -112,8 +112,8 @@ local function DisplayValue(props) return e("TextLabel", { Text = textRepresentation, BackgroundTransparency = 1, - Font = Enum.Font.GothamMedium, - TextSize = 14, + FontFace = theme.Font.Main, + TextSize = theme.TextSize.Body, TextColor3 = props.textColor, TextXAlignment = Enum.TextXAlignment.Left, TextTransparency = props.transparency, diff --git a/plugin/src/App/Components/PatchVisualizer/DomLabel.lua b/plugin/src/App/Components/PatchVisualizer/DomLabel.lua index a842dcbc..689f8143 100644 --- a/plugin/src/App/Components/PatchVisualizer/DomLabel.lua +++ b/plugin/src/App/Components/PatchVisualizer/DomLabel.lua @@ -225,8 +225,8 @@ function DomLabel:render() Text = (if props.isWarning then "⚠ " else "") .. props.name, RichText = true, BackgroundTransparency = 1, - Font = if props.patchType then Enum.Font.GothamBold else Enum.Font.GothamMedium, - TextSize = 14, + FontFace = if props.patchType then theme.Font.Bold else theme.Font.Main, + TextSize = theme.TextSize.Body, TextColor3 = color, TextXAlignment = Enum.TextXAlignment.Left, TextTransparency = props.transparency, @@ -251,11 +251,11 @@ function DomLabel:render() then e("TextLabel", { Text = props.changeInfo.edits .. if props.changeInfo.failed then "," else "", BackgroundTransparency = 1, - Font = Enum.Font.Gotham, - TextSize = 14, + FontFace = theme.Font.Thin, + TextSize = theme.TextSize.Body, TextColor3 = theme.SubTextColor, TextTransparency = props.transparency, - Size = UDim2.new(0, 0, 0, 16), + Size = UDim2.new(0, 0, 0, theme.TextSize.Body), AutomaticSize = Enum.AutomaticSize.X, LayoutOrder = 2, }) @@ -264,11 +264,11 @@ function DomLabel:render() then e("TextLabel", { Text = props.changeInfo.failed, BackgroundTransparency = 1, - Font = Enum.Font.Gotham, - TextSize = 14, + FontFace = theme.Font.Thin, + TextSize = theme.TextSize.Body, TextColor3 = theme.Diff.Warning, TextTransparency = props.transparency, - Size = UDim2.new(0, 0, 0, 16), + Size = UDim2.new(0, 0, 0, theme.TextSize.Body), AutomaticSize = Enum.AutomaticSize.X, LayoutOrder = 6, }) diff --git a/plugin/src/App/Components/PatchVisualizer/init.lua b/plugin/src/App/Components/PatchVisualizer/init.lua index a87d499b..3e600dca 100644 --- a/plugin/src/App/Components/PatchVisualizer/init.lua +++ b/plugin/src/App/Components/PatchVisualizer/init.lua @@ -124,8 +124,8 @@ function PatchVisualizer:render() CleanMerge = e("TextLabel", { Visible = #scrollElements == 0, Text = "No changes to sync, project is up to date.", - Font = Enum.Font.GothamMedium, - TextSize = 15, + FontFace = theme.Font.Main, + TextSize = theme.TextSize.Medium, TextColor3 = theme.TextColor, TextWrapped = true, Size = UDim2.new(1, 0, 1, 0), diff --git a/plugin/src/App/Components/StringDiffVisualizer/init.lua b/plugin/src/App/Components/StringDiffVisualizer/init.lua index e4203c21..a826c649 100644 --- a/plugin/src/App/Components/StringDiffVisualizer/init.lua +++ b/plugin/src/App/Components/StringDiffVisualizer/init.lua @@ -1,5 +1,3 @@ -local TextService = game:GetService("TextService") - local Rojo = script:FindFirstAncestor("Rojo") local Plugin = Rojo.Plugin local Packages = Rojo.Packages @@ -11,6 +9,7 @@ local StringDiff = require(script:FindFirstChild("StringDiff")) local Timer = require(Plugin.Timer) local Theme = require(Plugin.App.Theme) +local getTextBoundsAsync = require(Plugin.App.getTextBoundsAsync) local CodeLabel = require(Plugin.App.Components.CodeLabel) local BorderedContainer = require(Plugin.App.Components.BorderedContainer) @@ -32,7 +31,6 @@ function StringDiffVisualizer:init() end) end) - self:calculateContentSize() self:updateScriptBackground() self:setState({ @@ -54,7 +52,6 @@ end function StringDiffVisualizer:didUpdate(previousProps) if previousProps.oldString ~= self.props.oldString or previousProps.newString ~= self.props.newString then - self:calculateContentSize() local add, remove = self:calculateDiffLines() self:setState({ add = add, @@ -63,11 +60,11 @@ function StringDiffVisualizer:didUpdate(previousProps) end end -function StringDiffVisualizer:calculateContentSize() +function StringDiffVisualizer:calculateContentSize(theme) local oldString, newString = self.props.oldString, self.props.newString - local oldStringBounds = TextService:GetTextSize(oldString, 16, Enum.Font.RobotoMono, Vector2.new(99999, 99999)) - local newStringBounds = TextService:GetTextSize(newString, 16, Enum.Font.RobotoMono, Vector2.new(99999, 99999)) + local oldStringBounds = getTextBoundsAsync(oldString, theme.Font.Code, theme.TextSize.Code, math.huge) + local newStringBounds = getTextBoundsAsync(newString, theme.Font.Code, theme.TextSize.Code, math.huge) self.setContentSize( Vector2.new(math.max(oldStringBounds.X, newStringBounds.X), math.max(oldStringBounds.Y, newStringBounds.Y)) @@ -143,6 +140,8 @@ function StringDiffVisualizer:render() local oldString, newString = self.props.oldString, self.props.newString return Theme.with(function(theme) + self:calculateContentSize(theme) + return e(BorderedContainer, { size = self.props.size, position = self.props.position, diff --git a/plugin/src/App/Components/TableDiffVisualizer/Array.lua b/plugin/src/App/Components/TableDiffVisualizer/Array.lua index febd07e9..4e9a3daf 100644 --- a/plugin/src/App/Components/TableDiffVisualizer/Array.lua +++ b/plugin/src/App/Components/TableDiffVisualizer/Array.lua @@ -152,8 +152,8 @@ function Array:render() BackgroundTransparency = 1, Text = "Old", TextXAlignment = Enum.TextXAlignment.Left, - Font = Enum.Font.GothamBold, - TextSize = 14, + FontFace = theme.Font.Bold, + TextSize = theme.TextSize.Body, TextColor3 = theme.Settings.Setting.DescriptionColor, TextTruncate = Enum.TextTruncate.AtEnd, }), @@ -163,8 +163,8 @@ function Array:render() BackgroundTransparency = 1, Text = "New", TextXAlignment = Enum.TextXAlignment.Left, - Font = Enum.Font.GothamBold, - TextSize = 14, + FontFace = theme.Font.Bold, + TextSize = theme.TextSize.Body, TextColor3 = theme.Settings.Setting.DescriptionColor, TextTruncate = Enum.TextTruncate.AtEnd, }), diff --git a/plugin/src/App/Components/TableDiffVisualizer/Dictionary.lua b/plugin/src/App/Components/TableDiffVisualizer/Dictionary.lua index 033956c3..db3697bd 100644 --- a/plugin/src/App/Components/TableDiffVisualizer/Dictionary.lua +++ b/plugin/src/App/Components/TableDiffVisualizer/Dictionary.lua @@ -112,8 +112,8 @@ function Dictionary:render() BackgroundTransparency = 1, Text = key, TextXAlignment = Enum.TextXAlignment.Left, - Font = Enum.Font.GothamMedium, - TextSize = 14, + FontFace = theme.Font.Main, + TextSize = theme.TextSize.Body, TextColor3 = theme.Settings.Setting.DescriptionColor, TextTruncate = Enum.TextTruncate.AtEnd, }), @@ -157,8 +157,8 @@ function Dictionary:render() BackgroundTransparency = 1, Text = "Key", TextXAlignment = Enum.TextXAlignment.Left, - Font = Enum.Font.GothamBold, - TextSize = 14, + FontFace = theme.Font.Bold, + TextSize = theme.TextSize.Body, TextColor3 = theme.Settings.Setting.DescriptionColor, TextTruncate = Enum.TextTruncate.AtEnd, }), @@ -168,8 +168,8 @@ function Dictionary:render() BackgroundTransparency = 1, Text = "Old", TextXAlignment = Enum.TextXAlignment.Left, - Font = Enum.Font.GothamBold, - TextSize = 14, + FontFace = theme.Font.Bold, + TextSize = theme.TextSize.Body, TextColor3 = theme.Settings.Setting.DescriptionColor, TextTruncate = Enum.TextTruncate.AtEnd, }), @@ -179,8 +179,8 @@ function Dictionary:render() BackgroundTransparency = 1, Text = "New", TextXAlignment = Enum.TextXAlignment.Left, - Font = Enum.Font.GothamBold, - TextSize = 14, + FontFace = theme.Font.Bold, + TextSize = theme.TextSize.Body, TextColor3 = theme.Settings.Setting.DescriptionColor, TextTruncate = Enum.TextTruncate.AtEnd, }), diff --git a/plugin/src/App/Components/Tag.lua b/plugin/src/App/Components/Tag.lua index 9fed3d33..6e6d4c4f 100644 --- a/plugin/src/App/Components/Tag.lua +++ b/plugin/src/App/Components/Tag.lua @@ -4,6 +4,7 @@ local Packages = Rojo.Packages local Roact = require(Packages.Roact) +local Theme = require(Plugin.App.Theme) local Assets = require(Plugin.Assets) local SlicedImage = require(Plugin.App.Components.SlicedImage) @@ -11,46 +12,48 @@ local SlicedImage = require(Plugin.App.Components.SlicedImage) local e = Roact.createElement return function(props) - return e(SlicedImage, { - slice = Assets.Slices.RoundedBackground, - color = props.color, - transparency = props.transparency:map(function(transparency) - return 0.9 + (0.1 * transparency) - end), - layoutOrder = props.layoutOrder, - position = props.position, - anchorPoint = props.anchorPoint, - size = UDim2.new(0, 0, 0, 16), - automaticSize = Enum.AutomaticSize.X, - }, { - Padding = e("UIPadding", { - PaddingLeft = UDim.new(0, 4), - PaddingRight = UDim.new(0, 4), - PaddingTop = UDim.new(0, 2), - PaddingBottom = UDim.new(0, 2), - }), - Icon = if props.icon - then e("ImageLabel", { - Size = UDim2.new(0, 12, 0, 12), - Position = UDim2.new(0, 0, 0.5, 0), - AnchorPoint = Vector2.new(0, 0.5), - Image = props.icon, + return Theme.with(function(theme) + return e(SlicedImage, { + slice = Assets.Slices.RoundedBackground, + color = props.color, + transparency = props.transparency:map(function(transparency) + return 0.9 + (0.1 * transparency) + end), + layoutOrder = props.layoutOrder, + position = props.position, + anchorPoint = props.anchorPoint, + size = UDim2.new(0, 0, 0, theme.TextSize.Medium), + automaticSize = Enum.AutomaticSize.X, + }, { + Padding = e("UIPadding", { + PaddingLeft = UDim.new(0, 4), + PaddingRight = UDim.new(0, 4), + PaddingTop = UDim.new(0, 2), + PaddingBottom = UDim.new(0, 2), + }), + Icon = if props.icon + then e("ImageLabel", { + Size = UDim2.new(0, 12, 0, 12), + Position = UDim2.new(0, 0, 0.5, 0), + AnchorPoint = Vector2.new(0, 0.5), + Image = props.icon, + BackgroundTransparency = 1, + ImageColor3 = props.color, + ImageTransparency = props.transparency, + }) + else nil, + Text = e("TextLabel", { + Text = props.text, + FontFace = theme.Font.Main, + TextSize = theme.TextSize.Small, + TextColor3 = props.color, + TextXAlignment = Enum.TextXAlignment.Center, + TextTransparency = props.transparency, + Size = UDim2.new(0, 0, 1, 0), + Position = UDim2.new(0, if props.icon then 15 else 0, 0, 0), + AutomaticSize = Enum.AutomaticSize.X, BackgroundTransparency = 1, - ImageColor3 = props.color, - ImageTransparency = props.transparency, - }) - else nil, - Text = e("TextLabel", { - Text = props.text, - Font = Enum.Font.GothamMedium, - TextSize = 12, - TextColor3 = props.color, - TextXAlignment = Enum.TextXAlignment.Center, - TextTransparency = props.transparency, - Size = UDim2.new(0, 0, 1, 0), - Position = UDim2.new(0, if props.icon then 15 else 0, 0, 0), - AutomaticSize = Enum.AutomaticSize.X, - BackgroundTransparency = 1, - }), - }) + }), + }) + end) end diff --git a/plugin/src/App/Components/TextButton.lua b/plugin/src/App/Components/TextButton.lua index bd643c79..f4243c1b 100644 --- a/plugin/src/App/Components/TextButton.lua +++ b/plugin/src/App/Components/TextButton.lua @@ -1,5 +1,3 @@ -local TextService = game:GetService("TextService") - local Rojo = script:FindFirstAncestor("Rojo") local Plugin = Rojo.Plugin local Packages = Rojo.Packages @@ -10,6 +8,7 @@ local Flipper = require(Packages.Flipper) local Theme = require(Plugin.App.Theme) local Assets = require(Plugin.Assets) local bindingUtil = require(Plugin.App.bindingUtil) +local getTextBoundsAsync = require(Plugin.App.getTextBoundsAsync) local SlicedImage = require(script.Parent.SlicedImage) local TouchRipple = require(script.Parent.TouchRipple) @@ -41,18 +40,17 @@ end function TextButton:render() return Theme.with(function(theme) - local textSize = - TextService:GetTextSize(self.props.text, 18, Enum.Font.GothamMedium, Vector2.new(math.huge, math.huge)) + local textBounds = getTextBoundsAsync(self.props.text, theme.Font.Main, theme.TextSize.Large, math.huge) local style = self.props.style - theme = theme.Button[style] + local buttonTheme = theme.Button[style] local bindingHover = bindingUtil.deriveProperty(self.binding, "hover") local bindingEnabled = bindingUtil.deriveProperty(self.binding, "enabled") return e("ImageButton", { - Size = UDim2.new(0, 15 + textSize.X + 15, 0, 34), + Size = UDim2.new(0, (theme.TextSize.Body * 2) + textBounds.X, 0, 34), Position = self.props.position, AnchorPoint = self.props.anchorPoint, @@ -74,18 +72,22 @@ function TextButton:render() end, }, { TouchRipple = e(TouchRipple, { - color = theme.ActionFillColor, + color = buttonTheme.ActionFillColor, transparency = self.props.transparency:map(function(value) - return bindingUtil.blendAlpha({ theme.ActionFillTransparency, value }) + return bindingUtil.blendAlpha({ buttonTheme.ActionFillTransparency, value }) end), zIndex = 2, }), Text = e("TextLabel", { Text = self.props.text, - Font = Enum.Font.GothamMedium, - TextSize = 18, - TextColor3 = bindingUtil.mapLerp(bindingEnabled, theme.Enabled.TextColor, theme.Disabled.TextColor), + FontFace = theme.Font.Main, + TextSize = theme.TextSize.Large, + TextColor3 = bindingUtil.mapLerp( + bindingEnabled, + buttonTheme.Enabled.TextColor, + buttonTheme.Disabled.TextColor + ), TextTransparency = self.props.transparency, Size = UDim2.new(1, 0, 1, 0), @@ -95,7 +97,11 @@ function TextButton:render() Border = style == "Bordered" and e(SlicedImage, { slice = Assets.Slices.RoundedBorder, - color = bindingUtil.mapLerp(bindingEnabled, theme.Enabled.BorderColor, theme.Disabled.BorderColor), + color = bindingUtil.mapLerp( + bindingEnabled, + buttonTheme.Enabled.BorderColor, + buttonTheme.Disabled.BorderColor + ), transparency = self.props.transparency, size = UDim2.new(1, 0, 1, 0), @@ -105,14 +111,18 @@ function TextButton:render() HoverOverlay = e(SlicedImage, { slice = Assets.Slices.RoundedBackground, - color = theme.ActionFillColor, + color = buttonTheme.ActionFillColor, transparency = Roact.joinBindings({ hover = bindingHover:map(function(value) return 1 - value end), transparency = self.props.transparency, }):map(function(values) - return bindingUtil.blendAlpha({ theme.ActionFillTransparency, values.hover, values.transparency }) + return bindingUtil.blendAlpha({ + buttonTheme.ActionFillTransparency, + values.hover, + values.transparency, + }) end), size = UDim2.new(1, 0, 1, 0), @@ -124,8 +134,8 @@ function TextButton:render() slice = Assets.Slices.RoundedBackground, color = bindingUtil.mapLerp( bindingEnabled, - theme.Enabled.BackgroundColor, - theme.Disabled.BackgroundColor + buttonTheme.Enabled.BackgroundColor, + buttonTheme.Disabled.BackgroundColor ), transparency = self.props.transparency, diff --git a/plugin/src/App/Components/TextInput.lua b/plugin/src/App/Components/TextInput.lua index 61b688d8..1187296b 100644 --- a/plugin/src/App/Components/TextInput.lua +++ b/plugin/src/App/Components/TextInput.lua @@ -38,14 +38,18 @@ end function TextInput:render() return Theme.with(function(theme) - theme = theme.TextInput + local textInputTheme = theme.TextInput local bindingHover = bindingUtil.deriveProperty(self.binding, "hover") local bindingEnabled = bindingUtil.deriveProperty(self.binding, "enabled") return e(SlicedImage, { slice = Assets.Slices.RoundedBorder, - color = bindingUtil.mapLerp(bindingEnabled, theme.Enabled.BorderColor, theme.Disabled.BorderColor), + color = bindingUtil.mapLerp( + bindingEnabled, + textInputTheme.Enabled.BorderColor, + textInputTheme.Disabled.BorderColor + ), transparency = self.props.transparency, size = self.props.size or UDim2.new(1, 0, 1, 0), @@ -55,14 +59,18 @@ function TextInput:render() }, { HoverOverlay = e(SlicedImage, { slice = Assets.Slices.RoundedBackground, - color = theme.ActionFillColor, + color = textInputTheme.ActionFillColor, transparency = Roact.joinBindings({ hover = bindingHover:map(function(value) return 1 - value end), transparency = self.props.transparency, }):map(function(values) - return bindingUtil.blendAlpha({ theme.ActionFillTransparency, values.hover, values.transparency }) + return bindingUtil.blendAlpha({ + textInputTheme.ActionFillTransparency, + values.hover, + values.transparency, + }) end), size = UDim2.new(1, 0, 1, 0), zIndex = -1, @@ -72,14 +80,18 @@ function TextInput:render() Size = UDim2.fromScale(1, 1), Text = self.props.text, PlaceholderText = self.props.placeholder, - Font = Enum.Font.GothamMedium, - TextColor3 = bindingUtil.mapLerp(bindingEnabled, theme.Disabled.TextColor, theme.Enabled.TextColor), + FontFace = theme.Font.Main, + TextColor3 = bindingUtil.mapLerp( + bindingEnabled, + textInputTheme.Disabled.TextColor, + textInputTheme.Enabled.TextColor + ), PlaceholderColor3 = bindingUtil.mapLerp( bindingEnabled, - theme.Disabled.PlaceholderColor, - theme.Enabled.PlaceholderColor + textInputTheme.Disabled.PlaceholderColor, + textInputTheme.Enabled.PlaceholderColor ), - TextSize = 18, + TextSize = theme.TextSize.Large, TextEditable = self.props.enabled, ClearTextOnFocus = self.props.clearTextOnFocus, diff --git a/plugin/src/App/Components/Tooltip.lua b/plugin/src/App/Components/Tooltip.lua index 2ab7e19a..55b056c2 100644 --- a/plugin/src/App/Components/Tooltip.lua +++ b/plugin/src/App/Components/Tooltip.lua @@ -1,4 +1,3 @@ -local TextService = game:GetService("TextService") local HttpService = game:GetService("HttpService") local Rojo = script:FindFirstAncestor("Rojo") @@ -8,6 +7,8 @@ local Packages = Rojo.Packages local Roact = require(Packages.Roact) local Theme = require(Plugin.App.Theme) +local getTextBoundsAsync = require(Plugin.App.getTextBoundsAsync) + local BorderedContainer = require(Plugin.App.Components.BorderedContainer) local e = Roact.createElement @@ -21,50 +22,51 @@ local Y_OVERLAP = 10 -- Let the triangle tail piece overlap the target a bit to local TooltipContext = Roact.createContext({}) local function Popup(props) - local textSize = TextService:GetTextSize( - props.Text, - 16, - Enum.Font.GothamMedium, - Vector2.new(math.min(props.parentSize.X, 160), math.huge) - ) + TEXT_PADDING + (Vector2.one * 2) - - local trigger = props.Trigger:getValue() - - local spaceBelow = props.parentSize.Y - - (trigger.AbsolutePosition.Y + trigger.AbsoluteSize.Y - Y_OVERLAP + TAIL_SIZE) - local spaceAbove = trigger.AbsolutePosition.Y + Y_OVERLAP - TAIL_SIZE - - -- If there's not enough space below, and there's more space above, then show the tooltip above the trigger - local displayAbove = spaceBelow < textSize.Y and spaceAbove > spaceBelow - - local X = math.clamp(props.Position.X - X_OFFSET, 0, props.parentSize.X - textSize.X) - local Y = 0 - - if displayAbove then - Y = math.max(trigger.AbsolutePosition.Y - TAIL_SIZE - textSize.Y + Y_OVERLAP, 0) - else - Y = math.min( - trigger.AbsolutePosition.Y + trigger.AbsoluteSize.Y + TAIL_SIZE - Y_OVERLAP, - props.parentSize.Y - textSize.Y - ) - end - return Theme.with(function(theme) + local textXSpace = math.min(props.parentSize.X, 120) + local textBounds = Vector2.new( + textXSpace, + getTextBoundsAsync(props.Text, theme.Font.Main, theme.TextSize.Medium, textXSpace).Y + ) + local contentSize = textBounds + TEXT_PADDING + (Vector2.one * 2) + + local trigger = props.Trigger:getValue() + + local spaceBelow = props.parentSize.Y + - (trigger.AbsolutePosition.Y + trigger.AbsoluteSize.Y - Y_OVERLAP + TAIL_SIZE) + local spaceAbove = trigger.AbsolutePosition.Y + Y_OVERLAP - TAIL_SIZE + + -- If there's not enough space below, and there's more space above, then show the tooltip above the trigger + local displayAbove = spaceBelow < contentSize.Y and spaceAbove > spaceBelow + + local X = math.clamp(props.Position.X - X_OFFSET, 0, props.parentSize.X - contentSize.X) + local Y = 0 + + if displayAbove then + Y = math.max(trigger.AbsolutePosition.Y - TAIL_SIZE - contentSize.Y + Y_OVERLAP, 0) + else + Y = math.min( + trigger.AbsolutePosition.Y + trigger.AbsoluteSize.Y + TAIL_SIZE - Y_OVERLAP, + props.parentSize.Y - contentSize.Y + ) + end + return e(BorderedContainer, { position = UDim2.fromOffset(X, Y), - size = UDim2.fromOffset(textSize.X, textSize.Y), + size = UDim2.fromOffset(contentSize.X, contentSize.Y), transparency = props.transparency, }, { Label = e("TextLabel", { BackgroundTransparency = 1, Position = UDim2.fromScale(0.5, 0.5), - Size = UDim2.new(1, -TEXT_PADDING.X, 1, -TEXT_PADDING.Y), AnchorPoint = Vector2.new(0.5, 0.5), + Size = UDim2.fromOffset(textBounds.X, textBounds.Y), Text = props.Text, - TextSize = 16, - Font = Enum.Font.GothamMedium, + TextSize = theme.TextSize.Medium, + FontFace = theme.Font.Main, TextWrapped = true, TextXAlignment = Enum.TextXAlignment.Left, + TextYAlignment = Enum.TextYAlignment.Center, TextColor3 = theme.Button.Bordered.Enabled.TextColor, TextTransparency = props.transparency, }), @@ -72,8 +74,8 @@ local function Popup(props) Tail = e("ImageLabel", { ZIndex = 100, Position = if displayAbove - then UDim2.new(0, math.clamp(props.Position.X - X, 6, textSize.X - 6), 1, -1) - else UDim2.new(0, math.clamp(props.Position.X - X, 6, textSize.X - 6), 0, -TAIL_SIZE + 1), + then UDim2.new(0, math.clamp(props.Position.X - X, 6, contentSize.X - 6), 1, -1) + else UDim2.new(0, math.clamp(props.Position.X - X, 6, contentSize.X - 6), 0, -TAIL_SIZE + 1), Size = UDim2.fromOffset(TAIL_SIZE, TAIL_SIZE), AnchorPoint = Vector2.new(0.5, 0), Rotation = if displayAbove then 180 else 0, diff --git a/plugin/src/App/Notifications.lua b/plugin/src/App/Notifications.lua index e934cc24..1204625d 100644 --- a/plugin/src/App/Notifications.lua +++ b/plugin/src/App/Notifications.lua @@ -1,4 +1,3 @@ -local TextService = game:GetService("TextService") local StudioService = game:GetService("StudioService") local Rojo = script:FindFirstAncestor("Rojo") @@ -9,10 +8,10 @@ local Roact = require(Packages.Roact) local Flipper = require(Packages.Flipper) local Log = require(Packages.Log) -local bindingUtil = require(script.Parent.bindingUtil) - local Theme = require(Plugin.App.Theme) local Assets = require(Plugin.Assets) +local bindingUtil = require(Plugin.App.bindingUtil) +local getTextBoundsAsync = require(Plugin.App.getTextBoundsAsync) local BorderedContainer = require(Plugin.App.Components.BorderedContainer) local TextButton = require(Plugin.App.Components.TextButton) @@ -86,55 +85,49 @@ function Notification:render() return 1 - value end) - local textBounds = TextService:GetTextSize(self.props.text, 15, Enum.Font.GothamMedium, Vector2.new(350, 700)) + return Theme.with(function(theme) + local actionButtons = {} + local buttonsX = 0 + if self.props.actions then + local count = 0 + for key, action in self.props.actions do + actionButtons[key] = e(TextButton, { + text = action.text, + style = action.style, + onClick = function() + local success, err = pcall(action.onClick, self) + if not success then + Log.warn("Error in notification action: " .. tostring(err)) + end + end, + layoutOrder = -action.layoutOrder, + transparency = transparency, + }) - local actionButtons = {} - local buttonsX = 0 - if self.props.actions then - local count = 0 - for key, action in self.props.actions do - actionButtons[key] = e(TextButton, { - text = action.text, - style = action.style, - onClick = function() - local success, err = pcall(action.onClick, self) - if not success then - Log.warn("Error in notification action: " .. tostring(err)) - end - end, - layoutOrder = -action.layoutOrder, - transparency = transparency, - }) + buttonsX += getTextBoundsAsync(action.text, theme.Font.Main, theme.TextSize.Large, math.huge).X + (theme.TextSize.Body * 2) - buttonsX += TextService:GetTextSize( - action.text, - 18, - Enum.Font.GothamMedium, - Vector2.new(math.huge, math.huge) - ).X + 30 + count += 1 + end - count += 1 + buttonsX += (count - 1) * 5 end - buttonsX += (count - 1) * 5 - end - - local paddingX, paddingY, logoSize = 24, 20, 32 - local sourceY = if self.props.source then logoSize + 5 else 0 - local sourceX = if self.props.source - then TextService:GetTextSize(self.props.source, 15, Enum.Font.GothamMedium, Vector2.new(350, 15)).X + logoSize + 5 - else 0 - local actionsY = if self.props.actions then 35 else 0 - local contentX = math.max(textBounds.X, buttonsX, sourceX) + (if self.props.thirdParty then 0 else logoSize + 3) + 2 - - local size = self.binding:map(function(value) - return UDim2.fromOffset( - (paddingX + contentX) * value, - 5 + actionsY + sourceY + paddingY + math.max(logoSize, textBounds.Y) + local paddingY, logoSize = 20, 32 + local actionsY = if self.props.actions then 35 else 0 + local textXSpace = math.max(250, buttonsX) + 35 + local textBounds = Vector2.new( + textXSpace, + getTextBoundsAsync(self.props.text, theme.Font.Main, theme.TextSize.Body, textXSpace).Y ) - end) + local contentX = math.max(textBounds.X, buttonsX) + + local size = self.binding:map(function(value) + return UDim2.fromOffset( + (35 + 40 + contentX) * value, + 5 + actionsY + paddingY + math.max(logoSize, textBounds.Y) + ) + end) - return Theme.with(function(theme) return e("TextButton", { BackgroundTransparency = 1, Size = size, @@ -150,45 +143,51 @@ function Notification:render() transparency = transparency, size = UDim2.new(1, 0, 1, 0), }, { - Logo = e("ImageLabel", { - ImageTransparency = transparency, - Image = if self.props.thirdParty - then Assets.Images.ThirdPartyPlugin - else Assets.Images.PluginButton, + Contents = e("Frame", { + Size = UDim2.new(1, 0, 1, 0), BackgroundTransparency = 1, - Size = UDim2.new(0, logoSize, 0, logoSize), - Position = UDim2.new(0, 0, 0, 0), - AnchorPoint = Vector2.new(0, 0), - }), - Source = if self.props.source - then e("TextLabel", { - Text = self.props.source, - Font = Enum.Font.GothamMedium, - TextSize = 15, + }, { + Logo = e("ImageLabel", { + ImageTransparency = transparency, + Image = Assets.Images.PluginButton, + BackgroundTransparency = 1, + Size = UDim2.new(0, logoSize, 0, logoSize), + Position = UDim2.new(0, 0, 0, 0), + AnchorPoint = Vector2.new(0, 0), + }), + Info = e("TextLabel", { + Text = self.props.text, + FontFace = theme.Font.Main, + TextSize = theme.TextSize.Body, TextColor3 = theme.Notification.InfoColor, TextTransparency = transparency, TextXAlignment = Enum.TextXAlignment.Left, - TextTruncate = Enum.TextTruncate.AtEnd, + TextYAlignment = Enum.TextYAlignment.Top, + TextWrapped = true, - Size = UDim2.new(1, -logoSize - 5, 0, logoSize), - Position = UDim2.fromOffset(logoSize + 5, 0), + Size = UDim2.new(1, -35, 1, -actionsY), + Position = UDim2.fromOffset(35, 0), + + LayoutOrder = 1, BackgroundTransparency = 1, - }) - else nil, - Message = e("TextLabel", { - Text = self.props.text, - Font = Enum.Font.GothamMedium, - TextSize = 15, - TextColor3 = theme.Notification.InfoColor, - TextTransparency = transparency, - TextXAlignment = Enum.TextXAlignment.Left, - TextWrapped = true, - - Size = UDim2.new(0, textBounds.X, 0, textBounds.Y), - Position = if self.props.thirdParty - then UDim2.fromOffset(0, logoSize + 5) - else UDim2.fromOffset(logoSize + 3, 0), - BackgroundTransparency = 1, + }), + Actions = if self.props.actions + then e("Frame", { + Size = UDim2.new(1, -40, 0, actionsY), + Position = UDim2.new(1, 0, 1, 0), + AnchorPoint = Vector2.new(1, 1), + BackgroundTransparency = 1, + }, { + Layout = e("UIListLayout", { + FillDirection = Enum.FillDirection.Horizontal, + HorizontalAlignment = Enum.HorizontalAlignment.Right, + VerticalAlignment = Enum.VerticalAlignment.Center, + SortOrder = Enum.SortOrder.LayoutOrder, + Padding = UDim.new(0, 5), + }), + Buttons = Roact.createFragment(actionButtons), + }) + else nil, }), Actions = if self.props.actions then e("Frame", { @@ -209,8 +208,8 @@ function Notification:render() else nil, Padding = e("UIPadding", { - PaddingLeft = UDim.new(0, paddingX / 2), - PaddingRight = UDim.new(0, paddingX / 2), + PaddingLeft = UDim.new(0, 17), + PaddingRight = UDim.new(0, 15), PaddingTop = UDim.new(0, paddingY / 2), PaddingBottom = UDim.new(0, paddingY / 2), }), diff --git a/plugin/src/App/StatusPages/Confirming.lua b/plugin/src/App/StatusPages/Confirming.lua index df8bf5b4..3f3958a4 100644 --- a/plugin/src/App/StatusPages/Confirming.lua +++ b/plugin/src/App/StatusPages/Confirming.lua @@ -64,13 +64,13 @@ function ConfirmingPage:render() "Sync changes for project '%s':", self.props.confirmData.serverInfo.projectName or "UNKNOWN" ), - Font = Enum.Font.Gotham, + FontFace = theme.Font.Thin, LineHeight = 1.2, - TextSize = 14, + TextSize = theme.TextSize.Body, TextColor3 = theme.TextColor, TextXAlignment = Enum.TextXAlignment.Left, TextTransparency = self.props.transparency, - Size = UDim2.new(1, 0, 0, 20), + Size = UDim2.new(1, 0, 0, theme.TextSize.Large + 2), BackgroundTransparency = 1, }), diff --git a/plugin/src/App/StatusPages/Connected.lua b/plugin/src/App/StatusPages/Connected.lua index b0bf33d9..1d089e28 100644 --- a/plugin/src/App/StatusPages/Connected.lua +++ b/plugin/src/App/StatusPages/Connected.lua @@ -61,12 +61,12 @@ function ChangesViewer:render() Title = e("TextLabel", { Text = "Sync", - Font = Enum.Font.GothamMedium, - TextSize = 17, + FontFace = theme.Font.Main, + TextSize = theme.TextSize.Large, TextXAlignment = Enum.TextXAlignment.Left, TextColor3 = theme.TextColor, TextTransparency = self.props.transparency, - Size = UDim2.new(1, -40, 0, 20), + Size = UDim2.new(1, -40, 0, theme.TextSize.Large + 2), Position = UDim2.new(0, 40, 0, 0), BackgroundTransparency = 1, }), @@ -74,13 +74,13 @@ function ChangesViewer:render() Subtitle = e("TextLabel", { Text = DateTime.fromUnixTimestamp(self.props.patchData.timestamp):FormatLocalTime("LTS", "en-us"), TextXAlignment = Enum.TextXAlignment.Left, - Font = Enum.Font.Gotham, - TextSize = 15, + FontFace = theme.Font.Thin, + TextSize = theme.TextSize.Medium, TextColor3 = theme.SubTextColor, TextTruncate = Enum.TextTruncate.AtEnd, TextTransparency = self.props.transparency, - Size = UDim2.new(1, -40, 0, 16), - Position = UDim2.new(0, 40, 0, 20), + Size = UDim2.new(1, -40, 0, theme.TextSize.Medium), + Position = UDim2.new(0, 40, 0, theme.TextSize.Large + 2), BackgroundTransparency = 1, }), @@ -131,8 +131,8 @@ function ChangesViewer:render() }), AppliedText = e("TextLabel", { Text = applied, - Font = Enum.Font.Gotham, - TextSize = 15, + FontFace = theme.Font.Thin, + TextSize = theme.TextSize.Body, TextColor3 = theme.TextColor, TextTransparency = self.props.transparency, Size = UDim2.new(0, 0, 1, 0), @@ -156,8 +156,8 @@ function ChangesViewer:render() }), UnappliedText = e("TextLabel", { Text = unapplied, - Font = Enum.Font.Gotham, - TextSize = 15, + FontFace = theme.Font.Thin, + TextSize = theme.TextSize.Body, TextColor3 = theme.Diff.Warning, TextTransparency = self.props.transparency, Size = UDim2.new(0, 0, 1, 0), @@ -217,13 +217,13 @@ local function ConnectionDetails(props) }, { ProjectName = e("TextLabel", { Text = props.projectName, - Font = Enum.Font.GothamBold, - TextSize = 20, + FontFace = theme.Font.Bold, + TextSize = theme.TextSize.Large, TextColor3 = theme.ConnectionDetails.ProjectNameColor, TextTransparency = props.transparency, TextXAlignment = Enum.TextXAlignment.Left, - Size = UDim2.new(1, 0, 0, 20), + Size = UDim2.new(1, 0, 0, theme.TextSize.Large), LayoutOrder = 1, BackgroundTransparency = 1, @@ -231,13 +231,13 @@ local function ConnectionDetails(props) Address = e("TextLabel", { Text = props.address, - Font = Enum.Font.Code, - TextSize = 15, + FontFace = theme.Font.Code, + TextSize = theme.TextSize.Medium, TextColor3 = theme.ConnectionDetails.AddressColor, TextTransparency = props.transparency, TextXAlignment = Enum.TextXAlignment.Left, - Size = UDim2.new(1, 0, 0, 15), + Size = UDim2.new(1, 0, 0, theme.TextSize.Medium), LayoutOrder = 2, BackgroundTransparency = 1, @@ -410,8 +410,8 @@ function ConnectedPage:render() Text = e("TextLabel", { BackgroundTransparency = 1, Text = self.changeInfoText, - Font = Enum.Font.Gotham, - TextSize = 15, + FontFace = theme.Font.Thin, + TextSize = theme.TextSize.Body, TextColor3 = if syncWarning then theme.Diff.Warning else theme.Header.VersionColor, TextTransparency = self.props.transparency, TextXAlignment = Enum.TextXAlignment.Right, diff --git a/plugin/src/App/StatusPages/Error.lua b/plugin/src/App/StatusPages/Error.lua index 5d1bcb5e..f51fb6a4 100644 --- a/plugin/src/App/StatusPages/Error.lua +++ b/plugin/src/App/StatusPages/Error.lua @@ -1,5 +1,3 @@ -local TextService = game:GetService("TextService") - local Rojo = script:FindFirstAncestor("Rojo") local Plugin = Rojo.Plugin local Packages = Rojo.Packages @@ -7,6 +5,7 @@ local Packages = Rojo.Packages local Roact = require(Packages.Roact) local Theme = require(Plugin.App.Theme) +local getTextBoundsAsync = require(Plugin.App.getTextBoundsAsync) local TextButton = require(Plugin.App.Components.TextButton) local BorderedContainer = require(Plugin.App.Components.BorderedContainer) @@ -24,43 +23,43 @@ function Error:init() end function Error:render() - return e(BorderedContainer, { - size = Roact.joinBindings({ - containerSize = self.props.containerSize, - contentSize = self.contentSize, - }):map(function(values) - local maximumSize = values.containerSize - maximumSize -= Vector2.new(14, 14) * 2 -- Page padding - maximumSize -= Vector2.new(0, 34 + 10) -- Buttons and spacing + return Theme.with(function(theme) + return e(BorderedContainer, { + size = Roact.joinBindings({ + containerSize = self.props.containerSize, + contentSize = self.contentSize, + }):map(function(values) + local maximumSize = values.containerSize + maximumSize -= Vector2.new(14, 14) * 2 -- Page padding + maximumSize -= Vector2.new(0, 34 + 10) -- Buttons and spacing - local outerSize = values.contentSize + ERROR_PADDING * 2 + local outerSize = values.contentSize + ERROR_PADDING * 2 - return UDim2.new(1, 0, 0, math.min(outerSize.Y, maximumSize.Y)) - end), - transparency = self.props.transparency, - }, { - ScrollingFrame = e(ScrollingFrame, { - size = UDim2.new(1, 0, 1, 0), - contentSize = self.contentSize:map(function(value) - return value + ERROR_PADDING * 2 + return UDim2.new(1, 0, 0, math.min(outerSize.Y, maximumSize.Y)) end), transparency = self.props.transparency, - - [Roact.Change.AbsoluteSize] = function(object) - local containerSize = object.AbsoluteSize - ERROR_PADDING * 2 - - local textBounds = TextService:GetTextSize( - self.props.errorMessage, - 16, - Enum.Font.Code, - Vector2.new(containerSize.X, math.huge) - ) - - self.setContentSize(Vector2.new(containerSize.X, textBounds.Y)) - end, }, { - ErrorMessage = Theme.with(function(theme) - return e("TextBox", { + ScrollingFrame = e(ScrollingFrame, { + size = UDim2.new(1, 0, 1, 0), + contentSize = self.contentSize:map(function(value) + return value + ERROR_PADDING * 2 + end), + transparency = self.props.transparency, + + [Roact.Change.AbsoluteSize] = function(object) + local containerSize = object.AbsoluteSize - ERROR_PADDING * 2 + + local textBounds = getTextBoundsAsync( + self.props.errorMessage, + theme.Font.Code, + theme.TextSize.Code, + containerSize.X + ) + + self.setContentSize(Vector2.new(containerSize.X, textBounds.Y)) + end, + }, { + ErrorMessage = e("TextBox", { [Roact.Event.InputBegan] = function(rbx, input) if input.UserInputType ~= Enum.UserInputType.MouseButton1 then return @@ -71,8 +70,8 @@ function Error:render() Text = self.props.errorMessage, TextEditable = false, - Font = Enum.Font.Code, - TextSize = 16, + FontFace = theme.Font.Code, + TextSize = theme.TextSize.Code, TextColor3 = theme.ErrorColor, TextXAlignment = Enum.TextXAlignment.Left, TextYAlignment = Enum.TextYAlignment.Top, @@ -81,17 +80,17 @@ function Error:render() ClearTextOnFocus = false, BackgroundTransparency = 1, Size = UDim2.new(1, 0, 1, 0), - }) - end), + }), - Padding = e("UIPadding", { - PaddingLeft = UDim.new(0, ERROR_PADDING.X), - PaddingRight = UDim.new(0, ERROR_PADDING.X), - PaddingTop = UDim.new(0, ERROR_PADDING.Y), - PaddingBottom = UDim.new(0, ERROR_PADDING.Y), + Padding = e("UIPadding", { + PaddingLeft = UDim.new(0, ERROR_PADDING.X), + PaddingRight = UDim.new(0, ERROR_PADDING.X), + PaddingTop = UDim.new(0, ERROR_PADDING.Y), + PaddingBottom = UDim.new(0, ERROR_PADDING.Y), + }), }), - }), - }) + }) + end) end local ErrorPage = Roact.Component:extend("ErrorPage") diff --git a/plugin/src/App/StatusPages/NotConnected.lua b/plugin/src/App/StatusPages/NotConnected.lua index e881e7eb..aabf7640 100644 --- a/plugin/src/App/StatusPages/NotConnected.lua +++ b/plugin/src/App/StatusPages/NotConnected.lua @@ -27,8 +27,8 @@ local function AddressEntry(props) }, { Host = e("TextBox", { Text = props.host or "", - Font = Enum.Font.Code, - TextSize = 18, + FontFace = theme.Font.Code, + TextSize = theme.TextSize.Large, TextColor3 = theme.AddressEntry.TextColor, TextXAlignment = Enum.TextXAlignment.Left, TextTransparency = props.transparency, @@ -51,8 +51,8 @@ local function AddressEntry(props) Port = e("TextBox", { Text = props.port or "", - Font = Enum.Font.Code, - TextSize = 18, + FontFace = theme.Font.Code, + TextSize = theme.TextSize.Large, TextColor3 = theme.AddressEntry.TextColor, TextTransparency = props.transparency, PlaceholderText = Config.defaultPort, diff --git a/plugin/src/App/StatusPages/Settings/Setting.lua b/plugin/src/App/StatusPages/Settings/Setting.lua index 75f391db..737b5be2 100644 --- a/plugin/src/App/StatusPages/Settings/Setting.lua +++ b/plugin/src/App/StatusPages/Settings/Setting.lua @@ -1,5 +1,3 @@ -local TextService = game:GetService("TextService") - local Rojo = script:FindFirstAncestor("Rojo") local Plugin = Rojo.Plugin local Packages = Rojo.Packages @@ -9,6 +7,7 @@ local Roact = require(Packages.Roact) local Settings = require(Plugin.Settings) local Assets = require(Plugin.Assets) local Theme = require(Plugin.App.Theme) +local getTextBoundsAsync = require(Plugin.App.getTextBoundsAsync) local Checkbox = require(Plugin.App.Components.Checkbox) local Dropdown = require(Plugin.App.Components.Dropdown) @@ -31,10 +30,16 @@ local TAG_TYPES = { }, } -local function getTextBounds(text, textSize, font, lineHeight, bounds) - local textBounds = TextService:GetTextSize(text, textSize, font, bounds) +local function getTextBoundsWithLineHeight( + text: string, + font: Font, + textSize: number, + width: number, + lineHeight: number +) + local textBounds = getTextBoundsAsync(text, font, textSize, width) - local lineCount = textBounds.Y / textSize + local lineCount = math.ceil(textBounds.Y / textSize) local lineHeightAbsolute = textSize * lineHeight return Vector2.new(textBounds.X, lineHeightAbsolute * lineCount - (lineHeightAbsolute - textSize)) @@ -148,7 +153,7 @@ function Setting:render() BackgroundTransparency = 1, }, { Heading = e("Frame", { - Size = UDim2.new(1, 0, 0, 16), + Size = UDim2.new(1, 0, 0, theme.TextSize.Medium), BackgroundTransparency = 1, }, { Layout = e("UIListLayout", { @@ -168,8 +173,8 @@ function Setting:render() else nil, Name = e("TextLabel", { Text = self.props.name, - Font = Enum.Font.GothamBold, - TextSize = 16, + FontFace = theme.Font.Bold, + TextSize = theme.TextSize.Medium, TextColor3 = if self.props.tag and TAG_TYPES[self.props.tag] then getThemeColorFromPath(theme, TAG_TYPES[self.props.tag].color) else settingsTheme.Setting.NameColor, @@ -177,7 +182,7 @@ function Setting:render() TextTransparency = self.props.transparency, RichText = true, - Size = UDim2.new(1, 0, 0, 16), + Size = UDim2.new(1, 0, 0, theme.TextSize.Medium), LayoutOrder = 2, BackgroundTransparency = 1, @@ -186,9 +191,9 @@ function Setting:render() Description = e("TextLabel", { Text = self.props.description, - Font = Enum.Font.Gotham, + FontFace = theme.Font.Main, LineHeight = 1.2, - TextSize = 14, + TextSize = theme.TextSize.Body, TextColor3 = settingsTheme.Setting.DescriptionColor, TextXAlignment = Enum.TextXAlignment.Left, TextTransparency = self.props.transparency, @@ -200,12 +205,12 @@ function Setting:render() inputSize = self.inputSize, }):map(function(values) local offset = values.inputSize.X + 5 - local textBounds = getTextBounds( + local textBounds = getTextBoundsWithLineHeight( self.props.description, - 14, - Enum.Font.Gotham, - 1.2, - Vector2.new(values.containerSize.X - offset, math.huge) + theme.Font.Main, + theme.TextSize.Body, + values.containerSize.X - offset, + 1.2 ) return UDim2.new(1, -offset, 0, textBounds.Y) end), diff --git a/plugin/src/App/StatusPages/Settings/init.lua b/plugin/src/App/StatusPages/Settings/init.lua index 31c76666..b2657d22 100644 --- a/plugin/src/App/StatusPages/Settings/init.lua +++ b/plugin/src/App/StatusPages/Settings/init.lua @@ -31,7 +31,7 @@ local confirmationBehaviors = { "Initial", "Always", "Large Changes", "Unlisted local function Navbar(props) return Theme.with(function(theme) - theme = theme.Settings.Navbar + local navbarTheme = theme.Settings.Navbar return e("Frame", { Size = UDim2.new(1, 0, 0, 46), @@ -41,7 +41,7 @@ local function Navbar(props) Back = e(IconButton, { icon = Assets.Images.Icons.Back, iconSize = 24, - color = theme.BackButtonColor, + color = navbarTheme.BackButtonColor, transparency = props.transparency, position = UDim2.new(0, 0, 0.5, 0), @@ -56,9 +56,9 @@ local function Navbar(props) Text = e("TextLabel", { Text = "Settings", - Font = Enum.Font.Gotham, - TextSize = 18, - TextColor3 = theme.TextColor, + FontFace = theme.Font.Thin, + TextSize = theme.TextSize.Large, + TextColor3 = navbarTheme.TextColor, TextTransparency = props.transparency, Size = UDim2.new(1, 0, 1, 0), @@ -153,14 +153,6 @@ function SettingsPage:render() }), }), - PlaySounds = e(Setting, { - id = "playSounds", - name = "Play Sounds", - description = "Toggle sound effects", - transparency = self.props.transparency, - layoutOrder = layoutIncrement(), - }), - Permissions = e(Setting, { name = "Third Party Permissions", description = "Manage permissions for third party plugins", @@ -198,6 +190,14 @@ function SettingsPage:render() }), }), + PlaySounds = e(Setting, { + id = "playSounds", + name = "Play Sounds", + description = "Toggle sound effects", + transparency = self.props.transparency, + layoutOrder = layoutIncrement(), + }), + CheckForUpdates = e(Setting, { id = "checkForUpdates", name = "Check For Updates", diff --git a/plugin/src/App/Theme.lua b/plugin/src/App/Theme.lua index 42d61942..6e644aca 100644 --- a/plugin/src/App/Theme.lua +++ b/plugin/src/App/Theme.lua @@ -1,7 +1,6 @@ --[[ - Theming system taking advantage of Roact's new context API. - Doesn't use colors provided by Studio and instead just branches on theme - name. This isn't exactly best practice. + Theming system provided through Roact's context. + Uses Studio colors when possible. ]] -- Studio does not exist outside Roblox Studio, so we'll lazily initialize it @@ -15,6 +14,8 @@ local function getStudio() return _Studio end +local ContentProvider = game:GetService("ContentProvider") + local Rojo = script:FindFirstAncestor("Rojo") local Packages = Rojo.Packages @@ -35,6 +36,27 @@ function StudioProvider:updateTheme() local isDark = studioTheme.Name == "Dark" local theme = strict(studioTheme.Name .. "Theme", { + Font = { + Main = Font.new("rbxasset://fonts/families/Montserrat.json", Enum.FontWeight.Medium, Enum.FontStyle.Normal), + Bold = Font.new("rbxasset://fonts/families/Montserrat.json", Enum.FontWeight.Bold, Enum.FontStyle.Normal), + Thin = Font.new( + "rbxasset://fonts/families/Montserrat.json", + Enum.FontWeight.Regular, + Enum.FontStyle.Normal + ), + Code = Font.new( + "rbxasset://fonts/families/Inconsolata.json", + Enum.FontWeight.Regular, + Enum.FontStyle.Normal + ), + }, + TextSize = { + Body = 15, + Small = 13, + Medium = 16, + Large = 18, + Code = 16, + }, BrandColor = BRAND_COLOR, BackgroundColor = studioTheme:GetColor(Enum.StudioStyleGuideColor.MainBackground), TextColor = studioTheme:GetColor(Enum.StudioStyleGuideColor.MainText), @@ -190,6 +212,13 @@ end function StudioProvider:init() self:updateTheme() + + -- Preload the Fonts so that getTextBoundsAsync won't yield + local fontAssetIds = {} + for _, font in self.state.theme.Font do + table.insert(fontAssetIds, font.Family) + end + pcall(ContentProvider.PreloadAsync, ContentProvider, fontAssetIds) end function StudioProvider:render() diff --git a/plugin/src/App/getTextBoundsAsync.lua b/plugin/src/App/getTextBoundsAsync.lua new file mode 100644 index 00000000..2813903e --- /dev/null +++ b/plugin/src/App/getTextBoundsAsync.lua @@ -0,0 +1,41 @@ +local TextService = game:GetService("TextService") + +local Rojo = script:FindFirstAncestor("Rojo") +local Packages = Rojo.Packages + +local Log = require(Packages.Log) + +local params = Instance.new("GetTextBoundsParams") + +local function getTextBoundsAsync( + text: string, + font: Font, + textSize: number, + width: number, + richText: boolean? +): Vector2 + if type(text) ~= "string" then + Log.warn(`Invalid text. Expected string, received {type(text)} instead`) + return Vector2.zero + end + if #text >= 200_000 then + Log.warn(`Invalid text. Exceeds the 199,999 character limit`) + return Vector2.zero + end + + params.Text = text + params.Font = font + params.Size = textSize + params.Width = width + params.RichText = not not richText + + local success, bounds = pcall(TextService.GetTextBoundsAsync, TextService, params) + if not success then + Log.warn(`Failed to get text bounds: {bounds}`) + return Vector2.zero + end + + return bounds +end + +return getTextBoundsAsync diff --git a/plugin/test b/plugin/test deleted file mode 100644 index caeb8874..00000000 --- a/plugin/test +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/sh - -rojo build test-place.project.json -o TestPlace.rbxlx -run-in-roblox --script run-tests.server.lua --place TestPlace.rbxlx \ No newline at end of file diff --git a/plugin/test-place.project.json b/plugin/test-place.project.json index 70875137..f4da9815 100644 --- a/plugin/test-place.project.json +++ b/plugin/test-place.project.json @@ -5,7 +5,7 @@ "ReplicatedStorage": { "Rojo": { - "$path": "default.project.json" + "$path": "../plugin.project.json" }, "Packages": { diff --git a/plugin/watch-build.sh b/plugin/watch-build.sh deleted file mode 100644 index 91ce6f89..00000000 --- a/plugin/watch-build.sh +++ /dev/null @@ -1,2 +0,0 @@ -# Continously build the rojo plugin into the local plugin directory on Windows -rojo build plugin/default.project.json -o $LOCALAPPDATA/Roblox/Plugins/Rojo.rbxm --watch diff --git a/scripts/unit-test-plugin.sh b/scripts/unit-test-plugin.sh new file mode 100644 index 00000000..c7ff7c79 --- /dev/null +++ b/scripts/unit-test-plugin.sh @@ -0,0 +1,2 @@ +rojo build plugin/test-place.project.json -o TestPlace.rbxl +run-in-roblox --script plugin/run-tests.server.lua --place TestPlace.rbxl \ No newline at end of file diff --git a/scripts/watch-build-plugin.sh b/scripts/watch-build-plugin.sh new file mode 100644 index 00000000..c21397f2 --- /dev/null +++ b/scripts/watch-build-plugin.sh @@ -0,0 +1 @@ +rojo build plugin.project.json --plugin Rojo.rbxm --watch \ No newline at end of file diff --git a/src/project.rs b/src/project.rs index b9573427..a83088c0 100644 --- a/src/project.rs +++ b/src/project.rs @@ -10,10 +10,7 @@ use memofs::Vfs; use serde::{Deserialize, Serialize}; use thiserror::Error; -use crate::{ - glob::Glob, resolution::UnresolvedValue, snapshot::SyncRule, - snapshot_middleware::default_sync_rules, -}; +use crate::{glob::Glob, resolution::UnresolvedValue, snapshot::SyncRule}; static PROJECT_FILENAME: &str = "default.project.json"; @@ -24,6 +21,13 @@ pub struct ProjectError(#[from] Error); #[derive(Debug, Error)] enum Error { + #[error( + "Rojo requires a project file, but no project file was found in path {}\n\ + See https://rojo.space/docs/ for guides and documentation.", + .path.display() + )] + NoProjectFound { path: PathBuf }, + #[error("The folder for the provided project cannot be used as a project name: {}\n\ Consider setting the `name` field on this project.", .path.display())] FolderNameInvalid { path: PathBuf }, @@ -222,7 +226,13 @@ impl Project { fuzzy_project_location: &Path, ) -> Result, ProjectError> { if let Some(project_path) = Self::locate(fuzzy_project_location) { - let contents = vfs.read(&project_path).map_err(Error::from)?; + let contents = vfs.read(&project_path).map_err(|e| match e.kind() { + io::ErrorKind::NotFound => Error::NoProjectFound { + path: project_path.to_path_buf(), + }, + _ => return e.into(), + })?; + Ok(Some(Self::load_from_slice(&contents, project_path, None)?)) } else { Ok(None) @@ -236,7 +246,13 @@ impl Project { fallback_name: Option<&str>, ) -> Result { let project_path = project_file_location.to_path_buf(); - let contents = vfs.read(&project_path).map_err(Error::from)?; + let contents = vfs.read(&project_path).map_err(|e| match e.kind() { + io::ErrorKind::NotFound => Error::NoProjectFound { + path: project_path.to_path_buf(), + }, + _ => return e.into(), + })?; + Ok(Self::load_from_slice( &contents, project_path, diff --git a/src/serve_session.rs b/src/serve_session.rs index ddb4dc21..25317d74 100644 --- a/src/serve_session.rs +++ b/src/serve_session.rs @@ -3,7 +3,7 @@ use std::{ collections::HashSet, io, net::IpAddr, - path::{Path, PathBuf}, + path::Path, sync::{Arc, Mutex, MutexGuard}, time::Instant, }; @@ -109,14 +109,7 @@ impl ServeSession { log::debug!("Loading project file from {}", project_path.display()); - let root_project = match Project::load_exact(&vfs, &project_path, None) { - Ok(project) => project, - Err(_) => { - return Err(ServeSessionError::NoProjectFound { - path: project_path.to_path_buf(), - }) - } - }; + let root_project = Project::load_exact(&vfs, &project_path, None)?; let mut tree = RojoTree::new(InstanceSnapshot::new()); @@ -226,13 +219,6 @@ impl ServeSession { #[derive(Debug, Error)] pub enum ServeSessionError { - #[error( - "Rojo requires a project file, but no project file was found in path {}\n\ - See https://rojo.space/docs/ for guides and documentation.", - .path.display() - )] - NoProjectFound { path: PathBuf }, - #[error(transparent)] Io { #[from]