From eb173a37ee6eba75daa5a61dd78d22457ceedfb4 Mon Sep 17 00:00:00 2001 From: boatbomber Date: Wed, 28 Feb 2024 22:11:56 -0800 Subject: [PATCH 01/29] Update patch visualizer with new design --- .../Components/PatchVisualizer/ChangeList.lua | 8 +- .../Components/PatchVisualizer/DomLabel.lua | 173 +++++++++++++----- .../App/Components/PatchVisualizer/init.lua | 67 ++++--- plugin/src/App/Theme.lua | 9 +- 4 files changed, 181 insertions(+), 76 deletions(-) diff --git a/plugin/src/App/Components/PatchVisualizer/ChangeList.lua b/plugin/src/App/Components/PatchVisualizer/ChangeList.lua index ce01b8010..2f10d4587 100644 --- a/plugin/src/App/Components/PatchVisualizer/ChangeList.lua +++ b/plugin/src/App/Components/PatchVisualizer/ChangeList.lua @@ -155,7 +155,7 @@ function ChangeList:render() local headerRow = changes[1] local headers = e("Frame", { - Size = UDim2.new(1, 0, 0, 30), + Size = UDim2.new(1, 0, 0, 24), BackgroundTransparency = rowTransparency, BackgroundColor3 = theme.Diff.Row, LayoutOrder = 0, @@ -214,7 +214,7 @@ function ChangeList:render() local isWarning = metadata.isWarning rows[row] = e("Frame", { - Size = UDim2.new(1, 0, 0, 30), + Size = UDim2.new(1, 0, 0, 24), BackgroundTransparency = row % 2 ~= 0 and rowTransparency or 1, BackgroundColor3 = theme.Diff.Row, BorderSizePixel = 0, @@ -269,8 +269,8 @@ function ChangeList:render() }, { Headers = headers, Values = e(ScrollingFrame, { - size = UDim2.new(1, 0, 1, -30), - position = UDim2.new(0, 0, 0, 30), + size = UDim2.new(1, 0, 1, -24), + position = UDim2.new(0, 0, 0, 24), contentSize = self.contentSize, transparency = props.transparency, }, rows), diff --git a/plugin/src/App/Components/PatchVisualizer/DomLabel.lua b/plugin/src/App/Components/PatchVisualizer/DomLabel.lua index 41ec0b8e9..2288fad3a 100644 --- a/plugin/src/App/Components/PatchVisualizer/DomLabel.lua +++ b/plugin/src/App/Components/PatchVisualizer/DomLabel.lua @@ -17,6 +17,67 @@ local e = Roact.createElement local ChangeList = require(script.Parent.ChangeList) local Tooltip = require(script.Parent.Parent.Tooltip) +local function getRecoloredClassIcon(className, color) + local iconProps = StudioService:GetClassIcon(className) + + if iconProps then + local success, editableImageSize, editableImagePixels = pcall(function() + local editableImage = game:GetService("AssetService"):CreateEditableImageAsync(iconProps.Image) + local pixels = editableImage:ReadPixels(Vector2.zero, editableImage.Size) + local hue, sat = color:ToHSV() + for i = 1, #pixels, 4 do + if pixels[i + 3] == 0 then + continue + end + local pixelColor = Color3.new(pixels[i], pixels[i + 1], pixels[i + 2]) + local _, _, val = pixelColor:ToHSV() + local newPixelColor = Color3.fromHSV(hue, sat, val) + pixels[i], pixels[i + 1], pixels[i + 2] = newPixelColor.R, newPixelColor.G, newPixelColor.B + end + return editableImage.Size, pixels + end) + if success then + iconProps.EditableImagePixels = editableImagePixels + iconProps.EditableImageSize = editableImageSize + end + end + + return iconProps +end + +local EditableImage = Roact.Component:extend("EditableImage") + +function EditableImage:init() + self.ref = Roact.createRef() +end + +function EditableImage:writePixels() + local image = self.ref.current + if not image then + return + end + if not self.props.pixels then + return + end + + image:WritePixels(Vector2.zero, self.props.size, self.props.pixels) +end + +function EditableImage:render() + return e("EditableImage", { + Size = self.props.size, + [Roact.Ref] = self.ref, + }) +end + +function EditableImage:didMount() + self:writePixels() +end + +function EditableImage:didUpdate() + self:writePixels() +end + local Expansion = Roact.Component:extend("Expansion") function Expansion:render() @@ -28,8 +89,8 @@ function Expansion:render() return e("Frame", { BackgroundTransparency = 1, - Size = UDim2.new(1, -props.indent, 1, -30), - Position = UDim2.new(0, props.indent, 0, 30), + Size = UDim2.new(1, -props.indent, 1, -24), + Position = UDim2.new(0, props.indent, 0, 24), }, { ChangeList = e(ChangeList, { changes = props.changeList, @@ -44,7 +105,7 @@ local DomLabel = Roact.Component:extend("DomLabel") function DomLabel:init() local initHeight = self.props.elementHeight:getValue() - self.expanded = initHeight > 30 + self.expanded = initHeight > 24 self.motor = Flipper.SingleMotor.new(initHeight) self.binding = bindingUtil.fromMotor(self.motor) @@ -53,7 +114,7 @@ function DomLabel:init() renderExpansion = self.expanded, }) self.motor:onStep(function(value) - local renderExpansion = value > 30 + local renderExpansion = value > 24 self.props.setElementHeight(value) if self.props.updateEvent then @@ -81,7 +142,7 @@ function DomLabel:didUpdate(prevProps) then -- Close the expansion when the domlabel is changed to a different thing self.expanded = false - self.motor:setGoal(Flipper.Spring.new(30, { + self.motor:setGoal(Flipper.Spring.new(24, { frequency = 5, dampingRatio = 1, })) @@ -90,28 +151,51 @@ end function DomLabel:render() local props = self.props + local depth = props.depth or 0 return Theme.with(function(theme) - local iconProps = StudioService:GetClassIcon(props.className) - local indent = (props.depth or 0) * 20 + 25 + local color = if props.isWarning + then theme.Diff.Warning + elseif props.patchType then theme.Diff[props.patchType] + else theme.TextColor + local iconProps = getRecoloredClassIcon(props.className, color) + + local indent = (props.depth or 0) * 12 + 15 -- Line guides help indent depth remain readable local lineGuides = {} - for i = 1, props.depth or 0 do - lineGuides["Line_" .. i] = e("Frame", { - Size = UDim2.new(0, 2, 1, 2), - Position = UDim2.new(0, (20 * i) + 15, 0, -1), - BorderSizePixel = 0, - BackgroundTransparency = props.transparency, - BackgroundColor3 = theme.BorderedContainer.BorderColor, - }) + for i = 1, depth do + if i == depth and props.isFinalChild and not props.hasChildren then + lineGuides["Line_" .. i] = e("Frame", { + Size = UDim2.new(0, 2, 1, 2 - 12), + Position = UDim2.new(0, (12 * i) + 6, 0, -1), + BorderSizePixel = 0, + BackgroundTransparency = props.transparency, + BackgroundColor3 = theme.BorderedContainer.BorderColor, + }) + else + lineGuides["Line_" .. i] = e("Frame", { + Size = UDim2.new(0, 2, 1, 2), + Position = UDim2.new(0, (12 * i) + 6, 0, -1), + BorderSizePixel = 0, + BackgroundTransparency = props.transparency, + BackgroundColor3 = theme.BorderedContainer.BorderColor, + }) + end end + lineGuides["Connector"] = e("Frame", { + Size = UDim2.new(0, 8, 0, 2), + Position = UDim2.new(0, (12 * props.depth) + 6, 0, 12), + BorderSizePixel = 0, + BackgroundTransparency = props.transparency, + BackgroundColor3 = theme.BorderedContainer.BorderColor, + }) + return e("Frame", { ClipsDescendants = true, - BackgroundColor3 = if props.patchType then theme.Diff[props.patchType] else nil, - BorderSizePixel = 0, - BackgroundTransparency = props.patchType and props.transparency or 1, + BackgroundTransparency = if props.elementIndex % 2 == 0 then 0.98 else 1, + BackgroundColor3 = theme.Diff.Row, Size = self.binding:map(function(expand) return UDim2.new(1, 0, 0, expand) end), @@ -141,8 +225,8 @@ function DomLabel:render() if props.changeList then self.expanded = not self.expanded - local goalHeight = 30 - + (if self.expanded then math.clamp(#props.changeList * 30, 30, 30 * 6) else 0) + local goalHeight = 24 + + (if self.expanded then math.clamp(#props.changeList * 24, 24, 24 * 6) else 0) self.motor:setGoal(Flipper.Spring.new(goalHeight, { frequency = 5, dampingRatio = 1, @@ -174,40 +258,45 @@ function DomLabel:render() DiffIcon = if props.patchType then e("ImageLabel", { Image = Assets.Images.Diff[props.patchType], - ImageColor3 = theme.AddressEntry.PlaceholderColor, + ImageColor3 = color, ImageTransparency = props.transparency, BackgroundTransparency = 1, - Size = UDim2.new(0, 20, 0, 20), - Position = UDim2.new(0, 0, 0, 15), + Size = UDim2.new(0, 14, 0, 14), + Position = UDim2.new(0, 0, 0, 12), AnchorPoint = Vector2.new(0, 0.5), }) else nil, - ClassIcon = e("ImageLabel", { - Image = iconProps.Image, - ImageTransparency = props.transparency, - ImageRectOffset = iconProps.ImageRectOffset, - ImageRectSize = iconProps.ImageRectSize, - BackgroundTransparency = 1, - Size = UDim2.new(0, 20, 0, 20), - Position = UDim2.new(0, indent, 0, 15), - AnchorPoint = Vector2.new(0, 0.5), - }), + ClassIcon = e( + "ImageLabel", + { + Image = iconProps.Image, + ImageTransparency = props.transparency, + ImageRectOffset = iconProps.ImageRectOffset, + ImageRectSize = iconProps.ImageRectSize, + BackgroundTransparency = 1, + Size = UDim2.new(0, 16, 0, 16), + Position = UDim2.new(0, indent + 2, 0, 12), + AnchorPoint = Vector2.new(0, 0.5), + }, + if iconProps.EditableImagePixels + then e(EditableImage, { + size = iconProps.EditableImageSize, + pixels = iconProps.EditableImagePixels, -- + }) + else nil + ), InstanceName = e("TextLabel", { - Text = (if props.isWarning then "⚠ " else "") .. props.name .. (props.hint and string.format( - ' %s', - theme.AddressEntry.PlaceholderColor:ToHex(), - props.hint - ) or ""), + Text = (if props.isWarning then "⚠ " else "") .. props.name, RichText = true, BackgroundTransparency = 1, - Font = Enum.Font.GothamMedium, + Font = if props.patchType then Enum.Font.GothamBold else Enum.Font.GothamMedium, TextSize = 14, - TextColor3 = if props.isWarning then theme.Diff.Warning else theme.TextColor, + TextColor3 = color, TextXAlignment = Enum.TextXAlignment.Left, TextTransparency = props.transparency, TextTruncate = Enum.TextTruncate.AtEnd, - Size = UDim2.new(1, -indent - 50, 0, 30), - Position = UDim2.new(0, indent + 30, 0, 0), + Size = UDim2.new(1, -indent - 50, 0, 24), + Position = UDim2.new(0, indent + 22, 0, 0), }), LineGuides = e("Folder", nil, lineGuides), }) diff --git a/plugin/src/App/Components/PatchVisualizer/init.lua b/plugin/src/App/Components/PatchVisualizer/init.lua index 67a4d7b20..1bdd221ad 100644 --- a/plugin/src/App/Components/PatchVisualizer/init.lua +++ b/plugin/src/App/Components/PatchVisualizer/init.lua @@ -55,31 +55,44 @@ function PatchVisualizer:render() end -- Recusively draw tree - local scrollElements, elementHeights = {}, {} + local scrollElements, elementHeights, elementIndex = {}, {}, 0 if patchTree then local function drawNode(node, depth) - local elementHeight, setElementHeight = Roact.createBinding(30) - table.insert(elementHeights, elementHeight) - table.insert( - scrollElements, - e(DomLabel, { - updateEvent = self.updateEvent, - elementHeight = elementHeight, - setElementHeight = setElementHeight, - patchType = node.patchType, - className = node.className, - isWarning = node.isWarning, - instance = node.instance, - name = node.name, - hint = node.hint, - changeList = node.changeList, - depth = depth, - transparency = self.props.transparency, - showStringDiff = self.props.showStringDiff, - showTableDiff = self.props.showTableDiff, - }) - ) + elementIndex += 1 + + local parentNode = patchTree:getNode(node.parentId) + local isFinalChild = true + if parentNode then + for _id, sibling in parentNode.children do + if type(sibling) == "table" and sibling.name and sibling.name > node.name then + isFinalChild = false + break + end + end + end + + local elementHeight, setElementHeight = Roact.createBinding(24) + elementHeights[elementIndex] = elementHeight + scrollElements[elementIndex] = e(DomLabel, { + updateEvent = self.updateEvent, + elementHeight = elementHeight, + setElementHeight = setElementHeight, + elementIndex = elementIndex, + patchType = node.patchType, + className = node.className, + isWarning = node.isWarning, + instance = node.instance, + name = node.name, + hint = node.hint, + changeList = node.changeList, + depth = depth, + hasChildren = (node.children ~= nil and next(node.children) ~= nil), + isFinalChild = isFinalChild, + transparency = self.props.transparency, + showStringDiff = self.props.showStringDiff, + showTableDiff = self.props.showTableDiff, + }) end patchTree:forEach(function(node, depth) @@ -88,11 +101,11 @@ function PatchVisualizer:render() end return Theme.with(function(theme) - return e(BorderedContainer, { - transparency = self.props.transparency, - size = self.props.size, - position = self.props.position, - layoutOrder = self.props.layoutOrder, + return e("Frame", { + BackgroundTransparency = 1, + Size = self.props.size, + Position = self.props.position, + LayoutOrder = self.props.layoutOrder, }, { CleanMerge = e("TextLabel", { Visible = #scrollElements == 0, diff --git a/plugin/src/App/Theme.lua b/plugin/src/App/Theme.lua index f1e9c3971..7810f8eaa 100644 --- a/plugin/src/App/Theme.lua +++ b/plugin/src/App/Theme.lua @@ -32,6 +32,8 @@ local StudioProvider = Roact.Component:extend("StudioProvider") function StudioProvider:updateTheme() local studioTheme = getStudio().Theme + local isDark = studioTheme.Name == "Dark" + local theme = strict(studioTheme.Name .. "Theme", { BackgroundColor = studioTheme:GetColor(Enum.StudioStyleGuideColor.MainBackground), TextColor = studioTheme:GetColor(Enum.StudioStyleGuideColor.MainText), @@ -139,9 +141,10 @@ function StudioProvider:updateTheme() BackgroundColor = studioTheme:GetColor(Enum.StudioStyleGuideColor.InputFieldBackground), }, Diff = { - Add = studioTheme:GetColor(Enum.StudioStyleGuideColor.DiffTextAdditionBackground), - Remove = studioTheme:GetColor(Enum.StudioStyleGuideColor.DiffTextDeletionBackground), - Edit = studioTheme:GetColor(Enum.StudioStyleGuideColor.DiffLineNumSeparatorBackground), + -- Studio doesn't have good colors since their diffs use backgrounds, not text + Add = if isDark then Color3.fromRGB(143, 227, 154) else Color3.fromRGB(44, 87, 33), + Remove = if isDark then Color3.fromRGB(242, 125, 125) else Color3.fromRGB(87, 33, 33), + Edit = if isDark then Color3.fromRGB(120, 154, 248) else Color3.fromRGB(33, 52, 87), Row = studioTheme:GetColor(Enum.StudioStyleGuideColor.BrightText), Warning = studioTheme:GetColor(Enum.StudioStyleGuideColor.WarningText), }, From 2b4f63b70b340652c123aa313087cb6247a44e16 Mon Sep 17 00:00:00 2001 From: boatbomber Date: Wed, 28 Feb 2024 22:14:32 -0800 Subject: [PATCH 02/29] Build tree correctly --- plugin/src/PatchTree.lua | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/plugin/src/PatchTree.lua b/plugin/src/PatchTree.lua index d0d99e37d..6d871001d 100644 --- a/plugin/src/PatchTree.lua +++ b/plugin/src/PatchTree.lua @@ -163,8 +163,7 @@ function Tree:buildAncestryNodes(previousId: string?, ancestryIds: { string }, p -- Build nodes for ancestry by going up the tree previousId = previousId or "ROOT" - for i = #ancestryIds, 1, -1 do - local ancestorId = ancestryIds[i] + for _, ancestorId in ancestryIds do local value = instanceMap.fromIds[ancestorId] or patch.added[ancestorId] if not value then Log.warn("Failed to find ancestor object for " .. ancestorId) @@ -200,7 +199,7 @@ function PatchTree.build(patch, instanceMap, changeListHeaders) end -- Gather ancestors from existing DOM - local ancestryIds, ancestryIndex = {}, 0 + local ancestryIds = {} local parentObject = instance.Parent local parentId = instanceMap.fromInstances[parentObject] local previousId = nil @@ -211,8 +210,7 @@ function PatchTree.build(patch, instanceMap, changeListHeaders) break end - ancestryIndex += 1 - ancestryIds[ancestryIndex] = parentId + table.insert(ancestryIds, 1, parentId) knownAncestors[parentId] = true parentObject = parentObject.Parent parentId = instanceMap.fromInstances[parentObject] From 94d593e41a6df481f83c3f647ccabf3c7d712c16 Mon Sep 17 00:00:00 2001 From: boatbomber Date: Wed, 28 Feb 2024 22:57:44 -0800 Subject: [PATCH 03/29] Improve tree guide lines --- .../Components/PatchVisualizer/DomLabel.lua | 19 ++++++++++++++++--- .../App/Components/PatchVisualizer/init.lua | 2 ++ plugin/src/PatchTree.lua | 9 +++++++++ 3 files changed, 27 insertions(+), 3 deletions(-) diff --git a/plugin/src/App/Components/PatchVisualizer/DomLabel.lua b/plugin/src/App/Components/PatchVisualizer/DomLabel.lua index 2288fad3a..abec052ce 100644 --- a/plugin/src/App/Components/PatchVisualizer/DomLabel.lua +++ b/plugin/src/App/Components/PatchVisualizer/DomLabel.lua @@ -165,7 +165,17 @@ function DomLabel:render() -- Line guides help indent depth remain readable local lineGuides = {} for i = 1, depth do - if i == depth and props.isFinalChild and not props.hasChildren then + if depth == 1 then + -- The root line starts halfway down since there's no parent to connect to + lineGuides["Line_" .. i] = e("Frame", { + Size = UDim2.new(0, 2, 1, -12), + Position = UDim2.new(0, (12 * i) + 6, 0, 12), + BorderSizePixel = 0, + BackgroundTransparency = props.transparency, + BackgroundColor3 = theme.BorderedContainer.BorderColor, + }) + elseif props.isFinalChild and not props.hasChildren and i == depth then + -- This line stops halfway down to merge with our connector for the right angle lineGuides["Line_" .. i] = e("Frame", { Size = UDim2.new(0, 2, 1, 2 - 12), Position = UDim2.new(0, (12 * i) + 6, 0, -1), @@ -174,8 +184,10 @@ function DomLabel:render() BackgroundColor3 = theme.BorderedContainer.BorderColor, }) else + -- All other lines go all the way + -- with the exception of the final element, which stops halfway down lineGuides["Line_" .. i] = e("Frame", { - Size = UDim2.new(0, 2, 1, 2), + Size = UDim2.new(0, 2, 1, 2 - (if props.isFinalElement then 12 else 0)), Position = UDim2.new(0, (12 * i) + 6, 0, -1), BorderSizePixel = 0, BackgroundTransparency = props.transparency, @@ -186,7 +198,8 @@ function DomLabel:render() lineGuides["Connector"] = e("Frame", { Size = UDim2.new(0, 8, 0, 2), - Position = UDim2.new(0, (12 * props.depth) + 6, 0, 12), + Position = UDim2.new(0, 2 + (12 * (props.depth + 1)), 0, 12), + AnchorPoint = Vector2.xAxis, BorderSizePixel = 0, BackgroundTransparency = props.transparency, BackgroundColor3 = theme.BorderedContainer.BorderColor, diff --git a/plugin/src/App/Components/PatchVisualizer/init.lua b/plugin/src/App/Components/PatchVisualizer/init.lua index 1bdd221ad..997b587c8 100644 --- a/plugin/src/App/Components/PatchVisualizer/init.lua +++ b/plugin/src/App/Components/PatchVisualizer/init.lua @@ -58,6 +58,7 @@ function PatchVisualizer:render() local scrollElements, elementHeights, elementIndex = {}, {}, 0 if patchTree then + local elementTotal = patchTree:getCount() local function drawNode(node, depth) elementIndex += 1 @@ -79,6 +80,7 @@ function PatchVisualizer:render() elementHeight = elementHeight, setElementHeight = setElementHeight, elementIndex = elementIndex, + isFinalElement = elementIndex == elementTotal, patchType = node.patchType, className = node.className, isWarning = node.isWarning, diff --git a/plugin/src/PatchTree.lua b/plugin/src/PatchTree.lua index 6d871001d..6f1dde1be 100644 --- a/plugin/src/PatchTree.lua +++ b/plugin/src/PatchTree.lua @@ -79,6 +79,15 @@ function Tree.new() return setmetatable(tree, Tree) end +-- Iterates over all nodes and counts them up +function Tree:getCount() + local count = 0 + self:forEach(function() + count += 1 + end) + return count +end + -- Iterates over all sub-nodes, depth first -- node is where to start from, defaults to root -- depth is used for recursion but can be used to set the starting depth From a813816a88054e14f2e09ff3ca50a6a3dd153c2f Mon Sep 17 00:00:00 2001 From: boatbomber Date: Wed, 28 Feb 2024 22:58:03 -0800 Subject: [PATCH 04/29] Remove padding from patch vis in confirming page --- plugin/src/App/StatusPages/Confirming.lua | 53 +++++++++++++---------- 1 file changed, 30 insertions(+), 23 deletions(-) diff --git a/plugin/src/App/StatusPages/Confirming.lua b/plugin/src/App/StatusPages/Confirming.lua index bd0fd0a13..0e093c45c 100644 --- a/plugin/src/App/StatusPages/Confirming.lua +++ b/plugin/src/App/StatusPages/Confirming.lua @@ -60,25 +60,32 @@ end function ConfirmingPage:render() return Theme.with(function(theme) local pageContent = Roact.createFragment({ - Header = e(Header, { - transparency = self.props.transparency, - layoutOrder = 1, - }), - - Title = e("TextLabel", { - Text = string.format( - "Sync changes for project '%s':", - self.props.confirmData.serverInfo.projectName or "UNKNOWN" - ), - LayoutOrder = 2, - Font = Enum.Font.Gotham, - LineHeight = 1.2, - TextSize = 14, - TextColor3 = theme.TextColor, - TextXAlignment = Enum.TextXAlignment.Left, - TextTransparency = self.props.transparency, - Size = UDim2.new(1, 0, 0, 20), + Heading = e("Frame", { + Size = UDim2.new(1, 0, 0, 54), BackgroundTransparency = 1, + }, { + Header = e(Header, { + transparency = self.props.transparency, + }), + Title = e("TextLabel", { + Text = string.format( + "Sync changes for project '%s':", + self.props.confirmData.serverInfo.projectName or "UNKNOWN" + ), + Font = Enum.Font.Gotham, + LineHeight = 1.2, + TextSize = 14, + TextColor3 = theme.TextColor, + TextXAlignment = Enum.TextXAlignment.Left, + TextTransparency = self.props.transparency, + Position = UDim2.new(0, 0, 0, 34), + Size = UDim2.new(1, 0, 0, 20), + BackgroundTransparency = 1, + }), + Padding = e("UIPadding", { + PaddingLeft = UDim.new(0, 20), + PaddingRight = UDim.new(0, 20), + }), }), PatchVisualizer = e(PatchVisualizer, { @@ -153,6 +160,11 @@ function ConfirmingPage:render() SortOrder = Enum.SortOrder.LayoutOrder, Padding = UDim.new(0, 10), }), + + Padding = e("UIPadding", { + PaddingLeft = UDim.new(0, 20), + PaddingRight = UDim.new(0, 20), + }), }), Layout = e("UIListLayout", { @@ -163,11 +175,6 @@ function ConfirmingPage:render() Padding = UDim.new(0, 10), }), - Padding = e("UIPadding", { - PaddingLeft = UDim.new(0, 20), - PaddingRight = UDim.new(0, 20), - }), - StringDiff = e(StudioPluginGui, { id = "Rojo_ConfirmingStringDiff", title = "String diff", From 2e136961ca34114a87bcef456060691a653ed4f3 Mon Sep 17 00:00:00 2001 From: boatbomber Date: Wed, 28 Feb 2024 23:02:14 -0800 Subject: [PATCH 05/29] Tree guidelines don't extend past child center --- plugin/src/App/Components/PatchVisualizer/DomLabel.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugin/src/App/Components/PatchVisualizer/DomLabel.lua b/plugin/src/App/Components/PatchVisualizer/DomLabel.lua index abec052ce..aee511c46 100644 --- a/plugin/src/App/Components/PatchVisualizer/DomLabel.lua +++ b/plugin/src/App/Components/PatchVisualizer/DomLabel.lua @@ -174,10 +174,10 @@ function DomLabel:render() BackgroundTransparency = props.transparency, BackgroundColor3 = theme.BorderedContainer.BorderColor, }) - elseif props.isFinalChild and not props.hasChildren and i == depth then + elseif props.isFinalChild and not props.hasChildren and i >= depth - 1 then -- This line stops halfway down to merge with our connector for the right angle lineGuides["Line_" .. i] = e("Frame", { - Size = UDim2.new(0, 2, 1, 2 - 12), + Size = UDim2.new(0, 2, 1, -9), Position = UDim2.new(0, (12 * i) + 6, 0, -1), BorderSizePixel = 0, BackgroundTransparency = props.transparency, @@ -187,7 +187,7 @@ function DomLabel:render() -- All other lines go all the way -- with the exception of the final element, which stops halfway down lineGuides["Line_" .. i] = e("Frame", { - Size = UDim2.new(0, 2, 1, 2 - (if props.isFinalElement then 12 else 0)), + Size = UDim2.new(0, 2, 1, if props.isFinalElement then -9 else 2), Position = UDim2.new(0, (12 * i) + 6, 0, -1), BorderSizePixel = 0, BackgroundTransparency = props.transparency, From 8c90e03d6e324a9a23ee819644477949b5394fc8 Mon Sep 17 00:00:00 2001 From: boatbomber Date: Thu, 29 Feb 2024 00:40:16 -0800 Subject: [PATCH 06/29] Dial in light theme colors --- plugin/src/App/Theme.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugin/src/App/Theme.lua b/plugin/src/App/Theme.lua index 7810f8eaa..75f08ae45 100644 --- a/plugin/src/App/Theme.lua +++ b/plugin/src/App/Theme.lua @@ -142,9 +142,9 @@ function StudioProvider:updateTheme() }, Diff = { -- Studio doesn't have good colors since their diffs use backgrounds, not text - Add = if isDark then Color3.fromRGB(143, 227, 154) else Color3.fromRGB(44, 87, 33), - Remove = if isDark then Color3.fromRGB(242, 125, 125) else Color3.fromRGB(87, 33, 33), - Edit = if isDark then Color3.fromRGB(120, 154, 248) else Color3.fromRGB(33, 52, 87), + Add = if isDark then Color3.fromRGB(143, 227, 154) else Color3.fromRGB(41, 164, 45), + Remove = if isDark then Color3.fromRGB(242, 125, 125) else Color3.fromRGB(150, 29, 29), + Edit = if isDark then Color3.fromRGB(120, 154, 248) else Color3.fromRGB(0, 70, 160), Row = studioTheme:GetColor(Enum.StudioStyleGuideColor.BrightText), Warning = studioTheme:GetColor(Enum.StudioStyleGuideColor.WarningText), }, From 2a8a8b4c0fc286b30d0176b6bfaa8e4cc59f324a Mon Sep 17 00:00:00 2001 From: boatbomber Date: Thu, 29 Feb 2024 00:41:06 -0800 Subject: [PATCH 07/29] Use new guideline style and move icon handling into components --- plugin/src/App/Components/ClassIcon.lua | 118 +++++++++++++++ plugin/src/App/Components/EditableImage.lua | 41 ++++++ .../Components/PatchVisualizer/DomLabel.lua | 134 ++++-------------- .../App/Components/PatchVisualizer/init.lua | 24 +++- 4 files changed, 205 insertions(+), 112 deletions(-) create mode 100644 plugin/src/App/Components/ClassIcon.lua create mode 100644 plugin/src/App/Components/EditableImage.lua diff --git a/plugin/src/App/Components/ClassIcon.lua b/plugin/src/App/Components/ClassIcon.lua new file mode 100644 index 000000000..74c5d5562 --- /dev/null +++ b/plugin/src/App/Components/ClassIcon.lua @@ -0,0 +1,118 @@ +local StudioService = game:GetService("StudioService") +local AssetService = game:GetService("AssetService") + +local Rojo = script:FindFirstAncestor("Rojo") +local Packages = Rojo.Packages + +local Roact = require(Packages.Roact) + +local e = Roact.createElement + +local EditableImage = require(script.Parent.EditableImage) + +local function getRecoloredClassIcon(className, color) + -- HACK: DataModel is currently missing an icon so we redirect it + if className == "DataModel" then + className = "Class" + end + + local iconProps = StudioService:GetClassIcon(className) + + if iconProps and color then + local success, editableImageSize, editableImagePixels = pcall(function() + local editableImage = AssetService:CreateEditableImageAsync(iconProps.Image) + local pixels = editableImage:ReadPixels(Vector2.zero, editableImage.Size) + + local minVal, maxVal = math.huge, -math.huge + for i = 1, #pixels, 4 do + if pixels[i + 3] == 0 then + continue + end + local pixelVal = math.max(pixels[i], pixels[i + 1], pixels[i + 2]) + + minVal = math.min(minVal, pixelVal) + maxVal = math.max(maxVal, pixelVal) + end + + local hue, sat, val = color:ToHSV() + for i = 1, #pixels, 4 do + if pixels[i + 3] == 0 then + continue + end + + local pixelVal = math.max(pixels[i], pixels[i + 1], pixels[i + 2]) + local newVal = val + if minVal < maxVal then + -- Remap minVal - maxVal to val*0.9 - val + newVal = val * (0.9 + 0.1 * (pixelVal - minVal) / (maxVal - minVal)) + end + + local newPixelColor = Color3.fromHSV(hue, sat, newVal) + pixels[i], pixels[i + 1], pixels[i + 2] = newPixelColor.R, newPixelColor.G, newPixelColor.B + end + return editableImage.Size, pixels + end) + if success then + iconProps.EditableImagePixels = editableImagePixels + iconProps.EditableImageSize = editableImageSize + end + end + + return iconProps +end + +local ClassIcon = Roact.PureComponent:extend("ClassIcon") + +function ClassIcon:init() + self.state = { + iconProps = nil, + } +end + +function ClassIcon:updateIcon() + local props = self.props + local iconProps = getRecoloredClassIcon(props.className, props.color) + self:setState({ + iconProps = iconProps, + }) +end + +function ClassIcon:didMount() + self:updateIcon() +end + +function ClassIcon:didUpdate(lastProps) + if lastProps.className ~= self.props.className or lastProps.color ~= self.props.color then + self:updateIcon() + end +end + +function ClassIcon:render() + local iconProps = self.state.iconProps + if not iconProps then + return nil + end + + return e( + "ImageLabel", + { + Size = self.props.size, + Position = self.props.position, + LayoutOrder = self.props.layoutOrder, + AnchorPoint = self.props.anchorPoint, + ImageTransparency = self.props.transparency, + Image = iconProps.Image, + ImageRectOffset = iconProps.ImageRectOffset, + ImageRectSize = iconProps.ImageRectSize, + BackgroundTransparency = 1, + }, + if iconProps.EditableImagePixels + then e(EditableImage, { + size = iconProps.EditableImageSize, + pixels = iconProps.EditableImagePixels, + }) + else nil + ) +end + +return ClassIcon diff --git a/plugin/src/App/Components/EditableImage.lua b/plugin/src/App/Components/EditableImage.lua new file mode 100644 index 000000000..501a37eee --- /dev/null +++ b/plugin/src/App/Components/EditableImage.lua @@ -0,0 +1,41 @@ +local Rojo = script:FindFirstAncestor("Rojo") +local Packages = Rojo.Packages + +local Roact = require(Packages.Roact) + +local e = Roact.createElement + +local EditableImage = Roact.PureComponent:extend("EditableImage") + +function EditableImage:init() + self.ref = Roact.createRef() +end + +function EditableImage:writePixels() + local image = self.ref.current + if not image then + return + end + if not self.props.pixels then + return + end + + image:WritePixels(Vector2.zero, self.props.size, self.props.pixels) +end + +function EditableImage:render() + return e("EditableImage", { + Size = self.props.size, + [Roact.Ref] = self.ref, + }) +end + +function EditableImage:didMount() + self:writePixels() +end + +function EditableImage:didUpdate() + self:writePixels() +end + +return EditableImage diff --git a/plugin/src/App/Components/PatchVisualizer/DomLabel.lua b/plugin/src/App/Components/PatchVisualizer/DomLabel.lua index aee511c46..9d7be5e66 100644 --- a/plugin/src/App/Components/PatchVisualizer/DomLabel.lua +++ b/plugin/src/App/Components/PatchVisualizer/DomLabel.lua @@ -16,67 +16,7 @@ local e = Roact.createElement local ChangeList = require(script.Parent.ChangeList) local Tooltip = require(script.Parent.Parent.Tooltip) - -local function getRecoloredClassIcon(className, color) - local iconProps = StudioService:GetClassIcon(className) - - if iconProps then - local success, editableImageSize, editableImagePixels = pcall(function() - local editableImage = game:GetService("AssetService"):CreateEditableImageAsync(iconProps.Image) - local pixels = editableImage:ReadPixels(Vector2.zero, editableImage.Size) - local hue, sat = color:ToHSV() - for i = 1, #pixels, 4 do - if pixels[i + 3] == 0 then - continue - end - local pixelColor = Color3.new(pixels[i], pixels[i + 1], pixels[i + 2]) - local _, _, val = pixelColor:ToHSV() - local newPixelColor = Color3.fromHSV(hue, sat, val) - pixels[i], pixels[i + 1], pixels[i + 2] = newPixelColor.R, newPixelColor.G, newPixelColor.B - end - return editableImage.Size, pixels - end) - if success then - iconProps.EditableImagePixels = editableImagePixels - iconProps.EditableImageSize = editableImageSize - end - end - - return iconProps -end - -local EditableImage = Roact.Component:extend("EditableImage") - -function EditableImage:init() - self.ref = Roact.createRef() -end - -function EditableImage:writePixels() - local image = self.ref.current - if not image then - return - end - if not self.props.pixels then - return - end - - image:WritePixels(Vector2.zero, self.props.size, self.props.pixels) -end - -function EditableImage:render() - return e("EditableImage", { - Size = self.props.size, - [Roact.Ref] = self.ref, - }) -end - -function EditableImage:didMount() - self:writePixels() -end - -function EditableImage:didUpdate() - self:writePixels() -end +local ClassIcon = require(script.Parent.Parent.ClassIcon) local Expansion = Roact.Component:extend("Expansion") @@ -151,34 +91,27 @@ end function DomLabel:render() local props = self.props - local depth = props.depth or 0 + local depth = props.depth or 1 return Theme.with(function(theme) local color = if props.isWarning then theme.Diff.Warning elseif props.patchType then theme.Diff[props.patchType] else theme.TextColor - local iconProps = getRecoloredClassIcon(props.className, color) - local indent = (props.depth or 0) * 12 + 15 + local indent = (depth - 1) * 12 + 15 -- Line guides help indent depth remain readable local lineGuides = {} - for i = 1, depth do - if depth == 1 then - -- The root line starts halfway down since there's no parent to connect to - lineGuides["Line_" .. i] = e("Frame", { - Size = UDim2.new(0, 2, 1, -12), - Position = UDim2.new(0, (12 * i) + 6, 0, 12), - BorderSizePixel = 0, - BackgroundTransparency = props.transparency, - BackgroundColor3 = theme.BorderedContainer.BorderColor, - }) - elseif props.isFinalChild and not props.hasChildren and i >= depth - 1 then + for i = 2, depth do + if props.depthsComplete[i] then + continue + end + if props.isFinalChild and i == depth then -- This line stops halfway down to merge with our connector for the right angle lineGuides["Line_" .. i] = e("Frame", { Size = UDim2.new(0, 2, 1, -9), - Position = UDim2.new(0, (12 * i) + 6, 0, -1), + Position = UDim2.new(0, (12 * (i - 1)) + 6, 0, -1), BorderSizePixel = 0, BackgroundTransparency = props.transparency, BackgroundColor3 = theme.BorderedContainer.BorderColor, @@ -188,7 +121,7 @@ function DomLabel:render() -- with the exception of the final element, which stops halfway down lineGuides["Line_" .. i] = e("Frame", { Size = UDim2.new(0, 2, 1, if props.isFinalElement then -9 else 2), - Position = UDim2.new(0, (12 * i) + 6, 0, -1), + Position = UDim2.new(0, (12 * (i - 1)) + 6, 0, -1), BorderSizePixel = 0, BackgroundTransparency = props.transparency, BackgroundColor3 = theme.BorderedContainer.BorderColor, @@ -196,18 +129,20 @@ function DomLabel:render() end end - lineGuides["Connector"] = e("Frame", { - Size = UDim2.new(0, 8, 0, 2), - Position = UDim2.new(0, 2 + (12 * (props.depth + 1)), 0, 12), - AnchorPoint = Vector2.xAxis, - BorderSizePixel = 0, - BackgroundTransparency = props.transparency, - BackgroundColor3 = theme.BorderedContainer.BorderColor, - }) + if depth ~= 1 then + lineGuides["Connector"] = e("Frame", { + Size = UDim2.new(0, 8, 0, 2), + Position = UDim2.new(0, 2 + (12 * props.depth), 0, 12), + AnchorPoint = Vector2.xAxis, + BorderSizePixel = 0, + BackgroundTransparency = props.transparency, + BackgroundColor3 = theme.BorderedContainer.BorderColor, + }) + end return e("Frame", { ClipsDescendants = true, - BackgroundTransparency = if props.elementIndex % 2 == 0 then 0.98 else 1, + BackgroundTransparency = if props.elementIndex % 2 == 0 then 0.985 else 1, BackgroundColor3 = theme.Diff.Row, Size = self.binding:map(function(expand) return UDim2.new(1, 0, 0, expand) @@ -279,25 +214,14 @@ function DomLabel:render() AnchorPoint = Vector2.new(0, 0.5), }) else nil, - ClassIcon = e( - "ImageLabel", - { - Image = iconProps.Image, - ImageTransparency = props.transparency, - ImageRectOffset = iconProps.ImageRectOffset, - ImageRectSize = iconProps.ImageRectSize, - BackgroundTransparency = 1, - Size = UDim2.new(0, 16, 0, 16), - Position = UDim2.new(0, indent + 2, 0, 12), - AnchorPoint = Vector2.new(0, 0.5), - }, - if iconProps.EditableImagePixels - then e(EditableImage, { - size = iconProps.EditableImageSize, - pixels = iconProps.EditableImagePixels, -- - }) - else nil - ), + ClassIcon = e(ClassIcon, { + className = props.className, + color = color, + transparency = props.transparency, + size = UDim2.new(0, 16, 0, 16), + position = UDim2.new(0, indent + 2, 0, 12), + anchorPoint = Vector2.new(0, 0.5), + }), InstanceName = e("TextLabel", { Text = (if props.isWarning then "⚠ " else "") .. props.name, RichText = true, diff --git a/plugin/src/App/Components/PatchVisualizer/init.lua b/plugin/src/App/Components/PatchVisualizer/init.lua index 997b587c8..a7fc4c0a6 100644 --- a/plugin/src/App/Components/PatchVisualizer/init.lua +++ b/plugin/src/App/Components/PatchVisualizer/init.lua @@ -8,7 +8,6 @@ local PatchTree = require(Plugin.PatchTree) local PatchSet = require(Plugin.PatchSet) local Theme = require(Plugin.App.Theme) -local BorderedContainer = require(Plugin.App.Components.BorderedContainer) local VirtualScroller = require(Plugin.App.Components.VirtualScroller) local e = Roact.createElement @@ -59,6 +58,7 @@ function PatchVisualizer:render() if patchTree then local elementTotal = patchTree:getCount() + local depthsComplete = {} local function drawNode(node, depth) elementIndex += 1 @@ -76,11 +76,18 @@ function PatchVisualizer:render() local elementHeight, setElementHeight = Roact.createBinding(24) elementHeights[elementIndex] = elementHeight scrollElements[elementIndex] = e(DomLabel, { + transparency = self.props.transparency, + showStringDiff = self.props.showStringDiff, + showTableDiff = self.props.showTableDiff, updateEvent = self.updateEvent, elementHeight = elementHeight, setElementHeight = setElementHeight, elementIndex = elementIndex, isFinalElement = elementIndex == elementTotal, + depth = depth, + depthsComplete = table.clone(depthsComplete), + hasChildren = (node.children ~= nil and next(node.children) ~= nil), + isFinalChild = isFinalChild, patchType = node.patchType, className = node.className, isWarning = node.isWarning, @@ -88,16 +95,19 @@ function PatchVisualizer:render() name = node.name, hint = node.hint, changeList = node.changeList, - depth = depth, - hasChildren = (node.children ~= nil and next(node.children) ~= nil), - isFinalChild = isFinalChild, - transparency = self.props.transparency, - showStringDiff = self.props.showStringDiff, - showTableDiff = self.props.showTableDiff, }) + + if isFinalChild then + depthsComplete[depth] = true + end end patchTree:forEach(function(node, depth) + depthsComplete[depth] = false + for i = depth + 1, #depthsComplete do + depthsComplete[i] = nil + end + drawNode(node, depth) end) end From e446940b10850ff730f6f96b2d85e9b22a472dd8 Mon Sep 17 00:00:00 2001 From: boatbomber Date: Thu, 29 Feb 2024 00:50:59 -0800 Subject: [PATCH 08/29] Remove rojo version heading from confirming page --- plugin/src/App/StatusPages/Confirming.lua | 34 +++++++++-------------- 1 file changed, 13 insertions(+), 21 deletions(-) diff --git a/plugin/src/App/StatusPages/Confirming.lua b/plugin/src/App/StatusPages/Confirming.lua index 0e093c45c..a239f7ea7 100644 --- a/plugin/src/App/StatusPages/Confirming.lua +++ b/plugin/src/App/StatusPages/Confirming.lua @@ -60,28 +60,20 @@ end function ConfirmingPage:render() return Theme.with(function(theme) local pageContent = Roact.createFragment({ - Heading = e("Frame", { - Size = UDim2.new(1, 0, 0, 54), + Title = e("TextLabel", { + Text = string.format( + "Sync changes for project '%s':", + self.props.confirmData.serverInfo.projectName or "UNKNOWN" + ), + Font = Enum.Font.Gotham, + LineHeight = 1.2, + TextSize = 14, + TextColor3 = theme.TextColor, + TextXAlignment = Enum.TextXAlignment.Left, + TextTransparency = self.props.transparency, + Size = UDim2.new(1, 0, 0, 20), BackgroundTransparency = 1, }, { - Header = e(Header, { - transparency = self.props.transparency, - }), - Title = e("TextLabel", { - Text = string.format( - "Sync changes for project '%s':", - self.props.confirmData.serverInfo.projectName or "UNKNOWN" - ), - Font = Enum.Font.Gotham, - LineHeight = 1.2, - TextSize = 14, - TextColor3 = theme.TextColor, - TextXAlignment = Enum.TextXAlignment.Left, - TextTransparency = self.props.transparency, - Position = UDim2.new(0, 0, 0, 34), - Size = UDim2.new(1, 0, 0, 20), - BackgroundTransparency = 1, - }), Padding = e("UIPadding", { PaddingLeft = UDim.new(0, 20), PaddingRight = UDim.new(0, 20), @@ -89,7 +81,7 @@ function ConfirmingPage:render() }), PatchVisualizer = e(PatchVisualizer, { - size = UDim2.new(1, 0, 1, -150), + size = UDim2.new(1, -10, 1, -100), transparency = self.props.transparency, layoutOrder = 3, From 95b80edee3927f4bcea0894b01dee6d8da095809 Mon Sep 17 00:00:00 2001 From: boatbomber Date: Thu, 29 Feb 2024 01:02:59 -0800 Subject: [PATCH 09/29] Use default theme colors from scroller --- plugin/src/App/Components/VirtualScroller.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugin/src/App/Components/VirtualScroller.lua b/plugin/src/App/Components/VirtualScroller.lua index 69bcf5f10..466da8a4f 100644 --- a/plugin/src/App/Components/VirtualScroller.lua +++ b/plugin/src/App/Components/VirtualScroller.lua @@ -131,8 +131,8 @@ function VirtualScroller:render() Position = props.position, AnchorPoint = props.anchorPoint, BackgroundTransparency = props.backgroundTransparency or 1, - BackgroundColor3 = props.backgroundColor3, - BorderColor3 = props.borderColor3, + BackgroundColor3 = props.backgroundColor3 or theme.BorderedContainer.BackgroundColor, + BorderColor3 = props.borderColor3 or theme.BorderedContainer.BorderColor, CanvasSize = self.totalCanvas:map(function(s) return UDim2.fromOffset(0, s) end), From a423fa83e8ca5ef6b9cce27973a449a9b77123d5 Mon Sep 17 00:00:00 2001 From: boatbomber Date: Thu, 29 Feb 2024 01:10:11 -0800 Subject: [PATCH 10/29] Use bordered container again --- plugin/src/App/Components/PatchVisualizer/init.lua | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/plugin/src/App/Components/PatchVisualizer/init.lua b/plugin/src/App/Components/PatchVisualizer/init.lua index a7fc4c0a6..55b373833 100644 --- a/plugin/src/App/Components/PatchVisualizer/init.lua +++ b/plugin/src/App/Components/PatchVisualizer/init.lua @@ -9,6 +9,7 @@ local PatchSet = require(Plugin.PatchSet) local Theme = require(Plugin.App.Theme) local VirtualScroller = require(Plugin.App.Components.VirtualScroller) +local BorderedContainer = require(Plugin.App.Components.BorderedContainer) local e = Roact.createElement @@ -113,11 +114,11 @@ function PatchVisualizer:render() end return Theme.with(function(theme) - return e("Frame", { - BackgroundTransparency = 1, - Size = self.props.size, - Position = self.props.position, - LayoutOrder = self.props.layoutOrder, + return e(BorderedContainer, { + transparency = self.props.transparency, + size = self.props.size, + position = self.props.position, + layoutOrder = self.props.layoutOrder, }, { CleanMerge = e("TextLabel", { Visible = #scrollElements == 0, From 7314e9b25316774da1e54b76cb179915d82714d0 Mon Sep 17 00:00:00 2001 From: boatbomber Date: Thu, 29 Feb 2024 01:10:20 -0800 Subject: [PATCH 11/29] Better require paths --- plugin/src/App/Components/ClassIcon.lua | 3 ++- plugin/src/App/Components/PatchVisualizer/DomLabel.lua | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/plugin/src/App/Components/ClassIcon.lua b/plugin/src/App/Components/ClassIcon.lua index 74c5d5562..06a2592a2 100644 --- a/plugin/src/App/Components/ClassIcon.lua +++ b/plugin/src/App/Components/ClassIcon.lua @@ -2,13 +2,14 @@ local StudioService = game:GetService("StudioService") local AssetService = game:GetService("AssetService") local Rojo = script:FindFirstAncestor("Rojo") +local Plugin = Rojo.Plugin local Packages = Rojo.Packages local Roact = require(Packages.Roact) local e = Roact.createElement -local EditableImage = require(script.Parent.EditableImage) +local EditableImage = require(Plugin.App.Components.EditableImage) local function getRecoloredClassIcon(className, color) -- HACK: DataModel is currently missing an icon so we redirect it diff --git a/plugin/src/App/Components/PatchVisualizer/DomLabel.lua b/plugin/src/App/Components/PatchVisualizer/DomLabel.lua index 9d7be5e66..d0495ab0a 100644 --- a/plugin/src/App/Components/PatchVisualizer/DomLabel.lua +++ b/plugin/src/App/Components/PatchVisualizer/DomLabel.lua @@ -15,8 +15,8 @@ local bindingUtil = require(Plugin.App.bindingUtil) local e = Roact.createElement local ChangeList = require(script.Parent.ChangeList) -local Tooltip = require(script.Parent.Parent.Tooltip) -local ClassIcon = require(script.Parent.Parent.ClassIcon) +local Tooltip = require(Plugin.App.Components.Tooltip) +local ClassIcon = require(Plugin.App.Components.ClassIcon) local Expansion = Roact.Component:extend("Expansion") From d26ad1d2c4f6325280deefc4c321e8c36894d9bd Mon Sep 17 00:00:00 2001 From: boatbomber Date: Thu, 29 Feb 2024 01:36:39 -0800 Subject: [PATCH 12/29] Padding and positioning --- plugin/src/App/Components/PatchVisualizer/init.lua | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugin/src/App/Components/PatchVisualizer/init.lua b/plugin/src/App/Components/PatchVisualizer/init.lua index 55b373833..d5f327f4b 100644 --- a/plugin/src/App/Components/PatchVisualizer/init.lua +++ b/plugin/src/App/Components/PatchVisualizer/init.lua @@ -118,6 +118,7 @@ function PatchVisualizer:render() transparency = self.props.transparency, size = self.props.size, position = self.props.position, + anchorPoint = self.props.anchorPoint, layoutOrder = self.props.layoutOrder, }, { CleanMerge = e("TextLabel", { @@ -132,7 +133,8 @@ function PatchVisualizer:render() }), VirtualScroller = e(VirtualScroller, { - size = UDim2.new(1, 0, 1, 0), + size = UDim2.new(1, 0, 1, -2), + position = UDim2.new(0, 0, 0, 2), transparency = self.props.transparency, count = #scrollElements, updateEvent = self.updateEvent.Event, From afbbf057fe6663c2d45593d5d3be68000ac279da Mon Sep 17 00:00:00 2001 From: boatbomber Date: Thu, 29 Feb 2024 02:40:37 -0800 Subject: [PATCH 13/29] Update patch viz for connected page --- assets/images/syncsuccess.png | Bin 0 -> 574 bytes assets/images/syncwarning.png | Bin 0 -> 607 bytes plugin/src/App/StatusPages/Connected.lua | 350 +++++++++++++---------- plugin/src/App/Theme.lua | 1 + plugin/src/App/init.lua | 2 +- plugin/src/Assets.lua | 2 + 6 files changed, 199 insertions(+), 156 deletions(-) create mode 100644 assets/images/syncsuccess.png create mode 100644 assets/images/syncwarning.png diff --git a/assets/images/syncsuccess.png b/assets/images/syncsuccess.png new file mode 100644 index 0000000000000000000000000000000000000000..74958cc132ae4fd3a455d75943cff3be1000a922 GIT binary patch literal 574 zcmV-E0>S->P)f|jX5i3&if;ims}EZ3$ZNVV zu*dQ`unLT2#Oo2(ZS{_4aJ%=rEg!c08ko$98{T8sFVEoEz=5c?6q&pSewK+Ri~8yr zd^WI^)R%b}uDEd*AN$C!4csR=@{!5rBjV4v-zd#8!kH0f9lYN$-T@lP>c0bB-Xa7v zjdTf&x}=AED6W$?6xRzRyUuG`GvXD+!Kq~o;`oqTlqin;den3PyaReO2A$j2RVgU$ zrugU6t}wv~#b5C*N-wn#!Y=SdQd82nq<;(~El7$5ZzTPxJjIiUzk0L-uJ1_%FaQ7m M07*qoM6N<$f`b6{*#H0l literal 0 HcmV?d00001 diff --git a/assets/images/syncwarning.png b/assets/images/syncwarning.png new file mode 100644 index 0000000000000000000000000000000000000000..eafb93d36a4b25da48c30615a447f7e169e42313 GIT binary patch literal 607 zcmV-l0-*hgP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!~g&e!~vBn4jTXf0rW{kK~zXf)s;Oj z1wj~vcYQSyB^_#h28Bq(MwhO-Z?wF zW0xyBPjd3kyyx6I+1cIM6lqo+8Jf>;7t+9dJI!Y&W2pzzGWxp<50J?@E zWCzfydB?bc#$?q8;cn=Ozs!&oK&u^EfKKR(O(@!tN)TGJcmY`@U^PH<&V{ZE1-T6=J{0&{P?sW%$vn0Vic<3 t30+}713pjP;ve!1+d}TaPyatj@(C7IN%0b1s#gF2002ovPDHLkV1f?A2L%8C literal 0 HcmV?d00001 diff --git a/plugin/src/App/StatusPages/Connected.lua b/plugin/src/App/StatusPages/Connected.lua index d8055b3f3..0d2b12511 100644 --- a/plugin/src/App/StatusPages/Connected.lua +++ b/plugin/src/App/StatusPages/Connected.lua @@ -3,9 +3,7 @@ local Plugin = Rojo.Plugin local Packages = Rojo.Packages local Roact = require(Packages.Roact) -local Flipper = require(Packages.Flipper) -local bindingUtil = require(Plugin.App.bindingUtil) local Theme = require(Plugin.App.Theme) local Assets = require(Plugin.Assets) local PatchSet = require(Plugin.PatchSet) @@ -23,28 +21,20 @@ local TableDiffVisualizer = require(Plugin.App.Components.TableDiffVisualizer) local e = Roact.createElement local AGE_UNITS = { - { 31556909, "year" }, - { 2629743, "month" }, - { 604800, "week" }, - { 86400, "day" }, - { 3600, "hour" }, - { - 60, - "minute", - }, + { 31556909, "y" }, + { 2629743, "mon" }, + { 604800, "w" }, + { 86400, "d" }, + { 3600, "h" }, + { 60, "m" }, } function timeSinceText(elapsed: number): string - if elapsed < 3 then - return "just now" - end - - local ageText = string.format("%d seconds ago", elapsed) + local ageText = string.format("%ds", elapsed) for _, UnitData in ipairs(AGE_UNITS) do local UnitSeconds, UnitName = UnitData[1], UnitData[2] if elapsed > UnitSeconds then - local c = math.floor(elapsed / UnitSeconds) - ageText = string.format("%d %s%s ago", c, UnitName, c > 1 and "s" or "") + ageText = elapsed // UnitSeconds .. UnitName break end end @@ -52,49 +42,94 @@ function timeSinceText(elapsed: number): string return ageText end -local ChangesDrawer = Roact.Component:extend("ChangesDrawer") +local ChangesViewer = Roact.Component:extend("ChangesViewer") -function ChangesDrawer:init() +function ChangesViewer:init() -- Hold onto the serve session during the lifecycle of this component -- so that it can still render during the fade out after disconnecting self.serveSession = self.props.serveSession end -function ChangesDrawer:render() - if self.props.rendered == false or self.serveSession == nil then +function ChangesViewer:render() + if self.props.rendered == false or self.serveSession == nil or self.props.patchData == nil then return nil end + local unapplied = PatchSet.countChanges(self.props.patchData.unapplied) + local applied = PatchSet.countChanges(self.props.patchData.patch) + return Theme.with(function(theme) - return e(BorderedContainer, { - transparency = self.props.transparency, - size = self.props.height:map(function(y) - return UDim2.new(1, 0, y, -220 * y) - end), - position = UDim2.new(0, 0, 1, 0), - anchorPoint = Vector2.new(0, 1), - layoutOrder = self.props.layoutOrder, - }, { - Close = e(IconButton, { - icon = Assets.Images.Icons.Close, - iconSize = 24, - color = theme.ConnectionDetails.DisconnectColor, - transparency = self.props.transparency, + return Roact.createFragment({ + Navbar = e("Frame", { + Size = UDim2.new(1, 0, 0, 40), + BackgroundTransparency = 1, + }, { + Back = e(IconButton, { + icon = Assets.Images.Icons.Back, + iconSize = 24, + color = theme.Settings.Navbar.BackButtonColor, + transparency = self.props.transparency, - position = UDim2.new(1, 0, 0, 0), - anchorPoint = Vector2.new(1, 0), + position = UDim2.new(0, 0, 0.5, 0), + anchorPoint = Vector2.new(0, 0.5), - onClick = self.props.onClose, - }, { - Tip = e(Tooltip.Trigger, { - text = "Close the patch visualizer", + onClick = self.props.onBack, + }, { + Tip = e(Tooltip.Trigger, { + text = "Back", + }), + }), + + Title = e("TextLabel", { + Text = "Sync " .. DateTime.fromUnixTimestamp(self.props.patchData.timestamp) + :FormatLocalTime("LTS", "en-us"), + Font = Enum.Font.Gotham, + TextSize = 17, + TextColor3 = if unapplied > 0 then theme.Diff.Warning else theme.TextColor, + TextTransparency = self.props.transparency, + Size = UDim2.new(1, 0, 0, 20), + BackgroundTransparency = 1, + }), + + Subtitle = e("TextLabel", { + Text = `Synced {applied} changes{if unapplied > 0 + then `, and {unapplied} changes failed to apply` + else ""}`, + RichText = true, + Font = Enum.Font.Gotham, + TextSize = 15, + TextColor3 = theme.SubTextColor, + TextTruncate = Enum.TextTruncate.AtEnd, + TextTransparency = self.props.transparency, + Size = UDim2.new(1, 0, 0, 16), + Position = UDim2.new(0, 0, 0, 20), + BackgroundTransparency = 1, + }), + + Divider = e("Frame", { + BackgroundColor3 = theme.Settings.DividerColor, + BackgroundTransparency = self.props.transparency, + Size = UDim2.new(1, 0, 0, 1), + Position = UDim2.new(0, 0, 1, 0), + BorderSizePixel = 0, + }, { + Gradient = e("UIGradient", { + Transparency = NumberSequence.new({ + NumberSequenceKeypoint.new(0, 1), + NumberSequenceKeypoint.new(0.1, 0), + NumberSequenceKeypoint.new(0.9, 0), + NumberSequenceKeypoint.new(1, 1), + }), + }), }), }), - PatchVisualizer = e(PatchVisualizer, { - size = UDim2.new(1, 0, 1, 0), + Patch = e(PatchVisualizer, { + size = UDim2.new(1, -10, 1, -65), + position = UDim2.new(0, 5, 1, -5), + anchorPoint = Vector2.new(0, 1), transparency = self.props.transparency, - layoutOrder = 3, + layoutOrder = self.props.layoutOrder, patchTree = self.props.patchTree, @@ -167,20 +202,7 @@ function ConnectedPage:getChangeInfoText() if patchData == nil then return "" end - - local elapsed = os.time() - patchData.timestamp - local unapplied = PatchSet.countChanges(patchData.unapplied) - - return "Synced " - .. timeSinceText(elapsed) - .. (if unapplied > 0 - then string.format( - ', but %d change%s failed to apply', - unapplied, - unapplied == 1 and "" or "s" - ) - else "") - .. "" + return timeSinceText(DateTime.now().UnixTimestamp - patchData.timestamp) end function ConnectedPage:startChangeInfoTextUpdater() @@ -190,13 +212,9 @@ function ConnectedPage:startChangeInfoTextUpdater() -- Start a new updater self.changeInfoTextUpdater = task.defer(function() while true do - if self.state.hoveringChangeInfo then - self.setChangeInfoText("" .. self:getChangeInfoText() .. "") - else - self.setChangeInfoText(self:getChangeInfoText()) - end + self.setChangeInfoText(self:getChangeInfoText()) - local elapsed = os.time() - self.props.patchData.timestamp + local elapsed = DateTime.now().UnixTimestamp - self.props.patchData.timestamp local updateInterval = 1 -- Update timestamp text as frequently as currently needed @@ -221,23 +239,6 @@ function ConnectedPage:stopChangeInfoTextUpdater() end function ConnectedPage:init() - self.changeDrawerMotor = Flipper.SingleMotor.new(0) - self.changeDrawerHeight = bindingUtil.fromMotor(self.changeDrawerMotor) - - self.changeDrawerMotor:onStep(function(value) - local renderChanges = value > 0.05 - - self:setState(function(state) - if state.renderChanges == renderChanges then - return nil - end - - return { - renderChanges = renderChanges, - } - end) - end) - self:setState({ renderChanges = false, hoveringChangeInfo = false, @@ -266,6 +267,10 @@ function ConnectedPage:didUpdate(previousProps) end function ConnectedPage:render() + local syncWarning = self.props.patchData + and self.props.patchData.unapplied + and PatchSet.countChanges(self.props.patchData.unapplied) > 0 + return Theme.with(function(theme) return Roact.createFragment({ Padding = e("UIPadding", { @@ -280,9 +285,69 @@ function ConnectedPage:render() Padding = UDim.new(0, 10), }), - Header = e(Header, { - transparency = self.props.transparency, - layoutOrder = 1, + Heading = e("Frame", { + BackgroundTransparency = 1, + Size = UDim2.new(1, 0, 0, 32), + }, { + Header = e(Header, { + transparency = self.props.transparency, + }), + + ChangeInfo = e("TextButton", { + Text = "", + Size = UDim2.new(0, 50, 1, 0), + BackgroundColor3 = theme.BorderedContainer.BorderedColor, + BackgroundTransparency = if self.state.hoveringChangeInfo then 0.7 else 1, + BorderSizePixel = 0, + Position = UDim2.new(1, -5, 0.5, 0), + AnchorPoint = Vector2.new(1, 0.5), + [Roact.Event.MouseEnter] = function() + self:setState({ + hoveringChangeInfo = true, + }) + end, + [Roact.Event.MouseLeave] = function() + self:setState({ + hoveringChangeInfo = false, + }) + end, + [Roact.Event.Activated] = function() + self:setState(function(prevState) + prevState = prevState or {} + return { + renderChanges = not prevState.renderChanges, + } + end) + end, + }, { + Corner = e("UICorner", { + CornerRadius = UDim.new(0, 5), + }), + Tooltip = e(Tooltip.Trigger, { + text = if self.state.renderChanges then "Hide changes" else "View changes", + }), + Icon = e("ImageLabel", { + BackgroundTransparency = 1, + Image = if syncWarning + then Assets.Images.Icons.SyncWarning + else Assets.Images.Icons.SyncSuccess, + ImageColor3 = if syncWarning then theme.Diff.Warning else theme.Header.VersionColor, + ImageTransparency = self.props.transparency, + Position = UDim2.new(1, -4, 0.5, 0), + AnchorPoint = Vector2.new(1, 0.5), + Size = UDim2.new(0, 16, 0, 16), + }), + Text = e("TextLabel", { + BackgroundTransparency = 1, + Text = self.changeInfoText, + Font = Enum.Font.Gotham, + TextSize = 15, + TextColor3 = if syncWarning then theme.Diff.Warning else theme.Header.VersionColor, + TextTransparency = self.props.transparency, + TextXAlignment = Enum.TextXAlignment.Right, + Size = UDim2.new(1, -25, 1, 0), + }), + }), }), ConnectionDetails = e(ConnectionDetails, { @@ -332,86 +397,61 @@ function ConnectedPage:render() }), }), - ChangeInfo = e("TextButton", { - Text = self.changeInfoText, - Font = Enum.Font.Gotham, - TextSize = 14, - TextWrapped = true, - RichText = true, - TextColor3 = theme.Header.VersionColor, - TextXAlignment = Enum.TextXAlignment.Left, - TextYAlignment = Enum.TextYAlignment.Top, - TextTransparency = self.props.transparency, - - Size = UDim2.new(1, 0, 0, 28), + ChangesViewer = e(StudioPluginGui, { + id = "Rojo_ChangesViewer", + title = "View changes", + active = self.state.renderChanges, + isEphemeral = true, - LayoutOrder = 4, - BackgroundTransparency = 1, + initDockState = Enum.InitialDockState.Float, + overridePreviousState = true, + floatingSize = Vector2.new(320, 210), + minimumSize = Vector2.new(300, 210), - [Roact.Event.MouseEnter] = function() - self:setState({ - hoveringChangeInfo = true, - }) - self.setChangeInfoText("" .. self:getChangeInfoText() .. "") - end, + zIndexBehavior = Enum.ZIndexBehavior.Sibling, - [Roact.Event.MouseLeave] = function() + onClose = function() self:setState({ - hoveringChangeInfo = false, + renderChanges = false, }) - self.setChangeInfoText(self:getChangeInfoText()) - end, - - [Roact.Event.Activated] = function() - if self.state.renderChanges then - self.changeDrawerMotor:setGoal(Flipper.Spring.new(0, { - frequency = 4, - dampingRatio = 1, - })) - else - self.changeDrawerMotor:setGoal(Flipper.Spring.new(1, { - frequency = 3, - dampingRatio = 1, - })) - end end, }, { - Tooltip = e(Tooltip.Trigger, { - text = if self.state.renderChanges then "Hide the changes" else "View the changes", + TooltipsProvider = e(Tooltip.Provider, nil, { + Tooltips = e(Tooltip.Container, nil), + Content = e("Frame", { + Size = UDim2.fromScale(1, 1), + BackgroundTransparency = 1, + }, { + Changes = e(ChangesViewer, { + transparency = self.props.transparency, + rendered = self.state.renderChanges, + patchData = self.props.patchData, + patchTree = self.props.patchTree, + serveSession = self.props.serveSession, + showStringDiff = function(oldString: string, newString: string) + self:setState({ + showingStringDiff = true, + oldString = oldString, + newString = newString, + }) + end, + showTableDiff = function(oldTable: { [any]: any? }, newTable: { [any]: any? }) + self:setState({ + showingTableDiff = true, + oldTable = oldTable, + newTable = newTable, + }) + end, + onBack = function() + self:setState({ + renderChanges = false, + }) + end, + }), + }), }), }), - ChangesDrawer = e(ChangesDrawer, { - rendered = self.state.renderChanges, - transparency = self.props.transparency, - patchTree = self.props.patchTree, - serveSession = self.props.serveSession, - height = self.changeDrawerHeight, - layoutOrder = 5, - - showStringDiff = function(oldString: string, newString: string) - self:setState({ - showingStringDiff = true, - oldString = oldString, - newString = newString, - }) - end, - showTableDiff = function(oldTable: { [any]: any? }, newTable: { [any]: any? }) - self:setState({ - showingTableDiff = true, - oldTable = oldTable, - newTable = newTable, - }) - end, - - onClose = function() - self.changeDrawerMotor:setGoal(Flipper.Spring.new(0, { - frequency = 4, - dampingRatio = 1, - })) - end, - }), - StringDiff = e(StudioPluginGui, { id = "Rojo_ConnectedStringDiff", title = "String diff", diff --git a/plugin/src/App/Theme.lua b/plugin/src/App/Theme.lua index 75f08ae45..84a429c6d 100644 --- a/plugin/src/App/Theme.lua +++ b/plugin/src/App/Theme.lua @@ -37,6 +37,7 @@ function StudioProvider:updateTheme() local theme = strict(studioTheme.Name .. "Theme", { BackgroundColor = studioTheme:GetColor(Enum.StudioStyleGuideColor.MainBackground), TextColor = studioTheme:GetColor(Enum.StudioStyleGuideColor.MainText), + SubTextColor = studioTheme:GetColor(Enum.StudioStyleGuideColor.SubText), Button = { Solid = { -- Solid uses brand theming, not Studio theming. diff --git a/plugin/src/App/init.lua b/plugin/src/App/init.lua index ecb807f05..716183feb 100644 --- a/plugin/src/App/init.lua +++ b/plugin/src/App/init.lua @@ -457,7 +457,7 @@ function App:startSession() end) serveSession:hookPostcommit(function(patch, _instanceMap, unapplied) - local now = os.time() + local now = DateTime.now().UnixTimestamp local old = self.state.patchData if PatchSet.isEmpty(patch) then diff --git a/plugin/src/Assets.lua b/plugin/src/Assets.lua index dbc60a166..8a9d15c0b 100644 --- a/plugin/src/Assets.lua +++ b/plugin/src/Assets.lua @@ -25,6 +25,8 @@ local Assets = { Back = "rbxassetid://6017213752", Reset = "rbxassetid://10142422327", Expand = "rbxassetid://12045401097", + SyncSuccess = "rbxassetid://16565035221", + SyncWarning = "rbxassetid://16565325171", }, Diff = { Add = "rbxassetid://10434145835", From 73aa24289f8737638471ae86d53a76c9bf4f567e Mon Sep 17 00:00:00 2001 From: boatbomber Date: Thu, 29 Feb 2024 13:16:48 -0800 Subject: [PATCH 14/29] Remove unused vars --- plugin/src/App/Components/PatchVisualizer/DomLabel.lua | 1 - plugin/src/App/StatusPages/Confirming.lua | 1 - 2 files changed, 2 deletions(-) diff --git a/plugin/src/App/Components/PatchVisualizer/DomLabel.lua b/plugin/src/App/Components/PatchVisualizer/DomLabel.lua index d0495ab0a..2af1996ce 100644 --- a/plugin/src/App/Components/PatchVisualizer/DomLabel.lua +++ b/plugin/src/App/Components/PatchVisualizer/DomLabel.lua @@ -1,5 +1,4 @@ local SelectionService = game:GetService("Selection") -local StudioService = game:GetService("StudioService") local Rojo = script:FindFirstAncestor("Rojo") local Plugin = Rojo.Plugin diff --git a/plugin/src/App/StatusPages/Confirming.lua b/plugin/src/App/StatusPages/Confirming.lua index a239f7ea7..6220672b3 100644 --- a/plugin/src/App/StatusPages/Confirming.lua +++ b/plugin/src/App/StatusPages/Confirming.lua @@ -9,7 +9,6 @@ local PatchTree = require(Plugin.PatchTree) local Settings = require(Plugin.Settings) local Theme = require(Plugin.App.Theme) local TextButton = require(Plugin.App.Components.TextButton) -local Header = require(Plugin.App.Components.Header) local StudioPluginGui = require(Plugin.App.Components.Studio.StudioPluginGui) local Tooltip = require(Plugin.App.Components.Tooltip) local PatchVisualizer = require(Plugin.App.Components.PatchVisualizer) From 8dca479d0bbb5ac8731c76ab1e1ace67274b27b0 Mon Sep 17 00:00:00 2001 From: boatbomber Date: Thu, 29 Feb 2024 14:08:07 -0800 Subject: [PATCH 15/29] Better connected info --- plugin/src/App/StatusPages/Connected.lua | 157 ++++++++++++++++++----- plugin/src/Assets.lua | 2 + 2 files changed, 125 insertions(+), 34 deletions(-) diff --git a/plugin/src/App/StatusPages/Connected.lua b/plugin/src/App/StatusPages/Connected.lua index 0d2b12511..feb9e74e7 100644 --- a/plugin/src/App/StatusPages/Connected.lua +++ b/plugin/src/App/StatusPages/Connected.lua @@ -64,8 +64,8 @@ function ChangesViewer:render() Size = UDim2.new(1, 0, 0, 40), BackgroundTransparency = 1, }, { - Back = e(IconButton, { - icon = Assets.Images.Icons.Back, + Close = e(IconButton, { + icon = Assets.Images.Icons.Close, iconSize = 24, color = theme.Settings.Navbar.BackButtonColor, transparency = self.props.transparency, @@ -76,36 +76,106 @@ function ChangesViewer:render() onClick = self.props.onBack, }, { Tip = e(Tooltip.Trigger, { - text = "Back", + text = "Close", }), }), Title = e("TextLabel", { - Text = "Sync " .. DateTime.fromUnixTimestamp(self.props.patchData.timestamp) - :FormatLocalTime("LTS", "en-us"), - Font = Enum.Font.Gotham, + Text = "Sync", + Font = Enum.Font.GothamMedium, TextSize = 17, - TextColor3 = if unapplied > 0 then theme.Diff.Warning else theme.TextColor, + TextXAlignment = Enum.TextXAlignment.Left, + TextColor3 = theme.TextColor, TextTransparency = self.props.transparency, - Size = UDim2.new(1, 0, 0, 20), + Size = UDim2.new(1, -40, 0, 20), + Position = UDim2.new(0, 40, 0, 0), BackgroundTransparency = 1, }), Subtitle = e("TextLabel", { - Text = `Synced {applied} changes{if unapplied > 0 - then `, and {unapplied} changes failed to apply` - else ""}`, - RichText = true, + Text = DateTime.fromUnixTimestamp(self.props.patchData.timestamp):FormatLocalTime("LTS", "en-us"), + TextXAlignment = Enum.TextXAlignment.Left, Font = Enum.Font.Gotham, TextSize = 15, TextColor3 = theme.SubTextColor, TextTruncate = Enum.TextTruncate.AtEnd, TextTransparency = self.props.transparency, - Size = UDim2.new(1, 0, 0, 16), - Position = UDim2.new(0, 0, 0, 20), + Size = UDim2.new(1, -40, 0, 16), + Position = UDim2.new(0, 40, 0, 20), BackgroundTransparency = 1, }), + Icon = e("ImageLabel", { + BackgroundTransparency = 1, + Image = if unapplied > 0 then Assets.Images.Icons.SyncWarning else Assets.Images.Icons.SyncSuccess, + ImageColor3 = if unapplied > 0 then theme.Diff.Warning else theme.TextColor, + Size = UDim2.new(0, 24, 0, 24), + Position = UDim2.new(1, -10, 0.5, 0), + AnchorPoint = Vector2.new(1, 0.5), + }), + + Info = e("Frame", { + BackgroundTransparency = 1, + Size = UDim2.new(0.5, 0, 0, 16), + Position = UDim2.new(0.5, 0, 0.5, 0), + AnchorPoint = Vector2.new(0.5, 0.5), + }, { + Layout = e("UIListLayout", { + FillDirection = Enum.FillDirection.Horizontal, + HorizontalAlignment = Enum.HorizontalAlignment.Center, + VerticalAlignment = Enum.VerticalAlignment.Center, + SortOrder = Enum.SortOrder.LayoutOrder, + Padding = UDim.new(0, 5), + }), + AppliedIcon = e("ImageLabel", { + BackgroundTransparency = 1, + Image = Assets.Images.Icons.Checkmark, + ImageColor3 = theme.TextColor, + Size = UDim2.new(0, 16, 0, 16), + LayoutOrder = 1, + }), + AppliedText = e("TextLabel", { + Text = applied, + Font = Enum.Font.Gotham, + TextSize = 15, + TextColor3 = theme.TextColor, + TextTransparency = self.props.transparency, + Size = UDim2.new(0, 0, 1, 0), + AutomaticSize = Enum.AutomaticSize.X, + BackgroundTransparency = 1, + LayoutOrder = 2, + }), + Warnings = if unapplied > 0 + then Roact.createFragment({ + Dot = e("ImageLabel", { + BackgroundTransparency = 1, + Image = Assets.Images.Circles[16], + ImageColor3 = theme.SubTextColor, + Size = UDim2.new(0, 4, 0, 4), + LayoutOrder = 3, + }), + UnappliedIcon = e("ImageLabel", { + BackgroundTransparency = 1, + Image = Assets.Images.Icons.Exclaimation, + ImageColor3 = theme.Diff.Warning, + Size = UDim2.new(0, 4, 0, 16), + LayoutOrder = 4, + }), + UnappliedText = e("TextLabel", { + Text = unapplied, + Font = Enum.Font.Gotham, + TextSize = 15, + TextColor3 = theme.Diff.Warning, + TextTransparency = self.props.transparency, + Size = UDim2.new(0, 0, 1, 0), + AutomaticSize = Enum.AutomaticSize.X, + BackgroundTransparency = 1, + LayoutOrder = 5, + }), + }) + else nil, + }), + Divider = e("Frame", { BackgroundColor3 = theme.Settings.DividerColor, BackgroundTransparency = self.props.transparency, @@ -295,7 +365,8 @@ function ConnectedPage:render() ChangeInfo = e("TextButton", { Text = "", - Size = UDim2.new(0, 50, 1, 0), + Size = UDim2.new(0, 0, 1, 0), + AutomaticSize = Enum.AutomaticSize.X, BackgroundColor3 = theme.BorderedContainer.BorderedColor, BackgroundTransparency = if self.state.hoveringChangeInfo then 0.7 else 1, BorderSizePixel = 0, @@ -326,26 +397,44 @@ function ConnectedPage:render() Tooltip = e(Tooltip.Trigger, { text = if self.state.renderChanges then "Hide changes" else "View changes", }), - Icon = e("ImageLabel", { - BackgroundTransparency = 1, - Image = if syncWarning - then Assets.Images.Icons.SyncWarning - else Assets.Images.Icons.SyncSuccess, - ImageColor3 = if syncWarning then theme.Diff.Warning else theme.Header.VersionColor, - ImageTransparency = self.props.transparency, - Position = UDim2.new(1, -4, 0.5, 0), - AnchorPoint = Vector2.new(1, 0.5), - Size = UDim2.new(0, 16, 0, 16), - }), - Text = e("TextLabel", { + Content = e("Frame", { BackgroundTransparency = 1, - Text = self.changeInfoText, - Font = Enum.Font.Gotham, - TextSize = 15, - TextColor3 = if syncWarning then theme.Diff.Warning else theme.Header.VersionColor, - TextTransparency = self.props.transparency, - TextXAlignment = Enum.TextXAlignment.Right, - Size = UDim2.new(1, -25, 1, 0), + Size = UDim2.new(0, 0, 1, 0), + AutomaticSize = Enum.AutomaticSize.X, + }, { + Layout = e("UIListLayout", { + FillDirection = Enum.FillDirection.Horizontal, + HorizontalAlignment = Enum.HorizontalAlignment.Center, + VerticalAlignment = Enum.VerticalAlignment.Center, + SortOrder = Enum.SortOrder.LayoutOrder, + Padding = UDim.new(0, 5), + }), + Padding = e("UIPadding", { + PaddingLeft = UDim.new(0, 5), + PaddingRight = UDim.new(0, 5), + }), + Text = e("TextLabel", { + BackgroundTransparency = 1, + Text = self.changeInfoText, + Font = Enum.Font.Gotham, + TextSize = 15, + TextColor3 = if syncWarning then theme.Diff.Warning else theme.Header.VersionColor, + TextTransparency = self.props.transparency, + TextXAlignment = Enum.TextXAlignment.Right, + Size = UDim2.new(0, 0, 1, 0), + AutomaticSize = Enum.AutomaticSize.X, + LayoutOrder = 1, + }), + Icon = e("ImageLabel", { + BackgroundTransparency = 1, + Image = if syncWarning + then Assets.Images.Icons.SyncWarning + else Assets.Images.Icons.SyncSuccess, + ImageColor3 = if syncWarning then theme.Diff.Warning else theme.Header.VersionColor, + ImageTransparency = self.props.transparency, + Size = UDim2.new(0, 24, 0, 24), + LayoutOrder = 2, + }), }), }), }), diff --git a/plugin/src/Assets.lua b/plugin/src/Assets.lua index 8a9d15c0b..1f3587e20 100644 --- a/plugin/src/Assets.lua +++ b/plugin/src/Assets.lua @@ -25,6 +25,8 @@ local Assets = { Back = "rbxassetid://6017213752", Reset = "rbxassetid://10142422327", Expand = "rbxassetid://12045401097", + Checkmark = "rbxassetid://16571012729", + Exclaimation = "rbxassetid://16571172190", SyncSuccess = "rbxassetid://16565035221", SyncWarning = "rbxassetid://16565325171", }, From fbb32e7ea6a846fad07ede280e1d769d7b57172c Mon Sep 17 00:00:00 2001 From: boatbomber Date: Thu, 29 Feb 2024 15:14:34 -0800 Subject: [PATCH 16/29] Improve info in changes heading --- plugin/src/App/StatusPages/Connected.lua | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/plugin/src/App/StatusPages/Connected.lua b/plugin/src/App/StatusPages/Connected.lua index feb9e74e7..1ab47c4ef 100644 --- a/plugin/src/App/StatusPages/Connected.lua +++ b/plugin/src/App/StatusPages/Connected.lua @@ -116,13 +116,13 @@ function ChangesViewer:render() Info = e("Frame", { BackgroundTransparency = 1, - Size = UDim2.new(0.5, 0, 0, 16), - Position = UDim2.new(0.5, 0, 0.5, 0), - AnchorPoint = Vector2.new(0.5, 0.5), + Size = UDim2.new(1, -90, 0, 16), + Position = UDim2.new(1, -45, 0.5, 0), + AnchorPoint = Vector2.new(1, 0.5), }, { Layout = e("UIListLayout", { FillDirection = Enum.FillDirection.Horizontal, - HorizontalAlignment = Enum.HorizontalAlignment.Center, + HorizontalAlignment = Enum.HorizontalAlignment.Right, VerticalAlignment = Enum.VerticalAlignment.Center, SortOrder = Enum.SortOrder.LayoutOrder, Padding = UDim.new(0, 5), @@ -147,10 +147,8 @@ function ChangesViewer:render() }), Warnings = if unapplied > 0 then Roact.createFragment({ - Dot = e("ImageLabel", { + Spacer = e("Frame", { BackgroundTransparency = 1, - Image = Assets.Images.Circles[16], - ImageColor3 = theme.SubTextColor, Size = UDim2.new(0, 4, 0, 4), LayoutOrder = 3, }), From ffbed993582d65bf6a99769aa2e16845115cf221 Mon Sep 17 00:00:00 2001 From: boatbomber Date: Thu, 29 Feb 2024 15:15:54 -0800 Subject: [PATCH 17/29] Better popout sizing --- plugin/src/App/StatusPages/Connected.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugin/src/App/StatusPages/Connected.lua b/plugin/src/App/StatusPages/Connected.lua index 1ab47c4ef..1a13df332 100644 --- a/plugin/src/App/StatusPages/Connected.lua +++ b/plugin/src/App/StatusPages/Connected.lua @@ -492,8 +492,8 @@ function ConnectedPage:render() initDockState = Enum.InitialDockState.Float, overridePreviousState = true, - floatingSize = Vector2.new(320, 210), - minimumSize = Vector2.new(300, 210), + floatingSize = Vector2.new(400, 500), + minimumSize = Vector2.new(300, 300), zIndexBehavior = Enum.ZIndexBehavior.Sibling, From 161f5bdfd2c11909c660af6a1b9bee5a98d939db Mon Sep 17 00:00:00 2001 From: boatbomber Date: Thu, 29 Feb 2024 15:17:42 -0800 Subject: [PATCH 18/29] Better padding --- plugin/src/App/StatusPages/Connected.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugin/src/App/StatusPages/Connected.lua b/plugin/src/App/StatusPages/Connected.lua index 1a13df332..809793cdd 100644 --- a/plugin/src/App/StatusPages/Connected.lua +++ b/plugin/src/App/StatusPages/Connected.lua @@ -117,7 +117,7 @@ function ChangesViewer:render() Info = e("Frame", { BackgroundTransparency = 1, Size = UDim2.new(1, -90, 0, 16), - Position = UDim2.new(1, -45, 0.5, 0), + Position = UDim2.new(1, -48, 0.5, 0), AnchorPoint = Vector2.new(1, 0.5), }, { Layout = e("UIListLayout", { @@ -125,7 +125,7 @@ function ChangesViewer:render() HorizontalAlignment = Enum.HorizontalAlignment.Right, VerticalAlignment = Enum.VerticalAlignment.Center, SortOrder = Enum.SortOrder.LayoutOrder, - Padding = UDim.new(0, 5), + Padding = UDim.new(0, 4), }), AppliedIcon = e("ImageLabel", { BackgroundTransparency = 1, From db7f0665e3e0df15142d1d521b94164b3e4cef52 Mon Sep 17 00:00:00 2001 From: boatbomber Date: Thu, 29 Feb 2024 15:34:30 -0800 Subject: [PATCH 19/29] Add tooltip to info heading --- plugin/src/App/StatusPages/Connected.lua | 133 +++++++++++++---------- 1 file changed, 75 insertions(+), 58 deletions(-) diff --git a/plugin/src/App/StatusPages/Connected.lua b/plugin/src/App/StatusPages/Connected.lua index 809793cdd..e6322492c 100644 --- a/plugin/src/App/StatusPages/Connected.lua +++ b/plugin/src/App/StatusPages/Connected.lua @@ -105,73 +105,90 @@ function ChangesViewer:render() BackgroundTransparency = 1, }), - Icon = e("ImageLabel", { - BackgroundTransparency = 1, - Image = if unapplied > 0 then Assets.Images.Icons.SyncWarning else Assets.Images.Icons.SyncSuccess, - ImageColor3 = if unapplied > 0 then theme.Diff.Warning else theme.TextColor, - Size = UDim2.new(0, 24, 0, 24), - Position = UDim2.new(1, -10, 0.5, 0), - AnchorPoint = Vector2.new(1, 0.5), - }), - Info = e("Frame", { BackgroundTransparency = 1, - Size = UDim2.new(1, -90, 0, 16), - Position = UDim2.new(1, -48, 0.5, 0), + Size = UDim2.new(0, 10, 0, 24), + AutomaticSize = Enum.AutomaticSize.X, + Position = UDim2.new(1, -5, 0.5, 0), AnchorPoint = Vector2.new(1, 0.5), }, { - Layout = e("UIListLayout", { - FillDirection = Enum.FillDirection.Horizontal, - HorizontalAlignment = Enum.HorizontalAlignment.Right, - VerticalAlignment = Enum.VerticalAlignment.Center, - SortOrder = Enum.SortOrder.LayoutOrder, - Padding = UDim.new(0, 4), + Tooltip = e(Tooltip.Trigger, { + text = `{applied} changes applied` + .. (if unapplied > 0 then `, {unapplied} changes failed` else ""), }), - AppliedIcon = e("ImageLabel", { + Content = e("Frame", { BackgroundTransparency = 1, - Image = Assets.Images.Icons.Checkmark, - ImageColor3 = theme.TextColor, - Size = UDim2.new(0, 16, 0, 16), - LayoutOrder = 1, - }), - AppliedText = e("TextLabel", { - Text = applied, - Font = Enum.Font.Gotham, - TextSize = 15, - TextColor3 = theme.TextColor, - TextTransparency = self.props.transparency, Size = UDim2.new(0, 0, 1, 0), AutomaticSize = Enum.AutomaticSize.X, - BackgroundTransparency = 1, - LayoutOrder = 2, + }, { + Layout = e("UIListLayout", { + FillDirection = Enum.FillDirection.Horizontal, + HorizontalAlignment = Enum.HorizontalAlignment.Right, + VerticalAlignment = Enum.VerticalAlignment.Center, + SortOrder = Enum.SortOrder.LayoutOrder, + Padding = UDim.new(0, 4), + }), + + StatusIcon = e("ImageLabel", { + BackgroundTransparency = 1, + Image = if unapplied > 0 + then Assets.Images.Icons.SyncWarning + else Assets.Images.Icons.SyncSuccess, + ImageColor3 = if unapplied > 0 then theme.Diff.Warning else theme.TextColor, + Size = UDim2.new(0, 24, 0, 24), + LayoutOrder = 10, + }), + StatusSpacer = e("Frame", { + BackgroundTransparency = 1, + Size = UDim2.new(0, 6, 0, 4), + LayoutOrder = 9, + }), + AppliedIcon = e("ImageLabel", { + BackgroundTransparency = 1, + Image = Assets.Images.Icons.Checkmark, + ImageColor3 = theme.TextColor, + Size = UDim2.new(0, 16, 0, 16), + LayoutOrder = 1, + }), + AppliedText = e("TextLabel", { + Text = applied, + Font = Enum.Font.Gotham, + TextSize = 15, + TextColor3 = theme.TextColor, + TextTransparency = self.props.transparency, + Size = UDim2.new(0, 0, 1, 0), + AutomaticSize = Enum.AutomaticSize.X, + BackgroundTransparency = 1, + LayoutOrder = 2, + }), + Warnings = if unapplied > 0 + then Roact.createFragment({ + WarningsSpacer = e("Frame", { + BackgroundTransparency = 1, + Size = UDim2.new(0, 4, 0, 4), + LayoutOrder = 3, + }), + UnappliedIcon = e("ImageLabel", { + BackgroundTransparency = 1, + Image = Assets.Images.Icons.Exclaimation, + ImageColor3 = theme.Diff.Warning, + Size = UDim2.new(0, 4, 0, 16), + LayoutOrder = 4, + }), + UnappliedText = e("TextLabel", { + Text = unapplied, + Font = Enum.Font.Gotham, + TextSize = 15, + TextColor3 = theme.Diff.Warning, + TextTransparency = self.props.transparency, + Size = UDim2.new(0, 0, 1, 0), + AutomaticSize = Enum.AutomaticSize.X, + BackgroundTransparency = 1, + LayoutOrder = 5, + }), + }) + else nil, }), - Warnings = if unapplied > 0 - then Roact.createFragment({ - Spacer = e("Frame", { - BackgroundTransparency = 1, - Size = UDim2.new(0, 4, 0, 4), - LayoutOrder = 3, - }), - UnappliedIcon = e("ImageLabel", { - BackgroundTransparency = 1, - Image = Assets.Images.Icons.Exclaimation, - ImageColor3 = theme.Diff.Warning, - Size = UDim2.new(0, 4, 0, 16), - LayoutOrder = 4, - }), - UnappliedText = e("TextLabel", { - Text = unapplied, - Font = Enum.Font.Gotham, - TextSize = 15, - TextColor3 = theme.Diff.Warning, - TextTransparency = self.props.transparency, - Size = UDim2.new(0, 0, 1, 0), - AutomaticSize = Enum.AutomaticSize.X, - BackgroundTransparency = 1, - LayoutOrder = 5, - }), - }) - else nil, }), Divider = e("Frame", { From f69c8e0bec3fda83fc76feca7f1a921ef67ad312 Mon Sep 17 00:00:00 2001 From: boatbomber Date: Thu, 29 Feb 2024 15:39:03 -0800 Subject: [PATCH 20/29] Fix terminated guideline overshooting when expanded --- plugin/src/App/Components/PatchVisualizer/DomLabel.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin/src/App/Components/PatchVisualizer/DomLabel.lua b/plugin/src/App/Components/PatchVisualizer/DomLabel.lua index 2af1996ce..70348eb5c 100644 --- a/plugin/src/App/Components/PatchVisualizer/DomLabel.lua +++ b/plugin/src/App/Components/PatchVisualizer/DomLabel.lua @@ -109,7 +109,7 @@ function DomLabel:render() if props.isFinalChild and i == depth then -- This line stops halfway down to merge with our connector for the right angle lineGuides["Line_" .. i] = e("Frame", { - Size = UDim2.new(0, 2, 1, -9), + Size = UDim2.new(0, 2, 0, 15), Position = UDim2.new(0, (12 * (i - 1)) + 6, 0, -1), BorderSizePixel = 0, BackgroundTransparency = props.transparency, From 55b20a5470f44821f2a3584a5e5682bdb972102a Mon Sep 17 00:00:00 2001 From: boatbomber Date: Thu, 29 Feb 2024 17:39:09 -0800 Subject: [PATCH 21/29] Fix unapplied being additional --- plugin/src/App/StatusPages/Connected.lua | 4 ++-- plugin/src/Assets.lua | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/plugin/src/App/StatusPages/Connected.lua b/plugin/src/App/StatusPages/Connected.lua index e6322492c..3f84d15f4 100644 --- a/plugin/src/App/StatusPages/Connected.lua +++ b/plugin/src/App/StatusPages/Connected.lua @@ -56,7 +56,7 @@ function ChangesViewer:render() end local unapplied = PatchSet.countChanges(self.props.patchData.unapplied) - local applied = PatchSet.countChanges(self.props.patchData.patch) + local applied = PatchSet.countChanges(self.props.patchData.patch) - unapplied return Theme.with(function(theme) return Roact.createFragment({ @@ -170,7 +170,7 @@ function ChangesViewer:render() }), UnappliedIcon = e("ImageLabel", { BackgroundTransparency = 1, - Image = Assets.Images.Icons.Exclaimation, + Image = Assets.Images.Icons.Exclamation, ImageColor3 = theme.Diff.Warning, Size = UDim2.new(0, 4, 0, 16), LayoutOrder = 4, diff --git a/plugin/src/Assets.lua b/plugin/src/Assets.lua index 1f3587e20..91f82d1ab 100644 --- a/plugin/src/Assets.lua +++ b/plugin/src/Assets.lua @@ -26,7 +26,7 @@ local Assets = { Reset = "rbxassetid://10142422327", Expand = "rbxassetid://12045401097", Checkmark = "rbxassetid://16571012729", - Exclaimation = "rbxassetid://16571172190", + Exclamation = "rbxassetid://16571172190", SyncSuccess = "rbxassetid://16565035221", SyncWarning = "rbxassetid://16565325171", }, From edcbfa1cbdafd788bd204542fa2f4b9c1ffb5889 Mon Sep 17 00:00:00 2001 From: boatbomber Date: Thu, 29 Feb 2024 18:12:00 -0800 Subject: [PATCH 22/29] Add changeinfo to domlabels --- .../Components/PatchVisualizer/DomLabel.lua | 53 +++++++++++ .../App/Components/PatchVisualizer/init.lua | 2 +- plugin/src/PatchSet.lua | 8 +- plugin/src/PatchTree.lua | 94 ++++++------------- 4 files changed, 89 insertions(+), 68 deletions(-) diff --git a/plugin/src/App/Components/PatchVisualizer/DomLabel.lua b/plugin/src/App/Components/PatchVisualizer/DomLabel.lua index 70348eb5c..5a4be9c09 100644 --- a/plugin/src/App/Components/PatchVisualizer/DomLabel.lua +++ b/plugin/src/App/Components/PatchVisualizer/DomLabel.lua @@ -234,6 +234,59 @@ function DomLabel:render() Size = UDim2.new(1, -indent - 50, 0, 24), Position = UDim2.new(0, indent + 22, 0, 0), }), + ChangeInfo = e("Frame", { + BackgroundTransparency = 1, + Size = UDim2.new(1, -indent - 80, 0, 24), + Position = UDim2.new(1, -2, 0, 0), + AnchorPoint = Vector2.new(1, 0), + }, { + Layout = e("UIListLayout", { + FillDirection = Enum.FillDirection.Horizontal, + HorizontalAlignment = Enum.HorizontalAlignment.Right, + VerticalAlignment = Enum.VerticalAlignment.Center, + SortOrder = Enum.SortOrder.LayoutOrder, + Padding = UDim.new(0, 4), + }), + Edits = if props.changeInfo and props.changeInfo.edits + then e("TextLabel", { + Text = props.changeInfo.edits .. " change" .. if props.changeInfo.edits > 1 then "s" else "", + BackgroundTransparency = 1, + Font = Enum.Font.Gotham, + TextSize = 14, + TextColor3 = theme.SubTextColor, + TextTransparency = props.transparency, + Size = UDim2.new(0, 0, 0, 16), + AutomaticSize = Enum.AutomaticSize.X, + LayoutOrder = 2, + }) + else nil, + Applied = if props.changeInfo and props.changeInfo.applied + then e("TextLabel", { + Text = props.changeInfo.applied .. " applied" .. if props.changeInfo.failed then "," else "", + BackgroundTransparency = 1, + Font = Enum.Font.Gotham, + TextSize = 14, + TextColor3 = theme.SubTextColor, + TextTransparency = props.transparency, + Size = UDim2.new(0, 0, 0, 16), + AutomaticSize = Enum.AutomaticSize.X, + LayoutOrder = 4, + }) + else nil, + Failed = if props.changeInfo and props.changeInfo.failed + then e("TextLabel", { + Text = props.changeInfo.failed .. " failed", + BackgroundTransparency = 1, + Font = Enum.Font.Gotham, + TextSize = 14, + TextColor3 = theme.Diff.Warning, + TextTransparency = props.transparency, + Size = UDim2.new(0, 0, 0, 16), + AutomaticSize = Enum.AutomaticSize.X, + LayoutOrder = 6, + }) + else nil, + }), LineGuides = e("Folder", nil, lineGuides), }) end) diff --git a/plugin/src/App/Components/PatchVisualizer/init.lua b/plugin/src/App/Components/PatchVisualizer/init.lua index d5f327f4b..a87d499bb 100644 --- a/plugin/src/App/Components/PatchVisualizer/init.lua +++ b/plugin/src/App/Components/PatchVisualizer/init.lua @@ -94,7 +94,7 @@ function PatchVisualizer:render() isWarning = node.isWarning, instance = node.instance, name = node.name, - hint = node.hint, + changeInfo = node.changeInfo, changeList = node.changeList, }) diff --git a/plugin/src/PatchSet.lua b/plugin/src/PatchSet.lua index abbac0cbb..064def36c 100644 --- a/plugin/src/PatchSet.lua +++ b/plugin/src/PatchSet.lua @@ -211,9 +211,11 @@ end function PatchSet.countChanges(patch) local count = 0 - for _ in patch.added do - -- Adding an instance is 1 change - count += 1 + for _, add in patch.added do + -- Adding an instance is 1 change per property + for _ in add.Properties do + count += 1 + end end for _ in patch.removed do -- Removing an instance is 1 change diff --git a/plugin/src/PatchTree.lua b/plugin/src/PatchTree.lua index 6f1dde1be..24416e68a 100644 --- a/plugin/src/PatchTree.lua +++ b/plugin/src/PatchTree.lua @@ -228,42 +228,14 @@ function PatchTree.build(patch, instanceMap, changeListHeaders) tree:buildAncestryNodes(previousId, ancestryIds, patch, instanceMap) -- Gather detail text - local changeList, hint = nil, nil + local changeList, changeInfo = nil, nil if next(change.changedProperties) or change.changedName then changeList = {} - local hintBuffer, hintBufferSize, hintOverflow = table.create(3), 0, 0 local changeIndex = 0 local function addProp(prop: string, current: any?, incoming: any?, metadata: any?) changeIndex += 1 changeList[changeIndex] = { prop, current, incoming, metadata } - - if hintBufferSize < 3 then - hintBufferSize += 1 - hintBuffer[hintBufferSize] = prop - return - end - - -- We only want to have 3 hints - -- to keep it deterministic, we sort them alphabetically - - -- Either this prop overflows, or it makes another one move to overflow - hintOverflow += 1 - - -- Shortcut for the common case - if hintBuffer[3] <= prop then - -- This prop is below the last hint, no need to insert - return - end - - -- Find the first available spot - for i, hintItem in hintBuffer do - if prop < hintItem then - -- This prop is before the currently selected hint, - -- so take its place and then continue to find a spot for the old hint - hintBuffer[i], prop = prop, hintBuffer[i] - end - end end -- Gather the changes @@ -283,8 +255,9 @@ function PatchTree.build(patch, instanceMap, changeListHeaders) ) end - -- Finalize detail values - hint = table.concat(hintBuffer, ", ") .. (if hintOverflow == 0 then "" else ", " .. hintOverflow .. " more") + changeInfo = { + edits = changeIndex, + } -- Sort changes and add header table.sort(changeList, function(a, b) @@ -300,7 +273,7 @@ function PatchTree.build(patch, instanceMap, changeListHeaders) className = instance.ClassName, name = instance.Name, instance = instance, - hint = hint, + changeInfo = changeInfo, changeList = changeList, }) end @@ -385,42 +358,14 @@ function PatchTree.build(patch, instanceMap, changeListHeaders) tree:buildAncestryNodes(previousId, ancestryIds, patch, instanceMap) -- Gather detail text - local changeList, hint = nil, nil + local changeList, changeInfo = nil, nil if next(change.Properties) then changeList = {} - local hintBuffer, hintBufferSize, hintOverflow = table.create(3), 0, 0 local changeIndex = 0 local function addProp(prop: string, incoming: any) changeIndex += 1 changeList[changeIndex] = { prop, "N/A", incoming } - - if hintBufferSize < 3 then - hintBufferSize += 1 - hintBuffer[hintBufferSize] = prop - return - end - - -- We only want to have 3 hints - -- to keep it deterministic, we sort them alphabetically - - -- Either this prop overflows, or it makes another one move to overflow - hintOverflow += 1 - - -- Shortcut for the common case - if hintBuffer[3] <= prop then - -- This prop is below the last hint, no need to insert - return - end - - -- Find the first available spot - for i, hintItem in hintBuffer do - if prop < hintItem then - -- This prop is before the currently selected hint, - -- so take its place and then continue to find a spot for the old hint - hintBuffer[i], prop = prop, hintBuffer[i] - end - end end for prop, incoming in change.Properties do @@ -428,8 +373,9 @@ function PatchTree.build(patch, instanceMap, changeListHeaders) addProp(prop, if success then incomingValue else select(2, next(incoming))) end - -- Finalize detail values - hint = table.concat(hintBuffer, ", ") .. (if hintOverflow == 0 then "" else ", " .. hintOverflow .. " more") + changeInfo = { + edits = changeIndex, + } -- Sort changes and add header table.sort(changeList, function(a, b) @@ -444,7 +390,7 @@ function PatchTree.build(patch, instanceMap, changeListHeaders) patchType = "Add", className = change.ClassName, name = change.Name, - hint = hint, + changeInfo = changeInfo, changeList = changeList, instance = instanceMap.fromIds[id], }) @@ -482,6 +428,8 @@ function PatchTree.updateMetadata(tree, patch, instanceMap, unappliedPatch) if not node.changeList then continue end + + local warnings = 0 for _, change in node.changeList do local property = change[1] local propertyFailedToApply = if property == "Name" @@ -492,6 +440,8 @@ function PatchTree.updateMetadata(tree, patch, instanceMap, unappliedPatch) -- This change didn't fail, no need to mark continue end + + warnings += 1 if change[4] == nil then change[4] = { isWarning = true } else @@ -499,6 +449,11 @@ function PatchTree.updateMetadata(tree, patch, instanceMap, unappliedPatch) end Log.trace(" Marked property as warning: {}.{}", node.name, property) end + + node.changeInfo = { + applied = (node.changeInfo.edits or (#node.changeList - 1)) - warnings, + failed = if warnings > 0 then warnings else nil, + } end for failedAdditionId in unappliedPatch.added do local node = tree:getNode(failedAdditionId) @@ -512,6 +467,7 @@ function PatchTree.updateMetadata(tree, patch, instanceMap, unappliedPatch) if not node.changeList then continue end + for _, change in node.changeList do -- Failed addition means that all properties failed to be added if change[4] == nil then @@ -521,6 +477,10 @@ function PatchTree.updateMetadata(tree, patch, instanceMap, unappliedPatch) end Log.trace(" Marked property as warning: {}.{}", node.name, change[1]) end + + node.changeInfo = { + failed = node.changeInfo.edits or (#node.changeList - 1), + } end for _, failedRemovalIdOrInstance in unappliedPatch.removed do local failedRemovalId = if Types.RbxId(failedRemovalIdOrInstance) @@ -543,6 +503,12 @@ function PatchTree.updateMetadata(tree, patch, instanceMap, unappliedPatch) -- Update if instances exist Timer.start("instanceAncestry") tree:forEach(function(node) + if node.changeInfo and node.changeInfo.edits then + node.changeInfo = { + applied = node.changeInfo.edits, + } + end + if node.instance then if node.instance.Parent == nil and node.instance ~= game then -- This instance has been removed From 36c5031e4729a9753c94a0a23a2b54340b98a2c0 Mon Sep 17 00:00:00 2001 From: boatbomber Date: Fri, 1 Mar 2024 23:01:10 -0800 Subject: [PATCH 23/29] Just numbers --- plugin/src/App/Components/PatchVisualizer/DomLabel.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugin/src/App/Components/PatchVisualizer/DomLabel.lua b/plugin/src/App/Components/PatchVisualizer/DomLabel.lua index 5a4be9c09..60310d251 100644 --- a/plugin/src/App/Components/PatchVisualizer/DomLabel.lua +++ b/plugin/src/App/Components/PatchVisualizer/DomLabel.lua @@ -249,7 +249,7 @@ function DomLabel:render() }), Edits = if props.changeInfo and props.changeInfo.edits then e("TextLabel", { - Text = props.changeInfo.edits .. " change" .. if props.changeInfo.edits > 1 then "s" else "", + Text = props.changeInfo.edits, BackgroundTransparency = 1, Font = Enum.Font.Gotham, TextSize = 14, @@ -262,11 +262,11 @@ function DomLabel:render() else nil, Applied = if props.changeInfo and props.changeInfo.applied then e("TextLabel", { - Text = props.changeInfo.applied .. " applied" .. if props.changeInfo.failed then "," else "", + Text = props.changeInfo.applied .. if props.changeInfo.failed then "," else "", BackgroundTransparency = 1, Font = Enum.Font.Gotham, TextSize = 14, - TextColor3 = theme.SubTextColor, + TextColor3 = theme.TextColor, TextTransparency = props.transparency, Size = UDim2.new(0, 0, 0, 16), AutomaticSize = Enum.AutomaticSize.X, @@ -275,7 +275,7 @@ function DomLabel:render() else nil, Failed = if props.changeInfo and props.changeInfo.failed then e("TextLabel", { - Text = props.changeInfo.failed .. " failed", + Text = props.changeInfo.failed, BackgroundTransparency = 1, Font = Enum.Font.Gotham, TextSize = 14, From fe164d252a60742cc6cb25ae9777feedd394ad9c Mon Sep 17 00:00:00 2001 From: boatbomber Date: Sat, 2 Mar 2024 00:25:56 -0800 Subject: [PATCH 24/29] Update changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2e5649013..a2abbeae7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## Unreleased Changes * Added popout diff visualizer for table properties like Attributes and Tags ([#834]) * Updated Theme to use Studio colors ([#838]) +* Improved patch visualizer UX ([#883]) * Added experimental setting for Auto Connect in playtests ([#840]) * 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`: @@ -55,6 +56,7 @@ [#834]: https://github.com/rojo-rbx/rojo/pull/834 [#838]: https://github.com/rojo-rbx/rojo/pull/838 [#840]: https://github.com/rojo-rbx/rojo/pull/840 +[#883]: https://github.com/rojo-rbx/rojo/pull/883 ## [7.4.1] - February 20, 2024 * Made the `name` field optional on project files ([#870]) From a9eb52f80970afa2f88691f85b0f0ccaad47792a Mon Sep 17 00:00:00 2001 From: boatbomber Date: Sat, 2 Mar 2024 00:31:26 -0800 Subject: [PATCH 25/29] Padding back to page wide since we went with the container style --- plugin/src/App/StatusPages/Confirming.lua | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/plugin/src/App/StatusPages/Confirming.lua b/plugin/src/App/StatusPages/Confirming.lua index 6220672b3..df8bf5b49 100644 --- a/plugin/src/App/StatusPages/Confirming.lua +++ b/plugin/src/App/StatusPages/Confirming.lua @@ -72,15 +72,10 @@ function ConfirmingPage:render() TextTransparency = self.props.transparency, Size = UDim2.new(1, 0, 0, 20), BackgroundTransparency = 1, - }, { - Padding = e("UIPadding", { - PaddingLeft = UDim.new(0, 20), - PaddingRight = UDim.new(0, 20), - }), }), PatchVisualizer = e(PatchVisualizer, { - size = UDim2.new(1, -10, 1, -100), + size = UDim2.new(1, 0, 1, -100), transparency = self.props.transparency, layoutOrder = 3, @@ -151,11 +146,11 @@ function ConfirmingPage:render() SortOrder = Enum.SortOrder.LayoutOrder, Padding = UDim.new(0, 10), }), + }), - Padding = e("UIPadding", { - PaddingLeft = UDim.new(0, 20), - PaddingRight = UDim.new(0, 20), - }), + Padding = e("UIPadding", { + PaddingLeft = UDim.new(0, 8), + PaddingRight = UDim.new(0, 8), }), Layout = e("UIListLayout", { From 51e3339c866f594bdb6c38c1ca52d1f287a6ea42 Mon Sep 17 00:00:00 2001 From: boatbomber Date: Sat, 2 Mar 2024 00:39:20 -0800 Subject: [PATCH 26/29] Simplify changeInfo handling to fit latest design --- .../App/Components/PatchVisualizer/DomLabel.lua | 15 +-------------- plugin/src/PatchTree.lua | 8 +------- 2 files changed, 2 insertions(+), 21 deletions(-) diff --git a/plugin/src/App/Components/PatchVisualizer/DomLabel.lua b/plugin/src/App/Components/PatchVisualizer/DomLabel.lua index 60310d251..a842dcbc5 100644 --- a/plugin/src/App/Components/PatchVisualizer/DomLabel.lua +++ b/plugin/src/App/Components/PatchVisualizer/DomLabel.lua @@ -249,7 +249,7 @@ function DomLabel:render() }), Edits = if props.changeInfo and props.changeInfo.edits then e("TextLabel", { - Text = props.changeInfo.edits, + Text = props.changeInfo.edits .. if props.changeInfo.failed then "," else "", BackgroundTransparency = 1, Font = Enum.Font.Gotham, TextSize = 14, @@ -260,19 +260,6 @@ function DomLabel:render() LayoutOrder = 2, }) else nil, - Applied = if props.changeInfo and props.changeInfo.applied - then e("TextLabel", { - Text = props.changeInfo.applied .. if props.changeInfo.failed then "," else "", - BackgroundTransparency = 1, - Font = Enum.Font.Gotham, - TextSize = 14, - TextColor3 = theme.TextColor, - TextTransparency = props.transparency, - Size = UDim2.new(0, 0, 0, 16), - AutomaticSize = Enum.AutomaticSize.X, - LayoutOrder = 4, - }) - else nil, Failed = if props.changeInfo and props.changeInfo.failed then e("TextLabel", { Text = props.changeInfo.failed, diff --git a/plugin/src/PatchTree.lua b/plugin/src/PatchTree.lua index 24416e68a..c1c5f91ca 100644 --- a/plugin/src/PatchTree.lua +++ b/plugin/src/PatchTree.lua @@ -451,7 +451,7 @@ function PatchTree.updateMetadata(tree, patch, instanceMap, unappliedPatch) end node.changeInfo = { - applied = (node.changeInfo.edits or (#node.changeList - 1)) - warnings, + edits = (node.changeInfo.edits or (#node.changeList - 1)) - warnings, failed = if warnings > 0 then warnings else nil, } end @@ -503,12 +503,6 @@ function PatchTree.updateMetadata(tree, patch, instanceMap, unappliedPatch) -- Update if instances exist Timer.start("instanceAncestry") tree:forEach(function(node) - if node.changeInfo and node.changeInfo.edits then - node.changeInfo = { - applied = node.changeInfo.edits, - } - end - if node.instance then if node.instance.Parent == nil and node.instance ~= game then -- This instance has been removed From 138acb1fe30cee18f94b4560aaba66c68d363c4b Mon Sep 17 00:00:00 2001 From: boatbomber Date: Mon, 25 Mar 2024 19:22:00 -0700 Subject: [PATCH 27/29] Remove hack --- plugin/src/App/Components/ClassIcon.lua | 5 ----- 1 file changed, 5 deletions(-) diff --git a/plugin/src/App/Components/ClassIcon.lua b/plugin/src/App/Components/ClassIcon.lua index 06a2592a2..9c428d147 100644 --- a/plugin/src/App/Components/ClassIcon.lua +++ b/plugin/src/App/Components/ClassIcon.lua @@ -12,11 +12,6 @@ local e = Roact.createElement local EditableImage = require(Plugin.App.Components.EditableImage) local function getRecoloredClassIcon(className, color) - -- HACK: DataModel is currently missing an icon so we redirect it - if className == "DataModel" then - className = "Class" - end - local iconProps = StudioService:GetClassIcon(className) if iconProps and color then From a62ba540a8fad9767994884affd1ed035fc0222b Mon Sep 17 00:00:00 2001 From: boatbomber Date: Mon, 25 Mar 2024 19:28:39 -0700 Subject: [PATCH 28/29] Cache classicon image data to avoid duplicate assetservice calls --- plugin/src/App/Components/ClassIcon.lua | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/plugin/src/App/Components/ClassIcon.lua b/plugin/src/App/Components/ClassIcon.lua index 9c428d147..58b57e3a1 100644 --- a/plugin/src/App/Components/ClassIcon.lua +++ b/plugin/src/App/Components/ClassIcon.lua @@ -11,13 +11,27 @@ local e = Roact.createElement local EditableImage = require(Plugin.App.Components.EditableImage) +local imageCache = {} +local function getImageSizeAndPixels(image) + if not imageCache[image] then + local editableImage = AssetService:CreateEditableImageAsync(image) + imageCache[image] = { + Size = editableImage.Size, + Pixels = editableImage:ReadPixels(Vector2.zero, editableImage.Size), + } + end + + imageCache[image].LastAccess = tick() + + return imageCache[image].Size, table.clone(imageCache[image].Pixels) +end + local function getRecoloredClassIcon(className, color) local iconProps = StudioService:GetClassIcon(className) if iconProps and color then local success, editableImageSize, editableImagePixels = pcall(function() - local editableImage = AssetService:CreateEditableImageAsync(iconProps.Image) - local pixels = editableImage:ReadPixels(Vector2.zero, editableImage.Size) + local size, pixels = getImageSizeAndPixels(iconProps.Image) local minVal, maxVal = math.huge, -math.huge for i = 1, #pixels, 4 do @@ -46,7 +60,7 @@ local function getRecoloredClassIcon(className, color) local newPixelColor = Color3.fromHSV(hue, sat, newVal) pixels[i], pixels[i + 1], pixels[i + 2] = newPixelColor.R, newPixelColor.G, newPixelColor.B end - return editableImage.Size, pixels + return size, pixels end) if success then iconProps.EditableImagePixels = editableImagePixels From 8f8f7f6ed2b9c5d3b1568a6eefe5c03377a12266 Mon Sep 17 00:00:00 2001 From: boatbomber Date: Mon, 25 Mar 2024 19:34:05 -0700 Subject: [PATCH 29/29] Remove lastaccess since we dont invalidate cache --- plugin/src/App/Components/ClassIcon.lua | 2 -- 1 file changed, 2 deletions(-) diff --git a/plugin/src/App/Components/ClassIcon.lua b/plugin/src/App/Components/ClassIcon.lua index 58b57e3a1..c3e46e0d2 100644 --- a/plugin/src/App/Components/ClassIcon.lua +++ b/plugin/src/App/Components/ClassIcon.lua @@ -21,8 +21,6 @@ local function getImageSizeAndPixels(image) } end - imageCache[image].LastAccess = tick() - return imageCache[image].Size, table.clone(imageCache[image].Pixels) end