Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -279,11 +279,12 @@ jobs:
-p:Copyright="© 2025–${{ env.CopyrightYear }} WolfwithSword and contributors"
-o build-avalonia

- name: Copy presets folder and license into build output
- name: Copy presets, resources folder and license into build output
shell: bash
run: |
mkdir -p build-avalonia/presets
mkdir -p build-avalonia/presets build-avalonia/resources
cp -R presets/. build-avalonia/presets/
cp -R resources/. build-avalonia/resources/
cp LICENSE build-avalonia/
rm -f build-avalonia/*.pdb

Expand Down Expand Up @@ -466,11 +467,12 @@ jobs:
-p:PublishTrimmed=false
-o build-pr

- name: Copy presets folder and license into build output
- name: Copy presets, resources folder and license into build output
shell: bash
run: |
mkdir -p build-pr/presets
mkdir -p build-pr/presets build-pr/resources
cp -R presets/. build-pr/presets/
cp -R resources/. build-pr/resources/
cp LICENSE build-pr/
rm -f build-pr/*.pdb

Expand Down
14 changes: 10 additions & 4 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
presets/harley/
presets/honse/

bin/
obj/
/packages/
Expand All @@ -17,9 +14,18 @@ riderModule.iml

exports/
imports/
cache/

*.smo
*.smw
*.smwc

presets/**
!presets/
!presets/**/
!presets/**/*.smw
!presets/**/*.smwc

# If we set this up
!.idea/codeStyles/
!.idea/inspectionProfiles/
!.idea/runConfigurations/
Expand Down
23 changes: 23 additions & 0 deletions SubathonManager.Core/Enums/WidgetType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,29 @@ public enum WidgetType
Video = 2
}

public enum WidgetContextAction
{
ToggleVisibility,
Clone,
Delete,
ResetScale,
Refresh
}

public enum WidgetDisplayKind
{
Widget,//
UnpackedWidget,//
Image,
Video
}

public enum WidgetCatalogSource
{
Imported,
Preset
}

public static class WidgetTypeHelper
{
private static readonly HashSet<string> ImageExtensions = new(StringComparer.OrdinalIgnoreCase)
Expand Down
9 changes: 8 additions & 1 deletion SubathonManager.Core/Events/WidgetEvents.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using SubathonManager.Core.Models;
using SubathonManager.Core.Enums;
using SubathonManager.Core.Models;
using System.Diagnostics.CodeAnalysis;
namespace SubathonManager.Core.Events;

Expand All @@ -9,6 +10,12 @@ public static class WidgetEvents
public static event Action<Widget>? WidgetScaleUpdated;
public static event Action<Widget>? WidgetSizeUpdated;
public static event Action<Guid>? SelectEditorWidget;
public static event Action<Guid, WidgetContextAction>? WidgetActionRequested;

public static void RaiseWidgetAction(Guid widgetId, WidgetContextAction action)
{
WidgetActionRequested?.Invoke(widgetId, action);
}

public static void RaisePositionUpdated(Widget widget)
{
Expand Down
30 changes: 30 additions & 0 deletions SubathonManager.Core/Interfaces/IWidgetFileSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
namespace SubathonManager.Core.Interfaces;

public interface IWidgetFileSystem
{
bool Exists(string path);
string? ReadAllText(string path);
byte[]? ReadAllBytes(string path);
bool IsPacked(string path);
string? GetRealFilePath(string path);
IEnumerable<string> EnumerateFiles(string directory);
}

public sealed class DiskWidgetFileSystem : IWidgetFileSystem
{
public bool Exists(string path) => File.Exists(path);
public string? ReadAllText(string path) => File.Exists(path) ? File.ReadAllText(path) : null;
public byte[]? ReadAllBytes(string path) => File.Exists(path) ? File.ReadAllBytes(path) : null;
public bool IsPacked(string path) => false;
public string? GetRealFilePath(string path) => File.Exists(path) ? path : null;

public IEnumerable<string> EnumerateFiles(string directory) =>
Directory.Exists(directory)
? Directory.EnumerateFiles(directory, "*", SearchOption.AllDirectories)
: [];
}

public static class WidgetFiles
{
public static IWidgetFileSystem Current { get; set; } = new DiskWidgetFileSystem();
}
5 changes: 5 additions & 0 deletions SubathonManager.Core/Models/StateValue.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,9 @@ public class StateValue
public static class StateKeys
{
public const string WheelSpinsOwed = "WheelSpinsOwed";

public const string WidgetBrowserCollapsed = "WidgetBrowserCollapsed";
public const string WidgetBrowserSkipDeleteConfirm = "WidgetBrowserSkipDeleteConfirm";
public const string WidgetBrowserAllVersions = "WidgetBrowserAllVersions";
public const string EditorPreviewLightBg = "EditorPreviewLightBg";
}
46 changes: 33 additions & 13 deletions SubathonManager.Core/Models/Widget.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.ComponentModel;
using SubathonManager.Core.Enums;
using SubathonManager.Core.Interfaces;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Diagnostics.CodeAnalysis;
Expand Down Expand Up @@ -155,7 +156,11 @@ WidgetVariableType.StringList or
}
else if (((WidgetVariableType?)Type).IsFileVariable())
{
if (!Value.StartsWith("./") && !string.IsNullOrWhiteSpace(Value))
if (ResourcePaths.IsResourceUrl(Value))
{
sb.Append($"\"{Value}\"");
}
else if (!Value.StartsWith("./") && !string.IsNullOrWhiteSpace(Value))
{
sb.Append($"\"externalPath/{Value}\"");
}
Expand Down Expand Up @@ -300,36 +305,51 @@ public void ScanCssVariables()
CssVariables = ExtractCssVariablesFromFiles();
}

public List<CssVariable> ExtractCssVariablesFromFiles()
public List<string> GetLinkedCssPaths()
{
var extractedVars = new List<CssVariable>();
var paths = new List<string>();

if (!File.Exists(HtmlPath))
return extractedVars;
var html = WidgetFiles.Current.ReadAllText(HtmlPath);
if (html == null)
return paths;

string htmlContent = File.ReadAllText(HtmlPath);
string baseDir = Path.GetDirectoryName(HtmlPath)!;

var cssMatches = CssLinkRegex().Matches(htmlContent);

foreach (Match match in cssMatches)
foreach (Match match in CssLinkMatches(html))
{
string cssFile = match.Groups[1].Value;
string cssPath = Path.IsPathRooted(cssFile)
? cssFile
: Path.Combine(baseDir, cssFile);

if (!File.Exists(cssPath))
if (!WidgetFiles.Current.Exists(cssPath))
continue;
if (paths.Contains(cssPath, StringComparer.OrdinalIgnoreCase))
continue;

string cssContent = File.ReadAllText(cssPath);
paths.Add(cssPath);
}

return paths;
}

public static MatchCollection CssLinkMatches(string htmlContent) => CssLinkRegex().Matches(htmlContent);

public List<CssVariable> ExtractCssVariablesFromFiles()
{
var extractedVars = new List<CssVariable>();

foreach (var cssPath in GetLinkedCssPaths())
{
string? cssContent = WidgetFiles.Current.ReadAllText(cssPath);
if (cssContent == null) continue;
var varMatches = CssVarRegex().Matches(cssContent);

string metaPath = cssPath + ".json";
Dictionary<string, Dictionary<string, string>>? varTypes = null;
if (File.Exists(metaPath))
var metaJson = WidgetFiles.Current.ReadAllText(metaPath);
if (metaJson != null)
{
var metaJson = File.ReadAllText(metaPath);
varTypes = JsonSerializer.Deserialize<Dictionary<string, Dictionary<string, string>>>(metaJson,
new JsonSerializerOptions { PropertyNameCaseInsensitive = false });
}
Expand Down
34 changes: 34 additions & 0 deletions SubathonManager.Core/Models/WidgetCatalogEntry.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using System.ComponentModel.DataAnnotations;
using System.Diagnostics.CodeAnalysis;
using SubathonManager.Core.Enums;

namespace SubathonManager.Core.Models;

[ExcludeFromCodeCoverage]
public class WidgetCatalogEntry
{
[Key]
public int Id { get; set; }

public string PackPath { get; set; } = string.Empty;
public long FileSize { get; set; }
public long FileModifiedTicks { get; set; }

public WidgetCatalogSource Source { get; set; } = WidgetCatalogSource.Imported;

public string PackId { get; set; } = string.Empty;
public string Name { get; set; } = string.Empty;
public string Author { get; set; } = string.Empty;
public string Group { get; set; } = string.Empty;
public string Version { get; set; } = string.Empty;
public string Entry { get; set; } = string.Empty;
public string DocsUrl { get; set; } = string.Empty;
public string Tags { get; set; } = string.Empty;
public string PreviewImage { get; set; } = string.Empty;
public string PreviewCachePath { get; set; } = string.Empty;

public DateTime LastSeenUtc { get; set; }

public float ScaleX { get; set; } = 1;
public float ScaleY { get; set; } = 1;
}
Loading