From d9fc69d665bbbd1d834d32d747db61e5854873a9 Mon Sep 17 00:00:00 2001 From: triston armstrong Date: Fri, 28 Aug 2026 00:56:26 -0500 Subject: [PATCH 01/17] Upgrade Wails to v2.15 --- frontend/wailsjs/runtime/runtime.d.ts | 122 +++++++++++++++++++------- frontend/wailsjs/runtime/runtime.js | 58 +++++++++++- go.mod | 15 ++-- go.sum | 30 ++++--- 4 files changed, 172 insertions(+), 53 deletions(-) diff --git a/frontend/wailsjs/runtime/runtime.d.ts b/frontend/wailsjs/runtime/runtime.d.ts index 713e3f86..3bbea848 100644 --- a/frontend/wailsjs/runtime/runtime.d.ts +++ b/frontend/wailsjs/runtime/runtime.d.ts @@ -21,8 +21,8 @@ export interface Size { export interface Screen { isCurrent: boolean; isPrimary: boolean; - width: number; - height: number; + width : number + height : number } // Environment information such as platform, buildtype, ... @@ -38,32 +38,19 @@ export interface EnvironmentInfo { export function EventsEmit(eventName: string, ...data: any): void; // [EventsOn](https://wails.io/docs/reference/runtime/events#eventson) sets up a listener for the given event name. -export function EventsOn( - eventName: string, - callback: (...data: any) => void -): () => void; +export function EventsOn(eventName: string, callback: (...data: any) => void): () => void; // [EventsOnMultiple](https://wails.io/docs/reference/runtime/events#eventsonmultiple) // sets up a listener for the given event name, but will only trigger a given number times. -export function EventsOnMultiple( - eventName: string, - callback: (...data: any) => void, - maxCallbacks: number -): () => void; +export function EventsOnMultiple(eventName: string, callback: (...data: any) => void, maxCallbacks: number): () => void; // [EventsOnce](https://wails.io/docs/reference/runtime/events#eventsonce) // sets up a listener for the given event name, but will only trigger once. -export function EventsOnce( - eventName: string, - callback: (...data: any) => void -): () => void; +export function EventsOnce(eventName: string, callback: (...data: any) => void): () => void; // [EventsOff](https://wails.io/docs/reference/runtime/events#eventsoff) // unregisters the listener for the given event name. -export function EventsOff( - eventName: string, - ...additionalEventNames: string[] -): void; +export function EventsOff(eventName: string, ...additionalEventNames: string[]): void; // [EventsOffAll](https://wails.io/docs/reference/runtime/events#eventsoffall) // unregisters all listeners. @@ -213,12 +200,7 @@ export function WindowIsNormal(): Promise; // [WindowSetBackgroundColour](https://wails.io/docs/reference/runtime/window#windowsetbackgroundcolour) // Sets the background colour of the window to the given RGBA colour definition. This colour will show through for all transparent pixels. -export function WindowSetBackgroundColour( - R: number, - G: number, - B: number, - A: number -): void; +export function WindowSetBackgroundColour(R: number, G: number, B: number, A: number): void; // [ScreenGetAll](https://wails.io/docs/reference/runtime/window#screengetall) // Gets the all screens. Call this anew each time you want to refresh data from the underlying windowing system. @@ -254,17 +236,95 @@ export function ClipboardSetText(text: string): Promise; // [OnFileDrop](https://wails.io/docs/reference/runtime/draganddrop#onfiledrop) // OnFileDrop listens to drag and drop events and calls the callback with the coordinates of the drop and an array of path strings. -export function OnFileDrop( - callback: (x: number, y: number, paths: string[]) => void, - useDropTarget: boolean -): void; +export function OnFileDrop(callback: (x: number, y: number ,paths: string[]) => void, useDropTarget: boolean) :void // [OnFileDropOff](https://wails.io/docs/reference/runtime/draganddrop#dragandddropoff) // OnFileDropOff removes the drag and drop listeners and handlers. -export function OnFileDropOff(): void; +export function OnFileDropOff() :void // Check if the file path resolver is available export function CanResolveFilePaths(): boolean; // Resolves file paths for an array of files -export function ResolveFilePaths(files: File[]): void; +export function ResolveFilePaths(files: File[]): void + +// Notification types +export interface NotificationOptions { + id: string; + title: string; + subtitle?: string; // macOS and Linux only + body?: string; + categoryId?: string; + data?: { [key: string]: any }; +} + +export interface NotificationAction { + id?: string; + title?: string; + destructive?: boolean; // macOS-specific +} + +export interface NotificationCategory { + id?: string; + actions?: NotificationAction[]; + hasReplyField?: boolean; + replyPlaceholder?: string; + replyButtonTitle?: string; +} + +// [InitializeNotifications](https://wails.io/docs/reference/runtime/notification#initializenotifications) +// Initializes the notification service for the application. +// This must be called before sending any notifications. +export function InitializeNotifications(): Promise; + +// [CleanupNotifications](https://wails.io/docs/reference/runtime/notification#cleanupnotifications) +// Cleans up notification resources and releases any held connections. +export function CleanupNotifications(): Promise; + +// [IsNotificationAvailable](https://wails.io/docs/reference/runtime/notification#isnotificationavailable) +// Checks if notifications are available on the current platform. +export function IsNotificationAvailable(): Promise; + +// [RequestNotificationAuthorization](https://wails.io/docs/reference/runtime/notification#requestnotificationauthorization) +// Requests notification authorization from the user (macOS only). +export function RequestNotificationAuthorization(): Promise; + +// [CheckNotificationAuthorization](https://wails.io/docs/reference/runtime/notification#checknotificationauthorization) +// Checks the current notification authorization status (macOS only). +export function CheckNotificationAuthorization(): Promise; + +// [SendNotification](https://wails.io/docs/reference/runtime/notification#sendnotification) +// Sends a basic notification with the given options. +export function SendNotification(options: NotificationOptions): Promise; + +// [SendNotificationWithActions](https://wails.io/docs/reference/runtime/notification#sendnotificationwithactions) +// Sends a notification with action buttons. Requires a registered category. +export function SendNotificationWithActions(options: NotificationOptions): Promise; + +// [RegisterNotificationCategory](https://wails.io/docs/reference/runtime/notification#registernotificationcategory) +// Registers a notification category that can be used with SendNotificationWithActions. +export function RegisterNotificationCategory(category: NotificationCategory): Promise; + +// [RemoveNotificationCategory](https://wails.io/docs/reference/runtime/notification#removenotificationcategory) +// Removes a previously registered notification category. +export function RemoveNotificationCategory(categoryId: string): Promise; + +// [RemoveAllPendingNotifications](https://wails.io/docs/reference/runtime/notification#removeallpendingnotifications) +// Removes all pending notifications from the notification center. +export function RemoveAllPendingNotifications(): Promise; + +// [RemovePendingNotification](https://wails.io/docs/reference/runtime/notification#removependingnotification) +// Removes a specific pending notification by its identifier. +export function RemovePendingNotification(identifier: string): Promise; + +// [RemoveAllDeliveredNotifications](https://wails.io/docs/reference/runtime/notification#removealldeliverednotifications) +// Removes all delivered notifications from the notification center. +export function RemoveAllDeliveredNotifications(): Promise; + +// [RemoveDeliveredNotification](https://wails.io/docs/reference/runtime/notification#removedeliverednotification) +// Removes a specific delivered notification by its identifier. +export function RemoveDeliveredNotification(identifier: string): Promise; + +// [RemoveNotification](https://wails.io/docs/reference/runtime/notification#removenotification) +// Removes a notification by its identifier (cross-platform convenience function). +export function RemoveNotification(identifier: string): Promise; \ No newline at end of file diff --git a/frontend/wailsjs/runtime/runtime.js b/frontend/wailsjs/runtime/runtime.js index 7674e0de..556621ee 100644 --- a/frontend/wailsjs/runtime/runtime.js +++ b/frontend/wailsjs/runtime/runtime.js @@ -49,7 +49,7 @@ export function EventsOff(eventName, ...additionalEventNames) { } export function EventsOffAll() { - return window.runtime.EventsOffAll(); + return window.runtime.EventsOffAll(); } export function EventsOnce(eventName, callback) { @@ -240,3 +240,59 @@ export function CanResolveFilePaths() { export function ResolveFilePaths(files) { return window.runtime.ResolveFilePaths(files); } + +export function InitializeNotifications() { + return window.runtime.InitializeNotifications(); +} + +export function CleanupNotifications() { + return window.runtime.CleanupNotifications(); +} + +export function IsNotificationAvailable() { + return window.runtime.IsNotificationAvailable(); +} + +export function RequestNotificationAuthorization() { + return window.runtime.RequestNotificationAuthorization(); +} + +export function CheckNotificationAuthorization() { + return window.runtime.CheckNotificationAuthorization(); +} + +export function SendNotification(options) { + return window.runtime.SendNotification(options); +} + +export function SendNotificationWithActions(options) { + return window.runtime.SendNotificationWithActions(options); +} + +export function RegisterNotificationCategory(category) { + return window.runtime.RegisterNotificationCategory(category); +} + +export function RemoveNotificationCategory(categoryId) { + return window.runtime.RemoveNotificationCategory(categoryId); +} + +export function RemoveAllPendingNotifications() { + return window.runtime.RemoveAllPendingNotifications(); +} + +export function RemovePendingNotification(identifier) { + return window.runtime.RemovePendingNotification(identifier); +} + +export function RemoveAllDeliveredNotifications() { + return window.runtime.RemoveAllDeliveredNotifications(); +} + +export function RemoveDeliveredNotification(identifier) { + return window.runtime.RemoveDeliveredNotification(identifier); +} + +export function RemoveNotification(identifier) { + return window.runtime.RemoveNotification(identifier); +} \ No newline at end of file diff --git a/go.mod b/go.mod index f9307500..49cf0840 100644 --- a/go.mod +++ b/go.mod @@ -1,13 +1,14 @@ module aether -go 1.23 +go 1.25.0 require ( - github.com/wailsapp/wails/v2 v2.11.0 - golang.org/x/image v0.23.0 + github.com/wailsapp/wails/v2 v2.15.0 + golang.org/x/image v0.41.0 ) require ( + git.sr.ht/~jackmordaunt/go-toast/v2 v2.0.3 // indirect github.com/bep/debounce v1.2.1 // indirect github.com/go-ole/go-ole v1.3.0 // indirect github.com/godbus/dbus/v5 v5.1.0 // indirect @@ -31,8 +32,8 @@ require ( github.com/valyala/fasttemplate v1.2.2 // indirect github.com/wailsapp/go-webview2 v1.0.22 // indirect github.com/wailsapp/mimetype v1.4.1 // indirect - golang.org/x/crypto v0.33.0 // indirect - golang.org/x/net v0.35.0 // indirect - golang.org/x/sys v0.30.0 // indirect - golang.org/x/text v0.22.0 // indirect + golang.org/x/crypto v0.53.0 // indirect + golang.org/x/net v0.56.0 // indirect + golang.org/x/sys v0.46.0 // indirect + golang.org/x/text v0.39.0 // indirect ) diff --git a/go.sum b/go.sum index fcaee669..46af5d39 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +git.sr.ht/~jackmordaunt/go-toast/v2 v2.0.3 h1:N3IGoHHp9pb6mj1cbXbuaSXV/UMKwmbKLf53nQmtqMA= +git.sr.ht/~jackmordaunt/go-toast/v2 v2.0.3/go.mod h1:QtOLZGz8olr4qH2vWK0QH0w0O4T9fEIjMuWpKUsH7nc= github.com/bep/debounce v1.2.1 h1:v67fRdBA9UQu2NhLFXrSg0Brw7CexQekrBwDMM8bzeY= github.com/bep/debounce v1.2.1/go.mod h1:H8yggRPQKLUhUoqrJC1bO2xNya7vanpDl7xR3ISbCJ0= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -45,8 +47,8 @@ github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/samber/lo v1.49.1 h1:4BIFyVfuQSEpluc7Fua+j1NolZHiEHEpaSEKdsH0tew= github.com/samber/lo v1.49.1/go.mod h1:dO6KHFzUKXgP8LDhU0oI8d2hekjXnGOu0DB8Jecxd6o= -github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= -github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/tkrajina/go-reflector v0.5.8 h1:yPADHrwmUbMq4RGEyaOUpz2H90sRsETNVpjzo3DLVQQ= github.com/tkrajina/go-reflector v0.5.8/go.mod h1:ECbqLgccecY5kPmPmXg1MrHW585yMcDkVl6IvJe64T4= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= @@ -57,27 +59,27 @@ github.com/wailsapp/go-webview2 v1.0.22 h1:YT61F5lj+GGaat5OB96Aa3b4QA+mybD0Ggq6N github.com/wailsapp/go-webview2 v1.0.22/go.mod h1:qJmWAmAmaniuKGZPWwne+uor3AHMB5PFhqiK0Bbj8kc= github.com/wailsapp/mimetype v1.4.1 h1:pQN9ycO7uo4vsUUuPeHEYoUkLVkaRntMnHJxVwYhwHs= github.com/wailsapp/mimetype v1.4.1/go.mod h1:9aV5k31bBOv5z6u+QP8TltzvNGJPmNJD4XlAL3U+j3o= -github.com/wailsapp/wails/v2 v2.11.0 h1:seLacV8pqupq32IjS4Y7V8ucab0WZwtK6VvUVxSBtqQ= -github.com/wailsapp/wails/v2 v2.11.0/go.mod h1:jrf0ZaM6+GBc1wRmXsM8cIvzlg0karYin3erahI4+0k= -golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus= -golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M= -golang.org/x/image v0.23.0 h1:HseQ7c2OpPKTPVzNjG5fwJsOTCiiwS4QdsYi5XU6H68= -golang.org/x/image v0.23.0/go.mod h1:wJJBTdLfCCf3tiHa1fNxpZmUI4mmoZvwMCPP0ddoNKY= +github.com/wailsapp/wails/v2 v2.15.0 h1:u7cHK+UesZOlYxyJxfYLteaCPhws6UsZoDdqUejuX6Q= +github.com/wailsapp/wails/v2 v2.15.0/go.mod h1:scxrgwfsv6yR6fE6cCF+Flfl+JeU+SR87T9x4kILJ6M= +golang.org/x/crypto v0.53.0 h1:QZ4Muo8THX6CizN2vPPd5fBGHyogrdK9fG4wLPFUsto= +golang.org/x/crypto v0.53.0/go.mod h1:DNLU434OwVakk9PzuwV8w62mAJpRJL3vsgcfp4Qnsio= +golang.org/x/image v0.41.0 h1:8wS72eGJMJaBxK6okTzd4WaXumUlTVlb753MlsSvTCo= +golang.org/x/image v0.41.0/go.mod h1:uIc348UZMSvS5Z65CVZ7iDPaNobNFEPeJ4kbqTOszmA= golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8= -golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk= +golang.org/x/net v0.56.0 h1:Rw8j/hFzGvJUZwNBXnAtf5sVDVt+65SK2C7IxCxZt5o= +golang.org/x/net v0.56.0/go.mod h1:D3Ku6r+V6JROoZK144D2XfMHFcMq/0zSfLelVTCFKec= golang.org/x/sys v0.0.0-20200810151505-1b9f1253b3ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= -golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw= +golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= -golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= +golang.org/x/text v0.39.0 h1:UbZz4pLOvn600D6Oh6GGEI6VAmndrEBLv8/6BEXzyus= +golang.org/x/text v0.39.0/go.mod h1:3UwRclnC2g0TU9x8PZiyfOajCd1zaUNHF9cvqcQZ+ZM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= From 39dbb25c3867e3c2d858dd17abe7b1e43da17f16 Mon Sep 17 00:00:00 2001 From: triston armstrong Date: Fri, 28 Aug 2026 01:10:02 -0500 Subject: [PATCH 02/17] Add blurred wallpaper variant Apply sends a backend-generated blurred JPEG instead of the original while extraction keeps sampling the unblurred source. Includes the regenerated Wails bindings (reformatted by the v2.15 CLI; also carries the ThemeFolderExists and originalWallpaperPath fields used by the following commits). --- app.go | 11 + frontend/src/lib/actions/themeActions.ts | 17 +- .../components/editor/WallpaperHero.svelte | 90 +- .../lib/components/layout/ActionBar.svelte | 40 +- frontend/src/lib/stores/theme.svelte.ts | 28 + frontend/wailsjs/go/main/App.d.ts | 156 +-- frontend/wailsjs/go/main/App.js | 144 +- frontend/wailsjs/go/models.ts | 1228 +++++++++-------- internal/platform/paths.go | 6 + internal/wallpaper/blur.go | 231 ++++ internal/wallpaper/blur_test.go | 168 +++ 11 files changed, 1344 insertions(+), 775 deletions(-) create mode 100644 internal/wallpaper/blur.go create mode 100644 internal/wallpaper/blur_test.go diff --git a/app.go b/app.go index 3a12dff3..7013a339 100644 --- a/app.go +++ b/app.go @@ -832,6 +832,17 @@ func (a *App) CancelBatchProcessing() { // File / Image Utilities // --------------------------------------------------------------------------- +// BlurWallpaper generates a heavily Gaussian-blurred JPEG variant of an image +// for use as the applied wallpaper. The original file is never modified, so +// color extraction keeps sampling the unblurred source. Returns the variant +// path (cached by source identity, so repeat calls are cheap). +func (a *App) BlurWallpaper(path string) (string, error) { + if !theme.IsImageFile(path) { + return "", fmt.Errorf("unsupported image file: %s", path) + } + return wallpaper.CreateBlurredVariant(path, platform.BlurDir()) +} + // ReadImageAsDataURL reads a local image file and returns it as a base64 data URL. // This is needed because webkit2gtk cannot load file:// paths directly. func (a *App) ReadImageAsDataURL(path string) (string, error) { diff --git a/frontend/src/lib/actions/themeActions.ts b/frontend/src/lib/actions/themeActions.ts index afc94ca9..990c35c1 100644 --- a/frontend/src/lib/actions/themeActions.ts +++ b/frontend/src/lib/actions/themeActions.ts @@ -10,6 +10,7 @@ import { getIsExtracting, setIsExtracting, getWallpaperPath, + getApplyWallpaperPath, setWallpaperPath, getPalette, setPalette, @@ -54,7 +55,8 @@ async function runApply(): Promise<{success: boolean}> { const {ApplyTheme} = await import('../../../wailsjs/go/main/App'); const result = await ApplyTheme({ palette: getPalette(), - wallpaperPath: getWallpaperPath(), + wallpaperPath: getApplyWallpaperPath(), + originalWallpaperPath: getWallpaperPath(), lightMode: getLightMode(), additionalImages: getAdditionalImages(), extendedColors: getExtendedColors(), @@ -143,14 +145,14 @@ export async function saveAndApplyTheme( if (getIsApplying()) return; setIsApplying(true); try { - const {SaveAndApplyTheme} = await import( - '../../../wailsjs/go/main/App' - ); + const {SaveAndApplyTheme} = + await import('../../../wailsjs/go/main/App'); const result = await SaveAndApplyTheme({ name, updateExisting, palette: getPalette(), - wallpaperPath: getWallpaperPath(), + wallpaperPath: getApplyWallpaperPath(), + originalWallpaperPath: getWallpaperPath(), lightMode: getLightMode(), additionalImages: getAdditionalImages(), extendedColors: getExtendedColors(), @@ -188,9 +190,8 @@ export async function applyWallpaperOnly(originalPath: string): Promise { if (path.startsWith('http://') || path.startsWith('https://')) { try { showToast('Downloading wallpaper…'); - const {DownloadWallpaper} = await import( - '../../../wailsjs/go/main/App' - ); + const {DownloadWallpaper} = + await import('../../../wailsjs/go/main/App'); path = await DownloadWallpaper(path); } catch { showToast('Failed to download wallpaper'); diff --git a/frontend/src/lib/components/editor/WallpaperHero.svelte b/frontend/src/lib/components/editor/WallpaperHero.svelte index 82c60ce0..5c8266a3 100644 --- a/frontend/src/lib/components/editor/WallpaperHero.svelte +++ b/frontend/src/lib/components/editor/WallpaperHero.svelte @@ -12,6 +12,9 @@ getExtractionMode, setAdjustments, getAdditionalImages, + getBlurredWallpaperPath, + setBlurredWallpaper, + clearBlurredWallpaper, } from '$lib/stores/theme.svelte'; import {DEFAULT_ADJUSTMENTS} from '$lib/types/theme'; import { @@ -34,9 +37,15 @@ $props(); let wallpaperPath = $derived(getWallpaperPath()); - let wallpaperImage = $derived(getCachedFullImage(wallpaperPath) || ''); + let blurredPath = $derived(getBlurredWallpaperPath()); + // The blurred variant is what gets applied, so it is what the hero shows. + // Extraction still uses wallpaperPath (the unblurred original). + let displayPath = $derived(blurredPath || wallpaperPath); + let wallpaperImage = $derived(getCachedFullImage(displayPath) || ''); let wallpaperName = $derived(wallpaperPath.split('/').pop() || ''); - let loading = $derived(isPending(wallpaperPath)); + let loading = $derived(isPending(displayPath)); + let blurred = $derived(!!blurredPath); + let isBlurring = $state(false); let previewOpen = $state(false); let eyedropperActive = $derived(getEyedropperActive()); let containerHeight = $derived(expanded ? 'h-[70vh]' : 'h-96'); @@ -60,7 +69,7 @@ let loupeHex = $state('#000000'); $effect(() => { - const path = getWallpaperPath(); + const path = displayPath; if (path && !getCachedFullImage(path)) { loadFullImage(path); } @@ -228,9 +237,8 @@ if (!path) return; setIsExtracting(true); try { - const {ExtractColors} = await import( - '../../../../wailsjs/go/main/App' - ); + const {ExtractColors} = + await import('../../../../wailsjs/go/main/App'); const colors = await ExtractColors( path, getLightMode(), @@ -253,9 +261,8 @@ if (paths.length === 0) return; setIsExtracting(true); try { - const {ExtractColorsFromImages} = await import( - '../../../../wailsjs/go/main/App' - ); + const {ExtractColorsFromImages} = + await import('../../../../wailsjs/go/main/App'); const result = await ExtractColorsFromImages( paths, getLightMode(), @@ -281,9 +288,8 @@ async function handleChange() { try { - const {OpenFileDialog} = await import( - '../../../../wailsjs/go/main/App' - ); + const {OpenFileDialog} = + await import('../../../../wailsjs/go/main/App'); const path = await OpenFileDialog(); if (path) { setWallpaperPath(path); @@ -295,6 +301,33 @@ showToast('Failed to change wallpaper'); } } + + // Toggle a heavy Gaussian-blurred variant as the applied wallpaper. The + // blurred copy is generated by the backend into its cache; the source + // file is never modified, so Extract keeps sampling the original. + async function handleToggleBlur() { + const source = getWallpaperPath(); + if (!source || isBlurring) return; + if (getBlurredWallpaperPath()) { + clearBlurredWallpaper(); + showToast('Blur removed — the original will be applied'); + return; + } + isBlurring = true; + try { + const {BlurWallpaper} = + await import('../../../../wailsjs/go/main/App'); + const path = await BlurWallpaper(source); + setBlurredWallpaper(source, path); + showToast( + 'Wallpaper blurred — Apply uses the blurred copy; palette still extracts from the original' + ); + } catch { + showToast("Couldn't blur wallpaper"); + } finally { + isBlurring = false; + } + }
@@ -330,7 +363,7 @@ {#if eyedropperActive}
Click to pick a color · Esc to cancel
@@ -395,6 +428,35 @@ {/if} +
enabled) .map(([key]) => key); @@ -213,7 +211,8 @@ name: exportName.trim(), includedApps, palette: getPalette(), - wallpaperPath: getWallpaperPath(), + wallpaperPath: getApplyWallpaperPath(), + originalWallpaperPath: getWallpaperPath(), lightMode: getLightMode(), additionalImages: getAdditionalImages(), extendedColors: getExtendedColors(), @@ -240,9 +239,8 @@ async function handleImport(fileType: string) { showImportMenu = false; try { - const {ImportFileDialog} = await import( - '../../../../wailsjs/go/main/App' - ); + const {ImportFileDialog} = + await import('../../../../wailsjs/go/main/App'); const result = await ImportFileDialog(fileType); console.log('Import result:', result); if (result?.colors?.length >= 16) { @@ -309,9 +307,8 @@ onclick={async () => { showExportDialog = true; try { - const {IsOmarchyInstalled} = await import( - '../../../../wailsjs/go/main/App' - ); + const {IsOmarchyInstalled} = + await import('../../../../wailsjs/go/main/App'); isOmarchy = await IsOmarchyInstalled(); } catch { isOmarchy = false; @@ -435,7 +432,7 @@ {/if} {#if dirty && !applying} {/if} @@ -468,7 +465,7 @@ role="presentation" >
+ {folderExists ? 'Update and Apply' : 'Save and Apply'} +
From d4af1708bb8dca19e14d8385cf278660e4a59832 Mon Sep 17 00:00:00 2001 From: Triston Armstrong Date: Fri, 28 Aug 2026 16:53:10 -0500 Subject: [PATCH 06/17] Add BlurWallpaper tests --- app_themes_test.go | 69 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/app_themes_test.go b/app_themes_test.go index a485f2c8..1565ea76 100644 --- a/app_themes_test.go +++ b/app_themes_test.go @@ -1,6 +1,9 @@ package main import ( + "image" + "image/color" + "image/png" "os" "path/filepath" "testing" @@ -44,3 +47,69 @@ func TestThemeFolderExists(t *testing.T) { } } } + +func writeTestImage(t *testing.T, dir, name string, w, h int) string { + t.Helper() + img := image.NewRGBA(image.Rect(0, 0, w, h)) + for y := 0; y < h; y++ { + for x := 0; x < w; x++ { + img.SetRGBA(x, y, color.RGBA{R: uint8(x % 256), G: uint8(y % 256), B: 128, A: 0xff}) + } + } + path := filepath.Join(dir, name) + f, err := os.Create(path) + if err != nil { + t.Fatalf("create test image: %v", err) + } + defer f.Close() + if err := png.Encode(f, img); err != nil { + t.Fatalf("encode test image: %v", err) + } + return path +} + +func TestBlurWallpaperCreatesVariant(t *testing.T) { + t.Setenv("XDG_CACHE_HOME", t.TempDir()) + t.Setenv("HOME", t.TempDir()) + + app := NewApp() + src := writeTestImage(t, t.TempDir(), "photo.png", 128, 80) + + got, err := app.BlurWallpaper(src) + if err != nil { + t.Fatalf("BlurWallpaper: %v", err) + } + if got == "" { + t.Fatal("BlurWallpaper returned empty path") + } + if _, err := os.Stat(got); err != nil { + t.Fatalf("variant not created: %v", err) + } + ext := filepath.Ext(got) + if ext != ".jpg" { + t.Errorf("variant ext = %q; want .jpg", ext) + } +} + +func TestBlurWallpaperRejectsNonImage(t *testing.T) { + t.Setenv("XDG_CACHE_HOME", t.TempDir()) + + app := NewApp() + txt := filepath.Join(t.TempDir(), "notes.txt") + if err := os.WriteFile(txt, []byte("not an image"), 0o644); err != nil { + t.Fatal(err) + } + + if _, err := app.BlurWallpaper(txt); err == nil { + t.Error("BlurWallpaper() error = nil; want error for non-image file") + } +} + +func TestBlurWallpaperRejectsMissingFile(t *testing.T) { + t.Setenv("XDG_CACHE_HOME", t.TempDir()) + + app := NewApp() + if _, err := app.BlurWallpaper("/nonexistent/photo.png"); err == nil { + t.Error("BlurWallpaper() error = nil; want error for missing file") + } +} From 48ec4851a4441bc4e829467985f93ad812ff4667 Mon Sep 17 00:00:00 2001 From: Triston Armstrong Date: Fri, 28 Aug 2026 16:56:14 -0500 Subject: [PATCH 07/17] Revert "Upgrade Wails to v2.15" This reverts commit 8405404456e54f6295c5678dbfbb430c3c5b2d97. --- frontend/wailsjs/runtime/runtime.d.ts | 122 +++++++------------------- frontend/wailsjs/runtime/runtime.js | 58 +----------- go.mod | 15 ++-- go.sum | 30 +++---- 4 files changed, 53 insertions(+), 172 deletions(-) diff --git a/frontend/wailsjs/runtime/runtime.d.ts b/frontend/wailsjs/runtime/runtime.d.ts index 3bbea848..713e3f86 100644 --- a/frontend/wailsjs/runtime/runtime.d.ts +++ b/frontend/wailsjs/runtime/runtime.d.ts @@ -21,8 +21,8 @@ export interface Size { export interface Screen { isCurrent: boolean; isPrimary: boolean; - width : number - height : number + width: number; + height: number; } // Environment information such as platform, buildtype, ... @@ -38,19 +38,32 @@ export interface EnvironmentInfo { export function EventsEmit(eventName: string, ...data: any): void; // [EventsOn](https://wails.io/docs/reference/runtime/events#eventson) sets up a listener for the given event name. -export function EventsOn(eventName: string, callback: (...data: any) => void): () => void; +export function EventsOn( + eventName: string, + callback: (...data: any) => void +): () => void; // [EventsOnMultiple](https://wails.io/docs/reference/runtime/events#eventsonmultiple) // sets up a listener for the given event name, but will only trigger a given number times. -export function EventsOnMultiple(eventName: string, callback: (...data: any) => void, maxCallbacks: number): () => void; +export function EventsOnMultiple( + eventName: string, + callback: (...data: any) => void, + maxCallbacks: number +): () => void; // [EventsOnce](https://wails.io/docs/reference/runtime/events#eventsonce) // sets up a listener for the given event name, but will only trigger once. -export function EventsOnce(eventName: string, callback: (...data: any) => void): () => void; +export function EventsOnce( + eventName: string, + callback: (...data: any) => void +): () => void; // [EventsOff](https://wails.io/docs/reference/runtime/events#eventsoff) // unregisters the listener for the given event name. -export function EventsOff(eventName: string, ...additionalEventNames: string[]): void; +export function EventsOff( + eventName: string, + ...additionalEventNames: string[] +): void; // [EventsOffAll](https://wails.io/docs/reference/runtime/events#eventsoffall) // unregisters all listeners. @@ -200,7 +213,12 @@ export function WindowIsNormal(): Promise; // [WindowSetBackgroundColour](https://wails.io/docs/reference/runtime/window#windowsetbackgroundcolour) // Sets the background colour of the window to the given RGBA colour definition. This colour will show through for all transparent pixels. -export function WindowSetBackgroundColour(R: number, G: number, B: number, A: number): void; +export function WindowSetBackgroundColour( + R: number, + G: number, + B: number, + A: number +): void; // [ScreenGetAll](https://wails.io/docs/reference/runtime/window#screengetall) // Gets the all screens. Call this anew each time you want to refresh data from the underlying windowing system. @@ -236,95 +254,17 @@ export function ClipboardSetText(text: string): Promise; // [OnFileDrop](https://wails.io/docs/reference/runtime/draganddrop#onfiledrop) // OnFileDrop listens to drag and drop events and calls the callback with the coordinates of the drop and an array of path strings. -export function OnFileDrop(callback: (x: number, y: number ,paths: string[]) => void, useDropTarget: boolean) :void +export function OnFileDrop( + callback: (x: number, y: number, paths: string[]) => void, + useDropTarget: boolean +): void; // [OnFileDropOff](https://wails.io/docs/reference/runtime/draganddrop#dragandddropoff) // OnFileDropOff removes the drag and drop listeners and handlers. -export function OnFileDropOff() :void +export function OnFileDropOff(): void; // Check if the file path resolver is available export function CanResolveFilePaths(): boolean; // Resolves file paths for an array of files -export function ResolveFilePaths(files: File[]): void - -// Notification types -export interface NotificationOptions { - id: string; - title: string; - subtitle?: string; // macOS and Linux only - body?: string; - categoryId?: string; - data?: { [key: string]: any }; -} - -export interface NotificationAction { - id?: string; - title?: string; - destructive?: boolean; // macOS-specific -} - -export interface NotificationCategory { - id?: string; - actions?: NotificationAction[]; - hasReplyField?: boolean; - replyPlaceholder?: string; - replyButtonTitle?: string; -} - -// [InitializeNotifications](https://wails.io/docs/reference/runtime/notification#initializenotifications) -// Initializes the notification service for the application. -// This must be called before sending any notifications. -export function InitializeNotifications(): Promise; - -// [CleanupNotifications](https://wails.io/docs/reference/runtime/notification#cleanupnotifications) -// Cleans up notification resources and releases any held connections. -export function CleanupNotifications(): Promise; - -// [IsNotificationAvailable](https://wails.io/docs/reference/runtime/notification#isnotificationavailable) -// Checks if notifications are available on the current platform. -export function IsNotificationAvailable(): Promise; - -// [RequestNotificationAuthorization](https://wails.io/docs/reference/runtime/notification#requestnotificationauthorization) -// Requests notification authorization from the user (macOS only). -export function RequestNotificationAuthorization(): Promise; - -// [CheckNotificationAuthorization](https://wails.io/docs/reference/runtime/notification#checknotificationauthorization) -// Checks the current notification authorization status (macOS only). -export function CheckNotificationAuthorization(): Promise; - -// [SendNotification](https://wails.io/docs/reference/runtime/notification#sendnotification) -// Sends a basic notification with the given options. -export function SendNotification(options: NotificationOptions): Promise; - -// [SendNotificationWithActions](https://wails.io/docs/reference/runtime/notification#sendnotificationwithactions) -// Sends a notification with action buttons. Requires a registered category. -export function SendNotificationWithActions(options: NotificationOptions): Promise; - -// [RegisterNotificationCategory](https://wails.io/docs/reference/runtime/notification#registernotificationcategory) -// Registers a notification category that can be used with SendNotificationWithActions. -export function RegisterNotificationCategory(category: NotificationCategory): Promise; - -// [RemoveNotificationCategory](https://wails.io/docs/reference/runtime/notification#removenotificationcategory) -// Removes a previously registered notification category. -export function RemoveNotificationCategory(categoryId: string): Promise; - -// [RemoveAllPendingNotifications](https://wails.io/docs/reference/runtime/notification#removeallpendingnotifications) -// Removes all pending notifications from the notification center. -export function RemoveAllPendingNotifications(): Promise; - -// [RemovePendingNotification](https://wails.io/docs/reference/runtime/notification#removependingnotification) -// Removes a specific pending notification by its identifier. -export function RemovePendingNotification(identifier: string): Promise; - -// [RemoveAllDeliveredNotifications](https://wails.io/docs/reference/runtime/notification#removealldeliverednotifications) -// Removes all delivered notifications from the notification center. -export function RemoveAllDeliveredNotifications(): Promise; - -// [RemoveDeliveredNotification](https://wails.io/docs/reference/runtime/notification#removedeliverednotification) -// Removes a specific delivered notification by its identifier. -export function RemoveDeliveredNotification(identifier: string): Promise; - -// [RemoveNotification](https://wails.io/docs/reference/runtime/notification#removenotification) -// Removes a notification by its identifier (cross-platform convenience function). -export function RemoveNotification(identifier: string): Promise; \ No newline at end of file +export function ResolveFilePaths(files: File[]): void; diff --git a/frontend/wailsjs/runtime/runtime.js b/frontend/wailsjs/runtime/runtime.js index 556621ee..7674e0de 100644 --- a/frontend/wailsjs/runtime/runtime.js +++ b/frontend/wailsjs/runtime/runtime.js @@ -49,7 +49,7 @@ export function EventsOff(eventName, ...additionalEventNames) { } export function EventsOffAll() { - return window.runtime.EventsOffAll(); + return window.runtime.EventsOffAll(); } export function EventsOnce(eventName, callback) { @@ -240,59 +240,3 @@ export function CanResolveFilePaths() { export function ResolveFilePaths(files) { return window.runtime.ResolveFilePaths(files); } - -export function InitializeNotifications() { - return window.runtime.InitializeNotifications(); -} - -export function CleanupNotifications() { - return window.runtime.CleanupNotifications(); -} - -export function IsNotificationAvailable() { - return window.runtime.IsNotificationAvailable(); -} - -export function RequestNotificationAuthorization() { - return window.runtime.RequestNotificationAuthorization(); -} - -export function CheckNotificationAuthorization() { - return window.runtime.CheckNotificationAuthorization(); -} - -export function SendNotification(options) { - return window.runtime.SendNotification(options); -} - -export function SendNotificationWithActions(options) { - return window.runtime.SendNotificationWithActions(options); -} - -export function RegisterNotificationCategory(category) { - return window.runtime.RegisterNotificationCategory(category); -} - -export function RemoveNotificationCategory(categoryId) { - return window.runtime.RemoveNotificationCategory(categoryId); -} - -export function RemoveAllPendingNotifications() { - return window.runtime.RemoveAllPendingNotifications(); -} - -export function RemovePendingNotification(identifier) { - return window.runtime.RemovePendingNotification(identifier); -} - -export function RemoveAllDeliveredNotifications() { - return window.runtime.RemoveAllDeliveredNotifications(); -} - -export function RemoveDeliveredNotification(identifier) { - return window.runtime.RemoveDeliveredNotification(identifier); -} - -export function RemoveNotification(identifier) { - return window.runtime.RemoveNotification(identifier); -} \ No newline at end of file diff --git a/go.mod b/go.mod index 49cf0840..f9307500 100644 --- a/go.mod +++ b/go.mod @@ -1,14 +1,13 @@ module aether -go 1.25.0 +go 1.23 require ( - github.com/wailsapp/wails/v2 v2.15.0 - golang.org/x/image v0.41.0 + github.com/wailsapp/wails/v2 v2.11.0 + golang.org/x/image v0.23.0 ) require ( - git.sr.ht/~jackmordaunt/go-toast/v2 v2.0.3 // indirect github.com/bep/debounce v1.2.1 // indirect github.com/go-ole/go-ole v1.3.0 // indirect github.com/godbus/dbus/v5 v5.1.0 // indirect @@ -32,8 +31,8 @@ require ( github.com/valyala/fasttemplate v1.2.2 // indirect github.com/wailsapp/go-webview2 v1.0.22 // indirect github.com/wailsapp/mimetype v1.4.1 // indirect - golang.org/x/crypto v0.53.0 // indirect - golang.org/x/net v0.56.0 // indirect - golang.org/x/sys v0.46.0 // indirect - golang.org/x/text v0.39.0 // indirect + golang.org/x/crypto v0.33.0 // indirect + golang.org/x/net v0.35.0 // indirect + golang.org/x/sys v0.30.0 // indirect + golang.org/x/text v0.22.0 // indirect ) diff --git a/go.sum b/go.sum index 46af5d39..fcaee669 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,3 @@ -git.sr.ht/~jackmordaunt/go-toast/v2 v2.0.3 h1:N3IGoHHp9pb6mj1cbXbuaSXV/UMKwmbKLf53nQmtqMA= -git.sr.ht/~jackmordaunt/go-toast/v2 v2.0.3/go.mod h1:QtOLZGz8olr4qH2vWK0QH0w0O4T9fEIjMuWpKUsH7nc= github.com/bep/debounce v1.2.1 h1:v67fRdBA9UQu2NhLFXrSg0Brw7CexQekrBwDMM8bzeY= github.com/bep/debounce v1.2.1/go.mod h1:H8yggRPQKLUhUoqrJC1bO2xNya7vanpDl7xR3ISbCJ0= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -47,8 +45,8 @@ github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/samber/lo v1.49.1 h1:4BIFyVfuQSEpluc7Fua+j1NolZHiEHEpaSEKdsH0tew= github.com/samber/lo v1.49.1/go.mod h1:dO6KHFzUKXgP8LDhU0oI8d2hekjXnGOu0DB8Jecxd6o= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/tkrajina/go-reflector v0.5.8 h1:yPADHrwmUbMq4RGEyaOUpz2H90sRsETNVpjzo3DLVQQ= github.com/tkrajina/go-reflector v0.5.8/go.mod h1:ECbqLgccecY5kPmPmXg1MrHW585yMcDkVl6IvJe64T4= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= @@ -59,27 +57,27 @@ github.com/wailsapp/go-webview2 v1.0.22 h1:YT61F5lj+GGaat5OB96Aa3b4QA+mybD0Ggq6N github.com/wailsapp/go-webview2 v1.0.22/go.mod h1:qJmWAmAmaniuKGZPWwne+uor3AHMB5PFhqiK0Bbj8kc= github.com/wailsapp/mimetype v1.4.1 h1:pQN9ycO7uo4vsUUuPeHEYoUkLVkaRntMnHJxVwYhwHs= github.com/wailsapp/mimetype v1.4.1/go.mod h1:9aV5k31bBOv5z6u+QP8TltzvNGJPmNJD4XlAL3U+j3o= -github.com/wailsapp/wails/v2 v2.15.0 h1:u7cHK+UesZOlYxyJxfYLteaCPhws6UsZoDdqUejuX6Q= -github.com/wailsapp/wails/v2 v2.15.0/go.mod h1:scxrgwfsv6yR6fE6cCF+Flfl+JeU+SR87T9x4kILJ6M= -golang.org/x/crypto v0.53.0 h1:QZ4Muo8THX6CizN2vPPd5fBGHyogrdK9fG4wLPFUsto= -golang.org/x/crypto v0.53.0/go.mod h1:DNLU434OwVakk9PzuwV8w62mAJpRJL3vsgcfp4Qnsio= -golang.org/x/image v0.41.0 h1:8wS72eGJMJaBxK6okTzd4WaXumUlTVlb753MlsSvTCo= -golang.org/x/image v0.41.0/go.mod h1:uIc348UZMSvS5Z65CVZ7iDPaNobNFEPeJ4kbqTOszmA= +github.com/wailsapp/wails/v2 v2.11.0 h1:seLacV8pqupq32IjS4Y7V8ucab0WZwtK6VvUVxSBtqQ= +github.com/wailsapp/wails/v2 v2.11.0/go.mod h1:jrf0ZaM6+GBc1wRmXsM8cIvzlg0karYin3erahI4+0k= +golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus= +golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M= +golang.org/x/image v0.23.0 h1:HseQ7c2OpPKTPVzNjG5fwJsOTCiiwS4QdsYi5XU6H68= +golang.org/x/image v0.23.0/go.mod h1:wJJBTdLfCCf3tiHa1fNxpZmUI4mmoZvwMCPP0ddoNKY= golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.56.0 h1:Rw8j/hFzGvJUZwNBXnAtf5sVDVt+65SK2C7IxCxZt5o= -golang.org/x/net v0.56.0/go.mod h1:D3Ku6r+V6JROoZK144D2XfMHFcMq/0zSfLelVTCFKec= +golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8= +golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk= golang.org/x/sys v0.0.0-20200810151505-1b9f1253b3ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw= -golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= +golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.39.0 h1:UbZz4pLOvn600D6Oh6GGEI6VAmndrEBLv8/6BEXzyus= -golang.org/x/text v0.39.0/go.mod h1:3UwRclnC2g0TU9x8PZiyfOajCd1zaUNHF9cvqcQZ+ZM= +golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= +golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= From 3c00c9d9b94d40109f755706f3271c3aece626fc Mon Sep 17 00:00:00 2001 From: Triston Armstrong Date: Fri, 28 Aug 2026 17:13:30 -0500 Subject: [PATCH 08/17] Resolve rebase conflicts and fix models.ts structure --- app.go | 2 +- frontend/wailsjs/go/models.ts | 87 +---------------------------------- internal/theme/writer.go | 4 +- internal/theme/writer_test.go | 2 +- 4 files changed, 6 insertions(+), 89 deletions(-) diff --git a/app.go b/app.go index 84c76e26..f5365a4e 100644 --- a/app.go +++ b/app.go @@ -407,7 +407,7 @@ func (a *App) SaveAndApplyTheme(req SaveAndApplyThemeRequest) (*theme.ApplyResul } else if !os.IsNotExist(err) { return nil, fmt.Errorf("check theme folder: %w", err) } - wallpaperDest, err := a.writer.GenerateOmarchyV4Only(state, targetDir) + wallpaperDest, err := a.writer.GenerateOmarchyV4Only(state, req.Settings, targetDir) if err != nil { return nil, fmt.Errorf("save theme: %w", err) } diff --git a/frontend/wailsjs/go/models.ts b/frontend/wailsjs/go/models.ts index 65ea297b..7ea95034 100755 --- a/frontend/wailsjs/go/models.ts +++ b/frontend/wailsjs/go/models.ts @@ -499,6 +499,7 @@ export namespace theme { includeVscode: boolean; includeNeovim: boolean; selectedNeovimConfig: string; + includedApps?: Record; excludedApps?: Record; static createFrom(source: any = {}) { @@ -511,6 +512,7 @@ export namespace theme { this.includeVscode = source["includeVscode"]; this.includeNeovim = source["includeNeovim"]; this.selectedNeovimConfig = source["selectedNeovimConfig"]; + this.includedApps = source["includedApps"]; this.excludedApps = source["excludedApps"]; } } @@ -560,91 +562,6 @@ export namespace theme { return a; } } - - static createFrom(source: any = {}) { - return new ApplyResult(source); - } - - constructor(source: any = {}) { - if ('string' === typeof source) source = JSON.parse(source); - this.success = source['success']; - this.isOmarchy = source['isOmarchy']; - this.themePath = source['themePath']; - } - } - export class Settings { - includeZed: boolean; - includeVscode: boolean; - includeNeovim: boolean; - selectedNeovimConfig: string; - includedApps?: Record; - excludedApps?: Record; - - static createFrom(source: any = {}) { - return new Settings(source); - } - - constructor(source: any = {}) { - if ('string' === typeof source) source = JSON.parse(source); - this.includeZed = source['includeZed']; - this.includeVscode = source['includeVscode']; - this.includeNeovim = source['includeNeovim']; - this.selectedNeovimConfig = source['selectedNeovimConfig']; - this.includedApps = source['includedApps']; - this.excludedApps = source['excludedApps']; - } - } - export class StateSnapshot { - palette: string[]; - wallpaperPath: string; - lightMode: boolean; - lockedColors: Record; - colorRoles: template.ColorRoles; - extendedColors: Record; - extractionMode: string; - additionalImages: string[]; - appOverrides: Record; - - static createFrom(source: any = {}) { - return new StateSnapshot(source); - } - - constructor(source: any = {}) { - if ('string' === typeof source) source = JSON.parse(source); - this.palette = source['palette']; - this.wallpaperPath = source['wallpaperPath']; - this.lightMode = source['lightMode']; - this.lockedColors = source['lockedColors']; - this.colorRoles = this.convertValues( - source['colorRoles'], - template.ColorRoles - ); - this.extendedColors = source['extendedColors']; - this.extractionMode = source['extractionMode']; - this.additionalImages = source['additionalImages']; - this.appOverrides = source['appOverrides']; - } - - convertValues(a: any, classs: any, asMap: boolean = false): any { - if (!a) { - return a; - } - if (a.slice && a.map) { - return (a as any[]).map(elem => - this.convertValues(elem, classs) - ); - } else if ('object' === typeof a) { - if (asMap) { - for (const key of Object.keys(a)) { - a[key] = new classs(a[key]); - } - return a; - } - return new classs(a); - } - return a; - } - } } export namespace wallhaven { diff --git a/internal/theme/writer.go b/internal/theme/writer.go index ef836762..f546af9b 100644 --- a/internal/theme/writer.go +++ b/internal/theme/writer.go @@ -262,7 +262,7 @@ func (w *Writer) processOmarchyV4Templates( // GenerateOmarchyV4Only writes the files Omarchy v4 reads directly from a // reusable theme folder. Omarchy generates all other app-specific files. // Returns the wallpaper destination path ("" when no wallpaper was set). -func (w *Writer) GenerateOmarchyV4Only(state *ThemeState, outputPath string) (string, error) { +func (w *Writer) GenerateOmarchyV4Only(state *ThemeState, settings Settings, outputPath string) (string, error) { variables := template.BuildVariables(state.ColorRoles, state.LightMode, state.ExtendedColors) if err := validateTemplateInputs(variables, state.AppOverrides); err != nil { return "", err @@ -271,7 +271,7 @@ func (w *Writer) GenerateOmarchyV4Only(state *ThemeState, outputPath string) (st if err != nil { return "", err } - w.processOmarchyV4Templates(outputPath, variables, state.AppOverrides, state.ExtendedColors) + w.processOmarchyV4Templates(outputPath, variables, settings, state.AppOverrides, state.ExtendedColors) return wallpaperDest, nil } diff --git a/internal/theme/writer_test.go b/internal/theme/writer_test.go index 4c80ba3c..8590bfe4 100644 --- a/internal/theme/writer_test.go +++ b/internal/theme/writer_test.go @@ -308,7 +308,7 @@ func TestGenerateOmarchyV4OnlyRemovesLegacyFiles(t *testing.T) { state := NewThemeState() state.ColorRoles.Background = "#1e1e2e" state.ColorRoles.Magenta = "#ff0000" - if _, err := writer.GenerateOmarchyV4Only(state, themeDir); err != nil { + if _, err := writer.GenerateOmarchyV4Only(state, Settings{IncludedApps: map[string]bool{"icons": true}}, themeDir); err != nil { t.Fatal(err) } From 1a0b45114c8576c1d0007346a1370931ea361fc9 Mon Sep 17 00:00:00 2001 From: Triston Armstrong Date: Fri, 28 Aug 2026 17:20:34 -0500 Subject: [PATCH 09/17] Revert formatting changes in app.go --- app.go | 122 ++++++++++++++++++++++++++++----------------------------- 1 file changed, 61 insertions(+), 61 deletions(-) diff --git a/app.go b/app.go index f5365a4e..7cae1cfb 100644 --- a/app.go +++ b/app.go @@ -220,13 +220,13 @@ func (a *App) SetExtractionMode(mode string) { // SyncStateRequest mirrors the frontend editor state into Go so IPC readers // (`aether status`, etc.) reflect live edits without requiring Apply. type SyncStateRequest struct { - Palette []string `json:"palette"` - WallpaperPath string `json:"wallpaperPath"` - OriginalWallpaperPath string `json:"originalWallpaperPath"` - LightMode bool `json:"lightMode"` - ExtendedColors map[string]string `json:"extendedColors"` - AppOverrides map[string]map[string]string `json:"appOverrides"` - AdditionalImages []string `json:"additionalImages"` + Palette []string `json:"palette"` + WallpaperPath string `json:"wallpaperPath"` + OriginalWallpaperPath string `json:"originalWallpaperPath"` + LightMode bool `json:"lightMode"` + ExtendedColors map[string]string `json:"extendedColors"` + AppOverrides map[string]map[string]string `json:"appOverrides"` + AdditionalImages []string `json:"additionalImages"` } // SyncState is called (debounced) by the frontend whenever the editor state @@ -321,14 +321,14 @@ func (a *App) ComputeVariables(paletteSlice []string, extendedColors map[string] // ApplyThemeRequest is the payload from the frontend containing all current state. type ApplyThemeRequest struct { - Palette []string `json:"palette"` - WallpaperPath string `json:"wallpaperPath"` - OriginalWallpaperPath string `json:"originalWallpaperPath"` - LightMode bool `json:"lightMode"` - AdditionalImages []string `json:"additionalImages"` - ExtendedColors map[string]string `json:"extendedColors"` - Settings theme.Settings `json:"settings"` - AppOverrides map[string]map[string]string `json:"appOverrides"` + Palette []string `json:"palette"` + WallpaperPath string `json:"wallpaperPath"` + OriginalWallpaperPath string `json:"originalWallpaperPath"` + LightMode bool `json:"lightMode"` + AdditionalImages []string `json:"additionalImages"` + ExtendedColors map[string]string `json:"extendedColors"` + Settings theme.Settings `json:"settings"` + AppOverrides map[string]map[string]string `json:"appOverrides"` } // ApplyTheme processes all templates and applies the theme to the system. @@ -341,14 +341,14 @@ func (a *App) ApplyTheme(req ApplyThemeRequest) (*theme.ApplyResult, error) { } state := &theme.ThemeState{ - Palette: palette, - WallpaperPath: req.WallpaperPath, + Palette: palette, + WallpaperPath: req.WallpaperPath, OriginalWallpaperPath: req.OriginalWallpaperPath, - LightMode: req.LightMode, - ColorRoles: roles, - ExtendedColors: req.ExtendedColors, - AdditionalImages: req.AdditionalImages, - AppOverrides: appOverrides, + LightMode: req.LightMode, + ColorRoles: roles, + ExtendedColors: req.ExtendedColors, + AdditionalImages: req.AdditionalImages, + AppOverrides: appOverrides, } return a.writer.ApplyTheme(state, req.Settings) @@ -357,16 +357,16 @@ func (a *App) ApplyTheme(req ApplyThemeRequest) (*theme.ApplyResult, error) { // SaveAndApplyThemeRequest is the payload for saving the current state as a // named theme folder before activating it. type SaveAndApplyThemeRequest struct { - Name string `json:"name"` - UpdateExisting bool `json:"updateExisting"` - Palette []string `json:"palette"` - WallpaperPath string `json:"wallpaperPath"` - OriginalWallpaperPath string `json:"originalWallpaperPath"` - LightMode bool `json:"lightMode"` - AdditionalImages []string `json:"additionalImages"` - ExtendedColors map[string]string `json:"extendedColors"` - Settings theme.Settings `json:"settings"` - AppOverrides map[string]map[string]string `json:"appOverrides"` + Name string `json:"name"` + UpdateExisting bool `json:"updateExisting"` + Palette []string `json:"palette"` + WallpaperPath string `json:"wallpaperPath"` + OriginalWallpaperPath string `json:"originalWallpaperPath"` + LightMode bool `json:"lightMode"` + AdditionalImages []string `json:"additionalImages"` + ExtendedColors map[string]string `json:"extendedColors"` + Settings theme.Settings `json:"settings"` + AppOverrides map[string]map[string]string `json:"appOverrides"` } // SaveAndApplyTheme writes a reusable named theme folder before applying it. @@ -383,14 +383,14 @@ func (a *App) SaveAndApplyTheme(req SaveAndApplyThemeRequest) (*theme.ApplyResul palette, roles := buildColorRoles(req.Palette, req.ExtendedColors) state := &theme.ThemeState{ - Palette: palette, - WallpaperPath: req.WallpaperPath, + Palette: palette, + WallpaperPath: req.WallpaperPath, OriginalWallpaperPath: req.OriginalWallpaperPath, - LightMode: req.LightMode, - ColorRoles: roles, - ExtendedColors: req.ExtendedColors, - AdditionalImages: req.AdditionalImages, - AppOverrides: req.AppOverrides, + LightMode: req.LightMode, + ColorRoles: roles, + ExtendedColors: req.ExtendedColors, + AdditionalImages: req.AdditionalImages, + AppOverrides: req.AppOverrides, } if state.AppOverrides == nil { state.AppOverrides = make(map[string]map[string]string) @@ -984,16 +984,16 @@ func (a *App) HandleDroppedFiles(paths []string) (string, error) { // ExportThemeRequest is the payload from the frontend for exporting a theme. type ExportThemeRequest struct { - Name string `json:"name"` - IncludedApps []string `json:"includedApps"` - Palette []string `json:"palette"` - WallpaperPath string `json:"wallpaperPath"` - OriginalWallpaperPath string `json:"originalWallpaperPath"` - LightMode bool `json:"lightMode"` - AdditionalImages []string `json:"additionalImages"` - ExtendedColors map[string]string `json:"extendedColors"` - InstallToOmarchy bool `json:"installToOmarchy"` - AppOverrides map[string]map[string]string `json:"appOverrides"` + Name string `json:"name"` + IncludedApps []string `json:"includedApps"` + Palette []string `json:"palette"` + WallpaperPath string `json:"wallpaperPath"` + OriginalWallpaperPath string `json:"originalWallpaperPath"` + LightMode bool `json:"lightMode"` + AdditionalImages []string `json:"additionalImages"` + ExtendedColors map[string]string `json:"extendedColors"` + InstallToOmarchy bool `json:"installToOmarchy"` + AppOverrides map[string]map[string]string `json:"appOverrides"` } // allExportableApps is the full set of app names that can be exported. @@ -1032,14 +1032,14 @@ func (a *App) ExportTheme(req ExportThemeRequest) (string, error) { } state := &theme.ThemeState{ - Palette: palette, - WallpaperPath: req.WallpaperPath, + Palette: palette, + WallpaperPath: req.WallpaperPath, OriginalWallpaperPath: req.OriginalWallpaperPath, - LightMode: req.LightMode, - ColorRoles: roles, - ExtendedColors: req.ExtendedColors, - AdditionalImages: req.AdditionalImages, - AppOverrides: exportOverrides, + LightMode: req.LightMode, + ColorRoles: roles, + ExtendedColors: req.ExtendedColors, + AdditionalImages: req.AdditionalImages, + AppOverrides: exportOverrides, } // Build included set from the request @@ -1324,12 +1324,12 @@ func (a *App) HandleIPC(req ipc.Request) ipc.Response { case "apply": result, err := a.ApplyTheme(ApplyThemeRequest{ - Palette: a.state.Palette[:], - WallpaperPath: a.state.WallpaperPath, + Palette: a.state.Palette[:], + WallpaperPath: a.state.WallpaperPath, OriginalWallpaperPath: a.state.OriginalWallpaperPath, - LightMode: a.state.LightMode, - ExtendedColors: a.state.ExtendedColors, - AppOverrides: a.state.AppOverrides, + LightMode: a.state.LightMode, + ExtendedColors: a.state.ExtendedColors, + AppOverrides: a.state.AppOverrides, }) if err != nil { return ipc.Response{OK: false, Error: err.Error()} From 051f003caf563873b735f2e6c311c957eeedb0fa Mon Sep 17 00:00:00 2001 From: Triston Armstrong Date: Fri, 28 Aug 2026 17:21:03 -0500 Subject: [PATCH 10/17] Revert formatting changes in App.svelte --- frontend/src/App.svelte | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/frontend/src/App.svelte b/frontend/src/App.svelte index 41491afe..3497821a 100644 --- a/frontend/src/App.svelte +++ b/frontend/src/App.svelte @@ -282,8 +282,9 @@ // Listen for events from Go (async () => { try { - const {EventsOn, WindowSetBackgroundColour} = - await import('../wailsjs/runtime/runtime'); + const {EventsOn, WindowSetBackgroundColour} = await import( + '../wailsjs/runtime/runtime' + ); const applyThemeColors = (colors: Record) => { const root = document.documentElement; @@ -402,8 +403,9 @@ // Pull before subscribing-is-too-late: EventsOn attaches // after the watcher's startup emit has already fired. try { - const {GetThemeColors} = - await import('../wailsjs/go/main/App'); + const {GetThemeColors} = await import( + '../wailsjs/go/main/App' + ); const colors = await GetThemeColors(); if (colors && Object.keys(colors).length > 0) { applyThemeColors(colors); From ca06115b26b3d6e15bf44ae3d2b6ed6f3cc22708 Mon Sep 17 00:00:00 2001 From: Triston Armstrong Date: Fri, 28 Aug 2026 17:21:21 -0500 Subject: [PATCH 11/17] Revert formatting changes in themeActions.ts --- frontend/src/lib/actions/themeActions.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/frontend/src/lib/actions/themeActions.ts b/frontend/src/lib/actions/themeActions.ts index 990c35c1..231cb144 100644 --- a/frontend/src/lib/actions/themeActions.ts +++ b/frontend/src/lib/actions/themeActions.ts @@ -145,8 +145,9 @@ export async function saveAndApplyTheme( if (getIsApplying()) return; setIsApplying(true); try { - const {SaveAndApplyTheme} = - await import('../../../wailsjs/go/main/App'); + const {SaveAndApplyTheme} = await import( + '../../../wailsjs/go/main/App' + ); const result = await SaveAndApplyTheme({ name, updateExisting, @@ -190,8 +191,9 @@ export async function applyWallpaperOnly(originalPath: string): Promise { if (path.startsWith('http://') || path.startsWith('https://')) { try { showToast('Downloading wallpaper…'); - const {DownloadWallpaper} = - await import('../../../wailsjs/go/main/App'); + const {DownloadWallpaper} = await import( + '../../../wailsjs/go/main/App' + ); path = await DownloadWallpaper(path); } catch { showToast('Failed to download wallpaper'); From b12cb4d694b92bbc98e363a6eba98fec116f8a48 Mon Sep 17 00:00:00 2001 From: Triston Armstrong Date: Fri, 28 Aug 2026 17:21:55 -0500 Subject: [PATCH 12/17] Revert formatting changes in WallpaperHero.svelte --- .../components/editor/WallpaperHero.svelte | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/frontend/src/lib/components/editor/WallpaperHero.svelte b/frontend/src/lib/components/editor/WallpaperHero.svelte index 5c8266a3..430ad823 100644 --- a/frontend/src/lib/components/editor/WallpaperHero.svelte +++ b/frontend/src/lib/components/editor/WallpaperHero.svelte @@ -237,8 +237,9 @@ if (!path) return; setIsExtracting(true); try { - const {ExtractColors} = - await import('../../../../wailsjs/go/main/App'); + const {ExtractColors} = await import( + '../../../../wailsjs/go/main/App' + ); const colors = await ExtractColors( path, getLightMode(), @@ -261,8 +262,9 @@ if (paths.length === 0) return; setIsExtracting(true); try { - const {ExtractColorsFromImages} = - await import('../../../../wailsjs/go/main/App'); + const {ExtractColorsFromImages} = await import( + '../../../../wailsjs/go/main/App' + ); const result = await ExtractColorsFromImages( paths, getLightMode(), @@ -288,8 +290,9 @@ async function handleChange() { try { - const {OpenFileDialog} = - await import('../../../../wailsjs/go/main/App'); + const {OpenFileDialog} = await import( + '../../../../wailsjs/go/main/App' + ); const path = await OpenFileDialog(); if (path) { setWallpaperPath(path); @@ -363,7 +366,7 @@ {#if eyedropperActive}
Click to pick a color · Esc to cancel
@@ -525,7 +528,7 @@ class="border-fg-primary block border-2 shadow-lg" >
Date: Fri, 28 Aug 2026 17:22:32 -0500 Subject: [PATCH 13/17] Revert formatting changes in ActionBar.svelte --- .../lib/components/layout/ActionBar.svelte | 36 +++++++++++-------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/frontend/src/lib/components/layout/ActionBar.svelte b/frontend/src/lib/components/layout/ActionBar.svelte index 69db695f..3ec5ed15 100644 --- a/frontend/src/lib/components/layout/ActionBar.svelte +++ b/frontend/src/lib/components/layout/ActionBar.svelte @@ -177,8 +177,9 @@ async function handleClear() { try { - const {ClearTheme} = - await import('../../../../wailsjs/go/main/App'); + const {ClearTheme} = await import( + '../../../../wailsjs/go/main/App' + ); await ClearTheme(); showToast('Reverted to system theme'); } catch { @@ -188,8 +189,9 @@ async function handleReset() { try { - const {ResetState} = - await import('../../../../wailsjs/go/main/App'); + const {ResetState} = await import( + '../../../../wailsjs/go/main/App' + ); await ResetState(); resetTheme(); showToast('Editor reset'); @@ -202,8 +204,9 @@ async function handleExport() { if (!exportName.trim()) return; try { - const {ExportTheme} = - await import('../../../../wailsjs/go/main/App'); + const {ExportTheme} = await import( + '../../../../wailsjs/go/main/App' + ); const includedApps = Object.entries(exportApps) .filter(([, enabled]) => enabled) .map(([key]) => key); @@ -239,8 +242,9 @@ async function handleImport(fileType: string) { showImportMenu = false; try { - const {ImportFileDialog} = - await import('../../../../wailsjs/go/main/App'); + const {ImportFileDialog} = await import( + '../../../../wailsjs/go/main/App' + ); const result = await ImportFileDialog(fileType); console.log('Import result:', result); if (result?.colors?.length >= 16) { @@ -307,8 +311,9 @@ onclick={async () => { showExportDialog = true; try { - const {IsOmarchyInstalled} = - await import('../../../../wailsjs/go/main/App'); + const {IsOmarchyInstalled} = await import( + '../../../../wailsjs/go/main/App' + ); isOmarchy = await IsOmarchyInstalled(); } catch { isOmarchy = false; @@ -432,7 +437,7 @@ {/if} {#if dirty && !applying} {/if} @@ -465,7 +470,7 @@ role="presentation" >