From 5fb639ec7b872eb7b3fc4c8ffd7fa23e47e12339 Mon Sep 17 00:00:00 2001 From: devoreofox <232652342+devoreofox@users.noreply.github.com> Date: Sat, 16 May 2026 22:19:34 -0300 Subject: [PATCH 1/4] Split into Panels, added folders and alias display names --- Silkstring/Configuration.cs | 1 + Silkstring/Models/AliasEntry.cs | 1 + Silkstring/Models/AliasFolder.cs | 9 + Silkstring/Plugin.cs | 11 +- Silkstring/UI/AliasEditPanel.cs | 131 ++++++++++++ Silkstring/UI/AliasSelectPanel.cs | 333 ++++++++++++++++++++++++++++++ Silkstring/Windows/MainWindow.cs | 117 +++-------- 7 files changed, 509 insertions(+), 94 deletions(-) create mode 100644 Silkstring/Models/AliasFolder.cs create mode 100644 Silkstring/UI/AliasEditPanel.cs create mode 100644 Silkstring/UI/AliasSelectPanel.cs diff --git a/Silkstring/Configuration.cs b/Silkstring/Configuration.cs index 94a5163..01bdebb 100644 --- a/Silkstring/Configuration.cs +++ b/Silkstring/Configuration.cs @@ -9,6 +9,7 @@ namespace Silkstring; public class Configuration : IPluginConfiguration { public int Version { get; set; } = 1; + public List Folders = new(); public List Aliases = new(); public int CommandDelay { get; set; } = 100; public bool MultilineCommands { get; set; } = false; diff --git a/Silkstring/Models/AliasEntry.cs b/Silkstring/Models/AliasEntry.cs index 14a73ac..875967a 100644 --- a/Silkstring/Models/AliasEntry.cs +++ b/Silkstring/Models/AliasEntry.cs @@ -8,6 +8,7 @@ public class AliasEntry { public static readonly string[] Blacklist = ["silkstring", "xlplugins", "xlsettings", "xldclose", "xldev"]; + public string DisplayName = string.Empty; public bool Enabled = true; public string Name = string.Empty; public List Output = new(); diff --git a/Silkstring/Models/AliasFolder.cs b/Silkstring/Models/AliasFolder.cs new file mode 100644 index 0000000..d7dd9be --- /dev/null +++ b/Silkstring/Models/AliasFolder.cs @@ -0,0 +1,9 @@ +using System.Collections.Generic; + +namespace Silkstring.Models; + +public class AliasFolder +{ + public string Name { get; set; } = string.Empty; + public List Aliases { get; set; } = new(); +} diff --git a/Silkstring/Plugin.cs b/Silkstring/Plugin.cs index 29e45f4..3c5c315 100644 --- a/Silkstring/Plugin.cs +++ b/Silkstring/Plugin.cs @@ -55,7 +55,7 @@ public Plugin() EditWindow = new EditWindow(this); ConfigWindow = new ConfigWindow(this); - MainWindow = new MainWindow(this, EditWindow, ConfigWindow); + MainWindow = new MainWindow(this, ToggleConfigUi); WindowSystem.AddWindow(MainWindow); WindowSystem.AddWindow(ConfigWindow); @@ -110,10 +110,11 @@ private void ProcessChatInputDetour(ShellCommandModule* shellCommandModule, Utf8 if (splitString.Length > 0) { var commandName = splitString[0][1..]; - var alias = Configuration.Aliases.FirstOrDefault(a => - a.Enabled && - a.IsValid() && - a.Name.Split('|', StringSplitOptions.TrimEntries).Any(n => n.Equals(commandName, StringComparison.OrdinalIgnoreCase))); + var alias = Configuration.Aliases.Concat( + Configuration.Folders.SelectMany(g => g.Aliases)).FirstOrDefault(a => + a.Enabled && + a.IsValid() && + a.Name.Split('|', StringSplitOptions.TrimEntries).Any(n => n.Equals(commandName, StringComparison.OrdinalIgnoreCase))); if (alias != null) { var commands = alias.Output diff --git a/Silkstring/UI/AliasEditPanel.cs b/Silkstring/UI/AliasEditPanel.cs new file mode 100644 index 0000000..4965dd1 --- /dev/null +++ b/Silkstring/UI/AliasEditPanel.cs @@ -0,0 +1,131 @@ +using System; +using System.Linq; +using System.Numerics; +using Dalamud.Bindings.ImGui; +using Dalamud.Interface; +using Dalamud.Interface.Components; +using ECommons.ImGuiMethods; +using Silkstring.Models; +using Silkstring.Windows; + +namespace Silkstring.Ui; + +public class AliasEditPanel +{ + private readonly Configuration _configuration; + private readonly MainWindow _mainWindow; + + private string _multilineBuffer = string.Empty; + private int _multilineAliasId = -1; + + public AliasEditPanel(Configuration configuration, MainWindow mainWindow) + { + _configuration = configuration; + _mainWindow = mainWindow; + } + + public void Draw() + { + var alias = _mainWindow.SelectedAlias; + + if (alias == null) + { + DrawEmptyState(); + return; + } + + DrawAliasHeader(alias); + ImGui.Separator(); + DrawCommandList(alias); + } + + private void DrawEmptyState() + { + var placeholder = "Select an alias to edit"; + var size = ImGui.CalcTextSize(placeholder); + var region = ImGui.GetContentRegionAvail(); + + ImGui.SetCursorPos(new Vector2((region.X - size.X) / 2, (region.Y - size.Y) / 2)); + ImGui.TextDisabled(placeholder); + } + + private void DrawAliasHeader(AliasEntry alias) + { + if (ImGui.Checkbox($"###enabled{alias.UniqueId}", ref alias.Enabled)) _configuration.Save(); + ImGui.SameLine(); + ImGui.SetNextItemWidth(-1); + if(ImGui.InputTextWithHint($"###aliasName{alias.UniqueId}", "activation command", ref alias.Name, 100)) _configuration.Save(); + if(ImGui.IsItemHovered()) ImGui.SetTooltip("Seperate multiple aliases with | e.g. mew|meow|mreow"); + } + + private void DrawCommandList(AliasEntry alias) + { + if (_configuration.MultilineCommands) + { + DrawMultlineView(alias); + } + else + { + DrawListView(alias); + } + } + + private void DrawMultlineView(AliasEntry alias) + { + if (_multilineAliasId != alias.UniqueId) + { + _multilineBuffer = string.Join("\n", alias.Output.Select(c => c.Command)); + _multilineAliasId = alias.UniqueId; + } + + if (ImGui.InputTextMultiline($"###multiline{alias.UniqueId}", ref _multilineBuffer, 5000, new Vector2(-1, ImGui.GetContentRegionAvail().Y))) + { + var lines = _multilineBuffer.Split('\n', StringSplitOptions.RemoveEmptyEntries).Select(c => c.Trim()).ToList(); + for (var i = 0; i < lines.Count; i++) + { + if (i < alias.Output.Count) alias.Output[i].Command = lines[i]; + else alias.Output.Add(new CommandEntry { Command = lines[i] }); + } + + if (alias.Output.Count > lines.Count) alias.Output.RemoveRange(lines.Count, alias.Output.Count - lines.Count); + + _configuration.Save(); + } + } + + private void DrawListView(AliasEntry alias) + { + ImGui.Text("Commands:"); + if (ImGui.BeginChild("###commandList")) + { + foreach (var command in alias.Output) + { + if (command.UniqueId == 0) command.UniqueId = alias.Output.Count == 0 ? 1 : alias.Output.Max(c => c.UniqueId) + 1; + DrawCommandRow(command); + } + + alias.Output.RemoveAll(c => c.Delete); + } + + if (ImGuiComponents.IconButton((int)FontAwesomeIcon.Plus, FontAwesomeIcon.Plus)) + { + alias.Output.Add(new CommandEntry()); + _configuration.Save(); + } + if (ImGui.IsItemHovered()) ImGui.SetTooltip("Add Command"); + ImGui.EndChild(); + } + + private void DrawCommandRow(CommandEntry command) + { + ImGui.SetNextItemWidth(-60); + if (ImGui.InputText($"###cmd{command.UniqueId}", ref command.Command, 200)) _configuration.Save(); + ImGui.SameLine(); + + var canDelete = ImGui.GetIO().KeyShift && ImGui.GetIO().KeyCtrl; + ImGui.BeginDisabled(!canDelete); + if (ImGuiComponents.IconButton(command.UniqueId, FontAwesomeIcon.Trash)) command.Delete = true; + ImGui.EndDisabled(); + if (ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled)) ImGui.SetTooltip("Hold Shift + Ctrl to delete"); + } +} diff --git a/Silkstring/UI/AliasSelectPanel.cs b/Silkstring/UI/AliasSelectPanel.cs new file mode 100644 index 0000000..e96aec9 --- /dev/null +++ b/Silkstring/UI/AliasSelectPanel.cs @@ -0,0 +1,333 @@ +using System; +using System.Linq; +using System.Numerics; +using Dalamud.Bindings.ImGui; +using Dalamud.Interface; +using Dalamud.Interface.Components; +using Silkstring.Models; +using Silkstring.Windows; + +namespace Silkstring.Ui; + +public class AliasSelectPanel +{ + private readonly Configuration _configuration; + private readonly MainWindow _mainWindow; + private readonly Action _openSettings; + + private string _filter = string.Empty; + private bool MatchesFilter(AliasEntry alias) => string.IsNullOrWhiteSpace(_filter) || alias.Name.Contains(_filter, StringComparison.OrdinalIgnoreCase); + + private AliasEntry? _draggedAlias; + private AliasFolder? _draggedFromFolder; + + private AliasFolder? _renamingFolder; + private string _renameBuffer = string.Empty; + private string _preRenameName = string.Empty; + private bool _focusRename = false; + + private AliasEntry? _renamingAlias; + private string _renameAliasBuffer = string.Empty; + private bool _focusRenameAlias = false; + + private const string DragDropType = "ALIAS"; + + private static readonly Vector4 FolderColor = new(0.7f, 0.5f, 1.0f, 1.0f); + + public AliasSelectPanel(Configuration configuration, MainWindow mainWindow, Action openSettings) + { + _configuration = configuration; + _mainWindow = mainWindow; + _openSettings = openSettings; + } + + public void Draw() + { + ImGui.SetNextItemWidth(-1); + ImGui.InputTextWithHint("###filter", "Filter...", ref _filter, 100); + + var frameHeight = ImGui.GetFrameHeight(); + var listHeight = ImGui.GetContentRegionAvail().Y - frameHeight - ImGui.GetStyle().ItemSpacing.Y; + + if (ImGui.BeginChild("###aliasList", new Vector2(0, listHeight))) + { + DrawFolders(); + DrawUnsorted(); + } + ImGui.EndChild(); + + ImGui.PushStyleVar(ImGuiStyleVar.ItemSpacing, Vector2.Zero); + ImGui.PushStyleVar(ImGuiStyleVar.WindowPadding, Vector2.Zero); + + if (ImGui.BeginChild("###footer", new Vector2(-1, frameHeight))) DrawFooter(); + ImGui.EndChild(); + ImGui.PopStyleVar(2); + } + + private void DrawFolders() + { + foreach (var folder in _configuration.Folders.ToList()) + { + var filtered = folder.Aliases.Where(a => MatchesFilter(a)) + .OrderBy(a => string.IsNullOrWhiteSpace(a.DisplayName) ? a.Name : a.DisplayName) + .ToList(); + + if (!string.IsNullOrEmpty(_filter) && filtered.Count == 0) continue; + + bool open; + bool needsTreePop = false; + + if (_renamingFolder == folder) + { + if (_focusRename) + { + ImGui.SetKeyboardFocusHere(); + _focusRename = false; + } + + ImGui.SetNextItemWidth(-1); + ImGui.InputText($"###rename{folder.GetHashCode()}", ref _renameBuffer, 100); + + if (ImGui.IsItemDeactivated()) + { + var isDuplicate = _configuration.Folders.Any(f => f != folder && f.Name.Equals(_renameBuffer, StringComparison.OrdinalIgnoreCase)); + + if (string.IsNullOrWhiteSpace(_renameBuffer) || isDuplicate) + { + if (string.IsNullOrWhiteSpace(_preRenameName)) _configuration.Folders.Remove(folder); + } + else folder.Name = _renameBuffer; + _renamingFolder = null; + _configuration.Save(); + } + + open = true; + } + else + { + ImGui.PushStyleColor(ImGuiCol.Text, FolderColor); + open = ImGui.TreeNodeEx($"{folder.Name}###{folder.GetHashCode()}folder", + ImGuiTreeNodeFlags.SpanAvailWidth); + ImGui.PopStyleColor(); + needsTreePop = open; + + if (ImGui.BeginPopupContextItem($"###folderContext{folder.GetHashCode()}")) + { + if (ImGui.MenuItem("Rename")) + { + _renamingFolder = folder; + _renameBuffer = folder.Name; + _preRenameName = folder.Name; + _focusRename = true; + } + + if (ImGui.MenuItem("Delete")) + { + foreach (var alias in folder.Aliases) + { + _configuration.Aliases.Add(alias); + } + + _configuration.Folders.Remove(folder); + _configuration.Save(); + } + + ImGui.EndPopup(); + } + } + + if (ImGui.BeginDragDropTarget()) + { + var payload = ImGui.AcceptDragDropPayload(DragDropType); + if (!payload.IsNull && payload.IsDelivery() && _draggedAlias != null) + MoveAlias(_draggedAlias, _draggedFromFolder, toFolder: folder); + ImGui.EndDragDropTarget(); + } + + if (open) + { + foreach (var alias in filtered) + { + DrawAliasRow(alias, owningFolder: folder); + } + + if (needsTreePop) ImGui.TreePop(); + } + } + } + + private void DrawUnsorted() + { + var filtered = _configuration.Aliases.Where(a => MatchesFilter(a)).OrderBy(a => string.IsNullOrWhiteSpace(a.DisplayName) ? a.Name : a.DisplayName).ToList(); + + foreach (var alias in filtered) + { + DrawAliasRow(alias, owningFolder: null); + } + + var remaining = ImGui.GetContentRegionAvail(); + if (remaining.Y > 0) + { + ImGui.InvisibleButton("###dropTarget", new Vector2(-1, remaining.Y)); + if (ImGui.BeginDragDropTarget()) + { + var payload = ImGui.AcceptDragDropPayload(DragDropType); + if (!payload.IsNull && payload.IsDelivery() && _draggedAlias != null) MoveAlias(_draggedAlias, _draggedFromFolder, toFolder: null); + ImGui.EndDragDropTarget(); + } + } + } + + private void DrawAliasRow(AliasEntry alias, AliasFolder? owningFolder) + { + if (alias.UniqueId == 0) + { + var allAliases = _configuration.Aliases.Concat(_configuration.Folders.SelectMany(f => f.Aliases)); + alias.UniqueId = allAliases.Any() ? allAliases.Max(a => a.UniqueId) + 1 : 1; + } + + if (_renamingAlias == alias) + { + if (_focusRenameAlias) + { + ImGui.SetKeyboardFocusHere(); + _focusRenameAlias = false; + } + + ImGui.SetNextItemWidth(-1); + ImGui.InputText($"###renameAlias{alias.UniqueId}", ref _renameAliasBuffer, 100); + + if (ImGui.IsItemDeactivated()) + { + alias.DisplayName = _renameAliasBuffer.Trim(); + _renamingAlias = null; + _configuration.Save(); + } + + return; + } + + var displayName = string.IsNullOrWhiteSpace(alias.DisplayName) ? alias.Name : alias.DisplayName; + var label = owningFolder == null ? $" {displayName}###{alias.UniqueId}" : $"{displayName}###{alias.UniqueId}"; + var isSelected = _mainWindow.SelectedAlias == alias; + if (ImGui.Selectable(label, isSelected)) + { + _mainWindow.SelectedAlias = alias; + _mainWindow.SelectedFolder = owningFolder; + } + + if (ImGui.BeginPopupContextItem($"###aliasContext{alias.UniqueId}")) + { + if (ImGui.MenuItem("Rename")) + { + _renamingAlias = alias; + _renameAliasBuffer = alias.DisplayName; + _focusRenameAlias = true; + } + + if (!string.IsNullOrWhiteSpace(alias.DisplayName) && ImGui.MenuItem("Clear Display Name")) + { + alias.DisplayName = string.Empty; + _configuration.Save(); + } + + ImGui.EndPopup(); + } + + if (ImGui.BeginDragDropSource()) + { + _draggedAlias = alias; + _draggedFromFolder = owningFolder; + ImGui.SetDragDropPayload(DragDropType, ReadOnlySpan.Empty, 0); + ImGui.Text($"Moving {displayName}..."); + ImGui.EndDragDropSource(); + } + } + + private void MoveAlias(AliasEntry alias, AliasFolder? from, AliasFolder? toFolder) + { + if (from == toFolder) return; + + if (from != null) from.Aliases.Remove(alias); + else _configuration.Aliases.Remove(alias); + + if (toFolder != null) toFolder.Aliases.Add(alias); + else _configuration.Aliases.Add(alias); + + _configuration.Save(); + _draggedAlias = null; + _draggedFromFolder = null; + } + + private void DrawFooter() + { + var available = ImGui.GetContentRegionAvail(); + var buttonCount = 5; + var buttonSize = new Vector2(MathF.Floor(available.X / buttonCount), available.Y); + var canDelete = ImGui.GetIO().KeyShift && ImGui.GetIO().KeyCtrl; + + ImGui.PushStyleVar(ImGuiStyleVar.ItemSpacing, Vector2.Zero); + + if (DrawIconButton(FontAwesomeIcon.Plus, buttonSize, "New Alias")) + { + var newAlias = new AliasEntry(); + _configuration.Aliases.Add(newAlias); + _configuration.Save(); + _mainWindow.SelectedAlias = newAlias; + _mainWindow.SelectedFolder = null; + _renamingAlias = newAlias; + _renameAliasBuffer = string.Empty; + _focusRenameAlias = true; + } + + ImGui.SameLine(); + DrawIconButton(FontAwesomeIcon.FileImport, buttonSize, "Import from Clipboard (Coming Soon)", disabled: true); + + ImGui.SameLine(); + DrawIconButton(FontAwesomeIcon.Clone, buttonSize, "Clone Alias (Coming Soon)", disabled: true); + + ImGui.SameLine(); + if (DrawIconButton(FontAwesomeIcon.FolderPlus, buttonSize, "New Folder")) + { + var newFolder = new AliasFolder { Name = "New Folder" }; + _configuration.Folders.Add(newFolder); + _configuration.Save(); + _renamingFolder = newFolder; + _renameBuffer = string.Empty; + _preRenameName = string.Empty; + _focusRename = true; + } + + ImGui.SameLine(); + if (DrawIconButton(FontAwesomeIcon.Trash, buttonSize, + canDelete ? "Delete Selected" : "Hold Shift + Ctrl to delete", + disabled: !canDelete || _mainWindow.SelectedAlias == null)) + { + if (_mainWindow.SelectedFolder != null) _mainWindow.SelectedFolder.Aliases.Remove(_mainWindow.SelectedAlias!); + else _configuration.Aliases.Remove(_mainWindow.SelectedAlias!); + + _mainWindow.SelectedAlias = null; + _mainWindow.SelectedFolder = null; + _configuration.Save(); + } + + ImGui.PopStyleVar(); + } + + private bool DrawIconButton(FontAwesomeIcon icon, Vector2 size, string tooltip, bool disabled = false) + { + var framePadding = ImGui.GetStyle().FramePadding; + var pX = Math.Max(0, (size.X - ImGui.GetFrameHeight()) / 2f + framePadding.X); + + if (disabled) ImGui.BeginDisabled(); + ImGui.PushStyleVar(ImGuiStyleVar.FramePadding, new Vector2(pX, framePadding.Y)); + ImGui.PushStyleVar(ImGuiStyleVar.FrameRounding, 0f); + var clicked = ImGuiComponents.IconButton((int)icon, icon); + ImGui.PopStyleVar(2); + if (disabled) ImGui.EndDisabled(); + + if (ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled)) ImGui.SetTooltip(tooltip); + return clicked; + } + +} diff --git a/Silkstring/Windows/MainWindow.cs b/Silkstring/Windows/MainWindow.cs index 901a780..bbbea95 100644 --- a/Silkstring/Windows/MainWindow.cs +++ b/Silkstring/Windows/MainWindow.cs @@ -1,22 +1,34 @@ using System; -using System.Linq; using Dalamud.Bindings.ImGui; using Dalamud.Interface.Windowing; using System.Numerics; +using Dalamud.Interface; using Silkstring.Models; +using Silkstring.Ui; namespace Silkstring.Windows; public class MainWindow : Window, IDisposable { - private readonly Configuration configuration; - private readonly EditWindow editWindow; - private readonly ConfigWindow configWindow; - public MainWindow(Plugin plugin, EditWindow editWindow, ConfigWindow configWindow) : base("Silkstring Aliases###Main") + private readonly AliasSelectPanel _selectPanel; + private readonly AliasEditPanel _editPanel; + private readonly Action _openSettings; + + internal AliasEntry? SelectedAlias { get; set; } + internal AliasFolder? SelectedFolder { get; set; } + + public MainWindow(Plugin plugin, Action openSettings) : base("Silkstring###Main") { - configuration = plugin.Configuration; - this.editWindow = editWindow; - this.configWindow = configWindow; + _openSettings = openSettings; + _selectPanel = new AliasSelectPanel(plugin.Configuration, this, openSettings); + _editPanel = new AliasEditPanel(plugin.Configuration, this); + + TitleBarButtons.Add(new TitleBarButton + { + Icon = FontAwesomeIcon.Cog, + Click = _ => openSettings(), + ShowTooltip = () => ImGui.SetTooltip("Settings") + }); } public void Dispose() { } @@ -25,95 +37,22 @@ public override void PreDraw() { SizeConstraints = new WindowSizeConstraints { - MinimumSize = new Vector2(400, 200), + MinimumSize = new Vector2(600, 300), MaximumSize = new Vector2(float.MaxValue, float.MaxValue) }; } public override void Draw() { - ImGui.Columns(4); - var size = ImGui.GetIO().FontGlobalScale; - ImGui.SetColumnWidth(0, 60 * size); - ImGui.SetColumnWidth(1, 200 * size); - ImGui.SetColumnWidth(2, 40 * size); - ImGui.SetColumnWidth(3, 55 * size); - - ImGui.Text("Enabled"); - ImGui.NextColumn(); - - ImGui.Text("Command"); - if (ImGui.IsItemHovered()) - ImGui.SetTooltip("Separate multiple aliases with | e.g. hello|hi|hey"); - ImGui.NextColumn(); - - ImGui.NextColumn(); - ImGui.NextColumn(); - - foreach (var alias in configuration.Aliases) - { - if (alias.UniqueId == 0) - { - alias.UniqueId = configuration.Aliases.Max(a => a.UniqueId) + 1; - } - - ImGui.SetCursorPosX(ImGui.GetCursorPosX() + (ImGui.GetColumnWidth() / 2) - (16 * size)); - if (ImGui.Checkbox($"###toggle{alias.UniqueId}", ref alias.Enabled)) - { - configuration.Save(); - } - ImGui.NextColumn(); + var scale= ImGui.GetIO().FontGlobalScale; + var leftWidth = new Vector2(250 * scale, 0); - ImGui.SetNextItemWidth(-1); - if (ImGui.InputText("###name" + alias.UniqueId, ref alias.Name, 100)) - { - configuration.Save(); - } - ImGui.NextColumn(); + if (ImGui.BeginChild("###selector", leftWidth, true, ImGuiWindowFlags.NoScrollbar | ImGuiWindowFlags.NoScrollWithMouse)) _selectPanel.Draw(); + ImGui.EndChild(); - if (ImGui.Button("Edit###edit" + alias.UniqueId)) - { - editWindow.Open(alias); - } - ImGui.NextColumn(); + ImGui.SameLine(); - var canDelete = ImGui.GetIO().KeyShift && ImGui.GetIO().KeyCtrl; - ImGui.BeginDisabled(!canDelete); - if (ImGui.Button("Delete###delete" + alias.UniqueId)) alias.Delete = true; - ImGui.EndDisabled(); - if (ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled)) ImGui.SetTooltip("Hold Shift + Ctrl to delete"); - ImGui.NextColumn(); - } - - ImGui.Columns(1); - - if (configuration.Aliases.RemoveAll(a => a.Delete) > 0) - { - configuration.Save(); - } - - ImGui.Separator(); - if (ImGui.Button("Add Alias")) - { - configuration.Aliases.Add(new AliasEntry()); - configuration.Save(); - } - - var availableHeight = ImGui.GetContentRegionAvail().Y; - var buttonHeight = ImGui.GetFrameHeight() + ImGui.GetStyle().ItemSpacing.Y + ImGui.GetStyle().WindowPadding.Y * 2 + 4; - ImGui.SetCursorPosY(ImGui.GetCursorPosY() + availableHeight - buttonHeight); - ImGui.Separator(); - ImGui.SetCursorPosY(ImGui.GetCursorPosY() + 25); - if (ImGui.Button("Settings")) - { - configWindow.Open(); - } - - var closeButtonWidth = ImGui.CalcTextSize("Close").X + ImGui.GetStyle().FramePadding.X * 2; - ImGui.SameLine(ImGui.GetWindowWidth() - closeButtonWidth - ImGui.GetStyle().ItemSpacing.X - ImGui.GetStyle().WindowPadding.X - 16); - if (ImGui.Button("Close")) - { - IsOpen = false; - } + if (ImGui.BeginChild("###editor", new Vector2(0, 0), true)) _editPanel.Draw(); + ImGui.EndChild(); } } From 1976285df3b46d34091b75c90ab0026a1aca5133 Mon Sep 17 00:00:00 2001 From: devoreofox <232652342+devoreofox@users.noreply.github.com> Date: Sat, 16 May 2026 22:28:39 -0300 Subject: [PATCH 2/4] Add export to/import from clipboard functionality --- Silkstring/UI/AliasSelectPanel.cs | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/Silkstring/UI/AliasSelectPanel.cs b/Silkstring/UI/AliasSelectPanel.cs index e96aec9..5584005 100644 --- a/Silkstring/UI/AliasSelectPanel.cs +++ b/Silkstring/UI/AliasSelectPanel.cs @@ -1,6 +1,7 @@ using System; using System.Linq; using System.Numerics; +using System.Text.Json; using Dalamud.Bindings.ImGui; using Dalamud.Interface; using Dalamud.Interface.Components; @@ -231,6 +232,12 @@ private void DrawAliasRow(AliasEntry alias, AliasFolder? owningFolder) _configuration.Save(); } + if (ImGui.MenuItem("Export to Clipboard")) + { + var json = JsonSerializer.Serialize(alias, new JsonSerializerOptions { IncludeFields = true }); + ImGui.SetClipboardText(json); + } + ImGui.EndPopup(); } @@ -281,7 +288,25 @@ private void DrawFooter() } ImGui.SameLine(); - DrawIconButton(FontAwesomeIcon.FileImport, buttonSize, "Import from Clipboard (Coming Soon)", disabled: true); + if (DrawIconButton(FontAwesomeIcon.FileImport, buttonSize, "Import from Clipboard")) + { + try + { + var json = ImGui.GetClipboardText(); + var imported = JsonSerializer.Deserialize(json, new JsonSerializerOptions { IncludeFields = true }); + if (imported != null) + { + imported.UniqueId = 0; + _configuration.Aliases.Add(imported); + _configuration.Save(); + _mainWindow.SelectedAlias = imported; + _renamingAlias = imported; + _renameAliasBuffer = imported.DisplayName; + _focusRenameAlias = true; + } + } + catch {} + } ImGui.SameLine(); DrawIconButton(FontAwesomeIcon.Clone, buttonSize, "Clone Alias (Coming Soon)", disabled: true); From 9d751dbf8e40012f2efc7f1de8081505af1b2ba0 Mon Sep 17 00:00:00 2001 From: devoreofox <232652342+devoreofox@users.noreply.github.com> Date: Sat, 16 May 2026 22:37:31 -0300 Subject: [PATCH 3/4] Added clone button functionality --- Silkstring/UI/AliasSelectPanel.cs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/Silkstring/UI/AliasSelectPanel.cs b/Silkstring/UI/AliasSelectPanel.cs index 5584005..b9e594a 100644 --- a/Silkstring/UI/AliasSelectPanel.cs +++ b/Silkstring/UI/AliasSelectPanel.cs @@ -309,7 +309,21 @@ private void DrawFooter() } ImGui.SameLine(); - DrawIconButton(FontAwesomeIcon.Clone, buttonSize, "Clone Alias (Coming Soon)", disabled: true); + if(DrawIconButton(FontAwesomeIcon.Clone, buttonSize, "Clone Alias", disabled: _mainWindow.SelectedAlias == null)) + { + var source = _mainWindow.SelectedAlias!; + var cloned = source.Clone(); + cloned.UniqueId = 0; + + if (_mainWindow.SelectedFolder != null) _mainWindow.SelectedFolder.Aliases.Add(cloned); + else _configuration.Aliases.Add(cloned); + + _configuration.Save(); + _mainWindow.SelectedAlias = cloned; + _renamingAlias = cloned; + _renameAliasBuffer = cloned.DisplayName; + _focusRenameAlias = true; + } ImGui.SameLine(); if (DrawIconButton(FontAwesomeIcon.FolderPlus, buttonSize, "New Folder")) From d9131d49bcd958f1fee21ef0681080bcb8867a16 Mon Sep 17 00:00:00 2001 From: devoreofox <232652342+devoreofox@users.noreply.github.com> Date: Sat, 16 May 2026 22:58:00 -0300 Subject: [PATCH 4/4] Added DisplayName to alias clone function --- Silkstring/Models/AliasEntry.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Silkstring/Models/AliasEntry.cs b/Silkstring/Models/AliasEntry.cs index 875967a..a4abae8 100644 --- a/Silkstring/Models/AliasEntry.cs +++ b/Silkstring/Models/AliasEntry.cs @@ -39,6 +39,7 @@ public AliasEntry Clone() { return new AliasEntry { + DisplayName = DisplayName, Enabled = Enabled, Name = Name, Output = Output.Select(c => c.Clone()).ToList()