diff --git a/.eslintrc.yml b/.eslintrc.yml deleted file mode 100644 index edd99f84..00000000 --- a/.eslintrc.yml +++ /dev/null @@ -1,129 +0,0 @@ -env: - es2021: true -extends: - - "eslint:recommended" - - "plugin:@typescript-eslint/recommended" -parser: "@typescript-eslint/parser" -parserOptions: - ecmaVersion: 2022 - sourceType: "module" - project: "./tsconfig.json" - warnOnUnsupportedTypeScriptVersion: false -root: true -ignorePatterns: - - example/ - - types/ - - gi-types/ - - _build/ - - build/ - - result/ -plugins: - - "@typescript-eslint" -rules: - "@typescript-eslint/ban-ts-comment": - - "off" - "@typescript-eslint/no-non-null-assertion": - - "off" - "@typescript-eslint/no-explicit-any": - - "off" - "@typescript-eslint/no-unused-vars": - - error - - varsIgnorePattern: (^unused|_$) - argsIgnorePattern: ^(unused|_) - "@typescript-eslint/no-empty-interface": - - "off" - - arrow-parens: - - error - - as-needed - comma-dangle: - - error - - always-multiline - comma-spacing: - - error - - before: false - after: true - comma-style: - - error - - last - curly: - - error - - multi-or-nest - - consistent - dot-location: - - error - - property - eol-last: - - error - indent: - - error - - 4 - - SwitchCase: 1 - keyword-spacing: - - error - - before: true - lines-between-class-members: - - error - - always - - exceptAfterSingleLine: true - padded-blocks: - - error - - never - - allowSingleLineBlocks: false - prefer-const: - - error - quotes: - - error - - single - - avoidEscape: true - semi: - - error - - always - nonblock-statement-body-position: - - error - - below - no-trailing-spaces: - - error - no-useless-escape: - - off - max-len: - - error - - code: 100 - - func-call-spacing: - - error - array-bracket-spacing: - - error - space-before-function-paren: - - error - - anonymous: never - named: never - asyncArrow: ignore - space-before-blocks: - - error - key-spacing: - - error - object-curly-spacing: - - error - - always - -globals: - pkg: readonly - ARGV: readonly - Debugger: readonly - GIRepositoryGType: readonly - globalThis: readonly - imports: readonly - Intl: readonly - log: readonly - logError: readonly - print: readonly - printerr: readonly - window: readonly - TextEncoder: readonly - TextDecoder: readonly - console: readonly - setTimeout: readonly - setInterval: readonly - clearTimeout: readonly - clearInterval: readonly diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..59e97d66 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +data/**/* linguist-vendored diff --git a/.github/workflows/cachix.yml b/.github/workflows/cachix.yml index 9f035354..88c85af4 100644 --- a/.github/workflows/cachix.yml +++ b/.github/workflows/cachix.yml @@ -6,19 +6,18 @@ jobs: name: "Build" runs-on: ubuntu-latest steps: - - name: Checkout - uses: actions/checkout@v3 - with: - submodules: recursive + - name: Checkout + uses: actions/checkout@v3 + with: + submodules: recursive - - uses: cachix/install-nix-action@v25 - - uses: DeterminateSystems/magic-nix-cache-action@main - - uses: cachix/cachix-action@v12 - with: - name: ags - authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' + - uses: cachix/install-nix-action@v25 + - uses: DeterminateSystems/magic-nix-cache-action@main + - uses: cachix/cachix-action@v12 + with: + name: ags + authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}" - - name: Build ags - run: | - nix build --print-build-logs - nix build --print-build-logs .#agsWithTypes + - name: Build ags + run: | + nix build --print-build-logs diff --git a/.github/workflows/create_release.yaml b/.github/workflows/create_release.yaml deleted file mode 100644 index c209d25f..00000000 --- a/.github/workflows/create_release.yaml +++ /dev/null @@ -1,35 +0,0 @@ -name: Create Release - -permissions: - contents: write - -on: - release: - types: [published] - -jobs: - release: - name: Create Release - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - submodules: 'recursive' - - name: Create ags tar file - run: | - cd .. - tar -czf "ags-${{ github.ref_name }}.tar.gz" "ags" - - name: setup node - uses: actions/setup-node@v3 - with: - node-version: latest - - name: npm install - run: npm install - - name: create node tar file - run: tar -czf "node_modules-${{ github.ref_name }}.tar.gz" "node_modules" - - name: Upload assets - uses: softprops/action-gh-release@v1 - with: - files: | - ../ags-${{ github.ref_name }}.tar.gz - node_modules-${{ github.ref_name }}.tar.gz diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml deleted file mode 100644 index e10ec6eb..00000000 --- a/.github/workflows/lint.yml +++ /dev/null @@ -1,16 +0,0 @@ -name: Lint - -on: [push, pull_request] - -jobs: - lint: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: lint and typecheck - uses: actions/setup-node@v3 - with: - node-version: latest - cache: 'npm' - - run: npm ci - - run: npm test diff --git a/.gitignore b/.gitignore index 77d456ac..bfa2f0c1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,5 @@ -node_modules -_build -build -run.sh -tmp -result +node_modules/ +build/ +dist/ +result/ +@girs/ diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index e3feba7f..00000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "subprojects/gvc"] - path = subprojects/gvc - url = https://gitlab.gnome.org/GNOME/libgnome-volume-control diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index 47aa0f11..00000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,199 +0,0 @@ -# 1.8.2 - -## Features - -- Calendar.detail -- SpinButton.range -- SpinButton.increments -- Network.frequency -- recursive Utils.monitorFile -- add: Network.vpn -- add write and writeAsync to Utils.subprocess (#388) - -## Fixes - -- compiles with typescript >= 5.0.4 -- DrawingArea.draw-fn -- hyprland: active client empty on window close -- dispose signal on Variable -- skip unnecessary value setting in Utils.derive and Utils.merge -- properly log errors from Variables -- adjust Hyprland socket (#398) - -## Breaking Changes - -- Stream.is_muted corresponds to actual mute state -- Utils.exec returns stderr on error - -# 1.8.0 - -## Features - -- add: Utils.watch -- custom hookable objects -- add: App.config -- impove widget subclasses - - Calendar.on_day_selected - - ColorButton.on_color_set - - DrawingArea.draw_fn - - FileChooserButton.on_file_set - - FontButton.on_font_set - - LevelBar.vertical - - LevelBar.bar_mode - - Separator.vertical - - SpinButton.on_value_changed - - Spinner starts based on visibility - - Switch.on_activate - - ToggleButton.on_toggled -- print notification daemons's name when its already running - -## Fixes - -- Widget.attribute assign falsy values -- Overlay child type - -## Breaking Changes - -- revert: hyprland service: workspace and monitor signal emit number -- types: Label's and Icon's Props type renamed to LabelProps, IconProps -- deprecate: default export config object in favor of App.config - -# 1.7.7 - -## Features - -- App.addIcons, App.gtkTheme, App.cursorTheme, App.iconTheme -- add: Notifications.clearDelay -- add MprisPlayer.track_album -- add MprisPlayer.metadata -- add Widget.keybind -- App.applyCss takes stylesheets, and an optional reset parameter - -## Fixes - -- prepend icons from config instead of append -- Network.wifi.enabled signal -- Utils.merge connect to notify signal - -## Breaking Changes - -- deprecate: Window.popup - -# 1.7.6 - -## Features - -- Utils.writeFileSync -- add Utils.merge, Utils.derive -- add Binding.as alias for Binding.transform - -## Fixes - -- Stack.add_named -- Scrollable destroy child on destroy event - -## Breaking Changes - -- hyprland service: workspace and monitor signal emit number -- hyprland service: deprecate sendMessage, introduce message and messageAsync -- Variable: value check on setter, force on setValue -- `Utils.monitorFile()` no longer takes the `type` (`file` or `directory`) parameter. It will monitor each accordingly without specifying it. - -# 1.7.5 - -## Features - -- generate types for utils subdirectory (#287) -- export gobject utils in Utils -- bind service methods -- make App.closeWindowDelay writable - -## Fixes - -- widget: button, eventbox child second parameter - -## Breaking Changes - -- add: Stack.children -- deprecate: Stack.items - -# 1.7.4 - -## Features - -- add: Overlay.overlay Box.child -- add: params to Utils.fetch -- feat(circular-progress): end-at property (#239) -- feat(Utils.notify) -- feat(notifications): support every hint -- add: Widget.click-through (#245) -- feat: --init cli flag -- add: Widget.keymode -- improved types -- add: Window.gdkmonitor -- export modules globally -- make Audio.microphone and Audio.speaker always -- feat: greetd service (#282) -- feat(pam): Utils.authenticate (#273) -- feat: child property as second parameter [#265](https://github.com/Aylur/ags/pull/265/) - -## Breaking Changes - -- subclassing of widgets - -## Fixes - -- notifications: warn on non 8 bits image - -# 1.6.3 - -## Features - -- feat: Service.bind and Variable.bind -- feat: AgsWidget.register -- export Widget.createCtor utility -- add: Applications.reload -- add: Utils.idle -- use GLib.shell_parse_argv on Utils.execAsync -- feat: Utils.fetch -- overwrite toJSON method on GObjects -- feat: PowerProfile Service - -## Breaking Changes - -- update: Hyprland.active.monitor to be an object - -# 1.5.5 - -## Features - -- feat: support print from client with --run-js -- feat: support shebang with --run-file -- add: Utils.monitorFile -- feat: Utils.readFile and readFileAsync can take a Gio.File -- improve Button, EventBox hover events -- parse passed files starting with . -- feat: binds targetProp can be in kebab, camel or snake case too -- add: hook, on, poll, bind, attribute - -# 1.5.4 - -## Features - -- add: notificationForceTimeout option -- add: bluetooth device-added, device-removed signal -- add: cursor property -- feat: window popup close on click away -- add: config.onWindowToggled & config.onConfigParsed -- add: marks property setter to slider #186 -- feat: --run-js async support -- add: --run-file - -## Breaking Changes - -- feat: Window.exclusivity -- deprecate: --run-promise cli flag - -## Fixes - -- overlay pass-through #168 diff --git a/README.md b/README.md index 19521003..1be50d6e 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,10 @@ -# Aylur's Gtk Shell +# AGS -This is a library built for [GJS](https://gitlab.gnome.org/GNOME/gjs) to allow defining GTK widgets in a declarative way. It also provides services and other utilities to interact with the system so that these widgets can have functionality. -GJS is a JavaScript runtime built on Firefox's SpiderMonkey JavaScript engine and the GNOME platform libraries, the same runtime [GNOME Shell](https://gitlab.gnome.org/GNOME/gnome-shell) runs on. - -It was heavily inspired by [EWW](https://github.com/elkowar/eww). - -Currently, only Wayland is supported, but it also works on X11, [see #19](https://github.com/Aylur/ags/issues/19). +CLI around [Astal](https://github.com/aylur/astal) to scaffold and run projects. +Astal is a set of libraries written in Vala/C that makes writing a Desktop Shell easy. +It also has an accompanying JavaScript library which lets you write Desktop Shells in JSX running on GJS. +GJS is a JavaScript runtime built on Firefox's SpiderMonkey JavaScript engine and the GNOME platform libraries, the same runtime [GNOME Shell](https://gitlab.gnome.org/GNOME/gnome-shell) runs on. ## Get started -To get started read the [wiki](https://aylur.github.io/ags-docs). +To get started read the [wiki](https://aylur.github.io/ags). diff --git a/TODO.md b/TODO.md deleted file mode 100644 index aa442f3c..00000000 --- a/TODO.md +++ /dev/null @@ -1,40 +0,0 @@ -# Planned features/improvements - -- services - - [x] power profiles [#218](https://github.com/Aylur/ags/pull/218) - - [x] greetd [#282](https://github.com/Aylur/ags/pull/282) - - [ ] evolution data server - allows for to sync with calendars, todos and contact lists - - [ ] improve Network service - its currently very barebones, and state changes are not properly signaled - -- utility gobject based library in c - - [x] pam module [#273](https://github.com/Aylur/ags/pull/273) - - [ ] ext-session-lock - -- [x] fetch util function [#187](https://github.com/Aylur/ags/pull/187) -- [x] toJSON overridies [#203](https://github.com/Aylur/ags/pull/203) - -- [ ] circular slider widget - -- subclass more widget - - [ ] Gtk.Fixed - - [ ] Gtk.Grid - -- Nix - - [ ] NixOS module - - [x] binary cache [#212](https://github.com/Aylur/ags/pull/212) - -- package generated types and @gir types - - [ ] ~~github action to package types~~ - - [x] install them at ~~/etc/ags~~ pkgdatadir/share with meson - - [x] `--init` cli flag - -- Wiki - - [x] update to use `bind` `hook` `on` `poll` - - [x] update examples - - [x] Frequent GTK issues page - - [x] Single children issues - - - [x] Move wiki to aylur.github.io/ags - - ~~maybe? rename id from com.github.Aylur.ags to io.Aylur.ags~~ - -- [ ] add JSDoc to most stuff diff --git a/cmd/bundle.go b/cmd/bundle.go new file mode 100644 index 00000000..b34f8d63 --- /dev/null +++ b/cmd/bundle.go @@ -0,0 +1,48 @@ +package cmd + +import ( + "ags/lib" + "os" + "path/filepath" + + "github.com/spf13/cobra" +) + +var ( + tsconfig string + workingDir string +) + +var bundleCommand = &cobra.Command{ + Use: "bundle [entryfile] [outfile]", + Short: "Bundle an app", + Args: cobra.ExactArgs(2), + Run: func(cmd *cobra.Command, args []string) { + path, err := filepath.Abs(args[0]) + if err != nil { + lib.Err(err) + } + + outfile, err := filepath.Abs(args[1]) + if err != nil { + lib.Err(err) + } + + info, err := os.Stat(path) + if err != nil { + lib.Err(err) + } + + if info.IsDir() { + lib.Bundle(getAppEntry(path), outfile, tsconfig, workingDir) + } else { + lib.Bundle(path, outfile, tsconfig, workingDir) + } + }, +} + +func init() { + f := bundleCommand.Flags() + f.StringVar(&tsconfig, "tsconfig", "", "path to tsconfig.json") + f.StringVar(&workingDir, "src", "", "source directory of the bundle") +} diff --git a/cmd/init.go b/cmd/init.go new file mode 100644 index 00000000..d4e7a4b1 --- /dev/null +++ b/cmd/init.go @@ -0,0 +1,78 @@ +package cmd + +import ( + "ags/lib" + "fmt" + "os" + "path/filepath" + "strings" + + "github.com/spf13/cobra" +) + +var ( + force bool + gtk int + initDirectory string +) + +var initCommand = &cobra.Command{ + Use: "init", + Short: "Initialize a project directory", + Long: `Initialize a project directory by setting up files needed by TypeScript, +generating types and setting up a basic bar example`, + Args: cobra.MaximumNArgs(1), + Run: initConfig, +} + +func init() { + f := initCommand.Flags() + + f.IntVarP(>k, "gtk", "g", 3, "gtk version to use") + f.BoolVarP(&force, "force", "f", false, "override existing files") + f.StringVarP(&initDirectory, "directory", "d", defaultConfigDir(), "target directory") +} + +func getDataFile(name string) string { + content, err := data.ReadFile("data/" + name) + if err != nil { + lib.Err(err) + } + return string(content) +} + +func initConfig(cmd *cobra.Command, args []string) { + // TODO: gtk4 template + if gtk != 3 { + lib.Err("currently only gtk3 is supported") + } + + var configDir string + if len(args) > 0 { + var err error + configDir, err = filepath.Abs(args[0]) + if err != nil { + lib.Err(err) + } + } else { + configDir = defaultConfigDir() + } + + if info, err := os.Stat(configDir); err == nil && info.IsDir() && !force { + lib.Err("could not initialize: " + lib.Cyan(configDir) + " already exists") + } + + tsconf := strings.ReplaceAll(getDataFile("tsconfig.json"), "@ASTAL_GJS@", astalGjs) + tsconf = strings.ReplaceAll(tsconf, "@GTK_VERSION@", "gtk3") + lib.Mkdir(configDir + "/widget") + + lib.WriteFile(configDir+"/.gitignore", "@girs/\nnode_modules/") + lib.WriteFile(configDir+"/tsconfig.json", tsconf) + lib.WriteFile(configDir+"/env.d.ts", getDataFile("env.d.ts")) + lib.WriteFile(configDir+"/style.scss", getDataFile("style.scss")) + lib.WriteFile(configDir+"/widget/Bar.tsx", getDataFile("gtk3/Bar.tsx")) + lib.WriteFile(configDir+"/app.ts", getDataFile("gtk3/app.ts")) + + genTypes(configDir, "*") + fmt.Println(lib.Green("project ready") + " at " + lib.Cyan(configDir)) +} diff --git a/cmd/inspect.go b/cmd/inspect.go new file mode 100644 index 00000000..bbe7a453 --- /dev/null +++ b/cmd/inspect.go @@ -0,0 +1,21 @@ +package cmd + +import ( + "ags/lib" + + "github.com/spf13/cobra" +) + +var inspectCommand = &cobra.Command{ + Use: "inspect", + Short: "Open up Gtk debug tool", + Args: cobra.NoArgs, + Run: func(cmd *cobra.Command, args []string) { + instance, _ := cmd.Flags().GetString("instance") + lib.Astal("--instance", instance, "--inspector") + }, +} + +func init() { + lib.AddInstanceFlag(inspectCommand) +} diff --git a/cmd/list.go b/cmd/list.go new file mode 100644 index 00000000..6c215522 --- /dev/null +++ b/cmd/list.go @@ -0,0 +1,16 @@ +package cmd + +import ( + "ags/lib" + + "github.com/spf13/cobra" +) + +var listCommand = &cobra.Command{ + Use: "list", + Short: "List running instances", + Args: cobra.NoArgs, + Run: func(cmd *cobra.Command, args []string) { + lib.Astal("--list") + }, +} diff --git a/cmd/quit.go b/cmd/quit.go new file mode 100644 index 00000000..00bc218b --- /dev/null +++ b/cmd/quit.go @@ -0,0 +1,21 @@ +package cmd + +import ( + "ags/lib" + + "github.com/spf13/cobra" +) + +var quitCommand = &cobra.Command{ + Use: "quit", + Short: "Quit an instances", + Args: cobra.NoArgs, + Run: func(cmd *cobra.Command, args []string) { + instance, _ := cmd.Flags().GetString("instance") + lib.Astal("--quit", "--instance", instance) + }, +} + +func init() { + lib.AddInstanceFlag(quitCommand) +} diff --git a/cmd/request.go b/cmd/request.go new file mode 100644 index 00000000..6f77f5ff --- /dev/null +++ b/cmd/request.go @@ -0,0 +1,21 @@ +package cmd + +import ( + "ags/lib" + + "github.com/spf13/cobra" +) + +var reqCommand = &cobra.Command{ + Use: "request [message]", + Short: "Send a request to an instance", + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + instance, _ := cmd.Flags().GetString("instance") + lib.Astal("--instance", instance, args[0]) + }, +} + +func init() { + lib.AddInstanceFlag(reqCommand) +} diff --git a/cmd/root.go b/cmd/root.go new file mode 100644 index 00000000..0ca0721d --- /dev/null +++ b/cmd/root.go @@ -0,0 +1,75 @@ +package cmd + +import ( + "embed" + "os" + "path/filepath" + + "github.com/spf13/cobra" +) + +var ( + version string + data embed.FS + gtk4LayerShell string + astalGjs string + tsForGir string +) + +var rootCmd = &cobra.Command{ + Use: "ags", + Short: "Scaffolding CLI tool for Astal+TypeScript projects.", + Args: cobra.NoArgs, +} + +type Variables struct { + Version string + Data embed.FS + Gtk4LayerShell string + AstalGjs string + TsForGir string +} + +func Initialize(vars Variables) { + version = vars.Version + data = vars.Data + gtk4LayerShell = vars.Gtk4LayerShell + astalGjs = vars.AstalGjs + tsForGir = vars.TsForGir + + rootCmd.Version = version +} + +func init() { + rootCmd.CompletionOptions.HiddenDefaultCmd = true + cobra.EnableCommandSorting = false + + rootCmd.AddCommand(runCommand) + rootCmd.AddCommand(reqCommand) + rootCmd.AddCommand(listCommand) + rootCmd.AddCommand(inspectCommand) + rootCmd.AddCommand(toggleCommand) + rootCmd.AddCommand(quitCommand) + rootCmd.AddCommand(typesCommand) + rootCmd.AddCommand(bundleCommand) + rootCmd.AddCommand(initCommand) +} + +func Execute() { + err := rootCmd.Execute() + if err != nil { + os.Exit(1) + } + + help, _ := rootCmd.Flags().GetBool("help") + ver, _ := rootCmd.Flags().GetBool("version") + + if help || ver { + os.Exit(0) + } +} + +func defaultConfigDir() string { + dotconf, _ := os.UserConfigDir() + return filepath.Join(dotconf, "ags") +} diff --git a/cmd/run.go b/cmd/run.go new file mode 100644 index 00000000..3bbdc17e --- /dev/null +++ b/cmd/run.go @@ -0,0 +1,110 @@ +package cmd + +import ( + "ags/lib" + "fmt" + "os" + "path/filepath" + + "github.com/spf13/cobra" +) + +var ( + gtk4 bool + targetDir string + args []string +) + +var runCommand = &cobra.Command{ + Use: "run [file]", + Short: "Run an app", + Args: cobra.ArbitraryArgs, + Run: func(cmd *cobra.Command, args []string) { + if len(args) > 0 { + path, err := filepath.Abs(args[0]) + if err != nil { + lib.Err(err) + } + + info, err := os.Stat(path) + if err != nil { + lib.Err(err) + } + + if info.IsDir() { + run(getAppEntry(path)) + } else { + run(path) + } + + } else { + run(getAppEntry(targetDir)) + } + }, +} + +func init() { + f := runCommand.Flags() + f.BoolVar(>k4, "gtk4", false, "preload Gtk4LayerShell") + f.StringVarP(&targetDir, "directory", "d", defaultConfigDir(), + `directory to search for an "app" entry file +when no positional argument is given +`+"\b") + + f.StringArrayVarP(&args, "arg", "a", []string{}, "cli args to pass to gjs") + f.StringVar(&tsconfig, "tsconfig", "", "path to tsconfig.json") + f.MarkHidden("tsconfig") +} + +func getOutfile() string { + rundir, found := os.LookupEnv("XDG_RUNTIME_DIR") + + if !found { + rundir = "/tmp" + } + + return filepath.Join(rundir, "ags.js") +} + +func getAppEntry(dir string) string { + infile := filepath.Join(dir, "app") + valid := []string{"js", "ts", "jsx", "tsx"} + + app := lib.Some(valid, func(ext string) bool { + _, err := os.Stat(infile + "." + ext) + return !os.IsNotExist(err) + }) + + if !app { + msg := "no such file or directory: " + + fmt.Sprintf("\"%s\"\n", lib.Cyan(dir+"/app")) + + lib.Magenta("tip: ") + "valid names are: " + for _, v := range valid { + msg = msg + fmt.Sprintf(` "%s"`, lib.Cyan("app."+v)) + } + lib.Err(msg) + } + + return infile +} + +func run(infile string) { + outfile := getOutfile() + lib.Bundle(infile, outfile, tsconfig, "") + + if gtk4 { + os.Setenv("LD_PRELOAD", gtk4LayerShell) + } + + args = append([]string{"-m", outfile}, args...) + gjs := lib.Exec("gjs", args...) + gjs.Stdout = os.Stdout + gjs.Stderr = os.Stderr + gjs.Stdin = os.Stdin + gjs.Dir = filepath.Dir(infile) + + // TODO: watch and restart + if err := gjs.Run(); err != nil { + lib.Err(err) + } +} diff --git a/cmd/toggle.go b/cmd/toggle.go new file mode 100644 index 00000000..76cb069d --- /dev/null +++ b/cmd/toggle.go @@ -0,0 +1,21 @@ +package cmd + +import ( + "ags/lib" + + "github.com/spf13/cobra" +) + +var toggleCommand = &cobra.Command{ + Use: "toggle [name]", + Short: "Toggle visibility of a Window", + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + instance, _ := cmd.Flags().GetString("instance") + lib.Astal("--instance", instance, "--toggle-window", args[0]) + }, +} + +func init() { + lib.AddInstanceFlag(toggleCommand) +} diff --git a/cmd/types.go b/cmd/types.go new file mode 100644 index 00000000..1ce63c9c --- /dev/null +++ b/cmd/types.go @@ -0,0 +1,122 @@ +package cmd + +import ( + "ags/lib" + "fmt" + "os" + "os/exec" + "path/filepath" + "strings" + "time" + + "github.com/spf13/cobra" +) + +var ( + ignoreModules []string + updateTsconfig bool +) + +var typesCommand = &cobra.Command{ + Use: "types [pattern]", + Short: "Generate TypeScript types", + Args: cobra.MaximumNArgs(1), + Example: ` ags types Astal* --ignore Gtk3 --ignore Astal3`, + Run: func(cmd *cobra.Command, args []string) { + if updateTsconfig { + lib.WriteFile(targetDir+"/tsconfig.json", lib.GetTsconfig(targetDir)) + + envdts := targetDir + "/env.d.ts" + if !lib.FileExists(envdts) { + lib.WriteFile(envdts, getDataFile("env.d.ts")) + } + } + + if len(args) > 0 { + genTypes(targetDir, args[0]) + } else { + genTypes(targetDir, "*") + } + }, +} + +func init() { + f := typesCommand.Flags() + + f.BoolVar(&updateTsconfig, "tsconfig", false, "update tsconfig.json") + f.StringVarP(&targetDir, "directory", "d", defaultConfigDir(), "target directory") + f.StringArrayVarP(&ignoreModules, "ignore", "i", []string{}, "modules that should be ignored") +} + +func girDirectories() []string { + dataDirs := append([]string{ + "/usr/local/share", + "/usr/share", + "/usr/share/*", + }, strings.Split(os.Getenv("NIX_GI_DIRS"), ":")...) + + return lib.Map(dataDirs, func(dir string) string { + return filepath.Join(dir, "gir-1.0") + }) +} + +func spinner(stopChan chan bool) { + chars := []string{"⣾", "⣽", "⣻", "⢿", "⡿", "⣟", "⣯", "⣷"} + + for { + select { + case <-stopChan: + fmt.Print("\r\033[K") + return + default: + for _, c := range chars { + fmt.Printf("\r%s Generating types, this might take a while...", lib.Cyan(c)) + time.Sleep(time.Second / 10) + } + } + } +} + +func hideCursor() { + fmt.Print("\033[?25l") +} + +func showCursor() { + fmt.Print("\033[?25h") +} + +func genTypes(configDir, pattern string) { + lib.Mkdir(configDir) + + npx, err := exec.LookPath("npx") + if err != nil { + lib.Err(err) + } + + flags := []string{ + "-y", tsForGir, "generate", pattern, + "--ignoreVersionConflicts", + "--outdir", filepath.Join(configDir, "@girs"), + } + + for _, path := range girDirectories() { + flags = append(flags, "-g", path) + } + + hideCursor() + + cmd := exec.Command(npx, flags...) + + stopChan := make(chan bool) + go spinner(stopChan) + + err = cmd.Run() + stopChan <- true + + showCursor() + + if err != nil { + lib.Err("type generation failed, try running\n" + + lib.Yellow(npx+" "+strings.Join(flags, " "))) + } +} diff --git a/data/env.d.ts b/data/env.d.ts new file mode 100644 index 00000000..4e7e508b --- /dev/null +++ b/data/env.d.ts @@ -0,0 +1,21 @@ +const SRC: string + +declare module "inline:*" { + const content: string + export default content +} + +declare module "*.scss" { + const content: string + export default content +} + +declare module "*.blp" { + const content: string + export default content +} + +declare module "*.css" { + const content: string + export default content +} diff --git a/data/gtk3/Bar.tsx b/data/gtk3/Bar.tsx new file mode 100644 index 00000000..ed1d845d --- /dev/null +++ b/data/gtk3/Bar.tsx @@ -0,0 +1,29 @@ +import { App, Astal, Gtk, Gdk } from "astal/gtk3" +import { Variable } from "astal" + +const time = Variable("").poll(1000, "date") + +export default function Bar(gdkmonitor: Gdk.Monitor) { + return + + + + + + +} diff --git a/data/gtk3/app.ts b/data/gtk3/app.ts new file mode 100644 index 00000000..83217ef7 --- /dev/null +++ b/data/gtk3/app.ts @@ -0,0 +1,10 @@ +import { App } from "astal/gtk3" +import style from "./style.scss" +import Bar from "./widget/Bar" + +App.start({ + css: style, + main() { + App.get_monitors().map(Bar) + }, +}) diff --git a/data/style.scss b/data/style.scss new file mode 100644 index 00000000..9ae9604d --- /dev/null +++ b/data/style.scss @@ -0,0 +1,20 @@ +// https://gitlab.gnome.org/GNOME/gtk/-/blob/gtk-3-24/gtk/theme/Adwaita/_colors-public.scss +$theme_fg_color: "@theme_fg_color"; +$theme_bg_color: "@theme_bg_color"; + +window.Bar { + background: transparent; + color: #{$theme_bg_color}; + font-weight: bold; + + >centerbox { + background: #{$theme_bg_color}; + border-radius: 10px; + margin: 8px; + } + + button { + border-radius: 8px; + margin: 2px; + } +} diff --git a/data/tsconfig.json b/data/tsconfig.json new file mode 100644 index 00000000..c1e72444 --- /dev/null +++ b/data/tsconfig.json @@ -0,0 +1,22 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "compilerOptions": { + "experimentalDecorators": true, + "strict": true, + "target": "ES2022", + "module": "ES2022", + "moduleResolution": "Bundler", + // "checkJs": true, + // "allowJs": true, + "jsx": "react-jsx", + "jsxImportSource": "@ASTAL_GJS@/@GTK_VERSION@", + "paths": { + "astal": [ + "@ASTAL_GJS@" + ], + "astal/*": [ + "@ASTAL_GJS@/*" + ] + }, + } +} diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 00000000..8901a5da --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1,5 @@ +dist/ +result/ +.vitepress/cache/ +node_modules/ +npm-debug.log* diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts new file mode 100644 index 00000000..3bfd4506 --- /dev/null +++ b/docs/.vitepress/config.ts @@ -0,0 +1 @@ +export { default } from "../vitepress.config" diff --git a/docs/.vitepress/theme/index.ts b/docs/.vitepress/theme/index.ts new file mode 100644 index 00000000..a2ca770f --- /dev/null +++ b/docs/.vitepress/theme/index.ts @@ -0,0 +1,5 @@ +import DefaultTheme from "vitepress/theme" +import "../../vitepress.theme.css" +import "devicon/devicon.min.css" + +export default DefaultTheme diff --git a/docs/guide/astal-cli.md b/docs/guide/astal-cli.md new file mode 100644 index 00000000..4a3def13 --- /dev/null +++ b/docs/guide/astal-cli.md @@ -0,0 +1,86 @@ +# Astal CLI + +AGS provides aliases for the [Astal CLI](https://aylur.github.io/astal/guide/typescript/cli-app). + +## [Request](https://aylur.github.io/astal/guide/typescript/cli-app#messaging-from-cli) + +```ts +App.start({ + requestHandler(request: string, res: (response: any) => void) { + if (request == "say hi") { + res("hi cli") + } + res("unknown command") + }, +}) +``` + +:::code-group + +```sh [astal] +astal say hi --instance astal +# hi cli +``` + +```sh [ags] +ags request "say hi" --instance astal +# hi cli +``` + +::: + +## List + +:::code-group + +```sh [astal] +astal --list +``` + +```sh [ags] +ags list +``` + +::: + +## [Opening the inspector](https://aylur.github.io/astal/guide/typescript/theming#inspector) + +:::code-group + +```sh [astal] +astal --inspector --instance astal +``` + +```sh [ags] +ags inspect --instance astal +``` + +::: + +## [Window toggling](https://aylur.github.io/astal/guide/typescript/cli-app#toggling-windows-by-their-name) + +:::code-group + +```sh [astal] +astal --toggle-window window-name --instance astal +``` + +```sh [ags] +ags toggle window-name --instance astal +``` + +::: + +## Quitting App + +:::code-group + +```sh [astal] +astal --quit --instance astal +``` + +```sh [ags] +ags quit --instance astal +``` + +::: diff --git a/docs/guide/bundling.md b/docs/guide/bundling.md new file mode 100644 index 00000000..28640639 --- /dev/null +++ b/docs/guide/bundling.md @@ -0,0 +1,108 @@ +# Bundling projects + +Bundling can be done with the `ags bundle` command. + +:::details +Uses [esbuild](https://esbuild.github.io/) under the hood. +::: + +```sh +$ ags bundle --help + +Bundle an app + +Usage: + ags bundle [entryfile] [outfile] [flags] + +Flags: + -h, --help help for bundle + --src string source directory of the bundle + --tsconfig string path to tsconfig.json +``` + +Currently there are 3 builtin plugins. + +- css: import `.css` will be inlined as a string +- sass: importing `.scss` files will go through the sass transpiler and be inlined as a string that contains valid css + - uses the `sass` executable found on $PATH +- blp: importing `.blp` files will go through [blueprint](https://jwestman.pages.gitlab.gnome.org/blueprint-compiler/) and be inlined as a string that contains valid xml template definitions + +AGS also defines a global `SRC` variable which will be replaced by the value of the `--src` flag. +By default it will point to the directory of `entryfile`. + +```js +#!/usr/bin/ags run +App.start({ + main() { + print(`source dir is ${SRC}`) + } +}) +``` + +By default `ags bundle` will look for a `tsconfig.json` in the root directory, +read it and update it internally so that `paths` and `jsxImportSource` points to +the correct location of the Astal js package. +This behavior can be altered by using the `--tsconfig` flag which won't be +updated internally and will use the paths defined in it. + +## Example + +:::code-group + +```blp [Bar.blp] +using Gtk 4.0; +using Astal 4.0; + +template $Bar: Astal.Window { + // bitfields currently don't work in blueprint + // anchor: top | left | right; + exclusivity: exclusive; + + CenterBox { + center-widget: Label { + label: "hello"; + }; + } +} +``` + +::: + +:::code-group + +```ts [app.ts] +#!/usr/bin/gjs -m +import { register } from "astal/gobject" +import { App, Astal } from "astal/gtk4" +import Template from "./Bar.blp" + +const { TOP, LEFT, RIGHT } = Astal.WindowAnchor + +@register({ GTypeName: "Bar", Template }) +class Bar extends Astal.Window { +} + +App.start({ + instanceName: "bar", + main() { + new Bar({ + application: App, + anchor: TOP | LEFT | RIGHT, + visible: true, + }) + } +}) +``` + +::: + +```sh +ags bundle ./app.ts bar +chmod +x ./bar + +./bar +``` + +> [!NOTE] +> On Nix this still has to be done in a derivation +> as the bundled script is not wrapped. diff --git a/docs/guide/example.md b/docs/guide/example.md new file mode 100644 index 00000000..de9d2070 --- /dev/null +++ b/docs/guide/example.md @@ -0,0 +1,101 @@ +# Dialog Example + +Simple dialog example to get a `no`/`yes` answer. + +![2024-11-13_00-45-58](https://github.com/user-attachments/assets/73a20155-fa0e-4156-aff8-3a0d055abb9b) + +```tsx +#!/usr/bin/ags run +import { App, Astal, Gtk, Gdk } from "astal/gtk3" + +const { TOP, BOTTOM, LEFT, RIGHT } = Astal.WindowAnchor +const { IGNORE } = Astal.Exclusivity +const { EXCLUSIVE } = Astal.Keymode +const { CENTER } = Gtk.Align + +App.start({ + instanceName: "tmp" + Date.now(), + gtkTheme: "adw-gtk3-dark", + css: /* css */` + window { + all: unset; + background-color: alpha(black, 0.3); + } + + window > box { + margin: 10px; + padding: 6px; + box-shadow: 2px 3px 5px 0 alpha(black, 0.6); + border-radius: 11px; + background-color: #181818; + color: white; + min-width: 200px; + } + + box > label { + font-size: large; + margin: 6px; + } + + label.title { + font-size: 1.4em; + } + + .action { + color: alpha(white, 0.8); + } + + button { + margin: 6px; + } + `, + main: (action = "XYZ") => { + function yes() { + print("yes") + App.quit() + } + + function no() { + print("no") + App.quit() + } + + function onKeyPress(_: Astal.Window, event: Gdk.Event) { + if (event.get_keyval()[1] === Gdk.KEY_Escape) { + no() + } + } + + + + + + } +}) +``` + +Then it can be used in any script. + +```sh +if [[ "$(script -a Shutdown)" == "yes" ]]; then + shutdown now +fi +``` + +> [!TIP] +> If you are happy with the script and don't plan to change it anymore [bundle](./bundling.md) it, +> which will remove the dependency on AGS. diff --git a/docs/guide/init.md b/docs/guide/init.md new file mode 100644 index 00000000..e96a7d7b --- /dev/null +++ b/docs/guide/init.md @@ -0,0 +1,84 @@ +# Setting up a project + +You can initalize a project with the `init` command. + +```sh +$ ags init --help + +Initialize a project directory by setting up files needed by TypeScript, +generating types and setting up a basic bar example + +Usage: + ags init [flags] + +Flags: + -d, --directory string target directory (default "~/.config/ags") + -f, --force override existing files + -g, --gtk int gtk version to use (default 3) + -h, --help help for init + +``` + +By default it will set it up at `$HOME/.config/ags`, +but you can specify the target directory with the `-d` flag. + +It will generate the following files: + +```txt +. +├── .gitignore +├── @girs/ # generated types +├── widget/ +│ └── Bar.tsx +├── app.ts # entry proint +├── env.d.ts # additional types +├── style.scss +└── tsconfig.json # needed by LSPs +``` + +The `@girs` directory contains the generated types, which are +created when running the `init` command or the `types` command. + +Assuming this directory will be tracked with git, +it generates a `.gitignore` file which is set to ignore `@girs` and `node_modules`. +Initially `node_modules` doesn't exist, but if you decide to install any `npm` +package it is not needed to track them with git. You can also add `tsconfig.json` +and `env.d.ts` to this list, as they are only used for developing and can be +regenerated with the `types` command. Only track `tsconfig.json` if you add +anything additional to `compilerOptions.paths`. + +> [!NOTE] +> Since the runtime is `gjs`, very few packages will run from `npm`. + +The `env.d.ts` will tell the LSP that `.css`, `.scss` and `.blp` files can be +imported and will be inlined as a string. It also tells it that imports +prefixed with `inline:` will be inlined as well as that a global `SRC` variable +is available. + +The `tsconfig.json` file tells information to the LSP so that +intellisense can do its thing and provide great DX. + +`app.ts` is the entry point of the project which usually contains only +an `App.start` call where you define [main](https://aylur.github.io/astal/guide/typescript/cli-app#entry-point) and [requestHandler](https://aylur.github.io/astal/guide/typescript/cli-app#messaging-from-cli), +but can contain any other code. + +> [!NOTE] +> You could also name the entry file `app.tsx` and write any JSX there. + +> [!TIP] +> You are not forced to use TypeScript. Adding `"allowJs": true` +> in `tsconfig.json` and optionally `"checkJs": true` will allow +> JavaScript, although it is very much recommended to TypeScript. + +You are not forced into a project structure. You can put +`style.scss` and `widget/Bar.ts` anywhere you like, only the entry file matters. + +## Running projects + +`tsconfig.json`, `env.d.ts` and `@girs` are only significant for the LSP, +they are not needed to run the project. + +:::tip +You can also use `ags run` as a shebang line for simple scripts. +See an [simple dialog example](./example.md) +::: diff --git a/docs/guide/install.md b/docs/guide/install.md new file mode 100644 index 00000000..6b128c92 --- /dev/null +++ b/docs/guide/install.md @@ -0,0 +1,50 @@ +# Installation + +## Arch + +maintainer: [@kotontrion](https://github.com/kotontrion) + +```sh +yay -S aylurs-gtk-shell-git +``` + +## Nix + +maintainer: [@Aylur](https://github.com/Aylur) + +Read more about it on the [nix page](./nix) + +## From Source + +1. Install [Astal](https://aylur.github.io/astal/guide/getting-started/installation) + +2. Install the Astal js package + +```sh +git clone https://github.com/aylur/astal +cd astal/lang/gjs +meson setup --prefix /usr build +meson install -C build +``` + +2. Install AGS + +```sh +git clone https://github.com/aylur/ags.git +cd ags + +go install -ldflags "\ + -X 'main.gtk4LayerShell=$(pkg-config --variable=libdir gtk4-layer-shell-0)/libgtk4-layer-shell.so' \ + -X 'main.astalGjs=$(pkg-config --variable=srcdir astal-gjs)'" +``` + +:::tip +`go install` will install to `$GOPATH/bin/ags` or `$HOME/go/bin/ags`. +You might wish to move the binary to more traditional linux directories. + +```sh +sudo mv $GOPATH/bin/ags /usr/bin/ags +mv $GOPATH/bin/ags ~/.local/bin/ags +``` + +::: diff --git a/docs/guide/nix.md b/docs/guide/nix.md new file mode 100644 index 00000000..9544abc0 --- /dev/null +++ b/docs/guide/nix.md @@ -0,0 +1,191 @@ +# Usage on NixOS + +Initialize a directory using the template. + +```sh +nix flake init --template github:aylur/ags +``` + +## Bundle and DevShell + +The flake exposes a `lib.bundle` function which can bundle your projects. +Using nix, you'll technically never have to use the `ags` cli. + +:::code-group + +```nix [ flake.nix] +{ + inputs = { + nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable"; + ags.url = "github:aylur/ags"; + }; + + outputs = { self, nixpkgs, ags }: let + system = "x86_64-linux"; + pkgs = nixpkgs.legacyPackages.${system}; + in { + packages.${system}.default = ags.lib.bundle { # [!code focus:12] + inherit pkgs; + src = ./.; + name = "my-shell"; # name of executable + entry = "app.ts"; + + # additional libraries and executables to add to gjs' runtime + extraPackages = [ + # ags.packages.${system}.battery + # pkgs.fzf + ]; + }; + }; +} +``` + +::: + +While working on the project, it would make sense to use the `ags` cli +instead of building it everytime with `nix`. + +You could enter a shell with `agsFull` package which +exposes AGS + every [Astal library](https://aylur.github.io/astal/guide/libraries/references#astal-libraries). + +```sh +nix shell github:aylur/ags#agsFull +``` + +Or define a `devShell` and cherry pick packages. + +:::code-group + +```nix [ flake.nix] +{ + inputs = { + nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable"; + ags.url = "github:aylur/ags"; + }; + + outputs = { self, nixpkgs, ags }: let + system = "x86_64-linux"; + pkgs = nixpkgs.legacyPackages.${system}; + in { + devShells.${system}.default = pkgs.mkShell { + buildInputs = [ + # includes astal3 astal4 astal-io by default + (ags.packages.${system}.default.overrideAttrs { # [!code focus:5] + extraPackages = [ + # cherry pick packages + ]; + }) + ]; + }; + }; +} +``` + +## Using home-manager + +If you prefer the workflow of AGS v1, you can use the home-manager module. + +::: + +Example content of `flake.nix` + +:::code-group + +```nix [ flake.nix] +{ + inputs = { + nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; + home-manager = { + url = "github:nix-community/home-manager"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + + ags.url = "github:aylur/ags"; # [!code focus] + }; + + outputs = { home-manager, nixpkgs, ... }@inputs: + let + system = "x86_64-linux"; + in + { + homeConfigurations."${username}" = home-manager.lib.homeManagerConfiguration { + pkgs = import nixpkgs { inherit system; }; + + # pass inputs as specialArgs # [!code focus:2] + extraSpecialArgs = { inherit inputs; }; + + # import your home.nix # [!code focus:2] + modules = [ ./home-manager/home.nix ]; + }; + }; +} +``` + +::: +Example content of `home.nix` file + +:::code-group + +```nix [ home.nix] +{ inputs, pkgs, ... }: +{ + # add the home manager module + imports = [ inputs.ags.homeManagerModules.default ]; + + programs.ags = { + enable = true; + + # symlink to ~/.config/ags + configDir = ../ags; + + # additional packages to add to gjs's runtime + extraPackages = with pkgs; [ + inputs.ags.packages.${pkgs.system}.battery + fzf + ]; + }; +} +``` + +::: + +The module only includes the core `astal3`, `astal4` and `astal-io` libraries. +If you want to include any other [library](https://aylur.github.io/astal/guide/libraries/references#astal-libraries) you have to add them to `extraPackages`. +You can also add binaries which will be added to the gjs runtime. + +:::warning +The `configDir` option symlinks the given path to `~/.config/ags`. +If you already have your source code there leave it as `null`. +::: + +## Using Astal CLI tools + +The home-manager module does not expose the `astal` cli to the home environment, +you have to do that yourself if you want: + +:::code-group + +```nix [ home.nix] +home.packages = [ inputs.ags.packages.${pkgs.system}.io ]; +``` + +```sh [ sh] +astal --help +``` + +::: + +Same applies to the `extraPackages` option, it does not expose the passed packages to the home environment. +To make astal cli tools available to home environment, you have to add them yourself: + +:::code-group + +```nix [ home.nix] +home.packages = [ inputs.ags.packages.${pkgs.system}.notifd ]; +``` + +```sh [ sh] +astal-notifd --help +``` + +::: diff --git a/docs/guide/quick-start.md b/docs/guide/quick-start.md new file mode 100644 index 00000000..902ce779 --- /dev/null +++ b/docs/guide/quick-start.md @@ -0,0 +1,31 @@ +# Quick Start + +1. Install + +:::code-group + +```sh [ Arch] +yay -S aylurs-gtk-shell-git +``` + +```sh [ NixOS] +nix shell github:aylur/ags # ags in a temporary shell +``` + +::: + +2. Initialize a project + +```sh +ags init +``` + +3. Run the project + +```sh +ags run +``` + +4. Learn [TypeScript in Y minutes](https://learnxinyminutes.com/docs/typescript/) + +5. Read the [Astal Documentation](https://aylur.github.io/astal/guide/typescript/first-widgets) to start developing diff --git a/docs/guide/types.md b/docs/guide/types.md new file mode 100644 index 00000000..ff3efa27 --- /dev/null +++ b/docs/guide/types.md @@ -0,0 +1,32 @@ +# Generating TypeScript types + +Generating types and a tsconfig.json can be done with the `ags types` command. + +:::details +Uses [gjsify/ts-for-gir](https://github.com/gjsify/ts-for-gir) under the hood. +::: + +```sh +ags types --help + +Generate TypeScript types + +Usage: + ags types [pattern] [flags] + +Examples: + ags types Astal* --ignore Gtk3 --ignore Astal3 + +Flags: + -d, --directory string target directory (default "~/.config/ags") + -h, --help help for types + -i, --ignore stringArray modules that should be ignored + --tsconfig update tsconfig.json +``` + +You will be using this command when adding libraries already into development +or when cloning existing projects like [astal/examples/js](https://github.com/Aylur/astal/tree/main/examples/js) +in which case you will be using the `--tsconfig` flag. + +> [!NOTE] +> `ags init` will invoke this command with the default `*` pattern diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 00000000..17f185c4 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,87 @@ +--- +layout: home +pageClass: home-page + +hero: + name: "AGS" + text: "Scaffolding CLI for Astal+TypeScript" + tagline: Initialize, bundle, or run Astal projects written in TypeScript/JavaScript with a single command + image: https://aylur.github.io/astal/icon.svg + actions: + - theme: brand + text: Quick Start + link: /guide/quick-start + - theme: alt + text: Astal Documentation + link: https://aylur.github.io/astal/ + +features: + - title: Initialize projects + details: With ags init you can initialize a project, which generates a basic template. + - title: Generate TypeScript types. + details: With ags types you can generate types from GObject based libraries. + - title: Bundle projects + details: With ags bundle you can bundle your project into a single executable script. + - title: Run projects + details: With ags run you can run a projects without bundling them first. +--- + + diff --git a/docs/package-lock.json b/docs/package-lock.json new file mode 100644 index 00000000..18423360 --- /dev/null +++ b/docs/package-lock.json @@ -0,0 +1,2430 @@ +{ + "name": "ags-docs", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "ags-docs", + "dependencies": { + "devicon": "^2.16.0" + }, + "devDependencies": { + "vitepress": "^1.5.0" + } + }, + "node_modules/@algolia/autocomplete-core": { + "version": "1.17.6", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-core/-/autocomplete-core-1.17.6.tgz", + "integrity": "sha512-lkDoW4I7h2kKlIgf3pUt1LqvxyYKkVyiypoGLlUnhPSnCpmeOwudM6rNq6YYsCmdQtnDQoW5lUNNuj6ASg3qeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/autocomplete-plugin-algolia-insights": "1.17.6", + "@algolia/autocomplete-shared": "1.17.6" + } + }, + "node_modules/@algolia/autocomplete-plugin-algolia-insights": { + "version": "1.17.6", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-plugin-algolia-insights/-/autocomplete-plugin-algolia-insights-1.17.6.tgz", + "integrity": "sha512-17NnaacuFzSWVuZu4NKzVeaFIe9Abpw8w+/gjc7xhZFtqj+GadufzodIdchwiB2eM2cDdiR3icW7gbNTB3K2YA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/autocomplete-shared": "1.17.6" + }, + "peerDependencies": { + "search-insights": ">= 1 < 3" + } + }, + "node_modules/@algolia/autocomplete-preset-algolia": { + "version": "1.17.6", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.17.6.tgz", + "integrity": "sha512-Cvg5JENdSCMuClwhJ1ON1/jSuojaYMiUW2KePm18IkdCzPJj/NXojaOxw58RFtQFpJgfVW8h2E8mEoDtLlMdeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/autocomplete-shared": "1.17.6" + }, + "peerDependencies": { + "@algolia/client-search": ">= 4.9.1 < 6", + "algoliasearch": ">= 4.9.1 < 6" + } + }, + "node_modules/@algolia/autocomplete-shared": { + "version": "1.17.6", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-shared/-/autocomplete-shared-1.17.6.tgz", + "integrity": "sha512-aq/3V9E00Tw2GC/PqgyPGXtqJUlVc17v4cn1EUhSc+O/4zd04Uwb3UmPm8KDaYQQOrkt1lwvCj2vG2wRE5IKhw==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@algolia/client-search": ">= 4.9.1 < 6", + "algoliasearch": ">= 4.9.1 < 6" + } + }, + "node_modules/@algolia/client-abtesting": { + "version": "5.13.0", + "resolved": "https://registry.npmjs.org/@algolia/client-abtesting/-/client-abtesting-5.13.0.tgz", + "integrity": "sha512-6CoQjlMi1pmQYMQO8tXfuGxSPf6iKX5FP9MuMe6IWmvC81wwTvOehnwchyBl2wuPVhcw2Ar53K53mQ60DAC64g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.13.0", + "@algolia/requester-browser-xhr": "5.13.0", + "@algolia/requester-fetch": "5.13.0", + "@algolia/requester-node-http": "5.13.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-analytics": { + "version": "5.13.0", + "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-5.13.0.tgz", + "integrity": "sha512-pS3qyXiWTwKnrt/jE79fqkNqZp7kjsFNlJDcBGkSWid74DNc6DmArlkvPqyLxnoaYGjUGACT6g56n7E3mVV2TA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.13.0", + "@algolia/requester-browser-xhr": "5.13.0", + "@algolia/requester-fetch": "5.13.0", + "@algolia/requester-node-http": "5.13.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-common": { + "version": "5.13.0", + "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-5.13.0.tgz", + "integrity": "sha512-2SP6bGGWOTN920MLZv8s7yIR3OqY03vEe4U+vb2MGdL8a/8EQznF3L/nTC/rGf/hvEfZlX2tGFxPJaF2waravg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-insights": { + "version": "5.13.0", + "resolved": "https://registry.npmjs.org/@algolia/client-insights/-/client-insights-5.13.0.tgz", + "integrity": "sha512-ldHTe+LVgC6L4Wr6doAQQ7Ku0jAdhaaPg1T+IHzmmiRZb2Uq5OsjW2yC65JifOmzPCiMkIZE2mGRpWgkn5ktlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.13.0", + "@algolia/requester-browser-xhr": "5.13.0", + "@algolia/requester-fetch": "5.13.0", + "@algolia/requester-node-http": "5.13.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-personalization": { + "version": "5.13.0", + "resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-5.13.0.tgz", + "integrity": "sha512-RnCfOSN4OUJDuMNHFca2M8lY64Tmw0kQOZikge4TknTqHmlbKJb8IbJE7Rol79Z80W2Y+B1ydcjV7DPje4GMRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.13.0", + "@algolia/requester-browser-xhr": "5.13.0", + "@algolia/requester-fetch": "5.13.0", + "@algolia/requester-node-http": "5.13.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-query-suggestions": { + "version": "5.13.0", + "resolved": "https://registry.npmjs.org/@algolia/client-query-suggestions/-/client-query-suggestions-5.13.0.tgz", + "integrity": "sha512-pYo0jbLUtPDN1r341UHTaF2fgN5rbaZfDZqjPRKPM+FRlRmxFxqFQm1UUfpkSUWYGn7lECwDpbKYiKUf81MTwA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.13.0", + "@algolia/requester-browser-xhr": "5.13.0", + "@algolia/requester-fetch": "5.13.0", + "@algolia/requester-node-http": "5.13.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-search": { + "version": "5.13.0", + "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-5.13.0.tgz", + "integrity": "sha512-s2ge3uZ6Zg2sPSFibqijgEYsuorxcc8KVHg3I95nOPHvFHdnBtSHymhZvq4sp/fu8ijt/Y8jLwkuqm5myn+2Sg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.13.0", + "@algolia/requester-browser-xhr": "5.13.0", + "@algolia/requester-fetch": "5.13.0", + "@algolia/requester-node-http": "5.13.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/ingestion": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@algolia/ingestion/-/ingestion-1.13.0.tgz", + "integrity": "sha512-fm5LEOe4FPDOc1D+M9stEs8hfcdmbdD+pt9og5shql6ueTZJANDbFoQhDOpiPJizR/ps1GwmjkWfUEywx3sV+Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.13.0", + "@algolia/requester-browser-xhr": "5.13.0", + "@algolia/requester-fetch": "5.13.0", + "@algolia/requester-node-http": "5.13.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/monitoring": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@algolia/monitoring/-/monitoring-1.13.0.tgz", + "integrity": "sha512-e8Hshlnm2G5fapyUgWTBwhJ22yXcnLtPC4LWZKx7KOvv35GcdoHtlUBX94I/sWCJLraUr65JvR8qOo3LXC43dg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.13.0", + "@algolia/requester-browser-xhr": "5.13.0", + "@algolia/requester-fetch": "5.13.0", + "@algolia/requester-node-http": "5.13.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/recommend": { + "version": "5.13.0", + "resolved": "https://registry.npmjs.org/@algolia/recommend/-/recommend-5.13.0.tgz", + "integrity": "sha512-53/wW96oaj1FKMzGdFcZ/epygfTppLDUvgI1thLkd475EtVZCH3ZZVUNCEvf1AtnNyH1RnItkFzX8ayWCpx2PQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.13.0", + "@algolia/requester-browser-xhr": "5.13.0", + "@algolia/requester-fetch": "5.13.0", + "@algolia/requester-node-http": "5.13.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/requester-browser-xhr": { + "version": "5.13.0", + "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-5.13.0.tgz", + "integrity": "sha512-NV6oSCt5lFuzfsVQoSBpewEWf/h4ySr7pv2bfwu9yF/jc/g39pig8+YpuqsxlRWBm/lTGVA2V0Ai9ySwrNumIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.13.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/requester-fetch": { + "version": "5.13.0", + "resolved": "https://registry.npmjs.org/@algolia/requester-fetch/-/requester-fetch-5.13.0.tgz", + "integrity": "sha512-094bK4rumf+rXJazxv3mq6eKRM0ep5AxIo8T0YmOdldswQt79apeufFiPLN19nHEWH22xR2FelimD+T/wRSP+Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.13.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/requester-node-http": { + "version": "5.13.0", + "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-5.13.0.tgz", + "integrity": "sha512-JY5xhEYMgki53Wm+A6R2jUpOUdD0zZnBq+PC5R1TGMNOYL1s6JjDrJeMsvaI2YWxYMUSoCnRoltN/yf9RI8n3A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.13.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", + "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", + "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.2.tgz", + "integrity": "sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.26.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/types": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.0.tgz", + "integrity": "sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@docsearch/css": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/@docsearch/css/-/css-3.7.0.tgz", + "integrity": "sha512-1OorbTwi1eeDmr0v5t+ckSRlt1zM5GHjm92iIl3kUu7im3GHuP+csf6E0WBg8pdXQczTWP9J9+o9n+Vg6DH5cQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@docsearch/js": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/@docsearch/js/-/js-3.7.0.tgz", + "integrity": "sha512-ScfqOIKrSr8SImbpxVaD59xc/bytbL8QEM2GUpe3aICmoICflWp5DyTRzAdFky16HY+yEOAVZXt3COXQ1NOCWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@docsearch/react": "3.7.0", + "preact": "^10.0.0" + } + }, + "node_modules/@docsearch/react": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/@docsearch/react/-/react-3.7.0.tgz", + "integrity": "sha512-8e6tdDfkYoxafEEPuX5eE1h9cTkLvhe4KgoFkO5JCddXSQONnN1FHcDZRI4r8894eMpbYq6rdJF0dVYh8ikwNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/autocomplete-core": "1.17.6", + "@algolia/autocomplete-preset-algolia": "1.17.6", + "@docsearch/css": "3.7.0", + "algoliasearch": "^5.12.0" + }, + "peerDependencies": { + "@types/react": ">= 16.8.0 < 19.0.0", + "react": ">= 16.8.0 < 19.0.0", + "react-dom": ">= 16.8.0 < 19.0.0", + "search-insights": ">= 1 < 3" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + }, + "search-insights": { + "optional": true + } + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@iconify-json/simple-icons": { + "version": "1.2.11", + "resolved": "https://registry.npmjs.org/@iconify-json/simple-icons/-/simple-icons-1.2.11.tgz", + "integrity": "sha512-AHCGDtBRqP+JzAbBzgO8uN/08CXxEmuaC6lQQZ3b5burKhRU12AJnJczwbUw2K5Mb/U85EpSUNhYMG3F28b8NA==", + "dev": true, + "license": "CC0-1.0", + "dependencies": { + "@iconify/types": "*" + } + }, + "node_modules/@iconify/types": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz", + "integrity": "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.25.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.25.0.tgz", + "integrity": "sha512-CC/ZqFZwlAIbU1wUPisHyV/XRc5RydFrNLtgl3dGYskdwPZdt4HERtKm50a/+DtTlKeCq9IXFEWR+P6blwjqBA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.25.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.25.0.tgz", + "integrity": "sha512-/Y76tmLGUJqVBXXCfVS8Q8FJqYGhgH4wl4qTA24E9v/IJM0XvJCGQVSW1QZ4J+VURO9h8YCa28sTFacZXwK7Rg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.25.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.25.0.tgz", + "integrity": "sha512-YVT6L3UrKTlC0FpCZd0MGA7NVdp7YNaEqkENbWQ7AOVOqd/7VzyHpgIpc1mIaxRAo1ZsJRH45fq8j4N63I/vvg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.25.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.25.0.tgz", + "integrity": "sha512-ZRL+gexs3+ZmmWmGKEU43Bdn67kWnMeWXLFhcVv5Un8FQcx38yulHBA7XR2+KQdYIOtD0yZDWBCudmfj6lQJoA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.25.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.25.0.tgz", + "integrity": "sha512-xpEIXhiP27EAylEpreCozozsxWQ2TJbOLSivGfXhU4G1TBVEYtUPi2pOZBnvGXHyOdLAUUhPnJzH3ah5cqF01g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.25.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.25.0.tgz", + "integrity": "sha512-sC5FsmZGlJv5dOcURrsnIK7ngc3Kirnx3as2XU9uER+zjfyqIjdcMVgzy4cOawhsssqzoAX19qmxgJ8a14Qrqw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.25.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.25.0.tgz", + "integrity": "sha512-uD/dbLSs1BEPzg564TpRAQ/YvTnCds2XxyOndAO8nJhaQcqQGFgv/DAVko/ZHap3boCvxnzYMa3mTkV/B/3SWA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.25.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.25.0.tgz", + "integrity": "sha512-ZVt/XkrDlQWegDWrwyC3l0OfAF7yeJUF4fq5RMS07YM72BlSfn2fQQ6lPyBNjt+YbczMguPiJoCfaQC2dnflpQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.25.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.25.0.tgz", + "integrity": "sha512-qboZ+T0gHAW2kkSDPHxu7quaFaaBlynODXpBVnPxUgvWYaE84xgCKAPEYE+fSMd3Zv5PyFZR+L0tCdYCMAtG0A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.25.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.25.0.tgz", + "integrity": "sha512-ndWTSEmAaKr88dBuogGH2NZaxe7u2rDoArsejNslugHZ+r44NfWiwjzizVS1nUOHo+n1Z6qV3X60rqE/HlISgw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.25.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.25.0.tgz", + "integrity": "sha512-BVSQvVa2v5hKwJSy6X7W1fjDex6yZnNKy3Kx1JGimccHft6HV0THTwNtC2zawtNXKUu+S5CjXslilYdKBAadzA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.25.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.25.0.tgz", + "integrity": "sha512-G4hTREQrIdeV0PE2JruzI+vXdRnaK1pg64hemHq2v5fhv8C7WjVaeXc9P5i4Q5UC06d/L+zA0mszYIKl+wY8oA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.25.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.25.0.tgz", + "integrity": "sha512-9T/w0kQ+upxdkFL9zPVB6zy9vWW1deA3g8IauJxojN4bnz5FwSsUAD034KpXIVX5j5p/rn6XqumBMxfRkcHapQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.25.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.25.0.tgz", + "integrity": "sha512-ThcnU0EcMDn+J4B9LD++OgBYxZusuA7iemIIiz5yzEcFg04VZFzdFjuwPdlURmYPZw+fgVrFzj4CA64jSTG4Ig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.25.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.25.0.tgz", + "integrity": "sha512-zx71aY2oQxGxAT1JShfhNG79PnjYhMC6voAjzpu/xmMjDnKNf6Nl/xv7YaB/9SIa9jDYf8RBPWEnjcdlhlv1rQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.25.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.25.0.tgz", + "integrity": "sha512-JT8tcjNocMs4CylWY/CxVLnv8e1lE7ff1fi6kbGocWwxDq9pj30IJ28Peb+Y8yiPNSF28oad42ApJB8oUkwGww==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.25.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.25.0.tgz", + "integrity": "sha512-dRLjLsO3dNOfSN6tjyVlG+Msm4IiZnGkuZ7G5NmpzwF9oOc582FZG05+UdfTbz5Jd4buK/wMb6UeHFhG18+OEg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.25.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.25.0.tgz", + "integrity": "sha512-/RqrIFtLB926frMhZD0a5oDa4eFIbyNEwLLloMTEjmqfwZWXywwVVOVmwTsuyhC9HKkVEZcOOi+KV4U9wmOdlg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@shikijs/core": { + "version": "1.22.2", + "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-1.22.2.tgz", + "integrity": "sha512-bvIQcd8BEeR1yFvOYv6HDiyta2FFVePbzeowf5pPS1avczrPK+cjmaxxh0nx5QzbON7+Sv0sQfQVciO7bN72sg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/engine-javascript": "1.22.2", + "@shikijs/engine-oniguruma": "1.22.2", + "@shikijs/types": "1.22.2", + "@shikijs/vscode-textmate": "^9.3.0", + "@types/hast": "^3.0.4", + "hast-util-to-html": "^9.0.3" + } + }, + "node_modules/@shikijs/engine-javascript": { + "version": "1.22.2", + "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-1.22.2.tgz", + "integrity": "sha512-iOvql09ql6m+3d1vtvP8fLCVCK7BQD1pJFmHIECsujB0V32BJ0Ab6hxk1ewVSMFA58FI0pR2Had9BKZdyQrxTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/types": "1.22.2", + "@shikijs/vscode-textmate": "^9.3.0", + "oniguruma-to-js": "0.4.3" + } + }, + "node_modules/@shikijs/engine-oniguruma": { + "version": "1.22.2", + "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-1.22.2.tgz", + "integrity": "sha512-GIZPAGzQOy56mGvWMoZRPggn0dTlBf1gutV5TdceLCZlFNqWmuc7u+CzD0Gd9vQUTgLbrt0KLzz6FNprqYAxlA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/types": "1.22.2", + "@shikijs/vscode-textmate": "^9.3.0" + } + }, + "node_modules/@shikijs/transformers": { + "version": "1.22.2", + "resolved": "https://registry.npmjs.org/@shikijs/transformers/-/transformers-1.22.2.tgz", + "integrity": "sha512-8f78OiBa6pZDoZ53lYTmuvpFPlWtevn23bzG+azpPVvZg7ITax57o/K3TC91eYL3OMJOO0onPbgnQyZjRos8XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "shiki": "1.22.2" + } + }, + "node_modules/@shikijs/types": { + "version": "1.22.2", + "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-1.22.2.tgz", + "integrity": "sha512-NCWDa6LGZqTuzjsGfXOBWfjS/fDIbDdmVDug+7ykVe1IKT4c1gakrvlfFYp5NhAXH/lyqLM8wsAPo5wNy73Feg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/vscode-textmate": "^9.3.0", + "@types/hast": "^3.0.4" + } + }, + "node_modules/@shikijs/vscode-textmate": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-9.3.0.tgz", + "integrity": "sha512-jn7/7ky30idSkd/O5yDBfAnVt+JJpepofP/POZ1iMOxK59cOfqIgg/Dj0eFsjOTMw+4ycJN0uhZH/Eb0bs/EUA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/markdown-it": { + "version": "14.1.2", + "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.2.tgz", + "integrity": "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/linkify-it": "^5", + "@types/mdurl": "^2" + } + }, + "node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/web-bluetooth": { + "version": "0.0.20", + "resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.20.tgz", + "integrity": "sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow==", + "dev": true, + "license": "MIT" + }, + "node_modules/@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/@vitejs/plugin-vue": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.1.5.tgz", + "integrity": "sha512-dlnib73G05CDBAUR/YpuZcQQ47fpjihnnNouAAqN62z+oqSsWJ+kh52GRzIxpkgFG3q11eXK7Di7RMmoCwISZA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "vite": "^5.0.0", + "vue": "^3.2.25" + } + }, + "node_modules/@vue/compiler-core": { + "version": "3.5.12", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.12.tgz", + "integrity": "sha512-ISyBTRMmMYagUxhcpyEH0hpXRd/KqDU4ymofPgl2XAkY9ZhQ+h0ovEZJIiPop13UmR/54oA2cgMDjgroRelaEw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.25.3", + "@vue/shared": "3.5.12", + "entities": "^4.5.0", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.0" + } + }, + "node_modules/@vue/compiler-dom": { + "version": "3.5.12", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.12.tgz", + "integrity": "sha512-9G6PbJ03uwxLHKQ3P42cMTi85lDRvGLB2rSGOiQqtXELat6uI4n8cNz9yjfVHRPIu+MsK6TE418Giruvgptckg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/compiler-core": "3.5.12", + "@vue/shared": "3.5.12" + } + }, + "node_modules/@vue/compiler-sfc": { + "version": "3.5.12", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.12.tgz", + "integrity": "sha512-2k973OGo2JuAa5+ZlekuQJtitI5CgLMOwgl94BzMCsKZCX/xiqzJYzapl4opFogKHqwJk34vfsaKpfEhd1k5nw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.25.3", + "@vue/compiler-core": "3.5.12", + "@vue/compiler-dom": "3.5.12", + "@vue/compiler-ssr": "3.5.12", + "@vue/shared": "3.5.12", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.11", + "postcss": "^8.4.47", + "source-map-js": "^1.2.0" + } + }, + "node_modules/@vue/compiler-ssr": { + "version": "3.5.12", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.12.tgz", + "integrity": "sha512-eLwc7v6bfGBSM7wZOGPmRavSWzNFF6+PdRhE+VFJhNCgHiF8AM7ccoqcv5kBXA2eWUfigD7byekvf/JsOfKvPA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.12", + "@vue/shared": "3.5.12" + } + }, + "node_modules/@vue/devtools-api": { + "version": "7.6.4", + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-7.6.4.tgz", + "integrity": "sha512-5AaJ5ELBIuevmFMZYYLuOO9HUuY/6OlkOELHE7oeDhy4XD/hSODIzktlsvBOsn+bto3aD0psj36LGzwVu5Ip8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/devtools-kit": "^7.6.4" + } + }, + "node_modules/@vue/devtools-kit": { + "version": "7.6.4", + "resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-7.6.4.tgz", + "integrity": "sha512-Zs86qIXXM9icU0PiGY09PQCle4TI750IPLmAJzW5Kf9n9t5HzSYf6Rz6fyzSwmfMPiR51SUKJh9sXVZu78h2QA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/devtools-shared": "^7.6.4", + "birpc": "^0.2.19", + "hookable": "^5.5.3", + "mitt": "^3.0.1", + "perfect-debounce": "^1.0.0", + "speakingurl": "^14.0.1", + "superjson": "^2.2.1" + } + }, + "node_modules/@vue/devtools-shared": { + "version": "7.6.4", + "resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-7.6.4.tgz", + "integrity": "sha512-nD6CUvBEel+y7zpyorjiUocy0nh77DThZJ0k1GRnJeOmY3ATq2fWijEp7wk37gb023Cb0R396uYh5qMSBQ5WFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "rfdc": "^1.4.1" + } + }, + "node_modules/@vue/reactivity": { + "version": "3.5.12", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.12.tgz", + "integrity": "sha512-UzaN3Da7xnJXdz4Okb/BGbAaomRHc3RdoWqTzlvd9+WBR5m3J39J1fGcHes7U3za0ruYn/iYy/a1euhMEHvTAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/shared": "3.5.12" + } + }, + "node_modules/@vue/runtime-core": { + "version": "3.5.12", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.12.tgz", + "integrity": "sha512-hrMUYV6tpocr3TL3Ad8DqxOdpDe4zuQY4HPY3X/VRh+L2myQO8MFXPAMarIOSGNu0bFAjh1yBkMPXZBqCk62Uw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.12", + "@vue/shared": "3.5.12" + } + }, + "node_modules/@vue/runtime-dom": { + "version": "3.5.12", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.12.tgz", + "integrity": "sha512-q8VFxR9A2MRfBr6/55Q3umyoN7ya836FzRXajPB6/Vvuv0zOPL+qltd9rIMzG/DbRLAIlREmnLsplEF/kotXKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.12", + "@vue/runtime-core": "3.5.12", + "@vue/shared": "3.5.12", + "csstype": "^3.1.3" + } + }, + "node_modules/@vue/server-renderer": { + "version": "3.5.12", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.12.tgz", + "integrity": "sha512-I3QoeDDeEPZm8yR28JtY+rk880Oqmj43hreIBVTicisFTx/Dl7JpG72g/X7YF8hnQD3IFhkky5i2bPonwrTVPg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/compiler-ssr": "3.5.12", + "@vue/shared": "3.5.12" + }, + "peerDependencies": { + "vue": "3.5.12" + } + }, + "node_modules/@vue/shared": { + "version": "3.5.12", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.12.tgz", + "integrity": "sha512-L2RPSAwUFbgZH20etwrXyVyCBu9OxRSi8T/38QsvnkJyvq2LufW2lDCOzm7t/U9C1mkhJGWYfCuFBCmIuNivrg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vueuse/core": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-11.2.0.tgz", + "integrity": "sha512-JIUwRcOqOWzcdu1dGlfW04kaJhW3EXnnjJJfLTtddJanymTL7lF1C0+dVVZ/siLfc73mWn+cGP1PE1PKPruRSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/web-bluetooth": "^0.0.20", + "@vueuse/metadata": "11.2.0", + "@vueuse/shared": "11.2.0", + "vue-demi": ">=0.14.10" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@vueuse/core/node_modules/vue-demi": { + "version": "0.14.10", + "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz", + "integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "vue-demi-fix": "bin/vue-demi-fix.js", + "vue-demi-switch": "bin/vue-demi-switch.js" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.0-rc.1", + "vue": "^3.0.0-0 || ^2.6.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } + }, + "node_modules/@vueuse/integrations": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/@vueuse/integrations/-/integrations-11.2.0.tgz", + "integrity": "sha512-zGXz3dsxNHKwiD9jPMvR3DAxQEOV6VWIEYTGVSB9PNpk4pTWR+pXrHz9gvXWcP2sTk3W2oqqS6KwWDdntUvNVA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vueuse/core": "11.2.0", + "@vueuse/shared": "11.2.0", + "vue-demi": ">=0.14.10" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "async-validator": "^4", + "axios": "^1", + "change-case": "^5", + "drauu": "^0.4", + "focus-trap": "^7", + "fuse.js": "^7", + "idb-keyval": "^6", + "jwt-decode": "^4", + "nprogress": "^0.2", + "qrcode": "^1.5", + "sortablejs": "^1", + "universal-cookie": "^7" + }, + "peerDependenciesMeta": { + "async-validator": { + "optional": true + }, + "axios": { + "optional": true + }, + "change-case": { + "optional": true + }, + "drauu": { + "optional": true + }, + "focus-trap": { + "optional": true + }, + "fuse.js": { + "optional": true + }, + "idb-keyval": { + "optional": true + }, + "jwt-decode": { + "optional": true + }, + "nprogress": { + "optional": true + }, + "qrcode": { + "optional": true + }, + "sortablejs": { + "optional": true + }, + "universal-cookie": { + "optional": true + } + } + }, + "node_modules/@vueuse/integrations/node_modules/vue-demi": { + "version": "0.14.10", + "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz", + "integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "vue-demi-fix": "bin/vue-demi-fix.js", + "vue-demi-switch": "bin/vue-demi-switch.js" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.0-rc.1", + "vue": "^3.0.0-0 || ^2.6.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } + }, + "node_modules/@vueuse/metadata": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-11.2.0.tgz", + "integrity": "sha512-L0ZmtRmNx+ZW95DmrgD6vn484gSpVeRbgpWevFKXwqqQxW9hnSi2Ppuh2BzMjnbv4aJRiIw8tQatXT9uOB23dQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@vueuse/shared": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-11.2.0.tgz", + "integrity": "sha512-VxFjie0EanOudYSgMErxXfq6fo8vhr5ICI+BuE3I9FnX7ePllEsVrRQ7O6Q1TLgApeLuPKcHQxAXpP+KnlrJsg==", + "dev": true, + "license": "MIT", + "dependencies": { + "vue-demi": ">=0.14.10" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@vueuse/shared/node_modules/vue-demi": { + "version": "0.14.10", + "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz", + "integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "vue-demi-fix": "bin/vue-demi-fix.js", + "vue-demi-switch": "bin/vue-demi-switch.js" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.0-rc.1", + "vue": "^3.0.0-0 || ^2.6.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } + }, + "node_modules/algoliasearch": { + "version": "5.13.0", + "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-5.13.0.tgz", + "integrity": "sha512-04lyQX3Ev/oLYQx+aagamQDXvkUUfX1mwrLrus15+9fNaYj28GDxxEzbwaRfvmHFcZyoxvup7mMtDTTw8SrTEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-abtesting": "5.13.0", + "@algolia/client-analytics": "5.13.0", + "@algolia/client-common": "5.13.0", + "@algolia/client-insights": "5.13.0", + "@algolia/client-personalization": "5.13.0", + "@algolia/client-query-suggestions": "5.13.0", + "@algolia/client-search": "5.13.0", + "@algolia/ingestion": "1.13.0", + "@algolia/monitoring": "1.13.0", + "@algolia/recommend": "5.13.0", + "@algolia/requester-browser-xhr": "5.13.0", + "@algolia/requester-fetch": "5.13.0", + "@algolia/requester-node-http": "5.13.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/birpc": { + "version": "0.2.19", + "resolved": "https://registry.npmjs.org/birpc/-/birpc-0.2.19.tgz", + "integrity": "sha512-5WeXXAvTmitV1RqJFppT5QtUiz2p1mRSYU000Jkft5ZUCLJIk4uQriYNO50HknxKwM6jd8utNc66K1qGIwwWBQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/ccount": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", + "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-html4": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", + "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-legacy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", + "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/comma-separated-tokens": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", + "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/copy-anything": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-3.0.5.tgz", + "integrity": "sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-what": "^4.1.8" + }, + "engines": { + "node": ">=12.13" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "dev": true, + "license": "MIT" + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/devicon": { + "version": "2.16.0", + "resolved": "https://registry.npmjs.org/devicon/-/devicon-2.16.0.tgz", + "integrity": "sha512-PE5a2HBNeN4av+Iu975OiiWEwS8LJPw5HAvlv0JUHb62jZTdYxTpz4ga+cQyvdtb3x1side2P9Sr1mmOmUkO/g==", + "license": "MIT" + }, + "node_modules/devlop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", + "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", + "dev": true, + "license": "MIT", + "dependencies": { + "dequal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/focus-trap": { + "version": "7.6.1", + "resolved": "https://registry.npmjs.org/focus-trap/-/focus-trap-7.6.1.tgz", + "integrity": "sha512-nB8y4nQl8PshahLpGKZOq1sb0xrMVFSn6at7u/qOsBZTlZRzaapISGENcB6mOkoezbClZyiMwEF/dGY8AZ00rA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tabbable": "^6.2.0" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/hast-util-to-html": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-9.0.3.tgz", + "integrity": "sha512-M17uBDzMJ9RPCqLMO92gNNUDuBSq10a25SDBI08iCCxmorf4Yy6sYHK57n9WAbRAAaU+DuR4W6GN9K4DFZesYg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-whitespace": "^3.0.0", + "html-void-elements": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "property-information": "^6.0.0", + "space-separated-tokens": "^2.0.0", + "stringify-entities": "^4.0.0", + "zwitch": "^2.0.4" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-whitespace": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", + "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hookable": { + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/hookable/-/hookable-5.5.3.tgz", + "integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/html-void-elements": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz", + "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-what": { + "version": "4.1.16", + "resolved": "https://registry.npmjs.org/is-what/-/is-what-4.1.16.tgz", + "integrity": "sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.13" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, + "node_modules/magic-string": { + "version": "0.30.12", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.12.tgz", + "integrity": "sha512-Ea8I3sQMVXr8JhN4z+H/d8zwo+tYDgHE9+5G4Wnrwhs0gaK9fXTKx0Tw5Xwsd/bCPTTZNRAdpyzvoeORe9LYpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0" + } + }, + "node_modules/mark.js": { + "version": "8.11.1", + "resolved": "https://registry.npmjs.org/mark.js/-/mark.js-8.11.1.tgz", + "integrity": "sha512-1I+1qpDt4idfgLQG+BNWmrqku+7/2bi5nLf4YwF8y8zXvmfiTBY3PV3ZibfrjBueCByROpuBjLLFCajqkgYoLQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/mdast-util-to-hast": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.0.tgz", + "integrity": "sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@ungap/structured-clone": "^1.0.0", + "devlop": "^1.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "trim-lines": "^3.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-util-character": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", + "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-encode": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.0.tgz", + "integrity": "sha512-pS+ROfCXAGLWCOc8egcBvT0kf27GoWMqtdarNfDcjb6YLuV5cM3ioG45Ys2qOVqeqSbjaKg72vU+Wby3eddPsA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-sanitize-uri": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.0.tgz", + "integrity": "sha512-WhYv5UEcZrbAtlsnPuChHUAsu/iBPOVaEVsntLBIdpibO0ddy8OzavZz3iL2xVvBZOpolujSliP65Kq0/7KIYw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-types": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.0.tgz", + "integrity": "sha512-oNh6S2WMHWRZrmutsRmDDfkzKtxF+bc2VxLC9dvtrDIRFln627VsFP6fLMgTryGDljgLPjkrzQSDcPrjPyDJ5w==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/minisearch": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/minisearch/-/minisearch-7.1.0.tgz", + "integrity": "sha512-tv7c/uefWdEhcu6hvrfTihflgeEi2tN6VV7HJnCjK6VxM75QQJh4t9FwJCsA2EsRS8LCnu3W87CuGPWMocOLCA==", + "dev": true, + "license": "MIT" + }, + "node_modules/mitt": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", + "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/oniguruma-to-js": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/oniguruma-to-js/-/oniguruma-to-js-0.4.3.tgz", + "integrity": "sha512-X0jWUcAlxORhOqqBREgPMgnshB7ZGYszBNspP+tS9hPD3l13CdaXcHbgImoHUHlrvGx/7AvFEkTRhAGYh+jzjQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "regex": "^4.3.2" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/perfect-debounce": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz", + "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/postcss": { + "version": "8.4.49", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz", + "integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/preact": { + "version": "10.24.3", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.24.3.tgz", + "integrity": "sha512-Z2dPnBnMUfyQfSQ+GBdsGa16hz35YmLmtTLhM169uW944hYL6xzTYkJjC07j+Wosz733pMWx0fgON3JNw1jJQA==", + "dev": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + } + }, + "node_modules/property-information": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.5.0.tgz", + "integrity": "sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/regex": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/regex/-/regex-4.4.0.tgz", + "integrity": "sha512-uCUSuobNVeqUupowbdZub6ggI5/JZkYyJdDogddJr60L764oxC2pMZov1fQ3wM9bdyzUILDG+Sqx6NAKAz9rKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", + "dev": true, + "license": "MIT" + }, + "node_modules/rollup": { + "version": "4.25.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.25.0.tgz", + "integrity": "sha512-uVbClXmR6wvx5R1M3Od4utyLUxrmOcEm3pAtMphn73Apq19PDtHpgZoEvqH2YnnaNUuvKmg2DgRd2Sqv+odyqg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.6" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.25.0", + "@rollup/rollup-android-arm64": "4.25.0", + "@rollup/rollup-darwin-arm64": "4.25.0", + "@rollup/rollup-darwin-x64": "4.25.0", + "@rollup/rollup-freebsd-arm64": "4.25.0", + "@rollup/rollup-freebsd-x64": "4.25.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.25.0", + "@rollup/rollup-linux-arm-musleabihf": "4.25.0", + "@rollup/rollup-linux-arm64-gnu": "4.25.0", + "@rollup/rollup-linux-arm64-musl": "4.25.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.25.0", + "@rollup/rollup-linux-riscv64-gnu": "4.25.0", + "@rollup/rollup-linux-s390x-gnu": "4.25.0", + "@rollup/rollup-linux-x64-gnu": "4.25.0", + "@rollup/rollup-linux-x64-musl": "4.25.0", + "@rollup/rollup-win32-arm64-msvc": "4.25.0", + "@rollup/rollup-win32-ia32-msvc": "4.25.0", + "@rollup/rollup-win32-x64-msvc": "4.25.0", + "fsevents": "~2.3.2" + } + }, + "node_modules/search-insights": { + "version": "2.17.2", + "resolved": "https://registry.npmjs.org/search-insights/-/search-insights-2.17.2.tgz", + "integrity": "sha512-zFNpOpUO+tY2D85KrxJ+aqwnIfdEGi06UH2+xEb+Bp9Mwznmauqc9djbnBibJO5mpfUPPa8st6Sx65+vbeO45g==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/shiki": { + "version": "1.22.2", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-1.22.2.tgz", + "integrity": "sha512-3IZau0NdGKXhH2bBlUk4w1IHNxPh6A5B2sUpyY+8utLu2j/h1QpFkAaUA1bAMxOWWGtTWcAh531vnS4NJKS/lA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/core": "1.22.2", + "@shikijs/engine-javascript": "1.22.2", + "@shikijs/engine-oniguruma": "1.22.2", + "@shikijs/types": "1.22.2", + "@shikijs/vscode-textmate": "^9.3.0", + "@types/hast": "^3.0.4" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/space-separated-tokens": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", + "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/speakingurl": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/speakingurl/-/speakingurl-14.0.1.tgz", + "integrity": "sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stringify-entities": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", + "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", + "dev": true, + "license": "MIT", + "dependencies": { + "character-entities-html4": "^2.0.0", + "character-entities-legacy": "^3.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/superjson": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/superjson/-/superjson-2.2.1.tgz", + "integrity": "sha512-8iGv75BYOa0xRJHK5vRLEjE2H/i4lulTjzpUXic3Eg8akftYjkmQDa8JARQ42rlczXyFR3IeRoeFCc7RxHsYZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "copy-anything": "^3.0.2" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/tabbable": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.2.0.tgz", + "integrity": "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==", + "dev": true, + "license": "MIT" + }, + "node_modules/trim-lines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", + "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/unist-util-is": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz", + "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", + "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz", + "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-parents": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz", + "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", + "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-message": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.2.tgz", + "integrity": "sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vite": { + "version": "5.4.11", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.11.tgz", + "integrity": "sha512-c7jFQRklXua0mTzneGW9QVyxFjUgwcihC4bXEtujIo2ouWCe1Ajt/amn2PCxYnhYfd5k09JX3SB7OYWFKYqj8Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vitepress": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/vitepress/-/vitepress-1.5.0.tgz", + "integrity": "sha512-q4Q/G2zjvynvizdB3/bupdYkCJe2umSAMv9Ju4d92E6/NXJ59z70xB0q5p/4lpRyAwflDsbwy1mLV9Q5+nlB+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@docsearch/css": "^3.6.2", + "@docsearch/js": "^3.6.2", + "@iconify-json/simple-icons": "^1.2.10", + "@shikijs/core": "^1.22.2", + "@shikijs/transformers": "^1.22.2", + "@shikijs/types": "^1.22.2", + "@types/markdown-it": "^14.1.2", + "@vitejs/plugin-vue": "^5.1.4", + "@vue/devtools-api": "^7.5.4", + "@vue/shared": "^3.5.12", + "@vueuse/core": "^11.1.0", + "@vueuse/integrations": "^11.1.0", + "focus-trap": "^7.6.0", + "mark.js": "8.11.1", + "minisearch": "^7.1.0", + "shiki": "^1.22.2", + "vite": "^5.4.10", + "vue": "^3.5.12" + }, + "bin": { + "vitepress": "bin/vitepress.js" + }, + "peerDependencies": { + "markdown-it-mathjax3": "^4", + "postcss": "^8" + }, + "peerDependenciesMeta": { + "markdown-it-mathjax3": { + "optional": true + }, + "postcss": { + "optional": true + } + } + }, + "node_modules/vue": { + "version": "3.5.12", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.12.tgz", + "integrity": "sha512-CLVZtXtn2ItBIi/zHZ0Sg1Xkb7+PU32bJJ8Bmy7ts3jxXTcbfsEfBivFYYWz1Hur+lalqGAh65Coin0r+HRUfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.12", + "@vue/compiler-sfc": "3.5.12", + "@vue/runtime-dom": "3.5.12", + "@vue/server-renderer": "3.5.12", + "@vue/shared": "3.5.12" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/zwitch": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", + "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + } + } +} diff --git a/docs/package.json b/docs/package.json new file mode 100644 index 00000000..9d12b47f --- /dev/null +++ b/docs/package.json @@ -0,0 +1,15 @@ +{ + "name": "ags-docs", + "type": "module", + "devDependencies": { + "vitepress": "^1.5.0" + }, + "scripts": { + "dev": "vitepress dev .", + "build": "vitepress build .", + "preview": "vitepress preview ." + }, + "dependencies": { + "devicon": "^2.16.0" + } +} diff --git a/docs/vitepress.config.ts b/docs/vitepress.config.ts new file mode 100644 index 00000000..ae231c43 --- /dev/null +++ b/docs/vitepress.config.ts @@ -0,0 +1,60 @@ +import { defineConfig } from 'vitepress' + +export default defineConfig({ + title: "AGS", + description: "Documentation website of AGS", + + outDir: "./dist", + base: "/ags/", + lastUpdated: true, + + head: [ + ["link", { rel: "icon", href: "https://aylur.github.io/astal/icon.svg" }], + ], + + themeConfig: { + outline: "deep", + + nav: [ + { + text: "Guide", + link: "/guide/installation", + activeMatch: "/guide/", + }, + { + text: "Astal", + link: "https://aylur.github.io/astal/", + }, + ], + + + sidebar: [ + { text: "Quick Start", link: "/guide/quick-start" }, + { text: "Installation", link: "/guide/install" }, + { text: "Setting up a project", link: "/guide/init" }, + { text: "Bundling", link: "/guide/bundling" }, + { text: "Generating types", link: "/guide/types" }, + { text: "Astal CLI", link: "/guide/astal-cli" }, + { text: "Example", link: "/guide/example" }, + { text: "Nix", link: "/guide/nix" }, + ], + + socialLinks: [ + { icon: "github", link: "https://github.com/aylur/ags" }, + { icon: "discord", link: "https://discord.gg/CXQpHwDuhY" }, + ], + + editLink: { + pattern: "https://github.com/aylur/ags/edit/main/docs/:path", + text: "Edit this page on GitHub", + }, + + lastUpdated: { + text: "Last updated", + }, + + search: { + provider: "local", + }, + }, +}) diff --git a/docs/vitepress.theme.css b/docs/vitepress.theme.css new file mode 100644 index 00000000..7bd694f9 --- /dev/null +++ b/docs/vitepress.theme.css @@ -0,0 +1,126 @@ +/** + * https://github.com/vuejs/vitepress/blob/main/src/client/theme-default/styles/vars.css + */ + +.VPNavBar .VPNavBarTitle span { + font-size: 1.2em; + font-weight: bold; + background: linear-gradient(120deg, var(--vp-c-brand-3), var(--vp-c-brand-1)); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; + color: transparent; +} + +:root { + --vp-c-gray-1: #dddde3; + --vp-c-gray-2: #e4e4e9; + --vp-c-gray-3: #ebebef; + --vp-c-gray-soft: rgba(142, 150, 170, 0.14); + + --vp-c-indigo-1: #3451b2; + --vp-c-indigo-2: #3a5ccc; + --vp-c-indigo-3: #5672cd; + --vp-c-indigo-soft: rgba(100, 108, 255, 0.14); + + --vp-c-purple-1: #6f42c1; + --vp-c-purple-2: #7e4cc9; + --vp-c-purple-3: #8e5cd9; + --vp-c-purple-soft: rgba(159, 122, 234, 0.14); + + --vp-c-green-1: #18794e; + --vp-c-green-2: #299764; + --vp-c-green-3: #30a46c; + --vp-c-green-soft: rgba(16, 185, 129, 0.14); + + --vp-c-yellow-1: #915930; + --vp-c-yellow-2: #946300; + --vp-c-yellow-3: #9f6a00; + --vp-c-yellow-soft: rgba(234, 179, 8, 0.14); + + --vp-c-red-1: #b8272c; + --vp-c-red-2: #d5393e; + --vp-c-red-3: #e0575b; + --vp-c-red-soft: rgba(244, 63, 94, 0.14); + + --vp-c-sponsor: #db2777; +} + +.dark { + --vp-c-gray-1: #515151; + --vp-c-gray-2: #464646; + --vp-c-gray-3: #323232; + --vp-c-gray-soft: hsla(0, 0%, 36%, 0.16); + + --vp-c-indigo-1: hsl(207, 76%, 61%); + --vp-c-indigo-2: hsl(207, 86%, 51%); + --vp-c-indigo-3: hsl(207, 96%, 41%); + --vp-c-indigo-soft: hsla(207, 76%, 61%, .18); + + --vp-c-purple-1: #c8abfa; + --vp-c-purple-2: #a879e6; + --vp-c-purple-3: #8e5cd9; + --vp-c-purple-soft: rgba(159, 122, 234, 0.16); + + --vp-c-green-1: hsl(158, 100%, 42%); + --vp-c-green-2: hsl(158, 95%, 37%); + --vp-c-green-3: hsl(158, 90%, 35%); + --vp-c-green-soft: hsla(158, 100%, 42%, 0.16); + + --vp-c-yellow-1: #f9b44e; + --vp-c-yellow-2: #da8b17; + --vp-c-yellow-3: #a46a0a; + --vp-c-yellow-soft: rgba(234, 179, 8, 0.16); + + --vp-c-red-1: #f66f81; + --vp-c-red-2: #f14158; + --vp-c-red-3: #b62a3c; + --vp-c-red-soft: rgba(244, 63, 94, 0.16); +} + +/* ---------- * + * Background * + * ---------- */ + +:root { + --vp-c-bg: #ffffff; + --vp-c-bg-alt: #f6f6f7; + --vp-c-bg-elv: #ffffff; + --vp-c-bg-soft: #f6f6f7; +} + +.dark { + --vp-c-bg: #111111; + --vp-c-bg-alt: #181818; + --vp-c-bg-elv: #202122; + --vp-c-bg-soft: #202122; +} + +/** Borders */ +:root { + --vp-c-border: #c2c2c4; + --vp-c-divider: #e2e2e3; + --vp-c-gutter: #e2e2e3; +} + +.dark { + --vp-c-border: #3c3d3e; + --vp-c-divider: #2e2e2e; + --vp-c-gutter: #000000; +} + +/* ---- * + * Text * + * ---- */ + +:root { + --vp-c-text-1: rgba(60, 60, 67); + --vp-c-text-2: rgba(60, 60, 67, 0.78); + --vp-c-text-3: rgba(60, 60, 67, 0.56); +} + +.dark { + --vp-c-text-1: rgba(255, 255, 245, 0.86); + --vp-c-text-2: rgba(235, 235, 245, 0.6); + --vp-c-text-3: rgba(235, 235, 245, 0.38); +} diff --git a/example/applauncher/README.md b/example/applauncher/README.md deleted file mode 100644 index 88791dc9..00000000 --- a/example/applauncher/README.md +++ /dev/null @@ -1,19 +0,0 @@ -# Application Launcher - -setup - -```bash -mkdir -p ~/.config/ags -git clone https://github.com/Aylur/ags.git /tmp/ags -cp -r /tmp/ags/example/applauncher/* ~/.config/ags - -# optionally setup types -ags --init -c ~/.config/ags/config.js -``` - -running - -```bash -ags -c ~/.config/ags/config.js & -ags -t applauncher -``` diff --git a/example/applauncher/applauncher.js b/example/applauncher/applauncher.js deleted file mode 100644 index 23dfa400..00000000 --- a/example/applauncher/applauncher.js +++ /dev/null @@ -1,107 +0,0 @@ -const { query } = await Service.import("applications") -const WINDOW_NAME = "applauncher" - -/** @param {import('resource:///com/github/Aylur/ags/service/applications.js').Application} app */ -const AppItem = app => Widget.Button({ - on_clicked: () => { - App.closeWindow(WINDOW_NAME) - app.launch() - }, - attribute: { app }, - child: Widget.Box({ - children: [ - Widget.Icon({ - icon: app.icon_name || "", - size: 42, - }), - Widget.Label({ - class_name: "title", - label: app.name, - xalign: 0, - vpack: "center", - truncate: "end", - }), - ], - }), -}) - -const Applauncher = ({ width = 500, height = 500, spacing = 12 }) => { - // list of application buttons - let applications = query("").map(AppItem) - - // container holding the buttons - const list = Widget.Box({ - vertical: true, - children: applications, - spacing, - }) - - // repopulate the box, so the most frequent apps are on top of the list - function repopulate() { - applications = query("").map(AppItem) - list.children = applications - } - - // search entry - const entry = Widget.Entry({ - hexpand: true, - css: `margin-bottom: ${spacing}px;`, - - // to launch the first item on Enter - on_accept: () => { - // make sure we only consider visible (searched for) applications - const results = applications.filter((item) => item.visible); - if (results[0]) { - App.toggleWindow(WINDOW_NAME) - results[0].attribute.app.launch() - } - }, - - // filter out the list - on_change: ({ text }) => applications.forEach(item => { - item.visible = item.attribute.app.match(text ?? "") - }), - }) - - return Widget.Box({ - vertical: true, - css: `margin: ${spacing * 2}px;`, - children: [ - entry, - - // wrap the list in a scrollable - Widget.Scrollable({ - hscroll: "never", - css: `min-width: ${width}px;` - + `min-height: ${height}px;`, - child: list, - }), - ], - setup: self => self.hook(App, (_, windowName, visible) => { - if (windowName !== WINDOW_NAME) - return - - // when the applauncher shows up - if (visible) { - repopulate() - entry.text = "" - entry.grab_focus() - } - }), - }) -} - -// there needs to be only one instance -export const applauncher = Widget.Window({ - name: WINDOW_NAME, - setup: self => self.keybind("Escape", () => { - App.closeWindow(WINDOW_NAME) - }), - visible: false, - keymode: "exclusive", - child: Applauncher({ - width: 500, - height: 500, - spacing: 12, - }), -}) diff --git a/example/applauncher/config.js b/example/applauncher/config.js deleted file mode 100644 index 61842eed..00000000 --- a/example/applauncher/config.js +++ /dev/null @@ -1,5 +0,0 @@ -import { applauncher } from "./applauncher.js" - -App.config({ - windows: [applauncher], -}) diff --git a/example/icon-browser/README.md b/example/icon-browser/README.md deleted file mode 100644 index 2657f693..00000000 --- a/example/icon-browser/README.md +++ /dev/null @@ -1,6 +0,0 @@ -# Icon Browser - -```bash -chmod +x icon-browser.js -./icon-browser.js -``` diff --git a/example/icon-browser/icon-browser.js b/example/icon-browser/icon-browser.js deleted file mode 100755 index f4187afb..00000000 --- a/example/icon-browser/icon-browser.js +++ /dev/null @@ -1,73 +0,0 @@ -#!/usr/bin/env -S ags -b icon-browser -c - -import Gtk from "gi://Gtk?version=3.0" - -const IconPicker = () => { - const selected = Widget.Label({ - css: "font-size: 1.2em;", - label: "Click on an icon te get its name", - selectable: true, - }) - - const flowbox = Widget.FlowBox({ - min_children_per_line: 7, - row_spacing: 12, - column_spacing: 12, - vpack: "start", - hpack: "start", - setup: self => { - self.connect("child-activated", (_, child) => { - selected.label = child.get_child()?.iconName || "" - }) - - Gtk.IconTheme.get_default().list_icons(null).sort().map(icon => { - !icon.endsWith(".symbolic") && self.insert(Widget.Icon({ - icon, - size: 38, - }), -1) - }) - - self.show_all() - }, - }) - - const entry = Widget.Entry({ - placeholder_text: "Type to seach...", - primary_icon_name: "system-search-symbolic", - on_change: ({ text }) => flowbox.get_children().forEach(child => { - child.visible = child.get_child().iconName.includes(text) - }), - }) - - return Widget.Box({ - css: "padding: 30px;", - spacing: 20, - vertical: true, - children: [ - entry, - Widget.Scrollable({ - hscroll: "never", - vscroll: "always", - hexpand: true, - vexpand: true, - css: "min-width: 400px;" - + "min-height: 500px;", - child: flowbox, - }), - selected, - ], - }) -} - -const win = new Gtk.Window({ - name: "icon-browser", - child: IconPicker(), -}) - -win.show_all() -win.connect("delete-event", () => { - App.quit() - return true -}) - -export default { windows: [win] } diff --git a/example/media-widget/Media.js b/example/media-widget/Media.js deleted file mode 100644 index 337656db..00000000 --- a/example/media-widget/Media.js +++ /dev/null @@ -1,153 +0,0 @@ -const mpris = await Service.import("mpris") -const players = mpris.bind("players") - -const FALLBACK_ICON = "audio-x-generic-symbolic" -const PLAY_ICON = "media-playback-start-symbolic" -const PAUSE_ICON = "media-playback-pause-symbolic" -const PREV_ICON = "media-skip-backward-symbolic" -const NEXT_ICON = "media-skip-forward-symbolic" - -/** @param {number} length */ -function lengthStr(length) { - const min = Math.floor(length / 60) - const sec = Math.floor(length % 60) - const sec0 = sec < 10 ? "0" : "" - return `${min}:${sec0}${sec}` -} - -/** @param {import('types/service/mpris').MprisPlayer} player */ -function Player(player) { - const img = Widget.Box({ - class_name: "img", - vpack: "start", - css: player.bind("cover_path").transform(p => ` - background-image: url('${p}'); - `), - }) - - const title = Widget.Label({ - class_name: "title", - wrap: true, - hpack: "start", - label: player.bind("track_title"), - }) - - const artist = Widget.Label({ - class_name: "artist", - wrap: true, - hpack: "start", - label: player.bind("track_artists").transform(a => a.join(", ")), - }) - - const positionSlider = Widget.Slider({ - class_name: "position", - draw_value: false, - on_change: ({ value }) => player.position = value * player.length, - visible: player.bind("length").as(l => l > 0), - setup: self => { - function update() { - const value = player.position / player.length - self.value = value > 0 ? value : 0 - } - self.hook(player, update) - self.hook(player, update, "position") - self.poll(1000, update) - }, - }) - - const positionLabel = Widget.Label({ - class_name: "position", - hpack: "start", - setup: self => { - const update = (_, time) => { - self.label = lengthStr(time || player.position) - self.visible = player.length > 0 - } - - self.hook(player, update, "position") - self.poll(1000, update) - }, - }) - - const lengthLabel = Widget.Label({ - class_name: "length", - hpack: "end", - visible: player.bind("length").transform(l => l > 0), - label: player.bind("length").transform(lengthStr), - }) - - const icon = Widget.Icon({ - class_name: "icon", - hexpand: true, - hpack: "end", - vpack: "start", - tooltip_text: player.identity || "", - icon: player.bind("entry").transform(entry => { - const name = `${entry}-symbolic` - return Utils.lookUpIcon(name) ? name : FALLBACK_ICON - }), - }) - - const playPause = Widget.Button({ - class_name: "play-pause", - on_clicked: () => player.playPause(), - visible: player.bind("can_play"), - child: Widget.Icon({ - icon: player.bind("play_back_status").transform(s => { - switch (s) { - case "Playing": return PAUSE_ICON - case "Paused": - case "Stopped": return PLAY_ICON - } - }), - }), - }) - - const prev = Widget.Button({ - on_clicked: () => player.previous(), - visible: player.bind("can_go_prev"), - child: Widget.Icon(PREV_ICON), - }) - - const next = Widget.Button({ - on_clicked: () => player.next(), - visible: player.bind("can_go_next"), - child: Widget.Icon(NEXT_ICON), - }) - - return Widget.Box( - { class_name: "player" }, - img, - Widget.Box( - { - vertical: true, - hexpand: true, - }, - Widget.Box([ - title, - icon, - ]), - artist, - Widget.Box({ vexpand: true }), - positionSlider, - Widget.CenterBox({ - start_widget: positionLabel, - center_widget: Widget.Box([ - prev, - playPause, - next, - ]), - end_widget: lengthLabel, - }), - ), - ) -} - -export function Media() { - return Widget.Box({ - vertical: true, - css: "min-height: 2px; min-width: 2px;", // small hack to make it visible - visible: players.as(p => p.length > 0), - children: players.as(p => p.map(Player)), - }) -} diff --git a/example/media-widget/README.md b/example/media-widget/README.md deleted file mode 100644 index 0db707c3..00000000 --- a/example/media-widget/README.md +++ /dev/null @@ -1,18 +0,0 @@ -# Media Widget - -setup - -```bash -mkdir -p ~/.config/ags -git clone https://github.com/Aylur/ags.git /tmp/ags -cp -r /tmp/ags/example/media-widget/* ~/.config/ags - -# optionally setup types -ags --init -c ~/.config/ags/config.js -``` - -running - -```bash -ags -c ~/.config/ags/config.js & -``` diff --git a/example/media-widget/config.js b/example/media-widget/config.js deleted file mode 100644 index d8f5c86f..00000000 --- a/example/media-widget/config.js +++ /dev/null @@ -1,12 +0,0 @@ -import { Media } from "./Media.js" - -const win = Widget.Window({ - name: "mpris", - anchor: ["top", "right"], - child: Media(), -}) - -App.config({ - style: "./style.css", - windows: [win], -}) diff --git a/example/media-widget/style.css b/example/media-widget/style.css deleted file mode 100644 index 8488ac86..00000000 --- a/example/media-widget/style.css +++ /dev/null @@ -1,49 +0,0 @@ -.player { - padding: 10px; - min-width: 350px; -} - -.player .img { - min-width: 100px; - min-height: 100px; - background-size: cover; - background-position: center; - border-radius: 13px; - margin-right: 1em; -} - -.player .title { - font-size: 1.2em; -} - -.player .artist { - font-size: 1.1em; - color: @insensitive_fg_color; -} - -.player scale.position { - padding: 0; - margin-bottom: .3em; -} - -.player scale.position trough { - min-height: 8px; -} - -.player scale.position highlight { - background-color: @theme_fg_color; -} - -.player scale.position slider { - all: unset; -} - -.player button { - min-height: 1em; - min-width: 1em; - padding: .3em; -} - -.player button.play-pause { - margin: 0 .3em; -} diff --git a/example/notification-popups/README.md b/example/notification-popups/README.md deleted file mode 100644 index 7ca2c7f8..00000000 --- a/example/notification-popups/README.md +++ /dev/null @@ -1,18 +0,0 @@ -# Notification Popups - -setup - -```bash -mkdir -p ~/.config/ags -git clone https://github.com/Aylur/ags.git /tmp/ags -cp -r /tmp/ags/example/notification-popups/* ~/.config/ags - -# optionally setup types -ags --init -c ~/.config/ags/config.js -``` - -running - -```bash -ags -c ~/.config/ags/config.js & -``` diff --git a/example/notification-popups/config.js b/example/notification-popups/config.js deleted file mode 100644 index bcb678b0..00000000 --- a/example/notification-popups/config.js +++ /dev/null @@ -1,18 +0,0 @@ -import { NotificationPopups } from "./notificationPopups.js" - -Utils.timeout(100, () => Utils.notify({ - summary: "Notification Popup Example", - iconName: "info-symbolic", - body: "Lorem ipsum dolor sit amet, qui minim labore adipisicing " - + "minim sint cillum sint consectetur cupidatat.", - actions: { - "Cool": () => print("pressed Cool"), - }, -})) - -App.config({ - style: App.configDir + "/style.css", - windows: [ - NotificationPopups(), - ], -}) diff --git a/example/notification-popups/notificationPopups.js b/example/notification-popups/notificationPopups.js deleted file mode 100644 index f360d3cd..00000000 --- a/example/notification-popups/notificationPopups.js +++ /dev/null @@ -1,130 +0,0 @@ -const notifications = await Service.import("notifications") - -/** @param {import('resource:///com/github/Aylur/ags/service/notifications.js').Notification} n */ -function NotificationIcon({ app_entry, app_icon, image }) { - if (image) { - return Widget.Box({ - css: `background-image: url("${image}");` - + "background-size: contain;" - + "background-repeat: no-repeat;" - + "background-position: center;", - }) - } - - let icon = "dialog-information-symbolic" - if (Utils.lookUpIcon(app_icon)) - icon = app_icon - - if (app_entry && Utils.lookUpIcon(app_entry)) - icon = app_entry - - return Widget.Box({ - child: Widget.Icon(icon), - }) -} - -/** @param {import('resource:///com/github/Aylur/ags/service/notifications.js').Notification} n */ -function Notification(n) { - const icon = Widget.Box({ - vpack: "start", - class_name: "icon", - child: NotificationIcon(n), - }) - - const title = Widget.Label({ - class_name: "title", - xalign: 0, - justification: "left", - hexpand: true, - max_width_chars: 24, - truncate: "end", - wrap: true, - label: n.summary, - use_markup: true, - }) - - const body = Widget.Label({ - class_name: "body", - hexpand: true, - use_markup: true, - xalign: 0, - justification: "left", - label: n.body, - wrap: true, - }) - - const actions = Widget.Box({ - class_name: "actions", - children: n.actions.map(({ id, label }) => Widget.Button({ - class_name: "action-button", - on_clicked: () => { - n.invoke(id) - n.dismiss() - }, - hexpand: true, - child: Widget.Label(label), - })), - }) - - return Widget.EventBox( - { - attribute: { id: n.id }, - on_primary_click: n.dismiss, - }, - Widget.Box( - { - class_name: `notification ${n.urgency}`, - vertical: true, - }, - Widget.Box([ - icon, - Widget.Box( - { vertical: true }, - title, - body, - ), - ]), - actions, - ), - ) -} - -export function NotificationPopups(monitor = 0) { - const list = Widget.Box({ - vertical: true, - children: notifications.popups.map(Notification), - }) - - function onNotified(_, /** @type {number} */ id) { - const n = notifications.getNotification(id) - if (n) - list.children = [Notification(n), ...list.children] - } - - function onDismissed(_, /** @type {number} */ id) { - list.children.find(n => n.attribute.id === id)?.destroy() - } - - list.hook(notifications, onNotified, "notified") - .hook(notifications, onDismissed, "dismissed") - - return Widget.Window({ - monitor, - name: `notifications${monitor}`, - class_name: "notification-popups", - anchor: ["top", "right"], - child: Widget.Box({ - css: "min-width: 2px; min-height: 2px;", - class_name: "notifications", - vertical: true, - child: list, - - /** this is a simple one liner that could be used instead of - hooking into the 'notified' and 'dismissed' signals. - but its not very optimized becuase it will recreate - the whole list everytime a notification is added or dismissed */ - // children: notifications.bind('popups') - // .as(popups => popups.map(Notification)) - }), - }) -} diff --git a/example/notification-popups/style.css b/example/notification-popups/style.css deleted file mode 100644 index 2d2883b5..00000000 --- a/example/notification-popups/style.css +++ /dev/null @@ -1,57 +0,0 @@ -window.notification-popups box.notifications { - padding: .5em; -} - -.icon { - min-width: 68px; - min-height: 68px; - margin-right: 1em; -} - -.icon image { - font-size: 58px; - /* to center the icon */ - margin: 5px; - color: @theme_fg_color; -} - -.icon box { - min-width: 68px; - min-height: 68px; - border-radius: 7px; -} - -.notification { - min-width: 350px; - border-radius: 11px; - padding: 1em; - margin: .5em; - border: 1px solid @wm_borders_edge; - background-color: @theme_bg_color; -} - -.notification.critical { - border: 1px solid lightcoral; -} - -.title { - color: @theme_fg_color; - font-size: 1.4em; -} - -.body { - color: @theme_unfocused_fg_color; -} - -.actions .action-button { - margin: 0 .4em; - margin-top: .8em; -} - -.actions .action-button:first-child { - margin-left: 0; -} - -.actions .action-button:last-child { - margin-right: 0; -} diff --git a/example/simple-bar/README.md b/example/simple-bar/README.md deleted file mode 100644 index 054a467d..00000000 --- a/example/simple-bar/README.md +++ /dev/null @@ -1,18 +0,0 @@ -# Simple Bar - -setup - -```bash -mkdir -p ~/.config/ags -git clone https://github.com/Aylur/ags.git /tmp/ags -cp -r /tmp/ags/example/simple-bar/* ~/.config/ags - -# optionally setup types -ags --init -c ~/.config/ags/config.js -``` - -running - -```bash -ags -c ~/.config/ags/config.js & -``` diff --git a/example/simple-bar/config.js b/example/simple-bar/config.js deleted file mode 100644 index 4c0a30bb..00000000 --- a/example/simple-bar/config.js +++ /dev/null @@ -1,219 +0,0 @@ -const hyprland = await Service.import("hyprland") -const notifications = await Service.import("notifications") -const mpris = await Service.import("mpris") -const audio = await Service.import("audio") -const battery = await Service.import("battery") -const systemtray = await Service.import("systemtray") - -const date = Variable("", { - poll: [1000, 'date "+%H:%M:%S %b %e."'], -}) - -// widgets can be only assigned as a child in one container -// so to make a reuseable widget, make it a function -// then you can simply instantiate one by calling it - -function Workspaces() { - const activeId = hyprland.active.workspace.bind("id") - const workspaces = hyprland.bind("workspaces") - .as(ws => ws.map(({ id }) => Widget.Button({ - on_clicked: () => hyprland.messageAsync(`dispatch workspace ${id}`), - child: Widget.Label(`${id}`), - class_name: activeId.as(i => `${i === id ? "focused" : ""}`), - }))) - - return Widget.Box({ - class_name: "workspaces", - children: workspaces, - }) -} - - -function ClientTitle() { - return Widget.Label({ - class_name: "client-title", - label: hyprland.active.client.bind("title"), - }) -} - - -function Clock() { - return Widget.Label({ - class_name: "clock", - label: date.bind(), - }) -} - - -// we don't need dunst or any other notification daemon -// because the Notifications module is a notification daemon itself -function Notification() { - const popups = notifications.bind("popups") - return Widget.Box({ - class_name: "notification", - visible: popups.as(p => p.length > 0), - children: [ - Widget.Icon({ - icon: "preferences-system-notifications-symbolic", - }), - Widget.Label({ - label: popups.as(p => p[0]?.summary || ""), - }), - ], - }) -} - - -function Media() { - const label = Utils.watch("", mpris, "player-changed", () => { - if (mpris.players[0]) { - const { track_artists, track_title } = mpris.players[0] - return `${track_artists.join(", ")} - ${track_title}` - } else { - return "Nothing is playing" - } - }) - - return Widget.Button({ - class_name: "media", - on_primary_click: () => mpris.getPlayer("")?.playPause(), - on_scroll_up: () => mpris.getPlayer("")?.next(), - on_scroll_down: () => mpris.getPlayer("")?.previous(), - child: Widget.Label({ label }), - }) -} - - -function Volume() { - const icons = { - 101: "overamplified", - 67: "high", - 34: "medium", - 1: "low", - 0: "muted", - } - - function getIcon() { - const icon = audio.speaker.is_muted ? 0 : [101, 67, 34, 1, 0].find( - threshold => threshold <= audio.speaker.volume * 100) - - return `audio-volume-${icons[icon]}-symbolic` - } - - const icon = Widget.Icon({ - icon: Utils.watch(getIcon(), audio.speaker, getIcon), - }) - - const slider = Widget.Slider({ - hexpand: true, - draw_value: false, - on_change: ({ value }) => audio.speaker.volume = value, - setup: self => self.hook(audio.speaker, () => { - self.value = audio.speaker.volume || 0 - }), - }) - - return Widget.Box({ - class_name: "volume", - css: "min-width: 180px", - children: [icon, slider], - }) -} - - -function BatteryLabel() { - const value = battery.bind("percent").as(p => p > 0 ? p / 100 : 0) - const icon = battery.bind("percent").as(p => - `battery-level-${Math.floor(p / 10) * 10}-symbolic`) - - return Widget.Box({ - class_name: "battery", - visible: battery.bind("available"), - children: [ - Widget.Icon({ icon }), - Widget.LevelBar({ - widthRequest: 140, - vpack: "center", - value, - }), - ], - }) -} - - -function SysTray() { - const items = systemtray.bind("items") - .as(items => items.map(item => Widget.Button({ - child: Widget.Icon({ icon: item.bind("icon") }), - on_primary_click: (_, event) => item.activate(event), - on_secondary_click: (_, event) => item.openMenu(event), - tooltip_markup: item.bind("tooltip_markup"), - }))) - - return Widget.Box({ - children: items, - }) -} - - -// layout of the bar -function Left() { - return Widget.Box({ - spacing: 8, - children: [ - Workspaces(), - ClientTitle(), - ], - }) -} - -function Center() { - return Widget.Box({ - spacing: 8, - children: [ - Media(), - Notification(), - ], - }) -} - -function Right() { - return Widget.Box({ - hpack: "end", - spacing: 8, - children: [ - Volume(), - BatteryLabel(), - Clock(), - SysTray(), - ], - }) -} - -function Bar(monitor = 0) { - return Widget.Window({ - name: `bar-${monitor}`, // name has to be unique - class_name: "bar", - monitor, - anchor: ["top", "left", "right"], - exclusivity: "exclusive", - child: Widget.CenterBox({ - start_widget: Left(), - center_widget: Center(), - end_widget: Right(), - }), - }) -} - -App.config({ - style: "./style.css", - windows: [ - Bar(), - - // you can call it, for each monitor - // Bar(0), - // Bar(1) - ], -}) - -export { } diff --git a/example/simple-bar/style.css b/example/simple-bar/style.css deleted file mode 100644 index 9ac7355b..00000000 --- a/example/simple-bar/style.css +++ /dev/null @@ -1,40 +0,0 @@ -window.bar { - background-color: @theme_bg_color; - color: @theme_fg_color; -} - -button { - min-width: 0; - padding-top: 0; - padding-bottom: 0; - background-color: transparent; -} - -button:active { - background-color: @theme_selected_bg_color; -} - -button:hover { - border-bottom: 3px solid @theme_fg_color; -} - -label { - font-weight: bold; -} - -.workspaces button.focused { - border-bottom: 3px solid @theme_selected_bg_color; -} - -.client-title { - color: @theme_selected_bg_color; -} - -.notification { - color: yellow; -} - -levelbar block, -highlight { - min-height: 10px; -} diff --git a/example/ts-starter-config/README.md b/example/ts-starter-config/README.md deleted file mode 100644 index 25696e12..00000000 --- a/example/ts-starter-config/README.md +++ /dev/null @@ -1,21 +0,0 @@ -# TypeScript Starter Config - -Dependency: `bun` - -```bash -mkdir -p ~/.config/ags -git clone https://github.com/Aylur/ags.git /tmp/ags -cp -r /tmp/ags/example/ts-starter-config/* ~/.config/ags -``` - -optionally setup types - -```bash -ags --init -c ~/.config/ags/config.js -``` - -running - -```bash -ags -c ~/.config/ags/config.js & -``` diff --git a/example/ts-starter-config/config.js b/example/ts-starter-config/config.js deleted file mode 100644 index d8d6444b..00000000 --- a/example/ts-starter-config/config.js +++ /dev/null @@ -1,15 +0,0 @@ -const main = '/tmp/ags/main.js'; - -try { - await Utils.execAsync([ - 'bun', 'build', `${App.configDir}/main.ts`, - '--outfile', main, - '--external', 'resource://*', - '--external', 'gi://*', - '--external', 'file://*', - ]); - await import(`file://${main}`); -} catch (error) { - console.error(error); - App.quit(); -} diff --git a/example/ts-starter-config/main.ts b/example/ts-starter-config/main.ts deleted file mode 100644 index f5a29a43..00000000 --- a/example/ts-starter-config/main.ts +++ /dev/null @@ -1,26 +0,0 @@ -const time = Variable('', { - poll: [1000, function() { - return Date().toString(); - }], -}); - -const Bar = (monitor: number) => Widget.Window({ - monitor, - name: `bar${monitor}`, - anchor: ['top', 'left', 'right'], - exclusivity: 'exclusive', - child: Widget.CenterBox({ - start_widget: Widget.Label({ - hpack: 'center', - label: 'Welcome to AGS!', - }), - end_widget: Widget.Label({ - hpack: 'center', - label: time.bind(), - }), - }), -}); - -App.config({ - windows: [Bar(0)], -}); diff --git a/flake.lock b/flake.lock index bb5d5cd8..38259974 100644 --- a/flake.lock +++ b/flake.lock @@ -1,16 +1,36 @@ { "nodes": { + "astal": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1730839135, + "narHash": "sha256-qAOABzoZxVokih2t/Tfn+DFdEZPzmjSwDUvlrBAJ6/4=", + "owner": "aylur", + "repo": "astal", + "rev": "684dd1c7313c40777bc3c63391d7e27659793af6", + "type": "github" + }, + "original": { + "owner": "aylur", + "repo": "astal", + "type": "github" + } + }, "nixpkgs": { "locked": { - "lastModified": 1718714799, - "narHash": "sha256-FUZpz9rg3gL8NVPKbqU8ei1VkPLsTIfAJ2fdAf5qjak=", - "owner": "NixOS", + "lastModified": 1730531603, + "narHash": "sha256-Dqg6si5CqIzm87sp57j5nTaeBbWhHFaVyG7V6L8k3lY=", + "owner": "nixos", "repo": "nixpkgs", - "rev": "c00d587b1a1afbf200b1d8f0b0e4ba9deb1c7f0e", + "rev": "7ffd9ae656aec493492b44d0ddfb28e79a1ea25d", "type": "github" }, "original": { - "owner": "NixOS", + "owner": "nixos", "ref": "nixos-unstable", "repo": "nixpkgs", "type": "github" @@ -18,6 +38,7 @@ }, "root": { "inputs": { + "astal": "astal", "nixpkgs": "nixpkgs" } } diff --git a/flake.nix b/flake.nix index aaca0c42..9dd4fc2b 100644 --- a/flake.nix +++ b/flake.nix @@ -1,31 +1,57 @@ { - description = "A customizable and extensible shell"; - inputs = { - nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; + + astal = { + url = "github:aylur/astal"; + inputs.nixpkgs.follows = "nixpkgs"; + }; }; outputs = { - nixpkgs, self, + nixpkgs, + astal, }: let - version = builtins.replaceStrings ["\n"] [""] (builtins.readFile ./version); - genSystems = nixpkgs.lib.genAttrs [ - "aarch64-linux" - "x86_64-linux" - ]; - pkgs = genSystems (system: import nixpkgs {inherit system;}); - in { - packages = genSystems (system: rec { - default = pkgs.${system}.callPackage ./nix {inherit version;}; - ags = default; - agsWithTypes = default; # for backwards compatibility - agsNoTypes = pkgs.${system}.callPackage ./nix { - inherit version; - buildTypes = false; + inherit (astal.packages.${system}) astal3 astal4 io gjs; + + system = "x86_64-linux"; # TODO: other architectures + pkgs = nixpkgs.legacyPackages.x86_64-linux; + + astal-io = io; + astal-gjs = "${gjs}/share/astal/gjs"; + + agsPackages = { + default = self.packages.${system}.ags; + ags = pkgs.callPackage ./nix { + inherit astal3 astal4 astal-io astal-gjs; + }; + agsFull = pkgs.callPackage ./nix { + inherit astal3 astal4 astal-io astal-gjs; + extraPackages = builtins.attrValues ( + builtins.removeAttrs astal.packages.${system} ["docs"] + ); }; - }); + }; + in { + lib.bundle = import ./nix/bundle.nix {inherit self pkgs;}; + + packages.${system} = astal.packages.${system} // agsPackages; + + templates.default = { + path = ./nix/template; + description = "Example flake.nix that shows how to package a project."; + welcomeText = '' + # Getting Started + - run `nix develop` to enter the development environment + - run `ags init . -f` to setup an initial ags project + - run `ags run .` to run the project + ''; + }; - homeManagerModules.default = import ./nix/hm-module.nix self; + homeManagerModules = { + default = self.homeManagerModules.ags; + ags = import ./nix/hm-module.nix self; + }; }; } diff --git a/go.mod b/go.mod new file mode 100644 index 00000000..ed25d418 --- /dev/null +++ b/go.mod @@ -0,0 +1,15 @@ +module ags + +go 1.22.2 + +require ( + github.com/evanw/esbuild v0.24.0 + github.com/spf13/cobra v1.8.1 +) + +require ( + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/titanous/json5 v1.0.0 // indirect + golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 00000000..96075725 --- /dev/null +++ b/go.sum @@ -0,0 +1,16 @@ +github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/evanw/esbuild v0.24.0 h1:GZ78naTLp7FKr+K7eNuM/SLs5maeiHYRPsTg6kmdsSE= +github.com/evanw/esbuild v0.24.0/go.mod h1:D2vIQZqV/vIf/VRHtViaUtViZmG7o+kKmlBfVQuRi48= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= +github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/titanous/json5 v1.0.0 h1:hJf8Su1d9NuI/ffpxgxQfxh/UiBFZX7bMPid0rIL/7s= +github.com/titanous/json5 v1.0.0/go.mod h1:7JH1M8/LHKc6cyP5o5g3CSaRj+mBrIimTxzpvmckH8c= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/lib/array.go b/lib/array.go new file mode 100644 index 00000000..096d09d8 --- /dev/null +++ b/lib/array.go @@ -0,0 +1,18 @@ +package lib + +func Map[T any, R any](input []T, fn func(T) R) []R { + result := make([]R, len(input)) + for i, v := range input { + result[i] = fn(v) + } + return result +} + +func Some[T any](input []T, fn func(T) bool) bool { + for _, v := range input { + if fn(v) { + return true + } + } + return false +} diff --git a/lib/astal.go b/lib/astal.go new file mode 100644 index 00000000..475b18c2 --- /dev/null +++ b/lib/astal.go @@ -0,0 +1,23 @@ +package lib + +import ( + "os" + + "github.com/spf13/cobra" +) + +// TODO: reimplement using DBus directly +func Astal(args ...string) { + cmd := Exec("astal", args...) + cmd.Stderr = os.Stderr + cmd.Stdout = os.Stdout + if err := cmd.Run(); err != nil { + os.Exit(1) + } +} + +var instance string + +func AddInstanceFlag(cmd *cobra.Command) { + cmd.Flags().StringVarP(&instance, "instance", "i", "astal", "name of the instance") +} diff --git a/lib/esbuild.go b/lib/esbuild.go new file mode 100644 index 00000000..379c59fc --- /dev/null +++ b/lib/esbuild.go @@ -0,0 +1,202 @@ +package lib + +import ( + "fmt" + "os" + "path" + "path/filepath" + "strings" + + "github.com/evanw/esbuild/pkg/api" +) + +var inlinePlugin api.Plugin = api.Plugin{ + Name: "inline", + Setup: func(build api.PluginBuild) { + build.OnResolve(api.OnResolveOptions{Filter: "^inline:"}, + func(args api.OnResolveArgs) (api.OnResolveResult, error) { + resolved := path.Join( + args.ResolveDir, + strings.Replace(args.Path, "inline:", "", 1), + ) + + return api.OnResolveResult{ + Path: resolved, + Namespace: "inline", + }, nil + }, + ) + + build.OnLoad(api.OnLoadOptions{Filter: ".*", Namespace: "inline"}, + func(args api.OnLoadArgs) (api.OnLoadResult, error) { + data, err := os.ReadFile(args.Path) + if err != nil { + return api.OnLoadResult{}, err + } + + content := string(data) + + return api.OnLoadResult{ + Contents: &content, + WatchFiles: []string{args.Path}, + Loader: api.LoaderText, + }, nil + }) + }, +} + +var sassPlugin api.Plugin = api.Plugin{ + Name: "sass", + Setup: func(build api.PluginBuild) { + build.OnResolve(api.OnResolveOptions{Filter: `.*\.scss$`}, + func(args api.OnResolveArgs) (api.OnResolveResult, error) { + return api.OnResolveResult{ + Path: path.Join( + args.ResolveDir, + args.Path, + ), + Namespace: "sass", + }, nil + }, + ) + + build.OnLoad(api.OnLoadOptions{Filter: `.*\.scss$`, Namespace: "sass"}, + func(args api.OnLoadArgs) (api.OnLoadResult, error) { + sass := Exec("sass", args.Path, "-I", filepath.Dir(args.Path)) + // if cwd is the path of the currently loaded file sass warns about it + // in order to avoid the deprecation warning we explicitly set it to something else + sass.Dir = "/" + sass.Stderr = os.Stderr + + data, err := sass.Output() + if err != nil { + return api.OnLoadResult{}, err + } + + content := strings.TrimSpace(string(data)) + return api.OnLoadResult{ + Contents: &content, + WatchFiles: []string{args.Path}, + Loader: api.LoaderText, + }, nil + }) + }, +} + +var blpPlugin api.Plugin = api.Plugin{ + Name: "blueprint", + Setup: func(build api.PluginBuild) { + build.OnResolve(api.OnResolveOptions{Filter: `.*\.blp$`}, + func(args api.OnResolveArgs) (api.OnResolveResult, error) { + return api.OnResolveResult{ + Path: path.Join( + args.ResolveDir, + args.Path, + ), + Namespace: "blueprint", + }, nil + }, + ) + + build.OnLoad(api.OnLoadOptions{Filter: `.*\.blp$`, Namespace: "blueprint"}, + func(args api.OnLoadArgs) (api.OnLoadResult, error) { + blp := Exec("blueprint-compiler", "compile", args.Path) + blp.Stderr = os.Stderr + + data, err := blp.Output() + if err != nil { + return api.OnLoadResult{}, err + } + + content := strings.TrimSpace(string(data)) + return api.OnLoadResult{ + Contents: &content, + WatchFiles: []string{args.Path}, + Loader: api.LoaderText, + }, nil + }) + }, +} + +// TODO: bundle plugins +// svg loader +// other css preproceccors +// http plugin with caching +func Bundle(infile, outfile, tsconfig, cwd string) { + srcdir := filepath.Dir(infile) + + if cwd == "" { + cwd = srcdir + } else { + var err error + cwd, err = filepath.Abs(cwd) + if err != nil { + Err(err) + } + } + + if tsconfig != "" { + // if a tsconfig file is specified use that + path, err := filepath.Abs(tsconfig) + if err != nil { + Err(err) + } + + data, err := os.ReadFile(path) + if err != nil { + Err(err) + } + + tsconfig = string(data) + } else if FileExists("tsconfig.json") { + // check cwd for tsconfig + cwd, err := os.Getwd() + if err != nil { + Err(err) + } + + tsconfig = GetTsconfig(cwd) + } else { + // fallback to default + tsconfig = GetTsconfig(srcdir) + } + + result := api.Build(api.BuildOptions{ + Color: api.ColorAlways, + LogLevel: api.LogLevelWarning, + EntryPoints: []string{infile}, + Bundle: true, + Outfile: outfile, + Format: api.FormatESModule, + Platform: api.PlatformNeutral, + AbsWorkingDir: cwd, + TsconfigRaw: tsconfig, + Write: true, + Define: map[string]string{ + "SRC": fmt.Sprintf(`"%s"`, srcdir), + }, + Loader: map[string]api.Loader{ + ".js": api.LoaderJSX, + ".css": api.LoaderText, + }, + External: []string{ + "console", + "system", + "cairo", + "gettext", + "file://*", + "gi://*", + "resource://*", + }, + Plugins: []api.Plugin{ + inlinePlugin, + sassPlugin, + blpPlugin, + }, + }) + + // TODO: custom error logs + if len(result.Errors) > 0 { + os.Exit(1) + } +} diff --git a/lib/os.go b/lib/os.go new file mode 100644 index 00000000..706b98ee --- /dev/null +++ b/lib/os.go @@ -0,0 +1,77 @@ +package lib + +import ( + "fmt" + "os" + "os/exec" +) + +var r = "\x1b[0m" + +func Mkdir(path string) { + _, err := os.Stat(path) + if os.IsNotExist(err) { + err := os.MkdirAll(path, os.ModePerm) + if err != nil { + Err(err) + } + } +} + +func FileExists(filename string) bool { + _, err := os.Stat(filename) + return !os.IsNotExist(err) +} + +func WriteFile(path string, content string) { + err := os.WriteFile(path, []byte(content), 0644) + if err != nil { + Err(err) + } +} + +func Exec(cmd string, args ...string) *exec.Cmd { + _, err := exec.LookPath(cmd) + if err != nil { + Err(`executable "` + Magenta(cmd) + `" not found in $PATH`) + } + return exec.Command(cmd, args...) +} + +func Invert(str string) string { + return "\x1b[7m" + str + r +} + +func Red(str string) string { + return "\x1b[31m" + str + r +} + +func Green(str string) string { + return "\x1b[32m" + str + r +} + +func Yellow(str string) string { + return "\x1b[33m" + str + r +} + +func Blue(str string) string { + return "\x1b[34m" + str + r +} + +func Magenta(str string) string { + return "\x1b[35m" + str + r +} + +func Cyan(str string) string { + return "\x1b[36m" + str + r +} + +func Err(err any) { + switch v := err.(type) { + case string: + fmt.Fprintln(os.Stderr, Red("error: ")+v) + case error: + fmt.Fprintln(os.Stderr, Red("error: ")+v.Error()) + } + os.Exit(1) +} diff --git a/lib/tsconfig.go b/lib/tsconfig.go new file mode 100644 index 00000000..5909e1a1 --- /dev/null +++ b/lib/tsconfig.go @@ -0,0 +1,102 @@ +package lib + +import ( + "encoding/json" + "os" + "strings" + + "github.com/titanous/json5" +) + +var ( + astalGjs string + defaultGtkVersion = "gtk3" +) + +func Initialize(_astalGjs string) { + astalGjs = _astalGjs +} + +func defaultTsconfig() map[string]interface{} { + return map[string]interface{}{ + "compilerOptions": map[string]interface{}{ + "experimentalDecorators": true, + "module": "ES2022", + "target": "ES2022", + "moduleResolution": "Bundler", + "jsx": "react-jsx", + "jsxImportSource": astalGjs + "/" + defaultGtkVersion, + "paths": map[string][]string{ + "astal": {astalGjs}, + "astal/*": {astalGjs + "/*"}, + }, + }, + } +} + +func updateTsconfig(tsconfig map[string]interface{}) { + opts, ok := tsconfig["compilerOptions"].(map[string]interface{}) + if !ok { + opts = map[string]interface{}{} + } + + src, ok := opts["jsxImportSource"].(string) + if !ok { + src = "/gtk3" + } + + var gtk string + if strings.HasSuffix(src, "gtk4") { + gtk = "/gtk4" + } else { + gtk = "/gtk3" + } + + opts["experimentalDecorators"] = true + opts["module"] = "ES2022" + opts["target"] = "ES2022" + opts["moduleResolution"] = "Bundler" + opts["jsx"] = "react-jsx" + opts["jsxImportSource"] = astalGjs + gtk + + paths, ok := opts["paths"].(map[string]interface{}) + if !ok { + paths = map[string]interface{}{ + "astal": []string{astalGjs}, + "astal/*": []string{astalGjs + "/*"}, + } + } + + paths["astal"] = []string{astalGjs} + paths["astal/*"] = []string{astalGjs + "/*"} +} + +// if tsconfig.json exists in srcdir returns an updated config +// otherwise returns a default config +func GetTsconfig(srcdir string) string { + path := srcdir + "/tsconfig.json" + + var tsconfig map[string]interface{} + if FileExists(path) { + data, err := os.ReadFile(path) + if err != nil { + Err(err) + } + + err = json5.Unmarshal(data, &tsconfig) + if err != nil { + Err(err) + } + + updateTsconfig(tsconfig) + } else { + tsconfig = defaultTsconfig() + } + + conf, err := json.MarshalIndent(tsconfig, "", " ") + if err != nil { + Err(err) + } + + return string(conf) +} diff --git a/main.go b/main.go new file mode 100644 index 00000000..83ee1fb1 --- /dev/null +++ b/main.go @@ -0,0 +1,32 @@ +package main + +import ( + "ags/cmd" + "ags/lib" + "embed" +) + +var ( + //go:embed version + version string + + //go:embed data + data embed.FS + + // should be overriden with -ldflags + gtk4LayerShell = "/usr/lib/libgtk4-layer-shell.so" + astalGjs = "/usr/share/astal/gjs" + tsForGir = "@ts-for-gir/cli@4.0.0-beta.15" +) + +func main() { + lib.Initialize(astalGjs) + cmd.Initialize(cmd.Variables{ + Version: version, + Data: data, + Gtk4LayerShell: gtk4LayerShell, + AstalGjs: astalGjs, + TsForGir: tsForGir, + }) + cmd.Execute() +} diff --git a/meson.build b/meson.build deleted file mode 100644 index d5ad6f2e..00000000 --- a/meson.build +++ /dev/null @@ -1,50 +0,0 @@ -project('ags', 'c', - version: run_command('cat', join_paths(meson.project_source_root(), 'version')).stdout().strip(), - meson_version: '>= 0.62.0', - license: ['GPL-3.0-or-later'], - default_options: [ 'warning_level=2', 'werror=false', ], -) - -app_id = 'com.github.Aylur.ags' -prefix = get_option('prefix') -libdir = join_paths(prefix, get_option('libdir')) -extensiondir = join_paths(libdir, meson.project_name()) -datadir = join_paths(prefix, get_option('datadir')) -pkgdatadir = join_paths(datadir, app_id) -bindir = join_paths(prefix, get_option('bindir')) - -dependency('glib-2.0') -dependency('gobject-introspection-1.0', version: '>= 1.49.1') -dependency('gio-2.0', version: '>= 2.56.0') -dependency('gjs-1.0', version: '>= 1.73.1') -dependency('gtk+-3.0', version: '>= 3.0') - -subproject('gvc', - default_options: [ - 'package_name=' + app_id, - 'pkgdatadir=' + pkgdatadir, - 'pkglibdir=' + libdir, - 'static=false', - 'introspection=true', - 'alsa=false' - ] -) - -subproject('gutils', - default_options: [ - 'pkgdatadir=' + pkgdatadir, - 'pkglibdir=' + libdir, - ] -) - -subdir('src') - -meson.add_install_script( - 'post_install.sh', - datadir, - pkgdatadir, - bindir, - app_id, - get_option('build_types').to_string(), - meson.project_source_root() -) diff --git a/meson_options.txt b/meson_options.txt deleted file mode 100644 index b691359e..00000000 --- a/meson_options.txt +++ /dev/null @@ -1 +0,0 @@ -option('build_types', type: 'boolean', value: true) diff --git a/nix/bundle.nix b/nix/bundle.nix new file mode 100644 index 00000000..15e02069 --- /dev/null +++ b/nix/bundle.nix @@ -0,0 +1,58 @@ +{ + self, + pkgs, +}: { + pkgs ? pkgs, + entry ? "app.ts", + src, + name, + extraPackages ? [], +}: +pkgs.stdenvNoCC.mkDerivation { + inherit src name; + + nativeBuildInputs = with pkgs; [ + wrapGAppsHook + gobject-introspection + gnused + self.packages.${system}.ags + ]; + + buildInputs = + extraPackages + ++ [ + pkgs.gjs + self.packages.${pkgs.system}.astal4 + self.packages.${pkgs.system}.astal3 + self.packages.${pkgs.system}.io + ]; + + preFixup = '' + gappsWrapperArgs+=( + --prefix PATH : ${with pkgs; + lib.makeBinPath (extraPackages + ++ [ + dart-sass + fzf + gtk3 + ])} + ) + ''; + + installPhase = '' + runHook preInstall + + mkdir -p $out/bin + mkdir -p $out/share + cp -r * $out/share + ags bundle ${entry} $out/bin/${name} --src $out/share + + chmod +x $out/bin/${name} + + if ! head -n 1 "$out/bin/${name}" | grep -q "^#!"; then + sed -i '1i #!/${pkgs.gjs}/bin/gjs -m' "$out/bin/${name}" + fi + + runHook postInstall + ''; +} diff --git a/nix/default.nix b/nix/default.nix index f20f04d7..dd47d32f 100644 --- a/nix/default.nix +++ b/nix/default.nix @@ -1,104 +1,75 @@ -{ lib -, stdenv -, buildNpmPackage -, fetchFromGitLab -, nodePackages -, meson -, pkg-config -, ninja -, gobject-introspection -, gtk3 -, libpulseaudio -, gjs -, wrapGAppsHook -, upower -, gnome -, gtk-layer-shell -, glib-networking -, networkmanager -, libdbusmenu-gtk3 -, gvfs -, libsoup_3 -, libnotify -, pam -, extraPackages ? [ ] -, version ? "git" -, buildTypes ? true -}: +{ + astal3, + astal4, + gtk4-layer-shell, + astal-io, + astal-gjs, + lib, + writers, + buildGoModule, + wrapGAppsHook, + gobject-introspection, + glib, + gjs, + nodejs, + dart-sass, + blueprint-compiler, + extraPackages ? [], +}: let + inherit (builtins) replaceStrings readFile; -let - gvc-src = fetchFromGitLab { - domain = "gitlab.gnome.org"; - owner = "GNOME"; - repo = "libgnome-volume-control"; - rev = "8e7a5a4c3e51007ce6579292642517e3d3eb9c50"; - sha256 = "sha256-FosJwgTCp6/EI6WVbJhPisokRBA6oT0eo7d+Ya7fFX8="; - }; -in -stdenv.mkDerivation rec { - pname = "ags"; - inherit version; - - src = buildNpmPackage { - name = pname; - src = lib.cleanSource ../.; - - dontBuild = true; - - npmDepsHash = "sha256-ucWdADdMqAdLXQYKGOXHNRNM9bhjKX4vkMcQ8q/GZ20="; - - installPhase = '' - mkdir $out - cp -r * $out - ''; - }; + datadirs = writers.writeNu "datadirs" '' + $env.XDG_DATA_DIRS + | split row ":" + | filter { $"($in)/gir-1.0" | path exists } + | str join ":" + ''; - mesonFlags = [ - "-Dbuild_types=${if buildTypes then "true" else "false"}" + bins = [ + gjs + nodejs + dart-sass + blueprint-compiler + astal-io # FIXME: should not be needed after the astal commends are properly implemented using dbus in astal.go ]; - prePatch = '' - mkdir -p ./subprojects/gvc - cp -r ${gvc-src}/* ./subprojects/gvc - ''; + version = replaceStrings ["\n"] [""] (readFile ../version); + pname = "ags"; +in + buildGoModule { + inherit pname version; - postPatch = '' - chmod +x post_install.sh - patchShebangs post_install.sh - ''; + src = builtins.path { + name = "${pname}-${version}"; + path = lib.cleanSource ../.; + }; - nativeBuildInputs = [ - pkg-config - meson - ninja - nodePackages.typescript - wrapGAppsHook - gobject-introspection - ]; + vendorHash = "sha256-Pw6UNT5YkDVz4HcH7b5LfOg+K3ohrBGPGB9wYGAQ9F4="; + proxyVendor = true; - buildInputs = [ - gjs - gtk3 - libpulseaudio - upower - gnome.gnome-bluetooth - gtk-layer-shell - glib-networking - networkmanager - libdbusmenu-gtk3 - gvfs - libsoup_3 - libnotify - pam - ] ++ extraPackages; + nativeBuildInputs = [ + wrapGAppsHook + gobject-introspection + ]; - outputs = [ "out" "lib" ]; + buildInputs = + extraPackages + ++ [ + glib + astal-io + astal3 + astal4 + ]; + + preFixup = '' + gappsWrapperArgs+=( + --prefix NIX_GI_DIRS : "$(${datadirs})" + --prefix PATH : "${lib.makeBinPath (bins ++ extraPackages)}" + ) + ''; - meta = with lib; { - description = "A customizable and extensible shell"; - homepage = "https://github.com/Aylur/ags"; - platforms = [ "x86_64-linux" "aarch64-linux" ]; - license = licenses.gpl3; - meta.maintainers = [ lib.maintainers.Aylur ]; - }; -} + ldflags = [ + "-X main.astalGjs=${astal-gjs}" + "-X main.gtk4LayerShell=${gtk4-layer-shell}/lib/libgtk4-layer-shell.so" + ]; + } diff --git a/nix/hm-module.nix b/nix/hm-module.nix index c319f448..90934be3 100644 --- a/nix/hm-module.nix +++ b/nix/hm-module.nix @@ -8,15 +8,20 @@ self: { inherit (lib.modules) mkIf; inherit (lib.options) mkOption mkEnableOption literalExpression; - defaultAgsPackage = self.packages.${pkgs.stdenv.hostPlatform.system}.default; cfg = config.programs.ags; + default = { + agsPackage = self.packages.${pkgs.stdenv.hostPlatform.system}.default; + gtk3Package = self.packages.${pkgs.stdenv.hostPlatform.system}.astal3; + ioPackage = self.packages.${pkgs.stdenv.hostPlatform.system}.io; + gjsPackage = self.packages.${pkgs.stdenv.hostPlatform.system}.gjs; + }; in { options.programs.ags = { enable = mkEnableOption "ags"; package = mkOption { - type = with types; nullOr package; - default = defaultAgsPackage; + type = types.package; + default = default.agsPackage; defaultText = literalExpression "inputs.ags.packages.${pkgs.stdenv.hostPlatform.system}.default"; description = '' The Ags package to use. @@ -25,6 +30,38 @@ in { ''; }; + astal.gtk3Package = mkOption { + type = types.package; + default = default.gtk3Package; + defaultText = literalExpression "inputs.ags.packages.${pkgs.stdenv.hostPlatform.system}.astal3"; + description = '' + The GTK3 Astal package to use. + + By default, this option will use the `packages.astal3` as exposed by this flake. + ''; + }; + + astal.ioPackage = mkOption { + type = types.package; + default = default.ioPackage; + defaultText = literalExpression "inputs.ags.packages.${pkgs.stdenv.hostPlatform.system}.io"; + description = '' + The core Astal package to use. + + By default, this option will use the `packages.io` as exposed by this flake. + ''; + }; + + astal.gjsPackage = mkOption { + type = types.package; + default = default.gjsPackage; + description = '' + The Astal Gjs package to use. + + By default, this option will use the `packages.gjs` as exposed by this flake. + ''; + }; + finalPackage = mkOption { type = types.package; readOnly = true; @@ -51,22 +88,52 @@ in { ''; example = literalExpression "[ pkgs.libsoup_3 ]"; }; + + systemd.enable = mkOption { + type = types.bool; + default = false; + example = true; + description = '' + Enable systemd integration. + ''; + }; }; config = mkIf cfg.enable (mkMerge [ (mkIf (cfg.configDir != null) { xdg.configFile."ags".source = cfg.configDir; }) - (mkIf (cfg.package != null) (let - path = "/share/com.github.Aylur.ags/types"; + (let pkg = cfg.package.override { extraPackages = cfg.extraPackages; - buildTypes = true; + astal3 = cfg.astal.gtk3Package; + astal-io = cfg.astal.ioPackage; + astal-gjs = "${config.home.homeDirectory}/.local/share/ags"; }; in { programs.ags.finalPackage = pkg; home.packages = [pkg]; - home.file.".local/${path}".source = "${pkg}/${path}"; - })) + home.file.".local/share/ags".source = "${cfg.astal.gjsPackage}/share/astal/gjs"; + }) + (mkIf cfg.systemd.enable { + systemd.user.services.ags = { + Unit = { + Description = "AGS - Tool for scaffolding Astal+TypeScript projects."; + Documentation = "https://github.com/Aylur/ags"; + PartOf = ["graphical-session.target"]; + After = ["graphical-session-pre.target"]; + }; + + Service = { + ExecStart = "${cfg.finalPackage}/bin/ags"; + Restart = "on-failure"; + KillMode = "mixed"; + }; + + Install = { + WantedBy = ["graphical-session.target"]; + }; + }; + }) ]); } diff --git a/nix/template/flake.nix b/nix/template/flake.nix new file mode 100644 index 00000000..eb8b6638 --- /dev/null +++ b/nix/template/flake.nix @@ -0,0 +1,52 @@ +{ + description = "My Awesome Desktop Shell"; + + inputs = { + nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable"; + + ags = { + url = "github:aylur/ags"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + }; + + outputs = { + self, + nixpkgs, + ags, + }: let + system = "x86_64-linux"; + pkgs = nixpkgs.legacyPackages.${system}; + in { + packages.${system} = { + default = ags.lib.bundle { + inherit pkgs; + src = ./.; + name = "my-shell"; + entry = "app.ts"; + + # additional libraries and executables to add to gjs' runtime + extraPackages = [ + # ags.packages.${system}.battery + # pkgs.fzf + ]; + }; + }; + + devShells.${system} = { + default = pkgs.mkShell { + buildInputs = [ + # includes all Astal libraries + # ags.packages.${system}.agsFull + + # includes astal3 astal4 astal-io by default + (ags.packages.${system}.default.overrideAttrs { + extraPackages = [ + # cherry pick packages + ]; + }) + ]; + }; + }; + }; +} diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index c5c818b6..00000000 --- a/package-lock.json +++ /dev/null @@ -1,2027 +0,0 @@ -{ - "name": "ags", - "version": "1.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "ags", - "version": "1.0.0", - "license": "GPL", - "dependencies": { - "typescript": "^5.1.0" - }, - "devDependencies": { - "@girs/cairo-1.0": "^1.0.0-3.2.7", - "@girs/dbusmenugtk3-0.4": "^0.4.0-3.2.0", - "@girs/gjs": "^3.2.7", - "@girs/gtk-3.0": "^3.24.39-3.2.2", - "@girs/gvc-1.0": "^1.0.0-3.1.0", - "@girs/nm-1.0": "^1.43.1-3.1.0", - "@girs/notify-0.7": "^0.8.3-3.2.7", - "@girs/soup-3.0": "^3.4.4-3.2.6", - "@typescript-eslint/eslint-plugin": "^5.33.0", - "@typescript-eslint/parser": "^5.33.0", - "eslint": "^8.42.0" - } - }, - "node_modules/@aashutoshrathi/word-wrap": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", - "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", - "dev": true, - "dependencies": { - "eslint-visitor-keys": "^3.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" - } - }, - "node_modules/@eslint-community/regexpp": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", - "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", - "dev": true, - "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" - } - }, - "node_modules/@eslint/eslintrc": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", - "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", - "dev": true, - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.6.0", - "globals": "^13.19.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" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint/js": { - "version": "8.56.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.56.0.tgz", - "integrity": "sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/@girs/atk-1.0": { - "version": "2.50.0-3.2.7", - "resolved": "https://registry.npmjs.org/@girs/atk-1.0/-/atk-1.0-2.50.0-3.2.7.tgz", - "integrity": "sha512-4HBiLdINigopQksxhZCToRHPh2oUjL32ogZ/JzHqrxB+SMF3wSNS2nuyRI/RRhx0wDIrq+8H9PgQ1gzsbhxdWg==", - "dev": true, - "dependencies": { - "@girs/gjs": "^3.2.7", - "@girs/glib-2.0": "^2.78.0-3.2.7", - "@girs/gobject-2.0": "^2.78.0-3.2.7" - } - }, - "node_modules/@girs/cairo-1.0": { - "version": "1.0.0-3.2.7", - "resolved": "https://registry.npmjs.org/@girs/cairo-1.0/-/cairo-1.0-1.0.0-3.2.7.tgz", - "integrity": "sha512-WeAyTY8tUoVe8W1IDbxOYDczWyUotg/7sjzWPv5TXQJJAe3vZ6TfkYPSIlnQHkQ43XfWhSa0JA2s5Oitttdu0g==", - "dev": true, - "dependencies": { - "@girs/gjs": "^3.2.7", - "@girs/glib-2.0": "^2.78.0-3.2.7", - "@girs/gobject-2.0": "^2.78.0-3.2.7" - } - }, - "node_modules/@girs/dbusmenu-0.4": { - "version": "0.4.0-3.2.7", - "resolved": "https://registry.npmjs.org/@girs/dbusmenu-0.4/-/dbusmenu-0.4-0.4.0-3.2.7.tgz", - "integrity": "sha512-an1dfWFolB7hLvjiiaS0P9z2FcjylCiGrn3KpAr/h3AKX64ayVjYCiuVpGv/VYBrO8XjfruYKVah2jRM8qrIEQ==", - "dev": true, - "dependencies": { - "@girs/gjs": "^3.2.7", - "@girs/glib-2.0": "^2.78.0-3.2.7", - "@girs/gobject-2.0": "^2.78.0-3.2.7" - } - }, - "node_modules/@girs/dbusmenugtk3-0.4": { - "version": "0.4.0-3.2.7", - "resolved": "https://registry.npmjs.org/@girs/dbusmenugtk3-0.4/-/dbusmenugtk3-0.4-0.4.0-3.2.7.tgz", - "integrity": "sha512-0R2j3/nyaZmzV2IF5T0jZZFiiFGiZhS24To3ameYv2w3Ez75pcuCg2JlNbO+V3CdOsCUNmAZMowCN5HL8zavMw==", - "dev": true, - "dependencies": { - "@girs/atk-1.0": "^2.50.0-3.2.7", - "@girs/cairo-1.0": "^1.0.0-3.2.7", - "@girs/dbusmenu-0.4": "^0.4.0-3.2.7", - "@girs/freetype2-2.0": "^2.0.0-3.2.7", - "@girs/gdk-3.0": "^3.24.38-3.2.7", - "@girs/gdkpixbuf-2.0": "^2.0.0-3.2.7", - "@girs/gio-2.0": "^2.78.0-3.2.7", - "@girs/gjs": "^3.2.7", - "@girs/glib-2.0": "^2.78.0-3.2.7", - "@girs/gmodule-2.0": "^2.0.0-3.2.7", - "@girs/gobject-2.0": "^2.78.0-3.2.7", - "@girs/gtk-3.0": "^3.24.38-3.2.7", - "@girs/harfbuzz-0.0": "^8.2.1-3.2.7", - "@girs/pango-1.0": "^1.51.0-3.2.7", - "@girs/xlib-2.0": "^2.0.0-3.2.7" - } - }, - "node_modules/@girs/dbusmenugtk3-0.4/node_modules/@girs/gtk-3.0": { - "version": "3.24.38-3.2.7", - "resolved": "https://registry.npmjs.org/@girs/gtk-3.0/-/gtk-3.0-3.24.38-3.2.7.tgz", - "integrity": "sha512-30abOOkD9YzURYgcKLlMs4auNgYbp8SxH5JdKfOmV0v8P5jEO+0rV8riJZn7bAQKIrl8Y5fGvqwx/yl/UVpVig==", - "dev": true, - "dependencies": { - "@girs/atk-1.0": "^2.50.0-3.2.7", - "@girs/cairo-1.0": "^1.0.0-3.2.7", - "@girs/freetype2-2.0": "^2.0.0-3.2.7", - "@girs/gdk-3.0": "^3.24.38-3.2.7", - "@girs/gdkpixbuf-2.0": "^2.0.0-3.2.7", - "@girs/gio-2.0": "^2.78.0-3.2.7", - "@girs/gjs": "^3.2.7", - "@girs/glib-2.0": "^2.78.0-3.2.7", - "@girs/gmodule-2.0": "^2.0.0-3.2.7", - "@girs/gobject-2.0": "^2.78.0-3.2.7", - "@girs/harfbuzz-0.0": "^8.2.1-3.2.7", - "@girs/pango-1.0": "^1.51.0-3.2.7", - "@girs/xlib-2.0": "^2.0.0-3.2.7" - } - }, - "node_modules/@girs/freetype2-2.0": { - "version": "2.0.0-3.2.7", - "resolved": "https://registry.npmjs.org/@girs/freetype2-2.0/-/freetype2-2.0-2.0.0-3.2.7.tgz", - "integrity": "sha512-oapcm7uCVWyL9N7VHuBOxbCWTa9QoF1EX6DFAwpGK4gwVrkjedZ4kZuxtza1gsZrkmYlWNK66H7YShFHgUWdPw==", - "dev": true, - "dependencies": { - "@girs/gjs": "^3.2.7", - "@girs/gobject-2.0": "^2.78.0-3.2.7" - } - }, - "node_modules/@girs/gdk-3.0": { - "version": "3.24.38-3.2.7", - "resolved": "https://registry.npmjs.org/@girs/gdk-3.0/-/gdk-3.0-3.24.38-3.2.7.tgz", - "integrity": "sha512-WEQji730jgaNbQh3g0Vhuq6TvZz16ysgt+FAuNHB2I+9lKVbb8xBNg5VOe2uqRFSKQ4/Rt+RN0U36rFFyo8M6A==", - "dev": true, - "dependencies": { - "@girs/cairo-1.0": "^1.0.0-3.2.7", - "@girs/freetype2-2.0": "^2.0.0-3.2.7", - "@girs/gdkpixbuf-2.0": "^2.0.0-3.2.7", - "@girs/gio-2.0": "^2.78.0-3.2.7", - "@girs/gjs": "^3.2.7", - "@girs/glib-2.0": "^2.78.0-3.2.7", - "@girs/gmodule-2.0": "^2.0.0-3.2.7", - "@girs/gobject-2.0": "^2.78.0-3.2.7", - "@girs/harfbuzz-0.0": "^8.2.1-3.2.7", - "@girs/pango-1.0": "^1.51.0-3.2.7" - } - }, - "node_modules/@girs/gdkpixbuf-2.0": { - "version": "2.0.0-3.2.7", - "resolved": "https://registry.npmjs.org/@girs/gdkpixbuf-2.0/-/gdkpixbuf-2.0-2.0.0-3.2.7.tgz", - "integrity": "sha512-/POHLvRlJ6QYO8Wkrj2PDQT2lYh1vJFOHcfWd/yhtr7P3WWKGqOEG5NJAg81bMG8UIZOrc00HALXfCgbAalKGg==", - "dev": true, - "dependencies": { - "@girs/gio-2.0": "^2.78.0-3.2.7", - "@girs/gjs": "^3.2.7", - "@girs/glib-2.0": "^2.78.0-3.2.7", - "@girs/gmodule-2.0": "^2.0.0-3.2.7", - "@girs/gobject-2.0": "^2.78.0-3.2.7" - } - }, - "node_modules/@girs/gio-2.0": { - "version": "2.78.0-3.2.7", - "resolved": "https://registry.npmjs.org/@girs/gio-2.0/-/gio-2.0-2.78.0-3.2.7.tgz", - "integrity": "sha512-88GNniBwxAsXrfA+T1wNUxoQk8HivDZUVq1KF3ZsomrQyRDcwCgQLcLyC6c5ZAkg6xwnHZmTIWoYgITiee/lLA==", - "dev": true, - "dependencies": { - "@girs/gjs": "^3.2.7", - "@girs/glib-2.0": "^2.78.0-3.2.7", - "@girs/gobject-2.0": "^2.78.0-3.2.7" - } - }, - "node_modules/@girs/gjs": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/@girs/gjs/-/gjs-3.2.7.tgz", - "integrity": "sha512-gaZLkffVm2e5zvyyGNcDgFgyL5WaHZWUr0XBLtuKEM17/qA4udZQRif7HkW//PG8T2AQpuOfSdRj7uJrItykNw==", - "dev": true, - "dependencies": { - "@girs/glib-2.0": "^2.78.0-3.2.7", - "@girs/gobject-2.0": "^2.78.0-3.2.7" - } - }, - "node_modules/@girs/glib-2.0": { - "version": "2.78.0-3.2.7", - "resolved": "https://registry.npmjs.org/@girs/glib-2.0/-/glib-2.0-2.78.0-3.2.7.tgz", - "integrity": "sha512-h1zGUd58VnQn93Xws91E0mwio8weBWdqDn6sm5Q0xzPmhGGH8+9h/z9JGc7YKj32PnlWrPJ8r9lxeyvO57zT6A==", - "dev": true, - "dependencies": { - "@girs/gjs": "^3.2.7", - "@girs/gobject-2.0": "^2.78.0-3.2.7" - } - }, - "node_modules/@girs/gmodule-2.0": { - "version": "2.0.0-3.2.7", - "resolved": "https://registry.npmjs.org/@girs/gmodule-2.0/-/gmodule-2.0-2.0.0-3.2.7.tgz", - "integrity": "sha512-7B+rAZ6grSYwSOz2p4h8hSKoK5QIuSVHEM7qGUTC6qpPiLvREcaBNGJSGgAMgxtRhyXPYVsUyLhMgUokrFUC+g==", - "dev": true, - "dependencies": { - "@girs/gjs": "^3.2.7", - "@girs/glib-2.0": "^2.78.0-3.2.7", - "@girs/gobject-2.0": "^2.78.0-3.2.7" - } - }, - "node_modules/@girs/gobject-2.0": { - "version": "2.78.0-3.2.7", - "resolved": "https://registry.npmjs.org/@girs/gobject-2.0/-/gobject-2.0-2.78.0-3.2.7.tgz", - "integrity": "sha512-BSYxXv4NHZS7ANo0up8N9Dfl3FysIxqsUXmHAMmVkUNdNPfaRyiXnSBiFYzkFdkHQFDoQhM5+RsXz4R94ZLlsw==", - "dev": true, - "dependencies": { - "@girs/gjs": "^3.2.7", - "@girs/glib-2.0": "^2.78.0-3.2.7" - } - }, - "node_modules/@girs/gtk-3.0": { - "version": "3.24.39-3.2.2", - "resolved": "https://registry.npmjs.org/@girs/gtk-3.0/-/gtk-3.0-3.24.39-3.2.2.tgz", - "integrity": "sha512-s12hGKHGM+aHD6ESOP0RhWaCV17ls6gIHLdtIMxFkHqV/2G146jowfl92c5IAzOgD7mS5ZvUmVRNrW4idY1BUQ==", - "dev": true, - "dependencies": { - "@girs/atk-1.0": "^2.45.1-3.2.2", - "@girs/cairo-1.0": "^1.0.0-3.2.2", - "@girs/freetype2-2.0": "^2.0.0-3.2.2", - "@girs/gdk-3.0": "^3.24.39-3.2.2", - "@girs/gdkpixbuf-2.0": "^2.0.0-3.2.2", - "@girs/gio-2.0": "^2.77.0-3.2.2", - "@girs/gjs": "^3.2.2", - "@girs/glib-2.0": "^2.77.0-3.2.2", - "@girs/gmodule-2.0": "^2.0.0-3.2.2", - "@girs/gobject-2.0": "^2.77.0-3.2.2", - "@girs/harfbuzz-0.0": "^7.1.0-3.2.2", - "@girs/pango-1.0": "^1.51.0-3.2.2", - "@girs/xlib-2.0": "^2.0.0-3.2.2" - } - }, - "node_modules/@girs/gtk-3.0/node_modules/@girs/atk-1.0": { - "version": "2.45.1-3.2.2", - "resolved": "https://registry.npmjs.org/@girs/atk-1.0/-/atk-1.0-2.45.1-3.2.2.tgz", - "integrity": "sha512-+ba+w8s3CuIHBXNtfUtqbMy+lpa+SxY+ksu5i9QSINvKod6RHXSglXYiRAjKsP3nVyTc+FaF7ymYvqGbM42HHw==", - "dev": true, - "dependencies": { - "@girs/gjs": "^3.2.2", - "@girs/glib-2.0": "^2.77.0-3.2.2", - "@girs/gobject-2.0": "^2.77.0-3.2.2" - } - }, - "node_modules/@girs/gtk-3.0/node_modules/@girs/gdk-3.0": { - "version": "3.24.39-3.2.2", - "resolved": "https://registry.npmjs.org/@girs/gdk-3.0/-/gdk-3.0-3.24.39-3.2.2.tgz", - "integrity": "sha512-nZ6DUQ/UfcW2y3ZtE1pfhBgerUkacXYpCuHJB2cp2moWHt82/w5H7zsUSr43rSzQqkFZCPQOsjUSX0VAJQfYTw==", - "dev": true, - "dependencies": { - "@girs/cairo-1.0": "^1.0.0-3.2.2", - "@girs/freetype2-2.0": "^2.0.0-3.2.2", - "@girs/gdkpixbuf-2.0": "^2.0.0-3.2.2", - "@girs/gio-2.0": "^2.77.0-3.2.2", - "@girs/gjs": "^3.2.2", - "@girs/glib-2.0": "^2.77.0-3.2.2", - "@girs/gmodule-2.0": "^2.0.0-3.2.2", - "@girs/gobject-2.0": "^2.77.0-3.2.2", - "@girs/harfbuzz-0.0": "^7.1.0-3.2.2", - "@girs/pango-1.0": "^1.51.0-3.2.2" - } - }, - "node_modules/@girs/gtk-3.0/node_modules/@girs/gio-2.0": { - "version": "2.77.0-3.2.2", - "resolved": "https://registry.npmjs.org/@girs/gio-2.0/-/gio-2.0-2.77.0-3.2.2.tgz", - "integrity": "sha512-AwxseNiPYXcPo5piyBc3Vb/+7ednxZgj2fcqOgys6Hmp40FrhNJphr4et87UBT1MOTA4sb4fyJNsSdQ2y9dfYQ==", - "dev": true, - "dependencies": { - "@girs/gjs": "^3.2.2", - "@girs/glib-2.0": "^2.77.0-3.2.2", - "@girs/gobject-2.0": "^2.77.0-3.2.2" - } - }, - "node_modules/@girs/gtk-3.0/node_modules/@girs/glib-2.0": { - "version": "2.77.0-3.2.2", - "resolved": "https://registry.npmjs.org/@girs/glib-2.0/-/glib-2.0-2.77.0-3.2.2.tgz", - "integrity": "sha512-cFWgYKKBR58+er7MOYTFcws5MHEQUD74VaGUEBLUTjFh9EsyIXOOcaU+Z6cteObGDo9ZYdlr8RBAdKI8H18sqQ==", - "dev": true, - "dependencies": { - "@girs/gjs": "^3.2.2", - "@girs/gobject-2.0": "^2.77.0-3.2.2" - } - }, - "node_modules/@girs/gtk-3.0/node_modules/@girs/gobject-2.0": { - "version": "2.77.0-3.2.2", - "resolved": "https://registry.npmjs.org/@girs/gobject-2.0/-/gobject-2.0-2.77.0-3.2.2.tgz", - "integrity": "sha512-psnnFhNY+49SXzdTDpHwnySMTDtHYDP9rxMx1Xb1UVE1JaxwuI0YLrylt+Zv0boGczmwol8afxZw2Z9TnSSocg==", - "dev": true, - "dependencies": { - "@girs/gjs": "^3.2.2", - "@girs/glib-2.0": "^2.77.0-3.2.2" - } - }, - "node_modules/@girs/gtk-3.0/node_modules/@girs/harfbuzz-0.0": { - "version": "7.1.0-3.2.3", - "resolved": "https://registry.npmjs.org/@girs/harfbuzz-0.0/-/harfbuzz-0.0-7.1.0-3.2.3.tgz", - "integrity": "sha512-z9fpiJBni97ioU2f1BIrfHCoTD2HDt0h2kaa0EL9n6DvzhntRXUc56cRRRohhXMyIwfUJmMOPuiqMFTI8kt4OQ==", - "dev": true, - "dependencies": { - "@girs/freetype2-2.0": "^2.0.0-3.2.3", - "@girs/gjs": "^3.2.3", - "@girs/glib-2.0": "^2.76.1-3.2.3", - "@girs/gobject-2.0": "^2.76.1-3.2.3" - } - }, - "node_modules/@girs/gtk-3.0/node_modules/@girs/harfbuzz-0.0/node_modules/@girs/glib-2.0": { - "version": "2.76.1-3.2.3", - "resolved": "https://registry.npmjs.org/@girs/glib-2.0/-/glib-2.0-2.76.1-3.2.3.tgz", - "integrity": "sha512-pY/ZE2WPco9E8Cz5+wTqxEOdVpipEQBRDJXVd/KQCoRjTSvgoMLLEE8B84W0LgOgR8ns99XtGZrf0U0y4qFqyA==", - "dev": true, - "dependencies": { - "@girs/gjs": "^3.2.3", - "@girs/gobject-2.0": "^2.76.1-3.2.3" - } - }, - "node_modules/@girs/gtk-3.0/node_modules/@girs/harfbuzz-0.0/node_modules/@girs/gobject-2.0": { - "version": "2.76.1-3.2.3", - "resolved": "https://registry.npmjs.org/@girs/gobject-2.0/-/gobject-2.0-2.76.1-3.2.3.tgz", - "integrity": "sha512-9X35d1ZRO4xY9b0iOm51MwjfbgvOGQtNs2A9IWcSlaoI4LB5mCRlPlQ1UFhpqcbbmdrL+Cce/0p3F36x5rn/dg==", - "dev": true, - "dependencies": { - "@girs/gjs": "^3.2.3", - "@girs/glib-2.0": "^2.76.1-3.2.3" - } - }, - "node_modules/@girs/gvc-1.0": { - "version": "1.0.0-3.2.7", - "resolved": "https://registry.npmjs.org/@girs/gvc-1.0/-/gvc-1.0-1.0.0-3.2.7.tgz", - "integrity": "sha512-5B6YxXRzqo6JbZg4B8KtEOEsUhfEZYo9yESgf95lhWeUXpS6JjJFg/lQ85E/KFDQmKtAJ/UYglmLa8omVvH5DA==", - "dev": true, - "dependencies": { - "@girs/gio-2.0": "^2.78.0-3.2.7", - "@girs/gjs": "^3.2.7", - "@girs/glib-2.0": "^2.78.0-3.2.7", - "@girs/gobject-2.0": "^2.78.0-3.2.7" - } - }, - "node_modules/@girs/harfbuzz-0.0": { - "version": "8.2.1-3.2.7", - "resolved": "https://registry.npmjs.org/@girs/harfbuzz-0.0/-/harfbuzz-0.0-8.2.1-3.2.7.tgz", - "integrity": "sha512-2ISw/O1eW90P2jeeFuTZRBE6+z/VWCWCaYZOMTTDaF498PGljKtuexm0e5BEzVrbayU7EgSHhqxfnfpsgI2+eQ==", - "dev": true, - "dependencies": { - "@girs/freetype2-2.0": "^2.0.0-3.2.7", - "@girs/gjs": "^3.2.7", - "@girs/glib-2.0": "^2.78.0-3.2.7", - "@girs/gobject-2.0": "^2.78.0-3.2.7" - } - }, - "node_modules/@girs/nm-1.0": { - "version": "1.43.1-3.1.0", - "resolved": "https://registry.npmjs.org/@girs/nm-1.0/-/nm-1.0-1.43.1-3.1.0.tgz", - "integrity": "sha512-2jawCPFAFlBmlsQ0334+SIYZJ1zXiHClU0PRiPZPAuOx+NzIpvAjEjprynrwFHwZ2azjmSjFp5AQ/wTTiDLJ/g==", - "dev": true, - "dependencies": { - "@girs/gio-2.0": "^2.76.1-3.1.0", - "@girs/gjs": "^3.1.0", - "@girs/glib-2.0": "^2.76.1-3.1.0", - "@girs/gobject-2.0": "^2.76.1-3.1.0" - } - }, - "node_modules/@girs/nm-1.0/node_modules/@girs/gio-2.0": { - "version": "2.76.1-3.2.3", - "resolved": "https://registry.npmjs.org/@girs/gio-2.0/-/gio-2.0-2.76.1-3.2.3.tgz", - "integrity": "sha512-6SK2z2YsZrxdoumXW7taI1S2HIBvWcFoKWAot0A5coMskJOnt6HnNxIMTBbN5YhkOs855YHqfaLYeNNU3Y3M2A==", - "dev": true, - "dependencies": { - "@girs/gjs": "^3.2.3", - "@girs/glib-2.0": "^2.76.1-3.2.3", - "@girs/gobject-2.0": "^2.76.1-3.2.3" - } - }, - "node_modules/@girs/nm-1.0/node_modules/@girs/glib-2.0": { - "version": "2.76.1-3.2.3", - "resolved": "https://registry.npmjs.org/@girs/glib-2.0/-/glib-2.0-2.76.1-3.2.3.tgz", - "integrity": "sha512-pY/ZE2WPco9E8Cz5+wTqxEOdVpipEQBRDJXVd/KQCoRjTSvgoMLLEE8B84W0LgOgR8ns99XtGZrf0U0y4qFqyA==", - "dev": true, - "dependencies": { - "@girs/gjs": "^3.2.3", - "@girs/gobject-2.0": "^2.76.1-3.2.3" - } - }, - "node_modules/@girs/nm-1.0/node_modules/@girs/gobject-2.0": { - "version": "2.76.1-3.2.3", - "resolved": "https://registry.npmjs.org/@girs/gobject-2.0/-/gobject-2.0-2.76.1-3.2.3.tgz", - "integrity": "sha512-9X35d1ZRO4xY9b0iOm51MwjfbgvOGQtNs2A9IWcSlaoI4LB5mCRlPlQ1UFhpqcbbmdrL+Cce/0p3F36x5rn/dg==", - "dev": true, - "dependencies": { - "@girs/gjs": "^3.2.3", - "@girs/glib-2.0": "^2.76.1-3.2.3" - } - }, - "node_modules/@girs/notify-0.7": { - "version": "0.8.3-3.2.7", - "resolved": "https://registry.npmjs.org/@girs/notify-0.7/-/notify-0.7-0.8.3-3.2.7.tgz", - "integrity": "sha512-ngdi3gNKgog/DLOIpUFaHxWGUZoRxB5JqHj6beWAlHKFlMuuPdBnArLVy0JzuotEGXMrfC0Oask8a64DEMU7Sw==", - "dev": true, - "dependencies": { - "@girs/gdkpixbuf-2.0": "^2.0.0-3.2.7", - "@girs/gio-2.0": "^2.78.0-3.2.7", - "@girs/gjs": "^3.2.7", - "@girs/glib-2.0": "^2.78.0-3.2.7", - "@girs/gmodule-2.0": "^2.0.0-3.2.7", - "@girs/gobject-2.0": "^2.78.0-3.2.7" - } - }, - "node_modules/@girs/pango-1.0": { - "version": "1.51.0-3.2.7", - "resolved": "https://registry.npmjs.org/@girs/pango-1.0/-/pango-1.0-1.51.0-3.2.7.tgz", - "integrity": "sha512-wxVVMrZnaWkgBmvTM4rWE20whh0CTRiXINgmK9V2NOdJHUUD3HqIvRxChG6YJmXQcb9h4Gcgpr+OyP34IgP2Sg==", - "dev": true, - "dependencies": { - "@girs/cairo-1.0": "^1.0.0-3.2.7", - "@girs/freetype2-2.0": "^2.0.0-3.2.7", - "@girs/gio-2.0": "^2.78.0-3.2.7", - "@girs/gjs": "^3.2.7", - "@girs/glib-2.0": "^2.78.0-3.2.7", - "@girs/gobject-2.0": "^2.78.0-3.2.7", - "@girs/harfbuzz-0.0": "^8.2.1-3.2.7" - } - }, - "node_modules/@girs/soup-3.0": { - "version": "3.4.4-3.2.7", - "resolved": "https://registry.npmjs.org/@girs/soup-3.0/-/soup-3.0-3.4.4-3.2.7.tgz", - "integrity": "sha512-g0wZX/xPgINy4gv9BsoOytjQjdUfUWmSvvOaL8l+3apWwUHGnBvA99oEye5PHh2xCEaZHhM4564DGy51TZC0/g==", - "dev": true, - "dependencies": { - "@girs/gio-2.0": "^2.78.0-3.2.7", - "@girs/gjs": "^3.2.7", - "@girs/glib-2.0": "^2.78.0-3.2.7", - "@girs/gobject-2.0": "^2.78.0-3.2.7" - } - }, - "node_modules/@girs/xlib-2.0": { - "version": "2.0.0-3.2.7", - "resolved": "https://registry.npmjs.org/@girs/xlib-2.0/-/xlib-2.0-2.0.0-3.2.7.tgz", - "integrity": "sha512-c4+y9V1BN7u0o2ijVuvT9WzEt7IvSJW/Dt4N7qQdTWfLnsQABPMnLuKQsZ6GRqPWBt+JBH0sOSR0tHBMLATC0g==", - "dev": true, - "dependencies": { - "@girs/gjs": "^3.2.7", - "@girs/gobject-2.0": "^2.78.0-3.2.7" - } - }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.11.14", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", - "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", - "dev": true, - "dependencies": { - "@humanwhocodes/object-schema": "^2.0.2", - "debug": "^4.3.1", - "minimatch": "^3.0.5" - }, - "engines": { - "node": ">=10.10.0" - } - }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true, - "engines": { - "node": ">=12.22" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/object-schema": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz", - "integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==", - "dev": true - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true - }, - "node_modules/@types/semver": { - "version": "7.5.6", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.6.tgz", - "integrity": "sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A==", - "dev": true - }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz", - "integrity": "sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==", - "dev": true, - "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", - "graphemer": "^1.4.0", - "ignore": "^5.2.0", - "natural-compare-lite": "^1.4.0", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^5.0.0", - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/parser": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.62.0.tgz", - "integrity": "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==", - "dev": true, - "dependencies": { - "@typescript-eslint/scope-manager": "5.62.0", - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/typescript-estree": "5.62.0", - "debug": "^4.3.4" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz", - "integrity": "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/visitor-keys": "5.62.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/type-utils": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz", - "integrity": "sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==", - "dev": true, - "dependencies": { - "@typescript-eslint/typescript-estree": "5.62.0", - "@typescript-eslint/utils": "5.62.0", - "debug": "^4.3.4", - "tsutils": "^3.21.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "*" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/types": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz", - "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz", - "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/visitor-keys": "5.62.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/utils": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz", - "integrity": "sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==", - "dev": true, - "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" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz", - "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.62.0", - "eslint-visitor-keys": "^3.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@ungap/structured-clone": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", - "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", - "dev": true - }, - "node_modules/acorn": { - "version": "8.11.3", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", - "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true - }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true - }, - "node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "dependencies": { - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint": { - "version": "8.56.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.56.0.tgz", - "integrity": "sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==", - "dev": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.56.0", - "@humanwhocodes/config-array": "^0.11.13", - "@humanwhocodes/module-importer": "^1.0.1", - "@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", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "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", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3", - "strip-ansi": "^6.0.1", - "text-table": "^0.2.0" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint/node_modules/eslint-scope": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", - "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/espree": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", - "dev": true, - "dependencies": { - "acorn": "^8.9.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/esquery": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", - "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", - "dev": true, - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esquery/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esrecurse/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, - "node_modules/fast-glob": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", - "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", - "dev": true, - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fast-glob/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true - }, - "node_modules/fastq": { - "version": "1.16.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.16.0.tgz", - "integrity": "sha512-ifCoaXsDrsdkWTtiNJX5uzHDsrck5TzfKKDcuFFTIrrc/BS076qgEIfoIy1VeZqViznfKiysPYTh/QeHtnIsYA==", - "dev": true, - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "dev": true, - "dependencies": { - "flat-cache": "^3.0.4" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/flat-cache": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", - "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", - "dev": true, - "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.3", - "rimraf": "^3.0.2" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/flatted": { - "version": "3.2.9", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz", - "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", - "dev": true - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true - }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "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" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", - "dev": true, - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/ignore": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz", - "integrity": "sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true - }, - "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true - }, - "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "dev": true, - "dependencies": { - "json-buffer": "3.0.1" - } - }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true - }, - "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dev": true, - "dependencies": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true - }, - "node_modules/natural-compare-lite": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", - "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", - "dev": true - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/optionator": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", - "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", - "dev": true, - "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" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true, - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, - "node_modules/tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, - "dependencies": { - "tslib": "^1.8.1" - }, - "engines": { - "node": ">= 6" - }, - "peerDependencies": { - "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" - } - }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/typescript": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", - "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true - }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - } - } -} diff --git a/package.json b/package.json deleted file mode 100644 index 92a69d57..00000000 --- a/package.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "name": "ags", - "version": "1.0.0", - "repository": "https://github.com/Aylur/ags", - "author": "Aylur", - "license": "GPL", - "dependencies": { - "typescript": "^5.1.0" - }, - "devDependencies": { - "@girs/cairo-1.0": "^1.0.0-3.2.7", - "@girs/dbusmenugtk3-0.4": "^0.4.0-3.2.0", - "@girs/gjs": "^3.2.7", - "@girs/gtk-3.0": "^3.24.39-3.2.2", - "@girs/gvc-1.0": "^1.0.0-3.1.0", - "@girs/nm-1.0": "^1.43.1-3.1.0", - "@girs/notify-0.7": "^0.8.3-3.2.7", - "@girs/soup-3.0": "^3.4.4-3.2.6", - "@typescript-eslint/eslint-plugin": "^5.33.0", - "@typescript-eslint/parser": "^5.33.0", - "eslint": "^8.42.0" - }, - "scripts": { - "test": "eslint ." - } -} diff --git a/post_install.sh b/post_install.sh deleted file mode 100644 index bb7c2f4f..00000000 --- a/post_install.sh +++ /dev/null @@ -1,73 +0,0 @@ -#!/usr/bin/env bash - -DATA_DIR="$DESTDIR/$1" -PKGDATA_DIR="$DESTDIR/$2" -BIN_DIR="$DESTDIR/$3" -APP_ID=$4 - -mkdir -p $BIN_DIR - -BIN_SRC="$PKGDATA_DIR/$APP_ID" -BIN_DEST="$BIN_DIR/ags" -ln -s -f $BIN_SRC $BIN_DEST - -if [[ "$5" == "false" ]]; then - exit 0 -fi - -SRC=$6 -TYPES="$PKGDATA_DIR/types" - -mkdir -p $TYPES - -tsc -p $SRC/tsconfig.json -d --declarationDir $TYPES --emitDeclarationOnly - -function fixPaths { - sed -i 's/node_modules/types/g' $1 - sed -i 's/import("@girs/import("types\/@girs/g' $1 -} - -export -f fixPaths -find $TYPES -type f | xargs -I % bash -c "fixPaths %" -cp -r "$SRC/node_modules/@girs" "$TYPES/@girs" - -# gen ags.d.ts -function mod { - echo "declare module '$1' { - const exports: typeof import('$2') - export = exports -}" -} - -function resource { - mod "resource:///com/github/Aylur/ags/$1.js" "./$1" -} - -dts="$TYPES/ags.d.ts" - -echo "declare function print(...args: any[]): void; -declare const Widget: typeof import('./widget').default -declare const Service: typeof import('./service').default -declare const Variable: typeof import('./variable').default -declare const Utils: typeof import('./utils').default -declare const App: typeof import('./app').default -" >$dts - -for file in $SRC/src/*.ts; do - f=$(basename -s .ts $file) - if [[ "$f" != "main" && "$f" != "client" ]]; then - resource "$(basename -s .ts $file)" >>$dts - fi -done - -for file in $SRC/src/service/*.ts; do - resource "service/$(basename -s .ts $file)" >>$dts -done - -for file in $SRC/src/widgets/*.ts; do - resource "widgets/$(basename -s .ts $file)" >>$dts -done - -for file in $SRC/src/utils/*.ts; do - resource "utils/$(basename -s .ts $file)" >>$dts -done diff --git a/src/app.ts b/src/app.ts deleted file mode 100644 index 5849e7ee..00000000 --- a/src/app.ts +++ /dev/null @@ -1,414 +0,0 @@ -import Gtk from 'gi://Gtk?version=3.0'; -import Gdk from 'gi://Gdk?version=3.0'; -import Gio from 'gi://Gio'; -import GLib from 'gi://GLib'; -import Service from './service.js'; -import Variable from './variable.js'; -import Widget from './widget.js'; -import Utils from './utils.js'; -import { timeout, readFileAsync } from './utils.js'; -import { loadInterfaceXML } from './utils.js'; - -function deprecated(config: Config) { - console.warn('passing the config object with default export is DEPRECATED. ' - + 'use App.config() instead'); - - const warning = (from: string, to: string) => console.warn( - `${from} config option has been removed: use ${to} instead`); - - if (config.notificationPopupTimeout !== undefined) - warning('notificationPopupTimeout', 'Notifications.popupTimeout'); - - if (config.notificationForceTimeout !== undefined) - warning('notificationForceTimeout', 'Notifications.forceTimeout'); - - if (config.cacheNotificationActions !== undefined) - warning('cacheNotificationActions', 'Notifications.cacheActions'); - - if (config.cacheCoverArt !== undefined) - warning('cacheCoverArt', 'Mpris.cacheCoverArt'); - - if (config.maxStreamVolume !== undefined) - warning('cacheCoverArt', 'Audio.maxStreamVolume'); -} - -const AgsIFace = (bus: string) => - loadInterfaceXML('com.github.Aylur.ags')?.replace('@BUS@', bus); - -export interface Config { - windows?: Gtk.Window[] | (() => Gtk.Window[]) - style?: string - icons?: string - gtkTheme?: string - iconTheme?: string - cursorTheme?: string - closeWindowDelay?: { [key: string]: number } - - onWindowToggled?: (windowName: string, visible: boolean) => void - onConfigParsed?: (app: App) => void - - // FIXME: deprecated - notificationPopupTimeout?: number - notificationForceTimeout?: boolean - cacheNotificationActions?: boolean - cacheCoverArt?: boolean - maxStreamVolume?: number -} - -export class App extends Gtk.Application { - static { - Service.register(this, { - 'window-toggled': ['string', 'boolean'], - 'config-parsed': [], - }); - } - - private _dbus!: Gio.DBusExportedObject; - private _cssProviders: Gtk.CssProvider[] = []; - private _objectPath!: string; - private _windows: Map = new Map(); - private _configPath!: string; - private _configDir!: string; - - private _closeWindowDelay: Config['closeWindowDelay'] = {}; - get closeWindowDelay() { return this._closeWindowDelay!; } - set closeWindowDelay(v) { this._closeWindowDelay = v; } - - get windows() { return [...this._windows.values()]; } - get configPath() { return this._configPath; } - get configDir() { return this._configDir; } - - set iconTheme(name: string) { Gtk.Settings.get_default()!.gtk_icon_theme_name = name; } - get iconTheme() { return Gtk.Settings.get_default()!.gtk_icon_theme_name || ''; } - - set cursorTheme(name: string) { Gtk.Settings.get_default()!.gtk_cursor_theme_name = name; } - get cursorTheme() { return Gtk.Settings.get_default()!.gtk_cursor_theme_name || ''; } - - set gtkTheme(name: string) { Gtk.Settings.get_default()!.gtk_theme_name = name; } - get gtkTheme() { return Gtk.Settings.get_default()!.gtk_theme_name || ''; } - - readonly resetCss = () => { - const screen = Gdk.Screen.get_default(); - if (!screen) { - console.error("couldn't get screen"); - return; - } - - this._cssProviders.forEach(provider => { - Gtk.StyleContext.remove_provider_for_screen(screen, provider); - }); - - this._cssProviders = []; - }; - - readonly applyCss = (pathOrStyle: string, reset = false) => { - const screen = Gdk.Screen.get_default(); - if (!screen) { - console.error("couldn't get screen"); - return; - } - - if (reset) - this.resetCss(); - - const cssProvider = new Gtk.CssProvider(); - cssProvider.connect('parsing-error', (_, section, err) => { - const file = section.get_file().get_path(); - const location = section.get_start_line(); - console.error(`CSS ERROR: ${err.message} at line ${location} in ${file}`); - }); - - try { - if (GLib.file_test(pathOrStyle, GLib.FileTest.EXISTS)) - cssProvider.load_from_path(pathOrStyle); - else - cssProvider.load_from_data(new TextEncoder().encode(pathOrStyle)); - } finally { - // log on parsing-error - } - - Gtk.StyleContext.add_provider_for_screen( - screen, - cssProvider, - Gtk.STYLE_PROVIDER_PRIORITY_USER, - ); - - this._cssProviders.push(cssProvider); - }; - - readonly addIcons = (path: string) => { - Gtk.IconTheme.get_default().prepend_search_path(path); - }; - - setup(bus: string, path: string, configDir: string, entry: string) { - this.application_id = bus; - this.flags = Gio.ApplicationFlags.DEFAULT_FLAGS; - this._objectPath = path; - - this._configDir = configDir; - this._configPath = entry; - } - - vfunc_activate() { - this.hold(); - - Object.assign(globalThis, { - Widget, - Service, - Variable, - Utils, - App: this, - }); - - this._register(); - this._load(); - } - - readonly connect = (signal = 'window-toggled', callback: (_: this, ...args: any[]) => void) => { - return super.connect(signal, callback); - }; - - readonly toggleWindow = (name: string) => { - const w = this.getWindow(name); - if (w) - w.visible ? this.closeWindow(name) : this.openWindow(name); - else - return 'There is no window named ' + name; - }; - - readonly openWindow = (name: string) => { - this.getWindow(name)?.show(); - }; - - readonly closeWindow = (name: string) => { - const w = this.getWindow(name); - if (!w || !w.visible) - return; - - const delay = this.closeWindowDelay[name]; - if (delay && w.visible) { - timeout(delay, () => w.hide()); - this.emit('window-toggled', name, false); - } - else { - w.hide(); - } - }; - - readonly getWindow = (name: string) => { - const w = this._windows.get(name); - if (!w) - console.error(Error(`There is no window named ${name}`)); - - return w; - }; - - readonly removeWindow = (w: Gtk.Window | string) => { - const name = typeof w === 'string' ? w : w.name || 'gtk-layer-shell'; - - const win = this._windows.get(name); - if (!win) { - console.error(Error('There is no window named ' + name)); - return; - } - - win.destroy(); - this._windows.delete(name); - }; - - readonly addWindow = (w: Gtk.Window) => { - if (!(w instanceof Gtk.Window)) { - return console.error(Error(`${w} is not an instanceof Gtk.Window, ` + - ` but it is of type ${typeof w}`)); - } - - if (!w.name) - return console.error(Error(`${w} has no name`)); - - w.connect('notify::visible', - () => this.emit('window-toggled', w.name, w.visible)); - - if (this._windows.has(w.name)) { - console.error(Error('There is already a window named' + w.name)); - this.quit(); - return; - } - - this._windows.set(w.name, w); - }; - - readonly quit = () => super.quit(); - - readonly config = (config: Config) => { - const { - windows, - closeWindowDelay, - style, - icons, - gtkTheme, - iconTheme, - cursorTheme, - onConfigParsed, - onWindowToggled, - } = config; - - if (closeWindowDelay) - this.closeWindowDelay = closeWindowDelay; - - if (gtkTheme) - this.gtkTheme = gtkTheme; - - if (iconTheme) - this.iconTheme = iconTheme; - - if (cursorTheme) - this.cursorTheme = cursorTheme; - - if (style) { - this.applyCss(style.startsWith('.') - ? `${this.configDir}${style.slice(1)}` - : style); - } - - if (icons) { - this.addIcons(icons.startsWith('.') - ? `${this.configDir}${icons.slice(1)}` - : icons); - } - - if (typeof onWindowToggled === 'function') - this.connect('window-toggled', (_, n, v) => onWindowToggled!(n, v)); - - if (typeof onConfigParsed === 'function') - this.connect('config-parsed', onConfigParsed); - - if (typeof windows === 'function') - windows().forEach(this.addWindow); - - if (Array.isArray(windows)) - windows.forEach(this.addWindow); - }; - - private async _load() { - try { - const entry = await import(`file://${this.configPath}`); - const config = entry.default as Config; - if (!config) - return this.emit('config-parsed'); - else - // FIXME: - deprecated(config); - - this.config(config); - this.emit('config-parsed'); - } catch (err) { - const error = err as { name?: string, message: string }; - const msg = `Unable to load file from: file://${this._configPath}`; - if (error?.name === 'ImportError' && error.message.includes(msg)) { - print(`config file not found: "${this._configPath}"`); - this.quit(); - } else { - logError(err); - } - } - } - - private _register() { - Gio.bus_own_name( - Gio.BusType.SESSION, - this.application_id!, - Gio.BusNameOwnerFlags.NONE, - (connection: Gio.DBusConnection) => { - this._dbus = Gio.DBusExportedObject - .wrapJSObject(AgsIFace(this.application_id!) as string, this); - - this._dbus.export(connection, this._objectPath); - }, - null, - null, - ); - } - - toJSON() { - return { - bus: this.application_id, - configDir: this.configDir, - windows: Object.fromEntries(this.windows.entries()), - }; - } - - RunJs(js: string, clientBusName?: string, clientObjPath?: string) { - let fn; - - const dbus = (method: 'Return' | 'Print') => (out: unknown) => Gio.DBus.session.call( - clientBusName!, clientObjPath!, clientBusName!, method, - new GLib.Variant('(s)', [`${out}`]), - null, Gio.DBusCallFlags.NONE, -1, null, null, - ); - - const response = dbus('Return'); - const print = dbus('Print'); - const client = clientBusName && clientObjPath; - - try { - fn = Function(`return (async function(print) { - ${js.includes(';') ? js : `return ${js}`} - })`); - } catch (error) { - client ? response(error) : logError(error); - return; - } - - fn()(print) - .then((out: unknown) => { - client ? response(`${out}`) : print(`${out}`); - }) - .catch((err: Error) => { - client ? response(`${err}`) : logError(err); - }); - } - - RunFile(file: string, bus?: string, path?: string) { - readFileAsync(file) - .then(content => { - if (content.startsWith('#!')) - content = content.split('\n').slice(1).join('\n'); - - this.RunJs(content, bus, path); - }) - .catch(logError); - } - - // FIXME: deprecated - RunPromise(js: string, busName?: string, objPath?: string) { - console.warn('--run-promise is DEPRECATED, ' + - ' use --run-js instead, which now supports await syntax'); - - const client = busName && objPath; - const response = (out: unknown) => Gio.DBus.session.call( - busName!, objPath!, busName!, 'Return', - new GLib.Variant('(s)', [`${out}`]), - null, Gio.DBusCallFlags.NONE, -1, null, null, - ); - - new Promise((res, rej) => Function('resolve', 'reject', js)(res, rej)) - .then(out => { - client ? response(`${out}`) : print(`${out}`); - }) - .catch(err => { - client ? response(`${err}`) : console.error(`${err}`); - }); - } - - ToggleWindow(name: string) { - this.toggleWindow(name); - return `${this.getWindow(name)?.visible}`; - } - - Inspector() { Gtk.Window.set_interactive_debugging(true); } - - Quit() { this.quit(); } -} - -export const app = new App; -export default app; diff --git a/src/client.ts b/src/client.ts deleted file mode 100644 index bf6b0cf1..00000000 --- a/src/client.ts +++ /dev/null @@ -1,119 +0,0 @@ -import Gtk from 'gi://Gtk?version=3.0'; -import GObject from 'gi://GObject'; -import Gio from 'gi://Gio'; -import GLib from 'gi://GLib'; -import { loadInterfaceXML } from './utils.js'; -import { type AgsProxy } from './dbus/types.js'; - -const AgsIFace = (bus: string) => - loadInterfaceXML('com.github.Aylur.ags')!.replace('@BUS@', bus); - -const ClientIFace = (bus: string) => - loadInterfaceXML('com.github.Aylur.ags.client')!.replace('@BUS@', bus); - -const TIME = `${GLib.DateTime.new_now_local().to_unix()}`; - -interface Flags { - busName: string - inspector: boolean - runJs: string - runFile: string - toggleWindow: string - quit: boolean - - // FIXME: deprecated - runPromise: string -} - -class Client extends Gtk.Application { - static { GObject.registerClass(this); } - - private _objectPath: string; - private _dbus!: Gio.DBusExportedObject; - private _proxy: AgsProxy; - private _callback!: () => void; - - constructor(bus: string, path: string, proxy: AgsProxy) { - super({ - application_id: bus + '.client' + TIME, - flags: Gio.ApplicationFlags.DEFAULT_FLAGS, - }); - - this._objectPath = path + '/client' + TIME; - this._proxy = proxy; - } - - private _register() { - Gio.bus_own_name( - Gio.BusType.SESSION, - this.application_id!, - Gio.BusNameOwnerFlags.NONE, - (connection: Gio.DBusConnection) => { - this._dbus = Gio.DBusExportedObject - .wrapJSObject(ClientIFace(this.application_id!) as string, this); - - this._dbus.export(connection, this._objectPath); - }, - null, - null, - ); - } - - Return(str: string) { - print(str); - this.quit(); - } - - Print(str: string) { - print(str); - } - - // FIXME: promise deprecated - remote(method: 'Js' | 'Promise' | 'File', body: string) { - if (method === 'Promise') { - console.warn('--run-promise is DEPRECATED, ' + - ' use --run-js instead, which now supports await syntax'); - } - - this._callback = () => this._proxy[`Run${method}Remote`]( - body, - this.application_id!, - this._objectPath, - ); - this.run(null); - } - - vfunc_activate(): void { - this.hold(); - this._register(); - this._callback(); - } -} - -export default function(bus: string, path: string, flags: Flags) { - const AgsProxy = Gio.DBusProxy.makeProxyWrapper(AgsIFace(bus)); - const proxy = AgsProxy(Gio.DBus.session, bus, path) as AgsProxy; - const client = new Client(bus, path, proxy); - - if (flags.toggleWindow) - print(proxy.ToggleWindowSync(flags.toggleWindow)); - - else if (flags.runJs) - client.remote('Js', flags.runJs); - - else if (flags.runFile) - client.remote('File', flags.runFile); - - else if (flags.inspector) - proxy.InspectorRemote(); - - else if (flags.quit) - proxy.QuitRemote(); - - // FIXME: deprecated - else if (flags.runPromise) - client.remote('Promise', flags.runPromise); - - else - print(`Ags with busname "${flags.busName}" is already running`); -} diff --git a/src/com.github.Aylur.ags.js.in b/src/com.github.Aylur.ags.js.in deleted file mode 100755 index 6c1c48be..00000000 --- a/src/com.github.Aylur.ags.js.in +++ /dev/null @@ -1,37 +0,0 @@ -#!@GJS@ -m - -/* - * Copyright 2023 Aylur@github - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * SPDX-License-Identifier: GPL-3.0-or-later - */ - -import { exit, programArgs, programInvocationName } from "system"; -import GIR from "gi://GIRepository?version=2.0"; - -imports.package.init({ - name: '@APP_ID@', - version: '@PACKAGE_VERSION@', - prefix: '@PREFIX@', - libdir: '@LIBDIR@', -}); - -GIR.Repository.prepend_search_path('@LIBDIR@'); -GIR.Repository.prepend_library_path('@LIBDIR@'); - -const module = await import('resource://@RESOURCE_PATH@/main.js'); -const exitCode = await module.main([programInvocationName, ...programArgs]); -exit(exitCode); diff --git a/src/com.github.Aylur.ags.src.gresource.xml b/src/com.github.Aylur.ags.src.gresource.xml deleted file mode 100644 index 35cc8aea..00000000 --- a/src/com.github.Aylur.ags.src.gresource.xml +++ /dev/null @@ -1,82 +0,0 @@ - - - - app.js - client.js - main.js - overrides.js - service.js - utils.js - variable.js - widget.js - - widgets/widget.js - widgets/box.js - widgets/button.js - widgets/calendar.js - widgets/centerbox.js - widgets/circularprogress.js - widgets/colorbutton.js - widgets/drawingarea.js - widgets/entry.js - widgets/eventbox.js - widgets/filechooserbutton.js - widgets/fixed.js - widgets/flowbox.js - widgets/fontbutton.js - widgets/icon.js - widgets/label.js - widgets/levelbar.js - widgets/listbox.js - widgets/menu.js - widgets/menubar.js - widgets/menuitem.js - widgets/overlay.js - widgets/progressbar.js - widgets/revealer.js - widgets/scrollable.js - widgets/separator.js - widgets/slider.js - widgets/spinbutton.js - widgets/spinner.js - widgets/stack.js - widgets/switch.js - widgets/togglebutton.js - widgets/window.js - - service/applications.js - service/audio.js - service/battery.js - service/bluetooth.js - service/greetd.js - service/hyprland.js - service/mpris.js - service/network.js - service/notifications.js - service/powerprofiles.js - service/systemtray.js - - dbus/types.js - dbus/com.github.Aylur.ags.xml - dbus/com.github.Aylur.ags.client.xml - dbus/net.hadess.PowerProfiles.xml - dbus/org.freedesktop.DBus.xml - dbus/org.freedesktop.Notifications.xml - dbus/org.freedesktop.UPower.Device.xml - dbus/org.mpris.MediaPlayer2.Player.xml - dbus/org.mpris.MediaPlayer2.xml - dbus/org.kde.StatusNotifierWatcher.xml - dbus/org.kde.StatusNotifierItem.xml - - utils/binding.js - utils/etc.js - utils/exec.js - utils/fetch.js - utils/file.js - utils/gobject.js - utils/init.js - utils/notify.js - utils/pam.js - utils/timeout.js - - diff --git a/src/dbus/com.github.Aylur.ags.client.xml b/src/dbus/com.github.Aylur.ags.client.xml deleted file mode 100644 index 64ba6e68..00000000 --- a/src/dbus/com.github.Aylur.ags.client.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/src/dbus/com.github.Aylur.ags.xml b/src/dbus/com.github.Aylur.ags.xml deleted file mode 100644 index 8e6ec740..00000000 --- a/src/dbus/com.github.Aylur.ags.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/dbus/net.hadess.PowerProfiles.xml b/src/dbus/net.hadess.PowerProfiles.xml deleted file mode 100644 index b72d6866..00000000 --- a/src/dbus/net.hadess.PowerProfiles.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/dbus/org.freedesktop.DBus.xml b/src/dbus/org.freedesktop.DBus.xml deleted file mode 100644 index b2159b5c..00000000 --- a/src/dbus/org.freedesktop.DBus.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - diff --git a/src/dbus/org.freedesktop.Notifications.xml b/src/dbus/org.freedesktop.Notifications.xml deleted file mode 100644 index 6b1d2f29..00000000 --- a/src/dbus/org.freedesktop.Notifications.xml +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/dbus/org.freedesktop.UPower.Device.xml b/src/dbus/org.freedesktop.UPower.Device.xml deleted file mode 100644 index 18fc41d2..00000000 --- a/src/dbus/org.freedesktop.UPower.Device.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - diff --git a/src/dbus/org.kde.StatusNotifierItem.xml b/src/dbus/org.kde.StatusNotifierItem.xml deleted file mode 100644 index f1f0c9d8..00000000 --- a/src/dbus/org.kde.StatusNotifierItem.xml +++ /dev/null @@ -1,50 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/dbus/org.kde.StatusNotifierWatcher.xml b/src/dbus/org.kde.StatusNotifierWatcher.xml deleted file mode 100644 index 4f0cd8b5..00000000 --- a/src/dbus/org.kde.StatusNotifierWatcher.xml +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/dbus/org.mpris.MediaPlayer2.Player.xml b/src/dbus/org.mpris.MediaPlayer2.Player.xml deleted file mode 100644 index a5abc1f9..00000000 --- a/src/dbus/org.mpris.MediaPlayer2.Player.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/dbus/org.mpris.MediaPlayer2.xml b/src/dbus/org.mpris.MediaPlayer2.xml deleted file mode 100644 index 4e86b73e..00000000 --- a/src/dbus/org.mpris.MediaPlayer2.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/src/dbus/types.ts b/src/dbus/types.ts deleted file mode 100644 index 68bcb4f7..00000000 --- a/src/dbus/types.ts +++ /dev/null @@ -1,129 +0,0 @@ -/* eslint-disable @typescript-eslint/no-misused-new */ -import GLib from 'gi://GLib'; -import Gio from 'gi://Gio'; - -export interface DBusProxy extends Gio.DBusProxy { - new(...args: unknown[]): DBusProxy - ListNamesAsync: () => Promise -} - -export interface PlayerProxy extends Gio.DBusProxy { - new(...args: unknown[]): PlayerProxy; - CanControl: boolean; - CanGoNext: boolean; - CanGoPrevious: boolean; - CanPlay: boolean; - CanPause: boolean; - Metadata: { [key: string]: GLib.Variant }; - PlaybackStatus: string; - Shuffle: boolean | null; - LoopStatus: string | null; - Volume: number; - Position: number; - SetPositionAsync: (trackid: string, position: number) => void; - PlayPauseAsync: () => Promise; - NextAsync: () => Promise; - PreviousAsync: () => Promise; - StopAsync: () => Promise; - PlayAsync: () => Promise; -} - -export interface MprisProxy extends Gio.DBusProxy { - new(...args: unknown[]): MprisProxy; - Raise: () => void; - Quit: () => void; - CanQuit: boolean; - CanRaise: boolean; - Identity: string; - DesktopEntry: string; -} - -export interface BatteryProxy extends Gio.DBusProxy { - new(...args: unknown[]): BatteryProxy; - State: number; - Percentage: number; - IsPresent: boolean; - TimeToEmpty: number; - TimeToFull: number; - Energy: number; - EnergyFull: number; - EnergyRate: number; -} - -export interface StatusNotifierItemProxy extends Gio.DBusProxy { - new(...args: unknown[]): StatusNotifierItemProxy; - Category: string; - Id: string; - Title: string; - Status: string; - WindowId: number; - IconThemePath: string; - ItemIsMenu: boolean; - Menu: string; - IconName: string; - IconPixmap: [number, number, Uint8Array][]; - AttentionIconName: string; - AttentionIconPixmap: [number, number, Uint8Array][]; - ToolTip: [string, [number, number, Uint8Array], string, string]; - ContextMenuAsync: (x: number, y: number) => Promise; - ActivateAsync: (x: number, y: number) => Promise; - SecondaryActivateAsync: (x: number, y: number) => Promise; - ScrollAsync: (delta: number, orientation: string) => Promise; -} - -export interface AgsProxy extends Gio.DBusProxy { - new(...args: unknown[]): AgsProxy; - InspectorRemote: () => void; - QuitRemote: () => void; - ToggleWindowSync: (name: string) => boolean; - RunFileRemote: ( - js: string, - busName?: string, - objPath?: string, - ) => void; - RunJsRemote: ( - js: string, - busName?: string, - objPath?: string, - ) => void; - - // FIXME: deprecated - RunPromiseRemote: ( - js: string, - busName?: string, - objPath?: string, - ) => void; -} - -export interface StatusNotifierItemProxy extends Gio.DBusProxy { - new(...args: unknown[]): StatusNotifierItemProxy; - Category: string; - Id: string; - Title: string; - Status: string; - WindowId: number; - IconThemePath: string; - ItemIsMenu: boolean; - Menu: string; - IconName: string; - IconPixmap: [number, number, Uint8Array][]; - AttentionIconName: string; - AttentionIconPixmap: [number, number, Uint8Array][]; - ToolTip: [string, [number, number, Uint8Array], string, string]; - ContextMenuAsync: (x: number, y: number) => Promise; - ActivateAsync: (x: number, y: number) => Promise; - SecondaryActivateAsync: (x: number, y: number) => Promise; - ScrollAsync: (delta: number, orientation: string) => Promise; -} - -export interface PowerProfilesProxy extends Gio.DBusProxy { - new(...args: unknown[]): PowerProfilesProxy; - ActiveProfile: string; - PerformanceInhibited: string; - PerformanceDegraded: string; - Profiles: Array<{ [key: string]: GLib.Variant }>; - Actions: string[]; - ActiveProfileHolds: Array<{ [key: string]: GLib.Variant }>; - HoldProfile(profile: string, reason: string, application_id: string): number; - ReleaseProfile(cookie: number): void; -} diff --git a/src/main.ts b/src/main.ts deleted file mode 100644 index 2762745c..00000000 --- a/src/main.ts +++ /dev/null @@ -1,160 +0,0 @@ -import './overrides.js'; -import Gio from 'gi://Gio'; -import GLib from 'gi://GLib'; -import * as Utils from './utils.js'; -import app from './app.js'; -import client from './client.js'; -import { isRunning, parsePath, init } from './utils/init.js'; - -const BIN_NAME = pkg.name.split('.').pop()!; -const APP_BUS = (name: string) => `${pkg.name}.${name}`; -const APP_PATH = (name: string) => `/${pkg.name.split('.').join('/')}/${name}`; -const DEFAULT_CONF = `${GLib.get_user_config_dir()}/${BIN_NAME}/config.js`; - -const help = (bin: string) => `USAGE: - ${bin} [OPTIONS] - -OPTIONS: - -h, --help Print this help and exit - -v, --version Print version and exit - -q, --quit Kill AGS - -c, --config Path to the config file. Default: ${DEFAULT_CONF} - -b, --bus-name Bus name of the process - -i, --inspector Open up the Gtk debug tool - -t, --toggle-window Show or hide a window - -r, --run-js Execute string as an async function - -f, --run-file Execute file as an async function - --init Initialize the configuration directory - --clear-cache Remove ${Utils.CACHE_DIR} and exit`; - -export async function main(args: string[]) { - const flags = { - busName: BIN_NAME, - config: DEFAULT_CONF, - inspector: false, - runJs: '', - runFile: '', - toggleWindow: '', - quit: false, - init: false, - - // FIXME: deprecated - runPromise: '', - }; - - for (let i = 1; i < args.length; ++i) { - switch (args[i]) { - case 'version': - case '-v': - case '--version': - print(pkg.version); - return; - - case 'help': - case '-h': - case '--help': - print(help(args[0])); - return; - - case 'clear-cache': - case '--clear-cache': - try { - Gio.File.new_for_path(Utils.CACHE_DIR).trash(null); - } catch { /**/ } - app.quit(); - break; - - case '-b': - case '--bus-name': - flags.busName = args[++i]; - break; - - case '-c': - case '--config': - flags.config = parsePath(args[++i]); - break; - - case 'inspector': - case '-i': - case '--inspector': - flags.inspector = true; - break; - - case 'init': - case '--init': - flags.init = true; - break; - - case 'run-js': - case '-r': - case '--run-js': - flags.runJs = args[++i]; - break; - - case 'run-file': - case '-f': - case '--run-file': - flags.runFile = parsePath(args[++i]); - break; - - // FIXME: deprecated - case 'run-promise': - case '-p': - case '--run-promise': - flags.runPromise = args[++i]; - break; - - case 'toggle-window': - case '-t': - case '--toggle-window': - flags.toggleWindow = args[++i]; - break; - - case 'quit': - case '-q': - case '--quit': - flags.quit = true; - break; - - default: - console.error(`unknown option: ${args[i]}`); - break; - } - } - - const configDir = flags.config.split('/').slice(0, -1).join('/'); - const bus = APP_BUS(flags.busName); - const path = APP_PATH(flags.busName); - - if (flags.init) - return await init(configDir, flags.config); - - if (isRunning(bus, 'session')) { - return client(bus, path, flags); - } else { - if (flags.quit) - return; - - app.setup(bus, path, configDir, flags.config); - app.connect('config-parsed', () => { - if (flags.toggleWindow) - app.ToggleWindow(flags.toggleWindow); - - if (flags.runJs) - app.RunJs(flags.runJs); - - if (flags.runFile) - app.RunFile(flags.runFile); - - // FIXME: deprecated - if (flags.runPromise) - app.RunPromise(flags.runPromise); - - if (flags.inspector) - app.Inspector(); - }); - - // @ts-expect-error missing type declaration - return app.runAsync(null); - } -} diff --git a/src/meson.build b/src/meson.build deleted file mode 100644 index e25ab8c6..00000000 --- a/src/meson.build +++ /dev/null @@ -1,38 +0,0 @@ -# typescript -tsc = find_program('tsc', required: true) -tsc_out = meson.project_build_root() / 'tsc-out' -typescript = custom_target( - 'typescript-compile', - input: files( 'main.ts' ), - build_by_default: true, - build_always_stale: true, - command: [ tsc, '--outDir', tsc_out ], - output: ['tsc-output'], -) - -# launcher binary -configure_file( - input : app_id + '.js.in', - output : app_id, - configuration: { - 'GJS': find_program('gjs').full_path(), - 'APP_ID': app_id, - 'PACKAGE_VERSION': meson.project_version(), - 'PREFIX': prefix, - 'LIBDIR': libdir, - 'RESOURCE_PATH': '/com/github/Aylur/ags', - }, - install: true, - install_dir: pkgdatadir -) - -# gresource -import('gnome').compile_resources( - app_id + '.src', - app_id + '.src.gresource.xml', - dependencies: typescript, - source_dir: tsc_out, - gresource_bundle: true, - install: true, - install_dir : pkgdatadir -) diff --git a/src/overrides.ts b/src/overrides.ts deleted file mode 100644 index 98083921..00000000 --- a/src/overrides.ts +++ /dev/null @@ -1,39 +0,0 @@ -import Gtk from 'gi://Gtk?version=3.0'; -import GObject from 'gi://GObject'; - -const PROP_FILTER = ['parent', 'window', 'font-options', 'pixels']; - -// @ts-expect-error -GObject.Object.prototype.toJSON = function() { - const result = {}; - const props = (this.constructor as unknown as GObject.ObjectClass) - //@ts-expect-error - .list_properties() - .filter(p => !PROP_FILTER.includes(p.name || '')); - - props.forEach(p => { - try { - //@ts-expect-error - result[p.name] = this[p.name]; - } - catch (e) { - logError(e as object, p.name); - } - }); - return result; -}; - -Object.defineProperty(Gtk.Bin.prototype, 'child', { - get() { return this.get_child(); }, - set(child) { - const prev = this.get_child(); - if (prev) - this.remove(prev); - - if (prev !== child) - prev?.destroy(); - - this.add(child); - }, -}); - diff --git a/src/service.ts b/src/service.ts deleted file mode 100644 index 41d73202..00000000 --- a/src/service.ts +++ /dev/null @@ -1,108 +0,0 @@ -import GObject from 'gi://GObject'; -import { pspec, registerGObject, PspecFlag, PspecType } from './utils/gobject.js'; - -export type Connectable = { - connect: (sig: string, callback: (...args: unknown[]) => unknown) => number - disconnect: (id: number) => void -} - -export type OnlyString = S extends string ? S : never; - -export type Props = Omit any ? never : OnlyString -}[keyof T]>, 'g_type_instance'>; - -export type BindableProps = { - [K in keyof T]: Binding> | T[K]; -} - -export class Binding< - Emitter extends GObject.Object, - Prop extends keyof Props, - Return = Emitter[Prop], -> { - emitter: Emitter; - prop: Prop; - transformFn = (v: any) => v; // see #262 - constructor(emitter: Emitter, prop: Prop) { - this.emitter = emitter; - this.prop = prop; - } - - /** alias for transform */ - as(fn: (v: Return) => T) { return this.transform(fn); } - - transform(fn: (v: Return) => T) { - const bind = new Binding(this.emitter, this.prop); - const prev = this.transformFn; - bind.transformFn = (v: Return) => fn(prev(v)); - return bind; - } -} - -interface Services { - applications: typeof import('./service/applications.js').default - audio: typeof import('./service/audio.js').default - battery: typeof import('./service/battery.js').default - bluetooth: typeof import('./service/bluetooth.js').default - hyprland: typeof import('./service/hyprland.js').default - mpris: typeof import('./service/mpris.js').default - network: typeof import('./service/network.js').default - notifications: typeof import('./service/notifications.js').default - powerprofiles: typeof import('./service/powerprofiles.js').default - systemtray: typeof import('./service/systemtray.js').default - greetd: typeof import('./service/greetd.js').default -} - -export default class Service extends GObject.Object { - static { - GObject.registerClass({ - GTypeName: 'AgsService', - Signals: { 'changed': {} }, - }, this); - } - - static async import(service: S): Promise { - return (await import(`./service/${service}.js`)).default; - } - - static pspec(name: string, type: PspecType = 'jsobject', handle: PspecFlag = 'r') { - return pspec(name, type, handle); - } - - static register( - service: new (...args: any[]) => GObject.Object, - signals?: { [signal: string]: PspecType[] }, - properties?: { [prop: string]: [type?: PspecType, handle?: PspecFlag] }, - ) { - registerGObject(service, { signals, properties }); - } - - connect(signal = 'changed', callback: (_: this, ...args: any[]) => void): number { - return super.connect(signal, callback); - } - - updateProperty(prop: string, value: unknown) { - if (this[prop as keyof typeof this] === value || - JSON.stringify(this[prop as keyof typeof this]) === JSON.stringify(value)) - return; - - const privateProp = prop - .split('-') - .map((w, i) => i > 0 ? w.charAt(0).toUpperCase() + w.slice(1) : w) - .join(''); - - // @ts-expect-error - this[`_${privateProp}`] = value; - this.notify(prop); - } - - changed(property: string) { - this.notify(property); - this.emit('changed'); - } - - bind>(prop: Prop) { - return new Binding(this, prop); - } -} diff --git a/src/service/applications.ts b/src/service/applications.ts deleted file mode 100644 index bf0af1f3..00000000 --- a/src/service/applications.ts +++ /dev/null @@ -1,138 +0,0 @@ -import Gio from 'gi://Gio'; -import Service from '../service.js'; -import { CACHE_DIR, ensureDirectory, readFile, writeFile } from '../utils.js'; - -const APPS_CACHE_DIR = `${CACHE_DIR}/apps`; -const CACHE_FILE = APPS_CACHE_DIR + '/apps_frequency.json'; - -export class Application extends Service { - static { - Service.register(this, {}, { - 'app': ['jsobject'], - 'frequency': ['int'], - 'name': ['string'], - 'desktop': ['jsobject'], - 'description': ['jsobject'], - 'wm-class': ['jsobject'], - 'executable': ['string'], - 'icon-name': ['string'], - }); - } - - private _app: Gio.DesktopAppInfo; - private _frequency: number; - - get app() { return this._app; } - - get frequency() { return this._frequency; } - set frequency(value) { - this._frequency = value; - this.changed('frequency'); - } - - get name() { return this._app.get_name(); } - get desktop() { return this._app.get_id(); } - get description() { return this._app.get_description(); } - get wm_class() { return this._app.get_startup_wm_class(); } - get executable() { return this._app.get_string('Exec') || this._app.get_executable(); } - get icon_name() { return this._app.get_string('Icon'); } - - constructor(app: Gio.DesktopAppInfo, frequency?: number) { - super(); - this._app = app; - this._frequency = frequency || 0; - } - - private _match(prop: string | null, search: string) { - if (!prop) - return false; - - if (!search) - return true; - - return prop?.toLowerCase().includes(search.toLowerCase()); - } - - readonly getKey = (key: string) => { - return this._app.get_string(key); - }; - - readonly match = (term: string) => { - const { name, desktop, description, executable } = this; - return this._match(name, term) || - this._match(desktop, term) || - this._match(executable, term) || - this._match(description, term); - }; - - readonly launch = () => { - this.app.launch([], null); - this.frequency++; - }; -} - -export class Applications extends Service { - static { - Service.register(this, {}, { - 'list': ['jsobject'], - 'frequents': ['jsobject'], - }); - } - - private _list!: Application[]; - private _frequents: { [app: string]: number }; - - readonly query = (term: string) => { - return this._list.filter(app => app.match(term)).sort((a, b) => { - return a.frequency < b.frequency ? 1 : 0; - }); - }; - - constructor() { - super(); - Gio.AppInfoMonitor.get().connect('changed', this.reload.bind(this)); - - try { - this._frequents = - JSON.parse(readFile(CACHE_FILE)) as { [app: string]: number }; - } catch (_) { - this._frequents = {}; - } - - this.reload(); - } - - get list() { return this._list; } - get frequents() { return this._frequents; } - - private _launched(id: string | null) { - if (!id) - return; - - typeof this._frequents[id] === 'number' - ? this._frequents[id] += 1 - : this._frequents[id] = 1; - - ensureDirectory(APPS_CACHE_DIR); - const json = JSON.stringify(this._frequents, null, 2); - writeFile(json, CACHE_FILE).catch(err => console.error(err)); - this.changed('frequents'); - } - - readonly reload = () => { - this._list = Gio.AppInfo.get_all() - .filter(app => app.should_show()) - .map(app => Gio.DesktopAppInfo.new(app.get_id() || '')) - .filter(app => app) - .map(app => new Application(app, this.frequents[app.get_id() || ''])); - - this._list.forEach(app => app.connect('notify::frequency', () => { - this._launched(app.desktop); - })); - - this.changed('list'); - }; -} - -export const applications = new Applications; -export default applications; diff --git a/src/service/audio.ts b/src/service/audio.ts deleted file mode 100644 index 72b50991..00000000 --- a/src/service/audio.ts +++ /dev/null @@ -1,248 +0,0 @@ -import Service from '../service.js'; -import GObject from 'gi://GObject'; -import Gvc from 'gi://Gvc'; -import { bulkConnect, bulkDisconnect } from '../utils.js'; - -const _MIXER_CONTROL_STATE = { - [Gvc.MixerControlState.CLOSED]: 'closed', - [Gvc.MixerControlState.READY]: 'ready', - [Gvc.MixerControlState.CONNECTING]: 'connecting', - [Gvc.MixerControlState.FAILED]: 'failed', -}; - -export class Stream extends Service { - static { - Service.register(this, { - 'closed': [], - }, { - 'application-id': ['string'], - 'description': ['string'], - 'is-muted': ['boolean'], - 'volume': ['float', 'rw'], - 'icon-name': ['string'], - 'id': ['int'], - 'state': ['string'], - 'stream': ['jsobject'], - }); - } - - private _stream?: Gvc.MixerStream; - private _ids?: number[]; - private _oldVolume = 0; - - readonly setStream = (stream: Gvc.MixerStream | null) => { - if (this._ids) - bulkDisconnect((this._stream as unknown) as GObject.Object, this._ids); - - if (!stream) - return; - - this._stream = stream; - this._ids = [ - 'application-id', - 'description', - 'is-muted', - 'volume', - 'icon-name', - 'id', - 'state', - ].map(prop => { - this.notify(prop); - return stream.connect(`notify::${prop}`, () => { - this.changed(prop); - }); - }); - - this.changed('stream'); - }; - - constructor(stream?: Gvc.MixerStream) { - super(); - this.setStream(stream || null); - } - - get application_id() { return this._stream?.application_id ?? null; } - get stream() { return this._stream ?? null; } - get description() { return this._stream?.description ?? null; } - get icon_name() { return this._stream?.icon_name ?? null; } - get id() { return this._stream?.id ?? null; } - get name() { return this._stream?.name ?? null; } - get state() { - return _MIXER_CONTROL_STATE[this._stream?.state || Gvc.MixerControlState.CLOSED]; - } - - get is_muted(): boolean | null { - return this._stream?.is_muted ?? null; - } - - set is_muted(mute: boolean) { - if (this._stream) { - this._stream.is_muted = mute; - this._stream.change_is_muted(mute); - } - } - - get volume() { - const max = audio.control.get_vol_max_norm(); - return this._stream ? this._stream.volume / max : 0; - } - - set volume(value) { // 0..100 - if (value > (audio.maxStreamVolume)) - value = (audio.maxStreamVolume); - - if (value < 0) - value = 0; - - const max = audio.control.get_vol_max_norm(); - this._stream?.set_volume(value * max); - this._stream?.push_volume(); - } - - readonly close = () => { - this.setStream(null); - this.emit('closed'); - }; -} - -export class Audio extends Service { - static { - Service.register(this, { - 'speaker-changed': [], - 'microphone-changed': [], - 'stream-added': ['int'], - 'stream-removed': ['int'], - }, { - 'apps': ['jsobject'], - 'recorders': ['jsobject'], - 'speakers': ['jsobject'], - 'microphones': ['jsobject'], - 'speaker': ['jsobject', 'rw'], - 'microphone': ['jsobject', 'rw'], - }); - } - - public maxStreamVolume = 1.5; - - private _control: Gvc.MixerControl; - private _streams: Map; - private _streamBindings: Map; - private _speaker!: Stream; - private _microphone!: Stream; - - constructor() { - super(); - - this._control = new Gvc.MixerControl({ - name: `${pkg.name} mixer control`, - }); - - this._streams = new Map(); - this._streamBindings = new Map(); - for (const s of ['speaker', 'microphone'] as const) { - this[`_${s}`] = new Stream(); - this[`_${s}`].connect('changed', () => { - this.emit(`${s}-changed`); - this.emit('changed'); - }); - } - - bulkConnect(this._control as unknown as GObject.Object, [ - ['default-sink-changed', (_c, id: number) => this._defaultChanged(id, 'speaker')], - ['default-source-changed', (_c, id: number) => this._defaultChanged(id, 'microphone')], - ['stream-added', this._streamAdded.bind(this)], - ['stream-removed', this._streamRemoved.bind(this)], - ]); - - this._control.open(); - } - - get control() { return this._control; } - - get speaker() { return this._speaker; } - set speaker(stream: Stream) { - this._control.set_default_sink(stream.stream!); - } - - get microphone() { return this._microphone; } - set microphone(stream: Stream) { - this._control.set_default_source(stream.stream!); - } - - get microphones() { return this._getStreams(Gvc.MixerSource); } - get speakers() { return this._getStreams(Gvc.MixerSink); } - get apps() { return this._getStreams(Gvc.MixerSinkInput); } - get recorders() { return this._getStreams(Gvc.MixerSourceOutput); } - - readonly getStream = (id: number) => { - return this._streams.get(id); - }; - - private _defaultChanged(id: number, type: 'speaker' | 'microphone') { - const stream = this._streams.get(id); - if (!stream) - return; - - this[`_${type}`].setStream(stream.stream); - this.changed(type); - this.emit(`${type}-changed`); - } - - private _streamAdded(_c: Gvc.MixerControl, id: number) { - if (this._streams.has(id)) - return; - - const gvcstream = this._control.lookup_stream_id(id); - const stream = new Stream(gvcstream); - const binding = stream.connect('changed', () => this.emit('changed')); - - this._streams.set(id, stream); - this._streamBindings.set(id, binding); - - this._notifyStreams(stream); - this.emit('stream-added', id); - this.emit('changed'); - } - - private _streamRemoved(_c: Gvc.MixerControl, id: number) { - const stream = this._streams.get(id); - if (!stream) - return; - - stream.disconnect(this._streamBindings.get(id) as number); - stream.close(); - - this._streams.delete(id); - this._streamBindings.delete(id); - this.emit('stream-removed', id); - - this._notifyStreams(stream); - this.emit('changed'); - } - - private _getStreams(filter: { new(): Gvc.MixerStream }) { - const list = []; - for (const [, stream] of this._streams) { - if (stream.stream instanceof filter) - list.push(stream); - } - return list; - } - - private _notifyStreams(stream: Stream) { - if (stream.stream instanceof Gvc.MixerSource) - this.notify('microphones'); - - if (stream.stream instanceof Gvc.MixerSink) - this.notify('speakers'); - - if (stream.stream instanceof Gvc.MixerSinkInput) - this.notify('apps'); - - if (stream.stream instanceof Gvc.MixerSourceOutput) - this.notify('recorders'); - } -} - -const audio = new Audio; -export default audio; diff --git a/src/service/battery.ts b/src/service/battery.ts deleted file mode 100644 index aea8fcc2..00000000 --- a/src/service/battery.ts +++ /dev/null @@ -1,105 +0,0 @@ -import Gio from 'gi://Gio'; -import Service from '../service.js'; -import { idle, loadInterfaceXML } from '../utils.js'; -import { type BatteryProxy } from '../dbus/types.js'; - -const BatteryIFace = loadInterfaceXML('org.freedesktop.UPower.Device')!; -const PowerManagerProxy = Gio.DBusProxy.makeProxyWrapper(BatteryIFace) as unknown as BatteryProxy; - -const DeviceState = { - CHARGING: 1, - FULLY_CHARGED: 4, -}; - -export class Battery extends Service { - static { - Service.register(this, { - 'closed': [], - }, { - 'available': ['boolean'], - 'percent': ['int'], - 'charging': ['boolean'], - 'charged': ['boolean'], - 'icon-name': ['string'], - 'time-remaining': ['float'], - 'energy': ['float'], - 'energy-full': ['float'], - 'energy-rate': ['float'], - }); - } - - private _proxy: BatteryProxy; - - private _available = false; - private _percent = -1; - private _charging = false; - private _charged = false; - private _iconName = 'battery-missing-symbolic'; - private _timeRemaining = 0; - private _energy = 0.0; - private _energyFull = 0.0; - private _energyRate = 0.0; - - get available() { return this._available; } - get percent() { return this._percent; } - get charging() { return this._charging; } - get charged() { return this._charged; } - get icon_name() { return this._iconName; } - get time_remaining() { return this._timeRemaining; } - get energy() { return this._energy; } - get energy_full() { return this._energyFull; } - get energy_rate() { return this._energyRate; } - - constructor() { - super(); - - this._proxy = new PowerManagerProxy( - Gio.DBus.system, - 'org.freedesktop.UPower', - '/org/freedesktop/UPower/devices/DisplayDevice'); - - this._proxy.connect('g-properties-changed', () => this._sync()); - idle(this._sync.bind(this)); - } - - private _sync() { - if (!this._proxy.IsPresent) - return this.updateProperty('available', false); - - const charging = this._proxy.State === DeviceState.CHARGING; - const percent = this._proxy.Percentage; - const charged = - this._proxy.State === DeviceState.FULLY_CHARGED || - (this._proxy.State === DeviceState.CHARGING && percent === 100); - - const level = Math.floor(percent / 10) * 10; - const state = this._proxy.State === - DeviceState.CHARGING ? '-charging' : ''; - - const iconName = charged - ? 'battery-level-100-charged-symbolic' - : `battery-level-${level}${state}-symbolic`; - - const timeRemaining = charging ? this._proxy.TimeToFull : this._proxy.TimeToEmpty; - - const energy = this._proxy.Energy; - - const energyFull = this._proxy.EnergyFull; - - const energyRate = this._proxy.EnergyRate; - - this.updateProperty('available', true); - this.updateProperty('icon-name', iconName); - this.updateProperty('percent', percent); - this.updateProperty('charging', charging); - this.updateProperty('charged', charged); - this.updateProperty('time-remaining', timeRemaining); - this.updateProperty('energy', energy); - this.updateProperty('energy-full', energyFull); - this.updateProperty('energy-rate', energyRate); - this.emit('changed'); - } -} - -export const battery = new Battery; -export default battery; diff --git a/src/service/bluetooth.ts b/src/service/bluetooth.ts deleted file mode 100644 index ef903fe1..00000000 --- a/src/service/bluetooth.ts +++ /dev/null @@ -1,202 +0,0 @@ -// @ts-expect-error missing types -import GnomeBluetooth from 'gi://GnomeBluetooth?version=3.0'; -import Service from '../service.js'; -import Gio from 'gi://Gio'; -import { bulkConnect, bulkDisconnect } from '../utils.js'; - -const _ADAPTER_STATE = { - [GnomeBluetooth.AdapterState.ABSENT]: 'absent', - [GnomeBluetooth.AdapterState.ON]: 'on', - [GnomeBluetooth.AdapterState.TURNING_ON]: 'turning-on', - [GnomeBluetooth.AdapterState.TURNING_OFF]: 'turning-off', - [GnomeBluetooth.AdapterState.OFF]: 'off', -}; - -export class BluetoothDevice extends Service { - static { - Service.register(this, {}, { - 'address': ['string'], - 'alias': ['string'], - 'battery-level': ['int'], - 'battery-percentage': ['int'], - 'connected': ['boolean'], - 'icon-name': ['string'], - 'name': ['string'], - 'paired': ['boolean'], - 'trusted': ['boolean'], - 'type': ['string'], - 'connecting': ['boolean'], - }); - } - - private _device: GnomeBluetooth.Device; - private _ids: number[]; - private _connecting = false; - - get device() { return this._device; } - - constructor(device: GnomeBluetooth.Device) { - super(); - - this._device = device; - this._ids = [ - 'address', - 'alias', - 'battery-level', - 'battery-percentage', - 'connected', - 'name', - 'paired', - 'trusted', - ].map(prop => device.connect(`notify::${prop}`, () => { - this.changed(prop); - })); - - this._ids.push(device.connect('notify::icon', () => { - this.changed('icon-name'); - })); - } - - close() { - bulkDisconnect(this._device, this._ids); - } - - get address() { return this._device.address; } - get alias() { return this._device.alias; } - get battery_level() { return this._device.battery_level; } - get battery_percentage() { return this._device.battery_percentage; } - get connected() { return this._device.connected; } - get icon_name() { return this._device.icon; } - get name() { return this._device.name; } - get paired() { return this._device.paired; } - get trusted() { return this._device.trusted; } - get type() { return GnomeBluetooth.type_to_string(this._device.type); } - get connecting() { return this._connecting || false; } - - readonly setConnection = (connect: boolean) => { - this._connecting = true; - bluetooth.connectDevice(this, connect, () => { - this._connecting = false; - this.changed('connecting'); - }); - this.changed('connecting'); - }; -} - -export class Bluetooth extends Service { - static { - Service.register(this, { - 'device-added': ['string'], - 'device-removed': ['string'], - }, { - 'devices': ['jsobject'], - 'connected-devices': ['jsobject'], - 'enabled': ['boolean', 'rw'], - 'state': ['string'], - }); - } - - private _client: GnomeBluetooth.Client; - private _devices: Map; - - constructor() { - super(); - - this._devices = new Map(); - this._client = new GnomeBluetooth.Client(); - bulkConnect(this._client, [ - ['device-added', this._deviceAdded.bind(this)], - ['device-removed', this._deviceRemoved.bind(this)], - ['notify::default-adapter-state', () => this.changed('state')], - ['notify::default-adapter-powered', () => this.changed('enabled')], - ]); - - this._getDevices().forEach(device => this._deviceAdded(this, device)); - } - - readonly toggle = () => { - this._client.default_adapter_powered = !this._client.default_adapter_powered; - }; - - private _getDevices() { - const devices = []; - const deviceStore = this._client.get_devices(); - - for (let i = 0; i < deviceStore.get_n_items(); ++i) { - const device = deviceStore.get_item(i); - - if (device.paired || device.trusted) - devices.push(device); - } - - return devices; - } - - private _deviceAdded(_: GnomeBluetooth.Client, device: GnomeBluetooth.Device) { - if (this._devices.has(device.address)) - return; - - const d = new BluetoothDevice(device); - d.connect('changed', () => this.emit('changed')); - d.connect('notify::connected', () => this.notify('connected-devices')); - this._devices.set(device.address, d); - this.changed('devices'); - this.emit('device-added', device.address); - } - - private _deviceRemoved(_: GnomeBluetooth.Client, path: string) { - const device = this.devices.find(d => d.device.get_object_path() === path); - if (!device || !this._devices.has(device.address)) - return; - - this._devices.get(device.address)?.close(); - this._devices.delete(device.address); - this.notify('devices'); - this.notify('connected-devices'); - this.emit('changed'); - this.emit('device-removed', device.address); - } - - readonly connectDevice = ( - device: BluetoothDevice, - connect: boolean, - callback: (s: boolean) => void, - ) => { - this._client.connect_service( - device.device.get_object_path(), - connect, - null, - (client: GnomeBluetooth.Client, res: Gio.AsyncResult) => { - try { - const s = client.connect_service_finish(res); - callback(s); - - this.changed('connected-devices'); - } catch (error) { - logError(error); - callback(false); - } - }, - ); - }; - - readonly getDevice = (address: string) => this._devices.get(address); - - set enabled(v) { this._client.default_adapter_powered = v; } - get enabled() { return this.state === 'on' || this.state === 'turning-on'; } - - get state() { return _ADAPTER_STATE[this._client.default_adapter_state]; } - - get devices() { return Array.from(this._devices.values()); } - get connected_devices() { - const list = []; - for (const [, device] of this._devices) { - if (device.connected) - list.push(device); - } - return list; - } -} - -export const bluetooth = new Bluetooth; -export default bluetooth; diff --git a/src/service/greetd.ts b/src/service/greetd.ts deleted file mode 100644 index 10a475ed..00000000 --- a/src/service/greetd.ts +++ /dev/null @@ -1,115 +0,0 @@ -import App from '../app.js'; -import Service from '../service.js'; -import GLib from 'gi://GLib'; -import Gio from 'gi://Gio'; - -Gio._promisify(Gio.InputStream.prototype, 'read_bytes_async'); -const SOCK = GLib.getenv('GREETD_SOCK'); - -type Request = { - create_session: { - username: string - } - post_auth_message_response: { - response?: string - } - start_session: { - cmd: string[] - env: string[] - } - cancel_session: Record -} - -type Response = { - type: 'success' -} | { - type: 'error' - error_type: 'auth_error' | 'error' - description: string -} | { - type: 'auth_message' - auth_message_type: 'visible' | 'secret' | 'info' | 'error' - auth_message: string -} - -export class Greetd extends Service { - static { Service.register(this); } - - private _decoder = new TextDecoder; - - readonly login = async ( - username: string, - password: string, - cmd: string[] | string, - env: string[] = [], - ) => { - const session = await this.createSession(username); - if (session.type !== 'auth_message') { - this.cancelSession(); - throw session; - } - - const auth = await this.postAuth(password); - if (auth.type !== 'success') { - this.cancelSession(); - throw auth; - } - - const start = await this.startSession(cmd, env); - if (start.type !== 'success') { - this.cancelSession(); - throw start; - } - - App.quit(); - }; - - readonly createSession = (username: string) => { - return this._send('create_session', { username }); - }; - - readonly postAuth = (response?: string) => { - return this._send('post_auth_message_response', { response }); - }; - - readonly startSession = (cmd: string[] | string, env: string[] = []) => { - const cmdv = Array.isArray(cmd) - ? cmd - : GLib.shell_parse_argv(cmd)[1]; - - return this._send('start_session', { cmd: cmdv, env }); - }; - - readonly cancelSession = () => { - return this._send('cancel_session', {}); - }; - - private async _send(req: R, payload: Request[R]): Promise { - const connection = new Gio.SocketClient() - .connect(new Gio.UnixSocketAddress({ path: SOCK }), null); - - try { - const json = JSON.stringify({ type: req, ...payload }); - const ostream = new Gio.DataOutputStream({ - close_base_stream: true, - base_stream: connection.get_output_stream(), - byte_order: Gio.DataStreamByteOrder.HOST_ENDIAN, - }); - - const istream = connection.get_input_stream(); - - ostream.put_int32(json.length, null); - ostream.put_string(json, null); - - const data = await istream.read_bytes_async(4, GLib.PRIORITY_DEFAULT, null); - const length = new Uint32Array(data.get_data()?.buffer || [0])[0]; - const res = await istream.read_bytes_async(length, GLib.PRIORITY_DEFAULT, null); - return JSON.parse(this._decoder.decode(res.get_data()!)) as Response; - } finally { - connection.close(null); - } - } -} - -export const greetd = new Greetd; -export default greetd; diff --git a/src/service/hyprland.ts b/src/service/hyprland.ts deleted file mode 100644 index 8dde935c..00000000 --- a/src/service/hyprland.ts +++ /dev/null @@ -1,463 +0,0 @@ -import Gdk from 'gi://Gdk?version=3.0'; -import GLib from 'gi://GLib'; -import Gio from 'gi://Gio'; -import Service from '../service.js'; - -Gio._promisify(Gio.DataInputStream.prototype, 'read_upto_async'); - -const HIS = GLib.getenv('HYPRLAND_INSTANCE_SIGNATURE'); -const XDG_RUNTIME_DIR = GLib.getenv('XDG_RUNTIME_DIR') || '/'; - -export class ActiveClient extends Service { - static { - Service.register(this, {}, { - 'address': ['string'], - 'title': ['string'], - 'class': ['string'], - }); - } - - private _address = ''; - private _title = ''; - private _class = ''; - - get address() { return this._address; } - get title() { return this._title; } - get class() { return this._class; } -} - -export class ActiveID extends Service { - static { - Service.register(this, {}, { - 'id': ['int'], - 'name': ['string'], - }); - } - - private _id = 1; - private _name = ''; - - get id() { return this._id; } - get name() { return this._name; } - - update(id: number, name: string) { - super.updateProperty('id', id); - super.updateProperty('name', name); - } -} - -export class Actives extends Service { - static { - Service.register(this, {}, { - 'client': ['jsobject'], - 'monitor': ['jsobject'], - 'workspace': ['jsobject'], - }); - } - - private _client = new ActiveClient; - private _monitor = new ActiveID; - private _workspace = new ActiveID; - - constructor() { - super(); - - (['client', 'workspace', 'monitor'] as const).forEach(obj => { - this[`_${obj}`].connect('changed', () => { - this.notify(obj); - this.emit('changed'); - }); - }); - } - - get client() { return this._client; } - get monitor() { return this._monitor; } - get workspace() { return this._workspace; } -} - -export class Hyprland extends Service { - static { - Service.register(this, { - 'event': ['string', 'string'], - 'urgent-window': ['string'], - 'submap': ['string'], - 'keyboard-layout': ['string', 'string'], - 'monitor-added': ['string'], - 'monitor-removed': ['string'], - 'workspace-added': ['string'], - 'workspace-removed': ['string'], - 'client-added': ['string'], - 'client-removed': ['string'], - 'fullscreen': ['boolean'], - }, { - 'active': ['jsobject'], - 'monitors': ['jsobject'], - 'workspaces': ['jsobject'], - 'clients': ['jsobject'], - }); - } - - private _active: Actives = new Actives(); - private _monitors: Map = new Map(); - private _workspaces: Map = new Map(); - private _clients: Map = new Map(); - private _decoder = new TextDecoder(); - private _encoder = new TextEncoder(); - - get active() { return this._active; } - get monitors() { return Array.from(this._monitors.values()); } - get workspaces() { return Array.from(this._workspaces.values()); } - get clients() { return Array.from(this._clients.values()); } - - readonly getMonitor = (id: number) => this._monitors.get(id); - readonly getWorkspace = (id: number) => this._workspaces.get(id); - readonly getClient = (address: string) => this._clients.get(address); - - readonly getGdkMonitor = (id: number) => { - const monitor = this._monitors.get(id); - if (!monitor) - return null; - - return Gdk.Display.get_default()?.get_monitor_at_point(monitor.x, monitor.y) || null; - }; - - constructor() { - if (!HIS) - console.error('Hyprland is not running'); - - super(); - - // init monitor - for (const m of JSON.parse(this.message('j/monitors')) as Monitor[]) { - this._monitors.set(m.id, m); - if (m.focused) { - this._active.monitor.update(m.id, m.name); - this._active.workspace.update(m.activeWorkspace.id, m.activeWorkspace.name); - } - } - - // init workspaces - for (const ws of JSON.parse(this.message('j/workspaces')) as Workspace[]) - this._workspaces.set(ws.id, ws); - - // init clients - for (const c of JSON.parse(this.message('j/clients')) as Client[]) - this._clients.set(c.address, c); - - this._watchSocket(new Gio.DataInputStream({ - close_base_stream: true, - base_stream: this._connection('socket2') - .get_input_stream(), - })); - - this._active.connect('changed', () => this.changed('active')); - } - - private _connection(socket: 'socket' | 'socket2') { - const sock = (pre: string) => `${pre}/hypr/${HIS}/.${socket}.sock`; - - const path = GLib.file_test(sock(XDG_RUNTIME_DIR), GLib.FileTest.EXISTS) - ? sock(XDG_RUNTIME_DIR) - : sock('/tmp'); - - return new Gio.SocketClient() - .connect(new Gio.UnixSocketAddress({ path }), null); - } - - private _watchSocket(stream: Gio.DataInputStream) { - stream.read_line_async(0, null, (stream, result) => { - if (!stream) - return console.error('Error reading Hyprland socket'); - - const [line] = stream.read_line_finish(result); - this._onEvent(this._decoder.decode(line)); - this._watchSocket(stream); - }); - } - - // FIXME: deprecated - readonly sendMessage = (cmd: string) => { - console.warn('hyprland.sendMessage is DEPRECATED, ' - + ' use hyprland.message or hyprland.messageAsync'); - return this.messageAsync(cmd); - }; - - private _socketStream(cmd: string) { - const connection = this._connection('socket'); - - connection - .get_output_stream() - .write(this._encoder.encode(cmd), null); - - const stream = new Gio.DataInputStream({ - close_base_stream: true, - base_stream: connection.get_input_stream(), - }); - - return [connection, stream] as const; - } - - readonly message = (cmd: string) => { - const [connection, stream] = this._socketStream(cmd); - try { - const [response] = stream.read_upto('\x04', -1, null); - return response || ''; - } catch (error) { - logError(error); - } finally { - connection.close(null); - } - return ''; - }; - - readonly messageAsync = async (cmd: string) => { - const [connection, stream] = this._socketStream(cmd); - try { - const result = await stream.read_upto_async('\x04', -1, 0, null); - const [response] = result as unknown as [string, number]; - return response; - } catch (error) { - logError(error); - } finally { - connection.close(null); - } - return ''; - }; - - private async _syncMonitors(notify = true) { - try { - const msg = await this.messageAsync('j/monitors'); - this._monitors.clear(); - for (const m of JSON.parse(msg) as Array) { - this._monitors.set(m.id, m); - if (m.focused) { - this._active.monitor.update(m.id, m.name); - this._active.workspace.update(m.activeWorkspace.id, m.activeWorkspace.name); - this._active.monitor.emit('changed'); - this._active.workspace.emit('changed'); - } - } - if (notify) - this.notify('monitors'); - } catch (error) { - logError(error); - } - } - - private async _syncWorkspaces(notify = true) { - try { - const msg = await this.messageAsync('j/workspaces'); - this._workspaces.clear(); - for (const ws of JSON.parse(msg) as Array) - this._workspaces.set(ws.id, ws); - - if (notify) - this.notify('workspaces'); - } catch (error) { - logError(error); - } - } - - private async _syncClients(notify = true) { - try { - const msg = await this.messageAsync('j/clients'); - this._clients.clear(); - for (const c of JSON.parse(msg) as Array) - this._clients.set(c.address, c); - - if (notify) - this.notify('clients'); - } catch (error) { - logError(error); - } - } - - private async _onEvent(event: string) { - if (!event) - return; - - const [e, params] = event.split('>>'); - const argv = params.split(','); - - try { - switch (e) { - case 'workspace': - case 'focusedmon': - await this._syncMonitors(); - break; - - case 'monitorremoved': - await this._syncMonitors(); - this.emit('monitor-removed', argv[0]); - break; - - case 'monitoradded': - await this._syncMonitors(); - this.emit('monitor-added', argv[0]); - break; - - case 'createworkspace': - await this._syncWorkspaces(); - this.emit('workspace-added', argv[0]); - break; - - case 'destroyworkspace': - await this._syncWorkspaces(); - this.emit('workspace-removed', argv[0]); - break; - - case 'openwindow': - await this._syncClients(false); - await this._syncWorkspaces(false); - ['clients', 'workspaces'].forEach(e => this.notify(e)); - this.emit('client-added', '0x' + argv[0]); - break; - - case 'movewindow': - case 'windowtitle': - await this._syncClients(false); - await this._syncWorkspaces(false); - ['clients', 'workspaces'].forEach(e => this.notify(e)); - break; - - case 'moveworkspace': - await this._syncClients(false); - await this._syncWorkspaces(false); - await this._syncMonitors(false); - ['clients', 'workspaces', 'monitors'].forEach(e => this.notify(e)); - break; - - case 'fullscreen': - await this._syncClients(false); - await this._syncWorkspaces(false); - ['clients', 'workspaces'].forEach(e => this.notify(e)); - this.emit('fullscreen', argv[0] === '1'); - break; - - case 'activewindow': - this._active.client.updateProperty('class', argv[0]); - this._active.client.updateProperty('title', argv.slice(1).join(',')); - this._active.client.emit('changed'); - break; - - case 'activewindowv2': - this._active.client.updateProperty('address', '0x' + argv[0]); - this._active.client.emit('changed'); - break; - - case 'closewindow': - await this._syncWorkspaces(false); - await this._syncClients(false); - if (this._active.client.address === '0x' + argv[0]) { - this._active.client.updateProperty('class', ''); - this._active.client.updateProperty('title', ''); - this._active.client.updateProperty('address', ''); - this._active.client.emit('changed'); - } - ['clients', 'workspaces'].forEach(e => this.notify(e)); - this.emit('client-removed', '0x' + argv[0]); - break; - - case 'urgent': - this.emit('urgent-window', '0x' + argv[0]); - break; - - case 'activelayout': - this.emit('keyboard-layout', `${argv[0]}`, `${argv[1]}`); - break; - - case 'changefloatingmode': { - await this._syncClients(); - break; - } - case 'submap': - this.emit('submap', argv[0]); - break; - - default: - break; - } - } catch (error) { - if (error instanceof Error) - console.error(error.message); - } - - this.emit('event', e, params); - this.emit('changed'); - } -} - -export interface Monitor { - id: number, - name: string, - description: string, - make: string, - model: string, - serial: string, - width: number, - height: number, - refreshRate: number - x: number - y: number - activeWorkspace: { - id: number - name: string - } - specialWorkspace: { - id: number - name: string - }, - reserved: [ - number, - number, - number, - number, - ] - scale: number - transform: number - focused: boolean - dpmsStatus: boolean - vrr: boolean - activelyTearing: boolean -} - -export interface Workspace { - id: number - name: string - monitor: string - monitorID: number - windows: number - hasfullscreen: boolean - lastwindow: string - lastwindowtitle: string -} - -export interface Client { - address: string - mapped: boolean - hidden: boolean - at: [number, number] - size: [number, number] - workspace: { - id: number - name: string - } - floating: boolean - monitor: number - class: string - title: string - initialClass: string - initialTitle: string - pid: number - xwayland: boolean - pinned: boolean - fullscreen: boolean - fullscreenMode: number - fakeFullscreen: boolean - grouped: [string], - swallowing: string - focusHistoryID: number -} - -export const hyprland = new Hyprland; -export default hyprland; diff --git a/src/service/mpris.ts b/src/service/mpris.ts deleted file mode 100644 index b34c3b5a..00000000 --- a/src/service/mpris.ts +++ /dev/null @@ -1,384 +0,0 @@ -import GLib from 'gi://GLib'; -import Gio from 'gi://Gio'; -import Service from '../service.js'; -import { ensureDirectory, idle } from '../utils.js'; -import { CACHE_DIR } from '../utils.js'; -import { loadInterfaceXML } from '../utils.js'; -import { DBusProxy, PlayerProxy, MprisProxy } from '../dbus/types.js'; - -const DBusIFace = loadInterfaceXML('org.freedesktop.DBus')!; -const PlayerIFace = loadInterfaceXML('org.mpris.MediaPlayer2.Player')!; -const MprisIFace = loadInterfaceXML('org.mpris.MediaPlayer2')!; -const DBusProxy = Gio.DBusProxy.makeProxyWrapper(DBusIFace) as unknown as DBusProxy; -const PlayerProxy = Gio.DBusProxy.makeProxyWrapper(PlayerIFace) as unknown as PlayerProxy; -const MprisProxy = Gio.DBusProxy.makeProxyWrapper(MprisIFace) as unknown as MprisProxy; - -const DBUS_PREFIX = 'org.mpris.MediaPlayer2.'; -const MEDIA_CACHE_PATH = `${CACHE_DIR}/media`; - -type PlaybackStatus = 'Playing' | 'Paused' | 'Stopped'; -type LoopStatus = 'None' | 'Track' | 'Playlist'; -type MprisMetadata = { - 'mpris:trackid'?: string - 'mpris:length'?: number - 'mpris:artUrl'?: string - 'xesam:album'?: string - 'xesam:albumArtist'?: string - 'xesam:artist'?: string[] - 'xesam:asText'?: string - 'xesam:audioBPM'?: number - 'xesam:autoRating'?: number - 'xesam:comment'?: string[] - 'xesam:composer'?: string[] - 'xesam:contentCreated'?: string - 'xesam:discNumber'?: number - 'xesam:firstUsed'?: string - 'xesam:genre'?: string[] - 'xesam:lastUsed'?: string - 'xesam:lyricist'?: string[] - 'xesam:title'?: string - 'xesam:trackNumber'?: number - 'xesam:url'?: string - 'xesam:useCount'?: number - 'xesam:userRating'?: number - [key: string]: unknown -} - -export class MprisPlayer extends Service { - static { - Service.register(this, { - 'closed': [], - 'position': ['int'], - }, { - 'bus-name': ['string'], - 'name': ['string'], - 'entry': ['string'], - 'identity': ['string'], - 'metadata': ['string'], - 'trackid': ['string'], - 'track-artists': ['jsobject'], - 'track-title': ['string'], - 'track-album': ['string'], - 'track-cover-url': ['string'], - 'cover-path': ['string'], - 'play-back-status': ['string'], - 'can-go-next': ['boolean'], - 'can-go-prev': ['boolean'], - 'can-play': ['boolean'], - 'shuffle-status': ['jsobject'], - 'loop-status': ['jsobject'], - 'length': ['int'], - 'position': ['float', 'rw'], - 'volume': ['float', 'rw'], - }); - } - - get bus_name() { return this._busName; } - get name() { return this._name; } - get entry() { return this._entry; } - get identity() { return this._identity; } - get metadata() { return this._metadata; } - - get trackid() { return this._trackid; } - get track_artists() { return this._trackArtists; } - get track_title() { return this._trackTitle; } - get track_album() { return this._trackAlbum; } - get track_cover_url() { return this._trackCoverUrl; } - get cover_path() { return this._coverPath; } - get play_back_status() { return this._playBackStatus; } - get can_go_next() { return this._canGoNext; } - get can_go_prev() { return this._canGoPrev; } - get can_play() { return this._canPlay; } - get shuffle_status() { return this._shuffleStatus; } - get loop_status() { return this._loopStatus; } - get length() { return this._length; } - - private _busName: string; - private _name: string; - private _entry!: string; - private _identity!: string; - private _metadata: MprisMetadata = {}; - - private _trackid!: string; - private _trackArtists!: string[]; - private _trackTitle!: string; - private _trackAlbum!: string; - private _trackCoverUrl!: string; - private _coverPath!: string; - private _playBackStatus!: PlaybackStatus; - private _canGoNext!: boolean; - private _canGoPrev!: boolean; - private _canPlay!: boolean; - private _shuffleStatus!: boolean | null; - private _loopStatus!: LoopStatus | null; - private _length!: number; - - private _binding = { mpris: 0, player: 0 }; - private _mprisProxy: MprisProxy; - private _playerProxy: PlayerProxy; - - constructor(busName: string) { - super(); - - this._busName = busName; - this._name = busName.substring(23).split('.')[0]; - - this._mprisProxy = new MprisProxy( - Gio.DBus.session, busName, - '/org/mpris/MediaPlayer2'); - - this._playerProxy = new PlayerProxy( - Gio.DBus.session, busName, - '/org/mpris/MediaPlayer2'); - - this._onPlayerProxyReady(); - this._onMprisProxyReady(); - this._updateState(); - idle(this._updateState.bind(this)); - } - - close() { - this._mprisProxy?.disconnect(this._binding.mpris); - this._playerProxy?.disconnect(this._binding.player); - this.emit('closed'); - } - - private _onMprisProxyReady() { - this._binding.mpris = this._mprisProxy.connect( - 'notify::g-name-owner', - () => { - if (!this._mprisProxy.g_name_owner) - this.close(); - }); - - this._identity = this._mprisProxy.Identity; - this._entry = this._mprisProxy.DesktopEntry; - if (!this._mprisProxy.g_name_owner) - this.close(); - } - - private _onPlayerProxyReady() { - this._binding.player = this._playerProxy.connect( - 'g-properties-changed', () => this._updateState()); - } - - private _updateState() { - const metadata = {} as MprisMetadata; - for (const prop in this._playerProxy.Metadata) - metadata[prop] = this._playerProxy.Metadata[prop].deepUnpack(); - - let trackArtists = metadata['xesam:artist']; - if (!Array.isArray(trackArtists) || - !trackArtists.every(artist => typeof artist === 'string')) - trackArtists = ['Unknown artist']; - - let trackTitle = metadata['xesam:title']; - if (typeof trackTitle !== 'string') - trackTitle = 'Unknown title'; - - let trackAlbum = metadata['xesam:album']; - if (typeof trackAlbum !== 'string') - trackAlbum = 'Unknown album'; - - let trackCoverUrl = metadata['mpris:artUrl']; - if (typeof trackCoverUrl !== 'string') - trackCoverUrl = ''; - - let length = metadata['mpris:length']; - length = typeof length === 'number' ? length / 1_000_000 : -1; - - this.updateProperty('metadata', metadata); - this.updateProperty('shuffle-status', this._playerProxy.Shuffle); - this.updateProperty('loop-status', this._playerProxy.LoopStatus); - this.updateProperty('can-go-next', this._playerProxy.CanGoNext); - this.updateProperty('can-go-prev', this._playerProxy.CanGoPrevious); - this.updateProperty('can-play', this._playerProxy.CanPlay); - this.updateProperty('play-back-status', this._playerProxy.PlaybackStatus); - this.updateProperty('trackid', metadata['mpris:trackid']); - this.updateProperty('track-artists', trackArtists); - this.updateProperty('track-title', trackTitle); - this.updateProperty('track-album', trackAlbum); - this.updateProperty('track-cover-url', trackCoverUrl); - this.updateProperty('length', length); - this.updateProperty('identity', this._mprisProxy.Identity); - this._cacheCoverArt(); - this.emit('changed'); - } - - private _cacheCoverArt() { - if (!mpris.cacheCoverArt || this._trackCoverUrl === '') - return; - - this._coverPath = MEDIA_CACHE_PATH + '/' + - GLib.compute_checksum_for_string(GLib.ChecksumType.SHA1, this._trackCoverUrl, -1); - - if (GLib.file_test(this._coverPath, GLib.FileTest.EXISTS)) - return this.changed('cover-path'); - - ensureDirectory(MEDIA_CACHE_PATH); - Gio.File.new_for_uri(this._trackCoverUrl).copy_async( - Gio.File.new_for_path(this._coverPath), - Gio.FileCopyFlags.OVERWRITE, - GLib.PRIORITY_DEFAULT, - null, null, (source: Gio.File, result: Gio.AsyncResult) => { - try { - source.copy_finish(result); - this.changed('cover-path'); - } - catch (err) { - logError(err); - console.error(`failed to cache ${this._coverPath},` + - ' do you have gvfs installed?'); - } - }, - ); - } - - get volume() { - let volume = this._playerProxy.Volume; - if (typeof volume !== 'number') - volume = -1; - - return volume; - } - - set volume(value) { - this._playerProxy.Volume = value; - } - - get position() { - const proxy = Gio.DBusProxy.new_for_bus_sync( - Gio.BusType.SESSION, - Gio.DBusProxyFlags.NONE, - null, - this._busName, - '/org/mpris/MediaPlayer2', - 'org.mpris.MediaPlayer2.Player', - null, - ); - - const pos = proxy.get_cached_property('Position')?.unpack() as number; - return pos ? pos / 1_000_000 : -1; - } - - set position(time: number) { - const micro = Math.floor(time * 1_000_000); - this._playerProxy.SetPositionAsync(this.trackid, micro); - this.notify('position'); - this.emit('position', time); - } - - readonly playPause = () => this._playerProxy.PlayPauseAsync().catch(console.error); - readonly play = () => this._playerProxy.PlayAsync().catch(console.error); - readonly stop = () => this._playerProxy.StopAsync().catch(console.error); - - readonly next = () => this._playerProxy.NextAsync().catch(console.error); - readonly previous = () => this._playerProxy.PreviousAsync().catch(console.error); - - readonly shuffle = () => this._playerProxy.Shuffle = !this._playerProxy.Shuffle; - readonly loop = () => { - switch (this._playerProxy.LoopStatus) { - case 'None': - this._playerProxy.LoopStatus = 'Track'; - break; - case 'Track': - this._playerProxy.LoopStatus = 'Playlist'; - break; - case 'Playlist': - this._playerProxy.LoopStatus = 'None'; - break; - default: - break; - } - }; -} - -export class Mpris extends Service { - static { - Service.register(this, { - 'player-changed': ['string'], - 'player-closed': ['string'], - 'player-added': ['string'], - }, { - 'players': ['jsobject'], - }); - } - - public cacheCoverArt = true; - - private _proxy: DBusProxy; - private _players: Map = new Map; - - get players() { - return Array.from(this._players.values()); - } - - constructor() { - super(); - - this._proxy = new DBusProxy(Gio.DBus.session, - 'org.freedesktop.DBus', - '/org/freedesktop/DBus', - this._onProxyReady.bind(this), - null, Gio.DBusProxyFlags.NONE); - } - - private _addPlayer(busName: string) { - if (this._players.has(busName)) - return; - - const player = new MprisPlayer(busName); - - player.connect('closed', () => { - this._players.delete(busName); - this.emit('player-closed', busName); - this.changed('players'); - }); - - player.connect('changed', () => { - this.emit('player-changed', busName); - this.emit('changed'); - }); - - this._players.set(busName, player); - this.emit('player-added', busName); - this.changed('players'); - } - - private async _onProxyReady(_: DBusProxy, error: GLib.Error) { - if (error) - return logError(error); - - const [names] = await this._proxy.ListNamesAsync(); - for (const name of names) { - if (name.startsWith(DBUS_PREFIX)) - this._addPlayer(name); - } - - this._proxy.connectSignal('NameOwnerChanged', - this._onNameOwnerChanged.bind(this)); - } - - private _onNameOwnerChanged( - _proxy: Gio.DBusProxy, - _sender: string, - [name, oldOwner, newOwner]: string[], - ) { - if (!name.startsWith(DBUS_PREFIX)) - return; - - if (newOwner && !oldOwner) - this._addPlayer(name); - } - - readonly getPlayer = (name = '') => { - for (const [busName, player] of this._players) { - if (busName.includes(name)) - return player; - } - return null; - }; -} - -export const mpris = new Mpris; -export default mpris; diff --git a/src/service/network.ts b/src/service/network.ts deleted file mode 100644 index 442d0eb8..00000000 --- a/src/service/network.ts +++ /dev/null @@ -1,551 +0,0 @@ -import NM from 'gi://NM'; -import GObject from 'gi://GObject'; -import Service from '../service.js'; -import { bulkConnect } from '../utils.js'; - -const _INTERNET = (device: NM.Device) => { - switch (device?.active_connection?.state) { - case NM.ActiveConnectionState.ACTIVATED: return 'connected'; - case NM.ActiveConnectionState.ACTIVATING: return 'connecting'; - case NM.ActiveConnectionState.DEACTIVATING: - case NM.ActiveConnectionState.DEACTIVATED: - default: return 'disconnected'; - } -}; - -const _DEVICE_STATE = (device: NM.Device) => { - switch (device?.state) { - case NM.DeviceState.UNMANAGED: return 'unmanaged'; - case NM.DeviceState.UNAVAILABLE: return 'unavailable'; - case NM.DeviceState.DISCONNECTED: return 'disconnected'; - case NM.DeviceState.PREPARE: return 'prepare'; - case NM.DeviceState.CONFIG: return 'config'; - case NM.DeviceState.NEED_AUTH: return 'need_auth'; - case NM.DeviceState.IP_CONFIG: return 'ip_config'; - case NM.DeviceState.IP_CHECK: return 'ip_check'; - case NM.DeviceState.SECONDARIES: return 'secondaries'; - case NM.DeviceState.ACTIVATED: return 'activated'; - case NM.DeviceState.DEACTIVATING: return 'deactivating'; - case NM.DeviceState.FAILED: return 'failed'; - default: return 'unknown'; - } -}; - -const _CONNECTIVITY_STATE = (client: NM.Client) => { - switch (client.connectivity) { - case NM.ConnectivityState.NONE: return 'none'; - case NM.ConnectivityState.PORTAL: return 'portal'; - case NM.ConnectivityState.LIMITED: return 'limited'; - case NM.ConnectivityState.FULL: return 'full'; - default: return 'unknown'; - } -}; - -const _CONNECTION_STATE = (activeConnection: NM.ActiveConnection | null) => { - switch (activeConnection?.get_state()) { - case NM.ActiveConnectionState.ACTIVATED: return 'connected'; - case NM.ActiveConnectionState.ACTIVATING: return 'connecting'; - case NM.ActiveConnectionState.DEACTIVATING: return 'disconnecting'; - case NM.ActiveConnectionState.DEACTIVATED: - default: return 'disconnected'; - } -}; - -const _VPN_CONNECTION_STATE = (activeVpnConnection: ActiveVpnConnection) => { - switch (activeVpnConnection?.get_vpn_state()) { - case NM.VpnConnectionState.UNKNOWN: return 'unknown'; - case NM.VpnConnectionState.PREPARE: return 'prepare'; - case NM.VpnConnectionState.NEED_AUTH: return 'needs_auth'; - case NM.VpnConnectionState.CONNECT: return 'connect'; - case NM.VpnConnectionState.IP_CONFIG_GET: return 'ip_config'; - case NM.VpnConnectionState.ACTIVATED: return 'activated'; - case NM.VpnConnectionState.FAILED: return 'failed'; - case NM.VpnConnectionState.DISCONNECTED: - default: return 'disconnected'; - } -}; - -const _STRENGTH_ICONS = [ - { value: 80, icon: 'network-wireless-signal-excellent-symbolic' }, - { value: 60, icon: 'network-wireless-signal-good-symbolic' }, - { value: 40, icon: 'network-wireless-signal-ok-symbolic' }, - { value: 20, icon: 'network-wireless-signal-weak-symbolic' }, - { value: 0, icon: 'network-wireless-signal-none-symbolic' }, -]; - -const DEVICE = (device: string) => { - switch (device) { - case '802-11-wireless': return 'wifi'; - case '802-3-ethernet': return 'wired'; - default: return null; - } -}; - -export class Wifi extends Service { - static { - Service.register(this, {}, { - 'enabled': ['boolean', 'rw'], - 'internet': ['boolean'], - 'strength': ['int'], - 'frequency': ['int'], - 'access-points': ['jsobject'], - 'ssid': ['string'], - 'state': ['string'], - 'icon-name': ['string'], - }); - } - - private _client: NM.Client; - private _device: NM.DeviceWifi; - private _ap!: NM.AccessPoint; - private _apBind!: number; - - constructor(client: NM.Client, device: NM.DeviceWifi) { - super(); - this._client = client; - this._device = device; - - this._client.connect('notify::wireless-enabled', () => this.changed('enabled')); - if (this._device) { - bulkConnect((this._device as unknown) as Service, [ - ['notify::active-access-point', this._activeAp.bind(this)], - ['access-point-added', () => this.emit('changed')], - ['access-point-removed', () => this.emit('changed')], - ]); - this._activeAp(); - } - } - - readonly scan = () => { - this._device.request_scan_async(null, (device, res) => { - device.request_scan_finish(res); - this.emit('changed'); - }); - }; - - private _activeAp() { - if (this._ap) - this._ap.disconnect(this._apBind); - - this._ap = this._device.get_active_access_point(); - if (!this._ap) - return; - - - // TODO make signals actually signal when they should - this._apBind = this._ap.connect('notify::strength', () => { - this.emit('changed'); - const props = [ - 'enabled', - 'internet', - 'strength', - 'frequency', - 'access-points', - 'ssid', - 'state', - 'icon-name', - ]; - props.map(prop => this.notify(prop)); - }); - } - - get access_points() { - return this._device.get_access_points().map(ap => ({ - bssid: ap.bssid, - address: ap.hw_address, - lastSeen: ap.last_seen, - ssid: ap.ssid - ? NM.utils_ssid_to_utf8(ap.ssid.get_data() || new Uint8Array) - : 'Unknown', - active: ap === this._ap, - strength: ap.strength, - frequency: ap.frequency, - iconName: _STRENGTH_ICONS.find(({ value }) => value <= ap.strength)?.icon, - })); - } - - get enabled() { return this._client.wireless_enabled; } - set enabled(v) { this._client.wireless_enabled = v; } - - get strength() { return this._ap?.strength || -1; } - get frequency() { return this._ap?.frequency || -1; } - get internet() { return _INTERNET(this._device); } - get ssid() { - if (!this._ap) - return ''; - - const ssid = this._ap.get_ssid().get_data(); - if (!ssid) - return 'Unknown'; - - return NM.utils_ssid_to_utf8(ssid); - } - - get state() { return _DEVICE_STATE(this._device); } - - get icon_name() { - const iconNames: [number, string][] = [ - [80, 'excellent'], - [60, 'good'], - [40, 'ok'], - [20, 'weak'], - [0, 'none'], - ]; - - // Check if wifi is enabled first, since internet might be provided by - // a wired network. - if (!this.enabled) - return 'network-wireless-offline-symbolic'; - - if (this.internet === 'connected') { - for (const [threshold, name] of iconNames) { - if (this.strength >= threshold) - return `network-wireless-signal-${name}-symbolic`; - } - } - - if (this.internet === 'connecting') - return 'network-wireless-acquiring-symbolic'; - - return 'network-wireless-disabled-symbolic'; - } -} - -export class Wired extends Service { - static { - Service.register(this, {}, { - 'speed': ['int'], - 'internet': ['string'], - 'state': ['string'], - 'icon-name': ['string'], - }); - } - - private _device: NM.DeviceEthernet; - - constructor(device: NM.DeviceEthernet) { - super(); - this._device = device; - - // TODO make signals actually signal when they should - this._device?.connect('notify::speed', () => { - this.emit('changed'); - ['speed', 'internet', 'state', 'icon-name'] - .map(prop => this.notify(prop)); - }); - } - - get speed() { return this._device.get_speed(); } - get internet() { return _INTERNET(this._device); } - get state() { return _DEVICE_STATE(this._device); } - get icon_name() { - if (this.internet === 'connecting') - return 'network-wired-acquiring-symbolic'; - - if (this.internet === 'connected') - return 'network-wired-symbolic'; - - if (network.connectivity !== 'full') - return 'network-wired-no-route-symbolic'; - - return 'network-wired-disconnected-symbolic'; - } -} - -export type ActiveVpnConnection = null | NM.VpnConnection; - -export class VpnConnection extends Service { - static { - Service.register(this, {}, { - 'id': ['string'], - 'state': ['string'], - 'vpn-state': ['string'], - 'icon-name': ['string'], - }); - } - - private _vpn!: Vpn; - private _connection!: NM.Connection; - private _id!: string; - private _activeConnection: ActiveVpnConnection = null; - private _state: ReturnType = 'disconnected'; - private _stateBind: undefined | number = undefined; - private _vpnState: ReturnType = 'disconnected'; - private _vpnStateBind: undefined | number = undefined; - - get connection() { return this._connection; } - get active_connection() { return this._activeConnection; } - get uuid() { return this._connection.get_uuid()!; } - get id() { return this._connection.get_id() || ''; } - get state() { return this._state; } - get vpn_state() { return this._vpnState; } - get icon_name() { - switch (this._state) { - case 'connected': return 'network-vpn-symbolic'; - case 'disconnected': return 'network-vpn-disabled-symbolic'; - case 'connecting': - case 'disconnecting': return 'network-vpn-acquiring-symbolic'; - } - } - - constructor(vpn: Vpn, connection: NM.RemoteConnection) { - super(); - - this._vpn = vpn; - this._connection = connection; - - this._id = this._connection.get_id() || ''; - this._connection.connect('changed', () => this._updateId()); - } - - private _updateId() { - const id = this._connection.get_id() || ''; - if (id !== this._id) { - this._id = id; - this.changed('id'); - } - } - - private _updateState() { - const state = _CONNECTION_STATE(this._activeConnection); - if (state !== this._state) { - this._state = state; - this.notify('state'); - this.notify('icon-name'); - this.emit('changed'); - } - } - - private _updateVpnState() { - const vpnState = _VPN_CONNECTION_STATE(this._activeConnection); - if (vpnState !== this._vpnState) { - this._vpnState = vpnState; - this.changed('vpn-state'); - } - } - - readonly updateActiveConnection = (activeConnection: ActiveVpnConnection) => { - if (this._activeConnection) { - if (this._stateBind) - this._activeConnection.disconnect(this._stateBind); - - if (this._vpnStateBind) - this._activeConnection.disconnect(this._vpnStateBind); - } - - this._activeConnection = activeConnection; - this._stateBind = this._activeConnection?.connect( - 'notify::state', - () => this._updateState(), - ); - this._vpnStateBind = this._activeConnection?.connect( - 'notify::vpn-state', - () => this._updateVpnState(), - ); - - this._updateState(); - this._updateVpnState(); - }; - - readonly setConnection = (connect: boolean) => { - if (connect) { - if (this._state === 'disconnected') - this._vpn.activateVpnConnection(this); - } - else { - if (this._state === 'connected') - this._vpn.deactivateVpnConnection(this); - } - }; -} - -export class Vpn extends Service { - static { - Service.register(this, { - 'connection-added': ['string'], - 'connection-removed': ['string'], - }, { - 'connections': ['jsobject'], - 'activated-connections': ['jsobject'], - }); - } - - private _client: NM.Client; - private _connections: Map; - - constructor(client: NM.Client) { - super(); - - this._client = client; - this._connections = new Map(); - - bulkConnect(this._client as unknown as GObject.Object, [ - ['connection-added', this._connectionAdded.bind(this)], - ['connection-removed', this._connectionRemoved.bind(this)], - ]); - - this._client.get_connections().map((connection: NM.RemoteConnection) => - this._connectionAdded(this._client, connection)); - - this._client.connect( - 'active-connection-added', - (_: NM.Client, ac: NM.ActiveConnection) => { - const uuid = ac.get_uuid(); - if (uuid && this._connections.has(uuid)) - this._connections.get(uuid)?.updateActiveConnection(ac as ActiveVpnConnection); - }, - ); - - this._client.connect( - 'active-connection-removed', - (_: NM.Client, ac: NM.ActiveConnection) => { - const uuid = ac.get_uuid(); - if (uuid && this._connections.has(uuid)) - this._connections.get(uuid)?.updateActiveConnection(null); - }, - ); - } - - private _connectionAdded(client: NM.Client, connection: NM.RemoteConnection) { - if (connection.get_connection_type() !== 'vpn' || connection.get_uuid() === null) - return; - - const vpnConnection = new VpnConnection(this, connection); - const activeConnection = client.get_active_connections() - .find(ac => ac.get_uuid() === vpnConnection.uuid); - - if (activeConnection) - vpnConnection.updateActiveConnection(activeConnection as NM.VpnConnection); - - vpnConnection.connect('changed', () => this.emit('changed')); - vpnConnection.connect('notify::state', (c: VpnConnection) => { - if (c.state === 'connected' || c.state === 'disconnected') - this.changed('activated-connections'); - }); - - this._connections.set(vpnConnection.uuid, vpnConnection); - - this.changed('connections'); - this.emit('connection-added', vpnConnection.uuid); - } - - private _connectionRemoved(_: NM.Client, connection: NM.RemoteConnection) { - const uuid = connection.get_uuid() || ''; - if (!uuid || !this._connections.has(uuid)) - return; - - this._connections.get(uuid)!.updateActiveConnection(null); - this._connections.delete(uuid); - - this.notify('connections'); - this.notify('activated-connections'); - this.emit('changed'); - this.emit('connection-removed', uuid); - } - - readonly activateVpnConnection = (vpn: VpnConnection) => { - this._client.activate_connection_async(vpn.connection, null, null, null, null); - }; - - readonly deactivateVpnConnection = (vpn: VpnConnection) => { - if (vpn.active_connection === null) - return; - - this._client.deactivate_connection_async(vpn.active_connection, null, null); - }; - - readonly getConnection = (uuid: string) => this._connections.get(uuid); - - get connections() { return Array.from(this._connections.values()); } - get activated_connections() { - const list: VpnConnection[] = []; - for (const [, connection] of this._connections) { - if (connection.state === 'connected') - list.push(connection); - } - return list; - } -} - -export class Network extends Service { - static { - Service.register(this, {}, { - 'wifi': ['jsobject'], - 'wired': ['jsobject'], - 'primary': ['string'], - 'connectivity': ['string'], - 'vpn': ['jsobject'], - }); - } - - private _client!: NM.Client; - - wifi!: Wifi; - wired!: Wired; - primary: null | 'wifi' | 'wired' = null; - connectivity!: string; - vpn!: Vpn; - - constructor() { - super(); - try { - this._client = new NM.Client; - this._client.init(null); - this._clientReady(); - } - catch (e) { - logError(e); - } - } - - readonly toggleWifi = () => { - this._client.wireless_enabled = !this._client.wireless_enabled; - }; - - private _getDevice(devType: NM.DeviceType) { - const valid_devices = this._client - .get_devices() - .filter(device => device.get_device_type() === devType); - - return valid_devices.find(d => d.active_connection !== null) || valid_devices.at(0); - } - - private _clientReady() { - bulkConnect(this._client as unknown as GObject.Object, [ - ['notify::wireless-enabled', this._sync.bind(this)], - ['notify::connectivity', this._sync.bind(this)], - ['notify::primary-connection', this._sync.bind(this)], - ['notify::activating-connection', this._sync.bind(this)], - ]); - - this.wifi = new Wifi(this._client, - this._getDevice(NM.DeviceType.WIFI) as NM.DeviceWifi); - - this.wired = new Wired( - this._getDevice(NM.DeviceType.ETHERNET) as NM.DeviceEthernet); - - this.vpn = new Vpn(this._client); - - this.wifi.connect('changed', this._sync.bind(this)); - this.wired.connect('changed', this._sync.bind(this)); - this.vpn.connect('changed', () => this.emit('changed')); - - this._sync(); - } - - private _sync() { - const mainConnection = - this._client.get_primary_connection() || - this._client.get_activating_connection(); - - this.primary = DEVICE(mainConnection?.type || ''); - this.connectivity = _CONNECTIVITY_STATE(this._client); - - this.notify('primary'); - this.notify('connectivity'); - this.emit('changed'); - } -} - -const network = new Network; -export default network; diff --git a/src/service/notifications.ts b/src/service/notifications.ts deleted file mode 100644 index 966897ef..00000000 --- a/src/service/notifications.ts +++ /dev/null @@ -1,497 +0,0 @@ -import Gio from 'gi://Gio'; -import GdkPixbuf from 'gi://GdkPixbuf'; -import GLib from 'gi://GLib'; -import Service from '../service.js'; -import { - CACHE_DIR, ensureDirectory, - loadInterfaceXML, readFileAsync, - timeout, writeFile, -} from '../utils.js'; -import { daemon } from '../utils/notify.js'; - -const NOTIFICATIONS_CACHE_PATH = `${CACHE_DIR}/notifications`; -const CACHE_FILE = NOTIFICATIONS_CACHE_PATH + '/notifications.json'; -const NotificationIFace = loadInterfaceXML('org.freedesktop.Notifications'); - -export interface Action { - id: string - label: string -} - -export interface Hints { - 'action-icons'?: GLib.Variant // boolean - 'category'?: GLib.Variant // string - 'desktop-entry'?: GLib.Variant // string - 'image-data'?: GLib.Variant // iiibiiay - 'image-path'?: GLib.Variant // string - 'resident'?: GLib.Variant // boolean - 'sound-file'?: GLib.Variant // string - 'sound-name'?: GLib.Variant // string - 'suppress-sound'?: GLib.Variant // boolean - 'transient'?: GLib.Variant // boolean - 'urgency'?: GLib.Variant // 0 | 1 | 2 - 'x'?: GLib.Variant // number - 'y'?: GLib.Variant // number - [hint: string]: GLib.Variant | undefined -} - -interface NotifcationJson { - id: number - appName: string - appIcon: string - summary: string - body: string - actions: Action[] - urgency: Urgency - time: number - image?: string; - appEntry?: string; - actionIcons?: boolean; - category?: string; - resident?: boolean; - soundFile?: string; - soundName?: string; - suppressSound?: boolean; - transient?: boolean; - x?: number; - y?: number; -} - -export type Urgency = 'low' | 'critical' | 'normal' - -const _URGENCY = (urgency?: number): Urgency => { - switch (urgency) { - case 0: return 'low'; - case 2: return 'critical'; - default: return 'normal'; - } -}; - -export class Notification extends Service { - static { - Service.register(this, { - 'dismissed': [], - 'closed': [], - 'invoked': ['string'], - }, { - 'action-icons': ['boolean'], - 'actions': ['jsobject'], - 'app-entry': ['string'], - 'app-icon': ['string'], - 'app-name': ['string'], - 'body': ['string'], - 'category': ['string'], - 'id': ['int'], - 'image': ['string'], - 'popup': ['boolean'], - 'resident': ['boolean'], - 'sound-file': ['string'], - 'sound-name': ['string'], - 'summary': ['string'], - 'suppress-sound': ['boolean'], - 'time': ['int'], - 'timeout': ['int', 'rw'], - 'transient': ['boolean'], - 'urgency': ['string'], - 'x': ['int'], - 'y': ['int'], - 'hints': ['jsobject'], - }); - } - - private _actionIcons?: boolean; - private _actions: Action[] = []; - private _appEntry?: string; - private _appIcon: string; - private _appName: string; - private _body: string; - private _category?: string; - private _id: number; - private _image?: string; - private _popup: boolean; - private _resident?: boolean; - private _soundFile?: string; - private _soundName?: string; - private _summary: string; - private _suppressSound?: boolean; - private _time: number; - private _timeout!: number; - private _transient?: boolean; - private _urgency: Urgency; - private _x?: number; - private _y?: number; - private _hints: Hints = {}; - - get action_icons() { return this._actionIcons; } - get actions() { return this._actions; } - get app_entry() { return this._appEntry; } - get app_icon() { return this._appIcon; } - get app_name() { return this._appName; } - get body() { return this._body; } - get category() { return this._category; } - get id() { return this._id; } - get image() { return this._image; } - get popup() { return this._popup; } - get resident() { return this._resident; } - get sound_file() { return this._soundFile; } - get sound_name() { return this._soundName; } - get summary() { return this._summary; } - get suppress_sound() { return this._suppressSound; } - get time() { return this._time; } - get timeout() { return this._timeout; } - get transient() { return this._transient; } - get urgency() { return this._urgency; } - get x() { return this._x; } - get y() { return this._y; } - get hints() { return this._hints; } - - constructor( - appName: string, - id: number, - appIcon: string, - summary: string, - body: string, - acts: string[], - hints: Hints, - popup: boolean, - ) { - super(); - - for (let i = 0; i < acts.length; i += 2) { - acts[i + 1] !== '' && this._actions.push({ - label: acts[i + 1], - id: acts[i], - }); - } - - this._id = id; - this._appName = appName; - this._appIcon = appIcon; - this._summary = summary; - this._body = body; - this._time = GLib.DateTime.new_now_local().to_unix(); - this._image = this._appIconImage() || - this._parseImageData(hints['image-data']) || - hints['image-path']?.unpack(); - - this._popup = popup; - this._urgency = _URGENCY(hints['urgency']?.unpack()); - - this._appEntry = hints['desktop-entry']?.unpack(); - this._actionIcons = hints['action-icons']?.unpack(); - this._category = hints['category']?.unpack(); - this._resident = hints['resident']?.unpack(); - this._soundFile = hints['sound-file']?.unpack(); - this._soundName = hints['sound-name']?.unpack(); - this._suppressSound = hints['suppress-sound']?.unpack(); - this._transient = hints['transient']?.unpack(); - this._x = hints['x']?.unpack(); - this._y = hints['y']?.unpack(); - this._hints = hints; - } - - readonly dismiss = () => { - this._popup = false; - this.changed('popup'); - this.emit('dismissed'); - }; - - readonly close = () => { - this.emit('closed'); - }; - - readonly invoke = (id: string) => { - this.emit('invoked', id); - if (!this.resident) - this.close(); - }; - - toJson(cacheActions = notifications.cacheActions): NotifcationJson { - return { - actionIcons: this._actionIcons, - actions: cacheActions ? this._actions : [], - appEntry: this._appEntry, - appIcon: this._appIcon, - appName: this._appName, - body: this._body, - category: this._category, - id: this._id, - image: this._image, - resident: this._resident, - soundFile: this._soundFile, - soundName: this._soundName, - summary: this._summary, - suppressSound: this._suppressSound, - time: this._time, - transient: this._transient, - urgency: this._urgency, - x: this._x, - y: this._y, - }; - } - - static fromJson(json: NotifcationJson) { - const { id, appName, appIcon, summary, body, ...j } = json; - - const n = new Notification(appName, id, appIcon, summary, body, [], {}, false); - for (const key of Object.keys(j)) - // @ts-expect-error too lazy to type - n[`_${key}`] = j[key]; - - return n; - } - - private _appIconImage() { - if (GLib.file_test(this._appIcon, GLib.FileTest.EXISTS) || - GLib.file_test(this._appIcon.replace(/^(file\:\/\/)/, ''), GLib.FileTest.EXISTS)) - return this._appIcon; - } - - private _parseImageData(imageData?: InstanceType) { - if (!imageData) - return null; - - ensureDirectory(NOTIFICATIONS_CACHE_PATH); - const fileName = NOTIFICATIONS_CACHE_PATH + `/${this._id}`; - const [w, h, rs, alpha, bps, _, data] = imageData // iiibiiay - .recursiveUnpack<[number, number, number, boolean, number, number, GLib.Bytes]>(); - - if (bps !== 8) { - console.warn(`Notification image error from ${this.app_name}: ` + - 'Currently only RGB images with 8 bits per sample are supported.'); - return null; - } - - const pixbuf = GdkPixbuf.Pixbuf.new_from_bytes( - data, GdkPixbuf.Colorspace.RGB, alpha, bps, w, h, rs); - - if (!pixbuf) - return null; - - const outputStream = Gio.File.new_for_path(fileName) - .replace(null, false, Gio.FileCreateFlags.NONE, null); - - pixbuf.save_to_streamv(outputStream, 'png', null, null, null); - outputStream.close(null); - - return fileName; - } -} - -export class Notifications extends Service { - static { - Service.register(this, { - 'dismissed': ['int'], - 'notified': ['int'], - 'closed': ['int'], - }, { - 'notifications': ['jsobject'], - 'popups': ['jsobject'], - 'dnd': ['boolean'], - }); - } - - public popupTimeout = 3000; - public forceTimeout = false; - public cacheActions = false; - public clearDelay = 100; - - private _dbus!: Gio.DBusExportedObject; - private _notifications: Map; - private _dnd = false; - private _idCount = 0; - - constructor() { - super(); - - this._notifications = new Map(); - this._readFromFile(); - this._register(); - } - - get dnd() { return this._dnd; } - set dnd(value: boolean) { - this._dnd = value; - this.changed('dnd'); - } - - get notifications() { - return Array.from(this._notifications.values()); - } - - get popups() { - const list = []; - for (const [, notification] of this._notifications) { - if (notification.popup) - list.push(notification); - } - return list; - } - - readonly getPopup = (id: number) => { - const n = this._notifications.get(id); - return n?.popup ? n : null; - }; - - readonly getNotification = (id: number) => { - return this._notifications.get(id); - }; - - Notify( - appName: string, - replacesId: number, - appIcon: string, - summary: string, - body: string, - acts: string[], - hints: Hints, - expiration: number, - ) { - const id = replacesId || this._idCount++; - const n = new Notification(appName, id, appIcon, summary, body, acts, hints, !this.dnd); - - if (this.forceTimeout || expiration === -1) { - n.updateProperty('timeout', this.popupTimeout); - timeout(this.popupTimeout, () => this.DismissNotification(id)); - } else { - n.updateProperty('timeout', expiration); - if (expiration > 0) - timeout(expiration, () => this.DismissNotification(id)); - } - - this._addNotification(n); - !this._dnd && this.notify('popups'); - this.notify('notifications'); - this.emit('notified', id); - this.emit('changed'); - this._cache(); - return id; - } - - Clear() { this.clear(); } - - DismissNotification(id: number) { - this._notifications.get(id)?.dismiss(); - } - - CloseNotification(id: number) { - this._notifications.get(id)?.close(); - } - - InvokeAction(id: number, actionId: string) { - this._notifications.get(id)?.invoke(actionId); - } - - GetCapabilities() { - return [ - 'action-icons', - 'actions', - 'body', - 'body-hyperlinks', - 'body-markup', - 'icon-static', - 'persistence', - 'sound', - ]; - } - - GetServerInformation() { - return new GLib.Variant('(ssss)', [pkg.name, 'Aylur', pkg.version, '1.2']); - } - - readonly clear = async () => { - const close = (n: Notification, delay: number) => new Promise(resolve => { - this._notifications.has(n.id) - ? timeout(delay, () => resolve(n.close())) - : resolve(null); - }); - return Promise.all(this.notifications.map((n, i) => close(n, this.clearDelay * i))); - }; - - private _addNotification(n: Notification) { - n.connect('dismissed', this._onDismissed.bind(this)); - n.connect('closed', this._onClosed.bind(this)); - n.connect('invoked', this._onInvoked.bind(this)); - this._notifications.set(n.id, n); - } - - private _onDismissed(n: Notification) { - this.emit('dismissed', n.id); - this.changed('popups'); - } - - private _onClosed(n: Notification) { - this._dbus.emit_signal('NotificationClosed', - new GLib.Variant('(uu)', [n.id, 3])); - - this._notifications.delete(n.id); - this.notify('notifications'); - this.notify('popups'); - this.emit('closed', n.id); - this.emit('changed'); - this._cache(); - } - - private _onInvoked(n: Notification, id: string) { - this._dbus.emit_signal('ActionInvoked', - new GLib.Variant('(us)', [n.id, id])); - } - - private _register() { - Gio.bus_own_name( - Gio.BusType.SESSION, - 'org.freedesktop.Notifications', - Gio.BusNameOwnerFlags.NONE, - (connection: Gio.DBusConnection) => { - this._dbus = Gio.DBusExportedObject - .wrapJSObject(NotificationIFace as string, this); - - this._dbus.export(connection, '/org/freedesktop/Notifications'); - }, - () => { - daemon.running = true; - }, - () => { - const [name] = Gio.DBus.session.call_sync( - 'org.freedesktop.Notifications', - '/org/freedesktop/Notifications', - 'org.freedesktop.Notifications', - 'GetServerInformation', - null, - null, - Gio.DBusCallFlags.NONE, - -1, - null).deepUnpack() as string[]; - - console.warn(`Another notification daemon is already running: ${name}`); - }, - ); - } - - private async _readFromFile() { - try { - const file = await readFileAsync(CACHE_FILE); - const notifications = JSON.parse(file) - .map((n: NotifcationJson) => Notification.fromJson(n)); - - for (const n of notifications) { - this._addNotification(n); - if (n.id > this._idCount) - this._idCount = n.id + 1; - } - - this.changed('notifications'); - } catch (_) { - // most likely there is no cache yet - } - } - - private _cache() { - ensureDirectory(NOTIFICATIONS_CACHE_PATH); - const arr = Array.from(this._notifications.values()).map(n => n.toJson()); - writeFile(JSON.stringify(arr, null, 2), CACHE_FILE).catch(err => console.error(err)); - } -} - -export const notifications = new Notifications; -export default notifications; diff --git a/src/service/powerprofiles.ts b/src/service/powerprofiles.ts deleted file mode 100644 index 444f96ee..00000000 --- a/src/service/powerprofiles.ts +++ /dev/null @@ -1,88 +0,0 @@ -import GLib from 'gi://GLib'; -import Gio from 'gi://Gio'; -import Service from '../service.js'; -import { loadInterfaceXML } from '../utils.js'; -import { PowerProfilesProxy } from '../dbus/types.js'; -import { kebabify } from '../utils/gobject.js'; -import { isRunning } from '../utils/init.js'; - -const BUSNAME = 'net.hadess.PowerProfiles'; -const PowerProfilesIFace = loadInterfaceXML(BUSNAME)!; -const PowerProfilesProxy = Gio.DBusProxy.makeProxyWrapper( - PowerProfilesIFace) as unknown as PowerProfilesProxy; - -const DummyProxy = { - ActiveProfile: '', - PerformanceInhibited: '', - PerformanceDegraded: '', - Profiles: [], - Actions: [], - ActiveProfileHolds: [], - HoldProfile: () => 0, - ReleaseProfile: () => null, -} as unknown as PowerProfilesProxy; - -class PowerProfiles extends Service { - static { - Service.register(this, { - 'profile-released': ['int'], - }, { - 'active-profile': ['string', 'rw'], - 'performance-inhibited': ['string', 'r'], - 'performance-degraded': ['string', 'r'], - 'profiles': ['jsobject', 'r'], - 'actions': ['jsobject', 'r'], - 'active-profile-holds': ['jsobject', 'r'], - 'icon-name': ['string', 'r'], - }); - } - - private _proxy = DummyProxy; - private _unpackDict(dict: { [prop: string]: GLib.Variant }) { - const data: { [key: string]: string } = {}; - for (const [key, variant] of Object.entries(dict)) - data[key] = variant.unpack(); - - return data; - } - - constructor() { - super(); - - if (isRunning(BUSNAME, 'system')) { - this._proxy = new PowerProfilesProxy( - Gio.DBus.system, - 'net.hadess.PowerProfiles', - '/net/hadess/PowerProfiles'); - - this._proxy.connect('g-properties-changed', (_, changed) => { - for (const prop of Object.keys(changed.deepUnpack())) { - this.notify(kebabify(prop)); - if (prop === 'ActiveProfile') - this.notify('icon-name'); - } - - this.emit('changed'); - }); - - this._proxy.connectSignal('ProfileReleased', (_p, _n, [cookie]) => { - this.emit('profile-released', cookie); - }); - } else { - console.error(`${BUSNAME} is not available`); - } - } - - get active_profile() { return this._proxy.ActiveProfile; } - set active_profile(profile: string) { this._proxy.ActiveProfile = profile; } - - get performance_inhibited() { return this._proxy.PerformanceInhibited; } - get performance_degraded() { return this._proxy.PerformanceDegraded; } - get profiles() { return this._proxy.Profiles.map(this._unpackDict); } - get actions() { return this._proxy.Actions; } - get active_profile_holds() { return this._proxy.ActiveProfileHolds.map(this._unpackDict); } - get icon_name() { return `power-profile-${this.active_profile}-symbolic`; } -} - -const service = new PowerProfiles; -export default service; diff --git a/src/service/systemtray.ts b/src/service/systemtray.ts deleted file mode 100644 index f5bbaf7b..00000000 --- a/src/service/systemtray.ts +++ /dev/null @@ -1,302 +0,0 @@ -import Gio from 'gi://Gio'; -import GLib from 'gi://GLib'; -import Gdk from 'gi://Gdk?version=3.0'; -import Gtk from 'gi://Gtk?version=3.0'; -import GdkPixbuf from 'gi://GdkPixbuf'; -import DbusmenuGtk3 from 'gi://DbusmenuGtk3'; -import Service from '../service.js'; -import { StatusNotifierItemProxy } from '../dbus/types.js'; -import { bulkConnect, loadInterfaceXML } from '../utils.js'; -import Widget from '../widget.js'; - -const StatusNotifierWatcherIFace = loadInterfaceXML('org.kde.StatusNotifierWatcher')!; -const StatusNotifierItemIFace = loadInterfaceXML('org.kde.StatusNotifierItem')!; -const StatusNotifierItemProxy = - Gio.DBusProxy.makeProxyWrapper(StatusNotifierItemIFace) as unknown as StatusNotifierItemProxy; - -const DbusmenuGtk3Menu = Widget< - typeof DbusmenuGtk3.Menu, - DbusmenuGtk3.Menu.ConstructorProperties ->(DbusmenuGtk3.Menu); - -export class TrayItem extends Service { - static { - Service.register(this, { - 'removed': ['string'], - 'ready': [], - }, { - 'menu': ['jsobject'], - 'category': ['string'], - 'id': ['string'], - 'title': ['string'], - 'status': ['string'], - 'window-id': ['int'], - 'is-menu': ['boolean'], - 'tooltip-markup': ['string'], - 'icon': ['jsobject'], - }); - } - - private _proxy: StatusNotifierItemProxy; - private _busName: string; - - private _iconTheme?: Gtk.IconTheme; - menu?: DbusmenuGtk3.Menu; - - constructor(busName: string, objectPath: string) { - super(); - - this._busName = busName; - - this._proxy = new StatusNotifierItemProxy( - Gio.DBus.session, - busName, - objectPath, - this._itemProxyAcquired.bind(this), - null, - Gio.DBusProxyFlags.NONE); - } - - readonly activate = (event: Gdk.Event) => { - this._proxy.ActivateAsync(event.get_root_coords()[1], event.get_root_coords()[2]); - }; - - readonly secondaryActivate = (event: Gdk.Event) => { - this._proxy.SecondaryActivateAsync(event.get_root_coords()[1], event.get_root_coords()[2]); - }; - - readonly scroll = (event: Gdk.EventScroll) => { - const direction = (event.direction == 0 || event.direction == 1) - ? 'vertical' : 'horizontal'; - - const delta = (event.direction == 0 || event.direction == 1) - ? event.delta_y : event.delta_x; - - this._proxy.ScrollAsync(delta, direction); - }; - - readonly openMenu = (event: Gdk.Event) => { - this.menu - ? this.menu.popup_at_pointer(event) - : this._proxy.ContextMenuAsync(event.get_root_coords()[1], event.get_root_coords()[2]); - }; - - get category() { return this._proxy.Category; } - get id() { return this._proxy.Id; } - get title() { return this._proxy.Title; } - get status() { return this._proxy.Status; } - get window_id() { return this._proxy.WindowId; } - get is_menu() { return this._proxy.ItemIsMenu; } - - get tooltip_markup() { - if (!this._proxy.ToolTip) - return ''; - - let tooltipMarkup = this._proxy.ToolTip[2]; - if (this._proxy.ToolTip[3] !== '') - tooltipMarkup += '\n' + this._proxy.ToolTip[3]; - - return tooltipMarkup; - } - - get icon() { - const iconName = this.status === 'NeedsAttention' - ? this._proxy.AttentionIconName - : this._proxy.IconName; - - if (this._iconTheme && iconName) { - const size = Math.max(...this._iconTheme.get_icon_sizes(iconName)); - const iconInfo = this._iconTheme.lookup_icon( - iconName, size, Gtk.IconLookupFlags.FORCE_SIZE); - - if (iconInfo) - return iconInfo.load_icon(); - } - const iconPixmap = this.status === 'NeedsAttention' - ? this._proxy.AttentionIconPixmap - : this._proxy.IconPixmap; - - return iconName || this._getPixbuf(iconPixmap) || 'image-missing'; - } - - private _itemProxyAcquired(proxy: StatusNotifierItemProxy) { - if (proxy.Menu) { - const menu = DbusmenuGtk3Menu({ - dbus_name: proxy.g_name_owner!, - dbus_object: proxy.Menu, - }); - this.menu = (menu as unknown) as DbusmenuGtk3.Menu; - } - - if (this._proxy.IconThemePath) { - this._iconTheme = Gtk.IconTheme.new(); - this._iconTheme?.set_search_path([this._proxy.IconThemePath]); - } - - bulkConnect(proxy, [ - ['notify::g-name-owner', () => { - if (!proxy.g_name_owner) - this.emit('removed', this._busName); - }], - ['g-signal', this._refreshAllProperties.bind(this)], - ['g-properties-changed', () => this.emit('changed')], - ]); - - ['Title', 'Icon', 'AttentionIcon', 'OverlayIcon', 'ToolTip', 'Status'] - .forEach(prop => proxy.connectSignal(`New${prop}`, () => { - this._notify(); - })); - - this.emit('ready'); - } - - private _notify() { - [ - 'menu', 'category', 'id', 'title', 'status', - 'window-id', 'is-menu', 'tooltip-markup', 'icon', - ].forEach(prop => this.notify(prop)); - this.emit('changed'); - } - - private _refreshAllProperties() { - this._proxy.g_connection.call( - this._proxy.g_name, - this._proxy.g_object_path!, - 'org.freedesktop.DBus.Properties', - 'GetAll', - new GLib.Variant('(s)', [this._proxy.g_interface_name]), - new GLib.VariantType('(a{sv})'), - Gio.DBusCallFlags.NONE, -1, - null, - (proxy, result) => { - const variant = proxy?.call_finish(result) as GLib.Variant; - if (!variant) - return; - const [properties] = variant.deepUnpack[]>(); - Object.entries(properties).map(([propertyName, value]) => { - this._proxy.set_cached_property(propertyName, value); - }); - - if (this._proxy.IconThemePath) { - if (!this._iconTheme) - this._iconTheme = Gtk.IconTheme.new(); - - this._iconTheme.set_search_path([this._proxy.IconThemePath]); - } - - this._notify(); - }, - ); - } - - private _getPixbuf(pixMapArray: [number, number, Uint8Array][]) { - if (!pixMapArray) - return; - - const pixMap = pixMapArray.sort((a, b) => a[0] - b[0]).pop(); - if (!pixMap) - return; - - const array = Uint8Array.from(pixMap[2]); - for (let i = 0; i < 4 * pixMap[0] * pixMap[1]; i += 4) { - const alpha = array[i]; - array[i] = array[i + 1]; - array[i + 1] = array[i + 2]; - array[i + 2] = array[i + 3]; - array[i + 3] = alpha; - } - return GdkPixbuf.Pixbuf.new_from_bytes( - new GLib.Bytes(array), - GdkPixbuf.Colorspace.RGB, - true, - 8, - pixMap[0], - pixMap[1], - pixMap[0] * 4, - ); - } -} - -export class SystemTray extends Service { - static { - Service.register(this, { - 'added': ['string'], - 'removed': ['string'], - }, { - 'items': ['jsobject'], - }); - } - - private _dbus!: Gio.DBusExportedObject; - private _items: Map; - - get IsStatusNotifierHostRegistered() { return true; } - get ProtocolVersion() { return 0; } - get RegisteredStatusNotifierItems() { return Array.from(this._items.keys()); } - - get items() { return Array.from(this._items.values()); } - readonly getItem = (name: string) => this._items.get(name); - - constructor() { - super(); - this._items = new Map(); - this._register(); - } - - private _register() { - Gio.bus_own_name( - Gio.BusType.SESSION, - 'org.kde.StatusNotifierWatcher', - Gio.BusNameOwnerFlags.NONE, - (connection: Gio.DBusConnection) => { - this._dbus = Gio.DBusExportedObject - .wrapJSObject(StatusNotifierWatcherIFace as string, this); - - this._dbus.export(connection, '/StatusNotifierWatcher'); - }, - null, - () => { - print('Another system tray is already running'); - }, - ); - } - - RegisterStatusNotifierItemAsync(serviceName: string[], invocation: Gio.DBusMethodInvocation) { - let busName: string, objectPath: string; - const [service] = serviceName; - if (service.startsWith('/')) { - objectPath = service; - busName = invocation.get_sender()!; - } else { - busName = service; - objectPath = '/StatusNotifierItem'; - } - - invocation.return_value(null); - - const item = new TrayItem(busName, objectPath); - item.connect('ready', () => { - this._items.set(busName, item); - this.emit('added', busName); - this.notify('items'); - this.emit('changed'); - this._dbus.emit_signal( - 'StatusNotifierItemRegistered', - new GLib.Variant('(s)', [busName + objectPath]), - ); - }); - item.connect('removed', () => { - this._items.delete(busName); - this.emit('removed', busName); - this.notify('items'); - this.emit('changed'); - this._dbus.emit_signal( - 'StatusNotifierItemUnregistered', - new GLib.Variant('(s)', [busName]), - ); - }); - } -} - -export const systemTray = new SystemTray; -export default systemTray; diff --git a/src/utils.ts b/src/utils.ts deleted file mode 100644 index 12cb7cb2..00000000 --- a/src/utils.ts +++ /dev/null @@ -1,102 +0,0 @@ -import GLib from 'gi://GLib'; -import * as Exec from './utils/exec.js'; -import * as File from './utils/file.js'; -import * as Etc from './utils/etc.js'; -import * as Timeout from './utils/timeout.js'; -import * as Fetch from './utils/fetch.js'; -import * as Notify from './utils/notify.js'; -import * as Pam from './utils/pam.js'; -import * as Gobject from './utils/gobject.js'; -import * as Binding from './utils/binding.js'; - -export const USER = GLib.get_user_name(); -export const HOME = GLib.get_home_dir(); -export const CACHE_DIR = `${GLib.get_user_cache_dir()}/${pkg.name.split('.').pop()}`; - -export const { - exec, - execAsync, - subprocess, -} = Exec; - -export const { - readFile, - readFileAsync, - writeFile, - writeFileSync, - monitorFile, -} = File; - -export const { - timeout, - interval, - idle, -} = Timeout; - -export const { - loadInterfaceXML, - bulkConnect, - bulkDisconnect, - ensureDirectory, - lookUpIcon, -} = Etc; - -export const { - authenticate, - authenticateUser, -} = Pam; - -export const { fetch } = Fetch; -export const { notify } = Notify; - -export const { - kebabify, - pspec, - registerGObject, -} = Gobject; - -export const { - merge, - derive, - watch, -} = Binding; - -export default { - USER, - HOME, - CACHE_DIR, - - exec, - execAsync, - subprocess, - - readFile, - readFileAsync, - writeFile, - writeFileSync, - monitorFile, - - timeout, - interval, - idle, - - loadInterfaceXML, - bulkConnect, - bulkDisconnect, - ensureDirectory, - lookUpIcon, - - fetch, - notify, - - authenticate, - authenticateUser, - - kebabify, - pspec, - registerGObject, - - merge, - derive, - watch, -}; diff --git a/src/utils/binding.ts b/src/utils/binding.ts deleted file mode 100644 index d6832512..00000000 --- a/src/utils/binding.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { Binding, Connectable } from '../service.js'; -import { Variable } from '../variable.js'; -import { kebabify } from './gobject.js'; - -// TODO: consider adding a guard for disposed Variables - -type Dep = Binding -export function merge[], - Args extends { [K in keyof Deps]: Deps[K] extends Dep ? T : never } ->(deps: Deps, fn: (...args: Args) => V) { - const update = () => fn(...deps.map(d => d.transformFn(d.emitter[d.prop])) as Args); - const watcher = new Variable(update()); - for (const dep of deps) - dep.emitter.connect(`notify::${kebabify(dep.prop)}`, () => watcher.value = update()); - - return watcher.bind(); -} - -export function derive[], - Args extends { [K in keyof Deps]: Deps[K] extends Variable ? T : never } ->(deps: Deps, fn: (...args: Args) => V) { - const update = () => fn(...deps.map(d => d.value) as Args); - const watcher = new Variable(update()); - for (const dep of deps) - dep.connect('changed', () => watcher.value = update()); - - return watcher; -} - -type B = Binding, any, T> - -// eslint-disable-next-line max-len -export function watch(init: T, objs: Array, callback: () => T): B -export function watch(init: T, obj: Connectable, signal: string, callback: () => T): B -export function watch(init: T, obj: Connectable, callback: () => T): B -export function watch( - init: T, - objs: Connectable | Array, - sigOrFn: string | (() => T), - callback?: () => T, -) { - const v = new Variable(init); - const f = typeof sigOrFn === 'function' ? sigOrFn : callback ?? (() => v.value); - const set = () => v.value = f(); - - if (Array.isArray(objs)) { - // multiple objects - for (const obj of objs) { - if (Array.isArray(obj)) { - // obj signal pair - const [o, s = 'changed'] = obj; - o.connect(s, set); - } else { - // obj on changed - obj.connect('changed', set); - } - } - } else { - // watch single object - const signal = typeof sigOrFn === 'string' ? sigOrFn : 'changed'; - objs.connect(signal, set); - } - - return v.bind(); -} diff --git a/src/utils/etc.ts b/src/utils/etc.ts deleted file mode 100644 index aa10cad1..00000000 --- a/src/utils/etc.ts +++ /dev/null @@ -1,49 +0,0 @@ -import Gtk from 'gi://Gtk?version=3.0'; -import GObject from 'gi://GObject'; -import GLib from 'gi://GLib'; -import Gio from 'gi://Gio'; - -export function loadInterfaceXML(iface: string) { - const uri = `resource:///com/github/Aylur/ags/dbus/${iface}.xml`; - const f = Gio.File.new_for_uri(uri); - - try { - const [, bytes] = f.load_contents(null); - return new TextDecoder().decode(bytes); - } catch (e) { - logError(e); - return null; - } -} - -export function bulkConnect( - service: GObject.Object, - list: Array<[event: string, callback: (...args: any[]) => void]>, -) { - const ids = []; - for (const [event, callback] of list) - ids.push(service.connect(event, callback)); - - return ids; -} - -export function bulkDisconnect(service: GObject.Object, ids: number[]) { - for (const id of ids) - service.disconnect(id); -} - -export function lookUpIcon(name?: string, size = 16) { - if (!name) - return null; - - return Gtk.IconTheme.get_default().lookup_icon( - name, - size, - Gtk.IconLookupFlags.USE_BUILTIN, - ); -} - -export function ensureDirectory(path: string) { - if (!GLib.file_test(path, GLib.FileTest.EXISTS)) - Gio.File.new_for_path(path).make_directory_with_parents(null); -} diff --git a/src/utils/exec.ts b/src/utils/exec.ts deleted file mode 100644 index 2daaa18f..00000000 --- a/src/utils/exec.ts +++ /dev/null @@ -1,148 +0,0 @@ -import Gtk from 'gi://Gtk?version=3.0'; -import Gio from 'gi://Gio'; -import GLib from 'gi://GLib'; - -type Args = { - cmd: string | string[], - out?: (stdout: string) => Out, - err?: (stderr: string) => Err, -} - -function proc(arg: Args | string | string[]) { - let cmd = Array.isArray(arg) || typeof arg === 'string' - ? arg - : arg.cmd; - - if (typeof cmd === 'string') { - const [, argv] = GLib.shell_parse_argv(cmd); - cmd = argv; - } - - return Gio.Subprocess.new( - cmd, - Gio.SubprocessFlags.STDIN_PIPE | - Gio.SubprocessFlags.STDOUT_PIPE | - Gio.SubprocessFlags.STDERR_PIPE, - ); -} - -function readStream(stream: Gio.DataInputStream, callback: (out: string) => void) { - stream.read_line_async(GLib.PRIORITY_DEFAULT, null, (_, res) => { - const output = stream?.read_line_finish_utf8(res)[0]; - if (typeof output === 'string') { - callback(output.trim()); - readStream(stream, callback); - } - }); -} - -export function subprocess(args: Args & { - bind?: Gtk.Widget, -}): Gio.Subprocess - -export function subprocess( - cmd: string | string[], - out?: (stdout: string) => void, - err?: (stderr: string) => void, - bind?: Gtk.Widget, -): Gio.Subprocess - -export function subprocess( - argsOrCmd: Args & { bind?: Gtk.Widget } | string | string[], - out: (stdout: string) => void = print, - err: (stderr: string) => void = err => console.error(Error(err)), - bind?: Gtk.Widget, -) { - const p = proc(argsOrCmd); - - const stdin = new Gio.DataOutputStream({ - base_stream: p.get_stdin_pipe(), - close_base_stream: true, - }); - - const stdout = new Gio.DataInputStream({ - base_stream: p.get_stdout_pipe(), - close_base_stream: true, - }); - - const stderr = new Gio.DataInputStream({ - base_stream: p.get_stderr_pipe(), - close_base_stream: true, - }); - - if (bind) - bind.connect('destroy', () => p.force_exit()); - - const onErr = Array.isArray(argsOrCmd) || typeof argsOrCmd === 'string' - ? err - : argsOrCmd.err; - - const onOut = Array.isArray(argsOrCmd) || typeof argsOrCmd === 'string' - ? out - : argsOrCmd.out; - - readStream(stdout, onOut ?? out); - readStream(stderr, onErr ?? err); - - return Object.assign(p, { - write(str: string): void { - stdin.write_all(new TextEncoder().encode(str), null); - }, - writeAsync(str: string): Promise { - return new Promise((resolve, reject) => { - stdin.write_all_async( - new TextEncoder().encode(str), - GLib.PRIORITY_DEFAULT, - null, - (stdin, res) => { - stdin.write_all_finish(res)[0] - ? resolve() - : reject(); - }, - ); - }); - }, - }); -} - -export function exec(args: Args): Out | Err -export function exec( - cmd: string | string[], - out?: (stdout: string) => Out, - err?: (stderr: string) => Err, -): Out | Err - -export function exec( - argsOrCmd: Args | string | string[], - out: (stdout: string) => Out = out => out as Out, - err: (stderr: string) => Err = out => out as Err, -): Out | Err { - const p = proc(argsOrCmd); - - const onErr = Array.isArray(argsOrCmd) || typeof argsOrCmd === 'string' - ? err - : argsOrCmd.err; - - const onOut = Array.isArray(argsOrCmd) || typeof argsOrCmd === 'string' - ? out - : argsOrCmd.out; - - const [, stdout, stderr] = p.communicate_utf8(null, null); - - return p.get_successful() - ? (onOut ?? out)(stdout!.trim()) - : (onErr ?? err)(stderr!.trim()); -} - -export function execAsync(cmd: string | string[]): Promise { - const p = proc(cmd); - - return new Promise((resolve, reject) => { - p.communicate_utf8_async(null, null, (_, res) => { - const [, stdout, stderr] = p.communicate_utf8_finish(res); - p.get_successful() - ? resolve(stdout!.trim()) - : reject(stderr!.trim()); - }); - }); -} diff --git a/src/utils/fetch.ts b/src/utils/fetch.ts deleted file mode 100644 index 67091dde..00000000 --- a/src/utils/fetch.ts +++ /dev/null @@ -1,139 +0,0 @@ -// code mostly take from https://github.com/sonnyp/troll - -import GLib from 'gi://GLib'; -import Gio from 'gi://Gio'; - -/* - * this module gets loaded on startup, so in order - * to make libsoup an optional dependency we do this - */ -let init = false; -async function libnotify() { - try { - import('gi://Soup?version=3.0'); - } catch (error) { - console.error(Error('Missing dependency: libsoup3')); - return null; - } - - const Soup = (await import('gi://Soup?version=3.0')).default; - - if (init) - return Soup; - - init = true; - Gio._promisify(Soup.Session.prototype, 'send_async'); - Gio._promisify(Gio.MemoryOutputStream.prototype, 'splice_async'); - return Soup; -} - -export type FetchOptions = { - method?: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH'; - body?: string; - headers?: Record; - params?: Record; -}; - -export class Response { - status: number; - statusText: string | null; - ok: boolean; - stream: Gio.InputStream | null; - type = 'basic'; - - constructor( - status: number, - statusText: string | null, - ok: boolean, - stream: Gio.InputStream | null, - ) { - this.status = status; - this.statusText = statusText; - this.ok = ok; - this.stream = stream; - } - - async json() { - const text = await this.text(); - return JSON.parse(text); - } - - async text() { - const gBytes = await this.gBytes(); - return new TextDecoder().decode(gBytes ? gBytes.toArray() : []); - } - - async arrayBuffer() { - const gBytes = await this.gBytes(); - if (!gBytes) - return null; - - return gBytes.toArray().buffer; - } - - async gBytes() { - const outputStream = Gio.MemoryOutputStream.new_resizable(); - if (!this.stream) - return null; - - await outputStream.splice_async(this.stream, - Gio.OutputStreamSpliceFlags.CLOSE_TARGET | - Gio.OutputStreamSpliceFlags.CLOSE_SOURCE, - GLib.PRIORITY_DEFAULT, - null); - - return outputStream.steal_as_bytes(); - } -} - -export async function fetch(url: string, options: FetchOptions = {}) { - const Soup = await libnotify(); - if (!Soup) { - console.error(Error('missing dependency: libsoup3')); - return new Response( - 400, - 'can not fetch: missing dependency: libsoup3', - false, - null, - ); - } - - const session = new Soup.Session(); - - if (options.params) { - url += '?' + Object.entries(options.params) - .map(([key, value]) => { - if (Array.isArray(value)) { - return value.map(val => - `${encodeURIComponent(key)}=${encodeURIComponent(val)}`).join('&'); - } else if (typeof value === 'object') { - return `${encodeURIComponent(key)}=${encodeURIComponent( - JSON.stringify(value))}`; - } else { - return `${encodeURIComponent(key)}=${encodeURIComponent(value)}`; - } - }) - .join('&'); - } - - const message = new Soup.Message({ - method: options.method || 'GET', - uri: GLib.Uri.parse(url, GLib.UriFlags.NONE), - }); - - if (options.headers) { - for (const key of Object.keys(options.headers)) - message.get_request_headers().append(key, options.headers[key]); - } - - if (typeof options.body === 'string') { - message.set_request_body_from_bytes(null, - new GLib.Bytes((new TextEncoder).encode(options.body))); - } - - const inputStream = await session.send_async(message, 0, null); - const { status_code, reason_phrase } = message; - const ok = status_code >= 200 && status_code < 300; - - return new Response(status_code, reason_phrase, ok, inputStream); -} diff --git a/src/utils/file.ts b/src/utils/file.ts deleted file mode 100644 index ec8d3bf5..00000000 --- a/src/utils/file.ts +++ /dev/null @@ -1,126 +0,0 @@ -import Gio from 'gi://Gio'; -import GLib from 'gi://GLib'; - -export function readFile(file: string | Gio.File) { - try { - const f = typeof file === 'string' - ? Gio.File.new_for_path(file) - : file; - - const [, bytes] = f.load_contents(null); - return new TextDecoder().decode(bytes); - } catch (_) { - return ''; - } -} - -export function readFileAsync(file: string | Gio.File): Promise { - const f = typeof file === 'string' - ? Gio.File.new_for_path(file) - : file; - - return new Promise((resolve, reject) => { - f.load_contents_async(null, (_, res) => { - try { - const [success, bytes] = f.load_contents_finish(res); - if (success) { - resolve(new TextDecoder().decode(bytes)); - } - else { - const path = typeof file === 'string' ? file : file.get_path(); - reject(Error(`reading file ${path} was unsuccessful`)); - } - } catch (error) { - reject(error); - } - }); - }); -} - -export function writeFile(string: string, path: string): Promise { - const file = Gio.File.new_for_path(path); - - return new Promise((resolve, reject) => { - file.replace_contents_bytes_async( - new GLib.Bytes(new TextEncoder().encode(string)), - null, - false, - Gio.FileCreateFlags.REPLACE_DESTINATION, - null, - (_, res) => { - try { - file.replace_contents_finish(res); - resolve(file); - } catch (error) { - reject(error); - } - }, - ); - }); -} - -export function writeFileSync(string: string, path: string): Gio.File { - const file = Gio.File.new_for_path(path); - file.replace_contents( - new TextEncoder().encode(string), - null, - false, - Gio.FileCreateFlags.REPLACE_DESTINATION, - null, - ); - return file; -} - -const fileMonitors: Map = new Map; -export function monitorFile( - path: string, - callback?: (file: Gio.File, event: Gio.FileMonitorEvent) => void, - options: { flags: Gio.FileMonitorFlags; recursive: boolean } = { - flags: Gio.FileMonitorFlags.NONE, - recursive: true, - }, -) { - // FIXME: remove the checking in the next release - if (typeof options === 'number') { - console.warn( - `${options}` + - ' passed as a parameter in `options`.\n' + - 'options parameter should be {flags: Gio.FileMonitorFlags, recursive: boolean}.', - ); - } - - try { - const file = Gio.File.new_for_path(path); - const mon = file.monitor(options.flags, null); - - if (callback) - mon.connect('changed', (_, file, _f, event) => callback(file, event)); - - if (options.recursive && GLib.file_test(path, GLib.FileTest.IS_DIR)) { - const enumerator = file.enumerate_children('standard::*', - Gio.FileQueryInfoFlags.NONE, null); - - let i = enumerator.next_file(null); - while (i) { - if (i.get_file_type() === Gio.FileType.DIRECTORY) { - const path = file.get_child(i.get_name()).get_path(); - if (path) - monitorFile(path, callback, options); - } - i = enumerator.next_file(null); - } - } - - // we need to save a reference in case the user doesn't - // otherwise GC will pick it up - fileMonitors.set(mon, true); - mon.connect('notify::cancelled', () => { - fileMonitors.delete(mon); - }); - - return mon; - } catch (error) { - logError(error); - return null; - } -} diff --git a/src/utils/gobject.ts b/src/utils/gobject.ts deleted file mode 100644 index aa255cfb..00000000 --- a/src/utils/gobject.ts +++ /dev/null @@ -1,111 +0,0 @@ -import Gtk from 'gi://Gtk?version=3.0'; -import GObject from 'gi://GObject'; - -type Camel = S extends `${infer P1}_${infer P2}${infer P3}` - ? `${Lowercase}${Uppercase}${Camel}` : S - -type Kebab = S extends `${infer Head}_${infer Tail}` - ? `${Head}-${Kebab}` : S - -/** - * turns a snake_cased Record into - * snake & camel & kebab keyed Record - */ -export type CtorProps = - T & - { [K in keyof T as Camel]: T[K] } & - { [K in keyof T as Kebab]: T[K] } - -export const kebabify = (str: string) => str - .replace(/([a-z])([A-Z])/g, '$1-$2') - .replaceAll('_', '-') - .toLowerCase(); - -export type PspecFlag = 'rw' | 'r' | 'w'; -export type PspecType = - | 'jsobject' - | 'string' - | 'int' - | 'float' - | 'double' - | 'boolean' - | 'gobject' - | 'widget'; - -export function pspec(name: string, type: PspecType = 'jsobject', handle: PspecFlag = 'r') { - const flags = (() => { - switch (handle) { - case 'w': return GObject.ParamFlags.WRITABLE; - case 'r': return GObject.ParamFlags.READABLE; - case 'rw': - default: return GObject.ParamFlags.READWRITE; - } - })(); - - switch (type) { - case 'string': return GObject.ParamSpec.string( - name, name, name, flags, ''); - - case 'int': return GObject.ParamSpec.int64( - name, name, name, flags, - Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER, 0); - - case 'float': return GObject.ParamSpec.float( - name, name, name, flags, - -1, 1, 0); - - case 'double': return GObject.ParamSpec.double( - name, name, name, flags, - Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER, 0); - - case 'boolean': return GObject.ParamSpec.boolean( - name, name, name, flags, false); - - case 'gobject': return GObject.ParamSpec.object( - name, name, name, flags, GObject.Object.$gtype); - - case 'widget': return GObject.ParamSpec.object( - name, name, name, flags, Gtk.Widget.$gtype); - - default: return GObject.ParamSpec.jsobject( - name, name, name, flags); - } -} - -export function registerGObject< - Obj extends { new(...args: any[]): GObject.Object }, - Config extends { - typename?: string, - signals?: { [signal: string]: PspecType[] }, - properties?: { [prop: string]: [type?: PspecType, handle?: PspecFlag] }, - cssName?: string, - }, ->(object: Obj, config?: Config) { - const Signals: { - [signal: string]: { param_types: GObject.GType[] } - } = {}; - - const Properties: { - [prop: string]: GObject.ParamSpec, - } = {}; - - if (config && config.signals) { - Object.keys(config.signals).forEach(signal => Signals[signal] = { - param_types: config.signals![signal].map(t => - // @ts-expect-error - GObject[`TYPE_${t.toUpperCase()}`]), - }); - } - - if (config && config.properties) { - Object.keys(config.properties).forEach(prop => - Properties[prop] = pspec(prop, ...config.properties![prop]), - ); - } - - return GObject.registerClass(Object.assign({ - GTypeName: config?.typename || `Ags_${object.name}`, - Signals, - Properties, - }, config?.cssName ? { CssName: config.cssName } : {}), object); -} diff --git a/src/utils/init.ts b/src/utils/init.ts deleted file mode 100644 index 2c2c1197..00000000 --- a/src/utils/init.ts +++ /dev/null @@ -1,143 +0,0 @@ -import Gio from 'gi://Gio'; -import GLib from 'gi://GLib'; -import { ensureDirectory } from './etc.js'; -import { readFile, writeFile } from './file.js'; -import { exec } from './exec.js'; -import { HOME } from '../utils.js'; - -export function isRunning(dbusName: string, bus: 'session' | 'system') { - return Gio.DBus[bus].call_sync( - 'org.freedesktop.DBus', - '/org/freedesktop/DBus', - 'org.freedesktop.DBus', - 'NameHasOwner', - GLib.Variant.new_tuple([new GLib.Variant('s', dbusName)]), - new GLib.VariantType('(b)'), - Gio.DBusCallFlags.NONE, - -1, - null, - ).deepUnpack()?.toString() === 'true' || false; -} - -export function parsePath(path: string) { - return path.startsWith('.') - ? `${GLib.getenv('PWD')}${path.slice(1)}` - : path; -} - -const defaultConfig = ` -const time = Variable('', { - poll: [1000, function() { - return Date().toString() - }], -}) - -const Bar = (/** @type {number} */ monitor) => Widget.Window({ - monitor, - name: \`bar\${monitor}\`, - anchor: ['top', 'left', 'right'], - exclusivity: 'exclusive', - child: Widget.CenterBox({ - start_widget: Widget.Label({ - hpack: 'center', - label: 'Welcome to AGS!', - }), - end_widget: Widget.Label({ - hpack: 'center', - label: time.bind(), - }), - }), -}) - -App.config({ - windows: [Bar(0)], -}) -`; - -const readMe = (types: string) => ` -# Starter Config - -if suggestions don't work, first make sure -you have TypeScript LSP working in your editor - -if you do not want typechecking only suggestions - -\`\`\`json -// tsconfig.json -"checkJs": false -\`\`\` - -types are symlinked to: -${types} -`; - -const defaultTsConfig = { - compilerOptions: { - target: 'ES2022', - module: 'ES2022', - lib: ['ES2022'], - allowJs: true, - checkJs: true, - strict: true, - noImplicitAny: false, - baseUrl: '.', - typeRoots: ['./types'], - skipLibCheck: true, - }, -}; - -const nixPaths = [ - `${HOME}/.local`, - `${HOME}/.nix-profile`, - `${HOME}/.local/state/nix/profiles/home-manager`, - '/run/current-system/sw', -].map(path => `${path}/share/${pkg.name}/types`); - -const nixWarning = `nix users! -if ags was installed with nix there is no guarantee -that the types directory got symlinked correctly -and there is no guarante the symlink won't break on updates -if it was symlinked to /nix/store you need to run --init on updates`; - -const tsconfigWarning = `existing tsconfig detected -make sure it has "typeRoots": ["./types"]`; - -export async function init(configDir: string, entry: string) { - ensureDirectory(configDir); - const tsconfig = configDir + '/' + 'tsconfig.json'; - - if (!GLib.file_test(entry, GLib.FileTest.EXISTS)) - await writeFile(defaultConfig.trim(), entry); - - - if (!GLib.file_test(tsconfig, GLib.FileTest.EXISTS)) { - await writeFile(JSON.stringify(defaultTsConfig, null, 2), tsconfig); - } else { - try { - const conf = JSON.parse(readFile(tsconfig)); - const list = conf.compilerOptions.typeRoots; - if (!list.includes('./types')) - conf.compilerOptions.typeRoots = [...list, './types']; - - await writeFile(JSON.stringify(conf, null, 2), tsconfig); - } catch (err) { - logError(err); - console.warn(tsconfigWarning); - } - } - - const linkStore = GLib.getenv('AGS_LINK_NIX_STORE'); - const nixPath = linkStore ? '' : nixPaths.find(path => { - if (GLib.file_test(path, GLib.FileTest.EXISTS)) - return true; - }); - - const types = nixPath || `${pkg.pkgdatadir}/types`; - - if (exec('which nix')) - console.warn(nixWarning); - - exec(`ln -s -f ${types} ${configDir}/types`); - await writeFile(readMe(types), `${configDir}/README.md`); - print(`config directory setup at "${configDir}"`); -} diff --git a/src/utils/notify.ts b/src/utils/notify.ts deleted file mode 100644 index dbd06099..00000000 --- a/src/utils/notify.ts +++ /dev/null @@ -1,187 +0,0 @@ -import App from '../app.js'; -import GLib from 'gi://GLib?version=2.0'; -import { type Urgency, type Hints } from '../service/notifications.js'; - -type ClosedReason = ReturnType - -// libnotify is not async, so it halts the js engine -// when the notification daemon is in the same process -// so when the daemon acquires the dbus name -// it will switch this to true, so we know to -// use the builtin daemon instead of libnotify -export const daemon = { - running: false, -}; - -const _URGENCY = (urgency: Urgency) => { - switch (urgency) { - case 'low': return 0; - case 'critical': return 2; - default: return 1; - } -}; - -const _CLOSED_REASON = (reason: number) => { - switch (reason) { - case -1: return 'unset'; - case 1: return 'timeout'; - case 2: return 'dismissed'; - case 3: return 'closed'; - default: return 'undefined'; - } -}; - -/* - * this module gets loaded on startup, so in order - * to make libnotify an optional dependency we do this - */ -async function libnotify() { - try { - const Notify = (await import('gi://Notify')).default; - - if (Notify.is_initted()) - return Notify; - - Notify.init(null); - return Notify; - } catch (error) { - console.error(Error('Missing dependency: libnotify')); - return null; - } -} - -export interface NotificationArgs { - appName?: string - body?: string - iconName?: string - id?: number - summary?: string - urgency?: Urgency - category?: string - actions?: { - [label: string]: () => void, - } - timeout?: number - onClosed?: (reason: ClosedReason) => void - - // hints - actionIcons?: boolean; - desktopEntry?: string; - image?: string; - resident?: boolean; - soundFile?: string; - soundName?: string; - suppressSound?: boolean; - transient?: boolean; - x?: number; - y?: number; -} - -export async function notify(args: NotificationArgs): Promise -export async function notify( - summary: string, body?: string, iconName?: string): Promise - -export async function notify( - argsOrSummary: NotificationArgs | string, - body = '', - iconName = '', -): Promise { - const args = typeof argsOrSummary === 'object' - ? argsOrSummary - : { - summary: argsOrSummary, - body, - iconName, - }; - - if (daemon.running) { - const { default: Daemon } = await import('../service/notifications.js'); - - const actions = Object.entries(args.actions || {}).map(([label, callback], i) => ({ - id: `${i}`, label, callback, - })); - - const hints: Hints = { - 'action-icons': new GLib.Variant('b', args.actionIcons ?? false), - 'category': new GLib.Variant('s', args.category ?? ''), - 'desktop-entry': new GLib.Variant('s', args.desktopEntry ?? ''), - 'image-path': new GLib.Variant('s', args.image ?? ''), - 'resident': new GLib.Variant('b', args.resident ?? false), - 'sound-file': new GLib.Variant('s', args.soundFile ?? ''), - 'sound-name': new GLib.Variant('s', args.soundName ?? ''), - 'suppress-sound': new GLib.Variant('b', args.suppressSound ?? false), - 'transient': new GLib.Variant('b', args.transient ?? false), - 'urgency': new GLib.Variant('i', args.urgency ?? 1), - }; - - if (args.x !== undefined) - hints['x'] = new GLib.Variant('i', args.x); - - if (args.y !== undefined) - hints['y'] = new GLib.Variant('i', args.y); - - const id = Daemon.Notify( - args.appName || App.applicationId!, - args.id || 0, - args.iconName || '', - args.summary || '', - args.body || '', - actions.flatMap(({ id, label }) => [id, label]), - hints, - args.timeout || 0, - ); - - Daemon.getNotification(id)?.connect('invoked', (_, actionId: string) => { - const action = actions.find(({ id }) => id === actionId); - if (action) - action.callback(); - }); - - return id; - } - - const Notify = await libnotify(); - if (!Notify) { - console.error(Error('missing dependency: libnotify')); - return -1; - } - - const n = new Notify.Notification({ - summary: args.summary ?? '', - body: args.body ?? '', - id: args.id ?? 0, - iconName: args.iconName ?? '', - appName: args.appName ?? Notify.get_app_name(), - }); - - n.set_urgency(_URGENCY(args.urgency ?? 'normal')); - n.set_timeout(args.timeout ?? 0); - - const hint = (key: string, type: 'b' | 's' | 'i', value?: boolean | string | number) => { - if (value) - n.set_hint(key, new GLib.Variant(type, value)); - }; - - hint('action-icons', 'b', args.actionIcons); - hint('desktop-entry', 's', args.desktopEntry); - hint('image-path', 's', args.image); - hint('resident', 'b', args.resident); - hint('sound-file', 's', args.soundFile); - hint('sound-name', 's', args.soundName); - hint('suppress-sound', 'b', args.suppressSound); - hint('transient', 'b', args.transient); - hint('x', 'i', args.x); - hint('y', 'i', args.y); - - Object.keys(args.actions || {}).forEach((action, i) => { - n.add_action(`${i}`, action, args.actions![action]); - }); - - n.connect('closed', () => { - if (args.onClosed) - args.onClosed(_CLOSED_REASON(n.get_closed_reason())); - }); - - n.show(); - return n.id; -} diff --git a/src/utils/pam.ts b/src/utils/pam.ts deleted file mode 100644 index 1cb0b2c6..00000000 --- a/src/utils/pam.ts +++ /dev/null @@ -1,30 +0,0 @@ -//@ts-expect-error missing types -import GUtils from 'gi://GUtils'; -import Gio from 'gi://Gio'; - -export function authenticate(password: string) { - return new Promise((resolve, reject) => { - GUtils.authenticate(password, 0, null, (_: unknown, res: Gio.AsyncResult) => { - try { - resolve(GUtils.authenticate_finish(res)); - } - catch (e) { - reject(e); - } - }); - }); -} - -export function authenticateUser(username: string, password: string) { - return new Promise((resolve, reject) => { - GUtils.authenticate_user( - username, password, 0, null, (_: unknown, res: Gio.AsyncResult) => { - try { - resolve(GUtils.authenticate_finish(res)); - } - catch (e) { - reject(e); - } - }); - }); -} diff --git a/src/utils/timeout.ts b/src/utils/timeout.ts deleted file mode 100644 index 83fdfa45..00000000 --- a/src/utils/timeout.ts +++ /dev/null @@ -1,32 +0,0 @@ -import Gtk from 'gi://Gtk?version=3.0'; -import GLib from 'gi://GLib'; - -export function interval( - interval: number, - callback: () => void, - bind?: Gtk.Widget, -) { - callback(); - const id = GLib.timeout_add(GLib.PRIORITY_DEFAULT, interval, () => { - callback(); - return true; - }); - if (bind) - bind.connect('destroy', () => GLib.source_remove(id)); - - return id; -} - -export function timeout(ms: number, callback: () => void) { - return GLib.timeout_add(GLib.PRIORITY_DEFAULT, ms, () => { - callback(); - return GLib.SOURCE_REMOVE; - }); -} - -export function idle(callback: () => void, prio = GLib.PRIORITY_DEFAULT) { - return GLib.idle_add(prio, () => { - callback(); - return GLib.SOURCE_REMOVE; - }); -} diff --git a/src/variable.ts b/src/variable.ts deleted file mode 100644 index f33f8e24..00000000 --- a/src/variable.ts +++ /dev/null @@ -1,175 +0,0 @@ -import GObject from 'gi://GObject'; -import Gio from 'gi://Gio'; -import GLib from 'gi://GLib'; -import Service, { Binding, Props } from './service.js'; -import { execAsync, interval, subprocess } from './utils.js'; - -type Listen = - | [string[] | string, (out: string, self: Variable) => T] - | [string[] | string] - | string[] - | string; - -type Poll = - | [number, string[] | string | ((self: Variable) => T) | ((self: Variable) => Promise)] - | [number, string[] | string, (out: string, self: Variable) => T]; - -export interface Options { - poll?: Poll - listen?: Listen -} - -export class Variable extends GObject.Object { - static { - Service.register(this, { - 'changed': [], - 'dispose': [], - }, { - 'value': ['jsobject', 'rw'], - 'is-listening': ['boolean', 'r'], - 'is-polling': ['boolean', 'r'], - }); - } - - protected _value!: T; - protected _poll?: Poll; - protected _listen?: Listen; - protected _interval?: number; - protected _subprocess?: Gio.Subprocess | null; - - constructor(value: T, { poll, listen }: Options = {}) { - super(); - this.value = value; - - if (poll) { - this._poll = poll; - this.startPoll(); - } - - if (listen) { - this._listen = listen; - this.startListen(); - } - } - - startPoll() { - if (!this._poll) - return console.error(Error(`${this} has no poll defined`)); - - if (this._interval) - return console.error(Error(`${this} is already polling`)); - - const [time, cmd, transform = (out: string) => out as T] = this._poll; - if (Array.isArray(cmd) || typeof cmd === 'string') { - this._interval = interval(time, () => execAsync(cmd) - .then(out => this.value = transform(out, this)) - .catch(console.error)); - } - if (typeof cmd === 'function') { - this._interval = interval(time, () => { - const value = cmd(this); - if (value instanceof Promise) - value.then(v => this.value = v).catch(console.error); - else - this.value = value; - }); - } - this.notify('is-polling'); - } - - stopPoll() { - if (this._interval) { - GLib.source_remove(this._interval); - this._interval = 0; - } else { - console.error(Error(`${this} has no poll running`)); - } - this.notify('is-polling'); - } - - startListen() { - if (!this._listen) - return console.error(Error(`${this} has no listen defined`)); - - if (this._subprocess) - return console.error(Error(`${this} is already listening`)); - - let cmd: string | string[]; - const transform = typeof this._listen[1] === 'function' - ? this._listen[1] - : (out: string) => out as T; - - // string - if (typeof this._listen === 'string') - cmd = this._listen; - - // string[] - else if (Array.isArray(this._listen) && this._listen.every(s => typeof s === 'string')) - cmd = this._listen as string[]; - - // [string, fn] - else if (Array.isArray(this._listen) && typeof this._listen[0] === 'string') - cmd = this._listen[0]; - - // [string[], fn] - else if (Array.isArray(this._listen) && Array.isArray(this._listen[0])) - cmd = this._listen[0]; - - else - return console.error(Error(`${this._listen} is not a valid type for Variable.listen`)); - - this._subprocess = subprocess(cmd, out => this.value = transform(out, this)); - this.notify('is-listening'); - } - - stopListen() { - if (this._subprocess) { - this._subprocess.force_exit(); - this._subprocess = null; - } else { - console.error(Error(`${this} has no listen running`)); - } - this.notify('is-listening'); - } - - get is_listening() { return !!this._subprocess; } - get is_polling() { return !!this._interval; } - - dispose() { - if (this._interval) - GLib.source_remove(this._interval); - - if (this._subprocess) - this._subprocess.force_exit(); - - this.emit('dispose'); - this.run_dispose(); - } - - getValue() { return this._value; } - setValue(value: T) { - this._value = value; - this.notify('value'); - this.emit('changed'); - } - - get value() { return this._value; } - set value(value: T) { - if (value === this.value) - return; - - this.setValue(value); - } - - connect(signal = 'notify::value', callback: (self: this, ...args: any[]) => void): number { - return super.connect(signal, callback); - } - - bind

>(): Binding - bind

>(prop?: P): Binding - bind

>(prop: P = 'value' as P) { - return new Binding(this, prop); - } -} - -export default (value: T, options?: Options) => new Variable(value, options); diff --git a/src/widget.ts b/src/widget.ts deleted file mode 100644 index f64a6af0..00000000 --- a/src/widget.ts +++ /dev/null @@ -1,125 +0,0 @@ -import Gtk from 'gi://Gtk?version=3.0'; -import { register, type BaseProps, type Widget as TWidget } from './widgets/widget.js'; -import { newBox as Box } from './widgets/box.js'; -import { newButton as Button } from './widgets/button.js'; -import { newCalendar as Calendar } from './widgets/calendar.js'; -import { newCenterBox as CenterBox } from './widgets/centerbox.js'; -import { newCircularProgress as CircularProgress } from './widgets/circularprogress.js'; -import { newColorButton as ColorButton } from './widgets/colorbutton.js'; -import { newDrawingArea as DrawingArea } from './widgets/drawingarea.js'; -import { newEntry as Entry } from './widgets/entry.js'; -import { newEventBox as EventBox } from './widgets/eventbox.js'; -import { newFileChooserButton as FileChooserButton } from './widgets/filechooserbutton.js'; -import { newFixed as Fixed } from './widgets/fixed.js'; -import { newFlowBox as FlowBox } from './widgets/flowbox.js'; -import { newFontButton as FontButton } from './widgets/fontbutton.js'; -import { newIcon as Icon } from './widgets/icon.js'; -import { newLabel as Label } from './widgets/label.js'; -import { newLevelBar as LevelBar } from './widgets/levelbar.js'; -import { newListBox as ListBox } from './widgets/listbox.js'; -import { newMenu as Menu } from './widgets/menu.js'; -import { newMenuBar as MenuBar } from './widgets/menubar.js'; -import { newMenuItem as MenuItem } from './widgets/menuitem.js'; -import { newOverlay as Overlay } from './widgets/overlay.js'; -import { newProgressBar as ProgressBar } from './widgets/progressbar.js'; -import { newRevealer as Revealer } from './widgets/revealer.js'; -import { newScrollable as Scrollable } from './widgets/scrollable.js'; -import { newSeparator as Separator } from './widgets/separator.js'; -import { newSlider as Slider } from './widgets/slider.js'; -import { newSpinButton as SpinButton } from './widgets/spinbutton.js'; -import { newSpinner as Spinner } from './widgets/spinner.js'; -import { newStack as Stack } from './widgets/stack.js'; -import { newSwitch as Switch } from './widgets/switch.js'; -import { newToggleButton as ToggleButton } from './widgets/togglebutton.js'; -import { newWindow as Window } from './widgets/window.js'; - -// ts can't compile export default { subclass, Box, Button ... } -// so we use a function and add members to it instead -// to bundle everything in a default export -export default function W< - T extends { new(...args: any[]): Gtk.Widget }, - Props, ->( - Base: T, typename = Base.name, -) { - class Subclassed extends Base { - static { register(this, { typename }); } - constructor(...params: any[]) { super(...params); } - } - type Instance = InstanceType & TWidget; - return (props: BaseProps, Props, Attr>) => { - return new Subclassed(props) as Instance; - }; -} - -export { - register, - W as subclass, - Box, - Button, - Calendar, - CenterBox, - CircularProgress, - ColorButton, - DrawingArea, - Entry, - EventBox, - FileChooserButton, - Fixed, - FlowBox, - FontButton, - Icon, - Label, - LevelBar, - ListBox, - Menu, - MenuBar, - MenuItem, - Overlay, - ProgressBar, - Revealer, - Scrollable, - Separator, - Slider, - SpinButton, - Spinner, - Stack, - Switch, - ToggleButton, - Window, -}; - -W.register = register; -W.subclass = W; -W.Box = Box; -W.Button = Button; -W.Calendar = Calendar; -W.CenterBox = CenterBox; -W.CircularProgress = CircularProgress; -W.ColorButton = ColorButton; -W.DrawingArea = DrawingArea; -W.Entry = Entry; -W.EventBox = EventBox; -W.FileChooserButton = FileChooserButton; -W.Fixed = Fixed; -W.FlowBox = FlowBox; -W.FontButton = FontButton; -W.Icon = Icon; -W.Label = Label; -W.LevelBar = LevelBar; -W.ListBox = ListBox; -W.Menu = Menu; -W.MenuBar = MenuBar; -W.MenuItem = MenuItem; -W.Overlay = Overlay; -W.ProgressBar = ProgressBar; -W.Revealer = Revealer; -W.Scrollable = Scrollable; -W.Separator = Separator; -W.Slider = Slider; -W.SpinButton = SpinButton; -W.Spinner = Spinner; -W.Stack = Stack; -W.Switch = Switch; -W.ToggleButton = ToggleButton; -W.Window = Window; diff --git a/src/widgets/box.ts b/src/widgets/box.ts deleted file mode 100644 index 7c938246..00000000 --- a/src/widgets/box.ts +++ /dev/null @@ -1,76 +0,0 @@ -import { register, type BaseProps, type Widget } from './widget.js'; -import Gtk from 'gi://Gtk?version=3.0'; - -export type BoxProps< - Child extends Gtk.Widget = Gtk.Widget, - Attr = unknown, - Self = Box -> = BaseProps; - -export function newBox< - Child extends Gtk.Widget = Gtk.Widget, - Attr = unknown ->(...props: ConstructorParameters>) { - return new Box(...props); -} - -// eslint-disable-next-line @typescript-eslint/no-unused-vars -export interface Box extends Widget { } -export class Box extends Gtk.Box { - static { - register(this, { - properties: { - 'vertical': ['boolean', 'rw'], - 'children': ['jsobject', 'rw'], - }, - }); - } - - constructor(propsOrChildren: BoxProps | Child[] = {}, ...children: Gtk.Widget[]) { - const props = Array.isArray(propsOrChildren) ? {} : propsOrChildren; - - if (Array.isArray(propsOrChildren)) - props.children = propsOrChildren; - - else if (children.length > 0) - props.children = children as Child[]; - - super(props as Gtk.Box.ConstructorProperties); - this.connect('notify::orientation', () => this.notify('vertical')); - } - - get child() { return this.children[0] as Child; } - set child(child: Child) { this.children = [child]; } - - get children() { return this.get_children() as Child[]; } - set children(children: Child[]) { - const newChildren = children || []; - - this.get_children() - .filter(ch => !newChildren?.includes(ch as Child)) - .forEach(ch => ch.destroy()); - - // remove any children that weren't destroyed so - // we can re-add everything in the correct new order - this.get_children() - .forEach(ch => this.remove(ch)); - - if (!children) - return; - - children.forEach(w => w && this.add(w)); - this.notify('children'); - this.show_all(); - } - - get vertical() { return this.orientation === Gtk.Orientation.VERTICAL; } - set vertical(v: boolean) { - this.orientation = Gtk.Orientation[v ? 'VERTICAL' : 'HORIZONTAL']; - } -} - -export default Box; diff --git a/src/widgets/button.ts b/src/widgets/button.ts deleted file mode 100644 index 41029cf3..00000000 --- a/src/widgets/button.ts +++ /dev/null @@ -1,172 +0,0 @@ -import { register, type BaseProps, type Widget } from './widget.js'; -import Gtk from 'gi://Gtk?version=3.0'; -import Gdk from 'gi://Gdk?version=3.0'; - -type EventHandler = (self: Self, event: Gdk.Event) => boolean | unknown; - -export type ButtonProps< - Child extends Gtk.Widget = Gtk.Widget, - Attr = unknown, - Self = Button, -> = BaseProps void - - on_hover?: EventHandler - on_hover_lost?: EventHandler - - on_scroll_up?: EventHandler - on_scroll_down?: EventHandler - - on_primary_click?: EventHandler - on_middle_click?: EventHandler - on_secondary_click?: EventHandler - - on_primary_click_release?: EventHandler - on_middle_click_release?: EventHandler - on_secondary_click_release?: EventHandler -}, Attr>; - -export function newButton< - Child extends Gtk.Widget = Gtk.Widget, - Attr = unknown, ->(...props: ConstructorParameters>) { - return new Button(...props); -} - -// eslint-disable-next-line @typescript-eslint/no-unused-vars -export interface Button extends Widget { } -export class Button extends Gtk.Button { - static { - register(this, { - properties: { - 'on-clicked': ['jsobject', 'rw'], - - 'on-hover': ['jsobject', 'rw'], - 'on-hover-lost': ['jsobject', 'rw'], - - 'on-scroll-up': ['jsobject', 'rw'], - 'on-scroll-down': ['jsobject', 'rw'], - - 'on-primary-click': ['jsobject', 'rw'], - 'on-secondary-click': ['jsobject', 'rw'], - 'on-middle-click': ['jsobject', 'rw'], - - 'on-primary-click-release': ['jsobject', 'rw'], - 'on-secondary-click-release': ['jsobject', 'rw'], - 'on-middle-click-release': ['jsobject', 'rw'], - }, - }); - } - - constructor(props: ButtonProps = {}, child?: Child) { - if (child) - props.child = child; - - super(props as Gtk.Button.ConstructorProperties); - this.add_events(Gdk.EventMask.SCROLL_MASK); - this.add_events(Gdk.EventMask.SMOOTH_SCROLL_MASK); - - this.connect('clicked', () => this.on_clicked?.(this)); - - this.connect('enter-notify-event', (_, event: Gdk.Event) => { - if (this.isHovered(event)) - return this.on_hover?.(this, event); - }); - - this.connect('leave-notify-event', (_, event: Gdk.Event) => { - if (!this.isHovered(event)) - return this.on_hover_lost?.(this, event); - }); - - this.connect('button-press-event', (_, event: Gdk.Event) => { - if (event.get_button()[1] === Gdk.BUTTON_PRIMARY) - return this.on_primary_click?.(this, event); - - else if (event.get_button()[1] === Gdk.BUTTON_MIDDLE) - return this.on_middle_click?.(this, event); - - else if (event.get_button()[1] === Gdk.BUTTON_SECONDARY) - return this.on_secondary_click?.(this, event); - }); - - this.connect('button-release-event', (_, event: Gdk.Event) => { - if (event.get_button()[1] === Gdk.BUTTON_PRIMARY) - return this.on_primary_click_release?.(this, event); - - else if (event.get_button()[1] === Gdk.BUTTON_MIDDLE) - return this.on_middle_click_release?.(this, event); - - else if (event.get_button()[1] === Gdk.BUTTON_SECONDARY) - return this.on_secondary_click_release?.(this, event); - }); - - this.connect('scroll-event', (_, event: Gdk.Event) => { - if (event.get_scroll_deltas()[2] < 0) - return this.on_scroll_up?.(this, event); - - else if (event.get_scroll_deltas()[2] > 0) - return this.on_scroll_down?.(this, event); - }); - } - - get child() { return super.child as Child; } - set child(child: Child) { super.child = child; } - - get on_clicked() { return this._get('on-clicked'); } - set on_clicked(callback: (self: this) => void) { - this._set('on-clicked', callback); - } - - get on_hover() { return this._get('on-hover'); } - set on_hover(callback: EventHandler) { - this._set('on-hover', callback); - } - - get on_hover_lost() { return this._get('on-hover-lost'); } - set on_hover_lost(callback: EventHandler) { - this._set('on-hover-lost', callback); - } - - get on_scroll_up() { return this._get('on-scroll-up'); } - set on_scroll_up(callback: EventHandler) { - this._set('on-scroll-up', callback); - } - - get on_scroll_down() { return this._get('on-scroll-down'); } - set on_scroll_down(callback: EventHandler) { - this._set('on-scroll-down', callback); - } - - get on_primary_click() { return this._get('on-primary-click'); } - set on_primary_click(callback: EventHandler) { - this._set('on-primary-click', callback); - } - - get on_middle_click() { return this._get('on-middle-click'); } - set on_middle_click(callback: EventHandler) { - this._set('on-middle-click', callback); - } - - get on_secondary_click() { return this._get('on-secondary-click'); } - set on_secondary_click(callback: EventHandler) { - this._set('on-secondary-click', callback); - } - - get on_primary_click_release() { return this._get('on-primary-click-release'); } - set on_primary_click_release(callback: EventHandler) { - this._set('on-primary-click-release', callback); - } - - get on_middle_click_release() { return this._get('on-middle-click-release'); } - set on_middle_click_release(callback: EventHandler) { - this._set('on-middle-click-release', callback); - } - - get on_secondary_click_release() { return this._get('on-secondary-click-release'); } - set on_secondary_click_release(callback: EventHandler) { - this._set('on-secondary-click-release', callback); - } -} - -export default Button; diff --git a/src/widgets/calendar.ts b/src/widgets/calendar.ts deleted file mode 100644 index c19cba81..00000000 --- a/src/widgets/calendar.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { register, type BaseProps, type Widget } from './widget.js'; -import Gtk from 'gi://Gtk?version=3.0'; - -type Event = (self: Self) => void -type Detail = (self: Self, year: number, month: number, day: number) => string | null - - -export type CalendarProps< - Attr = unknown, - Self = Calendar, -> = BaseProps - detail?: Detail, -}, Attr>; - -export function newCalendar< - Attr = unknown ->(...props: ConstructorParameters>) { - return new Calendar(...props); -} - -// eslint-disable-next-line @typescript-eslint/no-unused-vars -export interface Calendar extends Widget { } -export class Calendar extends Gtk.Calendar { - static { - register(this, { - properties: { - 'date': ['jsobject', 'r'], - 'on-day-selected': ['jsobject', 'rw'], - 'detail': ['jsobject', 'rw'], - }, - }); - } - - constructor(props: CalendarProps = {}) { - super(props as Gtk.Calendar.ConstructorProperties); - this.connect('notify::day', () => this.notify('date')); - this.connect('notify::month', () => this.notify('date')); - this.connect('notify::year', () => this.notify('date')); - this.connect('day-selected', this.on_day_selected.bind(this)); - } - - get date() { return this.get_date(); } - - get on_day_selected() { return this._get('on-day-selected') || (() => false); } - set on_day_selected(callback: Event) { this._set('on-day-selected', callback); } - - get detail() { return this._get('detail-func'); } - set detail(func: Detail) { - this._set('detail-func', func); - this.set_detail_func((self, ...date) => func(self as this, ...date)); - } -} - -export default Calendar; diff --git a/src/widgets/centerbox.ts b/src/widgets/centerbox.ts deleted file mode 100644 index 77c834a6..00000000 --- a/src/widgets/centerbox.ts +++ /dev/null @@ -1,134 +0,0 @@ -import Gtk from 'gi://Gtk?version=3.0'; -import { register, type BaseProps, type Widget } from './widget.js'; - -export type CenterBoxProps< - StartWidget extends Gtk.Widget = Gtk.Widget, - CenterWidget extends Gtk.Widget = Gtk.Widget, - EndWidget extends Gtk.Widget = Gtk.Widget, - Attr = unknown, - Self = CenterBox, -> = BaseProps; - -export function newCenterBox< - StartWidget extends Gtk.Widget = Gtk.Widget, - CenterWidget extends Gtk.Widget = Gtk.Widget, - EndWidget extends Gtk.Widget = Gtk.Widget, - Attr = unknown, ->(...props: ConstructorParameters>) { - return new CenterBox(...props); -} - -// eslint-disable-next-line @typescript-eslint/no-unused-vars -export interface CenterBox extends Widget { } -export class CenterBox< - StartWidget extends Gtk.Widget, - CenterWidget extends Gtk.Widget, - EndWidget extends Gtk.Widget, - Attr -> extends Gtk.Box { - static { - register(this, { - properties: { - 'vertical': ['boolean', 'rw'], - 'children': ['boolean', 'rw'], - 'start-widget': ['widget', 'rw'], - 'center-widget': ['widget', 'rw'], - 'end-widget': ['widget', 'rw'], - }, - }); - } - - constructor( - props: CenterBoxProps = {}, - startWidget?: StartWidget, - centerWidget?: CenterWidget, - endWidget?: EndWidget, - ) { - if (startWidget) - props.start_widget = startWidget; - - if (centerWidget) - props.center_widget = centerWidget; - - if (endWidget) - props.end_widget = endWidget; - - super(props as Gtk.Widget.ConstructorProperties); - } - - get children() { return [this.start_widget, this.center_widget, this.end_widget]; } - set children(children: [StartWidget | null, CenterWidget | null, EndWidget | null]) { - const newChildren = children || []; - - newChildren.filter(ch => !newChildren?.includes(ch)) - .forEach(ch => ch && ch.destroy()); - - if (children[0]) - this.start_widget = children[0]; - - if (children[1]) - this.center_widget = children[1]; - - if (children[2]) - this.end_widget = children[2]; - } - - get start_widget() { return this._get('start-widget') || null as StartWidget | null; } - set start_widget(child: StartWidget | null) { - if (this.start_widget) - this.start_widget.destroy(); - - this._set('start-widget', child); - - if (!child) - return; - - this.pack_start(child, true, true, 0); - this.show_all(); - } - - get end_widget() { return this._get('end-widget') || null as EndWidget | null; } - set end_widget(child: EndWidget | null) { - if (this.end_widget) - this.end_widget.destroy(); - - this._set('end-widget', child); - - if (!child) - return; - - this.pack_end(child, true, true, 0); - this.show_all(); - } - - get center_widget() { return this.get_center_widget() as CenterWidget | null; } - set center_widget(child: CenterWidget | null) { - const center_widget = this.get_center_widget(); - if (!child && center_widget) { - center_widget.destroy(); - return; - } - - this.set_center_widget(child); - this.notify('center-widget'); - } - - get vertical() { return this.orientation === Gtk.Orientation.VERTICAL; } - set vertical(vertical: boolean) { - if (this.vertical === vertical) - return; - - this.orientation = vertical - ? Gtk.Orientation.VERTICAL : Gtk.Orientation.HORIZONTAL; - - this.notify('vertical'); - } -} - -export default CenterBox; diff --git a/src/widgets/circularprogress.ts b/src/widgets/circularprogress.ts deleted file mode 100644 index 45c20003..00000000 --- a/src/widgets/circularprogress.ts +++ /dev/null @@ -1,280 +0,0 @@ -import { register, type BaseProps, type Widget } from './widget.js'; -import Gtk from 'gi://Gtk?version=3.0'; - -interface Context { - setSourceRGBA: (r: number, g: number, b: number, a: number) => void - arc: (x: number, y: number, r: number, a1: number, a2: number) => void - setLineWidth: (w: number) => void - lineTo: (x: number, y: number) => void - stroke: () => void - fill: () => void - $dispose: () => void -} - -export type CircularProgressProps< - Child extends Gtk.Widget = Gtk.Widget, - Attr = unknown, - Self = CircularProgress -> = BaseProps - -export function newCircularProgress< - Child extends Gtk.Widget = Gtk.Widget, - Attr = unknown, ->(...props: ConstructorParameters>) { - return new CircularProgress(...props); -} - -// eslint-disable-next-line @typescript-eslint/no-unused-vars -export interface CircularProgress extends Widget { } -export class CircularProgress< - Child extends Gtk.Widget, - Attr = unknown, -> extends Gtk.Bin { - static { - register(this, { - cssName: 'circular-progress', - properties: { - 'start-at': ['float', 'rw'], - 'end-at': ['float', 'rw'], - 'value': ['float', 'rw'], - 'inverted': ['boolean', 'rw'], - 'rounded': ['boolean', 'rw'], - }, - }); - } - - constructor(props: CircularProgressProps = {}, child?: Child) { - if (child) - props.child = child; - - super(props as Gtk.Bin.ConstructorProperties); - } - - get child() { return super.child as Child; } - set child(child: Child) { super.child = child; } - - get rounded() { return this._get('rounded') || false; } - set rounded(r: boolean) { - if (this.rounded === r) - return; - - this._set('rounded', r); - this.queue_draw(); - } - - get inverted() { return this._get('inverted') || false; } - set inverted(inverted: boolean) { - if (this.inverted === inverted) - return; - - this._set('inverted', inverted); - this.queue_draw(); - } - - get start_at() { return this._get('start-at') || 0; } - set start_at(value: number) { - if (this.start_at === value) - return; - - if (value > 1) - value = 1; - - if (value < 0) - value = 0; - - this._set('start-at', value); - this.queue_draw(); - } - - get end_at() { return this._get('end-at') || this.start_at; } - set end_at(value: number) { - if (this.end_at === value) - return; - - if (value > 1) - value = 1; - - if (value < 0) - value = 0; - - this._set('end-at', value); - this.queue_draw(); - } - - get value() { return this._get('value') || 0; } - set value(value: number) { - if (this.value === value) - return; - - if (value > 1) - value = 1; - - if (value < 0) - value = 0; - - - this._set('value', value); - this.queue_draw(); - } - - vfunc_get_preferred_height(): [number, number] { - let minHeight = this.get_style_context() - .get_property('min-height', Gtk.StateFlags.NORMAL) as number; - if (minHeight <= 0) - minHeight = 40; - - return [minHeight, minHeight]; - } - - vfunc_get_preferred_width(): [number, number] { - let minWidth = this.get_style_context() - .get_property('min-width', Gtk.StateFlags.NORMAL) as number; - if (minWidth <= 0) - minWidth = 40; - - return [minWidth, minWidth]; - } - - private _toRadian(percentage: number) { - percentage = Math.floor(percentage * 100); - return (percentage / 100) * (2 * Math.PI); - } - - - private _isFullCircle(start: number, end: number, epsilon = 1e-10): boolean { - // Ensure that start and end are between 0 and 1 - start = (start % 1 + 1) % 1; - end = (end % 1 + 1) % 1; - - // Check if the difference between start and end is close to 1 - return Math.abs(start - end) <= epsilon; - } - - private _scaleArcValue(start: number, end: number, value: number): number { - // Ensure that start and end are between 0 and 1 - start = (start % 1 + 1) % 1; - end = (end % 1 + 1) % 1; - - // Calculate the length of the arc - let arcLength = end - start; - if (arcLength < 0) - arcLength += 1; // Adjust for circular representation - - // Calculate the scaled value on the arc based on the arcLength - let scaled = arcLength * value; - - // Ensure the scaled value is between 0 and 1 - scaled = (scaled % 1 + 1) % 1; - - return scaled; - } - - vfunc_draw(cr: Context): boolean { - const allocation = this.get_allocation(); - const styles = this.get_style_context(); - const width = allocation.width; - const height = allocation.height; - const thickness = styles.get_property('font-size', Gtk.StateFlags.NORMAL) as number; - const margin = styles.get_margin(Gtk.StateFlags.NORMAL); - const fg = styles.get_color(Gtk.StateFlags.NORMAL); - const bg = styles.get_background_color(Gtk.StateFlags.NORMAL); - const bgStroke = thickness + Math.min(margin.bottom, margin.top, margin.left, margin.right); - const fgStroke = thickness; - const radius = Math.min(width, height) / 2.0 - Math.max(bgStroke, fgStroke) / 2.0; - const center = { x: width / 2, y: height / 2 }; - - const startBackground = this._toRadian(this.start_at); - let endBackground = this._toRadian(this.end_at); - let rangedValue; - - const isCircle = this._isFullCircle(this.start_at, this.end_at); - - if (isCircle) { - // Redefine endDraw in radius to create an accurate full circle - endBackground = startBackground + 2 * Math.PI; - rangedValue = this._toRadian(this.value); - } else { - // Scale the value for the arc shape - rangedValue = this._toRadian( - this._scaleArcValue( - this.start_at, - this.end_at, - this.value, - ), - ); - } - - let startProgress, endProgress; - - if (this.inverted) { - startProgress = endBackground - rangedValue; - endProgress = endBackground; - } else { - startProgress = startBackground; - endProgress = startBackground + rangedValue; - } - - // Draw background - cr.setSourceRGBA(bg.red, bg.green, bg.blue, bg.alpha); - cr.arc(center.x, center.y, radius, startBackground, endBackground); - cr.setLineWidth(bgStroke); - cr.stroke(); - - // Draw rounded background ends - if (this.rounded) { - const start = { - x: center.x + Math.cos(startBackground) * radius, - y: center.y + Math.sin(startBackground) * radius, - }; - const end = { - x: center.x + Math.cos(endBackground) * radius, - y: center.y + Math.sin(endBackground) * radius, - }; - cr.setLineWidth(0); - cr.arc(start.x, start.y, fgStroke / 2, 0, 0 - 0.01); - cr.fill(); - cr.arc(end.x, end.y, fgStroke / 2, 0, 0 - 0.01); - cr.fill(); - } - - // Draw progress - cr.setSourceRGBA(fg.red, fg.green, fg.blue, fg.alpha); - cr.arc(center.x, center.y, radius, startProgress, endProgress); - cr.setLineWidth(fgStroke); - cr.stroke(); - - // Draw rounded progress ends - if (this.rounded) { - const start = { - x: center.x + Math.cos(startProgress) * radius, - y: center.y + Math.sin(startProgress) * radius, - }; - const end = { - x: center.x + Math.cos(endProgress) * radius, - y: center.y + Math.sin(endProgress) * radius, - }; - cr.setLineWidth(0); - cr.arc(start.x, start.y, fgStroke / 2, 0, 0 - 0.01); - cr.fill(); - cr.arc(end.x, end.y, fgStroke / 2, 0, 0 - 0.01); - cr.fill(); - } - - if (this.child) { - this.child.size_allocate(allocation); - this.propagate_draw(this.child, cr); - } - - cr.$dispose(); - return true; - } -} - -export default CircularProgress; diff --git a/src/widgets/colorbutton.ts b/src/widgets/colorbutton.ts deleted file mode 100644 index 3bde0e55..00000000 --- a/src/widgets/colorbutton.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { register, type BaseProps, type Widget } from './widget.js'; -import Gtk from 'gi://Gtk?version=3.0'; - -type Event = (self: Self) => void | boolean - -export type ColorButtonProps< - Child extends Gtk.Widget = Gtk.Widget, - Attr = unknown, - Self = ColorButton, -> = BaseProps -}, Attr>; - -export function newColorButton< - Child extends Gtk.Widget = Gtk.Widget, - Attr = unknown, ->(...props: ConstructorParameters>) { - return new ColorButton(...props); -} - -// eslint-disable-next-line @typescript-eslint/no-unused-vars -export interface ColorButton extends Widget { } -export class ColorButton extends Gtk.ColorButton { - static { - register(this, { - properties: { - 'on-color-set': ['jsobject', 'rw'], - }, - }); - } - - constructor(props: ColorButtonProps = {}, child?: Child) { - if (child) - props.child = child; - - super(props as Gtk.ColorButton.ConstructorProperties); - this.connect('color-set', this.on_color_set.bind(this)); - } - - get child() { return super.child as Child; } - set child(child: Child) { super.child = child; } - - get on_color_set() { return this._get('on-color-set') || (() => false); } - set on_color_set(callback: Event) { this._set('on-color-set', callback); } -} - -export default ColorButton; diff --git a/src/widgets/drawingarea.ts b/src/widgets/drawingarea.ts deleted file mode 100644 index abdce3a8..00000000 --- a/src/widgets/drawingarea.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { register, type BaseProps, type Widget } from './widget.js'; -import Gtk from 'gi://Gtk?version=3.0'; -import Cairo from 'gi://cairo?version=1.0'; - -type DrawFn = (self: Self, cr: Cairo.Context, width: number, height: number) => void - -export type DrawingAreaProps< - Attr = unknown, - Self = DrawingArea, -> = BaseProps -}, Attr>; - -export function newDrawingArea< - Attr = unknown ->(...props: ConstructorParameters>) { - return new DrawingArea(...props); -} - -export interface DrawingArea extends Widget { } -export class DrawingArea extends Gtk.DrawingArea { - static { - register(this, { - properties: { - 'draw-fn': ['jsobject', 'rw'], - }, - }); - } - - constructor(props: DrawingAreaProps = {}) { - super(props as Gtk.DrawingArea.ConstructorProperties); - this.connect('draw', (self, cr: Cairo.Content) => { - const w = this.get_allocated_width(); - const h = this.get_allocated_height(); - this.draw_fn(self, cr, w, h); - }); - } - - get draw_fn() { return this._get('draw-fn') || (() => undefined); } - set draw_fn(fn: DrawFn) { this._set('draw-fn', fn); } -} - -export default DrawingArea; diff --git a/src/widgets/entry.ts b/src/widgets/entry.ts deleted file mode 100644 index 7625f4a0..00000000 --- a/src/widgets/entry.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { register, type BaseProps, type Widget } from './widget.js'; -import Gtk from 'gi://Gtk?version=3.0'; - -type EventHandler = (self: Self) => void | unknown; - -export type EntryProps< - Attr = unknown, - Self = Entry, -> = BaseProps - on_change?: EventHandler -}, Attr> - -export function newEntry< - Attr = unknown ->(...props: ConstructorParameters>) { - return new Entry(...props); -} - -export interface Entry extends Widget { } -export class Entry extends Gtk.Entry { - static { - register(this, { - properties: { - 'on-accept': ['jsobject', 'rw'], - 'on-change': ['jsobject', 'rw'], - }, - }); - } - - constructor(props: EntryProps = {}) { - super(props as Gtk.Entry.ConstructorProperties); - - this.connect('activate', () => this.on_accept?.(this)); - this.connect('notify::text', () => this.on_change?.(this)); - } - - get on_accept() { return this._get('on-accept'); } - set on_accept(callback: EventHandler) { - this._set('on-accept', callback); - } - - get on_change() { return this._get('on-change'); } - set on_change(callback: EventHandler) { - this._set('on-change', callback); - } -} - -export default Entry; diff --git a/src/widgets/eventbox.ts b/src/widgets/eventbox.ts deleted file mode 100644 index d26b112b..00000000 --- a/src/widgets/eventbox.ts +++ /dev/null @@ -1,169 +0,0 @@ -import { register, type BaseProps, type Widget } from './widget.js'; -import Gtk from 'gi://Gtk?version=3.0'; -import Gdk from 'gi://Gdk?version=3.0'; - -type EventHandler = (self: Self, event: Gdk.Event) => boolean | unknown; - -export type EventBoxProps< - Child extends Gtk.Widget = Gtk.Widget, - Attr = unknown, - Self = EventBox, -> = BaseProps - on_hover_lost?: EventHandler - - on_scroll_up?: EventHandler - on_scroll_down?: EventHandler - - on_primary_click?: EventHandler - on_middle_click?: EventHandler - on_secondary_click?: EventHandler - - on_primary_click_release?: EventHandler - on_middle_click_release?: EventHandler - on_secondary_click_release?: EventHandler -}, Attr> - -export function newEventBox< - Child extends Gtk.Widget = Gtk.Widget, - Attr = unknown ->(...props: ConstructorParameters>) { - return new EventBox(...props); -} - -// eslint-disable-next-line @typescript-eslint/no-unused-vars -export interface EventBox extends Widget { } -export class EventBox extends Gtk.EventBox { - static { - register(this, { - properties: { - 'on-clicked': ['jsobject', 'rw'], - - 'on-hover': ['jsobject', 'rw'], - 'on-hover-lost': ['jsobject', 'rw'], - - 'on-scroll-up': ['jsobject', 'rw'], - 'on-scroll-down': ['jsobject', 'rw'], - - 'on-primary-click': ['jsobject', 'rw'], - 'on-secondary-click': ['jsobject', 'rw'], - 'on-middle-click': ['jsobject', 'rw'], - - 'on-primary-click-release': ['jsobject', 'rw'], - 'on-secondary-click-release': ['jsobject', 'rw'], - 'on-middle-click-release': ['jsobject', 'rw'], - }, - }); - } - - constructor(props: EventBoxProps = {}, child?: Child) { - if (child) - props.child = child; - - super(props as Gtk.EventBox.ConstructorProperties); - this.add_events(Gdk.EventMask.SCROLL_MASK); - this.add_events(Gdk.EventMask.SMOOTH_SCROLL_MASK); - - this.connect('enter-notify-event', (_, event: Gdk.Event) => { - if (this.isHovered(event)) { - this.set_state_flags(Gtk.StateFlags.PRELIGHT, false); - return this.on_hover?.(this, event); - } - }); - - this.connect('leave-notify-event', (_, event: Gdk.Event) => { - if (!this.isHovered(event)) { - this.unset_state_flags(Gtk.StateFlags.PRELIGHT); - return this.on_hover_lost?.(this, event); - } - }); - - this.connect('button-press-event', (_, event: Gdk.Event) => { - this.set_state_flags(Gtk.StateFlags.ACTIVE, false); - if (event.get_button()[1] === Gdk.BUTTON_PRIMARY) - return this.on_primary_click?.(this, event); - - else if (event.get_button()[1] === Gdk.BUTTON_MIDDLE) - return this.on_middle_click?.(this, event); - - else if (event.get_button()[1] === Gdk.BUTTON_SECONDARY) - return this.on_secondary_click?.(this, event); - }); - - this.connect('button-release-event', (_, event: Gdk.Event) => { - this.unset_state_flags(Gtk.StateFlags.ACTIVE); - if (event.get_button()[1] === Gdk.BUTTON_PRIMARY) - return this.on_primary_click_release?.(this, event); - - else if (event.get_button()[1] === Gdk.BUTTON_MIDDLE) - return this.on_middle_click_release?.(this, event); - - else if (event.get_button()[1] === Gdk.BUTTON_SECONDARY) - return this.on_secondary_click_release?.(this, event); - }); - - this.connect('scroll-event', (_, event: Gdk.Event) => { - if (event.get_scroll_deltas()[2] < 0) - return this.on_scroll_up?.(this, event); - - else if (event.get_scroll_deltas()[2] > 0) - return this.on_scroll_down?.(this, event); - }); - } - - get child() { return super.child as Child; } - set child(child: Child) { super.child = child; } - - get on_hover() { return this._get('on-hover'); } - set on_hover(callback: EventHandler) { - this._set('on-hover', callback); - } - - get on_hover_lost() { return this._get('on-hover-lost'); } - set on_hover_lost(callback: EventHandler) { - this._set('on-hover-lost', callback); - } - - get on_scroll_up() { return this._get('on-scroll-up'); } - set on_scroll_up(callback: EventHandler) { - this._set('on-scroll-up', callback); - } - - get on_scroll_down() { return this._get('on-scroll-down'); } - set on_scroll_down(callback: EventHandler) { - this._set('on-scroll-down', callback); - } - - get on_primary_click() { return this._get('on-primary-click'); } - set on_primary_click(callback: EventHandler) { - this._set('on-primary-click', callback); - } - - get on_middle_click() { return this._get('on-middle-click'); } - set on_middle_click(callback: EventHandler) { - this._set('on-middle-click', callback); - } - - get on_secondary_click() { return this._get('on-secondary-click'); } - set on_secondary_click(callback: EventHandler) { - this._set('on-secondary-click', callback); - } - - get on_primary_click_release() { return this._get('on-primary-click-release'); } - set on_primary_click_release(callback: EventHandler) { - this._set('on-primary-click-release', callback); - } - - get on_middle_click_release() { return this._get('on-middle-click-release'); } - set on_middle_click_release(callback: EventHandler) { - this._set('on-middle-click-release', callback); - } - - get on_secondary_click_release() { return this._get('on-secondary-click-release'); } - set on_secondary_click_release(callback: EventHandler) { - this._set('on-secondary-click-release', callback); - } -} - -export default EventBox; diff --git a/src/widgets/filechooserbutton.ts b/src/widgets/filechooserbutton.ts deleted file mode 100644 index 4367ba6f..00000000 --- a/src/widgets/filechooserbutton.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { register, type BaseProps, type Widget } from './widget.js'; -import Gtk from 'gi://Gtk?version=3.0'; - -type Event = (self: Self) => void | boolean - -export type FileChooserButtonProps< - Child extends Gtk.Widget = Gtk.Widget, - Attr = unknown, - Self = FileChooserButton, -> = BaseProps -}, Attr>; - -export function newFileChooserButton< - Child extends Gtk.Widget = Gtk.Widget, - Attr = unknown, ->(...props: ConstructorParameters>) { - return new FileChooserButton(...props); -} - -// eslint-disable-next-line @typescript-eslint/no-unused-vars -export interface FileChooserButton extends Widget { } -export class FileChooserButton extends Gtk.FileChooserButton { - static { - register(this, { - properties: { - 'on-file-set': ['jsobject', 'rw'], - }, - }); - } - - constructor(props: FileChooserButtonProps = {}, child?: Child) { - if (child) - props.child = child; - - super(props as Gtk.FileChooserButton.ConstructorProperties); - this.connect('file-set', this.on_file_set.bind(this)); - } - - get child() { return super.child as Child; } - set child(child: Child) { super.child = child; } - - get on_file_set() { return this._get('on-file-set') || (() => false); } - set on_file_set(callback: Event) { this._set('on-file-set', callback); } - - get uri() { return this.get_uri(); } - get uris() { return this.get_uris(); } -} - -export default FileChooserButton; diff --git a/src/widgets/fixed.ts b/src/widgets/fixed.ts deleted file mode 100644 index 1348b05e..00000000 --- a/src/widgets/fixed.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { register, type BaseProps, type Widget } from './widget.js'; -import Gtk from 'gi://Gtk?version=3.0'; - -// TODO: - -export type FixedProps< - Attr = unknown, - Self = Fixed, -> = BaseProps - -export function newFixed< - Attr = unknown ->(...props: ConstructorParameters>) { - return new Fixed(...props); -} - -export interface Fixed extends Widget { } -export class Fixed extends Gtk.Fixed { - static { register(this); } - - constructor(props: FixedProps = {}) { - super(props as Gtk.Fixed.ConstructorProperties); - } -} - -export default Fixed; diff --git a/src/widgets/flowbox.ts b/src/widgets/flowbox.ts deleted file mode 100644 index c4e0b872..00000000 --- a/src/widgets/flowbox.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { register, type BaseProps, type Widget } from './widget.js'; -import Gtk from 'gi://Gtk?version=3.0'; - -// TODO: - -export type FlowBoxProps< - Attr = unknown, - Self = FlowBox, -> = BaseProps - -export function newFlowBox< - Attr = unknown ->(...props: ConstructorParameters>) { - return new FlowBox(...props); -} - -export interface FlowBox extends Widget { } -export class FlowBox extends Gtk.FlowBox { - static { register(this); } - - constructor(props: FlowBoxProps = {}) { - super(props as Gtk.FlowBox.ConstructorProperties); - } -} - -export default FlowBox; diff --git a/src/widgets/fontbutton.ts b/src/widgets/fontbutton.ts deleted file mode 100644 index e98b5b0a..00000000 --- a/src/widgets/fontbutton.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { register, type BaseProps, type Widget } from './widget.js'; -import Gtk from 'gi://Gtk?version=3.0'; - -type Event = (self: Self) => void | boolean - -export type FontButtonProps< - Child extends Gtk.Widget = Gtk.Widget, - Attr = unknown, - Self = FontButton, -> = BaseProps -}, Attr>; - -export function newFontButton< - Child extends Gtk.Widget = Gtk.Widget, - Attr = unknown, ->(...props: ConstructorParameters>) { - return new FontButton(...props); -} - -// eslint-disable-next-line @typescript-eslint/no-unused-vars -export interface FontButton extends Widget { } -export class FontButton extends Gtk.FontButton { - static { - register(this, { - properties: { - 'on-font-set': ['jsobject', 'rw'], - }, - }); - } - - constructor(props: FontButtonProps = {}, child?: Child) { - if (child) - props.child = child; - - super(props as Gtk.FontButton.ConstructorProperties); - this.connect('font-set', this.on_font_set.bind(this)); - } - - get child() { return super.child as Child; } - set child(child: Child) { super.child = child; } - - get on_font_set() { return this._get('on-font-set') || (() => false); } - set on_font_set(callback: Event) { this._set('on-font-set', callback); } -} - -export default FontButton; diff --git a/src/widgets/icon.ts b/src/widgets/icon.ts deleted file mode 100644 index 1502ebd2..00000000 --- a/src/widgets/icon.ts +++ /dev/null @@ -1,129 +0,0 @@ -import { register, type BaseProps, type Widget } from './widget.js'; -import Gtk from 'gi://Gtk?version=3.0'; -import GLib from 'gi://GLib'; -import GdkPixbuf from 'gi://GdkPixbuf'; -import Gdk from 'gi://Gdk?version=3.0'; -import cairo from '@girs/cairo-1.0'; -import { lookUpIcon } from '../utils.js'; - -type Ico = string | GdkPixbuf.Pixbuf - -export type IconProps< - Attr = unknown, - Self = Icon, -> = BaseProps - -export function newIcon< - Attr = unknown ->(...props: ConstructorParameters>) { - return new Icon(...props); -} - -export interface Icon extends Widget { } -export class Icon extends Gtk.Image { - static { - register(this, { - properties: { - 'size': ['double', 'rw'], - 'icon': ['jsobject', 'rw'], - }, - }); - } - - constructor(props: IconProps | Ico = {}) { - const { icon = '', ...rest } = props as IconProps; - super(typeof props === 'string' || props instanceof GdkPixbuf.Pixbuf - ? {} - : rest as Gtk.Image.ConstructorProperties); - - // jsobject pspec can't take a string, so we have to set it after the constructor - this._handleParamProp('icon', - typeof props === 'string' || props instanceof GdkPixbuf.Pixbuf - ? props : icon); - } - - get size() { return this._get('size') || this._fontSize || 0; } - set size(size: number) { - this._set('size', size); - this.queue_draw(); - } - - get icon() { return this._get('icon'); } - set icon(icon: string | GdkPixbuf.Pixbuf) { - this._set('icon', icon); - this._set('type', 'named', false); - - if (typeof icon === 'string') { - if (lookUpIcon(icon)) { - this._set('type', 'named', false); - } - else if (GLib.file_test(icon, GLib.FileTest.EXISTS)) { - this._set('type', 'file', false); - } - else if (icon !== '') { - console.warn(Error(`can't assign "${icon}" as icon, ` + - 'it is not a file nor a named icon')); - } - } - else if (icon instanceof GdkPixbuf.Pixbuf) { - this._set('type', 'pixbuf', false); - } - else { - console.warn(Error(`expected Pixbuf or string for icon, but got ${typeof icon}`)); - } - - this._size(); - } - - private _previousSize = 0; - private _fontSize = 0; - private _size() { - if (this.size === 0) - return; - - const type = this._get<'file' | 'named' | 'pixbuf'>('type'); - this._previousSize = this.size; - - if (type === 'file') { - const pb = GdkPixbuf.Pixbuf.new_from_file_at_size( - this.icon as string, - this.size * this.scale_factor, - this.size * this.scale_factor, - ); - const cs = Gdk.cairo_surface_create_from_pixbuf(pb, 0, this.get_window()); - this.set_from_surface(cs); - } - - else if (type === 'named') { - this.icon_name = this.icon as string; - this.pixel_size = this.size; - } - - else if (type === 'pixbuf') { - const pb_scaled = (this.icon as GdkPixbuf.Pixbuf).scale_simple( - this.size * this.scale_factor, - this.size * this.scale_factor, - GdkPixbuf.InterpType.BILINEAR, - ); - if (pb_scaled) { - const cs = Gdk.cairo_surface_create_from_pixbuf(pb_scaled, 0, this.get_window()); - this.set_from_surface(cs); - } - } - } - - vfunc_draw(cr: cairo.Context): boolean { - this._fontSize = this.get_style_context() - .get_property('font-size', Gtk.StateFlags.NORMAL) as number; - - if (this._previousSize !== this.size) - this._size(); - - return super.vfunc_draw(cr); - } -} - -export default Icon; diff --git a/src/widgets/label.ts b/src/widgets/label.ts deleted file mode 100644 index 45d3ab38..00000000 --- a/src/widgets/label.ts +++ /dev/null @@ -1,116 +0,0 @@ -import { register, type BaseProps, type Widget } from './widget.js'; -import Gtk from 'gi://Gtk?version=3.0'; -import GLib from 'gi://GLib'; -import Pango from 'gi://Pango'; - -const JUSTIFICATION = { - 'left': Gtk.Justification.LEFT, - 'right': Gtk.Justification.RIGHT, - 'center': Gtk.Justification.CENTER, - 'fill': Gtk.Justification.FILL, -} as const; - -const TRUNCATE = { - 'none': Pango.EllipsizeMode.NONE, - 'start': Pango.EllipsizeMode.START, - 'middle': Pango.EllipsizeMode.MIDDLE, - 'end': Pango.EllipsizeMode.END, -} as const; - -type Justification = keyof typeof JUSTIFICATION; -type Truncate = keyof typeof TRUNCATE; - -export type LabelProps< - Attr = unknown, - Self = Label, -> = BaseProps - -export function newLabel< - Attr = unknown ->(...props: ConstructorParameters>) { - return new Label(...props); -} - -export interface Label extends Widget { } -export class Label extends Gtk.Label { - static { - register(this, { - properties: { - 'justification': ['string', 'rw'], - 'truncate': ['string', 'rw'], - }, - }); - } - - constructor(props: LabelProps | string = {}) { - const { label, ...config } = props as Gtk.Label.ConstructorProperties; - const text = typeof props === 'string' ? props : label; - super(typeof props === 'string' ? {} : config); - this._handleParamProp('label', text || ''); - this.connect('notify::justify', () => this.notify('justification')); - this.connect('notify::ellipsize', () => this.notify('truncate')); - } - - get label() { return super.label || ''; } - set label(label: string) { - if (this.use_markup) { - try { - Pango.parse_markup(label, -1, '0'); - } catch (e) { - // @ts-expect-error - if (e instanceof GLib.MarkupError) - label = GLib.markup_escape_text(label, -1) || ''; - else - logError(e); - } - } - super.label = label; - } - - get truncate() { - return Object.keys(TRUNCATE).find(key => { - return TRUNCATE[key as Truncate] === this.ellipsize; - }) as Truncate; - } - - set truncate(truncate: Truncate) { - if (this.truncate === truncate) - return; - - if (!Object.keys(TRUNCATE).includes(truncate)) { - console.error(Error( - `truncate for Label has to be one of ${Object.keys(TRUNCATE)}, ` + - `but it is ${truncate}`, - )); - return; - } - - this.ellipsize = TRUNCATE[truncate]; - } - - get justification() { - return Object.keys(JUSTIFICATION).find(key => { - return JUSTIFICATION[key as Justification] === this.justify; - }) as Justification; - } - - set justification(justify: Justification) { - if (this.justification === justify) - return; - - if (!Object.keys(JUSTIFICATION).includes(justify)) { - console.error(Error( - `justify for Label has to be one of ${Object.keys(JUSTIFICATION)}, ` + - `but it is ${justify}`, - )); - return; - } - - this.justify = JUSTIFICATION[justify]; - } -} - -export default Label; diff --git a/src/widgets/levelbar.ts b/src/widgets/levelbar.ts deleted file mode 100644 index 9ee27fb7..00000000 --- a/src/widgets/levelbar.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { register, type BaseProps, type Widget } from './widget.js'; -import Gtk from 'gi://Gtk?version=3.0'; - -type BarMode = 'continuous' | 'discrete' - -export type LevelBarProps< - Attr = unknown, - Self = LevelBar, -> = BaseProps; - -export function newLevelBar< - Attr = unknown ->(...props: ConstructorParameters>) { - return new LevelBar(...props); -} - -// eslint-disable-next-line @typescript-eslint/no-unused-vars -export interface LevelBar extends Widget { } -export class LevelBar extends Gtk.LevelBar { - static { - register(this, { - properties: { - 'bar-mode': ['string', 'rw'], - 'vertical': ['boolean', 'rw'], - }, - }); - } - - constructor(props: LevelBarProps = {}) { - super(props as Gtk.LevelBar.ConstructorProperties); - this.connect('notify::mode', () => this.notify('bar-mode')); - this.connect('notify::orientation', () => this.notify('vertical')); - } - - get bar_mode() { - return this.mode === Gtk.LevelBarMode.CONTINUOUS ? 'continuous' : 'discrete'; - } - - set bar_mode(mode: BarMode) { - this.mode = Gtk.LevelBarMode[mode === 'continuous' ? 'CONTINUOUS' : 'DISCRETE']; - } - - get vertical() { return this.orientation === Gtk.Orientation.VERTICAL; } - set vertical(v: boolean) { - this.orientation = Gtk.Orientation[v ? 'VERTICAL' : 'HORIZONTAL']; - } -} - -export default LevelBar; diff --git a/src/widgets/listbox.ts b/src/widgets/listbox.ts deleted file mode 100644 index fcf7f193..00000000 --- a/src/widgets/listbox.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { register, type BaseProps, type Widget } from './widget.js'; -import Gtk from 'gi://Gtk?version=3.0'; - -// TODO: - -export type ListBoxProps< - Attr = unknown, - Self = ListBox, -> = BaseProps - -export function newListBox< - Attr = unknown ->(...props: ConstructorParameters>) { - return new ListBox(...props); -} - -export interface ListBox extends Widget { } -export class ListBox extends Gtk.ListBox { - static { register(this); } - - constructor(props: ListBoxProps = {}) { - super(props as Gtk.ListBox.ConstructorProperties); - } -} - -export default ListBox; diff --git a/src/widgets/menu.ts b/src/widgets/menu.ts deleted file mode 100644 index de1ded7d..00000000 --- a/src/widgets/menu.ts +++ /dev/null @@ -1,78 +0,0 @@ -import { register, type BaseProps, type Widget } from './widget.js'; -import Gtk from 'gi://Gtk?version=3.0'; - -type MenuEventHandler = { - on_popup?: ( - self: Self, - flipped_rect: any | null, - final_rect: any | null, - flipped_x: boolean, - flipped_y: boolean, - ) => void | unknown - on_move_scroll?: (self: Self, scroll_type: Gtk.ScrollType) => void | unknown -} - -export type MenuProps< - MenuItem extends Gtk.MenuItem = Gtk.MenuItem, - Attr = unknown, - Self = Menu, -> = BaseProps, Attr> - -export function newMenu< - MenuItem extends Gtk.MenuItem = Gtk.MenuItem, - Attr = unknown, ->(...props: ConstructorParameters>) { - return new Menu(...props); -} - -// eslint-disable-next-line @typescript-eslint/no-unused-vars -export interface Menu extends Widget { } -export class Menu extends Gtk.Menu { - static { - register(this, { - properties: { - 'children': ['jsobject', 'rw'], - 'on-popup': ['jsobject', 'rw'], - 'on-move-scroll': ['jsobject', 'rw'], - }, - }); - } - - constructor(props: MenuProps = {}, ...children: Gtk.MenuItem[]) { - if (children.length > 0) - props.children = children as MenuItem[]; - - super(props as Gtk.Menu.ConstructorProperties); - - this.connect('popped-up', (_, ...args) => this.on_popup?.(this, ...args)); - this.connect('move-scroll', (_, ...args) => this.on_move_scroll?.(this, ...args)); - } - - get on_popup() { return this._get('on-popup'); } - set on_popup(callback: MenuEventHandler['on_popup']) { - this._set('on-popup', callback); - } - - get on_move_scroll() { return this._get('on-move-scroll'); } - set on_move_scroll(callback: MenuEventHandler['on_move_scroll']) { - this._set('on-move-scroll', callback); - } - - get children() { return this.get_children() as MenuItem[]; } - set children(children: MenuItem[]) { - this.get_children().forEach(ch => ch.destroy()); - - if (!children) - return; - - children.forEach(w => w && this.add(w)); - - const visible = this.visible; - this.show_all(); - this.visible = visible; - } -} - -export default Menu; diff --git a/src/widgets/menubar.ts b/src/widgets/menubar.ts deleted file mode 100644 index 3d701ae6..00000000 --- a/src/widgets/menubar.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { register, type BaseProps, type Widget } from './widget.js'; -import Gtk from 'gi://Gtk?version=3.0'; - -// TODO: - -export type MenuBarProps< - Attr = unknown, - Self = MenuBar, -> = BaseProps - -export function newMenuBar< - Attr = unknown ->(...props: ConstructorParameters>) { - return new MenuBar(...props); -} - -export interface MenuBar extends Widget { } -export class MenuBar extends Gtk.MenuBar { - static { register(this); } - - constructor(props: MenuBarProps = {}) { - super(props as Gtk.MenuBar.ConstructorProperties); - } -} - -export default MenuBar; diff --git a/src/widgets/menuitem.ts b/src/widgets/menuitem.ts deleted file mode 100644 index 5fdf6c21..00000000 --- a/src/widgets/menuitem.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { register, type BaseProps, type Widget } from './widget.js'; -import Gtk from 'gi://Gtk?version=3.0'; - -type Event = (self: Self) => boolean | unknown; - -export type MenuItemProps< - Child extends Gtk.Widget = Gtk.Widget, - Attr = unknown, - Self = MenuItem, -> = BaseProps - on_select?: Event - on_deselct?: Event -}, Attr> - -export function newMenuItem< - Child extends Gtk.Widget = Gtk.Widget, - Attr = unknown, ->(...props: ConstructorParameters>) { - return new MenuItem(...props); -} - -// eslint-disable-next-line @typescript-eslint/no-unused-vars -export interface MenuItem extends Widget { } -export class MenuItem extends Gtk.MenuItem { - static { - register(this, { - properties: { - 'on-activate': ['jsobject', 'rw'], - 'on-select': ['jsobject', 'rw'], - 'on-deselect': ['jsobject', 'rw'], - }, - }); - } - - constructor(props: MenuItemProps = {}, child?: Child) { - if (child) - props.child = child; - - super(props as Gtk.MenuItem.ConstructorProperties); - - this.connect('activate', () => this.on_activate?.(this)); - this.connect('select', () => this.on_select?.(this)); - this.connect('deselect', () => this.on_deselect?.(this)); - } - - get child() { return super.child as Child; } - set child(child: Child) { super.child = child; } - - - get on_activate() { return this._get('on-activate'); } - set on_activate(callback: Event) { - this._set('on-activate', callback); - } - - get on_select() { return this._get('on-select'); } - set on_select(callback: Event) { - this._set('on-select', callback); - } - - get on_deselect() { return this._get('on-deselect'); } - set on_deselect(callback: Event) { - this._set('on-deselect', callback); - } -} - -export default MenuItem; diff --git a/src/widgets/overlay.ts b/src/widgets/overlay.ts deleted file mode 100644 index 4f798c38..00000000 --- a/src/widgets/overlay.ts +++ /dev/null @@ -1,95 +0,0 @@ -import { register, type BaseProps, type Widget } from './widget.js'; -import Gtk from 'gi://Gtk?version=3.0'; - -export type OverlayProps< - Child extends Gtk.Widget = Gtk.Widget, - OverlayChild extends Gtk.Widget = Gtk.Widget, - Attr = unknown, - Self = Overlay, -> = BaseProps - -export function newOverlay< - Child extends Gtk.Widget = Gtk.Widget, - OverlayChild extends Gtk.Widget = Gtk.Widget, - Attr = unknown, ->(...props: ConstructorParameters>) { - return new Overlay(...props); -} - -// eslint-disable-next-line @typescript-eslint/no-unused-vars -export interface Overlay extends Widget { } -export class Overlay< - Child extends Gtk.Widget, - OverlayChild extends Gtk.Widget, - Attr -> extends Gtk.Overlay { - static { - register(this, { - properties: { - 'pass-through': ['boolean', 'rw'], - 'overlays': ['jsobject', 'rw'], - 'overlay': ['jsobject', 'rw'], - }, - }); - } - - constructor( - props: OverlayProps = {}, - child?: Child, - ...overlays: Gtk.Widget[] - ) { - if (child) - props.child = child; - - if (overlays.length > 0) - props.overlays = overlays as OverlayChild[]; - - super(props as Gtk.Overlay.ConstructorProperties); - } - - private _updatePassThrough() { - this.get_children().forEach(ch => - this.set_overlay_pass_through(ch, this._get('pass-through'))); - } - - get pass_through() { return this._get('pass-through'); } - set pass_through(passthrough: boolean) { - if (this.pass_through === passthrough) - return; - - this._set('pass-through', passthrough); - this._updatePassThrough(); - this.notify('pass-through'); - } - - get overlay() { return this.overlays[0] as Child; } - set overlay(overlay: Child) { - this.overlays = [overlay]; - this.notify('overlay'); - } - - get overlays() { - return this.get_children().filter(ch => ch !== this.child) as Child[]; - } - - set overlays(overlays: Child[]) { - this.get_children() - .filter(ch => ch !== this.child && !overlays.includes(ch as Child)) - .forEach(ch => ch.destroy()); - - this.get_children() - .filter(ch => ch !== this.child) - .forEach(ch => this.remove(ch)); - - overlays.forEach(ch => this.add_overlay(ch)); - this._updatePassThrough(); - this.notify('overlays'); - } -} - -export default Overlay; diff --git a/src/widgets/progressbar.ts b/src/widgets/progressbar.ts deleted file mode 100644 index 80fae9f5..00000000 --- a/src/widgets/progressbar.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { register, type BaseProps, type Widget } from './widget.js'; -import Gtk from 'gi://Gtk?version=3.0'; - -export type ProgressBarProps< - Attr = unknown, - Self = ProgressBar, -> = BaseProps - -export function newProgressBar< - Attr = unknown ->(...props: ConstructorParameters>) { - return new ProgressBar(...props); -} - -export interface ProgressBar extends Widget { } -export class ProgressBar extends Gtk.ProgressBar { - static { - register(this, { - properties: { - 'vertical': ['boolean', 'rw'], - 'value': ['float', 'rw'], - }, - }); - } - - constructor(props: ProgressBarProps = {}) { - super(props as Gtk.ProgressBar.ConstructorProperties); - this.connect('notify::fraction', () => this.notify('value')); - this.connect('notify::orientation', () => this.notify('vertical')); - } - - get value() { return this.fraction; } - set value(value: number) { this.fraction = value; } - - get vertical() { return this.orientation === Gtk.Orientation.VERTICAL; } - set vertical(v: boolean) { - this.orientation = Gtk.Orientation[v ? 'VERTICAL' : 'HORIZONTAL']; - } -} - -export default ProgressBar; diff --git a/src/widgets/revealer.ts b/src/widgets/revealer.ts deleted file mode 100644 index 8c5f6b22..00000000 --- a/src/widgets/revealer.ts +++ /dev/null @@ -1,74 +0,0 @@ -import { register, type BaseProps, type Widget } from './widget.js'; -import Gtk from 'gi://Gtk?version=3.0'; - -const TRANSITION = { - 'none': Gtk.RevealerTransitionType.NONE, - 'crossfade': Gtk.RevealerTransitionType.CROSSFADE, - 'slide_right': Gtk.RevealerTransitionType.SLIDE_RIGHT, - 'slide_left': Gtk.RevealerTransitionType.SLIDE_LEFT, - 'slide_up': Gtk.RevealerTransitionType.SLIDE_UP, - 'slide_down': Gtk.RevealerTransitionType.SLIDE_DOWN, -} as const; - -type Transition = keyof typeof TRANSITION; - -export type RevealerProps< - Child extends Gtk.Widget = Gtk.Widget, - Attr = unknown, - Self = Revealer, -> = BaseProps - -export function newRevealer< - Child extends Gtk.Widget = Gtk.Widget, - Attr = unknown, ->(...props: ConstructorParameters>) { - return new Revealer(...props); -} - -// eslint-disable-next-line @typescript-eslint/no-unused-vars -export interface Revealer extends Widget { } -export class Revealer extends Gtk.Revealer { - static { - register(this, { - properties: { 'transition': ['string', 'rw'] }, - }); - } - - constructor(props: RevealerProps = {}, child?: Child) { - if (child) - props.child = child; - - super(props as Gtk.Revealer.ConstructorProperties); - this.connect('notify::transition-type', () => this.notify('transition')); - } - - get child() { return super.child as Child; } - set child(child: Child) { super.child = child; } - - - get transition() { - return Object.keys(TRANSITION).find(key => { - return TRANSITION[key as Transition] === this.transition_type; - }) as Transition; - } - - set transition(transition: Transition) { - if (this.transition === transition) - return; - - if (!Object.keys(TRANSITION).includes(transition)) { - console.error(Error( - `transition on Revealer has to be one of ${Object.keys(TRANSITION)}, ` + - `but it is ${transition}`, - )); - return; - } - - this.transition_type = TRANSITION[transition]; - } -} - -export default Revealer; diff --git a/src/widgets/scrollable.ts b/src/widgets/scrollable.ts deleted file mode 100644 index aa689291..00000000 --- a/src/widgets/scrollable.ts +++ /dev/null @@ -1,96 +0,0 @@ -import { register, type BaseProps, type Widget } from './widget.js'; -import Gtk from 'gi://Gtk?version=3.0'; - -const POLICY = { - 'automatic': Gtk.PolicyType.AUTOMATIC, - 'always': Gtk.PolicyType.ALWAYS, - 'never': Gtk.PolicyType.NEVER, - 'external': Gtk.PolicyType.EXTERNAL, -} as const; - -type Policy = keyof typeof POLICY; - -export type ScrollableProps< - Child extends Gtk.Widget = Gtk.Widget, - Attr = unknown, - Self = Scrollable, -> = BaseProps - -export function newScrollable< - Child extends Gtk.Widget = Gtk.Widget, - Attr = unknown, ->(...props: ConstructorParameters>) { - return new Scrollable(...props); -} - -// eslint-disable-next-line @typescript-eslint/no-unused-vars -export interface Scrollable extends Widget { } -export class Scrollable extends Gtk.ScrolledWindow { - static { - register(this, { - properties: { - 'hscroll': ['string', 'rw'], - 'vscroll': ['string', 'rw'], - }, - }); - } - - constructor(props: ScrollableProps = {}, child?: Child) { - if (child) - props.child = child; - - super({ - ...props as Gtk.ScrolledWindow.ConstructorProperties, - hadjustment: new Gtk.Adjustment(), - vadjustment: new Gtk.Adjustment(), - }); - - this.connect('destroy', () => { - if (this.child instanceof Gtk.Viewport) - this.child.child.destroy(); - }); - } - - get child() { return super.child as Child; } - set child(child: Child) { - if (this.child instanceof Gtk.Viewport) - this.child.child = child; - else - super.child = child; - } - - setScroll(orientation: 'h' | 'v', scroll: Policy) { - if (!scroll || this[`${orientation}scroll`] === scroll) - return; - - if (!Object.keys(POLICY).includes(scroll)) { - return console.error(Error( - `${orientation}scroll has to be one of ${Object.keys(POLICY)}, but it is ${scroll}`, - )); - } - - this._set(`${orientation}scroll`, scroll); - this._policy(); - } - - get hscroll() { return this._get('hscroll'); } - set hscroll(hscroll: Policy) { this.setScroll('h', hscroll); } - - get vscroll() { return this._get('vscroll'); } - set vscroll(vscroll: Policy) { this.setScroll('v', vscroll); } - - private _policy() { - const hscroll = POLICY[this.hscroll]; - const vscroll = POLICY[this.vscroll]; - this.set_policy( - hscroll === -1 ? Gtk.PolicyType.AUTOMATIC : hscroll, - vscroll === -1 ? Gtk.PolicyType.AUTOMATIC : vscroll, - ); - } -} - -export default Scrollable; diff --git a/src/widgets/separator.ts b/src/widgets/separator.ts deleted file mode 100644 index 4835fb62..00000000 --- a/src/widgets/separator.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { register, type BaseProps, type Widget } from './widget.js'; -import Gtk from 'gi://Gtk?version=3.0'; - -export type SeparatorProps< - Attr = unknown, - Self = Separator, -> = BaseProps; - -export function newSeparator< - Attr = unknown ->(...props: ConstructorParameters>) { - return new Separator(...props); -} - -// eslint-disable-next-line @typescript-eslint/no-unused-vars -export interface Separator extends Widget { } -export class Separator extends Gtk.Separator { - static { - register(this, { - properties: { - 'vertical': ['boolean', 'rw'], - }, - }); - } - - constructor(props: SeparatorProps = {}) { - super(props as Gtk.Separator.ConstructorProperties); - this.connect('notify::orientation', () => this.notify('vertical')); - } - - get vertical() { return this.orientation === Gtk.Orientation.VERTICAL; } - set vertical(v: boolean) { - this.orientation = Gtk.Orientation[v ? 'VERTICAL' : 'HORIZONTAL']; - } -} - -export default Separator; diff --git a/src/widgets/slider.ts b/src/widgets/slider.ts deleted file mode 100644 index 68fe8dab..00000000 --- a/src/widgets/slider.ts +++ /dev/null @@ -1,175 +0,0 @@ -import { register, type BaseProps, type Widget } from './widget.js'; -import Gtk from 'gi://Gtk?version=3.0'; -import Gdk from 'gi://Gdk?version=3.0'; - -type EventHandler = (self: Self, event: Gdk.Event) => void | unknown; - -const POSITION = { - 'left': Gtk.PositionType.LEFT, - 'right': Gtk.PositionType.RIGHT, - 'top': Gtk.PositionType.TOP, - 'bottom': Gtk.PositionType.BOTTOM, -} as const; - -type Position = keyof typeof POSITION; - -type Mark = [number, string?, Position?] | number; - -export type SliderProps< - Attr = unknown, - Self = Slider, -> = BaseProps, Gtk.Scale.ConstructorProperties & { - on_change?: EventHandler, - value?: number - slider?: boolean - min?: number - max?: number - step?: number - marks?: Mark[] -}, Attr> - -export function newSlider< - Attr = unknown ->(...props: ConstructorParameters>) { - return new Slider(...props); -} - -export interface Slider extends Widget { } -export class Slider extends Gtk.Scale { - static { - register(this, { - properties: { - 'dragging': ['boolean', 'r'], - 'vertical': ['boolean', 'rw'], - 'value': ['double', 'rw'], - 'min': ['double', 'rw'], - 'max': ['double', 'rw'], - 'step': ['double', 'rw'], - 'on-change': ['jsobject', 'rw'], - }, - }); - } - - constructor({ - value = 0, - min = 0, - max = 1, - step = 0.01, - marks = [], - ...rest - }: SliderProps = {}) { - super({ - adjustment: new Gtk.Adjustment, - ...rest as Gtk.Scale.ConstructorProperties, - }); - - this._handleParamProp('value', value); - this._handleParamProp('min', min); - this._handleParamProp('max', max); - this._handleParamProp('step', step); - this._handleParamProp('marks', marks); - - this.adjustment.connect('notify::value', (_, event: Gdk.Event) => { - if (!this.dragging) - return; - - this.on_change?.(this, event); - }); - } - - get on_change() { return this._get('on-change'); } - set on_change(callback: EventHandler) { this._set('on-change', callback); } - - get value() { return this.adjustment.value; } - set value(value: number) { - if (this.dragging || this.value === value) - return; - - this.adjustment.value = value; - this.notify('value'); - } - - get min() { return this.adjustment.lower; } - set min(min: number) { - if (this.min === min) - return; - - this.adjustment.lower = min; - this.notify('min'); - } - - get max() { return this.adjustment.upper; } - set max(max: number) { - if (this.max === max) - return; - - this.adjustment.upper = max; - this.notify('max'); - } - - get step() { return this.adjustment.step_increment; } - set step(step: number) { - if (this.step === step) - return; - - this.adjustment.step_increment = step; - this.notify('step'); - } - - set marks(marks: Mark[]) { - this.clear_marks(); - marks.forEach(mark => { - if (typeof mark === 'number') { - this.add_mark(mark, Gtk.PositionType.TOP, ''); - } - else { - const positionType = mark[2] - ? POSITION[mark[2]] - : Gtk.PositionType.TOP; - - this.add_mark(mark[0], positionType, mark[1] || ''); - } - }); - } - - - get dragging() { return this._get('dragging'); } - set dragging(dragging: boolean) { this._set('dragging', dragging); } - - get vertical() { return this.orientation === Gtk.Orientation.VERTICAL; } - set vertical(v: boolean) { - this.orientation = Gtk.Orientation[v ? 'VERTICAL' : 'HORIZONTAL']; - } - - vfunc_button_release_event(event: Gdk.EventButton): boolean { - this.dragging = false; - return super.vfunc_button_release_event(event); - } - - vfunc_button_press_event(event: Gdk.EventButton): boolean { - this.dragging = true; - return super.vfunc_button_press_event(event); - } - - vfunc_key_press_event(event: Gdk.EventKey): boolean { - this.dragging = true; - return super.vfunc_key_press_event(event); - } - - vfunc_key_release_event(event: Gdk.EventKey): boolean { - this.dragging = false; - return super.vfunc_key_release_event(event); - } - - vfunc_scroll_event(event: Gdk.EventScroll): boolean { - this.dragging = true; - event.delta_y > 0 - ? this.adjustment.value -= this.step - : this.adjustment.value += this.step; - - this.dragging = false; - return super.vfunc_scroll_event(event); - } -} - -export default Slider; diff --git a/src/widgets/spinbutton.ts b/src/widgets/spinbutton.ts deleted file mode 100644 index 0ecdc3c4..00000000 --- a/src/widgets/spinbutton.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { register, type BaseProps, type Widget } from './widget.js'; -import Gtk from 'gi://Gtk?version=3.0'; - -type Event = (self: Self) => void | boolean - -export type SpinButtonProps< - Attr = unknown, - Self = SpinButton, -> = BaseProps - range?: [min: number, max: number], - increments?: [step: number, page: number], -}, Attr>; - -export function newSpinButton< - Attr = unknown, ->(...props: ConstructorParameters>) { - return new SpinButton(...props); -} - -// eslint-disable-next-line @typescript-eslint/no-unused-vars -export interface SpinButton extends Widget { } -export class SpinButton extends Gtk.SpinButton { - static { - register(this, { - properties: { - 'on-value-changed': ['jsobject', 'rw'], - 'range': ['jsobject', 'rw'], - 'increments': ['jsobject', 'rw'], - }, - }); - } - - constructor(props: SpinButtonProps = {}) { - super(props as Gtk.SpinButton.ConstructorProperties); - this.connect('value-changed', this.on_value_changed.bind(this)); - } - - get on_value_changed() { return this._get('on-value-changed') || (() => false); } - set on_value_changed(callback: Event) { this._set('on-value-changed', callback); } - - get range() { return this.get_range(); } - set range([min, max]: [number, number]) { - if (typeof min === 'number' && typeof max === 'number') - this.set_range(min, max); - } - - get increments() { return this.get_increments(); } - set increments([step, page]: [number, number]) { - if (typeof step === 'number' && typeof page === 'number') - this.set_increments(step, page); - } -} - -export default SpinButton; diff --git a/src/widgets/spinner.ts b/src/widgets/spinner.ts deleted file mode 100644 index b0164725..00000000 --- a/src/widgets/spinner.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { register, type BaseProps, type Widget } from './widget.js'; -import Gtk from 'gi://Gtk?version=3.0'; - -export type SpinnerProps< - Attr = unknown -> = BaseProps, Gtk.Spinner.ConstructorProperties, Attr>; - -export function newSpinner< - Attr = unknown ->(...props: ConstructorParameters>) { - return new Spinner(...props); -} - -export interface Spinner extends Widget { } -export class Spinner extends Gtk.Spinner { - static { register(this); } - - constructor(props: BaseProps, Gtk.Spinner.ConstructorProperties, Attr> = {}) { - super(props as Gtk.Widget.ConstructorProperties); - this.start(); - this.connect('notify::visible', ({ visible }) => { - visible ? this.start() : this.stop(); - }); - } -} - -export default Spinner; diff --git a/src/widgets/stack.ts b/src/widgets/stack.ts deleted file mode 100644 index 422f60af..00000000 --- a/src/widgets/stack.ts +++ /dev/null @@ -1,156 +0,0 @@ -import { register, type BaseProps, type Widget } from './widget.js'; -import Gtk from 'gi://Gtk?version=3.0'; - -const TRANSITION = { - 'none': Gtk.StackTransitionType.NONE, - 'crossfade': Gtk.StackTransitionType.CROSSFADE, - 'slide_right': Gtk.StackTransitionType.SLIDE_RIGHT, - 'slide_left': Gtk.StackTransitionType.SLIDE_LEFT, - 'slide_up': Gtk.StackTransitionType.SLIDE_UP, - 'slide_down': Gtk.StackTransitionType.SLIDE_DOWN, - 'slide_left_right': Gtk.StackTransitionType.SLIDE_LEFT_RIGHT, - 'slide_up_down': Gtk.StackTransitionType.SLIDE_UP_DOWN, - 'over_up': Gtk.StackTransitionType.OVER_UP, - 'over_down': Gtk.StackTransitionType.OVER_DOWN, - 'over_left': Gtk.StackTransitionType.OVER_LEFT, - 'over_right': Gtk.StackTransitionType.OVER_RIGHT, - 'under_up': Gtk.StackTransitionType.UNDER_UP, - 'under_down': Gtk.StackTransitionType.UNDER_DOWN, - 'under_left': Gtk.StackTransitionType.UNDER_LEFT, - 'under_right': Gtk.StackTransitionType.UNDER_RIGHT, - 'over_up_down': Gtk.StackTransitionType.OVER_UP_DOWN, - 'over_down_up': Gtk.StackTransitionType.OVER_DOWN_UP, - 'over_left_right': Gtk.StackTransitionType.OVER_LEFT_RIGHT, - 'over_right_left': Gtk.StackTransitionType.OVER_RIGHT_LEFT, -} as const; - -type Transition = keyof typeof TRANSITION; - -export type StackProps< - Children extends { [name: string]: Gtk.Widget } = { [name: string]: Gtk.Widget }, - Attr = unknown, - Self = Stack, -> = BaseProps - -export function newStack< - Children extends { [name: string]: Gtk.Widget } = { [name: string]: Gtk.Widget }, - Attr = unknown, ->(...props: ConstructorParameters>) { - return new Stack(...props); -} - -// eslint-disable-next-line @typescript-eslint/no-unused-vars -export interface Stack extends Widget { } -export class Stack extends Gtk.Stack { - static { - register(this, { - properties: { - 'transition': ['string', 'rw'], - 'shown': ['string', 'rw'], - 'children': ['jsobject', 'rw'], - // FIXME: deprecated - 'items': ['jsobject', 'rw'], - }, - }); - } - - constructor(props: StackProps = {}, children?: Children) { - if (children) - props.children = children; - - super(props as Gtk.Stack.ConstructorProperties); - this.connect('notify::visible-child-name', () => this.notify('shown')); - } - - add_named(child: Gtk.Widget, name: string): void { - // @ts-expect-error - this.children[name] = child; - this.children = { ...this.children }; - } - - get children() { return this._get('children') || {}; } - set children(children: Children) { - if (!children) - return; - - const oldCh = Object.values(this.children); - const newCh = Object.values(children); - for (const widget of oldCh) { - if (!newCh.includes(widget)) - widget.destroy(); - else - this.remove(widget); - } - - this._set('children', children); - for (const [name, widget] of Object.entries(children)) - super.add_named(widget, name); - - this.notify('children'); - } - - get items() { - if (!Array.isArray(this._get('items'))) - this._set('items', []); - - return this._get('items'); - } - - set items(items: Array<[string, Gtk.Widget]>) { - if (items) - console.warn(Error('Stack.items is DEPRECATED, use Stack.children')); - - this.items - .filter(([name]) => !items.find(([n]) => n === name)) - .forEach(([, ch]) => ch.destroy()); - - this.items - .filter(([, ch]) => this.get_children().includes(ch)) - .forEach(([, ch]) => this.remove(ch)); - - items.forEach(([name, widget]) => { - widget && super.add_named(widget, name); - }); - - this._set('items', items); - this.show_all(); - } - - get transition() { - return Object.keys(TRANSITION).find(key => { - return TRANSITION[key as Transition] === this.transition_type; - }) as Transition; - } - - set transition(transition: Transition) { - if (this.transition === transition) - return; - - if (!Object.keys(TRANSITION).includes(transition)) { - console.error(Error( - `transition on Stack has to be one of ${Object.keys(TRANSITION)}, ` + - `but it is ${transition}`, - )); - return; - } - - this.transition_type = TRANSITION[transition]; - this.notify('transition'); - } - - get shown() { return this.visible_child_name as keyof Children; } - set shown(name: keyof Children) { - if (typeof name !== 'string') - return; - - this.set_visible_child_name(name); - } -} - -export default Stack; diff --git a/src/widgets/switch.ts b/src/widgets/switch.ts deleted file mode 100644 index 4cadd4fe..00000000 --- a/src/widgets/switch.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { register, type BaseProps, type Widget } from './widget.js'; -import Gtk from 'gi://Gtk?version=3.0'; - -type Event = (self: Self) => void | boolean - -export type SwitchProps< - Attr = unknown, - Self = Switch, -> = BaseProps -}, Attr>; - -export function newSwitch< - Attr = unknown ->(...props: ConstructorParameters>) { - return new Switch(...props); -} - -export interface Switch extends Widget { } -export class Switch extends Gtk.Switch { - static { - register(this, { - properties: { - 'on-activate': ['jsobject', 'rw'], - }, - }); - } - - constructor(props: SwitchProps = {}) { - super(props as Gtk.Switch.ConstructorProperties); - this.connect('notify::active', this.on_activate.bind(this)); - } - - get on_activate() { return this._get('on-activate') || (() => false); } - set on_activate(callback: Event) { this._set('on-activate', callback); } -} - -export default Switch; diff --git a/src/widgets/togglebutton.ts b/src/widgets/togglebutton.ts deleted file mode 100644 index 7f704ac5..00000000 --- a/src/widgets/togglebutton.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { register, type BaseProps, type Widget } from './widget.js'; -import Gtk from 'gi://Gtk?version=3.0'; - -type Event = (self: Self) => void | boolean - -export type ToggleButtonProps< - Child extends Gtk.Widget = Gtk.Widget, - Attr = unknown, - Self = ToggleButton, -> = BaseProps -}, Attr>; - -export function newToggleButton< - Child extends Gtk.Widget = Gtk.Widget, - Attr = unknown, ->(...props: ConstructorParameters>) { - return new ToggleButton(...props); -} - -// eslint-disable-next-line @typescript-eslint/no-unused-vars -export interface ToggleButton extends Widget { } -export class ToggleButton extends Gtk.ToggleButton { - static { - register(this, { - properties: { - 'on-toggled': ['jsobject', 'rw'], - }, - }); - } - - constructor(props: ToggleButtonProps = {}, child?: Child) { - if (child) - props.child = child; - - super(props as Gtk.ToggleButton.ConstructorProperties); - this.connect('toggled', this.on_toggled.bind(this)); - } - - get child() { return super.child as Child; } - set child(child: Child) { super.child = child; } - - get on_toggled() { return this._get('on-toggled') || (() => false); } - set on_toggled(callback: Event) { this._set('on-toggled', callback); } -} diff --git a/src/widgets/widget.ts b/src/widgets/widget.ts deleted file mode 100644 index 1f625037..00000000 --- a/src/widgets/widget.ts +++ /dev/null @@ -1,553 +0,0 @@ -import GObject from 'gi://GObject'; -import Gtk from 'gi://Gtk?version=3.0'; -import GLib from 'gi://GLib?version=2.0'; -import Gdk from 'gi://Gdk?version=3.0'; -import Cairo from 'gi://cairo?version=1.0'; -import { Props, BindableProps, Binding, Connectable } from '../service.js'; -import { registerGObject, kebabify, type CtorProps } from '../utils/gobject.js'; -import { interval, idle } from '../utils.js'; - -let warned = false; -function deprecated() { - if (warned) - return; - - console.warn(Error('Using "connections" and "binds" props are DEPRECATED\n' + - 'Use .hook() .bind() .poll() .on() instead, refer to the wiki to see examples')); - warned = true; -} - -const ALIGN = { - 'fill': Gtk.Align.FILL, - 'start': Gtk.Align.START, - 'end': Gtk.Align.END, - 'center': Gtk.Align.CENTER, - 'baseline': Gtk.Align.BASELINE, -} as const; - -type Align = keyof typeof ALIGN; - -type Keys = { - [K in keyof typeof Gdk as K extends `KEY_${infer U}` ? U : never]: number; -}; - -type ModifierKey = { - [K in keyof typeof Gdk.ModifierType as K extends `${infer M}_MASK` ? M : never]: number -} - -type Cursor = - | 'default' - | 'help' - | 'pointer' - | 'context-menu' - | 'progress' - | 'wait' - | 'cell' - | 'crosshair' - | 'text' - | 'vertical-text' - | 'alias' - | 'copy' - | 'no-drop' - | 'move' - | 'not-allowed' - | 'grab' - | 'grabbing' - | 'all-scroll' - | 'col-resize' - | 'row-resize' - | 'n-resize' - | 'e-resize' - | 's-resize' - | 'w-resize' - | 'ne-resize' - | 'nw-resize' - | 'sw-resize' - | 'se-resize' - | 'ew-resize' - | 'ns-resize' - | 'nesw-resize' - | 'nwse-resize' - | 'zoom-in' - | 'zoom-out' - -type Property = [prop: string, value: unknown]; - -type Connection = - | [GObject.Object, (self: Self, ...args: unknown[]) => unknown, string?] - | [string, (self: Self, ...args: unknown[]) => unknown] - | [number, (self: Self, ...args: unknown[]) => unknown]; - -type Bind = [ - prop: string, - obj: GObject.Object, - objProp?: string, - transform?: (value: any) => any, -]; - -interface CommonProps { - class_name?: string - class_names?: Array - click_through?: boolean - css?: string - hpack?: Align - vpack?: Align - cursor?: Cursor - attribute?: Attr -} - -export type BaseProps = { - setup?: (self: Self) => void -} & BindableProps>> - -type Required = { [K in keyof T]-?: T[K] }; -export interface Widget extends Required> { - hook( - gobject: Connectable, - callback: (self: this, ...args: any[]) => void, - signal?: string, - ): this - - bind< - Prop extends keyof Props, - GObj extends Connectable, - ObjProp extends keyof Props, - >( - prop: Prop, - gobject: GObj, - objProp?: ObjProp, - transform?: (value: GObj[ObjProp]) => this[Prop], - ): this - - on( - signal: string, - callback: (self: this, ...args: any[]) => void - ): this - - poll( - timeout: number, - callback: (self: this) => void, - ): this - - keybind< - Fn extends (self: this, event: Gdk.Event) => void, - Key extends keyof Keys, - >( - key: Key, - callback: Fn, - ): this - - keybind< - Fn extends (self: this, event: Gdk.Event) => void, - Key extends keyof Keys, - Mod extends Array, - >( - mods: Mod, - key: Key, - callback: Fn, - ): this, - - readonly is_destroyed: boolean - _handleParamProp(prop: keyof this, value: any): void - _get(field: string): T; - _set(field: string, value: T, notify?: boolean): void - - toggleClassName(className: string, condition?: boolean): void - setCss(css: string): void - isHovered(event?: Gdk.Event): boolean -} - -export class AgsWidget extends Gtk.Widget implements Widget { - set attribute(attr: Attr) { this._set('attribute', attr); } - get attribute(): Attr { return this._get('attribute'); } - - hook( - gobject: Connectable, - callback: (self: this, ...args: any[]) => void, - signal?: string, - ): this { - const con = typeof gobject?.connect !== 'function'; - const discon = typeof gobject?.disconnect !== 'function'; - if (con || discon) { - console.error(Error(`${gobject} is not a Connectable, missing ` + - ` ${[con ? 'connect' : '', discon ? 'disconnect' : ''].join(', ')} function`)); - return this; - } - - const id = gobject.connect(signal!, (_, ...args: unknown[]) => { - callback(this, ...args); - }); - - this.connect('destroy', () => { - gobject.disconnect(id); - }); - - GLib.idle_add(GLib.PRIORITY_DEFAULT_IDLE, () => { - if (!this.is_destroyed) - callback(this); - - return GLib.SOURCE_REMOVE; - }); - - return this; - } - - bind< - Prop extends keyof Props, - GObj extends Connectable, - ObjProp extends keyof Props, - >( - prop: Prop, - gobject: GObj, - objProp?: ObjProp, - transform?: (value: GObj[ObjProp]) => this[Prop], - ): this { - const targetProp = objProp || 'value'; - const callback = transform - ? () => { - // @ts-expect-error too lazy to type - this[prop] = transform(gobject[targetProp]); - } - : () => { - // @ts-expect-error too lazy to type - this[prop] = gobject[targetProp]; - }; - - this.hook(gobject, callback, `notify::${kebabify(targetProp)}`); - return this; - } - - on(signal: string, callback: (self: this, ...args: any[]) => void): this { - this.connect(signal, callback); - return this; - } - - poll(timeout: number, callback: (self: this) => void): this { - interval(timeout, () => callback(this), this); - return this; - } - - keybind< - // eslint-disable-next-line space-before-function-paren - Fn extends (self: this, event: Gdk.Event) => void, - Key extends keyof Keys, - Mod extends Array, - >( - modsOrKey: Key | Mod, - keyOrCallback: Key | Fn, - callback?: Fn, - ): this { - const mods = callback ? modsOrKey as Mod : [] as unknown as Mod; - const key = callback ? keyOrCallback as Key : modsOrKey as Key; - const fn = callback ? callback : keyOrCallback as Fn; - - this.connect('key-press-event', (_, event: Gdk.Event) => { - const k = event.get_keyval()[1]; - const m = event.get_state()[1]; - const ms = mods.reduce((ms, m) => ms | Gdk.ModifierType[`${m}_MASK`], 0); - - if (mods.length > 0 && k === Gdk[`KEY_${key}`] && m === ms) - return fn(this, event); - - if (mods.length === 0 && k === Gdk[`KEY_${key}`]) - return fn(this, event); - }); - - return this; - } - - _init( - config: BaseProps = {}, - child?: Gtk.Widget, - ) { - const { setup, attribute, ...props } = config; - - const binds = (Object.keys(props) as Array) - .map(prop => { - if (props[prop] instanceof Binding) { - const bind = [prop, props[prop]]; - delete props[prop]; - return bind; - } - }) - .filter(pair => pair); - - if (child) - props.child = child; - - super._init(props as Gtk.Widget.ConstructorProperties); - - if (attribute !== undefined) - this._set('attribute', attribute); - - (binds as unknown as Array<[keyof Props, Binding]>) - .forEach(([selfProp, { emitter, prop, transformFn }]) => { - this.bind(selfProp, emitter, prop, transformFn); - }); - - this.add_events(Gdk.EventMask.ENTER_NOTIFY_MASK); - this.add_events(Gdk.EventMask.LEAVE_NOTIFY_MASK); - - this.connect('enter-notify-event', this._updateCursor.bind(this)); - this.connect('leave-notify-event', this._updateCursor.bind(this)); - this.connect('destroy', () => this._set('is-destroyed', true)); - - idle(() => { - if (this.click_through && !this.is_destroyed) - this.input_shape_combine_region(new Cairo.Region); - }); - - if (setup) - setup(this); - } - - _handleParamProp(prop: keyof Props, value: any) { - if (value === undefined) - return; - - if (value instanceof Binding) - // @ts-expect-error implementation in Connectable - this.bind(prop, value.emitter, value.prop, value.transformFn); - else - this[prop as keyof this] = value; - } - - get is_destroyed(): boolean { return this._get('is-destroyed') || false; } - - // defining private fields for typescript causes - // gobject constructor field setters to be overridden - // so we use this _get and _set to avoid @ts-expect-error everywhere - _get(field: string) { - return (this as unknown as { [key: string]: unknown })[`__${field}`] as T; - } - - _set(field: string, value: T, notify = true) { - if (this._get(field) === value) - return; - - (this as unknown as { [key: string]: T })[`__${field}`] = value; - - if (notify) - this.notify(field); - } - - // FIXME: deprecated - set connections(connections: Connection[]) { - if (!connections) - return; - - deprecated(); - connections.forEach(([s, callback, event]) => { - if (s === undefined || callback === undefined) - return console.error(Error('missing arguments to connections')); - - if (typeof s === 'string') - this.connect(s, callback); - - else if (typeof s === 'number') - interval(s, () => callback(this), this); - - else if (s instanceof GObject.Object) - this.hook(s, callback, event); - - else - console.error(Error(`${s} is not a GObject | string | number`)); - }); - } - - // FIXME: deprecated - set binds(binds: Bind[]) { - if (!binds) - return; - - deprecated(); - binds.forEach(([prop, obj, objProp = 'value', transform = out => out]) => { - // @ts-expect-error - this.bind(prop, obj, objProp, transform); - }); - } - - // FIXME: deprecated - set properties(properties: Property[]) { - if (!properties) - return; - - console.warn(Error('"properties" is deprecated use "attribute" instead')); - properties.forEach(([key, value]) => { - (this as unknown as { [key: string]: unknown })[`_${key}`] = value; - }); - } - - // FIXME: deprecated - connectTo( - gobject: GObject, - callback: (self: this, ...args: any[]) => void, - signal?: string, - ) { - console.warn(Error('connectTo was renamed to hook')); - return this.hook(gobject, callback, signal); - } - - _setPack(orientation: 'h' | 'v', align: Align) { - if (!align) - return; - - if (!Object.keys(ALIGN).includes(align)) { - return console.error(Error( - `${orientation}pack has to be on of ${Object.keys(ALIGN)}, but it is ${align}`, - )); - } - - this[`${orientation}align`] = ALIGN[align]; - } - - _getPack(orientation: 'h' | 'v') { - return Object.keys(ALIGN).find(align => { - return ALIGN[align as Align] === this[`${orientation}align`]; - }) as Align; - } - - get hpack() { return this._getPack('h'); } - set hpack(align: Align) { this._setPack('h', align); } - - get vpack() { return this._getPack('v'); } - set vpack(align: Align) { this._setPack('v', align); } - - toggleClassName(className: string, condition = true) { - const c = this.get_style_context(); - condition - ? c.add_class(className) - : c.remove_class(className); - - this.notify('class-names'); - this.notify('class-name'); - } - - get class_name() { - return this.class_names.join(' '); - } - - set class_name(names: string) { - this.class_names = names.split(/\s+/); - } - - get class_names() { - return this.get_style_context().list_classes() || []; - } - - set class_names(names: string[]) { - this.class_names.forEach((cn: string) => this.toggleClassName(cn, false)); - names.forEach(cn => this.toggleClassName(cn)); - } - - _cssProvider!: Gtk.CssProvider; - setCss(css: string) { - if (!css.includes('{') || !css.includes('}')) - css = `* { ${css} }`; - - if (this._cssProvider) - this.get_style_context().remove_provider(this._cssProvider); - - this._cssProvider = new Gtk.CssProvider(); - this._cssProvider.load_from_data(new TextEncoder().encode(css)); - this.get_style_context() - .add_provider(this._cssProvider, Gtk.STYLE_PROVIDER_PRIORITY_USER); - - this.notify('css'); - } - - get css() { - return this._cssProvider?.to_string() || ''; - } - - set css(css: string) { - if (!css) - return; - - this.setCss(css); - } - - _updateCursor() { - if (!this.cursor) - return; - - const display = Gdk.Display.get_default(); - - if (this.isHovered() && display) { - const cursor = Gdk.Cursor.new_from_name(display, this.cursor); - this.get_window()?.set_cursor(cursor); - } - else if (display) { - const cursor = Gdk.Cursor.new_from_name(display, 'default'); - this.get_window()?.set_cursor(cursor); - } - } - - get cursor() { return this._get('cursor'); } - set cursor(cursor: Cursor) { - this._set('cursor', cursor); - this._updateCursor(); - } - - isHovered(event?: Gdk.Event) { - let [x, y] = this.get_pointer(); - const { width: w, height: h } = this.get_allocation(); - if (event) - [, x, y] = event.get_coords(); - - return x > 0 && x < w && y > 0 && y < h; - } - - get click_through() { return !!this._get('click-through'); } - set click_through(clickThrough: boolean) { - if (this.click_through === clickThrough) - return; - - const value = clickThrough ? new Cairo.Region : null; - this.input_shape_combine_region(value); - this._set('click-through', value); - this.notify('click-through'); - } -} - -export function register( - klass: T, - config?: Parameters[1] & { cssName?: string }, -) { - Object.getOwnPropertyNames(AgsWidget.prototype).forEach(name => { - Object.defineProperty(klass.prototype, name, - Object.getOwnPropertyDescriptor(AgsWidget.prototype, name) || - Object.create(null), - ); - }); - return registerGObject(klass, { - cssName: config?.cssName, - typename: config?.typename || `Ags_${klass.name}`, - signals: config?.signals, - properties: { - ...config?.properties, - 'class-name': ['string', 'rw'], - 'class-names': ['jsobject', 'rw'], - 'css': ['string', 'rw'], - 'hpack': ['string', 'rw'], - 'vpack': ['string', 'rw'], - 'cursor': ['string', 'rw'], - 'is-destroyed': ['boolean', 'r'], - 'attribute': ['jsobject', 'rw'], - 'click-through': ['boolean', 'rw'], - - // FIXME: deprecated - 'properties': ['jsobject', 'w'], - 'connections': ['jsobject', 'w'], - 'binds': ['jsobject', 'w'], - }, - }); -} - -// FIXME: backwards compatibility -export default function W(klass: any) { - return klass; -} -W.register = register; diff --git a/src/widgets/window.ts b/src/widgets/window.ts deleted file mode 100644 index f1094d0b..00000000 --- a/src/widgets/window.ts +++ /dev/null @@ -1,346 +0,0 @@ -import { register, type BaseProps, type Widget } from './widget.js'; -import Gtk from 'gi://Gtk?version=3.0'; -import Gdk from 'gi://Gdk?version=3.0'; -import { Binding } from '../service.js'; -import App from '../app.js'; -// @ts-expect-error missing types FIXME: -import { default as LayerShell } from 'gi://GtkLayerShell'; - -const ANCHOR = { - 'left': LayerShell.Edge.LEFT, - 'right': LayerShell.Edge.RIGHT, - 'top': LayerShell.Edge.TOP, - 'bottom': LayerShell.Edge.BOTTOM, -} as const; - -const LAYER = { - 'background': LayerShell.Layer.BACKGROUND, - 'bottom': LayerShell.Layer.BOTTOM, - 'top': LayerShell.Layer.TOP, - 'overlay': LayerShell.Layer.OVERLAY, -} as const; - -const KEYMODE = { - 'on-demand': LayerShell.KeyboardMode.ON_DEMAND, - 'exclusive': LayerShell.KeyboardMode.EXCLUSIVE, - 'none': LayerShell.KeyboardMode.NONE, -} as const; - -type Layer = keyof typeof LAYER; -type Anchor = keyof typeof ANCHOR; -type Exclusivity = 'normal' | 'ignore' | 'exclusive'; -type Keymode = keyof typeof KEYMODE; - -export type WindowProps< - Child extends Gtk.Widget = Gtk.Widget, - Attr = unknown, - Self = Window, -> = BaseProps - -export function newWindow< - Child extends Gtk.Widget = Gtk.Widget, - Attr = unknown, ->(...props: ConstructorParameters>) { - return new Window(...props); -} - -// eslint-disable-next-line @typescript-eslint/no-unused-vars -export interface Window extends Widget { } -export class Window extends Gtk.Window { - static { - register(this, { - properties: { - 'anchor': ['jsobject', 'rw'], - 'exclusive': ['boolean', 'rw'], - 'exclusivity': ['string', 'rw'], - 'focusable': ['boolean', 'rw'], - 'layer': ['string', 'rw'], - 'margins': ['jsobject', 'rw'], - 'monitor': ['int', 'rw'], - 'gdkmonitor': ['jsobject', 'rw'], - 'popup': ['boolean', 'rw'], - 'keymode': ['string', 'rw'], - }, - }); - } - - // the window has to be set as a layer, - // so we can't rely on gobject constructor - constructor({ - anchor = [], - exclusive, - exclusivity = 'normal', - focusable = false, - keymode = 'none', - layer = 'top', - margins = [], - monitor = -1, - gdkmonitor, - popup = false, - visible = true, - ...params - }: WindowProps = {}, child?: Child) { - if (child) - params.child = child; - - super(params as Gtk.Window.ConstructorProperties); - LayerShell.init_for_window(this); - LayerShell.set_namespace(this, this.name); - - - this._handleParamProp('anchor', anchor); - this._handleParamProp('exclusive', exclusive); - this._handleParamProp('exclusivity', exclusivity); - this._handleParamProp('focusable', focusable); - this._handleParamProp('layer', layer); - this._handleParamProp('margins', margins); - this._handleParamProp('monitor', monitor); - this._handleParamProp('gdkmonitor', gdkmonitor); - this._handleParamProp('keymode', keymode); - - this.show_all(); - this._handleParamProp('popup', popup); - - if (visible instanceof Binding) - this._handleParamProp('visible', visible); - else - this.visible = visible === true || visible === null && !popup; - } - - get child() { return super.child as Child; } - set child(child: Child) { super.child = child; } - - get gdkmonitor(): Gdk.Monitor | null { return this._get('gdkmonitor') || null; } - set gdkmonitor(monitor: Gdk.Monitor | null) { - this._set('gdkmonitor', monitor); - LayerShell.set_monitor(this, monitor); - this.notify('gdkmonitor'); - } - - get monitor(): number { return this._get('monitor'); } - set monitor(monitor: number) { - if (monitor < 0) - return; - - const m = Gdk.Display.get_default()?.get_monitor(monitor); - if (m) { - this.gdkmonitor = m; - this._set('monitor', monitor); - return; - } - - console.error(`Could not find monitor with id: ${monitor}`); - } - - // FIXME: deprecated - get exclusive() { return LayerShell.auto_exclusive_zone_is_enabled(this); } - set exclusive(exclusive: boolean) { - if (exclusive === undefined) - return; - - console.warn('Window.exclusive is DEPRECATED, use Window.exclusivity'); - if (this.exclusive === exclusive) - return; - - exclusive - ? LayerShell.auto_exclusive_zone_enable(this) - : LayerShell.set_exclusive_zone(this, 0); - - this.notify('exclusive'); - } - - get exclusivity(): Exclusivity { - if (LayerShell.auto_exclusive_zone_is_enabled(this)) - return 'exclusive'; - - if (LayerShell.get_exclusive_zone(this) === -1) - return 'ignore'; - - return 'normal'; - } - - set exclusivity(exclusivity: Exclusivity) { - if (this.exclusivity === exclusivity) - return; - - switch (exclusivity) { - case 'normal': - LayerShell.set_exclusive_zone(this, 0); - break; - - case 'ignore': - LayerShell.set_exclusive_zone(this, -1); - break; - - case 'exclusive': - LayerShell.auto_exclusive_zone_enable(this); - break; - - default: - console.error(Error('wrong value for exclusivity')); - break; - } - - this.notify('exclusivity'); - } - - get layer() { - return Object.keys(LAYER).find(layer => { - return LAYER[layer as Layer] === LayerShell.get_layer(this); - }) as Layer; - } - - set layer(layer: Layer) { - if (this.layer === layer) - return; - - if (!Object.keys(LAYER).includes(layer)) { - console.error('wrong layer value for Window'); - return; - } - - LayerShell.set_layer(this, LAYER[layer]); - this.notify('layer'); - } - - get anchor() { - return Object.keys(ANCHOR).filter(key => { - return LayerShell.get_anchor(this, ANCHOR[key as Anchor]); - }) as Anchor[]; - } - - set anchor(anchor: Anchor[]) { - if (this.anchor.length === anchor.length && - this.anchor.every(a => anchor.includes(a))) - return; - - // reset - Object.values(ANCHOR).forEach(side => LayerShell.set_anchor(this, side, false)); - - anchor.forEach(side => { - if (!Object.keys(ANCHOR).includes(side)) - return console.error(`${side} is not a valid anchor`); - - LayerShell.set_anchor(this, ANCHOR[side], true); - }); - - this.notify('anchor'); - } - - get margins() { - return ['TOP', 'RIGHT', 'BOTTOM', 'LEFT'].map(edge => - LayerShell.get_margin(this, LayerShell.Edge[edge]), - ); - } - - set margins(margin: number[]) { - let margins: [side: string, index: number][] = []; - switch (margin.length) { - case 1: - margins = [['TOP', 0], ['RIGHT', 0], ['BOTTOM', 0], ['LEFT', 0]]; - break; - case 2: - margins = [['TOP', 0], ['RIGHT', 1], ['BOTTOM', 0], ['LEFT', 1]]; - break; - case 3: - margins = [['TOP', 0], ['RIGHT', 1], ['BOTTOM', 2], ['LEFT', 1]]; - break; - case 4: - margins = [['TOP', 0], ['RIGHT', 1], ['BOTTOM', 2], ['LEFT', 3]]; - break; - default: - break; - } - - margins.forEach(([side, i]) => - LayerShell.set_margin(this, - LayerShell.Edge[side], (margin as number[])[i]), - ); - - this.notify('margins'); - } - - // FIXME: deprecated - get popup() { return !!this._get('popup'); } - set popup(popup: boolean) { - if (this.popup === popup) - return; - - console.warn('Window.popup is DEPRECATED. ' - + 'the click away functionality depends on a bug which was patched in Hyprland ' - + 'and it never worked on Sway anyway. ' - + 'to close on the esc key ' - + 'use self.keybind("Escape", () => App.closeWindow("window-name"))'); - - if (this.popup) { - const [esc, click] = this._get<[number, number]>('popup'); - this.disconnect(esc); - this.disconnect(click); - } - - if (popup) { - const esc = this.connect('key-press-event', (_, event: Gdk.Event) => { - if (event.get_keyval()[1] === Gdk.KEY_Escape) { - App.getWindow(this.name!) - ? App.closeWindow(this.name!) - : this.hide(); - } - }); - - const click = this.connect('button-release-event', () => { - const [x, y] = this.get_pointer(); - if (x === 0 && y === 0) - App.closeWindow(this.name!); - }); - - this._set('popup', [esc, click]); - } - } - - // FIXME: deprecated - get focusable() { - return LayerShell.get_keyboard_mode(this) === LayerShell.KeyboardMode.ON_DEMAND; - } - - set focusable(focusable: boolean) { - if (this.focusable === focusable) - return; - - console.warn('Window.focusable is DEPRECATED, use Window.keymode'); - LayerShell.set_keyboard_mode( - this, LayerShell.KeyboardMode[focusable ? 'ON_DEMAND' : 'NONE']); - - this.notify('focusable'); - } - - get keymode() { - return Object.keys(KEYMODE).find(layer => { - return KEYMODE[layer as Keymode] === LayerShell.get_keyboard_mode(this); - }) as Keymode; - } - - set keymode(mode: Keymode) { - if (this.keymode === mode) - return; - - LayerShell.set_keyboard_mode(this, KEYMODE[mode]); - this.notify('keymode'); - } -} - -export default Window; diff --git a/subprojects/gutils/ags b/subprojects/gutils/ags deleted file mode 100644 index 64ddbb73..00000000 --- a/subprojects/gutils/ags +++ /dev/null @@ -1,4 +0,0 @@ -# PAM configuration file for ags. By default, it includes -# the 'login' configuration file (see /etc/pam.d/login) - -auth include login diff --git a/subprojects/gutils/meson.build b/subprojects/gutils/meson.build deleted file mode 100644 index 4f530295..00000000 --- a/subprojects/gutils/meson.build +++ /dev/null @@ -1,40 +0,0 @@ -project('pam', 'c') - -pkglibdir = get_option('pkglibdir') -pkgdatadir = get_option('pkgdatadir') - -cc = meson.get_compiler('c') - -libsources = ['pam.c', 'pam.h'] - -deps = [ - dependency('gobject-2.0'), - dependency('gio-2.0'), - dependency('pam') -] - -girlib = shared_library( - 'gutils', - sources: libsources, - dependencies: deps, - install: true -) - -gnome = import('gnome') -gnome.generate_gir( - girlib, - sources: libsources, - nsversion: '1.0', - namespace: 'GUtils', - symbol_prefix: 'gutils', - identifier_prefix: 'GUtils', - includes: ['GObject-2.0', 'Gio-2.0'], - install_dir_gir: pkgdatadir, - install_dir_typelib: pkglibdir, - install: true -) - -install_data( - 'ags', - install_dir: get_option('sysconfdir') / 'pam.d' -) diff --git a/subprojects/gutils/meson_options.txt b/subprojects/gutils/meson_options.txt deleted file mode 100644 index a685bbb9..00000000 --- a/subprojects/gutils/meson_options.txt +++ /dev/null @@ -1,13 +0,0 @@ -option('pkglibdir', - type: 'string', - value: '', - description: 'The private directory the shared library/typelib will be installed into.' -) - -option('pkgdatadir', - type: 'string', - value: '', - description: 'The private directory the gir file will be installed into.' -) - - diff --git a/subprojects/gutils/pam.c b/subprojects/gutils/pam.c deleted file mode 100644 index a9e09544..00000000 --- a/subprojects/gutils/pam.c +++ /dev/null @@ -1,137 +0,0 @@ -#include "pam.h" -#include -#include -#include -#include - -typedef struct { - gchar *username; - gchar *password; -} auth_info; - -void free_auth_info(void* data) { - auth_info* info = (auth_info*) data; - free(info->username); - free(info->password); - free(info); -} - -int handle_conversation(int num_msg, - const struct pam_message **msg, - struct pam_response **resp, - void *appdata_ptr) { - struct pam_response *replies = NULL; - if (num_msg <= 0 || num_msg > PAM_MAX_NUM_MSG) { - return PAM_CONV_ERR; - } - replies = (struct pam_response *)calloc(num_msg, sizeof(struct pam_response)); - if (replies == NULL){ - return PAM_BUF_ERR; - } - for (int i = 0; i < num_msg; ++i) { - switch (msg[i]->msg_style) { - case PAM_PROMPT_ECHO_OFF: - case PAM_PROMPT_ECHO_ON: - replies[i].resp = strdup((const char *)appdata_ptr); - if (replies[i].resp == NULL) { - return PAM_ABORT; - } - break; - case PAM_ERROR_MSG: - case PAM_TEXT_INFO: - break; - } - } - *resp = replies; - return PAM_SUCCESS; -} - -void auth_thread (GTask *task, - gpointer object, - gpointer task_data, - GCancellable *cancellable) { - auth_info *info = (auth_info *)task_data; - - pam_handle_t *pamh = NULL; - const struct pam_conv conv = { - .conv = handle_conversation, - .appdata_ptr = (void *) info->password, - }; - int retval; - retval = pam_start("ags", info->username, &conv, &pamh); - if (retval == PAM_SUCCESS) { - retval = pam_authenticate(pamh, 0); - pam_end(pamh, retval); - } - if (retval != PAM_SUCCESS) { - g_task_return_new_error(task, G_IO_ERROR, G_IO_ERROR_FAILED, "%s", pam_strerror(pamh, retval)); - } - else { - g_task_return_int(task, retval); - } -} - -/** - * gutils_authenticate_user: - * @username: the username for which the password to be authenticated - * @password: the password to be authenticated - * @io_priority: the [I/O priority][io-priority] of the request - * @cancellable: (nullable): optional #GCancellable object, - * %NULL to ignore - * @callback: (scope async) (closure user_data): a #GAsyncReadyCallback - * to call when the request is satisfied - * @user_data: the data to pass to callback function - * - * Requests authentication of the provided password for the specified username using the PAM (Pluggable Authentication Modules) system. - */ -void gutils_authenticate_user (const gchar* username, - const gchar* password, - int io_priority, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) { - auth_info *info = (auth_info *) malloc(sizeof(auth_info)); - if (info == NULL) return; - info->username = strdup(username); - info->password = strdup(password); - - GTask *task; - task = g_task_new (NULL, cancellable, callback, user_data); - g_task_set_task_data(task, info, free_auth_info); - g_task_set_priority(task, io_priority); - g_task_run_in_thread(task, auth_thread); - g_object_unref (task); -} - -int gutils_authenticate_user_finish(GAsyncResult *res, - GError **error) { - return g_task_propagate_int(G_TASK(res), error) ; -} - -/** - * gutils_authenticate: - * @password: the password to be authenticated - * @io_priority: the [I/O priority][io-priority] of the request - * @cancellable: (nullable): optional #GCancellable object, - * %NULL to ignore - * @callback: (scope async) (closure user_data): a #GAsyncReadyCallback - * to call when the request is satisfied - * @user_data: the data to pass to callback function - * - * Requests authentication of the provided password using the PAM (Pluggable Authentication Modules) system. - */ -void gutils_authenticate (const gchar* password, - int io_priority, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) { - struct passwd *passwd = getpwuid(getuid()); - char *username = passwd->pw_name; - - return gutils_authenticate_user(username, password, io_priority, cancellable, callback, user_data); -} - -int gutils_authenticate_finish(GAsyncResult *res, - GError **error){ - return g_task_propagate_int(G_TASK(res), error) ; -} diff --git a/subprojects/gutils/pam.h b/subprojects/gutils/pam.h deleted file mode 100644 index 6bf06420..00000000 --- a/subprojects/gutils/pam.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef PAM_H -#define PAM_H - -#include -#include - -G_BEGIN_DECLS - -void gutils_authenticate_user (const char* username, - const char* password, - int io_priority, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); - -int gutils_authenticate_user_finish (GAsyncResult *res, - GError **error); - - -void gutils_authenticate (const char* password, - int io_priority, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); - -int gutils_authenticate_finish (GAsyncResult *res, - GError **error); - -G_END_DECLS - -#endif // !PAM_H diff --git a/subprojects/gvc b/subprojects/gvc deleted file mode 160000 index 8e7a5a4c..00000000 --- a/subprojects/gvc +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 8e7a5a4c3e51007ce6579292642517e3d3eb9c50 diff --git a/tsconfig.json b/tsconfig.json deleted file mode 100644 index 7f3406d2..00000000 --- a/tsconfig.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "compilerOptions": { - "target": "ES2022", - "module": "ES2022", - "lib": [ - "ES2022" - ], - "allowJs": true, - "checkJs": false, - "outDir": "_build/tsc-out", - "strict": true, - "moduleResolution": "node", - "baseUrl": ".", - "typeRoots": [ - "./node_modules/@girs" - ], - "skipLibCheck": true, - "forceConsistentCasingInFileNames": true - }, - "include": [ - "@girs", - "types/*", - "src" - ] -} diff --git a/types/ambient.d.ts b/types/ambient.d.ts deleted file mode 100644 index 7d89cb0f..00000000 --- a/types/ambient.d.ts +++ /dev/null @@ -1,44 +0,0 @@ -declare function print(...args: any[]): void; - -declare const pkg: { - version: string; - name: string; - pkgdatadir: string; -}; - -declare const imports: { - config: any; - gi: any; - searchPath: string[]; -} - -declare module console { - function error(obj: object, others?: object[]): void; - function error(msg: string, subsitutions?: any[]): void; - function log(obj: object, others?: object[]): void; - function log(msg: string, subsitutions?: any[]): void; - function warn(obj: object, others?: object[]): void; - function warn(msg: string, subsitutions?: any[]): void; -} - -declare interface String { - format(...replacements: string[]): string; - format(...replacements: number[]): string; -} - -declare interface Number { - toFixed(digits: number): number; -} - -declare class TextDecoder { - constructor(label?: string, options?: TextDecoderOptions); - decode(input?: BufferSource, options?: TextDecodeOptions): string; - readonly encoding: string; - readonly fatal: boolean; - readonly ignoreBOM: boolean; -} - -declare class TextEncoder { - constructor(); - encode(input?: string): Uint8Array; -} diff --git a/version b/version index 53adb84c..227cea21 100644 --- a/version +++ b/version @@ -1 +1 @@ -1.8.2 +2.0.0