Improved messaging.

This commit is contained in:
Ottermandias 2023-10-05 18:20:41 +02:00
parent 19c4c3b50e
commit 779d6b37a5
30 changed files with 146 additions and 139 deletions

@ -1 +1 @@
Subproject commit df07c4ed08e8e6c1188867c7863a19e02c8adb53 Subproject commit 96c9055a1d8a19d9cdb61f41ddfb372871e204ac

@ -1 +1 @@
Subproject commit bc9bd5f6bb06e61069704733841868307aed7a5c Subproject commit 839cc8f270abb6a938d71596bef05b4a9b3ab0ea

View file

@ -1,6 +1,7 @@
using Dalamud.Interface.Internal.Notifications; using Dalamud.Interface.Internal.Notifications;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using OtterGui.Classes;
using Penumbra.Services; using Penumbra.Services;
namespace Penumbra.Collections.Manager; namespace Penumbra.Collections.Manager;
@ -46,10 +47,8 @@ public static class ActiveCollectionMigration
{ {
if (!storage.ByName(collectionName, out var collection)) if (!storage.ByName(collectionName, out var collection))
{ {
Penumbra.Chat.NotificationMessage( Penumbra.Messager.NotificationMessage(
$"Last choice of <{player}>'s Collection {collectionName} is not available, reset to {ModCollection.Empty.Name}.", $"Last choice of <{player}>'s Collection {collectionName} is not available, reset to {ModCollection.Empty.Name}.", NotificationType.Warning);
"Load Failure",
NotificationType.Warning);
dict.Add(player, ModCollection.Empty); dict.Add(player, ModCollection.Empty);
} }
else else

View file

@ -2,6 +2,7 @@ using Dalamud.Interface.Internal.Notifications;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using OtterGui; using OtterGui;
using OtterGui.Classes;
using Penumbra.Communication; using Penumbra.Communication;
using Penumbra.GameData.Actors; using Penumbra.GameData.Actors;
using Penumbra.GameData.Enums; using Penumbra.GameData.Enums;
@ -331,10 +332,8 @@ public class ActiveCollections : ISavable, IDisposable
?? (configChanged ? ModCollection.DefaultCollectionName : ModCollection.Empty.Name); ?? (configChanged ? ModCollection.DefaultCollectionName : ModCollection.Empty.Name);
if (!_storage.ByName(defaultName, out var defaultCollection)) if (!_storage.ByName(defaultName, out var defaultCollection))
{ {
Penumbra.Chat.NotificationMessage( Penumbra.Messager.NotificationMessage(
$"Last choice of {TutorialService.DefaultCollection} {defaultName} is not available, reset to {ModCollection.Empty.Name}.", $"Last choice of {TutorialService.DefaultCollection} {defaultName} is not available, reset to {ModCollection.Empty.Name}.", NotificationType.Warning);
"Load Failure",
NotificationType.Warning);
Default = ModCollection.Empty; Default = ModCollection.Empty;
configChanged = true; configChanged = true;
} }
@ -347,9 +346,8 @@ public class ActiveCollections : ISavable, IDisposable
var interfaceName = jObject[nameof(Interface)]?.ToObject<string>() ?? Default.Name; var interfaceName = jObject[nameof(Interface)]?.ToObject<string>() ?? Default.Name;
if (!_storage.ByName(interfaceName, out var interfaceCollection)) if (!_storage.ByName(interfaceName, out var interfaceCollection))
{ {
Penumbra.Chat.NotificationMessage( Penumbra.Messager.NotificationMessage(
$"Last choice of {TutorialService.InterfaceCollection} {interfaceName} is not available, reset to {ModCollection.Empty.Name}.", $"Last choice of {TutorialService.InterfaceCollection} {interfaceName} is not available, reset to {ModCollection.Empty.Name}.", NotificationType.Warning);
"Load Failure", NotificationType.Warning);
Interface = ModCollection.Empty; Interface = ModCollection.Empty;
configChanged = true; configChanged = true;
} }
@ -362,9 +360,8 @@ public class ActiveCollections : ISavable, IDisposable
var currentName = jObject[nameof(Current)]?.ToObject<string>() ?? Default.Name; var currentName = jObject[nameof(Current)]?.ToObject<string>() ?? Default.Name;
if (!_storage.ByName(currentName, out var currentCollection)) if (!_storage.ByName(currentName, out var currentCollection))
{ {
Penumbra.Chat.NotificationMessage( Penumbra.Messager.NotificationMessage(
$"Last choice of {TutorialService.SelectedCollection} {currentName} is not available, reset to {ModCollection.DefaultCollectionName}.", $"Last choice of {TutorialService.SelectedCollection} {currentName} is not available, reset to {ModCollection.DefaultCollectionName}.", NotificationType.Warning);
"Load Failure", NotificationType.Warning);
Current = _storage.DefaultNamed; Current = _storage.DefaultNamed;
configChanged = true; configChanged = true;
} }
@ -381,8 +378,7 @@ public class ActiveCollections : ISavable, IDisposable
{ {
if (!_storage.ByName(typeName, out var typeCollection)) if (!_storage.ByName(typeName, out var typeCollection))
{ {
Penumbra.Chat.NotificationMessage($"Last choice of {name} Collection {typeName} is not available, removed.", Penumbra.Messager.NotificationMessage($"Last choice of {name} Collection {typeName} is not available, removed.",
"Load Failure",
NotificationType.Warning); NotificationType.Warning);
configChanged = true; configChanged = true;
} }

View file

@ -1,5 +1,6 @@
using Dalamud.Interface.Internal.Notifications; using Dalamud.Interface.Internal.Notifications;
using OtterGui; using OtterGui;
using OtterGui.Classes;
using OtterGui.Filesystem; using OtterGui.Filesystem;
using Penumbra.Communication; using Penumbra.Communication;
using Penumbra.Mods; using Penumbra.Mods;
@ -102,9 +103,8 @@ public class CollectionStorage : IReadOnlyList<ModCollection>, IDisposable
{ {
if (!CanAddCollection(name, out var fixedName)) if (!CanAddCollection(name, out var fixedName))
{ {
Penumbra.Chat.NotificationMessage( Penumbra.Messager.NotificationMessage(
$"The new collection {name} would lead to the same path {fixedName} as one that already exists.", "Warning", $"The new collection {name} would lead to the same path {fixedName} as one that already exists.", NotificationType.Warning, false);
NotificationType.Warning);
return false; return false;
} }
@ -113,8 +113,7 @@ public class CollectionStorage : IReadOnlyList<ModCollection>, IDisposable
_collections.Add(newCollection); _collections.Add(newCollection);
_saveService.ImmediateSave(new ModCollectionSave(_modStorage, newCollection)); _saveService.ImmediateSave(new ModCollectionSave(_modStorage, newCollection));
Penumbra.Chat.NotificationMessage($"Created new collection {newCollection.AnonymizedName}.", "Success", Penumbra.Messager.NotificationMessage($"Created new collection {newCollection.AnonymizedName}.", NotificationType.Success, false);
NotificationType.Success);
_communicator.CollectionChange.Invoke(CollectionType.Inactive, null, newCollection, string.Empty); _communicator.CollectionChange.Invoke(CollectionType.Inactive, null, newCollection, string.Empty);
return true; return true;
} }
@ -126,13 +125,13 @@ public class CollectionStorage : IReadOnlyList<ModCollection>, IDisposable
{ {
if (collection.Index <= ModCollection.Empty.Index || collection.Index >= _collections.Count) if (collection.Index <= ModCollection.Empty.Index || collection.Index >= _collections.Count)
{ {
Penumbra.Chat.NotificationMessage("Can not remove the empty collection.", "Error", NotificationType.Error); Penumbra.Messager.NotificationMessage("Can not remove the empty collection.", NotificationType.Error, false);
return false; return false;
} }
if (collection.Index == DefaultNamed.Index) if (collection.Index == DefaultNamed.Index)
{ {
Penumbra.Chat.NotificationMessage("Can not remove the default collection.", "Error", NotificationType.Error); Penumbra.Messager.NotificationMessage("Can not remove the default collection.", NotificationType.Error, false);
return false; return false;
} }
@ -142,7 +141,7 @@ public class CollectionStorage : IReadOnlyList<ModCollection>, IDisposable
for (var i = collection.Index; i < Count; ++i) for (var i = collection.Index; i < Count; ++i)
_collections[i].Index = i; _collections[i].Index = i;
Penumbra.Chat.NotificationMessage($"Deleted collection {collection.AnonymizedName}.", "Success", NotificationType.Success); Penumbra.Messager.NotificationMessage($"Deleted collection {collection.AnonymizedName}.", NotificationType.Success, false);
_communicator.CollectionChange.Invoke(CollectionType.Inactive, collection, null, string.Empty); _communicator.CollectionChange.Invoke(CollectionType.Inactive, collection, null, string.Empty);
return true; return true;
} }
@ -185,23 +184,20 @@ public class CollectionStorage : IReadOnlyList<ModCollection>, IDisposable
if (!IsValidName(name)) if (!IsValidName(name))
{ {
// TODO: handle better. // TODO: handle better.
Penumbra.Chat.NotificationMessage($"Collection of unsupported name found: {name} is not a valid collection name.", Penumbra.Messager.NotificationMessage($"Collection of unsupported name found: {name} is not a valid collection name.", NotificationType.Warning);
"Warning", NotificationType.Warning);
continue; continue;
} }
if (ByName(name, out _)) if (ByName(name, out _))
{ {
Penumbra.Chat.NotificationMessage($"Duplicate collection found: {name} already exists. Import skipped.", Penumbra.Messager.NotificationMessage($"Duplicate collection found: {name} already exists. Import skipped.", NotificationType.Warning);
"Warning", NotificationType.Warning);
continue; continue;
} }
var collection = ModCollection.CreateFromData(_saveService, _modStorage, name, version, Count, settings, inheritance); var collection = ModCollection.CreateFromData(_saveService, _modStorage, name, version, Count, settings, inheritance);
var correctName = _saveService.FileNames.CollectionFile(collection); var correctName = _saveService.FileNames.CollectionFile(collection);
if (file.FullName != correctName) if (file.FullName != correctName)
Penumbra.Chat.NotificationMessage($"Collection {file.Name} does not correspond to {collection.Name}.", "Warning", Penumbra.Messager.NotificationMessage($"Collection {file.Name} does not correspond to {collection.Name}.", NotificationType.Warning);
NotificationType.Warning);
_collections.Add(collection); _collections.Add(collection);
} }
@ -221,9 +217,8 @@ public class CollectionStorage : IReadOnlyList<ModCollection>, IDisposable
if (AddCollection(ModCollection.DefaultCollectionName, null)) if (AddCollection(ModCollection.DefaultCollectionName, null))
return _collections[^1]; return _collections[^1];
Penumbra.Chat.NotificationMessage( Penumbra.Messager.NotificationMessage(
$"Unknown problem creating a collection with the name {ModCollection.DefaultCollectionName}, which is required to exist.", "Error", $"Unknown problem creating a collection with the name {ModCollection.DefaultCollectionName}, which is required to exist.", NotificationType.Error);
NotificationType.Error);
return Count > 1 ? _collections[1] : _collections[0]; return Count > 1 ? _collections[1] : _collections[0];
} }

View file

@ -1,6 +1,7 @@
using Dalamud.Game.ClientState.Objects.Enums; using Dalamud.Game.ClientState.Objects.Enums;
using Dalamud.Interface.Internal.Notifications; using Dalamud.Interface.Internal.Notifications;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using OtterGui.Classes;
using Penumbra.GameData.Actors; using Penumbra.GameData.Actors;
using Penumbra.Services; using Penumbra.Services;
using Penumbra.String; using Penumbra.String;
@ -56,7 +57,7 @@ public partial class IndividualCollections
if (group.Length == 0 || group.Any(i => !i.IsValid)) if (group.Length == 0 || group.Any(i => !i.IsValid))
{ {
changes = true; changes = true;
Penumbra.Chat.NotificationMessage("Could not load an unknown individual collection, removed.", "Load Failure", Penumbra.Messager.NotificationMessage("Could not load an unknown individual collection, removed.",
NotificationType.Warning); NotificationType.Warning);
continue; continue;
} }
@ -65,9 +66,8 @@ public partial class IndividualCollections
if (collectionName.Length == 0 || !storage.ByName(collectionName, out var collection)) if (collectionName.Length == 0 || !storage.ByName(collectionName, out var collection))
{ {
changes = true; changes = true;
Penumbra.Chat.NotificationMessage( Penumbra.Messager.NotificationMessage(
$"Could not load the collection \"{collectionName}\" as individual collection for {identifier}, set to None.", $"Could not load the collection \"{collectionName}\" as individual collection for {identifier}, set to None.",
"Load Failure",
NotificationType.Warning); NotificationType.Warning);
continue; continue;
} }
@ -75,16 +75,14 @@ public partial class IndividualCollections
if (!Add(group, collection)) if (!Add(group, collection))
{ {
changes = true; changes = true;
Penumbra.Chat.NotificationMessage($"Could not add an individual collection for {identifier}, removed.", Penumbra.Messager.NotificationMessage($"Could not add an individual collection for {identifier}, removed.",
"Load Failure",
NotificationType.Warning); NotificationType.Warning);
} }
} }
catch (Exception e) catch (Exception e)
{ {
changes = true; changes = true;
Penumbra.Chat.NotificationMessage($"Could not load an unknown individual collection, removed:\n{e}", "Load Failure", Penumbra.Messager.NotificationMessage(e, $"Could not load an unknown individual collection, removed.", NotificationType.Error);
NotificationType.Error);
} }
} }
@ -124,9 +122,9 @@ public partial class IndividualCollections
if (Add($"{_actorService.AwaitedService.Data.ToName(kind, dataId)} ({kind.ToName()})", group, collection)) if (Add($"{_actorService.AwaitedService.Data.ToName(kind, dataId)} ({kind.ToName()})", group, collection))
Penumbra.Log.Information($"Migrated {name} ({kind.ToName()}) to NPC Identifiers [{ids}]."); Penumbra.Log.Information($"Migrated {name} ({kind.ToName()}) to NPC Identifiers [{ids}].");
else else
Penumbra.Chat.NotificationMessage( Penumbra.Messager.NotificationMessage(
$"Could not migrate {name} ({collection.AnonymizedName}) which was assumed to be a {kind.ToName()} with IDs [{ids}], please look through your individual collections.", $"Could not migrate {name} ({collection.AnonymizedName}) which was assumed to be a {kind.ToName()} with IDs [{ids}], please look through your individual collections.",
"Migration Failure", NotificationType.Error); NotificationType.Error);
} }
// If it is not a valid NPC name, check if it can be a player name. // If it is not a valid NPC name, check if it can be a player name.
else if (ActorManager.VerifyPlayerName(name)) else if (ActorManager.VerifyPlayerName(name))
@ -140,15 +138,15 @@ public partial class IndividualCollections
}, collection)) }, collection))
Penumbra.Log.Information($"Migrated {shortName} ({collection.AnonymizedName}) to Player Identifier."); Penumbra.Log.Information($"Migrated {shortName} ({collection.AnonymizedName}) to Player Identifier.");
else else
Penumbra.Chat.NotificationMessage( Penumbra.Messager.NotificationMessage(
$"Could not migrate {shortName} ({collection.AnonymizedName}), please look through your individual collections.", $"Could not migrate {shortName} ({collection.AnonymizedName}), please look through your individual collections.",
"Migration Failure", NotificationType.Error); NotificationType.Error);
} }
else else
{ {
Penumbra.Chat.NotificationMessage( Penumbra.Messager.NotificationMessage(
$"Could not migrate {name} ({collection.AnonymizedName}), which can not be a player name nor is it a known NPC name, please look through your individual collections.", $"Could not migrate {name} ({collection.AnonymizedName}), which can not be a player name nor is it a known NPC name, please look through your individual collections.",
"Migration Failure", NotificationType.Error); NotificationType.Error);
} }
} }
} }

View file

@ -1,5 +1,6 @@
using Dalamud.Interface.Internal.Notifications; using Dalamud.Interface.Internal.Notifications;
using OtterGui; using OtterGui;
using OtterGui.Classes;
using OtterGui.Filesystem; using OtterGui.Filesystem;
using Penumbra.Communication; using Penumbra.Communication;
using Penumbra.Mods.Manager; using Penumbra.Mods.Manager;
@ -143,14 +144,12 @@ public class InheritanceManager : IDisposable
continue; continue;
changes = true; changes = true;
Penumbra.Chat.NotificationMessage($"{collection.Name} can not inherit from {subCollection.Name}, removed.", "Warning", Penumbra.Messager.NotificationMessage($"{collection.Name} can not inherit from {subCollection.Name}, removed.", NotificationType.Warning);
NotificationType.Warning);
} }
else else
{ {
Penumbra.Chat.NotificationMessage( Penumbra.Messager.NotificationMessage(
$"Inherited collection {subCollectionName} for {collection.AnonymizedName} does not exist, it was removed.", "Warning", $"Inherited collection {subCollectionName} for {collection.AnonymizedName} does not exist, it was removed.", NotificationType.Warning);
NotificationType.Warning);
changes = true; changes = true;
} }
} }

View file

@ -137,9 +137,9 @@ public class Configuration : IPluginConfiguration, ISavable
} }
catch (Exception ex) catch (Exception ex)
{ {
Penumbra.Chat.NotificationMessage(ex, Penumbra.Messager.NotificationMessage(ex,
"Error reading Configuration, reverting to default.\nYou may be able to restore your configuration using the rolling backups in the XIVLauncher/backups/Penumbra directory.", "Error reading Configuration, reverting to default.\nYou may be able to restore your configuration using the rolling backups in the XIVLauncher/backups/Penumbra directory.",
"Error reading Configuration", "Error", NotificationType.Error); "Error reading Configuration", NotificationType.Error);
} }
migrator.Migrate(utility, this); migrator.Migrate(utility, this);

View file

@ -1,6 +1,7 @@
using Dalamud.Interface.Internal.Notifications; using Dalamud.Interface.Internal.Notifications;
using Dalamud.Utility; using Dalamud.Utility;
using OtterGui; using OtterGui;
using OtterGui.Classes;
using Penumbra.Api.Enums; using Penumbra.Api.Enums;
using Penumbra.Communication; using Penumbra.Communication;
using Penumbra.Meta.Manipulations; using Penumbra.Meta.Manipulations;
@ -81,9 +82,7 @@ public class ModMerger : IDisposable
catch (Exception ex) catch (Exception ex)
{ {
Error = ex; Error = ex;
Penumbra.Chat.NotificationMessage( Penumbra.Messager.NotificationMessage(ex, $"Could not merge {MergeFromMod!.Name} into {MergeToMod!.Name}, cleaning up changes.", NotificationType.Error, false);
$"Could not merge {MergeFromMod!.Name} into {MergeToMod!.Name}, cleaning up changes.:\n{ex}", "Failure",
NotificationType.Error);
FailureCleanup(); FailureCleanup();
DataCleanup(); DataCleanup();
} }

View file

@ -1,5 +1,6 @@
using Dalamud.Interface.Internal.Notifications; using Dalamud.Interface.Internal.Notifications;
using OtterGui; using OtterGui;
using OtterGui.Classes;
using OtterGui.Tasks; using OtterGui.Tasks;
using Penumbra.Mods.Manager; using Penumbra.Mods.Manager;
using Penumbra.Mods.Subclasses; using Penumbra.Mods.Subclasses;
@ -74,7 +75,7 @@ public class ModNormalizer
} }
catch (Exception e) catch (Exception e)
{ {
Penumbra.Chat.NotificationMessage($"Could not normalize mod:\n{e}", "Failure", NotificationType.Error); Penumbra.Messager.NotificationMessage(e, $"Could not normalize mod {Mod.Name}.", NotificationType.Error, false);
} }
finally finally
{ {
@ -87,17 +88,15 @@ public class ModNormalizer
{ {
if (Directory.Exists(_normalizationDirName)) if (Directory.Exists(_normalizationDirName))
{ {
Penumbra.Chat.NotificationMessage("Could not normalize mod:\n" Penumbra.Messager.NotificationMessage($"Could not normalize mod {Mod.Name}:\n"
+ "The directory TmpNormalization may not already exist when normalizing a mod.", "Failure", + "The directory TmpNormalization may not already exist when normalizing a mod.", NotificationType.Error, false);
NotificationType.Error);
return false; return false;
} }
if (Directory.Exists(_oldDirName)) if (Directory.Exists(_oldDirName))
{ {
Penumbra.Chat.NotificationMessage("Could not normalize mod:\n" Penumbra.Messager.NotificationMessage($"Could not normalize mod {Mod.Name}:\n"
+ "The directory TmpNormalizationOld may not already exist when normalizing a mod.", "Failure", + "The directory TmpNormalizationOld may not already exist when normalizing a mod.", NotificationType.Error, false);
NotificationType.Error);
return false; return false;
} }
@ -201,7 +200,7 @@ public class ModNormalizer
} }
catch (Exception e) catch (Exception e)
{ {
Penumbra.Chat.NotificationMessage($"Could not normalize mod:\n{e}", "Failure", NotificationType.Error); Penumbra.Messager.NotificationMessage(e, $"Could not normalize mod {Mod.Name}.", NotificationType.Error, false);
} }
return false; return false;
@ -229,8 +228,7 @@ public class ModNormalizer
} }
catch (Exception e) catch (Exception e)
{ {
Penumbra.Chat.NotificationMessage($"Could not move old files out of the way while normalizing mod mod:\n{e}", "Failure", Penumbra.Messager.NotificationMessage(e, $"Could not move old files out of the way while normalizing mod {Mod.Name}.", NotificationType.Error, false);
NotificationType.Error);
} }
return false; return false;
@ -253,8 +251,7 @@ public class ModNormalizer
} }
catch (Exception e) catch (Exception e)
{ {
Penumbra.Chat.NotificationMessage($"Could not move new files into the mod while normalizing mod mod:\n{e}", "Failure", Penumbra.Messager.NotificationMessage(e, $"Could not move new files into the mod while normalizing mod {Mod.Name}.", NotificationType.Error, false);
NotificationType.Error);
foreach (var dir in Mod.ModPath.EnumerateDirectories()) foreach (var dir in Mod.ModPath.EnumerateDirectories())
{ {
if (dir.FullName.Equals(_oldDirName, StringComparison.OrdinalIgnoreCase) if (dir.FullName.Equals(_oldDirName, StringComparison.OrdinalIgnoreCase)

View file

@ -1,4 +1,5 @@
using Dalamud.Interface.Internal.Notifications; using Dalamud.Interface.Internal.Notifications;
using OtterGui.Classes;
using Penumbra.Import; using Penumbra.Import;
using Penumbra.Mods.Editor; using Penumbra.Mods.Editor;
@ -42,8 +43,7 @@ public class ModImportManager : IDisposable
if (File.Exists(s)) if (File.Exists(s))
return true; return true;
Penumbra.Chat.NotificationMessage($"Failed to import queued mod at {s}, the file does not exist.", "Warning", Penumbra.Messager.NotificationMessage($"Failed to import queued mod at {s}, the file does not exist.", NotificationType.Warning, false);
NotificationType.Warning);
return false; return false;
}).Select(s => new FileInfo(s)).ToArray(); }).Select(s => new FileInfo(s)).ToArray();

View file

@ -1,5 +1,6 @@
using Dalamud.Interface.Internal.Notifications; using Dalamud.Interface.Internal.Notifications;
using OtterGui; using OtterGui;
using OtterGui.Classes;
using OtterGui.Filesystem; using OtterGui.Filesystem;
using Penumbra.Api.Enums; using Penumbra.Api.Enums;
using Penumbra.Meta.Manipulations; using Penumbra.Meta.Manipulations;
@ -395,9 +396,9 @@ public class ModOptionEditor
return true; return true;
if (message) if (message)
Penumbra.Chat.NotificationMessage( Penumbra.Messager.NotificationMessage(
$"Could not name option {newName} because option with same filename {path} already exists.", $"Could not name option {newName} because option with same filename {path} already exists.",
"Warning", NotificationType.Warning); NotificationType.Warning, false);
return false; return false;
} }

View file

@ -2,6 +2,7 @@ using Dalamud.Interface.Internal.Notifications;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using OtterGui; using OtterGui;
using OtterGui.Classes;
using OtterGui.Filesystem; using OtterGui.Filesystem;
using Penumbra.Api.Enums; using Penumbra.Api.Enums;
using Penumbra.GameData; using Penumbra.GameData;
@ -45,8 +46,7 @@ public partial class ModCreator
} }
catch (Exception e) catch (Exception e)
{ {
Penumbra.Chat.NotificationMessage($"Could not create directory for new Mod {newName}:\n{e}", "Failure", Penumbra.Messager.NotificationMessage(e, $"Could not create directory for new Mod {newName}.", NotificationType.Error, false);
NotificationType.Error);
return null; return null;
} }
} }

View file

@ -2,6 +2,7 @@ using Dalamud.Interface.Internal.Notifications;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using OtterGui; using OtterGui;
using OtterGui.Classes;
using OtterGui.Filesystem; using OtterGui.Filesystem;
using Penumbra.Api.Enums; using Penumbra.Api.Enums;
@ -54,9 +55,8 @@ public sealed class MultiModGroup : IModGroup
{ {
if (ret.PrioritizedOptions.Count == IModGroup.MaxMultiOptions) if (ret.PrioritizedOptions.Count == IModGroup.MaxMultiOptions)
{ {
Penumbra.Chat.NotificationMessage( Penumbra.Messager.NotificationMessage(
$"Multi Group {ret.Name} has more than {IModGroup.MaxMultiOptions} options, ignoring excessive options.", "Warning", $"Multi Group {ret.Name} in {mod.Name} has more than {IModGroup.MaxMultiOptions} options, ignoring excessive options.", NotificationType.Warning);
NotificationType.Warning);
break; break;
} }

View file

@ -31,7 +31,7 @@ public class Penumbra : IDalamudPlugin
=> "Penumbra"; => "Penumbra";
public static readonly Logger Log = new(); public static readonly Logger Log = new();
public static ChatService Chat { get; private set; } = null!; public static MessageService Messager { get; private set; } = null!;
private readonly ValidityChecker _validityChecker; private readonly ValidityChecker _validityChecker;
private readonly ResidentResourceManager _residentResources; private readonly ResidentResourceManager _residentResources;
@ -55,7 +55,7 @@ public class Penumbra : IDalamudPlugin
var startTimer = new StartTracker(); var startTimer = new StartTracker();
using var timer = startTimer.Measure(StartTimeType.Total); using var timer = startTimer.Measure(StartTimeType.Total);
_services = ServiceManager.CreateProvider(this, pluginInterface, Log, startTimer); _services = ServiceManager.CreateProvider(this, pluginInterface, Log, startTimer);
Chat = _services.GetRequiredService<ChatService>(); Messager = _services.GetRequiredService<MessageService>();
_validityChecker = _services.GetRequiredService<ValidityChecker>(); _validityChecker = _services.GetRequiredService<ValidityChecker>();
var startup = _services.GetRequiredService<DalamudServices>().GetDalamudConfig(DalamudServices.WaitingForPluginsOption, out bool s) var startup = _services.GetRequiredService<DalamudServices>().GetDalamudConfig(DalamudServices.WaitingForPluginsOption, out bool s)
? s.ToString() ? s.ToString()
@ -118,7 +118,7 @@ public class Penumbra : IDalamudPlugin
_communicatorService.ChangedItemClick.Subscribe((button, it) => _communicatorService.ChangedItemClick.Subscribe((button, it) =>
{ {
if (button == MouseButton.Left && it is Item item) if (button == MouseButton.Left && it is Item item)
Chat.LinkItem(item); Messager.LinkItem(item);
}, ChangedItemClick.Priority.Link); }, ChangedItemClick.Priority.Link);
} }

View file

@ -41,7 +41,7 @@ public class BackupService
{ {
Penumbra.Log.Error($"Failed to load {fileName}, trying to restore from backup:\n{ex}"); Penumbra.Log.Error($"Failed to load {fileName}, trying to restore from backup:\n{ex}");
Backup.TryGetFile(new DirectoryInfo(fileNames.ConfigDirectory), fileName, out ret, out var messages, JObject.Parse); Backup.TryGetFile(new DirectoryInfo(fileNames.ConfigDirectory), fileName, out ret, out var messages, JObject.Parse);
Penumbra.Chat.NotificationMessage(messages); Penumbra.Messager.NotificationMessage(messages);
} }
return ret; return ret;

View file

@ -1,20 +1,18 @@
using Dalamud.Game.Text; using Dalamud.Game.Text;
using Dalamud.Game.Text.SeStringHandling; using Dalamud.Game.Text.SeStringHandling;
using Dalamud.Game.Text.SeStringHandling.Payloads; using Dalamud.Game.Text.SeStringHandling.Payloads;
using Dalamud.Plugin; using Dalamud.Interface;
using Dalamud.Plugin.Services; using Dalamud.Plugin.Services;
using Lumina.Excel.GeneratedSheets; using Lumina.Excel.GeneratedSheets;
using OtterGui.Log; using OtterGui.Log;
namespace Penumbra.Services; namespace Penumbra.Services;
public class ChatService : OtterGui.Classes.ChatService public class MessageService : OtterGui.Classes.MessageService
{ {
private readonly IChatGui _chat; public MessageService(Logger log, UiBuilder uiBuilder, IChatGui chat)
: base(log, uiBuilder, chat)
public ChatService(Logger log, DalamudPluginInterface pi, IChatGui chat) { }
: base(log, pi)
=> _chat = chat;
public void LinkItem(Item item) public void LinkItem(Item item)
{ {
@ -37,7 +35,7 @@ public class ChatService : OtterGui.Classes.ChatService
var payload = new SeString(payloadList); var payload = new SeString(payloadList);
_chat.Print(new XivChatEntry Chat.Print(new XivChatEntry
{ {
Message = payload, Message = payload,
}); });

View file

@ -65,7 +65,7 @@ public static class ServiceManager
.AddSingleton<FilenameService>() .AddSingleton<FilenameService>()
.AddSingleton<BackupService>() .AddSingleton<BackupService>()
.AddSingleton<CommunicatorService>() .AddSingleton<CommunicatorService>()
.AddSingleton<ChatService>() .AddSingleton<MessageService>()
.AddSingleton<SaveService>() .AddSingleton<SaveService>()
.AddSingleton<FileCompactor>(); .AddSingleton<FileCompactor>();
@ -165,6 +165,7 @@ public static class ServiceManager
.AddSingleton<EffectiveTab>() .AddSingleton<EffectiveTab>()
.AddSingleton<OnScreenTab>() .AddSingleton<OnScreenTab>()
.AddSingleton<DebugTab>() .AddSingleton<DebugTab>()
.AddSingleton<MessagesTab>()
.AddSingleton<ResourceTab>() .AddSingleton<ResourceTab>()
.AddSingleton<ConfigTabBar>() .AddSingleton<ConfigTabBar>()
.AddSingleton<ResourceWatcher>() .AddSingleton<ResourceWatcher>()

View file

@ -1,5 +1,6 @@
using Dalamud.Interface.Internal.Notifications; using Dalamud.Interface.Internal.Notifications;
using Dalamud.Plugin; using Dalamud.Plugin;
using OtterGui.Classes;
namespace Penumbra.Services; namespace Penumbra.Services;
@ -33,8 +34,7 @@ public class ValidityChecker
public void LogExceptions() public void LogExceptions()
{ {
if (ImcExceptions.Count > 0) if (ImcExceptions.Count > 0)
Penumbra.Chat.NotificationMessage($"{ImcExceptions} IMC Exceptions thrown during Penumbra load. Please repair your game files.", Penumbra.Messager.NotificationMessage($"{ImcExceptions} IMC Exceptions thrown during Penumbra load. Please repair your game files.", NotificationType.Warning);
"Warning", NotificationType.Warning);
} }
// Because remnants of penumbra in devPlugins cause issues, we check for them to warn users to remove them. // Because remnants of penumbra in devPlugins cause issues, we check for them to warn users to remove them.

View file

@ -137,7 +137,7 @@ public class FileEditor<T> : IDisposable where T : class, IWritable
} }
catch (Exception e) catch (Exception e)
{ {
Penumbra.Chat.NotificationMessage($"Could not export {_defaultPath}:\n{e}", "Error", NotificationType.Error); Penumbra.Messager.NotificationMessage(e, $"Could not export {_defaultPath}.", NotificationType.Error);
} }
}, _getInitialPath(), false); }, _getInitialPath(), false);

View file

@ -4,6 +4,7 @@ using Dalamud.Utility;
using ImGuiNET; using ImGuiNET;
using Lumina.Excel.GeneratedSheets; using Lumina.Excel.GeneratedSheets;
using OtterGui; using OtterGui;
using OtterGui.Classes;
using OtterGui.Raii; using OtterGui.Raii;
using OtterGui.Widgets; using OtterGui.Widgets;
using Penumbra.Api.Enums; using Penumbra.Api.Enums;
@ -321,7 +322,7 @@ public class ItemSwapTab : IDisposable, ITab
} }
catch (Exception e) catch (Exception e)
{ {
Penumbra.Chat.NotificationMessage($"Could not create new Swap Option:\n{e}", "Error", NotificationType.Error); Penumbra.Messager.NotificationMessage(e, "Could not create new Swap Option.", NotificationType.Error, false);
try try
{ {
if (optionCreated && _selectedGroup != null) if (optionCreated && _selectedGroup != null)

View file

@ -113,8 +113,7 @@ public partial class ModEditWindow
LoadedShpkPath = FullPath.Empty; LoadedShpkPath = FullPath.Empty;
LoadedShpkPathName = string.Empty; LoadedShpkPathName = string.Empty;
AssociatedShpk = null; AssociatedShpk = null;
Penumbra.Chat.NotificationMessage($"Could not load {LoadedShpkPath.ToPath()}:\n{e}", "Penumbra Advanced Editing", Penumbra.Messager.NotificationMessage(e, $"Could not load {LoadedShpkPath.ToPath()}.", NotificationType.Error, false);
NotificationType.Error);
} }
if (LoadedShpkPath.InternalName.IsEmpty) if (LoadedShpkPath.InternalName.IsEmpty)

View file

@ -4,6 +4,7 @@ using ImGuiNET;
using Lumina.Misc; using Lumina.Misc;
using OtterGui.Raii; using OtterGui.Raii;
using OtterGui; using OtterGui;
using OtterGui.Classes;
using Penumbra.GameData; using Penumbra.GameData;
using Penumbra.GameData.Data; using Penumbra.GameData.Data;
using Penumbra.GameData.Files; using Penumbra.GameData.Files;
@ -87,15 +88,14 @@ public partial class ModEditWindow
} }
catch (Exception e) catch (Exception e)
{ {
Penumbra.Chat.NotificationMessage($"Could not export {defaultName}{tab.Extension} to {name}:\n{e.Message}", Penumbra.Messager.NotificationMessage(e, $"Could not export {defaultName}{tab.Extension} to {name}.",
"Penumbra Advanced Editing", NotificationType.Error, false);
NotificationType.Error);
return; return;
} }
Penumbra.Chat.NotificationMessage( Penumbra.Messager.NotificationMessage(
$"Shader Program Blob {defaultName}{tab.Extension} exported successfully to {Path.GetFileName(name)}", $"Shader Program Blob {defaultName}{tab.Extension} exported successfully to {Path.GetFileName(name)}.",
"Penumbra Advanced Editing", NotificationType.Success); NotificationType.Success, false);
}, null, false); }, null, false);
} }
@ -116,8 +116,7 @@ public partial class ModEditWindow
} }
catch (Exception e) catch (Exception e)
{ {
Penumbra.Chat.NotificationMessage($"Could not import {name}:\n{e.Message}", "Penumbra Advanced Editing", Penumbra.Messager.NotificationMessage(e, $"Could not import {name}.", NotificationType.Error, false);
NotificationType.Error);
return; return;
} }
@ -129,9 +128,8 @@ public partial class ModEditWindow
catch (Exception e) catch (Exception e)
{ {
tab.Shpk.SetInvalid(); tab.Shpk.SetInvalid();
Penumbra.Chat.NotificationMessage($"Failed to update resources after importing {name}:\n{e.Message}", Penumbra.Messager.NotificationMessage(e, $"Failed to update resources after importing {name}.", NotificationType.Error,
"Penumbra Advanced Editing", false);
NotificationType.Error);
return; return;
} }

View file

@ -142,7 +142,7 @@ public sealed class ConfigWindow : Window
ImGui.NewLine(); ImGui.NewLine();
ImGui.NewLine(); ImGui.NewLine();
CustomGui.DrawDiscordButton(Penumbra.Chat, 0); CustomGui.DrawDiscordButton(Penumbra.Messager, 0);
ImGui.SameLine(); ImGui.SameLine();
UiHelpers.DrawSupportButton(_penumbra!); UiHelpers.DrawSupportButton(_penumbra!);
ImGui.NewLine(); ImGui.NewLine();

View file

@ -18,14 +18,14 @@ using Penumbra.Mods.Manager;
using Penumbra.Mods.Subclasses; using Penumbra.Mods.Subclasses;
using Penumbra.Services; using Penumbra.Services;
using Penumbra.UI.Classes; using Penumbra.UI.Classes;
using ChatService = Penumbra.Services.ChatService; using MessageService = Penumbra.Services.MessageService;
namespace Penumbra.UI.ModsTab; namespace Penumbra.UI.ModsTab;
public sealed class ModFileSystemSelector : FileSystemSelector<Mod, ModFileSystemSelector.ModState> public sealed class ModFileSystemSelector : FileSystemSelector<Mod, ModFileSystemSelector.ModState>
{ {
private readonly CommunicatorService _communicator; private readonly CommunicatorService _communicator;
private readonly ChatService _chat; private readonly MessageService _messager;
private readonly Configuration _config; private readonly Configuration _config;
private readonly FileDialogService _fileDialog; private readonly FileDialogService _fileDialog;
private readonly ModManager _modManager; private readonly ModManager _modManager;
@ -37,7 +37,7 @@ public sealed class ModFileSystemSelector : FileSystemSelector<Mod, ModFileSyste
public ModCollection SelectedSettingCollection { get; private set; } = ModCollection.Empty; public ModCollection SelectedSettingCollection { get; private set; } = ModCollection.Empty;
public ModFileSystemSelector(IKeyState keyState, CommunicatorService communicator, ModFileSystem fileSystem, ModManager modManager, public ModFileSystemSelector(IKeyState keyState, CommunicatorService communicator, ModFileSystem fileSystem, ModManager modManager,
CollectionManager collectionManager, Configuration config, TutorialService tutorial, FileDialogService fileDialog, ChatService chat, CollectionManager collectionManager, Configuration config, TutorialService tutorial, FileDialogService fileDialog, MessageService messager,
ModImportManager modImportManager, IDragDropManager dragDrop) ModImportManager modImportManager, IDragDropManager dragDrop)
: base(fileSystem, keyState, Penumbra.Log, HandleException, allowMultipleSelection: true) : base(fileSystem, keyState, Penumbra.Log, HandleException, allowMultipleSelection: true)
{ {
@ -47,7 +47,7 @@ public sealed class ModFileSystemSelector : FileSystemSelector<Mod, ModFileSyste
_config = config; _config = config;
_tutorial = tutorial; _tutorial = tutorial;
_fileDialog = fileDialog; _fileDialog = fileDialog;
_chat = chat; _messager = messager;
_modImportManager = modImportManager; _modImportManager = modImportManager;
_dragDrop = dragDrop; _dragDrop = dragDrop;
@ -326,9 +326,7 @@ public sealed class ModFileSystemSelector : FileSystemSelector<Mod, ModFileSyste
} }
catch (Exception e) catch (Exception e)
{ {
_chat.NotificationMessage( _messager.NotificationMessage(e, $"Could not move newly imported mod {mod.Name} to default import folder {_config.DefaultImportFolder}.", NotificationType.Warning);
$"Could not move newly imported mod {mod.Name} to default import folder {_config.DefaultImportFolder}:\n{e}", "Warning",
NotificationType.Warning);
} }
} }
@ -392,7 +390,7 @@ public sealed class ModFileSystemSelector : FileSystemSelector<Mod, ModFileSyste
} }
private static void HandleException(Exception e) private static void HandleException(Exception e)
=> Penumbra.Chat.NotificationMessage(e.Message, "Failure", NotificationType.Warning); => Penumbra.Messager.NotificationMessage(e, e.Message, NotificationType.Warning);
#endregion #endregion

View file

@ -6,6 +6,7 @@ using ImGuiNET;
using OtterGui; using OtterGui;
using OtterGui.Raii; using OtterGui.Raii;
using OtterGui.Widgets; using OtterGui.Widgets;
using OtterGui.Classes;
using Penumbra.Api.Enums; using Penumbra.Api.Enums;
using Penumbra.Mods; using Penumbra.Mods;
using Penumbra.Mods.Editor; using Penumbra.Mods.Editor;
@ -18,7 +19,7 @@ namespace Penumbra.UI.ModsTab;
public class ModPanelEditTab : ITab public class ModPanelEditTab : ITab
{ {
private readonly ChatService _chat; private readonly Services.MessageService _messager;
private readonly FilenameService _filenames; private readonly FilenameService _filenames;
private readonly ModManager _modManager; private readonly ModManager _modManager;
private readonly ModExportManager _modExportManager; private readonly ModExportManager _modExportManager;
@ -35,13 +36,13 @@ public class ModPanelEditTab : ITab
private ModFileSystem.Leaf _leaf = null!; private ModFileSystem.Leaf _leaf = null!;
private Mod _mod = null!; private Mod _mod = null!;
public ModPanelEditTab(ModManager modManager, ModFileSystemSelector selector, ModFileSystem fileSystem, ChatService chat, public ModPanelEditTab(ModManager modManager, ModFileSystemSelector selector, ModFileSystem fileSystem, Services.MessageService messager,
ModEditWindow editWindow, ModEditor editor, FilenameService filenames, ModExportManager modExportManager, Configuration config) ModEditWindow editWindow, ModEditor editor, FilenameService filenames, ModExportManager modExportManager, Configuration config)
{ {
_modManager = modManager; _modManager = modManager;
_selector = selector; _selector = selector;
_fileSystem = fileSystem; _fileSystem = fileSystem;
_chat = chat; _messager = messager;
_editWindow = editWindow; _editWindow = editWindow;
_editor = editor; _editor = editor;
_filenames = filenames; _filenames = filenames;
@ -75,7 +76,7 @@ public class ModPanelEditTab : ITab
} }
catch (Exception e) catch (Exception e)
{ {
_chat.NotificationMessage(e.Message, "Warning", NotificationType.Warning); _messager.NotificationMessage(e.Message, NotificationType.Warning, false);
} }
UiHelpers.DefaultLineSpace(); UiHelpers.DefaultLineSpace();

View file

@ -19,7 +19,8 @@ public class ConfigTabBar : IDisposable
public readonly DebugTab Debug; public readonly DebugTab Debug;
public readonly ResourceTab Resource; public readonly ResourceTab Resource;
public readonly Watcher Watcher; public readonly Watcher Watcher;
public readonly OnScreenTab OnScreenTab; public readonly OnScreenTab OnScreen;
public readonly MessagesTab Messages;
public readonly ITab[] Tabs; public readonly ITab[] Tabs;
@ -28,7 +29,7 @@ public class ConfigTabBar : IDisposable
public ConfigTabBar(CommunicatorService communicator, SettingsTab settings, ModsTab mods, CollectionsTab collections, public ConfigTabBar(CommunicatorService communicator, SettingsTab settings, ModsTab mods, CollectionsTab collections,
ChangedItemsTab changedItems, EffectiveTab effective, DebugTab debug, ResourceTab resource, Watcher watcher, ChangedItemsTab changedItems, EffectiveTab effective, DebugTab debug, ResourceTab resource, Watcher watcher,
OnScreenTab onScreenTab) OnScreenTab onScreen, MessagesTab messages)
{ {
_communicator = communicator; _communicator = communicator;
@ -40,7 +41,8 @@ public class ConfigTabBar : IDisposable
Debug = debug; Debug = debug;
Resource = resource; Resource = resource;
Watcher = watcher; Watcher = watcher;
OnScreenTab = onScreenTab; OnScreen = onScreen;
Messages = messages;
Tabs = new ITab[] Tabs = new ITab[]
{ {
Settings, Settings,
@ -48,10 +50,11 @@ public class ConfigTabBar : IDisposable
Mods, Mods,
ChangedItems, ChangedItems,
Effective, Effective,
OnScreenTab, OnScreen,
Debug, Debug,
Resource, Resource,
Watcher, Watcher,
Messages,
}; };
_communicator.SelectTab.Subscribe(OnSelectTab, Communication.SelectTab.Priority.ConfigTabBar); _communicator.SelectTab.Subscribe(OnSelectTab, Communication.SelectTab.Priority.ConfigTabBar);
} }
@ -75,10 +78,11 @@ public class ConfigTabBar : IDisposable
TabType.Collections => Collections.Label, TabType.Collections => Collections.Label,
TabType.ChangedItems => ChangedItems.Label, TabType.ChangedItems => ChangedItems.Label,
TabType.EffectiveChanges => Effective.Label, TabType.EffectiveChanges => Effective.Label,
TabType.OnScreen => OnScreenTab.Label, TabType.OnScreen => OnScreen.Label,
TabType.ResourceWatcher => Watcher.Label, TabType.ResourceWatcher => Watcher.Label,
TabType.Debug => Debug.Label, TabType.Debug => Debug.Label,
TabType.ResourceManager => Resource.Label, TabType.ResourceManager => Resource.Label,
TabType.Messages => Messages.Label,
_ => ReadOnlySpan<byte>.Empty, _ => ReadOnlySpan<byte>.Empty,
}; };
@ -90,7 +94,8 @@ public class ConfigTabBar : IDisposable
if (label == Settings.Label) return TabType.Settings; if (label == Settings.Label) return TabType.Settings;
if (label == ChangedItems.Label) return TabType.ChangedItems; if (label == ChangedItems.Label) return TabType.ChangedItems;
if (label == Effective.Label) return TabType.EffectiveChanges; if (label == Effective.Label) return TabType.EffectiveChanges;
if (label == OnScreenTab.Label) return TabType.OnScreen; if (label == OnScreen.Label) return TabType.OnScreen;
if (label == Messages.Label) return TabType.Messages;
if (label == Watcher.Label) return TabType.ResourceWatcher; if (label == Watcher.Label) return TabType.ResourceWatcher;
if (label == Debug.Label) return TabType.Debug; if (label == Debug.Label) return TabType.Debug;
if (label == Resource.Label) return TabType.ResourceManager; if (label == Resource.Label) return TabType.ResourceManager;

View file

@ -0,0 +1,21 @@
using OtterGui.Widgets;
using Penumbra.Services;
namespace Penumbra.UI.Tabs;
public class MessagesTab : ITab
{
public ReadOnlySpan<byte> Label
=> "Messages"u8;
private readonly MessageService _messages;
public MessagesTab(MessageService messages)
=> _messages = messages;
public bool IsVisible
=> _messages.Count > 0;
public void DrawContent()
=> _messages.Draw();
}

View file

@ -851,10 +851,10 @@ public class SettingsTab : ITab
UiHelpers.DrawSupportButton(_penumbra); UiHelpers.DrawSupportButton(_penumbra);
ImGui.SetCursorPos(new Vector2(xPos, 0)); ImGui.SetCursorPos(new Vector2(xPos, 0));
CustomGui.DrawDiscordButton(Penumbra.Chat, width); CustomGui.DrawDiscordButton(Penumbra.Messager, width);
ImGui.SetCursorPos(new Vector2(xPos, 2 * ImGui.GetFrameHeightWithSpacing())); ImGui.SetCursorPos(new Vector2(xPos, 2 * ImGui.GetFrameHeightWithSpacing()));
CustomGui.DrawGuideButton(Penumbra.Chat, width); CustomGui.DrawGuideButton(Penumbra.Messager, width);
ImGui.SetCursorPos(new Vector2(xPos, 3 * ImGui.GetFrameHeightWithSpacing())); ImGui.SetCursorPos(new Vector2(xPos, 3 * ImGui.GetFrameHeightWithSpacing()));
if (ImGui.Button("Restart Tutorial", new Vector2(width, 0))) if (ImGui.Button("Restart Tutorial", new Vector2(width, 0)))

View file

@ -2,6 +2,7 @@ using Dalamud.Interface.Internal.Notifications;
using Dalamud.Interface.Utility; using Dalamud.Interface.Utility;
using ImGuiNET; using ImGuiNET;
using OtterGui; using OtterGui;
using OtterGui.Classes;
using OtterGui.Raii; using OtterGui.Raii;
using Penumbra.Interop.Structs; using Penumbra.Interop.Structs;
using Penumbra.String; using Penumbra.String;
@ -56,7 +57,7 @@ public static class UiHelpers
var text = penumbra.GatherSupportInformation(); var text = penumbra.GatherSupportInformation();
ImGui.SetClipboardText(text); ImGui.SetClipboardText(text);
Penumbra.Chat.NotificationMessage($"Copied Support Info to Clipboard.", "Success", NotificationType.Success); Penumbra.Messager.NotificationMessage($"Copied Support Info to Clipboard.", NotificationType.Success, false);
} }
/// <summary> Draw a button to open a specific directory in a file explorer.</summary> /// <summary> Draw a button to open a specific directory in a file explorer.</summary>