diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index f3cf888..889f81a 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -1,34 +1,99 @@
-name: Build
-
+name: "Build"
on:
+ workflow_dispatch:
push:
branches:
- - main
- development
+ - main
pull_request:
branches:
- - main
- development
+ - main
+
+permissions:
+ contents: write
jobs:
- build:
+ package:
+ name: Package
strategy:
- fail-fast: false
matrix:
- build:
- [
- {
- name: desktop-manager,
- platform: windows/amd64,
- os: windows-latest,
- },
- ]
- runs-on: ${{ matrix.build.os }}
+ platform: [windows-latest]
+ build-name: ["iconium"]
+ go-version: [1.22]
+ node-version: [20]
+ runs-on: ${{ matrix.platform }}
steps:
- - uses: actions/checkout@v4
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - name: Setup Go
+ uses: actions/setup-go@v5
+ with:
+ go-version: ${{ matrix.go-version }}
+
+ - name: Setup Node
+ uses: actions/setup-node@v4
+ with:
+ node-version: ${{ matrix.node-version }}
+
+ - name: Setup Wails
+ run: go install github.com/wailsapp/wails/v2/cmd/wails@latest
+
+ - name: Build Wails app
+ run: wails build -nsis
+
+ - name: Sign Windows binaries
+ shell: powershell
+ run: |
+ echo "Creating certificate file"
+ New-Item -ItemType directory -Path certificate
+ Set-Content -Path certificate\certificate.txt -Value '${{ secrets.WIN_SIGNING_CERT }}'
+ certutil -decode certificate\certificate.txt certificate\certificate.pfx
+ echo "Signing Binary"
+ & 'C:/Program Files (x86)/Windows Kits/10/bin/10.0.17763.0/x86/signtool.exe' sign /fd sha256 /tr http://ts.ssl.com /f certificate\certificate.pfx /p '${{ secrets.WIN_SIGNING_CERT_PASSWORD }}' .\build\bin\${{matrix.build-name}}.exe
+ echo "Signing Installer"
+ & 'C:/Program Files (x86)/Windows Kits/10/bin/10.0.17763.0/x86/signtool.exe' sign /fd sha256 /tr http://ts.ssl.com /f certificate\certificate.pfx /p '${{ secrets.WIN_SIGNING_CERT_PASSWORD }}' .\build\bin\${{matrix.build-name}}-amd64-installer.exe
+
+ - name: Upload artifacts
+ uses: actions/upload-artifact@v4
with:
- submodules: recursive
- - uses: dAppServer/wails-build-action@v2.2
+ name: binaries
+ path: build/bin/*
+
+ extract-version:
+ if: github.ref == 'refs/heads/main'
+ name: Extract version
+ runs-on: ubuntu-latest
+
+ outputs:
+ version: ${{ steps.extract_version.outputs.version }}
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v3
+
+ - name: Extract version
+ id: extract_version
+ run: |
+ version=$(jq -r '.info.productVersion' wails.json)
+ echo "version=$version" >> $GITHUB_OUTPUT
+
+ create-release:
+ if: github.ref == 'refs/heads/main'
+ name: Create release
+ needs: [extract-version, package]
+ runs-on: ubuntu-latest
+ steps:
+ - name: Download artifacts
+ uses: actions/download-artifact@v4
with:
- build-name: ${{ matrix.build.name }}
- build-platform: ${{ matrix.build.platform }}
+ name: binaries
+ path: ./artifacts
+
+ - name: Create Draft Release
+ env:
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ GH_REPO: ${{ github.repository }}
+ run: |
+ gh release create v${{ needs.extract-version.outputs.version }} ./artifacts/* --title "Release v${{ needs.extract-version.outputs.version }}" --draft --generate-notes
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index 58f282c..f7929bc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,11 +7,6 @@ yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
-node_modules
-dist
-dist-ssr
-*.local
-
# Editor directories and files
.vscode/*
!.vscode/extensions.json
@@ -23,6 +18,17 @@ dist-ssr
*.sln
*.sw?
-# wails
+# Wails
*.exe~
-./build/bin/*
\ No newline at end of file
+*.syso
+*.local
+node_modules
+/build/bin
+/build/windows/installer/wails_tools.nsh
+/build/windows/installer/tmp
+/frontend/dist
+/frontend/dist-ssr
+/frontend/package.json.md5
+
+# Certificate files
+certificate.*
diff --git a/README.md b/README.md
index 98b0d2e..c2b88c2 100644
--- a/README.md
+++ b/README.md
@@ -1,32 +1,77 @@
-# Desktop Manager
-Icon and description editor, profile creation tool for your desktop. (WIP)
+# Iconium
-## Screenshots
+
+
+
+
+
+ Website
-
+**Iconium** is a flexible tool for creating and managing icon packs. Iconium can apply icons to `.lnk`, `.url` and directories and extract them from files such as `.png`, `.jpg`, `.jpeg`, `.webp`, `.svg`, `.bmp`, `.ico`, `.exe`, `.lnk` and `.url` files. It also provides advanced features like file matching with environment variables or wildcards and customizing icon radius and opacity.
-## Features
+## Table of Contents
+- [Iconium](#iconium)
+ - [Table of Contents](#table-of-contents)
+ - [Screenshots](#screenshots)
+ - [Features](#features)
+ - [Planned Features](#planned-features)
+ - [Installation](#installation)
+ - [Translation](#translation)
+ - [Technologies](#technologies)
+ - [License](#license)
-- Create desktop profiles
-- Edit icon and description
-- Shortcut (.lnk) support
+## Screenshots
+
+
+
-## Planned
+## Features
+- Create, distribute and use icon packs
+- Edit icon radius and opacity
+- Match files by environment variables, wildcards, or destination paths
+- Supports `.lnk`, `.url`, and directories
+- Can extract icons from `.png`, `.jpg`, `.jpeg`, `.webp`, `.svg`, `.bmp`, `.ico`, `.exe`, `.lnk` and `.url` files
+- Highly customizable appearance
+- Automatic update
+- Color schemes and light/dark mode for each scheme
-- Save desktop layout to profile
-- Share profiles as files
-- Folder and .url support
-- Sync desktop with profile
+## Planned Features
+- Save desktop layouts to icon packs
+- Auto-apply scheduling for icon packs
+- Custom icon masks
-## Built With
+## Installation
+1. Install Wails: [Wails installation guide](https://wails.io/docs/gettingstarted/installation).
+
+2. Clone the repository:
+ ```bash
+ git clone https://github.com/beyenilmez/iconium.git
+ ```
+3. Navigate to the project directory:
+ ```bash
+ cd iconium
+ ```
+4. Run in dev mode:
+ ```bash
+ wails dev
+ ```
+ or
+
+ Build:
+ ```bash
+ wails build
+ ```
+## Translation
+1. Create a copy of `frontend/public/locales/en-US.json` file and rename it using your language code, which you can find [here](https://learn.microsoft.com/en-us/openspecs/office_standards/ms-oe376/6c085406-a698-4e12-9d4d-c3b0ee3dbc4a).
+2. Translate the file to your language.
+3. Update `frontend/src/locales.json` to include your language.
-- [Wails](https://wails.io/)
-- [React](https://react.dev/)
-- [tailwindcss](https://tailwindcss.com/)
-- [shadcn-ui](https://ui.shadcn.com/)
-- [lucide-react](https://lucide.dev/guide/packages/lucide-react)
+## Technologies
+- **Backend**: [Go](https://go.dev/), [Wails](https://wails.io/)
+- **Frontend**: [React](https://react.dev/), [TailwindCSS](https://tailwindcss.com/), [shadcnUI](https://ui.shadcn.com/)
+- **Icon Processing**: [ImageMagick](https://imagemagick.org/), [ExtractIcon](https://github.com/bertjohnson/ExtractIcon)
+- **Translation**: [i18next](https://react.i18next.com/)
## License
-
-Distributed under the MIT License. See [LICENSE](https://github.com/beyenilmez/desktop-manager/blob/main/LICENSE) for more information.
+Distributed under the MIT License. See [LICENSE](https://github.com/beyenilmez/iconium/blob/main/LICENSE) for more information.
diff --git a/app.go b/app.go
index 1a7e025..0ad60cd 100644
--- a/app.go
+++ b/app.go
@@ -2,616 +2,318 @@ package main
import (
"context"
- _ "embed"
- "encoding/base64"
"encoding/json"
"fmt"
- "io"
- "io/fs"
- "net/http"
"os"
"os/exec"
- "os/user"
- "path"
- "path/filepath"
"strings"
+ "github.com/gen2brain/beeep"
"github.com/google/uuid"
- lnk "github.com/parsiya/golnk"
- runtime "github.com/wailsapp/wails/v2/pkg/runtime"
+ "github.com/wailsapp/wails/v2/pkg/options"
+ "github.com/wailsapp/wails/v2/pkg/runtime"
+ "golang.org/x/sys/windows"
)
-//go:embed frontend/public/setlnkicon.vbs
-var setlnkicon string
-
-//go:embed frontend/public/setlnkdesc.vbs
-var setlnkdesc string
-
// App struct
type App struct {
ctx context.Context
}
+var appContext context.Context
+var app *App
+
// NewApp creates a new App application struct
func NewApp() *App {
- return &App{}
+ app = &App{}
+ return app
}
// startup is called at application startup
func (a *App) startup(ctx context.Context) {
- // Perform your setup here
a.ctx = ctx
-}
-
-// domReady is called after front-end resources have been loaded
-func (a App) domReady(ctx context.Context) {
- // Add your action here
-}
-
-// beforeClose is called when the application is about to quit,
-// either by clicking the window close button or calling runtime.Quit.
-// Returning true will cause the application to continue, false will continue shutdown as normal.
-func (a *App) beforeClose(ctx context.Context) (prevent bool) {
- return false
-}
-
-// shutdown is called at application termination
-func (a *App) shutdown(ctx context.Context) {
- // Perform your teardown here
-}
-
-type fileInfo struct {
- Name string `json:"name"`
- Description string `json:"description"`
- Path string `json:"path"`
- Destination string `json:"destination"`
- IconDestination string `json:"iconDestination"`
- IconIndex int `json:"iconIndex"`
- Extension string `json:"extension"`
- IsFolder bool `json:"isFolder"`
- IconId string `json:"iconId"`
- IconName string `json:"iconName"`
-}
-
-type profileInfo struct {
- Value any `json:"value"`
- Label string `json:"label"`
-}
-
-type profile struct {
- Name string `json:"name"`
- Id string `json:"id"`
- Value []fileInfo `json:"value"`
-}
+ appContext = ctx
-// Allowed file types
-var allowedTypes = []string{".lnk"}
+ runtime.LogInfo(appContext, "Starting application")
-func CheckErr(err error, msg string, fatal bool) {
- if err != nil {
- fmt.Printf("%s - %v\n", msg, err)
- if fatal {
- panic(msg)
- }
+ // Set window position
+ if *config.WindowStartPositionX >= 0 && *config.WindowStartPositionY >= 0 {
+ runtime.LogInfo(appContext, "Setting window position")
+ runtime.WindowSetPosition(appContext, *config.WindowStartPositionX, *config.WindowStartPositionY)
}
-}
-// Check if an item is in a slice
-func contains(slice []string, item string) bool {
- for _, value := range slice {
- if value == item {
- return true
- }
+ // Set window size
+ if *config.WindowStartSizeX >= 0 && *config.WindowStartSizeY >= 0 && runtime.WindowIsNormal(appContext) {
+ runtime.LogInfo(appContext, "Setting window size")
+ runtime.WindowSetSize(appContext, *config.WindowStartSizeX, *config.WindowStartSizeY)
}
- return false
-}
-// Copy copies the contents of the file at srcpath to a regular file
-// at dstpath. If the file named by dstpath already exists, it is
-// truncated. The function does not copy the file mode, file
-// permission bits, or file attributes.
-// Source: https://stackoverflow.com/a/74107689
-func Copy(srcpath, dstpath string) (err error) {
- r, err := os.Open(srcpath)
- if err != nil {
- return err
- }
- defer r.Close() // ignore error: file was opened read-only.
+ // Initiate paths
+ runtime.LogInfo(appContext, "Initiating paths")
+ err := path_init()
- w, err := os.Create(dstpath)
if err != nil {
- return err
+ runtime.LogError(appContext, err.Error())
}
- defer func() {
- // Report the error, if any, from Close, but do so
- // only if there isn't already an outgoing error.
- if c := w.Close(); err == nil {
- err = c
- }
- }()
-
- _, err = io.Copy(w, r)
- return err
-}
-
-// Returns the save directory
-func getSaveDir() string {
- userConfigDir, err := os.UserConfigDir()
-
- CheckErr(err, "Failed to get user config dir", true)
+ // Delete old log files
+ runtime.LogInfo(appContext, "Deleting old log files")
+ delete_old_logs()
- return filepath.Join(userConfigDir, "desktop-manager")
-}
-
-// Returns the profile directory
-func getProfileDir() string {
- return filepath.Join(getSaveDir(), "profiles")
-}
-
-// Returns the icon directory
-func getIconDir(profileName string) string {
- return filepath.Join(getSaveDir(), "icon", profileName)
-}
-
-// Returns the base64 icon directory
-func getBase64Dir(profileName string) string {
- return filepath.Join(getIconDir(profileName), "base64")
+ // Check if configPath exists
+ if _, err := os.Stat(configPath); os.IsNotExist(err) {
+ onFirstRun()
+ }
}
-// Returns the script directory
-func getScriptDir() string {
- return filepath.Join(getSaveDir(), "scripts")
-}
+// domReady is called after front-end resources have been loaded
+func (a *App) domReady(ctx context.Context) {
+ // Get version from wails.json
+ var wailsDeccodedJSON map[string]interface{}
+ err := json.Unmarshal(wailsJSON, &wailsDeccodedJSON)
+ if err != nil {
+ runtime.LogError(appContext, "Failed to decode wails.json: "+err.Error())
+ }
+ version = wailsDeccodedJSON["info"].(map[string]interface{})["productVersion"].(string)
-// Returns the desktop paths
-func getDesktopPaths() []string {
- userDir, err := user.Current()
- CheckErr(err, "Failed to get user dir", true)
+ // Check if admin privileges are needed
+ NeedsAdminPrivileges = checkAdminPrivileges()
- homedir := userDir.HomeDir
- desktop := filepath.Join(homedir, "Desktop")
+ // Get launch args
+ args = os.Args[1:]
+ runtime.LogInfo(appContext, "Launch args: "+strings.Join(args, " "))
- public := "C:\\Users\\Public\\Desktop"
+ // Show window
+ runtime.WindowShow(appContext)
+ runtime.Show(appContext)
- return []string{desktop, public}
-}
+ // Check updates
+ if *config.CheckForUpdates {
+ updateInfo := a.CheckForUpdate()
-// GetFileInfo retrieves information about a file.
-//
-// Parameters:
-// - path: the path of the directory containing the file.
-// - file: the file to retrieve information about.
-//
-// Returns:
-// - fileInfo: the information about the file.
-// - error: an error if the file type is not allowed or if there was a failure reading the file.
-func GetFileInfo(path string, file *fs.DirEntry) (fileInfo, error) {
- fileName := (*file).Name()
- extension := filepath.Ext(fileName)
- noExtFileName := strings.TrimSuffix(fileName, extension)
-
- if !contains(allowedTypes, extension) {
- return fileInfo{}, fmt.Errorf("file type not allowed")
- } else if extension == ".lnk" {
- filePath := filepath.Join(path, fileName)
-
- lnkInfo := fileInfo{
- Name: noExtFileName,
- Description: "",
- Path: filePath,
- Destination: "",
- IconDestination: "",
- IconIndex: 0,
- Extension: extension,
- IsFolder: false,
- IconId: "",
- IconName: "",
+ if updateInfo.UpdateAvailable {
+ a.SendNotification("settings.setting.update.update_available", "v"+updateInfo.CurrentVersion+" ⭢ "+updateInfo.LatestVersion, "__settings__update", "info")
}
+ }
- f, err := lnk.File(filePath)
- CheckErr(err, "Failed to read lnk file", false)
+ // Install external programs
+ restore_missing_external_programs()
- if f.StringData.NameString != "" {
- lnkInfo.Description = f.StringData.NameString
+ for i := 0; i < len(args); i++ {
+ switch args[i] {
+ case "--goto":
+ if i+1 < len(args) {
+ runtime.LogInfo(a.ctx, fmt.Sprintf("Goto: %s", args[i+1]))
+ runtime.WindowExecJS(a.ctx, fmt.Sprintf(`window.goto("%s");`, args[i+1]))
+ i++
+ }
+ case "--notify":
+ if i+4 < len(args) {
+ runtime.LogInfo(a.ctx, "Notify: "+args[i+1]+" "+args[i+2]+" "+args[i+3]+" "+args[i+4])
+ a.SendNotification(args[i+1], args[i+2], args[i+3], args[i+4])
+ i += 4
+ }
+ default:
+ runtime.LogInfo(a.ctx, fmt.Sprintf("Pack path: %s", args[i]))
+ runtime.WindowExecJS(a.ctx, "window.importIconPack('"+strings.ReplaceAll(args[i], "\\", "\\\\")+"')")
}
+ }
+}
- if f.LinkInfo.LocalBasePath != "" {
- lnkInfo.Destination = f.LinkInfo.LocalBasePath
- }
- if f.LinkInfo.LocalBasePathUnicode != "" {
- lnkInfo.Destination = f.LinkInfo.LocalBasePathUnicode
+// beforeClose is called when the application is about to quit,
+// either by clicking the window close button or calling runtime.Quit.
+// Returning true will cause the application to continue, false will continue shutdown as normal.
+func (a *App) beforeClose(ctx context.Context) (prevent bool) {
+ if *config.SaveWindowStatus {
+ if runtime.WindowIsMaximised(a.ctx) {
+ var windowState = 2
+ config.WindowStartState = &windowState
+ runtime.LogInfo(a.ctx, "Setting window state to maximized")
+ } else {
+ var windowState = 0
+ config.WindowStartState = &windowState
+ runtime.LogInfo(a.ctx, "Setting window state to normal")
}
- if f.StringData.IconLocation != "" {
- lnkInfo.IconDestination = f.StringData.IconLocation
+ windowPositionX, windowPositionY := runtime.WindowGetPosition(a.ctx)
+ if windowPositionX < 0 {
+ windowPositionX = 0
}
-
- if f.Header.IconIndex != 0 {
- lnkInfo.IconIndex = int(f.Header.IconIndex)
+ if windowPositionY < 0 {
+ windowPositionY = 0
}
+ config.WindowStartPositionX, config.WindowStartPositionY = &windowPositionX, &windowPositionY
+ runtime.LogInfo(a.ctx, fmt.Sprintf("Setting window position to %d,%d", windowPositionX, windowPositionY))
- return lnkInfo, nil
- } else {
- return fileInfo{}, nil
+ windowSizeX, windowSizeY := runtime.WindowGetSize(a.ctx)
+ config.WindowStartSizeX, config.WindowStartSizeY = &windowSizeX, &windowSizeY
+ runtime.LogInfo(a.ctx, fmt.Sprintf("Setting window size to %d,%d", windowSizeX, windowSizeY))
}
-}
-// GetFileInfoSlice retrieves file information for the provided paths.
-//
-// paths: a slice of strings representing the directories to read files from.
-// []fileInfo: a slice of fileInfo structs containing information about the files.
-func GetFileInfoSlice(paths []string) []fileInfo {
- icons := []fileInfo{}
+ runtime.LogInfo(a.ctx, "Saving config")
+ err := WriteConfig(configPath)
- for _, path := range paths {
- currentFiles, pathError := os.ReadDir(path)
- CheckErr(pathError, "Failed to read directory", false)
-
- for _, file := range currentFiles {
- fileInfo, err := GetFileInfo(path, &file)
- CheckErr(err, "Failed to get file info", false)
-
- if fileInfo.Path != "" && contains(allowedTypes, fileInfo.Extension) {
- icons = append(icons, fileInfo)
- }
- }
- }
-
- return icons
-}
-
-func (a *App) AddProfile(name string) {
- // Create profile
- profile := profile{
- Name: name,
- Id: uuid.New().String(),
- Value: []fileInfo{},
+ if err != nil {
+ runtime.LogError(a.ctx, err.Error())
+ return false
}
- // Convert to JSON
- profileJSON, err := json.Marshal(profile)
- CheckErr(err, "Failed to marshal profile", false)
-
- // Get save directory
- profileDir := getProfileDir()
+ runtime.LogInfo(a.ctx, "Saving config complete")
- // Create folder
- err = os.MkdirAll(profileDir, os.ModePerm)
- CheckErr(err, "Failed to create profile folder", false)
-
- // Write json
- err = os.WriteFile(filepath.Join(profileDir, name), profileJSON, 0644)
- CheckErr(err, "Failed to write profile file", false)
-}
-
-func (a *App) RemoveProfile(profileName string) {
- profileDir := getProfileDir()
- err := os.Remove(filepath.Join(profileDir, profileName))
- CheckErr(err, "Failed to remove profile file", false)
+ return false
}
-func CopyIcons(profile *profile) {
- for i, fileInfo := range (*profile).Value {
- if filepath.Ext(fileInfo.IconDestination) == ".ico" {
- // Generate UUID and save path
- uuid := uuid.New().String()
- savePath := filepath.Join(getIconDir((*profile).Name), uuid+".ico")
-
- // Create dir
- err := os.MkdirAll(filepath.Dir(savePath), os.ModePerm)
- CheckErr(err, "Failed to create icon folder", false)
-
- // Copy icon
- err = Copy(fileInfo.IconDestination, savePath)
- CheckErr(err, "Failed to copy icon", false)
-
- // Generate base64 version
- GenerateBase64Icon(&(*profile).Name, &fileInfo.IconDestination, &uuid)
-
- // Icon name
- fileInfo.IconName = uuid
-
- (*profile).Value[i] = fileInfo
- }
- }
+// shutdown is called at application termination
+func (a *App) shutdown(ctx context.Context) {
+ // Perform your teardown here
}
-func GenerateBase64Icon(profileName *string, filePath *string, fileName *string) {
- // Create folder
- err := os.MkdirAll(getBase64Dir(*profileName), os.ModePerm)
- CheckErr(err, "Failed to create base64 image folder", false)
+// onSecondInstanceLaunch is called when the application is launched from a second instance
+func (a *App) onSecondInstanceLaunch(secondInstanceData options.SecondInstanceData) {
+ secondInstanceArgs := secondInstanceData.Args
- destination := filepath.Join(getBase64Dir(*profileName), *fileName)
-
- // Read the entire file into a byte slice
- bytes, err := os.ReadFile(*filePath)
+ file, err := os.Create(activeIconFolder + "a.txt")
if err != nil {
- fmt.Println(err)
- }
-
- var base64Encoding string
-
- // Determine the content type of the image file
- mimeType := http.DetectContentType(bytes)
-
- // Prepend the appropriate URI scheme header depending
- // on the MIME type
- switch mimeType {
- case "image/x-icon":
- base64Encoding += "data:image/x-icon;base64,"
- destination += ".ico"
- default:
- fmt.Println("Unknown MIME type: ", mimeType)
+ runtime.LogError(appContext, err.Error())
}
+ defer file.Close()
- // Append the base64 encoded output
- base64Encoding += base64.StdEncoding.EncodeToString(bytes)
+ runtime.LogDebug(a.ctx, "User opened a second instance "+strings.Join(secondInstanceArgs, ","))
+ runtime.LogDebug(a.ctx, "User opened a second instance from "+secondInstanceData.WorkingDirectory)
- // Write the full base64 representation of the image to file
- err = os.WriteFile(destination, []byte(base64Encoding), 0644)
- CheckErr(err, "Failed to write icon file", false)
-}
+ runtime.WindowUnminimise(a.ctx)
+ runtime.Show(a.ctx)
+ go runtime.EventsEmit(a.ctx, "launchArgs", secondInstanceArgs)
-func (a *App) SyncDesktop(profileName string, includeIcons bool) profile {
- profile := a.GetProfile(profileName)
- profile.Value = GetFileInfoSlice(getDesktopPaths())
- if includeIcons {
- CopyIcons(&profile)
+ if len(secondInstanceArgs) != 1 {
+ return
}
-
- profileJSON, err := json.Marshal(profile)
- CheckErr(err, "Failed to marshal profile", false)
- profileJSONStr := string(profileJSON)
-
- a.SaveProfile(profile.Name, profileJSONStr)
-
- return profile
+ runtime.WindowExecJS(a.ctx, "window.importIconPack('"+strings.ReplaceAll(secondInstanceArgs[0], "\\", "\\\\")+"')")
}
-func (a *App) GetFileInfo(profileName string) fileInfo {
- defaultDirectory := getDesktopPaths()[0]
-
- println("Default directory: ", defaultDirectory)
+func onFirstRun() {
+ runtime.LogInfo(appContext, "First run detected")
- result, err := runtime.OpenFileDialog(a.ctx, runtime.OpenDialogOptions{
- DefaultDirectory: defaultDirectory,
- Title: "Select Icon",
- Filters: []runtime.FileFilter{
- {
- DisplayName: "Shortcuts (*.lnk)",
- Pattern: "*.lnk",
- },
- },
- })
- CheckErr(err, "Failed to open file dialog", false)
-
- println("Selected file: ", result)
-
- if len(result) == 0 {
- return fileInfo{}
- } else {
- var file fs.DirEntry
-
- files, err := os.ReadDir(filepath.Dir(result))
- CheckErr(err, "Failed to read directory", false)
- for _, f := range files {
- if f.Name() == filepath.Base(result) {
- file = f
- break
- }
- }
-
- fileInfo, err := GetFileInfo(filepath.Dir(result), &file)
- CheckErr(err, "Failed to get file info", false)
-
- return fileInfo
- }
+ runtime.LogInfo(appContext, "Setting default system language")
+ set_system_language()
}
-func (a *App) GetDesktopIcons() []fileInfo {
- return GetFileInfoSlice(getDesktopPaths())
+func (a *App) GetVersion() string {
+ return version
}
-func (a *App) GetProfiles() []profileInfo {
- saveDir := getProfileDir()
-
- profiles, profilesError := os.ReadDir(saveDir)
-
- if profilesError != nil {
- println("No profile found in ", saveDir)
- }
-
- profileInfos := []profileInfo{}
+// Send notification
+func (a *App) SendNotification(title string, message string, path string, variant string) {
+ runtime.LogInfo(a.ctx, "Sending notification")
- for _, profile := range profiles {
- profileInfos = append(profileInfos, profileInfo{
- Value: profile.Name(),
- Label: profile.Name(),
- })
+ if runtime.WindowIsNormal(a.ctx) || runtime.WindowIsMaximised(a.ctx) || runtime.WindowIsFullscreen(a.ctx) {
+ if path != "" {
+ runtime.WindowExecJS(a.ctx, `window.toast({
+ title: "`+title+`",
+ description: "`+message+`",
+ path: "`+path+`",
+ variant: "`+variant+`"
+ });`)
+ } else {
+ runtime.WindowExecJS(a.ctx, `window.toast({
+ title: "`+title+`",
+ description: "`+message+`",
+ variant: "`+variant+`"
+ });`)
+ }
+ } else {
+ runtime.WindowExecJS(a.ctx, `window.sendNotification("`+title+`", "`+message+`", "`+path+`", "`+variant+`")`)
}
-
- return profileInfos
}
-func (a *App) GetProfile(profileName string) profile {
- saveDir := getProfileDir()
-
- profileValue, err := os.ReadFile(filepath.Join(saveDir, profileName))
+func (a *App) SendWindowsNotification(title string, message string, path string, variant string) {
+ err := beeep.Notify(title, message, appIconPath)
if err != nil {
- fmt.Println(err)
- }
-
- var profileInfoSlice profile
- jsonErr := json.Unmarshal(profileValue, &profileInfoSlice)
- if jsonErr != nil {
- fmt.Println(jsonErr)
- }
-
- valueBytes, marshalErr := json.Marshal(profileInfoSlice.Value)
- if marshalErr != nil {
- fmt.Println(marshalErr)
+ runtime.LogError(a.ctx, "Error sending notification: "+err.Error())
}
-
- var fileInfoSlice []fileInfo
- jsonErr = json.Unmarshal(valueBytes, &fileInfoSlice)
- if jsonErr != nil {
- fmt.Println(jsonErr)
- }
-
- profile := profile{
- Name: profileName,
- Value: fileInfoSlice,
- }
-
- return profile
-}
-
-func (a *App) SaveProfile(profileName string, profile string) {
- err := os.WriteFile(filepath.Join(getProfileDir(), profileName), []byte(profile), 0644)
- CheckErr(err, "Failed to write profile file", false)
-}
-
-func (a *App) GetIcon(profileName string, iconName string) string {
- saveDir := filepath.Join(getBase64Dir(profileName), iconName+".ico")
-
- bytes, err := os.ReadFile(saveDir)
- CheckErr(err, "Failed to read icon file", false)
-
- return string(bytes)
}
-func (a *App) SaveIcon(profileName string, fileInfo fileInfo) string {
- var defaultDirectory string
-
- // check if path exists
- if _, err := os.Stat(fileInfo.IconDestination); err == nil {
- defaultDirectory = filepath.Dir(fileInfo.IconDestination)
- } else if _, err := os.Stat(fileInfo.Destination); err == nil {
- defaultDirectory = filepath.Dir(fileInfo.Destination)
- } else {
- defaultDirectory = getDesktopPaths()[0]
- }
-
- println("Default directory: ", defaultDirectory)
-
- result, err := runtime.OpenFileDialog(a.ctx, runtime.OpenDialogOptions{
- DefaultDirectory: defaultDirectory,
- Title: "Select Icon",
- Filters: []runtime.FileFilter{
- {
- DisplayName: "*.ico",
- Pattern: "*.ico",
- },
- },
- })
-
+func (a *App) RestartApplication(admin bool, args []string) error {
+ // Get the path to the current executable
+ executable, err := os.Executable()
if err != nil {
- fmt.Println(err)
+ runtime.LogError(a.ctx, "failed to get executable path: "+err.Error())
+ return err
}
- println("Selected file: ", result)
+ if admin {
+ verb := "runas"
+ showCmd := 1 // SW_NORMAL
+ runtime.LogDebug(a.ctx, "Attempting to restart with elevated privileges")
- if len(result) == 0 {
- return ""
- } else {
- if fileInfo.IconName != "" {
- // Delete existing icon
- err := os.Remove(filepath.Join(getIconDir(profileName), fileInfo.IconName+".ico"))
- if err != nil {
- fmt.Println(err)
- }
-
- // Delete base64 version
- err = os.Remove(filepath.Join(getBase64Dir(profileName), fileInfo.IconName+".ico"))
- if err != nil {
- fmt.Println(err)
- }
+ executablePtr, err := windows.UTF16PtrFromString(executable)
+ if err != nil {
+ runtime.LogError(a.ctx, "failed to convert executable path to UTF16: "+err.Error())
+ return err
}
- uuid := uuid.New().String()
- savePath := filepath.Join(getIconDir(profileName), uuid+".ico")
+ // Convert arguments to a single string
+ argStr := strings.Join(args, " ")
- // Create dir
- noFolderErr := os.MkdirAll(filepath.Dir(savePath), os.ModePerm)
- if noFolderErr != nil {
- fmt.Println(noFolderErr)
+ argPtr, err := windows.UTF16PtrFromString(argStr)
+ if err != nil {
+ runtime.LogError(a.ctx, "failed to convert arguments to UTF16: "+err.Error())
+ return err
}
- // Copy icon
- err := Copy(result, savePath)
+ // Execute with elevated privileges
+ err = windows.ShellExecute(0, windows.StringToUTF16Ptr(verb), executablePtr, argPtr, nil, int32(showCmd))
if err != nil {
- fmt.Println(err)
+ runtime.LogError(a.ctx, "ShellExecute failed: "+err.Error())
+ return fmt.Errorf("ShellExecute failed: %w", err)
}
- // Generate base64 version
- GenerateBase64Icon(&profileName, &result, &uuid)
+ runtime.LogDebug(a.ctx, "Successfully requested elevated privileges")
+ a.beforeClose(a.ctx)
- return uuid
+ // Exit the current process
+ os.Exit(0)
+ return nil
}
-}
-func MatchMissingFile(fileInfo *fileInfo) {
- files, err := os.ReadDir(filepath.Dir(fileInfo.Path))
- CheckErr(err, "Failed to read directory", false)
-
- for _, f := range files {
- currentFileInfo, err := GetFileInfo(filepath.Dir((*fileInfo).Path), &f)
- CheckErr(err, "Failed to get file info", false)
- // Match by destination
- if currentFileInfo.Destination == (*fileInfo).Destination {
- fmt.Println("Found matching file: ", currentFileInfo)
- // Rename currentFile to fileInfo.name
- fmt.Println("Attempting to rename: ", currentFileInfo.Path, " to ", filepath.Join(filepath.Dir(currentFileInfo.Path), (*fileInfo).Name)+(*fileInfo).Extension)
- err := os.Rename(currentFileInfo.Path, filepath.Join(filepath.Dir(currentFileInfo.Path), (*fileInfo).Name)+(*fileInfo).Extension)
- CheckErr(err, "Failed to rename file", false)
- if err == nil {
- fmt.Println("Successfully renamed: ", currentFileInfo.Path, " to ", filepath.Join(filepath.Dir(currentFileInfo.Path), (*fileInfo).Name)+(*fileInfo).Extension)
- }
- }
- }
-}
+ // Create the new process with the same arguments as the current process
+ cmd := exec.Command(executable)
+ cmd.Args = append(cmd.Args, args...)
+ cmd.Env = os.Environ()
+ cmd.Stdin = os.Stdin
+ cmd.Stdout = os.Stdout
+ cmd.Stderr = os.Stderr
-func SetIcon(profileName *string, fileInfo *fileInfo) {
- // Check if fileInfo.Path exists
- if _, err := os.Stat(filepath.Join(filepath.Dir((*fileInfo).Path), (*fileInfo).Name) + (*fileInfo).Extension); err != nil {
- fmt.Println("Failed to find file: ", (*fileInfo).Path)
- fmt.Println("Attempting to match missing file: ", (*fileInfo))
- MatchMissingFile(fileInfo)
- }
+ runtime.LogDebug(a.ctx, "Attempting to restart without elevated privileges")
- // Check if type is allowed
- if contains(allowedTypes, (*fileInfo).Extension) && (*fileInfo).IconName != "" {
- err := os.WriteFile(path.Join(getScriptDir(), "setlnkicon.vbs"), []byte(setlnkicon), 0644)
- CheckErr(err, "Failed to write setlnkicon.vbs", false)
+ // Start the new process
+ if err := cmd.Start(); err != nil {
+ runtime.LogError(a.ctx, "failed to start new process: "+err.Error())
+ return err
+ }
- cmd := exec.Command("cscript.exe", getScriptDir()+"\\setlnkicon.vbs", filepath.Dir((*fileInfo).Path), filepath.Base((*fileInfo).Path), getIconDir(*profileName)+"\\"+(*fileInfo).IconName+".ico", "0")
+ runtime.LogDebug(a.ctx, "Successfully started new process")
+ a.beforeClose(a.ctx)
- _, err = cmd.Output()
- CheckErr(err, "Failed to execute command", false)
- }
+ // Exit the current process
+ os.Exit(0)
+ return nil
}
-func SetDesc(fileInfo *fileInfo) {
- // Check if type is allowed
- if contains(allowedTypes, (*fileInfo).Extension) && (*fileInfo).Description != "" {
- err := os.WriteFile(path.Join(getScriptDir(), "setlnkdesc.vbs"), []byte(setlnkdesc), 0644)
- CheckErr(err, "Failed to write setlnkdesc.vbs", false)
+func checkAdminPrivileges() bool {
+ systemroot := os.Getenv("systemroot")
- cmd := exec.Command("cscript.exe", getScriptDir()+"\\setlnkdesc.vbs", filepath.Dir((*fileInfo).Path), filepath.Base((*fileInfo).Path), (*fileInfo).Description)
-
- _, err = cmd.Output()
- CheckErr(err, "Failed to execute command", false)
+ // Try to create a temporary file in the directory
+ tempFile, err := os.CreateTemp(systemroot, uuid.NewString())
+ if err != nil {
+ return os.IsPermission(err)
}
-}
+ tempFile.Close()
+ os.Remove(tempFile.Name())
-func (a *App) RunProfile(profileName string, fileInfos []fileInfo) {
- // Create scripts directory
- err := os.MkdirAll(getScriptDir(), os.ModePerm)
- CheckErr(err, "Failed to create script folder", false)
+ return false
+}
- for _, fileInfo := range fileInfos {
- SetIcon(&profileName, &fileInfo)
- SetDesc(&fileInfo)
- }
+func (app *App) NeedsAdminPrivileges() bool {
+ return NeedsAdminPrivileges
}
diff --git a/app_paths.go b/app_paths.go
new file mode 100644
index 0000000..66dff14
--- /dev/null
+++ b/app_paths.go
@@ -0,0 +1,604 @@
+package main
+
+import (
+ "embed"
+ "errors"
+ "fmt"
+ "io/fs"
+ "net/http"
+ "os"
+ "os/user"
+ "path/filepath"
+ "strconv"
+ "strings"
+ "sync"
+
+ "github.com/google/uuid"
+ cmap "github.com/orcaman/concurrent-map/v2"
+ "github.com/wailsapp/wails/v2/pkg/runtime"
+)
+
+//go:embed frontend/public/scripts
+var scriptsFolderEmbedded embed.FS
+
+var setLnkIconScriptPath string
+var setLnkDescScriptPath string
+
+var appFolder string
+
+var packsFolder string
+var logsFolder string
+var savedConfigFolder string
+var activeIconFolder string
+var tempFolder string
+var maskFolder string
+var scriptsFolder string
+var externalFolder string
+var configPath string
+var appIconPath string
+
+var imageMagickFolder string
+var extractIconFolder string
+var imageMagickPath string
+var extractIconPath string
+
+var tempPngPaths = cmap.New[string]()
+var deletePngPaths []string = []string{}
+
+var selectImages = cmap.New[SelectImage]()
+
+type SelectImage struct {
+ Id string `json:"id"`
+ Path string `json:"path"`
+ TempPath string `json:"tempPath"`
+ HasOriginal bool `json:"hasOriginal"`
+ HasTemp bool `json:"hasTemp"`
+ IsRemoved bool `json:"isRemoved"`
+}
+
+func path_init() error {
+ appData, err := os.UserConfigDir()
+ if err != nil {
+ appData = os.Getenv("APPDATA")
+ if appData == "" {
+ return errors.New("Could not find user config directory: " + err.Error())
+ }
+ }
+ runtime.LogDebug(appContext, "Found user config directory: "+appData)
+
+ appFolder = filepath.Join(appData, "iconium")
+
+ packsFolder = filepath.Join(appFolder, "packs")
+ logsFolder = filepath.Join(appFolder, "logs")
+ savedConfigFolder = filepath.Join(appFolder, "savedconfigs")
+ activeIconFolder = filepath.Join(appFolder, "icons")
+ tempFolder = filepath.Join(appFolder, "temp")
+ maskFolder = filepath.Join(appFolder, "masks")
+ scriptsFolder = filepath.Join(appFolder, "scripts")
+ externalFolder = filepath.Join(appFolder, "external")
+
+ configPath = filepath.Join(appFolder, "config.json")
+ appIconPath = filepath.Join(appFolder, "appicon.png")
+ setLnkIconScriptPath = filepath.Join(scriptsFolder, "setlnkicon.vbs")
+ setLnkDescScriptPath = filepath.Join(scriptsFolder, "setlnkdesc.vbs")
+
+ runtime.LogTrace(appContext, "Attempting to create folders")
+ err = create_folder(appFolder)
+ if err != nil {
+ return err
+ }
+
+ err = create_folder(packsFolder)
+ if err != nil {
+ return err
+ }
+ err = create_folder(logsFolder)
+ if err != nil {
+ return err
+ }
+ err = create_folder(savedConfigFolder)
+ if err != nil {
+ return err
+ }
+ err = create_folder(activeIconFolder)
+ if err != nil {
+ return err
+ }
+ err = create_folder(tempFolder)
+ if err != nil {
+ return err
+ }
+ err = create_folder(maskFolder)
+ if err != nil {
+ return err
+ }
+ err = create_folder(scriptsFolder)
+ if err != nil {
+ return err
+ }
+ err = create_folder(externalFolder)
+ if err != nil {
+ return err
+ }
+
+ runtime.LogTrace(appContext, "Creating folders complete")
+
+ runtime.LogTrace(appContext, "Attempting to create appicon")
+
+ // Create icon from embedded appIcon if it exists
+ if _, err := os.Stat(appIconPath); os.IsNotExist(err) {
+ runtime.LogTrace(appContext, "appicon not found, creating from embedded appIcon")
+ err = os.WriteFile(appIconPath, appIcon, 0o644)
+ if err != nil {
+ return err
+ }
+ }
+
+ runtime.LogTrace(appContext, "Creating appicon complete")
+
+ imageMagickFolder = filepath.Join(externalFolder, "ImageMagick-7.1.1-35-portable-Q16-x64")
+ imageMagickPath = filepath.Join(imageMagickFolder, "magick.exe")
+ runtime.LogDebugf(appContext, "ImageMagick path: %s", imageMagickPath)
+
+ extractIconFolder = filepath.Join(externalFolder, "ExtractIcon")
+ extractIconPath = filepath.Join(extractIconFolder, "extracticon.exe")
+ runtime.LogDebugf(appContext, "ExtractIcon path: %s", extractIconPath)
+
+ // Copy all files in scriptsFolderEmbedded to scriptsFolder
+ runtime.LogTrace(appContext, "Attempting to copy scripts from embedded folder to scripts folder")
+
+ err = fs.WalkDir(scriptsFolderEmbedded, ".", func(filePath string, d fs.DirEntry, err error) error {
+ if err != nil {
+ return err
+ }
+
+ // Skip directories, but ensure they exist in the target location
+ if d.IsDir() {
+ return nil
+ }
+
+ // Remove the prefix "frontend/public/scripts" from the file path
+ relativePath, err := filepath.Rel("frontend/public/scripts", filePath)
+ if err != nil {
+ return err
+ }
+
+ // Construct the full destination path
+ destFile := filepath.Join(scriptsFolder, relativePath)
+
+ // Check if destination file already exists
+ if _, err := os.Stat(destFile); err == nil {
+ return nil
+ }
+
+ // Create directories if they do not exist
+ if err := os.MkdirAll(filepath.Dir(destFile), 0o755); err != nil {
+ return err
+ }
+
+ // Copy the file contents to the target destination
+ contents, err := scriptsFolderEmbedded.ReadFile(filePath)
+ if err != nil {
+ return err
+ }
+ err = os.WriteFile(destFile, contents, 0o755)
+ if err != nil {
+ return err
+ }
+
+ return nil
+ })
+
+ if err != nil {
+ return err
+ }
+
+ runtime.LogTrace(appContext, "Copying scripts complete")
+
+ runtime.LogTrace(appContext, "Path initialization complete")
+
+ return nil
+}
+
+func get_logs_folder() (string, error) {
+ logsFolder = filepath.Join(os.Getenv("APPDATA"), "iconium", "logs")
+
+ // Create folder if it doesn't exist
+ if _, err := os.Stat(logsFolder); os.IsNotExist(err) {
+ err = os.MkdirAll(logsFolder, 0o755)
+ if err != nil {
+ return "", err
+ }
+ }
+ return logsFolder, nil
+}
+
+func get_config_path() string {
+ configPath = filepath.Join(os.Getenv("APPDATA"), "iconium", "config.json")
+
+ return configPath
+}
+
+// Returns the desktop paths
+func get_desktop_paths() (string, string) {
+ userDir, err := user.Current()
+
+ if err != nil {
+ return "", ""
+ }
+
+ homedir := userDir.HomeDir
+ desktop := filepath.Join(homedir, "Desktop")
+
+ publicDir := os.Getenv("PUBLIC")
+ public := filepath.Join(publicDir, "Desktop")
+
+ return desktop, public
+}
+
+func restore_missing_external_programs() {
+ // URL and filename mapping
+ filesToDownload := map[string]string{
+ "https://raw.githubusercontent.com/beyenilmez/iconium/main/build/ImageMagick-7.1.1-35-portable-Q16-x64/magick.exe": imageMagickFolder + "/magick.exe",
+ "https://raw.githubusercontent.com/beyenilmez/iconium/main/build/ImageMagick-7.1.1-35-portable-Q16-x64/colors.xml": imageMagickFolder + "/colors.xml",
+ "https://raw.githubusercontent.com/beyenilmez/iconium/main/build/ImageMagick-7.1.1-35-portable-Q16-x64/LICENSE.txt": imageMagickFolder + "/LICENSE.txt",
+ "https://raw.githubusercontent.com/beyenilmez/iconium/main/build/ExtractIcon/extracticon.exe": extractIconFolder + "/extracticon.exe",
+ "https://raw.githubusercontent.com/beyenilmez/iconium/main/build/ExtractIcon/LICENSE": extractIconFolder + "/LICENSE",
+ }
+
+ // Filter out files that already exist
+ for url := range filesToDownload {
+ if exists(filesToDownload[url]) {
+ delete(filesToDownload, url)
+ }
+ }
+
+ var wg sync.WaitGroup
+ progress := make(chan int64) // Channel for progress percentage
+ errors := make(chan error, len(filesToDownload))
+ done := make(chan struct{})
+
+ // Calculate total size of files to download
+ totalSize := int64(0)
+ for url := range filesToDownload {
+ resp, err := http.Head(url)
+ if err != nil {
+ runtime.LogError(appContext, err.Error())
+ return
+ }
+ contentLength := resp.Header.Get("Content-Length")
+ size, _ := strconv.ParseInt(contentLength, 10, 64)
+ totalSize += size
+ }
+
+ runtime.LogInfo(appContext, "Total size of files to download: "+strconv.FormatInt(totalSize, 10))
+
+ if totalSize == 0 {
+ runtime.LogInfo(appContext, "No files to download")
+ return
+ }
+
+ app.SendNotification("downloading_missing_external_programs", "", "", "info")
+
+ // Create folders if they don't exist
+ if err := create_folder(imageMagickFolder); err != nil {
+ runtime.LogError(appContext, err.Error())
+ return
+ }
+ if err := create_folder(extractIconFolder); err != nil {
+ runtime.LogError(appContext, err.Error())
+ return
+ }
+
+ runtime.LogInfo(appContext, "Downloading missing external programs...")
+
+ // Start downloading files
+ for url, dest := range filesToDownload {
+ wg.Add(1)
+ go downloadFile(url, dest, progress, errors, &wg)
+ }
+
+ sucessfull := true
+
+ // Start a goroutine to track progress
+ go func() {
+ var downloadedSize int64
+ for {
+ select {
+ case size := <-progress:
+ downloadedSize += size
+ percentage := float64(downloadedSize) / float64(totalSize) * 100
+ runtime.LogDebugf(appContext, "Progress: %.2f%%\n", percentage)
+ runtime.WindowExecJS(appContext, fmt.Sprintf(`window.setProgress(%f)`, percentage))
+ if downloadedSize == totalSize {
+ close(progress)
+ done <- struct{}{}
+ return
+ }
+ case err := <-errors:
+ if err != nil {
+ runtime.LogError(appContext, err.Error())
+ sucessfull = false
+ }
+ }
+ }
+ }()
+
+ // Wait for all downloads to complete
+ wg.Wait()
+ <-done
+
+ if sucessfull {
+ app.SendNotification("downloaded_missing_external_programs", "", "", "success")
+ }
+
+ runtime.WindowExecJS(appContext, `window.setProgress(0)`)
+
+ runtime.LogInfo(appContext, "Download complete")
+}
+
+func (a *App) ClearTempPngPaths() {
+ for i := range tempPngPaths.IterBuffered() {
+ err := os.Remove(i.Val)
+ if err != nil {
+ runtime.LogErrorf(appContext, "Error removing tempPngPath: %s", err)
+ continue
+ }
+ }
+
+ tempPngPaths.Clear()
+
+ runtime.LogDebug(appContext, "Cleared tempPngPaths")
+}
+
+func (a *App) GetTempPngPath(id string) string {
+ tempPath, ok := tempPngPaths.Get(id)
+ if ok {
+ tempPath = strings.TrimPrefix(tempPath, appFolder)
+ return tempPath
+ } else {
+ return ""
+ }
+}
+
+func (a *App) AddTempPngPath(id string, path string) {
+ if !contains(allowedImageExtensionsPng, filepath.Ext(path)) {
+ runtime.LogError(appContext, "Extension is not allowed: "+path+" ,Full path: "+path)
+ return
+ }
+
+ oldPath, ok := tempPngPaths.Get(id)
+
+ tempPngPath := filepath.Join(tempFolder, "iconium-"+uuid.NewString()+".png")
+ err := ConvertToPng(path, tempPngPath)
+
+ if err != nil {
+ runtime.LogErrorf(appContext, "Error converting to png: %s", err)
+ return
+ }
+
+ if ok {
+ err = os.Remove(oldPath)
+ if err != nil {
+ runtime.LogErrorf(appContext, "Error removing old temp png: %s", err)
+ return
+ }
+ }
+
+ tempPngPaths.Set(id, tempPngPath)
+}
+
+func (a *App) RemoveTempPng(id string) {
+ val, ok := tempPngPaths.Get(id)
+ if !ok {
+ runtime.LogWarning(appContext, "Temp png not found: "+id)
+ return
+ }
+
+ err := os.Remove(val)
+ if err != nil {
+ runtime.LogErrorf(appContext, "Error removing temp png: %s", err)
+ return
+ }
+
+ tempPngPaths.Remove(id)
+}
+
+func (a *App) AddDeletePngRelativePath(relPath string) {
+ path := filepath.Join(appFolder, relPath)
+
+ deletePngPaths = append(deletePngPaths, path)
+}
+
+func (a *App) ClearDeletePngPaths() {
+ deletePngPaths = []string{}
+}
+
+func (a *App) RemoveDeletePng(path string) {
+ paths := deletePngPaths
+
+ for i := 0; i < len(paths); i++ {
+ if paths[i] == path {
+ paths = append(paths[:i], paths[i+1:]...)
+ break
+ }
+ }
+
+ deletePngPaths = paths
+}
+
+func (a *App) DeleteDeletePngPaths() {
+ for _, path := range deletePngPaths {
+ err := os.Remove(path)
+ if err != nil {
+ runtime.LogErrorf(appContext, "Error removing delete png: %s", err)
+ }
+ }
+
+ deletePngPaths = []string{}
+}
+
+func (a *App) GetSelectImage(id string, path string) SelectImage {
+ selectImage, ok := selectImages.Get(id)
+ if ok {
+ return selectImage
+ }
+
+ fullPath := filepath.Join(appFolder, path)
+
+ isEmpty := path == ""
+
+ if filepath.Ext(path) != ".png" {
+ isEmpty = true
+ } else if !isEmpty {
+ isEmpty = !exists(fullPath)
+ }
+
+ selectImage = SelectImage{
+ Id: id,
+ Path: path,
+ TempPath: "",
+ HasOriginal: !isEmpty,
+ HasTemp: false,
+ IsRemoved: false,
+ }
+
+ selectImages.Set(id, selectImage)
+
+ return selectImage
+}
+
+func (a *App) UploadSelectImage(id string) SelectImage {
+ tempPngPath := a.GetTempPng(id)
+ if tempPngPath == "" {
+ return a.GetSelectImage(id, "")
+ }
+
+ selectImage := a.GetSelectImage(id, "")
+
+ selectImage.TempPath = tempPngPath
+ selectImage.HasTemp = true
+ selectImages.Set(id, selectImage)
+
+ return selectImage
+}
+
+func (a *App) SetTempImage(id string, path string) error {
+ selectImage, ok := selectImages.Get(id)
+ if !ok {
+ return errors.New("select image not found")
+ }
+
+ if selectImage.HasTemp {
+ a.RemoveTempPng(id)
+ }
+
+ selectImage.TempPath, _ = filepath.Rel(appFolder, path)
+ selectImage.HasTemp = true
+ selectImages.Set(id, selectImage)
+
+ runtime.LogDebugf(appContext, "Set temp image: %s", path)
+
+ return nil
+}
+
+func (a *App) SetSelectImage(id string, path string) {
+ selectImage := a.GetSelectImage(id, path)
+
+ if !selectImage.HasTemp {
+ a.RemoveTempPng(id)
+ }
+
+ a.AddTempPngPath(id, path)
+
+ tempPngPath := a.GetTempPng(id)
+
+ if tempPngPath != "" {
+ selectImage.TempPath, _ = filepath.Rel(appFolder, tempPngPath)
+ selectImage.HasTemp = true
+ } else {
+ selectImage.TempPath = ""
+ selectImage.HasTemp = false
+ }
+
+ selectImages.Set(id, selectImage)
+
+ runtime.LogDebugf(appContext, "Set select image: %s", path)
+}
+
+func (a *App) ActionSelectImage(id string) SelectImage {
+ selectImage := a.GetSelectImage(id, "")
+
+ if selectImage.HasOriginal {
+ if selectImage.HasTemp {
+ runtime.LogDebugf(appContext, "Removing temp png: %s", selectImage.TempPath)
+
+ a.RemoveTempPng(id)
+
+ selectImage.HasTemp = false
+ selectImage.TempPath = ""
+ } else {
+ if selectImage.IsRemoved {
+ runtime.LogDebugf(appContext, "Retrieving original png: %s", selectImage.Path)
+
+ a.RemoveDeletePng(filepath.Join(appFolder, selectImage.Path))
+
+ selectImage.IsRemoved = false
+ } else {
+ runtime.LogDebugf(appContext, "Removing original png: %s", selectImage.Path)
+
+ a.AddDeletePngRelativePath(selectImage.Path)
+
+ selectImage.IsRemoved = true
+ }
+ }
+ } else if selectImage.HasTemp {
+ runtime.LogDebugf(appContext, "Removing temp png2: %s", selectImage.TempPath)
+
+ a.RemoveTempPng(id)
+
+ selectImage.HasTemp = false
+ selectImage.TempPath = ""
+ }
+
+ selectImages.Set(id, selectImage)
+
+ return selectImage
+}
+
+func (a *App) ClearSelectImages() {
+ selectImages.Clear()
+}
+
+func (a *App) SetImageIfAbsent(id string, path string) {
+ path = ConvertToFullPath(path)
+
+ runtime.LogInfo(appContext, "SetImageIfAbsent path: "+path)
+
+ if path == "" {
+ runtime.LogInfo(appContext, "SetImageIfAbsent: path is empty")
+ return
+ }
+
+ selectImage := a.GetSelectImage(id, path)
+ if selectImage.Id == "" {
+ runtime.LogInfo(appContext, "SetImageIfAbsent: select image is empty")
+ return
+ }
+
+ if !(selectImage.HasOriginal || selectImage.HasTemp) {
+ a.AddTempPngPath(id, path)
+ tempPngPath, ok := tempPngPaths.Get(id)
+
+ if ok {
+ err := a.SetTempImage(id, tempPngPath)
+ if err != nil {
+ runtime.LogError(appContext, "SetImageIfAbsent: error setting temp image: "+err.Error())
+ }
+ } else {
+ runtime.LogInfo(appContext, "SetImageIfAbsent: tempPngPath is empty")
+ }
+ } else {
+ runtime.LogInfo(appContext, "SetImageIfAbsent: hasOriginal or hasTemp is true")
+ }
+}
diff --git a/assets/screenshot-1.png b/assets/screenshot-1.png
index f5c391d..7a566f9 100644
Binary files a/assets/screenshot-1.png and b/assets/screenshot-1.png differ
diff --git a/assets/screenshot-2.png b/assets/screenshot-2.png
new file mode 100644
index 0000000..271e31f
Binary files /dev/null and b/assets/screenshot-2.png differ
diff --git a/assets/screenshot-3.png b/assets/screenshot-3.png
new file mode 100644
index 0000000..41bc45f
Binary files /dev/null and b/assets/screenshot-3.png differ
diff --git a/build/ExtractIcon/LICENSE b/build/ExtractIcon/LICENSE
new file mode 100644
index 0000000..bf812af
--- /dev/null
+++ b/build/ExtractIcon/LICENSE
@@ -0,0 +1,25 @@
+### License ###
+
+ExtractIcon (https://github.com/bertjohnson/extracticon)
+
+Licensed according to the MIT License (http://mit-license.org/).
+
+Copyright © Bert Johnson (https://bertjohnson.com/) of Allcloud Inc. (https://allcloud.com/).
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/build/ExtractIcon/extracticon.exe b/build/ExtractIcon/extracticon.exe
new file mode 100644
index 0000000..14dab83
Binary files /dev/null and b/build/ExtractIcon/extracticon.exe differ
diff --git a/build/ImageMagick-7.1.1-35-portable-Q16-x64/LICENSE.txt b/build/ImageMagick-7.1.1-35-portable-Q16-x64/LICENSE.txt
new file mode 100644
index 0000000..73c0a91
--- /dev/null
+++ b/build/ImageMagick-7.1.1-35-portable-Q16-x64/LICENSE.txt
@@ -0,0 +1,106 @@
+ ImageMagick License
+ https://imagemagick.org/script/license.php
+
+Before we get to the text of the license, lets just review what the license says in simple terms:
+
+It allows you to:
+
+ * freely download and use ImageMagick software, in whole or in part, for personal, company internal, or commercial purposes;
+ * use ImageMagick software in packages or distributions that you create;
+ * link against a library under a different license;
+ * link code under a different license against a library under this license;
+ * merge code into a work under a different license;
+ * extend patent grants to any code using code under this license;
+ * and extend patent protection.
+
+It forbids you to:
+
+ * redistribute any piece of ImageMagick-originated software without proper attribution;
+ * use any marks owned by ImageMagick Studio LLC in any way that might state or imply that ImageMagick Studio LLC endorses your distribution;
+ * use any marks owned by ImageMagick Studio LLC in any way that might state or imply that you created the ImageMagick software in question.
+
+It requires you to:
+
+ * include a copy of the license in any redistribution you may make that includes ImageMagick software;
+ * provide clear attribution to ImageMagick Studio LLC for any distributions that include ImageMagick software.
+
+It does not require you to:
+
+ * include the source of the ImageMagick software itself, or of any modifications you may have made to it, in any redistribution you may assemble that includes it;
+ * submit changes that you make to the software back to the ImageMagick Studio LLC (though such feedback is encouraged).
+
+A few other clarifications include:
+
+ * ImageMagick is freely available without charge;
+ * you may include ImageMagick on a DVD as long as you comply with the terms of the license;
+ * you can give modified code away for free or sell it under the terms of the ImageMagick license or distribute the result under a different license, but you need to acknowledge the use of the ImageMagick software;
+ * the license is compatible with the GPL V3.
+ * when exporting the ImageMagick software, review its export classification.
+
+Terms and Conditions for Use, Reproduction, and Distribution
+
+The legally binding and authoritative terms and conditions for use, reproduction, and distribution of ImageMagick follow:
+
+Copyright @ 1999 ImageMagick Studio LLC, a non-profit organization dedicated to making software imaging solutions freely available.
+
+1. Definitions.
+
+License shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
+
+Licensor shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
+
+Legal Entity shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, control means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
+
+You (or Your) shall mean an individual or Legal Entity exercising permissions granted by this License.
+
+Source form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.
+
+Object form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.
+
+Work shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).
+
+Derivative Works shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.
+
+Contribution shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as Not a Contribution.
+
+Contributor shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.
+
+2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.
+
+3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.
+
+4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:
+
+ * You must give any other recipients of the Work or Derivative Works a copy of this License; and
+ * You must cause any modified files to carry prominent notices stating that You changed the files; and
+ * You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
+ * If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License.
+You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.
+
+5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.
+
+6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an AS IS BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.
+
+8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.
+
+How to Apply the License to your Work
+
+To apply the ImageMagick License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information (don't include the brackets). The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the ImageMagick License (the "License"); you may not use
+ this file except in compliance with the License. You may obtain a copy
+ of the License at
+
+ https://imagemagick.org/script/license.php
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ License for the specific language governing permissions and limitations
+ under the License.
diff --git a/build/ImageMagick-7.1.1-35-portable-Q16-x64/colors.xml b/build/ImageMagick-7.1.1-35-portable-Q16-x64/colors.xml
new file mode 100644
index 0000000..201b735
--- /dev/null
+++ b/build/ImageMagick-7.1.1-35-portable-Q16-x64/colors.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+]>
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/build/ImageMagick-7.1.1-35-portable-Q16-x64/magick.exe b/build/ImageMagick-7.1.1-35-portable-Q16-x64/magick.exe
new file mode 100644
index 0000000..1d1ce4a
Binary files /dev/null and b/build/ImageMagick-7.1.1-35-portable-Q16-x64/magick.exe differ
diff --git a/build/appicon.png b/build/appicon.png
index 2a2aa26..84bcdc2 100644
Binary files a/build/appicon.png and b/build/appicon.png differ
diff --git a/build/default-pack/73571c68-a850-4f52-b923-3615434075a1.png b/build/default-pack/73571c68-a850-4f52-b923-3615434075a1.png
new file mode 100644
index 0000000..fa58213
Binary files /dev/null and b/build/default-pack/73571c68-a850-4f52-b923-3615434075a1.png differ
diff --git a/build/default-pack/files.json b/build/default-pack/files.json
new file mode 100644
index 0000000..f3497fb
--- /dev/null
+++ b/build/default-pack/files.json
@@ -0,0 +1 @@
+[{"id":"72651379-284e-4427-aad2-2fad80a3ba77","name":"Eclipse","description":"IDE for Java Developers","path":"${DESKTOP}\\**\\Eclipse*.lnk","destinationPath":"${USERPROFILE}\\eclipse\\java*\\eclipse\\eclipse.exe","extension":".lnk","hasIcon":true,"iconId":"c6c9f904-1d2e-44e4-9cef-45b1203f24bf"},{"id":"a183547f-bb5e-4743-9cd6-e2182fe1bfdb","name":"Microsoft Edge","description":"","path":"${DESKTOP}\\**\\Microsoft Edge.lnk","destinationPath":"${PROGRAMFILES(X86)}\\Microsoft\\Edge\\Application\\msedge.exe","extension":".lnk","hasIcon":true,"iconId":"6b6006e1-9435-4f88-826d-5b430d7c4b58"},{"id":"1b7d67c5-f51c-4225-bc78-c515aef0c723","name":"GitHub Desktop","description":"","path":"${DESKTOP}\\**\\GitHub Desktop.lnk","destinationPath":"${LOCALAPPDATA}\\GitHubDesktop\\GitHubDesktop.exe","extension":".lnk","hasIcon":true,"iconId":"b0e8a5f2-0312-479a-81dd-8bc12519cfe5"},{"id":"68399993-7482-43b7-8164-0e5f6f396dc8","name":"Steam","description":"Steam Client","path":"${DESKTOP}\\**\\Steam.lnk","destinationPath":"${PROGRAMFILES(X86)}\\Steam\\steam.exe","extension":".lnk","hasIcon":true,"iconId":"c55a9223-6c7d-494b-98eb-98e565b4471f"},{"id":"9c54b60a-588c-4cfc-9d3d-eef552872cc5","name":"IntelliJ IDEA","description":"IDE for Java Developers","path":"${DESKTOP}\\**\\IntelliJ IDEA.lnk","destinationPath":"${PROGRAMFILES}\\JetBrains\\IntelliJ IDEA Community Edition*\\bin\\idea64.exe","extension":".lnk","hasIcon":true,"iconId":"b1cf6f13-3391-4b4d-a459-16841e42ccb7"},{"id":"28393bcf-743c-47d7-b96e-c24d32e5d45d","name":"Discord","description":"","path":"${DESKTOP}\\**\\Discord.lnk","destinationPath":"${LOCALAPPDATA}\\Discord\\Update.exe","extension":".lnk","hasIcon":true,"iconId":"b607cca3-fd0a-4ea5-b0c0-28f2cd73347c"},{"id":"e3f31522-b0e3-4aa1-977c-ef77f832eec2","name":"Visual Studio Code","description":"","path":"${DESKTOP}\\**\\Visual Studio Code.lnk","destinationPath":"${LOCALAPPDATA}\\Programs\\Microsoft VS Code\\Code.exe","extension":".lnk","hasIcon":true,"iconId":"c6a7e324-3a3f-4f71-a6f5-2a0bca82f1e4"},{"id":"778325ab-785f-4dd9-9ff7-ed9c83436743","name":"Internet Download Manager","description":"","path":"${DESKTOP}\\**\\Internet Download Manager.lnk","destinationPath":"${PROGRAMFILES(X86)}\\Internet Download Manager\\IDMan.exe","extension":".lnk","hasIcon":true,"iconId":"0630dde6-201f-4fb3-90b9-35173dd317ee"},{"id":"d2fec138-5c89-499b-b1c8-2d77ab5f8d1c","name":"GeForce Experience","description":"","path":"${DESKTOP}\\**\\GeForce Experience.lnk","destinationPath":"${PROGRAMFILES}\\NVIDIA Corporation\\NVIDIA GeForce Experience\\NVIDIA GeForce Experience.exe","extension":".lnk","hasIcon":true,"iconId":"46d144db-254b-4f0f-ae3d-c9b524438767"},{"id":"37347aa4-6892-45b3-b43a-a04465e14c8f","name":"Logi Options+","description":"","path":"${DESKTOP}\\**\\Logi Options+.lnk","destinationPath":"${PROGRAMFILES}\\LogiOptionsPlus\\logioptionsplus.exe","extension":".lnk","hasIcon":true,"iconId":"36c2413a-34c1-4b02-8e20-1b8072e4ea38"},{"id":"dfba32d5-ab27-4713-ba4a-f4227d55f615","name":"PeaZip","description":"","path":"${DESKTOP}\\**\\PeaZip.lnk","destinationPath":"${PROGRAMFILES}\\PeaZip\\peazip.exe","extension":".lnk","hasIcon":true,"iconId":"bd639d37-743f-4fd5-adde-a108e2743dd8"},{"id":"bd6e07fc-f042-4297-bbac-fa8b6bc5d2c5","name":"AMD Software꞉ Adrenalin Edition","description":"","path":"${DESKTOP}\\**\\AMD Software꞉ Adrenalin Edition.lnk","destinationPath":"${PROGRAMFILES}\\AMD\\CNext\\CNext\\RadeonSoftware.exe","extension":".lnk","hasIcon":true,"iconId":"fc612aef-d94a-485b-9139-d0892edaafe5"},{"id":"5ccf00ca-a5fd-4700-8534-099797f9d27d","name":"Google Chrome","description":"","path":"${DESKTOP}\\**\\Google Chrome.lnk","destinationPath":"${PROGRAMFILES}\\Google\\Chrome\\Application\\chrome.exe","extension":".lnk","hasIcon":true,"iconId":"74234aff-2ab1-4f3a-9238-c108a7f9d5b2"},{"id":"94ec31b7-3d34-460a-85c9-80003294532a","name":"Ubisoft Connect","description":"Ubisoft Client","path":"${DESKTOP}\\**\\Ubisoft Connect.lnk","destinationPath":"${PROGRAMFILES(X86)}\\Ubisoft\\Ubisoft Game Launcher\\UbisoftConnect.exe","extension":".lnk","hasIcon":true,"iconId":"121ed1ae-9f82-477a-ad97-9c7f42cb1cb1"},{"id":"25e57979-c3be-45ce-a636-838b3d9aa2bf","name":"PowerToys","description":"","path":"${DESKTOP}\\**\\PowerToys*.lnk","destinationPath":"${PROGRAMFILES}\\PowerToys\\PowerToys.exe","extension":".lnk","hasIcon":true,"iconId":"c47976ba-a0f5-4922-bcee-9baebb508eb1"},{"id":"573f8804-b253-40c8-80d3-d703a2d4a2eb","name":"WO Mic Client","description":"","path":"${DESKTOP}\\**\\WO Mic*.lnk","destinationPath":"${PROGRAMFILES(X86)}\\WOMic\\WOMicClient.exe","extension":".lnk","hasIcon":true,"iconId":"2c4afaee-8a43-4e1b-8e7b-68a088a0c4c2"},{"id":"e19dd8a5-5602-4fc8-b7fa-e6f7e00881e8","name":"Youtube Music","description":"","path":"${DESKTOP}\\**\\Youtube Music.lnk","destinationPath":"${PROGRAMFILES(X86)}\\Microsoft\\Edge\\Application\\msedge_proxy.exe","extension":".lnk","hasIcon":true,"iconId":"d8cd9db5-4434-49d0-bcf3-57f487873eef"},{"id":"332adf36-394f-40a3-bbf4-6a4a614b062d","name":"Xbox","description":"","path":"${DESKTOP}\\**\\Xbox.lnk","destinationPath":"","extension":".lnk","hasIcon":true,"iconId":"8ea50d4a-bece-4e4d-95ab-b156fcedee0b"},{"id":"5cf7ea2c-3a5b-4e8f-adb6-aa77a6884bef","name":"WhatsApp","description":"","path":"${DESKTOP}\\**\\WhatsApp.lnk","destinationPath":"","extension":".lnk","hasIcon":true,"iconId":"7baf0b6d-4882-42e8-9785-1364f0ee02fc"},{"id":"5f5a0d35-ba17-4718-86e1-c12f04f833ec","name":"Bitvise SSH Client","description":"","path":"${DESKTOP}\\**\\Bitvise SSH Client.lnk","destinationPath":"${PROGRAMFILES(X86)}\\Bitvise SSH Client\\BvSsh.exe","extension":".lnk","hasIcon":true,"iconId":"83112629-8b97-48f5-8232-2a58630b1c86"},{"id":"d86fae40-7671-4f1d-bbbe-ea09c47996ca","name":"Proton VPN","description":"","path":"${DESKTOP}\\**\\Proton VPN.lnk","destinationPath":"${PROGRAMFILES}\\Proton\\VPN\\ProtonVPN.Launcher.exe","extension":".lnk","hasIcon":true,"iconId":"35f88eff-6ee5-4c1d-a60e-21e6cfbf5a65"},{"id":"43086f4d-6618-4be9-a36c-942997605d4e","name":"Epic Games Launcher","description":"","path":"${DESKTOP}\\**\\Epic Games*.lnk","destinationPath":"${PROGRAMFILES(X86)}\\Epic Games\\Launcher\\Portal\\Binaries\\Win32\\EpicGamesLauncher.exe","extension":".lnk","hasIcon":true,"iconId":"322b350b-6458-4d5a-83eb-8a7afbeee1b8"},{"id":"f67ce6c1-d5a1-46b5-bdb2-bcee09b4e981","name":"Telegram","description":"","path":"${DESKTOP}\\**\\Telegram*.lnk","destinationPath":"","extension":".lnk","hasIcon":true,"iconId":"8bfcbaca-5a82-463a-b9d9-b03b80f74004"}]
diff --git a/build/default-pack/icons/1b7d67c5-f51c-4225-bc78-c515aef0c723.png b/build/default-pack/icons/1b7d67c5-f51c-4225-bc78-c515aef0c723.png
new file mode 100644
index 0000000..f610a23
Binary files /dev/null and b/build/default-pack/icons/1b7d67c5-f51c-4225-bc78-c515aef0c723.png differ
diff --git a/build/default-pack/icons/25e57979-c3be-45ce-a636-838b3d9aa2bf.png b/build/default-pack/icons/25e57979-c3be-45ce-a636-838b3d9aa2bf.png
new file mode 100644
index 0000000..30bca14
Binary files /dev/null and b/build/default-pack/icons/25e57979-c3be-45ce-a636-838b3d9aa2bf.png differ
diff --git a/build/default-pack/icons/28393bcf-743c-47d7-b96e-c24d32e5d45d.png b/build/default-pack/icons/28393bcf-743c-47d7-b96e-c24d32e5d45d.png
new file mode 100644
index 0000000..d128573
Binary files /dev/null and b/build/default-pack/icons/28393bcf-743c-47d7-b96e-c24d32e5d45d.png differ
diff --git a/build/default-pack/icons/332adf36-394f-40a3-bbf4-6a4a614b062d.png b/build/default-pack/icons/332adf36-394f-40a3-bbf4-6a4a614b062d.png
new file mode 100644
index 0000000..b397682
Binary files /dev/null and b/build/default-pack/icons/332adf36-394f-40a3-bbf4-6a4a614b062d.png differ
diff --git a/build/default-pack/icons/37347aa4-6892-45b3-b43a-a04465e14c8f.png b/build/default-pack/icons/37347aa4-6892-45b3-b43a-a04465e14c8f.png
new file mode 100644
index 0000000..33fcff3
Binary files /dev/null and b/build/default-pack/icons/37347aa4-6892-45b3-b43a-a04465e14c8f.png differ
diff --git a/build/default-pack/icons/43086f4d-6618-4be9-a36c-942997605d4e.png b/build/default-pack/icons/43086f4d-6618-4be9-a36c-942997605d4e.png
new file mode 100644
index 0000000..8a419cb
Binary files /dev/null and b/build/default-pack/icons/43086f4d-6618-4be9-a36c-942997605d4e.png differ
diff --git a/build/default-pack/icons/573f8804-b253-40c8-80d3-d703a2d4a2eb.png b/build/default-pack/icons/573f8804-b253-40c8-80d3-d703a2d4a2eb.png
new file mode 100644
index 0000000..622f1aa
Binary files /dev/null and b/build/default-pack/icons/573f8804-b253-40c8-80d3-d703a2d4a2eb.png differ
diff --git a/build/default-pack/icons/5ccf00ca-a5fd-4700-8534-099797f9d27d.png b/build/default-pack/icons/5ccf00ca-a5fd-4700-8534-099797f9d27d.png
new file mode 100644
index 0000000..5acad85
Binary files /dev/null and b/build/default-pack/icons/5ccf00ca-a5fd-4700-8534-099797f9d27d.png differ
diff --git a/build/default-pack/icons/5cf7ea2c-3a5b-4e8f-adb6-aa77a6884bef.png b/build/default-pack/icons/5cf7ea2c-3a5b-4e8f-adb6-aa77a6884bef.png
new file mode 100644
index 0000000..7fa2c12
Binary files /dev/null and b/build/default-pack/icons/5cf7ea2c-3a5b-4e8f-adb6-aa77a6884bef.png differ
diff --git a/build/default-pack/icons/5f5a0d35-ba17-4718-86e1-c12f04f833ec.png b/build/default-pack/icons/5f5a0d35-ba17-4718-86e1-c12f04f833ec.png
new file mode 100644
index 0000000..df5ca17
Binary files /dev/null and b/build/default-pack/icons/5f5a0d35-ba17-4718-86e1-c12f04f833ec.png differ
diff --git a/build/default-pack/icons/68399993-7482-43b7-8164-0e5f6f396dc8.png b/build/default-pack/icons/68399993-7482-43b7-8164-0e5f6f396dc8.png
new file mode 100644
index 0000000..622813a
Binary files /dev/null and b/build/default-pack/icons/68399993-7482-43b7-8164-0e5f6f396dc8.png differ
diff --git a/build/default-pack/icons/72651379-284e-4427-aad2-2fad80a3ba77.png b/build/default-pack/icons/72651379-284e-4427-aad2-2fad80a3ba77.png
new file mode 100644
index 0000000..b3e7197
Binary files /dev/null and b/build/default-pack/icons/72651379-284e-4427-aad2-2fad80a3ba77.png differ
diff --git a/build/default-pack/icons/778325ab-785f-4dd9-9ff7-ed9c83436743.png b/build/default-pack/icons/778325ab-785f-4dd9-9ff7-ed9c83436743.png
new file mode 100644
index 0000000..bb0edc1
Binary files /dev/null and b/build/default-pack/icons/778325ab-785f-4dd9-9ff7-ed9c83436743.png differ
diff --git a/build/default-pack/icons/94ec31b7-3d34-460a-85c9-80003294532a.png b/build/default-pack/icons/94ec31b7-3d34-460a-85c9-80003294532a.png
new file mode 100644
index 0000000..eaaff9c
Binary files /dev/null and b/build/default-pack/icons/94ec31b7-3d34-460a-85c9-80003294532a.png differ
diff --git a/build/default-pack/icons/9c54b60a-588c-4cfc-9d3d-eef552872cc5.png b/build/default-pack/icons/9c54b60a-588c-4cfc-9d3d-eef552872cc5.png
new file mode 100644
index 0000000..bccf947
Binary files /dev/null and b/build/default-pack/icons/9c54b60a-588c-4cfc-9d3d-eef552872cc5.png differ
diff --git a/build/default-pack/icons/a183547f-bb5e-4743-9cd6-e2182fe1bfdb.png b/build/default-pack/icons/a183547f-bb5e-4743-9cd6-e2182fe1bfdb.png
new file mode 100644
index 0000000..0086a50
Binary files /dev/null and b/build/default-pack/icons/a183547f-bb5e-4743-9cd6-e2182fe1bfdb.png differ
diff --git a/build/default-pack/icons/bd6e07fc-f042-4297-bbac-fa8b6bc5d2c5.png b/build/default-pack/icons/bd6e07fc-f042-4297-bbac-fa8b6bc5d2c5.png
new file mode 100644
index 0000000..e69a3c4
Binary files /dev/null and b/build/default-pack/icons/bd6e07fc-f042-4297-bbac-fa8b6bc5d2c5.png differ
diff --git a/build/default-pack/icons/d2fec138-5c89-499b-b1c8-2d77ab5f8d1c.png b/build/default-pack/icons/d2fec138-5c89-499b-b1c8-2d77ab5f8d1c.png
new file mode 100644
index 0000000..5bd3b27
Binary files /dev/null and b/build/default-pack/icons/d2fec138-5c89-499b-b1c8-2d77ab5f8d1c.png differ
diff --git a/build/default-pack/icons/d86fae40-7671-4f1d-bbbe-ea09c47996ca.png b/build/default-pack/icons/d86fae40-7671-4f1d-bbbe-ea09c47996ca.png
new file mode 100644
index 0000000..46f0575
Binary files /dev/null and b/build/default-pack/icons/d86fae40-7671-4f1d-bbbe-ea09c47996ca.png differ
diff --git a/build/default-pack/icons/dfba32d5-ab27-4713-ba4a-f4227d55f615.png b/build/default-pack/icons/dfba32d5-ab27-4713-ba4a-f4227d55f615.png
new file mode 100644
index 0000000..f58b9dc
Binary files /dev/null and b/build/default-pack/icons/dfba32d5-ab27-4713-ba4a-f4227d55f615.png differ
diff --git a/build/default-pack/icons/e19dd8a5-5602-4fc8-b7fa-e6f7e00881e8.png b/build/default-pack/icons/e19dd8a5-5602-4fc8-b7fa-e6f7e00881e8.png
new file mode 100644
index 0000000..60f3193
Binary files /dev/null and b/build/default-pack/icons/e19dd8a5-5602-4fc8-b7fa-e6f7e00881e8.png differ
diff --git a/build/default-pack/icons/e3f31522-b0e3-4aa1-977c-ef77f832eec2.png b/build/default-pack/icons/e3f31522-b0e3-4aa1-977c-ef77f832eec2.png
new file mode 100644
index 0000000..709d552
Binary files /dev/null and b/build/default-pack/icons/e3f31522-b0e3-4aa1-977c-ef77f832eec2.png differ
diff --git a/build/default-pack/icons/f67ce6c1-d5a1-46b5-bdb2-bcee09b4e981.png b/build/default-pack/icons/f67ce6c1-d5a1-46b5-bdb2-bcee09b4e981.png
new file mode 100644
index 0000000..77b8411
Binary files /dev/null and b/build/default-pack/icons/f67ce6c1-d5a1-46b5-bdb2-bcee09b4e981.png differ
diff --git a/build/default-pack/metadata.json b/build/default-pack/metadata.json
new file mode 100644
index 0000000..62335aa
--- /dev/null
+++ b/build/default-pack/metadata.json
@@ -0,0 +1 @@
+{"id":"default-pack","name":"Default Pack","version":"v1.0.0","author":"beyenilmez","license":"MIT","description":"This is a pack providing some simple icons.","iconName":"73571c68-a850-4f52-b923-3615434075a1"}
diff --git a/build/default-pack/settings.json b/build/default-pack/settings.json
new file mode 100644
index 0000000..30867e3
--- /dev/null
+++ b/build/default-pack/settings.json
@@ -0,0 +1 @@
+{"enabled":false,"cornerRadius":20,"opacity":100}
diff --git a/build/packicon.ico b/build/packicon.ico
new file mode 100644
index 0000000..5266ec0
Binary files /dev/null and b/build/packicon.ico differ
diff --git a/build/packicon.png b/build/packicon.png
new file mode 100644
index 0000000..822535b
Binary files /dev/null and b/build/packicon.png differ
diff --git a/build/windows/icon.ico b/build/windows/icon.ico
index 1320609..00e452b 100644
Binary files a/build/windows/icon.ico and b/build/windows/icon.ico differ
diff --git a/build/windows/installer/project.nsi b/build/windows/installer/project.nsi
index 654ae2e..8621eb2 100644
--- a/build/windows/installer/project.nsi
+++ b/build/windows/installer/project.nsi
@@ -72,7 +72,7 @@ ManifestDPIAware true
Name "${INFO_PRODUCTNAME}"
OutFile "..\..\bin\${INFO_PROJECTNAME}-${ARCH}-installer.exe" # Name of the installer's file.
-InstallDir "$PROGRAMFILES64\${INFO_COMPANYNAME}\${INFO_PRODUCTNAME}" # Default installing folder ($PROGRAMFILES is Program Files folder).
+InstallDir "$Appdata\iconium\bin" # Default installing folder ($PROGRAMFILES is Program Files folder).
ShowInstDetails show # This will always show the installation details.
Function .onInit
@@ -81,13 +81,24 @@ FunctionEnd
Section
!insertmacro wails.setShellContext
+ SetShellVarContext current
!insertmacro wails.webview2runtime
SetOutPath $INSTDIR
-
!insertmacro wails.files
+ SetOutPath "$AppData\iconium\external\ImageMagick-7.1.1-35-portable-Q16-x64"
+ File /r "..\..\ImageMagick-7.1.1-35-portable-Q16-x64\*.*"
+
+ SetOutPath "$AppData\iconium\external\ExtractIcon"
+ File /r "..\..\ExtractIcon\*.*"
+
+ SetOutPath "$AppData\iconium\packs\default-pack"
+ File /r "..\..\default-pack\*"
+
+ SetOutPath $INSTDIR
+
CreateShortcut "$SMPROGRAMS\${INFO_PRODUCTNAME}.lnk" "$INSTDIR\${PRODUCT_EXECUTABLE}"
CreateShortCut "$DESKTOP\${INFO_PRODUCTNAME}.lnk" "$INSTDIR\${PRODUCT_EXECUTABLE}"
@@ -99,8 +110,12 @@ SectionEnd
Section "uninstall"
!insertmacro wails.setShellContext
+ SetShellVarContext current
- RMDir /r "$AppData\${PRODUCT_EXECUTABLE}" # Remove the WebView2 DataPath
+ RMDir /r "$AppData\iconium\EBWebView" # Remove the WebView2 DataPath
+ RMDir /r "$AppData\iconium\scripts" # Remove scripts
+ RMDir /r "$AppData\iconium\external" # Remove external programs
+ RMDir /r "$AppData\iconium\temp" # Remove temp folder
RMDir /r $INSTDIR
diff --git a/build/windows/installer/wails_tools.nsh b/build/windows/installer/wails_tools.nsh
deleted file mode 100644
index f9c0f88..0000000
--- a/build/windows/installer/wails_tools.nsh
+++ /dev/null
@@ -1,249 +0,0 @@
-# DO NOT EDIT - Generated automatically by `wails build`
-
-!include "x64.nsh"
-!include "WinVer.nsh"
-!include "FileFunc.nsh"
-
-!ifndef INFO_PROJECTNAME
- !define INFO_PROJECTNAME "{{.Name}}"
-!endif
-!ifndef INFO_COMPANYNAME
- !define INFO_COMPANYNAME "{{.Info.CompanyName}}"
-!endif
-!ifndef INFO_PRODUCTNAME
- !define INFO_PRODUCTNAME "{{.Info.ProductName}}"
-!endif
-!ifndef INFO_PRODUCTVERSION
- !define INFO_PRODUCTVERSION "{{.Info.ProductVersion}}"
-!endif
-!ifndef INFO_COPYRIGHT
- !define INFO_COPYRIGHT "{{.Info.Copyright}}"
-!endif
-!ifndef PRODUCT_EXECUTABLE
- !define PRODUCT_EXECUTABLE "${INFO_PROJECTNAME}.exe"
-!endif
-!ifndef UNINST_KEY_NAME
- !define UNINST_KEY_NAME "${INFO_COMPANYNAME}${INFO_PRODUCTNAME}"
-!endif
-!define UNINST_KEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\${UNINST_KEY_NAME}"
-
-!ifndef REQUEST_EXECUTION_LEVEL
- !define REQUEST_EXECUTION_LEVEL "admin"
-!endif
-
-RequestExecutionLevel "${REQUEST_EXECUTION_LEVEL}"
-
-!ifdef ARG_WAILS_AMD64_BINARY
- !define SUPPORTS_AMD64
-!endif
-
-!ifdef ARG_WAILS_ARM64_BINARY
- !define SUPPORTS_ARM64
-!endif
-
-!ifdef SUPPORTS_AMD64
- !ifdef SUPPORTS_ARM64
- !define ARCH "amd64_arm64"
- !else
- !define ARCH "amd64"
- !endif
-!else
- !ifdef SUPPORTS_ARM64
- !define ARCH "arm64"
- !else
- !error "Wails: Undefined ARCH, please provide at least one of ARG_WAILS_AMD64_BINARY or ARG_WAILS_ARM64_BINARY"
- !endif
-!endif
-
-!macro wails.checkArchitecture
- !ifndef WAILS_WIN10_REQUIRED
- !define WAILS_WIN10_REQUIRED "This product is only supported on Windows 10 (Server 2016) and later."
- !endif
-
- !ifndef WAILS_ARCHITECTURE_NOT_SUPPORTED
- !define WAILS_ARCHITECTURE_NOT_SUPPORTED "This product can't be installed on the current Windows architecture. Supports: ${ARCH}"
- !endif
-
- ${If} ${AtLeastWin10}
- !ifdef SUPPORTS_AMD64
- ${if} ${IsNativeAMD64}
- Goto ok
- ${EndIf}
- !endif
-
- !ifdef SUPPORTS_ARM64
- ${if} ${IsNativeARM64}
- Goto ok
- ${EndIf}
- !endif
-
- IfSilent silentArch notSilentArch
- silentArch:
- SetErrorLevel 65
- Abort
- notSilentArch:
- MessageBox MB_OK "${WAILS_ARCHITECTURE_NOT_SUPPORTED}"
- Quit
- ${else}
- IfSilent silentWin notSilentWin
- silentWin:
- SetErrorLevel 64
- Abort
- notSilentWin:
- MessageBox MB_OK "${WAILS_WIN10_REQUIRED}"
- Quit
- ${EndIf}
-
- ok:
-!macroend
-
-!macro wails.files
- !ifdef SUPPORTS_AMD64
- ${if} ${IsNativeAMD64}
- File "/oname=${PRODUCT_EXECUTABLE}" "${ARG_WAILS_AMD64_BINARY}"
- ${EndIf}
- !endif
-
- !ifdef SUPPORTS_ARM64
- ${if} ${IsNativeARM64}
- File "/oname=${PRODUCT_EXECUTABLE}" "${ARG_WAILS_ARM64_BINARY}"
- ${EndIf}
- !endif
-!macroend
-
-!macro wails.writeUninstaller
- WriteUninstaller "$INSTDIR\uninstall.exe"
-
- SetRegView 64
- WriteRegStr HKLM "${UNINST_KEY}" "Publisher" "${INFO_COMPANYNAME}"
- WriteRegStr HKLM "${UNINST_KEY}" "DisplayName" "${INFO_PRODUCTNAME}"
- WriteRegStr HKLM "${UNINST_KEY}" "DisplayVersion" "${INFO_PRODUCTVERSION}"
- WriteRegStr HKLM "${UNINST_KEY}" "DisplayIcon" "$INSTDIR\${PRODUCT_EXECUTABLE}"
- WriteRegStr HKLM "${UNINST_KEY}" "UninstallString" "$\"$INSTDIR\uninstall.exe$\""
- WriteRegStr HKLM "${UNINST_KEY}" "QuietUninstallString" "$\"$INSTDIR\uninstall.exe$\" /S"
-
- ${GetSize} "$INSTDIR" "/S=0K" $0 $1 $2
- IntFmt $0 "0x%08X" $0
- WriteRegDWORD HKLM "${UNINST_KEY}" "EstimatedSize" "$0"
-!macroend
-
-!macro wails.deleteUninstaller
- Delete "$INSTDIR\uninstall.exe"
-
- SetRegView 64
- DeleteRegKey HKLM "${UNINST_KEY}"
-!macroend
-
-!macro wails.setShellContext
- ${If} ${REQUEST_EXECUTION_LEVEL} == "admin"
- SetShellVarContext all
- ${else}
- SetShellVarContext current
- ${EndIf}
-!macroend
-
-# Install webview2 by launching the bootstrapper
-# See https://docs.microsoft.com/en-us/microsoft-edge/webview2/concepts/distribution#online-only-deployment
-!macro wails.webview2runtime
- !ifndef WAILS_INSTALL_WEBVIEW_DETAILPRINT
- !define WAILS_INSTALL_WEBVIEW_DETAILPRINT "Installing: WebView2 Runtime"
- !endif
-
- SetRegView 64
- # If the admin key exists and is not empty then webview2 is already installed
- ReadRegStr $0 HKLM "SOFTWARE\WOW6432Node\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}" "pv"
- ${If} $0 != ""
- Goto ok
- ${EndIf}
-
- ${If} ${REQUEST_EXECUTION_LEVEL} == "user"
- # If the installer is run in user level, check the user specific key exists and is not empty then webview2 is already installed
- ReadRegStr $0 HKCU "Software\Microsoft\EdgeUpdate\Clients{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}" "pv"
- ${If} $0 != ""
- Goto ok
- ${EndIf}
- ${EndIf}
-
- SetDetailsPrint both
- DetailPrint "${WAILS_INSTALL_WEBVIEW_DETAILPRINT}"
- SetDetailsPrint listonly
-
- InitPluginsDir
- CreateDirectory "$pluginsdir\webview2bootstrapper"
- SetOutPath "$pluginsdir\webview2bootstrapper"
- File "tmp\MicrosoftEdgeWebview2Setup.exe"
- ExecWait '"$pluginsdir\webview2bootstrapper\MicrosoftEdgeWebview2Setup.exe" /silent /install'
-
- SetDetailsPrint both
- ok:
-!macroend
-
-# Copy of APP_ASSOCIATE and APP_UNASSOCIATE macros from here https://gist.github.com/nikku/281d0ef126dbc215dd58bfd5b3a5cd5b
-!macro APP_ASSOCIATE EXT FILECLASS DESCRIPTION ICON COMMANDTEXT COMMAND
- ; Backup the previously associated file class
- ReadRegStr $R0 SHELL_CONTEXT "Software\Classes\.${EXT}" ""
- WriteRegStr SHELL_CONTEXT "Software\Classes\.${EXT}" "${FILECLASS}_backup" "$R0"
-
- WriteRegStr SHELL_CONTEXT "Software\Classes\.${EXT}" "" "${FILECLASS}"
-
- WriteRegStr SHELL_CONTEXT "Software\Classes\${FILECLASS}" "" `${DESCRIPTION}`
- WriteRegStr SHELL_CONTEXT "Software\Classes\${FILECLASS}\DefaultIcon" "" `${ICON}`
- WriteRegStr SHELL_CONTEXT "Software\Classes\${FILECLASS}\shell" "" "open"
- WriteRegStr SHELL_CONTEXT "Software\Classes\${FILECLASS}\shell\open" "" `${COMMANDTEXT}`
- WriteRegStr SHELL_CONTEXT "Software\Classes\${FILECLASS}\shell\open\command" "" `${COMMAND}`
-!macroend
-
-!macro APP_UNASSOCIATE EXT FILECLASS
- ; Backup the previously associated file class
- ReadRegStr $R0 SHELL_CONTEXT "Software\Classes\.${EXT}" `${FILECLASS}_backup`
- WriteRegStr SHELL_CONTEXT "Software\Classes\.${EXT}" "" "$R0"
-
- DeleteRegKey SHELL_CONTEXT `Software\Classes\${FILECLASS}`
-!macroend
-
-!macro wails.associateFiles
- ; Create file associations
- {{range .Info.FileAssociations}}
- !insertmacro APP_ASSOCIATE "{{.Ext}}" "{{.Name}}" "{{.Description}}" "$INSTDIR\{{.IconName}}.ico" "Open with ${INFO_PRODUCTNAME}" "$INSTDIR\${PRODUCT_EXECUTABLE} $\"%1$\""
-
- File "..\{{.IconName}}.ico"
- {{end}}
-!macroend
-
-!macro wails.unassociateFiles
- ; Delete app associations
- {{range .Info.FileAssociations}}
- !insertmacro APP_UNASSOCIATE "{{.Ext}}" "{{.Name}}"
-
- Delete "$INSTDIR\{{.IconName}}.ico"
- {{end}}
-!macroend
-
-!macro CUSTOM_PROTOCOL_ASSOCIATE PROTOCOL DESCRIPTION ICON COMMAND
- DeleteRegKey SHELL_CONTEXT "Software\Classes\${PROTOCOL}"
- WriteRegStr SHELL_CONTEXT "Software\Classes\${PROTOCOL}" "" "${DESCRIPTION}"
- WriteRegStr SHELL_CONTEXT "Software\Classes\${PROTOCOL}" "URL Protocol" ""
- WriteRegStr SHELL_CONTEXT "Software\Classes\${PROTOCOL}\DefaultIcon" "" "${ICON}"
- WriteRegStr SHELL_CONTEXT "Software\Classes\${PROTOCOL}\shell" "" ""
- WriteRegStr SHELL_CONTEXT "Software\Classes\${PROTOCOL}\shell\open" "" ""
- WriteRegStr SHELL_CONTEXT "Software\Classes\${PROTOCOL}\shell\open\command" "" "${COMMAND}"
-!macroend
-
-!macro CUSTOM_PROTOCOL_UNASSOCIATE PROTOCOL
- DeleteRegKey SHELL_CONTEXT "Software\Classes\${PROTOCOL}"
-!macroend
-
-!macro wails.associateCustomProtocols
- ; Create custom protocols associations
- {{range .Info.Protocols}}
- !insertmacro CUSTOM_PROTOCOL_ASSOCIATE "{{.Scheme}}" "{{.Description}}" "$INSTDIR\${PRODUCT_EXECUTABLE},0" "$INSTDIR\${PRODUCT_EXECUTABLE} $\"%1$\""
-
- {{end}}
-!macroend
-
-!macro wails.unassociateCustomProtocols
- ; Delete app custom protocol associations
- {{range .Info.Protocols}}
- !insertmacro CUSTOM_PROTOCOL_UNASSOCIATE "{{.Scheme}}"
- {{end}}
-!macroend
diff --git a/build/windows/packicon.ico b/build/windows/packicon.ico
new file mode 100644
index 0000000..bb22703
Binary files /dev/null and b/build/windows/packicon.ico differ
diff --git a/config.go b/config.go
new file mode 100644
index 0000000..8d3988e
--- /dev/null
+++ b/config.go
@@ -0,0 +1,303 @@
+package main
+
+import (
+ "encoding/json"
+ "errors"
+ "fmt"
+ "os"
+ "reflect"
+ "strconv"
+
+ "github.com/wailsapp/wails/v2/pkg/runtime"
+)
+
+type Config struct {
+ Theme *string `json:"theme"` // system, light, dark
+ ColorScheme *string `json:"colorScheme"` // default, midnightAsh
+ UseSystemTitleBar *bool `json:"useSystemTitleBar"` // true, false
+ EnableLogging *bool `json:"enableLogging"` // true, false
+ EnableTrace *bool `json:"enableTrace"` // true, false
+ EnableDebug *bool `json:"enableDebug"` // true, false
+ EnableInfo *bool `json:"enableInfo"` // true, false
+ EnableWarn *bool `json:"enableWarn"` // true, false
+ EnableError *bool `json:"enableError"` // true, false
+ EnableFatal *bool `json:"enableFatal"` // true, false
+ MaxLogFiles *int `json:"maxLogFiles"` // int
+ Language *string `json:"language"` // en-US, tr-TR
+ SaveWindowStatus *bool `json:"saveWindowStatus"` // true, false
+ WindowStartState *int `json:"windowStartState"` // 0 = Normal, 1 = Maximized, 2 = Minimized, 3 = Fullscreen
+ WindowStartPositionX *int `json:"windowStartPositionX"` // x
+ WindowStartPositionY *int `json:"windowStartPositionY"` // y
+ WindowStartSizeX *int `json:"windowStartSizeX"` // x
+ WindowStartSizeY *int `json:"windowStartSizeY"` // y
+ WindowScale *int `json:"windowScale"` // %
+ Opacity *int `json:"opacity"` // %
+ WindowEffect *int `json:"windowEffect"` // 0 = Auto, 1 = None, 2 = Mica, 3 = Acrylic, 4 = Tabbed
+ CheckForUpdates *bool `json:"checkForUpdates"` // true, false
+ LastUpdateCheck *int `json:"lastUpdateCheck"` // unix timestamp
+ MatchLnkByDestination *bool `json:"matchLnkByDestination"` // true, false
+ MatchURLByDestination *bool `json:"matchURLByDestination"` // true, false
+ RenameMatchedFiles *bool `json:"renameMatchedFiles"` // true, false
+ ChangeDescriptionOfMathcedLnkFiles *bool `json:"changeDescriptionOfMathcedLnkFiles"` // true, false
+}
+
+func GetDefaultConfig() Config {
+ defaultTheme := "system"
+ defaultColorScheme := "default"
+ defaultUseSystemTitleBar := false
+ defaultEnableLogging := true
+ defaultEnableTrace := false
+ defaultEnableDebug := false
+ defaultEnableInfo := true
+ defaultEnableWarn := true
+ defaultEnableError := true
+ defaultEnableFatal := true
+ defaultMaxLogFiles := 20
+ defaultLanguage := "en-US"
+ defaultSaveWindowStatus := true
+ defaultWindowStartState := 0
+ defaultWindowStartPositionX := -100000
+ defaultWindowStartPositionY := -100000
+ defaultWindowStartSizeX := -100000
+ defaultWindowStartSizeY := -100000
+ defaultWindowScale := 100
+ defaultOpacity := 80
+ defaultWindowEffect := 3
+ defaultCheckForUpdates := true
+ defaultLastUpdateCheck := 0
+ defaultMatchLnkByDestination := true
+ defaultMatchURLByDestination := true
+ defaultRenameMatchedFiles := false
+ defaultChangeDescriptionOfMathcedLnkFiles := false
+
+ return Config{
+ Theme: &defaultTheme,
+ ColorScheme: &defaultColorScheme,
+ UseSystemTitleBar: &defaultUseSystemTitleBar,
+ EnableLogging: &defaultEnableLogging,
+ EnableTrace: &defaultEnableTrace,
+ EnableDebug: &defaultEnableDebug,
+ EnableInfo: &defaultEnableInfo,
+ EnableWarn: &defaultEnableWarn,
+ EnableError: &defaultEnableError,
+ EnableFatal: &defaultEnableFatal,
+ MaxLogFiles: &defaultMaxLogFiles,
+ Language: &defaultLanguage,
+ SaveWindowStatus: &defaultSaveWindowStatus,
+ WindowStartState: &defaultWindowStartState,
+ WindowStartPositionX: &defaultWindowStartPositionX,
+ WindowStartPositionY: &defaultWindowStartPositionY,
+ WindowStartSizeX: &defaultWindowStartSizeX,
+ WindowStartSizeY: &defaultWindowStartSizeY,
+ WindowScale: &defaultWindowScale,
+ Opacity: &defaultOpacity,
+ WindowEffect: &defaultWindowEffect,
+ CheckForUpdates: &defaultCheckForUpdates,
+ LastUpdateCheck: &defaultLastUpdateCheck,
+ MatchLnkByDestination: &defaultMatchLnkByDestination,
+ MatchURLByDestination: &defaultMatchURLByDestination,
+ RenameMatchedFiles: &defaultRenameMatchedFiles,
+ ChangeDescriptionOfMathcedLnkFiles: &defaultChangeDescriptionOfMathcedLnkFiles,
+ }
+}
+
+var config Config = GetDefaultConfig()
+
+func config_init() error {
+ err := CreateConfigIfNotExist()
+ if err != nil {
+ return errors.New("failed to create config file")
+ }
+ err = ReadConfig(configPath)
+ if err != nil {
+ config = GetDefaultConfig()
+ } else {
+ merge_defaults()
+ }
+
+ return nil
+}
+
+func merge_defaults() {
+ defaultConfig := GetDefaultConfig()
+
+ fmt.Println("Merging default config")
+
+ v := reflect.ValueOf(&config).Elem()
+ t := v.Type()
+
+ for i := 0; i < t.NumField(); i++ {
+ field := t.Field(i)
+ fieldName := field.Name
+ fieldValue := v.FieldByName(fieldName)
+
+ if fieldValue.Kind() == reflect.Ptr && fieldValue.IsNil() {
+ // If config's field is nil, set it to the default value's field
+ defaultValue := reflect.ValueOf(&defaultConfig).Elem().FieldByName(fieldName)
+ fieldValue.Set(defaultValue)
+ }
+ }
+}
+
+func (app *App) GetConfig() Config {
+ return config
+}
+
+func (app *App) GetConfigField(fieldName string) interface{} {
+ runtime.LogDebug(app.ctx, fmt.Sprintf("Attempting to get config field %s", fieldName))
+
+ // Get the reflection Type and Value of the Config struct
+ v := reflect.ValueOf(&config).Elem()
+ t := v.Type()
+
+ // Find the field by name
+ _, found := t.FieldByName(fieldName)
+ if !found {
+ runtime.LogWarning(app.ctx, fmt.Sprintf("Unknown config field: %s", fieldName))
+ return "undefined"
+ }
+
+ // Get the field value
+ fieldValue := v.FieldByName(fieldName)
+
+ // Check if the field is a pointer
+ if fieldValue.Kind() == reflect.Ptr {
+ if fieldValue.IsNil() {
+ runtime.LogWarning(app.ctx, fmt.Sprintf("Config field %s is nil", fieldName))
+ return "undefined"
+ }
+ // Dereference the pointer
+ fieldValue = fieldValue.Elem()
+ }
+
+ runtime.LogDebug(app.ctx, fmt.Sprintf("Config field %s has value: %v", fieldName, fieldValue.Interface()))
+ return fieldValue.Interface()
+}
+
+func (app *App) SetConfigField(fieldName string, value interface{}) {
+ runtime.LogDebug(app.ctx, fmt.Sprintf("Attempting to set config field %s to %v", fieldName, value))
+
+ v := reflect.ValueOf(&config).Elem()
+ t := v.Type()
+
+ _, found := t.FieldByName(fieldName)
+ if !found {
+ runtime.LogWarning(app.ctx, fmt.Sprintf("Unknown config field: %s", fieldName))
+ return
+ }
+
+ fieldValue := v.FieldByName(fieldName)
+
+ if !fieldValue.IsValid() {
+ runtime.LogWarning(app.ctx, fmt.Sprintf("Invalid field: %s", fieldName))
+ return
+ }
+
+ if fieldValue.Kind() == reflect.Ptr {
+ runtime.LogDebug(app.ctx, fmt.Sprintf("Dereferencing config field %s", fieldName))
+ fieldValue = fieldValue.Elem()
+ }
+
+ runtime.LogDebug(app.ctx, fmt.Sprintf("Config field %s type: %v", fieldName, fieldValue.Kind()))
+
+ switch fieldValue.Kind() {
+ case reflect.String:
+ strVal, ok := value.(string)
+ if !ok {
+ runtime.LogWarning(app.ctx, fmt.Sprintf("Invalid value type for string field %s: %v", fieldName, value))
+ return
+ }
+ fieldValue.SetString(strVal)
+
+ case reflect.Bool:
+ boolVal, ok := value.(bool)
+ if !ok {
+ runtime.LogWarning(app.ctx, fmt.Sprintf("Invalid value type for boolean field %s: %v", fieldName, value))
+ return
+ }
+ fieldValue.SetBool(boolVal)
+
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ intVal, err := strconv.Atoi(fmt.Sprintf("%v", value))
+ if err != nil {
+ runtime.LogWarning(app.ctx, fmt.Sprintf("Invalid value type for integer field %s: %v", fieldName, value))
+ return
+ }
+ fieldValue.SetInt(int64(intVal))
+
+ case reflect.Float32, reflect.Float64:
+ floatVal, ok := value.(float64)
+ if !ok {
+ runtime.LogWarning(app.ctx, fmt.Sprintf("Invalid value type for float field %s: %v", fieldName, value))
+ return
+ }
+ fieldValue.SetFloat(floatVal)
+
+ case reflect.Slice:
+ sliceVal, ok := value.([]string)
+ if !ok {
+ runtime.LogWarning(app.ctx, fmt.Sprintf("Invalid value type for slice field %s: %v", fieldName, value))
+ return
+ }
+ slice := reflect.ValueOf(sliceVal)
+ fieldValue.Set(slice)
+
+ default:
+ runtime.LogWarning(app.ctx, fmt.Sprintf("Unsupported field type for field %s of type %s", fieldName, fieldValue.Kind()))
+ return
+ }
+
+ runtime.LogDebug(app.ctx, fmt.Sprintf("Config field %s set to %v", fieldName, fieldValue.Interface()))
+}
+
+// Creates a default config at configPath if none exists
+func CreateConfigIfNotExist() error {
+ configPath = get_config_path()
+
+ if _, err := os.Stat(configPath); os.IsNotExist(err) {
+ config = GetDefaultConfig()
+ }
+ return nil
+}
+
+// WriteConfig writes the current config to the path
+func WriteConfig(path string) error {
+ file, err := os.Create(path)
+ if err != nil {
+ return err
+ }
+ defer file.Close()
+
+ encoder := json.NewEncoder(file)
+ err = encoder.Encode(config)
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+// Read config from path
+func ReadConfig(path string) error {
+ file, err := os.Open(path)
+
+ if err != nil {
+ return err
+ }
+
+ defer file.Close()
+ decoder := json.NewDecoder(file)
+
+ config = Config{}
+
+ err = decoder.Decode(&config)
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (app *App) ReadConfig(path string) error {
+ return ReadConfig(path)
+}
diff --git a/dialog.go b/dialog.go
new file mode 100644
index 0000000..a5cac1c
--- /dev/null
+++ b/dialog.go
@@ -0,0 +1,362 @@
+package main
+
+import (
+ "os"
+ "os/exec"
+ "path/filepath"
+ "strings"
+
+ "github.com/google/uuid"
+ "github.com/wailsapp/wails/v2/pkg/runtime"
+)
+
+func (a *App) SaveConfigDialog() {
+ path, err := runtime.SaveFileDialog(a.ctx, runtime.SaveDialogOptions{
+ Title: "Save configuration",
+ DefaultDirectory: savedConfigFolder,
+ DefaultFilename: "config.json",
+ CanCreateDirectories: true,
+ Filters: []runtime.FileFilter{
+ {
+ DisplayName: "JSON",
+ Pattern: "*.json",
+ },
+ },
+ })
+
+ if err != nil {
+ runtime.LogWarning(a.ctx, err.Error())
+ return
+ }
+
+ err = WriteConfig(path)
+
+ if err != nil {
+ if path == "" {
+ runtime.LogInfo(a.ctx, "No path given, not saving config")
+ return
+ }
+ runtime.LogWarning(a.ctx, err.Error())
+ a.SendNotification("", "settings.there_was_an_error_saving_the_config", "", "error")
+ return
+ }
+
+ runtime.LogInfo(a.ctx, "Config saved to "+path)
+ path = strings.ReplaceAll(path, "\\", "\\\\")
+ a.SendNotification("", "settings.config_saved", path, "success")
+}
+
+func (a *App) GetLoadConfigPath() string {
+ path, err := runtime.OpenFileDialog(a.ctx, runtime.OpenDialogOptions{
+ Title: "Load configuration",
+ DefaultDirectory: savedConfigFolder,
+ CanCreateDirectories: true,
+ Filters: []runtime.FileFilter{
+ {
+ DisplayName: "JSON",
+ Pattern: "*.json",
+ },
+ },
+ })
+
+ if err != nil {
+ runtime.LogWarning(a.ctx, err.Error())
+ return ""
+ }
+
+ return path
+}
+
+func (a *App) GetBase64Png() string {
+ path, err := runtime.OpenFileDialog(a.ctx, runtime.OpenDialogOptions{
+ Title: "Select image",
+ CanCreateDirectories: true,
+ Filters: []runtime.FileFilter{
+ {
+ DisplayName: "PNG",
+ Pattern: "*.png",
+ },
+ },
+ })
+
+ if err != nil {
+ runtime.LogWarning(a.ctx, err.Error())
+ return ""
+ }
+
+ if path == "" {
+ return ""
+ }
+
+ base64Png := GenerateBase64PngFromPath(path)
+
+ runtime.LogInfo(a.ctx, "Image selected: "+path)
+
+ return base64Png
+}
+
+func (a *App) GetTempPng(id string) string {
+ oldPackPngPath, ok := tempPngPaths.Get(id)
+
+ tempPackPngPath := filepath.Join(tempFolder, "iconium-"+uuid.NewString()+".png")
+
+ path, err := runtime.OpenFileDialog(a.ctx, runtime.OpenDialogOptions{
+ Title: "Select image",
+ Filters: []runtime.FileFilter{
+ {
+ DisplayName: "Image File",
+ Pattern: "*.png;*.jpg;*.jpeg;*.webp;*.svg;*.bmp;*.ico;*.exe;*.lnk;*.url",
+ },
+ },
+ })
+
+ if err != nil {
+ runtime.LogWarning(a.ctx, err.Error())
+ return ""
+ }
+ if path == "" {
+ return ""
+ }
+
+ err = ConvertToPng(path, tempPackPngPath)
+ if err != nil {
+ runtime.LogErrorf(a.ctx, "Error copying pack.png file: %s", err.Error())
+ return ""
+ }
+
+ runtime.LogInfof(a.ctx, "Trimming path: %s (cut %s)", tempPackPngPath, appFolder)
+ trimmedPath := strings.TrimPrefix(tempPackPngPath, appFolder)
+
+ tempPngPaths.Set(id, tempPackPngPath)
+
+ // Remove old pack png
+ if ok {
+ os.Remove(oldPackPngPath)
+ }
+
+ return trimmedPath
+}
+
+func (a *App) GetIconFolder() string {
+ path, err := runtime.OpenDirectoryDialog(a.ctx, runtime.OpenDialogOptions{
+ Title: "Select folder",
+ CanCreateDirectories: true,
+ })
+
+ if err != nil {
+ runtime.LogWarning(a.ctx, err.Error())
+ return ""
+ }
+
+ return path
+}
+
+func (a *App) GetIconFile() string {
+ path, err := runtime.OpenFileDialog(a.ctx, runtime.OpenDialogOptions{
+ Title: "Select file",
+ CanCreateDirectories: true,
+ Filters: []runtime.FileFilter{
+ {
+ DisplayName: "Shortcut",
+ Pattern: "*.lnk;*.url",
+ },
+ },
+ })
+
+ if err != nil {
+ runtime.LogWarning(a.ctx, err.Error())
+ return ""
+ }
+
+ return path
+}
+
+func (a *App) GetIconFiles() []string {
+ paths, err := runtime.OpenMultipleFilesDialog(a.ctx, runtime.OpenDialogOptions{
+ Title: "Select file",
+ CanCreateDirectories: true,
+ Filters: []runtime.FileFilter{
+ {
+ DisplayName: "Shortcut",
+ Pattern: "*.lnk;*.url",
+ },
+ },
+ })
+
+ if err != nil {
+ runtime.LogWarning(a.ctx, err.Error())
+ return nil
+ }
+
+ return paths
+}
+
+func (a *App) GetFilePath(generalPath string) string {
+ fullPath := ConvertToFullPath(filepath.Dir(generalPath))
+
+ path, err := runtime.OpenFileDialog(a.ctx, runtime.OpenDialogOptions{
+ Title: "Select file",
+ DefaultDirectory: fullPath,
+ ResolvesAliases: true,
+ Filters: []runtime.FileFilter{
+ {
+ DisplayName: "Shortcut",
+ Pattern: "*.lnk;*.url",
+ },
+ },
+ })
+
+ if err != nil {
+ runtime.LogWarning(a.ctx, err.Error())
+ return ""
+ }
+
+ return ConvertToGeneralPath(path)
+}
+
+func (a *App) ExportIconPack(packId string) string {
+ // Check if the icon pack exists
+ _, err := a.GetIconPack(packId)
+ if err != nil {
+ runtime.LogErrorf(appContext, "Icon pack %s not found: %s", packId, err.Error())
+ return ""
+ }
+
+ path, err := runtime.SaveFileDialog(a.ctx, runtime.SaveDialogOptions{
+ Title: "Export icon pack",
+ CanCreateDirectories: true,
+ Filters: []runtime.FileFilter{
+ {
+ DisplayName: "Iconium File",
+ Pattern: "*.icnm",
+ },
+ },
+ })
+ if err != nil {
+ runtime.LogWarning(a.ctx, err.Error())
+ return ""
+ }
+
+ iconPackPath := filepath.Join(packsFolder, packId)
+ runtime.LogInfof(a.ctx, "Exporting icon pack: %s", iconPackPath)
+
+ err = zip_folder(iconPackPath, path)
+
+ if err != nil {
+ runtime.LogErrorf(a.ctx, "Error exporting icon pack: %s", err.Error())
+ return ""
+ }
+
+ a.SendNotification("settings.icon_pack.exported", "", path, "success")
+
+ return path
+}
+
+func (a *App) GetIconPackPath() string {
+ path, err := runtime.OpenFileDialog(a.ctx, runtime.OpenDialogOptions{
+ Title: "Import icon pack",
+ CanCreateDirectories: true,
+ Filters: []runtime.FileFilter{
+ {
+ DisplayName: "Iconium File",
+ Pattern: "*.icnm",
+ },
+ },
+ })
+ if err != nil {
+ runtime.LogWarning(a.ctx, err.Error())
+ return ""
+ }
+
+ return path
+}
+
+func (a *App) ImportIconPack(path string) string {
+ runtime.LogInfof(a.ctx, "Importing icon pack: %s", path)
+
+ extractFolder := filepath.Join(tempFolder, "iconium-"+uuid.NewString())
+ defer os.RemoveAll(extractFolder)
+
+ err := unzip_folder(path, extractFolder)
+ if err != nil {
+ runtime.LogErrorf(a.ctx, "Error importing icon pack: %s", err.Error())
+ return ""
+ }
+
+ files, err := os.ReadDir(extractFolder)
+ if err != nil {
+ runtime.LogErrorf(a.ctx, "Error importing icon pack: %s", err.Error())
+ return ""
+ }
+ if len(files) != 1 {
+ runtime.LogErrorf(a.ctx, "Error importing icon pack")
+ return ""
+ }
+
+ packId := uuid.NewString()
+
+ tempIconPackPath := filepath.Join(extractFolder, files[0].Name())
+ targetPath := filepath.Join(packsFolder, packId)
+
+ err = os.Rename(tempIconPackPath, targetPath)
+ if err != nil {
+ runtime.LogErrorf(a.ctx, "Error importing icon pack: %s", err.Error())
+ return ""
+ }
+
+ a.SendNotification("my_packs.import_pack.success", "", "", "success")
+
+ return packId
+}
+
+func (a *App) GetIcnmMetadata(path string) Metadata {
+ extractFolder := filepath.Join(tempFolder, "iconium-"+uuid.NewString())
+
+ err := unzip_folder(path, extractFolder)
+ if err != nil {
+ runtime.LogErrorf(a.ctx, "Error importing icon pack: %s", err.Error())
+ return Metadata{}
+ }
+ defer os.RemoveAll(extractFolder)
+
+ files, err := os.ReadDir(extractFolder)
+ if err != nil {
+ runtime.LogErrorf(a.ctx, "Error importing icon pack: %s", err.Error())
+ return Metadata{}
+ }
+ if len(files) != 1 {
+ runtime.LogErrorf(a.ctx, "Error importing icon pack")
+ return Metadata{}
+ }
+
+ metadataFile := filepath.Join(extractFolder, files[0].Name(), "metadata.json")
+
+ var metadata Metadata
+ err = readJSON(metadataFile, &metadata)
+ if err != nil {
+ runtime.LogErrorf(a.ctx, "Error importing icon pack: %s", err.Error())
+ return Metadata{}
+ }
+
+ iconPath := filepath.Join(extractFolder, files[0].Name(), metadata.IconName+".png")
+ tempIconName := "iconium-" + uuid.NewString() + ".png"
+ tempIconPath := filepath.Join(tempFolder, tempIconName)
+
+ err = copy_file(iconPath, tempIconPath)
+ if err != nil {
+ runtime.LogWarningf(a.ctx, "Error copying icon: %s", err.Error())
+ }
+
+ metadata.IconName = "temp\\" + tempIconName
+
+ tempPngPaths.Set(uuid.NewString(), tempIconPath)
+
+ return metadata
+}
+
+func (a *App) OpenFileInExplorer(path string) {
+ runtime.LogInfo(a.ctx, "Opening file in explorer: "+path)
+
+ cmd := exec.Command(`explorer`, `/select,`, path)
+ cmd.Run()
+}
diff --git a/frontend/index.html b/frontend/index.html
index 38fe32c..9b1e671 100644
--- a/frontend/index.html
+++ b/frontend/index.html
@@ -2,8 +2,9 @@
+
- Desktop Manager
+ Iconium
diff --git a/frontend/package.json b/frontend/package.json
index 6ddcb42..81d76fb 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -1,5 +1,5 @@
{
- "name": "frontend",
+ "name": "iconium",
"private": true,
"version": "0.0.0",
"type": "module",
@@ -10,39 +10,56 @@
"preview": "vite preview"
},
"dependencies": {
- "@hookform/resolvers": "^3.3.4",
- "@radix-ui/react-accordion": "^1.1.2",
- "@radix-ui/react-dialog": "^1.0.5",
- "@radix-ui/react-dropdown-menu": "^2.0.6",
- "@radix-ui/react-label": "^2.0.2",
- "@radix-ui/react-popover": "^1.0.7",
- "@radix-ui/react-slot": "^1.0.2",
- "class-variance-authority": "^0.6.0",
- "clsx": "^1.2.1",
- "cmdk": "^0.2.1",
- "lucide-react": "^0.252.0",
- "react": "^18.2.0",
- "react-dom": "^18.2.0",
- "react-hook-form": "^7.51.0",
- "tailwind-merge": "^1.13.2",
- "tailwindcss-animate": "^1.0.6",
- "zod": "^3.22.4"
+ "@hookform/resolvers": "^3.9.0",
+ "@radix-ui/react-accordion": "^1.2.0",
+ "@radix-ui/react-checkbox": "^1.1.1",
+ "@radix-ui/react-dialog": "^1.1.1",
+ "@radix-ui/react-hover-card": "^1.1.1",
+ "@radix-ui/react-label": "^2.1.0",
+ "@radix-ui/react-popover": "^1.1.1",
+ "@radix-ui/react-progress": "^1.1.0",
+ "@radix-ui/react-slider": "^1.2.0",
+ "@radix-ui/react-slot": "^1.1.0",
+ "@radix-ui/react-switch": "^1.1.0",
+ "@radix-ui/react-tabs": "^1.1.0",
+ "@radix-ui/react-toast": "^1.2.1",
+ "@radix-ui/react-toggle": "^1.1.0",
+ "@radix-ui/react-toggle-group": "^1.1.0",
+ "@radix-ui/react-tooltip": "^1.1.2",
+ "class-variance-authority": "^0.7.0",
+ "clsx": "^2.1.1",
+ "cmdk": "^1.0.0",
+ "html-react-parser": "^5.1.15",
+ "i18next": "^23.11.5",
+ "i18next-browser-languagedetector": "^8.0.0",
+ "i18next-http-backend": "^2.5.2",
+ "lucide-react": "^0.396.0",
+ "next-themes": "^0.3.0",
+ "react": "^18.3.1",
+ "react-dom": "^18.3.1",
+ "react-hook-form": "^7.52.1",
+ "react-i18next": "^14.1.2",
+ "sonner": "^1.5.0",
+ "tailwind-merge": "^2.3.0",
+ "tailwindcss-animate": "^1.0.7",
+ "zod": "^3.23.8"
},
"devDependencies": {
- "@types/react": "^18.0.37",
- "@types/react-dom": "^18.0.11",
- "@typescript-eslint/eslint-plugin": "^5.59.0",
- "@typescript-eslint/parser": "^5.59.0",
- "@vitejs/plugin-react": "^4.0.0",
- "autoprefixer": "^10.4.12",
- "eslint": "^8.38.0",
- "eslint-plugin-react-hooks": "^4.6.0",
- "eslint-plugin-react-refresh": "^0.3.4",
+ "@types/react": "^18.3.3",
+ "@types/react-dom": "^18.3.0",
+ "@typescript-eslint/eslint-plugin": "^7.13.1",
+ "@typescript-eslint/parser": "^7.13.1",
+ "@vitejs/plugin-react": "^4.3.1",
+ "autoprefixer": "^10.4.19",
+ "eslint": "^9.5.0",
+ "eslint-plugin-react-hooks": "^4.6.2",
+ "eslint-plugin-react-refresh": "^0.4.7",
"path": "^0.12.7",
- "postcss": "^8.4.17",
- "tailwindcss": "^3.1.8",
- "typescript": "^5.0.2",
- "url": "^0.11.1",
- "vite": "^4.3.9"
+ "postcss": "^8.4.38",
+ "tailwind-scrollbar": "^3.1.0",
+ "tailwindcss": "^3.4.4",
+ "typescript": "^5.5.2",
+ "url": "^0.11.3",
+ "vite": "^4.0.0"
}
}
diff --git a/frontend/package.json.md5 b/frontend/package.json.md5
deleted file mode 100644
index 50d2b91..0000000
--- a/frontend/package.json.md5
+++ /dev/null
@@ -1 +0,0 @@
-7be0872a8fdf54877448dff68fb922a2
\ No newline at end of file
diff --git a/frontend/public/locales/en-US.json b/frontend/public/locales/en-US.json
new file mode 100644
index 0000000..f659866
--- /dev/null
+++ b/frontend/public/locales/en-US.json
@@ -0,0 +1,326 @@
+{
+ "save": "Save",
+ "cancel": "Cancel",
+ "yes": "Yes",
+ "no": "No",
+ "show_in_explorer": "Show in Explorer",
+ "import": "Import",
+ "export": "Export",
+ "error": "Error",
+ "show": "Show",
+ "create": "Create",
+ "delete": "Delete",
+
+ "downloading_missing_external_programs": "Downloading missing external programs...",
+ "downloaded_missing_external_programs": "Successfully downloaded missing external programs.",
+
+ "nav": {
+ "my_packs": "My Packs",
+ "settings": "Settings"
+ },
+
+ "file_info": {
+ "name": {
+ "label": "Name",
+ "placeholder": "File name",
+ "help": "This is the new name that will be applied to shortcuts that do not already have this name."
+ },
+
+ "description": {
+ "label": "Description",
+ "placeholder": "File description",
+ "help": "This is the description that will be set for the shortcut."
+ },
+
+ "path": {
+ "label": "Path",
+ "placeholder": "File path",
+ "help": "This is the path where the icon will be applied.",
+ "rules": " - - You can use environment variables like
${APPDATA}
. - - Use
${DESKTOP}
for both user and public desktop paths. - - Wildcards like
*
and ?
are supported. - - Character sets like
[abc]
are supported, allowing you to match any one of the characters listed (e.g., a
, b
, or c
). - - Use multiple
*
for recursive folder searches: **
for 1 or fewer levels, ***
for 2 or fewer levels, and so on.
"
+ },
+
+ "destination": {
+ "label": "Destination",
+ "placeholder": "File destination",
+ "help": "This is the path that will be matched with destination paths of shortcuts in the source folder if the file is not found in the original path."
+ },
+
+ "url": {
+ "label": "URL",
+ "placeholder": "URL",
+ "help": "This is the URL that will be matched with URLs of files in the source folder if the file is not found in the original path."
+ }
+ },
+
+ "my_packs": {
+ "create_new_pack": {
+ "title": "Create Icon Pack"
+ },
+
+ "delete_pack": {
+ "confirmation_title": "Are you sure you want to delete this pack?",
+ "confirmation_description": "This action cannot be undone.",
+ "delete_generated_icons": "Delete Generated Icons"
+ },
+
+ "import_pack": {
+ "confirmation_title": "Do you want to import this pack?",
+ "success": "Pack imported successfully."
+ },
+
+ "edit_pack": {
+ "path_found": "Path found"
+ },
+
+ "buttons": {
+ "add_pack": {
+ "tooltip": "Add new pack"
+ },
+
+ "reload_packs": {
+ "tooltip": "Reload packs"
+ },
+
+ "import_pack": {
+ "tooltip": "Import pack"
+ }
+ },
+
+ "card": {
+ "pack_information": {
+ "label": "Pack Information",
+
+ "information": {
+ "name": {
+ "label": "Name",
+ "placeholder": "My Pack",
+
+ "message": {
+ "name_required": "Name is required",
+ "name_max": "Name can not exceed 32 characters"
+ }
+ },
+
+ "version": {
+ "label": "Version",
+ "placeholder": "v1.0.0",
+
+ "message": {
+ "version_required": "Version is required",
+ "version_format": "Version must be in the format v1.0.0"
+ }
+ },
+
+ "author": {
+ "label": "Author",
+ "placeholder": "Your name",
+
+ "message": {
+ "author_max": "Author can not exceed 32 characters"
+ }
+ },
+
+ "license": {
+ "label": "License",
+ "placeholder": "MIT",
+
+ "message": {
+ "license_max": "License can not exceed 64 characters"
+ }
+ },
+
+ "description": {
+ "label": "Description",
+ "placeholder": "This is my icon pack",
+
+ "message": {
+ "description_required": "Description is required",
+ "description_max": "Description can not exceed 1024 characters"
+ }
+ },
+
+ "icon": {
+ "label": "Icon"
+ }
+ }
+ },
+
+ "pack_actions": {
+ "label": "Pack Actions",
+ "admin_warning": "Some icons are only applied when app is runned as administrator.",
+ "restart_as_admin": "Restart as Administrator",
+ "apply_icon_pack": "Apply",
+ "edit_icon_pack": "Edit",
+ "export_icon_pack": "Export",
+ "add_icons_from_desktop": "Add Icons from Desktop",
+ "add_icons": "Add Icons",
+ "add_folder": "Add Folder Icon",
+ "add_empty_icon": "Add Empty Icon"
+ },
+
+ "pack_settings": {
+ "label": "Pack Settings",
+
+ "setting": {
+ "enabled": {
+ "label": "Enabled"
+ },
+
+ "corner_radius": {
+ "label": "Corner Radius",
+ "description": "Change the corner radius of the icons in this pack."
+ },
+
+ "opacity": {
+ "label": "Opacity",
+ "description": "Change the opacity of the icons in this pack."
+ }
+ }
+ },
+
+ "icons": {
+ "label": "Icons"
+ }
+ }
+ },
+
+ "settings": {
+ "restart_the_app_for_changes_to_take_effect": "Restart the app for changes to take effect.",
+ "are_you_sure_you_want_to_import_this_config": "Are you sure you want to import this config?",
+ "the_app_will_restart_to_load_the_new_config": "The app will be restarted to load the new config.",
+ "config_saved": "Config saved",
+ "there_was_an_error_saving_the_config": "There was an error saving the config.",
+
+ "categories": {
+ "general": "General",
+ "application": "Application",
+ "icon_pack": "Icon Pack",
+ "system": "System",
+ "advanced": "Advanced",
+ "update": "Update"
+ },
+
+ "setting": {
+ "language": {
+ "label": "Language",
+ "description": "Select your preferred language.",
+ "select_language": "Select language...",
+ "search_language": "Search language...",
+ "no_languages_found": "No languages found."
+ },
+
+ "theme": {
+ "label": "Theme",
+ "description": "Choose a color scheme for the interface."
+ },
+
+ "color_scheme": {
+ "label": "Color Scheme",
+ "description": "Select your preferred color scheme.",
+ "select_color_scheme": "Select color scheme...",
+ "search_color_scheme": "Search color scheme...",
+ "no_color_schemes_found": "No color schemes found.",
+
+ "color_schemes": {
+ "default": "Default",
+ "midnightAsh": "Midnight Ash",
+ "dawnMist": "Dawn Mist",
+ "forestDawn": "Forest Dawn",
+ "goldenEmber": "Golden Ember"
+ }
+ },
+
+ "window_effect": {
+ "label": "Window Effect",
+ "description": "Select the window effect.",
+
+ "auto": "Auto",
+ "none": "None",
+ "acrylic": "Acrylic",
+ "mica": "Mica",
+ "tabbed": "Tabbed"
+ },
+
+ "window_opacity": {
+ "label": "Window Opacity",
+ "description": "Adjust the window opacity. (This setting is ignored if 'None' is selected for window effect.)"
+ },
+
+ "window_scale": {
+ "label": "Window Scale",
+ "description": "Adjust the window scale."
+ },
+
+ "use_system_title_bar": {
+ "label": "Use System Title Bar",
+ "description": "Use the default system title bar instead of a custom one."
+ },
+
+ "logging": {
+ "label": "Enable Logging",
+ "description": "Enable logging to files."
+ },
+
+ "log_levels": {
+ "label": "Log Levels",
+ "description": "Select the log levels to record."
+ },
+
+ "max_log_files": {
+ "label": "Max Log Files",
+ "description": "Set the maximum number of log files to retain."
+ },
+
+ "import_export": {
+ "label": "Import/Export Settings",
+ "description": "Import or export settings your settings from/to a JSON file."
+ },
+
+ "save_window_status": {
+ "label": "Save Window Status",
+ "description": "Save window size, position, and state."
+ },
+
+ "match_lnk_by_destination": {
+ "label": "Match Windows Shortuts (.lnk) by Destination",
+ "description": "Match windows shortcuts by destination if matching by path is not successful."
+ },
+
+ "match_url_by_destination": {
+ "label": "Match .url files by URL",
+ "description": "Match .url files by URL if matching by path is not successful."
+ },
+
+ "rename_matched_files": {
+ "label": "Rename Matched Files",
+ "description": "Rename matched files if the names does not match."
+ },
+
+ "change_description_of_matched_lnk_files": {
+ "label": "Change Description of Matched Windows Shortcut (.lnk) Files",
+ "description": "Change the description of matched shortucts."
+ },
+
+ "check_for_updates": {
+ "label": "Check For Updates On Startup",
+ "description": "Check for updates on startup."
+ },
+
+ "update": {
+ "update_available": "Update available",
+ "no_updates_available": "No updates available",
+ "last_checked": "Last checked",
+ "check_for_updates": "Check for updates",
+ "update": "Update",
+ "updating": "Updating...",
+ "failed_to_check_for_updates": "Failed to check for updates. Please try again later.",
+ "failed_to_download_update": "Failed to download update. Please try again later.",
+ "failed_to_apply_update": "Failed to apply update.",
+ "update_applied": "Update applied successfully.",
+ "restarting": "Restarting...",
+ "need_admin_privileges": "Administration privileges are needed to update the application.",
+ "update_successful": "Update was successfully applied."
+ }
+ }
+ }
+}
diff --git a/frontend/public/locales/tr-TR.json b/frontend/public/locales/tr-TR.json
new file mode 100644
index 0000000..22a26fb
--- /dev/null
+++ b/frontend/public/locales/tr-TR.json
@@ -0,0 +1,330 @@
+{
+ "save": "Kaydet",
+ "cancel": "İptal",
+ "yes": "Evet",
+ "no": "Hayır",
+ "show_in_explorer": "Klasörü aç",
+ "import": "İçe Aktar",
+ "export": "Dışa Aktar",
+ "error": "Hata",
+ "show": "Göster",
+ "create": "Oluştur",
+ "delete": "Sil",
+
+ "downloading_missing_external_programs": "Eksik dosyalar indiriliyor...",
+ "downloaded_missing_external_programs": "Eksik dosyalar başarıyla indirildi.",
+
+ "nav": {
+ "my_packs": "Paketlerim",
+ "settings": "Ayarlar"
+ },
+
+ "file_info": {
+ "name": {
+ "label": "Ad",
+ "placeholder": "Dosya adı",
+ "help": "Bu, zaten bu ada sahip olmayan kısayollara verilecek yeni isimdir."
+ },
+
+ "description": {
+ "label": "Açıklama",
+ "placeholder": "Dosya açıklaması",
+ "help": "Bu, kısayola verilecek açıklamadır."
+ },
+
+ "path": {
+ "label": "Dosya yolu",
+ "placeholder": "Dosya yolu",
+ "help": "Bu, simgenin uygulanacağı dosyanın yoludur.",
+ "rules": " - -
${APPDATA}
gibi ortam değişkenlerini kullanabilirsiniz. - - Kullanıcı ve ortak masaüstü yolları için
${DESKTOP}
kullanın. - -
*
ve ?
gibi joker karakterleri desteklenir. - -
[abc]
gibi karakter kümeleri desteklenir, böylece listelenen karakterlerden herhangi birini eşleştirebilirsiniz (örneğin, a
, b
veya c
). - - Yinelemeli klasör arama için birden fazla
*
kullanın: **
1 veya daha az seviye, ***
2 veya daha az seviye gibi.
"
+ },
+
+ "destination": {
+ "label": "Hedef dosya yolu",
+ "placeholder": "Hedef dosya yolu",
+ "help": "Bu, dosyanın orijinal yolda bulunmaması durumunda, kaynak klasöründeki kısayolların hedef yollarıyla eşleştirilecek yoldur."
+ },
+
+ "url": {
+ "label": "URL",
+ "placeholder": "URL",
+ "help": "Bu, dosyanın orijinal yolda bulunmaması durumunda, kaynak klasöründeki dosyaların URL'leriyle eşleştirilecek URL'dir."
+ }
+ },
+
+ "my_packs": {
+ "create_new_pack": {
+ "title": "Simge Paketi Oluştur"
+ },
+
+ "delete_pack": {
+ "confirmation_title": "Bu paketi silmek istediğinizden emin misiniz?",
+ "confirmation_description": "Bu işlem geri alınamaz.",
+ "delete_generated_icons": "Oluşturulan simgeleri sil"
+ },
+
+ "import_pack": {
+ "confirmation_title": "Bu paketi yüklemek istiyor musunuz?",
+ "success": "Paket başarıyla yüklendi."
+ },
+
+ "edit_pack": {
+ "path_found": "Yol bulundu"
+ },
+
+ "buttons": {
+ "add_pack": {
+ "tooltip": "Yeni paket ekle"
+ },
+
+ "reload_packs": {
+ "tooltip": "Paketleri yenile"
+ },
+
+ "import_pack": {
+ "tooltip": "Paket yükle"
+ }
+ },
+
+ "card": {
+ "pack_information": {
+ "label": "Paket Bilgileri",
+
+ "information": {
+ "name": {
+ "label": "Ad",
+ "placeholder": "Benim Paketim",
+
+ "message": {
+ "name_required": "Ad gereklidir",
+ "name_max": "Ad, 32 karakteri geçemez"
+ }
+ },
+
+ "version": {
+ "label": "Versiyon",
+ "placeholder": "v1.0.0",
+
+ "message": {
+ "version_required": "Versiyon gereklidir",
+ "version_format": "Versiyon, \"v1.0.0\" formatında olmalıdır"
+ }
+ },
+
+ "author": {
+ "label": "Yazar",
+ "placeholder": "Adınız",
+
+ "message": {
+ "author_max": "Yazar, 32 karakteri geçemez"
+ }
+ },
+
+ "license": {
+ "label": "Lisans",
+ "placeholder": "MIT",
+
+ "message": {
+ "license_max": "Lisans, 64 karakteri geçemez"
+ }
+ },
+
+ "description": {
+ "label": "Açıklama",
+ "placeholder": "Paket içeriği",
+
+ "message": {
+ "description_max": "Açıklama, 1024 karakteri geçemez"
+ }
+ },
+
+ "icon": {
+ "label": "Simge"
+ }
+ }
+ },
+
+ "pack_actions": {
+ "label": "Paket İşlemleri",
+ "admin_warning": "Bazı simgeler yalnızca uygulama yönetici olarak çalıştırıldığında uygulanır.",
+ "restart_as_admin": "Yönetici olarak yeniden başlat",
+ "apply_icon_pack": "Uygula",
+ "export_icon_pack": "Dışa Aktar",
+ "edit_icon_pack": "Düzenle",
+ "add_icons_from_desktop": "Masaüstü Simgelerini Ekle",
+ "add_icons": "Simge Ekle",
+ "add_folder": "Klasör Simgesi Ekle",
+ "add_empty_icon": "Boş Simge Ekle"
+ },
+
+ "pack_settings": {
+ "label": "Paket Ayarları",
+
+ "setting": {
+ "enabled": {
+ "label": "Etkin"
+ },
+
+ "corner_radius": {
+ "label": "Köşe Yarıçapı",
+ "description": "Bu paketteki simgelerin köşe yarıçapını değiştirin."
+ },
+
+ "opacity": {
+ "label": "Opaklık",
+ "description": "Bu paketteki simgelerin opaklığını değiştirin."
+ }
+ }
+ },
+
+ "icons": {
+ "label": "Simgeler"
+ }
+ }
+ },
+
+ "settings": {
+ "restart_the_app_for_changes_to_take_effect": "Değişikliklerin etkili olması için uygulamayı yeniden başlatın.",
+ "are_you_sure_you_want_to_import_this_config": "Bu ayarları yüklemek istediğinize emin misiniz?",
+ "the_app_will_restart_to_load_the_new_config": "Uygulama, yeni ayarları yüklemek için yeniden başlatılacak.",
+ "config_saved": "Ayarlar kaydedildi",
+ "there_was_an_error_saving_the_config": "Ayarlar kaydedilirken bir hata oluştu.",
+
+ "categories": {
+ "general": "Genel",
+ "application": "Uygulama",
+ "icon_pack": "Simge Paketi",
+ "system": "Sistem",
+ "advanced": "Gelişmiş",
+ "update": "Güncelleme"
+ },
+
+ "setting": {
+ "language": {
+ "label": "Dil",
+ "description": "Tercih ettiğiniz dili seçin.",
+ "select_language": "Dil seçin...",
+ "search_language": "Dil ara...",
+ "no_languages_found": "Hiçbir dil bulunamadı."
+ },
+
+ "theme": {
+ "label": "Tema",
+ "description": "Arayüz için bir tema seçin."
+ },
+
+ "color_scheme": {
+ "label": "Renk Şeması",
+ "description": "Tercih ettiğiniz renk şemasını seçin.",
+ "select_color_scheme": "Renk şeması seçin...",
+ "search_color_scheme": "Renk şeması ara...",
+ "no_color_schemes_found": "Renk şeması bulunamadı.",
+
+ "color_schemes": {
+ "default": "Varsayılan",
+ "midnightAsh": "Gece Külü",
+ "dawnMist": "Şafak Sisi",
+ "forestDawn": "Orman Şafağı",
+ "goldenEmber": "Altın Kor"
+ }
+ },
+
+ "window_effect": {
+ "label": "Pencere Efekti",
+ "description": "Pencere efektini seçin.",
+
+ "auto": "Otomatik",
+ "none": "Yok",
+ "acrylic": "Akrilik",
+ "mica": "Mika",
+ "tabbed": "Tabbed"
+ },
+
+ "window_opacity": {
+ "label": "Pencere Opaklığı",
+ "description": "Pencere opaklığını ayarlayın. (Pencere efekti için 'Yok' seçilmişse bu ayar yok sayılır.)"
+ },
+
+ "window_scale": {
+ "label": "Pencere Ölçeği",
+ "description": "Pencere ölçeğini ayarlayın."
+ },
+
+ "use_system_title_bar": {
+ "label": "Sistem Başlık Çubuğunu Kullan",
+ "description": "Özel başlık çubuğu yerine varsayılan sistem başlık çubuğunu kullanın."
+ },
+
+ "logging": {
+ "label": "Günlüğe Kaydetmeyi Etkinleştir",
+ "description": "Dosyalara günlüğe kaydetmeyi etkinleştir."
+ },
+
+ "log_levels": {
+ "label": "Günlük Seviyeleri",
+ "description": "Kaydedilecek günlük seviyelerini seçin."
+ },
+
+ "max_log_files": {
+ "label": "Maksimum Günlük Dosyaları",
+ "description": "Tutulacak maksimum günlük dosyası sayısını ayarlayın."
+ },
+
+ "import_export": {
+ "label": "Ayarları İçe/Dışa Aktar",
+ "description": "Ayarlarınızı bir JSON dosyasından içe veya dışa aktarın."
+ },
+
+ "save_window_status": {
+ "label": "Pencere Durumunu Kaydet",
+ "description": "Pencere boyutunu, konumunu ve durumunu kaydet."
+ },
+
+ "match_by_destination": {
+ "label": "Kısayolları Hedeflerine Göre Eşleştir",
+ "description": "Dosya yolu ile eşleştirme başarısız olursa, kısayolları hedeflerine göre eşleştir."
+ },
+
+ "match_lnk_by_destination": {
+ "label": "Windows Kısayollarını (.lnk) Hedeflerine Göre Eşleştir",
+ "description": "Dosya yolu ile eşleştirme başarısız olursa, kısayolları hedeflerine göre eşleştir."
+ },
+
+ "match_url_by_destination": {
+ "label": "URL dosyalarını URL'e Göre Eşleştir",
+ "description": "Dosya yolu ile eşleştirme başarısız olursa, dosyaları URL'lerine göre eşleştir."
+ },
+
+ "rename_matched_files": {
+ "label": "Eşleşen Dosyaları Yeniden Adlandır",
+ "description": "İsimler eşleşmiyorsa, eşleşen dosyaları yeniden adlandır."
+ },
+
+ "change_description_of_matched_lnk_files": {
+ "label": "Eşleşen Windows Kısayol (.lnk) Dosyalarının Açıklamasını Değiştir",
+ "description": "Eşleşen kısayoların açıklamasını değiştir."
+ },
+
+ "check_for_updates": {
+ "label": "Başlangıçta Güncellemeleri Kontrol Et",
+ "description": "Başlangıçta güncellemeleri kontrol et."
+ },
+
+ "update": {
+ "update_available": "Güncelleme mevcut",
+ "no_updates_available": "Güncelleme yok",
+ "last_checked": "Son kontrol",
+ "check_for_updates": "Güncellemeleri kontrol et",
+ "update": "Güncelle",
+ "updating": "Güncelleniyor...",
+ "failed_to_check_for_updates": "Güncellemeler kontrol edilemedi. Lütfen daha sonra tekrar deneyin.",
+ "failed_to_download_update": "Güncelleme indirilemedi. Lütfen daha sonra tekrar deneyin.",
+ "failed_to_apply_update": "Güncelleme uygulanamadı.",
+ "update_applied": "Güncelleme uygulandı.",
+ "restarting": "Yeniden başlatılıyor...",
+ "need_admin_privileges": "Uygulamayı güncellemek için yönetici yetkileri gereklidir.",
+ "update_successful": "Güncelleme başarıyla uygulandı."
+ }
+ }
+ }
+}
diff --git a/frontend/public/setlnkdesc.vbs b/frontend/public/scripts/setlnkdesc.vbs
similarity index 100%
rename from frontend/public/setlnkdesc.vbs
rename to frontend/public/scripts/setlnkdesc.vbs
diff --git a/frontend/public/setlnkicon.vbs b/frontend/public/scripts/setlnkicon.vbs
similarity index 87%
rename from frontend/public/setlnkicon.vbs
rename to frontend/public/scripts/setlnkicon.vbs
index ae84591..b5cf8a9 100644
--- a/frontend/public/setlnkicon.vbs
+++ b/frontend/public/scripts/setlnkicon.vbs
@@ -1,4 +1,4 @@
-Dim Arg, shortcutPath, shortcutName, iconPath
+Dim Arg, shortcutPath, shortcutName, iconPath, iconIndex
Set Arg = WScript.Arguments
shortcutPath = Arg(0)
diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx
index 1c4280d..4804b49 100644
--- a/frontend/src/App.tsx
+++ b/frontend/src/App.tsx
@@ -1,16 +1,164 @@
-import { ThemeProvider } from "./contexts/theme-provider";
-import { ProfileProvider } from "./contexts/profile-provider";
-import TopBar from "./components/TopBar";
-import { FileGrid } from "./components/FileGrid";
+import ModeToggle from "@/components/ModeToggle";
+import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
+import TitleBar from "./components/TitleBar";
+import Settings from "./components/Settings";
+import { useTranslation } from "react-i18next";
+import { useEffect, useLayoutEffect, useState } from "react";
+import { useStorage } from "./contexts/storage-provider";
+import { Toaster } from "./components/ui/sonner";
+import { toast } from "sonner";
+import {
+ OpenFileInExplorer,
+ SendWindowsNotification,
+} from "@/wailsjs/go/main/App";
+import React from "react";
+import { useConfig } from "./contexts/config-provider";
+import { LogDebug } from "@/wailsjs/runtime/runtime";
+import Packs from "./components/Packs";
+import { Progress } from "./components/ui/progress";
+import { useProgress } from "./contexts/progress-provider";
function App() {
+ const { config, initialConfig } = useConfig();
+ const { t } = useTranslation();
+ const { setValue, getValue } = useStorage();
+ const [tab, setTab] = useState("packs");
+
+ const { progress, setProgress } = useProgress();
+
+ useLayoutEffect(() => {
+ if (
+ config &&
+ initialConfig &&
+ config.windowScale !== undefined &&
+ config.opacity !== undefined &&
+ initialConfig.windowEffect !== undefined
+ ) {
+ document.documentElement.style.fontSize =
+ config.windowScale * (16 / 100) + "px";
+
+ document.documentElement.style.setProperty(
+ "--opacity",
+ (
+ (initialConfig.windowEffect === 1 ? 100 : config.opacity) / 100
+ ).toString()
+ );
+ }
+ }, [config?.windowScale, config?.opacity, initialConfig?.windowEffect]);
+
+ window.toast = ({ title, description, path, variant }: any) => {
+ const props = {
+ description: t(description),
+ action: path
+ ? {
+ label: path.startsWith("__") ? t("show") : t("show_in_explorer"),
+ onClick: () => handleToastGotoPath(path),
+ }
+ : undefined,
+ };
+ switch (variant) {
+ case "message":
+ toast.message(t(title), props);
+ break;
+ case "success":
+ toast.success(t(title), props);
+ break;
+ case "info":
+ toast.info(t(title), props);
+ break;
+ case "warning":
+ toast.warning(t(title), props);
+ break;
+ case "error":
+ toast.error(t(title), props);
+ break;
+ default:
+ toast(t(title), props);
+ break;
+ }
+ };
+
+ const handleToastGotoPath = (path: string) => {
+ if (path.startsWith("__")) {
+ window.goto(path.substring(2));
+ } else {
+ OpenFileInExplorer(path);
+ }
+ };
+
+ window.goto = (path: string) => {
+ LogDebug("window.goto: " + path);
+ const pathArray = path.split("__");
+
+ setTab(pathArray[0]);
+
+ for (let i = 0; i < pathArray.length - 1; i++) {
+ setValue(pathArray[i], pathArray[i + 1]);
+ }
+ };
+
+ useEffect(() => {
+ setValue("path1", tab);
+ }, [tab]);
+
+ window.sendNotification = (
+ title: string,
+ message: string,
+ path: string,
+ variant: string
+ ) => {
+ SendWindowsNotification(t(title), t(message), path, variant);
+ };
+
+ window.setProgress = (value: number) => {
+ setProgress(value);
+ };
+
return (
-
-
-
-
-
-
+
+
+
+
+
+
+
+ setTab("packs")}
+ disabled={getValue("editingIconPack")}
+ className="px-6"
+ >
+ {t("nav.my_packs")}
+
+ setTab("settings")}
+ disabled={getValue("editingIconPack")}
+ className="px-6"
+ >
+ {t("nav.settings")}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
);
}
diff --git a/frontend/src/assets/appicon.png b/frontend/src/assets/appicon.png
new file mode 100644
index 0000000..84bcdc2
Binary files /dev/null and b/frontend/src/assets/appicon.png differ
diff --git a/frontend/src/assets/folder-search.png b/frontend/src/assets/folder-search.png
deleted file mode 100644
index 66524d9..0000000
Binary files a/frontend/src/assets/folder-search.png and /dev/null differ
diff --git a/frontend/src/assets/folder-search.svg b/frontend/src/assets/folder-search.svg
deleted file mode 100644
index 0de6ce9..0000000
--- a/frontend/src/assets/folder-search.svg
+++ /dev/null
@@ -1,7 +0,0 @@
-
\ No newline at end of file
diff --git a/frontend/src/assets/react.svg b/frontend/src/assets/react.svg
deleted file mode 100644
index 6c87de9..0000000
--- a/frontend/src/assets/react.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/frontend/src/assets/unknown.png b/frontend/src/assets/unknown.png
deleted file mode 100644
index a886939..0000000
Binary files a/frontend/src/assets/unknown.png and /dev/null differ
diff --git a/frontend/src/assets/vite.svg b/frontend/src/assets/vite.svg
deleted file mode 100644
index e7b8dfb..0000000
--- a/frontend/src/assets/vite.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/frontend/src/colorSchemes.json b/frontend/src/colorSchemes.json
new file mode 100644
index 0000000..1580a3e
--- /dev/null
+++ b/frontend/src/colorSchemes.json
@@ -0,0 +1,24 @@
+{
+ "colorSchemes": [
+ {
+ "code": "default",
+ "authors": ["Bedirhan Yenilmez"]
+ },
+ {
+ "code": "midnightAsh",
+ "authors": ["Bedirhan Yenilmez"]
+ },
+ {
+ "code": "dawnMist",
+ "authors": ["Bedirhan Yenilmez"]
+ },
+ {
+ "code": "forestDawn",
+ "authors": ["Bedirhan Yenilmez"]
+ },
+ {
+ "code": "goldenEmber",
+ "authors": ["Bedirhan Yenilmez"]
+ }
+ ]
+}
diff --git a/frontend/src/components/CreateProfileForm.tsx b/frontend/src/components/CreateProfileForm.tsx
deleted file mode 100644
index bf6862c..0000000
--- a/frontend/src/components/CreateProfileForm.tsx
+++ /dev/null
@@ -1,76 +0,0 @@
-"use client"
-
-import { AddProfile } from "wailsjs/go/main/App";
-
-import { zodResolver } from "@hookform/resolvers/zod"
-import { useForm } from "react-hook-form"
-import { z } from "zod"
-
-import { Button } from "@/components/ui/button"
-import {
- Form,
- FormControl,
- /* FormDescription, */
- FormField,
- FormItem,
- FormLabel,
- FormMessage,
-} from "@/components/ui/form"
-import { Input } from "@/components/ui/input"
-
-const AddProfileF = (name: string) => {
- AddProfile(name).then(() => {
- console.log("Profile created: " + name);
- // select profile
- });
-}
-
-const formSchema = z.object({
- profileName: z.string()
- .min(1, { message: "Profile name is required" })
- .max(50, { message: "Profile name can not exceed 50 characters" })
- // Check for windows file name compatibility
- .regex(/^(?!\.)/, { message: "Profile name can not start with a period" })
- .regex(/^(?!.*\.$)/, { message: "Profile name can not end with a period" })
- .regex(/^[^<>:"/\\|?*]*$/, { message: "Profile name can not contain any of the following characters: < > : \" / \\ | ? *" })
- .regex(/^(?!con$)(?!prn$)(?!aux$)(?!nul$)(?!com[0-9]$)(?!lpt[0-9]$)(?!com[¹²³])(?!lpt[¹²³])/i, { message: "Profile name can not be any of the following: con, prn, aux, nul, com[0-9], lpt[0-9], com⁽¹⁻³⁾, lpt⁽¹⁻³⁾" })
- ,
-})
-
-export function CreateProfileForm() {
- const form = useForm>({
- resolver: zodResolver(formSchema),
- defaultValues: {
- profileName: "",
- }
- });
-
- function onSubmit(data: z.infer) {
- AddProfileF(data.profileName);
- }
-
- return (
-
-
- )
-
-}
\ No newline at end of file
diff --git a/frontend/src/components/FileContainer.tsx b/frontend/src/components/FileContainer.tsx
deleted file mode 100644
index 0098258..0000000
--- a/frontend/src/components/FileContainer.tsx
+++ /dev/null
@@ -1,90 +0,0 @@
-import { useEffect, useRef, useState } from "react"
-import { GetIcon, SaveIcon, SaveProfile } from "wailsjs/go/main/App"
-import { Input } from "@/components/ui/input"
-import { Pencil, Settings2, XOctagon } from "lucide-react"
-import { Button } from "./ui/button"
-import { useProfile } from "@/contexts/profile-provider"
-
-import unknown from "../assets/folder-search.svg"
-import { fileInfo } from "@/structs"
-
-
-interface FileContainerProps {
- fileInfo: fileInfo
- index: number
-}
-
-export const FileContainer = (props: FileContainerProps) => {
- const { profile, setProfile } = useProfile();
- const [editing, setEditing] = useState(false);
-
- const iconRef = useRef(null);
-
- useEffect(() => {
- GetIconF();
- }, [])
-
- useEffect(() => {
- if (props.fileInfo.iconName !== "") {
- GetIconF();
- } else {
- iconRef.current!.src = unknown
- }
- }, [props.fileInfo.iconName])
-
- useEffect(() => {
- if (editing) {
- SaveProfile(profile.name, JSON.stringify(profile)).then(() => setEditing(false))
- }
- }, [profile])
-
- function GetIconF() {
- if (props.fileInfo.iconName !== "") {
- GetIcon(profile.name, props.fileInfo.iconName).then((res) => {
- if (res) iconRef.current!.src = res
- });
- }
- }
-
- function DeleteRow() {
- setEditing(true)
- setProfile({ ...profile, value: profile.value.filter((_, i) => i !== props.index) })
- }
-
- return (
-
-
-
-
{
- setEditing(true)
- setProfile({ ...profile, value: profile.value.map((f, i) => i === props.index ? { ...f, name: e.target.value } : f) })
- }}
- />
-
{
- setEditing(true);
- setProfile({ ...profile, value: profile.value.map((f, i) => i === props.index ? { ...f, description: e.target.value } : f) })
- }} />
-
-
-
-
-
-
- )
-}
\ No newline at end of file
diff --git a/frontend/src/components/FileGrid.tsx b/frontend/src/components/FileGrid.tsx
deleted file mode 100644
index 2aaa9d5..0000000
--- a/frontend/src/components/FileGrid.tsx
+++ /dev/null
@@ -1,56 +0,0 @@
-import { useEffect, useState } from "react";
-import { useProfile } from "@/contexts/profile-provider";
-import { SaveProfile, GetFileInfo } from "wailsjs/go/main/App";
-import { FileContainer } from "./FileContainer"
-
-import { fileInfo } from "@/structs"
-import { Button } from "./ui/button";
-import { PlusSquare } from "lucide-react";
-
-export const FileGrid = () => {
- const { profile, setProfile } = useProfile();
- const [fileInfos, setFileInfos] = useState();
-
- /* const GetDesktopIconsF = () => {
- GetDesktopIcons().then((res) => {
- console.log(res);
- setFileInfos(res);
- })
- }
-
- useEffect(() => {
- GetDesktopIconsF();
- }, []) */
-
- useEffect(() => {
- setFileInfos(profile?.value);
- }, [profile])
-
- const AddRow = () => {
- GetFileInfo(profile.name).then((fileInfo) => {
- if (fileInfo.extension !== ".lnk") return
- setProfile({ ...profile, value: [...profile.value, fileInfo] })
- SaveProfile(profile.name, JSON.stringify({ ...profile, value: [...profile.value, fileInfo] }))
- })
- }
-
-
- return (
-
- {fileInfos?.map((fileInfo, i) => (
-
- ))}
-
- {profile.name && (
-
- )}
-
-
-
- )
-}
\ No newline at end of file
diff --git a/frontend/src/components/Image.tsx b/frontend/src/components/Image.tsx
new file mode 100644
index 0000000..50055de
--- /dev/null
+++ b/frontend/src/components/Image.tsx
@@ -0,0 +1,45 @@
+import React, { useState } from "react";
+import { Skeleton } from "./ui/skeleton";
+import { CircleHelp } from "lucide-react";
+
+// Define the props type for the Image component
+interface ImageProps {
+ src: string; // Assuming `icon` is a URL or path to the image
+ className?: string;
+ cornerRadius?: number;
+ opacity?: number;
+ unkown?: boolean;
+}
+
+const Image: React.FC = ({
+ src,
+ className,
+ cornerRadius = 0,
+ opacity = 100,
+ unkown = false,
+ ...rest
+}) => {
+ const [loading, setLoading] = useState(true);
+
+ return (
+ <>
+ {unkown ? :
+
+ }
+
{
+ setLoading(false)
+ }}
+ style={{
+ borderRadius: `${cornerRadius}%`,
+ opacity: `${opacity / 100}`,
+ }}
+ {...rest}
+ />
+ >
+ );
+};
+
+export default Image;
diff --git a/frontend/src/components/ModeToggle.tsx b/frontend/src/components/ModeToggle.tsx
new file mode 100644
index 0000000..4f07345
--- /dev/null
+++ b/frontend/src/components/ModeToggle.tsx
@@ -0,0 +1,23 @@
+import { Moon, Sun } from "lucide-react";
+import { Button } from "@/components/ui/button";
+import { useTheme } from "@/contexts/theme-provider";
+
+export default function ModeToggle() {
+ const { theme, setTheme } = useTheme();
+
+ return (
+
+ );
+}
diff --git a/frontend/src/components/Packs.tsx b/frontend/src/components/Packs.tsx
new file mode 100644
index 0000000..b98caad
--- /dev/null
+++ b/frontend/src/components/Packs.tsx
@@ -0,0 +1,1758 @@
+"use client";
+
+import { useState, useEffect, useRef, useCallback } from "react";
+import { useForm } from "react-hook-form";
+import { z } from "zod";
+import { zodResolver } from "@hookform/resolvers/zod";
+import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
+import { Button } from "@/components/ui/button";
+import { Input } from "@/components/ui/input";
+import { Switch } from "@/components/ui/switch";
+import {
+ Check,
+ CircleAlert,
+ Download,
+ Edit,
+ Folder,
+ FolderOpen,
+ Images,
+ Loader2,
+ LucideTrash,
+ Monitor,
+ Pencil,
+ Plus,
+ RefreshCw,
+ SquarePlus,
+ Trash,
+ UploadIcon,
+} from "lucide-react";
+import {
+ AreYouSureDialog,
+ AreYouSureDialogRef,
+} from "@/components/ui/are-you-sure";
+import {
+ Dialog,
+ DialogClose,
+ DialogContent,
+ DialogHeader,
+ DialogTitle,
+ DialogTrigger,
+} from "@/components/ui/dialog";
+import SelectImage from "./SelectImage";
+import {
+ AddDeletePngRelativePath,
+ AddFilesToIconPackFromDesktop,
+ AddFilesToIconPackFromPath,
+ AddFileToIconPackFromPath,
+ AddIconPack,
+ ApplyIconPack,
+ ClearDeletePngPaths,
+ ClearIconPackCache,
+ ClearSelectImages,
+ ClearTempPngPaths,
+ CreateLastTab,
+ DeleteDeletePngPaths,
+ DeleteIconPack,
+ Description,
+ Destination,
+ ExportIconPack,
+ Ext,
+ GeneralPathExits,
+ GetFileInfoFromDesktop,
+ GetFileInfoFromPaths,
+ GetFilePath,
+ GetIcnmMetadata,
+ GetIconFiles,
+ GetIconFolder,
+ GetIconPack,
+ GetIconPackList,
+ GetIconPackPath,
+ ImportIconPack,
+ Name,
+ NeedsAdminPrivileges,
+ ReadLastTab,
+ RestartApplication,
+ SetIconPackField,
+ SetIconPackFiles,
+ SetIconPackMetadata,
+ SetImageIfAbsent,
+ UUID,
+} from "@/wailsjs/go/main/App";
+import { main } from "@/wailsjs/go/models";
+import {
+ Form,
+ FormControl,
+ FormField,
+ FormItem,
+ FormLabel,
+ FormMessage,
+} from "@/components/ui/form";
+import {
+ SettingContent,
+ SettingDescription,
+ SettingLabel,
+ SettingsItem,
+} from "./ui/settings-group";
+import Image from "./Image";
+import { Slider } from "./ui/my-slider";
+import { Skeleton } from "./ui/skeleton";
+import { Checkbox } from "./ui/checkbox";
+import { useTranslation } from "react-i18next";
+import { useStorage } from "@/contexts/storage-provider";
+import {
+ Accordion,
+ AccordionContent,
+ AccordionItem,
+ AccordionTrigger,
+} from "@/components/ui/accordion";
+import { LogDebug } from "@/wailsjs/runtime/runtime";
+import { Label } from "./ui/label";
+import { Textarea } from "./ui/textarea";
+import { HoverCard, HoverCardContent, HoverCardTrigger } from "./ui/hover-card";
+import { HelpCard } from "./ui/help-card";
+import parse from "html-react-parser";
+import { useProgress } from "@/contexts/progress-provider";
+
+export default function Packs() {
+ const { t } = useTranslation();
+ const { getValue, setValue } = useStorage();
+
+ const [editingIconPack, setEditingIconPack] = useState(false);
+ useEffect(() => {
+ setValue("editingIconPack", editingIconPack);
+ }, [editingIconPack]);
+
+ const [selectedPackId, setSelectedPackId] = useState("");
+ const [iconPacks, setIconPacks] = useState();
+ const [selectedPackKeyCount, setSelectedPackKeyCount] = useState(0);
+
+ const [reloadingIconPacks, setReloadingIconPacks] = useState(false);
+
+ const dialogCloseRef = useRef(null);
+ const dialogImportRef = useRef(null);
+ const [importPackPath, setImportPackPath] = useState("");
+
+ const [tempMetadata, setTempMetadata] = useState(
+ main.Metadata.createFrom({})
+ );
+
+ const tabsListRef = useRef(null);
+ const [hasOverflow, setHasOverflow] = useState(false);
+ useEffect(() => {
+ const element = tabsListRef.current as HTMLElement | null;
+ if (!element) {
+ return;
+ }
+
+ const overflow = element.scrollHeight > element.clientHeight;
+ LogDebug(
+ element.scrollHeight + " > " + element.clientHeight + " = " + overflow
+ );
+ setHasOverflow(overflow);
+ }, [editingIconPack]);
+
+ useEffect(() => {
+ setSelectedPackId(getValue("packs") || "");
+ }, [getValue("packs")]);
+
+ const loadIconPacks = async () => {
+ const packs = await GetIconPackList();
+ setIconPacks(packs);
+ };
+
+ const reloadSelectedPack = () => {
+ setSelectedPackKeyCount(selectedPackKeyCount + 1);
+ };
+
+ const handleReloadIconPacks = async () => {
+ setReloadingIconPacks(true);
+
+ // Start the timer for at least 250ms
+ const spinMinDuration = new Promise((resolve) => setTimeout(resolve, 250));
+
+ // Clear icon cache and reload packs
+ const reloadJob = ClearIconPackCache().then(() => {
+ return loadIconPacks().then(() => {
+ reloadSelectedPack();
+ });
+ });
+
+ // Wait for both the spin duration and the job to complete
+ await Promise.all([spinMinDuration, reloadJob]);
+
+ // Stop the spin animation
+ setReloadingIconPacks(false);
+ };
+
+ const handleImportIconPack = () => {
+ GetIconPackPath().then((path) => {
+ if (path) {
+ setImportPackPath(path);
+ GetIcnmMetadata(path).then((metadata) => {
+ setTempMetadata(metadata);
+ dialogImportRef.current?.openDialog();
+ });
+ } else {
+ setImportPackPath("");
+ }
+ });
+ };
+
+ window.importIconPack = (path: string) => {
+ setImportPackPath(path);
+ GetIcnmMetadata(path).then((metadata) => {
+ if (metadata.id === "") {
+ setImportPackPath("");
+ } else {
+ setTempMetadata(metadata);
+ dialogImportRef.current?.openDialog();
+ }
+ });
+ };
+
+ const handleAcceptImportIconPack = () => {
+ ImportIconPack(importPackPath).then((id) => {
+ handleReloadIconPacks().then(() => {
+ ClearTempPngPaths();
+ setSelectedPackId(id);
+ });
+ });
+ };
+
+ useEffect(() => {
+ loadIconPacks();
+ }, []);
+
+ useEffect(() => {
+ reloadSelectedPack();
+ }, [selectedPackId]);
+
+ useEffect(() => {
+ if (!editingIconPack && selectedPackId !== "") {
+ CreateLastTab(selectedPackId).then(() => {
+ window.location.reload();
+ });
+ }
+ }, [editingIconPack]);
+
+ useEffect(() => {
+ ReadLastTab().then((packId) => {
+ if (packId) {
+ setSelectedPackId(packId);
+ }
+ });
+ }, []);
+
+ return (
+
+
+
+
+
+
+
+
+
{
+ setImportPackPath("");
+ ClearTempPngPaths();
+ }}
+ >
+
+
+
+
+ {tempMetadata.name}
+
+
+ {tempMetadata.version}
+
+
+
+
+
+
+
+
+
+ {iconPacks?.map((pack) => (
+
+ ))}
+
+
+
+ {selectedPackId &&
+ (editingIconPack ? (
+
+ ) : (
+
+ ))}
+
+ );
+}
+
+interface PackTriggerProps {
+ packId: string;
+ selectedPackId: string;
+ setSelectedPackId: (packId: string) => void;
+ reloadSelectedPack: () => void;
+ editingIconPack: boolean;
+ disabled?: boolean;
+ loadIconPacks: () => void;
+}
+
+function PackTrigger({
+ packId,
+ selectedPackId,
+ setSelectedPackId,
+ reloadSelectedPack,
+ editingIconPack,
+ disabled,
+ loadIconPacks,
+ ...props
+}: PackTriggerProps) {
+ const [iconPack, setIconPack] = useState();
+ const [enabledState, setEnabledState] = useState(false);
+
+ useEffect(() => {
+ GetIconPack(packId).then((iconPack) => {
+ setIconPack(iconPack);
+ setEnabledState(iconPack.settings.enabled);
+ });
+ }, [packId]);
+
+ const handleEnable = () => {
+ SetIconPackField(packId, "settings.json", "enabled", !enabledState).then(
+ () => {
+ setEnabledState(!enabledState);
+ loadIconPacks();
+ if (packId === selectedPackId) {
+ reloadSelectedPack();
+ }
+ }
+ );
+ };
+
+ return (
+ setSelectedPackId(packId)}
+ className="flex justify-between p-4 w-full"
+ disabled={disabled}
+ {...props}
+ >
+
+
+ {!editingIconPack && (
+
+
+ {iconPack?.metadata.name}
+
+
{iconPack?.metadata.version}
+
+ )}
+
+ {false && !editingIconPack && (
+ e.stopPropagation()}
+ />
+ )}
+
+ );
+}
+
+interface PackContentProps {
+ iconPackId: string;
+ setSelectedPackId: (packId: string) => void;
+ loadIconPacks: () => void;
+ setEditingIconPack: (editingIconPack: boolean) => void;
+ reloadIconPacks: () => void;
+}
+
+function PackContent({
+ iconPackId,
+ setSelectedPackId,
+ loadIconPacks,
+ setEditingIconPack,
+ reloadIconPacks,
+}: PackContentProps) {
+ const { t } = useTranslation();
+ const { progress } = useProgress();
+
+ const [needsAdmin, setNeedsAdmin] = useState(false);
+
+ const [loading, setLoading] = useState(true);
+ const [editingMetadata, setEditingMetadata] = useState(false);
+ const [iconPack, setIconPack] = useState();
+ const dialogRef = useRef(null);
+ const [deleteGeneratedIcons, setDeleteGeneratedIcons] = useState(false);
+
+ const [enabled, setEnabled] = useState(false);
+ const [cornerRadius, setCornerRadius] = useState(-1);
+ const [opacity, setOpacity] = useState(-1);
+
+ const [applyRunning, setApplyRunning] = useState(false);
+ useState(false);
+ const [addIconsFromDesktopRunning, setAddIconsFromDesktopRunning] =
+ useState(false);
+ const [addIconsRunning, setAddIconsRunning] = useState(false);
+ const [addFolderRunning, setAddFolderRunning] = useState(false);
+ const running =
+ applyRunning ||
+ addIconsFromDesktopRunning ||
+ addIconsRunning ||
+ addFolderRunning ||
+ editingMetadata ||
+ progress != 0;
+
+ useEffect(() => {
+ GetIconPack(iconPackId).then((pack) => {
+ setIconPack(pack);
+ setEnabled(pack.settings.enabled);
+ setOpacity(pack.settings.opacity);
+ setCornerRadius(pack.settings.cornerRadius);
+ setLoading(false);
+ });
+
+ NeedsAdminPrivileges().then((result) => {
+ setNeedsAdmin(result);
+ });
+ }, []);
+
+ const handleSettingChange = (
+ field: keyof main.IconPack["settings"],
+ value: boolean | number
+ ) => {
+ if (iconPack === undefined) {
+ return;
+ }
+
+ SetIconPackField(iconPackId, "settings.json", field, value).then(() => {
+ const newIconPack = {
+ ...iconPack,
+ settings: {
+ ...iconPack.settings,
+ [field]: value,
+ },
+ } as main.IconPack;
+
+ setIconPack(newIconPack);
+
+ if (field === "enabled") {
+ console.log("loadIconPacks");
+ loadIconPacks();
+ }
+ });
+ };
+
+ const handleEditStart = () => {
+ setEditingMetadata(true);
+ };
+
+ const handleEditSave = (metadata: main.Metadata) => {
+ const updateMetadataJob = async () => {
+ if (iconPack === undefined) {
+ return;
+ }
+
+ const oldMetadata = iconPack.metadata;
+
+ oldMetadata.name = metadata.name;
+ oldMetadata.version = metadata.version;
+ oldMetadata.author = metadata.author;
+ oldMetadata.license = metadata.license;
+ oldMetadata.description = metadata.description;
+
+ SetIconPackMetadata(iconPackId, oldMetadata);
+ };
+
+ Promise.all([
+ ClearSelectImages(),
+ DeleteDeletePngPaths(),
+ updateMetadataJob(),
+ ]).then(() => {
+ reloadIconPacks();
+ });
+ };
+
+ const handleEditCancel = () => {
+ setEditingMetadata(false);
+ ClearTempPngPaths();
+ ClearDeletePngPaths();
+ ClearSelectImages();
+ };
+
+ const handleDelete = () => {
+ DeleteIconPack(iconPackId, deleteGeneratedIcons).then(() => {
+ setSelectedPackId("");
+ loadIconPacks();
+ });
+ };
+
+ const fields: (keyof main.IconPack["metadata"])[] = [
+ "name",
+ "version",
+ "author",
+ "license",
+ ];
+
+ const handleEditIconPack = () => {
+ setEditingIconPack(true);
+ };
+
+ const handleExportIconPack = () => {
+ ExportIconPack(iconPackId);
+ };
+
+ const handleApplyIconPack = () => {
+ setApplyRunning(true);
+ ApplyIconPack(iconPackId).finally(() => {
+ setApplyRunning(false);
+ });
+ };
+
+ const handleAddIconsFromDesktop = () => {
+ setAddIconsFromDesktopRunning(true);
+
+ AddFilesToIconPackFromDesktop(iconPackId).then(() => {
+ GetIconPack(iconPackId)
+ .then((pack) => {
+ setIconPack(pack);
+ })
+ .finally(() => {
+ setAddIconsFromDesktopRunning(false);
+ });
+ });
+ };
+
+ const handleAddIcon = () => {
+ GetIconFiles().then((files) => {
+ if (files) {
+ setAddIconsRunning(true);
+
+ AddFilesToIconPackFromPath(iconPackId, files, true).then(() => {
+ GetIconPack(iconPackId)
+ .then((pack) => {
+ setIconPack(pack);
+ })
+ .finally(() => {
+ setAddIconsRunning(false);
+ });
+ });
+ }
+ });
+ };
+
+ const handleAddFolder = () => {
+ GetIconFolder().then((folder) => {
+ if (folder) {
+ setAddFolderRunning(true);
+
+ AddFileToIconPackFromPath(iconPackId, folder, true).then(() => {
+ GetIconPack(iconPackId)
+ .then((pack) => {
+ setIconPack(pack);
+ })
+ .finally(() => {
+ setAddFolderRunning(false);
+ });
+ });
+ }
+ });
+ };
+
+ const openDialog = useCallback(() => {
+ if (dialogRef.current) {
+ setDeleteGeneratedIcons(false);
+ dialogRef.current.openDialog();
+ }
+ }, []);
+
+ if (loading || iconPack === undefined) {
+ return (
+
+
+
+
+
+
+ );
+ }
+
+ return (
+
+
+
+ {t("my_packs.card.pack_information.label")}
+
+
+
+
+
+ {editingMetadata && (
+
+ )}
+
+
+
+ {!editingMetadata && (
+
+
+ {fields.map((field) => (
+
+
+ {t(
+ "my_packs.card.pack_information.information." +
+ field +
+ ".label"
+ )}
+
+
+ {iconPack.metadata[field]}
+
+
+ ))}
+
+ {iconPack.metadata.description && (
+
+
+ {t(
+ "my_packs.card.pack_information.information.description.label"
+ )}
+
+
+ {iconPack.metadata.description}
+
+
+ )}
+
+ )}
+
+ {editingMetadata && (
+
+ )}
+
+
+ {!editingMetadata && (
+
+
+
+
+
+
+ setDeleteGeneratedIcons(!deleteGeneratedIcons)
+ }
+ />
+
+
+
+
+ )}
+
+
+
+
+
+ {t("my_packs.card.pack_actions.label")}
+
+ {needsAdmin && (
+
+
+
+
+ {t("my_packs.card.pack_actions.admin_warning")}
+
+
+ )}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {t("my_packs.card.pack_settings.label")}
+
+ {false && (
+
+
+ {t("my_packs.card.pack_settings.setting.enabled.label")}
+
+
+ {
+ setEnabled(enabled as boolean);
+ handleSettingChange("enabled", enabled);
+ }}
+ />
+
+
+ )}
+
+
+
+
+ {t("my_packs.card.pack_settings.setting.corner_radius.label")}
+
+
+ {t(
+ "my_packs.card.pack_settings.setting.corner_radius.description"
+ )}
+
+
+
+
+
0%
+
setCornerRadius(value[0] as number)}
+ onPointerUp={() =>
+ handleSettingChange("cornerRadius", cornerRadius)
+ }
+ defaultValue={[iconPack.settings.cornerRadius]}
+ min={0}
+ max={50}
+ step={1}
+ className={"w-56 cursor-pointer"}
+ />
+ 50%
+
+ ({cornerRadius}%)
+
+
+
+
+
+
+
+
+ {t("my_packs.card.pack_settings.setting.opacity.label")}
+
+
+ {t("my_packs.card.pack_settings.setting.opacity.description")}
+
+
+
+
+
10%
+
setOpacity(value[0] as number)}
+ onPointerUp={() => handleSettingChange("opacity", opacity)}
+ defaultValue={[iconPack.settings.opacity]}
+ min={10}
+ max={100}
+ step={1}
+ className={"w-56 cursor-pointer"}
+ />
+ 100%
+ ({opacity}%)
+
+
+
+
+
+ {iconPack.files?.filter((file) => file.hasIcon).length > 0 && (
+
+
+ {t("my_packs.card.icons.label")}
+
+
+ {iconPack.files?.map((file) =>
+ file.hasIcon ? (
+
+ ) : null
+ )}
+
+
+ )}
+
+ );
+}
+
+interface PackEditProps {
+ iconPackId: string;
+ setEditingIconPack: (editingIconPack: boolean) => void;
+}
+
+function PackEdit({ iconPackId, setEditingIconPack }: PackEditProps) {
+ const { t } = useTranslation();
+ const { progress } = useProgress();
+
+ const [loading, setLoading] = useState(true);
+ const [files, setFiles] = useState();
+ const [updateArray, setUpdateArray] = useState(
+ Array.from({ length: 4096 }, (_, i) => i)
+ );
+ const [updateArray2, setUpdateArray2] = useState(
+ Array.from({ length: 4096 }, (_, i) => i)
+ );
+
+ const [addIconsFromDesktopRunning, setAddIconsFromDesktopRunning] =
+ useState(false);
+ const [addIconsRunning, setAddIconsRunning] = useState(false);
+ const [addFolderRunning, setAddFolderRunning] = useState(false);
+ const running =
+ addIconsFromDesktopRunning ||
+ addIconsRunning ||
+ addFolderRunning ||
+ progress != 0;
+
+ useEffect(() => {
+ GetIconPack(iconPackId).then((pack) => {
+ setFiles(pack.files);
+ setLoading(false);
+ });
+ }, []);
+
+ const handleAddIconsFromDesktop = () => {
+ setAddIconsFromDesktopRunning(true);
+
+ GetFileInfoFromDesktop("temp")
+ .then((fileInfos) => {
+ const oldFiles = files || [];
+ oldFiles.push(...fileInfos);
+ setFiles(oldFiles);
+ })
+ .finally(() => {
+ setAddIconsFromDesktopRunning(false);
+ });
+ };
+
+ const handleAddIcon = () => {
+ GetIconFiles().then((paths) => {
+ if (paths) {
+ setAddIconsRunning(true);
+
+ GetFileInfoFromPaths("temp", paths)
+ .then((fileInfos) => {
+ const oldFiles = files || [];
+ oldFiles.push(...fileInfos);
+ setFiles(oldFiles);
+ })
+ .finally(() => {
+ setAddIconsRunning(false);
+ });
+ }
+ });
+ };
+
+ const handleAddFolder = () => {
+ GetIconFolder().then((folder) => {
+ if (folder) {
+ setAddFolderRunning(true);
+
+ GetFileInfoFromPaths("temp", [folder])
+ .then((fileInfos) => {
+ const oldFiles = files || [];
+ oldFiles.push(...fileInfos);
+ setFiles(oldFiles);
+ })
+ .finally(() => {
+ setAddFolderRunning(false);
+ });
+ }
+ });
+ };
+
+ const handleAddEmptyIcon = () => {
+ setAddIconsRunning(true);
+
+ UUID()
+ .then((uuid) => {
+ const oldFiles = files || [];
+ oldFiles.push(
+ main.FileInfo.createFrom({
+ id: uuid,
+ name: "New file",
+ description: "",
+ path: "",
+ destinationPath: "",
+ extension: "",
+ hasIcon: false,
+ iconId: "",
+ })
+ );
+ setFiles(oldFiles);
+ })
+ .finally(() => {
+ setAddIconsRunning(false);
+ });
+ };
+
+ const handleRemoveIcon = (index: number) => {
+ if (!files) return;
+ setFiles((prevFiles) => prevFiles?.filter((_, i) => i !== index));
+ AddDeletePngRelativePath(
+ `packs\\${iconPackId}\\icons\\${files[index].id}.png`
+ );
+ };
+
+ const handleInputChange = (index: number, field: string, value: string) => {
+ setFiles((prevFiles) =>
+ prevFiles?.map((file, i) =>
+ i === index ? { ...file, [field]: value } : file
+ )
+ );
+ };
+
+ const handleCancelEdit = () => {
+ ClearTempPngPaths();
+ ClearSelectImages().then(() => setEditingIconPack(false));
+ };
+
+ const handleSaveEdit = () => {
+ if (!files) return;
+ DeleteDeletePngPaths().then(() => {
+ SetIconPackFiles(iconPackId, files).then(() => {
+ ClearTempPngPaths();
+ ClearSelectImages().then(() => setEditingIconPack(false));
+ });
+ });
+ };
+
+ if (loading || files === undefined) {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+ }
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {files.map((file, index) => (
+
+ {
+ e.stopPropagation();
+ handleRemoveIcon(index);
+ }}
+ >
+
+
+ }
+ >
+
+
+ {file.name}
+
+
+
+
+
+
+
+ {
+ setUpdateArray((prevUpdateArray) => {
+ const newArray = [...prevUpdateArray];
+ newArray[index] = prevUpdateArray[index] + 1;
+ return newArray;
+ });
+ }}
+ key={updateArray2[index]}
+ editable
+ />
+
+
{
+ handleInputChange(index, "name", value);
+ }}
+ label={t("file_info.name.label")}
+ className="justify-between w-full"
+ />
+
+ {file.extension === ".lnk" && (
+
{
+ handleInputChange(index, "description", value);
+ }}
+ label={t("file_info.description.label")}
+ />
+ )}
+ {
+ handleInputChange(index, "path", value);
+ Ext(value).then((ext) => {
+ handleInputChange(index, "extension", ext);
+ console.log(ext);
+ });
+ if (file.name === "" || file.name === "New file") {
+ Name(value).then((name) => {
+ handleInputChange(index, "name", name);
+ });
+ }
+ if (file.description === "") {
+ Description(value).then((description) => {
+ console.log(description);
+ handleInputChange(index, "description", description);
+ });
+ }
+ if (file.destinationPath === "") {
+ Destination(value).then((destinationPath) => {
+ console.log("d: " + destinationPath);
+ handleInputChange(
+ index,
+ "destinationPath",
+ destinationPath
+ );
+ });
+ }
+
+ SetImageIfAbsent(file.id, value).then(() => {
+ setUpdateArray((prevUpdateArray) => {
+ const newArray = [...prevUpdateArray];
+ newArray[index] = prevUpdateArray[index] + 1;
+ return newArray;
+ });
+ setUpdateArray2((prevUpdateArray) => {
+ const newArray = [...prevUpdateArray];
+ newArray[index] = prevUpdateArray[index] + 1;
+ return newArray;
+ });
+ });
+ }}
+ label={t("file_info.path.label")}
+ />
+
+ {(file.extension === ".lnk" || file.extension === ".url") && (
+ {
+ handleInputChange(index, "destinationPath", value);
+ }}
+ label={t(
+ `file_info.${
+ file.extension === ".url" ? "url" : "destination"
+ }.label`
+ )}
+ />
+ )}
+
+
+
+ ))}
+
+
+
+ );
+}
+
+function TextInput({
+ value,
+ placeholder,
+ onChange,
+ label,
+ className = "",
+ helpText,
+}: {
+ value: string;
+ placeholder?: string;
+ onChange: (value: string) => void;
+ label: string;
+ className?: string;
+ helpText?: string;
+}) {
+ return (
+
+
+
+ {helpText && }
+
+
{
+ onChange(e.target.value);
+ }}
+ />
+
+ );
+}
+
+function PathInput({
+ value,
+ placeholder,
+ onChange,
+ label,
+ helpText,
+}: {
+ value: string;
+ placeholder?: string;
+ onChange: (value: string) => void;
+ label: string;
+ helpText?: string;
+}) {
+ const { t } = useTranslation();
+ const [exists, setExists] = useState(false);
+
+ const handleChoosePath = () => {
+ GetFilePath(value).then((path) => {
+ if (path) {
+ LogDebug("Path selected: " + path);
+ onChange(path);
+ }
+ });
+ };
+
+ const updateExistence = () => {
+ GeneralPathExits(value).then((exists) => {
+ setExists(exists);
+ });
+ };
+
+ useEffect(() => {
+ updateExistence();
+ }, [value]);
+
+ return (
+
+
+ {
+ onChange(value);
+ updateExistence();
+ }}
+ label={label}
+ className="w-full"
+ helpText={helpText!}
+ />
+ {exists && (
+
+
+
+
+
+ {t("my_packs.edit_pack.path_found")}
+
+
+ )}
+
+
+
+ );
+}
+
+interface CreatePackFormProps {
+ reloadIconPacks?: () => void;
+ dialogCloseRef?: React.RefObject;
+ handleSave?: (metadata: main.Metadata) => void;
+ handleCancel?: () => void;
+ defaultValues?: main.Metadata;
+}
+
+function CreatePackForm({
+ reloadIconPacks,
+ dialogCloseRef,
+ handleSave,
+ handleCancel,
+ defaultValues,
+}: CreatePackFormProps) {
+ const { t } = useTranslation();
+
+ const formSchema = z.object({
+ icon: z.string(),
+ name: z
+ .string()
+ .min(1, {
+ message: t(
+ "my_packs.card.pack_information.information.name.message.name_required"
+ ),
+ })
+ .max(32, {
+ message: t(
+ "my_packs.card.pack_information.information.name.message.name_max"
+ ),
+ }),
+ version: z
+ .string()
+ .min(1, {
+ message: t(
+ "my_packs.card.pack_information.information.version.message.version_required"
+ ),
+ })
+ .regex(/^v[0-9]{1,4}\.[0-9]{1,4}\.[0-9]{1,4}$/, {
+ message: t(
+ "my_packs.card.pack_information.information.version.message.version_format"
+ ),
+ }),
+ author: z.string().max(32, {
+ message: t(
+ "my_packs.card.pack_information.information.author.message.author_max"
+ ),
+ }),
+ license: z.string().max(64, {
+ message: t(
+ "my_packs.card.pack_information.information.license.message.license_max"
+ ),
+ }),
+ description: z.string().max(1024, {
+ message: t(
+ "my_packs.card.pack_information.information.description.message.description_max"
+ ),
+ }),
+ });
+
+ const form = useForm>({
+ resolver: zodResolver(formSchema),
+ defaultValues: {
+ icon: "",
+ name: defaultValues?.name || "",
+ version: defaultValues?.version || "v1.0.0",
+ author: defaultValues?.author || "",
+ license: defaultValues?.license || "",
+ description: defaultValues?.description || "",
+ },
+ });
+
+ function onSubmit(data: z.infer) {
+ if (reloadIconPacks) {
+ AddIconPack(
+ data.name,
+ data.version,
+ data.author,
+ data.license,
+ data.description
+ ).then(() => {
+ reloadIconPacks();
+ dialogCloseRef?.current?.click();
+ });
+ } else {
+ handleSave?.(main.Metadata.createFrom(data));
+ }
+ }
+
+ useEffect(() => {
+ return () => {
+ // This function runs before the component unmounts
+ ClearTempPngPaths();
+ ClearSelectImages();
+ };
+ }, []);
+
+ return (
+
+
+ );
+}
diff --git a/frontend/src/components/SelectImage.tsx b/frontend/src/components/SelectImage.tsx
new file mode 100644
index 0000000..5349af2
--- /dev/null
+++ b/frontend/src/components/SelectImage.tsx
@@ -0,0 +1,123 @@
+import React, { useEffect, useState } from "react";
+import { CircleHelp, CircleX, Images, RotateCw } from "lucide-react";
+import { Button } from "./ui/button";
+import {
+ ActionSelectImage,
+ GetSelectImage,
+ UploadSelectImage,
+} from "@/wailsjs/go/main/App";
+import { main } from "@/wailsjs/go/models";
+import { Skeleton } from "./ui/skeleton";
+
+interface SelectImageProps {
+ src?: string;
+ packId?: string;
+ className?: string;
+ sizeClass?: string;
+ editSizeClass?: string;
+ editable?: boolean;
+ onChange?: () => void;
+}
+
+const SelectImage: React.FC = ({
+ src: originalSrc = "",
+ packId = "temp",
+ className,
+ sizeClass = "w-12 h-12",
+ editSizeClass = "w-7 h-7",
+ editable = false,
+ onChange,
+ ...rest
+}) => {
+ const [loading, setLoading] = useState(true);
+ const [imageProperties, setImageProperties] = useState({
+ id: packId,
+ path: originalSrc,
+ tempPath: "",
+ hasOriginal: true,
+ hasTemp: false,
+ isRemoved: false,
+ });
+
+ const handleUpload = () => {
+ UploadSelectImage(packId).then((properties) => {
+ setImageProperties(properties);
+ onChange?.();
+ });
+ };
+
+ const handleAction = () => {
+ ActionSelectImage(packId).then((properties) => {
+ setImageProperties(properties);
+ console.log(properties);
+ onChange?.();
+ });
+ };
+
+ useEffect(() => {
+ GetSelectImage(packId, originalSrc).then((properties) => {
+ setImageProperties(properties);
+ onChange?.();
+ });
+ }, [editable]);
+
+ return (
+
+ {!loading &&
+ editable &&
+ (imageProperties.hasOriginal ||
+ imageProperties.isRemoved ||
+ imageProperties.hasTemp) && (
+
+ )}
+
+
+ );
+};
+
+export default SelectImage;
diff --git a/frontend/src/components/SettingItems/ChangeDescriptionOfMathcedLnkFilesSetting.tsx b/frontend/src/components/SettingItems/ChangeDescriptionOfMathcedLnkFilesSetting.tsx
new file mode 100644
index 0000000..b9423e1
--- /dev/null
+++ b/frontend/src/components/SettingItems/ChangeDescriptionOfMathcedLnkFilesSetting.tsx
@@ -0,0 +1,18 @@
+import { useTranslation } from "react-i18next";
+import { SwitchConfig } from "./Presets/SwitchConfig";
+
+export function ChangeDescriptionOfMathcedLnkFilesSetting() {
+ const { t } = useTranslation();
+
+ return (
+
+ );
+}
diff --git a/frontend/src/components/SettingItems/CheckForUpdatesSetting.tsx b/frontend/src/components/SettingItems/CheckForUpdatesSetting.tsx
new file mode 100644
index 0000000..596712f
--- /dev/null
+++ b/frontend/src/components/SettingItems/CheckForUpdatesSetting.tsx
@@ -0,0 +1,14 @@
+import { useTranslation } from "react-i18next";
+import { SwitchConfig } from "./Presets/SwitchConfig";
+
+export function CheckForUpdatesSetting() {
+ const { t } = useTranslation();
+
+ return (
+
+ );
+}
diff --git a/frontend/src/components/SettingItems/ColorSchemeSetting.tsx b/frontend/src/components/SettingItems/ColorSchemeSetting.tsx
new file mode 100644
index 0000000..393f688
--- /dev/null
+++ b/frontend/src/components/SettingItems/ColorSchemeSetting.tsx
@@ -0,0 +1,65 @@
+import { useTranslation } from "react-i18next";
+import {
+ SettingsItem,
+ SettingContent,
+ SettingDescription,
+ SettingLabel,
+} from "@/components/ui/settings-group";
+import { Combobox } from "@/components/ui/combobox";
+import colorSchemes from "@/colorSchemes.json";
+import { ColorScheme, useColorScheme } from "@/contexts/color-scheme-provider";
+
+export function ColorSchemeSetting() {
+ const { colorScheme, setColorScheme, updateColorScheme } = useColorScheme();
+ const { t } = useTranslation();
+
+ const handleColorSchemeChange = (value: string) => {
+ setColorScheme(value as ColorScheme);
+ };
+
+ return (
+
+
+ {t("settings.setting.color_scheme.label")}
+
+ {t("settings.setting.color_scheme.description")}
+
+
+
+ ({
+ value: colorScheme.code,
+ label: t(
+ "settings.setting.color_scheme.color_schemes." + colorScheme.code
+ ),
+ }))}
+ placeholder={t("settings.setting.color_scheme.select_color_scheme")}
+ searchPlaceholder={t(
+ "settings.setting.color_scheme.search_color_scheme"
+ )}
+ nothingFoundMessage={t(
+ "settings.setting.color_scheme.no_color_schemes_found"
+ )}
+ onChange={handleColorSchemeChange}
+ onMouseEnter={(value) => {
+ const root = document.documentElement;
+
+ root.classList.remove(colorScheme);
+ root.classList.add(value);
+ }}
+ onMouseLeave={(value) => {
+ const root = document.documentElement;
+
+ root.classList.remove(value);
+ root.classList.add(colorScheme);
+ }}
+ onCollapse={() => {
+ updateColorScheme();
+ }}
+ />
+
+
+ );
+}
diff --git a/frontend/src/components/SettingItems/EnableLoggingSetting.tsx b/frontend/src/components/SettingItems/EnableLoggingSetting.tsx
new file mode 100644
index 0000000..a2d8e73
--- /dev/null
+++ b/frontend/src/components/SettingItems/EnableLoggingSetting.tsx
@@ -0,0 +1,15 @@
+import { useTranslation } from "react-i18next";
+import { SwitchConfig } from "./Presets/SwitchConfig";
+
+export function EnableLoggingSetting() {
+ const { t } = useTranslation();
+
+ return (
+
+ );
+}
diff --git a/frontend/src/components/SettingItems/ImportExportSetting.tsx b/frontend/src/components/SettingItems/ImportExportSetting.tsx
new file mode 100644
index 0000000..7e4aa8d
--- /dev/null
+++ b/frontend/src/components/SettingItems/ImportExportSetting.tsx
@@ -0,0 +1,67 @@
+import { useRef, useState } from "react";
+import { useTranslation } from "react-i18next";
+import {
+ GetLoadConfigPath,
+ ReadConfig,
+ RestartApplication,
+ SaveConfigDialog,
+} from "@/wailsjs/go/main/App";
+import {
+ SettingsItem,
+ SettingContent,
+ SettingDescription,
+ SettingLabel,
+} from "@/components/ui/settings-group";
+import { Button } from "@/components/ui/button";
+import {
+ AreYouSureDialog,
+ AreYouSureDialogRef,
+} from "@/components/ui/are-you-sure";
+import { LogDebug } from "@/wailsjs/runtime/runtime";
+
+export function ImportExportSetting() {
+ const { t } = useTranslation();
+ const dialogRef = useRef(null);
+ const [usePath, setUsePath] = useState("");
+
+ const handleImportButtonClick = async () => {
+ const path = await GetLoadConfigPath();
+ if (path) {
+ setUsePath(path);
+ dialogRef.current?.openDialog();
+ }
+ };
+
+ const handleAcceptImport = async () => {
+ LogDebug(`Attempting to read config from ${usePath}`);
+ await ReadConfig(usePath);
+ RestartApplication(false, ["--goto", "settings__advanced"]);
+ };
+
+ return (
+
+
+ {t("settings.setting.import_export.label")}
+
+ {t("settings.setting.import_export.description")}
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/frontend/src/components/SettingItems/LocaleSetting.tsx b/frontend/src/components/SettingItems/LocaleSetting.tsx
new file mode 100644
index 0000000..00bd23a
--- /dev/null
+++ b/frontend/src/components/SettingItems/LocaleSetting.tsx
@@ -0,0 +1,59 @@
+import { useEffect, useState } from "react";
+import { useTranslation } from "react-i18next";
+import {
+ SettingsItem,
+ SettingContent,
+ SettingDescription,
+ SettingLabel,
+} from "@/components/ui/settings-group";
+import { Combobox } from "@/components/ui/combobox";
+import locales from "@/locales.json";
+import { useConfig } from "@/contexts/config-provider";
+
+export function LocaleSetting() {
+ const { config, setConfigField } = useConfig();
+ const { t, i18n } = useTranslation();
+ const [{ isLoading, language }, setState] = useState({
+ isLoading: true,
+ language: "",
+ });
+
+ useEffect(() => {
+ if (isLoading && config?.language !== undefined) {
+ setState({ language: config.language, isLoading: false });
+ }
+ }, [isLoading, config?.language]);
+
+ const handleLanguageChange = (value: string) => {
+ setConfigField("language", value);
+ i18n.changeLanguage(value);
+ setState((prevState) => ({ ...prevState, language: value }));
+ };
+
+ return (
+
+
+ {t("settings.setting.language.label")}
+
+ {t("settings.setting.language.description")}
+
+
+
+ ({
+ value: locale.code,
+ label: locale.name,
+ }))}
+ placeholder={t("settings.setting.language.select_language")}
+ searchPlaceholder={t("settings.setting.language.search_language")}
+ nothingFoundMessage={t(
+ "settings.setting.language.no_languages_found"
+ )}
+ onChange={handleLanguageChange}
+ />
+
+
+ );
+}
diff --git a/frontend/src/components/SettingItems/LogLevelSetting.tsx b/frontend/src/components/SettingItems/LogLevelSetting.tsx
new file mode 100644
index 0000000..fc2055e
--- /dev/null
+++ b/frontend/src/components/SettingItems/LogLevelSetting.tsx
@@ -0,0 +1,113 @@
+import { useEffect, useState } from "react";
+import { useTranslation } from "react-i18next";
+import {
+ SettingsItem,
+ SettingContent,
+ SettingDescription,
+ SettingLabel,
+} from "@/components/ui/settings-group";
+import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group";
+import { useConfig } from "@/contexts/config-provider";
+
+type LogLevels = {
+ enableTrace: boolean;
+ enableDebug: boolean;
+ enableInfo: boolean;
+ enableWarn: boolean;
+ enableError: boolean;
+ enableFatal: boolean;
+};
+
+export function LogLevelSetting() {
+ const { config, setConfigField } = useConfig();
+ const { t } = useTranslation();
+ const [state, setState] = useState<{
+ isLoading: boolean;
+ logLevels: LogLevels;
+ }>({
+ isLoading: true,
+ logLevels: {
+ enableTrace: false,
+ enableDebug: false,
+ enableInfo: false,
+ enableWarn: false,
+ enableError: false,
+ enableFatal: false,
+ },
+ });
+
+ useEffect(() => {
+ if (state.isLoading && config) {
+ const {
+ enableTrace,
+ enableDebug,
+ enableInfo,
+ enableWarn,
+ enableError,
+ enableFatal,
+ } = config;
+ if (
+ enableTrace !== undefined &&
+ enableDebug !== undefined &&
+ enableInfo !== undefined &&
+ enableWarn !== undefined &&
+ enableError !== undefined &&
+ enableFatal !== undefined
+ ) {
+ setState({
+ isLoading: false,
+ logLevels: {
+ enableTrace,
+ enableDebug,
+ enableInfo,
+ enableWarn,
+ enableError,
+ enableFatal,
+ },
+ });
+ }
+ }
+ }, [config, state.isLoading]);
+
+ const handleToggle = (level: keyof LogLevels) => {
+ const newLogLevels = {
+ ...state.logLevels,
+ [level]: !state.logLevels[level],
+ };
+ setConfigField(level, newLogLevels[level]);
+ setState({ ...state, logLevels: newLogLevels });
+ };
+
+ const activeLogLevels = Object.entries(state.logLevels)
+ .filter(([, value]) => value)
+ .map(([key]) => key);
+
+ return (
+
+
+ {t("settings.setting.log_levels.label")}
+
+ {t("settings.setting.log_levels.description")}
+
+
+
+
+ {Object.keys(state.logLevels).map((level) => (
+ handleToggle(level as keyof LogLevels)}
+ >
+ {level.replace("enable", "")}
+
+ ))}
+
+
+
+ );
+}
diff --git a/frontend/src/components/SettingItems/MatchLnkByDestinationSetting.tsx b/frontend/src/components/SettingItems/MatchLnkByDestinationSetting.tsx
new file mode 100644
index 0000000..c5c576d
--- /dev/null
+++ b/frontend/src/components/SettingItems/MatchLnkByDestinationSetting.tsx
@@ -0,0 +1,14 @@
+import { useTranslation } from "react-i18next";
+import { SwitchConfig } from "./Presets/SwitchConfig";
+
+export function MatchLnkByDestinationSetting() {
+ const { t } = useTranslation();
+
+ return (
+
+ );
+}
diff --git a/frontend/src/components/SettingItems/MatchURLByDestinationSetting.tsx b/frontend/src/components/SettingItems/MatchURLByDestinationSetting.tsx
new file mode 100644
index 0000000..2899d95
--- /dev/null
+++ b/frontend/src/components/SettingItems/MatchURLByDestinationSetting.tsx
@@ -0,0 +1,14 @@
+import { useTranslation } from "react-i18next";
+import { SwitchConfig } from "./Presets/SwitchConfig";
+
+export function MatchURLByDestinationSetting() {
+ const { t } = useTranslation();
+
+ return (
+
+ );
+}
diff --git a/frontend/src/components/SettingItems/MaxLogFilesSetting.tsx b/frontend/src/components/SettingItems/MaxLogFilesSetting.tsx
new file mode 100644
index 0000000..27067af
--- /dev/null
+++ b/frontend/src/components/SettingItems/MaxLogFilesSetting.tsx
@@ -0,0 +1,64 @@
+import { useEffect, useState } from "react";
+import { useTranslation } from "react-i18next";
+import {
+ SettingsItem,
+ SettingContent,
+ SettingDescription,
+ SettingLabel,
+} from "@/components/ui/settings-group";
+import { Input } from "@/components/ui/input";
+import { useConfig } from "@/contexts/config-provider";
+
+export function MaxLogFilesSetting() {
+ const { config, setConfigField } = useConfig();
+ const { t } = useTranslation();
+ const [{ isLoading, maxLogFiles }, setState] = useState({
+ isLoading: true,
+ maxLogFiles: "",
+ });
+
+ useEffect(() => {
+ if (isLoading && config?.maxLogFiles !== undefined) {
+ setState({
+ isLoading: false,
+ maxLogFiles: config.maxLogFiles.toString(),
+ });
+ }
+ }, [isLoading, config?.maxLogFiles]);
+
+ const handleMaxLogFilesChange = (textValue: string) => {
+ const parsedValue = parseInt(textValue);
+ const value = isNaN(parsedValue)
+ ? 20
+ : Math.max(1, Math.min(10000, parsedValue));
+ setConfigField("maxLogFiles", value);
+ setState((prevState) => ({
+ ...prevState,
+ maxLogFiles: textValue === "" ? "" : value.toString(),
+ }));
+ };
+
+ return (
+
+
+ {t("settings.setting.max_log_files.label")}
+
+ {`${t("settings.setting.max_log_files.description")} (${t(
+ "settings.restart_the_app_for_changes_to_take_effect"
+ )})`}
+
+
+
+ handleMaxLogFilesChange(e.target.value)}
+ min={1}
+ max={10000}
+ onKeyDown={(e) => e.key.match(/[-+]/) && e.preventDefault()}
+ />
+
+
+ );
+}
diff --git a/frontend/src/components/SettingItems/Presets/SliderConfig.tsx b/frontend/src/components/SettingItems/Presets/SliderConfig.tsx
new file mode 100644
index 0000000..21c3c03
--- /dev/null
+++ b/frontend/src/components/SettingItems/Presets/SliderConfig.tsx
@@ -0,0 +1,98 @@
+import { useEffect, useState } from "react";
+import { useTranslation } from "react-i18next";
+import {
+ SettingsItem,
+ SettingContent,
+ SettingDescription,
+ SettingLabel,
+} from "@/components/ui/settings-group";
+import { useConfig } from "@/contexts/config-provider";
+import { main } from "@/wailsjs/go/models";
+import { Slider } from "@/components/ui/my-slider";
+
+interface SliderConfigProps {
+ configKey: keyof main.Config;
+ label: string;
+ description?: string;
+ requiresRestart?: boolean;
+ min?: number;
+ max?: number;
+ step?: number;
+ onChange?: (value: number) => void;
+ onSave?: (value: number) => void;
+ disabled?: boolean;
+}
+
+export function SliderConfig({
+ configKey,
+ label,
+ description = "",
+ requiresRestart = false,
+ min = 0,
+ max = 100,
+ step = 1,
+ onChange,
+ onSave,
+ disabled = false,
+ ...rest
+}: SliderConfigProps) {
+ const { config, setConfigField } = useConfig();
+ const { t } = useTranslation();
+ const [{ isLoading, value }, setState] = useState({
+ isLoading: true,
+ value: 50,
+ });
+
+ useEffect(() => {
+ if (isLoading && config?.[configKey] !== undefined) {
+ setState({ value: config[configKey] as number, isLoading: false });
+ }
+ }, [isLoading, config, configKey]);
+
+ const handleSliderChange = (value: number) => {
+ setState((prevState) => ({ ...prevState, value }));
+ onChange?.(value);
+ };
+
+ const handleSliderSave = () => {
+ setConfigField(configKey, value);
+ onSave?.(value);
+ };
+
+ return (
+
+
+ {label}
+ {description && (
+
+ {description}
+ {requiresRestart &&
+ ` (${t("settings.restart_the_app_for_changes_to_take_effect")})`}
+
+ )}
+
+
+
+
{min}%
+
handleSliderChange(value[0] as number)}
+ onPointerUp={handleSliderSave}
+ defaultValue={[value]}
+ min={min}
+ max={max}
+ step={step}
+ className={"w-64 cursor-pointer"}
+ {...rest}
+ />
+ {max}%
+ ({value}%)
+
+
+
+ );
+}
diff --git a/frontend/src/components/SettingItems/Presets/SwitchConfig.tsx b/frontend/src/components/SettingItems/Presets/SwitchConfig.tsx
new file mode 100644
index 0000000..c497fab
--- /dev/null
+++ b/frontend/src/components/SettingItems/Presets/SwitchConfig.tsx
@@ -0,0 +1,68 @@
+import { useEffect, useState } from "react";
+import { useTranslation } from "react-i18next";
+import {
+ SettingsItem,
+ SettingContent,
+ SettingDescription,
+ SettingLabel,
+} from "@/components/ui/settings-group";
+import { Switch } from "@/components/ui/switch";
+import { useConfig } from "@/contexts/config-provider";
+import { main } from "@/wailsjs/go/models";
+
+interface SwitchConfigProps {
+ configKey: keyof main.Config;
+ label: string;
+ description?: string;
+ requiresRestart?: boolean;
+}
+
+export function SwitchConfig({
+ configKey,
+ label,
+ description = "",
+ requiresRestart = false,
+}: SwitchConfigProps) {
+ const { config, setConfigField } = useConfig();
+ const { t } = useTranslation();
+ const [{ isLoading, switchValue }, setState] = useState({
+ isLoading: true,
+ switchValue: false,
+ });
+
+ useEffect(() => {
+ if (isLoading && config?.[configKey] !== undefined) {
+ setState({ switchValue: config[configKey] as boolean, isLoading: false });
+ }
+ }, [isLoading, config, configKey]);
+
+ const handleSwitch = (value: boolean) => {
+ setConfigField(configKey, value);
+ setState((prevState) => ({ ...prevState, switchValue: value }));
+ };
+
+ return (
+
+
+ {label}
+ {description && (
+
+ {description}
+ {requiresRestart &&
+ ` (${t("settings.restart_the_app_for_changes_to_take_effect")})`}
+
+ )}
+
+
+ handleSwitch(!switchValue)}
+ />
+
+
+ );
+}
diff --git a/frontend/src/components/SettingItems/RenameMatchedFilesSetting.tsx b/frontend/src/components/SettingItems/RenameMatchedFilesSetting.tsx
new file mode 100644
index 0000000..ae6a828
--- /dev/null
+++ b/frontend/src/components/SettingItems/RenameMatchedFilesSetting.tsx
@@ -0,0 +1,14 @@
+import { useTranslation } from "react-i18next";
+import { SwitchConfig } from "./Presets/SwitchConfig";
+
+export function RenameMatchedFilesSetting() {
+ const { t } = useTranslation();
+
+ return (
+
+ );
+}
diff --git a/frontend/src/components/SettingItems/SaveWindowStatusSetting.tsx b/frontend/src/components/SettingItems/SaveWindowStatusSetting.tsx
new file mode 100644
index 0000000..6738d05
--- /dev/null
+++ b/frontend/src/components/SettingItems/SaveWindowStatusSetting.tsx
@@ -0,0 +1,14 @@
+import { useTranslation } from "react-i18next";
+import { SwitchConfig } from "./Presets/SwitchConfig";
+
+export function SaveWindowStatusSetting() {
+ const { t } = useTranslation();
+
+ return (
+
+ );
+}
diff --git a/frontend/src/components/SettingItems/ThemeSetting.tsx b/frontend/src/components/SettingItems/ThemeSetting.tsx
new file mode 100644
index 0000000..7d4076f
--- /dev/null
+++ b/frontend/src/components/SettingItems/ThemeSetting.tsx
@@ -0,0 +1,62 @@
+import { useTranslation } from "react-i18next";
+import {
+ SettingsItem,
+ SettingContent,
+ SettingDescription,
+ SettingLabel,
+} from "@/components/ui/settings-group";
+import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group";
+import { Monitor, Moon, Sun } from "lucide-react";
+import { Theme, useTheme } from "@/contexts/theme-provider";
+
+export function ThemeSetting() {
+ const { t } = useTranslation();
+ const { theme, setTheme } = useTheme();
+
+ const themes = [
+ {
+ value: "system",
+ label: "Use system theme",
+ icon: ,
+ },
+ {
+ value: "light",
+ label: "Use light theme",
+ icon: ,
+ },
+ {
+ value: "dark",
+ label: "Use dark theme",
+ icon: ,
+ },
+ ];
+
+ const handleThemeChange = (selectedTheme: string) => {
+ setTheme(selectedTheme as Theme);
+ };
+
+ return (
+
+
+ {t("settings.setting.theme.label")}
+
+ {t("settings.setting.theme.description")}
+
+
+
+
+ {themes.map((item) => (
+ handleThemeChange(item.value)}
+ >
+ {item.icon}
+
+ ))}
+
+
+
+ );
+}
diff --git a/frontend/src/components/SettingItems/UpdateSetting.tsx b/frontend/src/components/SettingItems/UpdateSetting.tsx
new file mode 100644
index 0000000..f74c662
--- /dev/null
+++ b/frontend/src/components/SettingItems/UpdateSetting.tsx
@@ -0,0 +1,148 @@
+import { useEffect, useState } from "react";
+import { CheckForUpdate, Update, UpdateAsAdmin } from "@/wailsjs/go/main/App";
+import { main } from "@/wailsjs/go/models";
+import { Button } from "@/components/ui/button";
+import {
+ SettingContent,
+ SettingDescription,
+ SettingLabel,
+ SettingsItem,
+} from "@/components/ui/settings-group";
+import { ArrowRight, RefreshCw } from "lucide-react";
+import { GetConfigField, NeedsAdminPrivileges } from "@/wailsjs/go/main/App";
+import { useTranslation } from "react-i18next";
+import { useStorage } from "@/contexts/storage-provider";
+
+export function UpdateSetting() {
+ const { t } = useTranslation();
+ const { getValue } = useStorage();
+
+ const [updateInfo, setUpdateInfo] = useState(
+ main.UpdateInfo.createFrom({})
+ );
+
+ const [lastUpdateCheck, setLastUpdateCheck] = useState(0);
+ const [needsAdmin, setNeedsAdmin] = useState(false);
+ const [isChecking, setIsChecking] = useState(false);
+ const [isUpdating, setIsUpdating] = useState(false);
+
+ useEffect(() => {
+ // Initial check for admin privileges and update availability
+ NeedsAdminPrivileges().then(setNeedsAdmin);
+ handleCheckForUpdate();
+ }, []);
+
+ useEffect(() => {
+ // Update last update check timestamp from config
+ GetConfigField("LastUpdateCheck").then((value) =>
+ setLastUpdateCheck(value as number)
+ );
+ }, [updateInfo]);
+
+ useEffect(() => {
+ // Automatic update trigger from argument
+ const storedUpdate = getValue("update");
+ if (storedUpdate && !isChecking && !isUpdating) {
+ setIsUpdating(true);
+ Update(storedUpdate).finally(() => setIsUpdating(false));
+ }
+ }, [getValue]);
+
+ const handleCheckForUpdate = () => {
+ if (!isUpdating) {
+ // Start the spinner and set a minimum duration for the animation
+ setIsChecking(true);
+ const spinMinDuration = new Promise((resolve) =>
+ setTimeout(resolve, 500)
+ );
+
+ // Check for updates and update state accordingly
+ const updateJob = CheckForUpdate().then(setUpdateInfo);
+
+ // Wait for both the minimum spin duration and the update check to complete
+ Promise.all([spinMinDuration, updateJob]).finally(() =>
+ setIsChecking(false)
+ );
+ }
+ };
+
+ const handleUpdate = () => {
+ // Perform update based on admin privileges
+ setIsUpdating(true);
+ const updater = needsAdmin ? UpdateAsAdmin : Update;
+ updater(updateInfo.downloadUrl).finally(() => setIsUpdating(false));
+ };
+
+ const formatDate = (timestamp: number) => {
+ // Format timestamp into readable date string
+ const date = new Date(timestamp * 1000);
+ return `${date.toLocaleDateString()} ${date.toLocaleTimeString()}`;
+ };
+
+ return (
+
+
+
+
+
+
+
+ {updateInfo.updateAvailable
+ ? t("settings.setting.update.update_available")
+ : t("settings.setting.update.no_updates_available")}
+ {lastUpdateCheck && lastUpdateCheck !== 0 && (
+
+ {" (" +
+ t("settings.setting.update.last_checked") +
+ ": " +
+ formatDate(lastUpdateCheck) +
+ ")"}
+
+ )}
+
+
+ {updateInfo.updateAvailable && (
+
+
+ v{updateInfo.currentVersion}
+
+
+ {updateInfo.latestVersion}
+
+ )}
+ {!updateInfo.updateAvailable && (
+
+ v{updateInfo.currentVersion}
+
+ )}
+ {!updateInfo.updateAvailable && }
+
+
+
+
+
+ {updateInfo.updateAvailable && (
+
+ )}
+
+
+
+
+ {updateInfo.name}
+
+ {updateInfo.releaseNotes}
+
+
+
+ );
+}
diff --git a/frontend/src/components/SettingItems/UseSystemTitleBarSetting.tsx b/frontend/src/components/SettingItems/UseSystemTitleBarSetting.tsx
new file mode 100644
index 0000000..a502e5c
--- /dev/null
+++ b/frontend/src/components/SettingItems/UseSystemTitleBarSetting.tsx
@@ -0,0 +1,15 @@
+import { useTranslation } from "react-i18next";
+import { SwitchConfig } from "./Presets/SwitchConfig";
+
+export function UseSystemTitleBarSetting() {
+ const { t } = useTranslation();
+
+ return (
+
+ );
+}
diff --git a/frontend/src/components/SettingItems/WindowEffectSetting.tsx b/frontend/src/components/SettingItems/WindowEffectSetting.tsx
new file mode 100644
index 0000000..8ed6c73
--- /dev/null
+++ b/frontend/src/components/SettingItems/WindowEffectSetting.tsx
@@ -0,0 +1,97 @@
+import {
+ SettingsItem,
+ SettingContent,
+ SettingDescription,
+ SettingLabel,
+} from "@/components/ui/settings-group";
+import { useEffect, useState } from "react";
+import { useTranslation } from "react-i18next";
+import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group";
+import { useConfig } from "@/contexts/config-provider";
+
+export function WindowEffectSetting() {
+ const { config, initialConfig, setConfigField } = useConfig();
+ const { t } = useTranslation();
+
+ const [{ isLoading, useWindowEffect }, setLoadingAndEffect] = useState<{
+ isLoading: boolean;
+ useWindowEffect: string;
+ }>({ isLoading: true, useWindowEffect: "" });
+
+ useEffect(() => {
+ if (isLoading && config?.windowEffect !== undefined) {
+ setLoadingAndEffect({
+ isLoading: false,
+ useWindowEffect: config.windowEffect.toString(),
+ });
+ }
+ }, [config?.windowEffect]);
+
+ const handleToggle = (number: number) => {
+ setConfigField("windowEffect", number);
+ setLoadingAndEffect((prev) => ({
+ ...prev,
+ useWindowEffect: number.toString(),
+ }));
+ };
+
+ const windowEffectOptions = [
+ {
+ number: 1,
+ label: t("settings.setting.window_effect.none"),
+ aria: "No window effect",
+ },
+ {
+ number: 0,
+ label: t("settings.setting.window_effect.auto"),
+ aria: "Auto window effect",
+ },
+ {
+ number: 2,
+ label: t("settings.setting.window_effect.mica"),
+ aria: "Mica window effect",
+ },
+ {
+ number: 3,
+ label: t("settings.setting.window_effect.acrylic"),
+ aria: "Acrylic window effect",
+ },
+ {
+ number: 4,
+ label: t("settings.setting.window_effect.tabbed"),
+ aria: "Tabbed window effect",
+ },
+ ];
+
+ return (
+
+
+ {t("settings.setting.window_effect.label")}
+
+ {`${t("settings.setting.window_effect.description")} (${t(
+ "settings.restart_the_app_for_changes_to_take_effect"
+ )})`}
+
+
+
+
+ {windowEffectOptions.map(({ number, label, aria }) => (
+ handleToggle(number)}
+ className={
+ number === initialConfig?.windowEffect
+ ? "font-bold text-shadow-xl shadow-foreground"
+ : ""
+ }
+ >
+ {label}
+
+ ))}
+
+
+
+ );
+}
diff --git a/frontend/src/components/SettingItems/WindowOpacitySetting.tsx b/frontend/src/components/SettingItems/WindowOpacitySetting.tsx
new file mode 100644
index 0000000..3e8a408
--- /dev/null
+++ b/frontend/src/components/SettingItems/WindowOpacitySetting.tsx
@@ -0,0 +1,25 @@
+import { useTranslation } from "react-i18next";
+import { SliderConfig } from "./Presets/SliderConfig";
+import { useConfig } from "@/contexts/config-provider";
+
+export function WindowOpacitySetting() {
+ const { t } = useTranslation();
+ const { initialConfig } = useConfig();
+ return (
+ {
+ document.documentElement.style.setProperty(
+ "--opacity",
+ String(value / 100)
+ );
+ }}
+ />
+ );
+}
diff --git a/frontend/src/components/SettingItems/WindowScaleSetting.tsx b/frontend/src/components/SettingItems/WindowScaleSetting.tsx
new file mode 100644
index 0000000..a5967e4
--- /dev/null
+++ b/frontend/src/components/SettingItems/WindowScaleSetting.tsx
@@ -0,0 +1,19 @@
+import { useTranslation } from "react-i18next";
+import { SliderConfig } from "./Presets/SliderConfig";
+
+export function WindowScaleSetting() {
+ const { t } = useTranslation();
+ return (
+ {
+ document.documentElement.style.fontSize = value * (16 / 100) + "px";
+ }}
+ />
+ );
+}
diff --git a/frontend/src/components/Settings.tsx b/frontend/src/components/Settings.tsx
new file mode 100644
index 0000000..166eec4
--- /dev/null
+++ b/frontend/src/components/Settings.tsx
@@ -0,0 +1,147 @@
+import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
+import { useTranslation } from "react-i18next";
+import { LocaleSetting } from "./SettingItems/LocaleSetting";
+import { ThemeSetting } from "./SettingItems/ThemeSetting";
+import { SettingsGroup } from "./ui/settings-group";
+import { UseSystemTitleBarSetting } from "./SettingItems/UseSystemTitleBarSetting";
+import { WindowScaleSetting } from "./SettingItems/WindowScaleSetting";
+import { MaxLogFilesSetting } from "./SettingItems/MaxLogFilesSetting";
+import { EnableLoggingSetting } from "./SettingItems/EnableLoggingSetting";
+import { LogLevelSetting } from "./SettingItems/LogLevelSetting";
+import { ImportExportSetting } from "./SettingItems/ImportExportSetting";
+import { WindowEffectSetting } from "./SettingItems/WindowEffectSetting";
+import { WindowOpacitySetting } from "./SettingItems/WindowOpacitySetting";
+import { SaveWindowStatusSetting } from "./SettingItems/SaveWindowStatusSetting";
+import { CheckForUpdatesSetting } from "./SettingItems/CheckForUpdatesSetting";
+import { UpdateSetting } from "./SettingItems/UpdateSetting";
+import { useEffect, useState } from "react";
+import { useStorage } from "@/contexts/storage-provider";
+import { MatchLnkByDestinationSetting } from "./SettingItems/MatchLnkByDestinationSetting";
+import { RenameMatchedFilesSetting } from "./SettingItems/RenameMatchedFilesSetting";
+import { MatchURLByDestinationSetting } from "./SettingItems/MatchURLByDestinationSetting";
+import { ChangeDescriptionOfMathcedLnkFilesSetting } from "./SettingItems/ChangeDescriptionOfMathcedLnkFilesSetting";
+import { ColorSchemeSetting } from "./SettingItems/ColorSchemeSetting";
+
+export default function Settings() {
+ const { t } = useTranslation();
+ const [tab, setTab] = useState("general");
+ const { getValue, setValue } = useStorage();
+
+ useEffect(() => {
+ setTab(getValue("settings") || "general");
+ }, [getValue("settings")]);
+
+ useEffect(() => {
+ setValue("path2", tab);
+ }, [tab]);
+
+ return (
+
+
+ setTab("general")}
+ className="px-12 py-2 w-full"
+ >
+ {t("settings.categories.general")}
+
+ setTab("app")}
+ className="px-12 py-2 w-full"
+ >
+ {t("settings.categories.application")}
+
+ setTab("icon_pack")}
+ className="px-12 py-2 w-full"
+ >
+ {t("settings.categories.icon_pack")}
+
+ setTab("system")}
+ className="px-12 py-2 w-full"
+ >
+ {t("settings.categories.system")}
+
+ setTab("advanced")}
+ className="px-12 py-2 w-full"
+ >
+ {t("settings.categories.advanced")}
+
+ setTab("update")}
+ className="px-12 py-2 w-full"
+ >
+ {t("settings.categories.update")}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {false && }
+
+
+
+
+
+
+
+
+
+
+
+ {false && (
+
+ Edit your system settings here.
+
+ )}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/frontend/src/components/TitleBar.tsx b/frontend/src/components/TitleBar.tsx
new file mode 100644
index 0000000..d8e28a3
--- /dev/null
+++ b/frontend/src/components/TitleBar.tsx
@@ -0,0 +1,83 @@
+import { RestartApplication } from "@/wailsjs/go/main/App";
+import {
+ WindowMinimise,
+ WindowToggleMaximise,
+ Quit,
+} from "@/wailsjs/runtime/runtime";
+import { Minus, Copy, X, RotateCcw } from "lucide-react";
+import { Button } from "@/components/ui/button";
+import icon from "../assets/appicon.png";
+import { useEffect, useState } from "react";
+import { useRestart } from "@/contexts/restart-provider";
+import { useStorage } from "@/contexts/storage-provider";
+import { useConfig } from "@/contexts/config-provider";
+
+export default function TitleBar() {
+ const { initialConfig } = useConfig();
+ const [useSystemTitleBar, setUseSystemTitleBar] = useState(false);
+ const { restartRequired } = useRestart();
+ const { getValue } = useStorage();
+
+ useEffect(() => {
+ if (initialConfig && initialConfig.useSystemTitleBar !== undefined) {
+ setUseSystemTitleBar(initialConfig.useSystemTitleBar);
+ }
+ }, [initialConfig?.useSystemTitleBar]);
+
+ return (
+ !useSystemTitleBar && (
+ WindowToggleMaximise()}
+ >
+
+
+ {document.title}
+
+
+
+
+
+
+
+
+ )
+ );
+}
diff --git a/frontend/src/components/TopBar.tsx b/frontend/src/components/TopBar.tsx
deleted file mode 100644
index a3341bc..0000000
--- a/frontend/src/components/TopBar.tsx
+++ /dev/null
@@ -1,91 +0,0 @@
-import { useEffect, useState } from "react";
-import { useProfile } from "@/contexts/profile-provider";
-import { GetProfiles, GetProfile, SyncDesktop, RemoveProfile, RunProfile } from "wailsjs/go/main/App";
-import { Button } from "./ui/button";
-import { Combobox } from "./ui/combobox"
-import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"
-import { RefreshCw, Play, Plus, Pause } from 'lucide-react';
-import { ModeToggle } from "./mode-toggle"
-import { CreateProfileForm } from "./CreateProfileForm"
-
-import { /* profile as profStruct, */ profileInfo, fileInfo } from "@/structs";
-import React from "react";
-
-const TopBar = () => {
- const { profile, setProfile } = useProfile();
- const [running, setRunning] = useState(false);
-
- const [profiles, setProfiles] = useState([]);
-
- useEffect(() => {
- GetProfilesF();
- }, [])
-
- const GetProfilesF = () => {
- GetProfiles().then((res) => setProfiles(res));
- }
-
- const onProfileChange = (profileName: string) => {
- GetProfile(profileName).then((res) => {
- setProfile(res);
- });
- }
-
- return (
-
-
-
onProfileChange(value)}
- onExpand={() => GetProfilesF()}
- onElementContextMenu={(value) => console.log("Context: " + value)}
- />
-
-
-
-
-
-
-
-
-
-
- {profile.name && (
-
-
-
-
-
- )}
-
-
-
-
- )
-}
-
-export default TopBar;
\ No newline at end of file
diff --git a/frontend/src/components/mode-toggle.tsx b/frontend/src/components/mode-toggle.tsx
deleted file mode 100644
index 992546f..0000000
--- a/frontend/src/components/mode-toggle.tsx
+++ /dev/null
@@ -1,37 +0,0 @@
-import { Moon, Sun } from "lucide-react"
-
-import { Button } from "@/components/ui/button"
-import {
- DropdownMenu,
- DropdownMenuContent,
- DropdownMenuItem,
- DropdownMenuTrigger,
-} from "@/components/ui/dropdown-menu"
-import { useTheme } from "@/contexts/theme-provider"
-
-export function ModeToggle() {
- const { setTheme } = useTheme()
-
- return (
-
-
-
-
-
- setTheme("light")}>
- Light
-
- setTheme("dark")}>
- Dark
-
- setTheme("system")}>
- System
-
-
-
- )
-}
diff --git a/frontend/src/components/ui/accordion.tsx b/frontend/src/components/ui/accordion.tsx
index 5ee3e0b..98e2d45 100644
--- a/frontend/src/components/ui/accordion.tsx
+++ b/frontend/src/components/ui/accordion.tsx
@@ -1,10 +1,10 @@
-import * as React from "react"
-import * as AccordionPrimitive from "@radix-ui/react-accordion"
-import { ChevronDown } from "lucide-react"
+import * as React from "react";
+import * as AccordionPrimitive from "@radix-ui/react-accordion";
+import { ChevronDown } from "lucide-react";
-import { cn } from "@/lib/utils"
+import { cn } from "@/lib/utils";
-const Accordion = AccordionPrimitive.Root
+const Accordion = AccordionPrimitive.Root;
const AccordionItem = React.forwardRef<
React.ElementRef,
@@ -12,31 +12,43 @@ const AccordionItem = React.forwardRef<
>(({ className, ...props }, ref) => (
-))
-AccordionItem.displayName = "AccordionItem"
+));
+AccordionItem.displayName = "AccordionItem";
+
+// Define an interface for AccordionTrigger props
+interface AccordionTriggerProps
+ extends React.ComponentPropsWithoutRef {
+ end?: React.ReactNode; // Add the 'end' prop
+}
const AccordionTrigger = React.forwardRef<
React.ElementRef,
- React.ComponentPropsWithoutRef
->(({ className, children, ...props }, ref) => (
+ AccordionTriggerProps
+>(({ className, children, end, ...props }, ref) => (
svg]:rotate-180",
+ "flex flex-1 items-center justify-between font-medium transition-all hover:underline [&[data-state=open]>div>#chevron]:rotate-180",
className
)}
{...props}
>
{children}
-
+
+ {end}
+
+
-))
-AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName
+));
+AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName;
const AccordionContent = React.forwardRef<
React.ElementRef,
@@ -49,8 +61,8 @@ const AccordionContent = React.forwardRef<
>
{children}
-))
+));
-AccordionContent.displayName = AccordionPrimitive.Content.displayName
+AccordionContent.displayName = AccordionPrimitive.Content.displayName;
-export { Accordion, AccordionItem, AccordionTrigger, AccordionContent }
+export { Accordion, AccordionItem, AccordionTrigger, AccordionContent };
diff --git a/frontend/src/components/ui/are-you-sure.tsx b/frontend/src/components/ui/are-you-sure.tsx
new file mode 100644
index 0000000..b2c03e8
--- /dev/null
+++ b/frontend/src/components/ui/are-you-sure.tsx
@@ -0,0 +1,100 @@
+import { useState, forwardRef, useImperativeHandle, ReactNode } from "react";
+import { Button } from "@/components/ui/button";
+import {
+ Dialog,
+ DialogClose,
+ DialogContent,
+ DialogDescription,
+ DialogFooter,
+ DialogHeader,
+ DialogTitle,
+ DialogTrigger,
+} from "@/components/ui/dialog";
+
+interface AreYouSureDialogProps {
+ title?: string;
+ description?: string;
+ cancelText?: string;
+ acceptText?: string;
+ onCancel?: () => void;
+ onAccept?: () => void;
+ children?: ReactNode;
+}
+
+export interface AreYouSureDialogRef {
+ openDialog: () => void;
+ closeDialog: () => void;
+}
+
+export const AreYouSureDialog = forwardRef<
+ AreYouSureDialogRef,
+ AreYouSureDialogProps
+>(
+ (
+ {
+ title = "Are you sure?",
+ description = "",
+ cancelText = "Cancel",
+ acceptText = "Accept",
+ onCancel,
+ onAccept,
+ children,
+ },
+ ref
+ ) => {
+ const [isOpen, setIsOpen] = useState(false);
+
+ const openDialog = () => setIsOpen(true);
+ const closeDialog = () => setIsOpen(false);
+
+ useImperativeHandle(ref, () => ({
+ openDialog,
+ closeDialog,
+ }));
+
+ return (
+
+ );
+ }
+);
+
+AreYouSureDialog.displayName = "AreYouSureDialog";
diff --git a/frontend/src/components/ui/button.tsx b/frontend/src/components/ui/button.tsx
index ac8e0c9..be15c3e 100644
--- a/frontend/src/components/ui/button.tsx
+++ b/frontend/src/components/ui/button.tsx
@@ -1,11 +1,16 @@
-import * as React from "react"
-import { Slot } from "@radix-ui/react-slot"
-import { cva, type VariantProps } from "class-variance-authority"
-
-import { cn } from "@/lib/utils"
+import * as React from "react";
+import { Slot } from "@radix-ui/react-slot";
+import { cva, type VariantProps } from "class-variance-authority";
+import {
+ Tooltip,
+ TooltipContent,
+ TooltipProvider,
+ TooltipTrigger,
+} from "@/components/ui/tooltip";
+import { cn } from "@/lib/utils";
const buttonVariants = cva(
- "inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
+ "inline-flex justify-center items-center disabled:opacity-50 rounded-md font-medium text-sm whitespace-nowrap transition-colors ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none",
{
variants: {
variant: {
@@ -31,26 +36,45 @@ const buttonVariants = cva(
size: "default",
},
}
-)
+);
export interface ButtonProps
extends React.ButtonHTMLAttributes,
VariantProps {
- asChild?: boolean
+ asChild?: boolean;
+ tooltip?: string; // Add tooltip prop
}
const Button = React.forwardRef(
- ({ className, variant, size, asChild = false, ...props }, ref) => {
- const Comp = asChild ? Slot : "button"
- return (
+ ({ className, variant, size, asChild = false, tooltip, ...props }, ref) => {
+ const Comp = asChild ? Slot : "button";
+ const buttonElement = (
- )
+ );
+
+ return tooltip ? (
+
+
+ {buttonElement}
+ {
+ e.stopPropagation();
+ }}
+ >
+ {tooltip}
+
+
+
+ ) : (
+ buttonElement
+ );
}
-)
-Button.displayName = "Button"
+);
+
+Button.displayName = "Button";
-export { Button, buttonVariants }
+export { Button, buttonVariants };
diff --git a/frontend/src/components/ui/checkbox.tsx b/frontend/src/components/ui/checkbox.tsx
new file mode 100644
index 0000000..ddbdd01
--- /dev/null
+++ b/frontend/src/components/ui/checkbox.tsx
@@ -0,0 +1,28 @@
+import * as React from "react"
+import * as CheckboxPrimitive from "@radix-ui/react-checkbox"
+import { Check } from "lucide-react"
+
+import { cn } from "@/lib/utils"
+
+const Checkbox = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+
+
+
+
+))
+Checkbox.displayName = CheckboxPrimitive.Root.displayName
+
+export { Checkbox }
diff --git a/frontend/src/components/ui/combobox.tsx b/frontend/src/components/ui/combobox.tsx
index 1285f9b..17989be 100644
--- a/frontend/src/components/ui/combobox.tsx
+++ b/frontend/src/components/ui/combobox.tsx
@@ -1,86 +1,115 @@
-"use client"
+import * as React from "react";
+import { ChevronsUpDown, Check } from "lucide-react";
-import * as React from "react"
-import { Check, ChevronsUpDown } from "lucide-react"
-
-import { cn } from "@/lib/utils"
-import { Button } from "@/components/ui/button"
+import { cn } from "@/lib/utils";
+import { Button } from "@/components/ui/button";
import {
- Command,
- CommandEmpty,
- CommandGroup,
- CommandInput,
- CommandItem,
-} from "@/components/ui/command"
+ Command,
+ CommandEmpty,
+ CommandGroup,
+ CommandInput,
+ CommandItem,
+ CommandList,
+} from "@/components/ui/command";
import {
- Popover,
- PopoverContent,
- PopoverTrigger,
-} from "@/components/ui/popover"
+ Popover,
+ PopoverContent,
+ PopoverTrigger,
+} from "@/components/ui/popover";
interface ComboboxProps {
- elements: { value: any; label: string }[];
- placeholder?: string;
- noElementsText?: string;
- initialValue?: any;
- searchBar?: boolean;
- onChange: (value: any) => void;
- onExpand?: () => void;
- onElementContextMenu?: (value: any) => void;
+ elements: { value: any; label: string }[];
+ placeholder?: string;
+ searchPlaceholder?: string;
+ nothingFoundMessage?: string;
+ mandatory?: boolean;
+ initialValue?: any;
+ onChange: (value: any) => void;
+ onCollapse?: (value: any) => void;
+ onMouseEnter?: (value: any) => void;
+ onMouseLeave?: (value: any) => void;
}
export function Combobox(props: ComboboxProps) {
- const [open, setOpen] = React.useState(false)
- const [value, setValue] = React.useState(props.initialValue ? props.initialValue : "")
+ const [value, setValue] = React.useState(props.initialValue);
+ const [open, setOpen] = React.useState(false);
+
+ React.useEffect(() => {
+ props.onChange(value);
+ }, [value]);
+
+ React.useEffect(() => {
+ if (!open) {
+ props.onCollapse?.(value);
+ }
+ }, [open]);
- return (
-
-
-
-
-
-
- {(props.searchBar === true || props.searchBar === undefined) && (
-
+ {element.label}
+ {props.noElementsText ? props.noElementsText : "No element found."}
-
- {props.elements.map((element) => (
- {
- setValue(currentValue === value ? "" : currentValue)
- props.onChange(currentValue === value ? "" : currentValue)
- setOpen(false)
- }}
- onContextMenu={(value) => {
- props.onElementContextMenu?.(value.currentTarget.getAttribute("data-value"))
- }}
- >
-
- {element.label}
-
- ))}
-
-
-
-
- )
+ />
+
+ ))}
+
+
+
+
+
+ );
}
diff --git a/frontend/src/components/ui/command.tsx b/frontend/src/components/ui/command.tsx
index d623ee0..d34152d 100644
--- a/frontend/src/components/ui/command.tsx
+++ b/frontend/src/components/ui/command.tsx
@@ -115,7 +115,7 @@ const CommandItem = React.forwardRef<
,
- React.ComponentPropsWithoutRef & {
- inset?: boolean
- }
->(({ className, inset, children, ...props }, ref) => (
-
- {children}
-
-
-))
-DropdownMenuSubTrigger.displayName =
- DropdownMenuPrimitive.SubTrigger.displayName
-
-const DropdownMenuSubContent = React.forwardRef<
- React.ElementRef,
- React.ComponentPropsWithoutRef
->(({ className, ...props }, ref) => (
-
-))
-DropdownMenuSubContent.displayName =
- DropdownMenuPrimitive.SubContent.displayName
-
-const DropdownMenuContent = React.forwardRef<
- React.ElementRef,
- React.ComponentPropsWithoutRef
->(({ className, sideOffset = 4, ...props }, ref) => (
-
-
-
-))
-DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName
-
-const DropdownMenuItem = React.forwardRef<
- React.ElementRef,
- React.ComponentPropsWithoutRef & {
- inset?: boolean
- }
->(({ className, inset, ...props }, ref) => (
-
-))
-DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName
-
-const DropdownMenuCheckboxItem = React.forwardRef<
- React.ElementRef,
- React.ComponentPropsWithoutRef
->(({ className, children, checked, ...props }, ref) => (
-
-
-
-
-
-
- {children}
-
-))
-DropdownMenuCheckboxItem.displayName =
- DropdownMenuPrimitive.CheckboxItem.displayName
-
-const DropdownMenuRadioItem = React.forwardRef<
- React.ElementRef,
- React.ComponentPropsWithoutRef
->(({ className, children, ...props }, ref) => (
-
-
-
-
-
-
- {children}
-
-))
-DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName
-
-const DropdownMenuLabel = React.forwardRef<
- React.ElementRef,
- React.ComponentPropsWithoutRef & {
- inset?: boolean
- }
->(({ className, inset, ...props }, ref) => (
-
-))
-DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName
-
-const DropdownMenuSeparator = React.forwardRef<
- React.ElementRef,
- React.ComponentPropsWithoutRef
->(({ className, ...props }, ref) => (
-
-))
-DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName
-
-const DropdownMenuShortcut = ({
- className,
- ...props
-}: React.HTMLAttributes) => {
- return (
-
- )
-}
-DropdownMenuShortcut.displayName = "DropdownMenuShortcut"
-
-export {
- DropdownMenu,
- DropdownMenuTrigger,
- DropdownMenuContent,
- DropdownMenuItem,
- DropdownMenuCheckboxItem,
- DropdownMenuRadioItem,
- DropdownMenuLabel,
- DropdownMenuSeparator,
- DropdownMenuShortcut,
- DropdownMenuGroup,
- DropdownMenuPortal,
- DropdownMenuSub,
- DropdownMenuSubContent,
- DropdownMenuSubTrigger,
- DropdownMenuRadioGroup,
-}
diff --git a/frontend/src/components/ui/help-card.tsx b/frontend/src/components/ui/help-card.tsx
new file mode 100644
index 0000000..55b6cf0
--- /dev/null
+++ b/frontend/src/components/ui/help-card.tsx
@@ -0,0 +1,32 @@
+import React from "react";
+import { HoverCard, HoverCardContent, HoverCardTrigger } from "./hover-card";
+import { Button } from "./button"; // Assuming Button is in the same directory
+
+interface HelpCardProps {
+ content: React.ReactNode;
+ buttonText?: string;
+ buttonSize?: "default" | "sm" | "lg" | "icon";
+ buttonVariant?: "default" | "destructive" | "outline" | "secondary" | "ghost" | "link";
+ className?: string; // For additional custom styling
+}
+
+export const HelpCard: React.FC = ({
+ content,
+ buttonText = "?",
+ buttonSize = "icon",
+ buttonVariant = "link",
+ className = "",
+}) => {
+ return (
+
+
+
+ {buttonText}
+
+
+
+ {content}
+
+
+ );
+};
\ No newline at end of file
diff --git a/frontend/src/components/ui/hover-card.tsx b/frontend/src/components/ui/hover-card.tsx
new file mode 100644
index 0000000..dc17c21
--- /dev/null
+++ b/frontend/src/components/ui/hover-card.tsx
@@ -0,0 +1,27 @@
+import * as React from "react";
+import * as HoverCardPrimitive from "@radix-ui/react-hover-card";
+
+import { cn } from "@/lib/utils";
+
+const HoverCard = HoverCardPrimitive.Root;
+
+const HoverCardTrigger = HoverCardPrimitive.Trigger;
+
+const HoverCardContent = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, align = "center", sideOffset = 4, ...props }, ref) => (
+
+));
+HoverCardContent.displayName = HoverCardPrimitive.Content.displayName;
+
+export { HoverCard, HoverCardTrigger, HoverCardContent };
diff --git a/frontend/src/components/ui/input.tsx b/frontend/src/components/ui/input.tsx
index 013b6c1..677d05f 100644
--- a/frontend/src/components/ui/input.tsx
+++ b/frontend/src/components/ui/input.tsx
@@ -11,7 +11,7 @@ const Input = React.forwardRef(
{}
+
+
+export const Slider: React.FC = ({
+ className,
+ ...props
+}) => {
+ return (
+
+
+
+
+
+
+ );
+};
\ No newline at end of file
diff --git a/frontend/src/components/ui/progress.tsx b/frontend/src/components/ui/progress.tsx
new file mode 100644
index 0000000..546fb86
--- /dev/null
+++ b/frontend/src/components/ui/progress.tsx
@@ -0,0 +1,26 @@
+import * as React from "react"
+import * as ProgressPrimitive from "@radix-ui/react-progress"
+
+import { cn } from "@/lib/utils"
+
+const Progress = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, value, ...props }, ref) => (
+
+
+
+))
+Progress.displayName = ProgressPrimitive.Root.displayName
+
+export { Progress }
diff --git a/frontend/src/components/ui/settings-group.tsx b/frontend/src/components/ui/settings-group.tsx
new file mode 100644
index 0000000..28f2599
--- /dev/null
+++ b/frontend/src/components/ui/settings-group.tsx
@@ -0,0 +1,135 @@
+import React, { ReactNode, useEffect } from "react";
+import { Skeleton } from "@/components/ui/skeleton";
+import { useRestart } from "@/contexts/restart-provider";
+import { main } from "@/wailsjs/go/models";
+import { useConfig } from "@/contexts/config-provider";
+
+interface SettingsComponentProps extends React.HTMLAttributes {
+ children: ReactNode;
+}
+
+export const SettingsGroup: React.FC = ({
+ children,
+ className,
+ ...rest
+}) => (
+
+ {children}
+
+);
+
+interface SettingsItemProps extends React.HTMLAttributes {
+ loading?: boolean;
+ vertical?: boolean;
+ disabled?: boolean;
+ children: ReactNode;
+ configKey?: keyof main.Config | (keyof main.Config)[];
+ requiresRestart?: boolean;
+}
+
+export const SettingsItem: React.FC = ({
+ children,
+ className,
+ disabled,
+ loading,
+ vertical,
+ configKey,
+ requiresRestart,
+ ...rest
+}) => {
+ const { addRestartRequired, removeRestartRequired } = useRestart();
+ const { config, initialConfig } = useConfig();
+
+ useEffect(() => {
+ if (requiresRestart && configKey && config && initialConfig && !loading) {
+ if (Array.isArray(configKey)) {
+ // Handle array of keys
+ configKey.forEach((k) => {
+ const configValue = config[k as keyof main.Config];
+ const initialValue = initialConfig[k as keyof main.Config];
+ if (configValue === initialValue) {
+ removeRestartRequired(k as keyof main.Config);
+ } else {
+ addRestartRequired(k as keyof main.Config);
+ }
+ });
+ } else {
+ // Handle single key
+ const k = configKey as keyof main.Config;
+ const configValue = config[k];
+ const initialValue = initialConfig[k];
+ if (configValue === initialValue) {
+ removeRestartRequired(k);
+ } else {
+ addRestartRequired(k);
+ }
+ }
+ }
+ }, [config]);
+
+ if (loading) {
+ return ;
+ } else {
+ return (
+
+ {children}
+
+ );
+ }
+};
+
+export const SettingLabel: React.FC = ({
+ children,
+ className,
+ ...rest
+}) => (
+
+ {children}
+
+);
+
+export const SettingDescription: React.FC = ({
+ children,
+ className,
+ ...rest
+}) => (
+
+ {children}
+
+);
+
+export const SettingContent: React.FC = ({
+ children,
+ className,
+ ...rest
+}) => (
+
+ {children}
+
+);
+
+interface SettingsGroupSkeletonProps
+ extends React.HTMLAttributes {}
+
+const SettingsItemSkeleton: React.FC = ({
+ className,
+ ...rest
+}) => (
+
+);
diff --git a/frontend/src/components/ui/skeleton.tsx b/frontend/src/components/ui/skeleton.tsx
new file mode 100644
index 0000000..01b8b6d
--- /dev/null
+++ b/frontend/src/components/ui/skeleton.tsx
@@ -0,0 +1,15 @@
+import { cn } from "@/lib/utils"
+
+function Skeleton({
+ className,
+ ...props
+}: React.HTMLAttributes) {
+ return (
+
+ )
+}
+
+export { Skeleton }
diff --git a/frontend/src/components/ui/slider.tsx b/frontend/src/components/ui/slider.tsx
new file mode 100644
index 0000000..e161dae
--- /dev/null
+++ b/frontend/src/components/ui/slider.tsx
@@ -0,0 +1,26 @@
+import * as React from "react"
+import * as SliderPrimitive from "@radix-ui/react-slider"
+
+import { cn } from "@/lib/utils"
+
+const Slider = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+
+
+
+
+
+))
+Slider.displayName = SliderPrimitive.Root.displayName
+
+export { Slider }
diff --git a/frontend/src/components/ui/sonner.tsx b/frontend/src/components/ui/sonner.tsx
new file mode 100644
index 0000000..1128edf
--- /dev/null
+++ b/frontend/src/components/ui/sonner.tsx
@@ -0,0 +1,29 @@
+import { useTheme } from "next-themes"
+import { Toaster as Sonner } from "sonner"
+
+type ToasterProps = React.ComponentProps
+
+const Toaster = ({ ...props }: ToasterProps) => {
+ const { theme = "system" } = useTheme()
+
+ return (
+
+ )
+}
+
+export { Toaster }
diff --git a/frontend/src/components/ui/switch.tsx b/frontend/src/components/ui/switch.tsx
new file mode 100644
index 0000000..aa58baa
--- /dev/null
+++ b/frontend/src/components/ui/switch.tsx
@@ -0,0 +1,27 @@
+import * as React from "react"
+import * as SwitchPrimitives from "@radix-ui/react-switch"
+
+import { cn } from "@/lib/utils"
+
+const Switch = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+
+
+))
+Switch.displayName = SwitchPrimitives.Root.displayName
+
+export { Switch }
diff --git a/frontend/src/components/ui/tabs.tsx b/frontend/src/components/ui/tabs.tsx
new file mode 100644
index 0000000..090d6d6
--- /dev/null
+++ b/frontend/src/components/ui/tabs.tsx
@@ -0,0 +1,53 @@
+import * as React from "react"
+import * as TabsPrimitive from "@radix-ui/react-tabs"
+
+import { cn } from "@/lib/utils"
+
+const Tabs = TabsPrimitive.Root
+
+const TabsList = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+TabsList.displayName = TabsPrimitive.List.displayName
+
+const TabsTrigger = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+TabsTrigger.displayName = TabsPrimitive.Trigger.displayName
+
+const TabsContent = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+TabsContent.displayName = TabsPrimitive.Content.displayName
+
+export { Tabs, TabsList, TabsTrigger, TabsContent }
diff --git a/frontend/src/components/ui/textarea.tsx b/frontend/src/components/ui/textarea.tsx
new file mode 100644
index 0000000..9f9a6dc
--- /dev/null
+++ b/frontend/src/components/ui/textarea.tsx
@@ -0,0 +1,24 @@
+import * as React from "react"
+
+import { cn } from "@/lib/utils"
+
+export interface TextareaProps
+ extends React.TextareaHTMLAttributes {}
+
+const Textarea = React.forwardRef(
+ ({ className, ...props }, ref) => {
+ return (
+
+ )
+ }
+)
+Textarea.displayName = "Textarea"
+
+export { Textarea }
diff --git a/frontend/src/components/ui/toast.tsx b/frontend/src/components/ui/toast.tsx
new file mode 100644
index 0000000..115acc2
--- /dev/null
+++ b/frontend/src/components/ui/toast.tsx
@@ -0,0 +1,127 @@
+import * as React from "react";
+import * as ToastPrimitives from "@radix-ui/react-toast";
+import { cva, type VariantProps } from "class-variance-authority";
+import { X } from "lucide-react";
+
+import { cn } from "@/lib/utils";
+
+const ToastProvider = ToastPrimitives.Provider;
+
+const ToastViewport = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+ToastViewport.displayName = ToastPrimitives.Viewport.displayName;
+
+const toastVariants = cva(
+ "group pointer-events-auto relative flex w-full items-center justify-between space-x-4 overflow-hidden rounded-md border p-6 pr-8 shadow-lg transition-all data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=move]:transition-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full",
+ {
+ variants: {
+ variant: {
+ default: "border bg-background text-foreground",
+ destructive:
+ "destructive group border-destructive bg-destructive text-destructive-foreground",
+ },
+ },
+ defaultVariants: {
+ variant: "default",
+ },
+ }
+);
+
+const Toast = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef &
+ VariantProps
+>(({ className, variant, ...props }, ref) => {
+ return (
+
+ );
+});
+Toast.displayName = ToastPrimitives.Root.displayName;
+
+const ToastAction = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+ToastAction.displayName = ToastPrimitives.Action.displayName;
+
+const ToastClose = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+
+
+));
+ToastClose.displayName = ToastPrimitives.Close.displayName;
+
+const ToastTitle = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+ToastTitle.displayName = ToastPrimitives.Title.displayName;
+
+const ToastDescription = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+ToastDescription.displayName = ToastPrimitives.Description.displayName;
+
+type ToastProps = React.ComponentPropsWithoutRef;
+
+type ToastActionElement = React.ReactElement;
+
+export {
+ type ToastProps,
+ type ToastActionElement,
+ ToastProvider,
+ ToastViewport,
+ Toast,
+ ToastTitle,
+ ToastDescription,
+ ToastClose,
+ ToastAction,
+};
diff --git a/frontend/src/components/ui/toaster.tsx b/frontend/src/components/ui/toaster.tsx
new file mode 100644
index 0000000..e136bea
--- /dev/null
+++ b/frontend/src/components/ui/toaster.tsx
@@ -0,0 +1,33 @@
+import {
+ Toast,
+ ToastClose,
+ ToastDescription,
+ ToastProvider,
+ ToastTitle,
+ ToastViewport,
+} from "@/components/ui/toast";
+import { useToast } from "@/components/ui/use-toast";
+
+export function Toaster() {
+ const { toasts } = useToast();
+
+ return (
+
+ {toasts.map(function ({ id, title, description, action, ...props }) {
+ return (
+
+
+ {title && {title}}
+ {description && (
+ {description}
+ )}
+
+ {action}
+
+
+ );
+ })}
+
+
+ );
+}
diff --git a/frontend/src/components/ui/toggle-group.tsx b/frontend/src/components/ui/toggle-group.tsx
new file mode 100644
index 0000000..19505f9
--- /dev/null
+++ b/frontend/src/components/ui/toggle-group.tsx
@@ -0,0 +1,59 @@
+import * as React from "react"
+import * as ToggleGroupPrimitive from "@radix-ui/react-toggle-group"
+import { VariantProps } from "class-variance-authority"
+
+import { cn } from "@/lib/utils"
+import { toggleVariants } from "@/components/ui/toggle"
+
+const ToggleGroupContext = React.createContext<
+ VariantProps
+>({
+ size: "default",
+ variant: "default",
+})
+
+const ToggleGroup = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef &
+ VariantProps
+>(({ className, variant, size, children, ...props }, ref) => (
+
+
+ {children}
+
+
+))
+
+ToggleGroup.displayName = ToggleGroupPrimitive.Root.displayName
+
+const ToggleGroupItem = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef &
+ VariantProps
+>(({ className, children, variant, size, ...props }, ref) => {
+ const context = React.useContext(ToggleGroupContext)
+
+ return (
+
+ {children}
+
+ )
+})
+
+ToggleGroupItem.displayName = ToggleGroupPrimitive.Item.displayName
+
+export { ToggleGroup, ToggleGroupItem }
diff --git a/frontend/src/components/ui/toggle.tsx b/frontend/src/components/ui/toggle.tsx
new file mode 100644
index 0000000..9ecac28
--- /dev/null
+++ b/frontend/src/components/ui/toggle.tsx
@@ -0,0 +1,43 @@
+import * as React from "react"
+import * as TogglePrimitive from "@radix-ui/react-toggle"
+import { cva, type VariantProps } from "class-variance-authority"
+
+import { cn } from "@/lib/utils"
+
+const toggleVariants = cva(
+ "inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-background transition-colors hover:bg-muted hover:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=on]:bg-accent data-[state=on]:text-accent-foreground",
+ {
+ variants: {
+ variant: {
+ default: "bg-transparent",
+ outline:
+ "border border-input bg-transparent hover:bg-accent hover:text-accent-foreground",
+ },
+ size: {
+ default: "h-10 px-3",
+ sm: "h-9 px-2.5",
+ lg: "h-11 px-5",
+ },
+ },
+ defaultVariants: {
+ variant: "default",
+ size: "default",
+ },
+ }
+)
+
+const Toggle = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef &
+ VariantProps
+>(({ className, variant, size, ...props }, ref) => (
+
+))
+
+Toggle.displayName = TogglePrimitive.Root.displayName
+
+export { Toggle, toggleVariants }
diff --git a/frontend/src/components/ui/tooltip.tsx b/frontend/src/components/ui/tooltip.tsx
new file mode 100644
index 0000000..9653ebf
--- /dev/null
+++ b/frontend/src/components/ui/tooltip.tsx
@@ -0,0 +1,29 @@
+import * as React from "react";
+import * as TooltipPrimitive from "@radix-ui/react-tooltip";
+
+import { cn } from "@/lib/utils";
+
+const TooltipProvider = TooltipPrimitive.Provider;
+const Tooltip = TooltipPrimitive.Root;
+const TooltipTrigger = TooltipPrimitive.Trigger;
+
+const TooltipContent = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, sideOffset = 4, ...props }, ref) => (
+
+ {/* Use Portal to render TooltipContent outside the current stacking context */}
+
+
+));
+TooltipContent.displayName = TooltipPrimitive.Content.displayName;
+
+export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider };
diff --git a/frontend/src/components/ui/use-toast.ts b/frontend/src/components/ui/use-toast.ts
new file mode 100644
index 0000000..ea05e02
--- /dev/null
+++ b/frontend/src/components/ui/use-toast.ts
@@ -0,0 +1,189 @@
+// Inspired by react-hot-toast library
+import * as React from "react";
+
+import type { ToastActionElement, ToastProps } from "@/components/ui/toast";
+
+const TOAST_LIMIT = 3;
+const TOAST_REMOVE_DELAY = 1000000;
+
+type ToasterToast = ToastProps & {
+ id: string;
+ title?: React.ReactNode;
+ description?: React.ReactNode;
+ action?: ToastActionElement;
+};
+
+const actionTypes = {
+ ADD_TOAST: "ADD_TOAST",
+ UPDATE_TOAST: "UPDATE_TOAST",
+ DISMISS_TOAST: "DISMISS_TOAST",
+ REMOVE_TOAST: "REMOVE_TOAST",
+} as const;
+
+let count = 0;
+
+function genId() {
+ count = (count + 1) % Number.MAX_SAFE_INTEGER;
+ return count.toString();
+}
+
+type ActionType = typeof actionTypes;
+
+type Action =
+ | {
+ type: ActionType["ADD_TOAST"];
+ toast: ToasterToast;
+ }
+ | {
+ type: ActionType["UPDATE_TOAST"];
+ toast: Partial;
+ }
+ | {
+ type: ActionType["DISMISS_TOAST"];
+ toastId?: ToasterToast["id"];
+ }
+ | {
+ type: ActionType["REMOVE_TOAST"];
+ toastId?: ToasterToast["id"];
+ };
+
+interface State {
+ toasts: ToasterToast[];
+}
+
+const toastTimeouts = new Map>();
+
+const addToRemoveQueue = (toastId: string) => {
+ if (toastTimeouts.has(toastId)) {
+ return;
+ }
+
+ const timeout = setTimeout(() => {
+ toastTimeouts.delete(toastId);
+ dispatch({
+ type: "REMOVE_TOAST",
+ toastId: toastId,
+ });
+ }, TOAST_REMOVE_DELAY);
+
+ toastTimeouts.set(toastId, timeout);
+};
+
+export const reducer = (state: State, action: Action): State => {
+ switch (action.type) {
+ case "ADD_TOAST":
+ return {
+ ...state,
+ toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT),
+ };
+
+ case "UPDATE_TOAST":
+ return {
+ ...state,
+ toasts: state.toasts.map((t) =>
+ t.id === action.toast.id ? { ...t, ...action.toast } : t
+ ),
+ };
+
+ case "DISMISS_TOAST": {
+ const { toastId } = action;
+
+ // ! Side effects ! - This could be extracted into a dismissToast() action,
+ // but I'll keep it here for simplicity
+ if (toastId) {
+ addToRemoveQueue(toastId);
+ } else {
+ state.toasts.forEach((toast) => {
+ addToRemoveQueue(toast.id);
+ });
+ }
+
+ return {
+ ...state,
+ toasts: state.toasts.map((t) =>
+ t.id === toastId || toastId === undefined
+ ? {
+ ...t,
+ open: false,
+ }
+ : t
+ ),
+ };
+ }
+ case "REMOVE_TOAST":
+ if (action.toastId === undefined) {
+ return {
+ ...state,
+ toasts: [],
+ };
+ }
+ return {
+ ...state,
+ toasts: state.toasts.filter((t) => t.id !== action.toastId),
+ };
+ }
+};
+
+const listeners: Array<(state: State) => void> = [];
+
+let memoryState: State = { toasts: [] };
+
+function dispatch(action: Action) {
+ memoryState = reducer(memoryState, action);
+ listeners.forEach((listener) => {
+ listener(memoryState);
+ });
+}
+
+type Toast = Omit;
+
+function toast({ ...props }: Toast) {
+ const id = genId();
+
+ const update = (props: ToasterToast) =>
+ dispatch({
+ type: "UPDATE_TOAST",
+ toast: { ...props, id },
+ });
+ const dismiss = () => dispatch({ type: "DISMISS_TOAST", toastId: id });
+
+ dispatch({
+ type: "ADD_TOAST",
+ toast: {
+ ...props,
+ id,
+ open: true,
+ onOpenChange: (open) => {
+ if (!open) dismiss();
+ },
+ },
+ });
+
+ return {
+ id: id,
+ dismiss,
+ update,
+ };
+}
+
+function useToast() {
+ const [state, setState] = React.useState(memoryState);
+
+ React.useEffect(() => {
+ listeners.push(setState);
+ return () => {
+ const index = listeners.indexOf(setState);
+ if (index > -1) {
+ listeners.splice(index, 1);
+ }
+ };
+ }, [state]);
+
+ return {
+ ...state,
+ toast,
+ dismiss: (toastId?: string) => dispatch({ type: "DISMISS_TOAST", toastId }),
+ };
+}
+
+export { useToast, toast };
diff --git a/frontend/src/contexts/color-scheme-provider.tsx b/frontend/src/contexts/color-scheme-provider.tsx
new file mode 100644
index 0000000..2dfaf6b
--- /dev/null
+++ b/frontend/src/contexts/color-scheme-provider.tsx
@@ -0,0 +1,99 @@
+import React, { createContext, useContext, useEffect, useState } from "react";
+import { useConfig } from "./config-provider";
+import colorSchemes from "@/colorSchemes.json";
+
+export type ColorScheme =
+ | "default"
+ | "midnightAsh"
+ | "dawnMist"
+ | "forestDawn"
+ | "goldenEmber"; // Add other color schemes here
+
+type ColorSchemeProviderProps = {
+ children: React.ReactNode;
+ defaultColorScheme?: ColorScheme;
+};
+
+type ColorSchemeProviderState = {
+ colorScheme: ColorScheme;
+ setColorScheme: (colorScheme: ColorScheme) => Promise;
+ updateColorScheme: () => void; // The new function to update the color scheme without parameters
+};
+
+const initialState: ColorSchemeProviderState = {
+ colorScheme: "default", // Default color scheme
+ setColorScheme: async () => {},
+ updateColorScheme: () => {}, // Initial empty function
+};
+
+const ColorSchemeContext =
+ createContext(initialState);
+
+export function ColorSchemeProvider({
+ children,
+ defaultColorScheme = "default",
+ ...props
+}: ColorSchemeProviderProps) {
+ const { config, setConfigField } = useConfig();
+ const [colorScheme, setColorSchemeState] =
+ useState(defaultColorScheme);
+ const [updateTrigger, setUpdateTrigger] = useState(0); // A trigger to force update
+
+ // Sync color scheme with config
+ useEffect(() => {
+ if (config) {
+ setColorSchemeState(config.colorScheme as ColorScheme);
+ }
+ }, [config?.colorScheme]);
+
+ // Update the DOM with the selected color scheme
+ useEffect(() => {
+ const root = document.documentElement;
+ // Remove all color scheme classes from the root element
+ for (let i = 0; i < colorSchemes.colorSchemes.length; i++) {
+ root.classList.remove(colorSchemes.colorSchemes[i].code);
+ }
+
+ // Add the new color scheme class
+ root.classList.add(colorScheme);
+ }, [colorScheme, updateTrigger]);
+
+ // Function to update color scheme in the config
+ const setColorScheme = async (newColorScheme: ColorScheme) => {
+ try {
+ await setConfigField("colorScheme", newColorScheme);
+ setColorSchemeState(newColorScheme);
+ console.log(`Set color scheme to ${newColorScheme}`);
+ } catch (error) {
+ console.error("Failed to set color scheme");
+ }
+ };
+
+ // Function to manually trigger color scheme update
+ const updateColorScheme = () => {
+ // Increment the updateTrigger to force a re-run of the useEffect
+ setUpdateTrigger((prev) => prev + 1);
+ };
+
+ const value = {
+ colorScheme,
+ setColorScheme,
+ updateColorScheme, // Provide updateColorScheme in the context
+ };
+
+ return (
+
+ {children}
+
+ );
+}
+
+export const useColorScheme = () => {
+ const context = useContext(ColorSchemeContext);
+
+ if (context === undefined) {
+ throw new Error("useColorScheme must be used within a ColorSchemeProvider");
+ }
+
+ return context;
+};
diff --git a/frontend/src/contexts/config-provider.tsx b/frontend/src/contexts/config-provider.tsx
new file mode 100644
index 0000000..0e23fdd
--- /dev/null
+++ b/frontend/src/contexts/config-provider.tsx
@@ -0,0 +1,81 @@
+import React, {
+ createContext,
+ useContext,
+ useEffect,
+ useState,
+ ReactNode,
+} from "react";
+import { main } from "@/wailsjs/go/models";
+import {
+ GetConfig,
+ SetConfigField as SetConfigField_backend,
+} from "@/wailsjs/go/main/App";
+import { LogError } from "@/wailsjs/runtime/runtime";
+
+interface ConfigContextType {
+ config: main.Config | null;
+ initialConfig: main.Config | null;
+ setConfig: React.Dispatch>;
+ setConfigField: (key: keyof main.Config, value: any) => void;
+}
+
+// Create the context
+const ConfigContext = createContext(undefined);
+
+// Create a provider component
+export const ConfigProvider: React.FC<{ children: ReactNode }> = ({
+ children,
+}) => {
+ const [config, setConfig] = useState(null);
+ const [initialConfig, setInitialConfig] = useState(null);
+
+ useEffect(() => {
+ // Fetch the initial configuration
+ GetConfig()
+ .then((initialConfig: main.Config) => {
+ setConfig(initialConfig);
+ setInitialConfig(initialConfig);
+ })
+ .catch((error) => {
+ LogError("Failed to fetch initial config: " + error);
+ });
+ }, []);
+
+ const setConfigField = (key: keyof main.Config, value: any) => {
+ if (config) {
+ var strKey = key as string;
+ strKey = strKey.charAt(0).toUpperCase() + strKey.slice(1);
+ // Call the backend function to set the config field
+ SetConfigField_backend(strKey, value)
+ .then(() => {
+ // Update the config state with the new value
+ setConfig((prevConfig) => {
+ if (prevConfig) {
+ return { ...prevConfig, [key]: value };
+ }
+ return prevConfig;
+ });
+ })
+ .catch((error) => {
+ LogError("Failed to set config field " + key + ": " + error);
+ });
+ }
+ };
+
+ return (
+
+ {children}
+
+ );
+};
+
+// Custom hook to use the config context
+export const useConfig = (): ConfigContextType => {
+ const context = useContext(ConfigContext);
+ if (!context) {
+ throw new Error("useConfig must be used within a ConfigProvider");
+ }
+ return context;
+};
diff --git a/frontend/src/contexts/profile-provider.tsx b/frontend/src/contexts/profile-provider.tsx
deleted file mode 100644
index 50cc39a..0000000
--- a/frontend/src/contexts/profile-provider.tsx
+++ /dev/null
@@ -1,48 +0,0 @@
-import { createContext, useContext, useState } from "react"
-
-import { profile as profStruct } from "@/structs"
-
-type ProfileProviderProps = {
- children: React.ReactNode
-}
-
-type ProfileProviderState = {
- profile: profStruct
- setProfile: (profileName: profStruct) => void
-}
-
-const initialState: ProfileProviderState = {
- profile: { name: "", id: "", value: [] },
- setProfile: () => null,
-}
-
-const ProfileProviderContext = createContext(initialState)
-
-export function ProfileProvider({
- children,
- ...props
-}: ProfileProviderProps) {
- const [profile, setProfile] = useState({ name: "", id: "", value: [] });
-
- const value = {
- profile: profile,
- setProfile: (prof: profStruct) => {
- setProfile(prof)
- },
- }
-
- return (
-
- {children}
-
- )
-}
-
-export const useProfile = () => {
- const context = useContext(ProfileProviderContext)
-
- if (context === undefined)
- throw new Error("useProfile must be used within a ProfileProvider")
-
- return context
-}
diff --git a/frontend/src/contexts/progress-provider.tsx b/frontend/src/contexts/progress-provider.tsx
new file mode 100644
index 0000000..855ee7c
--- /dev/null
+++ b/frontend/src/contexts/progress-provider.tsx
@@ -0,0 +1,42 @@
+import React, { createContext, useContext, useState } from "react";
+
+type ProgressProviderProps = {
+ children: React.ReactNode;
+};
+
+type ProgressProviderState = {
+ progress: number;
+ setProgress: (progress: number) => void;
+};
+
+const initialState: ProgressProviderState = {
+ progress: 0,
+ setProgress: () => {}, // Initial no-op function
+};
+
+const ProgressProviderContext = createContext(initialState);
+
+export function ProgressProvider({ children }: ProgressProviderProps) {
+ const [progress, setProgress] = useState(initialState.progress);
+
+ const value = {
+ progress,
+ setProgress,
+ };
+
+ return (
+
+ {children}
+
+ );
+}
+
+export const useProgress = () => {
+ const context = useContext(ProgressProviderContext);
+
+ if (context === undefined) {
+ throw new Error("useProgress must be used within a ProgressProvider");
+ }
+
+ return context;
+};
diff --git a/frontend/src/contexts/providers.tsx b/frontend/src/contexts/providers.tsx
new file mode 100644
index 0000000..6535d8f
--- /dev/null
+++ b/frontend/src/contexts/providers.tsx
@@ -0,0 +1,29 @@
+import { ReactNode } from "react";
+import { RestartProvider } from "./restart-provider.tsx";
+import { ThemeProvider } from "./theme-provider.tsx";
+import { StorageProvider } from "./storage-provider.tsx";
+import { ConfigProvider } from "./config-provider.tsx";
+import { ProgressProvider } from "./progress-provider.tsx";
+import { ColorSchemeProvider } from "./color-scheme-provider.tsx";
+
+interface ProvidersProps {
+ children: ReactNode;
+}
+
+const Providers = ({ children }: ProvidersProps) => {
+ return (
+
+
+
+
+
+ {children}
+ {" "}
+
+
+
+
+ );
+};
+
+export default Providers;
diff --git a/frontend/src/contexts/restart-provider.tsx b/frontend/src/contexts/restart-provider.tsx
new file mode 100644
index 0000000..e5f5967
--- /dev/null
+++ b/frontend/src/contexts/restart-provider.tsx
@@ -0,0 +1,60 @@
+import React, { createContext, useContext, useState, ReactNode } from "react";
+
+interface RestartContextType {
+ restartRequired: boolean;
+ addRestartRequired: (item: string) => void;
+ removeRestartRequired: (item: string) => void;
+}
+
+// Create context with initial empty values
+const RestartContext = createContext({
+ restartRequired: false,
+ addRestartRequired: () => {},
+ removeRestartRequired: () => {},
+});
+
+// Custom hook to use RestartContext
+export function useRestart(): RestartContextType {
+ return useContext(RestartContext);
+}
+
+// RestartProvider component
+export const RestartProvider: React.FC<{ children: ReactNode }> = ({
+ children,
+}) => {
+ const [_, setRestartRequiredArray] = useState([]);
+ const [restartRequired, setRestartRequired] = useState(false);
+
+ // Function to add a restart requirement
+ const addRestartRequired = (item: string) => {
+ setRestartRequiredArray((prevArray) => {
+ if (!prevArray.includes(item)) {
+ const newArray = [...prevArray, item];
+ setRestartRequired(newArray.length > 0);
+ return newArray;
+ }
+ return prevArray;
+ });
+ };
+
+ // Function to remove a restart requirement
+ const removeRestartRequired = (item: string) => {
+ setRestartRequiredArray((prevArray) => {
+ const newArray = prevArray.filter((i) => i !== item);
+ setRestartRequired(newArray.length > 0);
+ return newArray;
+ });
+ };
+
+ return (
+
+ {children}
+
+ );
+};
diff --git a/frontend/src/contexts/storage-provider.tsx b/frontend/src/contexts/storage-provider.tsx
new file mode 100644
index 0000000..949d379
--- /dev/null
+++ b/frontend/src/contexts/storage-provider.tsx
@@ -0,0 +1,56 @@
+import React, { createContext, useContext, useState, ReactNode } from "react";
+
+interface StorageContextType {
+ setValue: (key: string, value: any) => void;
+ getValue: (key: string) => any;
+ setValueIfUndefined: (key: string, value: any) => void;
+}
+
+interface StorageProviderProps {
+ children: ReactNode;
+}
+
+const StorageContext = createContext(undefined);
+
+export const StorageProvider: React.FC = ({
+ children,
+}) => {
+ const [storage, setStorage] = useState<{ [key: string]: any }>({});
+
+ const setValue = (key: string, value: any) => {
+ setStorage((prevStorage) => ({
+ ...prevStorage,
+ [key]: value,
+ }));
+ };
+
+ const getValue = (key: string) => {
+ return storage[key];
+ };
+
+ const setValueIfUndefined = (key: string, value: any) => {
+ if (storage[key] === undefined) {
+ setValue(key, value);
+ }
+ };
+
+ const contextValue: StorageContextType = {
+ setValue,
+ getValue,
+ setValueIfUndefined,
+ };
+
+ return (
+
+ {children}
+
+ );
+};
+
+export const useStorage = () => {
+ const context = useContext(StorageContext);
+ if (!context) {
+ throw new Error("useStorage must be used within a StorageProvider");
+ }
+ return context;
+};
diff --git a/frontend/src/contexts/theme-provider.tsx b/frontend/src/contexts/theme-provider.tsx
index d89a82e..b8e77cb 100644
--- a/frontend/src/contexts/theme-provider.tsx
+++ b/frontend/src/contexts/theme-provider.tsx
@@ -1,73 +1,100 @@
-import { createContext, useContext, useEffect, useState } from "react"
-
-type Theme = "dark" | "light" | "system"
+import {
+ LogError,
+ LogInfo,
+ WindowSetDarkTheme,
+ WindowSetLightTheme,
+ WindowSetSystemDefaultTheme,
+} from "@/wailsjs/runtime/runtime";
+import { createContext, useContext, useEffect, useState } from "react";
+import { useConfig } from "./config-provider";
+
+export type Theme = "dark" | "light" | "system";
type ThemeProviderProps = {
- children: React.ReactNode
- defaultTheme?: Theme
- storageKey?: string
-}
+ children: React.ReactNode;
+ defaultTheme?: Theme;
+};
type ThemeProviderState = {
- theme: Theme
- setTheme: (theme: Theme) => void
-}
+ theme: Theme;
+ setTheme: (theme: Theme) => Promise; // Ensure setTheme is async
+};
const initialState: ThemeProviderState = {
theme: "system",
- setTheme: () => null,
-}
+ setTheme: async () => {}, // Initial no-op async function
+};
-const ThemeProviderContext = createContext(initialState)
+const ThemeProviderContext = createContext(initialState);
export function ThemeProvider({
children,
defaultTheme = "system",
- storageKey = "vite-ui-theme",
...props
}: ThemeProviderProps) {
- const [theme, setTheme] = useState(
- () => (localStorage.getItem(storageKey) as Theme) || defaultTheme
- )
+ const { config, setConfigField } = useConfig();
+ const [theme, setThemeState] = useState(defaultTheme);
useEffect(() => {
- const root = window.document.documentElement
+ if (config) {
+ setThemeState(config.theme as Theme);
+ }
+ }, [config?.theme]); // Update theme state when config changes
- root.classList.remove("light", "dark")
+ useEffect(() => {
+ const root = window.document.documentElement;
+
+ root.classList.remove("light", "dark");
if (theme === "system") {
const systemTheme = window.matchMedia("(prefers-color-scheme: dark)")
.matches
? "dark"
- : "light"
+ : "light";
- root.classList.add(systemTheme)
- return
+ root.classList.add(systemTheme);
+ return;
}
- root.classList.add(theme)
- }, [theme])
+ root.classList.add(theme);
+ }, [theme]);
+
+ const setTheme = async (newTheme: Theme) => {
+ try {
+ await setConfigField("theme", newTheme);
+ setThemeState(newTheme);
+ if (newTheme === "system") {
+ WindowSetSystemDefaultTheme();
+ } else if (newTheme === "light") {
+ WindowSetLightTheme();
+ } else {
+ WindowSetDarkTheme();
+ }
+
+ LogInfo(`Set theme to ${newTheme}`);
+ } catch (error) {
+ LogError("Failed to set theme");
+ }
+ };
const value = {
theme,
- setTheme: (theme: Theme) => {
- localStorage.setItem(storageKey, theme)
- setTheme(theme)
- },
- }
+ setTheme,
+ };
return (
{children}
- )
+ );
}
export const useTheme = () => {
- const context = useContext(ThemeProviderContext)
+ const context = useContext(ThemeProviderContext);
- if (context === undefined)
- throw new Error("useTheme must be used within a ThemeProvider")
+ if (context === undefined) {
+ throw new Error("useTheme must be used within a ThemeProvider");
+ }
- return context
-}
+ return context;
+};
diff --git a/frontend/src/global.d.ts b/frontend/src/global.d.ts
new file mode 100644
index 0000000..2106155
--- /dev/null
+++ b/frontend/src/global.d.ts
@@ -0,0 +1,12 @@
+declare global {
+ interface Window {
+ toast: (props: ToastProps) => void;
+ goto: goto
+ sendNotification: sendNotification
+ importIconPack: importIconPack
+ setProgress: setProgress
+ }
+ }
+
+ export {};
+
\ No newline at end of file
diff --git a/frontend/src/i18n.ts b/frontend/src/i18n.ts
new file mode 100644
index 0000000..4c76eb8
--- /dev/null
+++ b/frontend/src/i18n.ts
@@ -0,0 +1,34 @@
+import { GetConfigField } from "@/wailsjs/go/main/App";
+import i18n from "i18next";
+import { initReactI18next } from "react-i18next";
+import LanguageDetector from "i18next-browser-languagedetector";
+import HttpApi from "i18next-http-backend";
+import locales from "@/locales.json";
+
+const initializeI18n = async () => {
+ const language = ((await GetConfigField("Language")) as string) || "en-US";
+
+ const supportedLngs = locales.locales.map((language) => language.code);
+
+ i18n
+ .use(HttpApi)
+ .use(LanguageDetector)
+ .use(initReactI18next)
+ .init({
+ load: "currentOnly",
+ lng: language,
+ supportedLngs: supportedLngs, // Add supported languages here
+ fallbackLng: "en-US",
+ debug: true,
+ interpolation: {
+ escapeValue: false, // React already safes from XSS
+ },
+ backend: {
+ loadPath: "/locales/{{lng}}.json", // Path to translation files
+ },
+ });
+
+ return i18n;
+};
+
+export default initializeI18n;
diff --git a/frontend/src/index.css b/frontend/src/index.css
index 31bd520..a32cdb3 100644
--- a/frontend/src/index.css
+++ b/frontend/src/index.css
@@ -3,76 +3,436 @@
@tailwind utilities;
@layer base {
+ /* Default Color Scheme: Muted Twilight */
:root {
- --background: 0 0% 100%;
- --foreground: 222.2 84% 4.9%;
-
- --muted: 210 40% 96.1%;
- --muted-foreground: 215.4 16.3% 46.9%;
-
- --popover: 0 0% 100%;
- --popover-foreground: 222.2 84% 4.9%;
-
- --card: 0 0% 100%;
- --card-foreground: 222.2 84% 4.9%;
-
- --border: 214.3 31.8% 91.4%;
- --input: 214.3 31.8% 91.4%;
-
- --primary: 222.2 47.4% 11.2%;
- --primary-foreground: 210 40% 98%;
-
- --secondary: 210 40% 96.1%;
- --secondary-foreground: 222.2 47.4% 11.2%;
-
- --accent: 210 40% 96.1%;
- --accent-foreground: 222.2 47.4% 11.2%;
-
- --destructive: 0 84.2% 60.2%;
- --destructive-foreground: 210 40% 98%;
-
- --ring: 215 20.2% 65.1%;
-
+ --background: hsla(220, 12%, 96%, var(--opacity));
+ --foreground: hsl(220, 15%, 20%);
+ --muted: hsla(220, 12%, 86%, var(--opacity));
+ --muted-foreground: hsl(220, 12%, 40%);
+ --popover: hsla(220, 12%, 96%, var(--opacity));
+ --popover-foreground: hsl(220, 15%, 20%);
+ --card: hsla(220, 12%, 96%, var(--opacity));
+ --card-foreground: hsl(220, 15%, 20%);
+ --border: hsla(220, 12%, 85%, var(--opacity));
+ --input: hsla(220, 12%, 85%, var(--opacity));
+ --primary: hsla(220, 20%, 30%, var(--opacity));
+ --primary-foreground: hsl(0, 0%, 98%);
+ --secondary: hsla(220, 15%, 88%, var(--opacity));
+ --secondary-foreground: hsl(220, 20%, 30%);
+ --accent: hsla(220, 20%, 50%, var(--opacity));
+ --accent-foreground: hsl(0, 0%, 98%);
+ --destructive: hsla(0, 60%, 50%, var(--opacity));
+ --destructive-foreground: hsl(0, 0%, 98%);
+ --warning: hsla(45, 100%, 55%, var(--opacity));
+ --warning-foreground: hsl(220, 15%, 20%);
+ --success: hsla(120, 35%, 45%, var(--opacity));
+ --success-foreground: hsl(0, 0%, 98%);
+ --ring: hsla(220, 12%, 85%, var(--opacity));
--radius: 0.5rem;
+ --opacity: 1;
}
-
.dark {
- --background: 222.2 84% 4.9%;
- --foreground: 210 40% 98%;
-
- --muted: 217.2 32.6% 17.5%;
- --muted-foreground: 215 20.2% 65.1%;
-
- --popover: 222.2 84% 4.9%;
- --popover-foreground: 210 40% 98%;
-
- --card: 222.2 84% 4.9%;
- --card-foreground: 210 40% 98%;
-
- --border: 217.2 32.6% 17.5%;
- --input: 217.2 32.6% 17.5%;
-
- --primary: 210 40% 98%;
- --primary-foreground: 222.2 47.4% 11.2%;
+ --background: hsla(220, 15%, 20%, var(--opacity));
+ --foreground: hsl(220, 12%, 96%);
+ --muted: hsla(220, 15%, 28%, var(--opacity));
+ --muted-foreground: hsl(220, 15%, 75%);
+ --popover: hsla(220, 15%, 20%, var(--opacity));
+ --popover-foreground: hsl(220, 12%, 96%);
+ --card: hsla(220, 15%, 20%, var(--opacity));
+ --card-foreground: hsl(220, 12%, 96%);
+ --border: hsla(220, 15%, 28%, var(--opacity));
+ --input: hsla(220, 15%, 28%, var(--opacity));
+ --primary: hsla(220, 12%, 96%, var(--opacity));
+ --primary-foreground: hsl(220, 20%, 30%);
+ --secondary: hsla(220, 15%, 28%, var(--opacity));
+ --secondary-foreground: hsl(220, 12%, 96%);
+ --accent: hsla(220, 20%, 50%, var(--opacity));
+ --accent-foreground: hsl(220, 12%, 96%);
+ --destructive: hsla(0, 55%, 45%, var(--opacity));
+ --destructive-foreground: hsl(0, 0%, 98%);
+ --warning: hsla(45, 100%, 55%, var(--opacity));
+ --warning-foreground: hsl(220, 12%, 96%);
+ --success: hsla(120, 35%, 45%, var(--opacity));
+ --success-foreground: hsl(220, 12%, 96%);
+ --ring: hsla(220, 15%, 28%, var(--opacity));
+ }
- --secondary: 217.2 32.6% 17.5%;
- --secondary-foreground: 210 40% 98%;
+ .midnightAsh {
+ --background: hsla(
+ 230,
+ 20%,
+ 95%,
+ var(--opacity)
+ ); /* Darker background for more noticeable contrast */
+ --foreground: hsl(230, 25%, 15%); /* Much darker foreground */
+ --muted: hsla(
+ 230,
+ 30%,
+ 85%,
+ var(--opacity)
+ ); /* Stronger muted color for better distinction */
+ --muted-foreground: hsl(230, 25%, 30%);
+ --popover: hsla(230, 20%, 95%, var(--opacity));
+ --popover-foreground: hsl(230, 25%, 15%);
+ --card: hsla(230, 20%, 95%, var(--opacity));
+ --card-foreground: hsl(230, 25%, 15%);
+ --border: hsla(230, 25%, 80%, var(--opacity)); /* More distinct borders */
+ --input: hsla(230, 25%, 80%, var(--opacity));
+ --primary: hsla(
+ 230,
+ 40%,
+ 35%,
+ var(--opacity)
+ ); /* Stronger, deeper primary color */
+ --primary-foreground: hsl(0, 0%, 98%);
+ --secondary: hsla(
+ 230,
+ 30%,
+ 80%,
+ var(--opacity)
+ ); /* More vibrant secondary color */
+ --secondary-foreground: hsl(230, 40%, 35%);
+ --accent: hsla(
+ 230,
+ 50%,
+ 45%,
+ var(--opacity)
+ ); /* Even more noticeable accent */
+ --accent-foreground: hsl(0, 0%, 98%);
+ --destructive: hsla(0, 60%, 50%, var(--opacity));
+ --destructive-foreground: hsl(0, 0%, 98%);
+ --warning: hsla(45, 100%, 55%, var(--opacity));
+ --warning-foreground: hsl(230, 25%, 15%);
+ --success: hsla(120, 40%, 40%, var(--opacity));
+ --success-foreground: hsl(0, 0%, 98%);
+ --ring: hsla(230, 25%, 80%, var(--opacity));
+ }
+ .dark.midnightAsh {
+ --background: hsla(230, 20%, 18%, var(--opacity));
+ --foreground: hsl(230, 15%, 95%);
+ --muted: hsla(230, 18%, 28%, var(--opacity));
+ --muted-foreground: hsl(230, 15%, 75%);
+ --popover: hsla(230, 20%, 18%, var(--opacity));
+ --popover-foreground: hsl(230, 15%, 95%);
+ --card: hsla(230, 20%, 18%, var(--opacity));
+ --card-foreground: hsl(230, 15%, 95%);
+ --border: hsla(230, 18%, 28%, var(--opacity));
+ --input: hsla(230, 18%, 28%, var(--opacity));
+ --primary: hsla(230, 15%, 95%, var(--opacity));
+ --primary-foreground: hsl(230, 25%, 30%);
+ --secondary: hsla(230, 18%, 28%, var(--opacity));
+ --secondary-foreground: hsl(230, 15%, 95%);
+ --accent: hsla(230, 22%, 50%, var(--opacity));
+ --accent-foreground: hsl(230, 15%, 95%);
+ --destructive: hsla(0, 55%, 45%, var(--opacity));
+ --destructive-foreground: hsl(0, 0%, 98%);
+ --warning: hsla(45, 100%, 55%, var(--opacity));
+ --warning-foreground: hsl(230, 15%, 95%);
+ --success: hsla(120, 35%, 45%, var(--opacity));
+ --success-foreground: hsl(230, 15%, 95%);
+ --ring: hsla(230, 18%, 28%, var(--opacity));
+ }
- --accent: 217.2 32.6% 17.5%;
- --accent-foreground: 210 40% 98%;
+ .dawnMist {
+ --background: hsla(
+ 210,
+ 15%,
+ 95%,
+ var(--opacity)
+ ); /* Light gray with more contrast */
+ --foreground: hsl(
+ 220,
+ 25%,
+ 12%
+ ); /* Darker foreground for better contrast */
+ --muted: hsla(
+ 210,
+ 18%,
+ 85%,
+ var(--opacity)
+ ); /* Muted color with higher contrast */
+ --muted-foreground: hsl(220, 22%, 30%);
+ --popover: hsla(210, 15%, 95%, var(--opacity));
+ --popover-foreground: hsl(220, 25%, 12%);
+ --card: hsla(210, 15%, 95%, var(--opacity));
+ --card-foreground: hsl(220, 25%, 12%);
+ --border: hsla(
+ 220,
+ 25%,
+ 80%,
+ var(--opacity)
+ ); /* Stronger borders for more distinct elements */
+ --input: hsla(220, 25%, 80%, var(--opacity));
+ --primary: hsla(
+ 210,
+ 40%,
+ 35%,
+ var(--opacity)
+ ); /* Darker primary color for emphasis */
+ --primary-foreground: hsl(0, 0%, 98%);
+ --secondary: hsla(
+ 210,
+ 35%,
+ 75%,
+ var(--opacity)
+ ); /* More saturated secondary color */
+ --secondary-foreground: hsl(210, 40%, 35%);
+ --accent: hsla(210, 50%, 45%, var(--opacity)); /* Brighter accent color */
+ --accent-foreground: hsl(0, 0%, 98%);
+ --destructive: hsla(0, 60%, 50%, var(--opacity));
+ --destructive-foreground: hsl(0, 0%, 98%);
+ --warning: hsla(45, 100%, 55%, var(--opacity));
+ --warning-foreground: hsl(220, 25%, 12%);
+ --success: hsla(
+ 120,
+ 50%,
+ 45%,
+ var(--opacity)
+ ); /* More vivid success color */
+ --success-foreground: hsl(0, 0%, 98%);
+ --ring: hsla(220, 25%, 80%, var(--opacity));
+ }
+ .dark.dawnMist {
+ --background: hsla(210, 30%, 18%, var(--opacity)); /* Darker warm gray */
+ --foreground: hsl(210, 20%, 90%); /* Light gray foreground */
+ --muted: hsla(210, 30%, 25%, var(--opacity)); /* Muted dark gray */
+ --muted-foreground: hsl(210, 22%, 75%);
+ --popover: hsla(210, 30%, 18%, var(--opacity));
+ --popover-foreground: hsl(210, 20%, 90%);
+ --card: hsla(210, 30%, 18%, var(--opacity));
+ --card-foreground: hsl(210, 20%, 90%);
+ --border: hsla(210, 30%, 25%, var(--opacity));
+ --input: hsla(210, 30%, 25%, var(--opacity));
+ --primary: hsla(210, 20%, 90%, var(--opacity));
+ --primary-foreground: hsl(210, 30%, 30%);
+ --secondary: hsla(210, 30%, 25%, var(--opacity));
+ --secondary-foreground: hsl(210, 20%, 90%);
+ --accent: hsla(210, 40%, 45%, var(--opacity)); /* Warm, noticeable accent */
+ --accent-foreground: hsl(210, 20%, 90%);
+ --destructive: hsla(0, 55%, 45%, var(--opacity));
+ --destructive-foreground: hsl(0, 0%, 98%);
+ --warning: hsla(45, 100%, 55%, var(--opacity));
+ --warning-foreground: hsl(210, 20%, 90%);
+ --success: hsla(120, 40%, 45%, var(--opacity));
+ --success-foreground: hsl(210, 20%, 90%);
+ --ring: hsla(210, 30%, 25%, var(--opacity));
+ }
- --destructive: 0 62.8% 30.6%;
- --destructive-foreground: 0 85.7% 97.3%;
+ .forestDawn {
+ --background: hsla(
+ 120,
+ 10%,
+ 97%,
+ var(--opacity)
+ ); /* Very soft, almost neutral green */
+ --foreground: hsl(120, 15%, 20%); /* Subtle dark earthy green */
+ --muted: hsla(120, 10%, 90%, var(--opacity)); /* Soft muted neutral tone */
+ --muted-foreground: hsl(120, 12%, 35%);
+ --popover: hsla(
+ 120,
+ 10%,
+ 97%,
+ var(--opacity)
+ ); /* Consistent with background */
+ --popover-foreground: hsl(120, 15%, 20%);
+ --card: hsla(120, 10%, 97%, var(--opacity));
+ --card-foreground: hsl(120, 15%, 20%);
+ --border: hsla(120, 10%, 85%, var(--opacity)); /* Soft neutral border */
+ --input: hsla(120, 10%, 85%, var(--opacity));
+ --primary: hsla(
+ 120,
+ 25%,
+ 40%,
+ var(--opacity)
+ ); /* Subtle green for primary elements */
+ --primary-foreground: hsl(0, 0%, 98%);
+ --secondary: hsla(40, 20%, 55%, var(--opacity)); /* Muted, soft brown */
+ --secondary-foreground: hsl(120, 25%, 20%);
+ --accent: hsla(90, 25%, 35%, var(--opacity)); /* Gentle accent green */
+ --accent-foreground: hsl(0, 0%, 98%);
+ --destructive: hsla(
+ 0,
+ 50%,
+ 50%,
+ var(--opacity)
+ ); /* Softer red for destructive actions */
+ --destructive-foreground: hsl(0, 0%, 98%);
+ --warning: hsla(
+ 45,
+ 90%,
+ 55%,
+ var(--opacity)
+ ); /* Gentle yellow for warnings */
+ --warning-foreground: hsl(120, 15%, 20%);
+ --success: hsla(120, 30%, 40%, var(--opacity)); /* Soft green for success */
+ --success-foreground: hsl(0, 0%, 98%);
+ --ring: hsla(120, 10%, 85%, var(--opacity));
+ --radius: 0.5rem;
+ --opacity: 1;
+ }
+ .dark.forestDawn {
+ --background: hsla(
+ 120,
+ 10%,
+ 15%,
+ var(--opacity)
+ ); /* Dark, muted green-gray */
+ --foreground: hsl(120, 10%, 80%); /* Soft, light gray for text */
+ --muted: hsla(
+ 120,
+ 10%,
+ 25%,
+ var(--opacity)
+ ); /* Muted, dark neutral green */
+ --muted-foreground: hsl(120, 15%, 65%);
+ --popover: hsla(
+ 120,
+ 10%,
+ 15%,
+ var(--opacity)
+ ); /* Consistent with the background */
+ --popover-foreground: hsl(120, 10%, 80%);
+ --card: hsla(120, 10%, 15%, var(--opacity));
+ --card-foreground: hsl(120, 10%, 80%);
+ --border: hsla(120, 10%, 25%, var(--opacity)); /* Muted dark border */
+ --input: hsla(120, 10%, 25%, var(--opacity));
+ --primary: hsla(
+ 120,
+ 20%,
+ 45%,
+ var(--opacity)
+ ); /* Muted green for primary elements */
+ --primary-foreground: hsl(0, 0%, 98%);
+ --secondary: hsla(40, 20%, 35%, var(--opacity)); /* Muted, soft brown */
+ --secondary-foreground: hsl(120, 10%, 80%);
+ --accent: hsla(
+ 90,
+ 20%,
+ 40%,
+ var(--opacity)
+ ); /* Darker, subtle green for accents */
+ --accent-foreground: hsl(0, 0%, 98%);
+ --destructive: hsla(
+ 0,
+ 50%,
+ 40%,
+ var(--opacity)
+ ); /* Softer red for destructive actions */
+ --destructive-foreground: hsl(0, 0%, 98%);
+ --warning: hsla(45, 90%, 45%, var(--opacity)); /* Soft yellow warning */
+ --warning-foreground: hsl(120, 10%, 80%);
+ --success: hsla(
+ 120,
+ 25%,
+ 40%,
+ var(--opacity)
+ ); /* Subtle green for success */
+ --success-foreground: hsl(0, 0%, 98%);
+ --ring: hsla(120, 10%, 25%, var(--opacity)); /* Muted ring color */
+ }
- --ring: 217.2 32.6% 17.5%;
+ .goldenEmber {
+ --background: hsla(
+ 30,
+ 25%,
+ 95%,
+ var(--opacity)
+ ); /* Very soft, warm beige */
+ --foreground: hsl(30, 20%, 20%); /* Darker, warm brown */
+ --muted: hsla(30, 20%, 85%, var(--opacity)); /* Muted soft amber */
+ --muted-foreground: hsl(30, 18%, 40%);
+ --popover: hsla(
+ 30,
+ 25%,
+ 95%,
+ var(--opacity)
+ ); /* Consistent with background */
+ --popover-foreground: hsl(30, 20%, 20%);
+ --card: hsla(30, 25%, 95%, var(--opacity));
+ --card-foreground: hsl(30, 20%, 20%);
+ --border: hsla(
+ 30,
+ 20%,
+ 80%,
+ var(--opacity)
+ ); /* Subtle amber-tinted border */
+ --input: hsla(30, 20%, 80%, var(--opacity));
+ --primary: hsla(35, 50%, 50%, var(--opacity)); /* Soft, warm amber */
+ --primary-foreground: hsl(0, 0%, 98%);
+ --secondary: hsla(25, 35%, 60%, var(--opacity)); /* Muted, earthy tan */
+ --secondary-foreground: hsl(30, 20%, 25%);
+ --accent: hsla(35, 45%, 40%, var(--opacity)); /* Richer amber accent */
+ --accent-foreground: hsl(0, 0%, 98%);
+ --destructive: hsla(
+ 0,
+ 50%,
+ 55%,
+ var(--opacity)
+ ); /* Subdued red for destructive actions */
+ --destructive-foreground: hsl(0, 0%, 98%);
+ --warning: hsla(
+ 45,
+ 90%,
+ 60%,
+ var(--opacity)
+ ); /* Soft, warm yellow for warnings */
+ --warning-foreground: hsl(30, 20%, 20%);
+ --success: hsla(
+ 120,
+ 30%,
+ 45%,
+ var(--opacity)
+ ); /* Soft, muted green for success */
+ --success-foreground: hsl(0, 0%, 98%);
+ --ring: hsla(30, 20%, 80%, var(--opacity)); /* Subtle amber-tinted ring */
}
+ .dark.goldenEmber {
+ --background: hsla(30, 20%, 12%, var(--opacity)); /* Muted, darker warm brown */
+ --foreground: hsl(30, 10%, 80%); /* Softer, less intense beige for text */
+ --muted: hsla(30, 20%, 20%, var(--opacity)); /* Muted, darker amber */
+ --muted-foreground: hsl(30, 15%, 65%);
+ --popover: hsla(30, 20%, 12%, var(--opacity)); /* Slightly darker background */
+ --popover-foreground: hsl(30, 10%, 80%);
+ --card: hsla(30, 20%, 12%, var(--opacity)); /* Muted card background */
+ --card-foreground: hsl(30, 10%, 80%);
+ --border: hsla(30, 20%, 22%, var(--opacity)); /* Soft, dark amber-tinted border */
+ --input: hsla(30, 20%, 22%, var(--opacity));
+ --primary: hsla(35, 40%, 45%, var(--opacity)); /* Subdued amber for dark mode */
+ --primary-foreground: hsl(0, 0%, 95%); /* Softer contrast for text */
+ --secondary: hsla(25, 30%, 30%, var(--opacity)); /* Muted, earthy tan */
+ --secondary-foreground: hsl(30, 10%, 80%);
+ --accent: hsla(35, 35%, 40%, var(--opacity)); /* Subtle amber accent */
+ --accent-foreground: hsl(0, 0%, 95%);
+ --destructive: hsla(0, 45%, 50%, var(--opacity)); /* Subdued red */
+ --destructive-foreground: hsl(0, 0%, 98%);
+ --warning: hsla(45, 85%, 50%, var(--opacity)); /* Softer yellow for warnings */
+ --warning-foreground: hsl(30, 10%, 80%);
+ --success: hsla(120, 25%, 40%, var(--opacity)); /* Softer green for success */
+ --success-foreground: hsl(0, 0%, 95%);
+ --ring: hsla(30, 20%, 22%, var(--opacity)); /* Darker amber ring */
+}
+
}
@layer base {
+ html {
+ height: 100%;
+ overflow: hidden;
+ }
* {
- @apply border-border;
+ @apply border-border scrollbar-thin scrollbar-thumb-primary scrollbar-track-transparent;
}
body {
@apply bg-background text-foreground;
}
}
+
+@layer utilities {
+ .wails-drag {
+ --wails-draggable: drag;
+ }
+
+ .wails-nodrag {
+ --wails-draggable: no-drag;
+ }
+}
diff --git a/frontend/src/locales.json b/frontend/src/locales.json
new file mode 100644
index 0000000..2b4b9da
--- /dev/null
+++ b/frontend/src/locales.json
@@ -0,0 +1,14 @@
+{
+ "locales": [
+ {
+ "name": "English",
+ "code": "en-US",
+ "authors": ["Bedirhan Yenilmez"]
+ },
+ {
+ "name": "Türkçe",
+ "code": "tr-TR",
+ "authors": ["Bedirhan Yenilmez"]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx
index 91c03f3..dd75ebe 100644
--- a/frontend/src/main.tsx
+++ b/frontend/src/main.tsx
@@ -1,10 +1,18 @@
-import React from 'react'
-import ReactDOM from 'react-dom/client'
-import App from './App.tsx'
-import './index.css'
+import ReactDOM from "react-dom/client";
+import App from "./App.tsx";
+import "./index.css";
+import initializeI18n from "./i18n";
+import Providers from "./contexts/providers.tsx";
-ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
-
-
- ,
-)
+const startApp = async () => {
+ await initializeI18n();
+ ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
+ //
+
+
+
+ //
+ );
+};
+
+startApp();
diff --git a/frontend/src/structs.tsx b/frontend/src/structs.tsx
deleted file mode 100644
index c1dbee3..0000000
--- a/frontend/src/structs.tsx
+++ /dev/null
@@ -1,23 +0,0 @@
-export interface fileInfo {
- name: string
- description: string
- path: string
- destination: string
- iconDestination: string
- iconIndex: number
- extension: string
- isFolder: boolean
- iconId: string
- iconName: string
-}
-
-export interface profileInfo {
- value: any
- label: string
-}
-
-export interface profile {
- name: string
- id: string
- value: fileInfo[]
-}
\ No newline at end of file
diff --git a/frontend/src/wailsjs/go/main/App.d.ts b/frontend/src/wailsjs/go/main/App.d.ts
new file mode 100644
index 0000000..9d5ea16
--- /dev/null
+++ b/frontend/src/wailsjs/go/main/App.d.ts
@@ -0,0 +1,133 @@
+// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
+// This file is automatically generated. DO NOT EDIT
+import {main} from '../models';
+
+export function ActionSelectImage(arg1:string):Promise;
+
+export function AddDeletePngRelativePath(arg1:string):Promise;
+
+export function AddFileToIconPackFromPath(arg1:string,arg2:string,arg3:boolean):Promise;
+
+export function AddFilesToIconPackFromDesktop(arg1:string):Promise;
+
+export function AddFilesToIconPackFromFolder(arg1:string,arg2:string,arg3:boolean):Promise;
+
+export function AddFilesToIconPackFromPath(arg1:string,arg2:Array,arg3:boolean):Promise;
+
+export function AddIconPack(arg1:string,arg2:string,arg3:string,arg4:string,arg5:string):Promise;
+
+export function AddTempPngPath(arg1:string,arg2:string):Promise;
+
+export function ApplyIconPack(arg1:string):Promise;
+
+export function CheckForUpdate():Promise;
+
+export function ClearDeletePngPaths():Promise;
+
+export function ClearIconPackCache():Promise;
+
+export function ClearSelectImages():Promise;
+
+export function ClearTempPngPaths():Promise;
+
+export function CreateLastTab(arg1:string):Promise;
+
+export function DeleteDeletePngPaths():Promise;
+
+export function DeleteIconPack(arg1:string,arg2:boolean):Promise;
+
+export function Description(arg1:string):Promise;
+
+export function Destination(arg1:string):Promise;
+
+export function ExportIconPack(arg1:string):Promise;
+
+export function Ext(arg1:string):Promise;
+
+export function GeneralPathExits(arg1:string):Promise;
+
+export function GetBase64Png():Promise;
+
+export function GetConfig():Promise;
+
+export function GetConfigField(arg1:string):Promise;
+
+export function GetFileInfoFromDesktop(arg1:string):Promise>;
+
+export function GetFileInfoFromPaths(arg1:string,arg2:Array):Promise>;
+
+export function GetFilePath(arg1:string):Promise;
+
+export function GetIcnmMetadata(arg1:string):Promise;
+
+export function GetIconFile():Promise;
+
+export function GetIconFiles():Promise>;
+
+export function GetIconFolder():Promise;
+
+export function GetIconPack(arg1:string):Promise;
+
+export function GetIconPackList():Promise>;
+
+export function GetIconPackPath():Promise;
+
+export function GetLoadConfigPath():Promise;
+
+export function GetSelectImage(arg1:string,arg2:string):Promise;
+
+export function GetTempPng(arg1:string):Promise;
+
+export function GetTempPngPath(arg1:string):Promise;
+
+export function GetVersion():Promise;
+
+export function IconLocation(arg1:string):Promise;
+
+export function ImportIconPack(arg1:string):Promise;
+
+export function Name(arg1:string):Promise;
+
+export function NeedsAdminPrivileges():Promise;
+
+export function OpenFileInExplorer(arg1:string):Promise;
+
+export function ReadConfig(arg1:string):Promise;
+
+export function ReadLastTab():Promise;
+
+export function RemoveDeletePng(arg1:string):Promise;
+
+export function RemoveTempPng(arg1:string):Promise;
+
+export function RestartApplication(arg1:boolean,arg2:Array):Promise;
+
+export function SaveConfigDialog():Promise;
+
+export function SendNotification(arg1:string,arg2:string,arg3:string,arg4:string):Promise;
+
+export function SendWindowsNotification(arg1:string,arg2:string,arg3:string,arg4:string):Promise;
+
+export function SetConfigField(arg1:string,arg2:any):Promise;
+
+export function SetIconPack(arg1:main.IconPack):Promise;
+
+export function SetIconPackField(arg1:string,arg2:string,arg3:string,arg4:any):Promise;
+
+export function SetIconPackFiles(arg1:string,arg2:Array):Promise;
+
+export function SetIconPackMetadata(arg1:string,arg2:main.Metadata):Promise;
+
+export function SetImageIfAbsent(arg1:string,arg2:string):Promise;
+
+export function SetSelectImage(arg1:string,arg2:string):Promise;
+
+export function SetTempImage(arg1:string,arg2:string):Promise;
+
+export function UUID():Promise;
+
+export function Update(arg1:string):Promise;
+
+export function UpdateAsAdmin(arg1:string):Promise;
+
+export function UploadSelectImage(arg1:string):Promise;
diff --git a/frontend/src/wailsjs/go/main/App.js b/frontend/src/wailsjs/go/main/App.js
new file mode 100644
index 0000000..6944dfd
--- /dev/null
+++ b/frontend/src/wailsjs/go/main/App.js
@@ -0,0 +1,263 @@
+// @ts-check
+// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
+// This file is automatically generated. DO NOT EDIT
+
+export function ActionSelectImage(arg1) {
+ return window['go']['main']['App']['ActionSelectImage'](arg1);
+}
+
+export function AddDeletePngRelativePath(arg1) {
+ return window['go']['main']['App']['AddDeletePngRelativePath'](arg1);
+}
+
+export function AddFileToIconPackFromPath(arg1, arg2, arg3) {
+ return window['go']['main']['App']['AddFileToIconPackFromPath'](arg1, arg2, arg3);
+}
+
+export function AddFilesToIconPackFromDesktop(arg1) {
+ return window['go']['main']['App']['AddFilesToIconPackFromDesktop'](arg1);
+}
+
+export function AddFilesToIconPackFromFolder(arg1, arg2, arg3) {
+ return window['go']['main']['App']['AddFilesToIconPackFromFolder'](arg1, arg2, arg3);
+}
+
+export function AddFilesToIconPackFromPath(arg1, arg2, arg3) {
+ return window['go']['main']['App']['AddFilesToIconPackFromPath'](arg1, arg2, arg3);
+}
+
+export function AddIconPack(arg1, arg2, arg3, arg4, arg5) {
+ return window['go']['main']['App']['AddIconPack'](arg1, arg2, arg3, arg4, arg5);
+}
+
+export function AddTempPngPath(arg1, arg2) {
+ return window['go']['main']['App']['AddTempPngPath'](arg1, arg2);
+}
+
+export function ApplyIconPack(arg1) {
+ return window['go']['main']['App']['ApplyIconPack'](arg1);
+}
+
+export function CheckForUpdate() {
+ return window['go']['main']['App']['CheckForUpdate']();
+}
+
+export function ClearDeletePngPaths() {
+ return window['go']['main']['App']['ClearDeletePngPaths']();
+}
+
+export function ClearIconPackCache() {
+ return window['go']['main']['App']['ClearIconPackCache']();
+}
+
+export function ClearSelectImages() {
+ return window['go']['main']['App']['ClearSelectImages']();
+}
+
+export function ClearTempPngPaths() {
+ return window['go']['main']['App']['ClearTempPngPaths']();
+}
+
+export function CreateLastTab(arg1) {
+ return window['go']['main']['App']['CreateLastTab'](arg1);
+}
+
+export function DeleteDeletePngPaths() {
+ return window['go']['main']['App']['DeleteDeletePngPaths']();
+}
+
+export function DeleteIconPack(arg1, arg2) {
+ return window['go']['main']['App']['DeleteIconPack'](arg1, arg2);
+}
+
+export function Description(arg1) {
+ return window['go']['main']['App']['Description'](arg1);
+}
+
+export function Destination(arg1) {
+ return window['go']['main']['App']['Destination'](arg1);
+}
+
+export function ExportIconPack(arg1) {
+ return window['go']['main']['App']['ExportIconPack'](arg1);
+}
+
+export function Ext(arg1) {
+ return window['go']['main']['App']['Ext'](arg1);
+}
+
+export function GeneralPathExits(arg1) {
+ return window['go']['main']['App']['GeneralPathExits'](arg1);
+}
+
+export function GetBase64Png() {
+ return window['go']['main']['App']['GetBase64Png']();
+}
+
+export function GetConfig() {
+ return window['go']['main']['App']['GetConfig']();
+}
+
+export function GetConfigField(arg1) {
+ return window['go']['main']['App']['GetConfigField'](arg1);
+}
+
+export function GetFileInfoFromDesktop(arg1) {
+ return window['go']['main']['App']['GetFileInfoFromDesktop'](arg1);
+}
+
+export function GetFileInfoFromPaths(arg1, arg2) {
+ return window['go']['main']['App']['GetFileInfoFromPaths'](arg1, arg2);
+}
+
+export function GetFilePath(arg1) {
+ return window['go']['main']['App']['GetFilePath'](arg1);
+}
+
+export function GetIcnmMetadata(arg1) {
+ return window['go']['main']['App']['GetIcnmMetadata'](arg1);
+}
+
+export function GetIconFile() {
+ return window['go']['main']['App']['GetIconFile']();
+}
+
+export function GetIconFiles() {
+ return window['go']['main']['App']['GetIconFiles']();
+}
+
+export function GetIconFolder() {
+ return window['go']['main']['App']['GetIconFolder']();
+}
+
+export function GetIconPack(arg1) {
+ return window['go']['main']['App']['GetIconPack'](arg1);
+}
+
+export function GetIconPackList() {
+ return window['go']['main']['App']['GetIconPackList']();
+}
+
+export function GetIconPackPath() {
+ return window['go']['main']['App']['GetIconPackPath']();
+}
+
+export function GetLoadConfigPath() {
+ return window['go']['main']['App']['GetLoadConfigPath']();
+}
+
+export function GetSelectImage(arg1, arg2) {
+ return window['go']['main']['App']['GetSelectImage'](arg1, arg2);
+}
+
+export function GetTempPng(arg1) {
+ return window['go']['main']['App']['GetTempPng'](arg1);
+}
+
+export function GetTempPngPath(arg1) {
+ return window['go']['main']['App']['GetTempPngPath'](arg1);
+}
+
+export function GetVersion() {
+ return window['go']['main']['App']['GetVersion']();
+}
+
+export function IconLocation(arg1) {
+ return window['go']['main']['App']['IconLocation'](arg1);
+}
+
+export function ImportIconPack(arg1) {
+ return window['go']['main']['App']['ImportIconPack'](arg1);
+}
+
+export function Name(arg1) {
+ return window['go']['main']['App']['Name'](arg1);
+}
+
+export function NeedsAdminPrivileges() {
+ return window['go']['main']['App']['NeedsAdminPrivileges']();
+}
+
+export function OpenFileInExplorer(arg1) {
+ return window['go']['main']['App']['OpenFileInExplorer'](arg1);
+}
+
+export function ReadConfig(arg1) {
+ return window['go']['main']['App']['ReadConfig'](arg1);
+}
+
+export function ReadLastTab() {
+ return window['go']['main']['App']['ReadLastTab']();
+}
+
+export function RemoveDeletePng(arg1) {
+ return window['go']['main']['App']['RemoveDeletePng'](arg1);
+}
+
+export function RemoveTempPng(arg1) {
+ return window['go']['main']['App']['RemoveTempPng'](arg1);
+}
+
+export function RestartApplication(arg1, arg2) {
+ return window['go']['main']['App']['RestartApplication'](arg1, arg2);
+}
+
+export function SaveConfigDialog() {
+ return window['go']['main']['App']['SaveConfigDialog']();
+}
+
+export function SendNotification(arg1, arg2, arg3, arg4) {
+ return window['go']['main']['App']['SendNotification'](arg1, arg2, arg3, arg4);
+}
+
+export function SendWindowsNotification(arg1, arg2, arg3, arg4) {
+ return window['go']['main']['App']['SendWindowsNotification'](arg1, arg2, arg3, arg4);
+}
+
+export function SetConfigField(arg1, arg2) {
+ return window['go']['main']['App']['SetConfigField'](arg1, arg2);
+}
+
+export function SetIconPack(arg1) {
+ return window['go']['main']['App']['SetIconPack'](arg1);
+}
+
+export function SetIconPackField(arg1, arg2, arg3, arg4) {
+ return window['go']['main']['App']['SetIconPackField'](arg1, arg2, arg3, arg4);
+}
+
+export function SetIconPackFiles(arg1, arg2) {
+ return window['go']['main']['App']['SetIconPackFiles'](arg1, arg2);
+}
+
+export function SetIconPackMetadata(arg1, arg2) {
+ return window['go']['main']['App']['SetIconPackMetadata'](arg1, arg2);
+}
+
+export function SetImageIfAbsent(arg1, arg2) {
+ return window['go']['main']['App']['SetImageIfAbsent'](arg1, arg2);
+}
+
+export function SetSelectImage(arg1, arg2) {
+ return window['go']['main']['App']['SetSelectImage'](arg1, arg2);
+}
+
+export function SetTempImage(arg1, arg2) {
+ return window['go']['main']['App']['SetTempImage'](arg1, arg2);
+}
+
+export function UUID() {
+ return window['go']['main']['App']['UUID']();
+}
+
+export function Update(arg1) {
+ return window['go']['main']['App']['Update'](arg1);
+}
+
+export function UpdateAsAdmin(arg1) {
+ return window['go']['main']['App']['UpdateAsAdmin'](arg1);
+}
+
+export function UploadSelectImage(arg1) {
+ return window['go']['main']['App']['UploadSelectImage'](arg1);
+}
diff --git a/frontend/src/wailsjs/go/models.ts b/frontend/src/wailsjs/go/models.ts
new file mode 100644
index 0000000..2383c99
--- /dev/null
+++ b/frontend/src/wailsjs/go/models.ts
@@ -0,0 +1,215 @@
+export namespace main {
+
+ export class Config {
+ theme?: string;
+ colorScheme?: string;
+ useSystemTitleBar?: boolean;
+ enableLogging?: boolean;
+ enableTrace?: boolean;
+ enableDebug?: boolean;
+ enableInfo?: boolean;
+ enableWarn?: boolean;
+ enableError?: boolean;
+ enableFatal?: boolean;
+ maxLogFiles?: number;
+ language?: string;
+ saveWindowStatus?: boolean;
+ windowStartState?: number;
+ windowStartPositionX?: number;
+ windowStartPositionY?: number;
+ windowStartSizeX?: number;
+ windowStartSizeY?: number;
+ windowScale?: number;
+ opacity?: number;
+ windowEffect?: number;
+ checkForUpdates?: boolean;
+ lastUpdateCheck?: number;
+ matchLnkByDestination?: boolean;
+ matchURLByDestination?: boolean;
+ renameMatchedFiles?: boolean;
+ changeDescriptionOfMathcedLnkFiles?: boolean;
+
+ static createFrom(source: any = {}) {
+ return new Config(source);
+ }
+
+ constructor(source: any = {}) {
+ if ('string' === typeof source) source = JSON.parse(source);
+ this.theme = source["theme"];
+ this.colorScheme = source["colorScheme"];
+ this.useSystemTitleBar = source["useSystemTitleBar"];
+ this.enableLogging = source["enableLogging"];
+ this.enableTrace = source["enableTrace"];
+ this.enableDebug = source["enableDebug"];
+ this.enableInfo = source["enableInfo"];
+ this.enableWarn = source["enableWarn"];
+ this.enableError = source["enableError"];
+ this.enableFatal = source["enableFatal"];
+ this.maxLogFiles = source["maxLogFiles"];
+ this.language = source["language"];
+ this.saveWindowStatus = source["saveWindowStatus"];
+ this.windowStartState = source["windowStartState"];
+ this.windowStartPositionX = source["windowStartPositionX"];
+ this.windowStartPositionY = source["windowStartPositionY"];
+ this.windowStartSizeX = source["windowStartSizeX"];
+ this.windowStartSizeY = source["windowStartSizeY"];
+ this.windowScale = source["windowScale"];
+ this.opacity = source["opacity"];
+ this.windowEffect = source["windowEffect"];
+ this.checkForUpdates = source["checkForUpdates"];
+ this.lastUpdateCheck = source["lastUpdateCheck"];
+ this.matchLnkByDestination = source["matchLnkByDestination"];
+ this.matchURLByDestination = source["matchURLByDestination"];
+ this.renameMatchedFiles = source["renameMatchedFiles"];
+ this.changeDescriptionOfMathcedLnkFiles = source["changeDescriptionOfMathcedLnkFiles"];
+ }
+ }
+ export class FileInfo {
+ id: string;
+ name: string;
+ description: string;
+ path: string;
+ destinationPath: string;
+ extension: string;
+ hasIcon: boolean;
+ iconId: string;
+
+ static createFrom(source: any = {}) {
+ return new FileInfo(source);
+ }
+
+ constructor(source: any = {}) {
+ if ('string' === typeof source) source = JSON.parse(source);
+ this.id = source["id"];
+ this.name = source["name"];
+ this.description = source["description"];
+ this.path = source["path"];
+ this.destinationPath = source["destinationPath"];
+ this.extension = source["extension"];
+ this.hasIcon = source["hasIcon"];
+ this.iconId = source["iconId"];
+ }
+ }
+ export class IconPackSettings {
+ enabled: boolean;
+ cornerRadius: number;
+ opacity: number;
+
+ static createFrom(source: any = {}) {
+ return new IconPackSettings(source);
+ }
+
+ constructor(source: any = {}) {
+ if ('string' === typeof source) source = JSON.parse(source);
+ this.enabled = source["enabled"];
+ this.cornerRadius = source["cornerRadius"];
+ this.opacity = source["opacity"];
+ }
+ }
+ export class Metadata {
+ id: string;
+ name: string;
+ version: string;
+ author: string;
+ license: string;
+ description: string;
+ iconName: string;
+
+ static createFrom(source: any = {}) {
+ return new Metadata(source);
+ }
+
+ constructor(source: any = {}) {
+ if ('string' === typeof source) source = JSON.parse(source);
+ this.id = source["id"];
+ this.name = source["name"];
+ this.version = source["version"];
+ this.author = source["author"];
+ this.license = source["license"];
+ this.description = source["description"];
+ this.iconName = source["iconName"];
+ }
+ }
+ export class IconPack {
+ metadata: Metadata;
+ files: FileInfo[];
+ settings: IconPackSettings;
+
+ static createFrom(source: any = {}) {
+ return new IconPack(source);
+ }
+
+ constructor(source: any = {}) {
+ if ('string' === typeof source) source = JSON.parse(source);
+ this.metadata = this.convertValues(source["metadata"], Metadata);
+ this.files = this.convertValues(source["files"], FileInfo);
+ this.settings = this.convertValues(source["settings"], IconPackSettings);
+ }
+
+ 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 class SelectImage {
+ id: string;
+ path: string;
+ tempPath: string;
+ hasOriginal: boolean;
+ hasTemp: boolean;
+ isRemoved: boolean;
+
+ static createFrom(source: any = {}) {
+ return new SelectImage(source);
+ }
+
+ constructor(source: any = {}) {
+ if ('string' === typeof source) source = JSON.parse(source);
+ this.id = source["id"];
+ this.path = source["path"];
+ this.tempPath = source["tempPath"];
+ this.hasOriginal = source["hasOriginal"];
+ this.hasTemp = source["hasTemp"];
+ this.isRemoved = source["isRemoved"];
+ }
+ }
+ export class UpdateInfo {
+ updateAvailable: boolean;
+ currentVersion: string;
+ latestVersion: string;
+ name: string;
+ releaseNotes: string;
+ downloadUrl: string;
+
+ static createFrom(source: any = {}) {
+ return new UpdateInfo(source);
+ }
+
+ constructor(source: any = {}) {
+ if ('string' === typeof source) source = JSON.parse(source);
+ this.updateAvailable = source["updateAvailable"];
+ this.currentVersion = source["currentVersion"];
+ this.latestVersion = source["latestVersion"];
+ this.name = source["name"];
+ this.releaseNotes = source["releaseNotes"];
+ this.downloadUrl = source["downloadUrl"];
+ }
+ }
+
+}
+
diff --git a/frontend/wailsjs/runtime/package.json b/frontend/src/wailsjs/runtime/package.json
similarity index 100%
rename from frontend/wailsjs/runtime/package.json
rename to frontend/src/wailsjs/runtime/package.json
diff --git a/frontend/wailsjs/runtime/runtime.d.ts b/frontend/src/wailsjs/runtime/runtime.d.ts
similarity index 93%
rename from frontend/wailsjs/runtime/runtime.d.ts
rename to frontend/src/wailsjs/runtime/runtime.d.ts
index a3723f9..94778df 100644
--- a/frontend/wailsjs/runtime/runtime.d.ts
+++ b/frontend/src/wailsjs/runtime/runtime.d.ts
@@ -233,3 +233,17 @@ export function ClipboardGetText(): Promise;
// [ClipboardSetText](https://wails.io/docs/reference/runtime/clipboard#clipboardsettext)
// Sets a text on the clipboard
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
+
+// [OnFileDropOff](https://wails.io/docs/reference/runtime/draganddrop#dragandddropoff)
+// OnFileDropOff removes the drag and drop listeners and handlers.
+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
\ No newline at end of file
diff --git a/frontend/wailsjs/runtime/runtime.js b/frontend/src/wailsjs/runtime/runtime.js
similarity index 78%
rename from frontend/wailsjs/runtime/runtime.js
rename to frontend/src/wailsjs/runtime/runtime.js
index bd4f371..623397b 100644
--- a/frontend/wailsjs/runtime/runtime.js
+++ b/frontend/src/wailsjs/runtime/runtime.js
@@ -199,4 +199,40 @@ export function ClipboardGetText() {
export function ClipboardSetText(text) {
return window.runtime.ClipboardSetText(text);
+}
+
+/**
+ * Callback for OnFileDrop returns a slice of file path strings when a drop is finished.
+ *
+ * @export
+ * @callback OnFileDropCallback
+ * @param {number} x - x coordinate of the drop
+ * @param {number} y - y coordinate of the drop
+ * @param {string[]} paths - A list of file paths.
+ */
+
+/**
+ * OnFileDrop listens to drag and drop events and calls the callback with the coordinates of the drop and an array of path strings.
+ *
+ * @export
+ * @param {OnFileDropCallback} callback - Callback for OnFileDrop returns a slice of file path strings when a drop is finished.
+ * @param {boolean} [useDropTarget=true] - Only call the callback when the drop finished on an element that has the drop target style. (--wails-drop-target)
+ */
+export function OnFileDrop(callback, useDropTarget) {
+ return window.runtime.OnFileDrop(callback, useDropTarget);
+}
+
+/**
+ * OnFileDropOff removes the drag and drop listeners and handlers.
+ */
+export function OnFileDropOff() {
+ return window.runtime.OnFileDropOff();
+}
+
+export function CanResolveFilePaths() {
+ return window.runtime.CanResolveFilePaths();
+}
+
+export function ResolveFilePaths(files) {
+ return window.runtime.ResolveFilePaths(files);
}
\ No newline at end of file
diff --git a/frontend/tailwind.config.js b/frontend/tailwind.config.js
index 0377ea1..96cfd4a 100644
--- a/frontend/tailwind.config.js
+++ b/frontend/tailwind.config.js
@@ -1,12 +1,14 @@
/** @type {import('tailwindcss').Config} */
+const plugin = require("tailwindcss/plugin");
+
module.exports = {
darkMode: ["class"],
content: [
- './pages/**/*.{ts,tsx}',
- './components/**/*.{ts,tsx}',
- './app/**/*.{ts,tsx}',
- './src/**/*.{ts,tsx}',
- ],
+ "./pages/**/*.{ts,tsx}",
+ "./components/**/*.{ts,tsx}",
+ "./app/**/*.{ts,tsx}",
+ "./src/**/*.{ts,tsx}",
+ ],
theme: {
container: {
center: true,
@@ -17,38 +19,46 @@ module.exports = {
},
extend: {
colors: {
- border: "hsl(var(--border))",
- input: "hsl(var(--input))",
- ring: "hsl(var(--ring))",
- background: "hsl(var(--background))",
- foreground: "hsl(var(--foreground))",
+ border: "var(--border)",
+ input: "var(--input)",
+ ring: "var(--ring)",
+ background: "var(--background)",
+ foreground: "var(--foreground)",
primary: {
- DEFAULT: "hsl(var(--primary))",
- foreground: "hsl(var(--primary-foreground))",
+ DEFAULT: "var(--primary)",
+ foreground: "var(--primary-foreground)",
},
secondary: {
- DEFAULT: "hsl(var(--secondary))",
- foreground: "hsl(var(--secondary-foreground))",
+ DEFAULT: "var(--secondary)",
+ foreground: "var(--secondary-foreground)",
},
destructive: {
- DEFAULT: "hsl(var(--destructive))",
- foreground: "hsl(var(--destructive-foreground))",
+ DEFAULT: "var(--destructive)",
+ foreground: "var(--destructive-foreground)",
},
muted: {
- DEFAULT: "hsl(var(--muted))",
- foreground: "hsl(var(--muted-foreground))",
+ DEFAULT: "var(--muted)",
+ foreground: "var(--muted-foreground)",
},
accent: {
- DEFAULT: "hsl(var(--accent))",
- foreground: "hsl(var(--accent-foreground))",
+ DEFAULT: "var(--accent)",
+ foreground: "var(--accent-foreground)",
},
popover: {
- DEFAULT: "hsl(var(--popover))",
- foreground: "hsl(var(--popover-foreground))",
+ DEFAULT: "var(--popover)",
+ foreground: "var(--popover-foreground)",
},
card: {
- DEFAULT: "hsl(var(--card))",
- foreground: "hsl(var(--card-foreground))",
+ DEFAULT: "var(--card)",
+ foreground: "var(--card-foreground)",
+ },
+ warning: {
+ DEFAULT: "var(--warning)",
+ foreground: "var(--warning-foreground)",
+ },
+ success: {
+ DEFAULT: "var(--success)",
+ foreground: "var(--success-foreground)",
},
},
borderRadius: {
@@ -70,7 +80,37 @@ module.exports = {
"accordion-down": "accordion-down 0.2s ease-out",
"accordion-up": "accordion-up 0.2s ease-out",
},
+ transitionDuration: {
+ 250: "250ms",
+ },
+ boxShadow: {
+ "bottom-xs": "0px 1px 2px rgba(0,0,0,0.1)",
+ "bottom-sm": "0px 2px 4px rgba(0,0,0,0.1)",
+ "bottom-md": "0px 4px 6px rgba(0,0,0,0.1)",
+ "bottom-lg": "0px 6px 8px rgba(0,0,0,0.1)",
+ "bottom-xl": "0px 8px 10px rgba(0,0,0,0.1)",
+ "bottom-2xl": "0px 12px 16px rgba(0,0,0,0.1)",
+ },
+ textShadow: {
+ sm: "0 1px 2px var(--tw-shadow-color)",
+ DEFAULT: "0 2px 4px var(--tw-shadow-color)",
+ lg: "0 3px 16px var(--tw-shadow-color)",
+ xl: "0 4px 24px var(--tw-shadow-color)",
+ },
},
},
- plugins: [require("tailwindcss-animate")],
-}
\ No newline at end of file
+ plugins: [
+ require("tailwind-scrollbar"),
+ require("tailwindcss-animate"),
+ plugin(function ({ matchUtilities, theme }) {
+ matchUtilities(
+ {
+ "text-shadow": (value) => ({
+ textShadow: value,
+ }),
+ },
+ { values: theme("textShadow") }
+ );
+ }),
+ ],
+};
diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json
index 9222619..963495d 100644
--- a/frontend/tsconfig.json
+++ b/frontend/tsconfig.json
@@ -5,12 +5,10 @@
"lib": ["DOM", "DOM.Iterable", "ESNext"],
"module": "ESNext",
"skipLibCheck": true,
- "typeRoots" : ["./wailsjs/go/main/**/*.d.ts"],
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
- "allowSyntheticDefaultImports": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
@@ -26,8 +24,7 @@
"rootDir": "src",
"baseUrl": "./",
"paths": {
- "@/*": ["./src/*"],
- "wailsjs/*": ["./wailsjs/*"]
+ "@/*": ["./src/*"]
}
},
"include": ["src"],
diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts
index 0942c02..0464f87 100644
--- a/frontend/vite.config.ts
+++ b/frontend/vite.config.ts
@@ -1,12 +1,12 @@
-import { defineConfig } from 'vite'
-import react from '@vitejs/plugin-react'
-import path from 'path'
-import { fileURLToPath } from 'url'
-import { dirname } from 'path'
+import { defineConfig } from "vite";
+import react from "@vitejs/plugin-react";
+import path from "path";
+import { fileURLToPath } from "url";
+import { dirname } from "path";
// if in ESM context
-const __filename = fileURLToPath(import.meta.url)
-const __dirname = dirname(__filename)
+const __filename = fileURLToPath(import.meta.url);
+const __dirname = dirname(__filename);
// https://vitejs.dev/config/
export default defineConfig({
@@ -14,7 +14,6 @@ export default defineConfig({
resolve: {
alias: {
"@": path.resolve(__dirname, "./src"),
- "wailsjs": path.resolve(__dirname, "./wailsjs")
- }
- }
-})
+ },
+ },
+});
diff --git a/frontend/wailsjs/go/main/App.d.ts b/frontend/wailsjs/go/main/App.d.ts
deleted file mode 100644
index 4eb0406..0000000
--- a/frontend/wailsjs/go/main/App.d.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
-// This file is automatically generated. DO NOT EDIT
-import {main} from '../models';
-
-export function AddProfile(arg1:string):Promise;
-
-export function GetDesktopIcons():Promise>;
-
-export function GetFileInfo(arg1:string):Promise;
-
-export function GetIcon(arg1:string,arg2:string):Promise;
-
-export function GetProfile(arg1:string):Promise;
-
-export function GetProfiles():Promise>;
-
-export function RemoveProfile(arg1:string):Promise;
-
-export function RunProfile(arg1:string,arg2:Array):Promise;
-
-export function SaveIcon(arg1:string,arg2:main.fileInfo):Promise;
-
-export function SaveProfile(arg1:string,arg2:string):Promise;
-
-export function SyncDesktop(arg1:string,arg2:boolean):Promise;
diff --git a/frontend/wailsjs/go/main/App.js b/frontend/wailsjs/go/main/App.js
deleted file mode 100644
index 7516a4d..0000000
--- a/frontend/wailsjs/go/main/App.js
+++ /dev/null
@@ -1,47 +0,0 @@
-// @ts-check
-// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
-// This file is automatically generated. DO NOT EDIT
-
-export function AddProfile(arg1) {
- return window['go']['main']['App']['AddProfile'](arg1);
-}
-
-export function GetDesktopIcons() {
- return window['go']['main']['App']['GetDesktopIcons']();
-}
-
-export function GetFileInfo(arg1) {
- return window['go']['main']['App']['GetFileInfo'](arg1);
-}
-
-export function GetIcon(arg1, arg2) {
- return window['go']['main']['App']['GetIcon'](arg1, arg2);
-}
-
-export function GetProfile(arg1) {
- return window['go']['main']['App']['GetProfile'](arg1);
-}
-
-export function GetProfiles() {
- return window['go']['main']['App']['GetProfiles']();
-}
-
-export function RemoveProfile(arg1) {
- return window['go']['main']['App']['RemoveProfile'](arg1);
-}
-
-export function RunProfile(arg1, arg2) {
- return window['go']['main']['App']['RunProfile'](arg1, arg2);
-}
-
-export function SaveIcon(arg1, arg2) {
- return window['go']['main']['App']['SaveIcon'](arg1, arg2);
-}
-
-export function SaveProfile(arg1, arg2) {
- return window['go']['main']['App']['SaveProfile'](arg1, arg2);
-}
-
-export function SyncDesktop(arg1, arg2) {
- return window['go']['main']['App']['SyncDesktop'](arg1, arg2);
-}
diff --git a/frontend/wailsjs/go/models.ts b/frontend/wailsjs/go/models.ts
deleted file mode 100644
index f35e9df..0000000
--- a/frontend/wailsjs/go/models.ts
+++ /dev/null
@@ -1,83 +0,0 @@
-export namespace main {
-
- export class fileInfo {
- name: string;
- description: string;
- path: string;
- destination: string;
- iconDestination: string;
- iconIndex: number;
- extension: string;
- isFolder: boolean;
- iconId: string;
- iconName: string;
-
- static createFrom(source: any = {}) {
- return new fileInfo(source);
- }
-
- constructor(source: any = {}) {
- if ('string' === typeof source) source = JSON.parse(source);
- this.name = source["name"];
- this.description = source["description"];
- this.path = source["path"];
- this.destination = source["destination"];
- this.iconDestination = source["iconDestination"];
- this.iconIndex = source["iconIndex"];
- this.extension = source["extension"];
- this.isFolder = source["isFolder"];
- this.iconId = source["iconId"];
- this.iconName = source["iconName"];
- }
- }
- export class profile {
- name: string;
- id: string;
- value: fileInfo[];
-
- static createFrom(source: any = {}) {
- return new profile(source);
- }
-
- constructor(source: any = {}) {
- if ('string' === typeof source) source = JSON.parse(source);
- this.name = source["name"];
- this.id = source["id"];
- this.value = this.convertValues(source["value"], fileInfo);
- }
-
- convertValues(a: any, classs: any, asMap: boolean = false): any {
- if (!a) {
- return a;
- }
- if (a.slice) {
- 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 class profileInfo {
- value: any;
- label: string;
-
- static createFrom(source: any = {}) {
- return new profileInfo(source);
- }
-
- constructor(source: any = {}) {
- if ('string' === typeof source) source = JSON.parse(source);
- this.value = source["value"];
- this.label = source["label"];
- }
- }
-
-}
-
diff --git a/frontend/yarn.lock b/frontend/yarn.lock
index a4b90de..0a06fb7 100644
--- a/frontend/yarn.lock
+++ b/frontend/yarn.lock
@@ -2,227 +2,226 @@
# yarn lockfile v1
-"@aashutoshrathi/word-wrap@^1.2.3":
- version "1.2.6"
- resolved "https://registry.yarnpkg.com/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz#bd9154aec9983f77b3a034ecaa015c2e4201f6cf"
- integrity sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==
-
"@alloc/quick-lru@^5.2.0":
version "5.2.0"
resolved "https://registry.yarnpkg.com/@alloc/quick-lru/-/quick-lru-5.2.0.tgz#7bf68b20c0a350f936915fcae06f58e32007ce30"
integrity sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==
"@ampproject/remapping@^2.2.0":
- version "2.2.1"
- resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.1.tgz#99e8e11851128b8702cd57c33684f1d0f260b630"
- integrity sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.3.0.tgz#ed441b6fa600072520ce18b43d2c8cc8caecc7f4"
+ integrity sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==
dependencies:
- "@jridgewell/gen-mapping" "^0.3.0"
- "@jridgewell/trace-mapping" "^0.3.9"
+ "@jridgewell/gen-mapping" "^0.3.5"
+ "@jridgewell/trace-mapping" "^0.3.24"
-"@babel/code-frame@^7.23.5":
- version "7.23.5"
- resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.23.5.tgz#9009b69a8c602293476ad598ff53e4562e15c244"
- integrity sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==
+"@babel/code-frame@^7.24.7":
+ version "7.24.7"
+ resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.24.7.tgz#882fd9e09e8ee324e496bd040401c6f046ef4465"
+ integrity sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==
dependencies:
- "@babel/highlight" "^7.23.4"
- chalk "^2.4.2"
+ "@babel/highlight" "^7.24.7"
+ picocolors "^1.0.0"
-"@babel/compat-data@^7.23.5":
- version "7.23.5"
- resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.23.5.tgz#ffb878728bb6bdcb6f4510aa51b1be9afb8cfd98"
- integrity sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw==
+"@babel/compat-data@^7.24.7":
+ version "7.24.7"
+ resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.24.7.tgz#d23bbea508c3883ba8251fb4164982c36ea577ed"
+ integrity sha512-qJzAIcv03PyaWqxRgO4mSU3lihncDT296vnyuE2O8uA4w3UHWI4S3hgeZd1L8W1Bft40w9JxJ2b412iDUFFRhw==
-"@babel/core@^7.23.5":
- version "7.24.0"
- resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.24.0.tgz#56cbda6b185ae9d9bed369816a8f4423c5f2ff1b"
- integrity sha512-fQfkg0Gjkza3nf0c7/w6Xf34BW4YvzNfACRLmmb7XRLa6XHdR+K9AlJlxneFfWYf6uhOzuzZVTjF/8KfndZANw==
+"@babel/core@^7.24.5":
+ version "7.24.7"
+ resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.24.7.tgz#b676450141e0b52a3d43bc91da86aa608f950ac4"
+ integrity sha512-nykK+LEK86ahTkX/3TgauT0ikKoNCfKHEaZYTUVupJdTLzGNvrblu4u6fa7DhZONAltdf8e662t/abY8idrd/g==
dependencies:
"@ampproject/remapping" "^2.2.0"
- "@babel/code-frame" "^7.23.5"
- "@babel/generator" "^7.23.6"
- "@babel/helper-compilation-targets" "^7.23.6"
- "@babel/helper-module-transforms" "^7.23.3"
- "@babel/helpers" "^7.24.0"
- "@babel/parser" "^7.24.0"
- "@babel/template" "^7.24.0"
- "@babel/traverse" "^7.24.0"
- "@babel/types" "^7.24.0"
+ "@babel/code-frame" "^7.24.7"
+ "@babel/generator" "^7.24.7"
+ "@babel/helper-compilation-targets" "^7.24.7"
+ "@babel/helper-module-transforms" "^7.24.7"
+ "@babel/helpers" "^7.24.7"
+ "@babel/parser" "^7.24.7"
+ "@babel/template" "^7.24.7"
+ "@babel/traverse" "^7.24.7"
+ "@babel/types" "^7.24.7"
convert-source-map "^2.0.0"
debug "^4.1.0"
gensync "^1.0.0-beta.2"
json5 "^2.2.3"
semver "^6.3.1"
-"@babel/generator@^7.23.6":
- version "7.23.6"
- resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.23.6.tgz#9e1fca4811c77a10580d17d26b57b036133f3c2e"
- integrity sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==
+"@babel/generator@^7.24.7":
+ version "7.24.7"
+ resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.24.7.tgz#1654d01de20ad66b4b4d99c135471bc654c55e6d"
+ integrity sha512-oipXieGC3i45Y1A41t4tAqpnEZWgB/lC6Ehh6+rOviR5XWpTtMmLN+fGjz9vOiNRt0p6RtO6DtD0pdU3vpqdSA==
dependencies:
- "@babel/types" "^7.23.6"
- "@jridgewell/gen-mapping" "^0.3.2"
- "@jridgewell/trace-mapping" "^0.3.17"
+ "@babel/types" "^7.24.7"
+ "@jridgewell/gen-mapping" "^0.3.5"
+ "@jridgewell/trace-mapping" "^0.3.25"
jsesc "^2.5.1"
-"@babel/helper-compilation-targets@^7.23.6":
- version "7.23.6"
- resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz#4d79069b16cbcf1461289eccfbbd81501ae39991"
- integrity sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==
+"@babel/helper-compilation-targets@^7.24.7":
+ version "7.24.7"
+ resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.24.7.tgz#4eb6c4a80d6ffeac25ab8cd9a21b5dfa48d503a9"
+ integrity sha512-ctSdRHBi20qWOfy27RUb4Fhp07KSJ3sXcuSvTrXrc4aG8NSYDo1ici3Vhg9bg69y5bj0Mr1lh0aeEgTvc12rMg==
dependencies:
- "@babel/compat-data" "^7.23.5"
- "@babel/helper-validator-option" "^7.23.5"
+ "@babel/compat-data" "^7.24.7"
+ "@babel/helper-validator-option" "^7.24.7"
browserslist "^4.22.2"
lru-cache "^5.1.1"
semver "^6.3.1"
-"@babel/helper-environment-visitor@^7.22.20":
- version "7.22.20"
- resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz#96159db61d34a29dba454c959f5ae4a649ba9167"
- integrity sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==
-
-"@babel/helper-function-name@^7.23.0":
- version "7.23.0"
- resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz#1f9a3cdbd5b2698a670c30d2735f9af95ed52759"
- integrity sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==
- dependencies:
- "@babel/template" "^7.22.15"
- "@babel/types" "^7.23.0"
-
-"@babel/helper-hoist-variables@^7.22.5":
- version "7.22.5"
- resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz#c01a007dac05c085914e8fb652b339db50d823bb"
- integrity sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==
- dependencies:
- "@babel/types" "^7.22.5"
-
-"@babel/helper-module-imports@^7.22.15":
- version "7.22.15"
- resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz#16146307acdc40cc00c3b2c647713076464bdbf0"
- integrity sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==
- dependencies:
- "@babel/types" "^7.22.15"
-
-"@babel/helper-module-transforms@^7.23.3":
- version "7.23.3"
- resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz#d7d12c3c5d30af5b3c0fcab2a6d5217773e2d0f1"
- integrity sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==
- dependencies:
- "@babel/helper-environment-visitor" "^7.22.20"
- "@babel/helper-module-imports" "^7.22.15"
- "@babel/helper-simple-access" "^7.22.5"
- "@babel/helper-split-export-declaration" "^7.22.6"
- "@babel/helper-validator-identifier" "^7.22.20"
-
-"@babel/helper-plugin-utils@^7.22.5":
- version "7.24.0"
- resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.0.tgz#945681931a52f15ce879fd5b86ce2dae6d3d7f2a"
- integrity sha512-9cUznXMG0+FxRuJfvL82QlTqIzhVW9sL0KjMPHhAOOvpQGL8QtdxnBKILjBqxlHyliz0yCa1G903ZXI/FuHy2w==
-
-"@babel/helper-simple-access@^7.22.5":
- version "7.22.5"
- resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz#4938357dc7d782b80ed6dbb03a0fba3d22b1d5de"
- integrity sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==
- dependencies:
- "@babel/types" "^7.22.5"
-
-"@babel/helper-split-export-declaration@^7.22.6":
- version "7.22.6"
- resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz#322c61b7310c0997fe4c323955667f18fcefb91c"
- integrity sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==
- dependencies:
- "@babel/types" "^7.22.5"
-
-"@babel/helper-string-parser@^7.23.4":
- version "7.23.4"
- resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz#9478c707febcbbe1ddb38a3d91a2e054ae622d83"
- integrity sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==
-
-"@babel/helper-validator-identifier@^7.22.20":
- version "7.22.20"
- resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz#c4ae002c61d2879e724581d96665583dbc1dc0e0"
- integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==
-
-"@babel/helper-validator-option@^7.23.5":
- version "7.23.5"
- resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz#907a3fbd4523426285365d1206c423c4c5520307"
- integrity sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==
-
-"@babel/helpers@^7.24.0":
- version "7.24.0"
- resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.24.0.tgz#a3dd462b41769c95db8091e49cfe019389a9409b"
- integrity sha512-ulDZdc0Aj5uLc5nETsa7EPx2L7rM0YJM8r7ck7U73AXi7qOV44IHHRAYZHY6iU1rr3C5N4NtTmMRUJP6kwCWeA==
- dependencies:
- "@babel/template" "^7.24.0"
- "@babel/traverse" "^7.24.0"
- "@babel/types" "^7.24.0"
-
-"@babel/highlight@^7.23.4":
- version "7.23.4"
- resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.23.4.tgz#edaadf4d8232e1a961432db785091207ead0621b"
- integrity sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==
- dependencies:
- "@babel/helper-validator-identifier" "^7.22.20"
+"@babel/helper-environment-visitor@^7.24.7":
+ version "7.24.7"
+ resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.24.7.tgz#4b31ba9551d1f90781ba83491dd59cf9b269f7d9"
+ integrity sha512-DoiN84+4Gnd0ncbBOM9AZENV4a5ZiL39HYMyZJGZ/AZEykHYdJw0wW3kdcsh9/Kn+BRXHLkkklZ51ecPKmI1CQ==
+ dependencies:
+ "@babel/types" "^7.24.7"
+
+"@babel/helper-function-name@^7.24.7":
+ version "7.24.7"
+ resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.24.7.tgz#75f1e1725742f39ac6584ee0b16d94513da38dd2"
+ integrity sha512-FyoJTsj/PEUWu1/TYRiXTIHc8lbw+TDYkZuoE43opPS5TrI7MyONBE1oNvfguEXAD9yhQRrVBnXdXzSLQl9XnA==
+ dependencies:
+ "@babel/template" "^7.24.7"
+ "@babel/types" "^7.24.7"
+
+"@babel/helper-hoist-variables@^7.24.7":
+ version "7.24.7"
+ resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.24.7.tgz#b4ede1cde2fd89436397f30dc9376ee06b0f25ee"
+ integrity sha512-MJJwhkoGy5c4ehfoRyrJ/owKeMl19U54h27YYftT0o2teQ3FJ3nQUf/I3LlJsX4l3qlw7WRXUmiyajvHXoTubQ==
+ dependencies:
+ "@babel/types" "^7.24.7"
+
+"@babel/helper-module-imports@^7.24.7":
+ version "7.24.7"
+ resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.24.7.tgz#f2f980392de5b84c3328fc71d38bd81bbb83042b"
+ integrity sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==
+ dependencies:
+ "@babel/traverse" "^7.24.7"
+ "@babel/types" "^7.24.7"
+
+"@babel/helper-module-transforms@^7.24.7":
+ version "7.24.7"
+ resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.24.7.tgz#31b6c9a2930679498db65b685b1698bfd6c7daf8"
+ integrity sha512-1fuJEwIrp+97rM4RWdO+qrRsZlAeL1lQJoPqtCYWv0NL115XM93hIH4CSRln2w52SqvmY5hqdtauB6QFCDiZNQ==
+ dependencies:
+ "@babel/helper-environment-visitor" "^7.24.7"
+ "@babel/helper-module-imports" "^7.24.7"
+ "@babel/helper-simple-access" "^7.24.7"
+ "@babel/helper-split-export-declaration" "^7.24.7"
+ "@babel/helper-validator-identifier" "^7.24.7"
+
+"@babel/helper-plugin-utils@^7.24.7":
+ version "7.24.7"
+ resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.7.tgz#98c84fe6fe3d0d3ae7bfc3a5e166a46844feb2a0"
+ integrity sha512-Rq76wjt7yz9AAc1KnlRKNAi/dMSVWgDRx43FHoJEbcYU6xOWaE2dVPwcdTukJrjxS65GITyfbvEYHvkirZ6uEg==
+
+"@babel/helper-simple-access@^7.24.7":
+ version "7.24.7"
+ resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.24.7.tgz#bcade8da3aec8ed16b9c4953b74e506b51b5edb3"
+ integrity sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==
+ dependencies:
+ "@babel/traverse" "^7.24.7"
+ "@babel/types" "^7.24.7"
+
+"@babel/helper-split-export-declaration@^7.24.7":
+ version "7.24.7"
+ resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.7.tgz#83949436890e07fa3d6873c61a96e3bbf692d856"
+ integrity sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==
+ dependencies:
+ "@babel/types" "^7.24.7"
+
+"@babel/helper-string-parser@^7.24.7":
+ version "7.24.7"
+ resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.24.7.tgz#4d2d0f14820ede3b9807ea5fc36dfc8cd7da07f2"
+ integrity sha512-7MbVt6xrwFQbunH2DNQsAP5sTGxfqQtErvBIvIMi6EQnbgUOuVYanvREcmFrOPhoXBrTtjhhP+lW+o5UfK+tDg==
+
+"@babel/helper-validator-identifier@^7.24.7":
+ version "7.24.7"
+ resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz#75b889cfaf9e35c2aaf42cf0d72c8e91719251db"
+ integrity sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==
+
+"@babel/helper-validator-option@^7.24.7":
+ version "7.24.7"
+ resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.24.7.tgz#24c3bb77c7a425d1742eec8fb433b5a1b38e62f6"
+ integrity sha512-yy1/KvjhV/ZCL+SM7hBrvnZJ3ZuT9OuZgIJAGpPEToANvc3iM6iDvBnRjtElWibHU6n8/LPR/EjX9EtIEYO3pw==
+
+"@babel/helpers@^7.24.7":
+ version "7.24.7"
+ resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.24.7.tgz#aa2ccda29f62185acb5d42fb4a3a1b1082107416"
+ integrity sha512-NlmJJtvcw72yRJRcnCmGvSi+3jDEg8qFu3z0AFoymmzLx5ERVWyzd9kVXr7Th9/8yIJi2Zc6av4Tqz3wFs8QWg==
+ dependencies:
+ "@babel/template" "^7.24.7"
+ "@babel/types" "^7.24.7"
+
+"@babel/highlight@^7.24.7":
+ version "7.24.7"
+ resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.24.7.tgz#a05ab1df134b286558aae0ed41e6c5f731bf409d"
+ integrity sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==
+ dependencies:
+ "@babel/helper-validator-identifier" "^7.24.7"
chalk "^2.4.2"
js-tokens "^4.0.0"
+ picocolors "^1.0.0"
-"@babel/parser@^7.1.0", "@babel/parser@^7.20.7", "@babel/parser@^7.24.0":
- version "7.24.0"
- resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.24.0.tgz#26a3d1ff49031c53a97d03b604375f028746a9ac"
- integrity sha512-QuP/FxEAzMSjXygs8v4N9dvdXzEHN4W1oF3PxuWAtPo08UdM17u89RDMgjLn/mlc56iM0HlLmVkO/wgR+rDgHg==
+"@babel/parser@^7.1.0", "@babel/parser@^7.20.7", "@babel/parser@^7.24.7":
+ version "7.24.7"
+ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.24.7.tgz#9a5226f92f0c5c8ead550b750f5608e766c8ce85"
+ integrity sha512-9uUYRm6OqQrCqQdG1iCBwBPZgN8ciDBro2nIOFaiRz1/BCxaI7CNvQbDHvsArAC7Tw9Hda/B3U+6ui9u4HWXPw==
-"@babel/plugin-transform-react-jsx-self@^7.23.3":
- version "7.23.3"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.23.3.tgz#ed3e7dadde046cce761a8e3cf003a13d1a7972d9"
- integrity sha512-qXRvbeKDSfwnlJnanVRp0SfuWE5DQhwQr5xtLBzp56Wabyo+4CMosF6Kfp+eOD/4FYpql64XVJ2W0pVLlJZxOQ==
+"@babel/plugin-transform-react-jsx-self@^7.24.5":
+ version "7.24.7"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.24.7.tgz#66bff0248ea0b549972e733516ffad577477bdab"
+ integrity sha512-fOPQYbGSgH0HUp4UJO4sMBFjY6DuWq+2i8rixyUMb3CdGixs/gccURvYOAhajBdKDoGajFr3mUq5rH3phtkGzw==
dependencies:
- "@babel/helper-plugin-utils" "^7.22.5"
+ "@babel/helper-plugin-utils" "^7.24.7"
-"@babel/plugin-transform-react-jsx-source@^7.23.3":
- version "7.23.3"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.23.3.tgz#03527006bdc8775247a78643c51d4e715fe39a3e"
- integrity sha512-91RS0MDnAWDNvGC6Wio5XYkyWI39FMFO+JK9+4AlgaTH+yWwVTsw7/sn6LK0lH7c5F+TFkpv/3LfCJ1Ydwof/g==
+"@babel/plugin-transform-react-jsx-source@^7.24.1":
+ version "7.24.7"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.24.7.tgz#1198aab2548ad19582013815c938d3ebd8291ee3"
+ integrity sha512-J2z+MWzZHVOemyLweMqngXrgGC42jQ//R0KdxqkIz/OrbVIIlhFI3WigZ5fO+nwFvBlncr4MGapd8vTyc7RPNQ==
dependencies:
- "@babel/helper-plugin-utils" "^7.22.5"
+ "@babel/helper-plugin-utils" "^7.24.7"
-"@babel/runtime@^7.13.10":
- version "7.24.0"
- resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.24.0.tgz#584c450063ffda59697021430cb47101b085951e"
- integrity sha512-Chk32uHMg6TnQdvw2e9IlqPpFX/6NLuK0Ys2PqLb7/gL5uFn9mXvK715FGLlOLQrcO4qIkNHkvPGktzzXexsFw==
+"@babel/runtime@^7.13.10", "@babel/runtime@^7.23.2", "@babel/runtime@^7.23.9", "@babel/runtime@^7.24.1":
+ version "7.24.7"
+ resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.24.7.tgz#f4f0d5530e8dbdf59b3451b9b3e594b6ba082e12"
+ integrity sha512-UwgBRMjJP+xv857DCngvqXI3Iq6J4v0wXmwc6sapg+zyhbwmQX67LUEFrkK5tbyJ30jGuG3ZvWpBiB9LCy1kWw==
dependencies:
regenerator-runtime "^0.14.0"
-"@babel/template@^7.22.15", "@babel/template@^7.24.0":
- version "7.24.0"
- resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.24.0.tgz#c6a524aa93a4a05d66aaf31654258fae69d87d50"
- integrity sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==
- dependencies:
- "@babel/code-frame" "^7.23.5"
- "@babel/parser" "^7.24.0"
- "@babel/types" "^7.24.0"
-
-"@babel/traverse@^7.24.0":
- version "7.24.0"
- resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.24.0.tgz#4a408fbf364ff73135c714a2ab46a5eab2831b1e"
- integrity sha512-HfuJlI8qq3dEDmNU5ChzzpZRWq+oxCZQyMzIMEqLho+AQnhMnKQUzH6ydo3RBl/YjPCuk68Y6s0Gx0AeyULiWw==
- dependencies:
- "@babel/code-frame" "^7.23.5"
- "@babel/generator" "^7.23.6"
- "@babel/helper-environment-visitor" "^7.22.20"
- "@babel/helper-function-name" "^7.23.0"
- "@babel/helper-hoist-variables" "^7.22.5"
- "@babel/helper-split-export-declaration" "^7.22.6"
- "@babel/parser" "^7.24.0"
- "@babel/types" "^7.24.0"
+"@babel/template@^7.24.7":
+ version "7.24.7"
+ resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.24.7.tgz#02efcee317d0609d2c07117cb70ef8fb17ab7315"
+ integrity sha512-jYqfPrU9JTF0PmPy1tLYHW4Mp4KlgxJD9l2nP9fD6yT/ICi554DmrWBAEYpIelzjHf1msDP3PxJIRt/nFNfBig==
+ dependencies:
+ "@babel/code-frame" "^7.24.7"
+ "@babel/parser" "^7.24.7"
+ "@babel/types" "^7.24.7"
+
+"@babel/traverse@^7.24.7":
+ version "7.24.7"
+ resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.24.7.tgz#de2b900163fa741721ba382163fe46a936c40cf5"
+ integrity sha512-yb65Ed5S/QAcewNPh0nZczy9JdYXkkAbIsEo+P7BE7yO3txAY30Y/oPa3QkQ5It3xVG2kpKMg9MsdxZaO31uKA==
+ dependencies:
+ "@babel/code-frame" "^7.24.7"
+ "@babel/generator" "^7.24.7"
+ "@babel/helper-environment-visitor" "^7.24.7"
+ "@babel/helper-function-name" "^7.24.7"
+ "@babel/helper-hoist-variables" "^7.24.7"
+ "@babel/helper-split-export-declaration" "^7.24.7"
+ "@babel/parser" "^7.24.7"
+ "@babel/types" "^7.24.7"
debug "^4.3.1"
globals "^11.1.0"
-"@babel/types@^7.0.0", "@babel/types@^7.20.7", "@babel/types@^7.22.15", "@babel/types@^7.22.5", "@babel/types@^7.23.0", "@babel/types@^7.23.6", "@babel/types@^7.24.0":
- version "7.24.0"
- resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.24.0.tgz#3b951f435a92e7333eba05b7566fd297960ea1bf"
- integrity sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==
+"@babel/types@^7.0.0", "@babel/types@^7.20.7", "@babel/types@^7.24.7":
+ version "7.24.7"
+ resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.24.7.tgz#6027fe12bc1aa724cd32ab113fb7f1988f1f66f2"
+ integrity sha512-XEFXSlxiG5td2EJRe8vOmRbaXVgfcBlszKujvVmWIK/UpywWljQCfzAv3RQCGujWQ1RD4YYWEAqDXfuJiy8f5Q==
dependencies:
- "@babel/helper-string-parser" "^7.23.4"
- "@babel/helper-validator-identifier" "^7.22.20"
+ "@babel/helper-string-parser" "^7.24.7"
+ "@babel/helper-validator-identifier" "^7.24.7"
to-fast-properties "^2.0.0"
"@esbuild/android-arm64@0.18.20":
@@ -335,88 +334,93 @@
resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz#786c5f41f043b07afb1af37683d7c33668858f6d"
integrity sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==
-"@eslint-community/eslint-utils@^4.2.0":
+"@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0":
version "4.4.0"
resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59"
integrity sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==
dependencies:
eslint-visitor-keys "^3.3.0"
-"@eslint-community/regexpp@^4.4.0", "@eslint-community/regexpp@^4.6.1":
- version "4.10.0"
- resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.10.0.tgz#548f6de556857c8bb73bbee70c35dc82a2e74d63"
- integrity sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==
+"@eslint-community/regexpp@^4.10.0", "@eslint-community/regexpp@^4.6.1":
+ version "4.10.1"
+ resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.10.1.tgz#361461e5cb3845d874e61731c11cfedd664d83a0"
+ integrity sha512-Zm2NGpWELsQAD1xsJzGQpYfvICSsFkEpU0jxBjfdC6uNEWXcHnfs9hScFWtXVDVl+rBQJGrl4g1vcKIejpH9dA==
-"@eslint/eslintrc@^2.1.4":
- version "2.1.4"
- resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.1.4.tgz#388a269f0f25c1b6adc317b5a2c55714894c70ad"
- integrity sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==
+"@eslint/config-array@^0.16.0":
+ version "0.16.0"
+ resolved "https://registry.yarnpkg.com/@eslint/config-array/-/config-array-0.16.0.tgz#bb3364fc39ee84ec3a62abdc4b8d988d99dfd706"
+ integrity sha512-/jmuSd74i4Czf1XXn7wGRWZCuyaUZ330NH1Bek0Pplatt4Sy1S5haN21SCLLdbeKslQ+S0wEJ+++v5YibSi+Lg==
+ dependencies:
+ "@eslint/object-schema" "^2.1.4"
+ debug "^4.3.1"
+ minimatch "^3.0.5"
+
+"@eslint/eslintrc@^3.1.0":
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-3.1.0.tgz#dbd3482bfd91efa663cbe7aa1f506839868207b6"
+ integrity sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==
dependencies:
ajv "^6.12.4"
debug "^4.3.2"
- espree "^9.6.0"
- globals "^13.19.0"
+ espree "^10.0.1"
+ globals "^14.0.0"
ignore "^5.2.0"
import-fresh "^3.2.1"
js-yaml "^4.1.0"
minimatch "^3.1.2"
strip-json-comments "^3.1.1"
-"@eslint/js@8.57.0":
- version "8.57.0"
- resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.57.0.tgz#a5417ae8427873f1dd08b70b3574b453e67b5f7f"
- integrity sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==
+"@eslint/js@9.5.0":
+ version "9.5.0"
+ resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.5.0.tgz#0e9c24a670b8a5c86bff97b40be13d8d8f238045"
+ integrity sha512-A7+AOT2ICkodvtsWnxZP4Xxk3NbZ3VMHd8oihydLRGrJgqqdEz1qSeEgXYyT/Cu8h1TWWsQRejIx48mtjZ5y1w==
+
+"@eslint/object-schema@^2.1.4":
+ version "2.1.4"
+ resolved "https://registry.yarnpkg.com/@eslint/object-schema/-/object-schema-2.1.4.tgz#9e69f8bb4031e11df79e03db09f9dbbae1740843"
+ integrity sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==
"@floating-ui/core@^1.0.0":
- version "1.6.0"
- resolved "https://registry.yarnpkg.com/@floating-ui/core/-/core-1.6.0.tgz#fa41b87812a16bf123122bf945946bae3fdf7fc1"
- integrity sha512-PcF++MykgmTj3CIyOQbKA/hDzOAiqI3mhuoN44WRCopIs1sgoDoU4oty4Jtqaj/y3oDU6fnVSm4QG0a3t5i0+g==
+ version "1.6.2"
+ resolved "https://registry.yarnpkg.com/@floating-ui/core/-/core-1.6.2.tgz#d37f3e0ac1f1c756c7de45db13303a266226851a"
+ integrity sha512-+2XpQV9LLZeanU4ZevzRnGFg2neDeKHgFLjP6YLW+tly0IvrhqT4u8enLGjLH3qeh85g19xY5rsAusfwTdn5lg==
dependencies:
- "@floating-ui/utils" "^0.2.1"
+ "@floating-ui/utils" "^0.2.0"
-"@floating-ui/dom@^1.6.1":
- version "1.6.3"
- resolved "https://registry.yarnpkg.com/@floating-ui/dom/-/dom-1.6.3.tgz#954e46c1dd3ad48e49db9ada7218b0985cee75ef"
- integrity sha512-RnDthu3mzPlQ31Ss/BTwQ1zjzIhr3lk1gZB1OC56h/1vEtaXkESrOqL5fQVMfXpwGtRwX+YsZBdyHtJMQnkArw==
+"@floating-ui/dom@^1.0.0":
+ version "1.6.5"
+ resolved "https://registry.yarnpkg.com/@floating-ui/dom/-/dom-1.6.5.tgz#323f065c003f1d3ecf0ff16d2c2c4d38979f4cb9"
+ integrity sha512-Nsdud2X65Dz+1RHjAIP0t8z5e2ff/IRbei6BqFrl1urT8sDVzM1HMQ+R0XcU5ceRfyO3I6ayeqIfh+6Wb8LGTw==
dependencies:
"@floating-ui/core" "^1.0.0"
"@floating-ui/utils" "^0.2.0"
"@floating-ui/react-dom@^2.0.0":
- version "2.0.8"
- resolved "https://registry.yarnpkg.com/@floating-ui/react-dom/-/react-dom-2.0.8.tgz#afc24f9756d1b433e1fe0d047c24bd4d9cefaa5d"
- integrity sha512-HOdqOt3R3OGeTKidaLvJKcgg75S6tibQ3Tif4eyd91QnIJWr0NLvoXFpJA/j8HqkFSL68GDca9AuyWEHlhyClw==
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/@floating-ui/react-dom/-/react-dom-2.1.0.tgz#4f0e5e9920137874b2405f7d6c862873baf4beff"
+ integrity sha512-lNzj5EQmEKn5FFKc04+zasr09h/uX8RtJRNj5gUXsSQIXHVWTVh+hVAg1vOMCexkX8EgvemMvIFpQfkosnVNyA==
dependencies:
- "@floating-ui/dom" "^1.6.1"
+ "@floating-ui/dom" "^1.0.0"
-"@floating-ui/utils@^0.2.0", "@floating-ui/utils@^0.2.1":
- version "0.2.1"
- resolved "https://registry.yarnpkg.com/@floating-ui/utils/-/utils-0.2.1.tgz#16308cea045f0fc777b6ff20a9f25474dd8293d2"
- integrity sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q==
+"@floating-ui/utils@^0.2.0":
+ version "0.2.2"
+ resolved "https://registry.yarnpkg.com/@floating-ui/utils/-/utils-0.2.2.tgz#d8bae93ac8b815b2bd7a98078cf91e2724ef11e5"
+ integrity sha512-J4yDIIthosAsRZ5CPYP/jQvUAQtlZTTD/4suA08/FEnlxqW3sKS9iAhgsa9VYLZ6vDHn/ixJgIqRQPotoBjxIw==
-"@hookform/resolvers@^3.3.4":
- version "3.3.4"
- resolved "https://registry.yarnpkg.com/@hookform/resolvers/-/resolvers-3.3.4.tgz#de9b668c2835eb06892290192de6e2a5c906229b"
- integrity sha512-o5cgpGOuJYrd+iMKvkttOclgwRW86EsWJZZRC23prf0uU2i48Htq4PuT73AVb9ionFyZrwYEITuOFGF+BydEtQ==
-
-"@humanwhocodes/config-array@^0.11.14":
- version "0.11.14"
- resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.14.tgz#d78e481a039f7566ecc9660b4ea7fe6b1fec442b"
- integrity sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==
- dependencies:
- "@humanwhocodes/object-schema" "^2.0.2"
- debug "^4.3.1"
- minimatch "^3.0.5"
+"@hookform/resolvers@^3.9.0":
+ version "3.9.0"
+ resolved "https://registry.yarnpkg.com/@hookform/resolvers/-/resolvers-3.9.0.tgz#cf540ac21c6c0cd24a40cf53d8e6d64391fb753d"
+ integrity sha512-bU0Gr4EepJ/EQsH/IwEzYLsT/PEj5C0ynLQ4m+GSHS+xKH4TfSelhluTgOaoc4kA5s7eCsQbM4wvZLzELmWzUg==
"@humanwhocodes/module-importer@^1.0.1":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c"
integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==
-"@humanwhocodes/object-schema@^2.0.2":
- version "2.0.2"
- resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz#d9fae00a2d5cb40f92cfe64b47ad749fbc38f917"
- integrity sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==
+"@humanwhocodes/retry@^0.3.0":
+ version "0.3.0"
+ resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.3.0.tgz#6d86b8cb322660f03d3f0aa94b99bdd8e172d570"
+ integrity sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew==
"@isaacs/cliui@^8.0.2":
version "8.0.2"
@@ -430,21 +434,21 @@
wrap-ansi "^8.1.0"
wrap-ansi-cjs "npm:wrap-ansi@^7.0.0"
-"@jridgewell/gen-mapping@^0.3.0", "@jridgewell/gen-mapping@^0.3.2":
- version "0.3.4"
- resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.4.tgz#9b18145d26cf33d08576cf4c7665b28554480ed7"
- integrity sha512-Oud2QPM5dHviZNn4y/WhhYKSXksv+1xLEIsNrAbGcFzUN3ubqWRFT5gwPchNc5NuzILOU4tPBDTZ4VwhL8Y7cw==
+"@jridgewell/gen-mapping@^0.3.2", "@jridgewell/gen-mapping@^0.3.5":
+ version "0.3.5"
+ resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz#dcce6aff74bdf6dad1a95802b69b04a2fcb1fb36"
+ integrity sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==
dependencies:
- "@jridgewell/set-array" "^1.0.1"
+ "@jridgewell/set-array" "^1.2.1"
"@jridgewell/sourcemap-codec" "^1.4.10"
- "@jridgewell/trace-mapping" "^0.3.9"
+ "@jridgewell/trace-mapping" "^0.3.24"
"@jridgewell/resolve-uri@^3.1.0":
version "3.1.2"
resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6"
integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==
-"@jridgewell/set-array@^1.0.1":
+"@jridgewell/set-array@^1.2.1":
version "1.2.1"
resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.2.1.tgz#558fb6472ed16a4c850b889530e6b36438c49280"
integrity sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==
@@ -454,10 +458,10 @@
resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32"
integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==
-"@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.9":
- version "0.3.23"
- resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.23.tgz#afc96847f3f07841477f303eed687707a5aacd80"
- integrity sha512-9/4foRoUKp8s96tSkh8DlAAc5A0Ty8vLXld+l9gjKKY6ckwI8G15f0hskGmuLZu78ZlGa1vtsfOa+lnB4vG6Jg==
+"@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25":
+ version "0.3.25"
+ resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0"
+ integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==
dependencies:
"@jridgewell/resolve-uri" "^3.1.0"
"@jridgewell/sourcemap-codec" "^1.4.14"
@@ -488,12 +492,10 @@
resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33"
integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==
-"@radix-ui/primitive@1.0.0":
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/@radix-ui/primitive/-/primitive-1.0.0.tgz#e1d8ef30b10ea10e69c76e896f608d9276352253"
- integrity sha512-3e7rn8FDMin4CgeL7Z/49smCA3rFYY3Ha2rUQ7HRWFadS5iCRw08ZgVT1LaNTCNqgvrUiyczLflrVrF0SRQtNA==
- dependencies:
- "@babel/runtime" "^7.13.10"
+"@radix-ui/number@1.1.0":
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/@radix-ui/number/-/number-1.1.0.tgz#1e95610461a09cdf8bb05c152e76ca1278d5da46"
+ integrity sha512-V3gRzhVNU1ldS5XhAPTom1fOIo4ccrjjJgmE+LI2h/WaFpHmx0MQApT+KZHnx8abG6Avtfcz4WoEciMnpFT3HQ==
"@radix-ui/primitive@1.0.1":
version "1.0.1"
@@ -502,62 +504,70 @@
dependencies:
"@babel/runtime" "^7.13.10"
-"@radix-ui/react-accordion@^1.1.2":
- version "1.1.2"
- resolved "https://registry.yarnpkg.com/@radix-ui/react-accordion/-/react-accordion-1.1.2.tgz#738441f7343e5142273cdef94d12054c3287966f"
- integrity sha512-fDG7jcoNKVjSK6yfmuAs0EnPDro0WMXIhMtXdTBWqEioVW206ku+4Lw07e+13lUkFkpoEQ2PdeMIAGpdqEAmDg==
- dependencies:
- "@babel/runtime" "^7.13.10"
- "@radix-ui/primitive" "1.0.1"
- "@radix-ui/react-collapsible" "1.0.3"
- "@radix-ui/react-collection" "1.0.3"
- "@radix-ui/react-compose-refs" "1.0.1"
- "@radix-ui/react-context" "1.0.1"
- "@radix-ui/react-direction" "1.0.1"
- "@radix-ui/react-id" "1.0.1"
- "@radix-ui/react-primitive" "1.0.3"
- "@radix-ui/react-use-controllable-state" "1.0.1"
-
-"@radix-ui/react-arrow@1.0.3":
- version "1.0.3"
- resolved "https://registry.yarnpkg.com/@radix-ui/react-arrow/-/react-arrow-1.0.3.tgz#c24f7968996ed934d57fe6cde5d6ec7266e1d25d"
- integrity sha512-wSP+pHsB/jQRaL6voubsQ/ZlrGBHHrOjmBnr19hxYgtS0WvAFwZhK2WP/YY5yF9uKECCEEDGxuLxq1NBK51wFA==
- dependencies:
- "@babel/runtime" "^7.13.10"
- "@radix-ui/react-primitive" "1.0.3"
-
-"@radix-ui/react-collapsible@1.0.3":
- version "1.0.3"
- resolved "https://registry.yarnpkg.com/@radix-ui/react-collapsible/-/react-collapsible-1.0.3.tgz#df0e22e7a025439f13f62d4e4a9e92c4a0df5b81"
- integrity sha512-UBmVDkmR6IvDsloHVN+3rtx4Mi5TFvylYXpluuv0f37dtaz3H99bp8No0LGXRigVpl3UAT4l9j6bIchh42S/Gg==
- dependencies:
- "@babel/runtime" "^7.13.10"
- "@radix-ui/primitive" "1.0.1"
- "@radix-ui/react-compose-refs" "1.0.1"
- "@radix-ui/react-context" "1.0.1"
- "@radix-ui/react-id" "1.0.1"
- "@radix-ui/react-presence" "1.0.1"
- "@radix-ui/react-primitive" "1.0.3"
- "@radix-ui/react-use-controllable-state" "1.0.1"
- "@radix-ui/react-use-layout-effect" "1.0.1"
-
-"@radix-ui/react-collection@1.0.3":
- version "1.0.3"
- resolved "https://registry.yarnpkg.com/@radix-ui/react-collection/-/react-collection-1.0.3.tgz#9595a66e09026187524a36c6e7e9c7d286469159"
- integrity sha512-3SzW+0PW7yBBoQlT8wNcGtaxaD0XSu0uLUFgrtHY08Acx05TaHaOmVLR73c0j/cqpDy53KBMO7s0dx2wmOIDIA==
- dependencies:
- "@babel/runtime" "^7.13.10"
- "@radix-ui/react-compose-refs" "1.0.1"
- "@radix-ui/react-context" "1.0.1"
- "@radix-ui/react-primitive" "1.0.3"
- "@radix-ui/react-slot" "1.0.2"
+"@radix-ui/primitive@1.1.0":
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/@radix-ui/primitive/-/primitive-1.1.0.tgz#42ef83b3b56dccad5d703ae8c42919a68798bbe2"
+ integrity sha512-4Z8dn6Upk0qk4P74xBhZ6Hd/w0mPEzOOLxy4xiPXOXqjF7jZS0VAKk7/x/H6FyY2zCkYJqePf1G5KmkmNJ4RBA==
-"@radix-ui/react-compose-refs@1.0.0":
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/@radix-ui/react-compose-refs/-/react-compose-refs-1.0.0.tgz#37595b1f16ec7f228d698590e78eeed18ff218ae"
- integrity sha512-0KaSv6sx787/hK3eF53iOkiSLwAGlFMx5lotrqD2pTjB18KbybKoEIgkNZTKC60YECDQTKGTRcDBILwZVqVKvA==
+"@radix-ui/react-accordion@^1.2.0":
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-accordion/-/react-accordion-1.2.0.tgz#aed0770fcb16285db992d81873ccd7a014c7f17d"
+ integrity sha512-HJOzSX8dQqtsp/3jVxCU3CXEONF7/2jlGAB28oX8TTw1Dz8JYbEI1UcL8355PuLBE41/IRRMvCw7VkiK/jcUOQ==
+ dependencies:
+ "@radix-ui/primitive" "1.1.0"
+ "@radix-ui/react-collapsible" "1.1.0"
+ "@radix-ui/react-collection" "1.1.0"
+ "@radix-ui/react-compose-refs" "1.1.0"
+ "@radix-ui/react-context" "1.1.0"
+ "@radix-ui/react-direction" "1.1.0"
+ "@radix-ui/react-id" "1.1.0"
+ "@radix-ui/react-primitive" "2.0.0"
+ "@radix-ui/react-use-controllable-state" "1.1.0"
+
+"@radix-ui/react-arrow@1.1.0":
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-arrow/-/react-arrow-1.1.0.tgz#744f388182d360b86285217e43b6c63633f39e7a"
+ integrity sha512-FmlW1rCg7hBpEBwFbjHwCW6AmWLQM6g/v0Sn8XbP9NvmSZ2San1FpQeyPtufzOMSIx7Y4dzjlHoifhp+7NkZhw==
+ dependencies:
+ "@radix-ui/react-primitive" "2.0.0"
+
+"@radix-ui/react-checkbox@^1.1.1":
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-checkbox/-/react-checkbox-1.1.1.tgz#a559c4303957d797acee99914480b755aa1f27d6"
+ integrity sha512-0i/EKJ222Afa1FE0C6pNJxDq1itzcl3HChE9DwskA4th4KRse8ojx8a1nVcOjwJdbpDLcz7uol77yYnQNMHdKw==
+ dependencies:
+ "@radix-ui/primitive" "1.1.0"
+ "@radix-ui/react-compose-refs" "1.1.0"
+ "@radix-ui/react-context" "1.1.0"
+ "@radix-ui/react-presence" "1.1.0"
+ "@radix-ui/react-primitive" "2.0.0"
+ "@radix-ui/react-use-controllable-state" "1.1.0"
+ "@radix-ui/react-use-previous" "1.1.0"
+ "@radix-ui/react-use-size" "1.1.0"
+
+"@radix-ui/react-collapsible@1.1.0":
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-collapsible/-/react-collapsible-1.1.0.tgz#4d49ddcc7b7d38f6c82f1fd29674f6fab5353e77"
+ integrity sha512-zQY7Epa8sTL0mq4ajSJpjgn2YmCgyrG7RsQgLp3C0LQVkG7+Tf6Pv1CeNWZLyqMjhdPkBa5Lx7wYBeSu7uCSTA==
+ dependencies:
+ "@radix-ui/primitive" "1.1.0"
+ "@radix-ui/react-compose-refs" "1.1.0"
+ "@radix-ui/react-context" "1.1.0"
+ "@radix-ui/react-id" "1.1.0"
+ "@radix-ui/react-presence" "1.1.0"
+ "@radix-ui/react-primitive" "2.0.0"
+ "@radix-ui/react-use-controllable-state" "1.1.0"
+ "@radix-ui/react-use-layout-effect" "1.1.0"
+
+"@radix-ui/react-collection@1.1.0":
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-collection/-/react-collection-1.1.0.tgz#f18af78e46454a2360d103c2251773028b7724ed"
+ integrity sha512-GZsZslMJEyo1VKm5L1ZJY8tGDxZNPAoUeQUIbKeJfoi7Q4kmig5AsgLMYYuyYbfjd8fBmFORAIwYAkXMnXZgZw==
dependencies:
- "@babel/runtime" "^7.13.10"
+ "@radix-ui/react-compose-refs" "1.1.0"
+ "@radix-ui/react-context" "1.1.0"
+ "@radix-ui/react-primitive" "2.0.0"
+ "@radix-ui/react-slot" "1.1.0"
"@radix-ui/react-compose-refs@1.0.1":
version "1.0.1"
@@ -566,12 +576,10 @@
dependencies:
"@babel/runtime" "^7.13.10"
-"@radix-ui/react-context@1.0.0":
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/@radix-ui/react-context/-/react-context-1.0.0.tgz#f38e30c5859a9fb5e9aa9a9da452ee3ed9e0aee0"
- integrity sha512-1pVM9RfOQ+n/N5PJK33kRSKsr1glNxomxONs5c49MliinBY6Yw2Q995qfBUUo0/Mbg05B/sGA0gkgPI7kmSHBg==
- dependencies:
- "@babel/runtime" "^7.13.10"
+"@radix-ui/react-compose-refs@1.1.0":
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.0.tgz#656432461fc8283d7b591dcf0d79152fae9ecc74"
+ integrity sha512-b4inOtiaOnYf9KWyO3jAeeCG6FeyfY6ldiEPanbUjWd+xIk5wZeHa8yVwmrJ2vderhu/BQvzCrJI0lHd+wIiqw==
"@radix-ui/react-context@1.0.1":
version "1.0.1"
@@ -580,28 +588,12 @@
dependencies:
"@babel/runtime" "^7.13.10"
-"@radix-ui/react-dialog@1.0.0":
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/@radix-ui/react-dialog/-/react-dialog-1.0.0.tgz#997e97cb183bc90bd888b26b8e23a355ac9fe5f0"
- integrity sha512-Yn9YU+QlHYLWwV1XfKiqnGVpWYWk6MeBVM6x/bcoyPvxgjQGoeT35482viLPctTMWoMw0PoHgqfSox7Ig+957Q==
- dependencies:
- "@babel/runtime" "^7.13.10"
- "@radix-ui/primitive" "1.0.0"
- "@radix-ui/react-compose-refs" "1.0.0"
- "@radix-ui/react-context" "1.0.0"
- "@radix-ui/react-dismissable-layer" "1.0.0"
- "@radix-ui/react-focus-guards" "1.0.0"
- "@radix-ui/react-focus-scope" "1.0.0"
- "@radix-ui/react-id" "1.0.0"
- "@radix-ui/react-portal" "1.0.0"
- "@radix-ui/react-presence" "1.0.0"
- "@radix-ui/react-primitive" "1.0.0"
- "@radix-ui/react-slot" "1.0.0"
- "@radix-ui/react-use-controllable-state" "1.0.0"
- aria-hidden "^1.1.1"
- react-remove-scroll "2.5.4"
+"@radix-ui/react-context@1.1.0":
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-context/-/react-context-1.1.0.tgz#6df8d983546cfd1999c8512f3a8ad85a6e7fcee8"
+ integrity sha512-OKrckBy+sMEgYM/sMmqmErVn0kZqrHPJze+Ql3DzYsDDp0hl0L62nx/2122/Bvps1qz645jlcu2tD9lrRSdf8A==
-"@radix-ui/react-dialog@^1.0.5":
+"@radix-ui/react-dialog@1.0.5":
version "1.0.5"
resolved "https://registry.yarnpkg.com/@radix-ui/react-dialog/-/react-dialog-1.0.5.tgz#71657b1b116de6c7a0b03242d7d43e01062c7300"
integrity sha512-GjWJX/AUpB703eEBanuBnIWdIXg6NvJFCXcNlSZk4xdszCdhrJgBoUd1cGk67vFO+WdA2pfI/plOpqz/5GUP6Q==
@@ -622,24 +614,30 @@
aria-hidden "^1.1.1"
react-remove-scroll "2.5.5"
-"@radix-ui/react-direction@1.0.1":
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/@radix-ui/react-direction/-/react-direction-1.0.1.tgz#9cb61bf2ccf568f3421422d182637b7f47596c9b"
- integrity sha512-RXcvnXgyvYvBEOhCBuddKecVkoMiI10Jcm5cTI7abJRAHYfFxeu+FBQs/DvdxSYucxR5mna0dNsL6QFlds5TMA==
- dependencies:
- "@babel/runtime" "^7.13.10"
+"@radix-ui/react-dialog@^1.1.1":
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-dialog/-/react-dialog-1.1.1.tgz#4906507f7b4ad31e22d7dad69d9330c87c431d44"
+ integrity sha512-zysS+iU4YP3STKNS6USvFVqI4qqx8EpiwmT5TuCApVEBca+eRCbONi4EgzfNSuVnOXvC5UPHHMjs8RXO6DH9Bg==
+ dependencies:
+ "@radix-ui/primitive" "1.1.0"
+ "@radix-ui/react-compose-refs" "1.1.0"
+ "@radix-ui/react-context" "1.1.0"
+ "@radix-ui/react-dismissable-layer" "1.1.0"
+ "@radix-ui/react-focus-guards" "1.1.0"
+ "@radix-ui/react-focus-scope" "1.1.0"
+ "@radix-ui/react-id" "1.1.0"
+ "@radix-ui/react-portal" "1.1.1"
+ "@radix-ui/react-presence" "1.1.0"
+ "@radix-ui/react-primitive" "2.0.0"
+ "@radix-ui/react-slot" "1.1.0"
+ "@radix-ui/react-use-controllable-state" "1.1.0"
+ aria-hidden "^1.1.1"
+ react-remove-scroll "2.5.7"
-"@radix-ui/react-dismissable-layer@1.0.0":
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.0.0.tgz#35b7826fa262fd84370faef310e627161dffa76b"
- integrity sha512-n7kDRfx+LB1zLueRDvZ1Pd0bxdJWDUZNQ/GWoxDn2prnuJKRdxsjulejX/ePkOsLi2tTm6P24mDqlMSgQpsT6g==
- dependencies:
- "@babel/runtime" "^7.13.10"
- "@radix-ui/primitive" "1.0.0"
- "@radix-ui/react-compose-refs" "1.0.0"
- "@radix-ui/react-primitive" "1.0.0"
- "@radix-ui/react-use-callback-ref" "1.0.0"
- "@radix-ui/react-use-escape-keydown" "1.0.0"
+"@radix-ui/react-direction@1.1.0":
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-direction/-/react-direction-1.1.0.tgz#a7d39855f4d077adc2a1922f9c353c5977a09cdc"
+ integrity sha512-BUuBvgThEiAXh2DWu93XsT+a3aWrGqolGlqqw5VU1kG7p/ZH2cuDlM1sRLNnY3QcBS69UIz2mcKhMxDsdewhjg==
"@radix-ui/react-dismissable-layer@1.0.5":
version "1.0.5"
@@ -653,26 +651,16 @@
"@radix-ui/react-use-callback-ref" "1.0.1"
"@radix-ui/react-use-escape-keydown" "1.0.3"
-"@radix-ui/react-dropdown-menu@^2.0.6":
- version "2.0.6"
- resolved "https://registry.yarnpkg.com/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.0.6.tgz#cdf13c956c5e263afe4e5f3587b3071a25755b63"
- integrity sha512-i6TuFOoWmLWq+M/eCLGd/bQ2HfAX1RJgvrBQ6AQLmzfvsLdefxbWu8G9zczcPFfcSPehz9GcpF6K9QYreFV8hA==
- dependencies:
- "@babel/runtime" "^7.13.10"
- "@radix-ui/primitive" "1.0.1"
- "@radix-ui/react-compose-refs" "1.0.1"
- "@radix-ui/react-context" "1.0.1"
- "@radix-ui/react-id" "1.0.1"
- "@radix-ui/react-menu" "2.0.6"
- "@radix-ui/react-primitive" "1.0.3"
- "@radix-ui/react-use-controllable-state" "1.0.1"
-
-"@radix-ui/react-focus-guards@1.0.0":
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/@radix-ui/react-focus-guards/-/react-focus-guards-1.0.0.tgz#339c1c69c41628c1a5e655f15f7020bf11aa01fa"
- integrity sha512-UagjDk4ijOAnGu4WMUPj9ahi7/zJJqNZ9ZAiGPp7waUWJO0O1aWXi/udPphI0IUjvrhBsZJGSN66dR2dsueLWQ==
+"@radix-ui/react-dismissable-layer@1.1.0":
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.0.tgz#2cd0a49a732372513733754e6032d3fb7988834e"
+ integrity sha512-/UovfmmXGptwGcBQawLzvn2jOfM0t4z3/uKffoBlj724+n3FvBbZ7M0aaBOmkp6pqFYpO4yx8tSVJjx3Fl2jig==
dependencies:
- "@babel/runtime" "^7.13.10"
+ "@radix-ui/primitive" "1.1.0"
+ "@radix-ui/react-compose-refs" "1.1.0"
+ "@radix-ui/react-primitive" "2.0.0"
+ "@radix-ui/react-use-callback-ref" "1.1.0"
+ "@radix-ui/react-use-escape-keydown" "1.1.0"
"@radix-ui/react-focus-guards@1.0.1":
version "1.0.1"
@@ -681,15 +669,10 @@
dependencies:
"@babel/runtime" "^7.13.10"
-"@radix-ui/react-focus-scope@1.0.0":
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/@radix-ui/react-focus-scope/-/react-focus-scope-1.0.0.tgz#95a0c1188276dc8933b1eac5f1cdb6471e01ade5"
- integrity sha512-C4SWtsULLGf/2L4oGeIHlvWQx7Rf+7cX/vKOAD2dXW0A1b5QXwi3wWeaEgW+wn+SEVrraMUk05vLU9fZZz5HbQ==
- dependencies:
- "@babel/runtime" "^7.13.10"
- "@radix-ui/react-compose-refs" "1.0.0"
- "@radix-ui/react-primitive" "1.0.0"
- "@radix-ui/react-use-callback-ref" "1.0.0"
+"@radix-ui/react-focus-guards@1.1.0":
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.0.tgz#8e9abb472a9a394f59a1b45f3dd26cfe3fc6da13"
+ integrity sha512-w6XZNUPVv6xCpZUqb/yN9DL6auvpGX3C/ee6Hdi16v2UUy25HV2Q5bcflsiDyT/g5RwbPQ/GIT1vLkeRb+ITBw==
"@radix-ui/react-focus-scope@1.0.4":
version "1.0.4"
@@ -701,13 +684,29 @@
"@radix-ui/react-primitive" "1.0.3"
"@radix-ui/react-use-callback-ref" "1.0.1"
-"@radix-ui/react-id@1.0.0":
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/@radix-ui/react-id/-/react-id-1.0.0.tgz#8d43224910741870a45a8c9d092f25887bb6d11e"
- integrity sha512-Q6iAB/U7Tq3NTolBBQbHTgclPmGWE3OlktGGqrClPozSw4vkQ1DfQAOtzgRPecKsMdJINE05iaoDUG8tRzCBjw==
- dependencies:
- "@babel/runtime" "^7.13.10"
- "@radix-ui/react-use-layout-effect" "1.0.0"
+"@radix-ui/react-focus-scope@1.1.0":
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.0.tgz#ebe2891a298e0a33ad34daab2aad8dea31caf0b2"
+ integrity sha512-200UD8zylvEyL8Bx+z76RJnASR2gRMuxlgFCPAe/Q/679a/r0eK3MBVYMb7vZODZcffZBdob1EGnky78xmVvcA==
+ dependencies:
+ "@radix-ui/react-compose-refs" "1.1.0"
+ "@radix-ui/react-primitive" "2.0.0"
+ "@radix-ui/react-use-callback-ref" "1.1.0"
+
+"@radix-ui/react-hover-card@^1.1.1":
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-hover-card/-/react-hover-card-1.1.1.tgz#2982a5a91c7ae5a98e0cacd845fbdfbfdcdab355"
+ integrity sha512-IwzAOP97hQpDADYVKrEEHUH/b2LA+9MgB0LgdmnbFO2u/3M5hmEofjjr2M6CyzUblaAqJdFm6B7oFtU72DPXrA==
+ dependencies:
+ "@radix-ui/primitive" "1.1.0"
+ "@radix-ui/react-compose-refs" "1.1.0"
+ "@radix-ui/react-context" "1.1.0"
+ "@radix-ui/react-dismissable-layer" "1.1.0"
+ "@radix-ui/react-popper" "1.2.0"
+ "@radix-ui/react-portal" "1.1.1"
+ "@radix-ui/react-presence" "1.1.0"
+ "@radix-ui/react-primitive" "2.0.0"
+ "@radix-ui/react-use-controllable-state" "1.1.0"
"@radix-ui/react-id@1.0.1":
version "1.0.1"
@@ -717,85 +716,56 @@
"@babel/runtime" "^7.13.10"
"@radix-ui/react-use-layout-effect" "1.0.1"
-"@radix-ui/react-label@^2.0.2":
- version "2.0.2"
- resolved "https://registry.yarnpkg.com/@radix-ui/react-label/-/react-label-2.0.2.tgz#9c72f1d334aac996fdc27b48a8bdddd82108fb6d"
- integrity sha512-N5ehvlM7qoTLx7nWPodsPYPgMzA5WM8zZChQg8nyFJKnDO5WHdba1vv5/H6IO5LtJMfD2Q3wh1qHFGNtK0w3bQ==
+"@radix-ui/react-id@1.1.0":
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-id/-/react-id-1.1.0.tgz#de47339656594ad722eb87f94a6b25f9cffae0ed"
+ integrity sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA==
dependencies:
- "@babel/runtime" "^7.13.10"
- "@radix-ui/react-primitive" "1.0.3"
+ "@radix-ui/react-use-layout-effect" "1.1.0"
-"@radix-ui/react-menu@2.0.6":
- version "2.0.6"
- resolved "https://registry.yarnpkg.com/@radix-ui/react-menu/-/react-menu-2.0.6.tgz#2c9e093c1a5d5daa87304b2a2f884e32288ae79e"
- integrity sha512-BVkFLS+bUC8HcImkRKPSiVumA1VPOOEC5WBMiT+QAVsPzW1FJzI9KnqgGxVDPBcql5xXrHkD3JOVoXWEXD8SYA==
- dependencies:
- "@babel/runtime" "^7.13.10"
- "@radix-ui/primitive" "1.0.1"
- "@radix-ui/react-collection" "1.0.3"
- "@radix-ui/react-compose-refs" "1.0.1"
- "@radix-ui/react-context" "1.0.1"
- "@radix-ui/react-direction" "1.0.1"
- "@radix-ui/react-dismissable-layer" "1.0.5"
- "@radix-ui/react-focus-guards" "1.0.1"
- "@radix-ui/react-focus-scope" "1.0.4"
- "@radix-ui/react-id" "1.0.1"
- "@radix-ui/react-popper" "1.1.3"
- "@radix-ui/react-portal" "1.0.4"
- "@radix-ui/react-presence" "1.0.1"
- "@radix-ui/react-primitive" "1.0.3"
- "@radix-ui/react-roving-focus" "1.0.4"
- "@radix-ui/react-slot" "1.0.2"
- "@radix-ui/react-use-callback-ref" "1.0.1"
- aria-hidden "^1.1.1"
- react-remove-scroll "2.5.5"
-
-"@radix-ui/react-popover@^1.0.7":
- version "1.0.7"
- resolved "https://registry.yarnpkg.com/@radix-ui/react-popover/-/react-popover-1.0.7.tgz#23eb7e3327330cb75ec7b4092d685398c1654e3c"
- integrity sha512-shtvVnlsxT6faMnK/a7n0wptwBD23xc1Z5mdrtKLwVEfsEMXodS0r5s0/g5P0hX//EKYZS2sxUjqfzlg52ZSnQ==
- dependencies:
- "@babel/runtime" "^7.13.10"
- "@radix-ui/primitive" "1.0.1"
- "@radix-ui/react-compose-refs" "1.0.1"
- "@radix-ui/react-context" "1.0.1"
- "@radix-ui/react-dismissable-layer" "1.0.5"
- "@radix-ui/react-focus-guards" "1.0.1"
- "@radix-ui/react-focus-scope" "1.0.4"
- "@radix-ui/react-id" "1.0.1"
- "@radix-ui/react-popper" "1.1.3"
- "@radix-ui/react-portal" "1.0.4"
- "@radix-ui/react-presence" "1.0.1"
- "@radix-ui/react-primitive" "1.0.3"
- "@radix-ui/react-slot" "1.0.2"
- "@radix-ui/react-use-controllable-state" "1.0.1"
+"@radix-ui/react-label@^2.1.0":
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-label/-/react-label-2.1.0.tgz#3aa2418d70bb242be37c51ff5e51a2adcbc372e3"
+ integrity sha512-peLblDlFw/ngk3UWq0VnYaOLy6agTZZ+MUO/WhVfm14vJGML+xH4FAl2XQGLqdefjNb7ApRg6Yn7U42ZhmYXdw==
+ dependencies:
+ "@radix-ui/react-primitive" "2.0.0"
+
+"@radix-ui/react-popover@^1.1.1":
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-popover/-/react-popover-1.1.1.tgz#604b783cdb3494ed4f16a58c17f0e81e61ab7775"
+ integrity sha512-3y1A3isulwnWhvTTwmIreiB8CF4L+qRjZnK1wYLO7pplddzXKby/GnZ2M7OZY3qgnl6p9AodUIHRYGXNah8Y7g==
+ dependencies:
+ "@radix-ui/primitive" "1.1.0"
+ "@radix-ui/react-compose-refs" "1.1.0"
+ "@radix-ui/react-context" "1.1.0"
+ "@radix-ui/react-dismissable-layer" "1.1.0"
+ "@radix-ui/react-focus-guards" "1.1.0"
+ "@radix-ui/react-focus-scope" "1.1.0"
+ "@radix-ui/react-id" "1.1.0"
+ "@radix-ui/react-popper" "1.2.0"
+ "@radix-ui/react-portal" "1.1.1"
+ "@radix-ui/react-presence" "1.1.0"
+ "@radix-ui/react-primitive" "2.0.0"
+ "@radix-ui/react-slot" "1.1.0"
+ "@radix-ui/react-use-controllable-state" "1.1.0"
aria-hidden "^1.1.1"
- react-remove-scroll "2.5.5"
+ react-remove-scroll "2.5.7"
-"@radix-ui/react-popper@1.1.3":
- version "1.1.3"
- resolved "https://registry.yarnpkg.com/@radix-ui/react-popper/-/react-popper-1.1.3.tgz#24c03f527e7ac348fabf18c89795d85d21b00b42"
- integrity sha512-cKpopj/5RHZWjrbF2846jBNacjQVwkP068DfmgrNJXpvVWrOvlAmE9xSiy5OqeE+Gi8D9fP+oDhUnPqNMY8/5w==
+"@radix-ui/react-popper@1.2.0":
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-popper/-/react-popper-1.2.0.tgz#a3e500193d144fe2d8f5d5e60e393d64111f2a7a"
+ integrity sha512-ZnRMshKF43aBxVWPWvbj21+7TQCvhuULWJ4gNIKYpRlQt5xGRhLx66tMp8pya2UkGHTSlhpXwmjqltDYHhw7Vg==
dependencies:
- "@babel/runtime" "^7.13.10"
"@floating-ui/react-dom" "^2.0.0"
- "@radix-ui/react-arrow" "1.0.3"
- "@radix-ui/react-compose-refs" "1.0.1"
- "@radix-ui/react-context" "1.0.1"
- "@radix-ui/react-primitive" "1.0.3"
- "@radix-ui/react-use-callback-ref" "1.0.1"
- "@radix-ui/react-use-layout-effect" "1.0.1"
- "@radix-ui/react-use-rect" "1.0.1"
- "@radix-ui/react-use-size" "1.0.1"
- "@radix-ui/rect" "1.0.1"
-
-"@radix-ui/react-portal@1.0.0":
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/@radix-ui/react-portal/-/react-portal-1.0.0.tgz#7220b66743394fabb50c55cb32381395cc4a276b"
- integrity sha512-a8qyFO/Xb99d8wQdu4o7qnigNjTPG123uADNecz0eX4usnQEj7o+cG4ZX4zkqq98NYekT7UoEQIjxBNWIFuqTA==
- dependencies:
- "@babel/runtime" "^7.13.10"
- "@radix-ui/react-primitive" "1.0.0"
+ "@radix-ui/react-arrow" "1.1.0"
+ "@radix-ui/react-compose-refs" "1.1.0"
+ "@radix-ui/react-context" "1.1.0"
+ "@radix-ui/react-primitive" "2.0.0"
+ "@radix-ui/react-use-callback-ref" "1.1.0"
+ "@radix-ui/react-use-layout-effect" "1.1.0"
+ "@radix-ui/react-use-rect" "1.1.0"
+ "@radix-ui/react-use-size" "1.1.0"
+ "@radix-ui/rect" "1.1.0"
"@radix-ui/react-portal@1.0.4":
version "1.0.4"
@@ -805,14 +775,13 @@
"@babel/runtime" "^7.13.10"
"@radix-ui/react-primitive" "1.0.3"
-"@radix-ui/react-presence@1.0.0":
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/@radix-ui/react-presence/-/react-presence-1.0.0.tgz#814fe46df11f9a468808a6010e3f3ca7e0b2e84a"
- integrity sha512-A+6XEvN01NfVWiKu38ybawfHsBjWum42MRPnEuqPsBZ4eV7e/7K321B5VgYMPv3Xx5An6o1/l9ZuDBgmcmWK3w==
+"@radix-ui/react-portal@1.1.1":
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-portal/-/react-portal-1.1.1.tgz#1957f1eb2e1aedfb4a5475bd6867d67b50b1d15f"
+ integrity sha512-A3UtLk85UtqhzFqtoC8Q0KvR2GbXF3mtPgACSazajqq6A41mEQgo53iPzY4i6BwDxlIFqWIhiQ2G729n+2aw/g==
dependencies:
- "@babel/runtime" "^7.13.10"
- "@radix-ui/react-compose-refs" "1.0.0"
- "@radix-ui/react-use-layout-effect" "1.0.0"
+ "@radix-ui/react-primitive" "2.0.0"
+ "@radix-ui/react-use-layout-effect" "1.1.0"
"@radix-ui/react-presence@1.0.1":
version "1.0.1"
@@ -823,13 +792,13 @@
"@radix-ui/react-compose-refs" "1.0.1"
"@radix-ui/react-use-layout-effect" "1.0.1"
-"@radix-ui/react-primitive@1.0.0":
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/@radix-ui/react-primitive/-/react-primitive-1.0.0.tgz#376cd72b0fcd5e0e04d252ed33eb1b1f025af2b0"
- integrity sha512-EyXe6mnRlHZ8b6f4ilTDrXmkLShICIuOTTj0GX4w1rp+wSxf3+TD05u1UOITC8VsJ2a9nwHvdXtOXEOl0Cw/zQ==
+"@radix-ui/react-presence@1.1.0":
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-presence/-/react-presence-1.1.0.tgz#227d84d20ca6bfe7da97104b1a8b48a833bfb478"
+ integrity sha512-Gq6wuRN/asf9H/E/VzdKoUtT8GC9PQc9z40/vEr0VCJ4u5XvvhWIrSsCB6vD2/cH7ugTdSfYq9fLJCcM00acrQ==
dependencies:
- "@babel/runtime" "^7.13.10"
- "@radix-ui/react-slot" "1.0.0"
+ "@radix-ui/react-compose-refs" "1.1.0"
+ "@radix-ui/react-use-layout-effect" "1.1.0"
"@radix-ui/react-primitive@1.0.3":
version "1.0.3"
@@ -839,31 +808,54 @@
"@babel/runtime" "^7.13.10"
"@radix-ui/react-slot" "1.0.2"
-"@radix-ui/react-roving-focus@1.0.4":
- version "1.0.4"
- resolved "https://registry.yarnpkg.com/@radix-ui/react-roving-focus/-/react-roving-focus-1.0.4.tgz#e90c4a6a5f6ac09d3b8c1f5b5e81aab2f0db1974"
- integrity sha512-2mUg5Mgcu001VkGy+FfzZyzbmuUWzgWkj3rvv4yu+mLw03+mTzbxZHvfcGyFp2b8EkQeMkpRQ5FiA2Vr2O6TeQ==
+"@radix-ui/react-primitive@2.0.0":
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-primitive/-/react-primitive-2.0.0.tgz#fe05715faa9203a223ccc0be15dc44b9f9822884"
+ integrity sha512-ZSpFm0/uHa8zTvKBDjLFWLo8dkr4MBsiDLz0g3gMUwqgLHz9rTaRRGYDgvZPtBJgYCBKXkS9fzmoySgr8CO6Cw==
dependencies:
- "@babel/runtime" "^7.13.10"
- "@radix-ui/primitive" "1.0.1"
- "@radix-ui/react-collection" "1.0.3"
- "@radix-ui/react-compose-refs" "1.0.1"
- "@radix-ui/react-context" "1.0.1"
- "@radix-ui/react-direction" "1.0.1"
- "@radix-ui/react-id" "1.0.1"
- "@radix-ui/react-primitive" "1.0.3"
- "@radix-ui/react-use-callback-ref" "1.0.1"
- "@radix-ui/react-use-controllable-state" "1.0.1"
+ "@radix-ui/react-slot" "1.1.0"
-"@radix-ui/react-slot@1.0.0":
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/@radix-ui/react-slot/-/react-slot-1.0.0.tgz#7fa805b99891dea1e862d8f8fbe07f4d6d0fd698"
- integrity sha512-3mrKauI/tWXo1Ll+gN5dHcxDPdm/Df1ufcDLCecn+pnCIVcdWE7CujXo8QaXOWRJyZyQWWbpB8eFwHzWXlv5mQ==
+"@radix-ui/react-progress@^1.1.0":
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-progress/-/react-progress-1.1.0.tgz#28c267885ec154fc557ec7a66cb462787312f7e2"
+ integrity sha512-aSzvnYpP725CROcxAOEBVZZSIQVQdHgBr2QQFKySsaD14u8dNT0batuXI+AAGDdAHfXH8rbnHmjYFqVJ21KkRg==
dependencies:
- "@babel/runtime" "^7.13.10"
- "@radix-ui/react-compose-refs" "1.0.0"
+ "@radix-ui/react-context" "1.1.0"
+ "@radix-ui/react-primitive" "2.0.0"
-"@radix-ui/react-slot@1.0.2", "@radix-ui/react-slot@^1.0.2":
+"@radix-ui/react-roving-focus@1.1.0":
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.0.tgz#b30c59daf7e714c748805bfe11c76f96caaac35e"
+ integrity sha512-EA6AMGeq9AEeQDeSH0aZgG198qkfHSbvWTf1HvoDmOB5bBG/qTxjYMWUKMnYiV6J/iP/J8MEFSuB2zRU2n7ODA==
+ dependencies:
+ "@radix-ui/primitive" "1.1.0"
+ "@radix-ui/react-collection" "1.1.0"
+ "@radix-ui/react-compose-refs" "1.1.0"
+ "@radix-ui/react-context" "1.1.0"
+ "@radix-ui/react-direction" "1.1.0"
+ "@radix-ui/react-id" "1.1.0"
+ "@radix-ui/react-primitive" "2.0.0"
+ "@radix-ui/react-use-callback-ref" "1.1.0"
+ "@radix-ui/react-use-controllable-state" "1.1.0"
+
+"@radix-ui/react-slider@^1.2.0":
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-slider/-/react-slider-1.2.0.tgz#7a4c817d24386b420631a3fdc75563706d743472"
+ integrity sha512-dAHCDA4/ySXROEPaRtaMV5WHL8+JB/DbtyTbJjYkY0RXmKMO2Ln8DFZhywG5/mVQ4WqHDBc8smc14yPXPqZHYA==
+ dependencies:
+ "@radix-ui/number" "1.1.0"
+ "@radix-ui/primitive" "1.1.0"
+ "@radix-ui/react-collection" "1.1.0"
+ "@radix-ui/react-compose-refs" "1.1.0"
+ "@radix-ui/react-context" "1.1.0"
+ "@radix-ui/react-direction" "1.1.0"
+ "@radix-ui/react-primitive" "2.0.0"
+ "@radix-ui/react-use-controllable-state" "1.1.0"
+ "@radix-ui/react-use-layout-effect" "1.1.0"
+ "@radix-ui/react-use-previous" "1.1.0"
+ "@radix-ui/react-use-size" "1.1.0"
+
+"@radix-ui/react-slot@1.0.2":
version "1.0.2"
resolved "https://registry.yarnpkg.com/@radix-ui/react-slot/-/react-slot-1.0.2.tgz#a9ff4423eade67f501ffb32ec22064bc9d3099ab"
integrity sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==
@@ -871,12 +863,97 @@
"@babel/runtime" "^7.13.10"
"@radix-ui/react-compose-refs" "1.0.1"
-"@radix-ui/react-use-callback-ref@1.0.0":
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.0.0.tgz#9e7b8b6b4946fe3cbe8f748c82a2cce54e7b6a90"
- integrity sha512-GZtyzoHz95Rhs6S63D2t/eqvdFCm7I+yHMLVQheKM7nBD8mbZIt+ct1jz4536MDnaOGKIxynJ8eHTkVGVVkoTg==
+"@radix-ui/react-slot@1.1.0", "@radix-ui/react-slot@^1.1.0":
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-slot/-/react-slot-1.1.0.tgz#7c5e48c36ef5496d97b08f1357bb26ed7c714b84"
+ integrity sha512-FUCf5XMfmW4dtYl69pdS4DbxKy8nj4M7SafBgPllysxmdachynNflAdp/gCsnYWNDnge6tI9onzMp5ARYc1KNw==
dependencies:
- "@babel/runtime" "^7.13.10"
+ "@radix-ui/react-compose-refs" "1.1.0"
+
+"@radix-ui/react-switch@^1.1.0":
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-switch/-/react-switch-1.1.0.tgz#fcf8e778500f1d60d4b2bec2fc3fad77a7c118e3"
+ integrity sha512-OBzy5WAj641k0AOSpKQtreDMe+isX0MQJ1IVyF03ucdF3DunOnROVrjWs8zsXUxC3zfZ6JL9HFVCUlMghz9dJw==
+ dependencies:
+ "@radix-ui/primitive" "1.1.0"
+ "@radix-ui/react-compose-refs" "1.1.0"
+ "@radix-ui/react-context" "1.1.0"
+ "@radix-ui/react-primitive" "2.0.0"
+ "@radix-ui/react-use-controllable-state" "1.1.0"
+ "@radix-ui/react-use-previous" "1.1.0"
+ "@radix-ui/react-use-size" "1.1.0"
+
+"@radix-ui/react-tabs@^1.1.0":
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-tabs/-/react-tabs-1.1.0.tgz#0a6db1caed56776a1176aae68532060e301cc1c0"
+ integrity sha512-bZgOKB/LtZIij75FSuPzyEti/XBhJH52ExgtdVqjCIh+Nx/FW+LhnbXtbCzIi34ccyMsyOja8T0thCzoHFXNKA==
+ dependencies:
+ "@radix-ui/primitive" "1.1.0"
+ "@radix-ui/react-context" "1.1.0"
+ "@radix-ui/react-direction" "1.1.0"
+ "@radix-ui/react-id" "1.1.0"
+ "@radix-ui/react-presence" "1.1.0"
+ "@radix-ui/react-primitive" "2.0.0"
+ "@radix-ui/react-roving-focus" "1.1.0"
+ "@radix-ui/react-use-controllable-state" "1.1.0"
+
+"@radix-ui/react-toast@^1.2.1":
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-toast/-/react-toast-1.2.1.tgz#4bde231ed27d007dcd0455a446565ca619f92a2d"
+ integrity sha512-5trl7piMXcZiCq7MW6r8YYmu0bK5qDpTWz+FdEPdKyft2UixkspheYbjbrLXVN5NGKHFbOP7lm8eD0biiSqZqg==
+ dependencies:
+ "@radix-ui/primitive" "1.1.0"
+ "@radix-ui/react-collection" "1.1.0"
+ "@radix-ui/react-compose-refs" "1.1.0"
+ "@radix-ui/react-context" "1.1.0"
+ "@radix-ui/react-dismissable-layer" "1.1.0"
+ "@radix-ui/react-portal" "1.1.1"
+ "@radix-ui/react-presence" "1.1.0"
+ "@radix-ui/react-primitive" "2.0.0"
+ "@radix-ui/react-use-callback-ref" "1.1.0"
+ "@radix-ui/react-use-controllable-state" "1.1.0"
+ "@radix-ui/react-use-layout-effect" "1.1.0"
+ "@radix-ui/react-visually-hidden" "1.1.0"
+
+"@radix-ui/react-toggle-group@^1.1.0":
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-toggle-group/-/react-toggle-group-1.1.0.tgz#28714c4d1ff4961a8fd259b1feef58b4cac92f80"
+ integrity sha512-PpTJV68dZU2oqqgq75Uzto5o/XfOVgkrJ9rulVmfTKxWp3HfUjHE6CP/WLRR4AzPX9HWxw7vFow2me85Yu+Naw==
+ dependencies:
+ "@radix-ui/primitive" "1.1.0"
+ "@radix-ui/react-context" "1.1.0"
+ "@radix-ui/react-direction" "1.1.0"
+ "@radix-ui/react-primitive" "2.0.0"
+ "@radix-ui/react-roving-focus" "1.1.0"
+ "@radix-ui/react-toggle" "1.1.0"
+ "@radix-ui/react-use-controllable-state" "1.1.0"
+
+"@radix-ui/react-toggle@1.1.0", "@radix-ui/react-toggle@^1.1.0":
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-toggle/-/react-toggle-1.1.0.tgz#1f7697b82917019330a16c6f96f649f46b4606cf"
+ integrity sha512-gwoxaKZ0oJ4vIgzsfESBuSgJNdc0rv12VhHgcqN0TEJmmZixXG/2XpsLK8kzNWYcnaoRIEEQc0bEi3dIvdUpjw==
+ dependencies:
+ "@radix-ui/primitive" "1.1.0"
+ "@radix-ui/react-primitive" "2.0.0"
+ "@radix-ui/react-use-controllable-state" "1.1.0"
+
+"@radix-ui/react-tooltip@^1.1.2":
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-tooltip/-/react-tooltip-1.1.2.tgz#c42db2ffd7dcc6ff3d65407c8cb70490288f518d"
+ integrity sha512-9XRsLwe6Yb9B/tlnYCPVUd/TFS4J7HuOZW345DCeC6vKIxQGMZdx21RK4VoZauPD5frgkXTYVS5y90L+3YBn4w==
+ dependencies:
+ "@radix-ui/primitive" "1.1.0"
+ "@radix-ui/react-compose-refs" "1.1.0"
+ "@radix-ui/react-context" "1.1.0"
+ "@radix-ui/react-dismissable-layer" "1.1.0"
+ "@radix-ui/react-id" "1.1.0"
+ "@radix-ui/react-popper" "1.2.0"
+ "@radix-ui/react-portal" "1.1.1"
+ "@radix-ui/react-presence" "1.1.0"
+ "@radix-ui/react-primitive" "2.0.0"
+ "@radix-ui/react-slot" "1.1.0"
+ "@radix-ui/react-use-controllable-state" "1.1.0"
+ "@radix-ui/react-visually-hidden" "1.1.0"
"@radix-ui/react-use-callback-ref@1.0.1":
version "1.0.1"
@@ -885,13 +962,10 @@
dependencies:
"@babel/runtime" "^7.13.10"
-"@radix-ui/react-use-controllable-state@1.0.0":
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.0.0.tgz#a64deaafbbc52d5d407afaa22d493d687c538b7f"
- integrity sha512-FohDoZvk3mEXh9AWAVyRTYR4Sq7/gavuofglmiXB2g1aKyboUD4YtgWxKj8O5n+Uak52gXQ4wKz5IFST4vtJHg==
- dependencies:
- "@babel/runtime" "^7.13.10"
- "@radix-ui/react-use-callback-ref" "1.0.0"
+"@radix-ui/react-use-callback-ref@1.1.0":
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.0.tgz#bce938ca413675bc937944b0d01ef6f4a6dc5bf1"
+ integrity sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw==
"@radix-ui/react-use-controllable-state@1.0.1":
version "1.0.1"
@@ -901,13 +975,12 @@
"@babel/runtime" "^7.13.10"
"@radix-ui/react-use-callback-ref" "1.0.1"
-"@radix-ui/react-use-escape-keydown@1.0.0":
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.0.0.tgz#aef375db4736b9de38a5a679f6f49b45a060e5d1"
- integrity sha512-JwfBCUIfhXRxKExgIqGa4CQsiMemo1Xt0W/B4ei3fpzpvPENKpMKQ8mZSB6Acj3ebrAEgi2xiQvcI1PAAodvyg==
+"@radix-ui/react-use-controllable-state@1.1.0":
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.1.0.tgz#1321446857bb786917df54c0d4d084877aab04b0"
+ integrity sha512-MtfMVJiSr2NjzS0Aa90NPTnvTSg6C/JLCV7ma0W6+OMV78vd8OyRpID+Ng9LxzsPbLeuBnWBA1Nq30AtBIDChw==
dependencies:
- "@babel/runtime" "^7.13.10"
- "@radix-ui/react-use-callback-ref" "1.0.0"
+ "@radix-ui/react-use-callback-ref" "1.1.0"
"@radix-ui/react-use-escape-keydown@1.0.3":
version "1.0.3"
@@ -917,12 +990,12 @@
"@babel/runtime" "^7.13.10"
"@radix-ui/react-use-callback-ref" "1.0.1"
-"@radix-ui/react-use-layout-effect@1.0.0":
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.0.0.tgz#2fc19e97223a81de64cd3ba1dc42ceffd82374dc"
- integrity sha512-6Tpkq+R6LOlmQb1R5NNETLG0B4YP0wc+klfXafpUCj6JGyaUc8il7/kUZ7m59rGbXGczE9Bs+iz2qloqsZBduQ==
+"@radix-ui/react-use-escape-keydown@1.1.0":
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.0.tgz#31a5b87c3b726504b74e05dac1edce7437b98754"
+ integrity sha512-L7vwWlR1kTTQ3oh7g1O0CBF3YCyyTj8NmhLR+phShpyA50HCfBFKVJTpshm9PzLiKmehsrQzTYTpX9HvmC9rhw==
dependencies:
- "@babel/runtime" "^7.13.10"
+ "@radix-ui/react-use-callback-ref" "1.1.0"
"@radix-ui/react-use-layout-effect@1.0.1":
version "1.0.1"
@@ -931,28 +1004,41 @@
dependencies:
"@babel/runtime" "^7.13.10"
-"@radix-ui/react-use-rect@1.0.1":
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/@radix-ui/react-use-rect/-/react-use-rect-1.0.1.tgz#fde50b3bb9fd08f4a1cd204572e5943c244fcec2"
- integrity sha512-Cq5DLuSiuYVKNU8orzJMbl15TXilTnJKUCltMVQg53BQOF1/C5toAaGrowkgksdBQ9H+SRL23g0HDmg9tvmxXw==
+"@radix-ui/react-use-layout-effect@1.1.0":
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.0.tgz#3c2c8ce04827b26a39e442ff4888d9212268bd27"
+ integrity sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w==
+
+"@radix-ui/react-use-previous@1.1.0":
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-use-previous/-/react-use-previous-1.1.0.tgz#d4dd37b05520f1d996a384eb469320c2ada8377c"
+ integrity sha512-Z/e78qg2YFnnXcW88A4JmTtm4ADckLno6F7OXotmkQfeuCVaKuYzqAATPhVzl3delXE7CxIV8shofPn3jPc5Og==
+
+"@radix-ui/react-use-rect@1.1.0":
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-use-rect/-/react-use-rect-1.1.0.tgz#13b25b913bd3e3987cc9b073a1a164bb1cf47b88"
+ integrity sha512-0Fmkebhr6PiseyZlYAOtLS+nb7jLmpqTrJyv61Pe68MKYW6OWdRE2kI70TaYY27u7H0lajqM3hSMMLFq18Z7nQ==
dependencies:
- "@babel/runtime" "^7.13.10"
- "@radix-ui/rect" "1.0.1"
+ "@radix-ui/rect" "1.1.0"
-"@radix-ui/react-use-size@1.0.1":
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/@radix-ui/react-use-size/-/react-use-size-1.0.1.tgz#1c5f5fea940a7d7ade77694bb98116fb49f870b2"
- integrity sha512-ibay+VqrgcaI6veAojjofPATwledXiSmX+C0KrBk/xgpX9rBzPV3OsfwlhQdUOFbh+LKQorLYT+xTXW9V8yd0g==
+"@radix-ui/react-use-size@1.1.0":
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-use-size/-/react-use-size-1.1.0.tgz#b4dba7fbd3882ee09e8d2a44a3eed3a7e555246b"
+ integrity sha512-XW3/vWuIXHa+2Uwcc2ABSfcCledmXhhQPlGbfcRXbiUQI5Icjcg19BGCZVKKInYbvUCut/ufbbLLPFC5cbb1hw==
dependencies:
- "@babel/runtime" "^7.13.10"
- "@radix-ui/react-use-layout-effect" "1.0.1"
+ "@radix-ui/react-use-layout-effect" "1.1.0"
-"@radix-ui/rect@1.0.1":
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/@radix-ui/rect/-/rect-1.0.1.tgz#bf8e7d947671996da2e30f4904ece343bc4a883f"
- integrity sha512-fyrgCaedtvMg9NK3en0pnOYJdtfwxUcNolezkNPUsoX57X8oQk+NkqcvzHXD2uKNij6GXmWU9NDru2IWjrO4BQ==
+"@radix-ui/react-visually-hidden@1.1.0":
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.1.0.tgz#ad47a8572580f7034b3807c8e6740cd41038a5a2"
+ integrity sha512-N8MDZqtgCgG5S3aV60INAB475osJousYpZ4cTJ2cFbMpdHS5Y6loLTH8LPtkj2QN0x93J30HT/M3qJXM0+lyeQ==
dependencies:
- "@babel/runtime" "^7.13.10"
+ "@radix-ui/react-primitive" "2.0.0"
+
+"@radix-ui/rect@1.1.0":
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/@radix-ui/rect/-/rect-1.1.0.tgz#f817d1d3265ac5415dadc67edab30ae196696438"
+ integrity sha512-A9+lCBZoaMJlVKcRBz2YByCG+Cp2t6nAnMnNba+XiWxnj6r4JUFqfsgwocMBZU9LPtdxC6wB56ySYpc7LQIoJg==
"@types/babel__core@^7.20.5":
version "7.20.5"
@@ -981,157 +1067,133 @@
"@babel/types" "^7.0.0"
"@types/babel__traverse@*":
- version "7.20.5"
- resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.20.5.tgz#7b7502be0aa80cc4ef22978846b983edaafcd4dd"
- integrity sha512-WXCyOcRtH37HAUkpXhUduaxdm82b4GSlyTqajXviN4EfiuPgNYR109xMCKvpl6zPIpua0DGlMEDCq+g8EdoheQ==
+ version "7.20.6"
+ resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.20.6.tgz#8dc9f0ae0f202c08d8d4dab648912c8d6038e3f7"
+ integrity sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==
dependencies:
"@babel/types" "^7.20.7"
-"@types/json-schema@^7.0.9":
- version "7.0.15"
- resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841"
- integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==
-
"@types/prop-types@*":
- version "15.7.11"
- resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.11.tgz#2596fb352ee96a1379c657734d4b913a613ad563"
- integrity sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==
+ version "15.7.12"
+ resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.12.tgz#12bb1e2be27293c1406acb6af1c3f3a1481d98c6"
+ integrity sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==
-"@types/react-dom@^18.0.11":
- version "18.2.19"
- resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.2.19.tgz#b84b7c30c635a6c26c6a6dfbb599b2da9788be58"
- integrity sha512-aZvQL6uUbIJpjZk4U8JZGbau9KDeAwMfmhyWorxgBkqDIEf6ROjRozcmPIicqsUwPUjbkDfHKgGee1Lq65APcA==
+"@types/react-dom@^18.3.0":
+ version "18.3.0"
+ resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.3.0.tgz#0cbc818755d87066ab6ca74fbedb2547d74a82b0"
+ integrity sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==
dependencies:
"@types/react" "*"
-"@types/react@*", "@types/react@^18.0.37":
- version "18.2.61"
- resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.61.tgz#5607308495037436779939ec0348a5816c08799d"
- integrity sha512-NURTN0qNnJa7O/k4XUkEW2yfygA+NxS0V5h1+kp9jPwhzZy95q3ADoGMP0+JypMhrZBTTgjKAUlTctde1zzeQA==
+"@types/react@*", "@types/react@^18.3.3":
+ version "18.3.3"
+ resolved "https://registry.yarnpkg.com/@types/react/-/react-18.3.3.tgz#9679020895318b0915d7a3ab004d92d33375c45f"
+ integrity sha512-hti/R0pS0q1/xx+TsI73XIqk26eBsISZ2R0wUijXIngRK9R/e7Xw/cXVxQK7R5JjW+SV4zGcn5hXjudkN/pLIw==
dependencies:
"@types/prop-types" "*"
- "@types/scheduler" "*"
csstype "^3.0.2"
-"@types/scheduler@*":
- version "0.16.8"
- resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.8.tgz#ce5ace04cfeabe7ef87c0091e50752e36707deff"
- integrity sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==
-
-"@types/semver@^7.3.12":
- version "7.5.8"
- resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.8.tgz#8268a8c57a3e4abd25c165ecd36237db7948a55e"
- integrity sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==
-
-"@typescript-eslint/eslint-plugin@^5.59.0":
- version "5.62.0"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz#aeef0328d172b9e37d9bab6dbc13b87ed88977db"
- integrity sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==
- dependencies:
- "@eslint-community/regexpp" "^4.4.0"
- "@typescript-eslint/scope-manager" "5.62.0"
- "@typescript-eslint/type-utils" "5.62.0"
- "@typescript-eslint/utils" "5.62.0"
- debug "^4.3.4"
+"@typescript-eslint/eslint-plugin@^7.13.1":
+ version "7.13.1"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.13.1.tgz#cdc521c8bca38b55585cf30db787fb2abad3f9fd"
+ integrity sha512-kZqi+WZQaZfPKnsflLJQCz6Ze9FFSMfXrrIOcyargekQxG37ES7DJNpJUE9Q/X5n3yTIP/WPutVNzgknQ7biLg==
+ dependencies:
+ "@eslint-community/regexpp" "^4.10.0"
+ "@typescript-eslint/scope-manager" "7.13.1"
+ "@typescript-eslint/type-utils" "7.13.1"
+ "@typescript-eslint/utils" "7.13.1"
+ "@typescript-eslint/visitor-keys" "7.13.1"
graphemer "^1.4.0"
- ignore "^5.2.0"
- natural-compare-lite "^1.4.0"
- semver "^7.3.7"
- tsutils "^3.21.0"
-
-"@typescript-eslint/parser@^5.59.0":
- version "5.62.0"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.62.0.tgz#1b63d082d849a2fcae8a569248fbe2ee1b8a56c7"
- integrity sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==
- dependencies:
- "@typescript-eslint/scope-manager" "5.62.0"
- "@typescript-eslint/types" "5.62.0"
- "@typescript-eslint/typescript-estree" "5.62.0"
+ ignore "^5.3.1"
+ natural-compare "^1.4.0"
+ ts-api-utils "^1.3.0"
+
+"@typescript-eslint/parser@^7.13.1":
+ version "7.13.1"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-7.13.1.tgz#fac57811b3e519185f7259bac312291f7b9c4e72"
+ integrity sha512-1ELDPlnLvDQ5ybTSrMhRTFDfOQEOXNM+eP+3HT/Yq7ruWpciQw+Avi73pdEbA4SooCawEWo3dtYbF68gN7Ed1A==
+ dependencies:
+ "@typescript-eslint/scope-manager" "7.13.1"
+ "@typescript-eslint/types" "7.13.1"
+ "@typescript-eslint/typescript-estree" "7.13.1"
+ "@typescript-eslint/visitor-keys" "7.13.1"
debug "^4.3.4"
-"@typescript-eslint/scope-manager@5.62.0":
- version "5.62.0"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz#d9457ccc6a0b8d6b37d0eb252a23022478c5460c"
- integrity sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==
+"@typescript-eslint/scope-manager@7.13.1":
+ version "7.13.1"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-7.13.1.tgz#c08041206904bf36f0e6997efdb0ca775e0c452e"
+ integrity sha512-adbXNVEs6GmbzaCpymHQ0MB6E4TqoiVbC0iqG3uijR8ZYfpAXMGttouQzF4Oat3P2GxDVIrg7bMI/P65LiQZdg==
dependencies:
- "@typescript-eslint/types" "5.62.0"
- "@typescript-eslint/visitor-keys" "5.62.0"
+ "@typescript-eslint/types" "7.13.1"
+ "@typescript-eslint/visitor-keys" "7.13.1"
-"@typescript-eslint/type-utils@5.62.0":
- version "5.62.0"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz#286f0389c41681376cdad96b309cedd17d70346a"
- integrity sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==
+"@typescript-eslint/type-utils@7.13.1":
+ version "7.13.1"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-7.13.1.tgz#63bec3f1fb43cf0bc409cbdb88ef96d118ca8632"
+ integrity sha512-aWDbLu1s9bmgPGXSzNCxELu+0+HQOapV/y+60gPXafR8e2g1Bifxzevaa+4L2ytCWm+CHqpELq4CSoN9ELiwCg==
dependencies:
- "@typescript-eslint/typescript-estree" "5.62.0"
- "@typescript-eslint/utils" "5.62.0"
+ "@typescript-eslint/typescript-estree" "7.13.1"
+ "@typescript-eslint/utils" "7.13.1"
debug "^4.3.4"
- tsutils "^3.21.0"
+ ts-api-utils "^1.3.0"
-"@typescript-eslint/types@5.62.0":
- version "5.62.0"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.62.0.tgz#258607e60effa309f067608931c3df6fed41fd2f"
- integrity sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==
+"@typescript-eslint/types@7.13.1":
+ version "7.13.1"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-7.13.1.tgz#787db283bd0b58751094c90d5b58bbf5e9fc9bd8"
+ integrity sha512-7K7HMcSQIAND6RBL4kDl24sG/xKM13cA85dc7JnmQXw2cBDngg7c19B++JzvJHRG3zG36n9j1i451GBzRuHchw==
-"@typescript-eslint/typescript-estree@5.62.0":
- version "5.62.0"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz#7d17794b77fabcac615d6a48fb143330d962eb9b"
- integrity sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==
+"@typescript-eslint/typescript-estree@7.13.1":
+ version "7.13.1"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-7.13.1.tgz#3412841b130e070db2f675e3d9b8cb1ae49e1c3f"
+ integrity sha512-uxNr51CMV7npU1BxZzYjoVz9iyjckBduFBP0S5sLlh1tXYzHzgZ3BR9SVsNed+LmwKrmnqN3Kdl5t7eZ5TS1Yw==
dependencies:
- "@typescript-eslint/types" "5.62.0"
- "@typescript-eslint/visitor-keys" "5.62.0"
+ "@typescript-eslint/types" "7.13.1"
+ "@typescript-eslint/visitor-keys" "7.13.1"
debug "^4.3.4"
globby "^11.1.0"
is-glob "^4.0.3"
- semver "^7.3.7"
- tsutils "^3.21.0"
-
-"@typescript-eslint/utils@5.62.0":
- version "5.62.0"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.62.0.tgz#141e809c71636e4a75daa39faed2fb5f4b10df86"
- integrity sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==
- dependencies:
- "@eslint-community/eslint-utils" "^4.2.0"
- "@types/json-schema" "^7.0.9"
- "@types/semver" "^7.3.12"
- "@typescript-eslint/scope-manager" "5.62.0"
- "@typescript-eslint/types" "5.62.0"
- "@typescript-eslint/typescript-estree" "5.62.0"
- eslint-scope "^5.1.1"
- semver "^7.3.7"
-
-"@typescript-eslint/visitor-keys@5.62.0":
- version "5.62.0"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz#2174011917ce582875954ffe2f6912d5931e353e"
- integrity sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==
- dependencies:
- "@typescript-eslint/types" "5.62.0"
- eslint-visitor-keys "^3.3.0"
-
-"@ungap/structured-clone@^1.2.0":
- version "1.2.0"
- resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406"
- integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==
+ minimatch "^9.0.4"
+ semver "^7.6.0"
+ ts-api-utils "^1.3.0"
+
+"@typescript-eslint/utils@7.13.1":
+ version "7.13.1"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-7.13.1.tgz#611083379caa0d3a2c09d126c65065a3e4337ba2"
+ integrity sha512-h5MzFBD5a/Gh/fvNdp9pTfqJAbuQC4sCN2WzuXme71lqFJsZtLbjxfSk4r3p02WIArOF9N94pdsLiGutpDbrXQ==
+ dependencies:
+ "@eslint-community/eslint-utils" "^4.4.0"
+ "@typescript-eslint/scope-manager" "7.13.1"
+ "@typescript-eslint/types" "7.13.1"
+ "@typescript-eslint/typescript-estree" "7.13.1"
+
+"@typescript-eslint/visitor-keys@7.13.1":
+ version "7.13.1"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-7.13.1.tgz#9c229a795a919db61f2d7f2337ef584ac05fbe96"
+ integrity sha512-k/Bfne7lrP7hcb7m9zSsgcBmo+8eicqqfNAJ7uUY+jkTFpKeH2FSkWpFRtimBxgkyvqfu9jTPRbYOvud6isdXA==
+ dependencies:
+ "@typescript-eslint/types" "7.13.1"
+ eslint-visitor-keys "^3.4.3"
-"@vitejs/plugin-react@^4.0.0":
- version "4.2.1"
- resolved "https://registry.yarnpkg.com/@vitejs/plugin-react/-/plugin-react-4.2.1.tgz#744d8e4fcb120fc3dbaa471dadd3483f5a304bb9"
- integrity sha512-oojO9IDc4nCUUi8qIR11KoQm0XFFLIwsRBwHRR4d/88IWghn1y6ckz/bJ8GHDCsYEJee8mDzqtJxh15/cisJNQ==
+"@vitejs/plugin-react@^4.3.1":
+ version "4.3.1"
+ resolved "https://registry.yarnpkg.com/@vitejs/plugin-react/-/plugin-react-4.3.1.tgz#d0be6594051ded8957df555ff07a991fb618b48e"
+ integrity sha512-m/V2syj5CuVnaxcUJOQRel/Wr31FFXRFlnOoq1TVtkCxsY5veGMTEmpWHndrhB2U8ScHtCQB1e+4hWYExQc6Lg==
dependencies:
- "@babel/core" "^7.23.5"
- "@babel/plugin-transform-react-jsx-self" "^7.23.3"
- "@babel/plugin-transform-react-jsx-source" "^7.23.3"
+ "@babel/core" "^7.24.5"
+ "@babel/plugin-transform-react-jsx-self" "^7.24.5"
+ "@babel/plugin-transform-react-jsx-source" "^7.24.1"
"@types/babel__core" "^7.20.5"
- react-refresh "^0.14.0"
+ react-refresh "^0.14.2"
acorn-jsx@^5.3.2:
version "5.3.2"
resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937"
integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==
-acorn@^8.9.0:
- version "8.11.3"
- resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.11.3.tgz#71e0b14e13a4ec160724b38fb7b0f233b1b81d7a"
- integrity sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==
+acorn@^8.12.0:
+ version "8.12.0"
+ resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.12.0.tgz#1627bfa2e058148036133b8d9b51a700663c294c"
+ integrity sha512-RTvkC4w+KNXrM39/lWCUaG0IbRkWdCv7W/IOW9oU6SawyxulvkQy5HQPVTKxEjczcUvapcrw3cFx/60VN/NRNw==
ajv@^6.12.4:
version "6.12.6"
@@ -1196,9 +1258,9 @@ argparse@^2.0.1:
integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==
aria-hidden@^1.1.1:
- version "1.2.3"
- resolved "https://registry.yarnpkg.com/aria-hidden/-/aria-hidden-1.2.3.tgz#14aeb7fb692bbb72d69bebfa47279c1fd725e954"
- integrity sha512-xcLxITLe2HYa1cnYnwCjkOO1PqUHQpozB8x9AR0OgWN2woOBi5kSDVxKfd0b7sb1hw5qFeJhXm9H1nu3xSfLeQ==
+ version "1.2.4"
+ resolved "https://registry.yarnpkg.com/aria-hidden/-/aria-hidden-1.2.4.tgz#b78e383fdbc04d05762c78b4a25a501e736c4522"
+ integrity sha512-y+CcFFwelSXpLZk/7fMB2mUbGtX9lKycf1MWJ7CaTIERyitVlyQx6C+sxcROU2BAJ24OiZyK+8wj2i8AlBoS3A==
dependencies:
tslib "^2.0.0"
@@ -1207,13 +1269,13 @@ array-union@^2.1.0:
resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d"
integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==
-autoprefixer@^10.4.12:
- version "10.4.17"
- resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.17.tgz#35cd5695cbbe82f536a50fa025d561b01fdec8be"
- integrity sha512-/cpVNRLSfhOtcGflT13P2794gVSgmPgTR+erw5ifnMLZb0UnSlkK4tquLmkd3BhA+nLo5tX8Cu0upUsGKvKbmg==
+autoprefixer@^10.4.19:
+ version "10.4.19"
+ resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.19.tgz#ad25a856e82ee9d7898c59583c1afeb3fa65f89f"
+ integrity sha512-BaENR2+zBZ8xXhM4pUaKUxlVdxZ0EZhjvbopwnXmxRUfqDmwSpC2lAi/QXvx7NRdPCo1WKEcEF6mV64si1z4Ew==
dependencies:
- browserslist "^4.22.2"
- caniuse-lite "^1.0.30001578"
+ browserslist "^4.23.0"
+ caniuse-lite "^1.0.30001599"
fraction.js "^4.3.7"
normalize-range "^0.1.2"
picocolors "^1.0.0"
@@ -1225,9 +1287,9 @@ balanced-match@^1.0.0:
integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
binary-extensions@^2.0.0:
- version "2.2.0"
- resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d"
- integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.3.0.tgz#f6e14a97858d327252200242d4ccfe522c445522"
+ integrity sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==
brace-expansion@^1.1.7:
version "1.1.11"
@@ -1244,22 +1306,22 @@ brace-expansion@^2.0.1:
dependencies:
balanced-match "^1.0.0"
-braces@^3.0.2, braces@~3.0.2:
- version "3.0.2"
- resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
- integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==
+braces@^3.0.3, braces@~3.0.2:
+ version "3.0.3"
+ resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789"
+ integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==
dependencies:
- fill-range "^7.0.1"
+ fill-range "^7.1.1"
-browserslist@^4.22.2:
- version "4.23.0"
- resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.23.0.tgz#8f3acc2bbe73af7213399430890f86c63a5674ab"
- integrity sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==
+browserslist@^4.22.2, browserslist@^4.23.0:
+ version "4.23.1"
+ resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.23.1.tgz#ce4af0534b3d37db5c1a4ca98b9080f985041e96"
+ integrity sha512-TUfofFo/KsK/bWZ9TWQ5O26tsWW4Uhmt8IYklbnUa70udB6P2wA7w7o4PY4muaEPBQaAX+CEnmmIA41NVHtPVw==
dependencies:
- caniuse-lite "^1.0.30001587"
- electron-to-chromium "^1.4.668"
+ caniuse-lite "^1.0.30001629"
+ electron-to-chromium "^1.4.796"
node-releases "^2.0.14"
- update-browserslist-db "^1.0.13"
+ update-browserslist-db "^1.0.16"
call-bind@^1.0.7:
version "1.0.7"
@@ -1282,10 +1344,10 @@ camelcase-css@^2.0.1:
resolved "https://registry.yarnpkg.com/camelcase-css/-/camelcase-css-2.0.1.tgz#ee978f6947914cc30c6b44741b6ed1df7f043fd5"
integrity sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==
-caniuse-lite@^1.0.30001578, caniuse-lite@^1.0.30001587:
- version "1.0.30001591"
- resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001591.tgz#16745e50263edc9f395895a7cd468b9f3767cf33"
- integrity sha512-PCzRMei/vXjJyL5mJtzNiUCKP59dm8Apqc3PH8gJkMnMXZGox93RbE76jHsmLwmIo6/3nsYIpJtx0O7u5PqFuQ==
+caniuse-lite@^1.0.30001599, caniuse-lite@^1.0.30001629:
+ version "1.0.30001636"
+ resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001636.tgz#b15f52d2bdb95fad32c2f53c0b68032b85188a78"
+ integrity sha512-bMg2vmr8XBsbL6Lr0UHXy/21m84FTxDLWn2FSqMd5PrlbMxwJlQnC2YWYxVgp66PZE+BBNF2jYQUBKCo1FDeZg==
chalk@^2.4.2:
version "2.4.2"
@@ -1319,24 +1381,30 @@ chokidar@^3.5.3:
optionalDependencies:
fsevents "~2.3.2"
-class-variance-authority@^0.6.0:
- version "0.6.1"
- resolved "https://registry.yarnpkg.com/class-variance-authority/-/class-variance-authority-0.6.1.tgz#9482856c1496d33c21ef19e65b5d255460aa8039"
- integrity sha512-eurOEGc7YVx3majOrOb099PNKgO3KnKSApOprXI4BTq6bcfbqbQXPN2u+rPPmIJ2di23bMwhk0SxCCthBmszEQ==
+class-variance-authority@^0.7.0:
+ version "0.7.0"
+ resolved "https://registry.yarnpkg.com/class-variance-authority/-/class-variance-authority-0.7.0.tgz#1c3134d634d80271b1837452b06d821915954522"
+ integrity sha512-jFI8IQw4hczaL4ALINxqLEXQbWcNjoSkloa4IaufXCJr6QawJyw7tuRysRsrE8w2p/4gGaxKIt/hX3qz/IbD1A==
dependencies:
- clsx "1.2.1"
+ clsx "2.0.0"
-clsx@1.2.1, clsx@^1.2.1:
- version "1.2.1"
- resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.2.1.tgz#0ddc4a20a549b59c93a4116bb26f5294ca17dc12"
- integrity sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==
+clsx@2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/clsx/-/clsx-2.0.0.tgz#12658f3fd98fafe62075595a5c30e43d18f3d00b"
+ integrity sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q==
+
+clsx@^2.1.1:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/clsx/-/clsx-2.1.1.tgz#eed397c9fd8bd882bfb18deab7102049a2f32999"
+ integrity sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==
-cmdk@^0.2.1:
- version "0.2.1"
- resolved "https://registry.yarnpkg.com/cmdk/-/cmdk-0.2.1.tgz#aa8e1332bb0b8d8484e793017c82537351188d9a"
- integrity sha512-U6//9lQ6JvT47+6OF6Gi8BvkxYQ8SCRRSKIJkthIMsFsLZRG0cKvTtuTaefyIKMQb8rvvXy0wGdpTNq/jPtm+g==
+cmdk@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/cmdk/-/cmdk-1.0.0.tgz#0a095fdafca3dfabed82d1db78a6262fb163ded9"
+ integrity sha512-gDzVf0a09TvoJ5jnuPvygTB77+XdOSwEmJ88L6XPFPlv7T3RxbP9jgenfylrAMD0+Le1aO0nVjQUzl2g+vjz5Q==
dependencies:
- "@radix-ui/react-dialog" "1.0.0"
+ "@radix-ui/react-dialog" "1.0.5"
+ "@radix-ui/react-primitive" "1.0.3"
color-convert@^1.9.0:
version "1.9.3"
@@ -1377,6 +1445,13 @@ convert-source-map@^2.0.0:
resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a"
integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==
+cross-fetch@4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-4.0.0.tgz#f037aef1580bb3a1a35164ea2a848ba81b445983"
+ integrity sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==
+ dependencies:
+ node-fetch "^2.6.12"
+
cross-spawn@^7.0.0, cross-spawn@^7.0.2:
version "7.0.3"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
@@ -1397,9 +1472,9 @@ csstype@^3.0.2:
integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==
debug@^4.1.0, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4:
- version "4.3.4"
- resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
- integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
+ version "4.3.5"
+ resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.5.tgz#e83444eceb9fedd4a1da56d671ae2446a01a6e1e"
+ integrity sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==
dependencies:
ms "2.1.2"
@@ -1408,7 +1483,7 @@ deep-is@^0.1.3:
resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831"
integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==
-define-data-property@^1.1.2:
+define-data-property@^1.1.4:
version "1.1.4"
resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e"
integrity sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==
@@ -1439,22 +1514,45 @@ dlv@^1.1.3:
resolved "https://registry.yarnpkg.com/dlv/-/dlv-1.1.3.tgz#5c198a8a11453596e751494d49874bc7732f2e79"
integrity sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==
-doctrine@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961"
- integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==
+dom-serializer@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-2.0.0.tgz#e41b802e1eedf9f6cae183ce5e622d789d7d8e53"
+ integrity sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==
dependencies:
- esutils "^2.0.2"
+ domelementtype "^2.3.0"
+ domhandler "^5.0.2"
+ entities "^4.2.0"
+
+domelementtype@^2.3.0:
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.3.0.tgz#5c45e8e869952626331d7aab326d01daf65d589d"
+ integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==
+
+domhandler@5.0.3, domhandler@^5.0.2, domhandler@^5.0.3:
+ version "5.0.3"
+ resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-5.0.3.tgz#cc385f7f751f1d1fc650c21374804254538c7d31"
+ integrity sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==
+ dependencies:
+ domelementtype "^2.3.0"
+
+domutils@^3.1.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/domutils/-/domutils-3.1.0.tgz#c47f551278d3dc4b0b1ab8cbb42d751a6f0d824e"
+ integrity sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==
+ dependencies:
+ dom-serializer "^2.0.0"
+ domelementtype "^2.3.0"
+ domhandler "^5.0.3"
eastasianwidth@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb"
integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==
-electron-to-chromium@^1.4.668:
- version "1.4.688"
- resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.688.tgz#a5dade91d948c4ec1e3b5ab0e9dea95c3b787294"
- integrity sha512-3/tHg2ChPF00eukURIB8cSVt3/9oeS1oTUIEt3ivngBInUaEcBhG2VdyEDejhwQdR6SLqaiEAEc0dHS0V52pOw==
+electron-to-chromium@^1.4.796:
+ version "1.4.810"
+ resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.810.tgz#7dee01b090b9e048e6db752f7b30921790230654"
+ integrity sha512-Kaxhu4T7SJGpRQx99tq216gCq2nMxJo+uuT6uzz9l8TVN2stL7M06MIIXAtr9jsrLs2Glflgf2vMQRepxawOdQ==
emoji-regex@^8.0.0:
version "8.0.0"
@@ -1466,6 +1564,11 @@ emoji-regex@^9.2.2:
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72"
integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==
+entities@^4.2.0, entities@^4.5.0:
+ version "4.5.0"
+ resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48"
+ integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==
+
es-define-property@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.0.tgz#c7faefbdff8b2696cf5f46921edfb77cc4ba3845"
@@ -1506,7 +1609,7 @@ esbuild@^0.18.10:
"@esbuild/win32-ia32" "0.18.20"
"@esbuild/win32-x64" "0.18.20"
-escalade@^3.1.1:
+escalade@^3.1.2:
version "3.1.2"
resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.2.tgz#54076e9ab29ea5bf3d8f1ed62acffbb88272df27"
integrity sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==
@@ -1521,72 +1624,65 @@ escape-string-regexp@^4.0.0:
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34"
integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==
-eslint-plugin-react-hooks@^4.6.0:
- version "4.6.0"
- resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz#4c3e697ad95b77e93f8646aaa1630c1ba607edd3"
- integrity sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==
-
-eslint-plugin-react-refresh@^0.3.4:
- version "0.3.5"
- resolved "https://registry.yarnpkg.com/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.3.5.tgz#0121e3f05f940250d3544bfaeff52e1c6adf4117"
- integrity sha512-61qNIsc7fo9Pp/mju0J83kzvLm0Bsayu7OQSLEoJxLDCBjIIyb87bkzufoOvdDxLkSlMfkF7UxomC4+eztUBSA==
+eslint-plugin-react-hooks@^4.6.2:
+ version "4.6.2"
+ resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.2.tgz#c829eb06c0e6f484b3fbb85a97e57784f328c596"
+ integrity sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ==
-eslint-scope@^5.1.1:
- version "5.1.1"
- resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c"
- integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==
- dependencies:
- esrecurse "^4.3.0"
- estraverse "^4.1.1"
+eslint-plugin-react-refresh@^0.4.7:
+ version "0.4.7"
+ resolved "https://registry.yarnpkg.com/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.7.tgz#1f597f9093b254f10ee0961c139a749acb19af7d"
+ integrity sha512-yrj+KInFmwuQS2UQcg1SF83ha1tuHC1jMQbRNyuWtlEzzKRDgAl7L4Yp4NlDUZTZNlWvHEzOtJhMi40R7JxcSw==
-eslint-scope@^7.2.2:
- version "7.2.2"
- resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.2.tgz#deb4f92563390f32006894af62a22dba1c46423f"
- integrity sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==
+eslint-scope@^8.0.1:
+ version "8.0.1"
+ resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-8.0.1.tgz#a9601e4b81a0b9171657c343fb13111688963cfc"
+ integrity sha512-pL8XjgP4ZOmmwfFE8mEhSxA7ZY4C+LWyqjQ3o4yWkkmD0qcMT9kkW3zWHOczhWcjTSgqycYAgwSlXvZltv65og==
dependencies:
esrecurse "^4.3.0"
estraverse "^5.2.0"
-eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.3:
+eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.3:
version "3.4.3"
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800"
integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==
-eslint@^8.38.0:
- version "8.57.0"
- resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.57.0.tgz#c786a6fd0e0b68941aaf624596fb987089195668"
- integrity sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==
+eslint-visitor-keys@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz#e3adc021aa038a2a8e0b2f8b0ce8f66b9483b1fb"
+ integrity sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==
+
+eslint@^9.5.0:
+ version "9.5.0"
+ resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.5.0.tgz#11856034b94a9e1a02cfcc7e96a9f0956963cd2f"
+ integrity sha512-+NAOZFrW/jFTS3dASCGBxX1pkFD0/fsO+hfAkJ4TyYKwgsXZbqzrw+seCYFCcPCYXvnD67tAnglU7GQTz6kcVw==
dependencies:
"@eslint-community/eslint-utils" "^4.2.0"
"@eslint-community/regexpp" "^4.6.1"
- "@eslint/eslintrc" "^2.1.4"
- "@eslint/js" "8.57.0"
- "@humanwhocodes/config-array" "^0.11.14"
+ "@eslint/config-array" "^0.16.0"
+ "@eslint/eslintrc" "^3.1.0"
+ "@eslint/js" "9.5.0"
"@humanwhocodes/module-importer" "^1.0.1"
+ "@humanwhocodes/retry" "^0.3.0"
"@nodelib/fs.walk" "^1.2.8"
- "@ungap/structured-clone" "^1.2.0"
ajv "^6.12.4"
chalk "^4.0.0"
cross-spawn "^7.0.2"
debug "^4.3.2"
- doctrine "^3.0.0"
escape-string-regexp "^4.0.0"
- eslint-scope "^7.2.2"
- eslint-visitor-keys "^3.4.3"
- espree "^9.6.1"
- esquery "^1.4.2"
+ eslint-scope "^8.0.1"
+ eslint-visitor-keys "^4.0.0"
+ espree "^10.0.1"
+ esquery "^1.5.0"
esutils "^2.0.2"
fast-deep-equal "^3.1.3"
- file-entry-cache "^6.0.1"
+ file-entry-cache "^8.0.0"
find-up "^5.0.0"
glob-parent "^6.0.2"
- globals "^13.19.0"
- graphemer "^1.4.0"
ignore "^5.2.0"
imurmurhash "^0.1.4"
is-glob "^4.0.0"
is-path-inside "^3.0.3"
- js-yaml "^4.1.0"
json-stable-stringify-without-jsonify "^1.0.1"
levn "^0.4.1"
lodash.merge "^4.6.2"
@@ -1596,16 +1692,16 @@ eslint@^8.38.0:
strip-ansi "^6.0.1"
text-table "^0.2.0"
-espree@^9.6.0, espree@^9.6.1:
- version "9.6.1"
- resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f"
- integrity sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==
+espree@^10.0.1:
+ version "10.1.0"
+ resolved "https://registry.yarnpkg.com/espree/-/espree-10.1.0.tgz#8788dae611574c0f070691f522e4116c5a11fc56"
+ integrity sha512-M1M6CpiE6ffoigIOWYO9UDP8TMUw9kqb21tf+08IgDYjCsOvCuDt4jQcZmoYxx+w7zlKw9/N0KXfto+I8/FrXA==
dependencies:
- acorn "^8.9.0"
+ acorn "^8.12.0"
acorn-jsx "^5.3.2"
- eslint-visitor-keys "^3.4.1"
+ eslint-visitor-keys "^4.0.0"
-esquery@^1.4.2:
+esquery@^1.5.0:
version "1.5.0"
resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.5.0.tgz#6ce17738de8577694edd7361c57182ac8cb0db0b"
integrity sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==
@@ -1619,11 +1715,6 @@ esrecurse@^4.3.0:
dependencies:
estraverse "^5.2.0"
-estraverse@^4.1.1:
- version "4.3.0"
- resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d"
- integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==
-
estraverse@^5.1.0, estraverse@^5.2.0:
version "5.3.0"
resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123"
@@ -1667,17 +1758,17 @@ fastq@^1.6.0:
dependencies:
reusify "^1.0.4"
-file-entry-cache@^6.0.1:
- version "6.0.1"
- resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027"
- integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==
+file-entry-cache@^8.0.0:
+ version "8.0.0"
+ resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-8.0.0.tgz#7787bddcf1131bffb92636c69457bbc0edd6d81f"
+ integrity sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==
dependencies:
- flat-cache "^3.0.4"
+ flat-cache "^4.0.0"
-fill-range@^7.0.1:
- version "7.0.1"
- resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40"
- integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==
+fill-range@^7.1.1:
+ version "7.1.1"
+ resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292"
+ integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==
dependencies:
to-regex-range "^5.0.1"
@@ -1689,14 +1780,13 @@ find-up@^5.0.0:
locate-path "^6.0.0"
path-exists "^4.0.0"
-flat-cache@^3.0.4:
- version "3.2.0"
- resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.2.0.tgz#2c0c2d5040c99b1632771a9d105725c0115363ee"
- integrity sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==
+flat-cache@^4.0.0:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-4.0.1.tgz#0ece39fcb14ee012f4b0410bd33dd9c1f011127c"
+ integrity sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==
dependencies:
flatted "^3.2.9"
- keyv "^4.5.3"
- rimraf "^3.0.2"
+ keyv "^4.5.4"
flatted@^3.2.9:
version "3.3.1"
@@ -1704,9 +1794,9 @@ flatted@^3.2.9:
integrity sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==
foreground-child@^3.1.0:
- version "3.1.1"
- resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.1.1.tgz#1d173e776d75d2772fed08efe4a0de1ea1b12d0d"
- integrity sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==
+ version "3.2.1"
+ resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.2.1.tgz#767004ccf3a5b30df39bed90718bab43fe0a59f7"
+ integrity sha512-PXUUyLqrR2XCWICfv6ukppP96sdFwWbNEnfEMt7jNsISjMsvaLNinAHNDYyvkyU+SZG2BTSbT5NjG+vZslfGTA==
dependencies:
cross-spawn "^7.0.0"
signal-exit "^4.0.1"
@@ -1716,11 +1806,6 @@ fraction.js@^4.3.7:
resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.3.7.tgz#06ca0085157e42fda7f9e726e79fefc4068840f7"
integrity sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==
-fs.realpath@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
- integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==
-
fsevents@~2.3.2:
version "2.3.3"
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6"
@@ -1736,7 +1821,7 @@ gensync@^1.0.0-beta.2:
resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0"
integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==
-get-intrinsic@^1.1.3, get-intrinsic@^1.2.3, get-intrinsic@^1.2.4:
+get-intrinsic@^1.1.3, get-intrinsic@^1.2.4:
version "1.2.4"
resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd"
integrity sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==
@@ -1767,39 +1852,26 @@ glob-parent@^6.0.2:
is-glob "^4.0.3"
glob@^10.3.10:
- version "10.3.10"
- resolved "https://registry.yarnpkg.com/glob/-/glob-10.3.10.tgz#0351ebb809fd187fe421ab96af83d3a70715df4b"
- integrity sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==
+ version "10.4.2"
+ resolved "https://registry.yarnpkg.com/glob/-/glob-10.4.2.tgz#bed6b95dade5c1f80b4434daced233aee76160e5"
+ integrity sha512-GwMlUF6PkPo3Gk21UxkCohOv0PLcIXVtKyLlpEI28R/cO/4eNOdmLk3CMW1wROV/WR/EsZOWAfBbBOqYvs88/w==
dependencies:
foreground-child "^3.1.0"
- jackspeak "^2.3.5"
- minimatch "^9.0.1"
- minipass "^5.0.0 || ^6.0.2 || ^7.0.0"
- path-scurry "^1.10.1"
-
-glob@^7.1.3:
- version "7.2.3"
- resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b"
- integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==
- dependencies:
- fs.realpath "^1.0.0"
- inflight "^1.0.4"
- inherits "2"
- minimatch "^3.1.1"
- once "^1.3.0"
- path-is-absolute "^1.0.0"
+ jackspeak "^3.1.2"
+ minimatch "^9.0.4"
+ minipass "^7.1.2"
+ package-json-from-dist "^1.0.0"
+ path-scurry "^1.11.1"
globals@^11.1.0:
version "11.12.0"
resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e"
integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==
-globals@^13.19.0:
- version "13.24.0"
- resolved "https://registry.yarnpkg.com/globals/-/globals-13.24.0.tgz#8432a19d78ce0c1e833949c36adb345400bb1171"
- integrity sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==
- dependencies:
- type-fest "^0.20.2"
+globals@^14.0.0:
+ version "14.0.0"
+ resolved "https://registry.yarnpkg.com/globals/-/globals-14.0.0.tgz#898d7413c29babcf6bafe56fcadded858ada724e"
+ integrity sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==
globby@^11.1.0:
version "11.1.0"
@@ -1835,7 +1907,7 @@ has-flag@^4.0.0:
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b"
integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
-has-property-descriptors@^1.0.1:
+has-property-descriptors@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854"
integrity sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==
@@ -1852,14 +1924,70 @@ has-symbols@^1.0.3:
resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8"
integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==
-hasown@^2.0.0:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.1.tgz#26f48f039de2c0f8d3356c223fb8d50253519faa"
- integrity sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA==
+hasown@^2.0.0, hasown@^2.0.2:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003"
+ integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==
dependencies:
function-bind "^1.1.2"
-ignore@^5.2.0:
+html-dom-parser@5.0.10:
+ version "5.0.10"
+ resolved "https://registry.yarnpkg.com/html-dom-parser/-/html-dom-parser-5.0.10.tgz#bf46b05c50f35c2fcadfc8e91566c54d3caf9bd7"
+ integrity sha512-GwArYL3V3V8yU/mLKoFF7HlLBv80BZ2Ey1BzfVNRpAci0cEKhFHI/Qh8o8oyt3qlAMLlK250wsxLdYX4viedvg==
+ dependencies:
+ domhandler "5.0.3"
+ htmlparser2 "9.1.0"
+
+html-parse-stringify@^3.0.1:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz#dfc1017347ce9f77c8141a507f233040c59c55d2"
+ integrity sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==
+ dependencies:
+ void-elements "3.1.0"
+
+html-react-parser@^5.1.15:
+ version "5.1.15"
+ resolved "https://registry.yarnpkg.com/html-react-parser/-/html-react-parser-5.1.15.tgz#f3121f31434ba5a84d06c8849e2e6fe0f64eed20"
+ integrity sha512-LRwSTseAZtdtzYbBaN0a+pJ48x4qmwPzQC5tvwAp9IvuNf7afxtTHLpCPYCsVjRKRUqhXvfjTaKJJrhctxkHJA==
+ dependencies:
+ domhandler "5.0.3"
+ html-dom-parser "5.0.10"
+ react-property "2.0.2"
+ style-to-js "1.1.13"
+
+htmlparser2@9.1.0:
+ version "9.1.0"
+ resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-9.1.0.tgz#cdb498d8a75a51f739b61d3f718136c369bc8c23"
+ integrity sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ==
+ dependencies:
+ domelementtype "^2.3.0"
+ domhandler "^5.0.3"
+ domutils "^3.1.0"
+ entities "^4.5.0"
+
+i18next-browser-languagedetector@^8.0.0:
+ version "8.0.0"
+ resolved "https://registry.yarnpkg.com/i18next-browser-languagedetector/-/i18next-browser-languagedetector-8.0.0.tgz#b6fdd9b43af67c47f2c26c9ba27710a1eaf31e2f"
+ integrity sha512-zhXdJXTTCoG39QsrOCiOabnWj2jecouOqbchu3EfhtSHxIB5Uugnm9JaizenOy39h7ne3+fLikIjeW88+rgszw==
+ dependencies:
+ "@babel/runtime" "^7.23.2"
+
+i18next-http-backend@^2.5.2:
+ version "2.5.2"
+ resolved "https://registry.yarnpkg.com/i18next-http-backend/-/i18next-http-backend-2.5.2.tgz#3d846cc239987fe7700d1cf0f17975807bfd25d3"
+ integrity sha512-+K8HbDfrvc1/2X8jpb7RLhI9ZxBDpx3xogYkQwGKlWAUXLSEGXzgdt3EcUjLlBCdMwdQY+K+EUF6oh8oB6rwHw==
+ dependencies:
+ cross-fetch "4.0.0"
+
+i18next@^23.11.5:
+ version "23.11.5"
+ resolved "https://registry.yarnpkg.com/i18next/-/i18next-23.11.5.tgz#d71eb717a7e65498d87d0594f2664237f9e361ef"
+ integrity sha512-41pvpVbW9rhZPk5xjCX2TPJi2861LEig/YRhUkY+1FQ2IQPS0bKUDYnEqY8XPPbB48h1uIwLnP9iiEfuSl20CA==
+ dependencies:
+ "@babel/runtime" "^7.23.2"
+
+ignore@^5.2.0, ignore@^5.3.1:
version "5.3.1"
resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.1.tgz#5073e554cd42c5b33b394375f538b8593e34d4ef"
integrity sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==
@@ -1877,24 +2005,16 @@ imurmurhash@^0.1.4:
resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==
-inflight@^1.0.4:
- version "1.0.6"
- resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
- integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==
- dependencies:
- once "^1.3.0"
- wrappy "1"
-
-inherits@2:
- version "2.0.4"
- resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
- integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
-
inherits@2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
integrity sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==
+inline-style-parser@0.2.3:
+ version "0.2.3"
+ resolved "https://registry.yarnpkg.com/inline-style-parser/-/inline-style-parser-0.2.3.tgz#e35c5fb45f3a83ed7849fe487336eb7efa25971c"
+ integrity sha512-qlD8YNDqyTKTyuITrDOffsl6Tdhv+UC4hcdAVuQsK4IMQ99nSgd1MIA/Q+jQYoh9r3hVUXhYh7urSRmXPkW04g==
+
invariant@^2.2.4:
version "2.2.4"
resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6"
@@ -1910,11 +2030,11 @@ is-binary-path@~2.1.0:
binary-extensions "^2.0.0"
is-core-module@^2.13.0:
- version "2.13.1"
- resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.1.tgz#ad0d7532c6fea9da1ebdc82742d74525c6273384"
- integrity sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==
+ version "2.14.0"
+ resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.14.0.tgz#43b8ef9f46a6a08888db67b1ffd4ec9e3dfd59d1"
+ integrity sha512-a5dFJih5ZLYlRtDc0dZWP7RiKr6xIKzmn/oAYCDvdLThadVgyJwlaoQPmRtMSpz+rk0OGAgIu+TcM9HUF0fk1A==
dependencies:
- hasown "^2.0.0"
+ hasown "^2.0.2"
is-extglob@^2.1.1:
version "2.1.1"
@@ -1948,19 +2068,19 @@ isexe@^2.0.0:
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==
-jackspeak@^2.3.5:
- version "2.3.6"
- resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-2.3.6.tgz#647ecc472238aee4b06ac0e461acc21a8c505ca8"
- integrity sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==
+jackspeak@^3.1.2:
+ version "3.4.0"
+ resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-3.4.0.tgz#a75763ff36ad778ede6a156d8ee8b124de445b4a"
+ integrity sha512-JVYhQnN59LVPFCEcVa2C3CrEKYacvjRfqIQl+h8oi91aLYQVWRYbxjPcv1bUiUy/kLmQaANrYfNMCO3kuEDHfw==
dependencies:
"@isaacs/cliui" "^8.0.2"
optionalDependencies:
"@pkgjs/parseargs" "^0.11.0"
-jiti@^1.19.1:
- version "1.21.0"
- resolved "https://registry.yarnpkg.com/jiti/-/jiti-1.21.0.tgz#7c97f8fe045724e136a397f7340475244156105d"
- integrity sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==
+jiti@^1.21.0:
+ version "1.21.6"
+ resolved "https://registry.yarnpkg.com/jiti/-/jiti-1.21.6.tgz#6c7f7398dd4b3142767f9a168af2f317a428d268"
+ integrity sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==
"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
version "4.0.0"
@@ -1999,7 +2119,7 @@ json5@^2.2.3:
resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283"
integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==
-keyv@^4.5.3:
+keyv@^4.5.4:
version "4.5.4"
resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93"
integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==
@@ -2020,9 +2140,9 @@ lilconfig@^2.1.0:
integrity sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==
lilconfig@^3.0.0:
- version "3.1.1"
- resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-3.1.1.tgz#9d8a246fa753106cfc205fd2d77042faca56e5e3"
- integrity sha512-O18pf7nyvHTckunPWCV1XUNXU1piu01y2b7ATJ0ppkUkk8ocqVWBrYjJBCwHDjD/ZWcfyrA0P4gKhzWGi5EINQ==
+ version "3.1.2"
+ resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-3.1.2.tgz#e4a7c3cb549e3a606c8dcc32e5ae1005e62c05cb"
+ integrity sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==
lines-and-columns@^1.1.6:
version "1.2.4"
@@ -2048,6 +2168,11 @@ loose-envify@^1.0.0, loose-envify@^1.1.0:
dependencies:
js-tokens "^3.0.0 || ^4.0.0"
+lru-cache@^10.2.0:
+ version "10.2.2"
+ resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.2.2.tgz#48206bc114c1252940c41b25b41af5b545aca878"
+ integrity sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==
+
lru-cache@^5.1.1:
version "5.1.1"
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920"
@@ -2055,22 +2180,10 @@ lru-cache@^5.1.1:
dependencies:
yallist "^3.0.2"
-lru-cache@^6.0.0:
- version "6.0.0"
- resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94"
- integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==
- dependencies:
- yallist "^4.0.0"
-
-"lru-cache@^9.1.1 || ^10.0.0":
- version "10.2.0"
- resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.2.0.tgz#0bd445ca57363465900f4d1f9bd8db343a4d95c3"
- integrity sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==
-
-lucide-react@^0.252.0:
- version "0.252.0"
- resolved "https://registry.yarnpkg.com/lucide-react/-/lucide-react-0.252.0.tgz#4fd8829c95456a9de6a3377b395dd412bf8345d7"
- integrity sha512-98hUdm23F3YlC3UN4mzv1FAsWr81YYdxF31cYhm19c51FwOph4dn5B4NjKp45UXBiR1Xx+cKrdmSIZX0ldS8zw==
+lucide-react@^0.396.0:
+ version "0.396.0"
+ resolved "https://registry.yarnpkg.com/lucide-react/-/lucide-react-0.396.0.tgz#f9a7afdfa0911bcd6eb3107d4bc60564f0399cf5"
+ integrity sha512-N/zP+9vEfEYHiMWMpjwH/M5diaV0e4UFe07BpgdzaRYce5QvXi87hixf7F0Xqdr3zuX/9u7H/2D4MVXIK22O0A==
merge2@^1.3.0, merge2@^1.4.1:
version "1.4.1"
@@ -2078,31 +2191,31 @@ merge2@^1.3.0, merge2@^1.4.1:
integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==
micromatch@^4.0.4, micromatch@^4.0.5:
- version "4.0.5"
- resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6"
- integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==
+ version "4.0.7"
+ resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.7.tgz#33e8190d9fe474a9895525f5618eee136d46c2e5"
+ integrity sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==
dependencies:
- braces "^3.0.2"
+ braces "^3.0.3"
picomatch "^2.3.1"
-minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2:
+minimatch@^3.0.5, minimatch@^3.1.2:
version "3.1.2"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
dependencies:
brace-expansion "^1.1.7"
-minimatch@^9.0.1:
- version "9.0.3"
- resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.3.tgz#a6e00c3de44c3a542bfaae70abfc22420a6da825"
- integrity sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==
+minimatch@^9.0.4:
+ version "9.0.4"
+ resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.4.tgz#8e49c731d1749cbec05050ee5145147b32496a51"
+ integrity sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==
dependencies:
brace-expansion "^2.0.1"
-"minipass@^5.0.0 || ^6.0.2 || ^7.0.0":
- version "7.0.4"
- resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.0.4.tgz#dbce03740f50a4786ba994c1fb908844d27b038c"
- integrity sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==
+"minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.1.2:
+ version "7.1.2"
+ resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707"
+ integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==
ms@2.1.2:
version "2.1.2"
@@ -2123,16 +2236,23 @@ nanoid@^3.3.7:
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.7.tgz#d0c301a691bc8d54efa0a2226ccf3fe2fd656bd8"
integrity sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==
-natural-compare-lite@^1.4.0:
- version "1.4.0"
- resolved "https://registry.yarnpkg.com/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz#17b09581988979fddafe0201e931ba933c96cbb4"
- integrity sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==
-
natural-compare@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==
+next-themes@^0.3.0:
+ version "0.3.0"
+ resolved "https://registry.yarnpkg.com/next-themes/-/next-themes-0.3.0.tgz#b4d2a866137a67d42564b07f3a3e720e2ff3871a"
+ integrity sha512-/QHIrsYpd6Kfk7xakK4svpDI5mmXP0gfvCoJdGpZQ2TOrQZmsW0QxjaiLn8wbIKjtm4BTSqLoix4lxYYOnLJ/w==
+
+node-fetch@^2.6.12:
+ version "2.7.0"
+ resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d"
+ integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==
+ dependencies:
+ whatwg-url "^5.0.0"
+
node-releases@^2.0.14:
version "2.0.14"
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.14.tgz#2ffb053bceb8b2be8495ece1ab6ce600c4461b0b"
@@ -2159,28 +2279,21 @@ object-hash@^3.0.0:
integrity sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==
object-inspect@^1.13.1:
- version "1.13.1"
- resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.1.tgz#b96c6109324ccfef6b12216a956ca4dc2ff94bc2"
- integrity sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==
-
-once@^1.3.0:
- version "1.4.0"
- resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
- integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==
- dependencies:
- wrappy "1"
+ version "1.13.2"
+ resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.2.tgz#dea0088467fb991e67af4058147a24824a3043ff"
+ integrity sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==
optionator@^0.9.3:
- version "0.9.3"
- resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.3.tgz#007397d44ed1872fdc6ed31360190f81814e2c64"
- integrity sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==
+ version "0.9.4"
+ resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.4.tgz#7ea1c1a5d91d764fb282139c88fe11e182a3a734"
+ integrity sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==
dependencies:
- "@aashutoshrathi/word-wrap" "^1.2.3"
deep-is "^0.1.3"
fast-levenshtein "^2.0.6"
levn "^0.4.1"
prelude-ls "^1.2.1"
type-check "^0.4.0"
+ word-wrap "^1.2.5"
p-limit@^3.0.2:
version "3.1.0"
@@ -2196,6 +2309,11 @@ p-locate@^5.0.0:
dependencies:
p-limit "^3.0.2"
+package-json-from-dist@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz#e501cd3094b278495eb4258d4c9f6d5ac3019f00"
+ integrity sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==
+
parent-module@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2"
@@ -2208,11 +2326,6 @@ path-exists@^4.0.0:
resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3"
integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==
-path-is-absolute@^1.0.0:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
- integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==
-
path-key@^3.1.0:
version "3.1.1"
resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375"
@@ -2223,12 +2336,12 @@ path-parse@^1.0.7:
resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
-path-scurry@^1.10.1:
- version "1.10.1"
- resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.10.1.tgz#9ba6bf5aa8500fe9fd67df4f0d9483b2b0bfc698"
- integrity sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==
+path-scurry@^1.11.1:
+ version "1.11.1"
+ resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.11.1.tgz#7960a668888594a0720b12a911d1a742ab9f11d2"
+ integrity sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==
dependencies:
- lru-cache "^9.1.1 || ^10.0.0"
+ lru-cache "^10.2.0"
minipass "^5.0.0 || ^6.0.2 || ^7.0.0"
path-type@^4.0.0:
@@ -2244,10 +2357,10 @@ path@^0.12.7:
process "^0.11.1"
util "^0.10.3"
-picocolors@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c"
- integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==
+picocolors@^1.0.0, picocolors@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.1.tgz#a8ad579b571952f0e5d25892de5445bcfe25aaa1"
+ integrity sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==
picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1:
version "2.3.1"
@@ -2296,9 +2409,9 @@ postcss-nested@^6.0.1:
postcss-selector-parser "^6.0.11"
postcss-selector-parser@^6.0.11:
- version "6.0.15"
- resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.15.tgz#11cc2b21eebc0b99ea374ffb9887174855a01535"
- integrity sha512-rEYkQOMUCEMhsKbK66tbEU9QVIxbhN18YiniAwA7XQYTVBqrBy+P2p5JcdqsHgKM2zWylp8d7J6eszocfds5Sw==
+ version "6.1.0"
+ resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.1.0.tgz#49694cb4e7c649299fea510a29fa6577104bcf53"
+ integrity sha512-UMz42UD0UY0EApS0ZL9o1XnLhSTtvvvLe5Dc2H2O56fvRZi+KulDyf5ctDhhtYJBGKStV2FL1fy6253cmLgqVQ==
dependencies:
cssesc "^3.0.0"
util-deprecate "^1.0.2"
@@ -2308,14 +2421,23 @@ postcss-value-parser@^4.0.0, postcss-value-parser@^4.2.0:
resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514"
integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==
-postcss@^8.4.17, postcss@^8.4.23, postcss@^8.4.27:
- version "8.4.35"
- resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.35.tgz#60997775689ce09011edf083a549cea44aabe2f7"
- integrity sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==
+postcss@^8.4.23, postcss@^8.4.38:
+ version "8.4.38"
+ resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.38.tgz#b387d533baf2054288e337066d81c6bee9db9e0e"
+ integrity sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==
dependencies:
nanoid "^3.3.7"
picocolors "^1.0.0"
- source-map-js "^1.0.2"
+ source-map-js "^1.2.0"
+
+postcss@^8.4.27:
+ version "8.4.39"
+ resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.39.tgz#aa3c94998b61d3a9c259efa51db4b392e1bde0e3"
+ integrity sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw==
+ dependencies:
+ nanoid "^3.3.7"
+ picocolors "^1.0.1"
+ source-map-js "^1.2.0"
prelude-ls@^1.2.1:
version "1.2.1"
@@ -2338,47 +2460,60 @@ punycode@^2.1.0:
integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==
qs@^6.11.2:
- version "6.11.2"
- resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.2.tgz#64bea51f12c1f5da1bc01496f48ffcff7c69d7d9"
- integrity sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA==
+ version "6.12.1"
+ resolved "https://registry.yarnpkg.com/qs/-/qs-6.12.1.tgz#39422111ca7cbdb70425541cba20c7d7b216599a"
+ integrity sha512-zWmv4RSuB9r2mYQw3zxQuHWeU+42aKi1wWig/j4ele4ygELZ7PEO6MM7rim9oAQH2A5MWfsAVf/jPvTPgCbvUQ==
dependencies:
- side-channel "^1.0.4"
+ side-channel "^1.0.6"
queue-microtask@^1.2.2:
version "1.2.3"
resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243"
integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==
-react-dom@^18.2.0:
- version "18.2.0"
- resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d"
- integrity sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==
+react-dom@^18.3.1:
+ version "18.3.1"
+ resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.3.1.tgz#c2265d79511b57d479b3dd3fdfa51536494c5cb4"
+ integrity sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==
dependencies:
loose-envify "^1.1.0"
- scheduler "^0.23.0"
+ scheduler "^0.23.2"
-react-hook-form@^7.51.0:
- version "7.51.0"
- resolved "https://registry.yarnpkg.com/react-hook-form/-/react-hook-form-7.51.0.tgz#757ae71b37c26e00590bd3788508287dcc5ecdaf"
- integrity sha512-BggOy5j58RdhdMzzRUHGOYhSz1oeylFAv6jUSG86OvCIvlAvS7KvnRY7yoAf2pfEiPN7BesnR0xx73nEk3qIiw==
+react-hook-form@^7.52.1:
+ version "7.52.1"
+ resolved "https://registry.yarnpkg.com/react-hook-form/-/react-hook-form-7.52.1.tgz#ec2c96437b977f8b89ae2d541a70736c66284852"
+ integrity sha512-uNKIhaoICJ5KQALYZ4TOaOLElyM+xipord+Ha3crEFhTntdLvWZqVY49Wqd/0GiVCA/f9NjemLeiNPjG7Hpurg==
-react-refresh@^0.14.0:
- version "0.14.0"
- resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.14.0.tgz#4e02825378a5f227079554d4284889354e5f553e"
- integrity sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==
+react-i18next@^14.1.2:
+ version "14.1.2"
+ resolved "https://registry.yarnpkg.com/react-i18next/-/react-i18next-14.1.2.tgz#cd57a755f25a32a5fcc3dbe546cf3cc62b4f3ebd"
+ integrity sha512-FSIcJy6oauJbGEXfhUgVeLzvWBhIBIS+/9c6Lj4niwKZyGaGb4V4vUbATXSlsHJDXXB+ociNxqFNiFuV1gmoqg==
+ dependencies:
+ "@babel/runtime" "^7.23.9"
+ html-parse-stringify "^3.0.1"
+
+react-property@2.0.2:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/react-property/-/react-property-2.0.2.tgz#d5ac9e244cef564880a610bc8d868bd6f60fdda6"
+ integrity sha512-+PbtI3VuDV0l6CleQMsx2gtK0JZbZKbpdu5ynr+lbsuvtmgbNcS3VM0tuY2QjFNOcWxvXeHjDpy42RO+4U2rug==
-react-remove-scroll-bar@^2.3.3:
- version "2.3.5"
- resolved "https://registry.yarnpkg.com/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.5.tgz#cd2543b3ed7716c7c5b446342d21b0e0b303f47c"
- integrity sha512-3cqjOqg6s0XbOjWvmasmqHch+RLxIEk2r/70rzGXuz3iIGQsQheEQyqYCBb5EECoD01Vo2SIbDqW4paLeLTASw==
+react-refresh@^0.14.2:
+ version "0.14.2"
+ resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.14.2.tgz#3833da01ce32da470f1f936b9d477da5c7028bf9"
+ integrity sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==
+
+react-remove-scroll-bar@^2.3.3, react-remove-scroll-bar@^2.3.4:
+ version "2.3.6"
+ resolved "https://registry.yarnpkg.com/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.6.tgz#3e585e9d163be84a010180b18721e851ac81a29c"
+ integrity sha512-DtSYaao4mBmX+HDo5YWYdBWQwYIQQshUV/dVxFxK+KM26Wjwp1gZ6rv6OC3oujI6Bfu6Xyg3TwK533AQutsn/g==
dependencies:
react-style-singleton "^2.2.1"
tslib "^2.0.0"
-react-remove-scroll@2.5.4:
- version "2.5.4"
- resolved "https://registry.yarnpkg.com/react-remove-scroll/-/react-remove-scroll-2.5.4.tgz#afe6491acabde26f628f844b67647645488d2ea0"
- integrity sha512-xGVKJJr0SJGQVirVFAUZ2k1QLyO6m+2fy0l8Qawbp5Jgrv3DeLalrfMNBFSlmz5kriGGzsVBtGVnf4pTKIhhWA==
+react-remove-scroll@2.5.5:
+ version "2.5.5"
+ resolved "https://registry.yarnpkg.com/react-remove-scroll/-/react-remove-scroll-2.5.5.tgz#1e31a1260df08887a8a0e46d09271b52b3a37e77"
+ integrity sha512-ImKhrzJJsyXJfBZ4bzu8Bwpka14c/fQt0k+cyFp/PBhTfyDnU5hjOtM4AG/0AMyy8oKzOTR0lDgJIM7pYXI0kw==
dependencies:
react-remove-scroll-bar "^2.3.3"
react-style-singleton "^2.2.1"
@@ -2386,12 +2521,12 @@ react-remove-scroll@2.5.4:
use-callback-ref "^1.3.0"
use-sidecar "^1.1.2"
-react-remove-scroll@2.5.5:
- version "2.5.5"
- resolved "https://registry.yarnpkg.com/react-remove-scroll/-/react-remove-scroll-2.5.5.tgz#1e31a1260df08887a8a0e46d09271b52b3a37e77"
- integrity sha512-ImKhrzJJsyXJfBZ4bzu8Bwpka14c/fQt0k+cyFp/PBhTfyDnU5hjOtM4AG/0AMyy8oKzOTR0lDgJIM7pYXI0kw==
+react-remove-scroll@2.5.7:
+ version "2.5.7"
+ resolved "https://registry.yarnpkg.com/react-remove-scroll/-/react-remove-scroll-2.5.7.tgz#15a1fd038e8497f65a695bf26a4a57970cac1ccb"
+ integrity sha512-FnrTWO4L7/Bhhf3CYBNArEG/yROV0tKmTv7/3h9QCFvH6sndeFf1wPqOcbFVu5VAulS5dV1wGT3GZZ/1GawqiA==
dependencies:
- react-remove-scroll-bar "^2.3.3"
+ react-remove-scroll-bar "^2.3.4"
react-style-singleton "^2.2.1"
tslib "^2.1.0"
use-callback-ref "^1.3.0"
@@ -2406,10 +2541,10 @@ react-style-singleton@^2.2.1:
invariant "^2.2.4"
tslib "^2.0.0"
-react@^18.2.0:
- version "18.2.0"
- resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5"
- integrity sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==
+react@^18.3.1:
+ version "18.3.1"
+ resolved "https://registry.yarnpkg.com/react/-/react-18.3.1.tgz#49ab892009c53933625bd16b2533fc754cab2891"
+ integrity sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==
dependencies:
loose-envify "^1.1.0"
@@ -2451,13 +2586,6 @@ reusify@^1.0.4:
resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76"
integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==
-rimraf@^3.0.2:
- version "3.0.2"
- resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a"
- integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==
- dependencies:
- glob "^7.1.3"
-
rollup@^3.27.1:
version "3.29.4"
resolved "https://registry.yarnpkg.com/rollup/-/rollup-3.29.4.tgz#4d70c0f9834146df8705bfb69a9a19c9e1109981"
@@ -2472,10 +2600,10 @@ run-parallel@^1.1.9:
dependencies:
queue-microtask "^1.2.2"
-scheduler@^0.23.0:
- version "0.23.0"
- resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.0.tgz#ba8041afc3d30eb206a487b6b384002e4e61fdfe"
- integrity sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==
+scheduler@^0.23.2:
+ version "0.23.2"
+ resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.2.tgz#414ba64a3b282892e944cf2108ecc078d115cdc3"
+ integrity sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==
dependencies:
loose-envify "^1.1.0"
@@ -2484,24 +2612,22 @@ semver@^6.3.1:
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4"
integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==
-semver@^7.3.7:
- version "7.6.0"
- resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.0.tgz#1a46a4db4bffcccd97b743b5005c8325f23d4e2d"
- integrity sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==
- dependencies:
- lru-cache "^6.0.0"
+semver@^7.6.0:
+ version "7.6.2"
+ resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.2.tgz#1e3b34759f896e8f14d6134732ce798aeb0c6e13"
+ integrity sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==
set-function-length@^1.2.1:
- version "1.2.1"
- resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.1.tgz#47cc5945f2c771e2cf261c6737cf9684a2a5e425"
- integrity sha512-j4t6ccc+VsKwYHso+kElc5neZpjtq9EnRICFZtWyBsLojhmeF/ZBd/elqm22WJh/BziDe/SBiOeAt0m2mfLD0g==
+ version "1.2.2"
+ resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449"
+ integrity sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==
dependencies:
- define-data-property "^1.1.2"
+ define-data-property "^1.1.4"
es-errors "^1.3.0"
function-bind "^1.1.2"
- get-intrinsic "^1.2.3"
+ get-intrinsic "^1.2.4"
gopd "^1.0.1"
- has-property-descriptors "^1.0.1"
+ has-property-descriptors "^1.0.2"
shebang-command@^2.0.0:
version "2.0.0"
@@ -2515,7 +2641,7 @@ shebang-regex@^3.0.0:
resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172"
integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==
-side-channel@^1.0.4:
+side-channel@^1.0.6:
version "1.0.6"
resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.6.tgz#abd25fb7cd24baf45466406b1096b7831c9215f2"
integrity sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==
@@ -2535,10 +2661,15 @@ slash@^3.0.0:
resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634"
integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==
-source-map-js@^1.0.2:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c"
- integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==
+sonner@^1.5.0:
+ version "1.5.0"
+ resolved "https://registry.yarnpkg.com/sonner/-/sonner-1.5.0.tgz#af359f817063318415326b33aab54c5d17c747b7"
+ integrity sha512-FBjhG/gnnbN6FY0jaNnqZOMmB73R+5IiyYAw8yBj7L54ER7HB3fOSE5OFiQiE2iXWxeXKvg6fIP4LtVppHEdJA==
+
+source-map-js@^1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.0.tgz#16b809c162517b5b8c3e7dcd315a2a5c2612b2af"
+ integrity sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==
"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0:
name string-width-cjs
@@ -2578,6 +2709,20 @@ strip-json-comments@^3.1.1:
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006"
integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==
+style-to-js@1.1.13:
+ version "1.1.13"
+ resolved "https://registry.yarnpkg.com/style-to-js/-/style-to-js-1.1.13.tgz#8d17f2560a9cc9515b94834aaeb2305887d7ced5"
+ integrity sha512-+43kvxwjrW9n5gFR40Rv98A0/Mcjew7Lt+p5Nnw1KGR9SZf/ZaKqmMwl9Enj9EnYNcJ5VzuCjejC5KZzvH2lOA==
+ dependencies:
+ style-to-object "1.0.6"
+
+style-to-object@1.0.6:
+ version "1.0.6"
+ resolved "https://registry.yarnpkg.com/style-to-object/-/style-to-object-1.0.6.tgz#0c28aed8be1813d166c60d962719b2907c26547b"
+ integrity sha512-khxq+Qm3xEyZfKd/y9L3oIWQimxuc4STrQKtQn8aSDRHb8mFgpukgX1hdzfrMEW6JCjyJ8p89x+IUMVnCBI1PA==
+ dependencies:
+ inline-style-parser "0.2.3"
+
sucrase@^3.32.0:
version "3.35.0"
resolved "https://registry.yarnpkg.com/sucrase/-/sucrase-3.35.0.tgz#57f17a3d7e19b36d8995f06679d121be914ae263"
@@ -2610,20 +2755,27 @@ supports-preserve-symlinks-flag@^1.0.0:
resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
-tailwind-merge@^1.13.2:
- version "1.14.0"
- resolved "https://registry.yarnpkg.com/tailwind-merge/-/tailwind-merge-1.14.0.tgz#e677f55d864edc6794562c63f5001f45093cdb8b"
- integrity sha512-3mFKyCo/MBcgyOTlrY8T7odzZFx+w+qKSMAmdFzRvqBfLlSigU6TZnlFHK0lkMwj9Bj8OYU+9yW9lmGuS0QEnQ==
+tailwind-merge@^2.3.0:
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/tailwind-merge/-/tailwind-merge-2.3.0.tgz#27d2134fd00a1f77eca22bcaafdd67055917d286"
+ integrity sha512-vkYrLpIP+lgR0tQCG6AP7zZXCTLc1Lnv/CCRT3BqJ9CZ3ui2++GPaGb1x/ILsINIMSYqqvrpqjUFsMNLlW99EA==
+ dependencies:
+ "@babel/runtime" "^7.24.1"
-tailwindcss-animate@^1.0.6:
+tailwind-scrollbar@^3.1.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/tailwind-scrollbar/-/tailwind-scrollbar-3.1.0.tgz#ff7596407b6da5209261d8ff03860ab9206a59e3"
+ integrity sha512-pmrtDIZeHyu2idTejfV59SbaJyvp1VRjYxAjZBH0jnyrPRo6HL1kD5Glz8VPagasqr6oAx6M05+Tuw429Z8jxg==
+
+tailwindcss-animate@^1.0.7:
version "1.0.7"
resolved "https://registry.yarnpkg.com/tailwindcss-animate/-/tailwindcss-animate-1.0.7.tgz#318b692c4c42676cc9e67b19b78775742388bef4"
integrity sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA==
-tailwindcss@^3.1.8:
- version "3.4.1"
- resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-3.4.1.tgz#f512ca5d1dd4c9503c7d3d28a968f1ad8f5c839d"
- integrity sha512-qAYmXRfk3ENzuPBakNK0SRrUDipP8NQnEY6772uDhflcQz5EhRdD7JNZxyrFHVQNCwULPBn6FNPp9brpO7ctcA==
+tailwindcss@^3.4.4:
+ version "3.4.4"
+ resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-3.4.4.tgz#351d932273e6abfa75ce7d226b5bf3a6cb257c05"
+ integrity sha512-ZoyXOdJjISB7/BcLTR6SEsLgKtDStYyYZVLsUtWChO4Ps20CBad7lfJKVDiejocV4ME1hLmyY0WJE3hSDcmQ2A==
dependencies:
"@alloc/quick-lru" "^5.2.0"
arg "^5.0.2"
@@ -2633,7 +2785,7 @@ tailwindcss@^3.1.8:
fast-glob "^3.3.0"
glob-parent "^6.0.2"
is-glob "^4.0.3"
- jiti "^1.19.1"
+ jiti "^1.21.0"
lilconfig "^2.1.0"
micromatch "^4.0.5"
normalize-path "^3.0.0"
@@ -2679,27 +2831,25 @@ to-regex-range@^5.0.1:
dependencies:
is-number "^7.0.0"
+tr46@~0.0.3:
+ version "0.0.3"
+ resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a"
+ integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==
+
+ts-api-utils@^1.3.0:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.3.0.tgz#4b490e27129f1e8e686b45cc4ab63714dc60eea1"
+ integrity sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==
+
ts-interface-checker@^0.1.9:
version "0.1.13"
resolved "https://registry.yarnpkg.com/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz#784fd3d679722bc103b1b4b8030bcddb5db2a699"
integrity sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==
-tslib@^1.8.1:
- version "1.14.1"
- resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
- integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
-
tslib@^2.0.0, tslib@^2.1.0:
- version "2.6.2"
- resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae"
- integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==
-
-tsutils@^3.21.0:
- version "3.21.0"
- resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623"
- integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==
- dependencies:
- tslib "^1.8.1"
+ version "2.6.3"
+ resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.3.tgz#0438f810ad7a9edcde7a241c3d80db693c8cbfe0"
+ integrity sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==
type-check@^0.4.0, type-check@~0.4.0:
version "0.4.0"
@@ -2708,23 +2858,18 @@ type-check@^0.4.0, type-check@~0.4.0:
dependencies:
prelude-ls "^1.2.1"
-type-fest@^0.20.2:
- version "0.20.2"
- resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4"
- integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==
-
-typescript@^5.0.2:
- version "5.3.3"
- resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.3.3.tgz#b3ce6ba258e72e6305ba66f5c9b452aaee3ffe37"
- integrity sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==
+typescript@^5.5.2:
+ version "5.5.2"
+ resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.5.2.tgz#c26f023cb0054e657ce04f72583ea2d85f8d0507"
+ integrity sha512-NcRtPEOsPFFWjobJEtfihkLCZCXZt/os3zf8nTxjVH3RvTSxjrCamJpbExGvYOF+tFHc3pA65qpdwPbzjohhew==
-update-browserslist-db@^1.0.13:
- version "1.0.13"
- resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz#3c5e4f5c083661bd38ef64b6328c26ed6c8248c4"
- integrity sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==
+update-browserslist-db@^1.0.16:
+ version "1.0.16"
+ resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.16.tgz#f6d489ed90fb2f07d67784eb3f53d7891f736356"
+ integrity sha512-KVbTxlBYlckhF5wgfyZXTWnMn7MMZjMu9XG8bPlliUOP9ThaF4QnhP8qrjrH7DRzHfSk0oQv1wToW+iA5GajEQ==
dependencies:
- escalade "^3.1.1"
- picocolors "^1.0.0"
+ escalade "^3.1.2"
+ picocolors "^1.0.1"
uri-js@^4.2.2:
version "4.4.1"
@@ -2733,7 +2878,7 @@ uri-js@^4.2.2:
dependencies:
punycode "^2.1.0"
-url@^0.11.1:
+url@^0.11.3:
version "0.11.3"
resolved "https://registry.yarnpkg.com/url/-/url-0.11.3.tgz#6f495f4b935de40ce4a0a52faee8954244f3d3ad"
integrity sha512-6hxOLGfZASQK/cijlZnZJTq8OXAkt/3YGfQX45vvMYXpZoo8NdWZcY73K108Jf759lS1Bv/8wXnHDTSz17dSRw==
@@ -2742,9 +2887,9 @@ url@^0.11.1:
qs "^6.11.2"
use-callback-ref@^1.3.0:
- version "1.3.1"
- resolved "https://registry.yarnpkg.com/use-callback-ref/-/use-callback-ref-1.3.1.tgz#9be64c3902cbd72b07fe55e56408ae3a26036fd0"
- integrity sha512-Lg4Vx1XZQauB42Hw3kK7JM6yjVjgFmFC5/Ab797s79aARomD2nEErc4mCgM8EZrARLmmbWpi5DGCadmK50DcAQ==
+ version "1.3.2"
+ resolved "https://registry.yarnpkg.com/use-callback-ref/-/use-callback-ref-1.3.2.tgz#6134c7f6ff76e2be0b56c809b17a650c942b1693"
+ integrity sha512-elOQwe6Q8gqZgDA8mrh44qRTQqpIHDcZ3hXTLjBe1i4ph8XpNJnO+aQf3NaG+lriLopI4HMx9VjQLfPQ6vhnoA==
dependencies:
tslib "^2.0.0"
@@ -2768,10 +2913,10 @@ util@^0.10.3:
dependencies:
inherits "2.0.3"
-vite@^4.3.9:
- version "4.5.2"
- resolved "https://registry.yarnpkg.com/vite/-/vite-4.5.2.tgz#d6ea8610e099851dad8c7371599969e0f8b97e82"
- integrity sha512-tBCZBNSBbHQkaGyhGCDUGqeo2ph8Fstyp6FMSvTtsXeZSPpSMGlviAOav2hxVTqFcx8Hj/twtWKsMJXNY0xI8w==
+vite@^4.0.0:
+ version "4.5.3"
+ resolved "https://registry.yarnpkg.com/vite/-/vite-4.5.3.tgz#d88a4529ea58bae97294c7e2e6f0eab39a50fb1a"
+ integrity sha512-kQL23kMeX92v3ph7IauVkXkikdDRsYMGTVl5KY2E9OY4ONLvkHf04MDTbnfo6NKxZiDLWzVpP5oTa8hQD8U3dg==
dependencies:
esbuild "^0.18.10"
postcss "^8.4.27"
@@ -2779,6 +2924,24 @@ vite@^4.3.9:
optionalDependencies:
fsevents "~2.3.2"
+void-elements@3.1.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-3.1.0.tgz#614f7fbf8d801f0bb5f0661f5b2f5785750e4f09"
+ integrity sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==
+
+webidl-conversions@^3.0.0:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871"
+ integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==
+
+whatwg-url@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d"
+ integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==
+ dependencies:
+ tr46 "~0.0.3"
+ webidl-conversions "^3.0.0"
+
which@^2.0.1:
version "2.0.2"
resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1"
@@ -2786,6 +2949,11 @@ which@^2.0.1:
dependencies:
isexe "^2.0.0"
+word-wrap@^1.2.5:
+ version "1.2.5"
+ resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34"
+ integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==
+
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0":
version "7.0.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
@@ -2804,32 +2972,22 @@ wrap-ansi@^8.1.0:
string-width "^5.0.1"
strip-ansi "^7.0.1"
-wrappy@1:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
- integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==
-
yallist@^3.0.2:
version "3.1.1"
resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd"
integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==
-yallist@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"
- integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==
-
yaml@^2.3.4:
- version "2.4.0"
- resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.4.0.tgz#2376db1083d157f4b3a452995803dbcf43b08140"
- integrity sha512-j9iR8g+/t0lArF4V6NE/QCfT+CO7iLqrXAHZbJdo+LfjqP1vR8Fg5bSiaq6Q2lOD1AUEVrEVIgABvBFYojJVYQ==
+ version "2.4.5"
+ resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.4.5.tgz#60630b206dd6d84df97003d33fc1ddf6296cca5e"
+ integrity sha512-aBx2bnqDzVOyNKfsysjA2ms5ZlnjSAW2eG3/L5G/CSujfjLJTJsEw1bGw8kCf04KodQWk1pxlGnZ56CRxiawmg==
yocto-queue@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"
integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==
-zod@^3.22.4:
- version "3.22.4"
- resolved "https://registry.yarnpkg.com/zod/-/zod-3.22.4.tgz#f31c3a9386f61b1f228af56faa9255e845cf3fff"
- integrity sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==
+zod@^3.23.8:
+ version "3.23.8"
+ resolved "https://registry.yarnpkg.com/zod/-/zod-3.23.8.tgz#e37b957b5d52079769fb8097099b592f0ef4067d"
+ integrity sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==
diff --git a/go.mod b/go.mod
index c3d66ba..20ae0e2 100644
--- a/go.mod
+++ b/go.mod
@@ -1,19 +1,32 @@
-module desktop-manager
+module iconium
go 1.21
toolchain go1.22.0
require (
- github.com/google/uuid v1.6.0
+ github.com/gen2brain/beeep v0.0.0-20240516210008-9c006672e7f4
+ github.com/jeandeaual/go-locale v0.0.0-20240223122105-ce5225dcaa49
+ github.com/minio/selfupdate v0.6.0
+ github.com/orcaman/concurrent-map/v2 v2.0.1
github.com/parsiya/golnk v0.0.0-20221103095132-740a4c27c4ff
- github.com/wailsapp/wails/v2 v2.8.1
+ github.com/wailsapp/wails/v2 v2.9.1
+ gopkg.in/ini.v1 v1.67.0
)
require (
+ github.com/mattn/go-runewidth v0.0.13 // indirect
+ github.com/olekukonko/tablewriter v0.0.5 // indirect
+)
+
+require (
+ aead.dev/minisign v0.2.0 // indirect
github.com/bep/debounce v1.2.1 // indirect
- github.com/go-ole/go-ole v1.2.6 // indirect
+ github.com/blang/semver v3.5.1+incompatible
+ github.com/go-ole/go-ole v1.3.0
+ github.com/go-toast/toast v0.0.0-20190211030409-01e6764cf0a4 // indirect
github.com/godbus/dbus/v5 v5.1.0 // indirect
+ github.com/google/uuid v1.3.0
github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e // indirect
github.com/labstack/echo/v4 v4.10.2 // indirect
github.com/labstack/gommon v0.4.0 // indirect
@@ -23,22 +36,22 @@ require (
github.com/leaanthony/u v1.1.0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect
- github.com/mattn/go-runewidth v0.0.13 // indirect
- github.com/olekukonko/tablewriter v0.0.5 // indirect
+ github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d // indirect
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/rivo/uniseg v0.4.4 // indirect
github.com/samber/lo v1.38.1 // indirect
+ github.com/tadvi/systray v0.0.0-20190226123456-11a2b8fa57af // indirect
github.com/tkrajina/go-reflector v0.5.6 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasttemplate v1.2.2 // indirect
github.com/wailsapp/go-webview2 v1.0.10 // indirect
github.com/wailsapp/mimetype v1.4.1 // indirect
- golang.org/x/crypto v0.18.0 // indirect
+ golang.org/x/crypto v0.23.0 // indirect
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 // indirect
- golang.org/x/net v0.20.0 // indirect
- golang.org/x/sys v0.16.0 // indirect
- golang.org/x/text v0.14.0 // indirect
+ golang.org/x/net v0.25.0 // indirect
+ golang.org/x/sys v0.21.0
+ golang.org/x/text v0.15.0 // indirect
)
-// replace github.com/wailsapp/wails/v2 v2.8.0 => C:\Users\bedoy\go\pkg\mod
+// replace github.com/wailsapp/wails/v2 v2.9.1 => C:\Users\bedoy\go\pkg\mod
diff --git a/go.sum b/go.sum
index eb97fbc..3fb9685 100644
--- a/go.sum
+++ b/go.sum
@@ -1,16 +1,26 @@
+aead.dev/minisign v0.2.0 h1:kAWrq/hBRu4AARY6AlciO83xhNnW9UaC8YipS2uhLPk=
+aead.dev/minisign v0.2.0/go.mod h1:zdq6LdSd9TbuSxchxwhpA9zEb9YXcVGoE8JakuiGaIQ=
github.com/bep/debounce v1.2.1 h1:v67fRdBA9UQu2NhLFXrSg0Brw7CexQekrBwDMM8bzeY=
github.com/bep/debounce v1.2.1/go.mod h1:H8yggRPQKLUhUoqrJC1bO2xNya7vanpDl7xR3ISbCJ0=
+github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ=
+github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
-github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
+github.com/gen2brain/beeep v0.0.0-20240516210008-9c006672e7f4 h1:ygs9POGDQpQGLJPlq4+0LBUmMBNox1N4JSpw+OETcvI=
+github.com/gen2brain/beeep v0.0.0-20240516210008-9c006672e7f4/go.mod h1:0W7dI87PvXJ1Sjs0QPvWXKcQmNERY77e8l7GFhZB/s4=
+github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
+github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
+github.com/go-toast/toast v0.0.0-20190211030409-01e6764cf0a4 h1:qZNfIGkIANxGv/OqtnntR4DfOY2+BgwR60cAcu/i3SE=
+github.com/go-toast/toast v0.0.0-20190211030409-01e6764cf0a4/go.mod h1:kW3HQ4UdaAyrUCSSDR4xUzBKW6O2iA4uHhk7AtyYp10=
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
-github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
-github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
+github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e h1:Q3+PugElBCf4PFpxhErSzU3/PY5sFL5Z6rfv4AbGAck=
github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e/go.mod h1:alcuEEnZsY1WQsagKhZDsoPCRoOijYqhZvPwLG0kzVs=
+github.com/jeandeaual/go-locale v0.0.0-20240223122105-ce5225dcaa49 h1:Po+wkNdMmN+Zj1tDsJQy7mJlPlwGNQd9JZoPjObagf8=
+github.com/jeandeaual/go-locale v0.0.0-20240223122105-ce5225dcaa49/go.mod h1:YiutDnxPRLk5DLUFj6Rw4pRBBURZY07GFr54NdV9mQg=
github.com/labstack/echo/v4 v4.10.2 h1:n1jAhnq/elIFTHr1EYpiYtyKgx4RW9ccVgkqByZaN2M=
github.com/labstack/echo/v4 v4.10.2/go.mod h1:OEyqf2//K1DFdE57vw2DRgWY0M7s65IVQO2FzvI4J5k=
github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8=
@@ -38,8 +48,14 @@ github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
+github.com/minio/selfupdate v0.6.0 h1:i76PgT0K5xO9+hjzKcacQtO7+MjJ4JKA8Ak8XQ9DDwU=
+github.com/minio/selfupdate v0.6.0/go.mod h1:bO02GTIPCMQFTEvE5h4DjYB58bCoZ35XLeBf0buTDdM=
+github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d h1:VhgPp6v9qf9Agr/56bj7Y/xa04UccTW04VP0Qed4vnQ=
+github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d/go.mod h1:YUTz3bUH2ZwIWBy3CJBeOBEugqcmXREj14T+iG/4k4U=
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
+github.com/orcaman/concurrent-map/v2 v2.0.1 h1:jOJ5Pg2w1oeB6PeDurIYf6k9PQ+aTITr/6lP/L/zp6c=
+github.com/orcaman/concurrent-map/v2 v2.0.1/go.mod h1:9Eq3TG2oBe5FirmYWQfYO5iH1q0Jv47PLaNK++uCdOM=
github.com/parsiya/golnk v0.0.0-20221103095132-740a4c27c4ff h1:japdIZgV4tJIgn7NqUD7mAkLiPRsPK5LXVgjNwFtDA4=
github.com/parsiya/golnk v0.0.0-20221103095132-740a4c27c4ff/go.mod h1:A24WXUol4NXZlK8grjh/CsZnPlimfwaQFt5PQsqS27s=
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU=
@@ -57,6 +73,8 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
+github.com/tadvi/systray v0.0.0-20190226123456-11a2b8fa57af h1:6yITBqGTE2lEeTPG04SN9W+iWHCRyHqlVYILiSXziwk=
+github.com/tadvi/systray v0.0.0-20190226123456-11a2b8fa57af/go.mod h1:4F09kP5F+am0jAwlQLddpoMDM+iewkxxt6nxUQ5nq5o=
github.com/tkrajina/go-reflector v0.5.6 h1:hKQ0gyocG7vgMD2M3dRlYN6WBBOmdoOzJ6njQSepKdE=
github.com/tkrajina/go-reflector v0.5.6/go.mod h1:ECbqLgccecY5kPmPmXg1MrHW585yMcDkVl6IvJe64T4=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
@@ -68,33 +86,46 @@ github.com/wailsapp/go-webview2 v1.0.10 h1:PP5Hug6pnQEAhfRzLCoOh2jJaPdrqeRgJKZhy
github.com/wailsapp/go-webview2 v1.0.10/go.mod h1:Uk2BePfCRzttBBjFrBmqKGJd41P6QIHeV9kTgIeOZNo=
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.8.1 h1:KAudNjlFaiXnDfFEfSNoLoibJ1ovoutSrJ8poerTPW0=
-github.com/wailsapp/wails/v2 v2.8.1/go.mod h1:EFUGWkUX3KofO4fmKR/GmsLy3HhPH7NbyOEaMt8lBF0=
-golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
-golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
+github.com/wailsapp/wails/v2 v2.9.1 h1:irsXnoQrCpeKzKTYZ2SUVlRRyeMR6I0vCO9Q1cvlEdc=
+github.com/wailsapp/wails/v2 v2.9.1/go.mod h1:7maJV2h+Egl11Ak8QZN/jlGLj2wg05bsQS+ywJPT0gI=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
+golang.org/x/crypto v0.0.0-20211209193657-4570a0811e8b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
+golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
+golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 h1:k/i9J1pBpvlfR+9QsetwPyERsqu1GIbi967PQMq3Ivc=
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
-golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
-golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
-golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
+golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
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-20210228012217-479acdf4ea46/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
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.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
-golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
+golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
-golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
+golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
+golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
+gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
diff --git a/iconpack.go b/iconpack.go
new file mode 100644
index 0000000..2eb052f
--- /dev/null
+++ b/iconpack.go
@@ -0,0 +1,1312 @@
+package main
+
+import (
+ "errors"
+ "fmt"
+ "os"
+ "path"
+ "path/filepath"
+ "sort"
+ "strconv"
+ "strings"
+ "sync"
+ "sync/atomic"
+ "syscall"
+ "time"
+
+ "github.com/go-ole/go-ole"
+ "github.com/go-ole/go-ole/oleutil"
+ "github.com/google/uuid"
+ "github.com/wailsapp/wails/v2/pkg/runtime"
+ "gopkg.in/ini.v1"
+
+ cmap "github.com/orcaman/concurrent-map/v2"
+ lnk "github.com/parsiya/golnk"
+)
+
+type FileInfo struct {
+ Id string `json:"id"`
+ Name string `json:"name"`
+ Description string `json:"description"`
+ Path string `json:"path"`
+ Destination string `json:"destinationPath"`
+ Extension string `json:"extension"`
+ HasIcon bool `json:"hasIcon"`
+ IconId string `json:"iconId"`
+}
+
+type Metadata struct {
+ Id string `json:"id"`
+ Name string `json:"name"`
+ Version string `json:"version"`
+ Author string `json:"author"`
+ License string `json:"license"`
+ Description string `json:"description"`
+ IconName string `json:"iconName"`
+}
+
+type IconPack struct {
+ Metadata Metadata `json:"metadata"`
+ Files []FileInfo `json:"files"`
+ Settings IconPackSettings `json:"settings"`
+}
+
+type IconPackSettings struct {
+ Enabled bool `json:"enabled"`
+ CornerRadius int `json:"cornerRadius"`
+ Opacity int `json:"opacity"`
+}
+
+var allowedFileExtensions = []string{".lnk", ".dir", ".url"}
+
+var iconPackCache = cmap.New[IconPack]()
+
+func CreateIconPack(name string, version string, author string, license string, description string) (IconPack, error) {
+ var iconPack IconPack
+ iconPack.Metadata.Id = uuid.NewString()
+ iconPack.Metadata.Name = name
+ iconPack.Metadata.Version = version
+ iconPack.Metadata.Author = author
+ iconPack.Metadata.License = license
+ iconPack.Metadata.Description = description
+ iconPack.Files = []FileInfo{}
+
+ iconPack.Settings.Enabled = false
+ iconPack.Settings.CornerRadius = 0
+ iconPack.Settings.Opacity = 100
+
+ var err error
+
+ if name != "Unknown Pack" {
+ err = WriteIconPack(iconPack)
+ if err != nil {
+ return IconPack{}, err
+ }
+ }
+
+ return iconPack, err
+}
+
+func WriteIconPack(iconPack IconPack) error {
+ packPath := path.Join(packsFolder, iconPack.Metadata.Id)
+ iconFolderPath := path.Join(packPath, "icons")
+ metadataPath := path.Join(packPath, "metadata.json")
+ settingsPath := path.Join(packPath, "settings.json")
+ filesPath := path.Join(packPath, "files.json")
+
+ // Create necessary folders
+ if err := create_folder(packPath); err != nil {
+ return err
+ }
+ if err := create_folder(iconFolderPath); err != nil {
+ return err
+ }
+
+ // Write metadata and files to their respective JSON files
+ if err := writeJSON(metadataPath, iconPack.Metadata); err != nil {
+ return err
+ }
+ if err := writeJSON(settingsPath, iconPack.Settings); err != nil {
+ return err
+ }
+ if err := writeJSON(filesPath, iconPack.Files); err != nil {
+ return err
+ }
+
+ iconPackCache.Set(iconPack.Metadata.Id, iconPack)
+
+ return nil
+}
+
+func ReadIconPack(id string) (IconPack, error) {
+ packPath := path.Join(packsFolder, id)
+ iconFolderPath := path.Join(packPath, "icons")
+ metadataPath := path.Join(packPath, "metadata.json")
+ settingsPath := path.Join(packPath, "settings.json")
+ filesPath := path.Join(packPath, "files.json")
+
+ if _, err := os.Stat(packPath); os.IsNotExist(err) {
+ return IconPack{}, err
+ }
+
+ iconPack := IconPack{}
+
+ defaultPack, err := CreateIconPack("Unknown Pack", "v1.0.0", "", "", "")
+ if err != nil {
+ runtime.LogWarningf(appContext, "Failed to create default icon pack: %s", err.Error())
+ return IconPack{}, err
+ }
+
+ // Read metadata and files from their respective JSON files
+ if err := readJSON(metadataPath, &iconPack.Metadata); err != nil {
+ runtime.LogErrorf(appContext, "Failed to read icon pack metadata: %s", err.Error())
+ iconPack.Metadata = defaultPack.Metadata
+ }
+
+ metadataChange := false
+ if iconPack.Metadata.Id != id {
+ iconPack.Metadata.Id = id
+ metadataChange = true
+ }
+ iconPath := filepath.Join(packsFolder, iconPack.Metadata.Id, iconPack.Metadata.IconName+".png")
+ if iconPack.Metadata.IconName != "" && !exists(iconPath) {
+ iconPack.Metadata.IconName = ""
+ metadataChange = true
+ }
+ if metadataChange {
+ writeJSON(metadataPath, iconPack.Metadata)
+ }
+
+ if err := readJSON(settingsPath, &iconPack.Settings); err != nil {
+ runtime.LogWarningf(appContext, "Failed to read icon pack settings: %s", err.Error())
+ iconPack.Settings = defaultPack.Settings
+ writeJSON(settingsPath, iconPack.Settings)
+ }
+ if err := readJSON(filesPath, &iconPack.Files); err != nil {
+ runtime.LogWarningf(appContext, "Failed to read icon pack files: %s", err.Error())
+ iconPack.Files = []FileInfo{}
+ writeJSON(filesPath, iconPack.Files)
+ }
+
+ // Create necessary folders
+ if err := create_folder(iconFolderPath); err != nil {
+ return IconPack{}, err
+ }
+
+ iconPackCache.Set(id, iconPack)
+
+ return iconPack, nil
+}
+
+func (a *App) GetIconPack(id string) (IconPack, error) {
+ if iconPack, ok := iconPackCache.Get(id); ok {
+ return iconPack, nil
+ }
+
+ iconPack, err := ReadIconPack(id)
+
+ if err != nil {
+ runtime.LogError(a.ctx, fmt.Sprintf("Failed to read icon pack: %s", err.Error()))
+ return IconPack{}, err
+ }
+
+ return iconPack, nil
+}
+
+func (a *App) SetIconPack(iconPack IconPack) error {
+ err := WriteIconPack(iconPack)
+
+ if err != nil {
+ runtime.LogError(a.ctx, fmt.Sprintf("Failed to write icon pack: %s", err.Error()))
+ return err
+ }
+
+ return nil
+}
+
+func (a *App) SetIconPackField(packId string, fileName string, field string, value interface{}) {
+ // Check if the icon pack exists
+ iconPack, err := a.GetIconPack(packId)
+ if err != nil {
+ runtime.LogError(appContext, fmt.Sprintf("Icon pack %s not found: %s", packId, err.Error()))
+ return
+ }
+
+ // Update cache
+ switch fileName {
+ case "metadata.json":
+ switch field {
+ case "id":
+ iconPack.Metadata.Id = value.(string)
+ case "name":
+ iconPack.Metadata.Name = value.(string)
+ case "version":
+ iconPack.Metadata.Version = value.(string)
+ case "author":
+ iconPack.Metadata.Author = value.(string)
+ case "description":
+ iconPack.Metadata.Description = value.(string)
+ case "iconName":
+ iconPack.Metadata.IconName = value.(string)
+ }
+ case "settings.json":
+ switch field {
+ case "enabled":
+ iconPack.Settings.Enabled = value.(bool)
+ case "cornerRadius":
+ intVal, err := strconv.Atoi(fmt.Sprintf("%v", value))
+ if err != nil {
+ runtime.LogWarning(appContext, fmt.Sprintf("Failed to convert %v to int: %s", value, err.Error()))
+ return
+ }
+ iconPack.Settings.CornerRadius = intVal
+ case "opacity":
+ intVal, err := strconv.Atoi(fmt.Sprintf("%v", value))
+ if err != nil {
+ runtime.LogWarning(appContext, fmt.Sprintf("Failed to convert %v to int: %s", value, err.Error()))
+ return
+ }
+ iconPack.Settings.Opacity = intVal
+ }
+ }
+
+ a.SetIconPack(iconPack)
+}
+
+func (a *App) SetIconPackMetadata(packId string, metadata Metadata) {
+ // Check if the icon pack exists
+ iconPack, err := a.GetIconPack(packId)
+ if err != nil {
+ runtime.LogError(appContext, fmt.Sprintf("Icon pack %s not found: %s", packId, err.Error()))
+ return
+ }
+
+ tempPackPngPath, ok := tempPngPaths.Get(packId)
+
+ if ok {
+ runtime.LogDebugf(appContext, "Copying temp pack png: %s", tempPackPngPath)
+
+ metadata.IconName = uuid.New().String()
+
+ // Copy temp pack png
+ err = copy_file(tempPackPngPath, path.Join(packsFolder, iconPack.Metadata.Id, metadata.IconName+".png"))
+ if err != nil {
+ runtime.LogError(appContext, fmt.Sprintf("Failed to copy temp pack png: %s", err.Error()))
+ return
+ }
+
+ // Remove temp pack png
+ err = os.Remove(tempPackPngPath)
+ if err != nil {
+ runtime.LogError(appContext, fmt.Sprintf("Failed to remove temp pack png: %s", err.Error()))
+ return
+ }
+ tempPngPaths.Remove(packId)
+
+ os.Remove(path.Join(packsFolder, iconPack.Metadata.Id, iconPack.Metadata.IconName+".png"))
+ }
+
+ // Update cache
+ iconPack.Metadata = metadata
+ a.SetIconPack(iconPack)
+}
+
+func (a *App) SetIconPackFiles(packId string, files []FileInfo) {
+ // Check if the icon pack exists
+ iconPack, err := a.GetIconPack(packId)
+ if err != nil {
+ runtime.LogError(appContext, fmt.Sprintf("Icon pack %s not found: %s", packId, err.Error()))
+ return
+ }
+
+ for i, file := range files {
+ tempPngPath, ok := tempPngPaths.Get(file.Id)
+ if ok {
+ runtime.LogDebugf(appContext, "Attempting to copy temp png: %s", tempPngPath)
+
+ // Copy temp png
+ err = copy_file(tempPngPath, path.Join(packsFolder, packId, "icons", file.Id+".png"))
+ if err != nil {
+ runtime.LogError(appContext, fmt.Sprintf("Failed to copy temp png: %s", err.Error()))
+ continue
+ }
+
+ // Remove temp png
+ err = os.Remove(tempPngPath)
+ if err != nil {
+ runtime.LogError(appContext, fmt.Sprintf("Failed to remove temp png: %s", err.Error()))
+ continue
+ }
+ tempPngPaths.Remove(file.Id)
+ }
+
+ iconPath := filepath.Join(packsFolder, packId, "icons", file.Id+".png")
+ file.HasIcon = exists(iconPath)
+
+ files[i] = file
+ }
+
+ // Delete the unused icons
+ a.DeleteDeletePngPaths()
+
+ // Delete apply.json
+ applyFile := filepath.Join(packsFolder, packId, "apply.json")
+ runtime.LogDebugf(appContext, "Attempting to remove apply.json: %s", applyFile)
+ err = os.Remove(applyFile)
+ if err != nil {
+ runtime.LogWarning(appContext, fmt.Sprintf("Failed to remove apply.json icon: %s", err.Error()))
+ }
+
+ // Update cache
+ iconPack.Files = files
+ a.SetIconPack(iconPack)
+}
+
+func CacheIconPacks() error {
+ // Clear cache
+ iconPackCache.Clear()
+
+ files, err := os.ReadDir(packsFolder)
+ if err != nil {
+ return err
+ }
+
+ for _, file := range files {
+ if file.IsDir() {
+ iconPack, err := ReadIconPack(file.Name())
+
+ if err != nil {
+ runtime.LogWarningf(appContext, fmt.Sprintf("Failed to read icon pack (%s): %s", file.Name(), err.Error()))
+ continue
+ }
+
+ iconPackCache.Set(iconPack.Metadata.Id, iconPack)
+ }
+ }
+
+ return nil
+}
+
+func (a *App) ClearIconPackCache() {
+ iconPackCache.Clear()
+}
+
+func (a *App) GetIconPackList() []IconPack {
+ if iconPackCache.IsEmpty() {
+ CacheIconPacks()
+ }
+
+ iconPacks := make([]IconPack, 0, iconPackCache.Count())
+
+ for i := range iconPackCache.IterBuffered() {
+ iconPacks = append(iconPacks, i.Val)
+ }
+
+ // Sort icon packs by name
+ sort.Slice(iconPacks, func(i, j int) bool {
+ return iconPacks[i].Metadata.Name < iconPacks[j].Metadata.Name
+ })
+
+ return iconPacks
+}
+
+func CreateFileInfo(packId string, path string) (FileInfo, error) {
+ var fileInfo FileInfo
+ fileInfo.Id = uuid.NewString()
+ fileInfo.Name = strings.TrimSuffix(filepath.Base(path), filepath.Ext(path))
+ fileInfo.Path = ConvertToGeneralPath(path)
+ fileInfo.Extension = strings.ToLower(filepath.Ext(path))
+ if is_dir(path) {
+ fileInfo.Extension = ".dir"
+ }
+
+ runtime.LogDebugf(appContext, "Pack id: %s", packId)
+ runtime.LogDebugf(appContext, "File extension for %s: %s", path, fileInfo.Extension)
+
+ if fileInfo.Extension == ".lnk" {
+ link, err := lnk.File(path)
+ if err != nil {
+ return FileInfo{}, err
+ }
+
+ if link.StringData.NameString != "" {
+ fileInfo.Description = link.StringData.NameString
+ }
+
+ if link.LinkInfo.LocalBasePath != "" {
+ fileInfo.Destination = link.LinkInfo.LocalBasePath
+ }
+ if link.LinkInfo.LocalBasePathUnicode != "" {
+ fileInfo.Destination = link.LinkInfo.LocalBasePathUnicode
+ }
+ fileInfo.Destination = ConvertToGeneralPath(fileInfo.Destination)
+ } else if fileInfo.Extension == ".url" {
+ iniContent, err := os.ReadFile(path)
+ if err != nil {
+ return FileInfo{}, err
+ }
+
+ iniFile, err := ini.Load(iniContent)
+ if err != nil {
+ return FileInfo{}, err
+ }
+
+ section := iniFile.Section("InternetShortcut")
+
+ url := section.Key("URL").String()
+
+ fileInfo.Destination = url
+ }
+
+ hasAppliedIcon := true
+
+ appliedIconPath, err := GetAppliedIcon(path)
+ if err != nil {
+ runtime.LogError(appContext, err.Error())
+ }
+
+ hasAppliedIcon = appliedIconPath != ""
+
+ if appliedIconPath != "" && strings.ToLower(filepath.Ext(appliedIconPath)) == ".ico" {
+ hasAppliedIcon = exists(appliedIconPath)
+ }
+
+ if packId != "" && hasAppliedIcon {
+ if packId == "temp" {
+ tempName := "iconium-" + uuid.NewString()
+ iconPath := filepath.Join(tempFolder, tempName+".png")
+
+ err := ConvertToPng(path, iconPath)
+ if err != nil {
+ runtime.LogError(appContext, err.Error())
+ } else {
+ fileInfo.HasIcon = true
+ tempPngPaths.Set(fileInfo.Id, iconPath)
+ }
+ } else {
+ iconPath := filepath.Join(packsFolder, packId, "icons", fileInfo.Id+".png")
+ err := ConvertToPng(path, iconPath)
+ if err != nil {
+ runtime.LogError(appContext, err.Error())
+ } else {
+ fileInfo.HasIcon = true
+ }
+ }
+ }
+
+ if !contains(allowedFileExtensions, fileInfo.Extension) {
+ return FileInfo{}, errors.New("file extension not allowed: " + fileInfo.Extension)
+ }
+
+ return fileInfo, nil
+}
+
+func GetAppliedIcon(path string) (string, error) {
+ ext := strings.ToLower(filepath.Ext(path))
+
+ if ext == ".lnk" {
+ link, err := lnk.File(path)
+ if err != nil {
+ return "", fmt.Errorf("failed to open .lnk file: %s", err.Error())
+ }
+
+ if link.StringData.IconLocation != "" {
+ return link.StringData.IconLocation, nil
+ }
+ } else if ext == ".url" {
+ iniPath := filepath.Join(path)
+ if !exists(iniPath) {
+ return "", nil
+ }
+ iniContent, err := os.ReadFile(iniPath)
+ if err != nil {
+ return "", err
+ }
+ iniFile, err := ini.Load(iniContent)
+ if err != nil {
+ return "", err
+ }
+ section := iniFile.Section("InternetShortcut")
+
+ if !section.HasKey("IconFile") {
+ return "", errors.New("icon not found")
+ }
+
+ return section.Key("IconFile").String(), nil
+ } else if is_dir(path) {
+ iniPath := filepath.Join(path, "desktop.ini")
+ if !exists(iniPath) {
+ return "", nil
+ }
+
+ iniContent, err := os.ReadFile(iniPath)
+ if err != nil {
+ return "", err
+ }
+ iniFile, err := ini.Load(iniContent)
+ if err != nil {
+ return "", err
+ }
+ section := iniFile.Section(".ShellClassInfo")
+ iconResource := section.Key("IconResource").String()
+
+ iconResource = strings.Split(iconResource, ",")[0]
+
+ runtime.LogDebugf(appContext, "Icon resource: %s", iconResource)
+
+ return iconResource, nil
+ }
+
+ return "", errors.New("icon not found")
+}
+
+func GetAppliedDescription(path string) (string, error) {
+ ext := strings.ToLower(filepath.Ext(path))
+
+ if ext == ".lnk" {
+ link, err := lnk.File(path)
+ if err != nil {
+ return "", fmt.Errorf("failed to open .lnk file: %s", err.Error())
+ }
+
+ if link.StringData.NameString != "" {
+ return link.StringData.NameString, nil
+ }
+ }
+
+ return "", errors.New("description not found")
+}
+
+func (a *App) AddIconPack(name string, version string, author string, license string, description string) error {
+ iconPack, err := CreateIconPack(name, version, author, license, description)
+ if err != nil {
+ return err
+ }
+
+ tempPackPngPath, ok := tempPngPaths.Get("temp")
+
+ if ok {
+ runtime.LogDebug(appContext, "Adding pack png for "+iconPack.Metadata.Id)
+
+ iconPack.Metadata.IconName = uuid.NewString()
+
+ // Copy temp pack png
+ err = copy_file(tempPackPngPath, path.Join(packsFolder, iconPack.Metadata.Id, iconPack.Metadata.IconName+".png"))
+ if err != nil {
+ return err
+ }
+
+ // Remove temp pack png
+ err = os.Remove(tempPackPngPath)
+ if err != nil {
+ return err
+ }
+ tempPngPaths.Remove("temp")
+
+ a.SetIconPack(iconPack)
+ }
+
+ return nil
+}
+
+func (a *App) DeleteIconPack(id string, deleteGeneratedIcons bool) error {
+ iconPackPath := path.Join(packsFolder, id)
+
+ if _, err := os.Stat(iconPackPath); os.IsNotExist(err) {
+ return err
+ }
+ if err := os.RemoveAll(iconPackPath); err != nil {
+ return err
+ }
+
+ if deleteGeneratedIcons {
+ activeIconsPath := path.Join(activeIconFolder, id)
+ if _, err := os.Stat(activeIconsPath); os.IsNotExist(err) {
+ return err
+ }
+ if err := os.RemoveAll(activeIconsPath); err != nil {
+ return err
+ }
+ }
+
+ iconPackCache.Remove(id)
+
+ return nil
+}
+
+func (a *App) AddFileToIconPackFromPath(id string, path string, save bool) {
+ fileInfo, err := CreateFileInfo(id, path)
+ if err != nil {
+ runtime.LogError(a.ctx, err.Error())
+ return
+ }
+
+ cachedPack, err := a.GetIconPack(id)
+ if err != nil {
+ runtime.LogError(a.ctx, fmt.Sprintf("Failed to read icon pack: %s", err.Error()))
+ return
+ }
+ cachedPack.Files = append(cachedPack.Files, fileInfo)
+
+ iconPackCache.Set(id, cachedPack)
+
+ if save {
+ a.SetIconPack(cachedPack)
+
+ runtime.WindowExecJS(appContext, "window.setProgress(100)")
+ time.Sleep(200 * time.Millisecond)
+ runtime.WindowExecJS(appContext, "window.setProgress(0)")
+ }
+}
+
+func (a *App) AddFilesToIconPackFromPath(id string, paths []string, save bool) {
+ var wg sync.WaitGroup
+ progress := make(chan int)
+
+ totalFiles := len(paths)
+ processedFiles := 0
+
+ // Start a goroutine to monitor and print progress
+ go func() {
+ for range progress {
+ processedFiles++
+ percentage := float64(processedFiles) / float64(totalFiles) * 100
+ runtime.WindowExecJS(appContext, fmt.Sprintf("window.setProgress(%f)", percentage))
+ }
+ }()
+
+ // Add each file to the icon pack asynchronously
+ for _, path := range paths {
+ wg.Add(1)
+ go func(p string) {
+ defer wg.Done()
+ a.AddFileToIconPackFromPath(id, p, false)
+ progress <- 1 // Send progress update
+ }(path)
+ }
+
+ // Wait for all goroutines to finish
+ wg.Wait()
+ close(progress) // Close the progress channel
+
+ // Save the icon pack if required
+ if save {
+ iconPack, ok := iconPackCache.Get(id)
+ if !ok {
+ return
+ }
+ a.SetIconPack(iconPack)
+ }
+
+ time.Sleep(200 * time.Millisecond)
+
+ runtime.WindowExecJS(appContext, "window.setProgress(0)")
+}
+
+func (a *App) GetFileInfoFromPaths(id string, paths []string) ([]FileInfo, error) {
+ var fileInfos []FileInfo
+ var fileInfosMutex sync.Mutex
+ var wg sync.WaitGroup
+ progress := make(chan int)
+
+ totalFiles := len(paths)
+ processedFiles := 0
+
+ // Start a goroutine to monitor and print progress
+ go func() {
+ for range progress {
+ processedFiles++
+ percentage := float64(processedFiles) / float64(totalFiles) * 100
+ runtime.WindowExecJS(appContext, fmt.Sprintf("window.setProgress(%f)", percentage))
+ }
+ }()
+
+ // Process each path asynchronously
+ for _, path := range paths {
+ wg.Add(1)
+ go func(p string) {
+ defer wg.Done()
+ if (!contains(allowedFileExtensions, filepath.Ext(p)) && !is_dir(p)) || !exists(p) {
+ progress <- 1 // Send progress update even if file is skipped
+ return
+ }
+
+ fileInfo, err := CreateFileInfo(id, p)
+ if err != nil {
+ fmt.Printf("Error creating file info for path %s: %v\n", p, err)
+ return
+ }
+
+ tempPngPath, ok := tempPngPaths.Get(fileInfo.Id)
+ selectImage := SelectImage{
+ Id: fileInfo.Id,
+ Path: "",
+ TempPath: "",
+ HasOriginal: false,
+ HasTemp: false,
+ IsRemoved: false,
+ }
+ if ok {
+ relativeTempPngPath, err := filepath.Rel(appFolder, tempPngPath)
+ if err == nil {
+ selectImage.TempPath = relativeTempPngPath
+ selectImage.HasTemp = true
+ selectImages.Set(fileInfo.Id, selectImage)
+ }
+ }
+
+ fileInfosMutex.Lock()
+ fileInfos = append(fileInfos, fileInfo)
+ fileInfosMutex.Unlock()
+
+ progress <- 1 // Send progress update after processing the file
+ }(path)
+ }
+
+ // Wait for all goroutines to finish
+ wg.Wait()
+ close(progress) // Close the progress channel
+
+ time.Sleep(200 * time.Millisecond)
+
+ runtime.WindowExecJS(appContext, "window.setProgress(0)")
+
+ return fileInfos, nil
+}
+
+func (a *App) GetFileInfoFromDesktop(id string) ([]FileInfo, error) {
+ desktop, public := get_desktop_paths()
+
+ dirEntries, err := os.ReadDir(desktop)
+ if err != nil {
+ return nil, err
+ }
+ dirEntries2, err := os.ReadDir(public)
+ if err != nil {
+ return nil, err
+ }
+
+ paths := []string{}
+
+ for _, dirEntry := range dirEntries {
+ if contains(allowedFileExtensions, filepath.Ext(dirEntry.Name())) || is_dir(filepath.Join(desktop, dirEntry.Name())) {
+ paths = append(paths, filepath.Join(desktop, dirEntry.Name()))
+ }
+ }
+ for _, dirEntry := range dirEntries2 {
+ if contains(allowedFileExtensions, filepath.Ext(dirEntry.Name())) || is_dir(filepath.Join(public, dirEntry.Name())) {
+ paths = append(paths, filepath.Join(public, dirEntry.Name()))
+ }
+ }
+
+ return a.GetFileInfoFromPaths(id, paths)
+}
+
+func (a *App) AddFilesToIconPackFromFolder(id string, path string, save bool) {
+ files, err := os.ReadDir(path)
+ if err != nil {
+ runtime.LogError(a.ctx, err.Error())
+ return
+ }
+
+ var wg sync.WaitGroup
+
+ for _, file := range files {
+ wg.Add(1)
+ go func(file os.DirEntry) {
+ defer wg.Done()
+ a.AddFileToIconPackFromPath(id, path+"\\"+file.Name(), false)
+ }(file)
+
+ }
+
+ wg.Wait()
+
+ if save {
+ runtime.LogInfo(a.ctx, fmt.Sprintf("Saving icon pack %s", id))
+ iconPack, ok := iconPackCache.Get(id)
+ if !ok {
+ runtime.LogErrorf(a.ctx, "Failed to read icon pack: %v", err)
+ return
+ }
+
+ err = a.SetIconPack(iconPack)
+ if err != nil {
+ runtime.LogError(a.ctx, err.Error())
+ }
+ }
+}
+
+func (a *App) AddFilesToIconPackFromDesktop(id string) {
+ desktop, public := get_desktop_paths()
+
+ desktopEntries, err := os.ReadDir(desktop)
+ if err != nil {
+ runtime.LogError(appContext, err.Error())
+ return
+ }
+ publicEntries, err := os.ReadDir(public)
+ if err != nil {
+ runtime.LogError(appContext, err.Error())
+ return
+ }
+
+ paths := []string{}
+
+ for _, desktopEntry := range desktopEntries {
+ path := filepath.Join(desktop, desktopEntry.Name())
+
+ if contains(allowedFileExtensions, strings.ToLower(filepath.Ext(desktopEntry.Name()))) || is_dir(path) {
+ paths = append(paths, path)
+ }
+ }
+ for _, publicEntry := range publicEntries {
+ path := filepath.Join(public, publicEntry.Name())
+
+ if contains(allowedFileExtensions, strings.ToLower(filepath.Ext(publicEntry.Name()))) || is_dir(path) {
+ paths = append(paths, path)
+ }
+ }
+
+ a.AddFilesToIconPackFromPath(id, paths, true)
+}
+
+func (a *App) ApplyIconPack(id string) {
+ pack, err := a.GetIconPack(id)
+
+ if err != nil {
+ runtime.LogError(appContext, err.Error())
+ return
+ }
+
+ err = pack.Apply()
+
+ // Save icon pack
+ if err == nil {
+ runtime.LogInfo(appContext, fmt.Sprintf("Applied icon pack %s, attempting to save", pack.Metadata.Id))
+ err = a.SetIconPack(pack)
+
+ if err == nil {
+ runtime.LogInfo(appContext, fmt.Sprintf("Saved icon pack %s", pack.Metadata.Id))
+ } else {
+ runtime.LogError(appContext, err.Error())
+ }
+ } else {
+ runtime.LogError(appContext, err.Error())
+ }
+}
+
+func (pack *IconPack) Apply() error {
+ targetFolder := filepath.Join(activeIconFolder, pack.Metadata.Id)
+ err := create_folder(targetFolder)
+ if err != nil {
+ return err
+ }
+
+ var wg sync.WaitGroup
+ var completed int64 // Counter for completed files
+ totalFiles := int64(len(pack.Files))
+
+ for i := range pack.Files {
+ wg.Add(1)
+
+ // Use a pointer to the file in the slice
+ file := &pack.Files[i]
+
+ go func(file *FileInfo) {
+ defer wg.Done()
+
+ match := file.MatchFile()
+ runtime.LogDebug(appContext, "Match: "+match)
+
+ if file.HasIcon && match != "" {
+ targetPath := filepath.Join(targetFolder, file.IconId+".ico")
+
+ targetPathExists := false
+ if _, err := os.Stat(targetPath); err == nil {
+ targetPathExists = true
+ }
+
+ oldIconPath := targetPath
+
+ // Regenerate
+ if !targetPathExists || !pack.IsApplied() {
+ // Update the IconId directly on the file pointer
+ file.IconId = uuid.NewString()
+ targetPath = filepath.Join(targetFolder, file.IconId+".ico")
+
+ iconPath := filepath.Join(packsFolder, pack.Metadata.Id, "icons", file.Id+".png")
+ if _, err := os.Stat(iconPath); err != nil {
+ runtime.LogError(appContext, err.Error())
+ return
+ }
+ err = ConvertToIco(iconPath, targetPath, pack.Settings)
+
+ if err != nil {
+ runtime.LogError(appContext, err.Error())
+ return
+ }
+ }
+
+ appliedIconPath, err := GetAppliedIcon(match)
+ if err != nil {
+ runtime.LogWarningf(appContext, "Failed to get applied icon for %s: %v", match, err)
+ }
+ runtime.LogDebug(appContext, "Applied icon path: "+appliedIconPath)
+
+ if appliedIconPath == targetPath {
+ runtime.LogDebug(appContext, "Icon already applied")
+ } else {
+ runtime.LogDebug(appContext, "Applying icon")
+
+ errSetIcon := SetIcon(match, targetPath)
+ if errSetIcon != nil {
+ runtime.LogWarningf(appContext, "Failed to set icon for %s: %v", match, errSetIcon)
+
+ newAppliedPath, _ := GetAppliedIcon(match)
+ runtime.LogDebugf(appContext, "%s / %s", targetPath, newAppliedPath)
+ if newAppliedPath != targetPath {
+ app.SendNotification(fmt.Sprintf("Failed to set icon for %s", filepath.Base(match)), strings.ReplaceAll(fmt.Sprint(errSetIcon), "\"", "\\\""), "", "warning")
+ }
+ } else if targetPathExists {
+ err = os.Remove(oldIconPath)
+ if err != nil {
+ runtime.LogWarningf(appContext, "Failed to remove old icon %s: %v", oldIconPath, err)
+ }
+ }
+ }
+ }
+
+ if *config.ChangeDescriptionOfMathcedLnkFiles && file.Description != "" && match != "" {
+ appliedDescriptionPath, err := GetAppliedDescription(match)
+ if err != nil {
+ runtime.LogWarningf(appContext, "Failed to get applied description for %s: %v", match, err)
+ }
+
+ if appliedDescriptionPath == file.Description {
+ runtime.LogDebug(appContext, "Description already applied")
+ } else {
+ runtime.LogDebug(appContext, "Applying description")
+ err = SetDescription(match, file.Description)
+ if err != nil {
+ runtime.LogWarningf(appContext, "Failed to set description for %s: %s", match, err.Error())
+ }
+ }
+ }
+
+ // Increment the completed counter
+ atomic.AddInt64(&completed, 1)
+ percentage := (float64(atomic.LoadInt64(&completed)) / float64(totalFiles)) * 100
+
+ runtime.WindowExecJS(appContext, fmt.Sprintf(`window.setProgress(%f)`, percentage))
+ }(file)
+ }
+
+ wg.Wait()
+
+ // Copy settings.json to apply.json
+ settingsPath := filepath.Join(packsFolder, pack.Metadata.Id, "settings.json")
+ applyPath := filepath.Join(packsFolder, pack.Metadata.Id, "apply.json")
+ err = copy_file(settingsPath, applyPath)
+ if err != nil {
+ runtime.LogError(appContext, err.Error())
+ return err
+ }
+
+ // Wait for progress bar to finish
+ time.Sleep(200 * time.Millisecond)
+
+ runtime.WindowExecJS(appContext, `window.setProgress(0)`)
+
+ return nil
+}
+
+func (pack *IconPack) IsApplied() bool {
+ applyPath := filepath.Join(packsFolder, pack.Metadata.Id, "apply.json")
+
+ if _, err := os.Stat(applyPath); os.IsNotExist(err) {
+ return false
+ }
+
+ var apply IconPackSettings
+ err := readJSON(applyPath, &apply)
+ if err != nil {
+ runtime.LogError(appContext, err.Error())
+ return false
+ }
+
+ return apply.CornerRadius == pack.Settings.CornerRadius && apply.Opacity == pack.Settings.Opacity
+}
+
+func (fileInfo *FileInfo) MatchFile() string {
+ pathPattern := fileInfo.Path
+ path := ConvertToFullPath(pathPattern)
+ if path != "" {
+ if *config.RenameMatchedFiles && fileInfo.Name != filepath.Base(path) {
+ newPath := filepath.Join(filepath.Dir(path), fileInfo.Name+fileInfo.Extension)
+ os.Rename(path, newPath)
+ path = newPath
+ }
+
+ return path
+ }
+
+ if (*config.MatchLnkByDestination && fileInfo.Extension == ".lnk") || (*config.MatchURLByDestination && fileInfo.Extension == ".url") {
+ pathDir := ConvertToFullPath(filepath.Dir(pathPattern))
+
+ files, err := os.ReadDir(pathDir)
+ if err != nil {
+ runtime.LogError(appContext, err.Error())
+ return ""
+ }
+
+ for _, file := range files {
+ if filepath.Ext(file.Name()) == filepath.Ext(pathPattern) {
+ currentFilePath := filepath.Join(pathDir, file.Name())
+ currentFileInfo, err := CreateFileInfo("", currentFilePath)
+ if err != nil {
+ runtime.LogError(appContext, err.Error())
+ return ""
+ }
+ if currentFileInfo.Destination == fileInfo.Destination {
+ runtime.LogDebug(appContext, "Matched file: "+currentFilePath)
+
+ if *config.RenameMatchedFiles && currentFileInfo.Name != fileInfo.Name {
+ newPath := filepath.Join(pathDir, fileInfo.Name+fileInfo.Extension)
+ os.Rename(currentFilePath, newPath)
+ currentFilePath = newPath
+ runtime.LogDebug(appContext, "Renamed file: "+currentFilePath)
+ }
+
+ return currentFilePath
+ }
+ }
+ }
+ }
+
+ return ""
+}
+
+func SetIcon(path string, iconPath string) error {
+ ext := strings.ToLower(filepath.Ext(path))
+ if is_dir(path) {
+ ext = ".dir"
+ }
+
+ runtime.LogDebug(appContext, fmt.Sprintf("Setting icon for %s to %s", path, iconPath))
+
+ if ext == ".lnk" {
+ err1 := setLnkIcon(path, iconPath)
+ if err1 != nil {
+ err2 := setIconScript(path, iconPath)
+ if err2 != nil {
+ return err2
+ } else {
+ return err1
+ }
+ } else {
+ return nil
+ }
+ } else if ext == ".url" {
+ err1 := setIconScript(path, iconPath)
+ if err1 != nil {
+ err2 := setUrlIcon(path, iconPath)
+ if err2 != nil {
+ return err2
+ } else {
+ return err1
+ }
+ } else {
+ return nil
+ }
+ } else if ext == ".dir" {
+ return setDirIcon(path, iconPath)
+ }
+
+ return errors.New("unsupported file type")
+}
+
+func SetDescription(path string, desc string) error {
+ ext := strings.ToLower(filepath.Ext(path))
+
+ if ext == ".lnk" {
+ err := setLnkDesc(path, desc)
+ if err != nil {
+ return setDescScript(path, desc)
+ } else {
+ return nil
+ }
+ }
+
+ return errors.New("unsupported file type")
+}
+
+func setIconScript(path string, iconPath string) error {
+ _, err := sendCommand("cscript.exe", setLnkIconScriptPath, filepath.Dir(path), filepath.Base(path), iconPath, "0")
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+func setDescScript(path string, desc string) error {
+ _, err := sendCommand("cscript.exe", setLnkDescScriptPath, filepath.Dir(path), filepath.Base(path), desc)
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+func setLnkIcon(linkPath string, iconPath string) error {
+ // Initialize COM
+ err := ole.CoInitialize(0)
+ if err != nil {
+ return fmt.Errorf("failed to initialize COM: %v", err)
+ }
+ defer ole.CoUninitialize()
+
+ // Create a WScript.Shell object
+ wshShell, err := oleutil.CreateObject("WScript.Shell")
+ if err != nil {
+ return fmt.Errorf("failed to create WScript.Shell object: %v", err)
+ }
+ defer wshShell.Release()
+
+ // Get the IDispatch interface
+ wshShellDisp, err := wshShell.QueryInterface(ole.IID_IDispatch)
+ if err != nil {
+ return fmt.Errorf("failed to get IDispatch interface for WScript.Shell: %v", err)
+ }
+ defer wshShellDisp.Release()
+
+ // Create a shortcut object
+ shortcut, err := oleutil.CallMethod(wshShellDisp, "CreateShortcut", linkPath)
+ if err != nil {
+ return fmt.Errorf("failed to create shortcut object: %v", err)
+ }
+ shortcutDisp := shortcut.ToIDispatch()
+ defer shortcutDisp.Release()
+
+ // Set the icon location
+ _, err = oleutil.PutProperty(shortcutDisp, "IconLocation", iconPath+",0")
+ if err != nil {
+ return fmt.Errorf("failed to set icon location: %v", err)
+ }
+
+ // Save the shortcut
+ _, err = oleutil.CallMethod(shortcutDisp, "Save")
+ if err != nil {
+ return fmt.Errorf("failed to save shortcut: %v", err)
+ }
+
+ return nil
+}
+
+func setLnkDesc(linkPath string, desc string) error {
+ // Initialize COM
+ err := ole.CoInitialize(0)
+ if err != nil {
+ return fmt.Errorf("failed to initialize COM: %v", err)
+ }
+ defer ole.CoUninitialize()
+
+ // Create a WScript.Shell object
+ wshShell, err := oleutil.CreateObject("WScript.Shell")
+ if err != nil {
+ return fmt.Errorf("failed to create WScript.Shell object: %v", err)
+ }
+ defer wshShell.Release()
+
+ // Get the IDispatch interface
+ wshShellDisp, err := wshShell.QueryInterface(ole.IID_IDispatch)
+ if err != nil {
+ return fmt.Errorf("failed to get IDispatch interface for WScript.Shell: %v", err)
+ }
+ defer wshShellDisp.Release()
+
+ // Create a shortcut object
+ shortcut, err := oleutil.CallMethod(wshShellDisp, "CreateShortcut", linkPath)
+ if err != nil {
+ return fmt.Errorf("failed to create shortcut object: %v", err)
+ }
+ shortcutDisp := shortcut.ToIDispatch()
+ defer shortcutDisp.Release()
+
+ // Set the icon location
+ _, err = oleutil.PutProperty(shortcutDisp, "Description", desc)
+ if err != nil {
+ return fmt.Errorf("failed to set icon location: %v", err)
+ }
+
+ // Save the shortcut
+ _, err = oleutil.CallMethod(shortcutDisp, "Save")
+ if err != nil {
+ return fmt.Errorf("failed to save shortcut: %v", err)
+ }
+
+ return nil
+}
+
+func setUrlIcon(urlPath string, iconPath string) error {
+ iniContent, err := os.ReadFile(urlPath)
+ if err != nil {
+ return err
+ }
+ iniFile, err := ini.Load(iniContent)
+ if err != nil {
+ return err
+ }
+ section := iniFile.Section("InternetShortcut")
+ section.Key("IconFile").SetValue(iconPath)
+ section.Key("IconIndex").SetValue("0")
+ err = iniFile.SaveTo(urlPath)
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+func setDirIcon(dirPath string, iconPath string) error {
+ iniPath := filepath.Join(dirPath, "desktop.ini")
+ iniPathTxt := filepath.Join(dirPath, uuid.NewString()+"-desktop.txt")
+
+ if exists(iniPath) {
+ iniContent, err := os.ReadFile(iniPath)
+ if err != nil {
+ return err
+ }
+ iniFile, err := ini.Load(iniContent)
+ if err != nil {
+ return err
+ }
+ section := iniFile.Section(".ShellClassInfo")
+ iconResource := section.Key("IconResource")
+ iconResource.SetValue(iconPath + ",0")
+
+ err = iniFile.SaveTo(iniPathTxt)
+ if err != nil {
+ return err
+ }
+
+ err = os.Rename(iniPathTxt, iniPath)
+ if err != nil {
+ return err
+ }
+ } else {
+ // Create desktop.ini
+ file, err := os.Create(iniPathTxt)
+ if err != nil {
+ return err
+ }
+
+ _, err = file.WriteString("[.ShellClassInfo]\nIconResource=" + iconPath + ",0")
+ if err != nil {
+ return err
+ }
+
+ file.Close()
+
+ err = os.Rename(iniPathTxt, iniPath)
+ if err != nil {
+ return err
+ }
+ }
+
+ // Set attributes
+ if err := setFileAttributes(iniPath, syscall.FILE_ATTRIBUTE_HIDDEN|syscall.FILE_ATTRIBUTE_SYSTEM); err != nil {
+ return err
+ }
+ if err := setFileAttributes(dirPath, syscall.FILE_ATTRIBUTE_READONLY); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func setFileAttributes(path string, attributes uint32) error {
+ pathUTF16, err := syscall.UTF16PtrFromString(path)
+ if err != nil {
+ return err
+ }
+ return syscall.SetFileAttributes(pathUTF16, attributes)
+}
diff --git a/image.go b/image.go
new file mode 100644
index 0000000..8865bde
--- /dev/null
+++ b/image.go
@@ -0,0 +1,206 @@
+package main
+
+import (
+ "fmt"
+ "math"
+ "os"
+ "path/filepath"
+ "strconv"
+ "strings"
+
+ lnk "github.com/parsiya/golnk"
+ "github.com/wailsapp/wails/v2/pkg/runtime"
+ "gopkg.in/ini.v1"
+)
+
+var allowedImageExtensionsPng = []string{".ico", ".png", ".jpg", ".jpeg", ".bmp", ".webp", ".svg", ".exe", ".lnk", ".url"}
+
+var allowedImageExtensionsIco = []string{".png", ".jpg", ".jpeg"}
+
+func GetMaskPath(radius int) (string, error) {
+ if radius <= 0 {
+ radius = 1
+ } else if radius > 100 {
+ radius = 100
+ }
+
+ // Create a rounded rectangle mask
+ maskPath := filepath.Join(maskFolder, fmt.Sprintf("mask_r%d.png", radius))
+
+ // Check if the mask already exists
+ if _, err := os.Stat(maskPath); err == nil {
+ return maskPath, nil
+ }
+
+ // Round the corner radius to the closest integer
+ roundedRadius := int(math.Round(float64(radius) * 2.56))
+
+ maskArgs := []string{
+ imageMagickPath,
+ "-size", fmt.Sprintf("%dx%d", 256, 256), // Set the size of the canvas
+ "xc:none", // Create a blank canvas with transparency
+ "-fill", "white", // Set the fill color to white for the mask
+ "-draw", fmt.Sprintf("roundrectangle 0,0 %d,%d %d,%d", 255, 255, roundedRadius, roundedRadius), // Draw a rounded rectangle with the specified corner radius
+ "-alpha", "off", // Turn off alpha
+ maskPath, // Save the mask to the destination file
+ }
+
+ // Execute ImageMagick command to create mask
+ _, err := sendCommand(maskArgs...)
+ if err != nil {
+ return "", fmt.Errorf("error creating mask: %ws", err)
+ }
+
+ return maskPath, nil
+}
+
+// ConvertToIco converts an image to an ICO file with specified corner radius
+func ConvertToIco(path string, destination string, settings IconPackSettings) error {
+ extension := filepath.Ext(path)
+
+ // Check if the input file has a valid image extension
+ if extension != ".png" {
+ return fmt.Errorf("invalid image extension: %s", extension)
+ }
+
+ maskPath, err := GetMaskPath(settings.CornerRadius)
+
+ if err != nil {
+ return err
+ }
+
+ args := []string{
+ imageMagickPath,
+ path,
+ maskPath,
+ "-alpha", "off",
+ "-compose", "CopyOpacity",
+ "-composite",
+ "-define", "icon:auto-resize=16,24,32,48,64,72,96,128,256",
+ "-channel", "A", // target the alpha channel
+ "-evaluate", "Multiply", fmt.Sprintf("%.2f", (float64(settings.Opacity) / 100.0)), // apply the opacity adjustment
+ "-channel", "RGBA", // reset channel targeting
+ destination,
+ }
+
+ // Execute ImageMagick command silently
+ _, err = sendCommand(args...)
+ if err != nil {
+ return fmt.Errorf("error converting image: %w", err)
+ }
+
+ return nil
+}
+
+func ConvertToPng(path string, destination string) error {
+ extension := filepath.Ext(path)
+
+ if !is_dir(path) && !contains(allowedImageExtensionsPng, extension) {
+ return fmt.Errorf("invalid image extension: %s", extension)
+ }
+
+ iconDestination := ""
+ iconLocation := ""
+
+ if extension == ".lnk" {
+ link, err := lnk.File(path)
+ if err != nil {
+ return fmt.Errorf("failed to open .lnk file: %w", err)
+ }
+
+ iconLocation := link.StringData.IconLocation
+ iconDestination = link.LinkInfo.LocalBasePath
+
+ if strings.ToLower(filepath.Ext(iconLocation)) == ".ico" {
+ err = ConvertToPng(iconLocation, destination)
+ if err == nil {
+ return nil
+ }
+ }
+ } else if extension == ".url" {
+ iniContent, err := os.ReadFile(path)
+ if err != nil {
+ return fmt.Errorf("failed to open .url file: %w", err)
+ }
+ iniFile, err := ini.Load(iniContent)
+ if err != nil {
+ return fmt.Errorf("failed to parse .url file: %w", err)
+ }
+
+ section := iniFile.Section("InternetShortcut")
+ iconLocation = section.Key("IconFile").String()
+
+ return ConvertToPng(iconLocation, destination)
+ }
+
+ // Build magick command arguments
+ args := []string{extractIconPath, path, destination}
+ useImagick := extension != ".ico" && extension != ".exe" && extension != ".lnk" && !is_dir(path)
+
+ if useImagick {
+ args = []string{imageMagickPath, path, "-alpha", "on", "-background", "none", "-resize", "256x256\\!", destination}
+ }
+
+ runtime.LogDebugf(appContext, "ConvertToPng: %s", strings.Join(args, " "))
+
+ // Execute magick command silently
+ _, err := sendCommand(args...)
+
+ if err != nil {
+ if extension == ".lnk" {
+ if strings.ToLower(filepath.Ext(iconLocation)) != ".ico" {
+ err = ConvertToPng(iconLocation, destination)
+ if err == nil {
+ return nil
+ }
+ }
+ return ConvertToPng(iconDestination, destination)
+ }
+ return fmt.Errorf("error converting image: %w", err)
+ } else {
+ if !useImagick {
+ width, err := GetImageWidth(destination)
+ if err != nil {
+ return err
+ }
+
+ if width != 256 {
+ resizeArgs := []string{imageMagickPath, destination, "-resize", "256x256", destination}
+ runtime.LogDebugf(appContext, "ConvertToPng: %s", strings.Join(resizeArgs, " "))
+
+ // Execute magick command silently
+ _, err := sendCommand(resizeArgs...)
+ if err != nil {
+ return fmt.Errorf("error resizing image: %w", err)
+ }
+ }
+ }
+ }
+
+ return nil
+}
+
+func GetImageWidth(path string) (int, error) {
+ extension := filepath.Ext(path)
+
+ if !contains(allowedImageExtensionsIco, extension) {
+ return -1, fmt.Errorf("invalid image extension: %s", extension)
+ }
+
+ // Build magick command arguments
+ args := []string{imageMagickPath, "identify", "-ping", "-format", "%w", path}
+
+ // Execute magick command silently
+ output, err := sendCommand(args...)
+ if err != nil {
+ return -1, fmt.Errorf("error getting image width: %w\n%s", err, output)
+ }
+
+ width, err := strconv.Atoi(strings.TrimSpace(output))
+
+ if err != nil {
+ return -1, err
+ }
+
+ return width, nil
+}
diff --git a/language.go b/language.go
new file mode 100644
index 0000000..81e163d
--- /dev/null
+++ b/language.go
@@ -0,0 +1,52 @@
+package main
+
+import (
+ _ "embed"
+
+ "encoding/json"
+
+ "github.com/jeandeaual/go-locale"
+)
+
+//go:embed frontend/src/locales.json
+var localesJSON string
+
+func set_system_language() error {
+
+ userLocales, err := locale.GetLocales()
+ if err != nil {
+ return err
+ }
+
+ var localesData struct {
+ Locales []struct {
+ Code string `json:"code"`
+ } `json:"locales"`
+ }
+ err = json.Unmarshal([]byte(localesJSON), &localesData)
+ if err != nil {
+ return err
+ }
+ availableLocales := make([]string, len(localesData.Locales))
+ for i, l := range localesData.Locales {
+ availableLocales[i] = l.Code
+ }
+
+ for _, l := range userLocales {
+ if contains(availableLocales, l) {
+ config.Language = &l
+ }
+ }
+
+ return nil
+}
+
+func contains(haystack []string, needle string) bool {
+ for _, v := range haystack {
+ if v == needle {
+ return true
+ }
+ }
+
+ return false
+}
diff --git a/logger.go b/logger.go
new file mode 100644
index 0000000..58a8de7
--- /dev/null
+++ b/logger.go
@@ -0,0 +1,134 @@
+package main
+
+import (
+ "fmt"
+ "log"
+ "os"
+ "path"
+ "sort"
+
+ "github.com/wailsapp/wails/v2/pkg/runtime"
+)
+
+type Logger interface {
+ Print(message string)
+ Trace(message string)
+ Debug(message string)
+ Info(message string)
+ Warning(message string)
+ Error(message string)
+ Fatal(message string)
+}
+
+// Logger is a utility to log messages to a number of destinations
+type FileLogger struct {
+ filename string
+}
+
+// NewLogger creates a new Logger.
+func NewLogger(filename string) Logger {
+ return &FileLogger{
+ filename: filename,
+ }
+}
+
+// Print
+func (l *FileLogger) Print(message string) {
+ f, err := os.OpenFile(l.filename, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o644)
+ if err != nil {
+ log.Fatal(err)
+ }
+ if _, err := f.WriteString(message); err != nil {
+ f.Close()
+ log.Fatal(err)
+ }
+ f.Close()
+}
+
+func (l *FileLogger) Println(message string) {
+ l.Print(message + "\n")
+}
+
+// Trace level logging.
+func (l *FileLogger) Trace(message string) {
+ if *config.EnableTrace {
+ println("TRACE | " + message)
+ l.Println("TRACE | " + message)
+ }
+}
+
+// Debug level logging.
+func (l *FileLogger) Debug(message string) {
+ if *config.EnableDebug {
+ println("DEBUG | " + message)
+ l.Println("DEBUG | " + message)
+ }
+}
+
+// Info level logging.
+func (l *FileLogger) Info(message string) {
+ if *config.EnableInfo {
+ println("INFO | " + message)
+ l.Println("INFO | " + message)
+ }
+}
+
+// Warning level logging.
+func (l *FileLogger) Warning(message string) {
+ if *config.EnableWarn {
+ println("WARN | " + message)
+ l.Println("WARN | " + message)
+ }
+}
+
+// Error level logging.
+func (l *FileLogger) Error(message string) {
+ if *config.EnableError {
+ println("ERROR | " + message)
+ l.Println("ERROR | " + message)
+ }
+}
+
+// Fatal level logging.
+func (l *FileLogger) Fatal(message string) {
+ if *config.EnableFatal {
+ println("FATAL | " + message)
+ l.Println("FATAL | " + message)
+ }
+ os.Exit(1)
+}
+
+func delete_old_logs() {
+ maxLogFiles := *config.MaxLogFiles
+
+ files, err := os.ReadDir(logsFolder)
+ if err != nil {
+ runtime.LogWarning(appContext, "Failed to read log files in logs folder: "+err.Error())
+ }
+
+ runtime.LogTrace(appContext, "Attempting to delete old log files")
+ runtime.LogTrace(appContext, "Attempting to sort log files")
+
+ sort.Slice(files, func(i, j int) bool {
+ infoI, err := os.Stat(path.Join(logsFolder, files[i].Name()))
+ if err != nil {
+ return false
+ }
+
+ infoJ, err := os.Stat(path.Join(logsFolder, files[j].Name()))
+ if err != nil {
+ return false
+ }
+
+ return infoI.ModTime().Before(infoJ.ModTime())
+ })
+
+ runtime.LogTrace(appContext, "Sorting log files complete")
+
+ if len(files) > maxLogFiles {
+ runtime.LogDebug(appContext, fmt.Sprintf("Attempting to delete oldest %d log files", len(files)-maxLogFiles))
+ for i := 0; i < len(files)-maxLogFiles; i++ {
+ os.Remove(path.Join(logsFolder, files[i].Name()))
+ }
+ }
+}
diff --git a/main.go b/main.go
index bc7ecfb..55577ec 100644
--- a/main.go
+++ b/main.go
@@ -2,7 +2,16 @@ package main
import (
"embed"
+ "fmt"
"log"
+ "net/http"
+ "os"
+ "os/exec"
+ "path"
+ "path/filepath"
+ "strings"
+ "syscall"
+ "time"
"github.com/wailsapp/wails/v2"
"github.com/wailsapp/wails/v2/pkg/logger"
@@ -15,47 +24,131 @@ import (
var assets embed.FS
//go:embed build/appicon.png
-var icon []byte
+var appIcon []byte
+
+//go:embed wails.json
+var wailsJSON []byte
+
+var version string
+var NeedsAdminPrivileges bool
+var args []string
+
+type FileLoader struct {
+ http.Handler
+}
+
+func NewFileLoader() *FileLoader {
+ return &FileLoader{}
+}
+
+func (h *FileLoader) ServeHTTP(res http.ResponseWriter, req *http.Request) {
+ var err error
+ requestedFilename := strings.TrimPrefix(filepath.Join(appFolder, req.URL.Path), "/")
+ println("Requesting file:", requestedFilename)
+ fileData, err := os.ReadFile(requestedFilename)
+ if err != nil {
+ res.WriteHeader(http.StatusBadRequest)
+ res.Write([]byte(fmt.Sprintf("Could not load file %s", requestedFilename)))
+ }
+
+ res.Write(fileData)
+}
func main() {
// Create an instance of the app structure
app := NewApp()
+ // Config
+ err := config_init()
+ if err != nil {
+ log.Println(err)
+ }
+
+ // Logger
+ var fileLogger Logger
+
+ if *config.EnableLogging {
+ logsFolder, err = get_logs_folder()
+ if err != nil {
+ log.Println(err)
+ }
+
+ logFile := path.Join(logsFolder, time.Now().Format("2006-01-02_15-04-05")+".log")
+ fileLogger = NewLogger(logFile)
+ }
+
+ // Window State
+ windowsStateInt := *config.WindowStartState
+ windowState := options.Normal
+
+ switch windowsStateInt {
+ case 1:
+ windowState = options.Minimised
+ case 2:
+ windowState = options.Maximised
+ case 3:
+ windowState = options.Fullscreen
+ }
+
+ // Window Effect
+ windowEffectInt := *config.WindowEffect
+ windowEffect := windows.Auto
+ windowTransparent := true
+
+ switch windowEffectInt {
+ case 1:
+ windowEffect = windows.None
+ windowTransparent = false
+ case 2:
+ windowEffect = windows.Mica
+ case 3:
+ windowEffect = windows.Acrylic
+ case 4:
+ windowEffect = windows.Tabbed
+ }
+
// Create application with options
- err := wails.Run(&options.App{
- Title: "Desktop Manager",
- Width: 1024,
- Height: 768,
+ err = wails.Run(&options.App{
+ Title: "Iconium",
+ Width: 1280,
+ Height: 800,
MinWidth: 1024,
MinHeight: 768,
DisableResize: false,
- Fullscreen: false,
- Frameless: false,
- StartHidden: false,
+ Frameless: !*config.UseSystemTitleBar,
+ StartHidden: true,
HideWindowOnClose: false,
BackgroundColour: &options.RGBA{R: 255, G: 255, B: 255, A: 255},
AssetServer: &assetserver.Options{
- Assets: assets,
+ Assets: assets,
+ Handler: NewFileLoader(),
+ },
+ Menu: nil,
+ Logger: fileLogger,
+ LogLevel: logger.TRACE,
+ LogLevelProduction: logger.TRACE,
+ OnStartup: app.startup,
+ OnDomReady: app.domReady,
+ OnBeforeClose: app.beforeClose,
+ OnShutdown: app.shutdown,
+ WindowStartState: windowState,
+ SingleInstanceLock: &options.SingleInstanceLock{
+ UniqueId: "4d1e7b7b-42a3-4357-92f3-425b1087b545",
+ OnSecondInstanceLaunch: app.onSecondInstanceLaunch,
},
- Menu: nil,
- Logger: nil,
- LogLevel: logger.DEBUG,
- OnStartup: app.startup,
- OnDomReady: app.domReady,
- OnBeforeClose: app.beforeClose,
- OnShutdown: app.shutdown,
- WindowStartState: options.Normal,
Bind: []interface{}{
app,
},
// Windows platform specific options
Windows: &windows.Options{
- WebviewIsTransparent: false,
- WindowIsTranslucent: false,
+ WebviewIsTransparent: windowTransparent,
+ WindowIsTranslucent: windowTransparent,
DisableWindowIcon: false,
+ BackdropType: windowEffect,
// DisableFramelessWindowDecorations: false,
- WebviewUserDataPath: "",
+ WebviewUserDataPath: path.Join(os.Getenv("APPDATA"), "iconium"),
ZoomFactor: 1.0,
+ DisablePinchZoom: true,
},
})
@@ -63,3 +156,15 @@ func main() {
log.Fatal(err)
}
}
+
+func sendCommand(command ...string) (string, error) {
+ // Use `exec.Command` to send a command to the existing cmd window
+ cmd := exec.Command("cmd.exe", append([]string{"/C"}, command...)...)
+ cmd.SysProcAttr = &syscall.SysProcAttr{
+ HideWindow: true,
+ }
+
+ output, err := cmd.Output()
+
+ return string(output), err
+}
diff --git a/update.go b/update.go
new file mode 100644
index 0000000..e9f858e
--- /dev/null
+++ b/update.go
@@ -0,0 +1,169 @@
+package main
+
+import (
+ "encoding/json"
+ "fmt"
+ "io"
+ "net/http"
+ "strings"
+ "time"
+
+ "github.com/blang/semver"
+ "github.com/minio/selfupdate"
+ "github.com/wailsapp/wails/v2/pkg/runtime"
+)
+
+type UpdateInfo struct {
+ UpdateAvailable bool `json:"updateAvailable"`
+ CurrentVersion string `json:"currentVersion"`
+ LatestVersion string `json:"latestVersion"`
+ Name string `json:"name"`
+ ReleaseNotes string `json:"releaseNotes"`
+ DownloadUrl string `json:"downloadUrl"`
+}
+
+type Release struct {
+ TagName string `json:"tag_name"`
+ Name string `json:"name"`
+ ReleaseNotes string `json:"body"`
+ Prerelease bool `json:"prerelease"`
+}
+
+func (app *App) CheckForUpdate() UpdateInfo {
+ var updateInfo UpdateInfo = UpdateInfo{
+ UpdateAvailable: false,
+ CurrentVersion: version,
+ LatestVersion: "",
+ ReleaseNotes: "",
+ DownloadUrl: "",
+ }
+ updateInfo.CurrentVersion = version
+
+ // Set last update check
+ lastUpdateCheck := int(time.Now().Unix())
+ config.LastUpdateCheck = &lastUpdateCheck
+
+ repoOwner := "beyenilmez"
+ repoName := "iconium"
+
+ // GitHub API endpoint to fetch latest release
+ apiUrl := fmt.Sprintf("https://api.github.com/repos/%s/%s/releases/latest", repoOwner, repoName)
+ runtime.LogDebug(app.ctx, "GitHub API URL: "+apiUrl)
+
+ // Make GET request to GitHub API
+ resp, err := http.Get(apiUrl)
+ if err != nil {
+ runtime.LogError(app.ctx, "Error sending request: "+err.Error())
+ app.SendNotification("settings.setting.update.failed_to_check_for_updates", "", "", "error")
+ return updateInfo
+ }
+ defer resp.Body.Close()
+ runtime.LogDebug(app.ctx, fmt.Sprintf("GitHub API response status: %d", resp.StatusCode))
+
+ // Check if response was successful
+ if resp.StatusCode != http.StatusOK {
+ app.SendNotification("settings.setting.update.failed_to_check_for_updates", "", "", "error")
+ return updateInfo
+ }
+
+ // Read response body
+ body, err := io.ReadAll(resp.Body)
+ if err != nil {
+ runtime.LogError(app.ctx, "Error reading response: "+err.Error())
+ return updateInfo
+ }
+ runtime.LogTrace(app.ctx, "GitHub API response body: "+string(body))
+
+ // Parse JSON response
+ var release Release
+ err = json.Unmarshal(body, &release)
+ if err != nil {
+ runtime.LogError(app.ctx, "Error decoding JSON: "+err.Error())
+ return updateInfo
+ }
+
+ // Extract release information
+ latestVersion := release.TagName
+ releaseNotes := release.ReleaseNotes
+ prerelease := release.Prerelease
+ name := release.Name
+ downloadUrl := fmt.Sprintf("https://github.com/%s/%s/releases/download/%s/iconium.exe", repoOwner, repoName, latestVersion)
+
+ // Parse current and latest versions
+ parsedVersion, err := semver.ParseTolerant(version)
+ if err != nil {
+ runtime.LogError(app.ctx, "Error parsing current version: "+err.Error())
+ updateInfo.UpdateAvailable = false
+ return updateInfo
+ }
+ parsedLatestVersion, err := semver.ParseTolerant(strings.ReplaceAll(strings.ReplaceAll(strings.ReplaceAll(strings.ReplaceAll(latestVersion, "v", ""), "-", ""), "alpha", ""), "beta", ""))
+ if err != nil {
+ runtime.LogError(app.ctx, "Error parsing latest version: "+err.Error())
+ updateInfo.UpdateAvailable = false
+ return updateInfo
+ }
+
+ // Log release information
+ runtime.LogDebug(app.ctx, fmt.Sprintf("Current version: %s", parsedVersion))
+ runtime.LogDebug(app.ctx, fmt.Sprintf("Latest version: %s", parsedLatestVersion))
+ runtime.LogDebug(app.ctx, fmt.Sprintf("Prerelease: %t", prerelease))
+ runtime.LogDebug(app.ctx, fmt.Sprintf("Release name: %s", name))
+ runtime.LogDebug(app.ctx, fmt.Sprintf("Release notes: %s", releaseNotes))
+ runtime.LogDebug(app.ctx, fmt.Sprintf("Download URL: %s", downloadUrl))
+
+ // Check if a new version is available
+ if parsedVersion.Compare(parsedLatestVersion) < 0 && !prerelease {
+ runtime.LogInfo(app.ctx, fmt.Sprintf("A new version (%s) is available.", latestVersion))
+ updateInfo.UpdateAvailable = true
+ updateInfo.LatestVersion = latestVersion
+ updateInfo.Name = name
+ updateInfo.ReleaseNotes = releaseNotes
+ updateInfo.DownloadUrl = downloadUrl
+ } else {
+ runtime.LogInfo(app.ctx, "You have the latest version.")
+ }
+
+ return updateInfo
+}
+
+func (app *App) Update(downloadUrl string) error {
+ // Log the download URL
+ runtime.LogInfo(app.ctx, "Starting update download from: "+downloadUrl)
+
+ resp, err := http.Get(downloadUrl)
+ if err != nil {
+ runtime.LogError(app.ctx, "Error downloading update: "+err.Error())
+ app.SendNotification("settings.setting.update.failed_to_download_update", "", "", "error")
+ return err
+ }
+ defer resp.Body.Close()
+
+ // Log the status code from the response
+ runtime.LogDebug(app.ctx, fmt.Sprintf("Download response status: %d", resp.StatusCode))
+
+ // Check if the response was successful
+ if resp.StatusCode != http.StatusOK {
+ app.SendNotification("settings.setting.update.failed_to_download_update", "", "", "error")
+ return err
+ }
+
+ // Apply the update
+ err = selfupdate.Apply(resp.Body, selfupdate.Options{})
+ if err != nil {
+ runtime.LogError(app.ctx, "Error applying update: "+err.Error())
+ app.SendNotification("settings.setting.update.failed_to_apply_update", err.Error(), "", "error")
+ return err
+ }
+
+ runtime.LogInfo(app.ctx, "Update applied successfully. Restarting.")
+ app.SendNotification("settings.setting.update.update_applied", "settings.setting.update.restarting", "", "success")
+
+ // Restart the application
+ app.RestartApplication(false, []string{"--goto", "settings__update", "--notify", "settings.setting.update.update_successful", "", "", "success"})
+
+ return nil
+}
+
+func (app *App) UpdateAsAdmin(downloadUrl string) {
+ app.RestartApplication(true, []string{"--goto", "settings__update__" + downloadUrl})
+}
diff --git a/utils.go b/utils.go
new file mode 100644
index 0000000..d7e7bdd
--- /dev/null
+++ b/utils.go
@@ -0,0 +1,574 @@
+package main
+
+import (
+ "archive/zip"
+ "encoding/base64"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "io"
+ "net/http"
+ "os"
+ "path/filepath"
+ "strings"
+ "sync"
+
+ "github.com/google/uuid"
+ lnk "github.com/parsiya/golnk"
+ "github.com/wailsapp/wails/v2/pkg/runtime"
+ "gopkg.in/ini.v1"
+)
+
+func writeJSON(path string, data interface{}) error {
+ file, err := os.Create(path)
+ if err != nil {
+ return err
+ }
+ defer file.Close()
+
+ return json.NewEncoder(file).Encode(data)
+}
+
+func readJSON(path string, data interface{}) error {
+ file, err := os.Open(path)
+ if err != nil {
+ return err
+ }
+ defer file.Close()
+
+ return json.NewDecoder(file).Decode(data)
+}
+
+// Create folder if it doesn't exist, return error
+func create_folder(folder string) error {
+ if _, err := os.Stat(folder); os.IsNotExist(err) {
+ err = os.MkdirAll(folder, 0o755)
+ if err != nil {
+ return err
+ }
+ } else {
+ runtime.LogDebug(appContext, "Folder already exists: "+folder)
+ return nil
+ }
+ runtime.LogDebug(appContext, "Created folder: "+folder)
+
+ return nil
+}
+
+func downloadFile(url, dest string, progress chan<- int64, errors chan<- error, wg *sync.WaitGroup) {
+ defer wg.Done()
+
+ // Create file
+ out, err := os.Create(dest)
+ if err != nil {
+ errors <- err
+ return
+ }
+ defer out.Close()
+
+ // Fetch file size
+ resp, err := http.Get(url)
+ if err != nil {
+ errors <- err
+ return
+ }
+ defer resp.Body.Close()
+
+ buf := make([]byte, 1024*8)
+ for {
+ n, err := resp.Body.Read(buf)
+ if n > 0 {
+ _, writeErr := out.Write(buf[:n])
+ if writeErr != nil {
+ errors <- writeErr
+ return
+ }
+ progress <- int64(n) // Report the number of bytes read
+ }
+ if err != nil {
+ if err != io.EOF {
+ errors <- err
+ }
+ break
+ }
+ }
+}
+
+func GenerateBase64Png(bytes []byte) string {
+ var base64Encoding string
+
+ // Determine the content type of the image file
+ mimeType := http.DetectContentType(bytes)
+
+ // Prepend the appropriate URI scheme header depending
+ // on the MIME type
+ switch mimeType {
+ case "image/png":
+ base64Encoding += "data:image/png;base64,"
+ case "image/x-icon":
+ base64Encoding += "data:image/x-icon;base64,"
+ default:
+ runtime.LogWarning(appContext, "Unrecognized image mime type: "+mimeType)
+ return ""
+ }
+
+ // Append the base64 encoded output
+ base64Encoding += base64.StdEncoding.EncodeToString(bytes)
+
+ return base64Encoding
+}
+
+func GenerateBase64PngFromPath(filePath string) string {
+ // Read the entire file into a byte slice
+ bytes, err := os.ReadFile(filePath)
+
+ if err != nil {
+ runtime.LogError(appContext, "Error reading file: "+err.Error())
+ }
+
+ ext := filepath.Ext(filePath)
+
+ switch strings.ToLower(ext) {
+ case ".png":
+ return GenerateBase64Png(bytes)
+ case ".ico":
+ return GenerateBase64Png(bytes)
+ case ".lnk":
+ link, err := lnk.File(filePath)
+ if err != nil {
+ runtime.LogError(appContext, "Error reading link: "+err.Error())
+ }
+
+ runtime.LogDebug(appContext, "Icon location: "+link.StringData.IconLocation)
+
+ if strings.ToLower(filepath.Ext(link.StringData.IconLocation)) == ".ico" {
+ return GenerateBase64PngFromPath(link.StringData.IconLocation)
+ }
+ default:
+ runtime.LogWarning(appContext, "Unrecognized file type: "+ext)
+ return ""
+ }
+
+ return ""
+}
+
+func ConvertToGeneralPath(path string) string {
+ desktop, public := get_desktop_paths()
+
+ // List of common environment variables to replace
+ envVars := []string{
+ "PROGRAMFILES(X86)",
+ "PROGRAMFILES",
+ "APPDATA",
+ "LOCALAPPDATA",
+ "PROGRAMDATA",
+ "USERPROFILE",
+ "PUBLIC",
+ "SYSTEMROOT",
+ "WINDIR",
+ "HOMEDRIVE",
+ "SYSTEMDRIVE",
+ }
+
+ if strings.HasPrefix(strings.ToLower(path), strings.ToLower(desktop)) {
+ path = strings.ReplaceAll(path, desktop, "${DESKTOP}")
+ } else if strings.HasPrefix(strings.ToLower(path), strings.ToLower(public)) {
+ path = strings.ReplaceAll(path, public, "${DESKTOP}")
+ }
+
+ // Replace environment variables
+ for _, envVar := range envVars {
+ placeholder := "${" + envVar + "}"
+ envValue := os.Getenv(envVar)
+
+ if strings.Contains(strings.ToLower(path), strings.ToLower(envValue)) {
+ path = strings.ReplaceAll(path, envValue, placeholder)
+ }
+ }
+
+ return path
+}
+
+func ConvertToFullPath(path string) string {
+ path = filepath.Clean(path)
+
+ os.Setenv("DESKTOP", "")
+ path = os.ExpandEnv(path)
+
+ paths := []string{path}
+
+ if strings.Contains(strings.ToUpper(path), "") {
+ desktop, public := get_desktop_paths()
+
+ path1 := strings.ReplaceAll(path, "", desktop)
+ path2 := strings.ReplaceAll(path, "", public)
+
+ paths = []string{path1, path2}
+ }
+
+ if strings.Contains(path, `**`) {
+ newPaths := []string{}
+
+ for _, path := range paths {
+ paths := generateCombinations(path)
+
+ newPaths = append(newPaths, paths...)
+ }
+
+ paths = newPaths
+ }
+
+ for _, path := range paths {
+ matches, err := filepath.Glob(path)
+ if err != nil {
+ runtime.LogErrorf(appContext, "Error globbing path: %s", err)
+ continue
+ }
+
+ if len(matches) > 0 {
+ return matches[0]
+ }
+ }
+
+ return ""
+}
+
+func generateCombinations(path string) []string {
+ // Find the first occurrence of multiple consecutive asterisks pattern (e.g., **, ***)
+ index := strings.Index(path, "**")
+ if index == -1 {
+ return []string{filepath.Clean(path)} // No more '**' to replace, return cleaned path
+ }
+
+ // Count consecutive asterisks to determine maximum depth
+ maxDepth := 0
+ for i := index; i < len(path) && path[i] == '*'; i++ {
+ maxDepth++
+ }
+
+ runtime.LogDebugf(appContext, "Generating combinations with max depth: %d", maxDepth-1)
+
+ var results []string
+ base := path[:index]
+ suffix := path[index+maxDepth:] // Adjust suffix to skip over the asterisks
+
+ // Generate combinations based on current asterisk count (depth)
+ for i := 0; i < maxDepth; i++ {
+ // Combine path with varying depth of wildcards
+ if i == 0 {
+ // Add combination without extra depth
+ combinedPath := filepath.Clean(base + suffix)
+ results = append(results, generateCombinations(combinedPath)...)
+ } else {
+ // Add combinations with increasing depth
+ wildcards := strings.Repeat(`*\`, i)
+ combinedPath := filepath.Clean(base + wildcards + suffix)
+ results = append(results, generateCombinations(combinedPath)...)
+ }
+ }
+
+ return results
+}
+
+func copy_file(src string, dst string) error {
+ input, err := os.ReadFile(src)
+ if err != nil {
+ return err
+ }
+ err = os.WriteFile(dst, input, 0o644)
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+func zip_folder(src string, dst string) error {
+ // Create the destination zip file
+ zipFile, err := os.Create(dst)
+ if err != nil {
+ return fmt.Errorf("failed to create zip file: %w", err)
+ }
+ defer zipFile.Close()
+
+ // Initialize the zip writer
+ archive := zip.NewWriter(zipFile)
+ defer func() {
+ if cerr := archive.Close(); cerr != nil && err == nil {
+ err = fmt.Errorf("failed to close archive: %w", cerr)
+ }
+ }()
+
+ // Walk the directory tree
+ err = filepath.Walk(src, func(path string, info os.FileInfo, err error) error {
+ if err != nil {
+ return fmt.Errorf("error accessing path %s: %w", path, err)
+ }
+
+ // Skip directories but add them to the zip archive to preserve structure
+ if info.IsDir() {
+ return nil
+ }
+
+ if info.Name() == "apply.json" {
+ return nil
+ }
+
+ // Open the file to be zipped
+ file, err := os.Open(path)
+ if err != nil {
+ return fmt.Errorf("failed to open file %s: %w", path, err)
+ }
+ defer file.Close()
+
+ // Create the file header in the archive
+ relPath, err := filepath.Rel(filepath.Dir(src), path)
+ if err != nil {
+ return fmt.Errorf("failed to get relative path: %w", err)
+ }
+
+ f, err := archive.Create(relPath)
+ if err != nil {
+ return fmt.Errorf("failed to create entry for %s in zip file: %w", relPath, err)
+ }
+
+ // Copy the file content to the archive
+ if _, err = io.Copy(f, file); err != nil {
+ return fmt.Errorf("failed to write file %s to archive: %w", relPath, err)
+ }
+
+ return nil
+ })
+
+ if err != nil {
+ return fmt.Errorf("failed to zip folder %s: %w", src, err)
+ }
+
+ return nil
+}
+
+func unzip_folder(src, dst string) error {
+ // Open the ZIP file
+ zipFile, err := os.Open(src)
+ if err != nil {
+ return fmt.Errorf("failed to open zip file: %w", err)
+ }
+ defer zipFile.Close()
+
+ // Read the ZIP file
+ stat, err := zipFile.Stat()
+ if err != nil {
+ return fmt.Errorf("failed to get zip file info: %w", err)
+ }
+
+ reader, err := zip.NewReader(zipFile, stat.Size())
+ if err != nil {
+ return fmt.Errorf("failed to create zip reader: %w", err)
+ }
+
+ // Extract each file and directory
+ for _, file := range reader.File {
+ if filepath.Base(file.FileInfo().Name()) == "apply.json" {
+ continue
+ }
+
+ filePath := filepath.Join(dst, file.Name)
+
+ // Ensure the path is within the destination folder
+ if !strings.HasPrefix(filePath, filepath.Clean(dst)+string(os.PathSeparator)) {
+ return fmt.Errorf("illegal file path: %s", filePath)
+ }
+
+ // If the file is a directory, create it
+ if file.FileInfo().IsDir() {
+ if err := os.MkdirAll(filePath, os.ModePerm); err != nil {
+ return fmt.Errorf("failed to create directory: %w", err)
+ }
+ continue
+ }
+
+ // Ensure parent directory exists
+ if err := os.MkdirAll(filepath.Dir(filePath), os.ModePerm); err != nil {
+ return fmt.Errorf("failed to create directory for file: %w", err)
+ }
+
+ // Extract the file
+ destFile, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, file.Mode())
+ if err != nil {
+ return fmt.Errorf("failed to open file for writing: %w", err)
+ }
+ defer destFile.Close()
+
+ fileInArchive, err := file.Open()
+ if err != nil {
+ return fmt.Errorf("failed to open file in archive: %w", err)
+ }
+ defer fileInArchive.Close()
+
+ if _, err := io.Copy(destFile, fileInArchive); err != nil {
+ return fmt.Errorf("failed to copy file content: %w", err)
+ }
+ }
+
+ return nil
+}
+
+func exists(path string) bool {
+ _, err := os.Stat(path)
+ return !os.IsNotExist(err)
+}
+
+func is_dir(path string) bool {
+ info, err := os.Stat(path)
+ if err != nil {
+ return false
+ }
+ return info.IsDir()
+}
+
+func (a *App) UUID() string {
+ return uuid.NewString()
+}
+
+func (a *App) GeneralPathExits(path string) bool {
+ return ConvertToFullPath(path) != ""
+}
+
+func (a *App) Ext(path string) string {
+ fullPath := ConvertToFullPath(path)
+ if fullPath == "" {
+ return strings.ToLower(filepath.Ext(path))
+ } else {
+ return strings.ToLower(filepath.Ext(fullPath))
+ }
+}
+
+func (a *App) Name(path string) string {
+ fullPath := ConvertToFullPath(path)
+ if fullPath == "" {
+ base := filepath.Base(path)
+ return strings.TrimSuffix(base, filepath.Ext(base))
+ }
+ base := filepath.Base(fullPath)
+ return strings.TrimSuffix(base, filepath.Ext(base))
+}
+
+func (a *App) Description(path string) string {
+ path = ConvertToFullPath(path)
+ if path == "" {
+ return ""
+ }
+ link, err := lnk.File(path)
+ if err != nil {
+ return ""
+ }
+
+ return link.StringData.NameString
+}
+
+func (a *App) Destination(path string) string {
+ path = ConvertToFullPath(path)
+ if path == "" {
+ return ""
+ }
+
+ ext := a.Ext(path)
+
+ var destination string
+
+ if ext == ".lnk" {
+ link, err := lnk.File(path)
+ if err != nil {
+ return ""
+ }
+
+ if link.LinkInfo.LocalBasePath != "" {
+ destination = link.LinkInfo.LocalBasePath
+ }
+ if link.LinkInfo.LocalBasePathUnicode != "" {
+ destination = link.LinkInfo.LocalBasePathUnicode
+ }
+ } else if ext == ".url" {
+ iniPath := filepath.Join(path)
+ if !exists(iniPath) {
+ return ""
+ }
+ iniContent, err := os.ReadFile(iniPath)
+ if err != nil {
+ return ""
+ }
+ iniFile, err := ini.Load(iniContent)
+ if err != nil {
+ return ""
+ }
+ section := iniFile.Section("InternetShortcut")
+
+ destination = section.Key("URL").String()
+ }
+
+ runtime.LogDebugf(appContext, "Destination: %s", destination)
+
+ return ConvertToGeneralPath(destination)
+}
+
+func (a *App) IconLocation(path string) string {
+ path = ConvertToFullPath(path)
+ if path == "" {
+ return ""
+ }
+
+ if a.Ext(path) == ".lnk" {
+ link, err := lnk.File(path)
+ if err != nil {
+ return ""
+ }
+
+ return link.StringData.IconLocation
+ } else if a.Ext(path) == ".url" {
+ iniPath := filepath.Join(path)
+ if !exists(iniPath) {
+ return ""
+ }
+ iniContent, err := os.ReadFile(iniPath)
+ if err != nil {
+ return ""
+ }
+ iniFile, err := ini.Load(iniContent)
+ if err != nil {
+ return ""
+ }
+ section := iniFile.Section("InternetShortcut")
+
+ return section.Key("IconFile").String()
+ }
+
+ return ""
+}
+
+func (a *App) CreateLastTab(path string) {
+ tempFilePath := filepath.Join(tempFolder, "iconium-last-tab.txt")
+
+ if err := os.WriteFile(tempFilePath, []byte(path), 0o644); err != nil {
+ runtime.LogErrorf(appContext, "Error writing last tab path: %s", err)
+ }
+}
+
+func (a *App) ReadLastTab() string {
+ tempFilePath := filepath.Join(tempFolder, "iconium-last-tab.txt")
+
+ if _, err := os.Stat(tempFilePath); errors.Is(err, os.ErrNotExist) {
+ return ""
+ }
+
+ content, err := os.ReadFile(tempFilePath)
+ if err != nil {
+ runtime.LogErrorf(appContext, "Error reading last tab path: %s", err)
+ return ""
+ }
+
+ // Delete the temp file
+ if err := os.Remove(tempFilePath); err != nil {
+ runtime.LogErrorf(appContext, "Error removing temp file: %s", err)
+ }
+
+ return string(content)
+}
diff --git a/wails.json b/wails.json
index 4f93630..2ac8a64 100644
--- a/wails.json
+++ b/wails.json
@@ -1,7 +1,8 @@
{
"$schema": "https://wails.io/schemas/config.v2.json",
- "name": "desktop-manager",
- "outputfilename": "desktop-manager",
+ "name": "iconium",
+ "outputfilename": "iconium",
+ "wailsjsdir": "./frontend/src",
"frontend:install": "yarn",
"frontend:build": "yarn build",
"frontend:dev:watcher": "yarn dev",
@@ -11,7 +12,16 @@
"email": "yenilmezbedirhan@gmail.com"
},
"info": {
- "productName": "Desktop Manager",
- "productVersion": "0.1.0"
+ "productName": "Iconium",
+ "productVersion": "1.0.0",
+ "copyright": "Copyright © 2024 Bedirhan Yenilmez",
+ "fileAssociations": [
+ {
+ "ext": "icnm",
+ "name": "ICNM",
+ "description": "Icon Pack",
+ "iconName": "packicon"
+ }
+ ]
}
}